diff --git a/lib/src/bindings/repository.dart b/lib/src/bindings/repository.dart index 2cdbe28..b2cb040 100644 --- a/lib/src/bindings/repository.dart +++ b/lib/src/bindings/repository.dart @@ -71,13 +71,55 @@ String discover(String startPath, String ceilingDirs) { /// Creates a new Git repository in the given folder. /// /// Throws a [LibGit2Error] if error occured. -Pointer init(String path, bool isBare) { +Pointer init( + String path, + int flags, + int mode, + String workdirPath, + String description, + String templatePath, + String initialHead, + String originUrl, +) { final out = calloc>(); final pathC = path.toNativeUtf8().cast(); - final isBareC = isBare ? 1 : 0; - final error = libgit2.git_repository_init(out, pathC, isBareC); + final workdirPathC = + workdirPath.isEmpty ? nullptr : workdirPath.toNativeUtf8().cast(); + final descriptionC = + description.isEmpty ? nullptr : description.toNativeUtf8().cast(); + final templatePathC = + templatePath.isEmpty ? nullptr : templatePath.toNativeUtf8().cast(); + final initialHeadC = + initialHead.isEmpty ? nullptr : initialHead.toNativeUtf8().cast(); + final originUrlC = + originUrl.isEmpty ? nullptr : originUrl.toNativeUtf8().cast(); + final opts = calloc(); + final optsError = libgit2.git_repository_init_options_init( + opts, + GIT_REPOSITORY_INIT_OPTIONS_VERSION, + ); + + if (optsError < 0) { + throw LibGit2Error(libgit2.git_error_last()); + } + + opts.ref.flags = flags; + opts.ref.mode = mode; + opts.ref.workdir_path = workdirPathC; + opts.ref.description = descriptionC; + opts.ref.template_path = templatePathC; + opts.ref.initial_head = initialHeadC; + opts.ref.origin_url = originUrlC; + + final error = libgit2.git_repository_init_ext(out, pathC, opts); calloc.free(pathC); + calloc.free(workdirPathC); + calloc.free(descriptionC); + calloc.free(templatePathC); + calloc.free(initialHeadC); + calloc.free(originUrlC); + calloc.free(opts); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); @@ -98,6 +140,9 @@ Pointer clone( final out = calloc>(); final urlC = url.toNativeUtf8().cast(); final localPathC = localPath.toNativeUtf8().cast(); + final checkoutBranchC = checkoutBranch.isEmpty + ? nullptr + : checkoutBranch.toNativeUtf8().cast(); final cloneOptions = calloc(); final cloneOptionsError = libgit2.git_clone_options_init(cloneOptions, GIT_CLONE_OPTIONS_VERSION); @@ -115,16 +160,16 @@ Pointer clone( } cloneOptions.ref.bare = bare ? 1 : 0; - cloneOptions.ref.checkout_branch = checkoutBranch.isEmpty - ? nullptr - : checkoutBranch.toNativeUtf8().cast(); + cloneOptions.ref.checkout_branch = checkoutBranchC; cloneOptions.ref.fetch_opts = fetchOptions.ref; final error = libgit2.git_clone(out, urlC, localPathC, cloneOptions); calloc.free(urlC); calloc.free(localPathC); + calloc.free(checkoutBranchC); calloc.free(cloneOptions); + calloc.free(fetchOptions); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); diff --git a/lib/src/git_types.dart b/lib/src/git_types.dart index bc71af2..56c67e0 100644 --- a/lib/src/git_types.dart +++ b/lib/src/git_types.dart @@ -1215,3 +1215,57 @@ class GitFetchPrune { @override String toString() => 'GitFetchPrune.$_name'; } + +/// Option flags for [Repository] init. +class GitRepositoryInit { + const GitRepositoryInit._(this._value, this._name); + final int _value; + final String _name; + + /// Create a bare repository with no working directory. + static const bare = GitRepositoryInit._(1, 'bare'); + + /// Return an GIT_EEXISTS error if the repo path appears to already be + /// an git repository. + static const noReinit = GitRepositoryInit._(2, 'noReinit'); + + /// Normally a "/.git/" will be appended to the repo path for + /// non-bare repos (if it is not already there), but passing this flag + /// prevents that behavior. + static const noDotGitDir = GitRepositoryInit._(4, 'noDotGitDir'); + + /// Make the repo path (and workdir path) as needed. Init is always willing + /// to create the ".git" directory even without this flag. This flag tells + /// init to create the trailing component of the repo and workdir paths + /// as needed. + static const mkdir = GitRepositoryInit._(8, 'mkdir'); + + /// Recursively make all components of the repo and workdir paths as + /// necessary. + static const mkpath = GitRepositoryInit._(16, 'mkpath'); + + /// libgit2 normally uses internal templates to initialize a new repo. + /// This flags enables external templates, looking the [templatePath] from + /// the options if set, or the `init.templatedir` global config if not, + /// or falling back on "/usr/share/git-core/templates" if it exists. + static const externalTemplate = GitRepositoryInit._(32, 'externalTemplate'); + + /// If an alternate workdir is specified, use relative paths for the gitdir + /// and core.worktree. + static const relativeGitlink = GitRepositoryInit._(64, 'relativeGitlink'); + + static const List values = [ + bare, + noReinit, + noDotGitDir, + mkdir, + mkpath, + externalTemplate, + relativeGitlink, + ]; + + int get value => _value; + + @override + String toString() => 'GitRepositoryInit.$_name'; +} diff --git a/lib/src/repository.dart b/lib/src/repository.dart index bad2cae..eb38d25 100644 --- a/lib/src/repository.dart +++ b/lib/src/repository.dart @@ -35,15 +35,40 @@ class Repository { Repository(this._repoPointer); /// Initializes a new instance of the [Repository] class by creating a new - /// Git repository in the given folder. + /// git repository in the given folder. /// /// Should be freed with `free()` to release allocated memory. /// /// Throws a [LibGit2Error] if error occured. - Repository.init(String path, {bool isBare = false}) { + Repository.init({ + required String path, + bool bare = false, + Set flags = const {GitRepositoryInit.mkpath}, + int mode = 0, + String workdirPath = '', + String description = '', + String templatePath = '', + String initialHead = '', + String originUrl = '', + }) { libgit2.git_libgit2_init(); - _repoPointer = bindings.init(path, isBare); + int flagsInt = flags.fold(0, (previousValue, e) => previousValue | e.value); + + if (bare) { + flagsInt |= GitRepositoryInit.bare.value; + } + + _repoPointer = bindings.init( + path, + flagsInt, + mode, + workdirPath, + description, + templatePath, + initialHead, + originUrl, + ); } /// Initializes a new instance of the [Repository] class by opening repository diff --git a/test/repository_init_test.dart b/test/repository_init_test.dart index ba283f1..7d050ba 100644 --- a/test/repository_init_test.dart +++ b/test/repository_init_test.dart @@ -20,16 +20,36 @@ void main() { }); group('Repository.init', () { test('successfully creates new bare repo at provided path', () { - repo = Repository.init(initDir.path, isBare: true); + repo = Repository.init(path: initDir.path, bare: true); + expect(repo.path, '${initDir.path}/'); expect(repo.isBare, true); }); test('successfully creates new standard repo at provided path', () { - repo = Repository.init(initDir.path); + repo = Repository.init(path: initDir.path); + expect(repo.path, '${initDir.path}/.git/'); expect(repo.isBare, false); expect(repo.isEmpty, true); }); + + test('successfully creates new standard repo with provided options', () { + repo = Repository.init( + path: initDir.path, + description: 'test repo', + originUrl: 'test.url', + flags: {GitRepositoryInit.mkdir, GitRepositoryInit.mkpath}, + ); + + expect(repo.path, '${initDir.path}/.git/'); + expect(repo.isBare, false); + expect(repo.isEmpty, true); + expect( + File('${initDir.path}/.git/description').readAsStringSync(), + 'test repo', + ); + expect(repo.remotes['origin'].url, 'test.url'); + }); }); }