feat(worktree): add ability to create worktree from provided reference

This commit is contained in:
Aleksey Kulikov 2021-09-07 16:47:43 +03:00
parent a00078ba76
commit db21f2e890
6 changed files with 80 additions and 5 deletions

View file

@ -158,6 +158,22 @@ bool isHead(Pointer<git_reference> branch) {
} }
} }
/// Determine if any HEAD points to the current branch.
///
/// This will iterate over all known linked repositories (usually in the form of worktrees)
/// and report whether any HEAD is pointing at the current branch.
///
/// Throws a [LibGit2Error] if error occured.
bool isCheckedOut(Pointer<git_reference> branch) {
final result = libgit2.git_branch_is_checked_out(branch);
if (result < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return result == 1 ? true : false;
}
}
/// Get the branch name. /// Get the branch name.
/// ///
/// Given a reference object, this will check that it really is a branch /// Given a reference object, this will check that it really is a branch

View file

@ -15,14 +15,23 @@ Pointer<git_worktree> create(
Pointer<git_repository> repo, Pointer<git_repository> repo,
String name, String name,
String path, String path,
Pointer<git_reference>? ref,
) { ) {
final out = calloc<Pointer<git_worktree>>(); final out = calloc<Pointer<git_worktree>>();
final nameC = name.toNativeUtf8().cast<Int8>(); final nameC = name.toNativeUtf8().cast<Int8>();
final pathC = path.toNativeUtf8().cast<Int8>(); final pathC = path.toNativeUtf8().cast<Int8>();
final error = libgit2.git_worktree_add(out, repo, nameC, pathC, nullptr); final opts =
calloc<git_worktree_add_options>(sizeOf<git_worktree_add_options>());
opts.ref.version = 1;
opts.ref.lock = 0;
if (ref != null) {
opts.ref.ref = ref;
}
final error = libgit2.git_worktree_add(out, repo, nameC, pathC, opts);
calloc.free(nameC); calloc.free(nameC);
calloc.free(pathC); calloc.free(pathC);
calloc.free(opts);
if (error < 0) { if (error < 0) {
throw LibGit2Error(libgit2.git_error_last()); throw LibGit2Error(libgit2.git_error_last());

View file

@ -115,11 +115,19 @@ class Branch {
return Branch(bindings.rename(_branchPointer, newName, force)); return Branch(bindings.rename(_branchPointer, newName, force));
} }
/// Determines if HEAD points to the given branch. /// Checks if HEAD points to the given branch.
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
bool get isHead => bindings.isHead(_branchPointer); bool get isHead => bindings.isHead(_branchPointer);
/// Checks if any HEAD points to the current branch.
///
/// This will iterate over all known linked repositories (usually in the form of worktrees)
/// and report whether any HEAD is pointing at the current branch.
///
/// Throws a [LibGit2Error] if error occured.
bool get isCheckedOut => bindings.isCheckedOut(_branchPointer);
/// Returns the branch name. /// Returns the branch name.
/// ///
/// Given a reference object, this will check that it really is a branch /// Given a reference object, this will check that it really is a branch

View file

@ -134,12 +134,14 @@ class Reference {
); );
} }
/// Pointer to memory address for allocated reference object.
late Pointer<git_reference> _refPointer; late Pointer<git_reference> _refPointer;
/// Pointer to memory address for allocated repository object. /// Pointer to memory address for allocated repository object.
late final Pointer<git_repository> _repoPointer; late final Pointer<git_repository> _repoPointer;
/// Pointer to memory address for allocated reference object.
Pointer<git_reference> get pointer => _refPointer;
/// Returns the type of the reference. /// Returns the type of the reference.
ReferenceType get type { ReferenceType get type {
return bindings.referenceType(_refPointer) == 1 return bindings.referenceType(_refPointer) == 1

View file

@ -1,11 +1,16 @@
import 'dart:ffi'; import 'dart:ffi';
import 'bindings/libgit2_bindings.dart'; import 'bindings/libgit2_bindings.dart';
import 'bindings/worktree.dart' as bindings; import 'bindings/worktree.dart' as bindings;
import 'reference.dart';
import 'repository.dart'; import 'repository.dart';
class Worktree { class Worktree {
/// Initializes a new instance of [Worktree] class by creating new worktree /// Initializes a new instance of [Worktree] class by creating new worktree
/// with provided [Repository] object worktree [name] and [path]. /// with provided [Repository] object worktree [name], [path] and optional [ref]
/// [Reference] object.
///
/// If [ref] is provided, no new branch will be created but specified [ref] will
/// be used instead.
/// ///
/// Should be freed with `free()` to release allocated memory. /// Should be freed with `free()` to release allocated memory.
/// ///
@ -14,8 +19,9 @@ class Worktree {
required Repository repo, required Repository repo,
required String name, required String name,
required String path, required String path,
Reference? ref,
}) { }) {
_worktreePointer = bindings.create(repo.pointer, name, path); _worktreePointer = bindings.create(repo.pointer, name, path, ref?.pointer);
} }
/// Initializes a new instance of [Worktree] class by looking up existing worktree /// Initializes a new instance of [Worktree] class by looking up existing worktree

View file

@ -52,6 +52,40 @@ void main() {
worktree.free(); worktree.free();
}); });
test(
'successfully creates worktree at provided path from provided reference',
() {
const worktreeName = 'worktree';
final head = repo.revParseSingle('HEAD');
final worktreeRef = repo.branches.create(name: 'v1', target: head);
final worktreeBranch = repo.branches['v1'];
expect(Worktree.list(repo), []);
final worktree = Worktree.create(
repo: repo,
name: worktreeName,
path: worktreeDir,
ref: worktreeRef,
);
expect(Worktree.list(repo), [worktreeName]);
expect(repo.branches.list(), contains('v1'));
expect(repo.branches.list(), isNot(contains(worktreeName)));
expect(worktreeBranch.isCheckedOut, true);
Directory(worktreeDir).deleteSync(recursive: true);
worktree.prune();
expect(Worktree.list(repo), []);
expect(worktreeBranch.isCheckedOut, false);
expect(repo.branches.list(), contains('v1'));
worktreeBranch.free();
worktreeRef.free();
head.free();
worktree.free();
});
test('successfully prunes worktree', () { test('successfully prunes worktree', () {
const worktreeName = 'worktree'; const worktreeName = 'worktree';
expect(Worktree.list(repo), []); expect(Worktree.list(repo), []);