diff --git a/refs/files-backend.c b/refs/files-backend.c index abc2165339..0ff047d0df 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,65 @@ 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; + bool errors_found; +}; + +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 || + data.errors_found) + 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