mirror of
https://git.proxmox.com/git/libgit2
synced 2025-08-03 05:30:40 +00:00
worktree: implement git_worktree_add
Implement the `git_worktree_add` function which can be used to create new working trees for a given repository.
This commit is contained in:
parent
372dc9ff6a
commit
dea7488e93
@ -61,6 +61,21 @@ GIT_EXTERN(void) git_worktree_free(git_worktree *wt);
|
||||
*/
|
||||
GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt);
|
||||
|
||||
/**
|
||||
* Add a new working tree
|
||||
*
|
||||
* Add a new working tree for the repository, that is create the
|
||||
* required data structures inside the repository and check out
|
||||
* the current HEAD at `path`
|
||||
*
|
||||
* @param out Output pointer containing new working tree
|
||||
* @param repo Repository to create working tree for
|
||||
* @param name Name of the working tree
|
||||
* @param path Path to create working tree at
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_worktree_add(git_worktree **out, git_repository *repo, const char *name, const char *path);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
102
src/worktree.c
102
src/worktree.c
@ -5,9 +5,12 @@
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "git2/branch.h"
|
||||
#include "git2/commit.h"
|
||||
#include "git2/worktree.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "repository.h"
|
||||
#include "worktree.h"
|
||||
|
||||
@ -90,6 +93,25 @@ err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int write_wtfile(const char *base, const char *file, const git_buf *buf)
|
||||
{
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
int err;
|
||||
|
||||
assert(base && file && buf);
|
||||
|
||||
if ((err = git_buf_joinpath(&path, base, file)) < 0)
|
||||
goto out;
|
||||
|
||||
if ((err = git_futils_writebuffer(buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
git_buf_free(&path);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name)
|
||||
{
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
@ -183,3 +205,81 @@ out:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int git_worktree_add(git_worktree **out, git_repository *repo, const char *name, const char *worktree)
|
||||
{
|
||||
git_buf path = GIT_BUF_INIT, buf = GIT_BUF_INIT;
|
||||
git_reference *ref = NULL, *head = NULL;
|
||||
git_commit *commit = NULL;
|
||||
git_repository *wt = NULL;
|
||||
git_checkout_options coopts = GIT_CHECKOUT_OPTIONS_INIT;
|
||||
int err;
|
||||
|
||||
assert(out && repo && name && worktree);
|
||||
|
||||
*out = NULL;
|
||||
|
||||
/* Create worktree related files in commondir */
|
||||
if ((err = git_buf_joinpath(&path, repo->commondir, "worktrees")) < 0)
|
||||
goto out;
|
||||
if (!git_path_exists(path.ptr))
|
||||
if ((err = git_futils_mkdir(path.ptr, 0755, GIT_MKDIR_EXCL)) < 0)
|
||||
goto out;
|
||||
if ((err = git_buf_joinpath(&path, path.ptr, name)) < 0)
|
||||
goto out;
|
||||
if ((err = git_futils_mkdir(path.ptr, 0755, GIT_MKDIR_EXCL)) < 0)
|
||||
goto out;
|
||||
|
||||
/* Create worktree work dir */
|
||||
if ((err = git_futils_mkdir(worktree, 0755, GIT_MKDIR_EXCL)) < 0)
|
||||
goto out;
|
||||
|
||||
/* Create worktree .git file */
|
||||
if ((err = git_buf_printf(&buf, "gitdir: %s\n", path.ptr)) < 0)
|
||||
goto out;
|
||||
if ((err = write_wtfile(worktree, ".git", &buf)) < 0)
|
||||
goto out;
|
||||
|
||||
/* Create commondir files */
|
||||
if ((err = git_buf_sets(&buf, repo->commondir)) < 0
|
||||
|| (err = git_buf_putc(&buf, '\n')) < 0
|
||||
|| (err = write_wtfile(path.ptr, "commondir", &buf)) < 0)
|
||||
goto out;
|
||||
if ((err = git_buf_joinpath(&buf, worktree, ".git")) < 0
|
||||
|| (err = git_buf_putc(&buf, '\n')) < 0
|
||||
|| (err = write_wtfile(path.ptr, "gitdir", &buf)) < 0)
|
||||
goto out;
|
||||
|
||||
/* Create new branch */
|
||||
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(path.ptr, name)) < 0)
|
||||
goto out;
|
||||
if ((err = git_repository_open(&wt, worktree)) < 0)
|
||||
goto out;
|
||||
|
||||
/* Checkout worktree's HEAD */
|
||||
coopts.checkout_strategy = GIT_CHECKOUT_FORCE;
|
||||
if ((err = git_checkout_head(wt, &coopts)) < 0)
|
||||
goto out;
|
||||
|
||||
/* Load result */
|
||||
if ((err = git_worktree_lookup(out, repo, name)) < 0)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
git_buf_free(&path);
|
||||
git_buf_free(&buf);
|
||||
git_reference_free(ref);
|
||||
git_reference_free(head);
|
||||
git_commit_free(commit);
|
||||
git_repository_free(wt);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -204,6 +204,89 @@ void test_worktree_worktree__open_invalid_parent(void)
|
||||
git_worktree_free(wt);
|
||||
}
|
||||
|
||||
void test_worktree_worktree__init(void)
|
||||
{
|
||||
git_worktree *wt;
|
||||
git_repository *repo;
|
||||
git_reference *branch;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
|
||||
cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
|
||||
cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr));
|
||||
|
||||
/* Open and verify created repo */
|
||||
cl_git_pass(git_repository_open(&repo, path.ptr));
|
||||
cl_git_pass(git_branch_lookup(&branch, repo, "worktree-new", GIT_BRANCH_LOCAL));
|
||||
|
||||
git_buf_free(&path);
|
||||
git_worktree_free(wt);
|
||||
git_reference_free(branch);
|
||||
git_repository_free(repo);
|
||||
}
|
||||
|
||||
void test_worktree_worktree__init_existing_branch(void)
|
||||
{
|
||||
git_reference *head, *branch;
|
||||
git_commit *commit;
|
||||
git_worktree *wt;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
|
||||
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_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
|
||||
cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr));
|
||||
|
||||
git_buf_free(&path);
|
||||
git_commit_free(commit);
|
||||
git_reference_free(head);
|
||||
git_reference_free(branch);
|
||||
}
|
||||
|
||||
void test_worktree_worktree__init_existing_worktree(void)
|
||||
{
|
||||
git_worktree *wt;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
|
||||
cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
|
||||
cl_git_fail(git_worktree_add(&wt, fixture.repo, "testrepo-worktree", path.ptr));
|
||||
|
||||
cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
|
||||
cl_assert_equal_s(wt->gitlink_path, fixture.worktree->path_gitlink);
|
||||
|
||||
git_buf_free(&path);
|
||||
git_worktree_free(wt);
|
||||
}
|
||||
|
||||
void test_worktree_worktree__init_existing_path(void)
|
||||
{
|
||||
const char *wtfiles[] = { "HEAD", "commondir", "gitdir", "index" };
|
||||
git_worktree *wt;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
unsigned i;
|
||||
|
||||
/* Delete files to verify they have not been created by
|
||||
* the init call */
|
||||
for (i = 0; i < ARRAY_SIZE(wtfiles); i++) {
|
||||
cl_git_pass(git_buf_joinpath(&path,
|
||||
fixture.worktree->path_repository, wtfiles[i]));
|
||||
cl_git_pass(p_unlink(path.ptr));
|
||||
}
|
||||
|
||||
cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../testrepo-worktree"));
|
||||
cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr));
|
||||
|
||||
/* Verify files have not been re-created */
|
||||
for (i = 0; i < ARRAY_SIZE(wtfiles); i++) {
|
||||
cl_git_pass(git_buf_joinpath(&path,
|
||||
fixture.worktree->path_repository, wtfiles[i]));
|
||||
cl_assert(!git_path_exists(path.ptr));
|
||||
}
|
||||
|
||||
git_buf_free(&path);
|
||||
}
|
||||
|
||||
void test_worktree_worktree__validate(void)
|
||||
{
|
||||
git_worktree *wt;
|
||||
|
Loading…
Reference in New Issue
Block a user