Commit 04fb12ab by Patrick Steinhardt

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.
parent f0cfc341
......@@ -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
*
......
......@@ -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
......
......@@ -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;
......
#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);
}
#include "clar_libgit2.h"
#include "worktree_helpers.h"
#include "checkout.h"
#include "repository.h"
#include "worktree.h"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment