From: Jiang Xin <zhiyou.jx@xxxxxxxxxxxxxxx> Refactor t5548 and add new test cases for git-push with both --porcelain and --dry-run flags in order to cover the issues reported by Patrick. When executing git-push(1) with the "--porcelain" flag, then we will print updated references in a machine-readable format that looks like this: To destination = refs/heads/noop:refs/heads/noop [up to date] ! refs/heads/rejected:refs/heads/rejected [rejected] (atomic push failed) ! refs/heads/noff:refs/heads/(off (non-fast-forward) Done The final "Done" stanza was introduced via 77555854be (git-push: make git push --porcelain print "Done", 2010-02-26), where it was printed "unless any errors have occurred". For the purpose of the "Done" line, knowing a ref will be rejected in a --dry-run does not count as an error. Actual rejections in non --dry-run pushes do count as errors. The new test cases will be used in the next commit to protect the above behaviors of porcelain output of git push. Reported-by: Patrick Steinhardt <ps@xxxxxx> Signed-off-by: Jiang Xin <zhiyou.jx@xxxxxxxxxxxxxxx> --- t/t5548-push-porcelain.sh | 338 ++++++++++++++++++++++++++++---------- 1 file changed, 250 insertions(+), 88 deletions(-) diff --git a/t/t5548-push-porcelain.sh b/t/t5548-push-porcelain.sh index ecb3877aa4..ca5cf684bc 100755 --- a/t/t5548-push-porcelain.sh +++ b/t/t5548-push-porcelain.sh @@ -4,6 +4,9 @@ # test_description='Test git push porcelain output' +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh @@ -56,10 +59,10 @@ format_and_save_expect () { } setup_upstream_and_workbench () { - # Upstream after setup : main(B) foo(A) bar(A) baz(A) - # Workbench after setup : main(A) + # Upstream after setup: main(B) foo(A) bar(A) baz(A) + # Workbench after setup: main(A) baz(A) next(A) test_expect_success "setup upstream repository and workbench" ' - rm -rf upstream.git workbench && + rm -rf upstream.git upstream-backup.git workbench && git init --bare upstream.git && git init workbench && create_commits_in workbench A B && @@ -68,19 +71,29 @@ setup_upstream_and_workbench () { # Try to make a stable fixed width for abbreviated commit ID, # this fixed-width oid will be replaced with "<OID>". git config core.abbrev 7 && - git remote add origin ../upstream.git && git update-ref refs/heads/main $A && + git update-ref refs/heads/baz $A && + git update-ref refs/heads/next $A && + git remote add origin ../upstream.git && git push origin \ $B:refs/heads/main \ $A:refs/heads/foo \ $A:refs/heads/bar \ $A:refs/heads/baz ) && + git clone --mirror upstream.git upstream-backup.git && git -C "workbench" config advice.pushUpdateRejected false && upstream=upstream.git ' } +restore_upstream () { + test -n "$upstream" && + rm -rf "$upstream" && + git clone --mirror upstream-backup.git "$upstream" && + git -C "$upstream" config http.receivepack true +} + run_git_push_porcelain_output_test() { case $1 in http) @@ -96,19 +109,46 @@ run_git_push_porcelain_output_test() { # Refs of upstream : main(B) foo(A) bar(A) baz(A) # Refs of workbench: main(A) baz(A) next(A) # git-push : main(A) NULL (B) baz(A) next(A) - test_expect_success "porcelain output of successful git-push ($PROTOCOL)" ' - ( - cd workbench && - git update-ref refs/heads/main $A && - git update-ref refs/heads/baz $A && - git update-ref refs/heads/next $A && - git push --porcelain --force origin \ - main \ - :refs/heads/foo \ - $B:bar \ - baz \ - next - ) >out && + test_expect_success "git-push --porcelain --dry-run ($PROTOCOL)" ' + test_must_fail git -C workbench push --porcelain --dry-run origin \ + main \ + :refs/heads/foo \ + $B:bar \ + baz \ + next >out && + make_user_friendly_and_stable_output <out >actual && + format_and_save_expect <<-EOF && + > To <URL/of/upstream.git> + > = refs/heads/baz:refs/heads/baz [up to date] + > <COMMIT-B>:refs/heads/bar <COMMIT-A>..<COMMIT-B> + > - :refs/heads/foo [deleted] + > * refs/heads/next:refs/heads/next [new branch] + > ! refs/heads/main:refs/heads/main [rejected] (non-fast-forward) + > Done + EOF + test_cmp expect actual && + + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output <out >actual && + cat >expect <<-EOF && + <COMMIT-A> refs/heads/bar + <COMMIT-A> refs/heads/baz + <COMMIT-A> refs/heads/foo + <COMMIT-B> refs/heads/main + EOF + test_cmp expect actual + ' + + # Refs of upstream : main(B) foo(A) bar(A) baz(A) + # Refs of workbench: main(A) baz(A) next(A) + # push : main(A) NULL (B) baz(A) next(A) + test_expect_success "git-push --porcelain --dry-run --force ($PROTOCOL)" ' + git -C workbench push --porcelain --dry-run --force origin \ + main \ + :refs/heads/foo \ + $B:bar \ + baz \ + next >out && make_user_friendly_and_stable_output <out >actual && format_and_save_expect <<-EOF && > To <URL/of/upstream.git> @@ -121,39 +161,137 @@ run_git_push_porcelain_output_test() { EOF test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output <out >actual && + cat >expect <<-EOF && + <COMMIT-A> refs/heads/bar + <COMMIT-A> refs/heads/baz + <COMMIT-A> refs/heads/foo + <COMMIT-B> refs/heads/main + EOF + test_cmp expect actual + ' + + # Refs of upstream : main(B) foo(A) bar(A) baz(A) + # Refs of workbench: main(A) baz(A) next(A) + # git-push : main(A) NULL (B) baz(A) next(A) + test_expect_success "git-push --porcelain --dry-run --atomic ($PROTOCOL)" ' + test_must_fail git -C workbench push --porcelain --dry-run --atomic origin \ + main \ + :refs/heads/foo \ + $B:bar \ + baz \ + next >out && + make_user_friendly_and_stable_output <out >actual && + format_and_save_expect <<-EOF && + > To <URL/of/upstream.git> + > = refs/heads/baz:refs/heads/baz [up to date] + > ! <COMMIT-B>:refs/heads/bar [rejected] (atomic push failed) + > ! (delete):refs/heads/foo [rejected] (atomic push failed) + > ! refs/heads/main:refs/heads/main [rejected] (non-fast-forward) + > ! refs/heads/next:refs/heads/next [rejected] (atomic push failed) + > Done + EOF + test_cmp expect actual && + + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output <out >actual && + cat >expect <<-EOF && + <COMMIT-A> refs/heads/bar + <COMMIT-A> refs/heads/baz + <COMMIT-A> refs/heads/foo + <COMMIT-B> refs/heads/main + EOF + test_cmp expect actual + ' + + # Refs of upstream : main(B) foo(A) bar(A) baz(A) + # Refs of workbench: main(A) baz(A) next(A) + # push : main(A) NULL (B) baz(A) next(A) + test_expect_success "git-push --porcelain --dry-run --atomic --force ($PROTOCOL)" ' + git -C workbench push --porcelain --dry-run --atomic --force origin \ + main \ + :refs/heads/foo \ + $B:bar \ + baz \ + next >out && + make_user_friendly_and_stable_output <out >actual && + format_and_save_expect <<-EOF && + > To <URL/of/upstream.git> + > = refs/heads/baz:refs/heads/baz [up to date] + > <COMMIT-B>:refs/heads/bar <COMMIT-A>..<COMMIT-B> + > - :refs/heads/foo [deleted] + > + refs/heads/main:refs/heads/main <COMMIT-B>...<COMMIT-A> (forced update) + > * refs/heads/next:refs/heads/next [new branch] + > Done + EOF + test_cmp expect actual && + + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output <out >actual && + cat >expect <<-EOF && + <COMMIT-A> refs/heads/bar + <COMMIT-A> refs/heads/baz + <COMMIT-A> refs/heads/foo + <COMMIT-B> refs/heads/main + EOF + test_cmp expect actual + ' + + # Refs of upstream : main(B) foo(A) bar(A) baz(A) + # Refs of workbench: main(A) baz(A) next(A) + # git-push : main(A) NULL (B) baz(A) next(A) + test_expect_success "git-push --porcelain ($PROTOCOL)" ' + test_when_finished restore_upstream && + test_must_fail git -C workbench push --porcelain origin \ + main \ + :refs/heads/foo \ + $B:bar \ + baz \ + next >out && + make_user_friendly_and_stable_output <out >actual && + format_and_save_expect <<-EOF && + > To <URL/of/upstream.git> + > = refs/heads/baz:refs/heads/baz [up to date] + > <COMMIT-B>:refs/heads/bar <COMMIT-A>..<COMMIT-B> + > - :refs/heads/foo [deleted] + > * refs/heads/next:refs/heads/next [new branch] + > ! refs/heads/main:refs/heads/main [rejected] (non-fast-forward) + > Done + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && make_user_friendly_and_stable_output <out >actual && cat >expect <<-EOF && <COMMIT-B> refs/heads/bar <COMMIT-A> refs/heads/baz - <COMMIT-A> refs/heads/main + <COMMIT-B> refs/heads/main <COMMIT-A> refs/heads/next EOF test_cmp expect actual ' - # Refs of upstream : main(A) bar(B) baz(A) next(A) - # Refs of workbench: main(B) bar(A) baz(A) next(A) - # git-push : main(B) bar(A) NULL next(A) - test_expect_success "atomic push failed ($PROTOCOL)" ' - ( - cd workbench && - git update-ref refs/heads/main $B && - git update-ref refs/heads/bar $A && - test_must_fail git push --atomic --porcelain origin \ - main \ - bar \ - :baz \ - next - ) >out && + # Refs of upstream : main(B) foo(A) bar(A) baz(A) + # Refs of workbench: main(A) baz(A) next(A) + # git-push : main(A) NULL (B) baz(A) next(A) + test_expect_success "git-push --porcelain --force ($PROTOCOL)" ' + test_when_finished restore_upstream && + git -C workbench push --porcelain --force origin \ + main \ + :refs/heads/foo \ + $B:bar \ + baz \ + next >out && make_user_friendly_and_stable_output <out >actual && format_and_save_expect <<-EOF && - To <URL/of/upstream.git> - > = refs/heads/next:refs/heads/next [up to date] - > ! refs/heads/bar:refs/heads/bar [rejected] (non-fast-forward) - > ! (delete):refs/heads/baz [rejected] (atomic push failed) - > ! refs/heads/main:refs/heads/main [rejected] (atomic push failed) - Done + > To <URL/of/upstream.git> + > = refs/heads/baz:refs/heads/baz [up to date] + > <COMMIT-B>:refs/heads/bar <COMMIT-A>..<COMMIT-B> + > - :refs/heads/foo [deleted] + > + refs/heads/main:refs/heads/main <COMMIT-B>...<COMMIT-A> (forced update) + > * refs/heads/next:refs/heads/next [new branch] + > Done EOF test_cmp expect actual && @@ -168,34 +306,60 @@ run_git_push_porcelain_output_test() { test_cmp expect actual ' - test_expect_success "prepare pre-receive hook ($PROTOCOL)" ' - test_hook --setup -C "$upstream" pre-receive <<-EOF - exit 1 + # Refs of upstream : main(B) foo(A) bar(A) baz(A) + # Refs of workbench: main(A) baz(A) next(A) + # git-push : main(A) NULL (B) baz(A) next(A) + test_expect_success "git push --porcelain --atomic ($PROTOCOL)" ' + test_when_finished restore_upstream && + test_must_fail git -C workbench push --porcelain --atomic origin \ + main \ + :refs/heads/foo \ + $B:bar \ + baz \ + next >out && + make_user_friendly_and_stable_output <out >actual && + format_and_save_expect <<-EOF && + > To <URL/of/upstream.git> + > = refs/heads/baz:refs/heads/baz [up to date] + > ! <COMMIT-B>:refs/heads/bar [rejected] (atomic push failed) + > ! (delete):refs/heads/foo [rejected] (atomic push failed) + > ! refs/heads/main:refs/heads/main [rejected] (non-fast-forward) + > ! refs/heads/next:refs/heads/next [rejected] (atomic push failed) + > Done EOF + test_cmp expect actual && + + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output <out >actual && + cat >expect <<-EOF && + <COMMIT-A> refs/heads/bar + <COMMIT-A> refs/heads/baz + <COMMIT-A> refs/heads/foo + <COMMIT-B> refs/heads/main + EOF + test_cmp expect actual ' - # Refs of upstream : main(A) bar(B) baz(A) next(A) - # Refs of workbench: main(B) bar(A) baz(A) next(A) - # git-push : main(B) bar(A) NULL next(A) - test_expect_success "pre-receive hook declined ($PROTOCOL)" ' - ( - cd workbench && - git update-ref refs/heads/main $B && - git update-ref refs/heads/bar $A && - test_must_fail git push --porcelain --force origin \ - main \ - bar \ - :baz \ - next - ) >out && + # Refs of upstream : main(B) foo(A) bar(A) baz(A) + # Refs of workbench: main(A) baz(A) next(A) + # git-push : main(A) NULL (B) baz(A) next(A) + test_expect_success "git push --porcelain --atomic --force ($PROTOCOL)" ' + test_when_finished restore_upstream && + git -C workbench push --porcelain --atomic --force origin \ + main \ + :refs/heads/foo \ + $B:bar \ + baz \ + next >out && make_user_friendly_and_stable_output <out >actual && format_and_save_expect <<-EOF && - To <URL/of/upstream.git> - > = refs/heads/next:refs/heads/next [up to date] - > ! refs/heads/bar:refs/heads/bar [remote rejected] (pre-receive hook declined) - > ! :refs/heads/baz [remote rejected] (pre-receive hook declined) - > ! refs/heads/main:refs/heads/main [remote rejected] (pre-receive hook declined) - Done + > To <URL/of/upstream.git> + > = refs/heads/baz:refs/heads/baz [up to date] + > <COMMIT-B>:refs/heads/bar <COMMIT-A>..<COMMIT-B> + > - :refs/heads/foo [deleted] + > + refs/heads/main:refs/heads/main <COMMIT-B>...<COMMIT-A> (forced update) + > * refs/heads/next:refs/heads/next [new branch] + > Done EOF test_cmp expect actual && @@ -210,39 +374,39 @@ run_git_push_porcelain_output_test() { test_cmp expect actual ' - test_expect_success "remove pre-receive hook ($PROTOCOL)" ' - rm "$upstream/hooks/pre-receive" - ' - - # Refs of upstream : main(A) bar(B) baz(A) next(A) - # Refs of workbench: main(B) bar(A) baz(A) next(A) - # git-push : main(B) bar(A) NULL next(A) - test_expect_success "non-fastforward push ($PROTOCOL)" ' - ( - cd workbench && - test_must_fail git push --porcelain origin \ - main \ - bar \ - :baz \ - next - ) >out && + # Refs of upstream : main(B) foo(A) bar(A) baz(A) + # Refs of workbench: main(A) baz(A) next(A) + # git-push : main(A) NULL (B) baz(A) next(A) + test_expect_success "pre-receive hook declined ($PROTOCOL)" ' + test_when_finished restore_upstream && + test_hook --setup -C "$upstream" pre-receive <<-EOF && + exit 1 + EOF + test_must_fail git -C workbench push --porcelain --force origin \ + main \ + :refs/heads/foo \ + $B:bar \ + baz \ + next >out && make_user_friendly_and_stable_output <out >actual && format_and_save_expect <<-EOF && - To <URL/of/upstream.git> - > = refs/heads/next:refs/heads/next [up to date] - > - :refs/heads/baz [deleted] - > refs/heads/main:refs/heads/main <COMMIT-A>..<COMMIT-B> - > ! refs/heads/bar:refs/heads/bar [rejected] (non-fast-forward) - Done + > To <URL/of/upstream.git> + > = refs/heads/baz:refs/heads/baz [up to date] + > ! <COMMIT-B>:refs/heads/bar [remote rejected] (pre-receive hook declined) + > ! :refs/heads/foo [remote rejected] (pre-receive hook declined) + > ! refs/heads/main:refs/heads/main [remote rejected] (pre-receive hook declined) + > ! refs/heads/next:refs/heads/next [remote rejected] (pre-receive hook declined) + > Done EOF test_cmp expect actual && git -C "$upstream" show-ref >out && make_user_friendly_and_stable_output <out >actual && cat >expect <<-EOF && - <COMMIT-B> refs/heads/bar + <COMMIT-A> refs/heads/bar + <COMMIT-A> refs/heads/baz + <COMMIT-A> refs/heads/foo <COMMIT-B> refs/heads/main - <COMMIT-A> refs/heads/next EOF test_cmp expect actual ' @@ -264,10 +428,8 @@ start_httpd setup_upstream_and_workbench test_expect_success "setup for http" ' - git -C upstream.git config http.receivepack true && upstream="$HTTPD_DOCUMENT_ROOT_PATH/upstream.git" && - mv upstream.git "$upstream" && - + restore_upstream && git -C workbench remote set-url origin $HTTPD_URL/smart/upstream.git ' -- 2.47.0.rc1.21.g81e7bd6151