Skip to content

Commit a22f19e

Browse files
committed
worktree: add ability to create worktree with pre-existing branch
Currently, we always create a new branch after the new worktree's name when creating a worktree. In some workflows, though, the caller may want to check out an already existing reference instead of creating a new one, which is impossible to do right now. Add a new option `ref` to the options structure for adding worktrees. In case it is set, a branch and not already checked out by another worktree, we will re-use this reference instead of creating a new one.
1 parent f722594 commit a22f19e

File tree

3 files changed

+56
-8
lines changed

3 files changed

+56
-8
lines changed

include/git2/worktree.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,11 @@ typedef struct git_worktree_add_options {
7878
unsigned int version;
7979

8080
int lock; /**< lock newly created worktree */
81+
git_reference *ref; /**< reference to use for the new worktree HEAD */
8182
} git_worktree_add_options;
8283

8384
#define GIT_WORKTREE_ADD_OPTIONS_VERSION 1
84-
#define GIT_WORKTREE_ADD_OPTIONS_INIT {GIT_WORKTREE_ADD_OPTIONS_VERSION,0}
85+
#define GIT_WORKTREE_ADD_OPTIONS_INIT {GIT_WORKTREE_ADD_OPTIONS_VERSION,0,NULL}
8586

8687
/**
8788
* Initializes a `git_worktree_add_options` with default vaules.

src/worktree.c

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -348,13 +348,30 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
348348
|| (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0)
349349
goto out;
350350

351-
/* Create new branch */
352-
if ((err = git_repository_head(&head, repo)) < 0)
353-
goto out;
354-
if ((err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0)
355-
goto out;
356-
if ((err = git_branch_create(&ref, repo, name, commit, false)) < 0)
357-
goto out;
351+
/* Set up worktree reference */
352+
if (wtopts.ref) {
353+
if (!git_reference_is_branch(wtopts.ref)) {
354+
giterr_set(GITERR_WORKTREE, "reference is not a branch");
355+
err = -1;
356+
goto out;
357+
}
358+
359+
if (git_branch_is_checked_out(wtopts.ref)) {
360+
giterr_set(GITERR_WORKTREE, "reference is already checked out");
361+
err = -1;
362+
goto out;
363+
}
364+
365+
if ((err = git_reference_dup(&ref, wtopts.ref)) < 0)
366+
goto out;
367+
} else {
368+
if ((err = git_repository_head(&head, repo)) < 0)
369+
goto out;
370+
if ((err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0)
371+
goto out;
372+
if ((err = git_branch_create(&ref, repo, name, commit, false)) < 0)
373+
goto out;
374+
}
358375

359376
/* Set worktree's HEAD */
360377
if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0)

tests/worktree/worktree.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,36 @@ void test_worktree_worktree__init_existing_branch(void)
273273
git_reference_free(branch);
274274
}
275275

276+
void test_worktree_worktree__add_with_explicit_branch(void)
277+
{
278+
git_reference *head, *branch, *wthead;
279+
git_commit *commit;
280+
git_worktree *wt;
281+
git_repository *wtrepo;
282+
git_buf path = GIT_BUF_INIT;
283+
git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
284+
285+
cl_git_pass(git_repository_head(&head, fixture.repo));
286+
cl_git_pass(git_commit_lookup(&commit, fixture.repo, &head->target.oid));
287+
cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-with-ref", commit, false));
288+
289+
opts.ref = branch;
290+
291+
cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-with-different-name"));
292+
cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-with-different-name", path.ptr, &opts));
293+
cl_git_pass(git_repository_open_from_worktree(&wtrepo, wt));
294+
cl_git_pass(git_repository_head(&wthead, wtrepo));
295+
cl_assert_equal_s(git_reference_name(wthead), "refs/heads/worktree-with-ref");
296+
297+
git_buf_free(&path);
298+
git_commit_free(commit);
299+
git_reference_free(head);
300+
git_reference_free(branch);
301+
git_reference_free(wthead);
302+
git_repository_free(wtrepo);
303+
}
304+
305+
276306
void test_worktree_worktree__init_existing_worktree(void)
277307
{
278308
git_worktree *wt;

0 commit comments

Comments
 (0)