refs/files: skip lock files during consistency checks

Consistency checks in the files reference backend involve two steps:

1. Iterate over all entries within the 'refs/' directory and call
`files_fsck_ref()` on each.
2. Iterate over all root refs via `for_each_root_ref()` and call
`files_fsck_ref()` on each.

`files_fsck_ref()` then runs all fsck checks defined in
`fsck_refs_fn[]`. Step 2 goes through the refs API and only sees valid
refs, but step 1 iterates the directory directly and may also encounter
intermediate '*.lock' files.

Currently, `files_fsck_refs_name()`, one of the functions in
`fsck_refs_fn[]`, filters out lock files itself. The other function,
`files_fsck_refs_content()`, has no such check and would parse the lock
file. Any new function added to `fsck_refs_fn[]` would have the same
problem.

Move the filter up into `files_fsck_refs_dir()`, where the directory
iteration happens. Since step 2 cannot produce lock files, this is the
only site where the filter is needed, and individual checks no longer
have to re-implement it.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
main
Karthik Nayak 2026-05-17 19:32:05 +02:00 committed by Junio C Hamano
parent 94f057755b
commit e0fcba2d9c
2 changed files with 52 additions and 11 deletions

View File

@ -3864,22 +3864,12 @@ cleanup:
static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
struct fsck_options *o,
const char *refname,
const char *path,
const char *path UNUSED,
int mode UNUSED)
{
struct strbuf sb = STRBUF_INIT;
const char *filename;
int ret = 0;

filename = basename((char *) path);

/*
* Ignore the files ending with ".lock" as they may be lock files
* However, do not allow bare ".lock" files.
*/
if (filename[0] != '.' && ends_with(filename, ".lock"))
goto cleanup;

if (is_root_ref(refname))
goto cleanup;

@ -3939,6 +3929,7 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
struct strbuf refname = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT;
struct dir_iterator *iter;
const char *filename;
int iter_status;
int ret = 0;

@ -3962,6 +3953,15 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
strbuf_addf(&refname, "worktrees/%s/", wt->id);
strbuf_addf(&refname, "refs/%s", iter->relative_path);

filename = basename((char *) iter->path.buf);

/*
* Ignore the files ending with ".lock" as they may be lock files.
* However, do not skip invalid refnames with '.lock' suffix.
*/
if (filename[0] != '.' && ends_with(filename, ".lock"))
continue;

if (files_fsck_ref(ref_store, o, refname.buf,
iter->path.buf, iter->st.st_mode) < 0)
ret = -1;

View File

@ -87,6 +87,47 @@ test_expect_success 'ref name should be checked' '
)
'

test_expect_success 'lock files should be ignored' '
test_when_finished "rm -rf repo" &&
git init repo &&
(
cd repo &&
git commit --allow-empty -m initial &&
git checkout -b branch-1 &&

touch .git/refs/heads/branch-1.lock &&
git refs verify 2>err &&
test_must_be_empty err &&

echo "foobar" >.git/refs/heads/branch-2 &&
test_must_fail git refs verify 2>err &&
cat >expect <<-EOF &&
error: refs/heads/branch-2: badRefContent: foobar
EOF
test_cmp expect err
)
'

test_expect_success 'bare lock files should not be ignored' '
test_when_finished "rm -rf repo" &&
git init repo &&
(
cd repo &&
git commit --allow-empty -m initial &&
git checkout -b branch-1 &&

# invalid refname should be reported
cp .git/refs/heads/branch-1 .git/refs/heads/.branch-1.lock &&
# invalid refname and content should be reported
touch .git/refs/heads/.lock &&

test_must_fail git refs verify 2>err &&
test_grep "error: refs/heads/.branch-1.lock: badRefName: invalid refname format" err &&
test_grep "error: refs/heads/.lock: badRefName: invalid refname format" err &&
test_grep "error: refs/heads/.lock: badRefContent: " err
)
'

test_expect_success 'ref name check should be adapted into fsck messages' '
test_when_finished "rm -rf repo" &&
git init repo &&