]> git.ipfire.org Git - thirdparty/git.git/commitdiff
send-pack: complain about "expecting report" with --helper-status
authorJeff King <peff@peff.net>
Mon, 18 Oct 2021 19:43:47 +0000 (15:43 -0400)
committerJunio C Hamano <gitster@pobox.com>
Mon, 18 Oct 2021 20:26:52 +0000 (13:26 -0700)
When pushing to a server which erroneously omits the final ref-status
report, the client side should complain about the refs for which we
didn't receive the status (because we can't just assume they were
updated). This works over most transports like ssh, but for http we'll
print a very misleading "Everything up-to-date".

It works for ssh because send-pack internally sets the status of each
ref to REF_STATUS_EXPECTING_REPORT, and then if the server doesn't tell
us about a particular ref, it will stay at that value. When we print the
final status table, we'll see that we're still on EXPECTING_REPORT and
complain then.

But for http, we go through remote-curl, which invokes send-pack with
"--stateless-rpc --helper-status". The latter option causes send-pack to
return a machine-readable list of ref statuses to the remote helper. But
ever since its inception in de1a2fdd38 (Smart push over HTTP: client
side, 2009-10-30), the send-pack code has simply omitted mention of any
ref which ended up in EXPECTING_REPORT.

In the remote helper, we then take the absence of any status report
from send-pack to mean that the ref was not even something we tried to
send, and thus it prints "Everything up-to-date". Fortunately it does
detect the eventual non-zero exit from send-pack, and propagates that in
its own non-zero exit code. So at least a careful script invoking "git
push" would notice the failure.  But sending the misleading message on
stderr is certainly confusing for humans (not to mention the
machine-readable "push --porcelain" output, though again, any careful
script should be checking the exit code from push, too).

Nobody seems to have noticed because the server in this instance has to
be misbehaving: it has promised to support the ref-status capability
(otherwise the client will not set EXPECTING_REPORT at all), but didn't
send us any. If the connection were simply cut, then send-pack would
complain about getting EOF while trying to read the status. But if the
server actually sends a flush packet (i.e., saying "now you have all of
the ref statuses" without actually sending any), then the client ends up
in this confused situation.

The fix is simple: we should return an error message from "send-pack
--helper-status", just like we would for any other error per-ref error
condition (in the test I included, the server simply omits all ref
status responses, but a more insidious version of this would skip only
some of them).

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/send-pack.c
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/lib-httpd/error-no-report.sh [new file with mode: 0644]
t/t5541-http-push-smart.sh

index 729dea1d255d354b044e477732eafc3a29a284b0..20ca07ecc84d29b809a87c116051170ab9adc6b3 100644 (file)
@@ -87,6 +87,10 @@ static void print_helper_status(struct ref *ref)
                        break;
 
                case REF_STATUS_EXPECTING_REPORT:
+                       res = "error";
+                       msg = "expecting report";
+                       break;
+
                default:
                        continue;
                }
index d2edfa4c503af07f72a282dddb8cd4cf1d8e868e..782891908d729b6cab46ae16775331bfca6e9add 100644 (file)
@@ -131,6 +131,7 @@ prepare_httpd() {
        cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
        install_script incomplete-length-upload-pack-v2-http.sh
        install_script incomplete-body-upload-pack-v2-http.sh
+       install_script error-no-report.sh
        install_script broken-smart-http.sh
        install_script error-smart-http.sh
        install_script error.sh
index afa91e38b0e2130cc27de6a40c3498b88eb8d122..1a0746639c2157ef4cf58f1ad75ace65f03a38f5 100644 (file)
@@ -119,6 +119,7 @@ Alias /auth/dumb/ www/auth/dumb/
 </LocationMatch>
 ScriptAlias /smart/incomplete_length/git-upload-pack incomplete-length-upload-pack-v2-http.sh/
 ScriptAlias /smart/incomplete_body/git-upload-pack incomplete-body-upload-pack-v2-http.sh/
+ScriptAlias /smart/no_report/git-receive-pack error-no-report.sh/
 ScriptAliasMatch /error_git_upload_pack/(.*)/git-upload-pack error.sh/
 ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
 ScriptAlias /broken_smart/ broken-smart-http.sh/
@@ -134,6 +135,9 @@ ScriptAliasMatch /one_time_perl/(.*) apply-one-time-perl.sh/$1
 <Files incomplete-body-upload-pack-v2-http.sh>
        Options ExecCGI
 </Files>
+<Files error-no-report.sh>
+       Options ExecCGI
+</Files>
 <Files broken-smart-http.sh>
        Options ExecCGI
 </Files>
diff --git a/t/lib-httpd/error-no-report.sh b/t/lib-httpd/error-no-report.sh
new file mode 100644 (file)
index 0000000..39ff75b
--- /dev/null
@@ -0,0 +1,6 @@
+echo "Content-Type: application/x-git-receive-pack-result"
+echo
+printf '0013\001000eunpack ok\n'
+printf '0015\002skipping report\n'
+printf '0009\0010000'
+printf '0000'
index c024fa2818314d3d127e93da2e281dc259820733..9c61dccc24b31412f55f9c22c3088249708a5bae 100755 (executable)
@@ -509,4 +509,20 @@ test_expect_success 'colorize errors/hints' '
        test_i18ngrep ! "^hint: " decoded
 '
 
+test_expect_success 'report error server does not provide ref status' '
+       git init "$HTTPD_DOCUMENT_ROOT_PATH/no_report" &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/no_report" config http.receivepack true &&
+       test_must_fail git push --porcelain \
+               $HTTPD_URL_USER_PASS/smart/no_report \
+               HEAD:refs/tags/will-fail >actual &&
+       test_must_fail git -C "$HTTPD_DOCUMENT_ROOT_PATH/no_report" \
+               rev-parse --verify refs/tags/will-fail &&
+       cat >expect <<-EOF &&
+       To $HTTPD_URL/smart/no_report
+       !       HEAD:refs/tags/will-fail        [remote rejected] (expecting report)
+       Done
+       EOF
+       test_cmp expect actual
+'
+
 test_done