From: "Shawn O. Pearce" <spearce@spearce.org>
To: Junio C Hamano <gitster@pobox.com>
Cc: git <git@vger.kernel.org>
Subject: [PATCH] push: Use sideband channel for hook messages
Date: Thu, 4 Feb 2010 19:37:48 -0800 [thread overview]
Message-ID: <20100205033748.GA19255@spearce.org> (raw)
Rather than sending hook messages over stderr, and losing them
entirely on git:// and smart HTTP transports, receive-pack now
puts them onto a multiplexed sideband channel if the send-pack
client asks for the side-band-64k capablity. This ensures that
hooks from the server can report their detailed error messages,
if any, no matter what git-aware transport is being used.
When the side band channel is being used the push client will wind up
prefixing all server messages with "remote: ", just like fetch does.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
We should have done this back when we added smart HTTP, because
otherwise the server can't send its hook messages back to the
client. I'd argue that is a bug, and so this should go in maint,
but I can also see how some might consider this a new feature... so
whatever. :-)
builtin-receive-pack.c | 111 +++++++++++++++++++++++++++++++++++++----------
builtin-send-pack.c | 67 +++++++++++++++++++++-------
run-command.c | 24 ++++++++--
run-command.h | 1 +
t/t5401-update-hooks.sh | 22 +++++-----
5 files changed, 170 insertions(+), 55 deletions(-)
diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c
index 78c0e69..e7bdd71 100644
--- a/builtin-receive-pack.c
+++ b/builtin-receive-pack.c
@@ -2,6 +2,7 @@
#include "pack.h"
#include "refs.h"
#include "pkt-line.h"
+#include "sideband.h"
#include "run-command.h"
#include "exec_cmd.h"
#include "commit.h"
@@ -27,11 +28,12 @@ static int receive_unpack_limit = -1;
static int transfer_unpack_limit = -1;
static int unpack_limit = 100;
static int report_status;
+static int use_sideband;
static int prefer_ofs_delta = 1;
static int auto_update_server_info;
static int auto_gc = 1;
static const char *head_name;
-static char *capabilities_to_send;
+static int send_capabilities = 1;
static enum deny_action parse_deny_action(const char *var, const char *value)
{
@@ -105,19 +107,21 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
- if (!capabilities_to_send)
+ if (!send_capabilities)
packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
else
- packet_write(1, "%s %s%c%s\n",
- sha1_to_hex(sha1), path, 0, capabilities_to_send);
- capabilities_to_send = NULL;
+ packet_write(1, "%s %s%c%s%s\n",
+ sha1_to_hex(sha1), path, 0,
+ " report-status delete-refs side-band-64k",
+ prefer_ofs_delta ? " ofs-delta" : "");
+ send_capabilities = 0;
return 0;
}
static void write_head_info(void)
{
for_each_ref(show_ref, NULL);
- if (capabilities_to_send)
+ if (send_capabilities)
show_ref("capabilities^{}", null_sha1, 0, NULL);
}
@@ -135,11 +139,25 @@ static struct command *commands;
static const char pre_receive_hook[] = "hooks/pre-receive";
static const char post_receive_hook[] = "hooks/post-receive";
+static int copy_to_sideband(int in, void *arg)
+{
+ char data[128];
+ while (1) {
+ ssize_t sz = xread(in, data, sizeof(data));
+ if (sz <= 0)
+ break;
+ send_sideband(1, 2, data, sz, use_sideband);
+ }
+ close(in);
+ return 0;
+}
+
static int run_receive_hook(const char *hook_name)
{
static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
struct command *cmd;
struct child_process proc;
+ struct async sideband_mux;
const char *argv[2];
int have_input = 0, code;
@@ -159,9 +177,23 @@ static int run_receive_hook(const char *hook_name)
proc.in = -1;
proc.stdout_to_stderr = 1;
+ if (use_sideband) {
+ memset(&sideband_mux, 0, sizeof(sideband_mux));
+ sideband_mux.proc = copy_to_sideband;
+ sideband_mux.is_reader = 1;
+ code = start_async(&sideband_mux);
+ if (code)
+ return code;
+ proc.err = sideband_mux.out;
+ }
+
code = start_command(&proc);
- if (code)
+ if (code) {
+ if (use_sideband)
+ finish_async(&sideband_mux);
return code;
+ }
+
for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->error_string) {
size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
@@ -173,6 +205,8 @@ static int run_receive_hook(const char *hook_name)
}
}
close(proc.in);
+ if (use_sideband)
+ finish_async(&sideband_mux);
return finish_command(&proc);
}
@@ -180,6 +214,8 @@ static int run_update_hook(struct command *cmd)
{
static const char update_hook[] = "hooks/update";
const char *argv[5];
+ struct child_process proc;
+ int code;
if (access(update_hook, X_OK) < 0)
return 0;
@@ -190,8 +226,18 @@ static int run_update_hook(struct command *cmd)
argv[3] = sha1_to_hex(cmd->new_sha1);
argv[4] = NULL;
- return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN |
- RUN_COMMAND_STDOUT_TO_STDERR);
+ memset(&proc, 0, sizeof(proc));
+ proc.no_stdin = 1;
+ proc.stdout_to_stderr = 1;
+ proc.err = use_sideband ? -1 : 0;
+ proc.argv = argv;
+
+ code = start_command(&proc);
+ if (code)
+ return code;
+ if (use_sideband)
+ copy_to_sideband(proc.err, NULL);
+ return finish_command(&proc);
}
static int is_ref_checked_out(const char *ref)
@@ -380,8 +426,9 @@ static char update_post_hook[] = "hooks/post-update";
static void run_update_post_hook(struct command *cmd)
{
struct command *cmd_p;
- int argc, status;
+ int argc;
const char **argv;
+ struct child_process proc;
for (argc = 0, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
if (cmd_p->error_string)
@@ -403,8 +450,18 @@ static void run_update_post_hook(struct command *cmd)
argc++;
}
argv[argc] = NULL;
- status = run_command_v_opt(argv, RUN_COMMAND_NO_STDIN
- | RUN_COMMAND_STDOUT_TO_STDERR);
+
+ memset(&proc, 0, sizeof(proc));
+ proc.no_stdin = 1;
+ proc.stdout_to_stderr = 1;
+ proc.err = use_sideband ? -1 : 0;
+ proc.argv = argv;
+
+ if (!start_command(&proc)) {
+ if (use_sideband)
+ copy_to_sideband(proc.err, NULL);
+ finish_command(&proc);
+ }
}
static void execute_commands(const char *unpacker_error)
@@ -464,6 +521,8 @@ static void read_head_info(void)
if (reflen + 82 < len) {
if (strstr(refname + reflen + 1, "report-status"))
report_status = 1;
+ if (strstr(refname + reflen + 1, "side-band-64k"))
+ use_sideband = LARGE_PACKET_MAX;
}
cmd = xmalloc(sizeof(struct command) + len - 80);
hashcpy(cmd->old_sha1, old_sha1);
@@ -563,17 +622,25 @@ static const char *unpack(void)
static void report(const char *unpack_status)
{
struct command *cmd;
- packet_write(1, "unpack %s\n",
- unpack_status ? unpack_status : "ok");
+ struct strbuf buf = STRBUF_INIT;
+
+ packet_buf_write(&buf, "unpack %s\n",
+ unpack_status ? unpack_status : "ok");
for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->error_string)
- packet_write(1, "ok %s\n",
- cmd->ref_name);
+ packet_buf_write(&buf, "ok %s\n",
+ cmd->ref_name);
else
- packet_write(1, "ng %s %s\n",
- cmd->ref_name, cmd->error_string);
+ packet_buf_write(&buf, "ng %s %s\n",
+ cmd->ref_name, cmd->error_string);
}
- packet_flush(1);
+ packet_buf_flush(&buf);
+
+ if (use_sideband)
+ send_sideband(1, 1, buf.buf, buf.len, use_sideband);
+ else
+ safe_write(1, buf.buf, buf.len);
+ strbuf_release(&buf);
}
static int delete_only(struct command *cmd)
@@ -670,10 +737,6 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
else if (0 <= receive_unpack_limit)
unpack_limit = receive_unpack_limit;
- capabilities_to_send = (prefer_ofs_delta) ?
- " report-status delete-refs ofs-delta " :
- " report-status delete-refs ";
-
if (advertise_refs || !stateless_rpc) {
add_alternate_refs();
write_head_info();
@@ -707,5 +770,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
if (auto_update_server_info)
update_server_info(0);
}
+ if (use_sideband)
+ packet_flush(1);
return 0;
}
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 8fffdbf..4bbc39f 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -372,6 +372,15 @@ static void print_helper_status(struct ref *ref)
strbuf_release(&buf);
}
+static int sideband_decoder_callback(int out, void *data)
+{
+ int *fd = data;
+ int in = fd[0];
+ int ret = recv_sideband("send-pack", in, out);
+ close(out);
+ return ret;
+}
+
int send_pack(struct send_pack_args *args,
int fd[], struct child_process *conn,
struct ref *remote_refs,
@@ -382,18 +391,22 @@ int send_pack(struct send_pack_args *args,
struct strbuf req_buf = STRBUF_INIT;
struct ref *ref;
int new_refs;
- int ask_for_status_report = 0;
int allow_deleting_refs = 0;
- int expect_status_report = 0;
+ int status_report = 0;
+ int use_sideband = 0;
+ unsigned cmds_sent = 0;
int ret;
+ struct async sideband_decoder;
/* Does the other end support the reporting? */
if (server_supports("report-status"))
- ask_for_status_report = 1;
+ status_report = 1;
if (server_supports("delete-refs"))
allow_deleting_refs = 1;
if (server_supports("ofs-delta"))
args->use_ofs_delta = 1;
+ if (server_supports("side-band-64k"))
+ use_sideband = 1;
if (!remote_refs) {
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
@@ -456,28 +469,30 @@ int send_pack(struct send_pack_args *args,
if (!ref->deletion)
new_refs++;
- if (!args->dry_run) {
+ if (args->dry_run) {
+ ref->status = REF_STATUS_OK;
+ } else {
char *old_hex = sha1_to_hex(ref->old_sha1);
char *new_hex = sha1_to_hex(ref->new_sha1);
- if (ask_for_status_report) {
- packet_buf_write(&req_buf, "%s %s %s%c%s",
+ if (!cmds_sent && (status_report || use_sideband)) {
+ packet_buf_write(&req_buf, "%s %s %s%c%s%s",
old_hex, new_hex, ref->name, 0,
- "report-status");
- ask_for_status_report = 0;
- expect_status_report = 1;
+ status_report ? " report-status" : "",
+ use_sideband ? " side-band-64k" : "");
}
else
packet_buf_write(&req_buf, "%s %s %s",
old_hex, new_hex, ref->name);
+ ref->status = status_report ?
+ REF_STATUS_EXPECTING_REPORT :
+ REF_STATUS_OK;
+ cmds_sent++;
}
- ref->status = expect_status_report ?
- REF_STATUS_EXPECTING_REPORT :
- REF_STATUS_OK;
}
if (args->stateless_rpc) {
- if (!args->dry_run) {
+ if (!args->dry_run && cmds_sent) {
packet_buf_flush(&req_buf);
send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
}
@@ -487,23 +502,43 @@ int send_pack(struct send_pack_args *args,
}
strbuf_release(&req_buf);
- if (new_refs && !args->dry_run) {
+ if (use_sideband && cmds_sent) {
+ memset(&sideband_decoder, 0, sizeof(sideband_decoder));
+ sideband_decoder.proc = sideband_decoder_callback;
+ sideband_decoder.data = fd;
+ if (start_async(&sideband_decoder))
+ die("cannot start sideband decoder");
+ in = sideband_decoder.out;
+ }
+
+ if (new_refs && cmds_sent) {
if (pack_objects(out, remote_refs, extra_have, args) < 0) {
for (ref = remote_refs; ref; ref = ref->next)
ref->status = REF_STATUS_NONE;
+ if (use_sideband)
+ finish_async(&sideband_decoder);
return -1;
}
}
- if (args->stateless_rpc && !args->dry_run)
+ if (args->stateless_rpc && cmds_sent)
packet_flush(out);
- if (expect_status_report)
+ if (status_report && cmds_sent)
ret = receive_status(in, remote_refs);
else
ret = 0;
if (args->stateless_rpc)
packet_flush(out);
+ if (use_sideband && cmds_sent) {
+ int err = finish_async(&sideband_decoder);
+ if (err) {
+ error("sideband decoder failed %d", err);
+ ret = -1;
+ }
+ close(sideband_decoder.out);
+ }
+
if (ret < 0)
return ret;
for (ref = remote_refs; ref; ref = ref->next) {
diff --git a/run-command.c b/run-command.c
index cf2d8f7..7d1fd88 100644
--- a/run-command.c
+++ b/run-command.c
@@ -94,6 +94,9 @@ fail_pipe:
else if (need_err) {
dup2(fderr[1], 2);
close_pair(fderr);
+ } else if (cmd->err > 1) {
+ dup2(cmd->err, 2);
+ close(cmd->err);
}
if (cmd->no_stdout)
@@ -228,6 +231,8 @@ fail_pipe:
if (need_err)
close(fderr[1]);
+ else if (cmd->err)
+ close(cmd->err);
return 0;
}
@@ -326,10 +331,19 @@ static unsigned __stdcall run_thread(void *data)
int start_async(struct async *async)
{
int pipe_out[2];
+ int proc_fd, call_fd;
if (pipe(pipe_out) < 0)
return error("cannot create pipe: %s", strerror(errno));
- async->out = pipe_out[0];
+
+ if (async->is_reader) {
+ proc_fd = pipe_out[0];
+ call_fd = pipe_out[1];
+ } else {
+ call_fd = pipe_out[0];
+ proc_fd = pipe_out[1];
+ }
+ async->out = call_fd;
#ifndef WIN32
/* Flush stdio before fork() to avoid cloning buffers */
@@ -342,12 +356,12 @@ int start_async(struct async *async)
return -1;
}
if (!async->pid) {
- close(pipe_out[0]);
- exit(!!async->proc(pipe_out[1], async->data));
+ close(call_fd);
+ exit(!!async->proc(proc_fd, async->data));
}
- close(pipe_out[1]);
+ close(proc_fd);
#else
- async->fd_for_proc = pipe_out[1];
+ async->fd_for_proc = proc_fd;
async->tid = (HANDLE) _beginthreadex(NULL, 0, run_thread, async, 0, NULL);
if (!async->tid) {
error("cannot create thread: %s", strerror(errno));
diff --git a/run-command.h b/run-command.h
index fb34209..8aed83f 100644
--- a/run-command.h
+++ b/run-command.h
@@ -70,6 +70,7 @@ struct async {
int (*proc)(int fd, void *data);
void *data;
int out; /* caller reads from here and closes it */
+ unsigned is_reader:1;
#ifndef WIN32
pid_t pid;
#else
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index 64f66c9..c3cf397 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -118,19 +118,19 @@ test_expect_success 'send-pack produced no output' '
'
cat <<EOF >expect
-STDOUT pre-receive
-STDERR pre-receive
-STDOUT update refs/heads/master
-STDERR update refs/heads/master
-STDOUT update refs/heads/tofail
-STDERR update refs/heads/tofail
-STDOUT post-receive
-STDERR post-receive
-STDOUT post-update
-STDERR post-update
+remote: STDOUT pre-receive
+remote: STDERR pre-receive
+remote: STDOUT update refs/heads/master
+remote: STDERR update refs/heads/master
+remote: STDOUT update refs/heads/tofail
+remote: STDERR update refs/heads/tofail
+remote: STDOUT post-receive
+remote: STDERR post-receive
+remote: STDOUT post-update
+remote: STDERR post-update
EOF
test_expect_success 'send-pack stderr contains hook messages' '
- grep ^STD send.err >actual &&
+ grep ^remote: send.err | sed "s/ *\$//" >actual &&
test_cmp - actual <expect
'
--
1.7.0.rc1.199.g9253ab
--
Shawn.
next reply other threads:[~2010-02-05 10:03 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-02-05 3:37 Shawn O. Pearce [this message]
2010-02-05 5:53 ` [PATCH] push: Use sideband channel for hook messages Junio C Hamano
2010-02-05 11:58 ` Johannes Sixt
2010-02-05 15:32 ` Shawn O. Pearce
2010-02-05 15:51 ` Johannes Sixt
2010-02-05 16:14 ` Erik Faye-Lund
2010-02-05 17:29 ` Shawn O. Pearce
2010-02-05 12:07 ` Ilari Liusvaara
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20100205033748.GA19255@spearce.org \
--to=spearce@spearce.org \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).