hooks: convert worktree 'post-checkout' hook to hook library

Move the running of the 'post-checkout' hook away from run-command.h to the new hook.h library in builtin/worktree.c. For this special case we need a change to the hook API to teach it to run the hook from a given directory. We cannot skip the "absolute_path" flag and just check if "dir" is specified as we'd then fail to find our hook in the new dir we'd chdir() to. We currently don't have a use-case for running a hook not in our "base" repository at a given absolute path, so let's have "dir" imply absolute_path(find_hook(hook_name)). Signed-off-by: Emily Shaffer <emilyshaffer@google.com> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Acked-by: Emily Shaffer <emilyshaffer@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Emily Shaffer committed Dec 22, 2021 at 04:59 UTC 1a3017d908824ab42ca7a5af7073f4cdc5c88fbf
3 files changed +25 -15
builtin/worktree.c
+11 -15
@@ -382,21 +382,17 @@ done:
382 * is_junk is cleared, but do return appropriate code when hook fails.
383 */
384 if (!ret && opts->checkout) {
385 - const char *hook = find_hook("post-checkout");
386 - if (hook) {
387 - const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
388 - struct child_process cp = CHILD_PROCESS_INIT;
389 - cp.no_stdin = 1;
390 - cp.stdout_to_stderr = 1;
391 - cp.dir = path;
392 - strvec_pushv(&cp.env_array, env);
393 - cp.trace2_hook_name = "post-checkout";
394 - strvec_pushl(&cp.args, absolute_path(hook),
395 - oid_to_hex(null_oid()),
396 - oid_to_hex(&commit->object.oid),
397 - "1", NULL);
398 - ret = run_command(&cp);
399 - }
385 + struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
386 +
387 + strvec_pushl(&opt.env, "GIT_DIR", "GIT_WORK_TREE", NULL);
388 + strvec_pushl(&opt.args,
389 + oid_to_hex(null_oid()),
390 + oid_to_hex(&commit->object.oid),
391 + "1",
392 + NULL);
393 + opt.dir = path;
394 +
395 + ret = run_hooks_opt("post-checkout", &opt);
396 }
397
398 strvec_clear(&child_env);
hook.c
+8
@@ -57,6 +57,7 @@ static int pick_next_hook(struct child_process *cp,
57 strvec_pushv(&cp->env_array, hook_cb->options->env.v);
58 cp->stdout_to_stderr = 1;
59 cp->trace2_hook_name = hook_cb->hook_name;
60 + cp->dir = hook_cb->options->dir;
61
62 strvec_push(&cp->args, hook_path);
63 strvec_pushv(&cp->args, hook_cb->options->args.v);
@@ -109,6 +110,7 @@ static void run_hooks_opt_clear(struct run_hooks_opt *options)
110
111 int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
112 {
113 + struct strbuf abs_path = STRBUF_INIT;
114 struct hook_cb_data cb_data = {
115 .rc = 0,
116 .hook_name = hook_name,
@@ -130,6 +132,11 @@ int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
132 }
133
134 cb_data.hook_path = hook_path;
135 + if (options->dir) {
136 + strbuf_add_absolute_path(&abs_path, hook_path);
137 + cb_data.hook_path = abs_path.buf;
138 + }
139 +
140 run_processes_parallel_tr2(jobs,
141 pick_next_hook,
142 notify_start_failure,
@@ -139,6 +146,7 @@ int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
146 hook_name);
147 ret = cb_data.rc;
148 cleanup:
149 + strbuf_release(&abs_path);
150 run_hooks_opt_clear(options);
151 return ret;
152 }
hook.h
+6
@@ -12,6 +12,12 @@ struct run_hooks_opt
12
13 /* Emit an error if the hook is missing */
14 unsigned int error_if_missing:1;
15 +
16 + /**
17 + * An optional initial working directory for the hook,
18 + * translates to "struct child_process"'s "dir" member.
19 + */
20 + const char *dir;
21 };
22
23 #define RUN_HOOKS_OPT_INIT { \