| #!/bin/sh | |
| test_description='compare & swap push force/delete safety' | |
| GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main | |
| export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME | |
| . ./test-lib.sh | |
| setup_srcdst_basic () { | |
| rm -fr src dst && | |
| git clone --no-local . src && | |
| git clone --no-local src dst && | |
| ( | |
| cd src && git checkout HEAD^0 | |
| ) | |
| } | |
| # For tests with "--force-if-includes". | |
| setup_src_dup_dst () { | |
| rm -fr src dup dst && | |
| git init --bare dst && | |
| git clone --no-local dst src && | |
| git clone --no-local dst dup | |
| ( | |
| cd src && | |
| test_commit A && | |
| test_commit B && | |
| test_commit C && | |
| git push origin | |
| ) && | |
| ( | |
| cd dup && | |
| git fetch && | |
| git merge origin/main && | |
| git switch -c branch main~2 && | |
| test_commit D && | |
| test_commit E && | |
| git push origin --all | |
| ) && | |
| ( | |
| cd src && | |
| git switch main && | |
| git fetch --all && | |
| git branch branch --track origin/branch && | |
| git rebase origin/main | |
| ) && | |
| ( | |
| cd dup && | |
| git switch main && | |
| test_commit F && | |
| test_commit G && | |
| git switch branch && | |
| test_commit H && | |
| git push origin --all | |
| ) | |
| } | |
| test_expect_success setup ' | |
| # create template repository | |
| test_commit A && | |
| test_commit B && | |
| test_commit C | |
| ' | |
| test_expect_success 'push to update (protected)' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd dst && | |
| test_commit D && | |
| test_must_fail git push --force-with-lease=main:main origin main 2>err && | |
| test_grep "stale info" err | |
| ) && | |
| git ls-remote . refs/heads/main >expect && | |
| git ls-remote src refs/heads/main >actual && | |
| test_cmp expect actual | |
| ' | |
| test_expect_success 'push to update (protected, forced)' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd dst && | |
| test_commit D && | |
| git push --force --force-with-lease=main:main origin main 2>err && | |
| test_grep "forced update" err | |
| ) && | |
| git ls-remote dst refs/heads/main >expect && | |
| git ls-remote src refs/heads/main >actual && | |
| test_cmp expect actual | |
| ' | |
| test_expect_success 'push to update (protected, tracking)' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd src && | |
| git checkout main && | |
| test_commit D && | |
| git checkout HEAD^0 | |
| ) && | |
| git ls-remote src refs/heads/main >expect && | |
| ( | |
| cd dst && | |
| test_commit E && | |
| git ls-remote . refs/remotes/origin/main >expect && | |
| test_must_fail git push --force-with-lease=main origin main && | |
| git ls-remote . refs/remotes/origin/main >actual && | |
| test_cmp expect actual | |
| ) && | |
| git ls-remote src refs/heads/main >actual && | |
| test_cmp expect actual | |
| ' | |
| test_expect_success 'push to update (protected, tracking, forced)' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd src && | |
| git checkout main && | |
| test_commit D && | |
| git checkout HEAD^0 | |
| ) && | |
| ( | |
| cd dst && | |
| test_commit E && | |
| git ls-remote . refs/remotes/origin/main >expect && | |
| git push --force --force-with-lease=main origin main | |
| ) && | |
| git ls-remote dst refs/heads/main >expect && | |
| git ls-remote src refs/heads/main >actual && | |
| test_cmp expect actual | |
| ' | |
| test_expect_success 'push to update (allowed)' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd dst && | |
| test_commit D && | |
| git push --force-with-lease=main:main^ origin main | |
| ) && | |
| git ls-remote dst refs/heads/main >expect && | |
| git ls-remote src refs/heads/main >actual && | |
| test_cmp expect actual | |
| ' | |
| test_expect_success 'push to update (allowed, tracking)' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd dst && | |
| test_commit D && | |
| git push --force-with-lease=main origin main 2>err && | |
| test_grep ! "forced update" err | |
| ) && | |
| git ls-remote dst refs/heads/main >expect && | |
| git ls-remote src refs/heads/main >actual && | |
| test_cmp expect actual | |
| ' | |
| test_expect_success 'push to update (allowed even though no-ff)' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd dst && | |
| git reset --hard HEAD^ && | |
| test_commit D && | |
| git push --force-with-lease=main origin main 2>err && | |
| test_grep "forced update" err | |
| ) && | |
| git ls-remote dst refs/heads/main >expect && | |
| git ls-remote src refs/heads/main >actual && | |
| test_cmp expect actual | |
| ' | |
| test_expect_success 'push to delete (protected)' ' | |
| setup_srcdst_basic && | |
| git ls-remote src refs/heads/main >expect && | |
| ( | |
| cd dst && | |
| test_must_fail git push --force-with-lease=main:main^ origin :main | |
| ) && | |
| git ls-remote src refs/heads/main >actual && | |
| test_cmp expect actual | |
| ' | |
| test_expect_success 'push to delete (protected, forced)' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd dst && | |
| git push --force --force-with-lease=main:main^ origin :main | |
| ) && | |
| git ls-remote src refs/heads/main >actual && | |
| test_must_be_empty actual | |
| ' | |
| test_expect_success 'push to delete (allowed)' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd dst && | |
| git push --force-with-lease=main origin :main 2>err && | |
| test_grep deleted err | |
| ) && | |
| git ls-remote src refs/heads/main >actual && | |
| test_must_be_empty actual | |
| ' | |
| test_expect_success 'cover everything with default force-with-lease (protected)' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd src && | |
| git branch nain main^ | |
| ) && | |
| git ls-remote src refs/heads/\* >expect && | |
| ( | |
| cd dst && | |
| test_must_fail git push --force-with-lease origin main main:nain | |
| ) && | |
| git ls-remote src refs/heads/\* >actual && | |
| test_cmp expect actual | |
| ' | |
| test_expect_success 'cover everything with default force-with-lease (allowed)' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd src && | |
| git branch nain main^ | |
| ) && | |
| ( | |
| cd dst && | |
| git fetch && | |
| git push --force-with-lease origin main main:nain | |
| ) && | |
| git ls-remote dst refs/heads/main | | |
| sed -e "s/main/nain/" >expect && | |
| git ls-remote src refs/heads/nain >actual && | |
| test_cmp expect actual | |
| ' | |
| test_expect_success 'new branch covered by force-with-lease' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd dst && | |
| git branch branch main && | |
| git push --force-with-lease=branch origin branch | |
| ) && | |
| git ls-remote dst refs/heads/branch >expect && | |
| git ls-remote src refs/heads/branch >actual && | |
| test_cmp expect actual | |
| ' | |
| test_expect_success 'new branch covered by force-with-lease (explicit)' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd dst && | |
| git branch branch main && | |
| git push --force-with-lease=branch: origin branch | |
| ) && | |
| git ls-remote dst refs/heads/branch >expect && | |
| git ls-remote src refs/heads/branch >actual && | |
| test_cmp expect actual | |
| ' | |
| test_expect_success 'new branch already exists' ' | |
| setup_srcdst_basic && | |
| ( | |
| cd src && | |
| git checkout -b branch main && | |
| test_commit F | |
| ) && | |
| ( | |
| cd dst && | |
| git branch branch main && | |
| test_must_fail git push --force-with-lease=branch: origin branch | |
| ) | |
| ' | |
| test_expect_success 'background updates of REMOTE can be mitigated with a non-updated REMOTE-push' ' | |
| rm -rf src dst && | |
| git init --bare src.bare && | |
| test_when_finished "rm -rf src.bare" && | |
| git clone --no-local src.bare dst && | |
| test_when_finished "rm -rf dst" && | |
| ( | |
| cd dst && | |
| test_commit G && | |
| git remote add origin-push ../src.bare && | |
| git push origin-push main:main | |
| ) && | |
| git clone --no-local src.bare dst2 && | |
| test_when_finished "rm -rf dst2" && | |
| ( | |
| cd dst2 && | |
| test_commit H && | |
| git push | |
| ) && | |
| ( | |
| cd dst && | |
| test_commit I && | |
| git fetch origin && | |
| test_must_fail git push --force-with-lease origin-push && | |
| git fetch origin-push && | |
| git push --force-with-lease origin-push | |
| ) | |
| ' | |
| test_expect_success 'background updates to remote can be mitigated with "--force-if-includes"' ' | |
| setup_src_dup_dst && | |
| test_when_finished "rm -fr dst src dup" && | |
| git ls-remote dst refs/heads/main >expect.main && | |
| git ls-remote dst refs/heads/branch >expect.branch && | |
| ( | |
| cd src && | |
| git switch branch && | |
| test_commit I && | |
| git switch main && | |
| test_commit J && | |
| git fetch --all && | |
| test_must_fail git push --force-with-lease --force-if-includes --all | |
| ) && | |
| git ls-remote dst refs/heads/main >actual.main && | |
| git ls-remote dst refs/heads/branch >actual.branch && | |
| test_cmp expect.main actual.main && | |
| test_cmp expect.branch actual.branch | |
| ' | |
| test_expect_success 'background updates to remote can be mitigated with "push.useForceIfIncludes"' ' | |
| setup_src_dup_dst && | |
| test_when_finished "rm -fr dst src dup" && | |
| git ls-remote dst refs/heads/main >expect.main && | |
| ( | |
| cd src && | |
| git switch branch && | |
| test_commit I && | |
| git switch main && | |
| test_commit J && | |
| git fetch --all && | |
| git config --local push.useForceIfIncludes true && | |
| test_must_fail git push --force-with-lease=main origin main | |
| ) && | |
| git ls-remote dst refs/heads/main >actual.main && | |
| test_cmp expect.main actual.main | |
| ' | |
| test_expect_success '"--force-if-includes" should be disabled for --force-with-lease="<refname>:<expect>"' ' | |
| setup_src_dup_dst && | |
| test_when_finished "rm -fr dst src dup" && | |
| git ls-remote dst refs/heads/main >expect.main && | |
| ( | |
| cd src && | |
| git switch branch && | |
| test_commit I && | |
| git switch main && | |
| test_commit J && | |
| remote_head="$(git rev-parse refs/remotes/origin/main)" && | |
| git fetch --all && | |
| test_must_fail git push --force-if-includes --force-with-lease="main:$remote_head" 2>err && | |
| test_grep "stale info" err | |
| ) && | |
| git ls-remote dst refs/heads/main >actual.main && | |
| test_cmp expect.main actual.main | |
| ' | |
| test_expect_success '"--force-if-includes" should allow forced update after a rebase ("pull --rebase")' ' | |
| setup_src_dup_dst && | |
| test_when_finished "rm -fr dst src dup" && | |
| ( | |
| cd src && | |
| git switch branch && | |
| test_commit I && | |
| git switch main && | |
| test_commit J && | |
| git pull --rebase origin main && | |
| git push --force-if-includes --force-with-lease="main" | |
| ) | |
| ' | |
| test_expect_success '"--force-if-includes" should allow forced update after a rebase ("pull --rebase", local rebase)' ' | |
| setup_src_dup_dst && | |
| test_when_finished "rm -fr dst src dup" && | |
| ( | |
| cd src && | |
| git switch branch && | |
| test_commit I && | |
| git switch main && | |
| test_commit J && | |
| git pull --rebase origin main && | |
| git rebase --onto HEAD~4 HEAD~1 && | |
| git push --force-if-includes --force-with-lease="main" | |
| ) | |
| ' | |
| test_expect_success '"--force-if-includes" should allow deletes' ' | |
| setup_src_dup_dst && | |
| test_when_finished "rm -fr dst src dup" && | |
| ( | |
| cd src && | |
| git switch branch && | |
| git pull --rebase origin branch && | |
| git push --force-if-includes --force-with-lease="branch" origin :branch | |
| ) | |
| ' | |
| test_expect_success '"--force-if-includes" should allow forced update when remote-tracking ref has no reflog' ' | |
| setup_src_dup_dst && | |
| test_when_finished "rm -fr dst src dup" && | |
| ( | |
| cd src && | |
| git switch branch && | |
| git pull --rebase origin branch && | |
| # the bug needs a remote-tracking ref with no reflog, and | |
| # the fetch above wrote one | |
| git reflog expire --expire=all refs/remotes/origin/branch && | |
| git reset --hard HEAD^ && | |
| test_commit I && | |
| git push --force-if-includes --force-with-lease="branch" | |
| ) | |
| ' | |
| test_done |