]> git.ipfire.org Git - thirdparty/git.git/commitdiff
refs/reftable: introduce generic checks for refs
authorPatrick Steinhardt <ps@pks.im>
Mon, 12 Jan 2026 09:03:03 +0000 (10:03 +0100)
committerJunio C Hamano <gitster@pobox.com>
Mon, 12 Jan 2026 14:55:41 +0000 (06:55 -0800)
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>
refs/reftable-backend.c
t/t0614-reftable-fsck.sh

index 6361b270155e38d8c8b8373dad598d8c5d7b0f92..fe74af73afdb7ab891bfad902e665537d9696d83 100644 (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;
+               }
+       }
+
+       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);
+               }
        }
 
-       return reftable_fsck_check(backend->stack, reftable_fsck_error_handler,
-                                  reftable_fsck_verbose_handler, o);
+       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 = {
index 4757eb5931dd5af0defb276b32349901113daa5a..d24b87f96119750a06b1113ee41978e7a56b8d9d 100755 (executable)
@@ -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