ref: support multiple worktrees check for refs

We have already set up the infrastructure to check the consistency for refs, but we do not support multiple worktrees. However, "git-fsck(1)" will check the refs of worktrees. As we decide to get feature parity with "git-fsck(1)", we need to set up support for multiple worktrees. Because each worktree has its own specific refs, instead of just showing the users "refs/worktree/foo", we need to display the full name such as "worktrees/<id>/refs/worktree/foo". So we should know the id of the worktree to get the full name. Add a new parameter "struct worktree *" for "refs-internal.h::fsck_fn". Then change the related functions to follow this new interface. The "packed-refs" only exists in the main worktree, so we should only check "packed-refs" in the main worktree. Use "is_main_worktree" method to skip checking "packed-refs" in "packed_fsck" function. Then, enhance the "files-backend.c::files_fsck_refs_dir" function to add "worktree/<id>/" prefix when we are not in the main worktree. Last, add a new test to check the refname when there are multiple worktrees to exercise the code. 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 Nov 20, 2024 at 19:51 UTC 7c78d819e6a14434ae3fa8a2127bb3969376cac1
9 files changed +90 -15
builtin/refs.c
+8 -2
@@ -5,6 +5,7 @@
5 #include "parse-options.h"
6 #include "refs.h"
7 #include "strbuf.h"
8 +#include "worktree.h"
9
10 #define REFS_MIGRATE_USAGE \
11 N_("git refs migrate --ref-format=<format> [--dry-run]")
@@ -66,6 +67,7 @@ out:
67 static int cmd_refs_verify(int argc, const char **argv, const char *prefix)
68 {
69 struct fsck_options fsck_refs_options = FSCK_REFS_OPTIONS_DEFAULT;
70 + struct worktree **worktrees;
71 const char * const verify_usage[] = {
72 REFS_VERIFY_USAGE,
73 NULL,
@@ -75,7 +77,7 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix)
77 OPT_BOOL(0, "strict", &fsck_refs_options.strict, N_("enable strict checking")),
78 OPT_END(),
79 };
78 - int ret;
80 + int ret = 0;
81
82 argc = parse_options(argc, argv, prefix, options, verify_usage, 0);
83 if (argc)
@@ -84,9 +86,13 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix)
86 git_config(git_fsck_config, &fsck_refs_options);
87 prepare_repo_settings(the_repository);
88
87 - ret = refs_fsck(get_main_ref_store(the_repository), &fsck_refs_options);
89 + worktrees = get_worktrees();
90 + for (size_t i = 0; worktrees[i]; i++)
91 + ret |= refs_fsck(get_worktree_ref_store(worktrees[i]),
92 + &fsck_refs_options, worktrees[i]);
93
94 fsck_options_clear(&fsck_refs_options);
95 + free_worktrees(worktrees);
96 return ret;
97 }
98
refs.c
+3 -2
@@ -318,9 +318,10 @@ int check_refname_format(const char *refname, int flags)
318 return check_or_sanitize_refname(refname, flags, NULL);
319 }
320
321 -int refs_fsck(struct ref_store *refs, struct fsck_options *o)
321 +int refs_fsck(struct ref_store *refs, struct fsck_options *o,
322 + struct worktree *wt)
323 {
323 - return refs->be->fsck(refs, o);
324 + return refs->be->fsck(refs, o, wt);
325 }
326
327 void sanitize_refname_component(const char *refname, struct strbuf *out)
refs.h
+2 -1
@@ -549,7 +549,8 @@ int check_refname_format(const char *refname, int flags);
549 * reflogs are consistent, and non-zero otherwise. The errors will be
550 * written to stderr.
551 */
552 -int refs_fsck(struct ref_store *refs, struct fsck_options *o);
552 +int refs_fsck(struct ref_store *refs, struct fsck_options *o,
553 + struct worktree *wt);
554
555 /*
556 * Apply the rules from check_refname_format, but mutate the result until it
refs/debug.c
+3 -2
@@ -420,10 +420,11 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname,
420 }
421
422 static int debug_fsck(struct ref_store *ref_store,
423 - struct fsck_options *o)
423 + struct fsck_options *o,
424 + struct worktree *wt)
425 {
426 struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
426 - int res = drefs->refs->be->fsck(drefs->refs, o);
427 + int res = drefs->refs->be->fsck(drefs->refs, o, wt);
428 trace_printf_key(&trace_refs, "fsck: %d\n", res);
429 return res;
430 }
refs/files-backend.c
+12 -5
@@ -23,6 +23,7 @@
23 #include "../dir.h"
24 #include "../chdir-notify.h"
25 #include "../setup.h"
26 +#include "../worktree.h"
27 #include "../wrapper.h"
28 #include "../write-or-die.h"
29 #include "../revision.h"
@@ -3539,6 +3540,7 @@ cleanup:
3540 static int files_fsck_refs_dir(struct ref_store *ref_store,
3541 struct fsck_options *o,
3542 const char *refs_check_dir,
3543 + struct worktree *wt,
3544 files_fsck_refs_fn *fsck_refs_fn)
3545 {
3546 struct strbuf refname = STRBUF_INIT;
@@ -3561,6 +3563,9 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
3563 } else if (S_ISREG(iter->st.st_mode) ||
3564 S_ISLNK(iter->st.st_mode)) {
3565 strbuf_reset(&refname);
3566 +
3567 + if (!is_main_worktree(wt))
3568 + strbuf_addf(&refname, "worktrees/%s/", wt->id);
3569 strbuf_addf(&refname, "%s/%s", refs_check_dir,
3570 iter->relative_path);
3571
@@ -3590,7 +3595,8 @@ out:
3595 }
3596
3597 static int files_fsck_refs(struct ref_store *ref_store,
3593 - struct fsck_options *o)
3598 + struct fsck_options *o,
3599 + struct worktree *wt)
3600 {
3601 files_fsck_refs_fn fsck_refs_fn[]= {
3602 files_fsck_refs_name,
@@ -3599,17 +3605,18 @@ static int files_fsck_refs(struct ref_store *ref_store,
3605
3606 if (o->verbose)
3607 fprintf_ln(stderr, _("Checking references consistency"));
3602 - return files_fsck_refs_dir(ref_store, o, "refs", fsck_refs_fn);
3608 + return files_fsck_refs_dir(ref_store, o, "refs", wt, fsck_refs_fn);
3609 }
3610
3611 static int files_fsck(struct ref_store *ref_store,
3606 - struct fsck_options *o)
3612 + struct fsck_options *o,
3613 + struct worktree *wt)
3614 {
3615 struct files_ref_store *refs =
3616 files_downcast(ref_store, REF_STORE_READ, "fsck");
3617
3611 - return files_fsck_refs(ref_store, o) |
3612 - refs->packed_ref_store->be->fsck(refs->packed_ref_store, o);
3618 + return files_fsck_refs(ref_store, o, wt) |
3619 + refs->packed_ref_store->be->fsck(refs->packed_ref_store, o, wt);
3620 }
3621
3622 struct ref_storage_be refs_be_files = {
refs/packed-backend.c
+7 -1
@@ -13,6 +13,7 @@
13 #include "../lockfile.h"
14 #include "../chdir-notify.h"
15 #include "../statinfo.h"
16 +#include "../worktree.h"
17 #include "../wrapper.h"
18 #include "../write-or-die.h"
19 #include "../trace2.h"
@@ -1754,8 +1755,13 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
1755 }
1756
1757 static int packed_fsck(struct ref_store *ref_store UNUSED,
1757 - struct fsck_options *o UNUSED)
1758 + struct fsck_options *o UNUSED,
1759 + struct worktree *wt)
1760 {
1761 +
1762 + if (!is_main_worktree(wt))
1763 + return 0;
1764 +
1765 return 0;
1766 }
1767
refs/refs-internal.h
+2 -1
@@ -653,7 +653,8 @@ typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refnam
653 struct strbuf *referent);
654
655 typedef int fsck_fn(struct ref_store *ref_store,
656 - struct fsck_options *o);
656 + struct fsck_options *o,
657 + struct worktree *wt);
658
659 struct ref_storage_be {
660 const char *name;
refs/reftable-backend.c
+2 -1
@@ -2475,7 +2475,8 @@ done:
2475 }
2476
2477 static int reftable_be_fsck(struct ref_store *ref_store UNUSED,
2478 - struct fsck_options *o UNUSED)
2478 + struct fsck_options *o UNUSED,
2479 + struct worktree *wt UNUSED)
2480 {
2481 return 0;
2482 }
t/t0602-reffiles-fsck.sh
+51
@@ -107,4 +107,55 @@ test_expect_success 'ref name check should be adapted into fsck messages' '
107 test_must_be_empty err
108 '
109
110 +test_expect_success 'ref name check should work for multiple worktrees' '
111 + test_when_finished "rm -rf repo" &&
112 + git init repo &&
113 +
114 + cd repo &&
115 + test_commit initial &&
116 + git checkout -b branch-1 &&
117 + test_commit second &&
118 + git checkout -b branch-2 &&
119 + test_commit third &&
120 + git checkout -b branch-3 &&
121 + git worktree add ./worktree-1 branch-1 &&
122 + git worktree add ./worktree-2 branch-2 &&
123 + worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
124 + worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
125 +
126 + (
127 + cd worktree-1 &&
128 + git update-ref refs/worktree/branch-4 refs/heads/branch-3
129 + ) &&
130 + (
131 + cd worktree-2 &&
132 + git update-ref refs/worktree/branch-4 refs/heads/branch-3
133 + ) &&
134 +
135 + cp $worktree1_refdir_prefix/branch-4 $worktree1_refdir_prefix/'\'' branch-5'\'' &&
136 + cp $worktree2_refdir_prefix/branch-4 $worktree2_refdir_prefix/'\''~branch-6'\'' &&
137 +
138 + test_must_fail git refs verify 2>err &&
139 + cat >expect <<-EOF &&
140 + error: worktrees/worktree-1/refs/worktree/ branch-5: badRefName: invalid refname format
141 + error: worktrees/worktree-2/refs/worktree/~branch-6: badRefName: invalid refname format
142 + EOF
143 + sort err >sorted_err &&
144 + test_cmp expect sorted_err &&
145 +
146 + for worktree in "worktree-1" "worktree-2"
147 + do
148 + (
149 + cd $worktree &&
150 + test_must_fail git refs verify 2>err &&
151 + cat >expect <<-EOF &&
152 + error: worktrees/worktree-1/refs/worktree/ branch-5: badRefName: invalid refname format
153 + error: worktrees/worktree-2/refs/worktree/~branch-6: badRefName: invalid refname format
154 + EOF
155 + sort err >sorted_err &&
156 + test_cmp expect sorted_err || return 1
157 + )
158 + done
159 +'
160 +
161 test_done