add open_nofollow() helper
[git] / t / t5533-push-cas.sh
1 #!/bin/sh
2
3 test_description='compare & swap push force/delete safety'
4
5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
7
8 . ./test-lib.sh
9
10 setup_srcdst_basic () {
11         rm -fr src dst &&
12         git clone --no-local . src &&
13         git clone --no-local src dst &&
14         (
15                 cd src && git checkout HEAD^0
16         )
17 }
18
19 # For tests with "--force-if-includes".
20 setup_src_dup_dst () {
21         rm -fr src dup dst &&
22         git init --bare dst &&
23         git clone --no-local dst src &&
24         git clone --no-local dst dup
25         (
26                 cd src &&
27                 test_commit A &&
28                 test_commit B &&
29                 test_commit C &&
30                 git push origin
31         ) &&
32         (
33                 cd dup &&
34                 git fetch &&
35                 git merge origin/main &&
36                 git switch -c branch main~2 &&
37                 test_commit D &&
38                 test_commit E &&
39                 git push origin --all
40         ) &&
41         (
42                 cd src &&
43                 git switch main &&
44                 git fetch --all &&
45                 git branch branch --track origin/branch &&
46                 git rebase origin/main
47         ) &&
48         (
49                 cd dup &&
50                 git switch main &&
51                 test_commit F &&
52                 test_commit G &&
53                 git switch branch &&
54                 test_commit H &&
55                 git push origin --all
56         )
57 }
58
59 test_expect_success setup '
60         # create template repository
61         test_commit A &&
62         test_commit B &&
63         test_commit C
64 '
65
66 test_expect_success 'push to update (protected)' '
67         setup_srcdst_basic &&
68         (
69                 cd dst &&
70                 test_commit D &&
71                 test_must_fail git push --force-with-lease=main:main origin main 2>err &&
72                 grep "stale info" err
73         ) &&
74         git ls-remote . refs/heads/main >expect &&
75         git ls-remote src refs/heads/main >actual &&
76         test_cmp expect actual
77 '
78
79 test_expect_success 'push to update (protected, forced)' '
80         setup_srcdst_basic &&
81         (
82                 cd dst &&
83                 test_commit D &&
84                 git push --force --force-with-lease=main:main origin main 2>err &&
85                 grep "forced update" err
86         ) &&
87         git ls-remote dst refs/heads/main >expect &&
88         git ls-remote src refs/heads/main >actual &&
89         test_cmp expect actual
90 '
91
92 test_expect_success 'push to update (protected, tracking)' '
93         setup_srcdst_basic &&
94         (
95                 cd src &&
96                 git checkout main &&
97                 test_commit D &&
98                 git checkout HEAD^0
99         ) &&
100         git ls-remote src refs/heads/main >expect &&
101         (
102                 cd dst &&
103                 test_commit E &&
104                 git ls-remote . refs/remotes/origin/main >expect &&
105                 test_must_fail git push --force-with-lease=main origin main &&
106                 git ls-remote . refs/remotes/origin/main >actual &&
107                 test_cmp expect actual
108         ) &&
109         git ls-remote src refs/heads/main >actual &&
110         test_cmp expect actual
111 '
112
113 test_expect_success 'push to update (protected, tracking, forced)' '
114         setup_srcdst_basic &&
115         (
116                 cd src &&
117                 git checkout main &&
118                 test_commit D &&
119                 git checkout HEAD^0
120         ) &&
121         (
122                 cd dst &&
123                 test_commit E &&
124                 git ls-remote . refs/remotes/origin/main >expect &&
125                 git push --force --force-with-lease=main origin main
126         ) &&
127         git ls-remote dst refs/heads/main >expect &&
128         git ls-remote src refs/heads/main >actual &&
129         test_cmp expect actual
130 '
131
132 test_expect_success 'push to update (allowed)' '
133         setup_srcdst_basic &&
134         (
135                 cd dst &&
136                 test_commit D &&
137                 git push --force-with-lease=main:main^ origin main
138         ) &&
139         git ls-remote dst refs/heads/main >expect &&
140         git ls-remote src refs/heads/main >actual &&
141         test_cmp expect actual
142 '
143
144 test_expect_success 'push to update (allowed, tracking)' '
145         setup_srcdst_basic &&
146         (
147                 cd dst &&
148                 test_commit D &&
149                 git push --force-with-lease=main origin main 2>err &&
150                 ! grep "forced update" err
151         ) &&
152         git ls-remote dst refs/heads/main >expect &&
153         git ls-remote src refs/heads/main >actual &&
154         test_cmp expect actual
155 '
156
157 test_expect_success 'push to update (allowed even though no-ff)' '
158         setup_srcdst_basic &&
159         (
160                 cd dst &&
161                 git reset --hard HEAD^ &&
162                 test_commit D &&
163                 git push --force-with-lease=main origin main 2>err &&
164                 grep "forced update" err
165         ) &&
166         git ls-remote dst refs/heads/main >expect &&
167         git ls-remote src refs/heads/main >actual &&
168         test_cmp expect actual
169 '
170
171 test_expect_success 'push to delete (protected)' '
172         setup_srcdst_basic &&
173         git ls-remote src refs/heads/main >expect &&
174         (
175                 cd dst &&
176                 test_must_fail git push --force-with-lease=main:main^ origin :main
177         ) &&
178         git ls-remote src refs/heads/main >actual &&
179         test_cmp expect actual
180 '
181
182 test_expect_success 'push to delete (protected, forced)' '
183         setup_srcdst_basic &&
184         (
185                 cd dst &&
186                 git push --force --force-with-lease=main:main^ origin :main
187         ) &&
188         git ls-remote src refs/heads/main >actual &&
189         test_must_be_empty actual
190 '
191
192 test_expect_success 'push to delete (allowed)' '
193         setup_srcdst_basic &&
194         (
195                 cd dst &&
196                 git push --force-with-lease=main origin :main 2>err &&
197                 grep deleted err
198         ) &&
199         git ls-remote src refs/heads/main >actual &&
200         test_must_be_empty actual
201 '
202
203 test_expect_success 'cover everything with default force-with-lease (protected)' '
204         setup_srcdst_basic &&
205         (
206                 cd src &&
207                 git branch nain main^
208         ) &&
209         git ls-remote src refs/heads/\* >expect &&
210         (
211                 cd dst &&
212                 test_must_fail git push --force-with-lease origin main main:nain
213         ) &&
214         git ls-remote src refs/heads/\* >actual &&
215         test_cmp expect actual
216 '
217
218 test_expect_success 'cover everything with default force-with-lease (allowed)' '
219         setup_srcdst_basic &&
220         (
221                 cd src &&
222                 git branch nain main^
223         ) &&
224         (
225                 cd dst &&
226                 git fetch &&
227                 git push --force-with-lease origin main main:nain
228         ) &&
229         git ls-remote dst refs/heads/main |
230         sed -e "s/main/nain/" >expect &&
231         git ls-remote src refs/heads/nain >actual &&
232         test_cmp expect actual
233 '
234
235 test_expect_success 'new branch covered by force-with-lease' '
236         setup_srcdst_basic &&
237         (
238                 cd dst &&
239                 git branch branch main &&
240                 git push --force-with-lease=branch origin branch
241         ) &&
242         git ls-remote dst refs/heads/branch >expect &&
243         git ls-remote src refs/heads/branch >actual &&
244         test_cmp expect actual
245 '
246
247 test_expect_success 'new branch covered by force-with-lease (explicit)' '
248         setup_srcdst_basic &&
249         (
250                 cd dst &&
251                 git branch branch main &&
252                 git push --force-with-lease=branch: origin branch
253         ) &&
254         git ls-remote dst refs/heads/branch >expect &&
255         git ls-remote src refs/heads/branch >actual &&
256         test_cmp expect actual
257 '
258
259 test_expect_success 'new branch already exists' '
260         setup_srcdst_basic &&
261         (
262                 cd src &&
263                 git checkout -b branch main &&
264                 test_commit F
265         ) &&
266         (
267                 cd dst &&
268                 git branch branch main &&
269                 test_must_fail git push --force-with-lease=branch: origin branch
270         )
271 '
272
273 test_expect_success 'background updates of REMOTE can be mitigated with a non-updated REMOTE-push' '
274         rm -rf src dst &&
275         git init --bare src.bare &&
276         test_when_finished "rm -rf src.bare" &&
277         git clone --no-local src.bare dst &&
278         test_when_finished "rm -rf dst" &&
279         (
280                 cd dst &&
281                 test_commit G &&
282                 git remote add origin-push ../src.bare &&
283                 git push origin-push main:main
284         ) &&
285         git clone --no-local src.bare dst2 &&
286         test_when_finished "rm -rf dst2" &&
287         (
288                 cd dst2 &&
289                 test_commit H &&
290                 git push
291         ) &&
292         (
293                 cd dst &&
294                 test_commit I &&
295                 git fetch origin &&
296                 test_must_fail git push --force-with-lease origin-push &&
297                 git fetch origin-push &&
298                 git push --force-with-lease origin-push
299         )
300 '
301
302 test_expect_success 'background updates to remote can be mitigated with "--force-if-includes"' '
303         setup_src_dup_dst &&
304         test_when_finished "rm -fr dst src dup" &&
305         git ls-remote dst refs/heads/main >expect.main &&
306         git ls-remote dst refs/heads/branch >expect.branch &&
307         (
308                 cd src &&
309                 git switch branch &&
310                 test_commit I &&
311                 git switch main &&
312                 test_commit J &&
313                 git fetch --all &&
314                 test_must_fail git push --force-with-lease --force-if-includes --all
315         ) &&
316         git ls-remote dst refs/heads/main >actual.main &&
317         git ls-remote dst refs/heads/branch >actual.branch &&
318         test_cmp expect.main actual.main &&
319         test_cmp expect.branch actual.branch
320 '
321
322 test_expect_success 'background updates to remote can be mitigated with "push.useForceIfIncludes"' '
323         setup_src_dup_dst &&
324         test_when_finished "rm -fr dst src dup" &&
325         git ls-remote dst refs/heads/main >expect.main &&
326         (
327                 cd src &&
328                 git switch branch &&
329                 test_commit I &&
330                 git switch main &&
331                 test_commit J &&
332                 git fetch --all &&
333                 git config --local push.useForceIfIncludes true &&
334                 test_must_fail git push --force-with-lease=main origin main
335         ) &&
336         git ls-remote dst refs/heads/main >actual.main &&
337         test_cmp expect.main actual.main
338 '
339
340 test_expect_success '"--force-if-includes" should be disabled for --force-with-lease="<refname>:<expect>"' '
341         setup_src_dup_dst &&
342         test_when_finished "rm -fr dst src dup" &&
343         git ls-remote dst refs/heads/main >expect.main &&
344         (
345                 cd src &&
346                 git switch branch &&
347                 test_commit I &&
348                 git switch main &&
349                 test_commit J &&
350                 remote_head="$(git rev-parse refs/remotes/origin/main)" &&
351                 git fetch --all &&
352                 test_must_fail git push --force-if-includes --force-with-lease="main:$remote_head" 2>err &&
353                 grep "stale info" err
354         ) &&
355         git ls-remote dst refs/heads/main >actual.main &&
356         test_cmp expect.main actual.main
357 '
358
359 test_expect_success '"--force-if-includes" should allow forced update after a rebase ("pull --rebase")' '
360         setup_src_dup_dst &&
361         test_when_finished "rm -fr dst src dup" &&
362         (
363                 cd src &&
364                 git switch branch &&
365                 test_commit I &&
366                 git switch main &&
367                 test_commit J &&
368                 git pull --rebase origin main &&
369                 git push --force-if-includes --force-with-lease="main"
370         )
371 '
372
373 test_expect_success '"--force-if-includes" should allow forced update after a rebase ("pull --rebase", local rebase)' '
374         setup_src_dup_dst &&
375         test_when_finished "rm -fr dst src dup" &&
376         (
377                 cd src &&
378                 git switch branch &&
379                 test_commit I &&
380                 git switch main &&
381                 test_commit J &&
382                 git pull --rebase origin main &&
383                 git rebase --onto HEAD~4 HEAD~1 &&
384                 git push --force-if-includes --force-with-lease="main"
385         )
386 '
387
388 test_expect_success '"--force-if-includes" should allow deletes' '
389         setup_src_dup_dst &&
390         test_when_finished "rm -fr dst src dup" &&
391         (
392                 cd src &&
393                 git switch branch &&
394                 git pull --rebase origin branch &&
395                 git push --force-if-includes --force-with-lease="branch" origin :branch
396         )
397 '
398
399 test_done