builtin/fsck: add `git refs verify` child process

At now, we have already implemented the ref consistency checks for both "files-backend" and "packed-backend". Although we would check some redundant things, it won't cause trouble. So, let's integrate it into the "git-fsck(1)" command to get feedback from the users. And also by calling "git refs verify" in "git-fsck(1)", we make sure that the new added checks don't break. Introduce a new function "fsck_refs" that initializes and runs a child process to execute the "git refs verify" command. In order to provide the user interface create a progress which makes the total task be 1. It's hard to know how many loose refs we will check now. We might improve this later. Then, introduce the option to allow the user to disable checking ref database consistency. Put this function in the very first execution sequence of "git-fsck(1)" due to that we don't want the existing code of "git-fsck(1)" which would implicitly check the consistency of refs to die the program. Last, update the test 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 Feb 28, 2025 at 00:07 UTC c1cf918d3adbe832fdaf278a23151502be255bb2
3 files changed +77 -2
Documentation/git-fsck.txt
+6 -1
@@ -12,7 +12,7 @@ SYNOPSIS
12 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
13 [--[no-]full] [--strict] [--verbose] [--lost-found]
14 [--[no-]dangling] [--[no-]progress] [--connectivity-only]
15 - [--[no-]name-objects] [<object>...]
15 + [--[no-]name-objects] [--[no-]references] [<object>...]
16
17 DESCRIPTION
18 -----------
@@ -104,6 +104,11 @@ care about this output and want to speed it up further.
104 progress status even if the standard error stream is not
105 directed to a terminal.
106
107 +--[no-]references::
108 + Control whether to check the references database consistency
109 + via 'git refs verify'. See linkgit:git-refs[1] for details.
110 + The default is to check the references database.
111 +
112 CONFIGURATION
113 -------------
114
builtin/fsck.c
+32 -1
@@ -50,6 +50,7 @@ static int verbose;
50 static int show_progress = -1;
51 static int show_dangling = 1;
52 static int name_objects;
53 +static int check_references = 1;
54 #define ERROR_OBJECT 01
55 #define ERROR_REACHABLE 02
56 #define ERROR_PACK 04
@@ -902,11 +903,37 @@ static int check_pack_rev_indexes(struct repository *r, int show_progress)
903 return res;
904 }
905
906 +static void fsck_refs(struct repository *r)
907 +{
908 + struct child_process refs_verify = CHILD_PROCESS_INIT;
909 + struct progress *progress = NULL;
910 +
911 + if (show_progress)
912 + progress = start_progress(r, _("Checking ref database"), 1);
913 +
914 + if (verbose)
915 + fprintf_ln(stderr, _("Checking ref database"));
916 +
917 + child_process_init(&refs_verify);
918 + refs_verify.git_cmd = 1;
919 + strvec_pushl(&refs_verify.args, "refs", "verify", NULL);
920 + if (verbose)
921 + strvec_push(&refs_verify.args, "--verbose");
922 + if (check_strict)
923 + strvec_push(&refs_verify.args, "--strict");
924 +
925 + if (run_command(&refs_verify))
926 + errors_found |= ERROR_REFS;
927 +
928 + display_progress(progress, 1);
929 + stop_progress(&progress);
930 +}
931 +
932 static char const * const fsck_usage[] = {
933 N_("git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
934 " [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
935 " [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
909 - " [--[no-]name-objects] [<object>...]"),
936 + " [--[no-]name-objects] [--[no-]references] [<object>...]"),
937 NULL
938 };
939
@@ -925,6 +952,7 @@ static struct option fsck_opts[] = {
952 N_("write dangling objects in .git/lost-found")),
953 OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
954 OPT_BOOL(0, "name-objects", &name_objects, N_("show verbose names for reachable objects")),
955 + OPT_BOOL(0, "references", &check_references, N_("check reference database consistency")),
956 OPT_END(),
957 };
958
@@ -967,6 +995,9 @@ int cmd_fsck(int argc,
995 git_config(git_fsck_config, &fsck_obj_options);
996 prepare_repo_settings(the_repository);
997
998 + if (check_references)
999 + fsck_refs(the_repository);
1000 +
1001 if (connectivity_only) {
1002 for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
1003 for_each_packed_object(the_repository,
t/t0602-reffiles-fsck.sh
+39
@@ -830,4 +830,43 @@ test_expect_success 'packed-ref without sorted trait should not be checked' '
830 )
831 '
832
833 +test_expect_success '--[no-]references option should apply to fsck' '
834 + test_when_finished "rm -rf repo" &&
835 + git init repo &&
836 + branch_dir_prefix=.git/refs/heads &&
837 + (
838 + cd repo &&
839 + test_commit default &&
840 + for trailing_content in " garbage" " more garbage"
841 + do
842 + printf "%s" "$(git rev-parse HEAD)$trailing_content" >$branch_dir_prefix/branch-garbage &&
843 + git fsck 2>err &&
844 + cat >expect <<-EOF &&
845 + warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\''$trailing_content'\''
846 + EOF
847 + rm $branch_dir_prefix/branch-garbage &&
848 + test_cmp expect err || return 1
849 + done &&
850 +
851 + for trailing_content in " garbage" " more garbage"
852 + do
853 + printf "%s" "$(git rev-parse HEAD)$trailing_content" >$branch_dir_prefix/branch-garbage &&
854 + git fsck --references 2>err &&
855 + cat >expect <<-EOF &&
856 + warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\''$trailing_content'\''
857 + EOF
858 + rm $branch_dir_prefix/branch-garbage &&
859 + test_cmp expect err || return 1
860 + done &&
861 +
862 + for trailing_content in " garbage" " more garbage"
863 + do
864 + printf "%s" "$(git rev-parse HEAD)$trailing_content" >$branch_dir_prefix/branch-garbage &&
865 + git fsck --no-references 2>err &&
866 + rm $branch_dir_prefix/branch-garbage &&
867 + test_must_be_empty err || return 1
868 + done
869 + )
870 +'
871 +
872 test_done