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>

Karthik Nayak committed May 17, 2026 at 19:32 UTC e0fcba2d9cf86ba45943066924f111006d55ba08
2 files changed +52 -11
refs/files-backend.c
+11 -11
@@ -3864,22 +3864,12 @@ cleanup:
3864 static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
3865 struct fsck_options *o,
3866 const char *refname,
3867 - const char *path,
3867 + const char *path UNUSED,
3868 int mode UNUSED)
3869 {
3870 struct strbuf sb = STRBUF_INIT;
3871 - const char *filename;
3871 int ret = 0;
3872
3874 - filename = basename((char *) path);
3875 -
3876 - /*
3877 - * Ignore the files ending with ".lock" as they may be lock files
3878 - * However, do not allow bare ".lock" files.
3879 - */
3880 - if (filename[0] != '.' && ends_with(filename, ".lock"))
3881 - goto cleanup;
3882 -
3873 if (is_root_ref(refname))
3874 goto cleanup;
3875
@@ -3939,6 +3929,7 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
3929 struct strbuf refname = STRBUF_INIT;
3930 struct strbuf sb = STRBUF_INIT;
3931 struct dir_iterator *iter;
3932 + const char *filename;
3933 int iter_status;
3934 int ret = 0;
3935
@@ -3962,6 +3953,15 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
3953 strbuf_addf(&refname, "worktrees/%s/", wt->id);
3954 strbuf_addf(&refname, "refs/%s", iter->relative_path);
3955
3956 + filename = basename((char *) iter->path.buf);
3957 +
3958 + /*
3959 + * Ignore the files ending with ".lock" as they may be lock files.
3960 + * However, do not skip invalid refnames with '.lock' suffix.
3961 + */
3962 + if (filename[0] != '.' && ends_with(filename, ".lock"))
3963 + continue;
3964 +
3965 if (files_fsck_ref(ref_store, o, refname.buf,
3966 iter->path.buf, iter->st.st_mode) < 0)
3967 ret = -1;
t/t0602-reffiles-fsck.sh
+41
@@ -87,6 +87,47 @@ test_expect_success 'ref name should be checked' '
87 )
88 '
89
90 +test_expect_success 'lock files should be ignored' '
91 + test_when_finished "rm -rf repo" &&
92 + git init repo &&
93 + (
94 + cd repo &&
95 + git commit --allow-empty -m initial &&
96 + git checkout -b branch-1 &&
97 +
98 + touch .git/refs/heads/branch-1.lock &&
99 + git refs verify 2>err &&
100 + test_must_be_empty err &&
101 +
102 + echo "foobar" >.git/refs/heads/branch-2 &&
103 + test_must_fail git refs verify 2>err &&
104 + cat >expect <<-EOF &&
105 + error: refs/heads/branch-2: badRefContent: foobar
106 + EOF
107 + test_cmp expect err
108 + )
109 +'
110 +
111 +test_expect_success 'bare lock files should not be ignored' '
112 + test_when_finished "rm -rf repo" &&
113 + git init repo &&
114 + (
115 + cd repo &&
116 + git commit --allow-empty -m initial &&
117 + git checkout -b branch-1 &&
118 +
119 + # invalid refname should be reported
120 + cp .git/refs/heads/branch-1 .git/refs/heads/.branch-1.lock &&
121 + # invalid refname and content should be reported
122 + touch .git/refs/heads/.lock &&
123 +
124 + test_must_fail git refs verify 2>err &&
125 + test_grep "error: refs/heads/.branch-1.lock: badRefName: invalid refname format" err &&
126 + test_grep "error: refs/heads/.lock: badRefName: invalid refname format" err &&
127 + test_grep "error: refs/heads/.lock: badRefContent: " err
128 + )
129 +'
130 +
131 test_expect_success 'ref name check should be adapted into fsck messages' '
132 test_when_finished "rm -rf repo" &&
133 git init repo &&