diff --git a/include/git2/worktree.h b/include/git2/worktree.h index cad1284fa..4c4f9284d 100644 --- a/include/git2/worktree.h +++ b/include/git2/worktree.h @@ -43,6 +43,18 @@ GIT_EXTERN(int) git_worktree_list(git_strarray *out, git_repository *repo); */ GIT_EXTERN(int) git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name); +/** + * Open a worktree of a given repository + * + * If a repository is not the main tree but a worktree, this + * function will look up the worktree inside the parent + * repository and create a new `git_worktree` structure. + * + * @param out Out-pointer for the newly allocated worktree + * @param repo Repository to look up worktree for + */ +GIT_EXTERN(int) git_worktree_open_from_repository(git_worktree **out, git_repository *repo); + /** * Free a previously allocated worktree * diff --git a/src/worktree.c b/src/worktree.c index ad1a6e597..783b183b7 100644 --- a/src/worktree.c +++ b/src/worktree.c @@ -186,6 +186,39 @@ out: return error; } +int git_worktree_open_from_repository(git_worktree **out, git_repository *repo) +{ + git_buf parent = GIT_BUF_INIT; + const char *gitdir, *commondir; + char *name = NULL; + int error = 0; + + if (!git_repository_is_worktree(repo)) { + giterr_set(GITERR_WORKTREE, "cannot open worktree of a non-worktree repo"); + error = -1; + goto out; + } + + gitdir = git_repository_path(repo); + commondir = git_repository_commondir(repo); + + if ((error = git_path_prettify_dir(&parent, commondir, NULL)) < 0) + goto out; + + /* The name is defined by the last component in '.git/worktree/%s' */ + name = git_path_basename(gitdir); + + if ((error = open_worktree_dir(out, parent.ptr, gitdir, name)) < 0) + goto out; + +out: + if (error) + free(name); + git_buf_free(&parent); + + return error; +} + void git_worktree_free(git_worktree *wt) { if (!wt) diff --git a/tests/worktree/open.c b/tests/worktree/open.c index f5e0bc124..74b9007d9 100644 --- a/tests/worktree/open.c +++ b/tests/worktree/open.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "repository.h" +#include "worktree.h" #include "worktree_helpers.h" #define COMMON_REPO "testrepo" @@ -115,3 +116,28 @@ void test_worktree_open__repository_with_nonexistent_parent(void) cl_fixture_cleanup(WORKTREE_REPO); } + +void test_worktree_open__open_from_repository(void) +{ + git_worktree *opened, *lookedup; + + cl_git_pass(git_worktree_open_from_repository(&opened, fixture.worktree)); + cl_git_pass(git_worktree_lookup(&lookedup, fixture.repo, WORKTREE_REPO)); + + cl_assert_equal_s(opened->name, lookedup->name); + cl_assert_equal_s(opened->gitdir_path, lookedup->gitdir_path); + cl_assert_equal_s(opened->gitlink_path, lookedup->gitlink_path); + cl_assert_equal_s(opened->parent_path, lookedup->parent_path); + cl_assert_equal_s(opened->commondir_path, lookedup->commondir_path); + cl_assert_equal_i(opened->locked, lookedup->locked); + + git_worktree_free(opened); + git_worktree_free(lookedup); +} + +void test_worktree_open__open_from_nonworktree_fails(void) +{ + git_worktree *wt; + + cl_git_fail(git_worktree_open_from_repository(&wt, fixture.repo)); +}