{"thread":{"id":"65029","subject":"[PATCH 0/8] builtin/maintenance: use \"geometric\" strategy by default","startedAt":"2026-02-20T10:15:24Z","lastAt":"2026-08-10T15:12:58Z","messageCount":38,"participants":["Patrick Steinhardt","Derrick Stolee","Justin Tobler","Toon Claes","Stefan Haller"],"isPatch":true,"patchVersion":1,"patchTotal":8},"messages":[{"id":"536497","messageId":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-0-faeb321ad13b@pks.im","threadId":"65029","inReplyTo":null,"subject":"[PATCH 0/8] builtin/maintenance: use \"geometric\" strategy by default","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-20T10:15:04Z","receivedAt":"2026-02-20T10:15:24Z","isPatch":true,"body":"Hi,\n\nthis series converts our default strategy used by git-maintenance(1)\nfrom \"gc\" to \"geometric\". The aim of this is twofold:\n\n  - It completes the conversion to a more flexible infrastructure for\n    repository maintenance. git-maintenance(1) is structured around\n    tasks that can be toggled on/off as needed, and this is a lot easier\n    to extend going forward.\n\n  - We start to use a more efficient repacking strategy by default,\n    which should especially help large repositories out there.\n\nOut of these two, I think that the first point is actually the more\nimportant one.\n\nUnfortunately, a lot of our tests are racy or will fail with the new\nstrategy. This is mostly because the new strategy may decide to optimize\ndata structures in cases where the old strategy didn't, and because the\ntasks we perform might be different. The majority of this patch series\nthus adapts our tests accordingly. The actual change is a one-line\nchange in the final commit.\n\nI was a bit torn initially whether or not I want to make the geometric\nstrategy the default right away, or whether we might first want to use\n\"feature.experimental\" as an additional step. I'm quite happy to adapt\nthe series accordingly, but for the initial version I thought it might\ninvite more discussions if I pick the nuclear option :)\n\nOf course, no matter how we do this, it is still possible to revert back\nto the old strategy by setting \"maintenance.strategy=gc\".\n\nThanks!\n\nPatrick\n\n---\nPatrick Steinhardt (8):\n      t: fix races caused by background maintenance\n      t: disable maintenance where we verify object database structure\n      t34xx: don't expire reflogs where it matters\n      t5400: explicitly use \"gc\" strategy\n      t5510: explicitly use \"gc\" strategy\n      t6500: explicitly use \"gc\" strategy\n      t7900: prepare for switch of the default strategy\n      builtin/maintenance: use \"geometric\" strategy by default\n\n builtin/gc.c                            | 2 +-\n run-command.c                           | 2 +-\n t/t0081-find-pack.sh                    | 1 +\n t/t3404-rebase-interactive.sh           | 2 ++\n t/t3406-rebase-message.sh               | 3 +++\n t/t3431-rebase-fork-point.sh            | 2 ++\n t/t3432-rebase-fast-forward.sh          | 2 ++\n t/t5316-pack-delta-depth.sh             | 1 +\n t/t5319-multi-pack-index.sh             | 1 +\n t/t5326-multi-pack-bitmaps.sh           | 3 ++-\n t/t5327-multi-pack-bitmaps-rev.sh       | 3 ++-\n t/t5331-pack-objects-stdin.sh           | 2 ++\n t/t5332-multi-pack-reuse.sh             | 1 +\n t/t5334-incremental-multi-pack-index.sh | 1 +\n t/t5400-send-pack.sh                    | 1 +\n t/t5500-fetch-pack.sh                   | 3 ++-\n t/t5510-fetch.sh                        | 1 +\n t/t5616-partial-clone.sh                | 7 ++++---\n t/t6500-gc.sh                           | 1 +\n t/t7700-repack.sh                       | 3 +++\n t/t7900-maintenance.sh                  | 7 ++++++-\n t/test-lib.sh                           | 4 ++++\n 22 files changed, 44 insertions(+), 9 deletions(-)\n\n\n---\nbase-commit: 73fd77805fc6406f31c36212846d9e2541d19321\nchange-id: 20260218-b4-pks-maintenance-default-geometric-strategy-17fcedf92461\n\n"},{"id":"536498","messageId":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-1-faeb321ad13b@pks.im","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-0-faeb321ad13b@pks.im","subject":"[PATCH 1/8] t: fix races caused by background maintenance","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-20T10:15:05Z","receivedAt":"2026-02-20T10:15:26Z","isPatch":true,"body":"Many Git commands spawn git-maintenance(1) to optimize the repository in\nthe background. By default, performing the maintenance is for most of\nthe part asynchronous: we fork the executable and then continue with the\nrest of our business logic.\n\nThis is working as expected for our users, but this behaviour is\nsomewhat problematic for our test suite as this is inherently racy. We\nhave many tests that verify the on-disk state of repositories, and those\ntests may easily race with our background maintenance. In a similar\nfashion, we may end up with processes that \"leak\" out of a current test\ncase.\n\nUntil now this tends to not be much of a problem. Our maintenance uses\ngit-gc(1) by default, which knows to bail out in case there aren't\neither too many packfiles or too many loose objects. So even if other\ndata structures would need to be optimized, we won't do so unless the\nobject database also needs optimizations.\n\nThis is about to change though, as a subsequent commit will switch to\nthe \"geometric\" maintenance strategy as a default. The consequence is\nthat we will run required optimizations even if the object database is\nwell-optimized. And this uncovers races between our test suite and\nbackground maintenance all over the place.\n\nDisabling maintenance outright in our test suite is not really an\noption, as it would result in significantly divergence from the \"real\nworld\" and reduce our test coverage. But we've got an alternative up our\nsleeves: we can ensure that garbage collection runs synchronously by\noverriding the \"maintenance.autoDetach\" configuration.\n\nOf course that also diverges from the real world, as we now stop testing\nthat background maintenance interacts in a benign way with normal Git\ncommands. But on the other hand this ensures that the maintenance itself\ndoes not for example lead to data loss in a more reproducible way.\n\nAnother concern is that this would make execution of the test suite much\nslower. But a quick benchmark on my machine demonstrates that this does\nnot seem to be the case:\n\n    Benchmark 1: meson test (revision = HEAD~)\n      Time (mean ± σ):     131.182 s ±  1.293 s    [User: 853.737 s, System: 1160.479 s]\n      Range (min … max):   130.001 s … 132.563 s    3 runs\n\n    Benchmark 2: meson test (revision = HEAD)\n      Time (mean ± σ):     129.554 s ±  0.507 s    [User: 849.040 s, System: 1152.664 s]\n      Range (min … max):   129.000 s … 129.994 s    3 runs\n\n    Summary\n      meson test (revision = HEAD) ran\n        1.01 ± 0.01 times faster than meson test (revision = HEAD~)\n\nFunny enough, it even seems as if this speeds up test execution ever so\nslightly, but that may just as well be noise.\n\nIntroduce a new `GIT_TEST_MAINT_AUTO_DETACH` environment variable that\nallows us to override the auto-detach behaviour and set that varibale in\nour tests.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n run-command.c            | 2 +-\n t/t5616-partial-clone.sh | 6 +++---\n t/t7900-maintenance.sh   | 1 +\n t/test-lib.sh            | 4 ++++\n 4 files changed, 9 insertions(+), 4 deletions(-)\n\ndiff --git a/run-command.c b/run-command.c\nindex e3e02475cc..438a290d30 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -1828,7 +1828,7 @@ int prepare_auto_maintenance(int quiet, struct child_process *maint)\n \t */\n \tif (repo_config_get_bool(the_repository, \"maintenance.autodetach\", &auto_detach) &&\n \t    repo_config_get_bool(the_repository, \"gc.autodetach\", &auto_detach))\n-\t\tauto_detach = 1;\n+\t\tauto_detach = git_env_bool(\"GIT_TEST_MAINT_AUTO_DETACH\", true);\n \n \tmaint->git_cmd = 1;\n \tmaint->close_object_store = 1;\ndiff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh\nindex 1e354e057f..d62760eb92 100755\n--- a/t/t5616-partial-clone.sh\n+++ b/t/t5616-partial-clone.sh\n@@ -229,7 +229,7 @@ test_expect_success 'fetch --refetch triggers repacking' '\n \n \tGIT_TRACE2_EVENT=\"$PWD/trace1.event\" \\\n \tgit -C pc1 fetch --refetch origin &&\n-\ttest_subcommand git maintenance run --auto --no-quiet --detach <trace1.event &&\n+\ttest_subcommand git maintenance run --auto --no-quiet --no-detach <trace1.event &&\n \tgrep \\\"param\\\":\\\"gc.autopacklimit\\\",\\\"value\\\":\\\"1\\\" trace1.event &&\n \tgrep \\\"param\\\":\\\"maintenance.incremental-repack.auto\\\",\\\"value\\\":\\\"-1\\\" trace1.event &&\n \n@@ -238,7 +238,7 @@ test_expect_success 'fetch --refetch triggers repacking' '\n \t\t-c gc.autoPackLimit=0 \\\n \t\t-c maintenance.incremental-repack.auto=1234 \\\n \t\t-C pc1 fetch --refetch origin &&\n-\ttest_subcommand git maintenance run --auto --no-quiet --detach <trace2.event &&\n+\ttest_subcommand git maintenance run --auto --no-quiet --no-detach <trace2.event &&\n \tgrep \\\"param\\\":\\\"gc.autopacklimit\\\",\\\"value\\\":\\\"0\\\" trace2.event &&\n \tgrep \\\"param\\\":\\\"maintenance.incremental-repack.auto\\\",\\\"value\\\":\\\"-1\\\" trace2.event &&\n \n@@ -247,7 +247,7 @@ test_expect_success 'fetch --refetch triggers repacking' '\n \t\t-c gc.autoPackLimit=1234 \\\n \t\t-c maintenance.incremental-repack.auto=0 \\\n \t\t-C pc1 fetch --refetch origin &&\n-\ttest_subcommand git maintenance run --auto --no-quiet --detach <trace3.event &&\n+\ttest_subcommand git maintenance run --auto --no-quiet --no-detach <trace3.event &&\n \tgrep \\\"param\\\":\\\"gc.autopacklimit\\\",\\\"value\\\":\\\"1\\\" trace3.event &&\n \tgrep \\\"param\\\":\\\"maintenance.incremental-repack.auto\\\",\\\"value\\\":\\\"0\\\" trace3.event\n '\ndiff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\nindex 7cc0ce57f8..d11d6f8f15 100755\n--- a/t/t7900-maintenance.sh\n+++ b/t/t7900-maintenance.sh\n@@ -6,6 +6,7 @@ test_description='git maintenance builtin'\n \n GIT_TEST_COMMIT_GRAPH=0\n GIT_TEST_MULTI_PACK_INDEX=0\n+sane_unset GIT_TEST_MAINT_AUTO_DETACH\n \n test_lazy_prereq XMLLINT '\n \txmllint --version\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 0fb76f7d11..aa805a01ce 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -1947,6 +1947,10 @@ test_lazy_prereq COMPAT_HASH '\n GIT_TEST_MAINT_SCHEDULER=\"none:exit 1\"\n export GIT_TEST_MAINT_SCHEDULER\n \n+# Ensure that tests cannot race with background maintenance by default.\n+GIT_TEST_MAINT_AUTO_DETACH=\"false\"\n+export GIT_TEST_MAINT_AUTO_DETACH\n+\n # Does this platform support `git fsmonitor--daemon`\n #\n test_lazy_prereq FSMONITOR_DAEMON '\n\n-- \n2.53.0.414.gf7e9f6c205.dirty\n\n"},{"id":"536499","messageId":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-2-faeb321ad13b@pks.im","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-0-faeb321ad13b@pks.im","subject":"[PATCH 2/8] t: disable maintenance where we verify object database structure","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-20T10:15:06Z","receivedAt":"2026-02-20T10:15:28Z","isPatch":true,"body":"We have a couple of tests that explicitly verify the structure of the\nobject database. Naturally, this structure is dependent on whether or\nnot we run repository maintenance: if it decides to optimize the object\ndatabase the expected structure is likely to not materialize.\n\nExplicitly disable auto-maintenance in such tests so that we are not\ndependent on decisions made by our maintenance.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t0081-find-pack.sh                    | 1 +\n t/t5316-pack-delta-depth.sh             | 1 +\n t/t5319-multi-pack-index.sh             | 1 +\n t/t5326-multi-pack-bitmaps.sh           | 3 ++-\n t/t5327-multi-pack-bitmaps-rev.sh       | 3 ++-\n t/t5331-pack-objects-stdin.sh           | 2 ++\n t/t5332-multi-pack-reuse.sh             | 1 +\n t/t5334-incremental-multi-pack-index.sh | 1 +\n t/t5500-fetch-pack.sh                   | 3 ++-\n t/t5616-partial-clone.sh                | 1 +\n t/t7700-repack.sh                       | 3 +++\n 11 files changed, 17 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t0081-find-pack.sh b/t/t0081-find-pack.sh\nindex 5a628bf735..26f017422d 100755\n--- a/t/t0081-find-pack.sh\n+++ b/t/t0081-find-pack.sh\n@@ -68,6 +68,7 @@ test_expect_success 'add more packfiles' '\n '\n \n test_expect_success 'add more commits (as loose objects)' '\n+\ttest_config maintenance.auto false &&\n \ttest_commit six &&\n \ttest_commit seven &&\n \ndiff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh\nindex 03dfb7a61e..8a067a45cb 100755\n--- a/t/t5316-pack-delta-depth.sh\n+++ b/t/t5316-pack-delta-depth.sh\n@@ -48,6 +48,7 @@ test_description='pack-objects breaks long cross-pack delta chains'\n # repeatedly-modified file to generate the delta chain).\n \n test_expect_success 'create series of packs' '\n+\ttest_config maintenance.auto false &&\n \ttest-tool genrandom foo 4096 >content &&\n \tprev= &&\n \tfor i in $(test_seq 1 10)\ndiff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh\nindex faae98c7e7..7672d599d4 100755\n--- a/t/t5319-multi-pack-index.sh\n+++ b/t/t5319-multi-pack-index.sh\n@@ -1315,6 +1315,7 @@ test_expect_success 'bitmapped packs are stored via the BTMP chunk' '\n \tgit init repo &&\n \t(\n \t\tcd repo &&\n+\t\tgit config set maintenance.auto false &&\n \n \t\tfor i in 1 2 3 4 5\n \t\tdo\ndiff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh\nindex 892aeb09e4..62bd973d92 100755\n--- a/t/t5326-multi-pack-bitmaps.sh\n+++ b/t/t5326-multi-pack-bitmaps.sh\n@@ -93,7 +93,8 @@ test_midx_bitmap_cases () {\n \ttest_expect_success 'setup test_repository' '\n \t\trm -rf * .git &&\n \t\tgit init &&\n-\t\tgit config pack.writeBitmapLookupTable '\"$writeLookupTable\"'\n+\t\tgit config pack.writeBitmapLookupTable '\"$writeLookupTable\"' &&\n+\t\tgit config maintenance.auto false\n \t'\n \n \tmidx_bitmap_core\ndiff --git a/t/t5327-multi-pack-bitmaps-rev.sh b/t/t5327-multi-pack-bitmaps-rev.sh\nindex 9cac03a94b..cfa12de2a8 100755\n--- a/t/t5327-multi-pack-bitmaps-rev.sh\n+++ b/t/t5327-multi-pack-bitmaps-rev.sh\n@@ -30,7 +30,8 @@ test_midx_bitmap_rev () {\n \ttest_expect_success 'setup bitmap config' '\n \t\trm -rf * .git &&\n \t\tgit init &&\n-\t\tgit config pack.writeBitmapLookupTable '\"$writeLookupTable\"'\n+\t\tgit config pack.writeBitmapLookupTable '\"$writeLookupTable\"' &&\n+\t\tgit config maintenance.auto false\n \t'\n \n \tmidx_bitmap_core rev\ndiff --git a/t/t5331-pack-objects-stdin.sh b/t/t5331-pack-objects-stdin.sh\nindex cd949025b9..b03f6be164 100755\n--- a/t/t5331-pack-objects-stdin.sh\n+++ b/t/t5331-pack-objects-stdin.sh\n@@ -14,6 +14,7 @@ packed_objects () {\n \n test_expect_success 'setup for --stdin-packs tests' '\n \tgit init stdin-packs &&\n+\tgit -C stdin-packs config set maintenance.auto false &&\n \t(\n \t\tcd stdin-packs &&\n \n@@ -255,6 +256,7 @@ test_expect_success '--stdin-packs=follow walks into unknown packs' '\n \tgit init repo &&\n \t(\n \t\tcd repo &&\n+\t\tgit config set maintenance.auto false &&\n \n \t\tfor c in A B C D\n \t\tdo\ndiff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh\nindex 395d09444c..881ce668e1 100755\n--- a/t/t5332-multi-pack-reuse.sh\n+++ b/t/t5332-multi-pack-reuse.sh\n@@ -59,6 +59,7 @@ test_pack_objects_reused () {\n \n test_expect_success 'preferred pack is reused for single-pack reuse' '\n \ttest_config pack.allowPackReuse single &&\n+\tgit config set maintenance.auto false &&\n \n \tfor i in A B\n \tdo\ndiff --git a/t/t5334-incremental-multi-pack-index.sh b/t/t5334-incremental-multi-pack-index.sh\nindex d30d7253d6..99c7d44d8e 100755\n--- a/t/t5334-incremental-multi-pack-index.sh\n+++ b/t/t5334-incremental-multi-pack-index.sh\n@@ -15,6 +15,7 @@ midx_chain=$midxdir/multi-pack-index-chain\n \n test_expect_success 'convert non-incremental MIDX to incremental' '\n \ttest_commit base &&\n+\tgit config set maintenance.auto false &&\n \tgit repack -ad &&\n \tgit multi-pack-index write &&\n \ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex 4bb56c167a..0c88d04d0a 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -154,7 +154,8 @@ test_expect_success 'clone shallow depth 1 with fsck' '\n '\n \n test_expect_success 'clone shallow' '\n-\tgit clone --no-single-branch --depth 2 \"file://$(pwd)/.\" shallow\n+\tgit clone --no-single-branch --depth 2 \"file://$(pwd)/.\" shallow &&\n+\tgit -C shallow config set maintenance.auto false\n '\n \n test_expect_success 'clone shallow depth count' '\ndiff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh\nindex d62760eb92..1c2805acca 100755\n--- a/t/t5616-partial-clone.sh\n+++ b/t/t5616-partial-clone.sh\n@@ -585,6 +585,7 @@ test_expect_success 'verify fetch downloads only one pack when updating refs' '\n \tgit clone --filter=blob:none \"file://$(pwd)/srv.bare\" pack-test &&\n \tls pack-test/.git/objects/pack/*pack >pack-list &&\n \ttest_line_count = 2 pack-list &&\n+\ttest_config -C pack-test maintenance.auto false &&\n \tfor i in A B C\n \tdo\n \t\ttest_commit -C src $i &&\ndiff --git a/t/t7700-repack.sh b/t/t7700-repack.sh\nindex 73b78bdd88..acc2589f21 100755\n--- a/t/t7700-repack.sh\n+++ b/t/t7700-repack.sh\n@@ -217,6 +217,7 @@ test_expect_success 'repack --keep-pack' '\n \t\tcd keep-pack &&\n \t\t# avoid producing different packs due to delta/base choices\n \t\tgit config pack.window 0 &&\n+\t\tgit config maintenance.auto false &&\n \t\tP1=$(commit_and_pack 1) &&\n \t\tP2=$(commit_and_pack 2) &&\n \t\tP3=$(commit_and_pack 3) &&\n@@ -260,6 +261,7 @@ test_expect_success 'repacking fails when missing .pack actually means missing o\n \n \t\t# Avoid producing different packs due to delta/base choices\n \t\tgit config pack.window 0 &&\n+\t\tgit config maintenance.auto false &&\n \t\tP1=$(commit_and_pack 1) &&\n \t\tP2=$(commit_and_pack 2) &&\n \t\tP3=$(commit_and_pack 3) &&\n@@ -534,6 +536,7 @@ test_expect_success 'setup for --write-midx tests' '\n \t(\n \t\tcd midx &&\n \t\tgit config core.multiPackIndex true &&\n+\t\tgit config maintenance.auto false &&\n \n \t\ttest_commit base\n \t)\n\n-- \n2.53.0.414.gf7e9f6c205.dirty\n\n"},{"id":"536500","messageId":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-3-faeb321ad13b@pks.im","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-0-faeb321ad13b@pks.im","subject":"[PATCH 3/8] t34xx: don't expire reflogs where it matters","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-20T10:15:07Z","receivedAt":"2026-02-20T10:15:31Z","isPatch":true,"body":"We have a couple of tests in the t34xx range that rely on reflogs. This\nnever really used to be a problem, but in a subsequent commit we will\nchange the default maintenance strategy from \"gc\" to \"geometric\", and\nthis will cause us to drop all reflogs in these tests.\n\nThis may seem surprising and like a bug at first, but it's actually not.\nThe main difference between these two strategies is that the \"gc\"\nstrategy will skip all maintenance in case the object database is in a\nwell-optimized state. The \"geometric\" strategy has separate subtasks\nthough, and the conditions for each of these tasks is evaluated on a\ncase by case basis. This means that even if the object database is in\ngood shape, we may still decide to expire reflogs.\n\nSo why is that a problem? The issue is that Git's test suite hardcodes\nthe committer and author dates to a date in 2005. Interestingly though,\nthese hardcoded dates not only impact the commits, but also the reflog\nentries. The consequence is that all newly written reflog entries are\nimmediately considered stale as our reflog expiration threshold is in\nthe range of weeks, only. It follows that executing `git reflog expire`\nwill thus immediately purge all reflog entries.\n\nThis hasn't been a problem in our test suite by pure chance, as the\nrepository shapes simply didn't cause us to perform actual garbage\ncollection. But with the upcoming \"geometric\" strategy we _will_ start\nto execute `git reflog expire`, thus surfacing this issue.\n\nPrepare for this by explicitly disabling reflog expiration in tests\nimpacted by this upcoming change.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t3404-rebase-interactive.sh  | 2 ++\n t/t3406-rebase-message.sh      | 3 +++\n t/t3431-rebase-fork-point.sh   | 2 ++\n t/t3432-rebase-fast-forward.sh | 2 ++\n 4 files changed, 9 insertions(+)\n\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex e778dd8ae4..5e4623f7f1 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -31,6 +31,8 @@ Initial setup:\n . \"$TEST_DIRECTORY\"/lib-rebase.sh\n \n test_expect_success 'setup' '\n+\tgit config set gc.reflogExpire never &&\n+\tgit config set gc.reflogExpireUnreachable never &&\n \tgit switch -C primary &&\n \ttest_commit A file1 &&\n \ttest_commit B file1 &&\ndiff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh\nindex a1d7fa7f7c..f89209c8d9 100755\n--- a/t/t3406-rebase-message.sh\n+++ b/t/t3406-rebase-message.sh\n@@ -8,6 +8,9 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n+\tgit config set gc.reflogExpire never &&\n+\tgit config set gc.reflogExpireUnreachable never &&\n+\n \ttest_commit O fileO &&\n \ttest_commit X fileX &&\n \tgit branch fast-forward &&\ndiff --git a/t/t3431-rebase-fork-point.sh b/t/t3431-rebase-fork-point.sh\nindex be09fc78c1..3a3c3a70a5 100755\n--- a/t/t3431-rebase-fork-point.sh\n+++ b/t/t3431-rebase-fork-point.sh\n@@ -17,6 +17,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n # C was formerly part of main but main was rewound to remove C\n #\n test_expect_success setup '\n+\tgit config set gc.reflogExpire never &&\n+\tgit config set gc.reflogExpireUnreachable never &&\n \ttest_commit A &&\n \ttest_commit B &&\n \ttest_commit C &&\ndiff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh\nindex 5086e14c02..6e8de6c7aa 100755\n--- a/t/t3432-rebase-fast-forward.sh\n+++ b/t/t3432-rebase-fast-forward.sh\n@@ -11,6 +11,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n . ./test-lib.sh\n \n test_expect_success setup '\n+\tgit config set gc.reflogExpire never &&\n+\tgit config set gc.reflogExpireUnreachable never &&\n \ttest_commit A &&\n \ttest_commit B &&\n \ttest_commit C &&\n\n-- \n2.53.0.414.gf7e9f6c205.dirty\n\n"},{"id":"536501","messageId":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-4-faeb321ad13b@pks.im","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-0-faeb321ad13b@pks.im","subject":"[PATCH 4/8] t5400: explicitly use \"gc\" strategy","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-20T10:15:08Z","receivedAt":"2026-02-20T10:15:33Z","isPatch":true,"body":"In t5400 we verify that git-receive-pack(1) runs automated repository\nmaintenance in the remote repository. The check is performed indirectly\nby observing an effect that git-gc(1) would have, namely to prune a\ntemporary object from the object database. In a subsequent commit we're\nabout to switch to the \"geometric\" strategy by default though, and here\nwe stop observing that effect.\n\nAdapt the test to explicitly use the \"gc\" strategy to prepare for that\nupcoming change.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t5400-send-pack.sh | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh\nindex 83b42ff073..b32a0a6aa7 100755\n--- a/t/t5400-send-pack.sh\n+++ b/t/t5400-send-pack.sh\n@@ -187,6 +187,7 @@ test_expect_success 'receive-pack runs auto-gc in remote repo' '\n \t\tcd child &&\n \t\tgit config gc.autopacklimit 1 &&\n \t\tgit config gc.autodetach false &&\n+\t\tgit config maintenance.strategy gc &&\n \t\tgit branch test_auto_gc &&\n \t\t# And create a file that follows the temporary object naming\n \t\t# convention for the auto-gc to remove\n\n-- \n2.53.0.414.gf7e9f6c205.dirty\n\n"},{"id":"536502","messageId":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-5-faeb321ad13b@pks.im","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-0-faeb321ad13b@pks.im","subject":"[PATCH 5/8] t5510: explicitly use \"gc\" strategy","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-20T10:15:09Z","receivedAt":"2026-02-20T10:15:37Z","isPatch":true,"body":"One of the tests in t5510 wants to verify that auto-gc does not lock up\nwhen fetching into a repository. Adapt it to explicitly pick the \"gc\"\nstrategy for auto-maintenance.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t5510-fetch.sh | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex c69afb5a60..5dcb4b51a4 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -1321,6 +1321,7 @@ test_expect_success 'fetching with auto-gc does not lock up' '\n \t\tgit config fetch.unpackLimit 1 &&\n \t\tgit config gc.autoPackLimit 1 &&\n \t\tgit config gc.autoDetach false &&\n+\t\tgit config maintenance.strategy gc &&\n \t\tGIT_ASK_YESNO=\"$TRASH_DIRECTORY/askyesno\" git fetch --verbose >fetch.out 2>&1 &&\n \t\ttest_grep \"Auto packing the repository\" fetch.out &&\n \t\t! grep \"Should I try again\" fetch.out\n\n-- \n2.53.0.414.gf7e9f6c205.dirty\n\n"},{"id":"536503","messageId":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-6-faeb321ad13b@pks.im","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-0-faeb321ad13b@pks.im","subject":"[PATCH 6/8] t6500: explicitly use \"gc\" strategy","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-20T10:15:10Z","receivedAt":"2026-02-20T10:15:39Z","isPatch":true,"body":"The test in t6500 explicitly wants to exercise git-gc(1) and is thus\nhighly specific to the actual on-disk state of the repository and\nspecifically of the object database. An upcoming change modifies the\ndefault maintenance strategy to be the \"geometric\" strategy though,\nwhich breaks a couple of assumptions.\n\nOne fix would arguably be to disable auto-maintenance altogether, as we\ndo want to explicitly verify git-gc(1) anyway. But as the whole test\nsuite is about git-gc(1) in the first place it feels more sensible to\nconfigure the default maintenance strategy to be \"gc\".\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t6500-gc.sh | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/t/t6500-gc.sh b/t/t6500-gc.sh\nindex bef472cb8d..ea9aaad470 100755\n--- a/t/t6500-gc.sh\n+++ b/t/t6500-gc.sh\n@@ -11,6 +11,7 @@ test_expect_success 'setup' '\n \t# behavior, make sure we always pack everything to one pack by\n \t# default\n \tgit config gc.bigPackThreshold 2g &&\n+\tgit config set --global maintenance.strategy gc &&\n \ttest_oid_init\n '\n \n\n-- \n2.53.0.414.gf7e9f6c205.dirty\n\n"},{"id":"536504","messageId":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-7-faeb321ad13b@pks.im","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-0-faeb321ad13b@pks.im","subject":"[PATCH 7/8] t7900: prepare for switch of the default strategy","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-20T10:15:11Z","receivedAt":"2026-02-20T10:15:42Z","isPatch":true,"body":"The t7900 test suite is exercising git-maintenance(1) and is thus of\ncourse heavily reliant on the exact maintenance strategy. This reliance\ncomes in two flavors:\n\n  - One test explicitly wants to verify that git-gc(1) is run as part of\n    `git maintenance run`. This test is adapted by explicitly picking the\n    \"gc\" strategy.\n\n  - The other tests assume a specific shape of the object database,\n    which is dependent on whether or not we run auto-maintenance before\n    we come to the actual subject under test. These tests are adapted by\n    disabling auto-maintenance.\n\nWith these changes t7900 passes with both \"gc\" and \"geometric\" default\nstrategies.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t7900-maintenance.sh | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\nindex d11d6f8f15..63276dcc5f 100755\n--- a/t/t7900-maintenance.sh\n+++ b/t/t7900-maintenance.sh\n@@ -43,7 +43,8 @@ test_expect_success 'help text' '\n \ttest_grep \"usage: git maintenance\" err\n '\n \n-test_expect_success 'run [--auto|--quiet]' '\n+test_expect_success 'run [--auto|--quiet] with gc strategy' '\n+\ttest_config maintenance.strategy gc &&\n \tGIT_TRACE2_EVENT=\"$(pwd)/run-no-auto.txt\" \\\n \t\tgit maintenance run 2>/dev/null &&\n \tGIT_TRACE2_EVENT=\"$(pwd)/run-auto.txt\" \\\n@@ -497,6 +498,7 @@ test_expect_success 'maintenance.incremental-repack.auto' '\n \t(\n \t\tcd incremental-repack-true &&\n \t\tgit config core.multiPackIndex true &&\n+\t\tgit config maintenance.auto false &&\n \t\trun_incremental_repack_and_verify\n \t)\n '\n@@ -507,6 +509,7 @@ test_expect_success 'maintenance.incremental-repack.auto (when config is unset)'\n \t(\n \t\tcd incremental-repack-unset &&\n \t\ttest_unconfig core.multiPackIndex &&\n+\t\tgit config maintenance.auto false &&\n \t\trun_incremental_repack_and_verify\n \t)\n '\n@@ -617,6 +620,7 @@ test_expect_success 'geometric repacking with --auto' '\n \tgit init repo &&\n \t(\n \t\tcd repo &&\n+\t\tgit config set maintenance.auto false &&\n \n \t\t# An empty repository does not need repacking, except when\n \t\t# explicitly told to do it.\n\n-- \n2.53.0.414.gf7e9f6c205.dirty\n\n"},{"id":"536505","messageId":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-8-faeb321ad13b@pks.im","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-0-faeb321ad13b@pks.im","subject":"[PATCH 8/8] builtin/maintenance: use \"geometric\" strategy by default","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-20T10:15:12Z","receivedAt":"2026-02-20T10:15:45Z","isPatch":true,"body":"The git-gc(1) command has been introduced in the early days of Git in\n30f610b7b0 (Create 'git gc' to perform common maintenance operations.,\n2006-12-27) as the main repository maintenance utility. And while the\ntool has of course evolved since then to cover new parts, the basic\nstrategy it uses has never really changed much.\n\nIt is safe to say that since 2006 the Git ecosystem has changed quite a\nbit. Repositories tend to be much larger nowadays than they have been\nalmost 20 years ago, and large parts of the industry went crazy for\nmonorepos (for various wildly different definitions of \"monorepo\"). So\nthe maintenance strategy we used back then may not be the best fit\nnowadays anymore.\n\nArguably, most of the maintenance tasks that git-gc(1) does are still\nperfectly fine today: repacking references, expiring various data\nstructures and things like tend to not cause huge problems. But the big\nexception is the way we repack objects.\n\ngit-gc(1) by default uses a split strategy: it performs incremental\nrepacks by default, and then whenever we have too many packs we perform\na large all-into-one repack. This all-into-one repack is what is causing\nproblems nowadays, as it is an operation that is quite expensive. While\nit is wasteful in small- and medium-sized repositories, in large repos\nit may even be prohibitively expensive.\n\nWe have eventually introduced git-maintenance(1) that was slated as a\nreplacement for git-gc(1). In contrast to git-gc(1), it was much more\nflexible as it is structured around configurable tasks and strategies.\nAnd while it knows about the \"incremental\" strategy that we may use for\nscheduled maintenance when configured via Scalar, its default still is\nto use git-gc(1) in the background.\n\nThe \"incremental\" strategy isn't really a full replacement for git-gc(1)\nthough, as it doesn't know to expire unused data structures. In Git 2.52\nwe have thus introduced a new \"geometric\" strategy that is a proper\nreplacement for the old git-gc(1).\n\nIn contrast to the incremental/all-into-one split used by git-gc(1), the\nnew \"geometric\" strategy maintains a geometric progression of packfiles,\nwhich significantly reduces the number of all-into-one repacks that we\nhave to perform in large repositories. It is thus a much better fit for\nlarge repositories than git-gc(1).\n\nNote that the \"geometric\" strategy isn't perfect though: while we\nperform way less all-into-one repacks compared to git-gc(1), we still\nhave to perform them eventually. But for the largest repositories out\nthere this may not be an option, as client machines might not be\npowerful enough to perform such a repack in the first place. These cases\nwould thus still be covered by Scalar's \"incremental\" strategy.\n\nSwitch the default strategy away from \"gc\" to \"geometric\", but retain\nthe \"incremental\" strategy configured by Scalar.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/gc.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 4390eee6ec..fb329c2cff 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -1980,7 +1980,7 @@ static void initialize_task_config(struct maintenance_run_opts *opts,\n \t\tstrategy = none_strategy;\n \t\ttype = MAINTENANCE_TYPE_SCHEDULED;\n \t} else {\n-\t\tstrategy = gc_strategy;\n+\t\tstrategy = geometric_strategy;\n \t\ttype = MAINTENANCE_TYPE_MANUAL;\n \t}\n \n\n-- \n2.53.0.414.gf7e9f6c205.dirty\n\n"},{"id":"536678","messageId":"86490d73-dee3-4750-b99c-ff94848bcdbb@gmail.com","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-3-faeb321ad13b@pks.im","subject":"Re: [PATCH 3/8] t34xx: don't expire reflogs where it matters","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2026-02-23T00:48:20Z","receivedAt":"2026-02-23T00:48:22Z","isPatch":true,"body":"On 2/20/26 5:15 AM, Patrick Steinhardt wrote:\n> We have a couple of tests in the t34xx range that rely on reflogs. This\n> never really used to be a problem, but in a subsequent commit we will\n> change the default maintenance strategy from \"gc\" to \"geometric\", and\n> this will cause us to drop all reflogs in these tests.\n> \n> This may seem surprising and like a bug at first, but it's actually not.\n> The main difference between these two strategies is that the \"gc\"\n> strategy will skip all maintenance in case the object database is in a\n> well-optimized state. The \"geometric\" strategy has separate subtasks\n> though, and the conditions for each of these tasks is evaluated on a\n> case by case basis. This means that even if the object database is in\n> good shape, we may still decide to expire reflogs.\n> \n> So why is that a problem? The issue is that Git's test suite hardcodes\n> the committer and author dates to a date in 2005. Interestingly though,\n> these hardcoded dates not only impact the commits, but also the reflog\n> entries. The consequence is that all newly written reflog entries are\n> immediately considered stale as our reflog expiration threshold is in\n> the range of weeks, only. It follows that executing `git reflog expire`\n> will thus immediately purge all reflog entries.\n\nI found these two paragraphs very valuable in explaining this patch.\n\nThanks!\n-Stolee\n\n"},{"id":"536679","messageId":"4ec59d18-5aef-48e9-a4ec-77e20a2a14c8@gmail.com","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-8-faeb321ad13b@pks.im","subject":"Re: [PATCH 8/8] builtin/maintenance: use \"geometric\" strategy by default","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2026-02-23T00:52:40Z","receivedAt":"2026-02-23T00:52:42Z","isPatch":true,"body":"On 2/20/26 5:15 AM, Patrick Steinhardt wrote:\n> The git-gc(1) command has been introduced in the early days of Git in\n> 30f610b7b0 (Create 'git gc' to perform common maintenance operations.,\n> 2006-12-27) as the main repository maintenance utility. And while the\n> tool has of course evolved since then to cover new parts, the basic\n> strategy it uses has never really changed much.\n\nI agree that the 'gc' strategy no longer serves users as a good default.\nFor those that want foreground commands to trigger maintenance (detached\non Unix, and as a blocking child on Windows) the 'geometric' strategy is\na good one.\n\n> Switch the default strategy away from \"gc\" to \"geometric\", but retain\n> the \"incremental\" strategy configured by Scalar.\n\nInstead of \"configured by Scalar\" I'd say instead \"configured when\ninitializing background maintenance with 'git maintenance start'\" which\nis how how Scalar sets this up indirectly.\n\nUsers could still opt-in to 'geometric' in the background, but it\nwould cause difficulties for the largest of repos that rely on the\n'incremental' strategy's limit of the amount of data processed.\n\n>   \t} else {\n> -\t\tstrategy = gc_strategy;\n> +\t\tstrategy = geometric_strategy;\n>   \t\ttype = MAINTENANCE_TYPE_MANUAL;\n>   \t}\n\nShould this include some kind of documentation update in\nDocumentation/config/maintenance.adoc?\n\nThanks,\n-Stolee\n\n\n"},{"id":"536680","messageId":"968fc3da-a2f4-4277-af61-a06dc94afe7e@gmail.com","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-0-faeb321ad13b@pks.im","subject":"Re: [PATCH 0/8] builtin/maintenance: use \"geometric\" strategy by default","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2026-02-23T00:53:59Z","receivedAt":"2026-02-23T00:54:02Z","isPatch":true,"body":"On 2/20/26 5:15 AM, Patrick Steinhardt wrote:\n> Hi,\n> \n> this series converts our default strategy used by git-maintenance(1)\n> from \"gc\" to \"geometric\". The aim of this is twofold:\n> \n>    - It completes the conversion to a more flexible infrastructure for\n>      repository maintenance. git-maintenance(1) is structured around\n>      tasks that can be toggled on/off as needed, and this is a lot easier\n>      to extend going forward.\n> \n>    - We start to use a more efficient repacking strategy by default,\n>      which should especially help large repositories out there.\n> \n> Out of these two, I think that the first point is actually the more\n> important one.\n\nI fully support this change as implemented, though I had a nit about\nthe final commit message and think there should be a documentation update\nfor this.\n\n> Unfortunately, a lot of our tests are racy or will fail with the new\n> strategy. This is mostly because the new strategy may decide to optimize\n> data structures in cases where the old strategy didn't, and because the\n> tasks we perform might be different. The majority of this patch series\n> thus adapts our tests accordingly. The actual change is a one-line\n> change in the final commit.\n\nThe patches that update these tests all look sensible. Thanks for the\nextra care in explaining some tricky bits.\n\nThanks,\n-Stolee\n\n"},{"id":"536727","messageId":"aZwinjoywwnzEvRG@pks.im","threadId":"65029","inReplyTo":"4ec59d18-5aef-48e9-a4ec-77e20a2a14c8@gmail.com","subject":"Re: [PATCH 8/8] builtin/maintenance: use \"geometric\" strategy by default","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-23T09:49:18Z","receivedAt":"2026-02-23T09:49:24Z","isPatch":true,"body":"On Sun, Feb 22, 2026 at 07:52:40PM -0500, Derrick Stolee wrote:\n> On 2/20/26 5:15 AM, Patrick Steinhardt wrote:\n> > The git-gc(1) command has been introduced in the early days of Git in\n> > 30f610b7b0 (Create 'git gc' to perform common maintenance operations.,\n> > 2006-12-27) as the main repository maintenance utility. And while the\n> > tool has of course evolved since then to cover new parts, the basic\n> > strategy it uses has never really changed much.\n> \n> I agree that the 'gc' strategy no longer serves users as a good default.\n> For those that want foreground commands to trigger maintenance (detached\n> on Unix, and as a blocking child on Windows) the 'geometric' strategy is\n> a good one.\n> \n> > Switch the default strategy away from \"gc\" to \"geometric\", but retain\n> > the \"incremental\" strategy configured by Scalar.\n> \n> Instead of \"configured by Scalar\" I'd say instead \"configured when\n> initializing background maintenance with 'git maintenance start'\" which\n> is how how Scalar sets this up indirectly.\n> \n> Users could still opt-in to 'geometric' in the background, but it\n> would cause difficulties for the largest of repos that rely on the\n> 'incremental' strategy's limit of the amount of data processed.\n\nMakes sense, will rephrase.\n\n> >   \t} else {\n> > -\t\tstrategy = gc_strategy;\n> > +\t\tstrategy = geometric_strategy;\n> >   \t\ttype = MAINTENANCE_TYPE_MANUAL;\n> >   \t}\n> \n> Should this include some kind of documentation update in\n> Documentation/config/maintenance.adoc?\n\nOh, right, it definitely should!\n\nPatrick\n"},{"id":"536825","messageId":"aZx3NCv9hjap_yoP@denethor","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-1-faeb321ad13b@pks.im","subject":"Re: [PATCH 1/8] t: fix races caused by background maintenance","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2026-02-23T16:01:48Z","receivedAt":"2026-02-23T16:01:54Z","isPatch":true,"body":"On 26/02/20 11:15AM, Patrick Steinhardt wrote:\n> Many Git commands spawn git-maintenance(1) to optimize the repository in\n> the background. By default, performing the maintenance is for most of\n> the part asynchronous: we fork the executable and then continue with the\n> rest of our business logic.\n> \n> This is working as expected for our users, but this behaviour is\n> somewhat problematic for our test suite as this is inherently racy. We\n> have many tests that verify the on-disk state of repositories, and those\n> tests may easily race with our background maintenance. In a similar\n> fashion, we may end up with processes that \"leak\" out of a current test\n> case.\n> \n> Until now this tends to not be much of a problem. Our maintenance uses\n> git-gc(1) by default, which knows to bail out in case there aren't\n> either too many packfiles or too many loose objects. So even if other\n> data structures would need to be optimized, we won't do so unless the\n> object database also needs optimizations.\n> \n> This is about to change though, as a subsequent commit will switch to\n> the \"geometric\" maintenance strategy as a default. The consequence is\n> that we will run required optimizations even if the object database is\n> well-optimized. And this uncovers races between our test suite and\n> background maintenance all over the place.\n> \n> Disabling maintenance outright in our test suite is not really an\n> option, as it would result in significantly divergence from the \"real\n\ns/significantly/significant/\n\n> world\" and reduce our test coverage. But we've got an alternative up our\n> sleeves: we can ensure that garbage collection runs synchronously by\n> overriding the \"maintenance.autoDetach\" configuration.\n> \n> Of course that also diverges from the real world, as we now stop testing\n> that background maintenance interacts in a benign way with normal Git\n> commands. But on the other hand this ensures that the maintenance itself\n> does not for example lead to data loss in a more reproducible way.\n> \n> Another concern is that this would make execution of the test suite much\n> slower. But a quick benchmark on my machine demonstrates that this does\n> not seem to be the case:\n> \n>     Benchmark 1: meson test (revision = HEAD~)\n>       Time (mean ± σ):     131.182 s ±  1.293 s    [User: 853.737 s, System: 1160.479 s]\n>       Range (min … max):   130.001 s … 132.563 s    3 runs\n> \n>     Benchmark 2: meson test (revision = HEAD)\n>       Time (mean ± σ):     129.554 s ±  0.507 s    [User: 849.040 s, System: 1152.664 s]\n>       Range (min … max):   129.000 s … 129.994 s    3 runs\n> \n>     Summary\n>       meson test (revision = HEAD) ran\n>         1.01 ± 0.01 times faster than meson test (revision = HEAD~)\n> \n> Funny enough, it even seems as if this speeds up test execution ever so\n> slightly, but that may just as well be noise.\n> \n> Introduce a new `GIT_TEST_MAINT_AUTO_DETACH` environment variable that\n> allows us to override the auto-detach behaviour and set that varibale in\n\ns/varibale/variable/\n\n> our tests.\n> \n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  run-command.c            | 2 +-\n>  t/t5616-partial-clone.sh | 6 +++---\n>  t/t7900-maintenance.sh   | 1 +\n>  t/test-lib.sh            | 4 ++++\n>  4 files changed, 9 insertions(+), 4 deletions(-)\n> \n> diff --git a/run-command.c b/run-command.c\n> index e3e02475cc..438a290d30 100644\n> --- a/run-command.c\n> +++ b/run-command.c\n> @@ -1828,7 +1828,7 @@ int prepare_auto_maintenance(int quiet, struct child_process *maint)\n>  \t */\n>  \tif (repo_config_get_bool(the_repository, \"maintenance.autodetach\", &auto_detach) &&\n>  \t    repo_config_get_bool(the_repository, \"gc.autodetach\", &auto_detach))\n> -\t\tauto_detach = 1;\n> +\t\tauto_detach = git_env_bool(\"GIT_TEST_MAINT_AUTO_DETACH\", true);\n\nSo now if \"maintenance.autodetach\" and \"gc.autodetach\" are both not set,\nwe then check for the \"GIT_TEST_MAINT_AUTO_DETACH\" env before defaulting\nto true. Looks good.\n\n>  \n>  \tmaint->git_cmd = 1;\n>  \tmaint->close_object_store = 1;\n[snip]\n> diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\n> index 7cc0ce57f8..d11d6f8f15 100755\n> --- a/t/t7900-maintenance.sh\n> +++ b/t/t7900-maintenance.sh\n> @@ -6,6 +6,7 @@ test_description='git maintenance builtin'\n>  \n>  GIT_TEST_COMMIT_GRAPH=0\n>  GIT_TEST_MULTI_PACK_INDEX=0\n> +sane_unset GIT_TEST_MAINT_AUTO_DETACH\n\nI assume here we are unsetting the env for testing purposes. It might be\nnice to leave some sort of breadcrumb comment here to explain to future\nreaders.\n\n>  test_lazy_prereq XMLLINT '\n>  \txmllint --version\n> diff --git a/t/test-lib.sh b/t/test-lib.sh\n> index 0fb76f7d11..aa805a01ce 100644\n> --- a/t/test-lib.sh\n> +++ b/t/test-lib.sh\n> @@ -1947,6 +1947,10 @@ test_lazy_prereq COMPAT_HASH '\n>  GIT_TEST_MAINT_SCHEDULER=\"none:exit 1\"\n>  export GIT_TEST_MAINT_SCHEDULER\n>  \n> +# Ensure that tests cannot race with background maintenance by default.\n> +GIT_TEST_MAINT_AUTO_DETACH=\"false\"\n> +export GIT_TEST_MAINT_AUTO_DETACH\n\nLooks good.\n\n-Justin\n"},{"id":"536826","messageId":"aZx6eh9r73fGAT2k@denethor","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-2-faeb321ad13b@pks.im","subject":"Re: [PATCH 2/8] t: disable maintenance where we verify object database structure","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2026-02-23T16:07:06Z","receivedAt":"2026-02-23T16:07:08Z","isPatch":true,"body":"On 26/02/20 11:15AM, Patrick Steinhardt wrote:\n> We have a couple of tests that explicitly verify the structure of the\n> object database. Naturally, this structure is dependent on whether or\n> not we run repository maintenance: if it decides to optimize the object\n> database the expected structure is likely to not materialize.\n> \n> Explicitly disable auto-maintenance in such tests so that we are not\n> dependent on decisions made by our maintenance.\n\nI assume that these tests previously did not trigger maintenance to\nbegin with so now explicitly disabling maintenance does not change the\nresulting structure. Changing the default to geometric repacking may\nchange this though.\n\n-Justin\n"},{"id":"536829","messageId":"aZx72rF8OOXryMc5@denethor","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-3-faeb321ad13b@pks.im","subject":"Re: [PATCH 3/8] t34xx: don't expire reflogs where it matters","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2026-02-23T16:15:17Z","receivedAt":"2026-02-23T16:15:19Z","isPatch":true,"body":"On 26/02/20 11:15AM, Patrick Steinhardt wrote:\n> We have a couple of tests in the t34xx range that rely on reflogs. This\n> never really used to be a problem, but in a subsequent commit we will\n> change the default maintenance strategy from \"gc\" to \"geometric\", and\n> this will cause us to drop all reflogs in these tests.\n> \n> This may seem surprising and like a bug at first, but it's actually not.\n> The main difference between these two strategies is that the \"gc\"\n> strategy will skip all maintenance in case the object database is in a\n> well-optimized state. The \"geometric\" strategy has separate subtasks\n> though, and the conditions for each of these tasks is evaluated on a\n> case by case basis. This means that even if the object database is in\n> good shape, we may still decide to expire reflogs.\n> \n> So why is that a problem? The issue is that Git's test suite hardcodes\n> the committer and author dates to a date in 2005. Interestingly though,\n> these hardcoded dates not only impact the commits, but also the reflog\n> entries. The consequence is that all newly written reflog entries are\n> immediately considered stale as our reflog expiration threshold is in\n> the range of weeks, only. It follows that executing `git reflog expire`\n> will thus immediately purge all reflog entries.\n> \n> This hasn't been a problem in our test suite by pure chance, as the\n> repository shapes simply didn't cause us to perform actual garbage\n> collection. But with the upcoming \"geometric\" strategy we _will_ start\n> to execute `git reflog expire`, thus surfacing this issue.\n\nInteresting find.\n\n> Prepare for this by explicitly disabling reflog expiration in tests\n> impacted by this upcoming change.\n\nMakes sense.\n\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  t/t3404-rebase-interactive.sh  | 2 ++\n>  t/t3406-rebase-message.sh      | 3 +++\n>  t/t3431-rebase-fork-point.sh   | 2 ++\n>  t/t3432-rebase-fast-forward.sh | 2 ++\n>  4 files changed, 9 insertions(+)\n> \n> diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\n> index e778dd8ae4..5e4623f7f1 100755\n> --- a/t/t3404-rebase-interactive.sh\n> +++ b/t/t3404-rebase-interactive.sh\n> @@ -31,6 +31,8 @@ Initial setup:\n>  . \"$TEST_DIRECTORY\"/lib-rebase.sh\n>  \n>  test_expect_success 'setup' '\n> +\tgit config set gc.reflogExpire never &&\n> +\tgit config set gc.reflogExpireUnreachable never &&\n\nAs it may not be immediately obvious, it could be helpful for future\nreaders to explain in a comment that reflog dates are hardcoded to a\ndate that would be immediately expired and thus the need for this\nconfiguration.\n\nThis patch looks good to me.\n\n-Justin\n"},{"id":"536849","messageId":"aZyAOsRX5484naIU@denethor","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-8-faeb321ad13b@pks.im","subject":"Re: [PATCH 8/8] builtin/maintenance: use \"geometric\" strategy by default","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2026-02-23T16:48:59Z","receivedAt":"2026-02-23T16:49:03Z","isPatch":true,"body":"On 26/02/20 11:15AM, Patrick Steinhardt wrote:\n> The git-gc(1) command has been introduced in the early days of Git in\n> 30f610b7b0 (Create 'git gc' to perform common maintenance operations.,\n> 2006-12-27) as the main repository maintenance utility. And while the\n> tool has of course evolved since then to cover new parts, the basic\n> strategy it uses has never really changed much.\n> \n> It is safe to say that since 2006 the Git ecosystem has changed quite a\n> bit. Repositories tend to be much larger nowadays than they have been\n> almost 20 years ago, and large parts of the industry went crazy for\n> monorepos (for various wildly different definitions of \"monorepo\"). So\n> the maintenance strategy we used back then may not be the best fit\n> nowadays anymore.\n> \n> Arguably, most of the maintenance tasks that git-gc(1) does are still\n> perfectly fine today: repacking references, expiring various data\n> structures and things like tend to not cause huge problems. But the big\n> exception is the way we repack objects.\n> \n> git-gc(1) by default uses a split strategy: it performs incremental\n> repacks by default, and then whenever we have too many packs we perform\n> a large all-into-one repack. This all-into-one repack is what is causing\n> problems nowadays, as it is an operation that is quite expensive. While\n> it is wasteful in small- and medium-sized repositories, in large repos\n> it may even be prohibitively expensive.\n> \n> We have eventually introduced git-maintenance(1) that was slated as a\n> replacement for git-gc(1). In contrast to git-gc(1), it was much more\n> flexible as it is structured around configurable tasks and strategies.\n> And while it knows about the \"incremental\" strategy that we may use for\n> scheduled maintenance when configured via Scalar, its default still is\n> to use git-gc(1) in the background.\n\nI'm a tad bit confused here. git-gc(1) by default uses an\n\"incremental/all-into-one\" strategy and it is my understanding that this\nis what git-maintenance(1) is currently using. Is there also another\n\"incremental\" strategy for git-maintenance(1)?\n\n> The \"incremental\" strategy isn't really a full replacement for git-gc(1)\n> though, as it doesn't know to expire unused data structures. In Git 2.52\n> we have thus introduced a new \"geometric\" strategy that is a proper\n> replacement for the old git-gc(1).\n> \n> In contrast to the incremental/all-into-one split used by git-gc(1), the\n> new \"geometric\" strategy maintains a geometric progression of packfiles,\n> which significantly reduces the number of all-into-one repacks that we\n> have to perform in large repositories. It is thus a much better fit for\n> large repositories than git-gc(1).\n> \n> Note that the \"geometric\" strategy isn't perfect though: while we\n> perform way less all-into-one repacks compared to git-gc(1), we still\n> have to perform them eventually. But for the largest repositories out\n> there this may not be an option, as client machines might not be\n> powerful enough to perform such a repack in the first place. These cases\n> would thus still be covered by Scalar's \"incremental\" strategy.\n\nSo the \"problem\" is the \"all-into-one\" repack. This ultimately occurs\nfor both the \"gc\" and \"geometric\" strategies so changing the default\nstrategy shouldn't make anything worse. Geometric repacking should in\nfact delay the costly \"all-into-one\" repacks which is good.\n\n> Switch the default strategy away from \"gc\" to \"geometric\", but retain\n> the \"incremental\" strategy configured by Scalar.\n> \n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  builtin/gc.c | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n> \n> diff --git a/builtin/gc.c b/builtin/gc.c\n> index 4390eee6ec..fb329c2cff 100644\n> --- a/builtin/gc.c\n> +++ b/builtin/gc.c\n> @@ -1980,7 +1980,7 @@ static void initialize_task_config(struct maintenance_run_opts *opts,\n>  \t\tstrategy = none_strategy;\n>  \t\ttype = MAINTENANCE_TYPE_SCHEDULED;\n>  \t} else {\n> -\t\tstrategy = gc_strategy;\n> +\t\tstrategy = geometric_strategy;\n\nLooks good.\n\n-Justin\n"},{"id":"536933","messageId":"aZ1eENpDpagp77dN@pks.im","threadId":"65029","inReplyTo":"aZyAOsRX5484naIU@denethor","subject":"Re: [PATCH 8/8] builtin/maintenance: use \"geometric\" strategy by default","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-24T08:15:12Z","receivedAt":"2026-02-24T08:15:18Z","isPatch":true,"body":"On Mon, Feb 23, 2026 at 10:48:59AM -0600, Justin Tobler wrote:\n> On 26/02/20 11:15AM, Patrick Steinhardt wrote:\n> > The git-gc(1) command has been introduced in the early days of Git in\n> > 30f610b7b0 (Create 'git gc' to perform common maintenance operations.,\n> > 2006-12-27) as the main repository maintenance utility. And while the\n> > tool has of course evolved since then to cover new parts, the basic\n> > strategy it uses has never really changed much.\n> > \n> > It is safe to say that since 2006 the Git ecosystem has changed quite a\n> > bit. Repositories tend to be much larger nowadays than they have been\n> > almost 20 years ago, and large parts of the industry went crazy for\n> > monorepos (for various wildly different definitions of \"monorepo\"). So\n> > the maintenance strategy we used back then may not be the best fit\n> > nowadays anymore.\n> > \n> > Arguably, most of the maintenance tasks that git-gc(1) does are still\n> > perfectly fine today: repacking references, expiring various data\n> > structures and things like tend to not cause huge problems. But the big\n> > exception is the way we repack objects.\n> > \n> > git-gc(1) by default uses a split strategy: it performs incremental\n> > repacks by default, and then whenever we have too many packs we perform\n> > a large all-into-one repack. This all-into-one repack is what is causing\n> > problems nowadays, as it is an operation that is quite expensive. While\n> > it is wasteful in small- and medium-sized repositories, in large repos\n> > it may even be prohibitively expensive.\n> > \n> > We have eventually introduced git-maintenance(1) that was slated as a\n> > replacement for git-gc(1). In contrast to git-gc(1), it was much more\n> > flexible as it is structured around configurable tasks and strategies.\n> > And while it knows about the \"incremental\" strategy that we may use for\n> > scheduled maintenance when configured via Scalar, its default still is\n> > to use git-gc(1) in the background.\n> \n> I'm a tad bit confused here. git-gc(1) by default uses an\n> \"incremental/all-into-one\" strategy and it is my understanding that this\n> is what git-maintenance(1) is currently using. Is there also another\n> \"incremental\" strategy for git-maintenance(1)?\n\nThere are three strategies right now:\n\n  - \"gc\", which is the current default strategy that uses git-gc(1) to\n    perform the incremental packs followed by the all-into-one repacks.\n\n  - \"incremental\", which is set up by `git maintenance start`. This\n    strategy never performs the all-into-one repacks, and it instead\n    creates packs of a fixed upper size.\n\n  - \"geometric\", which is the strategy that'll become the new default.\n\nI'll try to clarify this a bit.\n\nPatrick\n"},{"id":"536936","messageId":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-0-8657338c6fa1@pks.im","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-0-faeb321ad13b@pks.im","subject":"[PATCH v2 0/8] builtin/maintenance: use \"geometric\" strategy by default","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-24T08:45:44Z","receivedAt":"2026-02-24T08:45:54Z","isPatch":true,"body":"Hi,\n\nthis series converts our default strategy used by git-maintenance(1)\nfrom \"gc\" to \"geometric\". The aim of this is twofold:\n\n  - It completes the conversion to a more flexible infrastructure for\n    repository maintenance. git-maintenance(1) is structured around\n    tasks that can be toggled on/off as needed, and this is a lot easier\n    to extend going forward.\n\n  - We start to use a more efficient repacking strategy by default,\n    which should especially help large repositories out there.\n\nOut of these two, I think that the first point is actually the more\nimportant one.\n\nUnfortunately, a lot of our tests are racy or will fail with the new\nstrategy. This is mostly because the new strategy may decide to optimize\ndata structures in cases where the old strategy didn't, and because the\ntasks we perform might be different. The majority of this patch series\nthus adapts our tests accordingly. The actual change is a one-line\nchange in the final commit.\n\nI was a bit torn initially whether or not I want to make the geometric\nstrategy the default right away, or whether we might first want to use\n\"feature.experimental\" as an additional step. I'm quite happy to adapt\nthe series accordingly, but for the initial version I thought it might\ninvite more discussions if I pick the nuclear option :)\n\nOf course, no matter how we do this, it is still possible to revert back\nto the old strategy by setting \"maintenance.strategy=gc\".\n\nChanges in v2:\n  - Document the updated default strategy.\n  - Clarify how this interacts with Scalar.\n  - Explain the current landscape of strategies a bit better.\n  - Leave some breadcrumbs in the tests.\n  - Link to v1: https://lore.kernel.org/r/20260220-b4-pks-maintenance-default-geometric-strategy-v1-0-faeb321ad13b@pks.im\n\nThanks!\n\nPatrick\n\n---\nPatrick Steinhardt (8):\n      t: fix races caused by background maintenance\n      t: disable maintenance where we verify object database structure\n      t34xx: don't expire reflogs where it matters\n      t5400: explicitly use \"gc\" strategy\n      t5510: explicitly use \"gc\" strategy\n      t6500: explicitly use \"gc\" strategy\n      t7900: prepare for switch of the default strategy\n      builtin/maintenance: use \"geometric\" strategy by default\n\n Documentation/config/maintenance.adoc   | 6 +++---\n builtin/gc.c                            | 2 +-\n run-command.c                           | 2 +-\n t/t0081-find-pack.sh                    | 1 +\n t/t3404-rebase-interactive.sh           | 6 ++++++\n t/t3406-rebase-message.sh               | 6 ++++++\n t/t3431-rebase-fork-point.sh            | 6 ++++++\n t/t3432-rebase-fast-forward.sh          | 6 ++++++\n t/t5316-pack-delta-depth.sh             | 1 +\n t/t5319-multi-pack-index.sh             | 1 +\n t/t5326-multi-pack-bitmaps.sh           | 3 ++-\n t/t5327-multi-pack-bitmaps-rev.sh       | 3 ++-\n t/t5331-pack-objects-stdin.sh           | 2 ++\n t/t5332-multi-pack-reuse.sh             | 1 +\n t/t5334-incremental-multi-pack-index.sh | 1 +\n t/t5400-send-pack.sh                    | 1 +\n t/t5500-fetch-pack.sh                   | 3 ++-\n t/t5510-fetch.sh                        | 1 +\n t/t5616-partial-clone.sh                | 7 ++++---\n t/t6500-gc.sh                           | 1 +\n t/t7700-repack.sh                       | 3 +++\n t/t7900-maintenance.sh                  | 9 ++++++++-\n t/test-lib.sh                           | 4 ++++\n 23 files changed, 64 insertions(+), 12 deletions(-)\n\nRange-diff versus v1:\n\n1:  c5fadf42d0 ! 1:  9efc6d0a22 t: fix races caused by background maintenance\n    @@ Commit message\n         background maintenance all over the place.\n     \n         Disabling maintenance outright in our test suite is not really an\n    -    option, as it would result in significantly divergence from the \"real\n    +    option, as it would result in significant divergence from the \"real\n         world\" and reduce our test coverage. But we've got an alternative up our\n         sleeves: we can ensure that garbage collection runs synchronously by\n         overriding the \"maintenance.autoDetach\" configuration.\n    @@ Commit message\n         slightly, but that may just as well be noise.\n     \n         Introduce a new `GIT_TEST_MAINT_AUTO_DETACH` environment variable that\n    -    allows us to override the auto-detach behaviour and set that varibale in\n    +    allows us to override the auto-detach behaviour and set that variable in\n         our tests.\n     \n         Signed-off-by: Patrick Steinhardt <ps@pks.im>\n    @@ t/t5616-partial-clone.sh: test_expect_success 'fetch --refetch triggers repackin\n     \n      ## t/t7900-maintenance.sh ##\n     @@ t/t7900-maintenance.sh: test_description='git maintenance builtin'\n    - \n      GIT_TEST_COMMIT_GRAPH=0\n      GIT_TEST_MULTI_PACK_INDEX=0\n    -+sane_unset GIT_TEST_MAINT_AUTO_DETACH\n      \n    ++# Ensure that auto-maintenance detaches as usual.\n    ++sane_unset GIT_TEST_MAINT_AUTO_DETACH\n    ++\n      test_lazy_prereq XMLLINT '\n      \txmllint --version\n    + '\n     \n      ## t/test-lib.sh ##\n     @@ t/test-lib.sh: test_lazy_prereq COMPAT_HASH '\n2:  805417a4a7 = 2:  f80bde1353 t: disable maintenance where we verify object database structure\n3:  8a579a768d ! 3:  7087a68815 t34xx: don't expire reflogs where it matters\n    @@ t/t3404-rebase-interactive.sh: Initial setup:\n      . \"$TEST_DIRECTORY\"/lib-rebase.sh\n      \n      test_expect_success 'setup' '\n    ++\t# Commit dates are hardcoded to 2005, and the reflog entries will have\n    ++\t# a matching timestamp. Maintenance may thus immediately expire\n    ++\t# reflogs if it was running.\n     +\tgit config set gc.reflogExpire never &&\n     +\tgit config set gc.reflogExpireUnreachable never &&\n    ++\n      \tgit switch -C primary &&\n      \ttest_commit A file1 &&\n      \ttest_commit B file1 &&\n    @@ t/t3406-rebase-message.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n      . ./test-lib.sh\n      \n      test_expect_success 'setup' '\n    ++\t# Commit dates are hardcoded to 2005, and the reflog entries will have\n    ++\t# a matching timestamp. Maintenance may thus immediately expire\n    ++\t# reflogs if it was running.\n     +\tgit config set gc.reflogExpire never &&\n     +\tgit config set gc.reflogExpireUnreachable never &&\n     +\n    @@ t/t3431-rebase-fork-point.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n      # C was formerly part of main but main was rewound to remove C\n      #\n      test_expect_success setup '\n    ++\t# Commit dates are hardcoded to 2005, and the reflog entries will have\n    ++\t# a matching timestamp. Maintenance may thus immediately expire\n    ++\t# reflogs if it was running.\n     +\tgit config set gc.reflogExpire never &&\n     +\tgit config set gc.reflogExpireUnreachable never &&\n    ++\n      \ttest_commit A &&\n      \ttest_commit B &&\n      \ttest_commit C &&\n    @@ t/t3432-rebase-fast-forward.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n      . ./test-lib.sh\n      \n      test_expect_success setup '\n    ++\t# Commit dates are hardcoded to 2005, and the reflog entries will have\n    ++\t# a matching timestamp. Maintenance may thus immediately expire\n    ++\t# reflogs if it was running.\n     +\tgit config set gc.reflogExpire never &&\n     +\tgit config set gc.reflogExpireUnreachable never &&\n    ++\n      \ttest_commit A &&\n      \ttest_commit B &&\n      \ttest_commit C &&\n4:  283143c1d8 = 4:  d230055b22 t5400: explicitly use \"gc\" strategy\n5:  410dc16eb0 = 5:  dba219391f t5510: explicitly use \"gc\" strategy\n6:  c4c8c5a7e4 = 6:  61bc1add2a t6500: explicitly use \"gc\" strategy\n7:  93893cfee3 = 7:  b89505178d t7900: prepare for switch of the default strategy\n8:  9e7aa390a5 < -:  ---------- builtin/maintenance: use \"geometric\" strategy by default\n-:  ---------- > 8:  647d46a239 builtin/maintenance: use \"geometric\" strategy by default\n\n---\nbase-commit: 73fd77805fc6406f31c36212846d9e2541d19321\nchange-id: 20260218-b4-pks-maintenance-default-geometric-strategy-17fcedf92461\n\n"},{"id":"536937","messageId":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-1-8657338c6fa1@pks.im","threadId":"65029","inReplyTo":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-0-8657338c6fa1@pks.im","subject":"[PATCH v2 1/8] t: fix races caused by background maintenance","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-24T08:45:45Z","receivedAt":"2026-02-24T08:45:56Z","isPatch":true,"body":"Many Git commands spawn git-maintenance(1) to optimize the repository in\nthe background. By default, performing the maintenance is for most of\nthe part asynchronous: we fork the executable and then continue with the\nrest of our business logic.\n\nThis is working as expected for our users, but this behaviour is\nsomewhat problematic for our test suite as this is inherently racy. We\nhave many tests that verify the on-disk state of repositories, and those\ntests may easily race with our background maintenance. In a similar\nfashion, we may end up with processes that \"leak\" out of a current test\ncase.\n\nUntil now this tends to not be much of a problem. Our maintenance uses\ngit-gc(1) by default, which knows to bail out in case there aren't\neither too many packfiles or too many loose objects. So even if other\ndata structures would need to be optimized, we won't do so unless the\nobject database also needs optimizations.\n\nThis is about to change though, as a subsequent commit will switch to\nthe \"geometric\" maintenance strategy as a default. The consequence is\nthat we will run required optimizations even if the object database is\nwell-optimized. And this uncovers races between our test suite and\nbackground maintenance all over the place.\n\nDisabling maintenance outright in our test suite is not really an\noption, as it would result in significant divergence from the \"real\nworld\" and reduce our test coverage. But we've got an alternative up our\nsleeves: we can ensure that garbage collection runs synchronously by\noverriding the \"maintenance.autoDetach\" configuration.\n\nOf course that also diverges from the real world, as we now stop testing\nthat background maintenance interacts in a benign way with normal Git\ncommands. But on the other hand this ensures that the maintenance itself\ndoes not for example lead to data loss in a more reproducible way.\n\nAnother concern is that this would make execution of the test suite much\nslower. But a quick benchmark on my machine demonstrates that this does\nnot seem to be the case:\n\n    Benchmark 1: meson test (revision = HEAD~)\n      Time (mean ± σ):     131.182 s ±  1.293 s    [User: 853.737 s, System: 1160.479 s]\n      Range (min … max):   130.001 s … 132.563 s    3 runs\n\n    Benchmark 2: meson test (revision = HEAD)\n      Time (mean ± σ):     129.554 s ±  0.507 s    [User: 849.040 s, System: 1152.664 s]\n      Range (min … max):   129.000 s … 129.994 s    3 runs\n\n    Summary\n      meson test (revision = HEAD) ran\n        1.01 ± 0.01 times faster than meson test (revision = HEAD~)\n\nFunny enough, it even seems as if this speeds up test execution ever so\nslightly, but that may just as well be noise.\n\nIntroduce a new `GIT_TEST_MAINT_AUTO_DETACH` environment variable that\nallows us to override the auto-detach behaviour and set that variable in\nour tests.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n run-command.c            | 2 +-\n t/t5616-partial-clone.sh | 6 +++---\n t/t7900-maintenance.sh   | 3 +++\n t/test-lib.sh            | 4 ++++\n 4 files changed, 11 insertions(+), 4 deletions(-)\n\ndiff --git a/run-command.c b/run-command.c\nindex e3e02475cc..438a290d30 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -1828,7 +1828,7 @@ int prepare_auto_maintenance(int quiet, struct child_process *maint)\n \t */\n \tif (repo_config_get_bool(the_repository, \"maintenance.autodetach\", &auto_detach) &&\n \t    repo_config_get_bool(the_repository, \"gc.autodetach\", &auto_detach))\n-\t\tauto_detach = 1;\n+\t\tauto_detach = git_env_bool(\"GIT_TEST_MAINT_AUTO_DETACH\", true);\n \n \tmaint->git_cmd = 1;\n \tmaint->close_object_store = 1;\ndiff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh\nindex 1e354e057f..d62760eb92 100755\n--- a/t/t5616-partial-clone.sh\n+++ b/t/t5616-partial-clone.sh\n@@ -229,7 +229,7 @@ test_expect_success 'fetch --refetch triggers repacking' '\n \n \tGIT_TRACE2_EVENT=\"$PWD/trace1.event\" \\\n \tgit -C pc1 fetch --refetch origin &&\n-\ttest_subcommand git maintenance run --auto --no-quiet --detach <trace1.event &&\n+\ttest_subcommand git maintenance run --auto --no-quiet --no-detach <trace1.event &&\n \tgrep \\\"param\\\":\\\"gc.autopacklimit\\\",\\\"value\\\":\\\"1\\\" trace1.event &&\n \tgrep \\\"param\\\":\\\"maintenance.incremental-repack.auto\\\",\\\"value\\\":\\\"-1\\\" trace1.event &&\n \n@@ -238,7 +238,7 @@ test_expect_success 'fetch --refetch triggers repacking' '\n \t\t-c gc.autoPackLimit=0 \\\n \t\t-c maintenance.incremental-repack.auto=1234 \\\n \t\t-C pc1 fetch --refetch origin &&\n-\ttest_subcommand git maintenance run --auto --no-quiet --detach <trace2.event &&\n+\ttest_subcommand git maintenance run --auto --no-quiet --no-detach <trace2.event &&\n \tgrep \\\"param\\\":\\\"gc.autopacklimit\\\",\\\"value\\\":\\\"0\\\" trace2.event &&\n \tgrep \\\"param\\\":\\\"maintenance.incremental-repack.auto\\\",\\\"value\\\":\\\"-1\\\" trace2.event &&\n \n@@ -247,7 +247,7 @@ test_expect_success 'fetch --refetch triggers repacking' '\n \t\t-c gc.autoPackLimit=1234 \\\n \t\t-c maintenance.incremental-repack.auto=0 \\\n \t\t-C pc1 fetch --refetch origin &&\n-\ttest_subcommand git maintenance run --auto --no-quiet --detach <trace3.event &&\n+\ttest_subcommand git maintenance run --auto --no-quiet --no-detach <trace3.event &&\n \tgrep \\\"param\\\":\\\"gc.autopacklimit\\\",\\\"value\\\":\\\"1\\\" trace3.event &&\n \tgrep \\\"param\\\":\\\"maintenance.incremental-repack.auto\\\",\\\"value\\\":\\\"0\\\" trace3.event\n '\ndiff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\nindex 7cc0ce57f8..fe344f47ee 100755\n--- a/t/t7900-maintenance.sh\n+++ b/t/t7900-maintenance.sh\n@@ -7,6 +7,9 @@ test_description='git maintenance builtin'\n GIT_TEST_COMMIT_GRAPH=0\n GIT_TEST_MULTI_PACK_INDEX=0\n \n+# Ensure that auto-maintenance detaches as usual.\n+sane_unset GIT_TEST_MAINT_AUTO_DETACH\n+\n test_lazy_prereq XMLLINT '\n \txmllint --version\n '\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 0fb76f7d11..aa805a01ce 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -1947,6 +1947,10 @@ test_lazy_prereq COMPAT_HASH '\n GIT_TEST_MAINT_SCHEDULER=\"none:exit 1\"\n export GIT_TEST_MAINT_SCHEDULER\n \n+# Ensure that tests cannot race with background maintenance by default.\n+GIT_TEST_MAINT_AUTO_DETACH=\"false\"\n+export GIT_TEST_MAINT_AUTO_DETACH\n+\n # Does this platform support `git fsmonitor--daemon`\n #\n test_lazy_prereq FSMONITOR_DAEMON '\n\n-- \n2.53.0.536.g309c995771.dirty\n\n"},{"id":"536938","messageId":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-2-8657338c6fa1@pks.im","threadId":"65029","inReplyTo":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-0-8657338c6fa1@pks.im","subject":"[PATCH v2 2/8] t: disable maintenance where we verify object database structure","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-24T08:45:46Z","receivedAt":"2026-02-24T08:45:59Z","isPatch":true,"body":"We have a couple of tests that explicitly verify the structure of the\nobject database. Naturally, this structure is dependent on whether or\nnot we run repository maintenance: if it decides to optimize the object\ndatabase the expected structure is likely to not materialize.\n\nExplicitly disable auto-maintenance in such tests so that we are not\ndependent on decisions made by our maintenance.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t0081-find-pack.sh                    | 1 +\n t/t5316-pack-delta-depth.sh             | 1 +\n t/t5319-multi-pack-index.sh             | 1 +\n t/t5326-multi-pack-bitmaps.sh           | 3 ++-\n t/t5327-multi-pack-bitmaps-rev.sh       | 3 ++-\n t/t5331-pack-objects-stdin.sh           | 2 ++\n t/t5332-multi-pack-reuse.sh             | 1 +\n t/t5334-incremental-multi-pack-index.sh | 1 +\n t/t5500-fetch-pack.sh                   | 3 ++-\n t/t5616-partial-clone.sh                | 1 +\n t/t7700-repack.sh                       | 3 +++\n 11 files changed, 17 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t0081-find-pack.sh b/t/t0081-find-pack.sh\nindex 5a628bf735..26f017422d 100755\n--- a/t/t0081-find-pack.sh\n+++ b/t/t0081-find-pack.sh\n@@ -68,6 +68,7 @@ test_expect_success 'add more packfiles' '\n '\n \n test_expect_success 'add more commits (as loose objects)' '\n+\ttest_config maintenance.auto false &&\n \ttest_commit six &&\n \ttest_commit seven &&\n \ndiff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh\nindex 03dfb7a61e..8a067a45cb 100755\n--- a/t/t5316-pack-delta-depth.sh\n+++ b/t/t5316-pack-delta-depth.sh\n@@ -48,6 +48,7 @@ test_description='pack-objects breaks long cross-pack delta chains'\n # repeatedly-modified file to generate the delta chain).\n \n test_expect_success 'create series of packs' '\n+\ttest_config maintenance.auto false &&\n \ttest-tool genrandom foo 4096 >content &&\n \tprev= &&\n \tfor i in $(test_seq 1 10)\ndiff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh\nindex faae98c7e7..7672d599d4 100755\n--- a/t/t5319-multi-pack-index.sh\n+++ b/t/t5319-multi-pack-index.sh\n@@ -1315,6 +1315,7 @@ test_expect_success 'bitmapped packs are stored via the BTMP chunk' '\n \tgit init repo &&\n \t(\n \t\tcd repo &&\n+\t\tgit config set maintenance.auto false &&\n \n \t\tfor i in 1 2 3 4 5\n \t\tdo\ndiff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh\nindex 892aeb09e4..62bd973d92 100755\n--- a/t/t5326-multi-pack-bitmaps.sh\n+++ b/t/t5326-multi-pack-bitmaps.sh\n@@ -93,7 +93,8 @@ test_midx_bitmap_cases () {\n \ttest_expect_success 'setup test_repository' '\n \t\trm -rf * .git &&\n \t\tgit init &&\n-\t\tgit config pack.writeBitmapLookupTable '\"$writeLookupTable\"'\n+\t\tgit config pack.writeBitmapLookupTable '\"$writeLookupTable\"' &&\n+\t\tgit config maintenance.auto false\n \t'\n \n \tmidx_bitmap_core\ndiff --git a/t/t5327-multi-pack-bitmaps-rev.sh b/t/t5327-multi-pack-bitmaps-rev.sh\nindex 9cac03a94b..cfa12de2a8 100755\n--- a/t/t5327-multi-pack-bitmaps-rev.sh\n+++ b/t/t5327-multi-pack-bitmaps-rev.sh\n@@ -30,7 +30,8 @@ test_midx_bitmap_rev () {\n \ttest_expect_success 'setup bitmap config' '\n \t\trm -rf * .git &&\n \t\tgit init &&\n-\t\tgit config pack.writeBitmapLookupTable '\"$writeLookupTable\"'\n+\t\tgit config pack.writeBitmapLookupTable '\"$writeLookupTable\"' &&\n+\t\tgit config maintenance.auto false\n \t'\n \n \tmidx_bitmap_core rev\ndiff --git a/t/t5331-pack-objects-stdin.sh b/t/t5331-pack-objects-stdin.sh\nindex cd949025b9..b03f6be164 100755\n--- a/t/t5331-pack-objects-stdin.sh\n+++ b/t/t5331-pack-objects-stdin.sh\n@@ -14,6 +14,7 @@ packed_objects () {\n \n test_expect_success 'setup for --stdin-packs tests' '\n \tgit init stdin-packs &&\n+\tgit -C stdin-packs config set maintenance.auto false &&\n \t(\n \t\tcd stdin-packs &&\n \n@@ -255,6 +256,7 @@ test_expect_success '--stdin-packs=follow walks into unknown packs' '\n \tgit init repo &&\n \t(\n \t\tcd repo &&\n+\t\tgit config set maintenance.auto false &&\n \n \t\tfor c in A B C D\n \t\tdo\ndiff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh\nindex 395d09444c..881ce668e1 100755\n--- a/t/t5332-multi-pack-reuse.sh\n+++ b/t/t5332-multi-pack-reuse.sh\n@@ -59,6 +59,7 @@ test_pack_objects_reused () {\n \n test_expect_success 'preferred pack is reused for single-pack reuse' '\n \ttest_config pack.allowPackReuse single &&\n+\tgit config set maintenance.auto false &&\n \n \tfor i in A B\n \tdo\ndiff --git a/t/t5334-incremental-multi-pack-index.sh b/t/t5334-incremental-multi-pack-index.sh\nindex d30d7253d6..99c7d44d8e 100755\n--- a/t/t5334-incremental-multi-pack-index.sh\n+++ b/t/t5334-incremental-multi-pack-index.sh\n@@ -15,6 +15,7 @@ midx_chain=$midxdir/multi-pack-index-chain\n \n test_expect_success 'convert non-incremental MIDX to incremental' '\n \ttest_commit base &&\n+\tgit config set maintenance.auto false &&\n \tgit repack -ad &&\n \tgit multi-pack-index write &&\n \ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex 4bb56c167a..0c88d04d0a 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -154,7 +154,8 @@ test_expect_success 'clone shallow depth 1 with fsck' '\n '\n \n test_expect_success 'clone shallow' '\n-\tgit clone --no-single-branch --depth 2 \"file://$(pwd)/.\" shallow\n+\tgit clone --no-single-branch --depth 2 \"file://$(pwd)/.\" shallow &&\n+\tgit -C shallow config set maintenance.auto false\n '\n \n test_expect_success 'clone shallow depth count' '\ndiff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh\nindex d62760eb92..1c2805acca 100755\n--- a/t/t5616-partial-clone.sh\n+++ b/t/t5616-partial-clone.sh\n@@ -585,6 +585,7 @@ test_expect_success 'verify fetch downloads only one pack when updating refs' '\n \tgit clone --filter=blob:none \"file://$(pwd)/srv.bare\" pack-test &&\n \tls pack-test/.git/objects/pack/*pack >pack-list &&\n \ttest_line_count = 2 pack-list &&\n+\ttest_config -C pack-test maintenance.auto false &&\n \tfor i in A B C\n \tdo\n \t\ttest_commit -C src $i &&\ndiff --git a/t/t7700-repack.sh b/t/t7700-repack.sh\nindex 73b78bdd88..acc2589f21 100755\n--- a/t/t7700-repack.sh\n+++ b/t/t7700-repack.sh\n@@ -217,6 +217,7 @@ test_expect_success 'repack --keep-pack' '\n \t\tcd keep-pack &&\n \t\t# avoid producing different packs due to delta/base choices\n \t\tgit config pack.window 0 &&\n+\t\tgit config maintenance.auto false &&\n \t\tP1=$(commit_and_pack 1) &&\n \t\tP2=$(commit_and_pack 2) &&\n \t\tP3=$(commit_and_pack 3) &&\n@@ -260,6 +261,7 @@ test_expect_success 'repacking fails when missing .pack actually means missing o\n \n \t\t# Avoid producing different packs due to delta/base choices\n \t\tgit config pack.window 0 &&\n+\t\tgit config maintenance.auto false &&\n \t\tP1=$(commit_and_pack 1) &&\n \t\tP2=$(commit_and_pack 2) &&\n \t\tP3=$(commit_and_pack 3) &&\n@@ -534,6 +536,7 @@ test_expect_success 'setup for --write-midx tests' '\n \t(\n \t\tcd midx &&\n \t\tgit config core.multiPackIndex true &&\n+\t\tgit config maintenance.auto false &&\n \n \t\ttest_commit base\n \t)\n\n-- \n2.53.0.536.g309c995771.dirty\n\n"},{"id":"536939","messageId":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-3-8657338c6fa1@pks.im","threadId":"65029","inReplyTo":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-0-8657338c6fa1@pks.im","subject":"[PATCH v2 3/8] t34xx: don't expire reflogs where it matters","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-24T08:45:47Z","receivedAt":"2026-02-24T08:46:02Z","isPatch":true,"body":"We have a couple of tests in the t34xx range that rely on reflogs. This\nnever really used to be a problem, but in a subsequent commit we will\nchange the default maintenance strategy from \"gc\" to \"geometric\", and\nthis will cause us to drop all reflogs in these tests.\n\nThis may seem surprising and like a bug at first, but it's actually not.\nThe main difference between these two strategies is that the \"gc\"\nstrategy will skip all maintenance in case the object database is in a\nwell-optimized state. The \"geometric\" strategy has separate subtasks\nthough, and the conditions for each of these tasks is evaluated on a\ncase by case basis. This means that even if the object database is in\ngood shape, we may still decide to expire reflogs.\n\nSo why is that a problem? The issue is that Git's test suite hardcodes\nthe committer and author dates to a date in 2005. Interestingly though,\nthese hardcoded dates not only impact the commits, but also the reflog\nentries. The consequence is that all newly written reflog entries are\nimmediately considered stale as our reflog expiration threshold is in\nthe range of weeks, only. It follows that executing `git reflog expire`\nwill thus immediately purge all reflog entries.\n\nThis hasn't been a problem in our test suite by pure chance, as the\nrepository shapes simply didn't cause us to perform actual garbage\ncollection. But with the upcoming \"geometric\" strategy we _will_ start\nto execute `git reflog expire`, thus surfacing this issue.\n\nPrepare for this by explicitly disabling reflog expiration in tests\nimpacted by this upcoming change.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t3404-rebase-interactive.sh  | 6 ++++++\n t/t3406-rebase-message.sh      | 6 ++++++\n t/t3431-rebase-fork-point.sh   | 6 ++++++\n t/t3432-rebase-fast-forward.sh | 6 ++++++\n 4 files changed, 24 insertions(+)\n\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex e778dd8ae4..3e44562afa 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -31,6 +31,12 @@ Initial setup:\n . \"$TEST_DIRECTORY\"/lib-rebase.sh\n \n test_expect_success 'setup' '\n+\t# Commit dates are hardcoded to 2005, and the reflog entries will have\n+\t# a matching timestamp. Maintenance may thus immediately expire\n+\t# reflogs if it was running.\n+\tgit config set gc.reflogExpire never &&\n+\tgit config set gc.reflogExpireUnreachable never &&\n+\n \tgit switch -C primary &&\n \ttest_commit A file1 &&\n \ttest_commit B file1 &&\ndiff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh\nindex a1d7fa7f7c..bc51a9d3a7 100755\n--- a/t/t3406-rebase-message.sh\n+++ b/t/t3406-rebase-message.sh\n@@ -8,6 +8,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n+\t# Commit dates are hardcoded to 2005, and the reflog entries will have\n+\t# a matching timestamp. Maintenance may thus immediately expire\n+\t# reflogs if it was running.\n+\tgit config set gc.reflogExpire never &&\n+\tgit config set gc.reflogExpireUnreachable never &&\n+\n \ttest_commit O fileO &&\n \ttest_commit X fileX &&\n \tgit branch fast-forward &&\ndiff --git a/t/t3431-rebase-fork-point.sh b/t/t3431-rebase-fork-point.sh\nindex be09fc78c1..4336f417c2 100755\n--- a/t/t3431-rebase-fork-point.sh\n+++ b/t/t3431-rebase-fork-point.sh\n@@ -17,6 +17,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n # C was formerly part of main but main was rewound to remove C\n #\n test_expect_success setup '\n+\t# Commit dates are hardcoded to 2005, and the reflog entries will have\n+\t# a matching timestamp. Maintenance may thus immediately expire\n+\t# reflogs if it was running.\n+\tgit config set gc.reflogExpire never &&\n+\tgit config set gc.reflogExpireUnreachable never &&\n+\n \ttest_commit A &&\n \ttest_commit B &&\n \ttest_commit C &&\ndiff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh\nindex 5086e14c02..181d19dcc1 100755\n--- a/t/t3432-rebase-fast-forward.sh\n+++ b/t/t3432-rebase-fast-forward.sh\n@@ -11,6 +11,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n . ./test-lib.sh\n \n test_expect_success setup '\n+\t# Commit dates are hardcoded to 2005, and the reflog entries will have\n+\t# a matching timestamp. Maintenance may thus immediately expire\n+\t# reflogs if it was running.\n+\tgit config set gc.reflogExpire never &&\n+\tgit config set gc.reflogExpireUnreachable never &&\n+\n \ttest_commit A &&\n \ttest_commit B &&\n \ttest_commit C &&\n\n-- \n2.53.0.536.g309c995771.dirty\n\n"},{"id":"536940","messageId":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-4-8657338c6fa1@pks.im","threadId":"65029","inReplyTo":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-0-8657338c6fa1@pks.im","subject":"[PATCH v2 4/8] t5400: explicitly use \"gc\" strategy","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-24T08:45:48Z","receivedAt":"2026-02-24T08:46:04Z","isPatch":true,"body":"In t5400 we verify that git-receive-pack(1) runs automated repository\nmaintenance in the remote repository. The check is performed indirectly\nby observing an effect that git-gc(1) would have, namely to prune a\ntemporary object from the object database. In a subsequent commit we're\nabout to switch to the \"geometric\" strategy by default though, and here\nwe stop observing that effect.\n\nAdapt the test to explicitly use the \"gc\" strategy to prepare for that\nupcoming change.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t5400-send-pack.sh | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh\nindex 83b42ff073..b32a0a6aa7 100755\n--- a/t/t5400-send-pack.sh\n+++ b/t/t5400-send-pack.sh\n@@ -187,6 +187,7 @@ test_expect_success 'receive-pack runs auto-gc in remote repo' '\n \t\tcd child &&\n \t\tgit config gc.autopacklimit 1 &&\n \t\tgit config gc.autodetach false &&\n+\t\tgit config maintenance.strategy gc &&\n \t\tgit branch test_auto_gc &&\n \t\t# And create a file that follows the temporary object naming\n \t\t# convention for the auto-gc to remove\n\n-- \n2.53.0.536.g309c995771.dirty\n\n"},{"id":"536941","messageId":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-5-8657338c6fa1@pks.im","threadId":"65029","inReplyTo":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-0-8657338c6fa1@pks.im","subject":"[PATCH v2 5/8] t5510: explicitly use \"gc\" strategy","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-24T08:45:49Z","receivedAt":"2026-02-24T08:46:07Z","isPatch":true,"body":"One of the tests in t5510 wants to verify that auto-gc does not lock up\nwhen fetching into a repository. Adapt it to explicitly pick the \"gc\"\nstrategy for auto-maintenance.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t5510-fetch.sh | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex c69afb5a60..5dcb4b51a4 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -1321,6 +1321,7 @@ test_expect_success 'fetching with auto-gc does not lock up' '\n \t\tgit config fetch.unpackLimit 1 &&\n \t\tgit config gc.autoPackLimit 1 &&\n \t\tgit config gc.autoDetach false &&\n+\t\tgit config maintenance.strategy gc &&\n \t\tGIT_ASK_YESNO=\"$TRASH_DIRECTORY/askyesno\" git fetch --verbose >fetch.out 2>&1 &&\n \t\ttest_grep \"Auto packing the repository\" fetch.out &&\n \t\t! grep \"Should I try again\" fetch.out\n\n-- \n2.53.0.536.g309c995771.dirty\n\n"},{"id":"536942","messageId":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-6-8657338c6fa1@pks.im","threadId":"65029","inReplyTo":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-0-8657338c6fa1@pks.im","subject":"[PATCH v2 6/8] t6500: explicitly use \"gc\" strategy","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-24T08:45:50Z","receivedAt":"2026-02-24T08:46:10Z","isPatch":true,"body":"The test in t6500 explicitly wants to exercise git-gc(1) and is thus\nhighly specific to the actual on-disk state of the repository and\nspecifically of the object database. An upcoming change modifies the\ndefault maintenance strategy to be the \"geometric\" strategy though,\nwhich breaks a couple of assumptions.\n\nOne fix would arguably be to disable auto-maintenance altogether, as we\ndo want to explicitly verify git-gc(1) anyway. But as the whole test\nsuite is about git-gc(1) in the first place it feels more sensible to\nconfigure the default maintenance strategy to be \"gc\".\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t6500-gc.sh | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/t/t6500-gc.sh b/t/t6500-gc.sh\nindex bef472cb8d..ea9aaad470 100755\n--- a/t/t6500-gc.sh\n+++ b/t/t6500-gc.sh\n@@ -11,6 +11,7 @@ test_expect_success 'setup' '\n \t# behavior, make sure we always pack everything to one pack by\n \t# default\n \tgit config gc.bigPackThreshold 2g &&\n+\tgit config set --global maintenance.strategy gc &&\n \ttest_oid_init\n '\n \n\n-- \n2.53.0.536.g309c995771.dirty\n\n"},{"id":"536943","messageId":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-7-8657338c6fa1@pks.im","threadId":"65029","inReplyTo":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-0-8657338c6fa1@pks.im","subject":"[PATCH v2 7/8] t7900: prepare for switch of the default strategy","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-24T08:45:51Z","receivedAt":"2026-02-24T08:46:13Z","isPatch":true,"body":"The t7900 test suite is exercising git-maintenance(1) and is thus of\ncourse heavily reliant on the exact maintenance strategy. This reliance\ncomes in two flavors:\n\n  - One test explicitly wants to verify that git-gc(1) is run as part of\n    `git maintenance run`. This test is adapted by explicitly picking the\n    \"gc\" strategy.\n\n  - The other tests assume a specific shape of the object database,\n    which is dependent on whether or not we run auto-maintenance before\n    we come to the actual subject under test. These tests are adapted by\n    disabling auto-maintenance.\n\nWith these changes t7900 passes with both \"gc\" and \"geometric\" default\nstrategies.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/t7900-maintenance.sh | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh\nindex fe344f47ee..4700beacc1 100755\n--- a/t/t7900-maintenance.sh\n+++ b/t/t7900-maintenance.sh\n@@ -45,7 +45,8 @@ test_expect_success 'help text' '\n \ttest_grep \"usage: git maintenance\" err\n '\n \n-test_expect_success 'run [--auto|--quiet]' '\n+test_expect_success 'run [--auto|--quiet] with gc strategy' '\n+\ttest_config maintenance.strategy gc &&\n \tGIT_TRACE2_EVENT=\"$(pwd)/run-no-auto.txt\" \\\n \t\tgit maintenance run 2>/dev/null &&\n \tGIT_TRACE2_EVENT=\"$(pwd)/run-auto.txt\" \\\n@@ -499,6 +500,7 @@ test_expect_success 'maintenance.incremental-repack.auto' '\n \t(\n \t\tcd incremental-repack-true &&\n \t\tgit config core.multiPackIndex true &&\n+\t\tgit config maintenance.auto false &&\n \t\trun_incremental_repack_and_verify\n \t)\n '\n@@ -509,6 +511,7 @@ test_expect_success 'maintenance.incremental-repack.auto (when config is unset)'\n \t(\n \t\tcd incremental-repack-unset &&\n \t\ttest_unconfig core.multiPackIndex &&\n+\t\tgit config maintenance.auto false &&\n \t\trun_incremental_repack_and_verify\n \t)\n '\n@@ -619,6 +622,7 @@ test_expect_success 'geometric repacking with --auto' '\n \tgit init repo &&\n \t(\n \t\tcd repo &&\n+\t\tgit config set maintenance.auto false &&\n \n \t\t# An empty repository does not need repacking, except when\n \t\t# explicitly told to do it.\n\n-- \n2.53.0.536.g309c995771.dirty\n\n"},{"id":"536944","messageId":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-8-8657338c6fa1@pks.im","threadId":"65029","inReplyTo":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-0-8657338c6fa1@pks.im","subject":"[PATCH v2 8/8] builtin/maintenance: use \"geometric\" strategy by default","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-02-24T08:45:52Z","receivedAt":"2026-02-24T08:46:16Z","isPatch":true,"body":"The git-gc(1) command has been introduced in the early days of Git in\n30f610b7b0 (Create 'git gc' to perform common maintenance operations.,\n2006-12-27) as the main repository maintenance utility. And while the\ntool has of course evolved since then to cover new parts, the basic\nstrategy it uses has never really changed much.\n\nIt is safe to say that since 2006 the Git ecosystem has changed quite a\nbit. Repositories tend to be much larger nowadays than they have been\nalmost 20 years ago, and large parts of the industry went crazy for\nmonorepos (for various wildly different definitions of \"monorepo\"). So\nthe maintenance strategy we used back then may not be the best fit\nnowadays anymore.\n\nArguably, most of the maintenance tasks that git-gc(1) does are still\nperfectly fine today: repacking references, expiring various data\nstructures and things like tend to not cause huge problems. But the big\nexception is the way we repack objects.\n\ngit-gc(1) by default uses a split strategy: it performs incremental\nrepacks by default, and then whenever we have too many packs we perform\na large all-into-one repack. This all-into-one repack is what is causing\nproblems nowadays, as it is an operation that is quite expensive. While\nit is wasteful in small- and medium-sized repositories, in large repos\nit may even be prohibitively expensive.\n\nWe have eventually introduced git-maintenance(1) that was slated as a\nreplacement for git-gc(1). In contrast to git-gc(1), it is much more\nflexible as it is structured around configurable tasks and strategies.\nSo while its default \"gc\" strategy still uses git-gc(1) under the hood,\nit allows us to iterate.\n\nA second strategy it knows about is the \"incremental\" strategy, which we\nconfigure when registering a repository for scheduled maintenance. This\nstrategy isn't really a full replacement for git-gc(1) though, as it\ndoesn't know to expire unused data structures. In Git 2.52 we have thus\nintroduced a new \"geometric\" strategy that is a proper replacement for\nthe old git-gc(1).\n\nIn contrast to the incremental/all-into-one split used by git-gc(1), the\nnew \"geometric\" strategy maintains a geometric progression of packfiles,\nwhich significantly reduces the number of all-into-one repacks that we\nhave to perform in large repositories. It is thus a much better fit for\nlarge repositories than git-gc(1).\n\nNote that the \"geometric\" strategy isn't perfect though: while we\nperform way less all-into-one repacks compared to git-gc(1), we still\nhave to perform them eventually. But for the largest repositories out\nthere this may not be an option either, as client machines might not be\npowerful enough to perform such a repack in the first place. These cases\nwould thus still be covered by the \"incremental\" strategy.\n\nSwitch the default strategy away from \"gc\" to \"geometric\", but retain\nthe \"incremental\" strategy configured when registering background\nmaintenance with `git maintenance register`.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n Documentation/config/maintenance.adoc | 6 +++---\n builtin/gc.c                          | 2 +-\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/config/maintenance.adoc b/Documentation/config/maintenance.adoc\nindex d0c38f03fa..b578856dde 100644\n--- a/Documentation/config/maintenance.adoc\n+++ b/Documentation/config/maintenance.adoc\n@@ -30,8 +30,7 @@ The possible strategies are:\n +\n * `none`: This strategy implies no tasks are run at all. This is the default\n   strategy for scheduled maintenance.\n-* `gc`: This strategy runs the `gc` task. This is the default strategy for\n-  manual maintenance.\n+* `gc`: This strategy runs the `gc` task.\n * `geometric`: This strategy performs geometric repacking of packfiles and\n   keeps auxiliary data structures up-to-date. The strategy expires data in the\n   reflog and removes worktrees that cannot be located anymore. When the\n@@ -40,7 +39,8 @@ The possible strategies are:\n   are already part of a cruft pack will be expired.\n +\n This repacking strategy is a full replacement for the `gc` strategy and is\n-recommended for large repositories.\n+recommended for large repositories. This is the default strategy for manual\n+maintenance.\n * `incremental`: This setting optimizes for performing small maintenance\n   activities that do not delete any data. This does not schedule the `gc`\n   task, but runs the `prefetch` and `commit-graph` tasks hourly, the\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 4390eee6ec..fb329c2cff 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -1980,7 +1980,7 @@ static void initialize_task_config(struct maintenance_run_opts *opts,\n \t\tstrategy = none_strategy;\n \t\ttype = MAINTENANCE_TYPE_SCHEDULED;\n \t} else {\n-\t\tstrategy = gc_strategy;\n+\t\tstrategy = geometric_strategy;\n \t\ttype = MAINTENANCE_TYPE_MANUAL;\n \t}\n \n\n-- \n2.53.0.536.g309c995771.dirty\n\n"},{"id":"536967","messageId":"20282180-d018-47db-a44e-93c53af10d00@gmail.com","threadId":"65029","inReplyTo":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-8-8657338c6fa1@pks.im","subject":"Re: [PATCH v2 8/8] builtin/maintenance: use \"geometric\" strategy by default","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2026-02-24T12:12:29Z","receivedAt":"2026-02-24T12:12:31Z","isPatch":true,"body":"On 2/24/26 3:45 AM, Patrick Steinhardt wrote:\n\n> @@ -30,8 +30,7 @@ The possible strategies are:\n>   +\n>   * `none`: This strategy implies no tasks are run at all. This is the default\n>     strategy for scheduled maintenance.\n> -* `gc`: This strategy runs the `gc` task. This is the default strategy for\n> -  manual maintenance.\n> +* `gc`: This strategy runs the `gc` task.\n>   * `geometric`: This strategy performs geometric repacking of packfiles and\n>     keeps auxiliary data structures up-to-date. The strategy expires data in the\n>     reflog and removes worktrees that cannot be located anymore. When the\n> @@ -40,7 +39,8 @@ The possible strategies are:\n>     are already part of a cruft pack will be expired.\n>   +\n>   This repacking strategy is a full replacement for the `gc` strategy and is\n> -recommended for large repositories.\n> +recommended for large repositories. This is the default strategy for manual\n> +maintenance.\n\nThanks for these updates.\n\nWith this, v2 looks good to me.\n\n-Stolee\n"},{"id":"536990","messageId":"aZ3zz4m4OJkw0Yfz@denethor","threadId":"65029","inReplyTo":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-0-8657338c6fa1@pks.im","subject":"Re: [PATCH v2 0/8] builtin/maintenance: use \"geometric\" strategy by default","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2026-02-24T18:54:41Z","receivedAt":"2026-02-24T18:54:45Z","isPatch":true,"body":"On 26/02/24 09:45AM, Patrick Steinhardt wrote:\n> Changes in v2:\n>   - Document the updated default strategy.\n>   - Clarify how this interacts with Scalar.\n>   - Explain the current landscape of strategies a bit better.\n>   - Leave some breadcrumbs in the tests.\n>   - Link to v1: https://lore.kernel.org/r/20260220-b4-pks-maintenance-default-geometric-strategy-v1-0-faeb321ad13b@pks.im\n\nThanks Patrick. This version addresses all my previous comments and\nlooks good to me.\n\n-Justin\n"},{"id":"537078","messageId":"871pi9nnao.fsf@iotcl.com","threadId":"65029","inReplyTo":"20260224-b4-pks-maintenance-default-geometric-strategy-v2-6-8657338c6fa1@pks.im","subject":"Re: [PATCH v2 6/8] t6500: explicitly use \"gc\" strategy","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2026-02-25T10:13:35Z","receivedAt":"2026-02-25T10:13:52Z","isPatch":true,"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> The test in t6500 explicitly wants to exercise git-gc(1) and is thus\n> highly specific to the actual on-disk state of the repository and\n> specifically of the object database. An upcoming change modifies the\n> default maintenance strategy to be the \"geometric\" strategy though,\n> which breaks a couple of assumptions.\n>\n> One fix would arguably be to disable auto-maintenance altogether, as we\n> do want to explicitly verify git-gc(1) anyway. But as the whole test\n> suite is about git-gc(1) in the first place it feels more sensible to\n> configure the default maintenance strategy to be \"gc\".\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  t/t6500-gc.sh | 1 +\n>  1 file changed, 1 insertion(+)\n>\n> diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh\n> index bef472cb8d..ea9aaad470 100755\n> --- a/t/t6500-gc.sh\n> +++ b/t/t6500-gc.sh\n> @@ -11,6 +11,7 @@ test_expect_success 'setup' '\n>  \t# behavior, make sure we always pack everything to one pack by\n>  \t# default\n>  \tgit config gc.bigPackThreshold 2g &&\n> +\tgit config set --global maintenance.strategy gc &&\n\nI wasn't sure (no more) what effect setting globally would have. But\nbecause each test file operates in it's own $TRASH_DIRECTORY, a global\nsetting only affects that file.\n\nMakes sense.\n\n-- \nCheers,\nToon\n"},{"id":"537080","messageId":"87wm01m7to.fsf@iotcl.com","threadId":"65029","inReplyTo":"20282180-d018-47db-a44e-93c53af10d00@gmail.com","subject":"Re: [PATCH v2 8/8] builtin/maintenance: use \"geometric\" strategy by default","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2026-02-25T10:33:07Z","receivedAt":"2026-02-25T10:33:21Z","isPatch":true,"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n> With this, v2 looks good to me.\n>\n> -Stolee\n>\n\nAlso did a review on v2, and I've got no comments about it. Approving.\n\n-- \nCheers,\nToon\n\n"},{"id":"550149","messageId":"17d460c0-564e-45fd-830e-548f60e01e01@haller-berlin.de","threadId":"65029","inReplyTo":"20260220-b4-pks-maintenance-default-geometric-strategy-v1-1-faeb321ad13b@pks.im","subject":"Re: [PATCH 1/8] t: fix races caused by background maintenance","fromName":"Stefan Haller","fromEmail":"lists@haller-berlin.de","sentAt":"2026-08-10T04:43:45Z","receivedAt":"2026-08-10T04:53:10Z","isPatch":true,"body":"On 20.02.26 11:15, Patrick Steinhardt wrote:\n> Introduce a new `GIT_TEST_MAINT_AUTO_DETACH` environment variable that\n> allows us to override the auto-detach behaviour and set that varibale in\n> our tests.\nI have just run into this problem with the lazygit test suite, and I\nworked around it there by turning off auto maintenance altogether. Some\nmore details of how this affected us and why can be found in [1].\n\nI'm fine with that solution, but I do wonder why we think this doesn't\nalso affect ordinary usage. Lazygit's integration test suite doesn't do\nanything special, it simply executes git commands like a normal user\nwould. Maybe a bit faster than a normal user would type them, but for\nscripts that create a bunch of files, stage them, and commit them, I see\nno reason why they shouldn't run into the same problem. Or am I missing\nsomething?\n\nThanks,\nStefan\n\n\n[1] <https://github.com/jesseduffield/lazygit/pull/5898/\n      changes/4ec91a0bf58e07ce040f08600cd0c6b64f996e07>\n"},{"id":"550152","messageId":"anlfk0P7UillhlUd@pks.im","threadId":"65029","inReplyTo":"17d460c0-564e-45fd-830e-548f60e01e01@haller-berlin.de","subject":"Re: [PATCH 1/8] t: fix races caused by background maintenance","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-08-10T05:20:19Z","receivedAt":"2026-08-10T05:20:26Z","isPatch":true,"body":"On Mon, Aug 10, 2026 at 06:43:45AM +0200, Stefan Haller wrote:\n> On 20.02.26 11:15, Patrick Steinhardt wrote:\n> > Introduce a new `GIT_TEST_MAINT_AUTO_DETACH` environment variable that\n> > allows us to override the auto-detach behaviour and set that varibale in\n> > our tests.\n> I have just run into this problem with the lazygit test suite, and I\n> worked around it there by turning off auto maintenance altogether. Some\n> more details of how this affected us and why can be found in [1].\n> \n> I'm fine with that solution, but I do wonder why we think this doesn't\n> also affect ordinary usage. Lazygit's integration test suite doesn't do\n> anything special, it simply executes git commands like a normal user\n> would. Maybe a bit faster than a normal user would type them, but for\n> scripts that create a bunch of files, stage them, and commit them, I see\n> no reason why they shouldn't run into the same problem. Or am I missing\n> something?\n\nIt does affect ordinary usage, but the expectation is that a \"normal\"\nuser should not care about repository maintenance running in parallel to\nus. It should be completely transparent to them in the best case. Git\ncommands should just work with concurrent maintenance, and if they don't\nthen it's worth to have a deeper look at why it doesn't.\n\nThe reason why it's not fine for the Git test suite is that in lots of\ncases we assume a lot about the on-disk state of the repository. We are\noften reaching into internals to verify that it looks as expected, and\nthat is of course racing with concurrent maintenance. And hence we have\nto be more careful than users, as they are not supposed to reach into\nrepository internals without Git or an implementation thereof.\n\nPatrick\n"},{"id":"550160","messageId":"801031d7-f219-4410-a863-7410cff7952f@haller-berlin.de","threadId":"65029","inReplyTo":"anlfk0P7UillhlUd@pks.im","subject":"Re: [PATCH 1/8] t: fix races caused by background maintenance","fromName":"Stefan Haller","fromEmail":"lists@haller-berlin.de","sentAt":"2026-08-10T07:37:01Z","receivedAt":"2026-08-10T07:37:03Z","isPatch":true,"body":"On 10.08.26 07:20, Patrick Steinhardt wrote:\n\n> Git commands should just work with concurrent maintenance, and if they\n> don't then it's worth to have a deeper look at why it doesn't.\n\nThat was my point; in lazygit's test suite I was getting errors when\nexecuting simple commands such as \"create a bunch of files, git add,\ngit commit\". I had cases where the commit fails with\n\n  error: invalid object 100644 50d5612... for 'file09.txt'\n  error: Error building trees\n\n> The reason why it's not fine for the Git test suite is that in lots of\n> cases we assume a lot about the on-disk state of the repository. We are\n> often reaching into internals to verify that it looks as expected, and\n> that is of course racing with concurrent maintenance.\n\nYes, I understand why git's test suite has reasons to disable concurrent\nmaintenance. My point was that lazygit doesn't have any such reasons,\nand shouldn't have to disable maintenance just so that the commands it\ninvokes don't error.\n\nStefan\n"},{"id":"550162","messageId":"anmNX-WVohAyjEcc@pks.im","threadId":"65029","inReplyTo":"801031d7-f219-4410-a863-7410cff7952f@haller-berlin.de","subject":"Re: [PATCH 1/8] t: fix races caused by background maintenance","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-08-10T08:35:43Z","receivedAt":"2026-08-10T08:35:50Z","isPatch":true,"body":"On Mon, Aug 10, 2026 at 09:37:01AM +0200, Stefan Haller wrote:\n> On 10.08.26 07:20, Patrick Steinhardt wrote:\n> \n> > Git commands should just work with concurrent maintenance, and if they\n> > don't then it's worth to have a deeper look at why it doesn't.\n> \n> That was my point; in lazygit's test suite I was getting errors when\n> executing simple commands such as \"create a bunch of files, git add,\n> git commit\". I had cases where the commit fails with\n> \n>   error: invalid object 100644 50d5612... for 'file09.txt'\n>   error: Error building trees\n\nThat's a bug then that we ought to fix. Do you maybe have a reproducer\nfor this?\n\nAlso, which version of Git are you testing this with? We recently had an\nissue with multiple concurrent git-maintenance(1) processes running at\nthe same time, which is something that shouldn't ever happen. That was\nfixed already, but IIRC Git 2.54 was still prone to this race.\n\nPatrick\n"},{"id":"550166","messageId":"4f6a96ac-d993-4872-b3c4-30d899f61ca9@haller-berlin.de","threadId":"65029","inReplyTo":"anmNX-WVohAyjEcc@pks.im","subject":"Re: [PATCH 1/8] t: fix races caused by background maintenance","fromName":"Stefan Haller","fromEmail":"lists@haller-berlin.de","sentAt":"2026-08-10T10:45:57Z","receivedAt":"2026-08-10T10:46:00Z","isPatch":true,"body":"On 10.08.26 10:35, Patrick Steinhardt wrote:\n\n> On Mon, Aug 10, 2026 at 09:37:01AM +0200, Stefan Haller wrote:\n>\n>> On 10.08.26 07:20, Patrick Steinhardt wrote:\n>>\n>> I had cases where the commit fails with\n>>\n>>   error: invalid object 100644 50d5612... for 'file09.txt'\n>>   error: Error building trees\n> \n> That's a bug then that we ought to fix. Do you maybe have a reproducer\n> for this?\n\nI had my friendly AI agent dig into this more, and below is what it came up\nwith. Disclaimer: I only skimmed it, and didn't check it for accuracy; I'm way\ntoo unfamiliar with any of this stuff. I hope this is of some use.\n\n----------\n\nSubject: [BUG] geometric-repack --auto fires on tiny repos, and the\n resulting background repacks race concurrent commands\n\nSince v2.54.0, `git commit` in an ordinary small repository can leave a\ndetached `git repack` running behind it, and it does so far more often\nthan intended: the auto condition that is documented as \"at least 100\nloose objects\" is in practice satisfied by a repository containing four\nobjects.  The background repacks that result break concurrent git\ncommands at a rate of roughly one in a thousand commits in my testing.\n\nThere are two separate issues here.  The first is a plain bug; the\nsecond is its fallout.\n\n1. The geometric-repack auto condition triggers ~256x too eagerly\n----------------------------------------------------------------\n\nDocumentation/config/maintenance.adoc says:\n\n    maintenance.geometric-repack.auto::\n            [...] a positive value implies the command should run either\n            when there are packfiles that need to be merged together to\n            retain the geometric progression, or when there are at least\n            this many loose objects that would be written into a new\n            packfile. The default value is 100.\n\nReproducer, on a repository with four objects (v2.54.0 and v2.55.0):\n\n    git init repro && cd repro\n    git config maintenance.auto false   # so that the setup itself does\n                                        # not fork any maintenance\n    echo 263 >a.txt                     # blob 175b6c5d...\n    echo 410 >b.txt                     # blob 17e344e7...\n    git add . && git commit -m initial\n\n    $ find .git/objects -type f\n    .git/objects/17/5b6c5dfd7f9bf6e2b2c4e2dcf3e2341298575d\n    .git/objects/17/e344e7c08441fa81d5b56c21008dc0feeeaa20\n    .git/objects/70/a9a72c46fc85c654e626f54e77c4946950da52\n    .git/objects/7f/174478a536fc641c735e6b6e1a1944c868ba0c\n\n    $ git maintenance run --auto --no-detach\n    $ ls .git/objects/pack/\n    multi-pack-index\n    pack-f826a2dd....idx\n    pack-f826a2dd....pack\n    pack-f826a2dd....rev\n\n(only the two blob IDs are fixed; the tree and commit depend on the\ncommit timestamp.)  Committing only a.txt -- three objects, one of them\nin objects/17 -- does not trigger the repack.  Adding the second blob\nwhose object ID starts with \"17\" does.  That is the whole condition.\n\ngeometric_repack_auto_condition() (builtin/gc.c) passes its threshold to\ntoo_many_loose_objects(), which does not count loose objects: it counts\nthe entries of .git/objects/17 and scales by 256.  In v2.54.0:\n\n    int auto_threshold = DIV_ROUND_UP(limit, 256);\n    [...]\n            if (++num_loose > auto_threshold) {\n\nand equivalently after the rewrite in v2.55.0:\n\n    /*\n     * This is weird, but stems from legacy behaviour: [...]\n     */\n    int auto_threshold = DIV_ROUND_UP(limit, 256) * 256;\n    [...]\n    return loose_count > auto_threshold;\n\nwith loose_count coming from ODB_COUNT_OBJECTS_APPROXIMATE, i.e. the\nsame one-directory estimate.  Either way, any limit <= 256 collapses to\n\"two or more objects share the objects/17 directory\".\n\nThat estimator is fine for gc.auto, whose default of 6700 needs 27\nentries in that directory -- a number you only reach with thousands of\nobjects, and whose documentation says \"approximately\".  It falls apart\nfor a threshold below 256, where the smallest representable estimate\nstep exceeds the threshold itself and a single fanout collision decides\nthe outcome.  For a repository with n objects the condition is satisfied\nwith probability ~1-(1-p)^n-np(1-p)^(n-1), p=1/256: about 5% at 90\nobjects, and much higher for repositories that accumulate objects over\ntime.\n\nBy contrast maintenance.loose-objects.auto, documented in the same terms,\nis implemented as an exact count in loose_object_auto_condition().\n\nSince 452b12c2e0 (builtin/maintenance: use \"geometric\" strategy by\ndefault, 2026-02-24) this condition governs what `git maintenance run\n--auto` does, and run_auto_maintenance() invokes that from commit, am,\nfetch, merge and rebase, with --detach unless gc.autoDetach says\notherwise.  So the practical effect is: once any two objects in a\nrepository collide in objects/17 -- and the condition only becomes\neasier to satisfy as the repository grows -- every subsequent commit\nforks a detached repack.\n\nMeasured on a fixture repository built by 40 commits of \"write a file,\ngit add, git commit\": a background repack fired in 4 of 25 runs (the\nprediction from the 40 nondeterministic commit object IDs alone is\n1-(255/256)^40 = 14%).\n\n2. The resulting background repacks break concurrent commands\n-------------------------------------------------------------\n\n`git repack -d` installs the new pack, removes the redundant ones and\nthen calls prune_packed_objects(), which unlinks the loose copies of\nobjects that are now packed and rmdir()s the fanout directories it\nempties.  Doing that concurrently with unrelated git processes in the\nsame repository is new exposure: before v2.54.0 the same repositories\nnever reached the gc.auto threshold and no such repack ever ran.\n\nStress test: 24 concurrent workers, each repeatedly creating a fresh\nrepository and running 40 iterations of \"write a file, git add, git\ncommit\", with maintenance.geometric-repack.auto=-1 so that the condition\nfires on every commit rather than in ~15% of repositories.  Nothing else\ntouches the repositories; the only concurrency is git's own detached\nauto-maintenance.\n\n109280 commits produced 97 failing commands, i.e. one per ~1100 commits.\nMessage lines across those 97 failures (one failure often emits two of\nthem, e.g. a temporary-file error followed by a fatal):\n\n      71  error: packfile .../pack-....pack index unavailable\n      50  fatal: failed to write commit object\n      37  error: unable to create temporary file: Invalid argument\n      23  error: unable to create temporary file: No such file or directory\n      10  error: unable to index file '...'\n      10  fatal: adding files failed\n\nAt least the ENOENT variant has a straightforward explanation:\n\n  - The writer, in create_tmpfile() (object-file.c), calls\n    mkstemp(\".git/objects/XX/tmp_obj_XXXXXX\").  On ENOENT it mkdir()s\n    the fanout directory and retries -- exactly once.\n\n  - The repack, in prune_subdir() (prune-packed.c), rmdir()s each fanout\n    directory after pruning its contents.\n\nAn rmdir() landing between the writer's mkdir() and its retried\nmkstemp() defeats the single retry, and the command aborts.  The writer\nretries a bounded number of times against interference that is not\nbounded.\n\n(For anyone hitting this meanwhile: setting maintenance.auto=false makes\nit go away, since it stops the fork at the source.  We have done that in\nlazygit's test configuration.)\n\n3. Where this came from\n-----------------------\n\nlazygit's integration tests started failing intermittently on CI, always\nand only in the job running the newest git (2.54.0 on GitHub Actions\nubuntu-latest), never in the jobs pinned to 2.32.0/2.38.2/2.44.0, and\nalways during test fixture setup, which is a plain loop of \"write a\nfile, git add, git commit\":\n\n    error: invalid object 100644 50d561270fcfcdc9afc85f6136f937c529accaaa for 'file09.txt'\n    error: invalid object 100644 50d561270fcfcdc9afc85f6136f937c529accaaa for 'file09.txt'\n    error: Error building trees\n\n(the first message really is printed twice), i.e. the cache-tree\nexistence check in update_one() (cache-tree.c) failing to find a blob\nthat a previous iteration had successfully committed.  The hash is the\ncorrect blob for the file's content, so `git add` had staged it fine.\n\nThose fixture repositories are exactly the case in section 1: they\ncontain one object in objects/17 by construction, so a single commit\nobject hashing into the same directory tips them over, and every\nfollowing commit forks a repack.  The affected tests were, without\nexception, the ones whose fixtures build six or more commits.\n\nI could not reproduce this specific failure outside Linux, and I have\nnot isolated the window that produces it.  A fresh process is robust\n(7200 lookups against a repack loop, no misses), and so is a `git\ncommit` whose repack completes mid-flight -- the flag the cache-tree\ncheck passes, ODB_HAS_OBJECT_RECHECK_PACKED, makes a missing lookup\nreprepare the pack list and retry, and that recovery does work in\neverything I could construct deliberately.  So I am reporting this as\nthe symptom that led to the above rather than as a diagnosed bug, in\ncase it is familiar to someone.\n\nOne observation from reading that retry path, which may or may not be\nrelated: packfile_store_reprepare() refreshes the pack list but never\nreloads the multi-pack-index, because prepare_multi_pack_index_one()\n(midx.c) returns early when one is already loaded.  A long-lived process\ntherefore keeps a midx that may reference packs a concurrent repack has\nsince deleted, for its entire lifetime.\n\nEnvironment\n-----------\n\nOriginal failures: git 2.54.0, GitHub Actions ubuntu-latest.\nReproducers and measurements above: git 2.55.0 (built from source),\nmacOS 26, APFS.  Code references are to v2.54.0 unless stated; I checked\nthat the behaviour is unchanged in v2.55.0.\n\nThanks,\nStefan\n\n"},{"id":"550188","messageId":"annYNOWrEx1PwjQw@pks.im","threadId":"65029","inReplyTo":"4f6a96ac-d993-4872-b3c4-30d899f61ca9@haller-berlin.de","subject":"Re: [PATCH 1/8] t: fix races caused by background maintenance","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-08-10T13:55:00Z","receivedAt":"2026-08-10T13:55:07Z","isPatch":true,"body":"On Mon, Aug 10, 2026 at 12:45:57PM +0200, Stefan Haller wrote:\n> On 10.08.26 10:35, Patrick Steinhardt wrote:\n> > On Mon, Aug 10, 2026 at 09:37:01AM +0200, Stefan Haller wrote:\n[snip]\n> geometric_repack_auto_condition() (builtin/gc.c) passes its threshold to\n> too_many_loose_objects(), which does not count loose objects: it counts\n> the entries of .git/objects/17 and scales by 256.  In v2.54.0:\n> \n>     int auto_threshold = DIV_ROUND_UP(limit, 256);\n>     [...]\n>             if (++num_loose > auto_threshold) {\n> \n> and equivalently after the rewrite in v2.55.0:\n> \n>     /*\n>      * This is weird, but stems from legacy behaviour: [...]\n>      */\n>     int auto_threshold = DIV_ROUND_UP(limit, 256) * 256;\n>     [...]\n>     return loose_count > auto_threshold;\n> \n> with loose_count coming from ODB_COUNT_OBJECTS_APPROXIMATE, i.e. the\n> same one-directory estimate.  Either way, any limit <= 256 collapses to\n> \"two or more objects share the objects/17 directory\".\n\nThat's by design, and is also true for git-gc(1).\n\n> That estimator is fine for gc.auto, whose default of 6700 needs 27\n> entries in that directory -- a number you only reach with thousands of\n> objects, and whose documentation says \"approximately\".  It falls apart\n> for a threshold below 256, where the smallest representable estimate\n> step exceeds the threshold itself and a single fanout collision decides\n> the outcome.  For a repository with n objects the condition is satisfied\n> with probability ~1-(1-p)^n-np(1-p)^(n-1), p=1/256: about 5% at 90\n> objects, and much higher for repositories that accumulate objects over\n> time.\n\nBut I tend to agree that the default value here is too low. That's an\neasy-enough change to make:\n\ndiff --git a/Documentation/config/maintenance.adoc b/Documentation/config/maintenance.adoc\nindex b578856dde..da8be9f812 100644\n--- a/Documentation/config/maintenance.adoc\n+++ b/Documentation/config/maintenance.adoc\n@@ -101,7 +101,7 @@ maintenance.geometric-repack.auto::\n \tthere are packfiles that need to be merged together to retain the\n \tgeometric progression, or when there are at least this many loose\n \tobjects that would be written into a new packfile. The default value is\n-\t100.\n+\t6700.\n \n maintenance.geometric-repack.splitFactor::\n \tThis integer config option controls the factor used for the geometric\ndiff --git a/odb/source-files.c b/odb/source-files.c\nindex 5a68af7d84..555e466145 100644\n--- a/odb/source-files.c\n+++ b/odb/source-files.c\n@@ -521,7 +521,7 @@ bool odb_source_files_optimize_required(struct odb_source *source,\n \t\t};\n \t\tstruct existing_packs existing_packs = EXISTING_PACKS_INIT;\n \t\tstruct string_list kept_packs = STRING_LIST_INIT_DUP;\n-\t\tint auto_value = 100;\n+\t\tint auto_value = 6700;\n \t\tbool ret;\n \n \t\trepo_config_get_int(repo, \"maintenance.geometric-repack.auto\",\n\n> 2. The resulting background repacks break concurrent commands\n> -------------------------------------------------------------\n> \n> `git repack -d` installs the new pack, removes the redundant ones and\n> then calls prune_packed_objects(), which unlinks the loose copies of\n> objects that are now packed and rmdir()s the fanout directories it\n> empties.  Doing that concurrently with unrelated git processes in the\n> same repository is new exposure: before v2.54.0 the same repositories\n> never reached the gc.auto threshold and no such repack ever ran.\n\nOkay, so the issue is basically preexistent, but because we now repack a\nlot more aggressively it's surfacing more often. Ideally, we'd fix that,\nbut it's also clear that repacking too often will make us race a lot\nmore, so we should avoid doing that too aggressively.\n\nDoes the issue go away if you set `maintenance.geometric-repack.auto=6700`?\nIf yes, I'd propose to simply change that default to be in line with\nwhat git-gc(1) uses.\n\nThanks!\n\nPatrick\n"},{"id":"550195","messageId":"7fbda96f-a16e-43f4-91c1-00c08d956775@haller-berlin.de","threadId":"65029","inReplyTo":"annYNOWrEx1PwjQw@pks.im","subject":"Re: [PATCH 1/8] t: fix races caused by background maintenance","fromName":"Stefan Haller","fromEmail":"lists@haller-berlin.de","sentAt":"2026-08-10T15:12:55Z","receivedAt":"2026-08-10T15:12:58Z","isPatch":true,"body":"On 10.08.26 15:55, Patrick Steinhardt wrote:\n\n> Does the issue go away if you set `maintenance.geometric-repack.auto=6700`?\n> If yes, I'd propose to simply change that default to be in line with\n> what git-gc(1) uses.\nI'm afraid it would take me too long to find out; the issue occurred\nsporadically on CI, so I'm not sure how many CI runs I'd have to trigger\non a branch to be reasonably sure it doesn't reoccur with that setting.\n(And I'd prefer not to make that change on main, I'm quite happy with my\ncurrent solution that's guaranteed stable.)\n\nStefan\n"}]}