Raw
1 #!/bin/sh
2
3 test_description='pack-objects --stdin'
4 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
5 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
6
7 . ./test-lib.sh
8
9 packed_objects () {
10 git show-index <"$1" >tmp-object-list &&
11 cut -d' ' -f2 tmp-object-list | sort &&
12 rm tmp-object-list
13 }
14
15 test_expect_success 'setup for --stdin-packs tests' '
16 git init stdin-packs &&
17 git -C stdin-packs config set maintenance.auto false &&
18 (
19 cd stdin-packs &&
20
21 test_commit A &&
22 test_commit B &&
23 test_commit C &&
24
25 for id in A B C
26 do
27 git pack-objects .git/objects/pack/pack-$id \
28 --incremental --revs <<-EOF || exit 1
29 refs/tags/$id
30 EOF
31 done &&
32
33 ls -la .git/objects/pack
34 )
35 '
36
37 test_expect_success '--stdin-packs with excluded packs' '
38 (
39 cd stdin-packs &&
40
41 PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
42 PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
43 PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
44
45 git pack-objects test --stdin-packs <<-EOF &&
46 $PACK_A
47 ^$PACK_B
48 $PACK_C
49 EOF
50
51 (
52 git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
53 git show-index <$(ls .git/objects/pack/pack-C-*.idx)
54 ) >expect.raw &&
55 git show-index <$(ls test-*.idx) >actual.raw &&
56
57 cut -d" " -f2 <expect.raw | sort >expect &&
58 cut -d" " -f2 <actual.raw | sort >actual &&
59 test_cmp expect actual
60 )
61 '
62
63 test_expect_success '--stdin-packs is incompatible with --filter' '
64 (
65 cd stdin-packs &&
66 test_must_fail git pack-objects --stdin-packs --stdout \
67 --filter=blob:none </dev/null 2>err &&
68 test_grep "options .--stdin-packs. and .--filter. cannot be used together" err
69 )
70 '
71
72 test_expect_success '--stdin-packs is incompatible with --revs' '
73 (
74 cd stdin-packs &&
75 test_must_fail git pack-objects --stdin-packs --revs out \
76 </dev/null 2>err &&
77 test_grep "cannot use internal rev list with --stdin-packs" err
78 )
79 '
80
81 test_expect_success '--stdin-packs with loose objects' '
82 (
83 cd stdin-packs &&
84
85 PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
86 PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
87 PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
88
89 test_commit D && # loose
90
91 git pack-objects test2 --stdin-packs --unpacked <<-EOF &&
92 $PACK_A
93 ^$PACK_B
94 $PACK_C
95 EOF
96
97 (
98 git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
99 git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
100 git rev-list --objects --no-object-names \
101 refs/tags/C..refs/tags/D
102
103 ) >expect.raw &&
104 ls -la . &&
105 git show-index <$(ls test2-*.idx) >actual.raw &&
106
107 cut -d" " -f2 <expect.raw | sort >expect &&
108 cut -d" " -f2 <actual.raw | sort >actual &&
109 test_cmp expect actual
110 )
111 '
112
113 test_expect_success '--stdin-packs with broken links' '
114 (
115 cd stdin-packs &&
116
117 # make an unreachable object with a bogus parent
118 git cat-file -p HEAD >commit &&
119 sed "s/$(git rev-parse HEAD^)/$(test_oid zero)/" <commit |
120 git hash-object -w -t commit --stdin >in &&
121
122 git pack-objects .git/objects/pack/pack-D <in &&
123
124 PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
125 PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
126 PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
127 PACK_D="$(basename .git/objects/pack/pack-D-*.pack)" &&
128
129 git pack-objects test3 --stdin-packs --unpacked <<-EOF &&
130 $PACK_A
131 ^$PACK_B
132 $PACK_C
133 $PACK_D
134 EOF
135
136 (
137 git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
138 git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
139 git show-index <$(ls .git/objects/pack/pack-D-*.idx) &&
140 git rev-list --objects --no-object-names \
141 refs/tags/C..refs/tags/D
142 ) >expect.raw &&
143 git show-index <$(ls test3-*.idx) >actual.raw &&
144
145 cut -d" " -f2 <expect.raw | sort >expect &&
146 cut -d" " -f2 <actual.raw | sort >actual &&
147 test_cmp expect actual
148 )
149 '
150
151 test_expect_success 'pack-objects --stdin with duplicate packfile' '
152 test_when_finished "rm -fr repo" &&
153
154 git init repo &&
155 (
156 cd repo &&
157 test_commit "commit" &&
158 git repack -ad &&
159
160 {
161 basename .git/objects/pack/pack-*.pack &&
162 basename .git/objects/pack/pack-*.pack
163 } >packfiles &&
164
165 git pack-objects --stdin-packs generated-pack <packfiles &&
166 packed_objects .git/objects/pack/pack-*.idx >expect &&
167 packed_objects generated-pack-*.idx >actual &&
168 test_cmp expect actual
169 )
170 '
171
172 test_expect_success 'pack-objects --stdin with same packfile excluded and included' '
173 test_when_finished "rm -fr repo" &&
174
175 git init repo &&
176 (
177 cd repo &&
178 test_commit "commit" &&
179 git repack -ad &&
180
181 {
182 basename .git/objects/pack/pack-*.pack &&
183 printf "^%s\n" "$(basename .git/objects/pack/pack-*.pack)"
184 } >packfiles &&
185
186 git pack-objects --stdin-packs generated-pack <packfiles &&
187 packed_objects generated-pack-*.idx >packed-objects &&
188 test_must_be_empty packed-objects
189 )
190 '
191
192 test_expect_success 'pack-objects --stdin with packfiles from alternate object database' '
193 test_when_finished "rm -fr shared member" &&
194
195 # Set up a shared repository with a single packfile.
196 git init shared &&
197 test_commit -C shared "shared-objects" &&
198 git -C shared repack -ad &&
199 basename shared/.git/objects/pack/pack-*.pack >packfile &&
200
201 # Set up a repository that is connected to the shared repository. This
202 # repository has no objects on its own, but we still expect to be able
203 # to pack objects from its alternate.
204 git clone --shared shared member &&
205 git -C member pack-objects --stdin-packs generated-pack <packfile &&
206 test_cmp shared/.git/objects/pack/pack-*.pack member/generated-pack-*.pack
207 '
208
209 test_expect_success 'pack-objects --stdin with packfiles from main and alternate object database' '
210 test_when_finished "rm -fr shared member" &&
211
212 # Set up a shared repository with a single packfile.
213 git init shared &&
214 test_commit -C shared "shared-commit" &&
215 git -C shared repack -ad &&
216
217 # Set up a repository that is connected to the shared repository. This
218 # repository has a second packfile so that we can verify that it is
219 # possible to write packs that include packfiles from different object
220 # databases.
221 git clone --shared shared member &&
222 test_commit -C member "local-commit" &&
223 git -C member repack -dl &&
224
225 {
226 basename shared/.git/objects/pack/pack-*.pack &&
227 basename member/.git/objects/pack/pack-*.pack
228 } >packfiles &&
229
230 {
231 packed_objects shared/.git/objects/pack/pack-*.idx &&
232 packed_objects member/.git/objects/pack/pack-*.idx
233 } | sort >expected-objects &&
234
235 git -C member pack-objects --stdin-packs generated-pack <packfiles &&
236 packed_objects member/generated-pack-*.idx >actual-objects &&
237 test_cmp expected-objects actual-objects
238 '
239
240 objdir=.git/objects
241 packdir=$objdir/pack
242
243 objects_in_packs () {
244 for p in "$@"
245 do
246 git show-index <"$packdir/pack-$p.idx" || return 1
247 done >objects.raw &&
248
249 cut -d' ' -f2 objects.raw | sort &&
250 rm -f objects.raw
251 }
252
253 test_expect_success '--stdin-packs=follow walks into unknown packs' '
254 test_when_finished "rm -fr repo" &&
255
256 git init repo &&
257 (
258 cd repo &&
259 git config set maintenance.auto false &&
260
261 for c in A B C D
262 do
263 test_commit "$c" || return 1
264 done &&
265
266 A="$(echo A | git pack-objects --revs $packdir/pack)" &&
267 B="$(echo A..B | git pack-objects --revs $packdir/pack)" &&
268 C="$(echo B..C | git pack-objects --revs $packdir/pack)" &&
269 D="$(echo C..D | git pack-objects --revs $packdir/pack)" &&
270 test_commit E &&
271
272 git prune-packed &&
273
274 cat >in <<-EOF &&
275 pack-$B.pack
276 ^pack-$C.pack
277 pack-$D.pack
278 EOF
279
280 # With just --stdin-packs, pack "A" is unknown to us, so
281 # only objects from packs "B" and "D" are included in
282 # the output pack.
283 P=$(git pack-objects --stdin-packs $packdir/pack <in) &&
284 objects_in_packs $B $D >expect &&
285 objects_in_packs $P >actual &&
286 test_cmp expect actual &&
287
288 # But with --stdin-packs=follow, objects from both
289 # included packs reach objects from the unknown pack, so
290 # objects from pack "A" is included in the output pack
291 # in addition to the above.
292 P=$(git pack-objects --stdin-packs=follow $packdir/pack <in) &&
293 objects_in_packs $A $B $D >expect &&
294 objects_in_packs $P >actual &&
295 test_cmp expect actual &&
296
297 # And with --unpacked, we will pick up objects from unknown
298 # packs that are reachable from loose objects. Loose object E
299 # reaches objects in pack A, but there are three excluded packs
300 # in between.
301 #
302 # The resulting pack should include objects reachable from E
303 # that are not present in packs B, C, or D, along with those
304 # present in pack A.
305 cat >in <<-EOF &&
306 ^pack-$B.pack
307 ^pack-$C.pack
308 ^pack-$D.pack
309 EOF
310
311 P=$(git pack-objects --stdin-packs=follow --unpacked \
312 $packdir/pack <in) &&
313
314 {
315 objects_in_packs $A &&
316 git rev-list --objects --no-object-names D..E
317 }>expect.raw &&
318 sort expect.raw >expect &&
319 objects_in_packs $P >actual &&
320 test_cmp expect actual
321 )
322 '
323
324 test_expect_success '--stdin-packs with promisors' '
325 test_when_finished "rm -fr repo" &&
326 git init repo &&
327 (
328 cd repo &&
329 git config set maintenance.auto false &&
330 git remote add promisor garbage &&
331 git config set remote.promisor.promisor true &&
332
333 for c in A B C D
334 do
335 echo "$c" >file &&
336 git add file &&
337 git commit --message "$c" &&
338 git tag "$c" || return 1
339 done &&
340
341 A="$(echo A | git pack-objects --revs $packdir/pack)" &&
342 B="$(echo A..B | git pack-objects --revs $packdir/pack --filter=blob:none)" &&
343 C="$(echo B..C | git pack-objects --revs $packdir/pack)" &&
344 D="$(echo C..D | git pack-objects --revs $packdir/pack)" &&
345 touch $packdir/pack-$B.promisor &&
346
347 test_must_fail git pack-objects --stdin-packs --exclude-promisor-objects pack- 2>err <<-EOF &&
348 pack-$B.pack
349 EOF
350 test_grep "is a promisor but --exclude-promisor-objects was given" err &&
351
352 PACK=$(git pack-objects --stdin-packs=follow --exclude-promisor-objects $packdir/pack <<-EOF
353 pack-$D.pack
354 EOF
355 ) &&
356 objects_in_packs $C $D >expect &&
357 objects_in_packs $PACK >actual &&
358 test_cmp expect actual &&
359 rm -f $packdir/pack-$PACK.*
360 )
361 '
362
363 test_expect_success '--stdin-packs does not perform backfill fetch' '
364 test_when_finished "rm -rf remote client" &&
365
366 git init remote &&
367 test_commit_bulk -C remote 10 &&
368 git -C remote config set --local uploadpack.allowfilter 1 &&
369 git -C remote config set --local uploadpack.allowanysha1inwant 1 &&
370
371 git clone --filter=tree:0 "file://$(pwd)/remote" client &&
372 (
373 cd client &&
374 ls .git/objects/pack/*.promisor | sed "s|.*/||; s/\.promisor$/.pack/" >packs &&
375 test_line_count -gt 1 packs &&
376 GIT_TRACE2_EVENT="$(pwd)/event.log" git pack-objects --stdin-packs pack <packs &&
377 test_grep ! "\"event\":\"child_start\"" event.log
378 )
379 '
380
381 stdin_packs__follow_with_only () {
382 rm -fr stdin_packs__follow_with_only &&
383 git init stdin_packs__follow_with_only &&
384 (
385 cd stdin_packs__follow_with_only &&
386
387 test_commit A &&
388 test_commit B &&
389
390 git rev-parse "$@" >B.objects &&
391
392 echo A | git pack-objects --revs $packdir/pack &&
393 B="$(git pack-objects $packdir/pack <B.objects)" &&
394
395 git cat-file --batch-check="%(objectname)" --batch-all-objects >objs &&
396 for obj in $(cat objs)
397 do
398 rm -f $objdir/$(test_oid_to_path $obj) || return 1
399 done &&
400
401 ( cd $packdir && ls pack-*.pack ) >in &&
402 git pack-objects --stdin-packs=follow --stdout >/dev/null <in
403 )
404 }
405
406 test_expect_success '--stdin-packs=follow tolerates missing blobs' '
407 stdin_packs__follow_with_only HEAD HEAD^{tree}
408 '
409
410 test_expect_success '--stdin-packs=follow tolerates missing trees' '
411 stdin_packs__follow_with_only HEAD HEAD:B.t
412 '
413
414 test_expect_success '--stdin-packs=follow tolerates missing commits' '
415 stdin_packs__follow_with_only HEAD HEAD^{tree}
416 '
417
418 test_expect_success '--stdin-packs=follow with open-excluded packs' '
419 test_when_finished "rm -fr repo" &&
420
421 git init repo &&
422 (
423 cd repo &&
424 git config set maintenance.auto false &&
425
426 git branch -M main &&
427
428 # Create the following commit structure:
429 #
430 # A <-- B <-- D (main)
431 # ^
432 # \
433 # C (other)
434 test_commit A &&
435 test_commit B &&
436 git checkout -B other &&
437 test_commit C &&
438 git checkout main &&
439 test_commit D &&
440
441 A="$(echo A | git pack-objects --revs $packdir/pack)" &&
442 B="$(echo A..B | git pack-objects --revs $packdir/pack)" &&
443 C="$(echo B..C | git pack-objects --revs $packdir/pack)" &&
444 D="$(echo B..D | git pack-objects --revs $packdir/pack)" &&
445
446 C_ONLY="$(git rev-parse other | git pack-objects $packdir/pack)" &&
447
448 git prune-packed &&
449
450 # Create a pack using --stdin-packs=follow where:
451 #
452 # - pack D is included,
453 # - pack C_ONLY is excluded, but open,
454 # - pack B is excluded, but closed, and
455 # - packs A and C are unknown
456 #
457 # The resulting pack should therefore contain:
458 #
459 # - objects from the included pack D,
460 # - A.t (rescued via D^{tree}), and
461 # - C^{tree} and C.t (rescued via pack C_ONLY)
462 #
463 # , but should omit:
464 #
465 # - C (excluded via C_ONLY),
466 # - objects from pack B (trivially excluded-closed)
467 # - A and A^{tree} (ancestors of B)
468 P=$(git pack-objects --stdin-packs=follow $packdir/pack <<-EOF
469 pack-$D.pack
470 !pack-$C_ONLY.pack
471 ^pack-$B.pack
472 EOF
473 ) &&
474
475 {
476 objects_in_packs $D &&
477 git rev-parse A:A.t "C^{tree}" C:C.t
478 } >expect.raw &&
479 sort expect.raw >expect &&
480
481 objects_in_packs $P >actual &&
482 test_cmp expect actual
483 )
484 '
485
486 test_expect_success '--stdin-packs with !-delimited pack without follow' '
487 test_when_finished "rm -fr repo" &&
488
489 git init repo &&
490 (
491 test_commit A &&
492 test_commit B &&
493 test_commit C &&
494
495 A="$(echo A | git pack-objects --revs $packdir/pack)" &&
496 B="$(echo A..B | git pack-objects --revs $packdir/pack)" &&
497 C="$(echo B..C | git pack-objects --revs $packdir/pack)" &&
498
499 cat >in <<-EOF &&
500 !pack-$A.pack
501 pack-$B.pack
502 pack-$C.pack
503 EOF
504
505 # Without --stdin-packs=follow, we treat the first
506 # line of input as a literal packfile name, and thus
507 # expect pack-objects to complain of a missing pack
508 test_must_fail git pack-objects --stdin-packs --stdout \
509 >/dev/null <in 2>err &&
510 test_grep "could not find pack .!pack-$A.pack." err &&
511
512 # With --stdin-packs=follow, we treat the second line
513 # of input as indicating pack-$A.pack is an excluded
514 # open pack, and thus expect pack-objects to succeed
515 P=$(git pack-objects --stdin-packs=follow $packdir/pack <in) &&
516
517 objects_in_packs $B $C >expect &&
518 objects_in_packs $P >actual &&
519 test_cmp expect actual
520 )
521 '
522
523 test_done