Skip to content

Commit 28b83e6

Browse files
KarthikNayakgitster
authored andcommitted
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 <karthik.188@gmail.com> Acked-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 8c1ce22 commit 28b83e6

File tree

3 files changed

+113
-17
lines changed

3 files changed

+113
-17
lines changed

Documentation/git-maintenance.adoc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ SYNOPSIS
1212
'git maintenance' run [<options>]
1313
'git maintenance' start [--scheduler=<scheduler>]
1414
'git maintenance' (stop|register|unregister) [<options>]
15+
'git maintenance' is-needed [<options>]
1516

1617

1718
DESCRIPTION
@@ -84,6 +85,16 @@ The `unregister` subcommand will report an error if the current repository
8485
is not already registered. Use the `--force` option to return success even
8586
when the current repository is not registered.
8687

88+
is-needed::
89+
Check whether maintenance needs to be run without actually running it.
90+
Exits with a 0 status code if maintenance needs to be run, 1 otherwise.
91+
Ideally used with the '--auto' flag.
92+
+
93+
If one or more `--task` options are specified, then those tasks are checked
94+
in that order. Otherwise, the tasks are determined by which
95+
`maintenance.<task>.enabled` config options are true. By default, only
96+
`maintenance.gc.enabled` is true.
97+
8798
TASKS
8899
-----
89100

@@ -183,6 +194,8 @@ OPTIONS
183194
in the `gc.auto` config setting, or when the number of pack-files
184195
exceeds the `gc.autoPackLimit` config setting. Not compatible with
185196
the `--schedule` option.
197+
When combined with the `is-needed` subcommand, check if the required
198+
thresholds are met without actually running maintenance.
186199

187200
--schedule::
188201
When combined with the `run` subcommand, run maintenance tasks

builtin/gc.c

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3253,7 +3253,67 @@ static int maintenance_stop(int argc, const char **argv, const char *prefix,
32533253
return update_background_schedule(NULL, 0);
32543254
}
32553255

