mirror of
https://github.com/SkinnyMind/libgit2dart.git
synced 2025-05-05 04:39:07 -04:00
feat(repository): add ability to pass callbacks for remote and repository creation during clone
This commit is contained in:
parent
b1f112a30d
commit
6d48ae742c
4 changed files with 161 additions and 6 deletions
|
@ -1,6 +1,8 @@
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
import '../error.dart';
|
import '../error.dart';
|
||||||
|
import '../remote.dart';
|
||||||
|
import '../repository.dart';
|
||||||
import 'libgit2_bindings.dart';
|
import 'libgit2_bindings.dart';
|
||||||
import '../util.dart';
|
import '../util.dart';
|
||||||
|
|
||||||
|
@ -135,6 +137,8 @@ Pointer<git_repository> clone(
|
||||||
String url,
|
String url,
|
||||||
String localPath,
|
String localPath,
|
||||||
bool bare,
|
bool bare,
|
||||||
|
Remote Function(Repository, String, String)? remote,
|
||||||
|
Repository Function(String, bool)? repository,
|
||||||
String checkoutBranch,
|
String checkoutBranch,
|
||||||
) {
|
) {
|
||||||
final out = calloc<Pointer<git_repository>>();
|
final out = calloc<Pointer<git_repository>>();
|
||||||
|
@ -143,6 +147,7 @@ Pointer<git_repository> clone(
|
||||||
final checkoutBranchC = checkoutBranch.isEmpty
|
final checkoutBranchC = checkoutBranch.isEmpty
|
||||||
? nullptr
|
? nullptr
|
||||||
: checkoutBranch.toNativeUtf8().cast<Int8>();
|
: checkoutBranch.toNativeUtf8().cast<Int8>();
|
||||||
|
|
||||||
final cloneOptions = calloc<git_clone_options>();
|
final cloneOptions = calloc<git_clone_options>();
|
||||||
final cloneOptionsError =
|
final cloneOptionsError =
|
||||||
libgit2.git_clone_options_init(cloneOptions, GIT_CLONE_OPTIONS_VERSION);
|
libgit2.git_clone_options_init(cloneOptions, GIT_CLONE_OPTIONS_VERSION);
|
||||||
|
@ -159,8 +164,24 @@ Pointer<git_repository> clone(
|
||||||
throw LibGit2Error(libgit2.git_error_last());
|
throw LibGit2Error(libgit2.git_error_last());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const except = -1;
|
||||||
|
|
||||||
|
git_remote_create_cb remoteCb = nullptr;
|
||||||
|
if (remote != null) {
|
||||||
|
_remoteFunction = remote;
|
||||||
|
remoteCb = Pointer.fromFunction(_remoteCb, except);
|
||||||
|
}
|
||||||
|
|
||||||
|
git_repository_create_cb repositoryCb = nullptr;
|
||||||
|
if (repository != null) {
|
||||||
|
_repositoryFunction = repository;
|
||||||
|
repositoryCb = Pointer.fromFunction(_repositoryCb, except);
|
||||||
|
}
|
||||||
|
|
||||||
cloneOptions.ref.bare = bare ? 1 : 0;
|
cloneOptions.ref.bare = bare ? 1 : 0;
|
||||||
|
cloneOptions.ref.remote_cb = remoteCb;
|
||||||
cloneOptions.ref.checkout_branch = checkoutBranchC;
|
cloneOptions.ref.checkout_branch = checkoutBranchC;
|
||||||
|
cloneOptions.ref.repository_cb = repositoryCb;
|
||||||
cloneOptions.ref.fetch_opts = fetchOptions.ref;
|
cloneOptions.ref.fetch_opts = fetchOptions.ref;
|
||||||
|
|
||||||
final error = libgit2.git_clone(out, urlC, localPathC, cloneOptions);
|
final error = libgit2.git_clone(out, urlC, localPathC, cloneOptions);
|
||||||
|
@ -170,6 +191,8 @@ Pointer<git_repository> clone(
|
||||||
calloc.free(checkoutBranchC);
|
calloc.free(checkoutBranchC);
|
||||||
calloc.free(cloneOptions);
|
calloc.free(cloneOptions);
|
||||||
calloc.free(fetchOptions);
|
calloc.free(fetchOptions);
|
||||||
|
_remoteFunction = null;
|
||||||
|
_repositoryFunction = null;
|
||||||
|
|
||||||
if (error < 0) {
|
if (error < 0) {
|
||||||
throw LibGit2Error(libgit2.git_error_last());
|
throw LibGit2Error(libgit2.git_error_last());
|
||||||
|
@ -178,6 +201,47 @@ Pointer<git_repository> clone(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A function matching the `Remote Function(Repository repo, String name, String url)` signature
|
||||||
|
/// to override the remote creation and customization process during a clone operation.
|
||||||
|
Remote Function(Repository, String, String)? _remoteFunction;
|
||||||
|
|
||||||
|
/// A callback used to create the git remote, prior to its being used to perform
|
||||||
|
/// the clone operation.
|
||||||
|
int _remoteCb(
|
||||||
|
Pointer<Pointer<git_remote>> remote,
|
||||||
|
Pointer<git_repository> repo,
|
||||||
|
Pointer<Int8> name,
|
||||||
|
Pointer<Int8> url,
|
||||||
|
Pointer<Void> payload,
|
||||||
|
) {
|
||||||
|
remote[0] = _remoteFunction!(
|
||||||
|
Repository(repo),
|
||||||
|
name.cast<Utf8>().toDartString(),
|
||||||
|
url.cast<Utf8>().toDartString(),
|
||||||
|
).pointer;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A function matching the `Repository Function(String path, bool bare)` signature to override
|
||||||
|
/// the repository creation and customization process during a clone operation.
|
||||||
|
Repository Function(String, bool)? _repositoryFunction;
|
||||||
|
|
||||||
|
/// A callback used to create the new repository into which to clone.
|
||||||
|
int _repositoryCb(
|
||||||
|
Pointer<Pointer<git_repository>> repo,
|
||||||
|
Pointer<Int8> path,
|
||||||
|
int bare,
|
||||||
|
Pointer<Void> payload,
|
||||||
|
) {
|
||||||
|
repo[0] = _repositoryFunction!(
|
||||||
|
path.cast<Utf8>().toDartString(),
|
||||||
|
bare == 1 ? true : false,
|
||||||
|
).pointer;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the path to the `.git` folder for normal repositories or the
|
/// Returns the path to the `.git` folder for normal repositories or the
|
||||||
/// repository itself for bare repositories.
|
/// repository itself for bare repositories.
|
||||||
String path(Pointer<git_repository> repo) {
|
String path(Pointer<git_repository> repo) {
|
||||||
|
|
|
@ -119,9 +119,11 @@ class Remote {
|
||||||
/// to remote object in memory.
|
/// to remote object in memory.
|
||||||
const Remote(this._remotePointer);
|
const Remote(this._remotePointer);
|
||||||
|
|
||||||
/// Pointer to memory address for allocated remote object.
|
|
||||||
final Pointer<git_remote> _remotePointer;
|
final Pointer<git_remote> _remotePointer;
|
||||||
|
|
||||||
|
/// Pointer to memory address for allocated remote object.
|
||||||
|
Pointer<git_remote> get pointer => _remotePointer;
|
||||||
|
|
||||||
/// Returns the remote's name.
|
/// Returns the remote's name.
|
||||||
String get name => bindings.name(_remotePointer);
|
String get name => bindings.name(_remotePointer);
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,12 @@ class Repository {
|
||||||
/// Initializes a new instance of the [Repository] class by cloning a remote repository
|
/// Initializes a new instance of the [Repository] class by cloning a remote repository
|
||||||
/// at provided [url] into [localPath].
|
/// at provided [url] into [localPath].
|
||||||
///
|
///
|
||||||
|
/// [remote] is the callback function with `Remote Function(Repository repo, String name, String url)`
|
||||||
|
/// signature. The [Remote] it returns will be used instead of default one.
|
||||||
|
///
|
||||||
|
/// [repository] is the callback function matching the `Repository Function(String path, bool bare)`
|
||||||
|
/// signature. The [Repository] it returns will be used instead of creating a new one.
|
||||||
|
///
|
||||||
/// [checkoutBranch] is the name of the branch to checkout after the clone. Defaults
|
/// [checkoutBranch] is the name of the branch to checkout after the clone. Defaults
|
||||||
/// to using the remote's default branch.
|
/// to using the remote's default branch.
|
||||||
///
|
///
|
||||||
|
@ -98,11 +104,20 @@ class Repository {
|
||||||
required String url,
|
required String url,
|
||||||
required String localPath,
|
required String localPath,
|
||||||
bool bare = false,
|
bool bare = false,
|
||||||
|
Remote Function(Repository, String, String)? remote,
|
||||||
|
Repository Function(String, bool)? repository,
|
||||||
String checkoutBranch = '',
|
String checkoutBranch = '',
|
||||||
}) {
|
}) {
|
||||||
libgit2.git_libgit2_init();
|
libgit2.git_libgit2_init();
|
||||||
|
|
||||||
_repoPointer = bindings.clone(url, localPath, bare, checkoutBranch);
|
_repoPointer = bindings.clone(
|
||||||
|
url,
|
||||||
|
localPath,
|
||||||
|
bare,
|
||||||
|
remote,
|
||||||
|
repository,
|
||||||
|
checkoutBranch,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
late final Pointer<git_repository> _repoPointer;
|
late final Pointer<git_repository> _repoPointer;
|
||||||
|
|
|
@ -20,11 +20,13 @@ void main() {
|
||||||
tearDown(() async {
|
tearDown(() async {
|
||||||
repo.free();
|
repo.free();
|
||||||
await tmpDir.delete(recursive: true);
|
await tmpDir.delete(recursive: true);
|
||||||
cloneDir.delete(recursive: true);
|
if (await cloneDir.exists()) {
|
||||||
|
cloneDir.delete(recursive: true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
group('Repository.clone', () {
|
group('Repository.clone', () {
|
||||||
test('successfully clones repository', () async {
|
test('successfully clones repository', () {
|
||||||
final clonedRepo = Repository.clone(
|
final clonedRepo = Repository.clone(
|
||||||
url: tmpDir.path,
|
url: tmpDir.path,
|
||||||
localPath: cloneDir.path,
|
localPath: cloneDir.path,
|
||||||
|
@ -36,7 +38,7 @@ void main() {
|
||||||
clonedRepo.free();
|
clonedRepo.free();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('successfully clones repository as bare', () async {
|
test('successfully clones repository as bare', () {
|
||||||
final clonedRepo = Repository.clone(
|
final clonedRepo = Repository.clone(
|
||||||
url: tmpDir.path,
|
url: tmpDir.path,
|
||||||
localPath: cloneDir.path,
|
localPath: cloneDir.path,
|
||||||
|
@ -50,7 +52,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('successfully clones repository with provided checkout branch name',
|
test('successfully clones repository with provided checkout branch name',
|
||||||
() async {
|
() {
|
||||||
final clonedRepo = Repository.clone(
|
final clonedRepo = Repository.clone(
|
||||||
url: tmpDir.path,
|
url: tmpDir.path,
|
||||||
localPath: cloneDir.path,
|
localPath: cloneDir.path,
|
||||||
|
@ -64,5 +66,77 @@ void main() {
|
||||||
|
|
||||||
clonedRepo.free();
|
clonedRepo.free();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('successfully clones repository with provided remote callback', () {
|
||||||
|
Remote remote(Repository repo, String name, String url) =>
|
||||||
|
repo.remotes.create(name: 'test', url: tmpDir.path);
|
||||||
|
|
||||||
|
final clonedRepo = Repository.clone(
|
||||||
|
url: tmpDir.path,
|
||||||
|
localPath: cloneDir.path,
|
||||||
|
remote: remote,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(clonedRepo.isEmpty, false);
|
||||||
|
expect(clonedRepo.isBare, false);
|
||||||
|
expect(clonedRepo.remotes.list, ['test']);
|
||||||
|
expect(clonedRepo.references.list, contains('refs/remotes/test/master'));
|
||||||
|
|
||||||
|
clonedRepo.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when cloning repository with invalid remote callback', () {
|
||||||
|
Remote remote(Repository repo, String name, String url) =>
|
||||||
|
repo.remotes.create(name: '', url: '');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
() => Repository.clone(
|
||||||
|
url: tmpDir.path,
|
||||||
|
localPath: cloneDir.path,
|
||||||
|
remote: remote,
|
||||||
|
),
|
||||||
|
throwsA(isA<LibGit2Error>()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('successfully clones repository with provided repository callback',
|
||||||
|
() async {
|
||||||
|
final callbackPath =
|
||||||
|
Directory('${Directory.systemTemp.path}/callbackRepo');
|
||||||
|
if (await callbackPath.exists()) {
|
||||||
|
callbackPath.delete(recursive: true);
|
||||||
|
}
|
||||||
|
callbackPath.create();
|
||||||
|
|
||||||
|
Repository repository(String path, bool bare) =>
|
||||||
|
Repository.init(path: callbackPath.path);
|
||||||
|
|
||||||
|
final clonedRepo = Repository.clone(
|
||||||
|
url: tmpDir.path,
|
||||||
|
localPath: cloneDir.path,
|
||||||
|
repository: repository,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(clonedRepo.isEmpty, false);
|
||||||
|
expect(clonedRepo.isBare, false);
|
||||||
|
expect(clonedRepo.path, '${callbackPath.path}/.git/');
|
||||||
|
|
||||||
|
clonedRepo.free();
|
||||||
|
callbackPath.delete(recursive: true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when cloning repository with invalid repository callback', () {
|
||||||
|
Repository repository(String path, bool bare) =>
|
||||||
|
Repository.init(path: '');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
() => Repository.clone(
|
||||||
|
url: tmpDir.path,
|
||||||
|
localPath: cloneDir.path,
|
||||||
|
repository: repository,
|
||||||
|
),
|
||||||
|
throwsA(isA<LibGit2Error>()),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue