]> git.ipfire.org Git - thirdparty/git.git/commitdiff
ref: support multiple worktrees check for refs
authorshejialuo <shejialuo@gmail.com>
Wed, 20 Nov 2024 11:51:32 +0000 (19:51 +0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 20 Nov 2024 23:21:32 +0000 (08:21 +0900)
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>
builtin/refs.c
refs.c
refs.h
refs/debug.c
refs/files-backend.c
refs/packed-backend.c
refs/refs-internal.h
refs/reftable-backend.c
t/t0602-reffiles-fsck.sh

index 24978a7b7b081ac6ed8ca99016f201d34c0639f8..394b4101c65d8f5b49541a195d6d71a6e9f5581f 100644 (file)
@@ -5,6 +5,7 @@
 #include "parse-options.h"
 #include "refs.h"
 #include "strbuf.h"
+#include "worktree.h"
 
 #define REFS_MIGRATE_USAGE \
        N_("git refs migrate --ref-format=<format> [--dry-run]")
@@ -66,6 +67,7 @@ out:
 static int cmd_refs_verify(int argc, const char **argv, const char *prefix)
 {
        struct fsck_options fsck_refs_options = FSCK_REFS_OPTIONS_DEFAULT;
+       struct worktree **worktrees;
        const char * const verify_usage[] = {
                REFS_VERIFY_USAGE,
                NULL,
@@ -75,7 +77,7 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "strict", &fsck_refs_options.strict, N_("enable strict checking")),
                OPT_END(),
        };
-       int ret;
+       int ret = 0;
 
        argc = parse_options(argc, argv, prefix, options, verify_usage, 0);
        if (argc)
@@ -84,9 +86,13 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix)
        git_config(git_fsck_config, &fsck_refs_options);
        prepare_repo_settings(the_repository);
 
-       ret = refs_fsck(get_main_ref_store(the_repository), &fsck_refs_options);
+       worktrees = get_worktrees();
+       for (size_t i = 0; worktrees[i]; i++)
+               ret |= refs_fsck(get_worktree_ref_store(worktrees[i]),
+                                &fsck_refs_options, worktrees[i]);
 
        fsck_options_clear(&fsck_refs_options);
+       free_worktrees(worktrees);
        return ret;
 }
 
diff --git a/refs.c b/refs.c
index 5f729ed4124f7fe8fa9df7fd1f1ce951abefc585..395a17273ccb014311dc41032a31389187ce7b4b 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -318,9 +318,10 @@ int check_refname_format(const char *refname, int flags)
        return check_or_sanitize_refname(refname, flags, NULL);
 }
 
-int refs_fsck(struct ref_store *refs, struct fsck_options *o)
+int refs_fsck(struct ref_store *refs, struct fsck_options *o,
+             struct worktree *wt)
 {
-       return refs->be->fsck(refs, o);
+       return refs->be->fsck(refs, o, wt);
 }
 
 void sanitize_refname_component(const char *refname, struct strbuf *out)
diff --git a/refs.h b/refs.h
index 108dfc93b3428db491916ad8a55daea649d83ffd..341d43239c17e2d708ca0d1e4d41174e52f9e946 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -549,7 +549,8 @@ int check_refname_format(const char *refname, int flags);
  * reflogs are consistent, and non-zero otherwise. The errors will be
  * written to stderr.
  */
