3 test_description='compare & swap push force/delete safety'
5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
10 setup_srcdst_basic () {
12 git clone --no-local . src &&
13 git clone --no-local src dst &&
15 cd src && git checkout HEAD^0
19 # For tests with "--force-if-includes".
20 setup_src_dup_dst () {
22 git init --bare dst &&
23 git clone --no-local dst src &&
24 git clone --no-local dst dup
35 git merge origin/main &&
36 git switch -c branch main~2 &&
45 git branch branch --track origin/branch &&
46 git rebase origin/main
59 test_expect_success setup '
60 # create template repository
66 test_expect_success 'push to update (protected)' '
71 test_must_fail git push --force-with-lease=main:main origin main 2>err &&
74 git ls-remote . refs/heads/main >expect &&
75 git ls-remote src refs/heads/main >actual &&
76 test_cmp expect actual
79 test_expect_success 'push to update (protected, forced)' '
84 git push --force --force-with-lease=main:main origin main 2>err &&
85 grep "forced update" err
87 git ls-remote dst refs/heads/main >expect &&
88 git ls-remote src refs/heads/main >actual &&
89 test_cmp expect actual
92 test_expect_success 'push to update (protected, tracking)' '
100 git ls-remote src refs/heads/main >expect &&
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
109 git ls-remote src refs/heads/main >actual &&
110 test_cmp expect actual
113 test_expect_success 'push to update (protected, tracking, forced)' '
114 setup_srcdst_basic &&
124 git ls-remote . refs/remotes/origin/main >expect &&
125 git push --force --force-with-lease=main origin main
127 git ls-remote dst refs/heads/main >expect &&
128 git ls-remote src refs/heads/main >actual &&
129 test_cmp expect actual
132 test_expect_success 'push to update (allowed)' '
133 setup_srcdst_basic &&
137 git push --force-with-lease=main:main^ origin main
139 git ls-remote dst refs/heads/main >expect &&
140 git ls-remote src refs/heads/main >actual &&
141 test_cmp expect actual
144 test_expect_success 'push to update (allowed, tracking)' '
145 setup_srcdst_basic &&
149 git push --force-with-lease=main origin main 2>err &&
150 ! grep "forced update" err
152 git ls-remote dst refs/heads/main >expect &&
153 git ls-remote src refs/heads/main >actual &&
154 test_cmp expect actual
157 test_expect_success 'push to update (allowed even though no-ff)' '
158 setup_srcdst_basic &&
161 git reset --hard HEAD^ &&
163 git push --force-with-lease=main origin main 2>err &&
164 grep "forced update" err
166 git ls-remote dst refs/heads/main >expect &&
167 git ls-remote src refs/heads/main >actual &&
168 test_cmp expect actual
171 test_expect_success 'push to delete (protected)' '
172 setup_srcdst_basic &&
173 git ls-remote src refs/heads/main >expect &&
176 test_must_fail git push --force-with-lease=main:main^ origin :main
178 git ls-remote src refs/heads/main >actual &&
179 test_cmp expect actual
182 test_expect_success 'push to delete (protected, forced)' '
183 setup_srcdst_basic &&
186 git push --force --force-with-lease=main:main^ origin :main
188 git ls-remote src refs/heads/main >actual &&
189 test_must_be_empty actual
192 test_expect_success 'push to delete (allowed)' '
193 setup_srcdst_basic &&
196 git push --force-with-lease=main origin :main 2>err &&
199 git ls-remote src refs/heads/main >actual &&
200 test_must_be_empty actual
203 test_expect_success 'cover everything with default force-with-lease (protected)' '
204 setup_srcdst_basic &&
207 git branch nain main^
209 git ls-remote src refs/heads/\* >expect &&
212 test_must_fail git push --force-with-lease origin main main:nain
214 git ls-remote src refs/heads/\* >actual &&
215 test_cmp expect actual
218 test_expect_success 'cover everything with default force-with-lease (allowed)' '
219 setup_srcdst_basic &&
222 git branch nain main^
227 git push --force-with-lease origin main main:nain
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
235 test_expect_success 'new branch covered by force-with-lease' '
236 setup_srcdst_basic &&
239 git branch branch main &&
240 git push --force-with-lease=branch origin branch
242 git ls-remote dst refs/heads/branch >expect &&
243 git ls-remote src refs/heads/branch >actual &&
244 test_cmp expect actual
247 test_expect_success 'new branch covered by force-with-lease (explicit)' '
248 setup_srcdst_basic &&
251 git branch branch main &&
252 git push --force-with-lease=branch: origin branch
254 git ls-remote dst refs/heads/branch >expect &&
255 git ls-remote src refs/heads/branch >actual &&
256 test_cmp expect actual
259 test_expect_success 'new branch already exists' '
260 setup_srcdst_basic &&
263 git checkout -b branch main &&
268 git branch branch main &&
269 test_must_fail git push --force-with-lease=branch: origin branch
273 test_expect_success 'background updates of REMOTE can be mitigated with a non-updated REMOTE-push' '
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" &&
282 git remote add origin-push ../src.bare &&
283 git push origin-push main:main
285 git clone --no-local src.bare dst2 &&
286 test_when_finished "rm -rf dst2" &&
296 test_must_fail git push --force-with-lease origin-push &&
297 git fetch origin-push &&
298 git push --force-with-lease origin-push
302 test_expect_success 'background updates to remote can be mitigated with "--force-if-includes"' '
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 &&
314 test_must_fail git push --force-with-lease --force-if-includes --all
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
322 test_expect_success 'background updates to remote can be mitigated with "push.useForceIfIncludes"' '
324 test_when_finished "rm -fr dst src dup" &&
325 git ls-remote dst refs/heads/main >expect.main &&
333 git config --local push.useForceIfIncludes true &&
334 test_must_fail git push --force-with-lease=main origin main
336 git ls-remote dst refs/heads/main >actual.main &&
337 test_cmp expect.main actual.main
340 test_expect_success '"--force-if-includes" should be disabled for --force-with-lease="<refname>:<expect>"' '
342 test_when_finished "rm -fr dst src dup" &&
343 git ls-remote dst refs/heads/main >expect.main &&
350 remote_head="$(git rev-parse refs/remotes/origin/main)" &&
352 test_must_fail git push --force-if-includes --force-with-lease="main:$remote_head" 2>err &&
353 grep "stale info" err
355 git ls-remote dst refs/heads/main >actual.main &&
356 test_cmp expect.main actual.main
359 test_expect_success '"--force-if-includes" should allow forced update after a rebase ("pull --rebase")' '
361 test_when_finished "rm -fr dst src dup" &&
368 git pull --rebase origin main &&
369 git push --force-if-includes --force-with-lease="main"
373 test_expect_success '"--force-if-includes" should allow forced update after a rebase ("pull --rebase", local rebase)' '
375 test_when_finished "rm -fr dst src dup" &&
382 git pull --rebase origin main &&
383 git rebase --onto HEAD~4 HEAD~1 &&
384 git push --force-if-includes --force-with-lease="main"
388 test_expect_success '"--force-if-includes" should allow deletes' '
390 test_when_finished "rm -fr dst src dup" &&
394 git pull --rebase origin branch &&
395 git push --force-if-includes --force-with-lease="branch" origin :branch