Commit b895547ca135158bf1582b50f6c09f8f78066e87

Patrick Steinhardt 2020-06-30T09:35:21

refs: replace reimplementation of reference resolver The refs code currently has a second implementation that resolves references in order to find any final symbolic reference pointing to a nonexistent target branch. As we've just extended `git_refdb_resolve` to also return such references, let's use that one instead in order to reduce code duplication.

diff --git a/src/refs.c b/src/refs.c
index ddbfd7e..9ef1677 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -27,9 +27,6 @@
 
 bool git_reference__enable_symbolic_ref_target_validation = true;
 
-#define DEFAULT_NESTING_LEVEL	5
-#define MAX_NESTING_LEVEL		10
-
 enum {
 	GIT_PACKREF_HAS_PEEL = 1,
 	GIT_PACKREF_WAS_LOOSE = 2
@@ -1131,40 +1128,6 @@ int git_reference_cmp(
 	return git_oid__cmp(&ref1->target.oid, &ref2->target.oid);
 }
 
-/**
- * Get the end of a chain of references. If the final one is not
- * found, we return the reference just before that.
- */
-static int get_terminal(git_reference **out, git_repository *repo, const char *ref_name, int nesting)
-{
-	git_reference *ref;
-	int error = 0;
-
-	if (nesting > MAX_NESTING_LEVEL) {
-		git_error_set(GIT_ERROR_REFERENCE, "reference chain too deep (%d)", nesting);
-		return GIT_ENOTFOUND;
-	}
-
-	/* set to NULL to let the caller know that they're at the end of the chain */
-	if ((error = git_reference_lookup(&ref, repo, ref_name)) < 0) {
-		*out = NULL;
-		return error;
-	}
-
-	if (git_reference_type(ref) == GIT_REFERENCE_DIRECT) {
-		*out = ref;
-		error = 0;
-	} else {
-		error = get_terminal(out, repo, git_reference_symbolic_target(ref), nesting + 1);
-		if (error == GIT_ENOTFOUND && !*out)
-			*out = ref;
-		else
-			git_reference_free(ref);
-	}
-
-	return error;
-}
-
 /*
  * Starting with the reference given by `ref_name`, follows symbolic
  * references until a direct reference is found and updated the OID
@@ -1179,31 +1142,37 @@ int git_reference__update_terminal(
 {
 	git_reference *ref = NULL, *ref2 = NULL;
 	git_signature *who = NULL;
+	git_refdb *refdb = NULL;
 	const git_signature *to_use;
 	int error = 0;
 
 	if (!sig && (error = git_reference__log_signature(&who, repo)) < 0)
-		return error;
+		goto out;
 
 	to_use = sig ? sig : who;
-	error = get_terminal(&ref, repo, ref_name, 0);
 
-	/* found a dangling symref */
-	if (error == GIT_ENOTFOUND && ref) {
-		assert(git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC);
-		git_error_clear();
+	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
+		goto out;
+
+	if ((error = git_refdb_resolve(&ref, refdb, ref_name, -1)) < 0) {
+		if (error == GIT_ENOTFOUND) {
+			git_error_clear();
+			error = reference__create(&ref2, repo, ref_name, oid, NULL, 0, to_use,
+						  log_message, NULL, NULL);
+		}
+		goto out;
+	}
+
+	/* In case the resolved reference is symbolic, then it's a dangling symref. */
+	if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) {
 		error = reference__create(&ref2, repo, ref->target.symbolic, oid, NULL, 0, to_use,
 					  log_message, NULL, NULL);
-	} else if (error == GIT_ENOTFOUND) {
-		git_error_clear();
-		error = reference__create(&ref2, repo, ref_name, oid, NULL, 0, to_use,
-					  log_message, NULL, NULL);
-	}  else if (error == 0) {
-		assert(git_reference_type(ref) == GIT_REFERENCE_DIRECT);
+	} else {
 		error = reference__create(&ref2, repo, ref->name, oid, NULL, 1, to_use,
 					  log_message, &ref->target.oid, NULL);
 	}
 
+out:
 	git_reference_free(ref2);
 	git_reference_free(ref);
 	git_signature_free(who);