The second batch
[git] / t / t5582-fetch-negative-refspec.sh
1 #!/bin/sh
2 # Copyright (c) 2020, Jacob Keller.
3
4 test_description='"git fetch" with negative refspecs.
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 test_expect_success setup '
14         echo >file original &&
15         git add file &&
16         git commit -a -m original
17 '
18
19 test_expect_success "clone and setup child repos" '
20         git clone . one &&
21         (
22                 cd one &&
23                 echo >file updated by one &&
24                 git commit -a -m "updated by one" &&
25                 git switch -c alternate &&
26                 echo >file updated again by one &&
27                 git commit -a -m "updated by one again" &&
28                 git switch main
29         ) &&
30         git clone . two &&
31         (
32                 cd two &&
33                 git config branch.main.remote one &&
34                 git config remote.one.url ../one/.git/ &&
35                 git config remote.one.fetch +refs/heads/*:refs/remotes/one/* &&
36                 git config --add remote.one.fetch ^refs/heads/alternate
37         ) &&
38         git clone . three
39 '
40
41 test_expect_success "fetch one" '
42         echo >file updated by origin &&
43         git commit -a -m "updated by origin" &&
44         (
45                 cd two &&
46                 test_must_fail git rev-parse --verify refs/remotes/one/alternate &&
47                 git fetch one &&
48                 test_must_fail git rev-parse --verify refs/remotes/one/alternate &&
49                 git rev-parse --verify refs/remotes/one/main &&
50                 mine=$(git rev-parse refs/remotes/one/main) &&
51                 his=$(cd ../one && git rev-parse refs/heads/main) &&
52                 test "z$mine" = "z$his"
53         )
54 '
55
56 test_expect_success "fetch with negative refspec on commandline" '
57         echo >file updated by origin again &&
58         git commit -a -m "updated by origin again" &&
59         (
60                 cd three &&
61                 alternate_in_one=$(cd ../one && git rev-parse refs/heads/alternate) &&
62                 echo $alternate_in_one >expect &&
63                 git fetch ../one/.git refs/heads/*:refs/remotes/one/* ^refs/heads/main &&
64                 cut -f -1 .git/FETCH_HEAD >actual &&
65                 test_cmp expect actual
66         )
67 '
68
69 test_expect_success "fetch with negative sha1 refspec fails" '
70         echo >file updated by origin yet again &&
71         git commit -a -m "updated by origin yet again" &&
72         (
73                 cd three &&
74                 main_in_one=$(cd ../one && git rev-parse refs/heads/main) &&
75                 test_must_fail git fetch ../one/.git refs/heads/*:refs/remotes/one/* ^$main_in_one
76         )
77 '
78
79 test_expect_success "fetch with negative pattern refspec" '
80         echo >file updated by origin once more &&
81         git commit -a -m "updated by origin once more" &&
82         (
83                 cd three &&
84                 alternate_in_one=$(cd ../one && git rev-parse refs/heads/alternate) &&
85                 echo $alternate_in_one >expect &&
86                 git fetch ../one/.git refs/heads/*:refs/remotes/one/* ^refs/heads/m* &&
87                 cut -f -1 .git/FETCH_HEAD >actual &&
88                 test_cmp expect actual
89         )
90 '
91
92 test_expect_success "fetch with negative pattern refspec does not expand prefix" '
93         echo >file updated by origin another time &&
94         git commit -a -m "updated by origin another time" &&
95         (
96                 cd three &&
97                 alternate_in_one=$(cd ../one && git rev-parse refs/heads/alternate) &&
98                 main_in_one=$(cd ../one && git rev-parse refs/heads/main) &&
99                 echo $alternate_in_one >expect &&
100                 echo $main_in_one >>expect &&
101                 git fetch ../one/.git refs/heads/*:refs/remotes/one/* ^main &&
102                 cut -f -1 .git/FETCH_HEAD >actual &&
103                 test_cmp expect actual
104         )
105 '
106
107 test_expect_success "fetch with negative refspec avoids duplicate conflict" '
108         cd "$D" &&
109         (
110                 cd one &&
111                 git branch dups/a &&
112                 git branch dups/b &&
113                 git branch dups/c &&
114                 git branch other/a &&
115                 git rev-parse --verify refs/heads/other/a >../expect &&
116                 git rev-parse --verify refs/heads/dups/b >>../expect &&
117                 git rev-parse --verify refs/heads/dups/c >>../expect
118         ) &&
119         (
120                 cd three &&
121                 git fetch ../one/.git ^refs/heads/dups/a refs/heads/dups/*:refs/dups/* refs/heads/other/a:refs/dups/a &&
122                 git rev-parse --verify refs/dups/a >../actual &&
123                 git rev-parse --verify refs/dups/b >>../actual &&
124                 git rev-parse --verify refs/dups/c >>../actual
125         ) &&
126         test_cmp expect actual
127 '
128
129 test_expect_success "push --prune with negative refspec" '
130         (
131                 cd two &&
132                 git branch prune/a &&
133                 git branch prune/b &&
134                 git branch prune/c &&
135                 git push ../three refs/heads/prune/* &&
136                 git branch -d prune/a &&
137                 git branch -d prune/b &&
138                 git push --prune ../three refs/heads/prune/* ^refs/heads/prune/b
139         ) &&
140         (
141                 cd three &&
142                 test_write_lines b c >expect &&
143                 git for-each-ref --format="%(refname:lstrip=3)" refs/heads/prune/ >actual &&
144                 test_cmp expect actual
145         )
146 '
147
148 test_expect_success "push --prune with negative refspec apply to the destination" '
149         (
150                 cd two &&
151                 git branch ours/a &&
152                 git branch ours/b &&
153                 git branch ours/c &&
154                 git push ../three refs/heads/ours/*:refs/heads/theirs/* &&
155                 git branch -d ours/a &&
156                 git branch -d ours/b &&
157                 git push --prune ../three refs/heads/ours/*:refs/heads/theirs/* ^refs/heads/theirs/b
158         ) &&
159         (
160                 cd three &&
161                 test_write_lines b c >expect &&
162                 git for-each-ref --format="%(refname:lstrip=3)" refs/heads/theirs/ >actual &&
163                 test_cmp expect actual
164         )
165 '
166
167 test_expect_success "fetch --prune with negative refspec" '
168         (
169                 cd two &&
170                 git branch fetch/a &&
171                 git branch fetch/b &&
172                 git branch fetch/c
173         ) &&
174         (
175                 cd three &&
176                 git fetch ../two/.git refs/heads/fetch/*:refs/heads/copied/*
177         ) &&
178         (
179                 cd two &&
180                 git branch -d fetch/a &&
181                 git branch -d fetch/b
182         ) &&
183         (
184                 cd three &&
185                 test_write_lines b c >expect &&
186                 git fetch -v ../two/.git --prune refs/heads/fetch/*:refs/heads/copied/* ^refs/heads/fetch/b &&
187                 git for-each-ref --format="%(refname:lstrip=3)" refs/heads/copied/ >actual &&
188                 test_cmp expect actual
189         )
190 '
191
192 test_expect_success "push with matching : and negative refspec" '
193         # Manually handle cleanup, since test_config is not
194         # prepared to take arbitrary options like --add
195         test_when_finished "test_unconfig -C two remote.one.push" &&
196
197         # For convenience, we use "master" to refer to the name of
198         # the branch created by default in the following.
199         #
200         # Repositories two and one have branches other than "master"
201         # but they have no overlap---"master" is the only one that
202         # is shared between them.  And the master branch at two is
203         # behind the master branch at one by one commit.
204         git -C two config --add remote.one.push : &&
205
206         # A matching push tries to update master, fails due to non-ff
207         test_must_fail git -C two push one &&
208
209         # "master" may actually not be "master"---find it out.
210         current=$(git symbolic-ref HEAD) &&
211
212         # If master is in negative refspec, then the command will not attempt
213         # to push and succeed.
214         git -C two config --add remote.one.push "^$current" &&
215
216         # With "master" excluded, this push is a no-op.  Nothing gets
217         # pushed and it succeeds.
218         git -C two push -v one
219 '
220
221 test_expect_success "push with matching +: and negative refspec" '
222         test_when_finished "test_unconfig -C two remote.one.push" &&
223
224         # The same set-up as above, whose side-effect was a no-op.
225         git -C two config --add remote.one.push +: &&
226
227         # The push refuses to update the "master" branch that is checked
228         # out in the "one" repository, even when it is forced with +:
229         test_must_fail git -C two push one &&
230
231         # "master" may actually not be "master"---find it out.
232         current=$(git symbolic-ref HEAD) &&
233
234         # If master is in negative refspec, then the command will not attempt
235         # to push and succeed
236         git -C two config --add remote.one.push "^$current" &&
237
238         # With "master" excluded, this push is a no-op.  Nothing gets
239         # pushed and it succeeds.
240         git -C two push -v one
241 '
242
243 test_expect_success '--prefetch correctly modifies refspecs' '
244         git -C one config --unset-all remote.origin.fetch &&
245         git -C one config --add remote.origin.fetch ^refs/heads/bogus/ignore &&
246         git -C one config --add remote.origin.fetch "refs/tags/*:refs/tags/*" &&
247         git -C one config --add remote.origin.fetch "refs/heads/bogus/*:bogus/*" &&
248
249         git tag -a -m never never-fetch-tag HEAD &&
250
251         git branch bogus/fetched HEAD~1 &&
252         git branch bogus/ignore HEAD &&
253
254         git -C one fetch --prefetch --no-tags &&
255         test_must_fail git -C one rev-parse never-fetch-tag &&
256         git -C one rev-parse refs/prefetch/bogus/fetched &&
257         test_must_fail git -C one rev-parse refs/prefetch/bogus/ignore &&
258
259         # correctly handle when refspec set becomes empty
260         # after removing the refs/tags/* refspec.
261         git -C one config --unset-all remote.origin.fetch &&
262         git -C one config --add remote.origin.fetch "refs/tags/*:refs/tags/*" &&
263
264         git -C one fetch --prefetch --no-tags &&
265         test_must_fail git -C one rev-parse never-fetch-tag &&
266
267         # The refspec for refs that are not fully qualified
268         # are filtered multiple times.
269         git -C one rev-parse refs/prefetch/bogus/fetched &&
270         test_must_fail git -C one rev-parse refs/prefetch/bogus/ignore
271 '
272
273 test_expect_success '--prefetch succeeds when refspec becomes empty' '
274         git checkout bogus/fetched &&
275         test_commit extra &&
276
277         git -C one config --unset-all remote.origin.fetch &&
278         git -C one config --unset branch.main.remote &&
279         git -C one config remote.origin.fetch "+refs/tags/extra" &&
280         git -C one config remote.origin.skipfetchall true &&
281         git -C one config remote.origin.tagopt "--no-tags" &&
282
283         git -C one fetch --prefetch
284 '
285
286 test_done