mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-02 11:49:04 +00:00
Merge pull request #814 from nulltoken/topic/revparse-refac
Revparse refactoring: a start
This commit is contained in:
commit
db2d4061f6
@ -178,6 +178,25 @@ GIT_EXTERN(int) git_commit_parent(git_commit **parent, git_commit *commit, unsig
|
||||
*/
|
||||
GIT_EXTERN(const git_oid *) git_commit_parent_oid(git_commit *commit, unsigned int n);
|
||||
|
||||
/**
|
||||
* Get the commit object that is the <n>th generation ancestor
|
||||
* of the named commit object, following only the first parents.
|
||||
* The returned commit has to be freed by the caller.
|
||||
*
|
||||
* Passing `0` as the generation number returns another instance of the
|
||||
* base commit itself.
|
||||
*
|
||||
* @param ancestor Pointer where to store the ancestor commit
|
||||
* @param commit a previously loaded commit.
|
||||
* @param n the requested generation
|
||||
* @return 0 on success; GIT_ENOTFOUND if no matching ancestor exists
|
||||
* or an error code
|
||||
*/
|
||||
int git_commit_nth_gen_ancestor(
|
||||
git_commit **ancestor,
|
||||
const git_commit *commit,
|
||||
unsigned int n);
|
||||
|
||||
/**
|
||||
* Create a new commit in the repository using `git_object`
|
||||
* instances as parameters.
|
||||
|
@ -363,6 +363,27 @@ GIT_EXTERN(int) git_reference_foreach_glob(
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_has_log(git_reference *ref);
|
||||
|
||||
|
||||
/**
|
||||
* Return the reference supporting the remote tracking branch,
|
||||
* given a reference branch.
|
||||
*
|
||||
* The input reference has to be located in the `refs/heads`
|
||||
* namespace.
|
||||
*
|
||||
* @param tracking_ref Pointer where to store the retrieved
|
||||
* reference.
|
||||
*
|
||||
* @param branch_ref A git local branch reference.
|
||||
*
|
||||
* @return 0 on success; GIT_ENOTFOUND when no remote tracking
|
||||
* reference exists, otherwise an error code.
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_remote_tracking_from_branch(
|
||||
git_reference **tracking_ref,
|
||||
git_reference *branch_ref
|
||||
);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
63
src/commit.c
63
src/commit.c
@ -229,30 +229,63 @@ GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
|
||||
GIT_COMMIT_GETTER(unsigned int, parentcount, commit->parent_oids.length)
|
||||
GIT_COMMIT_GETTER(const git_oid *, tree_oid, &commit->tree_oid);
|
||||
|
||||
|
||||
int git_commit_tree(git_tree **tree_out, git_commit *commit)
|
||||
{
|
||||
assert(commit);
|
||||
return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_oid);
|
||||
}
|
||||
|
||||
int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
|
||||
{
|
||||
git_oid *parent_oid;
|
||||
assert(commit);
|
||||
|
||||
parent_oid = git_vector_get(&commit->parent_oids, n);
|
||||
if (parent_oid == NULL) {
|
||||
giterr_set(GITERR_INVALID, "Parent %u does not exist", n);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
return git_commit_lookup(parent, commit->object.repo, parent_oid);
|
||||
}
|
||||
|
||||
const git_oid *git_commit_parent_oid(git_commit *commit, unsigned int n)
|
||||
{
|
||||
assert(commit);
|
||||
|
||||
return git_vector_get(&commit->parent_oids, n);
|
||||
}
|
||||
|
||||
int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
|
||||
{
|
||||
const git_oid *parent_oid;
|
||||
assert(commit);
|
||||
|
||||
parent_oid = git_commit_parent_oid(commit, n);
|
||||
if (parent_oid == NULL) {
|
||||
giterr_set(GITERR_INVALID, "Parent %u does not exist", n);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
return git_commit_lookup(parent, commit->object.repo, parent_oid);
|
||||
}
|
||||
|
||||
int git_commit_nth_gen_ancestor(
|
||||
git_commit **ancestor,
|
||||
const git_commit *commit,
|
||||
unsigned int n)
|
||||
{
|
||||
git_commit *current, *parent;
|
||||
int error;
|
||||
|
||||
assert(ancestor && commit);
|
||||
|
||||
current = (git_commit *)commit;
|
||||
|
||||
if (n == 0)
|
||||
return git_commit_lookup(
|
||||
ancestor,
|
||||
commit->object.repo,
|
||||
git_object_id((const git_object *)commit));
|
||||
|
||||
while (n--) {
|
||||
error = git_commit_parent(&parent, (git_commit *)current, 0);
|
||||
|
||||
if (current != commit)
|
||||
git_commit_free(current);
|
||||
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
current = parent;
|
||||
}
|
||||
|
||||
*ancestor = parent;
|
||||
return 0;
|
||||
}
|
||||
|
74
src/refs.c
74
src/refs.c
@ -11,6 +11,7 @@
|
||||
#include "fileops.h"
|
||||
#include "pack.h"
|
||||
#include "reflog.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <git2/tag.h>
|
||||
#include <git2/object.h>
|
||||
@ -1811,3 +1812,76 @@ int git_reference_has_log(
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//TODO: How about also taking care of local tracking branches?
|
||||
//cf. http://alblue.bandlem.com/2011/07/git-tip-of-week-tracking-branches.html
|
||||
int git_reference_remote_tracking_from_branch(
|
||||
git_reference **tracking_ref,
|
||||
git_reference *branch_ref)
|
||||
{
|
||||
git_config *config = NULL;
|
||||
const char *name, *remote, *merge;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
int error = -1;
|
||||
|
||||
assert(tracking_ref && branch_ref);
|
||||
|
||||
name = git_reference_name(branch_ref);
|
||||
|
||||
if (git__prefixcmp(name, GIT_REFS_HEADS_DIR)) {
|
||||
giterr_set(
|
||||
GITERR_INVALID,
|
||||
"Failed to retrieve tracking reference - '%s' is not a branch.",
|
||||
name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (git_repository_config(&config, branch_ref->owner) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_buf_printf(
|
||||
&buf,
|
||||
"branch.%s.remote",
|
||||
name + strlen(GIT_REFS_HEADS_DIR)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_config_get_string(&remote, config, git_buf_cstr(&buf))) < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = -1;
|
||||
|
||||
git_buf_clear(&buf);
|
||||
|
||||
//TODO: Is it ok to fail when no merge target is found?
|
||||
if (git_buf_printf(
|
||||
&buf,
|
||||
"branch.%s.merge",
|
||||
name + strlen(GIT_REFS_HEADS_DIR)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (git_config_get_string(&merge, config, git_buf_cstr(&buf)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
//TODO: Should we test this?
|
||||
if (git__prefixcmp(merge, GIT_REFS_HEADS_DIR))
|
||||
goto cleanup;
|
||||
|
||||
git_buf_clear(&buf);
|
||||
|
||||
if (git_buf_printf(
|
||||
&buf,
|
||||
"refs/remotes/%s/%s",
|
||||
remote,
|
||||
merge + strlen(GIT_REFS_HEADS_DIR)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = git_reference_lookup(
|
||||
tracking_ref,
|
||||
branch_ref->owner,
|
||||
git_buf_cstr(&buf));
|
||||
|
||||
cleanup:
|
||||
git_config_free(config);
|
||||
git_buf_free(&buf);
|
||||
return error;
|
||||
}
|
||||
|
@ -104,7 +104,16 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
extern int revparse_lookup_object(git_object **out, git_repository *repo, const char *spec);
|
||||
static int maybe_sha_or_abbrev(git_object**out, git_repository *repo, const char *spec)
|
||||
{
|
||||
git_oid oid;
|
||||
size_t speclen = strlen(spec);
|
||||
|
||||
if (git_oid_fromstrn(&oid, spec, speclen) < 0)
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
return git_object_lookup_prefix(out, repo, &oid, speclen, GIT_OBJ_ANY);
|
||||
}
|
||||
|
||||
static int maybe_describe(git_object**out, git_repository *repo, const char *spec)
|
||||
{
|
||||
@ -123,21 +132,10 @@ static int maybe_describe(git_object**out, git_repository *repo, const char *spe
|
||||
if (!match)
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
return revparse_lookup_object(out, repo, substr+2);
|
||||
return maybe_sha_or_abbrev(out, repo, substr+2);
|
||||
}
|
||||
|
||||
static int maybe_sha_or_abbrev(git_object**out, git_repository *repo, const char *spec)
|
||||
{
|
||||
git_oid oid;
|
||||
size_t speclen = strlen(spec);
|
||||
|
||||
if (git_oid_fromstrn(&oid, spec, speclen) < 0)
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
return git_object_lookup_prefix(out, repo, &oid, speclen, GIT_OBJ_ANY);
|
||||
}
|
||||
|
||||
int revparse_lookup_object(git_object **out, git_repository *repo, const char *spec)
|
||||
static int revparse_lookup_object(git_object **out, git_repository *repo, const char *spec)
|
||||
{
|
||||
int error;
|
||||
git_reference *ref;
|
||||
@ -251,32 +249,12 @@ static int walk_ref_history(git_object **out, git_repository *repo, const char *
|
||||
date_error = git__date_parse(×tamp, git_buf_cstr(&datebuf));
|
||||
|
||||
/* @{u} or @{upstream} -> upstream branch, for a tracking branch. This is stored in the config. */
|
||||
if (!git__prefixcmp(git_reference_name(disambiguated), GIT_REFS_HEADS_DIR) &&
|
||||
(!strcmp(reflogspec, "@{u}") || !strcmp(reflogspec, "@{upstream}"))) {
|
||||
git_config *cfg;
|
||||
if (!git_repository_config(&cfg, repo)) {
|
||||
/* Is the ref a tracking branch? */
|
||||
const char *remote;
|
||||
git_buf_clear(&buf);
|
||||
git_buf_printf(&buf, "branch.%s.remote",
|
||||
git_reference_name(disambiguated) + strlen(GIT_REFS_HEADS_DIR));
|
||||
|
||||
if (!git_config_get_string(&remote, cfg, git_buf_cstr(&buf))) {
|
||||
/* Yes. Find the first merge target name. */
|
||||
const char *mergetarget;
|
||||
git_buf_clear(&buf);
|
||||
git_buf_printf(&buf, "branch.%s.merge",
|
||||
git_reference_name(disambiguated) + strlen(GIT_REFS_HEADS_DIR));
|
||||
|
||||
if (!git_config_get_string(&mergetarget, cfg, git_buf_cstr(&buf)) &&
|
||||
!git__prefixcmp(mergetarget, "refs/heads/")) {
|
||||
/* Success. Look up the target and fetch the object. */
|
||||
git_buf_clear(&buf);
|
||||
git_buf_printf(&buf, "refs/remotes/%s/%s", remote, mergetarget+11);
|
||||
retcode = revparse_lookup_fully_qualifed_ref(out, repo, git_buf_cstr(&buf));
|
||||
}
|
||||
}
|
||||
git_config_free(cfg);
|
||||
if (!strcmp(reflogspec, "@{u}") || !strcmp(reflogspec, "@{upstream}")) {
|
||||
git_reference *tracking;
|
||||
|
||||
if (!(retcode = git_reference_remote_tracking_from_branch(&tracking, disambiguated))) {
|
||||
retcode = revparse_lookup_fully_qualifed_ref(out, repo, git_reference_name(tracking));
|
||||
git_reference_free(tracking);
|
||||
}
|
||||
}
|
||||
|
||||
@ -524,8 +502,7 @@ static int handle_caret_syntax(git_object **out, git_repository *repo, git_objec
|
||||
|
||||
static int handle_linear_syntax(git_object **out, git_object *obj, const char *movement)
|
||||
{
|
||||
git_commit *commit1, *commit2;
|
||||
int i, n;
|
||||
int n;
|
||||
|
||||
/* Dereference until we reach a commit. */
|
||||
if (dereference_to_type(&obj, obj, GIT_OBJ_COMMIT) < 0) {
|
||||
@ -539,26 +516,8 @@ static int handle_linear_syntax(git_object **out, git_object *obj, const char *m
|
||||
} else if (git__strtol32(&n, movement, NULL, 0) < 0) {
|
||||
return GIT_ERROR;
|
||||
}
|
||||
commit1 = (git_commit*)obj;
|
||||
|
||||
/* "~0" just returns the input */
|
||||
if (n == 0) {
|
||||
*out = obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i=0; i<n; i++) {
|
||||
if (git_commit_parent(&commit2, commit1, 0) < 0) {
|
||||
return GIT_ERROR;
|
||||
}
|
||||
if (commit1 != (git_commit*)obj) {
|
||||
git_commit_free(commit1);
|
||||
}
|
||||
commit1 = commit2;
|
||||
}
|
||||
|
||||
*out = (git_object*)commit1;
|
||||
return 0;
|
||||
return git_commit_nth_gen_ancestor((git_commit **)out, (git_commit*)obj, n);
|
||||
}
|
||||
|
||||
static int oid_for_tree_path(git_oid *out, git_tree *tree, git_repository *repo, const char *path)
|
||||
|
57
tests-clar/commit/parent.c
Normal file
57
tests-clar/commit/parent.c
Normal file
@ -0,0 +1,57 @@
|
||||
#include "clar_libgit2.h"
|
||||
|
||||
static git_repository *_repo;
|
||||
static git_commit *commit;
|
||||
|
||||
void test_commit_parent__initialize(void)
|
||||
{
|
||||
git_oid oid;
|
||||
|
||||
cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
|
||||
|
||||
git_oid_fromstr(&oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
|
||||
cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
|
||||
}
|
||||
|
||||
void test_commit_parent__cleanup(void)
|
||||
{
|
||||
git_commit_free(commit);
|
||||
git_repository_free(_repo);
|
||||
}
|
||||
|
||||
static void assert_nth_gen_parent(unsigned int gen, const char *expected_oid)
|
||||
{
|
||||
git_commit *parent = NULL;
|
||||
int error;
|
||||
|
||||
error = git_commit_nth_gen_ancestor(&parent, commit, gen);
|
||||
|
||||
if (expected_oid != NULL) {
|
||||
cl_assert_equal_i(0, error);
|
||||
cl_assert_equal_i(0, git_oid_streq(git_commit_id(parent), expected_oid));
|
||||
} else
|
||||
cl_assert_equal_i(GIT_ENOTFOUND, error);
|
||||
|
||||
git_commit_free(parent);
|
||||
}
|
||||
|
||||
/*
|
||||
* $ git show be35~0
|
||||
* commit be3563ae3f795b2b4353bcce3a527ad0a4f7f644
|
||||
*
|
||||
* $ git show be35~1
|
||||
* commit 9fd738e8f7967c078dceed8190330fc8648ee56a
|
||||
*
|
||||
* $ git show be35~3
|
||||
* commit 5b5b025afb0b4c913b4c338a42934a3863bf3644
|
||||
*
|
||||
* $ git show be35~42
|
||||
* fatal: ambiguous argument 'be35~42': unknown revision or path not in the working tree.
|
||||
*/
|
||||
void test_commit_parent__can_retrieve_nth_generation_parent(void)
|
||||
{
|
||||
assert_nth_gen_parent(0, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
|
||||
assert_nth_gen_parent(1, "9fd738e8f7967c078dceed8190330fc8648ee56a");
|
||||
assert_nth_gen_parent(3, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
|
||||
assert_nth_gen_parent(42, NULL);
|
||||
}
|
@ -16,12 +16,12 @@ static git_repository *g_repo;
|
||||
|
||||
void test_refs_read__initialize(void)
|
||||
{
|
||||
g_repo = cl_git_sandbox_init("testrepo");
|
||||
cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
|
||||
}
|
||||
|
||||
void test_refs_read__cleanup(void)
|
||||
{
|
||||
cl_git_sandbox_cleanup();
|
||||
git_repository_free(g_repo);
|
||||
}
|
||||
|
||||
void test_refs_read__loose_tag(void)
|
||||
|
49
tests-clar/refs/remotetracking.c
Normal file
49
tests-clar/refs/remotetracking.c
Normal file
@ -0,0 +1,49 @@
|
||||
#include "clar_libgit2.h"
|
||||
|
||||
static git_repository *g_repo;
|
||||
|
||||
void test_refs_remotetracking__initialize(void)
|
||||
{
|
||||
cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
|
||||
}
|
||||
|
||||
void test_refs_remotetracking__cleanup(void)
|
||||
{
|
||||
git_repository_free(g_repo);
|
||||
}
|
||||
|
||||
void test_refs_remotetracking__unfound_returns_GIT_ENOTFOUND(void)
|
||||
{
|
||||
git_reference *branch, *tracking;
|
||||
|
||||
cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/heads/subtrees"));
|
||||
|
||||
cl_assert_equal_i(GIT_ENOTFOUND, git_reference_remote_tracking_from_branch(&tracking, branch));
|
||||
|
||||
git_reference_free(branch);
|
||||
}
|
||||
|
||||
void test_refs_remotetracking__retrieving_from_a_non_head_fails(void)
|
||||
{
|
||||
git_reference *branch, *tracking;
|
||||
|
||||
cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/tags/e90810b"));
|
||||
|
||||
cl_git_fail(git_reference_remote_tracking_from_branch(&tracking, branch));
|
||||
|
||||
git_reference_free(branch);
|
||||
}
|
||||
|
||||
void test_refs_remotetracking__can_retrieve_a_remote_tracking_branch_reference(void)
|
||||
{
|
||||
git_reference *branch, *tracking;
|
||||
|
||||
cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/heads/master"));
|
||||
|
||||
cl_git_pass(git_reference_remote_tracking_from_branch(&tracking, branch));
|
||||
|
||||
cl_assert_equal_s("refs/remotes/test/master", git_reference_name(tracking));
|
||||
|
||||
git_reference_free(branch);
|
||||
git_reference_free(tracking);
|
||||
}
|
Loading…
Reference in New Issue
Block a user