-int refs_fsck(struct ref_store *refs, struct fsck_options *o);
+int refs_fsck(struct ref_store *refs, struct fsck_options *o,
+             struct worktree *wt);
 
 /*
  * Apply the rules from check_refname_format, but mutate the result until it
index 45e2e784a0f8c49f492eaa9d371aa44f8c7916c3..72e80ddd6d20f5620a63626d445651d6fb6fed28 100644 (file)
@@ -420,10 +420,11 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname,
 }
 
 static int debug_fsck(struct ref_store *ref_store,
-                     struct fsck_options *o)
+                     struct fsck_options *o,
+                     struct worktree *wt)
 {
        struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
-       int res = drefs->refs->be->fsck(drefs->refs, o);
+       int res = drefs->refs->be->fsck(drefs->refs, o, wt);
        trace_printf_key(&trace_refs, "fsck: %d\n", res);
        return res;
 }
index 8edb700568499d278b1edc5915e786c25aec6fd4..8bfdce64bc6df267624982d0ca195925e10fb8ba 100644 (file)
@@ -23,6 +23,7 @@
 #include "../dir.h"
 #include "../chdir-notify.h"
 #include "../setup.h"
+#include "../worktree.h"
 #include "../wrapper.h"
 #include "../write-or-die.h"
 #include "../revision.h"
@@ -3539,6 +3540,7 @@ cleanup:
 static int files_fsck_refs_dir(struct ref_store *ref_store,
                               struct fsck_options *o,
                               const char *refs_check_dir,
+                              struct worktree *wt,
                               files_fsck_refs_fn *fsck_refs_fn)
 {
        struct strbuf refname = STRBUF_INIT;
@@ -3561,6 +3563,9 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
                } else if (S_ISREG(iter->st.st_mode) ||
                           S_ISLNK(iter->st.st_mode)) {
                        strbuf_reset(&refname);
+
+                       if (!is_main_worktree(wt))
+                               strbuf_addf(&refname, "worktrees/%s/", wt->id);
                        strbuf_addf(&refname, "%s/%s", refs_check_dir,
                                    iter->relative_path);
 
@@ -3590,7 +3595,8 @@ out:
 }
 
 static int files_fsck_refs(struct ref_store *ref_store,
-                          struct fsck_options *o)
+                          struct fsck_options *o,
+                          struct worktree *wt)
 {
        files_fsck_refs_fn fsck_refs_fn[]= {
                files_fsck_refs_name,
@@ -3599,17 +3605,18 @@ static int files_fsck_refs(struct ref_store *ref_store,
 
        if (o->verbose)
                fprintf_ln(stderr, _("Checking references consistency"));
-       return files_fsck_refs_dir(ref_store, o,  "refs", fsck_refs_fn);
+       return files_fsck_refs_dir(ref_store, o, "refs", wt, fsck_refs_fn);
 }
 
 static int files_fsck(struct ref_store *ref_store,
-                     struct fsck_options *o)
+                     struct fsck_options *o,
+                     struct worktree *wt)
 {
        struct files_ref_store *refs =
                files_downcast(ref_store, REF_STORE_READ, "fsck");
 
-       return files_fsck_refs(ref_store, o) |
-              refs->packed_ref_store->be->fsck(refs->packed_ref_store, o);
+       return files_fsck_refs(ref_store, o, wt) |
+              refs->packed_ref_store->be->fsck(refs->packed_ref_store, o, wt);
 }
 
 struct ref_storage_be refs_be_files = {
index 07c57fd541a5039d5fcb93d9bf78e1916f67b219..46dcaec6540528bcd37bc3907fcadce4b4430e11 100644 (file)
@@ -13,6 +13,7 @@
 #include "../lockfile.h"
 #include "../chdir-notify.h"
 #include "../statinfo.h"
+#include "../worktree.h"
 #include "../wrapper.h"
 #include "../write-or-die.h"
 #include "../trace2.h"
@@ -1754,8 +1755,13 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
 }
 
 static int packed_fsck(struct ref_store *ref_store UNUSED,
-                      struct fsck_options *o UNUSED)
+                      struct fsck_options *o UNUSED,
+                      struct worktree *wt)
 {
+
+       if (!is_main_worktree(wt))
+               return 0;
+
        return 0;
 }
 
index 2313c830d8facaa17b0b4b073df0de958023062a..037d7991cdd8d09c3ad51003b4abc39fee9c89d4 100644 (file)
@@ -653,7 +653,8 @@ typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refnam
                                 struct strbuf *referent);
 
 typedef int fsck_fn(struct ref_store *ref_store,
-                   struct fsck_options *o);
+                   struct fsck_options *o,
+                   struct worktree *wt);
 
 struct ref_storage_be {
        const char *name;
index 3c6107c7ce5380bbbb526e835979ba9289b7dd22..ea054ec6e9dcde87101f7d2dbe907540b3df8a00 100644 (file)
@@ -2475,7 +2475,8 @@ done:
 }
 
 static int reftable_be_fsck(struct ref_store *ref_store UNUSED,
-                           struct fsck_options *o UNUSED)
+                           struct fsck_options *o UNUSED,
+                           struct worktree *wt UNUSED)
 {
        return 0;
 }
index 2a172c913d26da33f318e54efe4f84ad0ec52493..1e17393a3dbe33e032da860e6bff3292c6c19d77 100755 (executable)
@@ -107,4 +107,55 @@ test_expect_success 'ref name check should be adapted into fsck messages' '
        test_must_be_empty err
 '
 
+test_expect_success 'ref name check should work for multiple worktrees' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+
+       cd repo &&
+       test_commit initial &&
+       git checkout -b branch-1 &&
+       test_commit second &&
+       git checkout -b branch-2 &&
+       test_commit third &&
+       git checkout -b branch-3 &&
+       git worktree add ./worktree-1 branch-1 &&
+       git worktree add ./worktree-2 branch-2 &&
+       worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
+       worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
+
+       (
+               cd worktree-1 &&
+               git update-ref refs/worktree/branch-4 refs/heads/branch-3
+       ) &&
+       (
+               cd worktree-2 &&
+               git update-ref refs/worktree/branch-4 refs/heads/branch-3
+       ) &&
+
+       cp $worktree1_refdir_prefix/branch-4 $worktree1_refdir_prefix/'\'' branch-5'\'' &&
+       cp $worktree2_refdir_prefix/branch-4 $worktree2_refdir_prefix/'\''~branch-6'\'' &&
+
+       test_must_fail git refs verify 2>err &&
+       cat >expect <<-EOF &&
+       error: worktrees/worktree-1/refs/worktree/ branch-5: badRefName: invalid refname format
+       error: worktrees/worktree-2/refs/worktree/~branch-6: badRefName: invalid refname format
+       EOF
+       sort err >sorted_err &&
+       test_cmp expect sorted_err &&
+
+       for worktree in "worktree-1" "worktree-2"
+       do
+               (
+                       cd $worktree &&
+                       test_must_fail git refs verify 2>err &&
+                       cat >expect <<-EOF &&
+                       error: worktrees/worktree-1/refs/worktree/ branch-5: badRefName: invalid refname format
+                       error: worktrees/worktree-2/refs/worktree/~branch-6: badRefName: invalid refname format
+                       EOF
+                       sort err >sorted_err &&
+                       test_cmp expect sorted_err || return 1
+               )
+       done
+'
+
 test_done