summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2015-10-27 12:37:51 +0100
committerPatrick Steinhardt <ps@pks.im>2017-02-13 11:03:01 +0100
commit04fb12abb24810391fa19af5696eb38629d650df (patch)
treecd926fbf2e9e3172ea9d0618efe6505e06089657
parentf0cfc34105fd68af9eb6e2024459c40c45e7d3a0 (diff)
downloadlibgit2-04fb12abb24810391fa19af5696eb38629d650df.tar.gz
worktree: implement functions reading HEAD
Implement `git_repository_head_for_worktree` and `git_repository_head_detached_for_worktree` for directly accessing a worktree's HEAD without opening it as a `git_repository` first.
-rw-r--r--include/git2/repository.h25
-rw-r--r--include/git2/worktree.h2
-rw-r--r--src/repository.c85
-rw-r--r--tests/worktree/repository.c63
-rw-r--r--tests/worktree/worktree.c1
5 files changed, 175 insertions, 1 deletions
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 29eb2da49..a396a5409 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -346,6 +346,17 @@ GIT_EXTERN(int) git_repository_init_ext(
GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo);
/**
+ * Retrieve the referenced HEAD for the worktree
+ *
+ * @param out pointer to the reference which will be retrieved
+ * @param repo a repository object
+ * @param name name of the worktree to retrieve HEAD for
+ * @return 0 when successful, error-code otherwise
+ */
+GIT_EXTERN(int) git_repository_head_for_worktree(git_reference **out, git_repository *repo,
+ const char *name);
+
+/**
* Check if a repository's HEAD is detached
*
* A repository's HEAD is detached when it points directly to a commit
@@ -357,6 +368,20 @@ GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo);
*/
GIT_EXTERN(int) git_repository_head_detached(git_repository *repo);
+/*
+ * Check if a worktree's HEAD is detached
+ *
+ * A worktree's HEAD is detached when it points directly to a
+ * commit instead of a branch.
+ *
+ * @param repo a repository object
+ * @param name name of the worktree to retrieve HEAD for
+ * @return 1 if HEAD is detached, 0 if its not; error code if
+ * there was an error
+ */
+GIT_EXTERN(int) git_repository_head_detached_for_worktree(git_repository *repo,
+ const char *name);
+
/**
* Check if the current branch is unborn
*
diff --git a/include/git2/worktree.h b/include/git2/worktree.h
index 594ff795b..ec869fb59 100644
--- a/include/git2/worktree.h
+++ b/include/git2/worktree.h
@@ -77,7 +77,7 @@ GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt);
*/
GIT_EXTERN(int) git_worktree_add(git_worktree **out, git_repository *repo, const char *name, const char *path);
-/*
+/**
* Lock worktree if not already locked
*
* Lock a worktree, optionally specifying a reason why the linked
diff --git a/src/repository.c b/src/repository.c
index 03e43909b..445005e96 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -2032,6 +2032,49 @@ int git_repository_head_detached(git_repository *repo)
return exists;
}
+static int read_worktree_head(git_buf *out, git_repository *repo, const char *name)
+{
+ git_buf path = GIT_BUF_INIT;
+ int err;
+
+ assert(out && repo && name);
+
+ git_buf_clear(out);
+
+ if ((err = git_buf_printf(&path, "%s/worktrees/%s/HEAD", repo->commondir, name)) < 0)
+ goto out;
+ if (!git_path_exists(path.ptr))
+ {
+ err = -1;
+ goto out;
+ }
+
+ if ((err = git_futils_readbuffer(out, path.ptr)) < 0)
+ goto out;
+ git_buf_rtrim(out);
+
+out:
+ git_buf_free(&path);
+
+ return err;
+}
+
+int git_repository_head_detached_for_worktree(git_repository *repo, const char *name)
+{
+ git_buf buf = GIT_BUF_INIT;
+ int ret;
+
+ assert(repo && name);
+
+ if (read_worktree_head(&buf, repo, name) < 0)
+ return -1;
+
+ ret = git__strncmp(buf.ptr, GIT_SYMREF, strlen(GIT_SYMREF)) != 0;
+ git_buf_free(&buf);
+
+ return ret;
+}
+
int git_repository_head(git_reference **head_out, git_repository *repo)
{
git_reference *head;
@@ -2051,6 +2094,48 @@ int git_repository_head(git_reference **head_out, git_repository *repo)
return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error;
}
+int git_repository_head_for_worktree(git_reference **out, git_repository *repo, const char *name)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_reference *head;
+ int err;
+
+ assert(out && repo && name);
+
+ *out = NULL;
+
+ if (git_repository_head_detached_for_worktree(repo, name))
+ return -1;
+ if ((err = read_worktree_head(&buf, repo, name)) < 0)
+ goto out;
+
+ /* We can only resolve symbolic references */
+ if (git__strncmp(buf.ptr, GIT_SYMREF, strlen(GIT_SYMREF)))
+ {
+ err = -1;
+ goto out;
+ }
+ git_buf_consume(&buf, buf.ptr + strlen(GIT_SYMREF));
+
+ if ((err = git_reference_lookup(&head, repo, buf.ptr)) < 0)
+ goto out;
+ if (git_reference_type(head) == GIT_REF_OID)
+ {
+ *out = head;
+ err = 0;
+ goto out;
+ }
+
+ err = git_reference_lookup_resolved(
+ out, repo, git_reference_symbolic_target(head), -1);
+ git_reference_free(head);
+
+out:
+ git_buf_free(&buf);
+
+ return err;
+}
+
int git_repository_head_unborn(git_repository *repo)
{
git_reference *ref = NULL;
diff --git a/tests/worktree/repository.c b/tests/worktree/repository.c
new file mode 100644
index 000000000..5c7595c64
--- /dev/null
+++ b/tests/worktree/repository.c
@@ -0,0 +1,63 @@
+#include "clar_libgit2.h"
+#include "worktree_helpers.h"
+#include "submodule/submodule_helpers.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_repository__initialize(void)
+{
+ setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_repository__cleanup(void)
+{
+ cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_repository__head(void)
+{
+ git_reference *ref, *head;
+
+ cl_git_pass(git_reference_lookup(&ref, fixture.repo, "refs/heads/testrepo-worktree"));
+ cl_git_pass(git_repository_head_for_worktree(&head, fixture.repo, "testrepo-worktree"));
+ cl_assert(git_reference_cmp(ref, head) == 0);
+
+ git_reference_free(ref);
+ git_reference_free(head);
+}
+
+void test_worktree_repository__head_fails_for_invalid_worktree(void)
+{
+ git_reference *head = NULL;
+
+ cl_git_fail(git_repository_head_for_worktree(&head, fixture.repo, "invalid"));
+ cl_assert(head == NULL);
+}
+
+void test_worktree_repository__head_detached(void)
+{
+ git_reference *ref, *head;
+
+ cl_git_pass(git_reference_lookup(&ref, fixture.repo, "refs/heads/testrepo-worktree"));
+ cl_git_pass(git_repository_set_head_detached(fixture.worktree, &ref->target.oid));
+
+ cl_assert(git_repository_head_detached(fixture.worktree));
+ cl_assert(git_repository_head_detached_for_worktree(fixture.repo, "testrepo-worktree"));
+ cl_git_fail(git_repository_head_for_worktree(&head, fixture.repo, "testrepo-worktree"));
+
+ git_reference_free(ref);
+}
+
+void test_worktree_repository__head_detached_fails_for_invalid_worktree(void)
+{
+ git_reference *head = NULL;
+
+ cl_git_fail(git_repository_head_detached_for_worktree(fixture.repo, "invalid"));
+ cl_assert(head == NULL);
+}
diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c
index 7758b1b1c..756cf387b 100644
--- a/tests/worktree/worktree.c
+++ b/tests/worktree/worktree.c
@@ -1,6 +1,7 @@
#include "clar_libgit2.h"
#include "worktree_helpers.h"
+#include "checkout.h"
#include "repository.h"
#include "worktree.h"