Thread (98 messages) flat view 98 messages, 7 authors, 4h ago
HOTtoday

Revision v4 of 7 in this series.

Revisions (7)
  1. v4 current
  2. v5 [diff vs current]
  3. v6 [diff vs current]
  4. v7 [diff vs current]
  5. v8 [diff vs current]
  6. v9 [diff vs current]
  7. v10 [diff vs current]

[PATCH v4 0/3] hook: introduce the receive-report hook

From: Karthik Nayak <hidden>
Date: 2026-08-26 10:19:56

Introduce a new receive-report hook which kicks in after the reference
transaction is complete, but before the report is sent to the client.
The hook receives the pkt-line encoded report in its stdin and its
stdout replaces the report transferred to the user. If the hook exits
with a non-zero exit code, all references are marked as rejected.

The first patch, adds missing documentation to 'git-receive-pack.adoc'.
The second patch refactors code and the third patch contains the new
hook.

---
Changes in v4:
- Change the name of the hook to be 'receive-report' to avoid ambiguity.
- Link to v3: https://patch.msgid.link/20260824-758-introduce-hook-v3-0-499526f0a062@gmail.com

Changes in v3:
- Move out addition of proc-receive hook doc to 'git-receive-pack.adoc'
  into a new commit.
- Add a new commit to move out the response generation in receive-pack
  to a new function.
- Instead of die-ing on non-zero exit code, we modify each reference to
  indicate that the hook failed.
- Instead of correctly listing out the protocol, link to
  linkgit:gitprotocol-pack[5], as the protocol also differs between v1
  and v2.
- Link to v2: https://patch.msgid.link/20260821-758-introduce-hook-v2-1-e90e2f7ac2cf@gmail.com

Changes in v2:
- Modify the documentation and commit message to be more verbose.
- Add documentation to 'git-receive-pack.adoc'
- Use 'ret' as the variable name for the return code.
- Modify the test to also check for the 'remote:'.
- Link to v1: https://patch.msgid.link/20260818-758-introduce-hook-v1-1-8a8d89e65838@gmail.com

 To: git@vger.kernel.org
 CC: ps@pks.im
 CC: gitster@pobox.com
 CC: jltobler@gmail.com
 CC: kristofferhaugsbakk@fastmail.com
 CC: phillip.wood123@gmail.com

---
Karthik Nayak (3):
      doc: add proc-receive hook info in 'git-receive-pack.adoc'
      receive-pack: move message generation to separate function
      hook: introduce the receive-report hook

 Documentation/git-receive-pack.adoc |  15 +++
 Documentation/githooks.adoc         |  43 ++++++++
 builtin/receive-pack.c              | 137 ++++++++++++++++--------
 t/meson.build                       |   1 +
 t/t5412-receive-report-hook.sh      | 200 ++++++++++++++++++++++++++++++++++++
 5 files changed, 356 insertions(+), 40 deletions(-)

Range-diff versus v3:

