commit-graph: when incompatible with graphs, indicate why
[git] / t / t5510-fetch.sh
1 #!/bin/sh
2 # Copyright (c) 2006, Junio C Hamano.
3
4 test_description='Per branch config variables affects "git fetch".
5
6 '
7
8 . ./test-lib.sh
9
10 D=$(pwd)
11
12 test_bundle_object_count () {
13         git verify-pack -v "$1" >verify.out &&
14         test "$2" = $(grep "^$OID_REGEX " verify.out | wc -l)
15 }
16
17 convert_bundle_to_pack () {
18         while read x && test -n "$x"
19         do
20                 :;
21         done
22         cat
23 }
24
25 test_expect_success setup '
26         echo >file original &&
27         git add file &&
28         git commit -a -m original &&
29         git branch -M main
30 '
31
32 test_expect_success "clone and setup child repos" '
33         git clone . one &&
34         (
35                 cd one &&
36                 echo >file updated by one &&
37                 git commit -a -m "updated by one"
38         ) &&
39         git clone . two &&
40         (
41                 cd two &&
42                 git config branch.main.remote one &&
43                 git config remote.one.url ../one/.git/ &&
44                 git config remote.one.fetch refs/heads/main:refs/heads/one
45         ) &&
46         git clone . three &&
47         (
48                 cd three &&
49                 git config branch.main.remote two &&
50                 git config branch.main.merge refs/heads/one &&
51                 mkdir -p .git/remotes &&
52                 {
53                         echo "URL: ../two/.git/"
54                         echo "Pull: refs/heads/main:refs/heads/two"
55                         echo "Pull: refs/heads/one:refs/heads/one"
56                 } >.git/remotes/two
57         ) &&
58         git clone . bundle &&
59         git clone . seven
60 '
61
62 test_expect_success "fetch test" '
63         cd "$D" &&
64         echo >file updated by origin &&
65         git commit -a -m "updated by origin" &&
66         cd two &&
67         git fetch &&
68         git rev-parse --verify refs/heads/one &&
69         mine=$(git rev-parse refs/heads/one) &&
70         his=$(cd ../one && git rev-parse refs/heads/main) &&
71         test "z$mine" = "z$his"
72 '
73
74 test_expect_success "fetch test for-merge" '
75         cd "$D" &&
76         cd three &&
77         git fetch &&
78         git rev-parse --verify refs/heads/two &&
79         git rev-parse --verify refs/heads/one &&
80         main_in_two=$(cd ../two && git rev-parse main) &&
81         one_in_two=$(cd ../two && git rev-parse one) &&
82         {
83                 echo "$one_in_two       "
84                 echo "$main_in_two      not-for-merge"
85         } >expected &&
86         cut -f -2 .git/FETCH_HEAD >actual &&
87         test_cmp expected actual'
88
89 test_expect_success 'fetch --prune on its own works as expected' '
90         cd "$D" &&
91         git clone . prune &&
92         cd prune &&
93         git update-ref refs/remotes/origin/extrabranch main &&
94
95         git fetch --prune origin &&
96         test_must_fail git rev-parse origin/extrabranch
97 '
98
99 test_expect_success 'fetch --prune with a branch name keeps branches' '
100         cd "$D" &&
101         git clone . prune-branch &&
102         cd prune-branch &&
103         git update-ref refs/remotes/origin/extrabranch main &&
104
105         git fetch --prune origin main &&
106         git rev-parse origin/extrabranch
107 '
108
109 test_expect_success 'fetch --prune with a namespace keeps other namespaces' '
110         cd "$D" &&
111         git clone . prune-namespace &&
112         cd prune-namespace &&
113
114         git fetch --prune origin refs/heads/a/*:refs/remotes/origin/a/* &&
115         git rev-parse origin/main
116 '
117
118 test_expect_success 'fetch --prune handles overlapping refspecs' '
119         cd "$D" &&
120         git update-ref refs/pull/42/head main &&
121         git clone . prune-overlapping &&
122         cd prune-overlapping &&
123         git config --add remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* &&
124
125         git fetch --prune origin &&
126         git rev-parse origin/main &&
127         git rev-parse origin/pr/42 &&
128
129         git config --unset-all remote.origin.fetch &&
130         git config remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* &&
131         git config --add remote.origin.fetch refs/heads/*:refs/remotes/origin/* &&
132
133         git fetch --prune origin &&
134         git rev-parse origin/main &&
135         git rev-parse origin/pr/42
136 '
137
138 test_expect_success 'fetch --prune --tags prunes branches but not tags' '
139         cd "$D" &&
140         git clone . prune-tags &&
141         cd prune-tags &&
142         git tag sometag main &&
143         # Create what looks like a remote-tracking branch from an earlier
144         # fetch that has since been deleted from the remote:
145         git update-ref refs/remotes/origin/fake-remote main &&
146
147         git fetch --prune --tags origin &&
148         git rev-parse origin/main &&
149         test_must_fail git rev-parse origin/fake-remote &&
150         git rev-parse sometag
151 '
152
153 test_expect_success 'fetch --prune --tags with branch does not prune other things' '
154         cd "$D" &&
155         git clone . prune-tags-branch &&
156         cd prune-tags-branch &&
157         git tag sometag main &&
158         git update-ref refs/remotes/origin/extrabranch main &&
159
160         git fetch --prune --tags origin main &&
161         git rev-parse origin/extrabranch &&
162         git rev-parse sometag
163 '
164
165 test_expect_success 'fetch --prune --tags with refspec prunes based on refspec' '
166         cd "$D" &&
167         git clone . prune-tags-refspec &&
168         cd prune-tags-refspec &&
169         git tag sometag main &&
170         git update-ref refs/remotes/origin/foo/otherbranch main &&
171         git update-ref refs/remotes/origin/extrabranch main &&
172
173         git fetch --prune --tags origin refs/heads/foo/*:refs/remotes/origin/foo/* &&
174         test_must_fail git rev-parse refs/remotes/origin/foo/otherbranch &&
175         git rev-parse origin/extrabranch &&
176         git rev-parse sometag
177 '
178
179 test_expect_success '--refmap="" ignores configured refspec' '
180         cd "$TRASH_DIRECTORY" &&
181         git clone "$D" remote-refs &&
182         git -C remote-refs rev-parse remotes/origin/main >old &&
183         git -C remote-refs update-ref refs/remotes/origin/main main~1 &&
184         git -C remote-refs rev-parse remotes/origin/main >new &&
185         git -C remote-refs fetch --refmap= origin "+refs/heads/*:refs/hidden/origin/*" &&
186         git -C remote-refs rev-parse remotes/origin/main >actual &&
187         test_cmp new actual &&
188         git -C remote-refs fetch origin &&
189         git -C remote-refs rev-parse remotes/origin/main >actual &&
190         test_cmp old actual
191 '
192
193 test_expect_success '--refmap="" and --prune' '
194         git -C remote-refs update-ref refs/remotes/origin/foo/otherbranch main &&
195         git -C remote-refs update-ref refs/hidden/foo/otherbranch main &&
196         git -C remote-refs fetch --prune --refmap="" origin +refs/heads/*:refs/hidden/* &&
197         git -C remote-refs rev-parse remotes/origin/foo/otherbranch &&
198         test_must_fail git -C remote-refs rev-parse refs/hidden/foo/otherbranch &&
199         git -C remote-refs fetch --prune origin &&
200         test_must_fail git -C remote-refs rev-parse remotes/origin/foo/otherbranch
201 '
202
203 test_expect_success 'fetch tags when there is no tags' '
204
205     cd "$D" &&
206
207     mkdir notags &&
208     cd notags &&
209     git init &&
210
211     git fetch -t ..
212
213 '
214
215 test_expect_success 'fetch following tags' '
216
217         cd "$D" &&
218         git tag -a -m "annotated" anno HEAD &&
219         git tag light HEAD &&
220
221         mkdir four &&
222         cd four &&
223         git init &&
224
225         git fetch .. :track &&
226         git show-ref --verify refs/tags/anno &&
227         git show-ref --verify refs/tags/light
228
229 '
230
231 test_expect_success 'fetch uses remote ref names to describe new refs' '
232         cd "$D" &&
233         git init descriptive &&
234         (
235                 cd descriptive &&
236                 git config remote.o.url .. &&
237                 git config remote.o.fetch "refs/heads/*:refs/crazyheads/*" &&
238                 git config --add remote.o.fetch "refs/others/*:refs/heads/*" &&
239                 git fetch o
240         ) &&
241         git tag -a -m "Descriptive tag" descriptive-tag &&
242         git branch descriptive-branch &&
243         git checkout descriptive-branch &&
244         echo "Nuts" >crazy &&
245         git add crazy &&
246         git commit -a -m "descriptive commit" &&
247         git update-ref refs/others/crazy HEAD &&
248         (
249                 cd descriptive &&
250                 git fetch o 2>actual &&
251                 test_i18ngrep "new branch.* -> refs/crazyheads/descriptive-branch$" actual &&
252                 test_i18ngrep "new tag.* -> descriptive-tag$" actual &&
253                 test_i18ngrep "new ref.* -> crazy$" actual
254         ) &&
255         git checkout main
256 '
257
258 test_expect_success 'fetch must not resolve short tag name' '
259
260         cd "$D" &&
261
262         mkdir five &&
263         cd five &&
264         git init &&
265
266         test_must_fail git fetch .. anno:five
267
268 '
269
270 test_expect_success 'fetch can now resolve short remote name' '
271
272         cd "$D" &&
273         git update-ref refs/remotes/six/HEAD HEAD &&
274
275         mkdir six &&
276         cd six &&
277         git init &&
278
279         git fetch .. six:six
280 '
281
282 test_expect_success 'create bundle 1' '
283         cd "$D" &&
284         echo >file updated again by origin &&
285         git commit -a -m "tip" &&
286         git bundle create --version=3 bundle1 main^..main
287 '
288
289 test_expect_success 'header of bundle looks right' '
290         cat >expect <<-EOF &&
291         # v3 git bundle
292         @object-format=$(test_oid algo)
293         -OID updated by origin
294         OID refs/heads/main
295
296         EOF
297         sed -e "s/$OID_REGEX/OID/g" -e "5q" "$D"/bundle1 >actual &&
298         test_cmp expect actual
299 '
300
301 test_expect_success 'create bundle 2' '
302         cd "$D" &&
303         git bundle create bundle2 main~2..main
304 '
305
306 test_expect_success 'unbundle 1' '
307         cd "$D/bundle" &&
308         git checkout -b some-branch &&
309         test_must_fail git fetch "$D/bundle1" main:main
310 '
311
312
313 test_expect_success 'bundle 1 has only 3 files ' '
314         cd "$D" &&
315         convert_bundle_to_pack <bundle1 >bundle.pack &&
316         git index-pack bundle.pack &&
317         test_bundle_object_count bundle.pack 3
318 '
319
320 test_expect_success 'unbundle 2' '
321         cd "$D/bundle" &&
322         git fetch ../bundle2 main:main &&
323         test "tip" = "$(git log -1 --pretty=oneline main | cut -d" " -f2)"
324 '
325
326 test_expect_success 'bundle does not prerequisite objects' '
327         cd "$D" &&
328         touch file2 &&
329         git add file2 &&
330         git commit -m add.file2 file2 &&
331         git bundle create bundle3 -1 HEAD &&
332         convert_bundle_to_pack <bundle3 >bundle.pack &&
333         git index-pack bundle.pack &&
334         test_bundle_object_count bundle.pack 3
335 '
336
337 test_expect_success 'bundle should be able to create a full history' '
338
339         cd "$D" &&
340         git tag -a -m "1.0" v1.0 main &&
341         git bundle create bundle4 v1.0
342
343 '
344
345 test_expect_success 'fetch with a non-applying branch.<name>.merge' '
346         git config branch.main.remote yeti &&
347         git config branch.main.merge refs/heads/bigfoot &&
348         git config remote.blub.url one &&
349         git config remote.blub.fetch "refs/heads/*:refs/remotes/one/*" &&
350         git fetch blub
351 '
352
353 # URL supplied to fetch does not match the url of the configured branch's remote
354 test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [1]' '
355         one_head=$(cd one && git rev-parse HEAD) &&
356         this_head=$(git rev-parse HEAD) &&
357         git update-ref -d FETCH_HEAD &&
358         git fetch one &&
359         test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
360         test $this_head = "$(git rev-parse --verify HEAD)"
361 '
362
363 # URL supplied to fetch matches the url of the configured branch's remote and
364 # the merge spec matches the branch the remote HEAD points to
365 test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [2]' '
366         one_ref=$(cd one && git symbolic-ref HEAD) &&
367         git config branch.main.remote blub &&
368         git config branch.main.merge "$one_ref" &&
369         git update-ref -d FETCH_HEAD &&
370         git fetch one &&
371         test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
372         test $this_head = "$(git rev-parse --verify HEAD)"
373 '
374
375 # URL supplied to fetch matches the url of the configured branch's remote, but
376 # the merge spec does not match the branch the remote HEAD points to
377 test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [3]' '
378         git config branch.main.merge "${one_ref}_not" &&
379         git update-ref -d FETCH_HEAD &&
380         git fetch one &&
381         test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
382         test $this_head = "$(git rev-parse --verify HEAD)"
383 '
384
385 # the strange name is: a\!'b
386 test_expect_success 'quoting of a strangely named repo' '
387         test_must_fail git fetch "a\\!'\''b" > result 2>&1 &&
388         grep "fatal: '\''a\\\\!'\''b'\''" result
389 '
390
391 test_expect_success 'bundle should record HEAD correctly' '
392
393         cd "$D" &&
394         git bundle create bundle5 HEAD main &&
395         git bundle list-heads bundle5 >actual &&
396         for h in HEAD refs/heads/main
397         do
398                 echo "$(git rev-parse --verify $h) $h"
399         done >expect &&
400         test_cmp expect actual
401
402 '
403
404 test_expect_success 'mark initial state of origin/main' '
405         (
406                 cd three &&
407                 git tag base-origin-main refs/remotes/origin/main
408         )
409 '
410
411 test_expect_success 'explicit fetch should update tracking' '
412
413         cd "$D" &&
414         git branch -f side &&
415         (
416                 cd three &&
417                 git update-ref refs/remotes/origin/main base-origin-main &&
418                 o=$(git rev-parse --verify refs/remotes/origin/main) &&
419                 git fetch origin main &&
420                 n=$(git rev-parse --verify refs/remotes/origin/main) &&
421                 test "$o" != "$n" &&
422                 test_must_fail git rev-parse --verify refs/remotes/origin/side
423         )
424 '
425
426 test_expect_success 'explicit pull should update tracking' '
427
428         cd "$D" &&
429         git branch -f side &&
430         (
431                 cd three &&
432                 git update-ref refs/remotes/origin/main base-origin-main &&
433                 o=$(git rev-parse --verify refs/remotes/origin/main) &&
434                 git pull origin main &&
435                 n=$(git rev-parse --verify refs/remotes/origin/main) &&
436                 test "$o" != "$n" &&
437                 test_must_fail git rev-parse --verify refs/remotes/origin/side
438         )
439 '
440
441 test_expect_success 'explicit --refmap is allowed only with command-line refspec' '
442         cd "$D" &&
443         (
444                 cd three &&
445                 test_must_fail git fetch --refmap="*:refs/remotes/none/*"
446         )
447 '
448
449 test_expect_success 'explicit --refmap option overrides remote.*.fetch' '
450         cd "$D" &&
451         git branch -f side &&
452         (
453                 cd three &&
454                 git update-ref refs/remotes/origin/main base-origin-main &&
455                 o=$(git rev-parse --verify refs/remotes/origin/main) &&
456                 git fetch --refmap="refs/heads/*:refs/remotes/other/*" origin main &&
457                 n=$(git rev-parse --verify refs/remotes/origin/main) &&
458                 test "$o" = "$n" &&
459                 test_must_fail git rev-parse --verify refs/remotes/origin/side &&
460                 git rev-parse --verify refs/remotes/other/main
461         )
462 '
463
464 test_expect_success 'explicitly empty --refmap option disables remote.*.fetch' '
465         cd "$D" &&
466         git branch -f side &&
467         (
468                 cd three &&
469                 git update-ref refs/remotes/origin/main base-origin-main &&
470                 o=$(git rev-parse --verify refs/remotes/origin/main) &&
471                 git fetch --refmap="" origin main &&
472                 n=$(git rev-parse --verify refs/remotes/origin/main) &&
473                 test "$o" = "$n" &&
474                 test_must_fail git rev-parse --verify refs/remotes/origin/side
475         )
476 '
477
478 test_expect_success 'configured fetch updates tracking' '
479
480         cd "$D" &&
481         git branch -f side &&
482         (
483                 cd three &&
484                 git update-ref refs/remotes/origin/main base-origin-main &&
485                 o=$(git rev-parse --verify refs/remotes/origin/main) &&
486                 git fetch origin &&
487                 n=$(git rev-parse --verify refs/remotes/origin/main) &&
488                 test "$o" != "$n" &&
489                 git rev-parse --verify refs/remotes/origin/side
490         )
491 '
492
493 test_expect_success 'non-matching refspecs do not confuse tracking update' '
494         cd "$D" &&
495         git update-ref refs/odd/location HEAD &&
496         (
497                 cd three &&
498                 git update-ref refs/remotes/origin/main base-origin-main &&
499                 git config --add remote.origin.fetch \
500                         refs/odd/location:refs/remotes/origin/odd &&
501                 o=$(git rev-parse --verify refs/remotes/origin/main) &&
502                 git fetch origin main &&
503                 n=$(git rev-parse --verify refs/remotes/origin/main) &&
504                 test "$o" != "$n" &&
505                 test_must_fail git rev-parse --verify refs/remotes/origin/odd
506         )
507 '
508
509 test_expect_success 'pushing nonexistent branch by mistake should not segv' '
510
511         cd "$D" &&
512         test_must_fail git push seven no:no
513
514 '
515
516 test_expect_success 'auto tag following fetches minimum' '
517
518         cd "$D" &&
519         git clone .git follow &&
520         git checkout HEAD^0 &&
521         (
522                 for i in 1 2 3 4 5 6 7
523                 do
524                         echo $i >>file &&
525                         git commit -m $i -a &&
526                         git tag -a -m $i excess-$i || exit 1
527                 done
528         ) &&
529         git checkout main &&
530         (
531                 cd follow &&
532                 git fetch
533         )
534 '
535
536 test_expect_success 'refuse to fetch into the current branch' '
537
538         test_must_fail git fetch . side:main
539
540 '
541
542 test_expect_success 'fetch into the current branch with --update-head-ok' '
543
544         git fetch --update-head-ok . side:main
545
546 '
547
548 test_expect_success 'fetch --dry-run does not touch FETCH_HEAD, but still prints what would be written' '
549         rm -f .git/FETCH_HEAD err &&
550         git fetch --dry-run . 2>err &&
551         ! test -f .git/FETCH_HEAD &&
552         grep FETCH_HEAD err
553 '
554
555 test_expect_success '--no-write-fetch-head does not touch FETCH_HEAD, and does not print what would be written' '
556         rm -f .git/FETCH_HEAD err &&
557         git fetch --no-write-fetch-head . 2>err &&
558         ! test -f .git/FETCH_HEAD &&
559         ! grep FETCH_HEAD err
560 '
561
562 test_expect_success '--write-fetch-head gets defeated by --dry-run' '
563         rm -f .git/FETCH_HEAD &&
564         git fetch --dry-run --write-fetch-head . &&
565         ! test -f .git/FETCH_HEAD
566 '
567
568 test_expect_success "should be able to fetch with duplicate refspecs" '
569         mkdir dups &&
570         (
571                 cd dups &&
572                 git init &&
573                 git config branch.main.remote three &&
574                 git config remote.three.url ../three/.git &&
575                 git config remote.three.fetch +refs/heads/*:refs/remotes/origin/* &&
576                 git config --add remote.three.fetch +refs/heads/*:refs/remotes/origin/* &&
577                 git fetch three
578         )
579 '
580
581 test_expect_success 'LHS of refspec follows ref disambiguation rules' '
582         mkdir lhs-ambiguous &&
583         (
584                 cd lhs-ambiguous &&
585                 git init server &&
586                 test_commit -C server unwanted &&
587                 test_commit -C server wanted &&
588
589                 git init client &&
590
591                 # Check a name coming after "refs" alphabetically ...
592                 git -C server update-ref refs/heads/s wanted &&
593                 git -C server update-ref refs/heads/refs/heads/s unwanted &&
594                 git -C client fetch ../server +refs/heads/s:refs/heads/checkthis &&
595                 git -C server rev-parse wanted >expect &&
596                 git -C client rev-parse checkthis >actual &&
597                 test_cmp expect actual &&
598
599                 # ... and one before.
600                 git -C server update-ref refs/heads/q wanted &&
601                 git -C server update-ref refs/heads/refs/heads/q unwanted &&
602                 git -C client fetch ../server +refs/heads/q:refs/heads/checkthis &&
603                 git -C server rev-parse wanted >expect &&
604                 git -C client rev-parse checkthis >actual &&
605                 test_cmp expect actual &&
606
607                 # Tags are preferred over branches like refs/{heads,tags}/*
608                 git -C server update-ref refs/tags/t wanted &&
609                 git -C server update-ref refs/heads/t unwanted &&
610                 git -C client fetch ../server +t:refs/heads/checkthis &&
611                 git -C server rev-parse wanted >expect &&
612                 git -C client rev-parse checkthis >actual
613         )
614 '
615
616 test_expect_success 'fetch.writeCommitGraph' '
617         git clone three write &&
618         (
619                 cd three &&
620                 test_commit new
621         ) &&
622         (
623                 cd write &&
624                 git -c fetch.writeCommitGraph fetch origin &&
625                 test_path_is_file .git/objects/info/commit-graphs/commit-graph-chain
626         )
627 '
628
629 test_expect_success 'fetch.writeCommitGraph with submodules' '
630         git clone dups super &&
631         (
632                 cd super &&
633                 git submodule add "file://$TRASH_DIRECTORY/three" &&
634                 git commit -m "add submodule"
635         ) &&
636         git clone "super" super-clone &&
637         (
638                 cd super-clone &&
639                 rm -rf .git/objects/info &&
640                 git -c fetch.writeCommitGraph=true fetch origin &&
641                 test_path_is_file .git/objects/info/commit-graphs/commit-graph-chain
642         )
643 '
644
645 # configured prune tests
646
647 set_config_tristate () {
648         # var=$1 val=$2
649         case "$2" in
650         unset)
651                 test_unconfig "$1"
652                 ;;
653         *)
654                 git config "$1" "$2"
655                 key=$(echo $1 | sed -e 's/^remote\.origin/fetch/')
656                 git_fetch_c="$git_fetch_c -c $key=$2"
657                 ;;
658         esac
659 }
660
661 test_configured_prune () {
662         test_configured_prune_type "$@" "name"
663         test_configured_prune_type "$@" "link"
664 }
665
666 test_configured_prune_type () {
667         fetch_prune=$1
668         remote_origin_prune=$2
669         fetch_prune_tags=$3
670         remote_origin_prune_tags=$4
671         expected_branch=$5
672         expected_tag=$6
673         cmdline=$7
674         mode=$8
675
676         if test -z "$cmdline_setup"
677         then
678                 test_expect_success 'setup cmdline_setup variable for subsequent test' '
679                         remote_url="file://$(git -C one config remote.origin.url)" &&
680                         remote_fetch="$(git -C one config remote.origin.fetch)" &&
681                         cmdline_setup="\"$remote_url\" \"$remote_fetch\""
682                 '
683         fi
684
685         if test "$mode" = 'link'
686         then
687                 new_cmdline=""
688
689                 if test "$cmdline" = ""
690                 then
691                         new_cmdline=$cmdline_setup
692                 else
693                         new_cmdline=$(printf "%s" "$cmdline" | perl -pe 's[origin(?!/)]["'"$remote_url"'"]g')
694                 fi
695
696                 if test "$fetch_prune_tags" = 'true' ||
697                    test "$remote_origin_prune_tags" = 'true'
698                 then
699                         if ! printf '%s' "$cmdline\n" | grep -q refs/remotes/origin/
700                         then
701                                 new_cmdline="$new_cmdline refs/tags/*:refs/tags/*"
702                         fi
703                 fi
704
705                 cmdline="$new_cmdline"
706         fi
707
708         test_expect_success "$mode prune fetch.prune=$1 remote.origin.prune=$2 fetch.pruneTags=$3 remote.origin.pruneTags=$4${7:+ $7}; branch:$5 tag:$6" '
709                 # make sure a newbranch is there in . and also in one
710                 git branch -f newbranch &&
711                 git tag -f newtag &&
712                 (
713                         cd one &&
714                         test_unconfig fetch.prune &&
715                         test_unconfig fetch.pruneTags &&
716                         test_unconfig remote.origin.prune &&
717                         test_unconfig remote.origin.pruneTags &&
718                         git fetch '"$cmdline_setup"' &&
719                         git rev-parse --verify refs/remotes/origin/newbranch &&
720                         git rev-parse --verify refs/tags/newtag
721                 ) &&
722
723                 # now remove them
724                 git branch -d newbranch &&
725                 git tag -d newtag &&
726
727                 # then test
728                 (
729                         cd one &&
730                         git_fetch_c="" &&
731                         set_config_tristate fetch.prune $fetch_prune &&
732                         set_config_tristate fetch.pruneTags $fetch_prune_tags &&
733                         set_config_tristate remote.origin.prune $remote_origin_prune &&
734                         set_config_tristate remote.origin.pruneTags $remote_origin_prune_tags &&
735
736                         if test "$mode" != "link"
737                         then
738                                 git_fetch_c=""
739                         fi &&
740                         git$git_fetch_c fetch '"$cmdline"' &&
741                         case "$expected_branch" in
742                         pruned)
743                                 test_must_fail git rev-parse --verify refs/remotes/origin/newbranch
744                                 ;;
745                         kept)
746                                 git rev-parse --verify refs/remotes/origin/newbranch
747                                 ;;
748                         esac &&
749                         case "$expected_tag" in
750                         pruned)
751                                 test_must_fail git rev-parse --verify refs/tags/newtag
752                                 ;;
753                         kept)
754                                 git rev-parse --verify refs/tags/newtag
755                                 ;;
756                         esac
757                 )
758         '
759 }
760
761 # $1 config: fetch.prune
762 # $2 config: remote.<name>.prune
763 # $3 config: fetch.pruneTags
764 # $4 config: remote.<name>.pruneTags
765 # $5 expect: branch to be pruned?
766 # $6 expect: tag to be pruned?
767 # $7 git-fetch $cmdline:
768 #
769 #                     $1    $2    $3    $4    $5     $6     $7
770 test_configured_prune unset unset unset unset kept   kept   ""
771 test_configured_prune unset unset unset unset kept   kept   "--no-prune"
772 test_configured_prune unset unset unset unset pruned kept   "--prune"
773 test_configured_prune unset unset unset unset kept   pruned \
774         "--prune origin refs/tags/*:refs/tags/*"
775 test_configured_prune unset unset unset unset pruned pruned \
776         "--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*"
777
778 test_configured_prune false unset unset unset kept   kept   ""
779 test_configured_prune false unset unset unset kept   kept   "--no-prune"
780 test_configured_prune false unset unset unset pruned kept   "--prune"
781
782 test_configured_prune true  unset unset unset pruned kept   ""
783 test_configured_prune true  unset unset unset pruned kept   "--prune"
784 test_configured_prune true  unset unset unset kept   kept   "--no-prune"
785
786 test_configured_prune unset false unset unset kept   kept   ""
787 test_configured_prune unset false unset unset kept   kept   "--no-prune"
788 test_configured_prune unset false unset unset pruned kept   "--prune"
789
790 test_configured_prune false false unset unset kept   kept   ""
791 test_configured_prune false false unset unset kept   kept   "--no-prune"
792 test_configured_prune false false unset unset pruned kept   "--prune"
793 test_configured_prune false false unset unset kept   pruned \
794         "--prune origin refs/tags/*:refs/tags/*"
795 test_configured_prune false false unset unset pruned pruned \
796         "--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*"
797
798 test_configured_prune true  false unset unset kept   kept   ""
799 test_configured_prune true  false unset unset pruned kept   "--prune"
800 test_configured_prune true  false unset unset kept   kept   "--no-prune"
801
802 test_configured_prune unset true  unset unset pruned kept   ""
803 test_configured_prune unset true  unset unset kept   kept   "--no-prune"
804 test_configured_prune unset true  unset unset pruned kept   "--prune"
805
806 test_configured_prune false true  unset unset pruned kept   ""
807 test_configured_prune false true  unset unset kept   kept   "--no-prune"
808 test_configured_prune false true  unset unset pruned kept   "--prune"
809
810 test_configured_prune true  true  unset unset pruned kept   ""
811 test_configured_prune true  true  unset unset pruned kept   "--prune"
812 test_configured_prune true  true  unset unset kept   kept   "--no-prune"
813 test_configured_prune true  true  unset unset kept   pruned \
814         "--prune origin refs/tags/*:refs/tags/*"
815 test_configured_prune true  true  unset unset pruned pruned \
816         "--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*"
817
818 # --prune-tags on its own does nothing, needs --prune as well, same
819 # for fetch.pruneTags without fetch.prune
820 test_configured_prune unset unset unset unset kept kept     "--prune-tags"
821 test_configured_prune unset unset true unset  kept kept     ""
822 test_configured_prune unset unset unset true  kept kept     ""
823
824 # These will prune the tags
825 test_configured_prune unset unset unset unset pruned pruned "--prune --prune-tags"
826 test_configured_prune true  unset true  unset pruned pruned ""
827 test_configured_prune unset true  unset true  pruned pruned ""
828
829 # remote.<name>.pruneTags overrides fetch.pruneTags, just like
830 # remote.<name>.prune overrides fetch.prune if set.
831 test_configured_prune true  unset true unset pruned pruned  ""
832 test_configured_prune false true  false true  pruned pruned ""
833 test_configured_prune true  false true  false kept   kept   ""
834
835 # When --prune-tags is supplied it's ignored if an explicit refspec is
836 # given, same for the configuration options.
837 test_configured_prune unset unset unset unset pruned kept \
838         "--prune --prune-tags origin +refs/heads/*:refs/remotes/origin/*"
839 test_configured_prune unset unset true  unset pruned kept \
840         "--prune origin +refs/heads/*:refs/remotes/origin/*"
841 test_configured_prune unset unset unset true pruned  kept \
842         "--prune origin +refs/heads/*:refs/remotes/origin/*"
843
844 # Pruning that also takes place if a file:// url replaces a named
845 # remote. However, because there's no implicit
846 # +refs/heads/*:refs/remotes/origin/* refspec and supplying it on the
847 # command-line negates --prune-tags, the branches will not be pruned.
848 test_configured_prune_type unset unset unset unset kept   kept   "origin --prune-tags" "name"
849 test_configured_prune_type unset unset unset unset kept   kept   "origin --prune-tags" "link"
850 test_configured_prune_type unset unset unset unset pruned pruned "origin --prune --prune-tags" "name"
851 test_configured_prune_type unset unset unset unset kept   pruned "origin --prune --prune-tags" "link"
852 test_configured_prune_type unset unset unset unset pruned pruned "--prune --prune-tags origin" "name"
853 test_configured_prune_type unset unset unset unset kept   pruned "--prune --prune-tags origin" "link"
854 test_configured_prune_type unset unset true  unset pruned pruned "--prune origin" "name"
855 test_configured_prune_type unset unset true  unset kept   pruned "--prune origin" "link"
856 test_configured_prune_type unset unset unset true  pruned pruned "--prune origin" "name"
857 test_configured_prune_type unset unset unset true  kept   pruned "--prune origin" "link"
858 test_configured_prune_type true  unset true  unset pruned pruned "origin" "name"
859 test_configured_prune_type true  unset true  unset kept   pruned "origin" "link"
860 test_configured_prune_type unset  true true  unset pruned pruned "origin" "name"
861 test_configured_prune_type unset  true true  unset kept   pruned "origin" "link"
862 test_configured_prune_type unset  true unset true  pruned pruned "origin" "name"
863 test_configured_prune_type unset  true unset true  kept   pruned "origin" "link"
864
865 # When all remote.origin.fetch settings are deleted a --prune
866 # --prune-tags still implicitly supplies refs/tags/*:refs/tags/* so
867 # tags, but not tracking branches, will be deleted.
868 test_expect_success 'remove remote.origin.fetch "one"' '
869         (
870                 cd one &&
871                 git config --unset-all remote.origin.fetch
872         )
873 '
874 test_configured_prune_type unset unset unset unset kept pruned "origin --prune --prune-tags" "name"
875 test_configured_prune_type unset unset unset unset kept pruned "origin --prune --prune-tags" "link"
876
877 test_expect_success 'all boundary commits are excluded' '
878         test_commit base &&
879         test_commit oneside &&
880         git checkout HEAD^ &&
881         test_commit otherside &&
882         git checkout main &&
883         test_tick &&
884         git merge otherside &&
885         ad=$(git log --no-walk --format=%ad HEAD) &&
886         git bundle create twoside-boundary.bdl main --since="$ad" &&
887         convert_bundle_to_pack <twoside-boundary.bdl >twoside-boundary.pack &&
888         pack=$(git index-pack --fix-thin --stdin <twoside-boundary.pack) &&
889         test_bundle_object_count .git/objects/pack/pack-${pack##pack    }.pack 3
890 '
891
892 test_expect_success 'fetch --prune prints the remotes url' '
893         git branch goodbye &&
894         git clone . only-prunes &&
895         git branch -D goodbye &&
896         (
897                 cd only-prunes &&
898                 git fetch --prune origin 2>&1 | head -n1 >../actual
899         ) &&
900         echo "From ${D}/." >expect &&
901         test_i18ncmp expect actual
902 '
903
904 test_expect_success 'branchname D/F conflict resolved by --prune' '
905         git branch dir/file &&
906         git clone . prune-df-conflict &&
907         git branch -D dir/file &&
908         git branch dir &&
909         (
910                 cd prune-df-conflict &&
911                 git fetch --prune &&
912                 git rev-parse origin/dir >../actual
913         ) &&
914         git rev-parse dir >expect &&
915         test_cmp expect actual
916 '
917
918 test_expect_success 'fetching a one-level ref works' '
919         test_commit extra &&
920         git reset --hard HEAD^ &&
921         git update-ref refs/foo extra &&
922         git init one-level &&
923         (
924                 cd one-level &&
925                 git fetch .. HEAD refs/foo
926         )
927 '
928
929 test_expect_success 'fetching with auto-gc does not lock up' '
930         write_script askyesno <<-\EOF &&
931         echo "$*" &&
932         false
933         EOF
934         git clone "file://$D" auto-gc &&
935         test_commit test2 &&
936         (
937                 cd auto-gc &&
938                 git config fetch.unpackLimit 1 &&
939                 git config gc.autoPackLimit 1 &&
940                 git config gc.autoDetach false &&
941                 GIT_ASK_YESNO="$D/askyesno" git fetch --verbose >fetch.out 2>&1 &&
942                 test_i18ngrep "Auto packing the repository" fetch.out &&
943                 ! grep "Should I try again" fetch.out
944         )
945 '
946
947 test_expect_success C_LOCALE_OUTPUT 'fetch aligned output' '
948         git clone . full-output &&
949         test_commit looooooooooooong-tag &&
950         (
951                 cd full-output &&
952                 git -c fetch.output=full fetch origin >actual 2>&1 &&
953                 grep -e "->" actual | cut -c 22- >../actual
954         ) &&
955         cat >expect <<-\EOF &&
956         main                 -> origin/main
957         looooooooooooong-tag -> looooooooooooong-tag
958         EOF
959         test_cmp expect actual
960 '
961
962 test_expect_success C_LOCALE_OUTPUT 'fetch compact output' '
963         git clone . compact &&
964         test_commit extraaa &&
965         (
966                 cd compact &&
967                 git -c fetch.output=compact fetch origin >actual 2>&1 &&
968                 grep -e "->" actual | cut -c 22- >../actual
969         ) &&
970         cat >expect <<-\EOF &&
971         main       -> origin/*
972         extraaa    -> *
973         EOF
974         test_cmp expect actual
975 '
976
977 test_expect_success '--no-show-forced-updates' '
978         mkdir forced-updates &&
979         (
980                 cd forced-updates &&
981                 git init &&
982                 test_commit 1 &&
983                 test_commit 2
984         ) &&
985         git clone forced-updates forced-update-clone &&
986         git clone forced-updates no-forced-update-clone &&
987         git -C forced-updates reset --hard HEAD~1 &&
988         (
989                 cd forced-update-clone &&
990                 git fetch --show-forced-updates origin 2>output &&
991                 test_i18ngrep "(forced update)" output
992         ) &&
993         (
994                 cd no-forced-update-clone &&
995                 git fetch --no-show-forced-updates origin 2>output &&
996                 test_i18ngrep ! "(forced update)" output
997         )
998 '
999
1000 setup_negotiation_tip () {
1001         SERVER="$1"
1002         URL="$2"
1003         USE_PROTOCOL_V2="$3"
1004
1005         rm -rf "$SERVER" client trace &&
1006         git init -b main "$SERVER" &&
1007         test_commit -C "$SERVER" alpha_1 &&
1008         test_commit -C "$SERVER" alpha_2 &&
1009         git -C "$SERVER" checkout --orphan beta &&
1010         test_commit -C "$SERVER" beta_1 &&
1011         test_commit -C "$SERVER" beta_2 &&
1012
1013         git clone "$URL" client &&
1014
1015         if test "$USE_PROTOCOL_V2" -eq 1
1016         then
1017                 git -C "$SERVER" config protocol.version 2 &&
1018                 git -C client config protocol.version 2
1019         fi &&
1020
1021         test_commit -C "$SERVER" beta_s &&
1022         git -C "$SERVER" checkout main &&
1023         test_commit -C "$SERVER" alpha_s &&
1024         git -C "$SERVER" tag -d alpha_1 alpha_2 beta_1 beta_2
1025 }
1026
1027 check_negotiation_tip () {
1028         # Ensure that {alpha,beta}_1 are sent as "have", but not {alpha_beta}_2
1029         ALPHA_1=$(git -C client rev-parse alpha_1) &&
1030         grep "fetch> have $ALPHA_1" trace &&
1031         BETA_1=$(git -C client rev-parse beta_1) &&
1032         grep "fetch> have $BETA_1" trace &&
1033         ALPHA_2=$(git -C client rev-parse alpha_2) &&
1034         ! grep "fetch> have $ALPHA_2" trace &&
1035         BETA_2=$(git -C client rev-parse beta_2) &&
1036         ! grep "fetch> have $BETA_2" trace
1037 }
1038
1039 test_expect_success '--negotiation-tip limits "have" lines sent' '
1040         setup_negotiation_tip server server 0 &&
1041         GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
1042                 --negotiation-tip=alpha_1 --negotiation-tip=beta_1 \
1043                 origin alpha_s beta_s &&
1044         check_negotiation_tip
1045 '
1046
1047 test_expect_success '--negotiation-tip understands globs' '
1048         setup_negotiation_tip server server 0 &&
1049         GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
1050                 --negotiation-tip=*_1 \
1051                 origin alpha_s beta_s &&
1052         check_negotiation_tip
1053 '
1054
1055 test_expect_success '--negotiation-tip understands abbreviated SHA-1' '
1056         setup_negotiation_tip server server 0 &&
1057         GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
1058                 --negotiation-tip=$(git -C client rev-parse --short alpha_1) \
1059                 --negotiation-tip=$(git -C client rev-parse --short beta_1) \
1060                 origin alpha_s beta_s &&
1061         check_negotiation_tip
1062 '
1063
1064 . "$TEST_DIRECTORY"/lib-httpd.sh
1065 start_httpd
1066
1067 test_expect_success '--negotiation-tip limits "have" lines sent with HTTP protocol v2' '
1068         setup_negotiation_tip "$HTTPD_DOCUMENT_ROOT_PATH/server" \
1069                 "$HTTPD_URL/smart/server" 1 &&
1070         GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
1071                 --negotiation-tip=alpha_1 --negotiation-tip=beta_1 \
1072                 origin alpha_s beta_s &&
1073         check_negotiation_tip
1074 '
1075
1076 # DO NOT add non-httpd-specific tests here, because the last part of this
1077 # test script is only executed when httpd is available and enabled.
1078
1079 test_done