git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 2/2] transport: don't ignore git-receive-pack(1) exit code on atomic push

From
Patrick Steinhardt <ps@pks.im>
Date
Nov 13, 2024, 11:24 UTC
Message-ID
<20241113-pks-push-atomic-respect-exit-code-v1-2-7965f01e7f4e@pks.im>
In-Reply-To
<20241113-pks-push-atomic-respect-exit-code-v1-0-7965f01e7f4e@pks.im>

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". This behaviour was later changed via 7dcbeaa0df (send-pack: fix inconsistent porcelain output, 2020-04-17) such that the stanza will also be printed when there was an error with atomic pushes, which was previously inconsistent with non-atomic pushes.

The fixup commit has introduced a new issue though. During atomic pushes it is expected that git-receive-pack(1) may exit early, and that causes us to receive an error on the client-side. We (seemingly) still want to print the "Done" stanza, but given that we only do so when the push has been successful we started to ignore the error code by the child process completely when doing an atomic push.

We'd typically notice this case because the refs would have their error message set. But there is an edge case when pushing refs succeeds, but git-receive-pack(1) exits with a non-zero exit code at a later point in time due to another error. An atomic git-push(1) would ignore that error code, and consequently it would return successfully and not print any error message at all.

Now it is somewhat unclear what the correct fix is in this case, mostly because the exact format of the porcelain output of git-push(1) is not specified to its full extent. What is clear though is that ignoring the error code is definitely not the correct thing to do.

Adapt the code such that we honor the error code and unconditionally print the "Done" stanza even when pushing refs has failed. This ensures that git-push(1) notices the error condition and exits with an error, but slightly changes the output format.

This requires a change to t5504, where we previously didn't print "Done" at the end of the push. As said, it is hard to say what the correct behaviour is in this case. But two test cases further up we have another test that fails in a similar way, and that test expects a final "Done" stanza. So if nothing else this at least seems to make the behaviour more consistent with other error cases.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 send-pack.c                     |  2 +-
 t/t5504-fetch-receive-strict.sh |  1 +
 t/t5543-atomic-push.sh          | 30 ++++++++++++++++++++++++++++++
 transport.c                     |  9 ++-------
 4 files changed, 34 insertions(+), 8 deletions(-)
diff --git a/send-pack.c b/send-pack.c
index 6677c44e8acd19f16706ad2d78f72fee889daa55..815c1f206c09da8fb1ffe9be11beb9c5fa29d0c5 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -630,7 +630,7 @@ int send_pack(struct send_pack_args *args,
 				reject_atomic_push(remote_refs, args->send_mirror);
 				error("atomic push failed for ref %s. status: %d",
 				      ref->name, ref->status);
-				ret = args->porcelain ? 0 : -1;
+				ret = -1;
 				goto out;
 			}
 			/* else fallthrough */
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
index 0a3883043baf5c4c0fc43b52e8c5fc375f10a56a..b3774e56e0f45a91d574a15f0d6b5c50c4da70d4 100755
--- a/t/t5504-fetch-receive-strict.sh
+++ b/t/t5504-fetch-receive-strict.sh
@@ -105,6 +105,7 @@ test_expect_success 'push with receive.fsckobjects' '
 	cat >exp <<-EOF &&
 	To dst
 	!	refs/heads/main:refs/heads/test	[remote rejected] (unpacker error)
+	Done
 	EOF
 	test_must_fail git push --porcelain dst main:refs/heads/test >act &&
 	test_cmp exp act
diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh
index 479d103469527e6b923877cd480825b59e7094d4..1d8f088a004cf6743ade8e55536f04ac04000cf7 100755
--- a/t/t5543-atomic-push.sh
+++ b/t/t5543-atomic-push.sh
@@ -281,4 +281,34 @@ test_expect_success 'atomic push reports (reject by non-ff)' '
 	test_cmp expect actual
 '
 
