Skip to content

Commit cf7dd05

Browse files
committed
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.
1 parent c54f40e commit cf7dd05

File tree

3 files changed

+40
-1
lines changed

3 files changed

+40
-1
lines changed

src/refdb.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,16 @@ int git_refdb_resolve(
161161

162162
if (ref->type == GIT_REFERENCE_DIRECT)
163163
break;
164-
if ((error = git_refdb_lookup(&resolved, db, git_reference_symbolic_target(ref))) < 0)
164+
165+
if ((error = git_refdb_lookup(&resolved, db, git_reference_symbolic_target(ref))) < 0) {
166+
/* If we found a symbolic reference with a nonexistent target, return it. */
167+
if (error == GIT_ENOTFOUND) {
168+
error = 0;
169+
*out = ref;
170+
ref = NULL;
171+
}
165172
goto out;
173+
}
166174

167175
git_reference_free(ref);
168176
ref = resolved;

src/refdb.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,25 @@ int git_refdb_lookup(
3030
git_refdb *refdb,
3131
const char *ref_name);
3232

33+
/**
34+
* Resolve the reference by following symbolic references.
35+
*
36+
* Given a reference name, this function will follow any symbolic references up
37+
* to `max_nesting` deep and return the resolved direct reference. If any of
38+
* the intermediate symbolic references points to a non-existing reference,
39+
* then that symbolic reference is returned instead with an error code of `0`.
40+
* If the given reference is a direct reference already, it is returned
41+
* directly.
42+
*
43+
* If `max_nesting` is `0`, the reference will not be resolved. If it's
44+
* negative, it will be set to the default resolve depth which is `5`.
45+
*
46+
* @param out Pointer to store the result in.
47+
* @param db The refdb to use for resolving the reference.
48+
* @param ref_name The reference name to lookup and resolve.
49+
* @param max_nesting The maximum nesting depth.
50+
* @return `0` on success, a negative error code otherwise.
51+
*/
3352
int git_refdb_resolve(
3453
git_reference **out,
3554
git_refdb *db,

src/refs.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,18 @@ int git_reference_lookup_resolved(
225225
(error = git_refdb_resolve(ref_out, refdb, normalized, max_nesting)) < 0)
226226
return error;
227227

228+
/*
229+
* The resolved reference may be a symbolic reference in case its
230+
* target doesn't exist. If the user asked us to resolve (e.g.
231+
* `max_nesting != 0`), then we need to return an error in case we got
232+
* a symbolic reference back.
233+
*/
234+
if (max_nesting && git_reference_type(*ref_out) == GIT_REFERENCE_SYMBOLIC) {
235+
git_reference_free(*ref_out);
236+
*ref_out = NULL;
237+
return GIT_ENOTFOUND;
238+
}
239+
228240
return 0;
229241
}
230242

0 commit comments

Comments
 (0)