]> git.ipfire.org Git - thirdparty/git.git/blame - t/t5329-pack-objects-cruft.sh
The third batch
[thirdparty/git.git] / t / t5329-pack-objects-cruft.sh
CommitLineData
b7573536
TB
1#!/bin/sh
2
3test_description='cruft pack related pack-objects tests'
4. ./test-lib.sh
5
6objdir=.git/objects
7packdir=$objdir/pack
8
9basic_cruft_pack_tests () {
10 expire="$1"
11
12 test_expect_success "unreachable loose objects are packed (expire $expire)" '
13 git init repo &&
14 test_when_finished "rm -fr repo" &&
15 (
16 cd repo &&
17
18 test_commit base &&
19 git repack -Ad &&
20 test_commit loose &&
21
22 test-tool chmtime +2000 "$objdir/$(test_oid_to_path \
23 $(git rev-parse loose:loose.t))" &&
24 test-tool chmtime +1000 "$objdir/$(test_oid_to_path \
25 $(git rev-parse loose^{tree}))" &&
26
27 (
28 git rev-list --objects --no-object-names base..loose |
29 while read oid
30 do
31 path="$objdir/$(test_oid_to_path "$oid")" &&
9905e80b
JH
32 printf "%s %d\n" "$oid" "$(test-tool chmtime --get "$path")" ||
33 echo "object list generation failed for $oid"
b7573536
TB
34 done |
35 sort -k1
36 ) >expect &&
37
38 keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
39 cruft="$(echo $keep | git pack-objects --cruft \
40 --cruft-expiration="$expire" $packdir/pack)" &&
41 test-tool pack-mtimes "pack-$cruft.mtimes" >actual &&
42
43 test_cmp expect actual
44 )
45 '
46
47 test_expect_success "unreachable packed objects are packed (expire $expire)" '
48 git init repo &&
49 test_when_finished "rm -fr repo" &&
50 (
51 cd repo &&
52
53 test_commit packed &&
54 git repack -Ad &&
55 test_commit other &&
56
57 git rev-list --objects --no-object-names packed.. >objects &&
58 keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
59 other="$(git pack-objects --delta-base-offset \
60 $packdir/pack <objects)" &&
61 git prune-packed &&
62
63 test-tool chmtime --get -100 "$packdir/pack-$other.pack" >expect &&
64
65 cruft="$(git pack-objects --cruft --cruft-expiration="$expire" $packdir/pack <<-EOF
66 $keep
67 -pack-$other.pack
68 EOF
69 )" &&
70 test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
71
72 cut -d" " -f2 <actual.raw | sort -u >actual &&
73
74 test_cmp expect actual
75 )
76 '
77
78 test_expect_success "unreachable cruft objects are repacked (expire $expire)" '
79 git init repo &&
80 test_when_finished "rm -fr repo" &&
81 (
82 cd repo &&
83
84 test_commit packed &&
85 git repack -Ad &&
86 test_commit other &&
87
88 git rev-list --objects --no-object-names packed.. >objects &&
89 keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
90
91 cruft_a="$(echo $keep | git pack-objects --cruft --cruft-expiration="$expire" $packdir/pack)" &&
92 git prune-packed &&
93 cruft_b="$(git pack-objects --cruft --cruft-expiration="$expire" $packdir/pack <<-EOF
94 $keep
95 -pack-$cruft_a.pack
96 EOF
97 )" &&
98
99 test-tool pack-mtimes "pack-$cruft_a.mtimes" >expect.raw &&
100 test-tool pack-mtimes "pack-$cruft_b.mtimes" >actual.raw &&
101
102 sort <expect.raw >expect &&
103 sort <actual.raw >actual &&
104
105 test_cmp expect actual
106 )
107 '
108
109 test_expect_success "multiple cruft packs (expire $expire)" '
110 git init repo &&
111 test_when_finished "rm -fr repo" &&
112 (
113 cd repo &&
114
115 test_commit reachable &&
116 git repack -Ad &&
117 keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
118
119 test_commit cruft &&
120 loose="$objdir/$(test_oid_to_path $(git rev-parse cruft))" &&
121
122 # generate three copies of the cruft object in different
123 # cruft packs, each with a unique mtime:
124 # - one expired (1000 seconds ago)
125 # - two non-expired (one 1000 seconds in the future,
126 # one 1500 seconds in the future)
127 test-tool chmtime =-1000 "$loose" &&
128 git pack-objects --cruft $packdir/pack-A <<-EOF &&
129 $keep
130 EOF
131 test-tool chmtime =+1000 "$loose" &&
132 git pack-objects --cruft $packdir/pack-B <<-EOF &&
133 $keep
134 -$(basename $(ls $packdir/pack-A-*.pack))
135 EOF
136 test-tool chmtime =+1500 "$loose" &&
137 git pack-objects --cruft $packdir/pack-C <<-EOF &&
138 $keep
139 -$(basename $(ls $packdir/pack-A-*.pack))
140 -$(basename $(ls $packdir/pack-B-*.pack))
141 EOF
142
143 # ensure the resulting cruft pack takes the most recent
144 # mtime among all copies
145 cruft="$(git pack-objects --cruft \
146 --cruft-expiration="$expire" \
147 $packdir/pack <<-EOF
148 $keep
149 -$(basename $(ls $packdir/pack-A-*.pack))
150 -$(basename $(ls $packdir/pack-B-*.pack))
151 -$(basename $(ls $packdir/pack-C-*.pack))
152 EOF
153 )" &&
154
155 test-tool pack-mtimes "$(basename $(ls $packdir/pack-C-*.mtimes))" >expect.raw &&
156 test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
157
158 sort expect.raw >expect &&
159 sort actual.raw >actual &&
160 test_cmp expect actual
161 )
162 '
163
164 test_expect_success "cruft packs tolerate missing trees (expire $expire)" '
165 git init repo &&
166 test_when_finished "rm -fr repo" &&
167 (
168 cd repo &&
169
170 test_commit reachable &&
171 test_commit cruft &&
172
173 tree="$(git rev-parse cruft^{tree})" &&
174
175 git reset --hard reachable &&
176 git tag -d cruft &&
177 git reflog expire --all --expire=all &&
178
179 # remove the unreachable tree, but leave the commit
180 # which has it as its root tree intact
181 rm -fr "$objdir/$(test_oid_to_path "$tree")" &&
182
183 git repack -Ad &&
184 basename $(ls $packdir/pack-*.pack) >in &&
185 git pack-objects --cruft --cruft-expiration="$expire" \
186 $packdir/pack <in
187 )
188 '
189
190 test_expect_success "cruft packs tolerate missing blobs (expire $expire)" '
191 git init repo &&
192 test_when_finished "rm -fr repo" &&
193 (
194 cd repo &&
195
196 test_commit reachable &&
197 test_commit cruft &&
198
199 blob="$(git rev-parse cruft:cruft.t)" &&
200
201 git reset --hard reachable &&
202 git tag -d cruft &&
203 git reflog expire --all --expire=all &&
204
205 # remove the unreachable blob, but leave the commit (and
206 # the root tree of that commit) intact
207 rm -fr "$objdir/$(test_oid_to_path "$blob")" &&
208
209 git repack -Ad &&
210 basename $(ls $packdir/pack-*.pack) >in &&
211 git pack-objects --cruft --cruft-expiration="$expire" \
212 $packdir/pack <in
213 )
214 '
215}
216
217basic_cruft_pack_tests never
a7d49383
TB
218basic_cruft_pack_tests 2.weeks.ago
219
220test_expect_success 'cruft tags rescue tagged objects' '
221 git init repo &&
222 test_when_finished "rm -fr repo" &&
223 (
224 cd repo &&
225
226 test_commit packed &&
227 git repack -Ad &&
228
229 test_commit tagged &&
230 git tag -a annotated -m tag &&
231
232 git rev-list --objects --no-object-names packed.. >objects &&
233 while read oid
234 do
235 test-tool chmtime -1000 \
0e66bc1b 236 "$objdir/$(test_oid_to_path $oid)" || exit 1
a7d49383
TB
237 done <objects &&
238
239 test-tool chmtime -500 \
240 "$objdir/$(test_oid_to_path $(git rev-parse annotated))" &&
241
242 keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
243 cruft="$(echo $keep | git pack-objects --cruft \
244 --cruft-expiration=750.seconds.ago \
245 $packdir/pack)" &&
246 test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
247 cut -f1 -d" " <actual.raw | sort >actual &&
248
249 (
250 cat objects &&
251 git rev-parse annotated
252 ) >expect.raw &&
253 sort <expect.raw >expect &&
254
255 test_cmp expect actual &&
256 cat actual
257 )
258'
259
260test_expect_success 'cruft commits rescue parents, trees' '
261 git init repo &&
262 test_when_finished "rm -fr repo" &&
263 (
264 cd repo &&
265
266 test_commit packed &&
267 git repack -Ad &&
268
269 test_commit old &&
270 test_commit new &&
271
272 git rev-list --objects --no-object-names packed..new >objects &&
273 while read object
274 do
275 test-tool chmtime -1000 \
0e66bc1b 276 "$objdir/$(test_oid_to_path $object)" || exit 1
a7d49383
TB
277 done <objects &&
278 test-tool chmtime +500 "$objdir/$(test_oid_to_path \
279 $(git rev-parse HEAD))" &&
280
281 keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
282 cruft="$(echo $keep | git pack-objects --cruft \
283 --cruft-expiration=750.seconds.ago \
284 $packdir/pack)" &&
285 test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
286
287 cut -d" " -f1 <actual.raw | sort >actual &&
288 sort <objects >expect &&
289
290 test_cmp expect actual
291 )
292'
293
294test_expect_success 'cruft trees rescue sub-trees, blobs' '
295 git init repo &&
296 test_when_finished "rm -fr repo" &&
297 (
298 cd repo &&
299
300 test_commit packed &&
301 git repack -Ad &&
302
303 mkdir -p dir/sub &&
304 echo foo >foo &&
305 echo bar >dir/bar &&
306 echo baz >dir/sub/baz &&
307
308 test_tick &&
309 git add . &&
310 git commit -m "pruned" &&
311
312 test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD))" &&
313 test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD^{tree}))" &&
314 test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:foo))" &&
315 test-tool chmtime -500 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:dir))" &&
316 test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:dir/bar))" &&
317 test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:dir/sub))" &&
318 test-tool chmtime -1000 "$objdir/$(test_oid_to_path $(git rev-parse HEAD:dir/sub/baz))" &&
319
320 keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
321 cruft="$(echo $keep | git pack-objects --cruft \
322 --cruft-expiration=750.seconds.ago \
323 $packdir/pack)" &&
324 test-tool pack-mtimes "pack-$cruft.mtimes" >actual.raw &&
325 cut -f1 -d" " <actual.raw | sort >actual &&
326
327 git rev-parse HEAD:dir HEAD:dir/bar HEAD:dir/sub HEAD:dir/sub/baz >expect.raw &&
328 sort <expect.raw >expect &&
329
330 test_cmp expect actual
331 )
332'
333
334test_expect_success 'expired objects are pruned' '
335 git init repo &&
336 test_when_finished "rm -fr repo" &&
337 (
338 cd repo &&
339
340 test_commit packed &&
341 git repack -Ad &&
342
343 test_commit pruned &&
344
345 git rev-list --objects --no-object-names packed..pruned >objects &&
346 while read object
347 do
348 test-tool chmtime -1000 \
0e66bc1b 349 "$objdir/$(test_oid_to_path $object)" || exit 1
a7d49383
TB
350 done <objects &&
351
352 keep="$(basename "$(ls $packdir/pack-*.pack)")" &&
353 cruft="$(echo $keep | git pack-objects --cruft \
354 --cruft-expiration=750.seconds.ago \
355 $packdir/pack)" &&
356
357 test-tool pack-mtimes "pack-$cruft.mtimes" >actual &&
358 test_must_be_empty actual
359 )
360'
b7573536 361
f9825d1c
TB
362test_expect_success 'repack --cruft generates a cruft pack' '
363 git init repo &&
364 test_when_finished "rm -fr repo" &&
365 (
366 cd repo &&
367
368 test_commit reachable &&
369 git branch -M main &&
370 git checkout --orphan other &&
371 test_commit unreachable &&
372
373 git checkout main &&
374 git branch -D other &&
375 git tag -d unreachable &&
376 # objects are not cruft if they are contained in the reflogs
377 git reflog expire --all --expire=all &&
378
379 git rev-list --objects --all --no-object-names >reachable.raw &&
380 git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
381 sort <reachable.raw >reachable &&
382 comm -13 reachable objects >unreachable &&
383
384 git repack --cruft -d &&
385
386 cruft=$(basename $(ls $packdir/pack-*.mtimes) .mtimes) &&
387 pack=$(basename $(ls $packdir/pack-*.pack | grep -v $cruft) .pack) &&
388
389 git show-index <$packdir/$pack.idx >actual.raw &&
390 cut -f2 -d" " actual.raw | sort >actual &&
391 test_cmp reachable actual &&
392
393 git show-index <$packdir/$cruft.idx >actual.raw &&
394 cut -f2 -d" " actual.raw | sort >actual &&
395 test_cmp unreachable actual
396 )
397'
398
399test_expect_success 'loose objects mtimes upsert others' '
400 git init repo &&
401 test_when_finished "rm -fr repo" &&
402 (
403 cd repo &&
404
405 test_commit reachable &&
406 git repack -Ad &&
407 git branch -M main &&
408
409 git checkout --orphan other &&
410 test_commit cruft &&
411 # incremental repack, leaving existing objects loose (so
412 # they can be "freshened")
413 git repack &&
414
415 tip="$(git rev-parse cruft)" &&
416 path="$objdir/$(test_oid_to_path "$tip")" &&
417 test-tool chmtime --get +1000 "$path" >expect &&
418
419 git checkout main &&
420 git branch -D other &&
421 git tag -d cruft &&
422 git reflog expire --all --expire=all &&
423
424 git repack --cruft -d &&
425
426 mtimes="$(basename $(ls $packdir/pack-*.mtimes))" &&
427 test-tool pack-mtimes "$mtimes" >actual.raw &&
428 grep "$tip" actual.raw | cut -d" " -f2 >actual &&
429 test_cmp expect actual
430 )
431'
432
5b92477f
TB
433test_expect_success 'expiring cruft objects with git gc' '
434 git init repo &&
435 test_when_finished "rm -fr repo" &&
436 (
437 cd repo &&
438
439 test_commit reachable &&
440 git branch -M main &&
441 git checkout --orphan other &&
442 test_commit unreachable &&
443
444 git checkout main &&
445 git branch -D other &&
446 git tag -d unreachable &&
447 # objects are not cruft if they are contained in the reflogs
448 git reflog expire --all --expire=all &&
449
450 git rev-list --objects --all --no-object-names >reachable.raw &&
451 git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
452 sort <reachable.raw >reachable &&
453 comm -13 reachable objects >unreachable &&
454
9aa1cba0
DS
455 # Write a cruft pack containing all unreachable objects.
456 git gc --cruft --prune="01-01-1980" &&
5b92477f
TB
457
458 mtimes=$(ls .git/objects/pack/pack-*.mtimes) &&
459 test_path_is_file $mtimes &&
460
9aa1cba0 461 # Prune all unreachable objects from the cruft pack.
5b92477f
TB
462 git gc --cruft --prune=now &&
463
464 git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
465
466 comm -23 unreachable objects >removed &&
467 test_cmp unreachable removed &&
468 test_path_is_missing $mtimes
469 )
470'
471
f9825d1c
TB
472test_expect_success 'cruft packs are not included in geometric repack' '
473 git init repo &&
474 test_when_finished "rm -fr repo" &&
475 (
476 cd repo &&
477
478 test_commit reachable &&
479 git repack -Ad &&
480 git branch -M main &&
481
482 git checkout --orphan other &&
483 test_commit cruft &&
484 git repack -d &&
485
486 git checkout main &&
487 git branch -D other &&
488 git tag -d cruft &&
489 git reflog expire --all --expire=all &&
490
491 git repack --cruft &&
492
493 find $packdir -type f | sort >before &&
494 git repack --geometric=2 -d &&
495 find $packdir -type f | sort >after &&
496
497 test_cmp before after
498 )
499'
500
501test_expect_success 'repack --geometric collects once-cruft objects' '
502 git init repo &&
503 test_when_finished "rm -fr repo" &&
504 (
505 cd repo &&
506
507 test_commit reachable &&
508 git repack -Ad &&
509 git branch -M main &&
510
511 git checkout --orphan other &&
512 git rm -rf . &&
513 test_commit --no-tag cruft &&
514 cruft="$(git rev-parse HEAD)" &&
515
516 git checkout main &&
517 git branch -D other &&
518 git reflog expire --all --expire=all &&
519
520 # Pack the objects created in the previous step into a cruft
521 # pack. Intentionally leave loose copies of those objects
522 # around so we can pick them up in a subsequent --geometric
523 # reapack.
524 git repack --cruft &&
525
526 # Now make those objects reachable, and ensure that they are
527 # packed into the new pack created via a --geometric repack.
528 git update-ref refs/heads/other $cruft &&
529
530 # Without this object, the set of unpacked objects is exactly
531 # the set of objects already in the cruft pack. Tweak that set
532 # to ensure we do not overwrite the cruft pack entirely.
533 test_commit reachable2 &&
534
535 find $packdir -name "pack-*.idx" | sort >before &&
536 git repack --geometric=2 -d &&
537 find $packdir -name "pack-*.idx" | sort >after &&
538
539 {
540 git rev-list --objects --no-object-names $cruft &&
541 git rev-list --objects --no-object-names reachable..reachable2
542 } >want.raw &&
543 sort want.raw >want &&
544
545 pack=$(comm -13 before after) &&
546 git show-index <$pack >objects.raw &&
547
548 cut -d" " -f2 objects.raw | sort >got &&
549
550 test_cmp want got
551 )
552'
553
554test_expect_success 'cruft repack with no reachable objects' '
555 git init repo &&
556 test_when_finished "rm -fr repo" &&
557 (
558 cd repo &&
559
560 test_commit base &&
561 git repack -ad &&
562
563 base="$(git rev-parse base)" &&
564
565 git for-each-ref --format="delete %(refname)" >in &&
566 git update-ref --stdin <in &&
567 git reflog expire --all --expire=all &&
568 rm -fr .git/index &&
569
570 git repack --cruft -d &&
571
572 git cat-file -t $base
573 )
574'
575
61568efa
TB
576write_blob () {
577 test-tool genrandom "$@" >in &&
578 git hash-object -w -t blob in
579}
580
581find_pack () {
582 for idx in $(ls $packdir/pack-*.idx)
583 do
584 git show-index <$idx >out &&
585 if grep -q "$1" out
586 then
587 echo $idx
588 fi || return 1
589 done
590}
591
592test_expect_success 'cruft repack with --max-pack-size' '
f9825d1c
TB
593 git init max-pack-size &&
594 (
595 cd max-pack-size &&
596 test_commit base &&
61568efa 597
f9825d1c 598 # two cruft objects which exceed the maximum pack size
61568efa
TB
599 foo=$(write_blob foo 1048576) &&
600 bar=$(write_blob bar 1048576) &&
601 test-tool chmtime --get -1000 \
602 "$objdir/$(test_oid_to_path $foo)" >foo.mtime &&
603 test-tool chmtime --get -2000 \
604 "$objdir/$(test_oid_to_path $bar)" >bar.mtime &&
f9825d1c
TB
605 git repack --cruft --max-pack-size=1M &&
606 find $packdir -name "*.mtimes" >cruft &&
61568efa
TB
607 test_line_count = 2 cruft &&
608
609 foo_mtimes="$(basename $(find_pack $foo) .idx).mtimes" &&
610 bar_mtimes="$(basename $(find_pack $bar) .idx).mtimes" &&
611 test-tool pack-mtimes $foo_mtimes >foo.actual &&
612 test-tool pack-mtimes $bar_mtimes >bar.actual &&
613
614 echo "$foo $(cat foo.mtime)" >foo.expect &&
615 echo "$bar $(cat bar.mtime)" >bar.expect &&
616
617 test_cmp foo.expect foo.actual &&
618 test_cmp bar.expect bar.actual &&
619 test "$foo_mtimes" != "$bar_mtimes"
f9825d1c
TB
620 )
621'
622
61568efa 623test_expect_success 'cruft repack with pack.packSizeLimit' '
f9825d1c
TB
624 (
625 cd max-pack-size &&
626 # repack everything back together to remove the existing cruft
627 # pack (but to keep its objects)
628 git repack -adk &&
629 git -c pack.packSizeLimit=1M repack --cruft &&
630 # ensure the same post condition is met when --max-pack-size
631 # would otherwise be inferred from the configuration
632 find $packdir -name "*.mtimes" >cruft &&
61568efa
TB
633 test_line_count = 2 cruft &&
634 for pack in $(cat cruft)
635 do
636 test-tool pack-mtimes "$(basename $pack)" >objects &&
637 test_line_count = 1 objects || return 1
638 done
f9825d1c
TB
639 )
640'
641
4571324b
TB
642test_expect_success 'cruft repack respects repack.cruftWindow' '
643 git init repo &&
644 test_when_finished "rm -fr repo" &&
645 (
646 cd repo &&
647
648 test_commit base &&
649
650 GIT_TRACE2_EVENT=$(pwd)/event.trace \
651 git -c pack.window=1 -c repack.cruftWindow=2 repack \
652 --cruft --window=3 &&
653
654 grep "pack-objects.*--window=2.*--cruft" event.trace
655 )
656'
657
658test_expect_success 'cruft repack respects --window by default' '
659 git init repo &&
660 test_when_finished "rm -fr repo" &&
661 (
662 cd repo &&
663
664 test_commit base &&
665
666 GIT_TRACE2_EVENT=$(pwd)/event.trace \
667 git -c pack.window=2 repack --cruft --window=3 &&
668
669 grep "pack-objects.*--window=3.*--cruft" event.trace
670 )
671'
672
673test_expect_success 'cruft repack respects --quiet' '
674 git init repo &&
675 test_when_finished "rm -fr repo" &&
676 (
677 cd repo &&
678
679 test_commit base &&
680 GIT_PROGRESS_DELAY=0 git repack --cruft --quiet 2>err &&
681 test_must_be_empty err
682 )
683'
684
685test_expect_success 'cruft --local drops unreachable objects' '
686 git init alternate &&
687 git init repo &&
688 test_when_finished "rm -fr alternate repo" &&
689
690 test_commit -C alternate base &&
691 # Pack all objects in alterate so that the cruft repack in "repo" sees
692 # the object it dropped due to `--local` as packed. Otherwise this
693 # object would not appear packed anywhere (since it is not packed in
694 # alternate and likewise not part of the cruft pack in the other repo
695 # because of `--local`).
696 git -C alternate repack -ad &&
697
698 (
699 cd repo &&
700
701 object="$(git -C ../alternate rev-parse HEAD:base.t)" &&
702 git -C ../alternate cat-file -p $object >contents &&
703
704 # Write some reachable objects and two unreachable ones: one
705 # that the alternate has and another that is unique.
706 test_commit other &&
707 git hash-object -w -t blob contents &&
708 cruft="$(echo cruft | git hash-object -w -t blob --stdin)" &&
709
710 ( cd ../alternate/.git/objects && pwd ) \
711 >.git/objects/info/alternates &&
712
713 test_path_is_file $objdir/$(test_oid_to_path $cruft) &&
714 test_path_is_file $objdir/$(test_oid_to_path $object) &&
715
716 git repack -d --cruft --local &&
717
718 test-tool pack-mtimes "$(basename $(ls $packdir/pack-*.mtimes))" \
719 >objects &&
720 ! grep $object objects &&
721 grep $cruft objects
722 )
723'
724
ddee3703
TB
725test_expect_success 'MIDX bitmaps tolerate reachable cruft objects' '
726 git init repo &&
727 test_when_finished "rm -fr repo" &&
728 (
729 cd repo &&
730
731 test_commit reachable &&
732 test_commit cruft &&
733 unreachable="$(git rev-parse cruft)" &&
734
735 git reset --hard $unreachable^ &&
736 git tag -d cruft &&
737 git reflog expire --all --expire=all &&
738
739 git repack --cruft -d &&
740
741 # resurrect the unreachable object via a new commit. the
742 # new commit will get selected for a bitmap, but be
743 # missing one of its parents from the selected packs.
744 git reset --hard $unreachable &&
745 test_commit resurrect &&
746
747 git repack --write-midx --write-bitmap-index --geometric=2 -d
748 )
749'
750
a6131642
TB
751test_expect_success 'cruft objects are freshend via loose' '
752 git init repo &&
753 test_when_finished "rm -fr repo" &&
754 (
755 cd repo &&
756
757 echo "cruft" >contents &&
758 blob="$(git hash-object -w -t blob contents)" &&
759 loose="$objdir/$(test_oid_to_path $blob)" &&
760
761 test_commit base &&
762
763 git repack --cruft -d &&
764
765 test_path_is_missing "$loose" &&
766 test-tool pack-mtimes "$(basename "$(ls $packdir/pack-*.mtimes)")" >cruft &&
767 grep "$blob" cruft &&
768
769 # write the same object again
770 git hash-object -w -t blob contents &&
771
772 test_path_is_file "$loose"
773 )
774'
775
4dc16e2c
TB
776test_expect_success 'gc.recentObjectsHook' '
777 git init repo &&
778 test_when_finished "rm -fr repo" &&
779 (
780 cd repo &&
781
782 # Create a handful of objects.
783 #
784 # - one reachable commit, "base", designated for the reachable
785 # pack
786 # - one unreachable commit, "cruft.discard", which is marked
787 # for deletion
788 # - one unreachable commit, "cruft.old", which would be marked
789 # for deletion, but is rescued as an extra cruft tip
790 # - one unreachable commit, "cruft.new", which is not marked
791 # for deletion
792 test_commit base &&
793 git branch -M main &&
794
795 git checkout --orphan discard &&
796 git rm -fr . &&
797 test_commit --no-tag cruft.discard &&
798
799 git checkout --orphan old &&
800 git rm -fr . &&
801 test_commit --no-tag cruft.old &&
802 cruft_old="$(git rev-parse HEAD)" &&
803
804 git checkout --orphan new &&
805 git rm -fr . &&
806 test_commit --no-tag cruft.new &&
807 cruft_new="$(git rev-parse HEAD)" &&
808
809 git checkout main &&
810 git branch -D discard old new &&
811 git reflog expire --all --expire=all &&
812
813 # mark cruft.old with an mtime that is many minutes
814 # older than the expiration period, and mark cruft.new
815 # with an mtime that is in the future (and thus not
816 # eligible for pruning).
817 test-tool chmtime -2000 "$objdir/$(test_oid_to_path $cruft_old)" &&
818 test-tool chmtime +1000 "$objdir/$(test_oid_to_path $cruft_new)" &&
819
820 # Write the list of cruft objects we expect to
821 # accumulate, which is comprised of everything reachable
822 # from cruft.old and cruft.new, but not cruft.discard.
823 git rev-list --objects --no-object-names \
824 $cruft_old $cruft_new >cruft.raw &&
825 sort cruft.raw >cruft.expect &&
826
827 # Write the script to list extra tips, which are limited
828 # to cruft.old, in this case.
829 write_script extra-tips <<-EOF &&
830 echo $cruft_old
831 EOF
832 git config gc.recentObjectsHook ./extra-tips &&
833
834 git repack --cruft --cruft-expiration=now -d &&
835
836 mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
837 git show-index <${mtimes%.mtimes}.idx >cruft &&
838 cut -d" " -f2 cruft | sort >cruft.actual &&
839 test_cmp cruft.expect cruft.actual &&
840
841 # Ensure that the "old" objects are removed after
842 # dropping the gc.recentObjectsHook hook.
843 git config --unset gc.recentObjectsHook &&
844 git repack --cruft --cruft-expiration=now -d &&
845
846 mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
847 git show-index <${mtimes%.mtimes}.idx >cruft &&
848 cut -d" " -f2 cruft | sort >cruft.actual &&
849
850 git rev-list --objects --no-object-names $cruft_new >cruft.raw &&
851 cp cruft.expect cruft.old &&
852 sort cruft.raw >cruft.expect &&
853 test_cmp cruft.expect cruft.actual &&
854
855 # ensure objects which are no longer in the cruft pack were
856 # removed from the repository
857 for object in $(comm -13 cruft.expect cruft.old)
858 do
859 test_must_fail git cat-file -t $object || return 1
860 done
861 )
862'
863
864test_expect_success 'multi-valued gc.recentObjectsHook' '
865 git init repo &&
866 test_when_finished "rm -fr repo" &&
867 (
868 cd repo &&
869
870 test_commit base &&
871 git branch -M main &&
872
873 git checkout --orphan cruft.a &&
874 git rm -fr . &&
875 test_commit --no-tag cruft.a &&
876 cruft_a="$(git rev-parse HEAD)" &&
877
878 git checkout --orphan cruft.b &&
879 git rm -fr . &&
880 test_commit --no-tag cruft.b &&
881 cruft_b="$(git rev-parse HEAD)" &&
882
883 git checkout main &&
884 git branch -D cruft.a cruft.b &&
885 git reflog expire --all --expire=all &&
886
887 echo "echo $cruft_a" | write_script extra-tips.a &&
888 echo "echo $cruft_b" | write_script extra-tips.b &&
889 echo "false" | write_script extra-tips.c &&
890
891 git rev-list --objects --no-object-names $cruft_a $cruft_b \
892 >cruft.raw &&
893 sort cruft.raw >cruft.expect &&
894
895 # ensure that each extra cruft tip is saved by its
896 # respective hook
897 git config --add gc.recentObjectsHook ./extra-tips.a &&
898 git config --add gc.recentObjectsHook ./extra-tips.b &&
899 git repack --cruft --cruft-expiration=now -d &&
900
901 mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
902 git show-index <${mtimes%.mtimes}.idx >cruft &&
903 cut -d" " -f2 cruft | sort >cruft.actual &&
904 test_cmp cruft.expect cruft.actual &&
905
906 # ensure that a dirty exit halts cruft pack generation
907 git config --add gc.recentObjectsHook ./extra-tips.c &&
908 test_must_fail git repack --cruft --cruft-expiration=now -d 2>err &&
909 grep "unable to enumerate additional recent objects" err &&
910
911 # and that the existing cruft pack is left alone
912 test_path_is_file "$mtimes"
913 )
914'
915
916test_expect_success 'additional cruft blobs via gc.recentObjectsHook' '
917 git init repo &&
918 test_when_finished "rm -fr repo" &&
919 (
920 cd repo &&
921
922 test_commit base &&
923
924 blob=$(echo "unreachable" | git hash-object -w --stdin) &&
925
926 # mark the unreachable blob we wrote above as having
927 # aged out of the retention period
928 test-tool chmtime -2000 "$objdir/$(test_oid_to_path $blob)" &&
929
930 # Write the script to list extra tips, which is just the
931 # extra blob as above.
932 write_script extra-tips <<-EOF &&
933 echo $blob
934 EOF
935 git config gc.recentObjectsHook ./extra-tips &&
936
937 git repack --cruft --cruft-expiration=now -d &&
938
939 mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
940 git show-index <${mtimes%.mtimes}.idx >cruft &&
941 cut -d" " -f2 cruft >actual &&
942 echo $blob >expect &&
943 test_cmp expect actual
944 )
945'
946
b7573536 947test_done