git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v3 08/10] builtin/maintenance: extend "maintenance.strategy" to manual maintenance

From
Patrick Steinhardt <ps@pks.im>
Date
Oct 24, 2025, 06:57 UTC
Message-ID
<20251024-pks-maintenance-geometric-strategy-v3-8-9b5b3bdb4387@pks.im>
In-Reply-To
<20251024-pks-maintenance-geometric-strategy-v3-0-9b5b3bdb4387@pks.im>

The "maintenance.strategy" configuration allows users to configure how Git is supposed to perform repository maintenance. The idea is that we provide a set of high-level strategies that may be useful in different contexts, like for example when handling a large monorepo. Furthermore, the strategy can be tweaked by the user by overriding specific tasks.

In its current form though, the strategy only applies to scheduled maintenance. This creates something of a gap, as scheduled and manual maintenance will now use _different_ strategies as the latter would continue to use git-gc(1) by default. This makes the strategies way less useful than they could be on the one hand. But even more importantly, the two different strategies might clash with one another, where one of the strategies performs maintenance in such a way that it discards benefits from the other strategy.

So ideally, it should be possible to pick one strategy that then applies globally to all the different ways that we perform maintenance. This doesn't necessarily mean that the strategy always does the _same_ thing for every maintenance type. But it means that the strategy can configure the different types to work in tandem with each other.

Change the meaning of "maintenance.strategy" accordingly so that the strategy is applied to both types, manual and scheduled. As preceding commits have introduced logic to run maintenance tasks depending on this type we can tweak strategies so that they perform those tasks depending on the context.

Note that this raises the question of backwards compatibility: when the user has configured the "incremental" strategy we would have ignored that strategy beforehand. Instead, repository maintenance would have continued to use git-gc(1) by default.

But luckily, we can match that behaviour by:
  - Keeping all current tasks of the incremental strategy as
    `MAINTENANCE_TYPE_SCHEDULED`. This ensures that those tasks will not
    run during manual maintenance.
  - Configuring the "gc" task so that it is invoked during manual
    maintenance.
Like this, the user shouldn't observe any difference in behaviour.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/config/maintenance.adoc | 22 ++++++++++++-------
 builtin/gc.c                          | 25 +++++++++++++++++-----
 t/t7900-maintenance.sh                | 40 +++++++++++++++++++++++++++++++++++
 3 files changed, 74 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/maintenance.adoc b/Documentation/config/maintenance.adoc
index 45fdafc2c63..b7e90a71a3d 100644
--- a/Documentation/config/maintenance.adoc
+++ b/Documentation/config/maintenance.adoc
@@ -16,19 +16,25 @@ detach.
 
 maintenance.strategy::
 	This string config option provides a way to specify one of a few
-	recommended schedules for background maintenance. This only affects
-	which tasks are run during `git maintenance run --schedule=X`
-	commands, provided no `--task=<task>` arguments are provided.
-	Further, if a `maintenance.<task>.schedule` config value is set,
-	then that value is used instead of the one provided by
-	`maintenance.strategy`. The possible strategy strings are:
+	recommended strategies for repository maintenance. This affects
+	which tasks are run during `git maintenance run`, provided no
+	`--task=<task>` arguments are provided. This setting impacts manual
+	maintenance, auto-maintenance as well as scheduled maintenance. The
+	tasks that run may be different depending on the maintenance type.
 +
-* `none`: This default setting implies no tasks are run at any schedule.
+The maintenance strategy can be further tweaked by setting
+`maintenance.<task>.enabled` and `maintenance.<task>.schedule`. If set, these
+values are used instead of the defaults provided by `maintenance.strategy`.
++
+The possible strategies are:
++
+* `none`: This strategy implies no tasks are run at all. This is the default
+  strategy for scheduled maintenance.
 * `incremental`: This setting optimizes for performing small maintenance
   activities that do not delete any data. This does not schedule the `gc`
   task, but runs the `prefetch` and `commit-graph` tasks hourly, the
   `loose-objects` and `incremental-repack` tasks daily, and the `pack-refs`
-  task weekly.
+  task weekly. Manual repository maintenance uses the `gc` task.
 
 maintenance.<task>.enabled::
 	This boolean config option controls whether the maintenance task
diff --git a/builtin/gc.c b/builtin/gc.c
index 6cc4f98c7aa..3c0a9a2e5df 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1873,6 +1873,20 @@ static const struct maintenance_strategy incremental_strategy = {
 			.type = MAINTENANCE_TYPE_SCHEDULED,
 			.schedule = SCHEDULE_WEEKLY,
 		},
+		/*
+		 * Historically, the "incremental" strategy was only available
+		 * in the context of scheduled maintenance when set up via
+		 * "maintenance.strategy". We have later expanded that config
+		 * to also cover manual maintenance.
+		 *
+		 * To retain backwards compatibility with the previous status
+		 * quo we thus run git-gc(1) in case manual maintenance was
+		 * requested. This is the same as the default strategy, which
+		 * would have been in use beforehand.
+		 */
+		[TASK_GC] = {
+			.type = MAINTENANCE_TYPE_MANUAL,
+		},
 	},
 };
 