3256-
static const char * const builtin_maintenance_usage[] = {
3256+
static const char *const builtin_maintenance_is_needed_usage[] = {
3257+
"git maintenance is-needed [--task=<task>] [--schedule]",
3258+
NULL
3259+
};
3260+
3261+
static int maintenance_is_needed(int argc, const char **argv, const char *prefix,
3262+
struct repository *repo UNUSED)
3263+
{
3264+
struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
3265+
struct string_list selected_tasks = STRING_LIST_INIT_DUP;
3266+
struct gc_config cfg = GC_CONFIG_INIT;
3267+
struct option options[] = {
3268+
OPT_BOOL(0, "auto", &opts.auto_flag,
3269+
N_("run tasks based on the state of the repository")),
3270+
OPT_CALLBACK_F(0, "task", &selected_tasks, N_("task"),
3271+
N_("check a specific task"),
3272+
PARSE_OPT_NONEG, task_option_parse),
3273+
OPT_END()
3274+
};
3275+
bool is_needed = false;
3276+
3277+
argc = parse_options(argc, argv, prefix, options,
3278+
builtin_maintenance_is_needed_usage,
3279+
PARSE_OPT_STOP_AT_NON_OPTION);
3280+
if (argc)
3281+
usage_with_options(builtin_maintenance_is_needed_usage, options);
3282+
3283+
gc_config(&cfg);
3284+
initialize_task_config(&opts, &selected_tasks);
3285+
3286+
if (opts.auto_flag) {
3287+
for (size_t i = 0; i < opts.tasks_nr; i++) {
3288+
if (tasks[opts.tasks[i]].auto_condition &&
3289+
tasks[opts.tasks[i]].auto_condition(&cfg)) {
3290+
is_needed = true;
3291+
break;
3292+
}
3293+
}
3294+
} else {
3295+
/*
3296+
* When not using --auto we always require maintenance right now.
3297+
*
3298+
* TODO: this certainly is too eager, as some maintenance tasks may
3299+
* decide to not do anything because the data structures are already
3300+
* fully optimized. We may eventually want to extend the auto
3301+
* condition to also cover non-auto runs so that we can detect such
3302+
* cases.
3303+
*/
3304+
is_needed = true;
3305+
}
3306+
3307+
string_list_clear(&selected_tasks, 0);
3308+
maintenance_run_opts_release(&opts);
3309+
gc_config_release(&cfg);
3310+
3311+
if (is_needed)
3312+
return 0;
3313+
return 1;
3314+
}
3315+
3316+
static const char *const builtin_maintenance_usage[] = {
32573317
N_("git maintenance <subcommand> [<options>]"),
32583318
NULL,
32593319
};
@@ -3270,6 +3330,7 @@ int cmd_maintenance(int argc,
32703330
OPT_SUBCOMMAND("stop", &fn, maintenance_stop),
32713331
OPT_SUBCOMMAND("register", &fn, maintenance_register),
32723332
OPT_SUBCOMMAND("unregister", &fn, maintenance_unregister),
3333+
OPT_SUBCOMMAND("is-needed", &fn, maintenance_is_needed),
32733334
OPT_END(),
32743335
};
32753336

t/t7900-maintenance.sh

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ test_expect_success 'run [--auto|--quiet]' '
4949
git maintenance run --auto 2>/dev/null &&
5050
GIT_TRACE2_EVENT="$(pwd)/run-no-quiet.txt" \
5151
git maintenance run --no-quiet 2>/dev/null &&
52+
git maintenance is-needed &&
5253
test_subcommand git gc --quiet --no-detach --skip-foreground-tasks <run-no-auto.txt &&
54+
! git maintenance is-needed --auto &&
5355
test_subcommand ! git gc --auto --quiet --no-detach --skip-foreground-tasks <run-auto.txt &&
5456
test_subcommand git gc --no-quiet --no-detach --skip-foreground-tasks <run-no-quiet.txt
5557
'
@@ -180,6 +182,11 @@ test_expect_success 'commit-graph auto condition' '
180182
181183
test_commit first &&
182184
185+
! git -c maintenance.commit-graph.auto=0 \
186+
maintenance is-needed --auto --task=commit-graph &&
187+
git -c maintenance.commit-graph.auto=1 \
188+
maintenance is-needed --auto --task=commit-graph &&
189+
183190
GIT_TRACE2_EVENT="$(pwd)/cg-zero-means-no.txt" \
184191
git -c maintenance.commit-graph.auto=0 $COMMAND &&
185192
GIT_TRACE2_EVENT="$(pwd)/cg-one-satisfied.txt" \
@@ -290,16 +297,23 @@ test_expect_success 'maintenance.loose-objects.auto' '
290297
git -c maintenance.loose-objects.auto=1 maintenance \
291298
run --auto --task=loose-objects 2>/dev/null &&
292299
test_subcommand ! git prune-packed --quiet <trace-lo1.txt &&
300+
293301
printf data-A | git hash-object -t blob --stdin -w &&
302+
! git -c maintenance.loose-objects.auto=2 \
303+
maintenance is-needed --auto --task=loose-objects &&
294304
GIT_TRACE2_EVENT="$(pwd)/trace-loA" \
295305
git -c maintenance.loose-objects.auto=2 \
296306
maintenance run --auto --task=loose-objects 2>/dev/null &&
297307
test_subcommand ! git prune-packed --quiet <trace-loA &&
308+
298309
printf data-B | git hash-object -t blob --stdin -w &&
310+
git -c maintenance.loose-objects.auto=2 \
311+
maintenance is-needed --auto --task=loose-objects &&
299312
GIT_TRACE2_EVENT="$(pwd)/trace-loB" \
300313
git -c maintenance.loose-objects.auto=2 \
301314
maintenance run --auto --task=loose-objects 2>/dev/null &&
302315
test_subcommand git prune-packed --quiet <trace-loB &&
316+
303317
GIT_TRACE2_EVENT="$(pwd)/trace-loC" \
304318
git -c maintenance.loose-objects.auto=2 \
305319
maintenance run --auto --task=loose-objects 2>/dev/null &&
@@ -421,10 +435,13 @@ run_incremental_repack_and_verify () {
421435
test_commit A &&
422436
git repack -adk &&
423437
git multi-pack-index write &&
438+
! git -c maintenance.incremental-repack.auto=1 \
439+
maintenance is-needed --auto --task=incremental-repack &&
424440
GIT_TRACE2_EVENT="$(pwd)/midx-init.txt" git \
425441
-c maintenance.incremental-repack.auto=1 \
426442
maintenance run --auto --task=incremental-repack 2>/dev/null &&
427443
test_subcommand ! git multi-pack-index write --no-progress <midx-init.txt &&
444+
428445
test_commit B &&
429446
git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
430447
HEAD
@@ -434,11 +451,14 @@ run_incremental_repack_and_verify () {
434451
-c maintenance.incremental-repack.auto=2 \
435452
maintenance run --auto --task=incremental-repack 2>/dev/null &&
436453
test_subcommand ! git multi-pack-index write --no-progress <trace-A &&
454+
437455
test_commit C &&
438456
git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
439457
HEAD
440458
^HEAD~1
441459
EOF
460+
git -c maintenance.incremental-repack.auto=2 \
461+
maintenance is-needed --auto --task=incremental-repack &&
442462
GIT_TRACE2_EVENT=$(pwd)/trace-B git \
443463
-c maintenance.incremental-repack.auto=2 \
444464
maintenance run --auto --task=incremental-repack 2>/dev/null &&
@@ -485,9 +505,15 @@ test_expect_success 'reflog-expire task --auto only packs when exceeding limits'
485505
git reflog expire --all --expire=now &&
486506
test_commit reflog-one &&
487507
test_commit reflog-two &&
508+
509+
! git -c maintenance.reflog-expire.auto=3 \
510+
maintenance is-needed --auto --task=reflog-expire &&
488511
GIT_TRACE2_EVENT="$(pwd)/reflog-expire-auto.txt" \
489512
git -c maintenance.reflog-expire.auto=3 maintenance run --auto --task=reflog-expire &&
490513
test_subcommand ! git reflog expire --all <reflog-expire-auto.txt &&
514+
515+
git -c maintenance.reflog-expire.auto=2 \
516+
maintenance is-needed --auto --task=reflog-expire &&
491517
GIT_TRACE2_EVENT="$(pwd)/reflog-expire-auto.txt" \
492518
git -c maintenance.reflog-expire.auto=2 maintenance run --auto --task=reflog-expire &&
493519
test_subcommand git reflog expire --all <reflog-expire-auto.txt
@@ -514,6 +540,7 @@ test_expect_success 'worktree-prune task --auto only prunes with prunable worktr
514540
test_expect_worktree_prune ! git maintenance run --auto --task=worktree-prune &&
515541
mkdir .git/worktrees &&
516542
: >.git/worktrees/abc &&
543+
git maintenance is-needed --auto --task=worktree-prune &&
517544
test_expect_worktree_prune git maintenance run --auto --task=worktree-prune
518545
'
519546

@@ -530,22 +557,7 @@ test_expect_success 'worktree-prune task with --auto honors maintenance.worktree
530557
test_expect_worktree_prune ! git -c maintenance.worktree-prune.auto=0 maintenance run --auto --task=worktree-prune &&
531558
# A positive value should require at least this many prunable worktrees.
532559
test_expect_worktree_prune ! git -c maintenance.worktree-prune.auto=4 maintenance run --auto --task=worktree-prune &&
533-
test_expect_worktree_prune git -c maintenance.worktree-prune.auto=3 maintenance run --auto --task=worktree-prune
534-
'
535-
536-
test_expect_success 'worktree-prune task with --auto honors maintenance.worktree-prune.auto' '
537-
# A negative value should always prune.
538-
test_expect_worktree_prune git -c maintenance.worktree-prune.auto=-1 maintenance run --auto --task=worktree-prune &&
539-
540-
mkdir .git/worktrees &&
541-
: >.git/worktrees/first &&
542-
: >.git/worktrees/second &&
543-
: >.git/worktrees/third &&
544-
545-
# Zero should never prune.
546-
test_expect_worktree_prune ! git -c maintenance.worktree-prune.auto=0 maintenance run --auto --task=worktree-prune &&
547-
# A positive value should require at least this many prunable worktrees.
548-
test_expect_worktree_prune ! git -c maintenance.worktree-prune.auto=4 maintenance run --auto --task=worktree-prune &&
560+
git -c maintenance.worktree-prune.auto=3 maintenance is-needed --auto --task=worktree-prune &&
549561
test_expect_worktree_prune git -c maintenance.worktree-prune.auto=3 maintenance run --auto --task=worktree-prune
550562
'
551563

@@ -554,11 +566,13 @@ test_expect_success 'worktree-prune task honors gc.worktreePruneExpire' '
554566
rm -rf worktree &&
555567
556568
rm -f worktree-prune.txt &&
569+
! git -c gc.worktreePruneExpire=1.week.ago maintenance is-needed --auto --task=worktree-prune &&
557570
GIT_TRACE2_EVENT="$(pwd)/worktree-prune.txt" git -c gc.worktreePruneExpire=1.week.ago maintenance run --auto --task=worktree-prune &&
558571
test_subcommand ! git worktree prune --expire 1.week.ago <worktree-prune.txt &&
559572
test_path_is_dir .git/worktrees/worktree &&
560573
561574
rm -f worktree-prune.txt &&
575+
git -c gc.worktreePruneExpire=now maintenance is-needed --auto --task=worktree-prune &&
562576
GIT_TRACE2_EVENT="$(pwd)/worktree-prune.txt" git -c gc.worktreePruneExpire=now maintenance run --auto --task=worktree-prune &&
563577
test_subcommand git worktree prune --expire now <worktree-prune.txt &&
564578
test_path_is_missing .git/worktrees/worktree
@@ -583,28 +597,36 @@ test_expect_success 'rerere-gc task without --auto always collects garbage' '
583597

584598
test_expect_success 'rerere-gc task with --auto only prunes with prunable entries' '
585599
test_when_finished "rm -rf .git/rr-cache" &&
600+
! git maintenance is-needed --auto --task=rerere-gc &&
586601
test_expect_rerere_gc ! git maintenance run --auto --task=rerere-gc &&
587602
mkdir .git/rr-cache &&
603+
! git maintenance is-needed --auto --task=rerere-gc &&
588604
test_expect_rerere_gc ! git maintenance run --auto --task=rerere-gc &&
589605
: >.git/rr-cache/entry &&
606+
git maintenance is-needed --auto --task=rerere-gc &&
590607
test_expect_rerere_gc git maintenance run --auto --task=rerere-gc
591608
'
592609

593610
test_expect_success 'rerere-gc task with --auto honors maintenance.rerere-gc.auto' '
594611
test_when_finished "rm -rf .git/rr-cache" &&
595612
596613
# A negative value should always prune.
614+
git -c maintenance.rerere-gc.auto=-1 maintenance is-needed --auto --task=rerere-gc &&
597615
test_expect_rerere_gc git -c maintenance.rerere-gc.auto=-1 maintenance run --auto --task=rerere-gc &&
598616
599617
# A positive value prunes when there is at least one entry.
618+
! git -c maintenance.rerere-gc.auto=9000 maintenance is-needed --auto --task=rerere-gc &&
600619
test_expect_rerere_gc ! git -c maintenance.rerere-gc.auto=9000 maintenance run --auto --task=rerere-gc &&
601620
mkdir .git/rr-cache &&
621+
! git -c maintenance.rerere-gc.auto=9000 maintenance is-needed --auto --task=rerere-gc &&
602622
test_expect_rerere_gc ! git -c maintenance.rerere-gc.auto=9000 maintenance run --auto --task=rerere-gc &&
603623
: >.git/rr-cache/entry-1 &&
624+
git -c maintenance.rerere-gc.auto=9000 maintenance is-needed --auto --task=rerere-gc &&
604625
test_expect_rerere_gc git -c maintenance.rerere-gc.auto=9000 maintenance run --auto --task=rerere-gc &&
605626
606627
# Zero should never prune.
607628
: >.git/rr-cache/entry-1 &&
629+
! git -c maintenance.rerere-gc.auto=0 maintenance is-needed --auto --task=rerere-gc &&
608630
test_expect_rerere_gc ! git -c maintenance.rerere-gc.auto=0 maintenance run --auto --task=rerere-gc
609631
'
610632

0 commit comments

Comments
 (0)