Merge branch 'es/perf-export-fix'
[git] / t / t5505-remote.sh
1 #!/bin/sh
2
3 test_description='git remote porcelain-ish'
4
5 . ./test-lib.sh
6
7 setup_repository () {
8         mkdir "$1" && (
9         cd "$1" &&
10         git init -b main &&
11         >file &&
12         git add file &&
13         test_tick &&
14         git commit -m "Initial" &&
15         git checkout -b side &&
16         >elif &&
17         git add elif &&
18         test_tick &&
19         git commit -m "Second" &&
20         git checkout main
21         )
22 }
23
24 tokens_match () {
25         echo "$1" | tr ' ' '\012' | sort | sed -e '/^$/d' >expect &&
26         echo "$2" | tr ' ' '\012' | sort | sed -e '/^$/d' >actual &&
27         test_cmp expect actual
28 }
29
30 check_remote_track () {
31         actual=$(git remote show "$1" | sed -ne 's|^    \(.*\) tracked$|\1|p')
32         shift &&
33         tokens_match "$*" "$actual"
34 }
35
36 check_tracking_branch () {
37         f="" &&
38         r=$(git for-each-ref "--format=%(refname)" |
39                 sed -ne "s|^refs/remotes/$1/||p") &&
40         shift &&
41         tokens_match "$*" "$r"
42 }
43
44 test_expect_success setup '
45         setup_repository one &&
46         setup_repository two &&
47         (
48                 cd two &&
49                 git branch another
50         ) &&
51         git clone one test
52 '
53
54 test_expect_success 'add remote whose URL agrees with url.<...>.insteadOf' '
55         test_config url.git@host.com:team/repo.git.insteadOf myremote &&
56         git remote add myremote git@host.com:team/repo.git
57 '
58
59 test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' '
60         (
61                 cd test &&
62                 tokens_match origin "$(git remote)" &&
63                 check_remote_track origin main side &&
64                 check_tracking_branch origin HEAD main side
65         )
66 '
67
68 test_expect_success 'add another remote' '
69         (
70                 cd test &&
71                 git remote add -f second ../two &&
72                 tokens_match "origin second" "$(git remote)" &&
73                 check_tracking_branch second main side another &&
74                 git for-each-ref "--format=%(refname)" refs/remotes |
75                 sed -e "/^refs\/remotes\/origin\//d" \
76                     -e "/^refs\/remotes\/second\//d" >actual &&
77                 test_must_be_empty actual
78         )
79 '
80
81 test_expect_success C_LOCALE_OUTPUT 'check remote-tracking' '
82         (
83                 cd test &&
84                 check_remote_track origin main side &&
85                 check_remote_track second main side another
86         )
87 '
88
89 test_expect_success 'remote forces tracking branches' '
90         (
91                 cd test &&
92                 case $(git config remote.second.fetch) in
93                 +*) true ;;
94                  *) false ;;
95                 esac
96         )
97 '
98
99 test_expect_success 'remove remote' '
100         (
101                 cd test &&
102                 git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/main &&
103                 git remote rm second
104         )
105 '
106
107 test_expect_success C_LOCALE_OUTPUT 'remove remote' '
108         (
109                 cd test &&
110                 tokens_match origin "$(git remote)" &&
111                 check_remote_track origin main side &&
112                 git for-each-ref "--format=%(refname)" refs/remotes |
113                 sed -e "/^refs\/remotes\/origin\//d" >actual &&
114                 test_must_be_empty actual
115         )
116 '
117
118 test_expect_success 'remove remote protects local branches' '
119         (
120                 cd test &&
121                 cat >expect1 <<-\EOF &&
122                 Note: A branch outside the refs/remotes/ hierarchy was not removed;
123                 to delete it, use:
124                   git branch -d main
125                 EOF
126                 cat >expect2 <<-\EOF &&
127                 Note: Some branches outside the refs/remotes/ hierarchy were not removed;
128                 to delete them, use:
129                   git branch -d foobranch
130                   git branch -d main
131                 EOF
132                 git tag footag &&
133                 git config --add remote.oops.fetch "+refs/*:refs/*" &&
134                 git remote remove oops 2>actual1 &&
135                 git branch foobranch &&
136                 git config --add remote.oops.fetch "+refs/*:refs/*" &&
137                 git remote rm oops 2>actual2 &&
138                 git branch -d foobranch &&
139                 git tag -d footag &&
140                 test_i18ncmp expect1 actual1 &&
141                 test_i18ncmp expect2 actual2
142         )
143 '
144
145 test_expect_success 'remove errors out early when deleting non-existent branch' '
146         (
147                 cd test &&
148                 echo "error: No such remote: '\''foo'\''" >expect &&
149                 test_expect_code 2 git remote rm foo 2>actual &&
150                 test_i18ncmp expect actual
151         )
152 '
153
154 test_expect_success 'remove remote with a branch without configured merge' '
155         test_when_finished "(
156                 git -C test checkout main;
157                 git -C test branch -D two;
158                 git -C test config --remove-section remote.two;
159                 git -C test config --remove-section branch.second;
160                 true
161         )" &&
162         (
163                 cd test &&
164                 git remote add two ../two &&
165                 git fetch two &&
166                 git checkout -b second two/main^0 &&
167                 git config branch.second.remote two &&
168                 git checkout main &&
169                 git remote rm two
170         )
171 '
172
173 test_expect_success 'rename errors out early when deleting non-existent branch' '
174         (
175                 cd test &&
176                 echo "error: No such remote: '\''foo'\''" >expect &&
177                 test_expect_code 2 git remote rename foo bar 2>actual &&
178                 test_i18ncmp expect actual
179         )
180 '
181
182 test_expect_success 'rename errors out early when when new name is invalid' '
183         test_config remote.foo.vcs bar &&
184         echo "fatal: '\''invalid...name'\'' is not a valid remote name" >expect &&
185         test_must_fail git remote rename foo invalid...name 2>actual &&
186         test_i18ncmp expect actual
187 '
188
189 test_expect_success 'add existing foreign_vcs remote' '
190         test_config remote.foo.vcs bar &&
191         echo "error: remote foo already exists." >expect &&
192         test_expect_code 3 git remote add foo bar 2>actual &&
193         test_i18ncmp expect actual
194 '
195
196 test_expect_success 'add existing foreign_vcs remote' '
197         test_config remote.foo.vcs bar &&
198         test_config remote.bar.vcs bar &&
199         echo "error: remote bar already exists." >expect &&
200         test_expect_code 3 git remote rename foo bar 2>actual &&
201         test_i18ncmp expect actual
202 '
203
204 test_expect_success 'add invalid foreign_vcs remote' '
205         echo "fatal: '\''invalid...name'\'' is not a valid remote name" >expect &&
206         test_must_fail git remote add invalid...name bar 2>actual &&
207         test_i18ncmp expect actual
208 '
209
210 cat >test/expect <<EOF
211 * remote origin
212   Fetch URL: $(pwd)/one
213   Push  URL: $(pwd)/one
214   HEAD branch: main
215   Remote branches:
216     main new (next fetch will store in remotes/origin)
217     side tracked
218   Local branches configured for 'git pull':
219     ahead    merges with remote main
220     main     merges with remote main
221     octopus  merges with remote topic-a
222                 and with remote topic-b
223                 and with remote topic-c
224     rebase  rebases onto remote main
225   Local refs configured for 'git push':
226     main pushes to main     (local out of date)
227     main pushes to upstream (create)
228 * remote two
229   Fetch URL: ../two
230   Push  URL: ../three
231   HEAD branch: main
232   Local refs configured for 'git push':
233     ahead forces to main    (fast-forwardable)
234     main  pushes to another (up to date)
235 EOF
236
237 test_expect_success 'show' '
238         (
239                 cd test &&
240                 git config --add remote.origin.fetch refs/heads/main:refs/heads/upstream &&
241                 git fetch &&
242                 git checkout -b ahead origin/main &&
243                 echo 1 >>file &&
244                 test_tick &&
245                 git commit -m update file &&
246                 git checkout main &&
247                 git branch --track octopus origin/main &&
248                 git branch --track rebase origin/main &&
249                 git branch -d -r origin/main &&
250                 git config --add remote.two.url ../two &&
251                 git config --add remote.two.pushurl ../three &&
252                 git config branch.rebase.rebase true &&
253                 git config branch.octopus.merge "topic-a topic-b topic-c" &&
254                 (
255                         cd ../one &&
256                         echo 1 >file &&
257                         test_tick &&
258                         git commit -m update file
259                 ) &&
260                 git config --add remote.origin.push : &&
261                 git config --add remote.origin.push refs/heads/main:refs/heads/upstream &&
262                 git config --add remote.origin.push +refs/tags/lastbackup &&
263                 git config --add remote.two.push +refs/heads/ahead:refs/heads/main &&
264                 git config --add remote.two.push refs/heads/main:refs/heads/another &&
265                 git remote show origin two >output &&
266                 git branch -d rebase octopus &&
267                 test_i18ncmp expect output
268         )
269 '
270
271 cat >test/expect <<EOF
272 * remote origin
273   Fetch URL: $(pwd)/one
274   Push  URL: $(pwd)/one
275   HEAD branch: (not queried)
276   Remote branches: (status not queried)
277     main
278     side
279   Local branches configured for 'git pull':
280     ahead merges with remote main
281     main  merges with remote main
282   Local refs configured for 'git push' (status not queried):
283     (matching)           pushes to (matching)
284     refs/heads/main      pushes to refs/heads/upstream
285     refs/tags/lastbackup forces to refs/tags/lastbackup
286 EOF
287
288 test_expect_success 'show -n' '
289         mv one one.unreachable &&
290         (
291                 cd test &&
292                 git remote show -n origin >output &&
293                 mv ../one.unreachable ../one &&
294                 test_i18ncmp expect output
295         )
296 '
297
298 test_expect_success 'prune' '
299         (
300                 cd one &&
301                 git branch -m side side2
302         ) &&
303         (
304                 cd test &&
305                 git fetch origin &&
306                 git remote prune origin &&
307                 git rev-parse refs/remotes/origin/side2 &&
308                 test_must_fail git rev-parse refs/remotes/origin/side
309         )
310 '
311
312 test_expect_success 'set-head --delete' '
313         (
314                 cd test &&
315                 git symbolic-ref refs/remotes/origin/HEAD &&
316                 git remote set-head --delete origin &&
317                 test_must_fail git symbolic-ref refs/remotes/origin/HEAD
318         )
319 '
320
321 test_expect_success 'set-head --auto' '
322         (
323                 cd test &&
324                 git remote set-head --auto origin &&
325                 echo refs/remotes/origin/main >expect &&
326                 git symbolic-ref refs/remotes/origin/HEAD >output &&
327                 test_cmp expect output
328         )
329 '
330
331 test_expect_success 'set-head --auto has no problem w/multiple HEADs' '
332         (
333                 cd test &&
334                 git fetch two "refs/heads/*:refs/remotes/two/*" &&
335                 git remote set-head --auto two >output 2>&1 &&
336                 echo "two/HEAD set to main" >expect &&
337                 test_i18ncmp expect output
338         )
339 '
340
341 cat >test/expect <<\EOF
342 refs/remotes/origin/side2
343 EOF
344
345 test_expect_success 'set-head explicit' '
346         (
347                 cd test &&
348                 git remote set-head origin side2 &&
349                 git symbolic-ref refs/remotes/origin/HEAD >output &&
350                 git remote set-head origin main &&
351                 test_cmp expect output
352         )
353 '
354
355 cat >test/expect <<EOF
356 Pruning origin
357 URL: $(pwd)/one
358  * [would prune] origin/side2
359 EOF
360
361 test_expect_success 'prune --dry-run' '
362         git -C one branch -m side2 side &&
363         test_when_finished "git -C one branch -m side side2" &&
364         (
365                 cd test &&
366                 git remote prune --dry-run origin >output &&
367                 git rev-parse refs/remotes/origin/side2 &&
368                 test_must_fail git rev-parse refs/remotes/origin/side &&
369                 test_i18ncmp expect output
370         )
371 '
372
373 test_expect_success 'add --mirror && prune' '
374         mkdir mirror &&
375         (
376                 cd mirror &&
377                 git init --bare &&
378                 git remote add --mirror -f origin ../one
379         ) &&
380         (
381                 cd one &&
382                 git branch -m side2 side
383         ) &&
384         (
385                 cd mirror &&
386                 git rev-parse --verify refs/heads/side2 &&
387                 test_must_fail git rev-parse --verify refs/heads/side &&
388                 git fetch origin &&
389                 git remote prune origin &&
390                 test_must_fail git rev-parse --verify refs/heads/side2 &&
391                 git rev-parse --verify refs/heads/side
392         )
393 '
394
395 test_expect_success 'add --mirror=fetch' '
396         mkdir mirror-fetch &&
397         git init -b main mirror-fetch/parent &&
398         (
399                 cd mirror-fetch/parent &&
400                 test_commit one
401         ) &&
402         git init --bare mirror-fetch/child &&
403         (
404                 cd mirror-fetch/child &&
405                 git remote add --mirror=fetch -f parent ../parent
406         )
407 '
408
409 test_expect_success 'fetch mirrors act as mirrors during fetch' '
410         (
411                 cd mirror-fetch/parent &&
412                 git branch new &&
413                 git branch -m main renamed
414         ) &&
415         (
416                 cd mirror-fetch/child &&
417                 git fetch parent &&
418                 git rev-parse --verify refs/heads/new &&
419                 git rev-parse --verify refs/heads/renamed
420         )
421 '
422
423 test_expect_success 'fetch mirrors can prune' '
424         (
425                 cd mirror-fetch/child &&
426                 git remote prune parent &&
427                 test_must_fail git rev-parse --verify refs/heads/main
428         )
429 '
430
431 test_expect_success 'fetch mirrors do not act as mirrors during push' '
432         (
433                 cd mirror-fetch/parent &&
434                 git checkout HEAD^0
435         ) &&
436         (
437                 cd mirror-fetch/child &&
438                 git branch -m renamed renamed2 &&
439                 git push parent :
440         ) &&
441         (
442                 cd mirror-fetch/parent &&
443                 git rev-parse --verify renamed &&
444                 test_must_fail git rev-parse --verify refs/heads/renamed2
445         )
446 '
447
448 test_expect_success 'add fetch mirror with specific branches' '
449         git init --bare mirror-fetch/track &&
450         (
451                 cd mirror-fetch/track &&
452                 git remote add --mirror=fetch -t heads/new parent ../parent
453         )
454 '
455
456 test_expect_success 'fetch mirror respects specific branches' '
457         (
458                 cd mirror-fetch/track &&
459                 git fetch parent &&
460                 git rev-parse --verify refs/heads/new &&
461                 test_must_fail git rev-parse --verify refs/heads/renamed
462         )
463 '
464
465 test_expect_success 'add --mirror=push' '
466         mkdir mirror-push &&
467         git init --bare mirror-push/public &&
468         git init -b main mirror-push/private &&
469         (
470                 cd mirror-push/private &&
471                 test_commit one &&
472                 git remote add --mirror=push public ../public
473         )
474 '
475
476 test_expect_success 'push mirrors act as mirrors during push' '
477         (
478                 cd mirror-push/private &&
479                 git branch new &&
480                 git branch -m main renamed &&
481                 git push public
482         ) &&
483         (
484                 cd mirror-push/private &&
485                 git rev-parse --verify refs/heads/new &&
486                 git rev-parse --verify refs/heads/renamed &&
487                 test_must_fail git rev-parse --verify refs/heads/main
488         )
489 '
490
491 test_expect_success 'push mirrors do not act as mirrors during fetch' '
492         (
493                 cd mirror-push/public &&
494                 git branch -m renamed renamed2 &&
495                 git symbolic-ref HEAD refs/heads/renamed2
496         ) &&
497         (
498                 cd mirror-push/private &&
499                 git fetch public &&
500                 git rev-parse --verify refs/heads/renamed &&
501                 test_must_fail git rev-parse --verify refs/heads/renamed2
502         )
503 '
504
505 test_expect_success 'push mirrors do not allow you to specify refs' '
506         git init mirror-push/track &&
507         (
508                 cd mirror-push/track &&
509                 test_must_fail git remote add --mirror=push -t new public ../public
510         )
511 '
512
513 test_expect_success 'add alt && prune' '
514         mkdir alttst &&
515         (
516                 cd alttst &&
517                 git init &&
518                 git remote add -f origin ../one &&
519                 git config remote.alt.url ../one &&
520                 git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*"
521         ) &&
522         (
523                 cd one &&
524                 git branch -m side side2
525         ) &&
526         (
527                 cd alttst &&
528                 git rev-parse --verify refs/remotes/origin/side &&
529                 test_must_fail git rev-parse --verify refs/remotes/origin/side2 &&
530                 git fetch alt &&
531                 git remote prune alt &&
532                 test_must_fail git rev-parse --verify refs/remotes/origin/side &&
533                 git rev-parse --verify refs/remotes/origin/side2
534         )
535 '
536
537 cat >test/expect <<\EOF
538 some-tag
539 EOF
540
541 test_expect_success 'add with reachable tags (default)' '
542         (
543                 cd one &&
544                 >foobar &&
545                 git add foobar &&
546                 git commit -m "Foobar" &&
547                 git tag -a -m "Foobar tag" foobar-tag &&
548                 git reset --hard HEAD~1 &&
549                 git tag -a -m "Some tag" some-tag
550         ) &&
551         mkdir add-tags &&
552         (
553                 cd add-tags &&
554                 git init &&
555                 git remote add -f origin ../one &&
556                 git tag -l some-tag >../test/output &&
557                 git tag -l foobar-tag >>../test/output &&
558                 test_must_fail git config remote.origin.tagopt
559         ) &&
560         test_cmp test/expect test/output
561 '
562
563 cat >test/expect <<\EOF
564 some-tag
565 foobar-tag
566 --tags
567 EOF
568
569 test_expect_success 'add --tags' '
570         rm -rf add-tags &&
571         (
572                 mkdir add-tags &&
573                 cd add-tags &&
574                 git init &&
575                 git remote add -f --tags origin ../one &&
576                 git tag -l some-tag >../test/output &&
577                 git tag -l foobar-tag >>../test/output &&
578                 git config remote.origin.tagopt >>../test/output
579         ) &&
580         test_cmp test/expect test/output
581 '
582
583 cat >test/expect <<\EOF
584 --no-tags
585 EOF
586
587 test_expect_success 'add --no-tags' '
588         rm -rf add-tags &&
589         (
590                 mkdir add-no-tags &&
591                 cd add-no-tags &&
592                 git init &&
593                 git remote add -f --no-tags origin ../one &&
594                 git tag -l some-tag >../test/output &&
595                 git tag -l foobar-tag >../test/output &&
596                 git config remote.origin.tagopt >>../test/output
597         ) &&
598         (
599                 cd one &&
600                 git tag -d some-tag foobar-tag
601         ) &&
602         test_cmp test/expect test/output
603 '
604
605 test_expect_success 'reject --no-no-tags' '
606         (
607                 cd add-no-tags &&
608                 test_must_fail git remote add -f --no-no-tags neworigin ../one
609         )
610 '
611
612 cat >one/expect <<\EOF
613   apis/main
614   apis/side
615   drosophila/another
616   drosophila/main
617   drosophila/side
618 EOF
619
620 test_expect_success 'update' '
621         (
622                 cd one &&
623                 git remote add drosophila ../two &&
624                 git remote add apis ../mirror &&
625                 git remote update &&
626                 git branch -r >output &&
627                 test_cmp expect output
628         )
629 '
630
631 cat >one/expect <<\EOF
632   drosophila/another
633   drosophila/main
634   drosophila/side
635   manduca/main
636   manduca/side
637   megaloprepus/main
638   megaloprepus/side
639 EOF
640
641 test_expect_success 'update with arguments' '
642         (
643                 cd one &&
644                 for b in $(git branch -r)
645                 do
646                 git branch -r -d $b || exit 1
647                 done &&
648                 git remote add manduca ../mirror &&
649                 git remote add megaloprepus ../mirror &&
650                 git config remotes.phobaeticus "drosophila megaloprepus" &&
651                 git config remotes.titanus manduca &&
652                 git remote update phobaeticus titanus &&
653                 git branch -r >output &&
654                 test_cmp expect output
655         )
656 '
657
658 test_expect_success 'update --prune' '
659         (
660                 cd one &&
661                 git branch -m side2 side3
662         ) &&
663         (
664                 cd test &&
665                 git remote update --prune &&
666                 (
667                         cd ../one &&
668                         git branch -m side3 side2
669                 ) &&
670                 git rev-parse refs/remotes/origin/side3 &&
671                 test_must_fail git rev-parse refs/remotes/origin/side2
672         )
673 '
674
675 cat >one/expect <<-\EOF
676   apis/main
677   apis/side
678   manduca/main
679   manduca/side
680   megaloprepus/main
681   megaloprepus/side
682 EOF
683
684 test_expect_success 'update default' '
685         (
686                 cd one &&
687                 for b in $(git branch -r)
688                 do
689                 git branch -r -d $b || exit 1
690                 done &&
691                 git config remote.drosophila.skipDefaultUpdate true &&
692                 git remote update default &&
693                 git branch -r >output &&
694                 test_cmp expect output
695         )
696 '
697
698 cat >one/expect <<\EOF
699   drosophila/another
700   drosophila/main
701   drosophila/side
702 EOF
703
704 test_expect_success 'update default (overridden, with funny whitespace)' '
705         (
706                 cd one &&
707                 for b in $(git branch -r)
708                 do
709                 git branch -r -d $b || exit 1
710                 done &&
711                 git config remotes.default "$(printf "\t drosophila  \n")" &&
712                 git remote update default &&
713                 git branch -r >output &&
714                 test_cmp expect output
715         )
716 '
717
718 test_expect_success 'update (with remotes.default defined)' '
719         (
720                 cd one &&
721                 for b in $(git branch -r)
722                 do
723                 git branch -r -d $b || exit 1
724                 done &&
725                 git config remotes.default "drosophila" &&
726                 git remote update &&
727                 git branch -r >output &&
728                 test_cmp expect output
729         )
730 '
731
732 test_expect_success '"remote show" does not show symbolic refs' '
733         git clone one three &&
734         (
735                 cd three &&
736                 git remote show origin >output &&
737                 ! grep "^ *HEAD$" < output &&
738                 ! grep -i stale < output
739         )
740 '
741
742 test_expect_success 'reject adding remote with an invalid name' '
743         test_must_fail git remote add some:url desired-name
744 '
745
746 # The first three test if the tracking branches are properly renamed,
747 # the last two ones check if the config is updated.
748
749 test_expect_success 'rename a remote' '
750         test_config_global remote.pushDefault origin &&
751         git clone one four &&
752         (
753                 cd four &&
754                 git config branch.main.pushRemote origin &&
755                 git remote rename origin upstream &&
756                 test -z "$(git for-each-ref refs/remotes/origin)" &&
757                 test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/main" &&
758                 test "$(git rev-parse upstream/main)" = "$(git rev-parse main)" &&
759                 test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
760                 test "$(git config branch.main.remote)" = "upstream" &&
761                 test "$(git config branch.main.pushRemote)" = "upstream" &&
762                 test "$(git config --global remote.pushDefault)" = "origin"
763         )
764 '
765
766 test_expect_success 'rename a remote renames repo remote.pushDefault' '
767         git clone one four.1 &&
768         (
769                 cd four.1 &&
770                 git config remote.pushDefault origin &&
771                 git remote rename origin upstream &&
772                 test "$(git config --local remote.pushDefault)" = "upstream"
773         )
774 '
775
776 test_expect_success 'rename a remote renames repo remote.pushDefault but ignores global' '
777         test_config_global remote.pushDefault other &&
778         git clone one four.2 &&
779         (
780                 cd four.2 &&
781                 git config remote.pushDefault origin &&
782                 git remote rename origin upstream &&
783                 test "$(git config --global remote.pushDefault)" = "other" &&
784                 test "$(git config --local remote.pushDefault)" = "upstream"
785         )
786 '
787
788 test_expect_success 'rename a remote renames repo remote.pushDefault but keeps global' '
789         test_config_global remote.pushDefault origin &&
790         git clone one four.3 &&
791         (
792                 cd four.3 &&
793                 git config remote.pushDefault origin &&
794                 git remote rename origin upstream &&
795                 test "$(git config --global remote.pushDefault)" = "origin" &&
796                 test "$(git config --local remote.pushDefault)" = "upstream"
797         )
798 '
799
800 test_expect_success 'rename does not update a non-default fetch refspec' '
801         git clone one four.one &&
802         (
803                 cd four.one &&
804                 git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* &&
805                 git remote rename origin upstream &&
806                 test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" &&
807                 git rev-parse -q origin/main
808         )
809 '
810
811 test_expect_success 'rename a remote with name part of fetch spec' '
812         git clone one four.two &&
813         (
814                 cd four.two &&
815                 git remote rename origin remote &&
816                 git remote rename remote upstream &&
817                 test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*"
818         )
819 '
820
821 test_expect_success 'rename a remote with name prefix of other remote' '
822         git clone one four.three &&
823         (
824                 cd four.three &&
825                 git remote add o git://example.com/repo.git &&
826                 git remote rename o upstream &&
827                 test "$(git rev-parse origin/main)" = "$(git rev-parse main)"
828         )
829 '
830
831 test_expect_success 'rename succeeds with existing remote.<target>.prune' '
832         git clone one four.four &&
833         test_when_finished git config --global --unset remote.upstream.prune &&
834         git config --global remote.upstream.prune true &&
835         git -C four.four remote rename origin upstream
836 '
837
838 test_expect_success 'remove a remote' '
839         test_config_global remote.pushDefault origin &&
840         git clone one four.five &&
841         (
842                 cd four.five &&
843                 git config branch.main.pushRemote origin &&
844                 git remote remove origin &&
845                 test -z "$(git for-each-ref refs/remotes/origin)" &&
846                 test_must_fail git config branch.main.remote &&
847                 test_must_fail git config branch.main.pushRemote &&
848                 test "$(git config --global remote.pushDefault)" = "origin"
849         )
850 '
851
852 test_expect_success 'remove a remote removes repo remote.pushDefault' '
853         git clone one four.five.1 &&
854         (
855                 cd four.five.1 &&
856                 git config remote.pushDefault origin &&
857                 git remote remove origin &&
858                 test_must_fail git config --local remote.pushDefault
859         )
860 '
861
862 test_expect_success 'remove a remote removes repo remote.pushDefault but ignores global' '
863         test_config_global remote.pushDefault other &&
864         git clone one four.five.2 &&
865         (
866                 cd four.five.2 &&
867                 git config remote.pushDefault origin &&
868                 git remote remove origin &&
869                 test "$(git config --global remote.pushDefault)" = "other" &&
870                 test_must_fail git config --local remote.pushDefault
871         )
872 '
873
874 test_expect_success 'remove a remote removes repo remote.pushDefault but keeps global' '
875         test_config_global remote.pushDefault origin &&
876         git clone one four.five.3 &&
877         (
878                 cd four.five.3 &&
879                 git config remote.pushDefault origin &&
880                 git remote remove origin &&
881                 test "$(git config --global remote.pushDefault)" = "origin" &&
882                 test_must_fail git config --local remote.pushDefault
883         )
884 '
885
886 cat >remotes_origin <<EOF
887 URL: $(pwd)/one
888 Push: refs/heads/main:refs/heads/upstream
889 Push: refs/heads/next:refs/heads/upstream2
890 Pull: refs/heads/main:refs/heads/origin
891 Pull: refs/heads/next:refs/heads/origin2
892 EOF
893
894 test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
895         git clone one five &&
896         origin_url=$(pwd)/one &&
897         (
898                 cd five &&
899                 git remote remove origin &&
900                 mkdir -p .git/remotes &&
901                 cat ../remotes_origin >.git/remotes/origin &&
902                 git remote rename origin origin &&
903                 test_path_is_missing .git/remotes/origin &&
904                 test "$(git config remote.origin.url)" = "$origin_url" &&
905                 cat >push_expected <<-\EOF &&
906                 refs/heads/main:refs/heads/upstream
907                 refs/heads/next:refs/heads/upstream2
908                 EOF
909                 cat >fetch_expected <<-\EOF &&
910                 refs/heads/main:refs/heads/origin
911                 refs/heads/next:refs/heads/origin2
912                 EOF
913                 git config --get-all remote.origin.push >push_actual &&
914                 git config --get-all remote.origin.fetch >fetch_actual &&
915                 test_cmp push_expected push_actual &&
916                 test_cmp fetch_expected fetch_actual
917         )
918 '
919
920 test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
921         git clone one six &&
922         origin_url=$(pwd)/one &&
923         (
924                 cd six &&
925                 git remote rm origin &&
926                 echo "$origin_url#main" >.git/branches/origin &&
927                 git remote rename origin origin &&
928                 test_path_is_missing .git/branches/origin &&
929                 test "$(git config remote.origin.url)" = "$origin_url" &&
930                 test "$(git config remote.origin.fetch)" = "refs/heads/main:refs/heads/origin" &&
931                 test "$(git config remote.origin.push)" = "HEAD:refs/heads/main"
932         )
933 '
934
935 test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)' '
936         git clone one seven &&
937         (
938                 cd seven &&
939                 git remote rm origin &&
940                 echo "quux#foom" > .git/branches/origin &&
941                 git remote rename origin origin &&
942                 test_path_is_missing .git/branches/origin &&
943                 test "$(git config remote.origin.url)" = "quux" &&
944                 test "$(git config remote.origin.fetch)" = "refs/heads/foom:refs/heads/origin" &&
945                 test "$(git config remote.origin.push)" = "HEAD:refs/heads/foom"
946         )
947 '
948
949 test_expect_success 'remote prune to cause a dangling symref' '
950         git clone one eight &&
951         (
952                 cd one &&
953                 git checkout side2 &&
954                 git branch -D main
955         ) &&
956         (
957                 cd eight &&
958                 git remote prune origin
959         ) >err 2>&1 &&
960         test_i18ngrep "has become dangling" err &&
961
962         : And the dangling symref will not cause other annoying errors &&
963         (
964                 cd eight &&
965                 git branch -a
966         ) 2>err &&
967         ! grep "points nowhere" err &&
968         (
969                 cd eight &&
970                 test_must_fail git branch nomore origin
971         ) 2>err &&
972         test_i18ngrep "dangling symref" err
973 '
974
975 test_expect_success 'show empty remote' '
976         test_create_repo empty &&
977         git clone empty empty-clone &&
978         (
979                 cd empty-clone &&
980                 git remote show origin
981         )
982 '
983
984 test_expect_success 'remote set-branches requires a remote' '
985         test_must_fail git remote set-branches &&
986         test_must_fail git remote set-branches --add
987 '
988
989 test_expect_success 'remote set-branches' '
990         echo "+refs/heads/*:refs/remotes/scratch/*" >expect.initial &&
991         sort <<-\EOF >expect.add &&
992         +refs/heads/*:refs/remotes/scratch/*
993         +refs/heads/other:refs/remotes/scratch/other
994         EOF
995         sort <<-\EOF >expect.replace &&
996         +refs/heads/maint:refs/remotes/scratch/maint
997         +refs/heads/main:refs/remotes/scratch/main
998         +refs/heads/next:refs/remotes/scratch/next
999         EOF
1000         sort <<-\EOF >expect.add-two &&
1001         +refs/heads/maint:refs/remotes/scratch/maint
1002         +refs/heads/main:refs/remotes/scratch/main
1003         +refs/heads/next:refs/remotes/scratch/next
1004         +refs/heads/seen:refs/remotes/scratch/seen
1005         +refs/heads/t/topic:refs/remotes/scratch/t/topic
1006         EOF
1007         sort <<-\EOF >expect.setup-ffonly &&
1008         refs/heads/main:refs/remotes/scratch/main
1009         +refs/heads/next:refs/remotes/scratch/next
1010         EOF
1011         sort <<-\EOF >expect.respect-ffonly &&
1012         refs/heads/main:refs/remotes/scratch/main
1013         +refs/heads/next:refs/remotes/scratch/next
1014         +refs/heads/seen:refs/remotes/scratch/seen
1015         EOF
1016
1017         git clone .git/ setbranches &&
1018         (
1019                 cd setbranches &&
1020                 git remote rename origin scratch &&
1021                 git config --get-all remote.scratch.fetch >config-result &&
1022                 sort <config-result >../actual.initial &&
1023
1024                 git remote set-branches scratch --add other &&
1025                 git config --get-all remote.scratch.fetch >config-result &&
1026                 sort <config-result >../actual.add &&
1027
1028                 git remote set-branches scratch maint main next &&
1029                 git config --get-all remote.scratch.fetch >config-result &&
1030                 sort <config-result >../actual.replace &&
1031
1032                 git remote set-branches --add scratch seen t/topic &&
1033                 git config --get-all remote.scratch.fetch >config-result &&
1034                 sort <config-result >../actual.add-two &&
1035
1036                 git config --unset-all remote.scratch.fetch &&
1037                 git config remote.scratch.fetch \
1038                         refs/heads/main:refs/remotes/scratch/main &&
1039                 git config --add remote.scratch.fetch \
1040                         +refs/heads/next:refs/remotes/scratch/next &&
1041                 git config --get-all remote.scratch.fetch >config-result &&
1042                 sort <config-result >../actual.setup-ffonly &&
1043
1044                 git remote set-branches --add scratch seen &&
1045                 git config --get-all remote.scratch.fetch >config-result &&
1046                 sort <config-result >../actual.respect-ffonly
1047         ) &&
1048         test_cmp expect.initial actual.initial &&
1049         test_cmp expect.add actual.add &&
1050         test_cmp expect.replace actual.replace &&
1051         test_cmp expect.add-two actual.add-two &&
1052         test_cmp expect.setup-ffonly actual.setup-ffonly &&
1053         test_cmp expect.respect-ffonly actual.respect-ffonly
1054 '
1055
1056 test_expect_success 'remote set-branches with --mirror' '
1057         echo "+refs/*:refs/*" >expect.initial &&
1058         echo "+refs/heads/main:refs/heads/main" >expect.replace &&
1059         git clone --mirror .git/ setbranches-mirror &&
1060         (
1061                 cd setbranches-mirror &&
1062                 git remote rename origin scratch &&
1063                 git config --get-all remote.scratch.fetch >../actual.initial &&
1064
1065                 git remote set-branches scratch heads/main &&
1066                 git config --get-all remote.scratch.fetch >../actual.replace
1067         ) &&
1068         test_cmp expect.initial actual.initial &&
1069         test_cmp expect.replace actual.replace
1070 '
1071
1072 test_expect_success 'new remote' '
1073         git remote add someremote foo &&
1074         echo foo >expect &&
1075         git config --get-all remote.someremote.url >actual &&
1076         cmp expect actual
1077 '
1078
1079 get_url_test () {
1080         cat >expect &&
1081         git remote get-url "$@" >actual &&
1082         test_cmp expect actual
1083 }
1084
1085 test_expect_success 'get-url on new remote' '
1086         echo foo | get_url_test someremote &&
1087         echo foo | get_url_test --all someremote &&
1088         echo foo | get_url_test --push someremote &&
1089         echo foo | get_url_test --push --all someremote
1090 '
1091
1092 test_expect_success 'remote set-url with locked config' '
1093         test_when_finished "rm -f .git/config.lock" &&
1094         git config --get-all remote.someremote.url >expect &&
1095         >.git/config.lock &&
1096         test_must_fail git remote set-url someremote baz &&
1097         git config --get-all remote.someremote.url >actual &&
1098         cmp expect actual
1099 '
1100
1101 test_expect_success 'remote set-url bar' '
1102         git remote set-url someremote bar &&
1103         echo bar >expect &&
1104         git config --get-all remote.someremote.url >actual &&
1105         cmp expect actual
1106 '
1107
1108 test_expect_success 'remote set-url baz bar' '
1109         git remote set-url someremote baz bar &&
1110         echo baz >expect &&
1111         git config --get-all remote.someremote.url >actual &&
1112         cmp expect actual
1113 '
1114
1115 test_expect_success 'remote set-url zot bar' '
1116         test_must_fail git remote set-url someremote zot bar &&
1117         echo baz >expect &&
1118         git config --get-all remote.someremote.url >actual &&
1119         cmp expect actual
1120 '
1121
1122 test_expect_success 'remote set-url --push zot baz' '
1123         test_must_fail git remote set-url --push someremote zot baz &&
1124         echo "YYY" >expect &&
1125         echo baz >>expect &&
1126         test_must_fail git config --get-all remote.someremote.pushurl >actual &&
1127         echo "YYY" >>actual &&
1128         git config --get-all remote.someremote.url >>actual &&
1129         cmp expect actual
1130 '
1131
1132 test_expect_success 'remote set-url --push zot' '
1133         git remote set-url --push someremote zot &&
1134         echo zot >expect &&
1135         echo "YYY" >>expect &&
1136         echo baz >>expect &&
1137         git config --get-all remote.someremote.pushurl >actual &&
1138         echo "YYY" >>actual &&
1139         git config --get-all remote.someremote.url >>actual &&
1140         cmp expect actual
1141 '
1142
1143 test_expect_success 'get-url with different urls' '
1144         echo baz | get_url_test someremote &&
1145         echo baz | get_url_test --all someremote &&
1146         echo zot | get_url_test --push someremote &&
1147         echo zot | get_url_test --push --all someremote
1148 '
1149
1150 test_expect_success 'remote set-url --push qux zot' '
1151         git remote set-url --push someremote qux zot &&
1152         echo qux >expect &&
1153         echo "YYY" >>expect &&
1154         echo baz >>expect &&
1155         git config --get-all remote.someremote.pushurl >actual &&
1156         echo "YYY" >>actual &&
1157         git config --get-all remote.someremote.url >>actual &&
1158         cmp expect actual
1159 '
1160
1161 test_expect_success 'remote set-url --push foo qu+x' '
1162         git remote set-url --push someremote foo qu+x &&
1163         echo foo >expect &&
1164         echo "YYY" >>expect &&
1165         echo baz >>expect &&
1166         git config --get-all remote.someremote.pushurl >actual &&
1167         echo "YYY" >>actual &&
1168         git config --get-all remote.someremote.url >>actual &&
1169         cmp expect actual
1170 '
1171
1172 test_expect_success 'remote set-url --push --add aaa' '
1173         git remote set-url --push --add someremote aaa &&
1174         echo foo >expect &&
1175         echo aaa >>expect &&
1176         echo "YYY" >>expect &&
1177         echo baz >>expect &&
1178         git config --get-all remote.someremote.pushurl >actual &&
1179         echo "YYY" >>actual &&
1180         git config --get-all remote.someremote.url >>actual &&
1181         cmp expect actual
1182 '
1183
1184 test_expect_success 'get-url on multi push remote' '
1185         echo foo | get_url_test --push someremote &&
1186         get_url_test --push --all someremote <<-\EOF
1187         foo
1188         aaa
1189         EOF
1190 '
1191
1192 test_expect_success 'remote set-url --push bar aaa' '
1193         git remote set-url --push someremote bar aaa &&
1194         echo foo >expect &&
1195         echo bar >>expect &&
1196         echo "YYY" >>expect &&
1197         echo baz >>expect &&
1198         git config --get-all remote.someremote.pushurl >actual &&
1199         echo "YYY" >>actual &&
1200         git config --get-all remote.someremote.url >>actual &&
1201         cmp expect actual
1202 '
1203
1204 test_expect_success 'remote set-url --push --delete bar' '
1205         git remote set-url --push --delete someremote bar &&
1206         echo foo >expect &&
1207         echo "YYY" >>expect &&
1208         echo baz >>expect &&
1209         git config --get-all remote.someremote.pushurl >actual &&
1210         echo "YYY" >>actual &&
1211         git config --get-all remote.someremote.url >>actual &&
1212         cmp expect actual
1213 '
1214
1215 test_expect_success 'remote set-url --push --delete foo' '
1216         git remote set-url --push --delete someremote foo &&
1217         echo "YYY" >expect &&
1218         echo baz >>expect &&
1219         test_must_fail git config --get-all remote.someremote.pushurl >actual &&
1220         echo "YYY" >>actual &&
1221         git config --get-all remote.someremote.url >>actual &&
1222         cmp expect actual
1223 '
1224
1225 test_expect_success 'remote set-url --add bbb' '
1226         git remote set-url --add someremote bbb &&
1227         echo "YYY" >expect &&
1228         echo baz >>expect &&
1229         echo bbb >>expect &&
1230         test_must_fail git config --get-all remote.someremote.pushurl >actual &&
1231         echo "YYY" >>actual &&
1232         git config --get-all remote.someremote.url >>actual &&
1233         cmp expect actual
1234 '
1235
1236 test_expect_success 'get-url on multi fetch remote' '
1237         echo baz | get_url_test someremote &&
1238         get_url_test --all someremote <<-\EOF
1239         baz
1240         bbb
1241         EOF
1242 '
1243
1244 test_expect_success 'remote set-url --delete .*' '
1245         test_must_fail git remote set-url --delete someremote .\* &&
1246         echo "YYY" >expect &&
1247         echo baz >>expect &&
1248         echo bbb >>expect &&
1249         test_must_fail git config --get-all remote.someremote.pushurl >actual &&
1250         echo "YYY" >>actual &&
1251         git config --get-all remote.someremote.url >>actual &&
1252         cmp expect actual
1253 '
1254
1255 test_expect_success 'remote set-url --delete bbb' '
1256         git remote set-url --delete someremote bbb &&
1257         echo "YYY" >expect &&
1258         echo baz >>expect &&
1259         test_must_fail git config --get-all remote.someremote.pushurl >actual &&
1260         echo "YYY" >>actual &&
1261         git config --get-all remote.someremote.url >>actual &&
1262         cmp expect actual
1263 '
1264
1265 test_expect_success 'remote set-url --delete baz' '
1266         test_must_fail git remote set-url --delete someremote baz &&
1267         echo "YYY" >expect &&
1268         echo baz >>expect &&
1269         test_must_fail git config --get-all remote.someremote.pushurl >actual &&
1270         echo "YYY" >>actual &&
1271         git config --get-all remote.someremote.url >>actual &&
1272         cmp expect actual
1273 '
1274
1275 test_expect_success 'remote set-url --add ccc' '
1276         git remote set-url --add someremote ccc &&
1277         echo "YYY" >expect &&
1278         echo baz >>expect &&
1279         echo ccc >>expect &&
1280         test_must_fail git config --get-all remote.someremote.pushurl >actual &&
1281         echo "YYY" >>actual &&
1282         git config --get-all remote.someremote.url >>actual &&
1283         cmp expect actual
1284 '
1285
1286 test_expect_success 'remote set-url --delete baz' '
1287         git remote set-url --delete someremote baz &&
1288         echo "YYY" >expect &&
1289         echo ccc >>expect &&
1290         test_must_fail git config --get-all remote.someremote.pushurl >actual &&
1291         echo "YYY" >>actual &&
1292         git config --get-all remote.someremote.url >>actual &&
1293         cmp expect actual
1294 '
1295
1296 test_expect_success 'extra args: setup' '
1297         # add a dummy origin so that this does not trigger failure
1298         git remote add origin .
1299 '
1300
1301 test_extra_arg () {
1302         test_expect_success "extra args: $*" "
1303                 test_must_fail git remote $* bogus_extra_arg 2>actual &&
1304                 test_i18ngrep '^usage:' actual
1305         "
1306 }
1307
1308 test_extra_arg add nick url
1309 test_extra_arg rename origin newname
1310 test_extra_arg remove origin
1311 test_extra_arg set-head origin main
1312 # set-branches takes any number of args
1313 test_extra_arg get-url origin newurl
1314 test_extra_arg set-url origin newurl oldurl
1315 # show takes any number of args
1316 # prune takes any number of args
1317 # update takes any number of args
1318
1319 test_expect_success 'add remote matching the "insteadOf" URL' '
1320         git config url.xyz@example.com.insteadOf backup &&
1321         git remote add backup xyz@example.com
1322 '
1323
1324 test_expect_success 'unqualified <dst> refspec DWIM and advice' '
1325         test_when_finished "(cd test && git tag -d some-tag)" &&
1326         (
1327                 cd test &&
1328                 git tag -a -m "Some tag" some-tag main &&
1329                 exit_with=true &&
1330                 for type in commit tag tree blob
1331                 do
1332                         if test "$type" = "blob"
1333                         then
1334                                 oid=$(git rev-parse some-tag:file)
1335                         else
1336                                 oid=$(git rev-parse some-tag^{$type})
1337                         fi &&
1338                         test_must_fail git push origin $oid:dst 2>err &&
1339                         test_i18ngrep "error: The destination you" err &&
1340                         test_i18ngrep "hint: Did you mean" err &&
1341                         test_must_fail git -c advice.pushUnqualifiedRefName=false \
1342                                 push origin $oid:dst 2>err &&
1343                         test_i18ngrep "error: The destination you" err &&
1344                         test_i18ngrep ! "hint: Did you mean" err ||
1345                         exit_with=false
1346                 done &&
1347                 $exit_with
1348         )
1349 '
1350
1351 test_expect_success 'refs/remotes/* <src> refspec and unqualified <dst> DWIM and advice' '
1352         (
1353                 cd two &&
1354                 git tag -a -m "Some tag" my-tag main &&
1355                 git update-ref refs/trees/my-head-tree HEAD^{tree} &&
1356                 git update-ref refs/blobs/my-file-blob HEAD:file
1357         ) &&
1358         (
1359                 cd test &&
1360                 git config --add remote.two.fetch "+refs/tags/*:refs/remotes/tags-from-two/*" &&
1361                 git config --add remote.two.fetch "+refs/trees/*:refs/remotes/trees-from-two/*" &&
1362                 git config --add remote.two.fetch "+refs/blobs/*:refs/remotes/blobs-from-two/*" &&
1363                 git fetch --no-tags two &&
1364
1365                 test_must_fail git push origin refs/remotes/two/another:dst 2>err &&
1366                 test_i18ngrep "error: The destination you" err &&
1367
1368                 test_must_fail git push origin refs/remotes/tags-from-two/my-tag:dst-tag 2>err &&
1369                 test_i18ngrep "error: The destination you" err &&
1370
1371                 test_must_fail git push origin refs/remotes/trees-from-two/my-head-tree:dst-tree 2>err &&
1372                 test_i18ngrep "error: The destination you" err &&
1373
1374                 test_must_fail git push origin refs/remotes/blobs-from-two/my-file-blob:dst-blob 2>err &&
1375                 test_i18ngrep "error: The destination you" err
1376         )
1377 '
1378
1379 test_done