Raw
1 #!/bin/sh
2
3 test_description='git repack works correctly'
4
5 . ./test-lib.sh
6
7 objdir=.git/objects
8 packdir=$objdir/pack
9
10 test_expect_success '--expire-to stores pruned objects (now)' '
11 git init expire-to-now &&
12 (
13 cd expire-to-now &&
14
15 git branch -M main &&
16
17 test_commit base &&
18
19 git checkout -b cruft &&
20 test_commit --no-tag cruft &&
21
22 git rev-list --objects --no-object-names main..cruft >moved.raw &&
23 sort moved.raw >moved.want &&
24
25 git rev-list --all --objects --no-object-names >expect.raw &&
26 sort expect.raw >expect &&
27
28 git checkout main &&
29 git branch -D cruft &&
30 git reflog expire --all --expire=all &&
31
32 git init --bare expired.git &&
33 git repack -d \
34 --cruft --cruft-expiration="now" \
35 --expire-to="expired.git/objects/pack/pack" &&
36
37 expired="$(ls expired.git/objects/pack/pack-*.idx)" &&
38 test_path_is_file "${expired%.idx}.mtimes" &&
39
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 \
46 >remaining.objects &&
47
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 &&
53
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
58 )
59 '
60
61 test_expect_success '--expire-to stores pruned objects (5.minutes.ago)' '
62 git init expire-to-5.minutes.ago &&
63 (
64 cd expire-to-5.minutes.ago &&
65
66 git branch -M main &&
67
68 test_commit base &&
69
70 # Create two classes of unreachable objects, one which
71 # is older than 5 minutes (stale), and another which is
72 # newer (recent).
73 for kind in stale recent
74 do
75 git checkout -b $kind main &&
76 test_commit --no-tag $kind || return 1
77 done &&
78
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)" &&
82
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 &&
87
88 # moved.want holds the set of objects we expect to find
89 # in expired.git
90 git rev-list --objects --no-object-names main..stale >out &&
91 sort out >moved.want &&
92
93 git checkout main &&
94 git branch -D stale recent &&
95 git reflog expire --all --expire=all &&
96 git prune-packed &&
97
98 git init --bare expired.git &&
99 git repack -d \
100 --cruft --cruft-expiration=5.minutes.ago \
101 --expire-to="expired.git/objects/pack/pack" &&
102
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 &&
110
111 (
112 cd expired.git &&
113
114 expired="$(ls objects/pack/pack-*.mtimes)" &&
115 test-tool pack-mtimes $(basename $expired) >out &&
116 cut -d" " -f1 out | sort >../moved.got &&
117
118 # Ensure that there are as many objects with the
119 # expected mtime as were moved to expired.git.
120 #
121 # In other words, ensure that the recorded
122 # mtimes of any moved objects was written
123 # correctly.
124 grep " $mtime$" out >matching &&
125 test_line_count = $(wc -l <../moved.want) matching
126 ) &&
127 test_cmp moved.want moved.got
128 )
129 '
130
131 generate_random_blob() {
132 test-tool genrandom "$@" >blob &&
133 git hash-object -w -t blob blob &&
134 rm blob
135 }
136
137 pack_random_blob () {
138 generate_random_blob "$@" &&
139 git repack -d -q >/dev/null
140 }
141
142 generate_cruft_pack () {
143 pack_random_blob "$@" >/dev/null &&
144
145 ls $packdir/pack-*.pack | xargs -n 1 basename >in &&
146 pack="$(git pack-objects --cruft $packdir/pack <in)" &&
147 git prune-packed &&
148
149 echo "$packdir/pack-$pack.mtimes"
150 }
151
152 test_expect_success '--max-cruft-size creates new packs when too large' '
153 git init max-cruft-size-large &&
154 (
155 cd max-cruft-size-large &&
156 test_commit base &&
157
158 foo="$(pack_random_blob foo $((1*1024*1024)))" &&
159 git repack --cruft -d &&
160 cruft_foo="$(ls $packdir/pack-*.mtimes)" &&
161
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)" &&
165
166 test-tool pack-mtimes $(basename "$cruft_foo") >foo.objects &&
167 test-tool pack-mtimes $(basename "$cruft_bar") >bar.objects &&
168
169 grep "^$foo" foo.objects &&
170 test_line_count = 1 foo.objects &&
171 grep "^$bar" bar.objects &&
172 test_line_count = 1 bar.objects
173 )
174 '
175
176 test_expect_success '--max-cruft-size combines existing packs when not too large' '
177 git init max-cruft-size-small &&
178 (
179 cd max-cruft-size-small &&
180 test_commit base &&
181
182 foo="$(pack_random_blob foo $((1*1024*1024)))" &&
183 git repack --cruft -d &&
184
185 bar="$(pack_random_blob bar $((1*1024*1024)))" &&
186 git repack --cruft -d --max-cruft-size=10M &&
187
188 cruft=$(ls $packdir/pack-*.mtimes) &&
189 test-tool pack-mtimes $(basename "$cruft") >cruft.objects &&
190
191 grep "^$foo" cruft.objects &&
192 grep "^$bar" cruft.objects &&
193 test_line_count = 2 cruft.objects
194 )
195 '
196
197 test_expect_success '--combine-cruft-below-size combines packs' '
198 repo=combine-cruft-below-size &&
199 test_when_finished "rm -fr $repo" &&
200
201 git init "$repo" &&
202 (
203 cd "$repo" &&
204
205 test_commit base &&
206 git repack -ad &&
207
208 cruft_foo="$(generate_cruft_pack foo 524288)" && # 0.5 MiB
209 cruft_bar="$(generate_cruft_pack bar 524288)" && # 0.5 MiB
210 cruft_baz="$(generate_cruft_pack baz 1048576)" && # 1.0 MiB
211 cruft_quux="$(generate_cruft_pack quux 1572864)" && # 1.5 MiB
212
213 test-tool pack-mtimes "$(basename $cruft_foo)" >expect.raw &&
214 test-tool pack-mtimes "$(basename $cruft_bar)" >>expect.raw &&
215 sort expect.raw >expect.objects &&
216
217 # Repacking with `--combine-cruft-below-size=1M`
218 # should combine both 0.5 MiB packs together, but
219 # ignore the two packs which are >= 1.0 MiB.
220 ls $packdir/pack-*.mtimes | sort >cruft.before &&
221 git repack -d --cruft --combine-cruft-below-size=1M &&
222 ls $packdir/pack-*.mtimes | sort >cruft.after &&
223
224 comm -13 cruft.before cruft.after >cruft.new &&
225 comm -23 cruft.before cruft.after >cruft.removed &&
226
227 test_line_count = 1 cruft.new &&
228 test_line_count = 2 cruft.removed &&
229
230 # The two packs smaller than 1.0MiB should be repacked
231 # together.
232 printf "%s\n" $cruft_foo $cruft_bar | sort >expect.removed &&
233 test_cmp expect.removed cruft.removed &&
234
235 # ...and contain the set of objects rolled up.
236 test-tool pack-mtimes "$(basename $(cat cruft.new))" >actual.raw &&
237 sort actual.raw >actual.objects &&
238
239 test_cmp expect.objects actual.objects
240 )
241 '
242
243 test_expect_success 'setup cruft with freshened objects' '
244 git init cruft-freshen &&
245 (
246 cd cruft-freshen &&
247
248 test_commit base &&
249 git repack -ad &&
250
251 foo="$(generate_random_blob foo 64)" &&
252 test-tool chmtime --get -10000 \
253 "$objdir/$(test_oid_to_path "$foo")" >foo.mtime &&
254
255 git repack --cruft -d &&
256
257 cruft="$(ls $packdir/pack-*.mtimes)" &&
258 test-tool pack-mtimes "$(basename $cruft)" >actual &&
259 echo "$foo $(cat foo.mtime)" >expect &&
260 test_cmp expect actual
261 )
262 '
263
264 test_expect_success 'cruft with freshened objects (loose)' '
265 (
266 cd cruft-freshen &&
267
268 # regenerate the object, setting its mtime to be more recent
269 foo="$(generate_random_blob foo 64)" &&
270 test-tool chmtime --get -100 \
271 "$objdir/$(test_oid_to_path "$foo")" >foo.mtime &&
272
273 git repack --cruft -d &&
274
275 cruft="$(ls $packdir/pack-*.mtimes)" &&
276 test-tool pack-mtimes "$(basename $cruft)" >actual &&
277 echo "$foo $(cat foo.mtime)" >expect &&
278 test_cmp expect actual
279 )
280 '
281
282 test_expect_success 'cruft with freshened objects (packed)' '
283 (
284 cd cruft-freshen &&
285
286 # regenerate the object and store it in a packfile,
287 # setting its mtime to be more recent
288 #
289 # store it alongside another cruft object so that we
290 # do not create an identical copy of the existing
291 # cruft pack (which contains $foo).
292 foo="$(generate_random_blob foo 64)" &&
293 bar="$(generate_random_blob bar 64)" &&
294 foo_pack="$(printf "%s\n" $foo $bar | git pack-objects $packdir/pack)" &&
295 git prune-packed &&
296
297 test-tool chmtime --get -10 \
298 "$packdir/pack-$foo_pack.pack" >foo.mtime &&
299
300 git repack --cruft -d &&
301
302 cruft="$(ls $packdir/pack-*.mtimes)" &&
303 test-tool pack-mtimes "$(basename $cruft)" >actual &&
304 echo "$foo $(cat foo.mtime)" >expect.raw &&
305 echo "$bar $(cat foo.mtime)" >>expect.raw &&
306 sort expect.raw >expect &&
307 test_cmp expect actual
308 )
309 '
310
311 test_expect_success 'multi-cruft with freshened objects (previously cruft)' '
312 repo="max-cruft-size-threshold" &&
313
314 test_when_finished "rm -fr $repo" &&
315 git init "$repo" &&
316 (
317 cd "$repo" &&
318
319 test_commit base &&
320 foo="$(generate_random_blob foo $((2*1024*1024)))" &&
321 bar="$(generate_random_blob bar $((2*1024*1024)))" &&
322 baz="$(generate_random_blob baz $((2*1024*1024)))" &&
323
324 test-tool chmtime --get -100000 \
325 "$objdir/$(test_oid_to_path "$foo")" >foo.old &&
326 test-tool chmtime --get -100000 \
327 "$objdir/$(test_oid_to_path "$bar")" >bar.old &&
328 test-tool chmtime --get -100000 \
329 "$objdir/$(test_oid_to_path "$baz")" >baz.old &&
330
331 git repack --cruft -d &&
332
333 # Make an identical copy of foo stored in a pack with a more
334 # recent mtime.
335 foo="$(generate_random_blob foo $((2*1024*1024)))" &&
336 foo_pack="$(echo "$foo" | git pack-objects $packdir/pack)" &&
337 test-tool chmtime --get -100 \
338 "$packdir/pack-$foo_pack.pack" >foo.new &&
339 git prune-packed &&
340
341 # Make a loose copy of bar, also with a more recent mtime.
342 bar="$(generate_random_blob bar $((2*1024*1024)))" &&
343 test-tool chmtime --get -100 \
344 "$objdir/$(test_oid_to_path "$bar")" >bar.new &&
345
346 # Make a new cruft object $quux to ensure we do not
347 # generate an identical pack to the existing cruft
348 # pack.
349 quux="$(generate_random_blob quux $((1024)))" &&
350 test-tool chmtime --get -100 \
351 "$objdir/$(test_oid_to_path "$quux")" >quux.new &&
352
353 git repack --cruft --max-cruft-size=3M -d &&
354
355 for p in $packdir/pack-*.mtimes
356 do
357 test-tool pack-mtimes "$(basename "$p")" || return 1
358 done >actual.raw &&
359 sort actual.raw >actual &&
360
361 # Among the set of all cruft packs, we should see the
362 # new mtimes for object $foo and $bar, as well as the
363 # single new copy of $baz.
364 sort >expect <<-EOF &&
365 $foo $(cat foo.new)
366 $bar $(cat bar.new)
367 $baz $(cat baz.old)
368 $quux $(cat quux.new)
369 EOF
370
371 test_cmp expect actual
372 )
373 '
374
375 test_expect_success '--max-cruft-size with pruning' '
376 git init max-cruft-size-prune &&
377 (
378 cd max-cruft-size-prune &&
379
380 test_commit base &&
381 foo="$(generate_random_blob foo $((1024*1024)))" &&
382 bar="$(generate_random_blob bar $((1024*1024)))" &&
383 baz="$(generate_random_blob baz $((1024*1024)))" &&
384
385 test-tool chmtime -10000 "$objdir/$(test_oid_to_path "$foo")" &&
386
387 git repack -d --cruft --max-cruft-size=1M &&
388
389 # backdate the mtimes of all cruft packs to validate
390 # that they were rewritten as a result of pruning
391 ls $packdir/pack-*.mtimes | sort >cruft.before &&
392 for cruft in $(cat cruft.before)
393 do
394 mtime="$(test-tool chmtime --get -10000 "$cruft")" &&
395 echo $cruft $mtime >>mtimes || return 1
396 done &&
397
398 # repack (and prune) with a --max-cruft-size to ensure
399 # that we appropriately split the resulting set of packs
400 git repack -d --cruft --max-cruft-size=1M \
401 --cruft-expiration=1000.seconds.ago &&
402 ls $packdir/pack-*.mtimes | sort >cruft.after &&
403
404 for cruft in $(cat cruft.after)
405 do
406 old_mtime="$(grep $cruft mtimes | cut -d" " -f2)" &&
407 new_mtime="$(test-tool chmtime --get $cruft)" &&
408 test $old_mtime -lt $new_mtime || return 1
409 done &&
410
411 test_line_count = 3 cruft.before &&
412 test_line_count = 2 cruft.after &&
413 test_must_fail git cat-file -e $foo &&
414 git cat-file -e $bar &&
415 git cat-file -e $baz
416 )
417 '
418
419 test_expect_success '--max-cruft-size ignores non-local packs' '
420 repo="max-cruft-size-non-local" &&
421 git init $repo &&
422 (
423 cd $repo &&
424 test_commit base &&
425 generate_random_blob foo 64 &&
426 git repack --cruft -d
427 ) &&
428
429 git clone --reference=$repo $repo $repo-alt &&
430 (
431 cd $repo-alt &&
432
433 test_commit other &&
434 generate_random_blob bar 64 &&
435
436 # ensure that we do not attempt to pick up packs from
437 # the non-alternated repository, which would result in a
438 # crash
439 git repack --cruft --max-cruft-size=1M -d
440 )
441 '
442
443 test_expect_success 'reachable packs are preferred over cruft ones' '
444 repo="cruft-preferred-packs" &&
445 git init "$repo" &&
446 (
447 cd "$repo" &&
448
449 # This test needs to exercise careful control over when a MIDX
450 # is and is not written. Unset the corresponding TEST variable
451 # accordingly.
452 sane_unset GIT_TEST_MULTI_PACK_INDEX &&
453
454 test_commit base &&
455 test_commit --no-tag cruft &&
456
457 non_cruft="$(echo base | git pack-objects --revs $packdir/pack)" &&
458 # Write a cruft pack which both (a) sorts ahead of the non-cruft
459 # pack in lexical order, and (b) has an older mtime to appease
460 # the MIDX preferred pack selection routine.
461 cruft="$(echo pack-$non_cruft.pack | git pack-objects --cruft $packdir/pack-A)" &&
462 test-tool chmtime -1000 $packdir/pack-A-$cruft.pack &&
463
464 test_commit other &&
465 git repack -d &&
466
467 git repack --geometric 2 -d --write-midx --write-bitmap-index &&
468
469 # After repacking, there are two packs left: one reachable one
470 # (which is the result of combining both of the existing two
471 # non-cruft packs), and one cruft pack.
472 find .git/objects/pack -type f -name "*.pack" >packs &&
473 test_line_count = 2 packs &&
474
475 # Make sure that the pack we just wrote is marked as preferred,
476 # not the cruft one.
477 pack="$(test-tool read-midx --preferred-pack $objdir)" &&
478 test_path_is_missing "$packdir/$(basename "$pack" ".idx").mtimes"
479 )
480 '
481
482 test_expect_success 'repack --cruft generates a cruft pack' '
483 git init repo &&
484 test_when_finished "rm -fr repo" &&
485 (
486 cd repo &&
487
488 test_commit reachable &&
489 git branch -M main &&
490 git checkout --orphan other &&
491 test_commit unreachable &&
492
493 git checkout main &&
494 git branch -D other &&
495 git tag -d unreachable &&
496 # objects are not cruft if they are contained in the reflogs
497 git reflog expire --all --expire=all &&
498
499 git rev-list --objects --all --no-object-names >reachable.raw &&
500 git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
501 sort <reachable.raw >reachable &&
502 comm -13 reachable objects >unreachable &&
503
504 git repack --cruft -d &&
505
506 cruft=$(basename $(ls $packdir/pack-*.mtimes) .mtimes) &&
507 pack=$(basename $(ls $packdir/pack-*.pack | grep -v $cruft) .pack) &&
508
509 git show-index <$packdir/$pack.idx >actual.raw &&
510 cut -f2 -d" " actual.raw | sort >actual &&
511 test_cmp reachable actual &&
512
513 git show-index <$packdir/$cruft.idx >actual.raw &&
514 cut -f2 -d" " actual.raw | sort >actual &&
515 test_cmp unreachable actual
516 )
517 '
518
519 test_expect_success 'cruft packs are not included in geometric repack' '
520 git init repo &&
521 test_when_finished "rm -fr repo" &&
522 (
523 cd repo &&
524
525 test_commit reachable &&
526 git repack -Ad &&
527 git branch -M main &&
528
529 git checkout --orphan other &&
530 test_commit cruft &&
531 git repack -d &&
532
533 git checkout main &&
534 git branch -D other &&
535 git tag -d cruft &&
536 git reflog expire --all --expire=all &&
537
538 git repack --cruft &&
539
540 find $packdir -type f | sort >before &&
541 git repack --geometric=2 -d &&
542 find $packdir -type f | sort >after &&
543
544 test_cmp before after
545 )
546 '
547
548 test_expect_success 'repack --geometric collects once-cruft objects' '
549 git init repo &&
550 test_when_finished "rm -fr repo" &&
551 (
552 cd repo &&
553
554 test_commit reachable &&
555 git repack -Ad &&
556 git branch -M main &&
557
558 git checkout --orphan other &&
559 git rm -rf . &&
560 test_commit --no-tag cruft &&
561 cruft="$(git rev-parse HEAD)" &&
562
563 git checkout main &&
564 git branch -D other &&
565 git reflog expire --all --expire=all &&
566
567 # Pack the objects created in the previous step into a cruft
568 # pack. Intentionally leave loose copies of those objects
569 # around so we can pick them up in a subsequent --geometric
570 # reapack.
571 git repack --cruft &&
572
573 # Now make those objects reachable, and ensure that they are
574 # packed into the new pack created via a --geometric repack.
575 git update-ref refs/heads/other $cruft &&
576
577 # Without this object, the set of unpacked objects is exactly
578 # the set of objects already in the cruft pack. Tweak that set
579 # to ensure we do not overwrite the cruft pack entirely.
580 test_commit reachable2 &&
581
582 find $packdir -name "pack-*.idx" | sort >before &&
583 git repack --geometric=2 -d &&
584 find $packdir -name "pack-*.idx" | sort >after &&
585
586 {
587 git rev-list --objects --no-object-names $cruft &&
588 git rev-list --objects --no-object-names reachable..reachable2
589 } >want.raw &&
590 sort want.raw >want &&
591
592 pack=$(comm -13 before after) &&
593 git show-index <$pack >objects.raw &&
594
595 cut -d" " -f2 objects.raw | sort >got &&
596
597 test_cmp want got
598 )
599 '
600
601 test_expect_success 'cruft repack with no reachable objects' '
602 git init repo &&
603 test_when_finished "rm -fr repo" &&
604 (
605 cd repo &&
606
607 test_commit base &&
608 git repack -ad &&
609
610 base="$(git rev-parse base)" &&
611
612 git for-each-ref --format="delete %(refname)" >in &&
613 git update-ref --stdin <in &&
614 git reflog expire --all --expire=all &&
615 rm -fr .git/index &&
616
617 git repack --cruft -d &&
618
619 git cat-file -t $base
620 )
621 '
622
623 find_pack () {
624 for idx in $(ls $packdir/pack-*.idx)
625 do
626 git show-index <$idx >out &&
627 if grep -q "$1" out
628 then
629 echo $idx
630 fi || return 1
631 done
632 }
633
634 test_expect_success 'cruft repack with --max-pack-size' '
635 git init max-pack-size &&
636 (
637 cd max-pack-size &&
638 test_commit base &&
639
640 # two cruft objects which exceed the maximum pack size
641 foo=$(generate_random_blob foo 1048576) &&
642 bar=$(generate_random_blob bar 1048576) &&
643 test-tool chmtime --get -1000 \
644 "$objdir/$(test_oid_to_path $foo)" >foo.mtime &&
645 test-tool chmtime --get -2000 \
646 "$objdir/$(test_oid_to_path $bar)" >bar.mtime &&
647 git repack --cruft --max-pack-size=1M &&
648 find $packdir -name "*.mtimes" >cruft &&
649 test_line_count = 2 cruft &&
650
651 foo_mtimes="$(basename $(find_pack $foo) .idx).mtimes" &&
652 bar_mtimes="$(basename $(find_pack $bar) .idx).mtimes" &&
653 test-tool pack-mtimes $foo_mtimes >foo.actual &&
654 test-tool pack-mtimes $bar_mtimes >bar.actual &&
655
656 echo "$foo $(cat foo.mtime)" >foo.expect &&
657 echo "$bar $(cat bar.mtime)" >bar.expect &&
658
659 test_cmp foo.expect foo.actual &&
660 test_cmp bar.expect bar.actual &&
661 test "$foo_mtimes" != "$bar_mtimes"
662 )
663 '
664
665 test_expect_success 'cruft repack with pack.packSizeLimit' '
666 (
667 cd max-pack-size &&
668 # repack everything back together to remove the existing cruft
669 # pack (but to keep its objects)
670 git repack -adk &&
671 git -c pack.packSizeLimit=1M repack --cruft &&
672 # ensure the same post condition is met when --max-pack-size
673 # would otherwise be inferred from the configuration
674 find $packdir -name "*.mtimes" >cruft &&
675 test_line_count = 2 cruft &&
676 for pack in $(cat cruft)
677 do
678 test-tool pack-mtimes "$(basename $pack)" >objects &&
679 test_line_count = 1 objects || return 1
680 done
681 )
682 '
683
684 test_expect_success 'cruft repack respects repack.cruftWindow' '
685 git init repo &&
686 test_when_finished "rm -fr repo" &&
687 (
688 cd repo &&
689
690 test_commit base &&
691
692 GIT_TRACE2_EVENT=$(pwd)/event.trace \
693 git -c pack.window=1 -c repack.cruftWindow=2 repack \
694 --cruft --window=3 &&
695
696 grep "pack-objects.*--window=2.*--cruft" event.trace
697 )
698 '
699
700 test_expect_success 'cruft repack respects --window by default' '
701 git init repo &&
702 test_when_finished "rm -fr repo" &&
703 (
704 cd repo &&
705
706 test_commit base &&
707
708 GIT_TRACE2_EVENT=$(pwd)/event.trace \
709 git -c pack.window=2 repack --cruft --window=3 &&
710
711 grep "pack-objects.*--window=3.*--cruft" event.trace
712 )
713 '
714
715 test_expect_success 'cruft repack respects --quiet' '
716 git init repo &&
717 test_when_finished "rm -fr repo" &&
718 (
719 cd repo &&
720
721 test_commit base &&
722 GIT_PROGRESS_DELAY=0 git repack --cruft --quiet 2>err &&
723 test_must_be_empty err
724 )
725 '
726
727 setup_cruft_exclude_tests() {
728 git init "$1" &&
729 (
730 cd "$1" &&
731
732 git config repack.midxMustContainCruft false &&
733
734 test_commit one &&
735
736 test_commit --no-tag two &&
737 two="$(git rev-parse HEAD)" &&
738 test_commit --no-tag three &&
739 three="$(git rev-parse HEAD)" &&
740 git reset --hard one &&
741 git reflog expire --all --expire=all &&
742
743 GIT_TEST_MULTI_PACK_INDEX=0 git repack --cruft -d &&
744
745 git merge $two &&
746 test_commit four
747 )
748 }
749
750 test_expect_success 'repack --write-midx excludes cruft where possible' '
751 setup_cruft_exclude_tests exclude-cruft-when-possible &&
752 (
753 cd exclude-cruft-when-possible &&
754
755 GIT_TEST_MULTI_PACK_INDEX=0 \
756 git repack -d --geometric=2 --write-midx --write-bitmap-index &&
757
758 test-tool read-midx --show-objects $objdir >midx &&
759 cruft="$(ls $packdir/*.mtimes)" &&
760 test_grep ! "$(basename "$cruft" .mtimes).idx" midx &&
761
762 git rev-list --all --objects --no-object-names >reachable.raw &&
763 sort reachable.raw >reachable.objects &&
764 awk "/\.pack$/ { print \$1 }" <midx | sort >midx.objects &&
765
766 test_cmp reachable.objects midx.objects
767 )
768 '
769
770 test_expect_success 'repack --write-midx includes cruft when instructed' '
771 setup_cruft_exclude_tests exclude-cruft-when-instructed &&
772 (
773 cd exclude-cruft-when-instructed &&
774
775 GIT_TEST_MULTI_PACK_INDEX=0 \
776 git -c repack.midxMustContainCruft=true repack \
777 -d --geometric=2 --write-midx --write-bitmap-index &&
778
779 test-tool read-midx --show-objects $objdir >midx &&
780 cruft="$(ls $packdir/*.mtimes)" &&
781 test_grep "$(basename "$cruft" .mtimes).idx" midx &&
782
783 git cat-file --batch-check="%(objectname)" --batch-all-objects \
784 >all.objects &&
785 awk "/\.pack$/ { print \$1 }" <midx | sort >midx.objects &&
786
787 test_cmp all.objects midx.objects
788 )
789 '
790
791 test_expect_success 'repack --write-midx includes cruft when necessary' '
792 setup_cruft_exclude_tests exclude-cruft-when-necessary &&
793 (
794 cd exclude-cruft-when-necessary &&
795
796 test_path_is_file $(ls $packdir/pack-*.mtimes) &&
797 ( cd $packdir && ls pack-*.idx ) | sort >packs.all &&
798 git multi-pack-index write --stdin-packs --bitmap <packs.all &&
799
800 test_commit five &&
801 GIT_TEST_MULTI_PACK_INDEX=0 \
802 git repack -d --geometric=2 --write-midx --write-bitmap-index &&
803
804 test-tool read-midx --show-objects $objdir >midx &&
805 awk "/\.pack$/ { print \$1 }" <midx | sort >midx.objects &&
806 git cat-file --batch-all-objects --batch-check="%(objectname)" \
807 >expect.objects &&
808 test_cmp expect.objects midx.objects &&
809
810 grep "^pack-" midx >midx.packs &&
811 test_line_count = "$(($(wc -l <packs.all) + 1))" midx.packs
812 )
813 '
814
815 test_expect_success 'repack --write-midx includes cruft when already geometric' '
816 git init repack--write-midx-geometric-noop &&
817 (
818 cd repack--write-midx-geometric-noop &&
819
820 git branch -M main &&
821 test_commit A &&
822 test_commit B &&
823
824 git checkout -B side &&
825 test_commit --no-tag C &&
826 C="$(git rev-parse HEAD)" &&
827
828 git checkout main &&
829 git branch -D side &&
830 git reflog expire --all --expire=all &&
831
832 # At this point we have two packs: one containing the
833 # objects belonging to commits A and B, and another
834 # (cruft) pack containing the objects belonging to
835 # commit C.
836 git repack --cruft -d &&
837
838 # Create a third pack which contains a merge commit
839 # making commit C reachable again.
840 #
841 # --no-ff is important here, as it ensures that we
842 # actually write a new object and subsequently a new
843 # pack to contain it.
844 git merge --no-ff $C &&
845 git repack -d &&
846
847 ls $packdir/pack-*.idx | sort >packs.all &&
848 cruft="$(ls $packdir/pack-*.mtimes)" &&
849 cruft="${cruft%.mtimes}.idx" &&
850
851 for idx in $(grep -v $cruft <packs.all)
852 do
853 git show-index <$idx >out &&
854 wc -l <out || return 1
855 done >sizes.raw &&
856
857 # Make sure that there are two non-cruft packs, and
858 # that one of them contains at least twice as many
859 # objects as the other, ensuring that they are already
860 # in a geometric progression.
861 sort -n sizes.raw >sizes &&
862 test_line_count = 2 sizes &&
863 s1=$(head -n 1 sizes) &&
864 s2=$(tail -n 1 sizes) &&
865 test "$s2" -gt "$((2 * $s1))" &&
866
867 git -c repack.midxMustContainCruft=false repack --geometric=2 \
868 --write-midx --write-bitmap-index
869 )
870 '
871
872 test_expect_success 'repack rescues once-cruft objects above geometric split' '
873 git config repack.midxMustContainCruft false &&
874
875 test_commit reachable &&
876 test_commit unreachable &&
877
878 unreachable="$(git rev-parse HEAD)" &&
879
880 git reset --hard HEAD^ &&
881 git tag -d unreachable &&
882 git reflog expire --all --expire=all &&
883
884 git repack --cruft -d &&
885
886 echo $unreachable | git pack-objects .git/objects/pack/pack &&
887
888 test_commit new &&
889
890 git update-ref refs/heads/other $unreachable &&
891 git repack --geometric=2 -d --write-midx --write-bitmap-index
892 '
893
894 test_done