1:  b899f31ffa = 1:  30784c0448 doc: add proc-receive hook info in 'git-receive-pack.adoc'
2:  335182cd3d = 2:  55d6a46815 receive-pack: move message generation to separate function
3:  80aa575dab ! 3:  99eeafb537 hook: introduce the report hook for git-receive-pack(1)
    @@ Metadata
     Author: Karthik Nayak [off-list ref]
     
      ## Commit message ##
    -    hook: introduce the report hook for git-receive-pack(1)
    +    hook: introduce the receive-report hook
     
         When running 'git-receive-pack(1)', there is no way for the server to
         intercept and modify the status report before it is sent back to the
    @@ Commit message
             too late, at the point where we have already reported success to the
             client.
     
    -    Introduce a new 'report' hook. The hook receives the complete pkt-line
    -    encoded status report on standard input, after all ref updates have
    -    been applied to the repository by execute_commands() but before the
    +    Introduce a new 'receive-report' hook. The hook receives the complete
    +    pkt-line encoded status report on standard input, after all ref updates
    +    have been applied to the repository by execute_commands() but before the
         report is sent to the client. See linkgit:gitprotocol-pack[5] details on
         the protocol structure.
     
    @@ Commit message
           status if any ref is 'ng'.
     
         - Non-zero exit: the hook's stdout is discarded, receive-pack modifies
    -      all references to be rejected with a 'report hook failed' error.
    +      all references to be rejected with a 'receive-report hook failed'
    +      error.
     
         In both cases, any output the hook writes to standard error is
         forwarded to the client over the sideband channel and appears as
    @@ Documentation/git-receive-pack.adoc: requests. It handles refs whose names match
      `receive.procReceiveRefs` and executes the actual ref updates. See
      linkgit:githooks[5] for the full protocol description.
      
    -+REPORT HOOK
    -+-----------
    ++RECEIVE-REPORT HOOK
    ++-------------------
     +This hook is invoked by 'git-receive-pack' after all the ref updates
     +have been applied but before the report is sent to the client. The hook
     +receives the complete report in pkt-line format on stdin and its stdout
    @@ Documentation/githooks.adoc: The exit status of the hook is ignored for any stat
      status will cause the transaction to be aborted. The hook will not be
      called with "aborted" state in that case.
      
    -+report
    -+~~~~~~
    ++receive-report
    ++~~~~~~~~~~~~~~
     +
     +This hook is invoked by linkgit:git-receive-pack[1] when it reacts to
     +`git push` and updates references in its repository. It executes on
    @@ Documentation/githooks.adoc: The exit status of the hook is ignored for any stat
     +
     +* To abort the entire push unconditionally, exit with a non-zero
     +  status. In this case the hook's stdout is discarded, `receive-pack`
    -+  modifies all references to be rejected with a 'report hook failed'
    -+  error.
    ++  modifies all references to be rejected with a 'receive-report hook
    ++  failed' error.
     +
     +Any output written to standard error is forwarded to the client over
     +the sideband channel and will appear as `remote:` lines on clients
    @@ builtin/receive-pack.c: static int run_update_hook(struct command *cmd)
      	return code;
      }
      
    -+static int run_report_hook(struct strbuf *report)
    ++static int run_receive_report_hook(struct strbuf *report)
     +{
     +	struct child_process proc = CHILD_PROCESS_INIT;
     +	struct async sideband_async;
    @@ builtin/receive-pack.c: static int run_update_hook(struct command *cmd)
     +	const char *hook_path;
     +	int ret;
     +
    -+	hook_path = find_hook(the_repository, "report");
    ++	hook_path = find_hook(the_repository, "receive-report");
     +	if (!hook_path)
     +		return 0;
     +
     +	strvec_push(&proc.args, hook_path);
    -+	proc.trace2_hook_name = "report";
    ++	proc.trace2_hook_name = "receive-report";
     +
     +	prepare_sideband_async(&sideband_async, &saved_stderr,
     +			       &sideband_async_started);
    @@ builtin/receive-pack.c: static void report(struct command *commands, const char
     -	generate_response(&buf, commands, unpack_status, false);
     +	generate_response(&buf, commands, unpack_status, false, NULL);
     +
    -+	if (run_report_hook(&buf)) {
    ++	if (run_receive_report_hook(&buf)) {
     +		strbuf_reset(&buf);
     +		generate_response(&buf, commands, unpack_status, false,
    -+				  "report hook failed");
    ++				  "receive-report hook failed");
     +	}
      
      	if (use_sideband)
    @@ builtin/receive-pack.c: static void report_v2(struct command *commands, const ch
     -	generate_response(&buf, commands, unpack_status, true);
     +	generate_response(&buf, commands, unpack_status, true, NULL);
     +
    -+	if (run_report_hook(&buf)) {
    ++	if (run_receive_report_hook(&buf)) {
     +		strbuf_reset(&buf);
     +		generate_response(&buf, commands, unpack_status, true,
    -+			  "report hook failed");
    ++			  "receive-report hook failed");
     +	}
      
      	if (use_sideband)
    @@ t/meson.build: integration_tests = [
        't5409-colorize-remote-messages.sh',
        't5410-receive-pack.sh',
        't5411-proc-receive-hook.sh',
    -+  't5412-report-hook.sh',
    ++  't5412-receive-report-hook.sh',
        't5500-fetch-pack.sh',
        't5501-fetch-push-alternates.sh',
        't5502-quickfetch.sh',
     
    - ## t/t5412-report-hook.sh (new) ##
    + ## t/t5412-receive-report-hook.sh (new) ##
     @@
     +#!/bin/sh
     +
    -+test_description='test report hook'
    ++test_description='test receive-report hook'
     +
     +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
     +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
    @@ t/t5412-report-hook.sh (new)
     +	test_when_finished "git -C workbench remote remove origin" &&
     +	git init --bare upstream &&
     +
    -+	test_hook -C upstream --setup report <<-\EOF &&
    ++	test_hook -C upstream --setup receive-report <<-\EOF &&
     +	cat
     +	EOF
     +
    @@ t/t5412-report-hook.sh (new)
     +	git -C workbench remote add origin ../upstream &&
     +	git -C workbench push origin $A:refs/heads/main &&
     +
    -+	test_hook -C upstream --setup report <<-\EOF &&
    ++	test_hook -C upstream --setup receive-report <<-\EOF &&
     +	exit 1
     +	EOF
     +
    @@ t/t5412-report-hook.sh (new)
     +	make_user_friendly_and_stable_output <out >actual &&
     +	cat >expect <<-\EOF &&
     +	To ../upstream
    -+	 ! [remote rejected] <COMMIT-B> -> main (report hook failed)
    ++	 ! [remote rejected] <COMMIT-B> -> main (receive-report hook failed)
     +	EOF
     +	test_cmp expect actual
     +'
    @@ t/t5412-report-hook.sh (new)
     +	test_when_finished "git -C workbench remote remove origin" &&
     +
     +	git init --bare upstream &&
    -+	test_hook -C upstream --setup report <<-EOF &&
    ++	test_hook -C upstream --setup receive-report <<-EOF &&
     +	tee raw
     +	EOF
     +
    @@ t/t5412-report-hook.sh (new)
     +	git -C workbench remote add origin ../upstream &&
     +	git -C workbench push origin $A:refs/heads/main &&
     +
    -+	test_hook -C upstream --setup report <<-\EOF &&
    ++	test_hook -C upstream --setup receive-report <<-\EOF &&
     +	test-tool pkt-line unpack |
     +	sed "s/^ok /ng /" |
     +	test-tool pkt-line pack
    @@ t/t5412-report-hook.sh (new)
     +	git -C workbench remote add origin ../upstream &&
     +	git -C workbench push origin $A:refs/heads/main &&
     +
    -+	test_hook -C upstream --setup report <<-\EOF &&
    ++	test_hook -C upstream --setup receive-report <<-\EOF &&
     +	echo "push rejected: service X is down" >&2
     +	test-tool pkt-line unpack |
     +	sed "s/^ok \(.*\)/ng \1 service-x-is-down/" |
    @@ t/t5412-report-hook.sh (new)
     +	git -C workbench remote add origin ../upstream &&
     +	git -C workbench push origin $A:refs/heads/main &&
     +
    -+	test_hook -C upstream --setup report <<-\EOF &&
    ++	test_hook -C upstream --setup receive-report <<-\EOF &&
     +	echo "push rejected: service X is down" >&2
     +	tee raw
     +	EOF
    @@ t/t5412-report-hook.sh (new)
     +	git -C workbench remote add origin ../upstream &&
     +	git -C workbench push origin $A:refs/heads/main &&
     +
    -+	test_hook -C upstream --setup report <<-\EOF &&
    ++	test_hook -C upstream --setup receive-report <<-\EOF &&
     +	echo "hook-stderr-message" >&2
     +	exit 1
     +	EOF

---
base-commit: 11c6700f10234578d10523faf35656ca491425c9
change-id: 20260812-758-introduce-hook-5b3af9f1a7e8


Thanks
- Karthik
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help