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