8000 worktree: add flag to allow re-using existing branches for new worktrees · libgit2/libgit2@a381078 · GitHub
[go: up one dir, main page]

Skip to content

Commit a381078

Browse files
committed
worktree: add flag to allow re-using existing branches for new worktrees
In this commit's parent, we have introduced logic that will automatically re-use of existing branches if the new worktree name matches the branch name. While this is a handy feature, it changes behaviour in a backwards-incompatible way and might thus surprise users. Furthermore, it's impossible to tell whether we have created the worktree with a new or an existing reference. To fix this, introduce a new option `checkout_existing` that toggles this behaviour. Only if the flag is set will we now allow re-use of existing branches, while it's set to "off" by default.
1 parent 751af7a commit a381078

File tree

3 files changed

+41
-47
lines changed

3 files changed

+41
-47
lines changed

include/git2/worktree.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,13 @@ GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt);
8484
typedef struct git_worktree_add_options {
8585
unsigned int version;
8686

87-
int lock; /**< lock newly created worktree */
88-
git_reference *ref; /**< reference to use for the new worktree HEAD */
87+
int lock; /**< lock newly created worktree */
88+
int checkout_existing; /**< allow checkout of existing branch matching worktree name */
89+
git_reference *ref; /**< reference to use for the new worktree HEAD */
8990
} git_worktree_add_options;
9091

91-
#define GIT_WORKTREE_ADD_OPTIONS_VERSION 1
92-
#define GIT_WORKTREE_ADD_OPTIONS_INIT {GIT_WORKTREE_ADD_OPTIONS_VERSION,0,NULL}
92+
#define GIT_WORKTREE_ADD_OPTIONS_VERSION 2
93+
#define GIT_WORKTREE_ADD_OPTIONS_INIT {GIT_WORKTREE_ADD_OPTIONS_VERSION,0,0,NULL}
9394

