worktree: implement `git_repository_open_from_worktree` Add function `git_repository_open_from_worktree`, which allows to open a `git_worktree` as repository.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
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);
+}