summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2020-06-30 13:26:05 +0200
committerPatrick Steinhardt <ps@pks.im>2020-07-12 16:01:21 +0200
commitcf7dd05bdab243e1150f771359be6c2a766a0a3a (patch)
treeb47f658cefc33e485122d1e47a61235aa874e5cd
parentc54f40e47193af508b81eeec3ac7002649101360 (diff)
downloadlibgit2-cf7dd05bdab243e1150f771359be6c2a766a0a3a.tar.gz
refdb: return resolved symbolic refs pointing to nonexistent refs
In some cases, resolving references requires us to also know about the final symbolic reference that's pointing to a nonexistent branch, e.g. in an empty repository where the main branch is yet unborn but HEAD already points to it. Right now, the resolving logic is thus split up into two, where one is the new refdb implementation and the second one is an ad-hoc implementation inside "refs.c". Let's extend `git_refdb_resolve` to also return such final dangling references pointing to nonexistent branches so we can deduplicate the resolving logic.
-rw-r--r--src/refdb.c10
-rw-r--r--src/refdb.h19
-rw-r--r--src/refs.c12
3 files changed, 40 insertions, 1 deletions
diff --git a/src/refdb.c b/src/refdb.c
index 12dfaf1e2..6879b6aab 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -161,8 +161,16 @@ int git_refdb_resolve(
if (ref->type == GIT_REFERENCE_DIRECT)
break;
- if ((error = git_refdb_lookup(&resolved, db, git_reference_symbolic_target(ref))) < 0)
+
+ if ((error = git_refdb_lookup(&resolved, db, git_reference_symbolic_target(ref))) < 0) {
+ /* If we found a symbolic reference with a nonexistent target, return it. */
+ if (error == GIT_ENOTFOUND) {
+ error = 0;
+ *out = ref;
+ ref = NULL;
+ }
goto out;
+ }
git_reference_free(ref);
ref = resolved;
diff --git a/src/refdb.h b/src/refdb.h
index aeddaf7da..84e19b1c3 100644
--- a/src/refdb.h
+++ b/src/refdb.h
@@ -30,6 +30,25 @@ int git_refdb_lookup(
git_refdb *refdb,
const char *ref_name);
+/**
+ * Resolve the reference by following symbolic references.
+ *
+ * Given a reference name, this function will follow any symbolic references up
+ * to `max_nesting` deep and return the resolved direct reference. If any of
+ * the intermediate symbolic references points to a non-existing reference,
+ * then that symbolic reference is returned instead with an error code of `0`.
+ * If the given reference is a direct reference already, it is returned
+ * directly.
+ *
+ * If `max_nesting` is `0`, the reference will not be resolved. If it's
+ * negative, it will be set to the default resolve depth which is `5`.
+ *
+ * @param out Pointer to store the result in.
+ * @param db The refdb to use for resolving the reference.
+ * @param ref_name The reference name to lookup and resolve.
+ * @param max_nesting The maximum nesting depth.
+ * @return `0` on success, a negative error code otherwise.
+ */
int git_refdb_resolve(
git_reference **out,
git_refdb *db,
diff --git a/src/refs.c b/src/refs.c
index e7105c7ae..ddbfd7e41 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -225,6 +225,18 @@ int git_reference_lookup_resolved(
(error = git_refdb_resolve(ref_out, refdb, normalized, max_nesting)) < 0)
return error;
+ /*
+ * The resolved reference may be a symbolic reference in case its
+ * target doesn't exist. If the user asked us to resolve (e.g.
+ * `max_nesting != 0`), then we need to return an error in case we got
+ * a symbolic reference back.
+ */
+ if (max_nesting && git_reference_type(*ref_out) == GIT_REFERENCE_SYMBOLIC) {
+ git_reference_free(*ref_out);
+ *ref_out = NULL;
+ return GIT_ENOTFOUND;
+ }
+
return 0;
}