[PATCH v3 0/3] hook: introduce the report hook for git-receive-pack(1)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]




---
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@xxxxxxxxx
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@xxxxxxxxx
---
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 report hook for git-receive-pack(1)
 Documentation/git-receive-pack.adoc | 15 +++
 Documentation/githooks.adoc | 43 ++++++++
 builtin/receive-pack.c | 137 ++++++++++++++++--------
 t/meson.build | 1 +
 t/t5412-report-hook.sh | 200 ++++++++++++++++++++++++++++++++++++
 5 files changed, 356 insertions(+), 40 deletions(-)
Range-diff versus v2:
-: ---------- > 1: 42aaf10403 doc: add proc-receive hook info in 'git-receive-pack.adoc'
-: ---------- > 2: cb55895d2a receive-pack: move message generation to separate function
1: 07fa5ba8bb ! 3: 5bfaea5033 hook: introduce the report hook for git-receive-pack(1)
 @@ Commit message
 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
 - report is sent to the client. The report consists of an 'unpack ok'
 - or 'unpack <error>' line, followed by one 'ok <refname>' or
 - 'ng <refname> <reason>' line per pushed ref, terminated by a flush
 - packet.
 + report is sent to the client. See linkgit:gitprotocol-pack[5] details on
 + the protocol structure.
 
 The hook's stdout fully replaces the report sent to the client.
 receive-pack fully buffers the hook's stdout before acting on the exit
 status, so the exit code is known before the client receives anything.
 - This gives two distinct behaviours depending on exit status:
 + This gives two distinct behaviors depending on exit status:
 
 - Exit 0: the hook's stdout is used as the report. The hook can
 rewrite 'ok' lines to 'ng' lines to signal per-ref rejection to the
 @@ Commit message
 rejected refs as '[remote rejected]' and exits with a non-zero
 status if any ref is 'ng'.
 
 - - Non-zero exit: the hook's stdout is discarded, receive-pack calls
 - die(), and no report is sent to the client at all. The client
 - observes a sideband disconnect and reports 'the remote end hung up
 - unexpectedly', treating the entire push as failed.
 + - Non-zero exit: the hook's stdout is discarded, receive-pack modifies
 + all references to be rejected with a '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
 @@ Commit message
 Signed-off-by: Karthik Nayak <karthik.188@xxxxxxxxx>
 
 ## Documentation/git-receive-pack.adoc ##
 -@@ Documentation/git-receive-pack.adoc: if the repository is packed and is served via a dumb transport.
 - exec git update-server-info
 - ----
 +@@ Documentation/git-receive-pack.adoc: requests. It handles refs whose names match the patterns defined by
 + `receive.procReceiveRefs` and executes the actual ref updates. See
 + linkgit:githooks[5] for the full protocol description.
 
 -+PROC-RECEIVE HOOK
 -+-----------------
 -+This hook is invoked by 'git-receive-pack' when it processes push
 -+requests. It handles refs whose names match the patterns defined by
 -+`receive.procReceiveRefs` and executes the actual ref updates. See
 -+linkgit:githooks[5] for the full protocol description.
 -+
 +REPORT HOOK
 +-----------
 +This hook is invoked by 'git-receive-pack' after all the ref updates
 @@ Documentation/git-receive-pack.adoc: if the repository is packed and is served v
 +replaces the report sent to the client. Allowing the hook to rewrite
 +the outcomes or abort the push completely. See linkgit:githooks[5] for
 +the full protocol description.
 - 
 ++
 QUARANTINE ENVIRONMENT
 ----------------------
 + 
 
 ## Documentation/githooks.adoc ##
 @@ Documentation/githooks.adoc: The exit status of the hook is ignored for any state except for the
 @@ Documentation/githooks.adoc: The exit status of the hook is ignored for any stat
 +
 +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
 -+the repository once after all refs have been updated and after
 -+`execute_commands()` has applied all accepted ref changes to the
 -+repository, but before the pkt-line encoded status report is sent back
 -+to the client.
 ++the repository once after all refs have been updated and after all
 ++accepted ref changes are applied to the repository, but before the
 ++pkt-line encoded status report is sent back to the client.
 +
 +The hook receives the complete pkt-line encoded status report on
 -+standard input. The report begins with an `unpack` line indicating
 -+whether the object transfer succeeded (`unpack ok` or
 -+`unpack <error>`), followed by one `ok <refname>` or
 -+`ng <refname> <reason>` line per ref that was pushed, and is
 -+terminated by a flush packet.
 -+
 -+The hook's standard output entirely replaces the report that is sent
 -+to the client. The hook must write a valid pkt-line encoded report in
 -+the same format it received. The hook's stdout is fully buffered by
 -+`receive-pack` before any data is sent to the client, so the hook's
 -+exit status is known before the client receives anything.
 ++standard input, see linkgit:gitprotocol-pack[5] for details on the
 ++structure. The hook's standard output entirely replaces the report
 ++that is sent to the client. The hook must write a valid pkt-line
 ++encoded report in the same format it received. The hook's stdout is
 ++fully buffered by `receive-pack` before any data is sent to the client,
 ++so the hook's exit status is known before the client receives anything.
 +
 +There are two distinct ways the hook can affect the push outcome:
 +
 @@ 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`
 -+ calls `die()`, and no report is sent to the client at all. The client
 -+ observes an unexpected sideband disconnect, making the entire push
 -+ appear to have failed. In general, the hook should never exit with a
 -+ non-zero status code and doing so would indicate a bug.
 ++ modifies all references to be rejected with a '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)
 static struct command *find_command_by_refname(struct command *list,
 					 const char *refname)
 {
 +@@ builtin/receive-pack.c: static void update_shallow_info(struct command *commands,
 + * Generate the response to be sent to the client invoking 'git-receive-pack(1)'.
 + * For v2 protocol, set `add_reports` to true, which will also add additional
 + * report per reference update.
 ++ * If `ref_error` is set, then all references will be rejected with the given
 ++ * error message.
 + */
 + static void generate_response(struct strbuf *buf, struct command *commands,
 +-			 const char *unpack_status, bool add_reports)
 ++			 const char *unpack_status, bool add_reports,
 ++			 const char *ref_error)
 + {
 + 	struct command *cmd;
 + 
 +@@ builtin/receive-pack.c: static void generate_response(struct strbuf *buf, struct command *commands,
 + 		if (cmd->error_string)
 + 			packet_buf_write(buf, "ng %s %s\n",
 + 					 cmd->ref_name, cmd->error_string);
 ++		else if (ref_error)
 ++			packet_buf_write(buf, "ng %s %s\n",
 ++					 cmd->ref_name, ref_error);
 + 		else
 + 			packet_buf_write(buf, "ok %s\n", cmd->ref_name);
 + 
 +-		if (!add_reports || cmd->error_string)
 ++		if (!add_reports || cmd->error_string || ref_error)
 + 			continue;
 + 
 + 		for (report = cmd->report; report; report = report->next) {
 @@ builtin/receive-pack.c: static void report(struct command *commands, const char *unpack_status)
 - 	}
 - 	packet_buf_flush(&buf);
 + {
 + 	struct strbuf buf = STRBUF_INIT;
 + 
 +-	generate_response(&buf, commands, unpack_status, false);
 ++	generate_response(&buf, commands, unpack_status, false, NULL);
 ++
 ++	if (run_report_hook(&buf)) {
 ++		strbuf_reset(&buf);
 ++		generate_response(&buf, commands, unpack_status, false,
 ++				 "report hook failed");
 ++	}
 
 -+	if (run_report_hook(&buf))
 -+		die("report hook failed");
 -+
 	if (use_sideband)
 		send_sideband(1, 1, buf.buf, buf.len, use_sideband);
 - 	else
 @@ builtin/receive-pack.c: static void report_v2(struct command *commands, const char *unpack_status)
 - 	}
 - 	packet_buf_flush(&buf);
 + {
 + 	struct strbuf buf = STRBUF_INIT;
 + 
 +-	generate_response(&buf, commands, unpack_status, true);
 ++	generate_response(&buf, commands, unpack_status, true, NULL);
 ++
 ++	if (run_report_hook(&buf)) {
 ++		strbuf_reset(&buf);
 ++		generate_response(&buf, commands, unpack_status, true,
 ++			 "report hook failed");
 ++	}
 
 -+	if (run_report_hook(&buf))
 -+		die("report hook failed");
 -+
 	if (use_sideband)
 		send_sideband(1, 1, buf.buf, buf.len, use_sideband);
 - 	else
 
 ## t/meson.build ##
 @@ t/meson.build: integration_tests = [
 @@ t/t5412-report-hook.sh (new)
 +	test_cmp expect actual
 +'
 +
 -+test_expect_success "non-zero exit causes receive-pack to die" '
 ++test_expect_success "non-zero exit reports as hook failed" '
 +	test_when_finished "rm -rf upstream" &&
 +	test_when_finished "git -C workbench remote remove origin" &&
 +
 @@ t/t5412-report-hook.sh (new)
 +	test_must_fail git -C workbench push origin $B:refs/heads/main >out 2>&1 &&
 +	make_user_friendly_and_stable_output <out >actual &&
 +	cat >expect <<-\EOF &&
 -+	fatal: report hook failed
 -+	send-pack: unexpected disconnect while reading sideband packet
 -+	fatal: the remote end hung up unexpectedly
 ++	To ../upstream
 ++	 ! [remote rejected] <COMMIT-B> -> main (report hook failed)
 +	EOF
 +	test_cmp expect actual
 +'
---
base-commit: 11c6700f10234578d10523faf35656ca491425c9
change-id: 20260812-758-introduce-hook-5b3af9f1a7e8
Thanks
- Karthik

[Index of Archives] [Linux Kernel Development] [Gcc Help] [IETF Annouce] [DCCP] [Netdev] [Networking] [Security] [V4L] [Bugtraq] [Yosemite] [MIPS Linux] [ARM Linux] [Linux Security] [Linux RAID] [Linux SCSI] [Fedora Users]

(追記) (追記ここまで)
Powered by Linux

AltStyle によって変換されたページ (->オリジナル) /