Skip to content

Commit fc148b1

Browse files
nasamuffingitster
authored andcommitted
receive-pack: convert update hooks to new API
The hook API avoids creating a custom struct child_process and other internal hook plumbing (e.g. calling find_hook()) and prepares for the specification of hooks via configs or running parallel hooks. Execution is still sequential through the run_hooks_opt .jobs == 1, which is the unchanged default for all hooks. When use_sideband==1, the async thread redirects the hook outputs to sideband 2, otherwise it is not used and the hooks write directly to the fds inherited from the main parent process. When .jobs == 1, run-command's poll loop is avoided entirely via the ungroup=1 option like before (this was Jeff's suggestion), achieving the same real-time output performance. When running in parallel, run-command with ungroup=0 will capture and de-interleave the output of each hook, then write to the parent stderr which is redirected via dup2 to the sideband thread, so that each parallel hook output is presented clearly to the client. Signed-off-by: Emily Shaffer <emilyshaffer@google.com> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Helped-by: Jeff King <peff@peff.net> Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent c45a34e commit fc148b1

1 file changed

Lines changed: 66 additions & 37 deletions

File tree

builtin/receive-pack.c

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,48 @@ static int copy_to_sideband(int in, int out UNUSED, void *arg UNUSED)
561561
return 0;
562562
}
563563

564+
/*
565+
* Start an async thread which redirects hook stderr over the sideband.
566+
* The original stderr fd is saved to `saved_stderr` and STDERR_FILENO is
567+
* redirected to the async's input pipe.
568+
*/
569+
static void prepare_sideband_async(struct async *sideband_async, int *saved_stderr, int *started)
570+
{
571+
*started = 0;
572+
573+
if (!use_sideband)
574+
return;
575+
576+
memset(sideband_async, 0, sizeof(*sideband_async));
577+
sideband_async->proc = copy_to_sideband;
578+
sideband_async->in = -1;
579+
580+
if (!start_async(sideband_async)) {
581+
*started = 1;
582+
*saved_stderr = dup(STDERR_FILENO);
583+
if (*saved_stderr >= 0)
584+
dup2(sideband_async->in, STDERR_FILENO);
585+
close(sideband_async->in);
586+
}
587+
}
588+
589+
/*
590+
* Restore the original stderr and wait for the async sideband thread to finish.
591+
*/
592+
static void finish_sideband_async(struct async *sideband_async, int saved_stderr, int started)
593+
{
594+
if (!use_sideband)
595+
return;
596+
597+
if (saved_stderr >= 0) {
598+
dup2(saved_stderr, STDERR_FILENO);
599+
close(saved_stderr);
600+
}
601+
602+
if (started)
603+
finish_async(sideband_async);
604+
}
605+
564606
static void hmac_hash(unsigned char *out,
565607
const char *key_in, size_t key_len,
566608
const char *text, size_t text_len)
@@ -941,29 +983,25 @@ static int run_receive_hook(struct command *commands,
941983

942984
static int run_update_hook(struct command *cmd)
943985
{
944-
struct child_process proc = CHILD_PROCESS_INIT;
986+
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
987+
struct async sideband_async;
988+
int sideband_async_started = 0;
989+
int saved_stderr = -1;
945990
int code;
946-
const char *hook_path = find_hook(the_repository, "update");
947991

948-
if (!hook_path)
949-
return 0;
992+
strvec_pushl(&opt.args,
993+
cmd->ref_name,
994+
oid_to_hex(&cmd->old_oid),
995+
oid_to_hex(&cmd->new_oid),
996+
NULL);
950997

951-
strvec_push(&proc.args, hook_path);
952-
strvec_push(&proc.args, cmd->ref_name);
953-
strvec_push(&proc.args, oid_to_hex(&cmd->old_oid));
954-
strvec_push(&proc.args, oid_to_hex(&cmd->new_oid));
998+
prepare_sideband_async(&sideband_async, &saved_stderr, &sideband_async_started);
955999

956-
proc.no_stdin = 1;
957-
proc.stdout_to_stderr = 1;
958-
proc.err = use_sideband ? -1 : 0;
959-
proc.trace2_hook_name = "update";
1000+
code = run_hooks_opt(the_repository, "update", &opt);
9601001

961-
code = start_command(&proc);
962-
if (code)
963-
return code;
964-
if (use_sideband)
965-
copy_to_sideband(proc.err, -1, NULL);
966-
return finish_command(&proc);
1002+
finish_sideband_async(&sideband_async, saved_stderr, sideband_async_started);
1003+
1004+
return code;
9671005
}
9681006

9691007
static struct command *find_command_by_refname(struct command *list,
@@ -1639,34 +1677,25 @@ static const char *update(struct command *cmd, struct shallow_info *si)
16391677

16401678
static void run_update_post_hook(struct command *commands)
16411679
{
1680+
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
1681+
struct async sideband_async;
16421682
struct command *cmd;
1643-
struct child_process proc = CHILD_PROCESS_INIT;
1644-
const char *hook;
1645-
1646-
hook = find_hook(the_repository, "post-update");
1647-
if (!hook)
1648-
return;
1683+
int sideband_async_started = 0;
1684+
int saved_stderr = -1;
16491685

16501686
for (cmd = commands; cmd; cmd = cmd->next) {
16511687
if (cmd->error_string || cmd->did_not_exist)
16521688
continue;
1653-
if (!proc.args.nr)
1654-
strvec_push(&proc.args, hook);
1655-
strvec_push(&proc.args, cmd->ref_name);
1689+
strvec_push(&opt.args, cmd->ref_name);
16561690
}
1657-
if (!proc.args.nr)
1691+
if (!opt.args.nr)
16581692
return;
16591693

1660-
proc.no_stdin = 1;
1661-
proc.stdout_to_stderr = 1;
1662-
proc.err = use_sideband ? -1 : 0;
1663-
proc.trace2_hook_name = "post-update";
1694+
prepare_sideband_async(&sideband_async, &saved_stderr, &sideband_async_started);
16641695

1665-
if (!start_command(&proc)) {
1666-
if (use_sideband)
1667-
copy_to_sideband(proc.err, -1, NULL);
1668-
finish_command(&proc);
1669-
}
1696+
run_hooks_opt(the_repository, "post-update", &opt);
1697+
1698+
finish_sideband_async(&sideband_async, saved_stderr, sideband_async_started);
16701699
}
16711700

16721701
static void check_aliased_update_internal(struct command *cmd,

0 commit comments

Comments
 (0)