+test_expect_success 'atomic push reports exit code failure' '
+	write_script receive-pack-wrapper <<-\EOF &&
+	git-receive-pack "$@"
+	exit 1
+	EOF
+	test_must_fail git -C workbench push --atomic \
+		--receive-pack="${SQ}$(pwd)${SQ}/receive-pack-wrapper" \
+		up HEAD:refs/heads/no-conflict 2>err &&
+	cat >expect <<-EOF &&
+	To ../upstream
+	 * [new branch]      HEAD -> no-conflict
+	error: failed to push some refs to ${SQ}../upstream${SQ}
+	EOF
+	test_cmp expect err
+'
+
+test_expect_success 'atomic push reports exit code failure with porcelain' '
+	write_script receive-pack-wrapper <<-\EOF &&
+	git-receive-pack "$@"
+	exit 1
+	EOF
+	test_must_fail git -C workbench push --atomic --porcelain \
+		--receive-pack="${SQ}$(pwd)${SQ}/receive-pack-wrapper" \
+		up HEAD:refs/heads/no-conflict-porcelain 2>err &&
+	cat >expect <<-EOF &&
+	error: failed to push some refs to ${SQ}../upstream${SQ}
+	EOF
+	test_cmp expect err
+'
+
 test_done
diff --git a/transport.c b/transport.c
index 47fda6a7732f4b8cdcb6e750f36b896a988ffd0b..76b0645a25fe936190ca52d595b5f02eedc27d23 100644
--- a/transport.c
+++ b/transport.c
@@ -921,12 +921,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
 
 	close(data->fd[1]);
 	close(data->fd[0]);
-	/*
-	 * Atomic push may abort the connection early and close the pipe,
-	 * which may cause an error for `finish_connect()`. Ignore this error
-	 * for atomic git-push.
-	 */
-	if (ret || args.atomic)
+	if (ret)
 		finish_connect(data->conn);
 	else
 		ret = finish_connect(data->conn);
@@ -1503,7 +1498,7 @@ int transport_push(struct repository *r,
 			transport_update_tracking_ref(transport->remote, ref, verbose);
 	}
 
-	if (porcelain && !push_ret)
+	if (porcelain)
 		puts("Done");
 	else if (!quiet && !ret && !transport_refs_pushed(remote_refs))
 		/* stable plumbing output; do not modify or localize */
