Merge branch 'worktree'

This commit is contained in:
Edward Thomson
2024-03-05 09:48:06 +00:00
3 changed files with 73 additions and 23 deletions

View File

@@ -85,8 +85,9 @@ GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt);
typedef struct git_worktree_add_options {
unsigned int version;
int lock; /**< lock newly created worktree */
git_reference *ref; /**< reference to use for the new worktree HEAD */
int lock; /**< lock newly created worktree */
int checkout_existing; /**< allow checkout of existing branch matching worktree name */
git_reference *ref; /**< reference to use for the new worktree HEAD */
/**
* Options for the checkout.
@@ -95,7 +96,8 @@ typedef struct git_worktree_add_options {
} git_worktree_add_options;
#define GIT_WORKTREE_ADD_OPTIONS_VERSION 1
#define GIT_WORKTREE_ADD_OPTIONS_INIT {GIT_WORKTREE_ADD_OPTIONS_VERSION,0,NULL,GIT_CHECKOUT_OPTIONS_INIT}
#define GIT_WORKTREE_ADD_OPTIONS_INIT { GIT_WORKTREE_ADD_OPTIONS_VERSION, \
0, 0, NULL, GIT_CHECKOUT_OPTIONS_INIT }
/**
* Initialize git_worktree_add_options structure

View File

@@ -335,11 +335,21 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
goto out;
}
if (git_branch_is_checked_out(wtopts.ref)) {
git_error_set(GIT_ERROR_WORKTREE, "reference is already checked out");
err = -1;
if ((err = git_reference_dup(&ref, wtopts.ref)) < 0)
goto out;
}
} else if (wtopts.checkout_existing && git_branch_lookup(&ref, repo, name, GIT_BRANCH_LOCAL) == 0) {
/* Do nothing */
} else if ((err = git_repository_head(&head, repo)) < 0 ||
(err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0 ||
(err = git_branch_create(&ref, repo, name, commit, false)) < 0) {
goto out;
}
if (git_branch_is_checked_out(ref)) {
git_error_set(GIT_ERROR_WORKTREE, "reference %s is already checked out",
git_reference_name(ref));
err = -1;
goto out;
}
/* Create gitdir directory ".git/worktrees/<name>" */
@@ -392,19 +402,6 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
|| (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0)
goto out;
/* Set up worktree reference */
if (wtopts.ref) {
if ((err = git_reference_dup(&ref, wtopts.ref)) < 0)
goto out;
} else {
if ((err = git_repository_head(&head, repo)) < 0)
goto out;
if ((err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0)
goto out;
if ((err = git_branch_create(&ref, repo, name, commit, false)) < 0)
goto out;
}
/* Set worktree's HEAD */
if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0)
goto out;

View File

@@ -217,6 +217,50 @@ void test_worktree_worktree__init(void)
git_repository_free(repo);
}
void test_worktree_worktree__add_remove_add(void)
{
git_worktree_add_options add_opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
git_str path = GIT_BUF_INIT;
git_reference *branch;
git_repository *repo;
git_worktree *wt;
/* Add the worktree */
cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-add-remove-add"));
cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, NULL));
/* Open and verify created repo */
cl_git_pass(git_repository_open(&repo, path.ptr));
cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-add-remove-add/") == 0);
cl_git_pass(git_branch_lookup(&branch, repo, "worktree-add-remove-add", GIT_BRANCH_LOCAL));
git_reference_free(branch);
git_repository_free(repo);
/* Prune the worktree */
opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_WORKING_TREE;
cl_git_pass(git_worktree_prune(wt, &opts));
cl_assert(!git_fs_path_exists(wt->gitdir_path));
cl_assert(!git_fs_path_exists(wt->gitlink_path));
git_worktree_free(wt);
/* Add the worktree back with default options should fail. */
cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, &add_opts));
/* If allowing checkout of existing branches, it should succeed. */
add_opts.checkout_existing = 1;
cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, &add_opts));
/* Open and verify created repo */
cl_git_pass(git_repository_open(&repo, path.ptr));
cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-add-remove-add/") == 0);
cl_git_pass(git_branch_lookup(&branch, repo, "worktree-add-remove-add", GIT_BRANCH_LOCAL));
git_reference_free(branch);
git_repository_free(repo);
git_str_dispose(&path);
git_worktree_free(wt);
}
void test_worktree_worktree__add_locked(void)
{
git_worktree *wt;
@@ -244,6 +288,7 @@ void test_worktree_worktree__add_locked(void)
void test_worktree_worktree__init_existing_branch(void)
{
git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
git_reference *head, *branch;
git_commit *commit;
git_worktree *wt;
@@ -251,12 +296,18 @@ void test_worktree_worktree__init_existing_branch(void)
cl_git_pass(git_repository_head(&head, fixture.repo));
cl_git_pass(git_commit_lookup(&commit, fixture.repo, &head->target.oid));
cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new", commit, false));
cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new-exist", commit, false));
cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL));
cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-new-exist"));
/* Add the worktree back with default options should fail. */
cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new-exist", path.ptr, NULL));
/* If allowing checkout of existing branches, it should succeed. */
opts.checkout_existing = 1;
cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-new-exist", path.ptr, &opts));
git_str_dispose(&path);
git_worktree_free(wt);
git_commit_free(commit);
git_reference_free(head);
git_reference_free(branch);