3 test_description
='git repack works correctly'
10 test_expect_success
'--expire-to stores pruned objects (now)' '
11 git init expire-to-now &&
19 git checkout -b cruft &&
20 test_commit --no-tag cruft &&
22 git rev-list --objects --no-object-names main..cruft >moved.raw &&
23 sort moved.raw >moved.want &&
25 git rev-list --all --objects --no-object-names >expect.raw &&
26 sort expect.raw >expect &&
29 git branch -D cruft &&
30 git reflog expire --all --expire=all &&
32 git init --bare expired.git &&
34 --cruft --cruft-expiration="now" \
35 --expire-to="expired.git/objects/pack/pack" &&
37 expired="$(ls expired.git/objects/pack/pack-*.idx)" &&
38 test_path_is_file "${expired%.idx}.mtimes" &&
40 # Since the `--cruft-expiration` is "now", the effective
41 # behavior is to move _all_ unreachable objects out to
42 # the location in `--expire-to`.
43 git show-index <$expired >expired.raw &&
44 cut -d" " -f2 expired.raw | sort >expired.objects &&
45 git rev-list --all --objects --no-object-names \
48 # ...in other words, the combined contents of this
49 # repository and expired.git should be the same as the
50 # set of objects we started with.
51 sort expired.objects remaining.objects >actual &&
52 test_cmp expect actual &&
54 # The "moved" objects (i.e., those in expired.git)
55 # should be the same as the cruft objects which were
56 # expired in the previous step.
57 test_cmp moved.want expired.objects
61 test_expect_success
'--expire-to stores pruned objects (5.minutes.ago)' '
62 git init expire-to-5.minutes.ago &&
64 cd expire-to-5.minutes.ago &&
70 # Create two classes of unreachable objects, one which
71 # is older than 5 minutes (stale), and another which is
73 for kind in stale recent
75 git checkout -b $kind main &&
76 test_commit --no-tag $kind || return 1
79 git rev-list --objects --no-object-names main..stale >in &&
80 stale="$(git pack-objects $objdir/pack/pack <in)" &&
81 mtime="$(test-tool chmtime --get =-600 $objdir/pack/pack-$stale.pack)" &&
83 # expect holds the set of objects we expect to find in
84 # this repository after repacking
85 git rev-list --objects --no-object-names recent >expect.raw &&
86 sort expect.raw >expect &&
88 # moved.want holds the set of objects we expect to find
90 git rev-list --objects --no-object-names main..stale >out &&
91 sort out >moved.want &&
94 git branch -D stale recent &&
95 git reflog expire --all --expire=all &&
98 git init --bare expired.git &&
100 --cruft --cruft-expiration=5.minutes.ago \
101 --expire-to="expired.git/objects/pack/pack" &&
103 # Some of the remaining objects in this repository are
104 # unreachable, so use `cat-file --batch-all-objects`
105 # instead of `rev-list` to get their names
106 git cat-file --batch-all-objects --batch-check="%(objectname)" \
107 >remaining.objects &&
108 sort remaining.objects >actual &&
109 test_cmp expect actual &&
114 expired="$(ls objects/pack/pack-*.mtimes)" &&
115 test-tool pack-mtimes $(basename $expired) >out &&
116 cut -d" " -f1 out | sort >../moved.got &&
118 # Ensure that there are as many objects with the
119 # expected mtime as were moved to expired.git.
121 # In other words, ensure that the recorded
122 # mtimes of any moved objects was written
124 grep " $mtime$" out >matching &&
125 test_line_count = $(wc -l <../moved.want) matching
127 test_cmp moved.want moved.got
131 generate_random_blob
() {
132 test-tool genrandom
"$@" >blob
&&
133 git hash-object
-w -t blob blob
&&
137 pack_random_blob
() {
138 generate_random_blob
"$@" &&
139 git repack
-d -q >/dev
/null
142 generate_cruft_pack
() {
143 pack_random_blob
"$@" >/dev
/null
&&
145 ls $packdir/pack-
*.pack |
xargs -n 1 basename >in &&
146 pack
="$(git pack-objects --cruft $packdir/pack <in)" &&
149 echo "$packdir/pack-$pack.mtimes"
152 test_expect_success
'--max-cruft-size creates new packs when above threshold' '
153 git init max-cruft-size-large &&
155 cd max-cruft-size-large &&
158 foo="$(pack_random_blob foo $((1*1024*1024)))" &&
159 git repack --cruft -d &&
160 cruft_foo="$(ls $packdir/pack-*.mtimes)" &&
162 bar="$(pack_random_blob bar $((1*1024*1024)))" &&
163 git repack --cruft -d --max-cruft-size=1M &&
164 cruft_bar="$(ls $packdir/pack-*.mtimes | grep -v $cruft_foo)" &&
166 test-tool pack-mtimes $(basename "$cruft_foo") >foo.objects &&
167 test-tool pack-mtimes $(basename "$cruft_bar") >bar.objects &&
169 grep "^$foo" foo.objects &&
170 test_line_count = 1 foo.objects &&
171 grep "^$bar" bar.objects &&
172 test_line_count = 1 bar.objects
176 test_expect_success
'--max-cruft-size combines existing packs when below threshold' '
177 git init max-cruft-size-small &&
179 cd max-cruft-size-small &&
182 foo="$(pack_random_blob foo $((1*1024*1024)))" &&
183 git repack --cruft -d &&
185 bar="$(pack_random_blob bar $((1*1024*1024)))" &&
186 git repack --cruft -d --max-cruft-size=10M &&
188 cruft=$(ls $packdir/pack-*.mtimes) &&
189 test-tool pack-mtimes $(basename "$cruft") >cruft.objects &&
191 grep "^$foo" cruft.objects &&
192 grep "^$bar" cruft.objects &&
193 test_line_count = 2 cruft.objects
197 test_expect_success
'--max-cruft-size combines smaller packs first' '
198 git init max-cruft-size-consume-small &&
200 cd max-cruft-size-consume-small &&
205 cruft_foo="$(generate_cruft_pack foo 524288)" && # 0.5 MiB
206 cruft_bar="$(generate_cruft_pack bar 524288)" && # 0.5 MiB
207 cruft_baz="$(generate_cruft_pack baz 1048576)" && # 1.0 MiB
208 cruft_quux="$(generate_cruft_pack quux 1572864)" && # 1.5 MiB
210 test-tool pack-mtimes "$(basename $cruft_foo)" >expect.raw &&
211 test-tool pack-mtimes "$(basename $cruft_bar)" >>expect.raw &&
212 sort expect.raw >expect.objects &&
214 # repacking with `--max-cruft-size=2M` should combine
215 # both 0.5 MiB packs together, instead of, say, one of
216 # the 0.5 MiB packs with the 1.0 MiB pack
217 ls $packdir/pack-*.mtimes | sort >cruft.before &&
218 git repack -d --cruft --max-cruft-size=2M &&
219 ls $packdir/pack-*.mtimes | sort >cruft.after &&
221 comm -13 cruft.before cruft.after >cruft.new &&
222 comm -23 cruft.before cruft.after >cruft.removed &&
224 test_line_count = 1 cruft.new &&
225 test_line_count = 2 cruft.removed &&
227 # the two smaller packs should be rolled up first
228 printf "%s\n" $cruft_foo $cruft_bar | sort >expect.removed &&
229 test_cmp expect.removed cruft.removed &&
231 # ...and contain the set of objects rolled up
232 test-tool pack-mtimes "$(basename $(cat cruft.new))" >actual.raw &&
233 sort actual.raw >actual.objects &&
235 test_cmp expect.objects actual.objects
239 test_expect_success
'setup --max-cruft-size with freshened objects' '
240 git init max-cruft-size-freshen &&
242 cd max-cruft-size-freshen &&
247 foo="$(generate_random_blob foo 64)" &&
248 test-tool chmtime --get -10000 \
249 "$objdir/$(test_oid_to_path "$foo")" >foo.mtime &&
251 git repack --cruft -d &&
253 cruft="$(ls $packdir/pack-*.mtimes)" &&
254 test-tool pack-mtimes "$(basename $cruft)" >actual &&
255 echo "$foo $(cat foo.mtime)" >expect &&
256 test_cmp expect actual
260 test_expect_success
'--max-cruft-size with freshened objects (loose)' '
262 cd max-cruft-size-freshen &&
264 # regenerate the object, setting its mtime to be more recent
265 foo="$(generate_random_blob foo 64)" &&
266 test-tool chmtime --get -100 \
267 "$objdir/$(test_oid_to_path "$foo")" >foo.mtime &&
269 git repack --cruft -d &&
271 cruft="$(ls $packdir/pack-*.mtimes)" &&
272 test-tool pack-mtimes "$(basename $cruft)" >actual &&
273 echo "$foo $(cat foo.mtime)" >expect &&
274 test_cmp expect actual
278 test_expect_success
'--max-cruft-size with freshened objects (packed)' '
280 cd max-cruft-size-freshen &&
282 # regenerate the object and store it in a packfile,
283 # setting its mtime to be more recent
285 # store it alongside another cruft object so that we
286 # do not create an identical copy of the existing
287 # cruft pack (which contains $foo).
288 foo="$(generate_random_blob foo 64)" &&
289 bar="$(generate_random_blob bar 64)" &&
290 foo_pack="$(printf "%s\n" $foo $bar | git pack-objects $packdir/pack)" &&
293 test-tool chmtime --get -10 \
294 "$packdir/pack-$foo_pack.pack" >foo.mtime &&
296 git repack --cruft -d &&
298 cruft="$(ls $packdir/pack-*.mtimes)" &&
299 test-tool pack-mtimes "$(basename $cruft)" >actual &&
300 echo "$foo $(cat foo.mtime)" >expect.raw &&
301 echo "$bar $(cat foo.mtime)" >>expect.raw &&
302 sort expect.raw >expect &&
303 test_cmp expect actual
307 test_expect_success
'--max-cruft-size with pruning' '
308 git init max-cruft-size-prune &&
310 cd max-cruft-size-prune &&
313 foo="$(generate_random_blob foo $((1024*1024)))" &&
314 bar="$(generate_random_blob bar $((1024*1024)))" &&
315 baz="$(generate_random_blob baz $((1024*1024)))" &&
317 test-tool chmtime -10000 "$objdir/$(test_oid_to_path "$foo")" &&
319 git repack -d --cruft --max-cruft-size=1M &&
321 # backdate the mtimes of all cruft packs to validate
322 # that they were rewritten as a result of pruning
323 ls $packdir/pack-*.mtimes | sort >cruft.before &&
324 for cruft in $(cat cruft.before)
326 mtime="$(test-tool chmtime --get -10000 "$cruft")" &&
327 echo $cruft $mtime >>mtimes || return 1
330 # repack (and prune) with a --max-cruft-size to ensure
331 # that we appropriately split the resulting set of packs
332 git repack -d --cruft --max-cruft-size=1M \
333 --cruft-expiration=10.seconds.ago &&
334 ls $packdir/pack-*.mtimes | sort >cruft.after &&
336 for cruft in $(cat cruft.after)
338 old_mtime="$(grep $cruft mtimes | cut -d" " -f2)" &&
339 new_mtime="$(test-tool chmtime --get $cruft)" &&
340 test $old_mtime -lt $new_mtime || return 1
343 test_line_count = 3 cruft.before &&
344 test_line_count = 2 cruft.after &&
345 test_must_fail git cat-file -e $foo &&
346 git cat-file -e $bar &&
351 test_expect_success
'--max-cruft-size ignores non-local packs' '
352 repo="max-cruft-size-non-local" &&
357 generate_random_blob foo 64 &&
358 git repack --cruft -d
361 git clone --reference=$repo $repo $repo-alt &&
366 generate_random_blob bar 64 &&
368 # ensure that we do not attempt to pick up packs from
369 # the non-alternated repository, which would result in a
371 git repack --cruft --max-cruft-size=1M -d
375 test_expect_success
'reachable packs are preferred over cruft ones' '
376 repo="cruft-preferred-packs" &&
381 # This test needs to exercise careful control over when a MIDX
382 # is and is not written. Unset the corresponding TEST variable
384 sane_unset GIT_TEST_MULTI_PACK_INDEX &&
387 test_commit --no-tag cruft &&
389 non_cruft="$(echo base | git pack-objects --revs $packdir/pack)" &&
390 # Write a cruft pack which both (a) sorts ahead of the non-cruft
391 # pack in lexical order, and (b) has an older mtime to appease
392 # the MIDX preferred pack selection routine.
393 cruft="$(echo pack-$non_cruft.pack | git pack-objects --cruft $packdir/pack-A)" &&
394 test-tool chmtime -1000 $packdir/pack-A-$cruft.pack &&
399 git repack --geometric 2 -d --write-midx --write-bitmap-index &&
401 # After repacking, there are two packs left: one reachable one
402 # (which is the result of combining both of the existing two
403 # non-cruft packs), and one cruft pack.
404 find .git/objects/pack -type f -name "*.pack" >packs &&
405 test_line_count = 2 packs &&
407 # Make sure that the pack we just wrote is marked as preferred,
409 pack="$(test-tool read-midx --preferred-pack $objdir)" &&
410 test_path_is_missing "$packdir/$(basename "$pack" ".idx").mtimes"