@@ -1916,19 +1930,20 @@ static void initialize_task_config(struct maintenance_run_opts *opts,
 	 *   - Unscheduled maintenance uses our default strategy.
 	 *
 	 * Both of these are affected by the gitconfig though, which may
-	 * override specific aspects of our strategy.
+	 * override specific aspects of our strategy. Furthermore, both
+	 * strategies can be overridden by setting "maintenance.strategy".
 	 */
 	if (opts->schedule) {
-		if (!repo_config_get_string_tmp(the_repository, "maintenance.strategy", &config_str))
-			strategy = parse_maintenance_strategy(config_str);
-		else
-			strategy = none_strategy;
+		strategy = none_strategy;
 		type = MAINTENANCE_TYPE_SCHEDULED;
 	} else {
 		strategy = default_strategy;
 		type = MAINTENANCE_TYPE_MANUAL;
 	}
 
+	if (!repo_config_get_string_tmp(the_repository, "maintenance.strategy", &config_str))
+		strategy = parse_maintenance_strategy(config_str);
+
 	for (size_t i = 0; i < TASK__COUNT; i++) {
 		int config_value;
 
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 0fb917dd7b7..5219bc17a69 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -886,6 +886,46 @@ test_expect_success 'maintenance.strategy inheritance' '
 		<modified-daily.txt
 '
 
+test_strategy () {
+	STRATEGY="$1"
+	shift
+
+	cat >expect &&
+	rm -f trace2.txt &&
+	GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+		git -c maintenance.strategy=$STRATEGY maintenance run --quiet "$@" &&
+	sed -n 's/{"event":"child_start","sid":"[^/"]*",.*,"argv":\["\(.*\)\"]}/\1/p' <trace2.txt |
+		sed 's/","/ /g'  >actual
+	test_cmp expect actual
+}
+
+test_expect_success 'maintenance.strategy is respected' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit initial &&
+
+		test_must_fail git -c maintenance.strategy=unknown maintenance run 2>err &&
+		test_grep "unknown maintenance strategy: .unknown." err &&
+
+		test_strategy incremental <<-\EOF &&
+		git pack-refs --all --prune
+		git reflog expire --all
+		git gc --quiet --no-detach --skip-foreground-tasks
+		EOF
+
+		test_strategy incremental --schedule=weekly <<-\EOF
+		git pack-refs --all --prune
+		git prune-packed --quiet
+		git multi-pack-index write --no-progress
+		git multi-pack-index expire --no-progress
+		git multi-pack-index repack --no-progress --batch-size=1
+		git commit-graph write --split --reachable --no-progress
+		EOF
+	)
+'
+
 test_expect_success 'register and unregister' '
 	test_when_finished git config --global --unset-all maintenance.repo &&
 
-- 
2.51.1.930.gacf6e81ea2.dirty
Previous: Patrick SteinhardtNext: Patrick Steinhardt
Message 51 of 69 in “builtin/maintenance: introduce "geometric" strategy”
  1. 0/8 builtin/maintenance: introduce "geometric" strategyPatrick Steinhardt, Oct 16, 2025
  2. 1/8 builtin/gc: remove global `repack` variablePatrick Steinhardt, Oct 16, 2025
  3. Justin ToblerOct 16, 2025
  4. Taylor BlauOct 17, 2025
  5. 2/8 builtin/gc: make `too_many_loose_objects()` reusable without GC configPatrick Steinhardt, Oct 16, 2025
  6. Junio C HamanoOct 16, 2025
  7. 3/8 builtin/maintenance: introduce "geometric-repack" taskPatrick Steinhardt, Oct 16, 2025
  8. Justin ToblerOct 16, 2025
  9. Patrick SteinhardtOct 17, 2025
  10. Taylor BlauOct 17, 2025
  11. Patrick SteinhardtOct 21, 2025
  12. Taylor BlauOct 23, 2025
  13. Patrick SteinhardtOct 24, 2025
  14. 4/8 builtin/maintenance: don't silently ignore invalid strategyPatrick Steinhardt, Oct 16, 2025
  15. 5/8 builtin/maintenance: run maintenance tasks depending on typePatrick Steinhardt, Oct 16, 2025
  16. 6/8 builtin/maintenance: extend "maintenance.strategy" to manual maintenancePatrick Steinhardt, Oct 16, 2025
  17. 7/8 builtin/maintenance: make "gc" strategy accessiblePatrick Steinhardt, Oct 16, 2025
  18. 8/8 builtin/maintenance: introduce "geometric" strategyPatrick Steinhardt, Oct 16, 2025
  19. 0/9 builtin/maintenance: introduce "geometric" strategyPatrick Steinhardt, Oct 21, 2025
  20. 1/9 builtin/gc: remove global `repack` variablePatrick Steinhardt, Oct 21, 2025
  21. 2/9 builtin/gc: make `too_many_loose_objects()` reusable without GC configPatrick Steinhardt, Oct 21, 2025
  22. 3/9 builtin/maintenance: introduce "geometric-repack" taskPatrick Steinhardt, Oct 21, 2025
  23. Taylor BlauOct 23, 2025
  24. Patrick SteinhardtOct 24, 2025
  25. 4/9 builtin/maintenance: make the geometric factor configurablePatrick Steinhardt, Oct 21, 2025
  26. Taylor BlauOct 23, 2025
  27. Patrick SteinhardtOct 24, 2025
  28. Taylor BlauOct 24, 2025
  29. 5/9 builtin/maintenance: don't silently ignore invalid strategyPatrick Steinhardt, Oct 21, 2025
  30. Taylor BlauOct 23, 2025
  31. 6/9 builtin/maintenance: run maintenance tasks depending on typePatrick Steinhardt, Oct 21, 2025
  32. Taylor BlauOct 23, 2025
  33. 7/9 builtin/maintenance: extend "maintenance.strategy" to manual maintenancePatrick Steinhardt, Oct 21, 2025
  34. 8/9 builtin/maintenance: make "gc" strategy accessiblePatrick Steinhardt, Oct 21, 2025
  35. 9/9 builtin/maintenance: introduce "geometric" strategyPatrick Steinhardt, Oct 21, 2025
  36. Taylor BlauOct 23, 2025
  37. Patrick SteinhardtOct 24, 2025
  38. Junio C HamanoOct 23, 2025
  39. Taylor BlauOct 23, 2025
  40. 00/10 builtin/maintenance: introduce "geometric" strategyPatrick Steinhardt, Oct 24, 2025
  41. 01/10 builtin/gc: remove global `repack` variablePatrick Steinhardt, Oct 24, 2025
  42. 02/10 builtin/gc: make `too_many_loose_objects()` reusable without GC configPatrick Steinhardt, Oct 24, 2025
  43. 03/10 builtin/maintenance: introduce "geometric-repack" taskPatrick Steinhardt, Oct 24, 2025
  44. Jeff KingOct 25, 2025
  45. Patrick SteinhardtOct 27, 2025
  46. Jeff KingOct 27, 2025
  47. 04/10 builtin/maintenance: make the geometric factor configurablePatrick Steinhardt, Oct 24, 2025
  48. 05/10 builtin/maintenance: don't silently ignore invalid strategyPatrick Steinhardt, Oct 24, 2025
  49. 06/10 builtin/maintenance: improve readability of strategiesPatrick Steinhardt, Oct 24, 2025
  50. 07/10 builtin/maintenance: run maintenance tasks depending on typePatrick Steinhardt, Oct 24, 2025
  51. 08/10 builtin/maintenance: extend "maintenance.strategy" to manual maintenancePatrick Steinhardt, Oct 24, 2025
  52. 09/10 builtin/maintenance: make "gc" strategy accessiblePatrick Steinhardt, Oct 24, 2025
  53. 10/10 builtin/maintenance: introduce "geometric" strategyPatrick Steinhardt, Oct 24, 2025
  54. Taylor BlauOct 24, 2025
  55. Junio C HamanoOct 24, 2025
  56. 00/10 builtin/maintenance: introduce "geometric" strategyPatrick Steinhardt, Oct 27, 2025
  57. 01/10 builtin/gc: remove global `repack` variablePatrick Steinhardt, Oct 27, 2025
  58. 02/10 builtin/gc: make `too_many_loose_objects()` reusable without GC configPatrick Steinhardt, Oct 27, 2025
  59. 03/10 builtin/maintenance: introduce "geometric-repack" taskPatrick Steinhardt, Oct 27, 2025
  60. 04/10 builtin/maintenance: make the geometric factor configurablePatrick Steinhardt, Oct 27, 2025
  61. 05/10 builtin/maintenance: don't silently ignore invalid strategyPatrick Steinhardt, Oct 27, 2025
  62. 06/10 builtin/maintenance: improve readability of strategiesPatrick Steinhardt, Oct 27, 2025
  63. 07/10 builtin/maintenance: run maintenance tasks depending on typePatrick Steinhardt, Oct 27, 2025
  64. 08/10 builtin/maintenance: extend "maintenance.strategy" to manual maintenancePatrick Steinhardt, Oct 27, 2025
  65. 09/10 builtin/maintenance: make "gc" strategy accessiblePatrick Steinhardt, Oct 27, 2025
  66. 10/10 builtin/maintenance: introduce "geometric" strategyPatrick Steinhardt, Oct 27, 2025
  67. Junio C HamanoOct 27, 2025
  68. Patrick SteinhardtOct 27, 2025
  69. Junio C HamanoOct 27, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.