Raw
1 #!/bin/sh
2
3 test_description='git repack --geometric works correctly'
4
5 . ./test-lib.sh
6
7 GIT_TEST_MULTI_PACK_INDEX=0
8
9 objdir=.git/objects
10 packdir=$objdir/pack
11 midx=$objdir/pack/multi-pack-index
12
13 packed_objects () {
14 git show-index <"$1" >tmp-object-list &&
15 cut -d' ' -f2 tmp-object-list | sort &&
16 rm tmp-object-list
17 }
18
19 test_expect_success '--geometric with no packs' '
20 git init geometric &&
21 test_when_finished "rm -fr geometric" &&
22 (
23 cd geometric &&
24
25 git repack --write-midx --geometric 2 >out &&
26 test_grep "Nothing new to pack" out
27 )
28 '
29
30 test_expect_success '--geometric with one pack' '
31 git init geometric &&
32 test_when_finished "rm -fr geometric" &&
33 (
34 cd geometric &&
35
36 test_commit "base" &&
37 git repack -d &&
38
39 git repack --geometric 2 >out &&
40
41 test_grep "Nothing new to pack" out
42 )
43 '
44
45 test_expect_success '--geometric with an intact progression' '
46 git init geometric &&
47 test_when_finished "rm -fr geometric" &&
48 (
49 cd geometric &&
50
51 # These packs already form a geometric progression.
52 test_commit_bulk --start=1 1 && # 3 objects
53 test_commit_bulk --start=2 2 && # 6 objects
54 test_commit_bulk --start=4 4 && # 12 objects
55
56 find $objdir/pack -name "*.pack" | sort >expect &&
57 git repack --geometric 2 -d &&
58 find $objdir/pack -name "*.pack" | sort >actual &&
59
60 test_cmp expect actual
61 )
62 '
63
64 test_expect_success '--geometric with loose objects' '
65 git init geometric &&
66 test_when_finished "rm -fr geometric" &&
67 (
68 cd geometric &&
69
70 # These packs already form a geometric progression.
71 test_commit_bulk --start=1 1 && # 3 objects
72 test_commit_bulk --start=2 2 && # 6 objects
73 # The loose objects are packed together, breaking the
74 # progression.
75 test_commit loose && # 3 objects
76
77 find $objdir/pack -name "*.pack" | sort >before &&
78 git repack --geometric 2 -d &&
79 find $objdir/pack -name "*.pack" | sort >after &&
80
81 comm -13 before after >new &&
82 comm -23 before after >removed &&
83
84 test_line_count = 1 new &&
85 test_must_be_empty removed &&
86
87 git repack --geometric 2 -d &&
88 find $objdir/pack -name "*.pack" | sort >after &&
89
90 # The progression (3, 3, 6) is combined into one new pack.
91 test_line_count = 1 after
92 )
93 '
94
95 test_expect_success '--geometric with small-pack rollup' '
96 git init geometric &&
97 test_when_finished "rm -fr geometric" &&
98 (
99 cd geometric &&
100
101 test_commit_bulk --start=1 1 && # 3 objects
102 test_commit_bulk --start=2 1 && # 3 objects
103 find $objdir/pack -name "*.pack" | sort >small &&
104 test_commit_bulk --start=3 4 && # 12 objects
105 test_commit_bulk --start=7 8 && # 24 objects
106 find $objdir/pack -name "*.pack" | sort >before &&
107
108 git repack --geometric 2 -d &&
109
110 # Three packs in total; two of the existing large ones, and one
111 # new one.
112 find $objdir/pack -name "*.pack" | sort >after &&
113 test_line_count = 3 after &&
114 comm -3 small before | tr -d "\t" >large &&
115 test_grep -qFf large after
116 )
117 '
118
119 test_expect_success '--geometric with small- and large-pack rollup' '
120 git init geometric &&
121 test_when_finished "rm -fr geometric" &&
122 (
123 cd geometric &&
124
125 # size(small1) + size(small2) > size(medium) / 2
126 test_commit_bulk --start=1 1 && # 3 objects
127 test_commit_bulk --start=2 1 && # 3 objects
128 test_commit_bulk --start=2 3 && # 7 objects
129 test_commit_bulk --start=6 9 && # 27 objects &&
130
131 find $objdir/pack -name "*.pack" | sort >before &&
132
133 git repack --geometric 2 -d &&
134
135 find $objdir/pack -name "*.pack" | sort >after &&
136 comm -12 before after >untouched &&
137
138 # Two packs in total; the largest pack from before running "git
139 # repack", and one new one.
140 test_line_count = 1 untouched &&
141 test_line_count = 2 after
142 )
143 '
144
145 test_expect_success '--geometric ignores kept packs' '
146 git init geometric &&
147 test_when_finished "rm -fr geometric" &&
148 (
149 cd geometric &&
150
151 test_commit kept && # 3 objects
152 test_commit pack && # 3 objects
153
154 KEPT=$(git pack-objects --revs $objdir/pack/pack <<-EOF
155 refs/tags/kept
156 EOF
157 ) &&
158 PACK=$(git pack-objects --revs $objdir/pack/pack <<-EOF
159 refs/tags/pack
160 ^refs/tags/kept
161 EOF
162 ) &&
163
164 # neither pack contains more than twice the number of objects in
165 # the other, so they should be combined. but, marking one as
166 # .kept on disk will "freeze" it, so the pack structure should
167 # remain unchanged.
168 touch $objdir/pack/pack-$KEPT.keep &&
169
170 find $objdir/pack -name "*.pack" | sort >before &&
171 git repack --geometric 2 -d &&
172 find $objdir/pack -name "*.pack" | sort >after &&
173
174 # both packs should still exist
175 test_path_is_file $objdir/pack/pack-$KEPT.pack &&
176 test_path_is_file $objdir/pack/pack-$PACK.pack &&
177
178 # and no new packs should be created
179 test_cmp before after &&
180
181 # Passing --pack-kept-objects causes packs with a .keep file to
182 # be repacked, too.
183 git repack --geometric 2 -d --pack-kept-objects &&
184
185 # After repacking, two packs remain: one new one (containing the
186 # objects in both the .keep and non-kept pack), and the .keep
187 # pack (since `--pack-kept-objects -d` does not actually delete
188 # the kept pack).
189 find $objdir/pack -name "*.pack" >after &&
190 test_line_count = 2 after
191 )
192 '
193
194 test_expect_success '--geometric ignores --keep-pack packs' '
195 git init geometric &&
196 test_when_finished "rm -fr geometric" &&
197 (
198 cd geometric &&
199
200 # Create two equal-sized packs
201 test_commit kept && # 3 objects
202 git repack -d &&
203 test_commit pack && # 3 objects
204 git repack -d &&
205
206 find $objdir/pack -type f -name "*.pack" | sort >packs.before &&
207 git repack --geometric 2 -dm \
208 --keep-pack="$(basename "$(head -n 1 packs.before)")" >out &&
209 find $objdir/pack -type f -name "*.pack" | sort >packs.after &&
210
211 # Packs should not have changed (only one non-kept pack, no
212 # loose objects), but $midx should now exist.
213 test_grep "Nothing new to pack" out &&
214 test_path_is_file $midx &&
215
216 test_cmp packs.before packs.after &&
217
218 git fsck
219 )
220 '
221
222 test_expect_success '--geometric chooses largest MIDX preferred pack' '
223 git init geometric &&
224 test_when_finished "rm -fr geometric" &&
225 (
226 cd geometric &&
227
228 # These packs already form a geometric progression.
229 test_commit_bulk --start=1 1 && # 3 objects
230 test_commit_bulk --start=2 2 && # 6 objects
231 ls $objdir/pack/pack-*.idx >before &&
232 test_commit_bulk --start=4 4 && # 12 objects
233 ls $objdir/pack/pack-*.idx >after &&
234
235 git repack --geometric 2 -dbm &&
236
237 comm -3 before after | xargs -n 1 basename >expect &&
238 test-tool read-midx --preferred-pack $objdir >actual &&
239
240 test_cmp expect actual
241 )
242 '
243
244 test_expect_success '--geometric with pack.packSizeLimit' '
245 git init pack-rewrite &&
246 test_when_finished "rm -fr pack-rewrite" &&
247 (
248 cd pack-rewrite &&
249
250 test-tool genrandom foo 1048576 >foo &&
251 test-tool genrandom bar 1048576 >bar &&
252
253 git add foo bar &&
254 test_tick &&
255 git commit -m base &&
256
257 git rev-parse HEAD:foo HEAD:bar >p1.objects &&
258 git rev-parse HEAD HEAD^{tree} >p2.objects &&
259
260 # These two packs each contain two objects, so the following
261 # `--geometric` repack will try to combine them.
262 p1="$(git pack-objects $packdir/pack <p1.objects)" &&
263 p2="$(git pack-objects $packdir/pack <p2.objects)" &&
264
265 # Remove any loose objects in packs, since we do not want extra
266 # copies around (which would mask over potential object
267 # corruption issues).
268 git prune-packed &&
269
270 # Both p1 and p2 will be rolled up, but pack-objects will write
271 # three packs:
272 #
273 # - one containing object "foo",
274 # - another containing object "bar",
275 # - a final pack containing the commit and tree objects
276 # (identical to p2 above)
277 git repack --geometric 2 -d --max-pack-size=1048576 &&
278
279 # Ensure `repack` can detect that the third pack it wrote
280 # (containing just the tree and commit objects) was identical to
281 # one that was below the geometric split, so that we can save it
282 # from deletion.
283 #
284 # If `repack` fails to do that, we will incorrectly delete p2,
285 # causing object corruption.
286 git fsck
287 )
288 '
289
290 test_expect_success '--geometric --write-midx retains up-to-date MIDX without bitmap index' '
291 test_when_finished "rm -fr repo" &&
292 git init repo &&
293 (
294 cd repo &&
295 test_commit initial &&
296
297 test_path_is_missing .git/objects/pack/multi-pack-index &&
298 git repack --geometric=2 --write-midx --no-write-bitmap-index &&
299 test_path_is_file .git/objects/pack/multi-pack-index &&
300 test-tool chmtime =0 .git/objects/pack/multi-pack-index &&
301
302 ls -l .git/objects/pack/ >expect &&
303 git repack --geometric=2 --write-midx --no-write-bitmap-index &&
304 ls -l .git/objects/pack/ >actual &&
305 test_cmp expect actual
306 )
307 '
308
309 test_expect_success '--geometric --write-midx retains up-to-date MIDX with bitmap index' '
310 test_when_finished "rm -fr repo" &&
311 git init repo &&
312 test_commit -C repo initial &&
313
314 test_path_is_missing repo/.git/objects/pack/multi-pack-index &&
315 git -C repo repack --geometric=2 --write-midx --write-bitmap-index &&
316 test_path_is_file repo/.git/objects/pack/multi-pack-index &&
317 test-tool chmtime =0 repo/.git/objects/pack/multi-pack-index &&
318
319 ls -l repo/.git/objects/pack/ >expect &&
320 git -C repo repack --geometric=2 --write-midx --write-bitmap-index &&
321 ls -l repo/.git/objects/pack/ >actual &&
322 test_cmp expect actual
323 '
324
325 test_expect_success '--geometric --write-midx with packfiles in main and alternate ODB' '
326 test_when_finished "rm -fr shared member" &&
327
328 # Create a shared repository that will serve as the alternate object
329 # database for the member linked to it. It has got some objects on its
330 # own that are packed into a single packfile.
331 git init shared &&
332 test_commit -C shared common-object &&
333 git -C shared repack -ad &&
334
335 # We create member so that its alternates file points to the shared
336 # repository. We then create a commit in it so that git-repack(1) has
337 # something to repack.
338 # of the shared object database.
339 git clone --shared shared member &&
340 test_commit -C member unique-object &&
341 git -C member repack --geometric=2 --write-midx 2>err &&
342 test_must_be_empty err &&
343
344 # We should see that a new packfile was generated.
345 find shared/.git/objects/pack -type f -name "*.pack" >packs &&
346 test_line_count = 1 packs &&
347
348 # We should also see a multi-pack-index. This multi-pack-index should
349 # never refer to any packfiles in the alternate object database.
350 test_path_is_file member/.git/objects/pack/multi-pack-index &&
351 test-tool read-midx member/.git/objects >packs.midx &&
352 grep "^pack-.*\.idx$" packs.midx | sort >actual &&
353 basename member/.git/objects/pack/pack-*.idx >expect &&
354 test_cmp expect actual
355 '
356
357 test_expect_success '--geometric --with-midx with no local objects' '
358 test_when_finished "rm -fr shared member" &&
359
360 # Create a repository with a single packfile that acts as alternate
361 # object database.
362 git init shared &&
363 test_commit -C shared "shared-objects" &&
364 git -C shared repack -ad &&
365
366 # Create a second repository linked to the first one and perform a
367 # geometric repack on it.
368 git clone --shared shared member &&
369 git -C member repack --geometric 2 --write-midx 2>err &&
370 test_must_be_empty err &&
371
372 # Assert that we wrote neither a new packfile nor a multi-pack-index.
373 # We should not have a packfile because the single packfile in the
374 # alternate object database does not invalidate the geometric sequence.
375 # And we should not have a multi-pack-index because these only index
376 # local packfiles, and there are none.
377 test_dir_is_empty member/$packdir
378 '
379
380 test_expect_success '--geometric with same pack in main and alternate ODB' '
381 test_when_finished "rm -fr shared member" &&
382
383 # Create a repository with a single packfile that acts as alternate
384 # object database.
385 git init shared &&
386 test_commit -C shared "shared-objects" &&
387 git -C shared repack -ad &&
388
389 # We create the member repository as an exact copy so that it has the
390 # same packfile.
391 cp -r shared member &&
392 test-tool path-utils real_path shared/.git/objects >member/.git/objects/info/alternates &&
393 find shared/.git/objects -type f >expected-files &&
394
395 # Verify that we can repack objects as expected without observing any
396 # error. Having the same packfile in both ODBs used to cause an error
397 # in git-pack-objects(1).
398 git -C member repack --geometric 2 2>err &&
399 test_must_be_empty err &&
400 # Nothing should have changed.
401 find shared/.git/objects -type f >actual-files &&
402 test_cmp expected-files actual-files
403 '
404
405 test_expect_success '--geometric -l with non-intact geometric sequence across ODBs' '
406 test_when_finished "rm -fr shared member" &&
407
408 git init shared &&
409 test_commit_bulk -C shared --start=1 1 &&
410
411 git clone --shared shared member &&
412 test_commit_bulk -C member --start=2 1 &&
413
414 # Verify that our assumptions actually hold: both generated packfiles
415 # should have three objects and should be non-equal.
416 packed_objects shared/.git/objects/pack/pack-*.idx >shared-objects &&
417 packed_objects member/.git/objects/pack/pack-*.idx >member-objects &&
418 test_line_count = 3 shared-objects &&
419 test_line_count = 3 member-objects &&
420 ! test_cmp shared-objects member-objects &&
421
422 # Perform the geometric repack. With `-l`, we should only see the local
423 # packfile and thus arrive at the conclusion that the geometric
424 # sequence is intact. We thus expect no changes.
425 #
426 # Note that we are tweaking mtimes of the packfiles so that we can
427 # verify they did not change. This is done in order to detect the case
428 # where we do repack objects, but the resulting packfile is the same.
429 test-tool chmtime --verbose =0 member/.git/objects/pack/* >expected-member-packs &&
430 git -C member repack --geometric=2 -l -d &&
431 test-tool chmtime --verbose member/.git/objects/pack/* >actual-member-packs &&
432 test_cmp expected-member-packs actual-member-packs &&
433
434 {
435 packed_objects shared/.git/objects/pack/pack-*.idx &&
436 packed_objects member/.git/objects/pack/pack-*.idx
437 } | sort >expected-objects &&
438
439 # On the other hand, when doing a non-local geometric repack we should
440 # see both packfiles and thus repack them. We expect that the shared
441 # object database was not changed.
442 test-tool chmtime --verbose =0 shared/.git/objects/pack/* >expected-shared-packs &&
443 git -C member repack --geometric=2 -d &&
444 test-tool chmtime --verbose shared/.git/objects/pack/* >actual-shared-packs &&
445 test_cmp expected-shared-packs actual-shared-packs &&
446
447 # Furthermore, we expect that the member repository now has a single
448 # packfile that contains the combined shared and non-shared objects.
449 ls member/.git/objects/pack/pack-*.idx >actual &&
450 test_line_count = 1 actual &&
451 packed_objects member/.git/objects/pack/pack-*.idx >actual-objects &&
452 test_line_count = 6 actual-objects &&
453 test_cmp expected-objects actual-objects
454 '
455
456 test_expect_success '--geometric -l disables writing bitmaps with non-local packfiles' '
457 test_when_finished "rm -fr shared member" &&
458
459 git init shared &&
460 test_commit_bulk -C shared --start=1 1 &&
461
462 git clone --shared shared member &&
463 test_commit_bulk -C member --start=2 1 &&
464
465 # When performing a geometric repack with `-l` while connected to an
466 # alternate object database that has a packfile we do not have full
467 # coverage of objects. As a result, we expect that writing the bitmap
468 # will be disabled.
469 git -C member repack -l --geometric=2 --write-midx --write-bitmap-index 2>err &&
470 cat >expect <<-EOF &&
471 warning: disabling bitmap writing, as some objects are not being packed
472 EOF
473 test_cmp expect err &&
474 test_path_is_missing member/.git/objects/pack/multi-pack-index-*.bitmap &&
475
476 # On the other hand, when we repack without `-l`, we should see that
477 # the bitmap gets created.
478 git -C member repack --geometric=2 --write-midx --write-bitmap-index 2>err &&
479 test_must_be_empty err &&
480 test_path_is_file member/.git/objects/pack/multi-pack-index-*.bitmap
481 '
482
483 write_packfile () {
484 NR="$1"
485 PREFIX="$2"
486
487 printf "blob\ndata <<EOB\n$PREFIX %s\nEOB\n" $(test_seq $NR) |
488 git fast-import &&
489 git pack-objects --pack-loose-unreachable .git/objects/pack/pack &&
490 git prune-packed
491 }
492
493 write_promisor_packfile () {
494 PACKFILE=$(write_packfile "$@") &&
495 touch .git/objects/pack/pack-$PACKFILE.promisor &&
496 echo "$PACKFILE"
497 }
498
499 test_expect_success 'geometric repack works with promisor packs' '
500 test_when_finished "rm -fr repo" &&
501 git init repo &&
502 (
503 cd repo &&
504 git config set maintenance.auto false &&
505 git remote add promisor garbage &&
506 git config set remote.promisor.promisor true &&
507
508 # Packs A and B need to be merged.
509 NORMAL_A=$(write_packfile 2 normal-a) &&
510 NORMAL_B=$(write_packfile 2 normal-b) &&
511 NORMAL_C=$(write_packfile 14 normal-c) &&
512
513 # Packs A, B and C need to be merged.
514 PROMISOR_A=$(write_promisor_packfile 1 promisor-a) &&
515 PROMISOR_B=$(write_promisor_packfile 3 promisor-b) &&
516 PROMISOR_C=$(write_promisor_packfile 3 promisor-c) &&
517 PROMISOR_D=$(write_promisor_packfile 20 promisor-d) &&
518 PROMISOR_E=$(write_promisor_packfile 40 promisor-e) &&
519
520 git cat-file --batch-all-objects --batch-check="%(objectname)" >objects-expect &&
521
522 ls .git/objects/pack/*.pack >packs-before &&
523 test_line_count = 8 packs-before &&
524 git repack --geometric=2 -d &&
525 ls .git/objects/pack/*.pack >packs-after &&
526 test_line_count = 5 packs-after &&
527 test_grep ! "$NORMAL_A" packs-after &&
528 test_grep ! "$NORMAL_B" packs-after &&
529 test_grep "$NORMAL_C" packs-after &&
530 test_grep ! "$PROMISOR_A" packs-after &&
531 test_grep ! "$PROMISOR_B" packs-after &&
532 test_grep ! "$PROMISOR_C" packs-after &&
533 test_grep "$PROMISOR_D" packs-after &&
534 test_grep "$PROMISOR_E" packs-after &&
535
536 ls .git/objects/pack/*.promisor >promisors &&
537 test_line_count = 3 promisors &&
538
539 git cat-file --batch-all-objects --batch-check="%(objectname)" >objects-actual &&
540 test_cmp objects-expect objects-actual
541 )
542 '
543
544 test_done