builtin/gc: refactor ODB optimizations to operate on "files" source

We have a couple of functions that are implementation details of how the "files" object database source performs optimizations. These functions often use global state like `the_repository` and implicitly derive the source they are supposed to optimize. Refactor these interfaces to accept a "files" source directly. This will make it easier to move around the whole logic into "odb/source-files.c" in a subsequent step. 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 0a778894b9676a1e93870589851731dad05e086a
1 file changed +41 -38
builtin/gc.c
+41 -38
@@ -428,9 +428,8 @@ out:
428 return should_gc;
429 }
430
431 -static int too_many_loose_objects(int limit)
431 +static int too_many_loose_objects(struct odb_source_files *files, int limit)
432 {
433 - struct odb_source_files *files = odb_source_files_downcast(the_repository->objects->sources);
433 /*
434 * This is weird, but stems from legacy behaviour: the GC auto
435 * threshold was always essentially interpreted as if it was rounded up
@@ -446,19 +445,21 @@ static int too_many_loose_objects(int limit)
445 return loose_count > auto_threshold;
446 }
447
449 -static struct packed_git *find_base_packs(struct string_list *packs,
448 +static struct packed_git *find_base_packs(struct odb_source_files *files,
449 + struct string_list *packs,
450 unsigned long limit)
451 {
452 - struct packed_git *p, *base = NULL;
452 + struct packfile_list_entry *e;
453 + struct packed_git *base = NULL;
454
454 - repo_for_each_pack(the_repository, p) {
455 - if (!p->pack_local || p->is_cruft)
455 + for (e = packfile_store_get_packs(files->packed); e; e = e->next) {
456 + if (e->pack->is_cruft)
457 continue;
458 if (limit) {
458 - if (p->pack_size >= limit)
459 - string_list_append(packs, p->pack_name);
460 - } else if (!base || base->pack_size < p->pack_size) {
461 - base = p;
459 + if (e->pack->pack_size >= limit)
460 + string_list_append(packs, e->pack->pack_name);
461 + } else if (!base || base->pack_size < e->pack->pack_size) {
462 + base = e->pack;
463 }
464 }
465
@@ -468,18 +469,16 @@ static struct packed_git *find_base_packs(struct string_list *packs,
469 return base;
470 }
471
471 -static int too_many_packs(int gc_auto_pack_limit)
472 +static int too_many_packs(struct odb_source_files *files, int gc_auto_pack_limit)
473 {
473 - struct packed_git *p;
474 + struct packfile_list_entry *e;
475 int cnt = 0;
476
477 if (gc_auto_pack_limit <= 0)
478 return 0;
479
479 - repo_for_each_pack(the_repository, p) {
480 - if (!p->pack_local)
481 - continue;
482 - if (p->pack_keep)
480 + for (e = packfile_store_get_packs(files->packed); e; e = e->next) {
481 + if (e->pack->pack_keep)
482 continue;
483 /*
484 * Perhaps check the size of the pack and count only
@@ -535,15 +534,16 @@ static uint64_t total_ram(void)
534 return 0;
535 }
536
538 -static uint64_t estimate_repack_memory(struct packed_git *pack)
537 +static uint64_t estimate_repack_memory(struct odb_source_files *files,
538 + struct packed_git *pack)
539 {
540 unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE;
541 unsigned long delta_base_cache_limit = DEFAULT_DELTA_BASE_CACHE_LIMIT;
542 unsigned long nr_objects;
543 size_t os_cache, heap;
544
545 - if (odb_count_objects(the_repository->objects,
546 - ODB_COUNT_OBJECTS_APPROXIMATE, &nr_objects) < 0)
545 + if (odb_source_count_objects(&files->base, ODB_COUNT_OBJECTS_APPROXIMATE,
546 + &nr_objects) < 0)
547 return 0;
548
549 if (!pack || !nr_objects)
@@ -679,6 +679,8 @@ static void add_repack_incremental_option(struct strvec *args)
679 static bool odb_optimize_required(struct object_database *odb,
680 const struct odb_optimize_options *opts)
681 {
682 + struct odb_source_files *files = odb_source_files_downcast(odb->sources);
683 +
684 switch (opts->strategy) {
685 case ODB_OPTIMIZE_INCREMENTAL: {
686 int gc_auto_threshold = 6700;
@@ -693,8 +695,8 @@ static bool odb_optimize_required(struct object_database *odb,
695 */
696 if (gc_auto_threshold <= 0)
697 return false;
696 - if (!too_many_packs(gc_auto_pack_limit) &&
697 - !too_many_loose_objects(gc_auto_threshold))
698 + if (!too_many_packs(files, gc_auto_pack_limit) &&
699 + !too_many_loose_objects(files, gc_auto_threshold))
700 return false;
701
702 return true;
@@ -739,7 +741,7 @@ static bool odb_optimize_required(struct object_database *odb,
741 * Otherwise, we estimate the number of loose objects to determine
742 * whether we want to create a new packfile or not.
743 */
742 - if (too_many_loose_objects(auto_value)) {
744 + if (too_many_loose_objects(files, auto_value)) {
745 ret = true;
746 goto out;
747 }
@@ -886,21 +888,22 @@ static int gc_foreground_tasks(struct maintenance_run_opts *opts,
888 static int odb_optimize(struct object_database *odb,
889 const struct odb_optimize_options *opts)
890 {
891 + struct odb_source_files *files = odb_source_files_downcast(odb->sources);
892 struct child_process repack_cmd = CHILD_PROCESS_INIT;
893 unsigned long big_pack_threshold = 0;
894 int gc_auto_threshold = 6700;
895 int gc_auto_pack_limit = 50;
896 int ret;
897
895 - repo_config_get_int(the_repository, "gc.auto", &gc_auto_threshold);
896 - repo_config_get_int(the_repository, "gc.autopacklimit", &gc_auto_pack_limit);
897 - repo_config_get_ulong(the_repository, "gc.bigpackthreshold", &big_pack_threshold);
898 + repo_config_get_int(odb->repo, "gc.auto", &gc_auto_threshold);
899 + repo_config_get_int(odb->repo, "gc.autopacklimit", &gc_auto_pack_limit);
900 + repo_config_get_ulong(odb->repo, "gc.bigpackthreshold", &big_pack_threshold);
901
902 if (odb->repo->repository_format_precious_objects)
903 return 0;
904
905 repack_cmd.git_cmd = 1;
903 - repack_cmd.odb_to_close = the_repository->objects;
906 + repack_cmd.odb_to_close = odb->repo->objects;
907
908 strvec_pushl(&repack_cmd.args, "repack", "-d", "-l", NULL);
909 if (opts->flags & ODB_OPTIMIZE_NO_REUSE_DELTAS)
@@ -930,29 +933,29 @@ static int odb_optimize(struct object_database *odb,
933
934 if (opts->keep_largest_pack != -1) {
935 if (opts->keep_largest_pack)
933 - find_base_packs(&keep_pack, 0);
936 + find_base_packs(files, &keep_pack, 0);
937 } else if (big_pack_threshold) {
935 - find_base_packs(&keep_pack, big_pack_threshold);
938 + find_base_packs(files, &keep_pack, big_pack_threshold);
939 }
940
941 add_repack_all_option(opts, &keep_pack, &repack_cmd.args);
942 string_list_clear(&keep_pack, 0);
943 } else {
941 - if (too_many_packs(gc_auto_pack_limit)) {
944 + if (too_many_packs(files, gc_auto_pack_limit)) {
945 struct string_list keep_pack = STRING_LIST_INIT_NODUP;
946
947 if (big_pack_threshold) {
945 - find_base_packs(&keep_pack, big_pack_threshold);
948 + find_base_packs(files, &keep_pack, big_pack_threshold);
949 if (keep_pack.nr >= gc_auto_pack_limit) {
950 string_list_clear(&keep_pack, 0);
948 - find_base_packs(&keep_pack, 0);
951 + find_base_packs(files, &keep_pack, 0);
952 }
953 } else {
951 - struct packed_git *p = find_base_packs(&keep_pack, 0);
954 + struct packed_git *p = find_base_packs(files, &keep_pack, 0);
955 uint64_t mem_have, mem_want;
956
957 mem_have = total_ram();
955 - mem_want = estimate_repack_memory(p);
958 + mem_want = estimate_repack_memory(files, p);
959
960 /*
961 * Only allow 1/2 of memory for pack-objects, leave
@@ -981,10 +984,10 @@ static int odb_optimize(struct object_database *odb,
984 struct existing_packs existing_packs = EXISTING_PACKS_INIT;
985 struct string_list kept_packs = STRING_LIST_INIT_DUP;
986
984 - repo_config_get_int(the_repository, "maintenance.geometric-repack.splitFactor",
987 + repo_config_get_int(odb->repo, "maintenance.geometric-repack.splitFactor",
988 &geometry.split_factor);
989
987 - existing_packs.repo = the_repository;
990 + existing_packs.repo = odb->repo;
991 existing_packs_collect(&existing_packs, &kept_packs);
992 pack_geometry_init(&geometry, &existing_packs, &po_args);
993 pack_geometry_split(&geometry);
@@ -995,7 +998,7 @@ static int odb_optimize(struct object_database *odb,
998 } else {
999 add_repack_all_option(opts, NULL, &repack_cmd.args);
1000 }
998 - if (the_repository->settings.core_multi_pack_index)
1001 + if (odb->repo->settings.core_multi_pack_index)
1002 strvec_push(&repack_cmd.args, "--write-midx");
1003
1004 existing_packs_release(&existing_packs);
@@ -1020,7 +1023,7 @@ static int odb_optimize(struct object_database *odb,
1023 strvec_push(&prune_cmd.args, opts->prune_expire);
1024 if (!(opts->flags & ODB_OPTIMIZE_VERBOSE))
1025 strvec_push(&prune_cmd.args, "--no-progress");
1023 - if (repo_has_promisor_remote(the_repository))
1026 + if (repo_has_promisor_remote(odb->repo))
1027 strvec_push(&prune_cmd.args,
1028 "--exclude-promisor-objects");
1029 prune_cmd.git_cmd = 1;
@@ -1031,7 +1034,7 @@ static int odb_optimize(struct object_database *odb,
1034 }
1035 }
1036
1034 - if (opts->flags & ODB_OPTIMIZE_AUTO && too_many_loose_objects(gc_auto_threshold))
1037 + if (opts->flags & ODB_OPTIMIZE_AUTO && too_many_loose_objects(files, gc_auto_threshold))
1038 warning(_("There are too many unreachable loose objects; "
1039 "run 'git prune' to remove them."));
1040