refs/files: extract function to check single ref

When checking the consistency of references we create a directory iterator and then verify each single reference in a loop. The logic to perform the actual checks is embedded into that loop, which makes it hard to reuse. But In a subsequent commit we're about to introduce a second path that wants to verify references. Prepare for this by extracting the logic to check a single reference into a standalone function. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jan 12, 2026 at 10:02 UTC 0ff9cf40b2ce3df2f6eda0875eb54fe3c3487f5b
1 file changed +51 -29
refs/files-backend.c
+51 -29
@@ -3715,7 +3715,8 @@ static int files_ref_store_remove_on_disk(struct ref_store *ref_store,
3715 typedef int (*files_fsck_refs_fn)(struct ref_store *ref_store,
3716 struct fsck_options *o,
3717 const char *refname,
3718 - struct dir_iterator *iter);
3718 + const char *path,
3719 + int mode);
3720
3721 static int files_fsck_symref_target(struct fsck_options *o,
3722 struct fsck_ref_report *report,
@@ -3772,7 +3773,8 @@ out:
3773 static int files_fsck_refs_content(struct ref_store *ref_store,
3774 struct fsck_options *o,
3775 const char *target_name,
3775 - struct dir_iterator *iter)
3776 + const char *path,
3777 + int mode)
3778 {
3779 struct strbuf ref_content = STRBUF_INIT;
3780 struct strbuf abs_gitdir = STRBUF_INIT;
@@ -3786,7 +3788,7 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
3788
3789 report.path = target_name;
3790
3789 - if (S_ISLNK(iter->st.st_mode)) {
3791 + if (S_ISLNK(mode)) {
3792 const char *relative_referent_path = NULL;
3793
3794 ret = fsck_report_ref(o, &report,
@@ -3798,7 +3800,7 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
3800 if (!is_dir_sep(abs_gitdir.buf[abs_gitdir.len - 1]))
3801 strbuf_addch(&abs_gitdir, '/');
3802
3801 - strbuf_add_real_path(&ref_content, iter->path.buf);
3803 + strbuf_add_real_path(&ref_content, path);
3804 skip_prefix(ref_content.buf, abs_gitdir.buf,
3805 &relative_referent_path);
3806
@@ -3811,7 +3813,7 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
3813 goto cleanup;
3814 }
3815
3814 - if (strbuf_read_file(&ref_content, iter->path.buf, 0) < 0) {
3816 + if (strbuf_read_file(&ref_content, path, 0) < 0) {
3817 /*
3818 * Ref file could be removed by another concurrent process. We should
3819 * ignore this error and continue to the next ref.
@@ -3819,7 +3821,7 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
3821 if (errno == ENOENT)
3822 goto cleanup;
3823
3822 - ret = error_errno(_("cannot read ref file '%s'"), iter->path.buf);
3824 + ret = error_errno(_("cannot read ref file '%s'"), path);
3825 goto cleanup;
3826 }
3827
@@ -3861,16 +3863,20 @@ cleanup:
3863 static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
3864 struct fsck_options *o,
3865 const char *refname,
3864 - struct dir_iterator *iter)
3866 + const char *path,
3867 + int mode UNUSED)
3868 {
3869 struct strbuf sb = STRBUF_INIT;
3870 + const char *filename;
3871 int ret = 0;
3872
3873 + filename = basename((char *) path);
3874 +
3875 /*
3876 * Ignore the files ending with ".lock" as they may be lock files
3877 * However, do not allow bare ".lock" files.
3878 */
3873 - if (iter->basename[0] != '.' && ends_with(iter->basename, ".lock"))
3879 + if (filename[0] != '.' && ends_with(filename, ".lock"))
3880 goto cleanup;
3881
3882 /*
@@ -3896,6 +3902,35 @@ static const files_fsck_refs_fn fsck_refs_fn[]= {
3902 NULL,
3903 };
3904
3905 +static int files_fsck_ref(struct ref_store *ref_store,
3906 + struct fsck_options *o,
3907 + const char *refname,
3908 + const char *path,
3909 + int mode)
3910 +{
3911 + int ret = 0;
3912 +
3913 + if (o->verbose)
3914 + fprintf_ln(stderr, "Checking %s", refname);
3915 +
3916 + if (!S_ISREG(mode) && !S_ISLNK(mode)) {
3917 + struct fsck_ref_report report = { .path = refname };
3918 +
3919 + if (fsck_report_ref(o, &report,
3920 + FSCK_MSG_BAD_REF_FILETYPE,
3921 + "unexpected file type"))
3922 + ret = -1;
3923 + goto out;
3924 + }
3925 +
3926 + for (size_t i = 0; fsck_refs_fn[i]; i++)
3927 + if (fsck_refs_fn[i](ref_store, o, refname, path, mode))
3928 + ret = -1;
3929 +
3930 +out:
3931 + return ret;
3932 +}
3933 +
3934 static int files_fsck_refs_dir(struct ref_store *ref_store,
3935 struct fsck_options *o,
3936 struct worktree *wt)
@@ -3918,30 +3953,17 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
3953 }
3954
3955 while ((iter_status = dir_iterator_advance(iter)) == ITER_OK) {
3921 - if (S_ISDIR(iter->st.st_mode)) {
3956 + if (S_ISDIR(iter->st.st_mode))
3957 continue;
3923 - } else if (S_ISREG(iter->st.st_mode) ||
3924 - S_ISLNK(iter->st.st_mode)) {
3925 - strbuf_reset(&refname);
3926 -
3927 - if (!is_main_worktree(wt))
3928 - strbuf_addf(&refname, "worktrees/%s/", wt->id);
3929 - strbuf_addf(&refname, "refs/%s", iter->relative_path);
3958
3931 - if (o->verbose)
3932 - fprintf_ln(stderr, "Checking %s", refname.buf);
3959 + strbuf_reset(&refname);
3960 + if (!is_main_worktree(wt))
3961 + strbuf_addf(&refname, "worktrees/%s/", wt->id);
3962 + strbuf_addf(&refname, "refs/%s", iter->relative_path);
3963
3934 - for (size_t i = 0; fsck_refs_fn[i]; i++) {
3935 - if (fsck_refs_fn[i](ref_store, o, refname.buf, iter))
3936 - ret = -1;
3937 - }
3938 - } else {
3939 - struct fsck_ref_report report = { .path = iter->basename };
3940 - if (fsck_report_ref(o, &report,
3941 - FSCK_MSG_BAD_REF_FILETYPE,
3942 - "unexpected file type"))
3943 - ret = -1;
3944 - }
3964 + if (files_fsck_ref(ref_store, o, refname.buf,
3965 + iter->path.buf, iter->st.st_mode) < 0)
3966 + ret = -1;
3967 }
3968
3969 if (iter_status != ITER_DONE)