|  | #!/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 && | 
|  | 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 && | 
|  | 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 && | 
|  | ! 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 && | 
|  | 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 && | 
|  | 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 && | 
|  | 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_done |