]> git.ipfire.org Git - thirdparty/git.git/blob - t/t7704-repack-cruft.sh
The third batch
[thirdparty/git.git] / t / t7704-repack-cruft.sh
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 above threshold' '
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 below threshold' '
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 '--max-cruft-size combines smaller packs first' '
198 git init max-cruft-size-consume-small &&
199 (
200 cd max-cruft-size-consume-small &&
201
202 test_commit base &&
203 git repack -ad &&
204
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
209
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 &&
213
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 &&
220
221 comm -13 cruft.before cruft.after >cruft.new &&
222 comm -23 cruft.before cruft.after >cruft.removed &&
223
224 test_line_count = 1 cruft.new &&
225 test_line_count = 2 cruft.removed &&
226
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 &&
230
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 &&
234
235 test_cmp expect.objects actual.objects
236 )
237 '
238
239 test_expect_success 'setup --max-cruft-size with freshened objects' '
240 git init max-cruft-size-freshen &&
241 (
242 cd max-cruft-size-freshen &&
243
244 test_commit base &&
245 git repack -ad &&
246
247 foo="$(generate_random_blob foo 64)" &&
248 test-tool chmtime --get -10000 \
249 "$objdir/$(test_oid_to_path "$foo")" >foo.mtime &&
250
251 git repack --cruft -d &&
252
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
257 )
258 '
259
260 test_expect_success '--max-cruft-size with freshened objects (loose)' '
261 (
262 cd max-cruft-size-freshen &&
263
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 &&
268
269 git repack --cruft -d &&
270
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
275 )
276 '
277
278 test_expect_success '--max-cruft-size with freshened objects (packed)' '
279 (
280 cd max-cruft-size-freshen &&
281
282 # regenerate the object and store it in a packfile,
283 # setting its mtime to be more recent
284 #
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)" &&
291 git prune-packed &&
292
293 test-tool chmtime --get -10 \
294 "$packdir/pack-$foo_pack.pack" >foo.mtime &&
295
296 git repack --cruft -d &&
297
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
304 )
305 '
306
307 test_expect_success '--max-cruft-size with pruning' '
308 git init max-cruft-size-prune &&
309 (
310 cd max-cruft-size-prune &&
311
312 test_commit base &&
313 foo="$(generate_random_blob foo $((1024*1024)))" &&
314 bar="$(generate_random_blob bar $((1024*1024)))" &&
315 baz="$(generate_random_blob baz $((1024*1024)))" &&
316
317 test-tool chmtime -10000 "$objdir/$(test_oid_to_path "$foo")" &&
318
319 git repack -d --cruft --max-cruft-size=1M &&
320
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)
325 do
326 mtime="$(test-tool chmtime --get -10000 "$cruft")" &&
327 echo $cruft $mtime >>mtimes || return 1
328 done &&
329
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 &&
335
336 for cruft in $(cat cruft.after)
337 do
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
341 done &&
342
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 &&
347 git cat-file -e $baz
348 )
349 '
350
351 test_expect_success '--max-cruft-size ignores non-local packs' '
352 repo="max-cruft-size-non-local" &&
353 git init $repo &&
354 (
355 cd $repo &&
356 test_commit base &&
357 generate_random_blob foo 64 &&
358 git repack --cruft -d
359 ) &&
360
361 git clone --reference=$repo $repo $repo-alt &&
362 (
363 cd $repo-alt &&
364
365 test_commit other &&
366 generate_random_blob bar 64 &&
367
368 # ensure that we do not attempt to pick up packs from
369 # the non-alternated repository, which would result in a
370 # crash
371 git repack --cruft --max-cruft-size=1M -d
372 )
373 '
374
375 test_expect_success 'reachable packs are preferred over cruft ones' '
376 repo="cruft-preferred-packs" &&
377 git init "$repo" &&
378 (
379 cd "$repo" &&
380
381 # This test needs to exercise careful control over when a MIDX
382 # is and is not written. Unset the corresponding TEST variable
383 # accordingly.
384 sane_unset GIT_TEST_MULTI_PACK_INDEX &&
385
386 test_commit base &&
387 test_commit --no-tag cruft &&
388
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 &&
395
396 test_commit other &&
397 git repack -d &&
398
399 git repack --geometric 2 -d --write-midx --write-bitmap-index &&
400
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 &&
406
407 # Make sure that the pack we just wrote is marked as preferred,
408 # not the cruft one.
409 pack="$(test-tool read-midx --preferred-pack $objdir)" &&
410 test_path_is_missing "$packdir/$(basename "$pack" ".idx").mtimes"
411 )
412 '
413
414 test_done