summaryrefslogtreecommitdiff
path: root/src/refs.c
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2012-04-13 10:33:14 -0700
committerRussell Belfer <rb@github.com>2012-04-17 10:44:50 -0700
commitf201d613a80f7ad6f54d90eb7a7a0d8b8c72676b (patch)
treec3cd8f0eee145f40b640519e06ae628cdb942eb3 /src/refs.c
parent1a6e8f8a54eea1159a950cd8a49cedae3699ff9a (diff)
downloadlibgit2-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.c139
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);
+}
+