summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/git2/worktree.h37
-rw-r--r--src/worktree.c58
-rw-r--r--tests/worktree/worktree.c107
3 files changed, 202 insertions, 0 deletions
diff --git a/include/git2/worktree.h b/include/git2/worktree.h
new file mode 100644
index 000000000..c09fa32d0
--- /dev/null
+++ b/include/git2/worktree.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_worktree_h__
+#define INCLUDE_git_worktree_h__
+
+#include "common.h"
+#include "types.h"
+#include "strarray.h"
+
+/**
+ * @file git2/worktrees.h
+ * @brief Git worktree related functions
+ * @defgroup git_commit Git worktree related functions
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * List names of linked working trees
+ *
+ * The returned list should be released with `git_strarray_free`
+ * when no longer needed.
+ *
+ * @param out pointer to the array of working tree names
+ * @param repo the repo to use when listing working trees
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_worktree_list(git_strarray *out, git_repository *repo);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/src/worktree.c b/src/worktree.c
new file mode 100644
index 000000000..28d895d5c
--- /dev/null
+++ b/src/worktree.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2/worktree.h"
+
+#include "common.h"
+#include "repository.h"
+
+static bool is_worktree_dir(git_buf *dir)
+{
+ return git_path_contains_file(dir, "commondir")
+ && git_path_contains_file(dir, "gitdir")
+ && git_path_contains_file(dir, "HEAD");
+}
+
+int git_worktree_list(git_strarray *wts, git_repository *repo)
+{
+ git_vector worktrees = GIT_VECTOR_INIT;
+ git_buf path = GIT_BUF_INIT;
+ char *worktree;
+ unsigned i, len;
+ int error;
+
+ assert(wts && repo);
+
+ wts->count = 0;
+ wts->strings = NULL;
+
+ if ((error = git_buf_printf(&path, "%s/worktrees/", repo->commondir)) < 0)
+ goto exit;
+ if (!git_path_exists(path.ptr) || git_path_is_empty_dir(path.ptr))
+ goto exit;
+ if ((error = git_path_dirload(&worktrees, path.ptr, path.size, 0x0)) < 0)
+ goto exit;
+
+ len = path.size;
+
+ git_vector_foreach(&worktrees, i, worktree) {
+ git_buf_truncate(&path, len);
+ git_buf_puts(&path, worktree);
+
+ if (!is_worktree_dir(&path)) {
+ git_vector_remove(&worktrees, i);
+ git__free(worktree);
+ }
+ }
+
+ wts->strings = (char **)git_vector_detach(&wts->count, NULL, &worktrees);
+
+exit:
+ git_buf_free(&path);
+
+ return error;
+}
diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c
new file mode 100644
index 000000000..3acae886e
--- /dev/null
+++ b/tests/worktree/worktree.c
@@ -0,0 +1,107 @@
+#include "clar_libgit2.h"
+#include "worktree_helpers.h"
+
+#include "git2/worktree.h"
+#include "repository.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+static worktree_fixture fixture =
+ WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+void test_worktree_worktree__initialize(void)
+{
+ setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_worktree__cleanup(void)
+{
+ cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_worktree__list(void)
+{
+ git_strarray wts;
+
+ cl_git_pass(git_worktree_list(&wts, fixture.repo));
+ cl_assert_equal_i(wts.count, 1);
+ cl_assert_equal_s(wts.strings[0], "testrepo-worktree");
+
+ git_strarray_free(&wts);
+}
+
+void test_worktree_worktree__list_with_invalid_worktree_dirs(void)
+{
+ const char *filesets[3][2] = {
+ { "gitdir", "commondir" },
+ { "gitdir", "HEAD" },
+ { "HEAD", "commondir" },
+ };
+ git_buf path = GIT_BUF_INIT;
+ git_strarray wts;
+ unsigned i, j, len;
+
+ cl_git_pass(git_buf_printf(&path, "%s/worktrees/invalid",
+ fixture.repo->commondir));
+ cl_git_pass(p_mkdir(path.ptr, 0755));
+
+ len = path.size;
+
+ for (i = 0; i < ARRAY_SIZE(filesets); i++) {
+
+ for (j = 0; j < ARRAY_SIZE(filesets[i]); j++) {
+ git_buf_truncate(&path, len);
+ cl_git_pass(git_buf_joinpath(&path, path.ptr, filesets[i][j]));
+ cl_git_pass(p_close(p_creat(path.ptr, 0644)));
+ }
+
+ cl_git_pass(git_worktree_list(&wts, fixture.worktree));
+ cl_assert_equal_i(wts.count, 1);
+ cl_assert_equal_s(wts.strings[0], "testrepo-worktree");
+ git_strarray_free(&wts);
+
+ for (j = 0; j < ARRAY_SIZE(filesets[i]); j++) {
+ git_buf_truncate(&path, len);
+ cl_git_pass(git_buf_joinpath(&path, path.ptr, filesets[i][j]));
+ p_unlink(path.ptr);
+ }
+ }
+
+ git_buf_free(&path);
+}
+
+void test_worktree_worktree__list_in_worktree_repo(void)
+{
+ git_strarray wts;
+
+ cl_git_pass(git_worktree_list(&wts, fixture.worktree));
+ cl_assert_equal_i(wts.count, 1);
+ cl_assert_equal_s(wts.strings[0], "testrepo-worktree");
+
+ git_strarray_free(&wts);
+}
+
+void test_worktree_worktree__list_bare(void)
+{
+ git_repository *repo;
+ git_strarray wts;
+
+ repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_worktree_list(&wts, repo));
+ cl_assert_equal_i(wts.count, 0);
+
+ git_repository_free(repo);
+}
+
+void test_worktree_worktree__list_without_worktrees(void)
+{
+ git_repository *repo;
+ git_strarray wts;
+
+ repo = cl_git_sandbox_init("testrepo2");
+ cl_git_pass(git_worktree_list(&wts, repo));
+ cl_assert_equal_i(wts.count, 0);
+
+ git_repository_free(repo);
+}