-- 
2.47.0.251.gb31fb630c0.dirty
Previous: Patrick SteinhardtNext: Jiang Xin
Message 3 of 54 in “transport: don't ignore git-receive-pack(1) exit code on atomic push”
  1. 0/2 transport: don't ignore git-receive-pack(1) exit code on atomic pushPatrick Steinhardt, Nov 13, 2024
  2. 1/2 t5504: modernize test by moving heredocs into test bodiesPatrick Steinhardt, Nov 13, 2024
  3. 2/2 transport: don't ignore git-receive-pack(1) exit code on atomic pushPatrick Steinhardt, Nov 13, 2024
  4. Jiang XinNov 14, 2024
  5. 0/6 fix behaviors of git-push --porcelainJiang Xin, Nov 14, 2024
  6. 1/6 t5548: new test cases for push --porcelain and --dry-runJiang Xin, Nov 14, 2024
  7. Patrick SteinhardtNov 25, 2024
  8. Jiang XinDec 3, 2024
  9. 2/6 push: fix the behavior of the Done message for porcelainJiang Xin, Nov 14, 2024
  10. Patrick SteinhardtNov 25, 2024
  11. 3/6 t5504: modernize test by moving heredocs into test bodiesJiang Xin, Nov 14, 2024
  12. 4/6 t5543: atomic push reports exit code failureJiang Xin, Nov 14, 2024
  13. 5/6 push: only ignore finish_connect() for dry-run modeJiang Xin, Nov 14, 2024
  14. Patrick SteinhardtNov 25, 2024
  15. 6/6 push: not send push-options to server with --dry-runJiang Xin, Nov 14, 2024
  16. Patrick SteinhardtNov 25, 2024
  17. 0/8 fix behaviors of git-push --porcelainJiang Xin, Dec 10, 2024
  18. 1/8 t5504: modernize test by moving heredocs into test bodiesJiang Xin, Dec 10, 2024
  19. 2/8 t5548: refactor to reuse setup_upstream() functionJiang Xin, Dec 10, 2024
  20. 3/8 t5548: refactor test cases by resetting upstreamJiang Xin, Dec 10, 2024
  21. 4/8 t5548: add new porcelain test casesJiang Xin, Dec 10, 2024
  22. 5/8 t5548: add porcelain push test cases for dry-run modeJiang Xin, Dec 10, 2024
  23. Jiang XinDec 10, 2024
  24. 6/8 send-pack: new return code "ERROR_SEND_PACK_BAD_REF_STATUS"Jiang Xin, Dec 10, 2024
  25. Patrick SteinhardtDec 16, 2024
  26. 7/8 t5543: atomic push reports exit code failureJiang Xin, Dec 10, 2024
  27. 8/8 send-pack: gracefully close the connection for atomic pushJiang Xin, Dec 10, 2024
  28. Patrick SteinhardtDec 16, 2024
  29. Junio C HamanoNov 14, 2024
  30. Patrick SteinhardtNov 25, 2024
  31. 0/8 transport: don't ignore git-receive-pack(1) exit code on atomic pushPatrick Steinhardt, Jan 31, 2025
  32. 1/8 t5504: modernize test by moving heredocs into test bodiesPatrick Steinhardt, Jan 31, 2025
  33. Eric SunshineJan 31, 2025
  34. Junio C HamanoJan 31, 2025
  35. Patrick SteinhardtFeb 3, 2025
  36. 2/8 t5548: refactor to reuse setup_upstream() functionPatrick Steinhardt, Jan 31, 2025
  37. Eric SunshineJan 31, 2025
  38. 4/8 t5548: add new porcelain test casesPatrick Steinhardt, Jan 31, 2025
  39. Eric SunshineJan 31, 2025
  40. 5/8 t5548: add porcelain push test cases for dry-run modePatrick Steinhardt, Jan 31, 2025
  41. 3/8 t5548: refactor test cases by resetting upstreamPatrick Steinhardt, Jan 31, 2025
  42. 7/8 t5543: atomic push reports exit code failurePatrick Steinhardt, Jan 31, 2025
  43. 6/8 send-pack: new return code "ERROR_SEND_PACK_BAD_REF_STATUS"Patrick Steinhardt, Jan 31, 2025
  44. 8/8 send-pack: gracefully close the connection for atomic pushPatrick Steinhardt, Jan 31, 2025
  45. 0/8 transport: don't ignore git-receive-pack(1) exit code on atomic pushPatrick Steinhardt, Feb 3, 2025
  46. 1/8 t5504: modernize test by moving heredocs into test bodiesPatrick Steinhardt, Feb 3, 2025
  47. 2/8 t5548: refactor to reuse setup_upstream() functionPatrick Steinhardt, Feb 3, 2025
  48. 3/8 t5548: refactor test cases by resetting upstreamPatrick Steinhardt, Feb 3, 2025
  49. 4/8 t5548: add new porcelain test casesPatrick Steinhardt, Feb 3, 2025
  50. 5/8 t5548: add porcelain push test cases for dry-run modePatrick Steinhardt, Feb 3, 2025
  51. 6/8 send-pack: new return code "ERROR_SEND_PACK_BAD_REF_STATUS"Patrick Steinhardt, Feb 3, 2025
  52. 7/8 t5543: atomic push reports exit code failurePatrick Steinhardt, Feb 3, 2025
  53. 8/8 send-pack: gracefully close the connection for atomic pushPatrick Steinhardt, Feb 3, 2025
  54. Junio C HamanoFeb 3, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.