aboutsummaryrefslogtreecommitdiffstats
path: root/t
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2025-10-24 08:57:16 +0200
committerJunio C Hamano <gitster@pobox.com>2025-10-24 13:42:43 -0700
commit9bc151850c1c593f4baf8d6d2a1d14bb4875844a (patch)
tree702f25cf8d1501e1f5a61f5f140a324a473e79e8 /t
parentbuiltin/gc: make `too_many_loose_objects()` reusable without GC config (diff)
downloadgit-9bc151850c1c593f4baf8d6d2a1d14bb4875844a.tar.gz
git-9bc151850c1c593f4baf8d6d2a1d14bb4875844a.zip
builtin/maintenance: introduce "geometric-repack" task
Introduce a new "geometric-repack" task. This task uses our geometric repack infrastructure as provided by git-repack(1) itself, which is a strategy that especially hosting providers tend to use to amortize the costs of repacking objects. There is one issue though with geometric repacks, namely that they unconditionally pack all loose objects, regardless of whether or not they are reachable. This is done because it means that we can completely skip the reachability step, which significantly speeds up the operation. But it has the big downside that we are unable to expire objects over time. To address this issue we thus use a split strategy in this new task: whenever a geometric repack would merge together all packs, we instead do an all-into-one repack. By default, these all-into-one repacks have cruft packs enabled, so unreachable objects would now be written into their own pack. Consequently, they won't be soaked up during geometric repacking anymore and can be expired with the next full repack, assuming that their expiry date has surpassed. Signed-off-by: Patrick Steinhardt <ps@pks.im> Acked-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 't')
-rwxr-xr-xt/t7900-maintenance.sh138
1 files changed, 138 insertions, 0 deletions
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index ddd273d8dc..ace0ba8300 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -465,6 +465,144 @@ test_expect_success 'maintenance.incremental-repack.auto (when config is unset)'
)
'
+run_and_verify_geometric_pack () {
+ EXPECTED_PACKS="$1" &&
+
+ # Verify that we perform a geometric repack.
+ rm -f "trace2.txt" &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git maintenance run --task=geometric-repack 2>/dev/null &&
+ test_subcommand git repack -d -l --geometric=2 \
+ --quiet --write-midx <trace2.txt &&
+
+ # Verify that the number of packfiles matches our expectation.
+ ls -l .git/objects/pack/*.pack >packfiles &&
+ test_line_count = "$EXPECTED_PACKS" packfiles &&
+
+ # And verify that there are no loose objects anymore.
+ git count-objects -v >count &&
+ test_grep '^count: 0$' count
+}
+
+test_expect_success 'geometric repacking task' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git config set maintenance.auto false &&
+ test_commit initial &&
+
+ # The initial repack causes an all-into-one repack.
+ GIT_TRACE2_EVENT="$(pwd)/initial-repack.txt" \
+ git maintenance run --task=geometric-repack 2>/dev/null &&
+ test_subcommand git repack -d -l --cruft --cruft-expiration=2.weeks.ago \
+ --quiet --write-midx <initial-repack.txt &&
+
+ # Repacking should now cause a no-op geometric repack because
+ # no packfiles need to be combined.
+ ls -l .git/objects/pack >before &&
+ run_and_verify_geometric_pack 1 &&
+ ls -l .git/objects/pack >after &&
+ test_cmp before after &&
+
+ # This incremental change creates a new packfile that only
+ # soaks up loose objects. The packfiles are not getting merged
+ # at this point.
+ test_commit loose &&
+ run_and_verify_geometric_pack 2 &&
+
+ # Both packfiles have 3 objects, so the next run would cause us
+ # to merge all packfiles together. This should be turned into
+ # an all-into-one-repack.
+ GIT_TRACE2_EVENT="$(pwd)/all-into-one-repack.txt" \
+ git maintenance run --task=geometric-repack 2>/dev/null &&
+ test_subcommand git repack -d -l --cruft --cruft-expiration=2.weeks.ago \
+ --quiet --write-midx <all-into-one-repack.txt &&
+
+ # The geometric repack soaks up unreachable objects.
+ echo blob-1 | git hash-object -w --stdin -t blob &&
+ run_and_verify_geometric_pack 2 &&
+
+ # A second unreachable object should be written into another packfile.
+ echo blob-2 | git hash-object -w --stdin -t blob &&
+ run_and_verify_geometric_pack 3 &&
+
+ # And these two small packs should now be merged via the
+ # geometric repack. The large packfile should remain intact.
+ run_and_verify_geometric_pack 2 &&
+
+ # If we now add two more objects and repack twice we should
+ # then see another all-into-one repack. This time around
+ # though, as we have unreachable objects, we should also see a
+ # cruft pack.
+ echo blob-3 | git hash-object -w --stdin -t blob &&
+ echo blob-4 | git hash-object -w --stdin -t blob &&
+ run_and_verify_geometric_pack 3 &&
+ GIT_TRACE2_EVENT="$(pwd)/cruft-repack.txt" \
+ git maintenance run --task=geometric-repack 2>/dev/null &&
+ test_subcommand git repack -d -l --cruft --cruft-expiration=2.weeks.ago \
+ --quiet --write-midx <cruft-repack.txt &&
+ ls .git/objects/pack/*.pack >packs &&
+ test_line_count = 2 packs &&
+ ls .git/objects/pack/*.mtimes >cruft &&
+ test_line_count = 1 cruft
+ )
+'
+
+test_geometric_repack_needed () {
+ NEEDED="$1"
+ GEOMETRIC_CONFIG="$2" &&
+ rm -f trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git ${GEOMETRIC_CONFIG:+-c maintenance.geometric-repack.$GEOMETRIC_CONFIG} \
+ maintenance run --auto --task=geometric-repack 2>/dev/null &&
+ case "$NEEDED" in
+ true)
+ test_grep "\[\"git\",\"repack\"," trace2.txt;;
+ false)
+ ! test_grep "\[\"git\",\"repack\"," trace2.txt;;
+ *)
+ BUG "invalid parameter: $NEEDED";;
+ esac
+}
+
+test_expect_success 'geometric repacking with --auto' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ # An empty repository does not need repacking, except when
+ # explicitly told to do it.
+ test_geometric_repack_needed false &&
+ test_geometric_repack_needed false auto=0 &&
+ test_geometric_repack_needed false auto=1 &&
+ test_geometric_repack_needed true auto=-1 &&
+
+ test_oid_init &&
+
+ # Loose objects cause a repack when crossing the limit. Note
+ # that the number of objects gets extrapolated by having a look
+ # at the "objects/17/" shard.
+ test_commit "$(test_oid blob17_1)" &&
+ test_geometric_repack_needed false &&
+ test_commit "$(test_oid blob17_2)" &&
+ test_geometric_repack_needed false auto=257 &&
+ test_geometric_repack_needed true auto=256 &&
+
+ # Force another repack.
+ test_commit first &&
+ test_commit second &&
+ test_geometric_repack_needed true auto=-1 &&
+
+ # We now have two packfiles that would be merged together. As
+ # such, the repack should always happen unless the user has
+ # disabled the auto task.
+ test_geometric_repack_needed false auto=0 &&
+ test_geometric_repack_needed true auto=9000
+ )
+'
+
test_expect_success 'pack-refs task' '
for n in $(test_seq 1 5)
do