builtin/gc: introduce object database optimization options
Introduce `struct odb_optimize_options` to decouple the options that are specific to optimizing the object database from `struct gc_config`. This structure will be moved into the object database layer in a subsequent commit. Note that there are a small set of backend-specific options in this structure. In an ideal world those of course wouldn't exist, but as we're introducing the object database abstractions retroactively we are somewhat forced to keep them. 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
cc78c5029e62e6e1283c7d05bf60d845922aac9b
1 file changed
+120
-61
builtin/gc.c
+120
-61
@@ -593,7 +593,39 @@ static int keep_one_pack(struct string_list_item *item, void *data)
593
return 0;
594
}
595
596
-static void add_repack_all_option(struct gc_config *cfg,
596
+enum odb_optimize_flags {
597
+ /* Enable verbose logging and progress reporting. */
598
+ ODB_OPTIMIZE_VERBOSE = (1 << 0),
599
+
600
+ /* Perform auto-maintenance, only optimizing objects as required. */
601
+ ODB_OPTIMIZE_AUTO = (1 << 1),
602
+
603
+ /* Recompute existing deltas. */
604
+ ODB_OPTIMIZE_NO_REUSE_DELTAS = (1 << 2),
605
+};
606
+
607
+struct odb_optimize_options {
608
+ enum odb_optimize_flags flags;
609
+ const char *prune_expire;
610
+ const char *expire_to;
611
+ int depth;
612
+ int window;
613
+
614
+ /* Backend-specific options. */
615
+ int keep_largest_pack;
616
+ int cruft_packs;
617
+ unsigned long max_cruft_size;
618
+};
619
+
620
+#define OPTIMIZE_FIELDS_FROM_GC_CONFIG(cfg, aggressive) \
621
+ .prune_expire = (cfg)->prune_expire, \
622
+ .expire_to = (cfg)->repack_expire_to, \
623
+ .cruft_packs = (cfg)->cruft_packs, \
624
+ .max_cruft_size = (cfg)->max_cruft_size, \
625
+ .window = (aggressive) ? (cfg)->aggressive_window : 0, \
626
+ .depth = (aggressive) ? (cfg)->aggressive_depth : 0
627
+
628
+static void add_repack_all_option(const struct odb_optimize_options *opts,
629
struct string_list *keep_pack,
630
struct strvec *args)
631
{
@@ -603,22 +635,22 @@ static void add_repack_all_option(struct gc_config *cfg,
635
repo_config_get_string(the_repository, "gc.repackfilter", &repack_filter);
636
repo_config_get_string(the_repository, "gc.repackfilterto", &repack_filter_to);
637
606
- if (cfg->prune_expire && !strcmp(cfg->prune_expire, "now")
607
- && !(cfg->cruft_packs && cfg->repack_expire_to))
638
+ if (opts->prune_expire && !strcmp(opts->prune_expire, "now") &&
639
+ !(opts->cruft_packs && opts->expire_to))
640
strvec_push(args, "-a");
609
- else if (cfg->cruft_packs) {
641
+ else if (opts->cruft_packs) {
642
strvec_push(args, "--cruft");
611
- if (cfg->prune_expire)
612
- strvec_pushf(args, "--cruft-expiration=%s", cfg->prune_expire);
613
- if (cfg->max_cruft_size)
643
+ if (opts->prune_expire)
644
+ strvec_pushf(args, "--cruft-expiration=%s", opts->prune_expire);
645
+ if (opts->max_cruft_size)
646
strvec_pushf(args, "--max-cruft-size=%lu",
615
- cfg->max_cruft_size);
616
- if (cfg->repack_expire_to)
617
- strvec_pushf(args, "--expire-to=%s", cfg->repack_expire_to);
647
+ opts->max_cruft_size);
648
+ if (opts->expire_to)
649
+ strvec_pushf(args, "--expire-to=%s", opts->expire_to);
650
} else {
651
strvec_push(args, "-A");
620
- if (cfg->prune_expire)
621
- strvec_pushf(args, "--unpack-unreachable=%s", cfg->prune_expire);
652
+ if (opts->prune_expire)
653
+ strvec_pushf(args, "--unpack-unreachable=%s", opts->prune_expire);
654
}
655
656
if (keep_pack)
@@ -786,10 +818,8 @@ static int gc_foreground_tasks(struct maintenance_run_opts *opts,
818
return 0;
819
}
820
789
-static int maintenance_task_odb(struct maintenance_run_opts *opts,
790
- struct gc_config *cfg,
791
- int keep_largest_pack,
792
- int aggressive)
821
+static int odb_optimize(struct object_database *odb,
822
+ const struct odb_optimize_options *opts)
823
{
824
struct child_process repack_cmd = CHILD_PROCESS_INIT;
825
unsigned long big_pack_threshold = 0;
@@ -801,21 +831,20 @@ static int maintenance_task_odb(struct maintenance_run_opts *opts,
831
repo_config_get_int(the_repository, "gc.autopacklimit", &gc_auto_pack_limit);
832
repo_config_get_ulong(the_repository, "gc.bigpackthreshold", &big_pack_threshold);
833
804
- if (the_repository->repository_format_precious_objects)
834
+ if (odb->repo->repository_format_precious_objects)
835
return 0;
836
837
repack_cmd.git_cmd = 1;
838
repack_cmd.odb_to_close = the_repository->objects;
839
840
strvec_pushl(&repack_cmd.args, "repack", "-d", "-l", NULL);
811
- if (aggressive) {
841
+ if (opts->flags & ODB_OPTIMIZE_NO_REUSE_DELTAS)
842
strvec_push(&repack_cmd.args, "-f");
813
- if (cfg->aggressive_depth > 0)
814
- strvec_pushf(&repack_cmd.args, "--depth=%d", cfg->aggressive_depth);
815
- if (cfg->aggressive_window > 0)
816
- strvec_pushf(&repack_cmd.args, "--window=%d", cfg->aggressive_window);
817
- }
818
- if (opts->quiet)
843
+ if (opts->depth > 0)
844
+ strvec_pushf(&repack_cmd.args, "--depth=%d", opts->depth);
845
+ if (opts->window > 0)
846
+ strvec_pushf(&repack_cmd.args, "--window=%d", opts->window);
847
+ if (!(opts->flags & ODB_OPTIMIZE_VERBOSE))
848
strvec_push(&repack_cmd.args, "-q");
849
850
/*
@@ -829,47 +858,49 @@ static int maintenance_task_odb(struct maintenance_run_opts *opts,
858
*
859
* - Otherwise we perform an incremental repack.
860
*/
832
- if (!opts->auto_flag) {
861
+ if (!(opts->flags & ODB_OPTIMIZE_AUTO)) {
862
struct string_list keep_pack = STRING_LIST_INIT_NODUP;
863
835
- if (keep_largest_pack != -1) {
836
- if (keep_largest_pack)
864
+ if (opts->keep_largest_pack != -1) {
865
+ if (opts->keep_largest_pack)
866
find_base_packs(&keep_pack, 0);
867
} else if (big_pack_threshold) {
868
find_base_packs(&keep_pack, big_pack_threshold);
869
}
870
842
- add_repack_all_option(cfg, &keep_pack, &repack_cmd.args);
871
+ add_repack_all_option(opts, &keep_pack, &repack_cmd.args);
872
string_list_clear(&keep_pack, 0);
844
- } else if (too_many_packs(gc_auto_pack_limit)) {
845
- struct string_list keep_pack = STRING_LIST_INIT_NODUP;
846
-
847
- if (big_pack_threshold) {
848
- find_base_packs(&keep_pack, big_pack_threshold);
849
- if (keep_pack.nr >= gc_auto_pack_limit) {
850
- string_list_clear(&keep_pack, 0);
851
- find_base_packs(&keep_pack, 0);
873
+ } else {
874
+ if (too_many_packs(gc_auto_pack_limit)) {
875
+ struct string_list keep_pack = STRING_LIST_INIT_NODUP;
876
+
877
+ if (big_pack_threshold) {
878
+ find_base_packs(&keep_pack, big_pack_threshold);
879
+ if (keep_pack.nr >= gc_auto_pack_limit) {
880
+ string_list_clear(&keep_pack, 0);
881
+ find_base_packs(&keep_pack, 0);
882
+ }
883
+ } else {
884
+ struct packed_git *p = find_base_packs(&keep_pack, 0);
885
+ uint64_t mem_have, mem_want;
886
+
887
+ mem_have = total_ram();
888
+ mem_want = estimate_repack_memory(p);
889
+
890
+ /*
891
+ * Only allow 1/2 of memory for pack-objects, leave
892
+ * the rest for the OS and other processes in the
893
+ * system.
894
+ */
895
+ if (!mem_have || mem_want < mem_have / 2)
896
+ string_list_clear(&keep_pack, 0);
897
}
853
- } else {
854
- struct packed_git *p = find_base_packs(&keep_pack, 0);
855
- uint64_t mem_have, mem_want;
856
-
857
- mem_have = total_ram();
858
- mem_want = estimate_repack_memory(p);
898
860
- /*
861
- * Only allow 1/2 of memory for pack-objects, leave
862
- * the rest for the OS and other processes in the
863
- * system.
864
- */
865
- if (!mem_have || mem_want < mem_have / 2)
866
- string_list_clear(&keep_pack, 0);
899
+ add_repack_all_option(opts, &keep_pack, &repack_cmd.args);
900
+ string_list_clear(&keep_pack, 0);
901
+ } else {
902
+ add_repack_incremental_option(&repack_cmd.args);
903
}
868
-
869
- add_repack_all_option(cfg, &keep_pack, &repack_cmd.args);
870
- string_list_clear(&keep_pack, 0);
871
- } else {
872
- add_repack_incremental_option(&repack_cmd.args);
904
}
905
906
if (run_command(&repack_cmd)) {
@@ -877,13 +908,13 @@ static int maintenance_task_odb(struct maintenance_run_opts *opts,
908
goto out;
909
}
910
880
- if (cfg->prune_expire) {
911
+ if (opts->prune_expire) {
912
struct child_process prune_cmd = CHILD_PROCESS_INIT;
913
914
strvec_pushl(&prune_cmd.args, "prune", "--expire", NULL);
915
/* run `git prune` even if using cruft packs */
885
- strvec_push(&prune_cmd.args, cfg->prune_expire);
886
- if (opts->quiet)
916
+ strvec_push(&prune_cmd.args, opts->prune_expire);
917
+ if (!(opts->flags & ODB_OPTIMIZE_VERBOSE))
918
strvec_push(&prune_cmd.args, "--no-progress");
919
if (repo_has_promisor_remote(the_repository))
920
strvec_push(&prune_cmd.args,
@@ -896,7 +927,7 @@ static int maintenance_task_odb(struct maintenance_run_opts *opts,
927
}
928
}
929
899
- if (opts->auto_flag && too_many_loose_objects(gc_auto_threshold))
930
+ if (opts->flags & ODB_OPTIMIZE_AUTO && too_many_loose_objects(gc_auto_threshold))
931
warning(_("There are too many unreachable loose objects; "
932
"run 'git prune' to remove them."));
933
@@ -906,6 +937,26 @@ out:
937
return ret;
938
}
939
940
+static int maintenance_task_odb(struct maintenance_run_opts *opts,
941
+ struct gc_config *cfg,
942
+ int keep_largest_pack,
943
+ int aggressive)
944
+{
945
+ struct odb_optimize_options odb_opts = {
946
+ .keep_largest_pack = keep_largest_pack,
947
+ OPTIMIZE_FIELDS_FROM_GC_CONFIG(cfg, aggressive),
948
+ };
949
+
950
+ if (opts->auto_flag)
951
+ odb_opts.flags |= ODB_OPTIMIZE_AUTO;
952
+ if (!opts->quiet)
953
+ odb_opts.flags |= ODB_OPTIMIZE_VERBOSE;
954
+ if (aggressive)
955
+ odb_opts.flags |= ODB_OPTIMIZE_NO_REUSE_DELTAS;
956
+
957
+ return odb_optimize(the_repository->objects, &odb_opts);
958
+}
959
+
960
int cmd_gc(int argc,
961
const char **argv,
962
const char *prefix,
@@ -1596,11 +1647,19 @@ static int maintenance_task_geometric_repack(struct maintenance_run_opts *opts,
1647
child.odb_to_close = the_repository->objects;
1648
1649
strvec_pushl(&child.args, "repack", "-d", "-l", NULL);
1599
- if (geometry.split < geometry.pack_nr)
1650
+ if (geometry.split < geometry.pack_nr) {
1651
strvec_pushf(&child.args, "--geometric=%d",
1652
geometry.split_factor);
1602
- else
1603
- add_repack_all_option(cfg, NULL, &child.args);
1653
+ } else {
1654
+ struct odb_optimize_options odb_opts = {
1655
+ OPTIMIZE_FIELDS_FROM_GC_CONFIG(cfg, 0),
1656
+ };
1657
+
1658
+ if (!opts->quiet)
1659
+ odb_opts.flags |= ODB_OPTIMIZE_VERBOSE;
1660
+
1661
+ add_repack_all_option(&odb_opts, NULL, &child.args);
1662
+ }
1663
if (opts->quiet)
1664
strvec_push(&child.args, "--quiet");
1665
if (the_repository->settings.core_multi_pack_index)