diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/commit.c | 45 | ||||
-rw-r--r-- | src/refs.c | 74 | ||||
-rw-r--r-- | src/revparse.c | 81 |
3 files changed, 133 insertions, 67 deletions
diff --git a/src/commit.c b/src/commit.c index a3baf9d4e..32c47944b 100644 --- a/src/commit.c +++ b/src/commit.c @@ -229,19 +229,25 @@ 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); } +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) { - git_oid *parent_oid; + const git_oid *parent_oid; assert(commit); - parent_oid = git_vector_get(&commit->parent_oids, n); + 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; @@ -250,9 +256,36 @@ int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n) return git_commit_lookup(parent, commit->object.repo, parent_oid); } -const git_oid *git_commit_parent_oid(git_commit *commit, unsigned int n) +int git_commit_nth_gen_ancestor( + git_commit **ancestor, + const git_commit *commit, + unsigned int n) { - assert(commit); + git_commit *current, *parent; + int error; - return git_vector_get(&commit->parent_oids, n); + 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; } diff --git a/src/refs.c b/src/refs.c index e8f9fc8dc..13022c7a5 100644 --- a/src/refs.c +++ b/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; +} diff --git a/src/revparse.c b/src/revparse.c index f9859b18b..2b03c86b4 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -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) |