refs/reftable: introduce generic checks for refs

In a preceding commit we have extracted generic checks for both direct
and symbolic refs that apply for all backends. Wire up those checks for
the "reftable" backend.

Note that this is done by iterating through all refs manually with the
low-level reftable ref iterator. We explicitly don't want to use the
higher-level iterator that is exposed to users of the reftable backend
as that iterator may swallow for example broken refs.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Patrick Steinhardt 2026-01-09 13:39:43 +01:00 committed by Junio C Hamano
parent d385bc1b75
commit 8a3de73c64
2 changed files with 88 additions and 6 deletions

View File

@ -2767,19 +2767,89 @@ static int reftable_be_fsck(struct ref_store *ref_store, struct fsck_options *o,
{
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_READ, "fsck");
struct reftable_ref_iterator *iter = NULL;
struct reftable_ref_record ref = { 0 };
struct fsck_ref_report report = { 0 };
struct strbuf refname = STRBUF_INIT;
struct reftable_backend *backend;
int ret, errors = 0;
if (is_main_worktree(wt)) {
backend = &refs->main_backend;
} else {
int ret = backend_for_worktree(&backend, refs, wt->id);
if (ret < 0)
return error(_("reftable stack for worktree '%s' is broken"),
wt->id);
ret = backend_for_worktree(&backend, refs, wt->id);
if (ret < 0) {
ret = error(_("reftable stack for worktree '%s' is broken"),
wt->id);
goto out;
}
}
return reftable_fsck_check(backend->stack, reftable_fsck_error_handler,
reftable_fsck_verbose_handler, o);
errors |= reftable_fsck_check(backend->stack, reftable_fsck_error_handler,
reftable_fsck_verbose_handler, o);
iter = ref_iterator_for_stack(refs, backend->stack, "", NULL, 0);
if (!iter) {
ret = error(_("could not create iterator for worktree '%s'"), wt->id);
goto out;
}
while (1) {
ret = reftable_iterator_next_ref(&iter->iter, &ref);
if (ret > 0)
break;
if (ret < 0) {
ret = error(_("could not read record for worktree '%s'"), wt->id);
goto out;
}
strbuf_reset(&refname);
if (!is_main_worktree(wt))
strbuf_addf(&refname, "worktrees/%s/", wt->id);
strbuf_addstr(&refname, ref.refname);
report.path = refname.buf;
switch (ref.value_type) {
case REFTABLE_REF_VAL1:
case REFTABLE_REF_VAL2: {
struct object_id oid;
unsigned hash_id;
switch (reftable_stack_hash_id(backend->stack)) {
case REFTABLE_HASH_SHA1:
hash_id = GIT_HASH_SHA1;
break;
case REFTABLE_HASH_SHA256:
hash_id = GIT_HASH_SHA256;
break;
default:
BUG("unhandled hash ID %d",
reftable_stack_hash_id(backend->stack));
}
oidread(&oid, reftable_ref_record_val1(&ref),
&hash_algos[hash_id]);
errors |= refs_fsck_ref(ref_store, o, &report, ref.refname, &oid);
break;
}
case REFTABLE_REF_SYMREF:
errors |= refs_fsck_symref(ref_store, o, &report, ref.refname,
ref.value.symref);
break;
default:
BUG("unhandled reference value type %d", ref.value_type);
}
}
ret = errors ? -1 : 0;
out:
if (iter)
ref_iterator_free(&iter->base);
reftable_ref_record_release(&ref);
strbuf_release(&refname);
return ret;
}
struct ref_storage_be refs_be_reftable = {

View File

@ -87,4 +87,16 @@ test_expect_success 'worktree stacks can be verified' '
done
'
test_expect_success 'invalid symref gets reported' '
test_when_finished "rm -rf repo" &&
git init repo &&
test_commit -C repo initial &&
git -C repo symbolic-ref refs/heads/symref garbage &&
test_must_fail git -C repo refs verify 2>err &&
cat >expect <<-EOF &&
error: refs/heads/symref: badReferentName: points to invalid refname ${SQ}garbage${SQ}
EOF
test_cmp expect err
'
test_done