]>
Commit | Line | Data |
---|---|---|
b7573536 TB |
1 | #!/bin/sh |
2 | ||
3 | test_description='cruft pack related pack-objects tests' | |
4 | . ./test-lib.sh | |
5 | ||
6 | objdir=.git/objects | |
7 | packdir=$objdir/pack | |
8 | ||
9 | basic_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 | ||
217 | basic_cruft_pack_tests never | |
a7d49383 TB |
218 | basic_cruft_pack_tests 2.weeks.ago |
219 | ||
220 | test_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 | ||
260 | test_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 | ||
294 | test_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 | ||
334 | test_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 |
362 | test_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 | ||
399 | test_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 |
433 | test_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 |
472 | test_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 | ||
501 | test_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 | ||
554 | test_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 |
576 | write_blob () { |
577 | test-tool genrandom "$@" >in && | |
578 | git hash-object -w -t blob in | |
579 | } | |
580 | ||
581 | find_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 | ||
592 | test_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 | 623 | test_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 |
642 | test_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 | ||
658 | test_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 | ||
673 | test_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 | ||
685 | test_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 |
725 | test_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 |
751 | test_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 |
776 | test_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 | ||
864 | test_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 | ||
916 | test_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 | 947 | test_done |