builtin/refs: get worktrees without reading head information

In "packed-backend.c", there are some functions such as "create_snapshot" and "next_record" which would check the correctness of the content of the "packed-ref" file. When anything is bad, the program will die. It may seem that we have nothing relevant to above feature, because we are going to read and parse the raw "packed-ref" file without creating the snapshot and using the ref iterator to check the consistency. However, when using "get_worktrees" in "builtin/refs", we would parse the "HEAD" information. If the referent of the "HEAD" is inside the "packed-ref", we will call "create_snapshot" function to parse the "packed-ref" to get the information. No matter whether the entry of "HEAD" in "packed-ref" is correct, "create_snapshot" would call "verify_buffer_safe" to check whether there is a newline in the last line of the file. If not, the program will die. Although this behavior has no harm for the program, it will short-circuit the program. When the users execute "git refs verify" or "git fsck", we should avoid reading the head information, which may execute the read operation in packed backend with stricter checks to die the program. Instead, we should continue to check other parts of the "packed-refs" file completely. Fortunately, in 465a22b338 (worktree: skip reading HEAD when repairing worktrees, 2023-12-29), we have introduced a function "get_worktrees_internal" which allows us to get worktrees without reading head information. Create a new exposed function "get_worktrees_without_reading_head", then replace the "get_worktrees" in "builtin/refs" with the new created function. Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: shejialuo <shejialuo@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

shejialuo committed Feb 28, 2025 at 00:06 UTC fdf3820b7ef69dcf887bd86565e2442f89edc7c0
3 files changed +14 -1
builtin/refs.c
+1 -1
@@ -88,7 +88,7 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
88 git_config(git_fsck_config, &fsck_refs_options);
89 prepare_repo_settings(the_repository);
90
91 - worktrees = get_worktrees();
91 + worktrees = get_worktrees_without_reading_head();
92 for (size_t i = 0; worktrees[i]; i++)
93 ret |= refs_fsck(get_worktree_ref_store(worktrees[i]),
94 &fsck_refs_options, worktrees[i]);
worktree.c
+5
@@ -175,6 +175,11 @@ struct worktree **get_worktrees(void)
175 return get_worktrees_internal(0);
176 }
177
178 +struct worktree **get_worktrees_without_reading_head(void)
179 +{
180 + return get_worktrees_internal(1);
181 +}
182 +
183 const char *get_worktree_git_dir(const struct worktree *wt)
184 {
185 if (!wt)
worktree.h
+8
@@ -30,6 +30,14 @@ struct worktree {
30 */
31 struct worktree **get_worktrees(void);
32
33 +/*
34 + * Like `get_worktrees`, but does not read HEAD. Skip reading HEAD allows to
35 + * get the worktree without worrying about failures pertaining to parsing
36 + * the HEAD ref. This is useful in contexts where it is assumed that the
37 + * refdb may not be in a consistent state.
38 + */
39 +struct worktree **get_worktrees_without_reading_head(void);
40 +
41 /*
42 * Returns 1 if linked worktrees exist, 0 otherwise.
43 */