]>
Commit | Line | Data |
---|---|---|
78de1c6c TB |
1 | #!/bin/sh |
2 | ||
3 | test_description='git repack works correctly' | |
4 | ||
5 | . ./test-lib.sh | |
6 | ||
7 | objdir=.git/objects | |
37dc6d81 | 8 | packdir=$objdir/pack |
78de1c6c TB |
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 | cat expired.objects remaining.objects | sort >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 | ||
37dc6d81 TB |
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 | ||
3c1e2c21 TB |
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 | ||
78de1c6c | 414 | test_done |