diff options
author | Russell Belfer <rb@github.com> | 2012-04-13 10:33:14 -0700 |
---|---|---|
committer | Russell Belfer <rb@github.com> | 2012-04-17 10:44:50 -0700 |
commit | f201d613a80f7ad6f54d90eb7a7a0d8b8c72676b (patch) | |
tree | c3cd8f0eee145f40b640519e06ae628cdb942eb3 /src/refs.c | |
parent | 1a6e8f8a54eea1159a950cd8a49cedae3699ff9a (diff) | |
download | libgit2-f201d613a80f7ad6f54d90eb7a7a0d8b8c72676b.tar.gz |
Add git_reference_lookup_oid and lookup_resolved
Adds a new public reference function `git_reference_lookup_oid`
that directly resolved a reference name to an OID without returning
the intermediate `git_reference` object (hence, no free needed).
Internally, this adds a `git_reference_lookup_resolved` function
that combines looking up and resolving a reference. This allows
us to be more efficient with memory reallocation.
The existing `git_reference_lookup` and `git_reference_resolve`
are reimplmented on top of the new utility and a few places in the
code are changed to use one of the two new functions.
Diffstat (limited to 'src/refs.c')
-rw-r--r-- | src/refs.c | 139 |
1 files changed, 88 insertions, 51 deletions
diff --git a/src/refs.c b/src/refs.c index fb23a0ef8..90cb920ee 100644 --- a/src/refs.c +++ b/src/refs.c @@ -15,7 +15,8 @@ #include <git2/tag.h> #include <git2/object.h> -#define MAX_NESTING_LEVEL 5 +#define DEFAULT_NESTING_LEVEL 5 +#define MAX_NESTING_LEVEL 10 enum { GIT_PACKREF_HAS_PEEL = 1, @@ -1057,24 +1058,80 @@ int git_reference_delete(git_reference *ref) int git_reference_lookup(git_reference **ref_out, git_repository *repo, const char *name) { - char normalized_name[GIT_REFNAME_MAX]; - git_reference *ref = NULL; - int result; + return git_reference_lookup_resolved(ref_out, repo, name, 0); +} + +int git_reference_lookup_oid( + git_oid *out, git_repository *repo, const char *name) +{ + int error; + git_reference *ref; + + if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0) + return error; + + git_oid_cpy(out, git_reference_oid(ref)); + git_reference_free(ref); + return 0; +} + +int git_reference_lookup_resolved( + git_reference **ref_out, + git_repository *repo, + const char *name, + int max_nesting) +{ + git_reference *scan; + int result, nesting; assert(ref_out && repo && name); + *ref_out = NULL; - if (normalize_name(normalized_name, sizeof(normalized_name), name, 0) < 0) - return -1; + if (max_nesting > MAX_NESTING_LEVEL) + max_nesting = MAX_NESTING_LEVEL; + else if (max_nesting < 0) + max_nesting = DEFAULT_NESTING_LEVEL; - if (reference_alloc(&ref, repo, normalized_name) < 0) - return -1; + scan = git__calloc(1, sizeof(git_reference)); + GITERR_CHECK_ALLOC(scan); - result = reference_lookup(ref); - if (result == 0) - *ref_out = ref; + scan->name = git__calloc(GIT_REFNAME_MAX + 1, sizeof(char)); + GITERR_CHECK_ALLOC(scan->name); - return result; + if ((result = normalize_name(scan->name, GIT_REFNAME_MAX, name, 0)) < 0) { + git_reference_free(scan); + return result; + } + + scan->target.symbolic = git__strdup(scan->name); + GITERR_CHECK_ALLOC(scan->target.symbolic); + + scan->owner = repo; + scan->flags = GIT_REF_SYMBOLIC; + + for (nesting = max_nesting; + nesting >= 0 && (scan->flags & GIT_REF_SYMBOLIC) != 0; + nesting--) + { + if (nesting != max_nesting) + strncpy(scan->name, scan->target.symbolic, GIT_REFNAME_MAX); + + scan->mtime = 0; + + if ((result = reference_lookup(scan)) < 0) + return result; /* lookup git_reference_free on scan already */ + } + + if ((scan->flags & GIT_REF_OID) == 0 && max_nesting != 0) { + giterr_set(GITERR_REFERENCE, + "Cannot resolve reference (>%u levels deep)", max_nesting); + git_reference_free(scan); + return -1; + } + + *ref_out = scan; + return 0; } /** @@ -1381,47 +1438,10 @@ rollback: int git_reference_resolve(git_reference **ref_out, git_reference *ref) { - int result, i = 0; - git_repository *repo; - - assert(ref); - - *ref_out = NULL; - repo = ref->owner; - - /* If the reference is already resolved, we need to return a - * copy. Instead of duplicating `ref`, we look it up again to - * ensure the copy is out to date */ if (ref->flags & GIT_REF_OID) return git_reference_lookup(ref_out, ref->owner, ref->name); - - /* Otherwise, keep iterating until the reference is resolved */ - for (i = 0; i < MAX_NESTING_LEVEL; ++i) { - git_reference *new_ref; - - result = git_reference_lookup(&new_ref, repo, ref->target.symbolic); - if (result < 0) - return result; - - /* Free intermediate references, except for the original one - * we've received */ - if (i > 0) - git_reference_free(ref); - - ref = new_ref; - - /* When the reference we've just looked up is an OID, we've - * successfully resolved the symbolic ref */ - if (ref->flags & GIT_REF_OID) { - *ref_out = ref; - return 0; - } - } - - giterr_set(GITERR_REFERENCE, - "Symbolic reference too nested (%d levels deep)", MAX_NESTING_LEVEL); - - return -1; + else + return git_reference_lookup_resolved(ref_out, ref->owner, ref->target.symbolic, -1); } int git_reference_packall(git_repository *repo) @@ -1649,3 +1669,20 @@ int git_reference__normalize_name_oid( { return normalize_name(buffer_out, out_size, name, 1); } + +#define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC) + +int git_reference_cmp(git_reference *ref1, git_reference *ref2) +{ + assert(ref1 && ref2); + + /* let's put symbolic refs before OIDs */ + if ((ref1->flags & GIT_REF_TYPEMASK) != (ref2->flags & GIT_REF_TYPEMASK)) + return (ref1->flags & GIT_REF_SYMBOLIC) ? -1 : 1; + + if (ref1->flags & GIT_REF_SYMBOLIC) + return strcmp(ref1->target.symbolic, ref2->target.symbolic); + + return git_oid_cmp(&ref1->target.oid, &ref2->target.oid); +} + |