9495
/**
9596
* Initialize git_worktree_add_options structure

src/worktree.c

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,7 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
278278
const char *name, const char *worktree,
279279
const git_worktree_add_options *opts)
280280
{
281-
git_buf gitdir = GIT_BUF_INIT, wddir = GIT_BUF_INIT;
282-
git_buf buf = GIT_BUF_INIT, ref_buf = GIT_BUF_INIT;
281+
git_buf gitdir = GIT_BUF_INIT, wddir = GIT_BUF_INIT, buf = GIT_BUF_INIT;
283282
git_reference *ref = NULL, *head = NULL;
284283
git_commit *commit = NULL;
285284
git_repository *wt = NULL;
@@ -304,11 +303,21 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
304303
goto out;
305304
}
306305

307-
if (git_branch_is_checked_out(wtopts.ref)) {
308-
git_error_set(GIT_ERROR_WORKTREE, "reference is already checked out");
309-
err = -1;
306+
if ((err = git_reference_dup(&ref, wtopts.ref)) < 0)
310307
goto out;
311-
}
308+
} else if (wtopts.checkout_existing && git_branch_lookup(&ref, repo, name, GIT_BRANCH_LOCAL) == 0) {
309+
/* Do nothing */
310+
} else if ((err = git_repository_head(&head, repo)) < 0 ||
311+
(err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0 ||
312+
(err = git_branch_create(&ref, repo, name, commit, false)) < 0) {
313+
goto out;
314+
}
315+
316+
if (git_branch_is_checked_out(ref)) {
317+
git_error_set(GIT_ERROR_WORKTREE, "reference %s is already checked out",
318+
git_reference_name(ref));
319+
err = -1;
320+
goto out;
312321
}
313322

314323
/* Create gitdir directory ".git/worktrees/<name>" */
@@ -361,31 +370,6 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
361370
|| (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0)
362371
goto out;
363372

364-
/* Set up worktree reference */
365-
if (wtopts.ref) {
366-
if ((err = git_reference_dup(&ref, wtopts.ref)) < 0)
367-
goto out;
368-
} else {
369-
if ((err = git_buf_printf(&ref_buf, "refs/heads/%s", name)) < 0)
370-
goto out;
371-
if (!git_reference_lookup(&ref, repo, ref_buf.ptr)) {
372-
if (git_branch_is_checked_out(ref)) {
373-
git_error_set(GIT_ERROR_WORKTREE,
374-
"reference %s is already checked out",
375-
ref_buf.ptr);
376-
err = -1;
377-
goto out;
378-
}
379-
} else {
380-
if ((err = git_repository_head(&head, repo)) < 0)
381-
goto out;
382-
if ((err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0)
383-
goto out;
384-
if ((err = git_branch_create(&ref, repo, name, commit, false)) < 0)
385-
goto out;
386-
}
387-
}
388-
389373
/* Set worktree's HEAD */
390374
if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0)
391375
goto out;
@@ -405,7 +389,6 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
405389
git_buf_dispose(&gitdir);
406390
git_buf_dispose(&wddir);
407391
git_buf_dispose(&buf);
408-
git_buf_dispose(&ref_buf);
409392
git_reference_free(ref);
410393
git_reference_free(head);
411394
git_commit_free(commit);

tests/worktree/worktree.c

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,12 @@ void test_worktree_worktree__init(void)
218218

219219
void test_worktree_worktree__add_remove_add(void)
220220
{
221-
git_worktree *wt;
222-
git_repository *repo;
223-
git_reference *branch;
224-
git_buf path = GIT_BUF_INIT;
225-
221+
git_worktree_add_options add_opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
226222
git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
223+
git_buf path = GIT_BUF_INIT;
224+
git_reference *branch;
225+
git_repository *repo;
226+
git_worktree *wt;
227227

228228
/* Add the worktree */
229229
cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-add-remove-add"));
@@ -233,6 +233,7 @@ void test_worktree_worktree__add_remove_add(void)
233233
cl_git_pass(git_repository_open(&repo, path.ptr));
234234
cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-add-remove-add/") == 0);
235235
cl_git_pass(git_branch_lookup(&branch, repo, "worktree-add-remove-add", GIT_BRANCH_LOCAL));
236+
git_reference_free(branch);
236237
git_repository_free(repo);
237238

238239
/* Prune the worktree */
@@ -242,18 +243,21 @@ void test_worktree_worktree__add_remove_add(void)
242243
cl_assert(!git_path_exists(wt->gitlink_path));
243244
git_worktree_free(wt);
244245

245-
/* Add the worktree back */
246-
cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, NULL));
246+
/* Add the worktree back with default options should fail. */
247+
cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, &add_opts));
248+
/* If allowing checkout of existing branches, it should succeed. */
249+
add_opts.checkout_existing = 1;
250+
cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, &add_opts));
247251

248252
/* Open and verify created repo */
249253
cl_git_pass(git_repository_open(&repo, path.ptr));
250254
cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-add-remove-add/") == 0);
251255
cl_git_pass(git_branch_lookup(&branch, repo, "worktree-add-remove-add", GIT_BRANCH_LOCAL));
256+
git_reference_free(branch);
257+
git_repository_free(repo);
252258

253259
git_buf_dispose(&path);
254260
git_worktree_free(wt);
255-
git_reference_free(branch);
256-
git_repository_free(repo);
257261
}
258262

259263
void test_worktree_worktree__add_locked(void)
@@ -283,6 +287,7 @@ void test_worktree_worktree__add_locked(void)
283287

284288
void test_worktree_worktree__init_existing_branch(void)
285289
{
290+
git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
286291
git_reference *head, *branch;
287292
git_commit *commit;
288293
git_worktree *wt;
@@ -293,7 +298,12 @@ void test_worktree_worktree__init_existing_branch(void)
293298
cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new-exist", commit, false));
294299

295300
cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new-exist"));
296-
cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-new-exist", path.ptr, NULL));
301+
302+
/* Add the worktree back with default options should fail. */
303+
cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new-exist", path.ptr, NULL));
304+
/* If allowing checkout of existing branches, it should succeed. */
305+
opts.checkout_existing = 1;
306+
cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-new-exist", path.ptr, &opts));
297307

298308
git_buf_dispose(&path);
299309
git_worktree_free(wt);
@@ -421,7 +431,7 @@ void test_worktree_worktree__name(void)
421431

422432
cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
423433
cl_assert_equal_s(git_worktree_name(wt), "testrepo-worktree");
424-
434+
425435
git_worktree_free(wt);
426436
}
427437

0 commit comments

Comments
 (0)
0