feat(repository): add extended options to repository init

This commit is contained in:
Aleksey Kulikov 2021-09-24 13:18:04 +03:00
parent ec8ff24e89
commit b1f112a30d
4 changed files with 155 additions and 11 deletions

View file

@ -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<git_repository> init(String path, bool isBare) {
Pointer<git_repository> init(
String path,
int flags,
int mode,
String workdirPath,
String description,
String templatePath,
String initialHead,
String originUrl,
) {
final out = calloc<Pointer<git_repository>>();
final pathC = path.toNativeUtf8().cast<Int8>();
final isBareC = isBare ? 1 : 0;
final error = libgit2.git_repository_init(out, pathC, isBareC);
final workdirPathC =
workdirPath.isEmpty ? nullptr : workdirPath.toNativeUtf8().cast<Int8>();
final descriptionC =
description.isEmpty ? nullptr : description.toNativeUtf8().cast<Int8>();
final templatePathC =
templatePath.isEmpty ? nullptr : templatePath.toNativeUtf8().cast<Int8>();
final initialHeadC =
initialHead.isEmpty ? nullptr : initialHead.toNativeUtf8().cast<Int8>();
final originUrlC =
originUrl.isEmpty ? nullptr : originUrl.toNativeUtf8().cast<Int8>();
final opts = calloc<git_repository_init_options>();
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<git_repository> clone(
final out = calloc<Pointer<git_repository>>();
final urlC = url.toNativeUtf8().cast<Int8>();
final localPathC = localPath.toNativeUtf8().cast<Int8>();
final checkoutBranchC = checkoutBranch.isEmpty
? nullptr
: checkoutBranch.toNativeUtf8().cast<Int8>();
final cloneOptions = calloc<git_clone_options>();
final cloneOptionsError =
libgit2.git_clone_options_init(cloneOptions, GIT_CLONE_OPTIONS_VERSION);
@ -115,16 +160,16 @@ Pointer<git_repository> clone(
}
cloneOptions.ref.bare = bare ? 1 : 0;
cloneOptions.ref.checkout_branch = checkoutBranch.isEmpty
? nullptr
: checkoutBranch.toNativeUtf8().cast<Int8>();
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());

View file

@ -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<GitRepositoryInit> values = [
bare,
noReinit,
noDotGitDir,
mkdir,
mkpath,
externalTemplate,
relativeGitlink,
];
int get value => _value;
@override
String toString() => 'GitRepositoryInit.$_name';
}

View file

@ -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<GitRepositoryInit> 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

View file

@ -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');
});
});
}