[PATCH 06/11] builtin/gc: introduce object database optimization options
From: Patrick Steinhardt <hidden>
Date: 2026-07-07 15:33:10
Subsystem:
the rest · Maintainer:
Linus Torvalds
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 <redacted> --- builtin/gc.c | 181 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 120 insertions(+), 61 deletions(-)
diff --git a/builtin/gc.c b/builtin/gc.c
index 5d445edaa0..17490106fc 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c@@ -593,7 +593,39 @@ static int keep_one_pack(struct string_list_item *item, void *data) return 0; } -static void add_repack_all_option(struct gc_config *cfg, +enum odb_optimize_flags { + /* Enable verbose logging and progress reporting. */ + ODB_OPTIMIZE_VERBOSE = (1 << 0), + + /* Perform auto-maintenance, only optimizing objects as required. */ + ODB_OPTIMIZE_AUTO = (1 << 1), + + /* Recompute existing deltas. */ + ODB_OPTIMIZE_NO_REUSE_DELTAS = (1 << 2), +}; + +struct odb_optimize_options { + enum odb_optimize_flags flags; + const char *prune_expire; + const char *expire_to; + int depth; + int window; + + /* Backend-specific options. */ + int keep_largest_pack; + int cruft_packs; + unsigned long max_cruft_size; +}; + +#define OPTIMIZE_FIELDS_FROM_GC_CONFIG(cfg, aggressive) \ + .prune_expire = (cfg)->prune_expire, \ + .expire_to = (cfg)->repack_expire_to, \ + .cruft_packs = (cfg)->cruft_packs, \ + .max_cruft_size = (cfg)->max_cruft_size, \ + .window = (aggressive) ? (cfg)->aggressive_window : 0, \ + .depth = (aggressive) ? (cfg)->aggressive_depth : 0 + +static void add_repack_all_option(const struct odb_optimize_options *opts, struct string_list *keep_pack, struct strvec *args) {
@@ -603,22 +635,22 @@ static void add_repack_all_option(struct gc_config *cfg, repo_config_get_string(the_repository, "gc.repackfilter", &repack_filter); repo_config_get_string(the_repository, "gc.repackfilterto", &repack_filter_to); - if (cfg->prune_expire && !strcmp(cfg->prune_expire, "now") - && !(cfg->cruft_packs && cfg->repack_expire_to)) + if (opts->prune_expire && !strcmp(opts->prune_expire, "now") && + !(opts->cruft_packs && opts->expire_to)) strvec_push(args, "-a"); - else if (cfg->cruft_packs) { + else if (opts->cruft_packs) { strvec_push(args, "--cruft"); - if (cfg->prune_expire) - strvec_pushf(args, "--cruft-expiration=%s", cfg->prune_expire); - if (cfg->max_cruft_size) + if (opts->prune_expire) + strvec_pushf(args, "--cruft-expiration=%s", opts->prune_expire); + if (opts->max_cruft_size) strvec_pushf(args, "--max-cruft-size=%lu", - cfg->max_cruft_size); - if (cfg->repack_expire_to) - strvec_pushf(args, "--expire-to=%s", cfg->repack_expire_to); + opts->max_cruft_size); + if (opts->expire_to) + strvec_pushf(args, "--expire-to=%s", opts->expire_to); } else { strvec_push(args, "-A"); - if (cfg->prune_expire) - strvec_pushf(args, "--unpack-unreachable=%s", cfg->prune_expire); + if (opts->prune_expire) + strvec_pushf(args, "--unpack-unreachable=%s", opts->prune_expire); } if (keep_pack)
@@ -786,10 +818,8 @@ static int gc_foreground_tasks(struct maintenance_run_opts *opts, return 0; } -static int maintenance_task_odb(struct maintenance_run_opts *opts, - struct gc_config *cfg, - int keep_largest_pack, - int aggressive) +static int odb_optimize(struct object_database *odb, + const struct odb_optimize_options *opts) { struct child_process repack_cmd = CHILD_PROCESS_INIT; unsigned long big_pack_threshold = 0;
@@ -801,21 +831,20 @@ static int maintenance_task_odb(struct maintenance_run_opts *opts, repo_config_get_int(the_repository, "gc.autopacklimit", &gc_auto_pack_limit); repo_config_get_ulong(the_repository, "gc.bigpackthreshold", &big_pack_threshold); - if (the_repository->repository_format_precious_objects) + if (odb->repo->repository_format_precious_objects) return 0; repack_cmd.git_cmd = 1; repack_cmd.odb_to_close = the_repository->objects; strvec_pushl(&repack_cmd.args, "repack", "-d", "-l", NULL); - if (aggressive) { + if (opts->flags & ODB_OPTIMIZE_NO_REUSE_DELTAS) strvec_push(&repack_cmd.args, "-f"); - if (cfg->aggressive_depth > 0) - strvec_pushf(&repack_cmd.args, "--depth=%d", cfg->aggressive_depth); - if (cfg->aggressive_window > 0) - strvec_pushf(&repack_cmd.args, "--window=%d", cfg->aggressive_window); - } - if (opts->quiet) + if (opts->depth > 0) + strvec_pushf(&repack_cmd.args, "--depth=%d", opts->depth); + if (opts->window > 0) + strvec_pushf(&repack_cmd.args, "--window=%d", opts->window); + if (!(opts->flags & ODB_OPTIMIZE_VERBOSE)) strvec_push(&repack_cmd.args, "-q"); /*
@@ -829,47 +858,49 @@ static int maintenance_task_odb(struct maintenance_run_opts *opts, * * - Otherwise we perform an incremental repack. */ - if (!opts->auto_flag) { + if (!(opts->flags & ODB_OPTIMIZE_AUTO)) { struct string_list keep_pack = STRING_LIST_INIT_NODUP; - if (keep_largest_pack != -1) { - if (keep_largest_pack) + if (opts->keep_largest_pack != -1) { + if (opts->keep_largest_pack) find_base_packs(&keep_pack, 0); } else if (big_pack_threshold) { find_base_packs(&keep_pack, big_pack_threshold); } - add_repack_all_option(cfg, &keep_pack, &repack_cmd.args); + add_repack_all_option(opts, &keep_pack, &repack_cmd.args); string_list_clear(&keep_pack, 0); - } else if (too_many_packs(gc_auto_pack_limit)) { - struct string_list keep_pack = STRING_LIST_INIT_NODUP; - - if (big_pack_threshold) { - find_base_packs(&keep_pack, big_pack_threshold); - if (keep_pack.nr >= gc_auto_pack_limit) { - string_list_clear(&keep_pack, 0); - find_base_packs(&keep_pack, 0); + } else { + if (too_many_packs(gc_auto_pack_limit)) { + struct string_list keep_pack = STRING_LIST_INIT_NODUP; + + if (big_pack_threshold) { + find_base_packs(&keep_pack, big_pack_threshold); + if (keep_pack.nr >= gc_auto_pack_limit) { + string_list_clear(&keep_pack, 0); + find_base_packs(&keep_pack, 0); + } + } else { + struct packed_git *p = find_base_packs(&keep_pack, 0); + uint64_t mem_have, mem_want; + + mem_have = total_ram(); + mem_want = estimate_repack_memory(p); + + /* + * Only allow 1/2 of memory for pack-objects, leave + * the rest for the OS and other processes in the + * system. + */ + if (!mem_have || mem_want < mem_have / 2) + string_list_clear(&keep_pack, 0); } - } else { - struct packed_git *p = find_base_packs(&keep_pack, 0); - uint64_t mem_have, mem_want; - - mem_have = total_ram(); - mem_want = estimate_repack_memory(p); - /* - * Only allow 1/2 of memory for pack-objects, leave - * the rest for the OS and other processes in the - * system. - */ - if (!mem_have || mem_want < mem_have / 2) - string_list_clear(&keep_pack, 0); + add_repack_all_option(opts, &keep_pack, &repack_cmd.args); + string_list_clear(&keep_pack, 0); + } else { + add_repack_incremental_option(&repack_cmd.args); } - - add_repack_all_option(cfg, &keep_pack, &repack_cmd.args); - string_list_clear(&keep_pack, 0); - } else { - add_repack_incremental_option(&repack_cmd.args); } if (run_command(&repack_cmd)) {
@@ -877,13 +908,13 @@ static int maintenance_task_odb(struct maintenance_run_opts *opts, goto out; } - if (cfg->prune_expire) { + if (opts->prune_expire) { struct child_process prune_cmd = CHILD_PROCESS_INIT; strvec_pushl(&prune_cmd.args, "prune", "--expire", NULL); /* run `git prune` even if using cruft packs */ - strvec_push(&prune_cmd.args, cfg->prune_expire); - if (opts->quiet) + strvec_push(&prune_cmd.args, opts->prune_expire); + if (!(opts->flags & ODB_OPTIMIZE_VERBOSE)) strvec_push(&prune_cmd.args, "--no-progress"); if (repo_has_promisor_remote(the_repository)) strvec_push(&prune_cmd.args,
@@ -896,7 +927,7 @@ static int maintenance_task_odb(struct maintenance_run_opts *opts, } } - if (opts->auto_flag && too_many_loose_objects(gc_auto_threshold)) + if (opts->flags & ODB_OPTIMIZE_AUTO && too_many_loose_objects(gc_auto_threshold)) warning(_("There are too many unreachable loose objects; " "run 'git prune' to remove them."));
@@ -906,6 +937,26 @@ static int maintenance_task_odb(struct maintenance_run_opts *opts, return ret; } +static int maintenance_task_odb(struct maintenance_run_opts *opts, + struct gc_config *cfg, + int keep_largest_pack, + int aggressive) +{ + struct odb_optimize_options odb_opts = { + .keep_largest_pack = keep_largest_pack, + OPTIMIZE_FIELDS_FROM_GC_CONFIG(cfg, aggressive), + }; + + if (opts->auto_flag) + odb_opts.flags |= ODB_OPTIMIZE_AUTO; + if (!opts->quiet) + odb_opts.flags |= ODB_OPTIMIZE_VERBOSE; + if (aggressive) + odb_opts.flags |= ODB_OPTIMIZE_NO_REUSE_DELTAS; + + return odb_optimize(the_repository->objects, &odb_opts); +} + int cmd_gc(int argc, const char **argv, const char *prefix,
@@ -1596,11 +1647,19 @@ static int maintenance_task_geometric_repack(struct maintenance_run_opts *opts, child.odb_to_close = the_repository->objects; strvec_pushl(&child.args, "repack", "-d", "-l", NULL); - if (geometry.split < geometry.pack_nr) + if (geometry.split < geometry.pack_nr) { strvec_pushf(&child.args, "--geometric=%d", geometry.split_factor); - else - add_repack_all_option(cfg, NULL, &child.args); + } else { + struct odb_optimize_options odb_opts = { + OPTIMIZE_FIELDS_FROM_GC_CONFIG(cfg, 0), + }; + + if (!opts->quiet) + odb_opts.flags |= ODB_OPTIMIZE_VERBOSE; + + add_repack_all_option(&odb_opts, NULL, &child.args); + } if (opts->quiet) strvec_push(&child.args, "--quiet"); if (the_repository->settings.core_multi_pack_index)
--
2.55.0.141.g00534a21ce.dirty