remote-curl: Fix push status report when all branches fail

The protocol between transport-helper.c and remote-curl requires remote-curl to always print a blank line after the push command has run. If the blank line is ommitted, transport-helper kills its container process (the git push the user started) with exit(128) and no message indicating a problem, assuming the helper already printed reasonable error text to the console. However if the remote rejects all branches with "ng" commands in the report-status reply, send-pack terminates with non-zero status, and in turn remote-curl exited with non-zero status before outputting the blank line after the helper status printed by send-pack. No error messages reach the user. This caused users to see the following from git push over HTTP when the remote side's update hook rejected the branch: $ git push http://... master Counting objects: 4, done. Delta compression using up to 6 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 301 bytes, done. Total 3 (delta 0), reused 0 (delta 0) $ Always print a blank line after the send-pack process terminates, ensuring the helper status report (if it was output) will be correctly parsed by the calling transport-helper.c. This ensures the helper doesn't abort before the status report can be shown to the user. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Shawn O. Pearce committed Jan 19, 2012 at 19:12 UTC 5238cbf65638d7a097bdb5ca8226f5acbe31f143
2 files changed +33 -4
remote-curl.c
+5 -4
@@ -797,7 +797,7 @@ static int push(int nr_spec, char **specs)
797 static void parse_push(struct strbuf *buf)
798 {
799 char **specs = NULL;
800 - int alloc_spec = 0, nr_spec = 0, i;
800 + int alloc_spec = 0, nr_spec = 0, i, ret;
801
802 do {
803 if (!prefixcmp(buf->buf, "push ")) {
@@ -814,12 +814,13 @@ static void parse_push(struct strbuf *buf)
814 break;
815 } while (1);
816
817 - if (push(nr_spec, specs))
818 - exit(128); /* error already reported */
819 -
817 + ret = push(nr_spec, specs);
818 printf("\n");
819 fflush(stdout);
820
821 + if (ret)
822 + exit(128); /* error already reported */
823 +
824 free_specs:
825 for (i = 0; i < nr_spec; i++)
826 free(specs[i]);
t/t5541-http-push.sh
+28
@@ -95,6 +95,32 @@ test_expect_success 'create and delete remote branch' '
95 test_must_fail git show-ref --verify refs/remotes/origin/dev
96 '
97
98 +cat >"$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update" <<EOF
99 +#!/bin/sh
100 +exit 1
101 +EOF
102 +chmod a+x "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
103 +
104 +cat >exp <<EOF
105 +remote: error: hook declined to update refs/heads/dev2
106 +To http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git
107 + ! [remote rejected] dev2 -> dev2 (hook declined)
108 +error: failed to push some refs to 'http://127.0.0.1:5541/smart/test_repo.git'
109 +EOF
110 +
111 +test_expect_success 'rejected update prints status' '
112 + cd "$ROOT_PATH"/test_repo_clone &&
113 + git checkout -b dev2 &&
114 + : >path4 &&
115 + git add path4 &&
116 + test_tick &&
117 + git commit -m dev2 &&
118 + test_must_fail git push origin dev2 2>act &&
119 + sed -e "/^remote: /s/ *$//" <act >cmp &&
120 + test_cmp exp cmp
121 +'
122 +rm -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
123 +
124 cat >exp <<EOF
125
126 GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
@@ -106,6 +132,8 @@ GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
132 POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
133 GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
134 POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
135 +GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
136 +POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
137 EOF
138 test_expect_success 'used receive-pack service' '
139 sed -e "