builtin/gc: make repack arguments self-contained

When optimizing the object database most of the heavy-lifting is done by git-repack(1). The arguments we pass to this function are assembled in global scope, which is hard to follow. Refactor the logic by moving the vector into `maintenance_task_odb()`. While that means we have to pass more arguments to this function, it has the upside that the logic becomes self-contained without any kind of global interdependencies. This is a pure refactoring with no intended functional change. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jul 13, 2026 at 07:52 UTC 4d93bf9873696a33a9959c912e7a3fa4fb56a82b
1 file changed +75 -81
builtin/gc.c
+75 -81
@@ -661,7 +661,7 @@ static void add_repack_incremental_option(struct strvec *args)
661 strvec_push(args, "--no-write-bitmap-index");
662 }
663
664 -static int need_to_gc(struct gc_config *cfg, struct strvec *repack_args)
664 +static int need_to_gc(struct gc_config *cfg)
665 {
666 /*
667 * Setting gc.auto to 0 or negative can disable the
@@ -669,46 +669,8 @@ static int need_to_gc(struct gc_config *cfg, struct strvec *repack_args)
669 */
670 if (cfg->gc_auto_threshold <= 0)
671 return 0;
672 -
673 - /*
674 - * If there are too many loose objects, but not too many
675 - * packs, we run "repack -d -l". If there are too many packs,
676 - * we run "repack -A -d -l". Otherwise we tell the caller
677 - * there is no need.
678 - */
679 - if (too_many_packs(cfg)) {
680 - struct string_list keep_pack = STRING_LIST_INIT_NODUP;
681 -
682 - if (cfg->big_pack_threshold) {
683 - find_base_packs(&keep_pack, cfg->big_pack_threshold);
684 - if (keep_pack.nr >= cfg->gc_auto_pack_limit) {
685 - cfg->big_pack_threshold = 0;
686 - string_list_clear(&keep_pack, 0);
687 - find_base_packs(&keep_pack, 0);
688 - }
689 - } else {
690 - struct packed_git *p = find_base_packs(&keep_pack, 0);
691 - uint64_t mem_have, mem_want;
692 -
693 - mem_have = total_ram();
694 - mem_want = estimate_repack_memory(cfg, p);
695 -
696 - /*
697 - * Only allow 1/2 of memory for pack-objects, leave
698 - * the rest for the OS and other processes in the
699 - * system.
700 - */
701 - if (!mem_have || mem_want < mem_have / 2)
702 - string_list_clear(&keep_pack, 0);
703 - }
704 -
705 - add_repack_all_option(cfg, &keep_pack, repack_args);
706 - string_list_clear(&keep_pack, 0);
707 - } else if (too_many_loose_objects(cfg->gc_auto_threshold))
708 - add_repack_incremental_option(repack_args);
709 - else
672 + if (!too_many_packs(cfg) && !too_many_loose_objects(cfg->gc_auto_threshold))
673 return 0;
711 -
674 return 1;
675 }
676
@@ -841,7 +803,8 @@ static int gc_foreground_tasks(struct maintenance_run_opts *opts,
803
804 static int maintenance_task_odb(struct maintenance_run_opts *opts,
805 struct gc_config *cfg,
844 - struct strvec *repack_args)
806 + int keep_largest_pack,
807 + int aggressive)
808 {
809 struct child_process repack_cmd = CHILD_PROCESS_INIT;
810 int ret;
@@ -851,9 +814,75 @@ static int maintenance_task_odb(struct maintenance_run_opts *opts,
814
815 repack_cmd.git_cmd = 1;
816 repack_cmd.odb_to_close = the_repository->objects;
854 - strvec_pushv(&repack_cmd.args, repack_args->v);
817 +
818 + strvec_pushl(&repack_cmd.args, "repack", "-d", "-l", NULL);
819 + if (aggressive) {
820 + strvec_push(&repack_cmd.args, "-f");
821 + if (cfg->aggressive_depth > 0)
822 + strvec_pushf(&repack_cmd.args, "--depth=%d", cfg->aggressive_depth);
823 + if (cfg->aggressive_window > 0)
824 + strvec_pushf(&repack_cmd.args, "--window=%d", cfg->aggressive_window);
825 + }
826 + if (opts->quiet)
827 + strvec_push(&repack_cmd.args, "-q");
828 +
829 + /*
830 + * There's three cases we need to consider:
831 + *
832 + * - If we're invoked without `--auto` we'll need to perform a full
833 + * repack.
834 + *
835 + * - If we're invoked with `--auto` and there's too many packs, then
836 + * we perform a full repack, as well.
837 + *
838 + * - Otherwise we perform an incremental repack.
839 + */
840 + if (!opts->auto_flag) {
841 + struct string_list keep_pack = STRING_LIST_INIT_NODUP;
842 +
843 + if (keep_largest_pack != -1) {
844 + if (keep_largest_pack)
845 + find_base_packs(&keep_pack, 0);
846 + } else if (cfg->big_pack_threshold) {
847 + find_base_packs(&keep_pack, cfg->big_pack_threshold);
848 + }
849 +
850 + add_repack_all_option(cfg, &keep_pack, &repack_cmd.args);
851 + string_list_clear(&keep_pack, 0);
852 + } else if (too_many_packs(cfg)) {
853 + struct string_list keep_pack = STRING_LIST_INIT_NODUP;
854 +
855 + if (cfg->big_pack_threshold) {
856 + find_base_packs(&keep_pack, cfg->big_pack_threshold);
857 + if (keep_pack.nr >= cfg->gc_auto_pack_limit) {
858 + cfg->big_pack_threshold = 0;
859 + string_list_clear(&keep_pack, 0);
860 + find_base_packs(&keep_pack, 0);
861 + }
862 + } else {
863 + struct packed_git *p = find_base_packs(&keep_pack, 0);
864 + uint64_t mem_have, mem_want;
865 +
866 + mem_have = total_ram();
867 + mem_want = estimate_repack_memory(cfg, p);
868 +
869 + /*
870 + * Only allow 1/2 of memory for pack-objects, leave
871 + * the rest for the OS and other processes in the
872 + * system.
873 + */
874 + if (!mem_have || mem_want < mem_have / 2)
875 + string_list_clear(&keep_pack, 0);
876 + }
877 +
878 + add_repack_all_option(cfg, &keep_pack, &repack_cmd.args);
879 + string_list_clear(&keep_pack, 0);
880 + } else {
881 + add_repack_incremental_option(&repack_cmd.args);
882 + }
883 +
884 if (run_command(&repack_cmd)) {
856 - ret = error(FAILED_RUN, repack_args->v[0]);
885 + ret = error(FAILED_RUN, repack_cmd.args.v[0]);
886 goto out;
887 }
888
@@ -899,7 +928,6 @@ int cmd_gc(int argc,
928 int keep_largest_pack = -1;
929 int skip_foreground_tasks = 0;
930 timestamp_t dummy;
902 - struct strvec repack_args = STRVEC_INIT;
931 struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
932 struct gc_config cfg = GC_CONFIG_INIT;
933 const char *prune_expire_sentinel = "sentinel";
@@ -939,8 +967,6 @@ int cmd_gc(int argc,
967 show_usage_with_options_if_asked(argc, argv,
968 builtin_gc_usage, builtin_gc_options);
969
942 - strvec_pushl(&repack_args, "repack", "-d", "-l", NULL);
943 -
970 gc_config(&cfg);
971
972 if (parse_expiry_date(cfg.gc_log_expire, &gc_log_expire_time))
@@ -961,16 +987,6 @@ int cmd_gc(int argc,
987 if (cfg.prune_expire && parse_expiry_date(cfg.prune_expire, &dummy))
988 die(_("failed to parse prune expiry value %s"), cfg.prune_expire);
989
964 - if (aggressive) {
965 - strvec_push(&repack_args, "-f");
966 - if (cfg.aggressive_depth > 0)
967 - strvec_pushf(&repack_args, "--depth=%d", cfg.aggressive_depth);
968 - if (cfg.aggressive_window > 0)
969 - strvec_pushf(&repack_args, "--window=%d", cfg.aggressive_window);
970 - }
971 - if (opts.quiet)
972 - strvec_push(&repack_args, "-q");
973 -
990 if (opts.auto_flag) {
991 if (cfg.detach_auto && opts.detach < 0)
992 opts.detach = 1;
@@ -978,8 +994,7 @@ int cmd_gc(int argc,
994 /*
995 * Auto-gc should be least intrusive as possible.
996 */
981 - if (!need_to_gc(&cfg, &repack_args) ||
982 - run_hooks(the_repository, "pre-auto-gc")) {
997 + if (!need_to_gc(&cfg) || run_hooks(the_repository, "pre-auto-gc")) {
998 ret = 0;
999 goto out;
1000 }
@@ -991,18 +1006,6 @@ int cmd_gc(int argc,
1006 fprintf(stderr, _("Auto packing the repository for optimum performance.\n"));
1007 fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
1008 }
994 - } else {
995 - struct string_list keep_pack = STRING_LIST_INIT_NODUP;
996 -
997 - if (keep_largest_pack != -1) {
998 - if (keep_largest_pack)
999 - find_base_packs(&keep_pack, 0);
1000 - } else if (cfg.big_pack_threshold) {
1001 - find_base_packs(&keep_pack, cfg.big_pack_threshold);
1002 - }
1003 -
1004 - add_repack_all_option(&cfg, &keep_pack, &repack_args);
1005 - string_list_clear(&keep_pack, 0);
1009 }
1010
1011 if (opts.detach > 0) {
@@ -1065,7 +1068,7 @@ int cmd_gc(int argc,
1068 if (maintenance_task_rerere_gc(&opts, &cfg))
1069 die(FAILED_RUN, "rerere");
1070
1068 - if (maintenance_task_odb(&opts, &cfg, &repack_args))
1071 + if (maintenance_task_odb(&opts, &cfg, keep_largest_pack, aggressive))
1072 die(NULL);
1073
1074 report_garbage = report_pack_garbage;
@@ -1088,7 +1091,6 @@ int cmd_gc(int argc,
1091
1092 out:
1093 maintenance_run_opts_release(&opts);
1091 - strvec_clear(&repack_args);
1094 gc_config_release(&cfg);
1095 return 0;
1096 }
@@ -1291,15 +1293,7 @@ static int maintenance_task_gc_background(struct maintenance_run_opts *opts,
1293
1294 static int gc_condition(struct gc_config *cfg)
1295 {
1294 - /*
1295 - * Note that it's fine to drop the repack arguments here, as we execute
1296 - * git-gc(1) as a separate child process anyway. So it knows to compute
1297 - * these arguments again.
1298 - */
1299 - struct strvec repack_args = STRVEC_INIT;
1300 - int ret = need_to_gc(cfg, &repack_args);
1301 - strvec_clear(&repack_args);
1302 - return ret;
1296 + return need_to_gc(cfg);
1297 }
1298
1299 static int prune_packed(struct maintenance_run_opts *opts)