builtin/repack.c: allow configuring cruft pack generation

In servers which set the pack.window configuration to a large value, we can wind up spending quite a lot of time finding new bases when breaking delta chains between reachable and unreachable objects while generating a cruft pack. Introduce a handful of `repack.cruft*` configuration variables to control the parameters used by pack-objects when generating a cruft pack. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Taylor Blau committed May 20, 2022 at 19:18 UTC 4571324b99fcf7bd9e58fce677801fbf72b0e0a5
3 files changed +127 -14
Documentation/config/repack.txt
+9
@@ -30,3 +30,12 @@ repack.updateServerInfo::
30 If set to false, linkgit:git-repack[1] will not run
31 linkgit:git-update-server-info[1]. Defaults to true. Can be overridden
32 when true by the `-n` option of linkgit:git-repack[1].
33 +
34 +repack.cruftWindow::
35 +repack.cruftWindowMemory::
36 +repack.cruftDepth::
37 +repack.cruftThreads::
38 + Parameters used by linkgit:git-pack-objects[1] when generating
39 + a cruft pack and the respective parameters are not given over
40 + the command line. See similarly named `pack.*` configuration
41 + variables for defaults and meaning.
builtin/repack.c
+35 -14
@@ -41,9 +41,21 @@ static const char incremental_bitmap_conflict_error[] = N_(
41 "--no-write-bitmap-index or disable the pack.writebitmaps configuration."
42 );
43
44 +struct pack_objects_args {
45 + const char *window;
46 + const char *window_memory;
47 + const char *depth;
48 + const char *threads;
49 + const char *max_pack_size;
50 + int no_reuse_delta;
51 + int no_reuse_object;
52 + int quiet;
53 + int local;
54 +};
55
56 static int repack_config(const char *var, const char *value, void *cb)
57 {
58 + struct pack_objects_args *cruft_po_args = cb;
59 if (!strcmp(var, "repack.usedeltabaseoffset")) {
60 delta_base_offset = git_config_bool(var, value);
61 return 0;
@@ -65,6 +77,14 @@ static int repack_config(const char *var, const char *value, void *cb)
77 run_update_server_info = git_config_bool(var, value);
78 return 0;
79 }
80 + if (!strcmp(var, "repack.cruftwindow"))
81 + return git_config_string(&cruft_po_args->window, var, value);
82 + if (!strcmp(var, "repack.cruftwindowmemory"))
83 + return git_config_string(&cruft_po_args->window_memory, var, value);
84 + if (!strcmp(var, "repack.cruftdepth"))
85 + return git_config_string(&cruft_po_args->depth, var, value);
86 + if (!strcmp(var, "repack.cruftthreads"))
87 + return git_config_string(&cruft_po_args->threads, var, value);
88 return git_default_config(var, value, cb);
89 }
90
@@ -157,18 +177,6 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name)
177 strbuf_release(&buf);
178 }
179
160 -struct pack_objects_args {
161 - const char *window;
162 - const char *window_memory;
163 - const char *depth;
164 - const char *threads;
165 - const char *max_pack_size;
166 - int no_reuse_delta;
167 - int no_reuse_object;
168 - int quiet;
169 - int local;
170 -};
171 -
180 static void prepare_pack_objects(struct child_process *cmd,
181 const struct pack_objects_args *args)
182 {
@@ -692,6 +700,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
700 int keep_unreachable = 0;
701 struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
702 struct pack_objects_args po_args = {NULL};
703 + struct pack_objects_args cruft_po_args = {NULL};
704 int geometric_factor = 0;
705 int write_midx = 0;
706
@@ -746,7 +755,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
755 OPT_END()
756 };
757
749 - git_config(repack_config, NULL);
758 + git_config(repack_config, &cruft_po_args);
759
760 argc = parse_options(argc, argv, prefix, builtin_repack_options,
761 git_repack_usage, 0);
@@ -921,7 +930,19 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
930 if (*pack_prefix == '/')
931 pack_prefix++;
932
924 - ret = write_cruft_pack(&po_args, pack_prefix, &names,
933 + if (!cruft_po_args.window)
934 + cruft_po_args.window = po_args.window;
935 + if (!cruft_po_args.window_memory)
936 + cruft_po_args.window_memory = po_args.window_memory;
937 + if (!cruft_po_args.depth)
938 + cruft_po_args.depth = po_args.depth;
939 + if (!cruft_po_args.threads)
940 + cruft_po_args.threads = po_args.threads;
941 +
942 + cruft_po_args.local = po_args.local;
943 + cruft_po_args.quiet = po_args.quiet;
944 +
945 + ret = write_cruft_pack(&cruft_po_args, pack_prefix, &names,
946 &existing_nonkept_packs,
947 &existing_kept_packs);
948 if (ret)
t/t5329-pack-objects-cruft.sh
+83
@@ -565,4 +565,87 @@ test_expect_success 'cruft repack ignores pack.packSizeLimit' '
565 )
566 '
567
568 +test_expect_success 'cruft repack respects repack.cruftWindow' '
569 + git init repo &&
570 + test_when_finished "rm -fr repo" &&
571 + (
572 + cd repo &&
573 +
574 + test_commit base &&
575 +
576 + GIT_TRACE2_EVENT=$(pwd)/event.trace \
577 + git -c pack.window=1 -c repack.cruftWindow=2 repack \
578 + --cruft --window=3 &&
579 +
580 + grep "pack-objects.*--window=2.*--cruft" event.trace
581 + )
582 +'
583 +
584 +test_expect_success 'cruft repack respects --window by default' '
585 + git init repo &&
586 + test_when_finished "rm -fr repo" &&
587 + (
588 + cd repo &&
589 +
590 + test_commit base &&
591 +
592 + GIT_TRACE2_EVENT=$(pwd)/event.trace \
593 + git -c pack.window=2 repack --cruft --window=3 &&
594 +
595 + grep "pack-objects.*--window=3.*--cruft" event.trace
596 + )
597 +'
598 +
599 +test_expect_success 'cruft repack respects --quiet' '
600 + git init repo &&
601 + test_when_finished "rm -fr repo" &&
602 + (
603 + cd repo &&
604 +
605 + test_commit base &&
606 + GIT_PROGRESS_DELAY=0 git repack --cruft --quiet 2>err &&
607 + test_must_be_empty err
608 + )
609 +'
610 +
611 +test_expect_success 'cruft --local drops unreachable objects' '
612 + git init alternate &&
613 + git init repo &&
614 + test_when_finished "rm -fr alternate repo" &&
615 +
616 + test_commit -C alternate base &&
617 + # Pack all objects in alterate so that the cruft repack in "repo" sees
618 + # the object it dropped due to `--local` as packed. Otherwise this
619 + # object would not appear packed anywhere (since it is not packed in
620 + # alternate and likewise not part of the cruft pack in the other repo
621 + # because of `--local`).
622 + git -C alternate repack -ad &&
623 +
624 + (
625 + cd repo &&
626 +
627 + object="$(git -C ../alternate rev-parse HEAD:base.t)" &&
628 + git -C ../alternate cat-file -p $object >contents &&
629 +
630 + # Write some reachable objects and two unreachable ones: one
631 + # that the alternate has and another that is unique.
632 + test_commit other &&
633 + git hash-object -w -t blob contents &&
634 + cruft="$(echo cruft | git hash-object -w -t blob --stdin)" &&
635 +
636 + ( cd ../alternate/.git/objects && pwd ) \
637 + >.git/objects/info/alternates &&
638 +
639 + test_path_is_file $objdir/$(test_oid_to_path $cruft) &&
640 + test_path_is_file $objdir/$(test_oid_to_path $object) &&
641 +
642 + git repack -d --cruft --local &&
643 +
644 + test-tool pack-mtimes "$(basename $(ls $packdir/pack-*.mtimes))" \
645 + >objects &&
646 + ! grep $object objects &&
647 + grep $cruft objects
648 + )
649 +'
650 +
651 test_done