Commit 3017ba94a33a5dae07521afd96a94e21afb07b8c

Patrick Steinhardt 2017-03-15T14:24:25

worktree: implement `git_worktree_open_from_repository` While we already provide functionality to look up a worktree from a repository, we cannot do so the other way round. That is given a repository, we want to look up its worktree if it actually exists. Getting the worktree of a repository is useful when we want to get certain meta information like the parent's location, getting the locked status, etc.

diff --git a/include/git2/worktree.h b/include/git2/worktree.h
index cad1284..4c4f928 100644
--- a/include/git2/worktree.h
+++ b/include/git2/worktree.h
@@ -44,6 +44,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
  *
  * @param wt worktree handle to close. If NULL nothing occurs.
diff --git a/src/worktree.c b/src/worktree.c
index ad1a6e5..783b183 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 f5e0bc1..74b9007 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));
+}