From db21f2e8903f0c762cf2950a9e150a5024d1b511 Mon Sep 17 00:00:00 2001 From: Aleksey Kulikov Date: Tue, 7 Sep 2021 16:47:43 +0300 Subject: [PATCH] feat(worktree): add ability to create worktree from provided reference --- lib/src/bindings/branch.dart | 16 ++++++++++++++++ lib/src/bindings/worktree.dart | 11 ++++++++++- lib/src/branch.dart | 10 +++++++++- lib/src/reference.dart | 4 +++- lib/src/worktree.dart | 10 ++++++++-- test/worktree_test.dart | 34 ++++++++++++++++++++++++++++++++++ 6 files changed, 80 insertions(+), 5 deletions(-) diff --git a/lib/src/bindings/branch.dart b/lib/src/bindings/branch.dart index fd1b5e0..13da365 100644 --- a/lib/src/bindings/branch.dart +++ b/lib/src/bindings/branch.dart @@ -158,6 +158,22 @@ bool isHead(Pointer 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 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. /// /// Given a reference object, this will check that it really is a branch diff --git a/lib/src/bindings/worktree.dart b/lib/src/bindings/worktree.dart index a610848..2ae130f 100644 --- a/lib/src/bindings/worktree.dart +++ b/lib/src/bindings/worktree.dart @@ -15,14 +15,23 @@ Pointer create( Pointer repo, String name, String path, + Pointer? ref, ) { final out = calloc>(); final nameC = name.toNativeUtf8().cast(); final pathC = path.toNativeUtf8().cast(); - final error = libgit2.git_worktree_add(out, repo, nameC, pathC, nullptr); + final opts = + calloc(sizeOf()); + 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(pathC); + calloc.free(opts); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); diff --git a/lib/src/branch.dart b/lib/src/branch.dart index 99f4571..1b9199f 100644 --- a/lib/src/branch.dart +++ b/lib/src/branch.dart @@ -115,11 +115,19 @@ class Branch { 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. 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. /// /// Given a reference object, this will check that it really is a branch diff --git a/lib/src/reference.dart b/lib/src/reference.dart index 02a8104..e7646cb 100644 --- a/lib/src/reference.dart +++ b/lib/src/reference.dart @@ -134,12 +134,14 @@ class Reference { ); } - /// Pointer to memory address for allocated reference object. late Pointer _refPointer; /// Pointer to memory address for allocated repository object. late final Pointer _repoPointer; + /// Pointer to memory address for allocated reference object. + Pointer get pointer => _refPointer; + /// Returns the type of the reference. ReferenceType get type { return bindings.referenceType(_refPointer) == 1 diff --git a/lib/src/worktree.dart b/lib/src/worktree.dart index e15a0b5..4d1ebab 100644 --- a/lib/src/worktree.dart +++ b/lib/src/worktree.dart @@ -1,11 +1,16 @@ import 'dart:ffi'; import 'bindings/libgit2_bindings.dart'; import 'bindings/worktree.dart' as bindings; +import 'reference.dart'; import 'repository.dart'; class 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. /// @@ -14,8 +19,9 @@ class Worktree { required Repository repo, required String name, 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 diff --git a/test/worktree_test.dart b/test/worktree_test.dart index c31b914..00d476d 100644 --- a/test/worktree_test.dart +++ b/test/worktree_test.dart @@ -52,6 +52,40 @@ void main() { 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', () { const worktreeName = 'worktree'; expect(Worktree.list(repo), []);