@@ -256,4 +256,30 @@ test_expect_success 'background updates of REMOTE can be mitigated with a non-up
)
'
+test_expect_success 'background updates of REMOTE can be mitigated with "--force-if-includes"' '
+ 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 push origin master:master
+ ) &&
+ 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 --force-if-includes origin
+ )
+'
+
test_done
new file mode 100755
@@ -0,0 +1,161 @@
+test_description='Test push "--force-if-includes" forced update safety.'
+
+. ./test-lib.sh
+
+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 foo &&
+ git push
+ ) &&
+ (
+ cd dup &&
+ git fetch &&
+ git merge origin/master &&
+ test_commit bar &&
+ git switch -c branch master~1 &&
+ test_commit baz &&
+ test_commit D &&
+ git push --all
+ ) &&
+ (
+ cd src &&
+ git switch master &&
+ git fetch --all &&
+ git branch branch --track origin/branch &&
+ git rebase origin/master
+ ) &&
+ (
+ cd dup &&
+ git switch master &&
+ test_commit qux &&
+ git switch branch &&
+ test_commit quux &&
+ git push origin --all
+ )
+}
+
+test_expect_success 'reject push if remote changes are not integrated locally (protected, all refs)' '
+ setup_src_dup_dst &&
+ test_when_finished "rm -fr dst src dup" &&
+ git ls-remote dst refs/heads/master >expect.master &&
+ git ls-remote dst refs/heads/master >expect.branch &&
+ (
+ cd src &&
+ git switch branch &&
+ test_commit wobble &&
+ git switch master &&
+ test_commit wubble &&
+ git fetch --all &&
+ test_must_fail git push --force-if-includes --all
+ ) &&
+ git ls-remote dst refs/heads/master >actual.master &&
+ git ls-remote dst refs/heads/master >actual.branch &&
+ test_cmp expect.master actual.master &&
+ test_cmp expect.branch actual.branch
+'
+
+test_expect_success 'reject push if remote changes are not integrated locally (protected, specific ref)' '
+ setup_src_dup_dst &&
+ test_when_finished "rm -fr dst src dup" &&
+ git ls-remote dst refs/heads/master >expect.master &&
+ (
+ cd src &&
+ git switch branch &&
+ test_commit wobble &&
+ git switch master &&
+ test_commit wubble &&
+ git fetch --all &&
+ test_must_fail git push --force-if-includes origin master
+ ) &&
+ git ls-remote dst refs/heads/master >actual.master &&
+ test_cmp expect.master actual.master
+'
+
+test_expect_success 'allow force push if "--force" is specified (forced, all refs)' '
+ setup_src_dup_dst &&
+ test_when_finished "rm -fr dst src dup" &&
+ (
+ cd src &&
+ git switch branch &&
+ test_commit wobble &&
+ git switch master &&
+ test_commit wubble &&
+ git fetch --all &&
+ git push --force --force-if-includes origin --all 2>err &&
+ grep "forced update" err
+ )
+'
+
+test_expect_success 'allow force push if "--delete" is specified' '
+ setup_src_dup_dst &&
+ test_when_finished "rm -fr dst src dup" &&
+ (
+ cd src &&
+ git switch branch &&
+ test_commit wobble &&
+ git switch master &&
+ test_commit wubble &&
+ git fetch --all &&
+ git push --delete --force-if-includes origin branch 2>err &&
+ grep "deleted" err
+ )
+'
+
+test_expect_success 'allow forced updates if specified with refspec (forced, specific ref)' '
+ setup_src_dup_dst &&
+ test_when_finished "rm -fr dst src dup" &&
+ (
+ cd src &&
+ git switch branch &&
+ test_commit wobble &&
+ git switch master &&
+ test_commit wubble &&
+ git fetch --all &&
+ git push --force-if-includes origin +branch 2>err &&
+ grep "forced update" err
+ )
+'
+
+test_expect_success 'allow deletes if specified with refspec (delete, specific ref)' '
+ setup_src_dup_dst &&
+ test_when_finished "rm -fr dst src dup" &&
+ (
+ cd src &&
+ git switch branch &&
+ test_commit wobble &&
+ git switch master &&
+ test_commit wubble &&
+ git fetch --all &&
+ git push --force-if-includes origin :branch 2>err &&
+ grep "deleted" err
+ )
+'
+
+test_expect_success 'must be disabled for --force-with-lease="<ref>:<expect>" (protected, specific ref)' '
+ setup_src_dup_dst &&
+ test_when_finished "rm -fr dst src dup" &&
+ git ls-remote dst refs/heads/master >expect.master &&
+ git ls-remote dst refs/heads/master >expect.branch &&
+ (
+ cd src &&
+ git switch branch &&
+ test_commit wobble &&
+ git switch master &&
+ test_commit wubble &&
+ r_head="$(git rev-parse refs/remotes/origin/master)" &&
+ git fetch --all &&
+ test_must_fail git push --force-if-includes --force-with-lease="master:$r_head" 2>err &&
+ grep "stale info" err
+ ) &&
+ git ls-remote dst refs/heads/master >actual.master &&
+ git ls-remote dst refs/heads/master >actual.branch &&
+ test_cmp expect.master actual.master &&
+ test_cmp expect.branch actual.branch
+'
+
+test_done
t/5533: * Add a test cases to verify when "--force-if-includes" is used along with "--force-with-lease[=<refname>[:expect]]" (when the "<expect>" value is unspecified) can help prevent unintended remote overwrites when remote refs are updated in the background. t/t5549: * Add test for the new option to cover the following scenarios: - Reject forced updates to remote, if the remote ref is updated in-between the time of checkout, rewrite and before the push, with cases for a specific ref, and "--all". - Allow forced updates for "--force", or if the refspec is prepended with a "+". - Allow deletes on the remote for "--delete", or if refspec is specified as ":<ref>". - Skip the reflog check introduced by the new option if `git-push` is specified with "--force-with-lease=<refname>:<expect>". Signed-off-by: Srinidhi Kaushik <shrinidhi.kaushik@gmail.com> --- t/t5533-push-cas.sh | 26 +++++ t/t5549-push-force-if-includes.sh | 161 ++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100755 t/t5549-push-force-if-includes.sh