apply: don't leak fd on fdopen() error
[git.git] / t / t6020-bundle-misc.sh
blob3e6bcbf30cdaf0e623ae73858a4f98838e03255c
1 #!/bin/sh
3 # Copyright (c) 2021 Jiang Xin
6 test_description='Test git-bundle'
8 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
9 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
11 . ./test-lib.sh
12 . "$TEST_DIRECTORY"/lib-bundle.sh
13 . "$TEST_DIRECTORY"/lib-terminal.sh
15 for cmd in create verify list-heads unbundle
17 test_expect_success "usage: git bundle $cmd needs an argument" '
18 test_expect_code 129 git bundle $cmd
20 done
22 # Create a commit or tag and set the variable with the object ID.
23 test_commit_setvar () {
24 notick=
25 signoff=
26 indir=
27 merge=
28 tag=
29 var=
31 while test $# != 0
33 case "$1" in
34 --merge)
35 merge=t
37 --tag)
38 tag=t
40 --notick)
41 notick=t
43 --signoff)
44 signoff="$1"
46 -C)
47 shift
48 indir="$1"
50 -*)
51 echo >&2 "error: unknown option $1"
52 return 1
55 break
57 esac
58 shift
59 done
60 if test $# -lt 2
61 then
62 echo >&2 "error: test_commit_setvar must have at least 2 arguments"
63 return 1
65 var=$1
66 shift
67 indir=${indir:+"$indir"/}
68 if test -z "$notick"
69 then
70 test_tick
71 fi &&
72 if test -n "$merge"
73 then
74 git ${indir:+ -C "$indir"} merge --no-edit --no-ff \
75 ${2:+-m "$2"} "$1" &&
76 oid=$(git ${indir:+ -C "$indir"} rev-parse HEAD)
77 elif test -n "$tag"
78 then
79 git ${indir:+ -C "$indir"} tag -m "$1" "$1" "${2:-HEAD}" &&
80 oid=$(git ${indir:+ -C "$indir"} rev-parse "$1")
81 else
82 file=${2:-"$1.t"} &&
83 echo "${3-$1}" >"$indir$file" &&
84 git ${indir:+ -C "$indir"} add "$file" &&
85 git ${indir:+ -C "$indir"} commit $signoff -m "$1" &&
86 oid=$(git ${indir:+ -C "$indir"} rev-parse HEAD)
87 fi &&
88 eval $var=$oid
91 get_abbrev_oid () {
92 oid=$1 &&
93 suffix=${oid#???????} &&
94 oid=${oid%$suffix} &&
95 if test -n "$oid"
96 then
97 echo "$oid"
98 else
99 echo "undefined-oid"
103 # Format the output of git commands to make a user-friendly and stable
104 # text. We can easily prepare the expect text without having to worry
105 # about future changes of the commit ID.
106 make_user_friendly_and_stable_output () {
107 sed \
108 -e "s/$(get_abbrev_oid $A)[0-9a-f]*/<COMMIT-A>/g" \
109 -e "s/$(get_abbrev_oid $B)[0-9a-f]*/<COMMIT-B>/g" \
110 -e "s/$(get_abbrev_oid $C)[0-9a-f]*/<COMMIT-C>/g" \
111 -e "s/$(get_abbrev_oid $D)[0-9a-f]*/<COMMIT-D>/g" \
112 -e "s/$(get_abbrev_oid $E)[0-9a-f]*/<COMMIT-E>/g" \
113 -e "s/$(get_abbrev_oid $F)[0-9a-f]*/<COMMIT-F>/g" \
114 -e "s/$(get_abbrev_oid $G)[0-9a-f]*/<COMMIT-G>/g" \
115 -e "s/$(get_abbrev_oid $H)[0-9a-f]*/<COMMIT-H>/g" \
116 -e "s/$(get_abbrev_oid $I)[0-9a-f]*/<COMMIT-I>/g" \
117 -e "s/$(get_abbrev_oid $J)[0-9a-f]*/<COMMIT-J>/g" \
118 -e "s/$(get_abbrev_oid $K)[0-9a-f]*/<COMMIT-K>/g" \
119 -e "s/$(get_abbrev_oid $L)[0-9a-f]*/<COMMIT-L>/g" \
120 -e "s/$(get_abbrev_oid $M)[0-9a-f]*/<COMMIT-M>/g" \
121 -e "s/$(get_abbrev_oid $N)[0-9a-f]*/<COMMIT-N>/g" \
122 -e "s/$(get_abbrev_oid $O)[0-9a-f]*/<COMMIT-O>/g" \
123 -e "s/$(get_abbrev_oid $P)[0-9a-f]*/<COMMIT-P>/g" \
124 -e "s/$(get_abbrev_oid $TAG1)[0-9a-f]*/<TAG-1>/g" \
125 -e "s/$(get_abbrev_oid $TAG2)[0-9a-f]*/<TAG-2>/g" \
126 -e "s/$(get_abbrev_oid $TAG3)[0-9a-f]*/<TAG-3>/g"
129 format_and_save_expect () {
130 sed -e 's/Z$//' >expect
133 HASH_MESSAGE="The bundle uses this hash algorithm: $GIT_DEFAULT_HASH"
135 # (C) (D, pull/1/head, topic/1)
136 # o --- o
137 # / \ (L)
138 # / \ o (H, topic/2) (M, tag:v2)
139 # / (F) \ / (N, tag:v3)
140 # / o --------- o (G, pull/2/head) o --- o --- o (release)
141 # / / \ \ / \
142 # o --- o --- o -------- o -- o ------------------ o ------- o --- o (main)
143 # (A) (B) (E, tag:v1) (I) (J) (K) (O) (P)
145 test_expect_success 'setup' '
146 # Try to make a stable fixed width for abbreviated commit ID,
147 # this fixed-width oid will be replaced with "<OID>".
148 git config core.abbrev 7 &&
150 # branch main: commit A & B
151 test_commit_setvar A "Commit A" main.txt &&
152 test_commit_setvar B "Commit B" main.txt &&
154 # branch topic/1: commit C & D, refs/pull/1/head
155 git checkout -b topic/1 &&
156 test_commit_setvar C "Commit C" topic-1.txt &&
157 test_commit_setvar D "Commit D" topic-1.txt &&
158 git update-ref refs/pull/1/head HEAD &&
160 # branch topic/1: commit E, tag v1
161 git checkout main &&
162 test_commit_setvar E "Commit E" main.txt &&
163 test_commit_setvar --tag TAG1 v1 &&
165 # branch topic/2: commit F & G, refs/pull/2/head
166 git checkout -b topic/2 &&
167 test_commit_setvar F "Commit F" topic-2.txt &&
168 test_commit_setvar G "Commit G" topic-2.txt &&
169 git update-ref refs/pull/2/head HEAD &&
170 test_commit_setvar H "Commit H" topic-2.txt &&
172 # branch main: merge commit I & J
173 git checkout main &&
174 test_commit_setvar --merge I topic/1 "Merge commit I" &&
175 test_commit_setvar --merge J refs/pull/2/head "Merge commit J" &&
177 # branch main: commit K
178 git checkout main &&
179 test_commit_setvar K "Commit K" main.txt &&
181 # branch release:
182 git checkout -b release &&
183 test_commit_setvar L "Commit L" release.txt &&
184 test_commit_setvar M "Commit M" release.txt &&
185 test_commit_setvar --tag TAG2 v2 &&
186 test_commit_setvar N "Commit N" release.txt &&
187 test_commit_setvar --tag TAG3 v3 &&
189 # branch main: merge commit O, commit P
190 git checkout main &&
191 test_commit_setvar --merge O tags/v2 "Merge commit O" &&
192 test_commit_setvar P "Commit P" main.txt
195 test_expect_success 'create bundle from special rev: main^!' '
196 git bundle create special-rev.bdl "main^!" &&
198 git bundle list-heads special-rev.bdl |
199 make_user_friendly_and_stable_output >actual &&
200 cat >expect <<-\EOF &&
201 <COMMIT-P> refs/heads/main
203 test_cmp expect actual &&
205 git bundle verify special-rev.bdl |
206 make_user_friendly_and_stable_output >actual &&
207 format_and_save_expect <<-EOF &&
208 The bundle contains this ref:
209 <COMMIT-P> refs/heads/main
210 The bundle requires this ref:
211 <COMMIT-O> Z
212 $HASH_MESSAGE
214 test_cmp expect actual &&
216 test_bundle_object_count special-rev.bdl 3
219 test_expect_success 'create bundle with --max-count option' '
220 git bundle create max-count.bdl --max-count 1 \
221 main \
222 "^release" \
223 refs/tags/v1 \
224 refs/pull/1/head \
225 refs/pull/2/head &&
227 git bundle verify max-count.bdl |
228 make_user_friendly_and_stable_output >actual &&
229 format_and_save_expect <<-EOF &&
230 The bundle contains these 2 refs:
231 <COMMIT-P> refs/heads/main
232 <TAG-1> refs/tags/v1
233 The bundle requires this ref:
234 <COMMIT-O> Z
235 $HASH_MESSAGE
237 test_cmp expect actual &&
239 test_bundle_object_count max-count.bdl 4
242 test_expect_success 'create bundle with --since option' '
243 git log -1 --pretty="%ad" $M >actual &&
244 cat >expect <<-\EOF &&
245 Thu Apr 7 15:26:13 2005 -0700
247 test_cmp expect actual &&
249 git bundle create since.bdl \
250 --since "Thu Apr 7 15:27:00 2005 -0700" \
251 --all &&
253 git bundle verify since.bdl |
254 make_user_friendly_and_stable_output >actual &&
255 format_and_save_expect <<-EOF &&
256 The bundle contains these 5 refs:
257 <COMMIT-P> refs/heads/main
258 <COMMIT-N> refs/heads/release
259 <TAG-2> refs/tags/v2
260 <TAG-3> refs/tags/v3
261 <COMMIT-P> HEAD
262 The bundle requires these 2 refs:
263 <COMMIT-M> Z
264 <COMMIT-K> Z
265 $HASH_MESSAGE
267 test_cmp expect actual &&
269 test_bundle_object_count --thin since.bdl 13
272 test_expect_success 'create bundle 1 - no prerequisites' '
273 # create bundle from args
274 git bundle create 1.bdl topic/1 topic/2 &&
276 # create bundle from stdin
277 cat >input <<-\EOF &&
278 topic/1
279 topic/2
281 git bundle create stdin-1.bdl --stdin <input &&
283 format_and_save_expect <<-EOF &&
284 The bundle contains these 2 refs:
285 <COMMIT-D> refs/heads/topic/1
286 <COMMIT-H> refs/heads/topic/2
287 The bundle records a complete history.
288 $HASH_MESSAGE
291 # verify bundle, which has no prerequisites
292 git bundle verify 1.bdl |
293 make_user_friendly_and_stable_output >actual &&
294 test_cmp expect actual &&
296 git bundle verify stdin-1.bdl |
297 make_user_friendly_and_stable_output >actual &&
298 test_cmp expect actual &&
300 test_bundle_object_count 1.bdl 24 &&
301 test_bundle_object_count stdin-1.bdl 24
304 test_expect_success 'create bundle 2 - has prerequisites' '
305 # create bundle from args
306 git bundle create 2.bdl \
307 --ignore-missing \
308 ^topic/deleted \
309 ^$D \
310 ^topic/2 \
311 release &&
313 # create bundle from stdin
314 # input has a non-exist reference: "topic/deleted"
315 cat >input <<-EOF &&
316 ^topic/deleted
318 ^topic/2
320 git bundle create stdin-2.bdl \
321 --ignore-missing \
322 --stdin \
323 release <input &&
325 format_and_save_expect <<-EOF &&
326 The bundle contains this ref:
327 <COMMIT-N> refs/heads/release
328 The bundle requires these 3 refs:
329 <COMMIT-D> Z
330 <COMMIT-E> Z
331 <COMMIT-G> Z
332 $HASH_MESSAGE
335 git bundle verify 2.bdl |
336 make_user_friendly_and_stable_output >actual &&
337 test_cmp expect actual &&
339 git bundle verify stdin-2.bdl |
340 make_user_friendly_and_stable_output >actual &&
341 test_cmp expect actual &&
343 test_bundle_object_count 2.bdl 16 &&
344 test_bundle_object_count stdin-2.bdl 16
347 test_expect_success 'fail to verify bundle without prerequisites' '
348 git init --bare test1.git &&
350 format_and_save_expect <<-\EOF &&
351 error: Repository lacks these prerequisite commits:
352 error: <COMMIT-D> Z
353 error: <COMMIT-E> Z
354 error: <COMMIT-G> Z
357 test_must_fail git -C test1.git bundle verify ../2.bdl 2>&1 |
358 make_user_friendly_and_stable_output >actual &&
359 test_cmp expect actual &&
361 test_must_fail git -C test1.git bundle verify ../stdin-2.bdl 2>&1 |
362 make_user_friendly_and_stable_output >actual &&
363 test_cmp expect actual
366 test_expect_success 'create bundle 3 - two refs, same object' '
367 # create bundle from args
368 git bundle create --version=3 3.bdl \
369 ^release \
370 ^topic/1 \
371 ^topic/2 \
372 main \
373 HEAD &&
375 # create bundle from stdin
376 cat >input <<-\EOF &&
377 ^release
378 ^topic/1
379 ^topic/2
381 git bundle create --version=3 stdin-3.bdl \
382 --stdin \
383 main HEAD <input &&
385 format_and_save_expect <<-EOF &&
386 The bundle contains these 2 refs:
387 <COMMIT-P> refs/heads/main
388 <COMMIT-P> HEAD
389 The bundle requires these 2 refs:
390 <COMMIT-M> Z
391 <COMMIT-K> Z
392 $HASH_MESSAGE
395 git bundle verify 3.bdl |
396 make_user_friendly_and_stable_output >actual &&
397 test_cmp expect actual &&
399 git bundle verify stdin-3.bdl |
400 make_user_friendly_and_stable_output >actual &&
401 test_cmp expect actual &&
403 test_bundle_object_count 3.bdl 4 &&
404 test_bundle_object_count stdin-3.bdl 4
407 test_expect_success 'create bundle 4 - with tags' '
408 # create bundle from args
409 git bundle create 4.bdl \
410 ^main \
411 ^release \
412 ^topic/1 \
413 ^topic/2 \
414 --all &&
416 # create bundle from stdin
417 cat >input <<-\EOF &&
418 ^main
419 ^release
420 ^topic/1
421 ^topic/2
423 git bundle create stdin-4.bdl \
424 --ignore-missing \
425 --stdin \
426 --all <input &&
428 cat >expect <<-EOF &&
429 The bundle contains these 3 refs:
430 <TAG-1> refs/tags/v1
431 <TAG-2> refs/tags/v2
432 <TAG-3> refs/tags/v3
433 The bundle records a complete history.
434 $HASH_MESSAGE
437 git bundle verify 4.bdl |
438 make_user_friendly_and_stable_output >actual &&
439 test_cmp expect actual &&
441 git bundle verify stdin-4.bdl |
442 make_user_friendly_and_stable_output >actual &&
443 test_cmp expect actual &&
445 test_bundle_object_count 4.bdl 3 &&
446 test_bundle_object_count stdin-4.bdl 3
449 test_expect_success 'clone from bundle' '
450 git clone --mirror 1.bdl mirror.git &&
451 git -C mirror.git show-ref |
452 make_user_friendly_and_stable_output >actual &&
453 cat >expect <<-\EOF &&
454 <COMMIT-D> refs/heads/topic/1
455 <COMMIT-H> refs/heads/topic/2
457 test_cmp expect actual &&
459 git -C mirror.git fetch ../2.bdl "+refs/*:refs/*" &&
460 git -C mirror.git show-ref |
461 make_user_friendly_and_stable_output >actual &&
462 cat >expect <<-\EOF &&
463 <COMMIT-N> refs/heads/release
464 <COMMIT-D> refs/heads/topic/1
465 <COMMIT-H> refs/heads/topic/2
467 test_cmp expect actual &&
469 git -C mirror.git fetch ../3.bdl "+refs/*:refs/*" &&
470 git -C mirror.git show-ref |
471 make_user_friendly_and_stable_output >actual &&
472 cat >expect <<-\EOF &&
473 <COMMIT-P> refs/heads/main
474 <COMMIT-N> refs/heads/release
475 <COMMIT-D> refs/heads/topic/1
476 <COMMIT-H> refs/heads/topic/2
478 test_cmp expect actual &&
480 git -C mirror.git fetch ../4.bdl "+refs/*:refs/*" &&
481 git -C mirror.git show-ref |
482 make_user_friendly_and_stable_output >actual &&
483 cat >expect <<-\EOF &&
484 <COMMIT-P> refs/heads/main
485 <COMMIT-N> refs/heads/release
486 <COMMIT-D> refs/heads/topic/1
487 <COMMIT-H> refs/heads/topic/2
488 <TAG-1> refs/tags/v1
489 <TAG-2> refs/tags/v2
490 <TAG-3> refs/tags/v3
492 test_cmp expect actual
495 test_expect_success 'unfiltered bundle with --objects' '
496 git bundle create all-objects.bdl \
497 --all --objects &&
498 git bundle create all.bdl \
499 --all &&
501 # Compare the headers of these files.
502 sed -n -e "/^$/q" -e "p" all.bdl >expect &&
503 sed -n -e "/^$/q" -e "p" all-objects.bdl >actual &&
504 test_cmp expect actual
507 for filter in "blob:none" "tree:0" "tree:1" "blob:limit=100"
509 test_expect_success "filtered bundle: $filter" '
510 test_when_finished rm -rf .git/objects/pack cloned unbundled &&
511 git bundle create partial.bdl \
512 --all \
513 --filter=$filter &&
515 git bundle verify partial.bdl >unfiltered &&
516 make_user_friendly_and_stable_output <unfiltered >actual &&
518 cat >expect <<-EOF &&
519 The bundle contains these 10 refs:
520 <COMMIT-P> refs/heads/main
521 <COMMIT-N> refs/heads/release
522 <COMMIT-D> refs/heads/topic/1
523 <COMMIT-H> refs/heads/topic/2
524 <COMMIT-D> refs/pull/1/head
525 <COMMIT-G> refs/pull/2/head
526 <TAG-1> refs/tags/v1
527 <TAG-2> refs/tags/v2
528 <TAG-3> refs/tags/v3
529 <COMMIT-P> HEAD
530 The bundle records a complete history.
531 $HASH_MESSAGE
532 The bundle uses this filter: $filter
534 test_cmp expect actual &&
536 test_config uploadpack.allowfilter 1 &&
537 test_config uploadpack.allowanysha1inwant 1 &&
538 git clone --no-local --filter=$filter --bare "file://$(pwd)" cloned &&
540 git init unbundled &&
541 git -C unbundled bundle unbundle ../partial.bdl >ref-list.txt &&
542 ls unbundled/.git/objects/pack/pack-*.promisor >promisor &&
543 test_line_count = 1 promisor &&
545 # Count the same number of reachable objects.
546 reflist=$(git for-each-ref --format="%(objectname)") &&
547 git rev-list --objects --filter=$filter --missing=allow-any \
548 $reflist >expect &&
549 for repo in cloned unbundled
551 git -C $repo rev-list --objects --missing=allow-any \
552 $reflist >actual &&
553 test_cmp expect actual || return 1
554 done
556 done
558 # NEEDSWORK: 'git clone --bare' should be able to clone from a filtered
559 # bundle, but that requires a change to promisor/filter config options.
560 # For now, we fail gracefully with a helpful error. This behavior can be
561 # changed in the future to succeed as much as possible.
562 test_expect_success 'cloning from filtered bundle has useful error' '
563 git bundle create partial.bdl \
564 --all \
565 --filter=blob:none &&
566 test_must_fail git clone --bare partial.bdl partial 2>err &&
567 grep "cannot clone from filtered bundle" err
570 test_expect_success 'verify catches unreachable, broken prerequisites' '
571 test_when_finished rm -rf clone-from clone-to &&
572 git init clone-from &&
574 cd clone-from &&
575 git checkout -b base &&
576 test_commit A &&
577 git checkout -b tip &&
578 git commit --allow-empty -m "will drop by shallow" &&
579 git commit --allow-empty -m "will keep by shallow" &&
580 git commit --allow-empty -m "for bundle, not clone" &&
581 git bundle create tip.bundle tip~1..tip &&
582 git reset --hard HEAD~1 &&
583 git checkout base
584 ) &&
585 BAD_OID=$(git -C clone-from rev-parse tip~1) &&
586 TIP_OID=$(git -C clone-from rev-parse tip) &&
587 git clone --depth=1 --no-single-branch \
588 "file://$(pwd)/clone-from" clone-to &&
590 cd clone-to &&
592 # Set up broken history by removing shallow markers
593 git update-ref -d refs/remotes/origin/tip &&
594 rm .git/shallow &&
596 # Verify should fail
597 test_must_fail git bundle verify \
598 ../clone-from/tip.bundle 2>err &&
599 grep "some prerequisite commits .* are not connected" err &&
600 test_line_count = 1 err &&
602 # Unbundling should fail
603 test_must_fail git bundle unbundle \
604 ../clone-from/tip.bundle 2>err &&
605 grep "some prerequisite commits .* are not connected" err &&
606 test_line_count = 1 err
610 test_expect_success 'bundle progress includes write phase' '
611 GIT_PROGRESS_DELAY=0 \
612 git bundle create --progress out.bundle --all 2>err &&
613 grep 'Writing' err
616 test_expect_success TTY 'create --quiet disables all bundle progress' '
617 test_terminal env GIT_PROGRESS_DELAY=0 \
618 git bundle create --quiet out.bundle --all 2>err &&
619 test_must_be_empty err
622 test_expect_success 'bundle progress with --no-quiet' '
623 GIT_PROGRESS_DELAY=0 \
624 git bundle create --no-quiet out.bundle --all 2>err &&
625 grep "%" err
628 test_expect_success 'read bundle over stdin' '
629 git bundle create some.bundle HEAD &&
631 git bundle verify - <some.bundle 2>err &&
632 grep "<stdin> is okay" err &&
634 git bundle list-heads some.bundle >expect &&
635 git bundle list-heads - <some.bundle >actual &&
636 test_cmp expect actual &&
638 git bundle unbundle some.bundle >expect &&
639 git bundle unbundle - <some.bundle >actual &&
640 test_cmp expect actual
643 test_expect_success 'send a bundle to standard output' '
644 git bundle create - --all HEAD >bundle-one &&
645 mkdir -p down &&
646 git -C down bundle create - --all HEAD >bundle-two &&
647 git bundle verify bundle-one &&
648 git bundle verify bundle-two &&
649 git ls-remote bundle-one >expect &&
650 git ls-remote bundle-two >actual &&
651 test_cmp expect actual
654 test_done