From 135f491f83d4763bdc61642eb0126ce2e6ada286 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Sat, 8 Nov 2025 22:51:53 +0100 Subject: [PATCH 1/5] reftable/stack: return stack segments directly The `stack_table_sizes_for_compaction()` function returns individual sizes of each reftable table. This function is only called by `reftable_stack_auto_compact()` to decide which tables need to be compacted, if any. Modify the function to directly return the segments, which avoids the extra step of receiving the sizes only to pass it to `suggest_compaction_segment()`. A future commit will also add functionality for checking whether auto-compaction is necessary without performing it. This change allows code re-usability in that context. Signed-off-by: Karthik Nayak Acked-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- reftable/stack.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/reftable/stack.c b/reftable/stack.c index 65d89820bd..49387f9344 100644 --- a/reftable/stack.c +++ b/reftable/stack.c @@ -1626,7 +1626,8 @@ struct segment suggest_compaction_segment(uint64_t *sizes, size_t n, return seg; } -static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st) +static int stack_segments_for_compaction(struct reftable_stack *st, + struct segment *seg) { int version = (st->opts.hash_id == REFTABLE_HASH_SHA1) ? 1 : 2; int overhead = header_size(version) - 1; @@ -1634,29 +1635,29 @@ static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st) REFTABLE_CALLOC_ARRAY(sizes, st->merged->tables_len); if (!sizes) - return NULL; + return REFTABLE_OUT_OF_MEMORY_ERROR; for (size_t i = 0; i < st->merged->tables_len; i++) sizes[i] = st->tables[i]->size - overhead; - return sizes; + *seg = suggest_compaction_segment(sizes, st->merged->tables_len, + st->opts.auto_compaction_factor); + reftable_free(sizes); + + return 0; } int reftable_stack_auto_compact(struct reftable_stack *st) { struct segment seg; - uint64_t *sizes; + int err; if (st->merged->tables_len < 2) return 0; - sizes = stack_table_sizes_for_compaction(st); - if (!sizes) - return REFTABLE_OUT_OF_MEMORY_ERROR; - - seg = suggest_compaction_segment(sizes, st->merged->tables_len, - st->opts.auto_compaction_factor); - reftable_free(sizes); + err = stack_segments_for_compaction(st, &seg); + if (err) + return err; if (segment_size(&seg) > 0) return stack_compact_range(st, seg.start, seg.end - 1, From e35155588aa9f0355eb7e116ea418c189479f62d Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Sat, 8 Nov 2025 22:51:54 +0100 Subject: [PATCH 2/5] reftable/stack: add function to check if optimization is required The reftable backend performs auto-compaction as part of its regular flow, which is required to keep the number of tables part of a stack at bay. This allows it to stay optimized. Compaction can also be triggered voluntarily by the user via the 'git pack-refs' or the 'git refs optimize' command. However, currently there is no way for the user to check if optimization is required without actually performing it. Extract out the heuristics logic from 'reftable_stack_auto_compact()' into an internal function 'update_segment_if_compaction_required()'. Then use this to add and expose `reftable_stack_compaction_required()` which will allow users to check if the reftable backend can be optimized. Signed-off-by: Karthik Nayak Acked-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- reftable/reftable-stack.h | 11 +++++++++ reftable/stack.c | 42 +++++++++++++++++++++++++++++---- t/unit-tests/u-reftable-stack.c | 12 ++++++++-- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h index d70fcb705d..c2415cbc6e 100644 --- a/reftable/reftable-stack.h +++ b/reftable/reftable-stack.h @@ -123,6 +123,17 @@ struct reftable_log_expiry_config { int reftable_stack_compact_all(struct reftable_stack *st, struct reftable_log_expiry_config *config); +/* + * Check if compaction is required. + * + * When `use_heuristics` is false, check if all tables can be compacted to a + * single table. If true, use heuristics to determine if the tables need to be + * compacted to maintain geometric progression. + */ +int reftable_stack_compaction_required(struct reftable_stack *st, + bool use_heuristics, + bool *required); + /* heuristically compact unbalanced table stack. */ int reftable_stack_auto_compact(struct reftable_stack *st); diff --git a/reftable/stack.c b/reftable/stack.c index 49387f9344..1c9f21dfe1 100644 --- a/reftable/stack.c +++ b/reftable/stack.c @@ -1647,19 +1647,51 @@ static int stack_segments_for_compaction(struct reftable_stack *st, return 0; } -int reftable_stack_auto_compact(struct reftable_stack *st) +static int update_segment_if_compaction_required(struct reftable_stack *st, + struct segment *seg, + bool use_geometric, + bool *required) { - struct segment seg; int err; - if (st->merged->tables_len < 2) + if (st->merged->tables_len < 2) { + *required = false; return 0; + } - err = stack_segments_for_compaction(st, &seg); + if (!use_geometric) { + *required = true; + return 0; + } + + err = stack_segments_for_compaction(st, seg); if (err) return err; - if (segment_size(&seg) > 0) + *required = segment_size(seg) > 0; + return 0; +} + +int reftable_stack_compaction_required(struct reftable_stack *st, + bool use_heuristics, + bool *required) +{ + struct segment seg; + return update_segment_if_compaction_required(st, &seg, use_heuristics, + required); +} + +int reftable_stack_auto_compact(struct reftable_stack *st) +{ + struct segment seg; + bool required; + int err; + + err = update_segment_if_compaction_required(st, &seg, true, &required); + if (err) + return err; + + if (required) return stack_compact_range(st, seg.start, seg.end - 1, NULL, STACK_COMPACT_RANGE_BEST_EFFORT); diff --git a/t/unit-tests/u-reftable-stack.c b/t/unit-tests/u-reftable-stack.c index a8b91812e8..b8110cdeee 100644 --- a/t/unit-tests/u-reftable-stack.c +++ b/t/unit-tests/u-reftable-stack.c @@ -1067,6 +1067,7 @@ void test_reftable_stack__add_performs_auto_compaction(void) .value_type = REFTABLE_REF_SYMREF, .value.symref = (char *) "master", }; + bool required = false; char buf[128]; /* @@ -1087,10 +1088,17 @@ void test_reftable_stack__add_performs_auto_compaction(void) * auto compaction is disabled. When enabled, we should merge * all tables in the stack. */ - if (i != n) + cl_assert_equal_i(reftable_stack_compaction_required(st, true, &required), 0); + if (i != n) { cl_assert_equal_i(st->merged->tables_len, i + 1); - else + if (i < 1) + cl_assert_equal_b(required, false); + else + cl_assert_equal_b(required, true); + } else { cl_assert_equal_i(st->merged->tables_len, 1); + cl_assert_equal_b(required, false); + } } reftable_stack_destroy(st); From f6c5ca387a7693b16158826d157178be0ba439dc Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Sat, 8 Nov 2025 22:51:55 +0100 Subject: [PATCH 3/5] refs: add a `optimize_required` field to `struct ref_storage_be` To allow users of the refs namespace to check if the reference backend requires optimization, add a new field `optimize_required` field to `struct ref_storage_be`. This field is of type `optimize_required_fn` which is also introduced in this commit. Modify the debug, files, packed and reftable backend to implement this field. A following commit will expose this via 'git pack-refs' and 'git refs optimize'. Signed-off-by: Karthik Nayak Acked-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- refs.c | 7 +++++++ refs.h | 7 +++++++ refs/debug.c | 13 +++++++++++++ refs/files-backend.c | 11 +++++++++++ refs/packed-backend.c | 13 +++++++++++++ refs/refs-internal.h | 6 ++++++ refs/reftable-backend.c | 25 +++++++++++++++++++++++++ 7 files changed, 82 insertions(+) diff --git a/refs.c b/refs.c index 0d0831f29b..5583f6e09d 100644 --- a/refs.c +++ b/refs.c @@ -2318,6 +2318,13 @@ int refs_optimize(struct ref_store *refs, struct refs_optimize_opts *opts) return refs->be->optimize(refs, opts); } +int refs_optimize_required(struct ref_store *refs, + struct refs_optimize_opts *opts, + bool *required) +{ + return refs->be->optimize_required(refs, opts, required); +} + int reference_get_peeled_oid(struct repository *repo, const struct reference *ref, struct object_id *peeled_oid) diff --git a/refs.h b/refs.h index 6b05bba527..d9051bbb04 100644 --- a/refs.h +++ b/refs.h @@ -520,6 +520,13 @@ struct refs_optimize_opts { */ int refs_optimize(struct ref_store *refs, struct refs_optimize_opts *opts); +/* + * Check if refs backend can be optimized by calling 'refs_optimize'. + */ +int refs_optimize_required(struct ref_store *ref_store, + struct refs_optimize_opts *opts, + bool *required); + /* * Setup reflog before using. Fill in err and return -1 on failure. */ diff --git a/refs/debug.c b/refs/debug.c index 2defd2d465..36f8c58b6c 100644 --- a/refs/debug.c +++ b/refs/debug.c @@ -124,6 +124,17 @@ static int debug_optimize(struct ref_store *ref_store, struct refs_optimize_opts return res; } +static int debug_optimize_required(struct ref_store *ref_store, + struct refs_optimize_opts *opts, + bool *required) +{ + struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; + int res = drefs->refs->be->optimize_required(drefs->refs, opts, required); + trace_printf_key(&trace_refs, "optimize_required: %s, res: %d\n", + required ? "yes" : "no", res); + return res; +} + static int debug_rename_ref(struct ref_store *ref_store, const char *oldref, const char *newref, const char *logmsg) { @@ -431,6 +442,8 @@ struct ref_storage_be refs_be_debug = { .transaction_abort = debug_transaction_abort, .optimize = debug_optimize, + .optimize_required = debug_optimize_required, + .rename_ref = debug_rename_ref, .copy_ref = debug_copy_ref, diff --git a/refs/files-backend.c b/refs/files-backend.c index a1e70b1c10..6e0c9b340a 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1512,6 +1512,16 @@ static int files_optimize(struct ref_store *ref_store, return 0; } +static int files_optimize_required(struct ref_store *ref_store, + struct refs_optimize_opts *opts, + bool *required) +{ + struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_READ, + "optimize_required"); + *required = should_pack_refs(refs, opts); + return 0; +} + /* * People using contrib's git-new-workdir have .git/logs/refs -> * /some/other/path/.git/logs/refs, and that may live on another device. @@ -3982,6 +3992,7 @@ struct ref_storage_be refs_be_files = { .transaction_abort = files_transaction_abort, .optimize = files_optimize, + .optimize_required = files_optimize_required, .rename_ref = files_rename_ref, .copy_ref = files_copy_ref, diff --git a/refs/packed-backend.c b/refs/packed-backend.c index 10062fd8b6..19ce4d5872 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -1784,6 +1784,17 @@ static int packed_optimize(struct ref_store *ref_store UNUSED, return 0; } +static int packed_optimize_required(struct ref_store *ref_store UNUSED, + struct refs_optimize_opts *opts UNUSED, + bool *required) +{ + /* + * Packed refs are already optimized. + */ + *required = false; + return 0; +} + static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store UNUSED) { return empty_ref_iterator_begin(); @@ -2130,6 +2141,8 @@ struct ref_storage_be refs_be_packed = { .transaction_abort = packed_transaction_abort, .optimize = packed_optimize, + .optimize_required = packed_optimize_required, + .rename_ref = NULL, .copy_ref = NULL, diff --git a/refs/refs-internal.h b/refs/refs-internal.h index dee42f231d..c7d2a6e50b 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -424,6 +424,11 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs, typedef int optimize_fn(struct ref_store *ref_store, struct refs_optimize_opts *opts); + +typedef int optimize_required_fn(struct ref_store *ref_store, + struct refs_optimize_opts *opts, + bool *required); + typedef int rename_ref_fn(struct ref_store *ref_store, const char *oldref, const char *newref, const char *logmsg); @@ -549,6 +554,7 @@ struct ref_storage_be { ref_transaction_abort_fn *transaction_abort; optimize_fn *optimize; + optimize_required_fn *optimize_required; rename_ref_fn *rename_ref; copy_ref_fn *copy_ref; diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index c23c45f3bf..a3ae0cf74a 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -1733,6 +1733,29 @@ out: return ret; } +static int reftable_be_optimize_required(struct ref_store *ref_store, + struct refs_optimize_opts *opts, + bool *required) +{ + struct reftable_ref_store *refs = reftable_be_downcast(ref_store, REF_STORE_READ, + "optimize_refs_required"); + struct reftable_stack *stack; + bool use_heuristics = false; + + if (refs->err) + return refs->err; + + stack = refs->worktree_backend.stack; + if (!stack) + stack = refs->main_backend.stack; + + if (opts->flags & REFS_OPTIMIZE_AUTO) + use_heuristics = true; + + return reftable_stack_compaction_required(stack, use_heuristics, + required); +} + struct write_create_symref_arg { struct reftable_ref_store *refs; struct reftable_stack *stack; @@ -2756,6 +2779,8 @@ struct ref_storage_be refs_be_reftable = { .transaction_abort = reftable_be_transaction_abort, .optimize = reftable_be_optimize, + .optimize_required = reftable_be_optimize_required, + .rename_ref = reftable_be_rename_ref, .copy_ref = reftable_be_copy_ref, From 8c1ce2204cc755bdafec85aaa4ac9c5a686a8bf4 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Sat, 8 Nov 2025 22:51:56 +0100 Subject: [PATCH 4/5] maintenance: add checking logic in `pack_refs_condition()` The 'git-maintenance(1)' command supports an '--auto' flag. Usage of the flag ensures to run maintenance tasks only if certain thresholds are met. The heuristic is defined on a task level, wherein each task defines an 'auto_condition', which states if the task should be run. The 'pack-refs' task is hard-coded to return 1 as: 1. There was never a way to check if the reference backend needs to be optimized without actually performing the optimization. 2. We can pass in the '--auto' flag to 'git-pack-refs(1)' which would optimize based on heuristics. The previous commit added a `refs_optimize_required()` function, which can be used to check if a reference backend required optimization. Use this within `pack_refs_condition()`. This allows us to add a 'git maintenance is-needed' subcommand which can notify the user if maintenance is needed without actually performing the optimization. Without this change, the reference backend would always state that optimization is needed. Since we import 'revision.h', we need to remove the definition for 'SEEN' which is duplicated in the included header. Signed-off-by: Karthik Nayak Acked-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/gc.c | 30 +++++++++++++++++++++--------- object.h | 1 - 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/builtin/gc.c b/builtin/gc.c index c6d62c74a7..85e9a38d10 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -35,6 +35,7 @@ #include "path.h" #include "reflog.h" #include "rerere.h" +#include "revision.h" #include "blob.h" #include "tree.h" #include "promisor-remote.h" @@ -285,12 +286,26 @@ static void maintenance_run_opts_release(struct maintenance_run_opts *opts) static int pack_refs_condition(UNUSED struct gc_config *cfg) { - /* - * The auto-repacking logic for refs is handled by the ref backends and - * exposed via `git pack-refs --auto`. We thus always return truish - * here and let the backend decide for us. - */ - return 1; + struct string_list included_refs = STRING_LIST_INIT_NODUP; + struct ref_exclusions excludes = REF_EXCLUSIONS_INIT; + struct refs_optimize_opts optimize_opts = { + .exclusions = &excludes, + .includes = &included_refs, + .flags = REFS_OPTIMIZE_PRUNE | REFS_OPTIMIZE_AUTO, + }; + bool required; + + /* Check for all refs, similar to 'git refs optimize --all'. */ + string_list_append(optimize_opts.includes, "*"); + + if (refs_optimize_required(get_main_ref_store(the_repository), + &optimize_opts, &required)) + return 0; + + clear_ref_exclusions(&excludes); + string_list_clear(&included_refs, 0); + + return required; } static int maintenance_task_pack_refs(struct maintenance_run_opts *opts, @@ -1090,9 +1105,6 @@ static int maintenance_opt_schedule(const struct option *opt, const char *arg, return 0; } -/* Remember to update object flag allocation in object.h */ -#define SEEN (1u<<0) - struct cg_auto_data { int num_not_in_graph; int limit; diff --git a/object.h b/object.h index 1499f63d50..832299e763 100644 --- a/object.h +++ b/object.h @@ -79,7 +79,6 @@ void object_array_init(struct object_array *array); * list-objects-filter.c: 21 * bloom.c: 2122 * builtin/fsck.c: 0--3 - * builtin/gc.c: 0 * builtin/index-pack.c: 2021 * reflog.c: 10--12 * builtin/show-branch.c: 0-------------------------------------------26 From 28b83e6f08ae022d54d79e518e72933ae0930091 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Sat, 8 Nov 2025 22:51:57 +0100 Subject: [PATCH 5/5] maintenance: add 'is-needed' subcommand The 'git-maintenance(1)' command provides tooling to run maintenance tasks over Git repositories. The 'run' subcommand, as the name suggests, runs the maintenance tasks. When used with the '--auto' flag, it uses heuristics to determine if the required thresholds are met for running said maintenance tasks. There is however a lack of insight into these heuristics. Meaning, the checks are linked to the execution. Add a new 'is-needed' subcommand to 'git-maintenance(1)' which allows users to simply check if it is needed to run maintenance without performing it. This subcommand can check if it is needed to run maintenance without actually running it. Ideally it should be used with the '--auto' flag, which would allow users to check if the thresholds required are met. The subcommand also supports the '--task' flag which can be used to check specific maintenance tasks. While adding the respective tests in 't/t7900-maintenance.sh', remove a duplicate of the test: 'worktree-prune task with --auto honors maintenance.worktree-prune.auto'. Signed-off-by: Karthik Nayak Acked-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- Documentation/git-maintenance.adoc | 13 ++++++ builtin/gc.c | 63 +++++++++++++++++++++++++++++- t/t7900-maintenance.sh | 54 +++++++++++++++++-------- 3 files changed, 113 insertions(+), 17 deletions(-) diff --git a/Documentation/git-maintenance.adoc b/Documentation/git-maintenance.adoc index 540b5cf68b..bda616f14c 100644 --- a/Documentation/git-maintenance.adoc +++ b/Documentation/git-maintenance.adoc @@ -12,6 +12,7 @@ SYNOPSIS 'git maintenance' run [] 'git maintenance' start [--scheduler=] 'git maintenance' (stop|register|unregister) [] +'git maintenance' is-needed [] DESCRIPTION @@ -84,6 +85,16 @@ The `unregister` subcommand will report an error if the current repository is not already registered. Use the `--force` option to return success even when the current repository is not registered. +is-needed:: + Check whether maintenance needs to be run without actually running it. + Exits with a 0 status code if maintenance needs to be run, 1 otherwise. + Ideally used with the '--auto' flag. ++ +If one or more `--task` options are specified, then those tasks are checked +in that order. Otherwise, the tasks are determined by which +`maintenance..enabled` config options are true. By default, only +`maintenance.gc.enabled` is true. + TASKS ----- @@ -183,6 +194,8 @@ OPTIONS in the `gc.auto` config setting, or when the number of pack-files exceeds the `gc.autoPackLimit` config setting. Not compatible with the `--schedule` option. + When combined with the `is-needed` subcommand, check if the required + thresholds are met without actually running maintenance. --schedule:: When combined with the `run` subcommand, run maintenance tasks diff --git a/builtin/gc.c b/builtin/gc.c index 85e9a38d10..928c805f02 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -3253,7 +3253,67 @@ static int maintenance_stop(int argc, const char **argv, const char *prefix, return update_background_schedule(NULL, 0); } -static const char * const builtin_maintenance_usage[] = { +static const char *const builtin_maintenance_is_needed_usage[] = { + "git maintenance is-needed [--task=] [--schedule]", + NULL +}; + +static int maintenance_is_needed(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) +{ + struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT; + struct string_list selected_tasks = STRING_LIST_INIT_DUP; + struct gc_config cfg = GC_CONFIG_INIT; + struct option options[] = { + OPT_BOOL(0, "auto", &opts.auto_flag, + N_("run tasks based on the state of the repository")), + OPT_CALLBACK_F(0, "task", &selected_tasks, N_("task"), + N_("check a specific task"), + PARSE_OPT_NONEG, task_option_parse), + OPT_END() + }; + bool is_needed = false; + + argc = parse_options(argc, argv, prefix, options, + builtin_maintenance_is_needed_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + if (argc) + usage_with_options(builtin_maintenance_is_needed_usage, options); + + gc_config(&cfg); + initialize_task_config(&opts, &selected_tasks); + + if (opts.auto_flag) { + for (size_t i = 0; i < opts.tasks_nr; i++) { + if (tasks[opts.tasks[i]].auto_condition && + tasks[opts.tasks[i]].auto_condition(&cfg)) { + is_needed = true; + break; + } + } + } else { + /* + * When not using --auto we always require maintenance right now. + * + * TODO: this certainly is too eager, as some maintenance tasks may + * decide to not do anything because the data structures are already + * fully optimized. We may eventually want to extend the auto + * condition to also cover non-auto runs so that we can detect such + * cases. + */ + is_needed = true; + } + + string_list_clear(&selected_tasks, 0); + maintenance_run_opts_release(&opts); + gc_config_release(&cfg); + + if (is_needed) + return 0; + return 1; +} + +static const char *const builtin_maintenance_usage[] = { N_("git maintenance []"), NULL, }; @@ -3270,6 +3330,7 @@ int cmd_maintenance(int argc, OPT_SUBCOMMAND("stop", &fn, maintenance_stop), OPT_SUBCOMMAND("register", &fn, maintenance_register), OPT_SUBCOMMAND("unregister", &fn, maintenance_unregister), + OPT_SUBCOMMAND("is-needed", &fn, maintenance_is_needed), OPT_END(), }; diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index ddd273d8dc..a17e2091c2 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -49,7 +49,9 @@ test_expect_success 'run [--auto|--quiet]' ' git maintenance run --auto 2>/dev/null && GIT_TRACE2_EVENT="$(pwd)/run-no-quiet.txt" \ git maintenance run --no-quiet 2>/dev/null && + git maintenance is-needed && test_subcommand git gc --quiet --no-detach --skip-foreground-tasks /dev/null && test_subcommand ! git prune-packed --quiet /dev/null && test_subcommand ! git prune-packed --quiet /dev/null && test_subcommand git prune-packed --quiet /dev/null && @@ -421,10 +435,13 @@ run_incremental_repack_and_verify () { test_commit A && git repack -adk && git multi-pack-index write && + ! git -c maintenance.incremental-repack.auto=1 \ + maintenance is-needed --auto --task=incremental-repack && GIT_TRACE2_EVENT="$(pwd)/midx-init.txt" git \ -c maintenance.incremental-repack.auto=1 \ maintenance run --auto --task=incremental-repack 2>/dev/null && test_subcommand ! git multi-pack-index write --no-progress /dev/null && test_subcommand ! git multi-pack-index write --no-progress /dev/null && @@ -485,9 +505,15 @@ test_expect_success 'reflog-expire task --auto only packs when exceeding limits' git reflog expire --all --expire=now && test_commit reflog-one && test_commit reflog-two && + + ! git -c maintenance.reflog-expire.auto=3 \ + maintenance is-needed --auto --task=reflog-expire && GIT_TRACE2_EVENT="$(pwd)/reflog-expire-auto.txt" \ git -c maintenance.reflog-expire.auto=3 maintenance run --auto --task=reflog-expire && test_subcommand ! git reflog expire --all .git/worktrees/abc && + git maintenance is-needed --auto --task=worktree-prune && test_expect_worktree_prune git maintenance run --auto --task=worktree-prune ' @@ -530,22 +557,7 @@ test_expect_success 'worktree-prune task with --auto honors maintenance.worktree test_expect_worktree_prune ! git -c maintenance.worktree-prune.auto=0 maintenance run --auto --task=worktree-prune && # A positive value should require at least this many prunable worktrees. test_expect_worktree_prune ! git -c maintenance.worktree-prune.auto=4 maintenance run --auto --task=worktree-prune && - test_expect_worktree_prune git -c maintenance.worktree-prune.auto=3 maintenance run --auto --task=worktree-prune -' - -test_expect_success 'worktree-prune task with --auto honors maintenance.worktree-prune.auto' ' - # A negative value should always prune. - test_expect_worktree_prune git -c maintenance.worktree-prune.auto=-1 maintenance run --auto --task=worktree-prune && - - mkdir .git/worktrees && - : >.git/worktrees/first && - : >.git/worktrees/second && - : >.git/worktrees/third && - - # Zero should never prune. - test_expect_worktree_prune ! git -c maintenance.worktree-prune.auto=0 maintenance run --auto --task=worktree-prune && - # A positive value should require at least this many prunable worktrees. - test_expect_worktree_prune ! git -c maintenance.worktree-prune.auto=4 maintenance run --auto --task=worktree-prune && + git -c maintenance.worktree-prune.auto=3 maintenance is-needed --auto --task=worktree-prune && test_expect_worktree_prune git -c maintenance.worktree-prune.auto=3 maintenance run --auto --task=worktree-prune ' @@ -554,11 +566,13 @@ test_expect_success 'worktree-prune task honors gc.worktreePruneExpire' ' rm -rf worktree && rm -f worktree-prune.txt && + ! git -c gc.worktreePruneExpire=1.week.ago maintenance is-needed --auto --task=worktree-prune && GIT_TRACE2_EVENT="$(pwd)/worktree-prune.txt" git -c gc.worktreePruneExpire=1.week.ago maintenance run --auto --task=worktree-prune && test_subcommand ! git worktree prune --expire 1.week.ago .git/rr-cache/entry && + git maintenance is-needed --auto --task=rerere-gc && test_expect_rerere_gc git maintenance run --auto --task=rerere-gc ' @@ -594,17 +611,22 @@ test_expect_success 'rerere-gc task with --auto honors maintenance.rerere-gc.aut test_when_finished "rm -rf .git/rr-cache" && # A negative value should always prune. + git -c maintenance.rerere-gc.auto=-1 maintenance is-needed --auto --task=rerere-gc && test_expect_rerere_gc git -c maintenance.rerere-gc.auto=-1 maintenance run --auto --task=rerere-gc && # A positive value prunes when there is at least one entry. + ! git -c maintenance.rerere-gc.auto=9000 maintenance is-needed --auto --task=rerere-gc && test_expect_rerere_gc ! git -c maintenance.rerere-gc.auto=9000 maintenance run --auto --task=rerere-gc && mkdir .git/rr-cache && + ! git -c maintenance.rerere-gc.auto=9000 maintenance is-needed --auto --task=rerere-gc && test_expect_rerere_gc ! git -c maintenance.rerere-gc.auto=9000 maintenance run --auto --task=rerere-gc && : >.git/rr-cache/entry-1 && + git -c maintenance.rerere-gc.auto=9000 maintenance is-needed --auto --task=rerere-gc && test_expect_rerere_gc git -c maintenance.rerere-gc.auto=9000 maintenance run --auto --task=rerere-gc && # Zero should never prune. : >.git/rr-cache/entry-1 && + ! git -c maintenance.rerere-gc.auto=0 maintenance is-needed --auto --task=rerere-gc && test_expect_rerere_gc ! git -c maintenance.rerere-gc.auto=0 maintenance run --auto --task=rerere-gc '