Commit c54f40e47193af508b81eeec3ac7002649101360

Patrick Steinhardt 2020-06-30T09:28:12

refs: move resolving of references into the refdb Resolving of symbolic references is currently implemented inside the "refs" layer. As a result, it's hard to call this function from low-level parts that only have a refdb available, but no repository, as the "refs" layer always operates on the repository-level. So let's move the function into the generic "refdb" implementation to lift this restriction.

diff --git a/src/refdb.c b/src/refdb.c
index 764d2c1..12dfaf1 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -17,6 +17,9 @@
 #include "reflog.h"
 #include "posix.h"
 
+#define DEFAULT_NESTING_LEVEL	5
+#define MAX_NESTING_LEVEL		10
+
 int git_refdb_new(git_refdb **out, git_repository *repo)
 {
 	git_refdb *db;
@@ -134,6 +137,51 @@ int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name)
 	return 0;
 }
 
+int git_refdb_resolve(
+	git_reference **out,
+	git_refdb *db,
+	const char *ref_name,
+	int max_nesting)
+{
+	git_reference *ref = NULL;
+	int error = 0, nesting;
+
+	*out = NULL;
+
+	if (max_nesting > MAX_NESTING_LEVEL)
+		max_nesting = MAX_NESTING_LEVEL;
+	else if (max_nesting < 0)
+		max_nesting = DEFAULT_NESTING_LEVEL;
+
+	if ((error = git_refdb_lookup(&ref, db, ref_name)) < 0)
+		goto out;
+
+	for (nesting = 0; nesting < max_nesting; nesting++) {
+		git_reference *resolved;
+
+		if (ref->type == GIT_REFERENCE_DIRECT)
+			break;
+		if ((error = git_refdb_lookup(&resolved, db, git_reference_symbolic_target(ref))) < 0)
+			goto out;
+
+		git_reference_free(ref);
+		ref = resolved;
+	}
+
+	if (ref->type != GIT_REFERENCE_DIRECT && max_nesting != 0) {
+		git_error_set(GIT_ERROR_REFERENCE,
+			"cannot resolve reference (>%u levels deep)", max_nesting);
+		error = -1;
+		goto out;
+	}
+
+	*out = ref;
+	ref = NULL;
+out:
+	git_reference_free(ref);
+	return error;
+}
+
 int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob)
 {
 	int error;
diff --git a/src/refdb.h b/src/refdb.h
index 05c0e1c..aeddaf7 100644
--- a/src/refdb.h
+++ b/src/refdb.h
@@ -30,6 +30,12 @@ int git_refdb_lookup(
 	git_refdb *refdb,
 	const char *ref_name);
 
+int git_refdb_resolve(
+	git_reference **out,
+	git_refdb *db,
+	const char *ref_name,
+	int max_nesting);
+
 int git_refdb_rename(
 	git_reference **out,
 	git_refdb *db,
diff --git a/src/refs.c b/src/refs.c
index c5d69c1..e7105c7 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -214,52 +214,17 @@ int git_reference_lookup_resolved(
 	const char *name,
 	int max_nesting)
 {
-	git_refname_t scan_name;
-	git_reference_t scan_type;
-	int error = 0, nesting;
-	git_reference *ref = NULL;
+	git_refname_t normalized;
 	git_refdb *refdb;
+	int error = 0;
 
 	assert(ref_out && repo && name);
 
-	*ref_out = NULL;
-
-	if (max_nesting > MAX_NESTING_LEVEL)
-		max_nesting = MAX_NESTING_LEVEL;
-	else if (max_nesting < 0)
-		max_nesting = DEFAULT_NESTING_LEVEL;
-
-	scan_type = GIT_REFERENCE_SYMBOLIC;
-
-	if ((error = reference_normalize_for_repo(scan_name, repo, name, true)) < 0)
+	if ((error = reference_normalize_for_repo(normalized, repo, name, true)) < 0 ||
+	    (error = git_repository_refdb__weakptr(&refdb, repo)) < 0 ||
+	    (error = git_refdb_resolve(ref_out, refdb, normalized, max_nesting)) < 0)
 		return error;
 
-	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
-		return error;
-
-	for (nesting = max_nesting;
-		 nesting >= 0 && scan_type == GIT_REFERENCE_SYMBOLIC;
-		 nesting--)
-	{
-		if (nesting != max_nesting) {
-			strncpy(scan_name, ref->target.symbolic, sizeof(scan_name));
-			git_reference_free(ref);
-		}
-
-		if ((error = git_refdb_lookup(&ref, refdb, scan_name)) < 0)
-			return error;
-
-		scan_type = ref->type;
-	}
-
-	if (scan_type != GIT_REFERENCE_DIRECT && max_nesting != 0) {
-		git_error_set(GIT_ERROR_REFERENCE,
-			"cannot resolve reference (>%u levels deep)", max_nesting);
-		git_reference_free(ref);
-		return -1;
-	}
-
-	*ref_out = ref;
 	return 0;
 }