refs: always read pseudorefs as loose refs

Regardless of which reference storage format is used, pseudorefs will
always be looked up via the filesystem as loose refs. This is because
pseudorefs do not strictly follow the reference format and may contain
additional metadata that is not present in a normal reference.

We don't honor this in `git_reference_lookup()` though but instead defer
to the refdb to read such references. This obviously works just fine
with the "files" backend, but any other backend would have to grow
custom logic to handle reading pseudorefs.

Refactor `git_reference_lookup_resolved()` so that it knows to always
read pseudorefs as loose references. This allows refdb implementations
to not care about pseudoref handling at all.
This commit is contained in:
Patrick Steinhardt
2025-07-14 12:43:58 +02:00
parent 7f35dc5817
commit b17ecb23e2
2 changed files with 24 additions and 0 deletions

View File

@@ -231,6 +231,14 @@ int git_reference_lookup_resolved(
GIT_ASSERT_ARG(repo);
GIT_ASSERT_ARG(name);
/*
* Pseudorefs are not stored in the reference backend, as they may
* contain additional data that doesn't even follow the normal ref
* format. So we look these up as "loose" refs directly.
*/
if (git_reference__is_pseudoref(name))
return git_reference__lookup_loose(ref_out, repo->gitdir, name, repo->oid_type);
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)
@@ -1278,6 +1286,21 @@ int git_reference_is_note(const git_reference *ref)
return git_reference__is_note(ref->name);
}
int git_reference__is_pseudoref(const char *ref_name)
{
const char * const pseudorefs[] = {
"MERGE_HEAD",
"FETCH_HEAD",
};
size_t i;
for (i = 0; i < ARRAY_SIZE(pseudorefs); i++)
if (git__strcmp(ref_name, pseudorefs[i]) == 0)
return 1;
return 0;
}
static int peel_error(int error, const git_reference *ref, const char *msg)
{
git_error_set(

View File

@@ -95,6 +95,7 @@ int git_reference__is_branch(const char *ref_name);
int git_reference__is_remote(const char *ref_name);
int git_reference__is_tag(const char *ref_name);
int git_reference__is_note(const char *ref_name);
int git_reference__is_pseudoref(const char *ref_name);
const char *git_reference__shorthand(const char *name);
/*