Commit 8c8d726ef784b3f47ed3cd9427202a74563f626e

Patrick Steinhardt 2015-10-21T12:10:30

worktree: implement `git_repository_open_from_worktree` Add function `git_repository_open_from_worktree`, which allows to open a `git_worktree` as repository.

diff --git a/include/git2/repository.h b/include/git2/repository.h
index 8cf7e8e..29eb2da 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -35,6 +35,17 @@ GIT_BEGIN_DECL
  * @return 0 or an error code
  */
 GIT_EXTERN(int) git_repository_open(git_repository **out, const char *path);
+/**
+ * Open working tree as a repository
+ *
+ * Open the working directory of the working tree as a normal
+ * repository that can then be worked on.
+ *
+ * @param out Output pointer containing opened repository
+ * @param wt Working tree to open
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_repository_open_from_worktree(git_repository **out, git_worktree *wt);
 
 /**
  * Create a "fake" repository to wrap an object database
diff --git a/src/repository.c b/src/repository.c
index 2e267b7..03e4390 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -28,6 +28,7 @@
 #include "diff_driver.h"
 #include "annotated_commit.h"
 #include "submodule.h"
+#include "worktree.h"
 
 GIT__USE_STRMAP
 #include "strmap.h"
@@ -817,6 +818,36 @@ int git_repository_open(git_repository **repo_out, const char *path)
 		repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL);
 }
 
+int git_repository_open_from_worktree(git_repository **repo_out, git_worktree *wt)
+{
+	git_buf path = GIT_BUF_INIT;
+	git_repository *repo = NULL;
+	int len, err;
+
+	assert(repo_out && wt);
+
+	*repo_out = NULL;
+	len = strlen(wt->gitlink_path);
+
+	if (len <= 4 || strcasecmp(wt->gitlink_path + len - 4, ".git")) {
+		err = -1;
+		goto out;
+	}
+
+	if ((err = git_buf_set(&path, wt->gitlink_path, len - 4)) < 0)
+		goto out;
+
+	if ((err = git_repository_open(&repo, path.ptr)) < 0)
+		goto out;
+
+	*repo_out = repo;
+
+out:
+	git_buf_free(&path);
+
+	return err;
+}
+
 int git_repository_wrap_odb(git_repository **repo_out, git_odb *odb)
 {
 	git_repository *repo;
diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c
index 28d8899..d891d6f 100644
--- a/tests/worktree/worktree.c
+++ b/tests/worktree/worktree.c
@@ -131,3 +131,75 @@ void test_worktree_worktree__lookup_nonexistent_worktree(void)
 	cl_git_fail(git_worktree_lookup(&wt, fixture.repo, "nonexistent"));
 	cl_assert_equal_p(wt, NULL);
 }
+
+void test_worktree_worktree__open(void)
+{
+	git_worktree *wt;
+	git_repository *repo;
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+
+	cl_git_pass(git_repository_open_from_worktree(&repo, wt));
+	cl_assert_equal_s(git_repository_workdir(repo),
+		git_repository_workdir(fixture.worktree));
+
+	git_repository_free(repo);
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__open_invalid_commondir(void)
+{
+	git_worktree *wt;
+	git_repository *repo;
+	git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
+
+	cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/commondir"));
+	cl_git_pass(git_buf_printf(&path,
+		    "%s/worktrees/testrepo-worktree/commondir",
+		    fixture.repo->commondir));
+	cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644));
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_fail(git_repository_open_from_worktree(&repo, wt));
+
+	git_buf_free(&buf);
+	git_buf_free(&path);
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__open_invalid_gitdir(void)
+{
+	git_worktree *wt;
+	git_repository *repo;
+	git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
+
+	cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/gitdir"));
+	cl_git_pass(git_buf_printf(&path,
+		    "%s/worktrees/testrepo-worktree/gitdir",
+		    fixture.repo->commondir));
+	cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644));
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_fail(git_repository_open_from_worktree(&repo, wt));
+
+	git_buf_free(&buf);
+	git_buf_free(&path);
+	git_worktree_free(wt);
+}
+
+void test_worktree_worktree__open_invalid_parent(void)
+{
+	git_worktree *wt;
+	git_repository *repo;
+	git_buf buf = GIT_BUF_INIT;
+
+	cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/gitdir"));
+	cl_git_pass(git_futils_writebuffer(&buf,
+		    fixture.worktree->path_gitlink, O_RDWR, 0644));
+
+	cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+	cl_git_fail(git_repository_open_from_worktree(&repo, wt));
+
+	git_buf_free(&buf);
+	git_worktree_free(wt);
+}