summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2026-01-12 10:02:56 +0100
committerJunio C Hamano <gitster@pobox.com>2026-01-12 06:55:40 -0800
commit7b8c36a2a74868b6fa47e3b11e2c1c0f89c88d43 (patch)
tree3ebc7b1cab06e6ab502e9fef3a2f868b3069bf09
parent9ebccf744a967f399579a3f3ffbeb40120f5a1e1 (diff)
downloadgit-7b8c36a2a74868b6fa47e3b11e2c1c0f89c88d43.tar.xz
refs/files: perform consistency checks for root refs
While the "files" backend already knows to perform consistency checks for the "refs/" hierarchy, it doesn't verify any of its root refs. Plug this omission. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r--refs/files-backend.c50
-rwxr-xr-xt/t0602-reffiles-fsck.sh30
2 files changed, 77 insertions, 3 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c
index abc2165339..9ae80b700a 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3877,9 +3877,9 @@ static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
if (filename[0] != '.' && ends_with(filename, ".lock"))
goto cleanup;
- /*
- * This works right now because we never check the root refs.
- */
+ if (is_root_ref(refname))
+ goto cleanup;
+
if (check_refname_format(refname, 0)) {
struct fsck_ref_report report = { 0 };
@@ -3974,19 +3974,63 @@ out:
return ret;
}
+struct files_fsck_root_ref_data {
+ struct files_ref_store *refs;
+ struct fsck_options *o;
+ struct worktree *wt;
+ struct strbuf refname;
+ struct strbuf path;
+};
+
+static int files_fsck_root_ref(const char *refname, void *cb_data)
+{
+ struct files_fsck_root_ref_data *data = cb_data;
+ struct stat st;
+
+ strbuf_reset(&data->refname);
+ if (!is_main_worktree(data->wt))
+ strbuf_addf(&data->refname, "worktrees/%s/", data->wt->id);
+ strbuf_addstr(&data->refname, refname);
+
+ strbuf_reset(&data->path);
+ strbuf_addf(&data->path, "%s/%s", data->refs->gitcommondir, data->refname.buf);
+
+ if (stat(data->path.buf, &st)) {
+ if (errno == ENOENT)
+ return 0;
+ return error_errno("failed to read ref: '%s'", data->path.buf);
+ }
+
+ return files_fsck_ref(&data->refs->base, data->o, data->refname.buf,
+ data->path.buf, st.st_mode);
+}
+
static int files_fsck(struct ref_store *ref_store,
struct fsck_options *o,
struct worktree *wt)
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_READ, "fsck");
+ struct files_fsck_root_ref_data data = {
+ .refs = refs,
+ .o = o,
+ .wt = wt,
+ .refname = STRBUF_INIT,
+ .path = STRBUF_INIT,
+ };
int ret = 0;
if (files_fsck_refs_dir(ref_store, o, wt) < 0)
ret = -1;
+
+ if (for_each_root_ref(refs, files_fsck_root_ref, &data) < 0)
+ ret = -1;
+
if (refs->packed_ref_store->be->fsck(refs->packed_ref_store, o, wt) < 0)
ret = -1;
+ strbuf_release(&data.refname);
+ strbuf_release(&data.path);
return ret;
}
diff --git a/t/t0602-reffiles-fsck.sh b/t/t0602-reffiles-fsck.sh
index 0ef483659d..479f3d528e 100755
--- a/t/t0602-reffiles-fsck.sh
+++ b/t/t0602-reffiles-fsck.sh
@@ -905,4 +905,34 @@ test_expect_success '--[no-]references option should apply to fsck' '
)
'
+test_expect_success 'complains about broken root ref' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ echo "ref: refs/../HEAD" >.git/HEAD &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: HEAD: badReferentName: points to invalid refname ${SQ}refs/../HEAD${SQ}
+ EOF
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'complains about broken root ref in worktree' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ git worktree add ../worktree &&
+ echo "ref: refs/../HEAD" >.git/worktrees/worktree/HEAD &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: worktrees/worktree/HEAD: badReferentName: points to invalid refname ${SQ}refs/../HEAD${SQ}
+ EOF
+ test_cmp expect err
+ )
+'
+
test_done