@@ -25,7 +25,7 @@ static int run(int argc, const char **argv, const char *prefix)
int rc = 0;
int ignore_missing = 0;
const char *hook_name;
- const char *hook_path;
+ struct list_head *hooks;
struct option run_options[] = {
OPT_BOOL(0, "ignore-missing", &ignore_missing,
@@ -58,15 +58,16 @@ static int run(int argc, const char **argv, const char *prefix)
* run_found_hooks() instead...
*/
hook_name = argv[0];
- hook_path = find_hook(hook_name);
- if (!hook_path) {
+ hooks = hook_list(hook_name);
+ if (list_empty(hooks)) {
/* ... act like run_hooks() under --ignore-missing */
if (ignore_missing)
return 0;
error("cannot find a hook named %s", hook_name);
return 1;
}
- rc = run_found_hooks(hook_name, hook_path, &opt);
+
+ rc = run_found_hooks(hook_name, hooks, &opt);
run_hooks_opt_clear(&opt);
@@ -4,6 +4,28 @@
#include "hook-list.h"
#include "config.h"
+static void free_hook(struct hook *ptr)
+{
+ if (ptr) {
+ free(ptr->feed_pipe_cb_data);
+ }
+ free(ptr);
+}
+
+static void remove_hook(struct list_head *to_remove)
+{
+ struct hook *hook_to_remove = list_entry(to_remove, struct hook, list);
+ list_del(to_remove);
+ free_hook(hook_to_remove);
+}
+
+void clear_hook_list(struct list_head *head)
+{
+ struct list_head *pos, *tmp;
+ list_for_each_safe(pos, tmp, head)
+ remove_hook(pos);
+}
+
static int known_hook(const char *name)
{
const char **p;
@@ -71,6 +93,30 @@ int hook_exists(const char *name)
return !!find_hook(name);
}
+struct list_head* hook_list(const char* hookname)
+{
+ struct list_head *hook_head = xmalloc(sizeof(struct list_head));
+
+ INIT_LIST_HEAD(hook_head);
+
+ if (!hookname)
+ return NULL;
+
+ if (have_git_dir()) {
+ const char *hook_path = find_hook(hookname);
+
+ /* Add the hook from the hookdir */
+ if (hook_path) {
+ struct hook *to_add = xmalloc(sizeof(*to_add));
+ to_add->hook_path = hook_path;
+ to_add->feed_pipe_cb_data = NULL;
+ list_add_tail(&to_add->list, hook_head);
+ }
+ }
+
+ return hook_head;
+}
+
void run_hooks_opt_clear(struct run_hooks_opt *o)
{
strvec_clear(&o->env);
@@ -108,6 +154,8 @@ static int pick_next_hook(struct child_process *cp,
struct hook_cb_data *hook_cb = pp_cb;
struct hook *run_me = hook_cb->run_me;
+ if (!run_me)
+ return 0;
/* reopen the file for stdin; run_command closes it. */
if (hook_cb->options->path_to_stdin) {
@@ -126,7 +174,10 @@ static int pick_next_hook(struct child_process *cp,
cp->dir = hook_cb->options->dir;
/* add command */
- strvec_push(&cp->args, run_me->hook_path);
+ if (hook_cb->options->absolute_path)
+ strvec_push(&cp->args, absolute_path(run_me->hook_path));
+ else
+ strvec_push(&cp->args, run_me->hook_path);
/*
* add passed-in argv, without expanding - let the user get back
@@ -137,6 +188,13 @@ static int pick_next_hook(struct child_process *cp,
/* Provide context for errors if necessary */
*pp_task_cb = run_me;
+ /* Get the next entry ready */
+ if (hook_cb->run_me->list.next == hook_cb->head)
+ hook_cb->run_me = NULL;
+ else
+ hook_cb->run_me = list_entry(hook_cb->run_me->list.next,
+ struct hook, list);
+
return 1;
}
@@ -170,24 +228,17 @@ static int notify_hook_finished(int result,
return 1;
}
-int run_found_hooks(const char *hook_name, const char *hook_path,
+int run_found_hooks(const char *hook_name, struct list_head *hooks,
struct run_hooks_opt *options)
{
- struct strbuf abs_path = STRBUF_INIT;
- struct hook my_hook = {
- .hook_path = hook_path,
- };
struct hook_cb_data cb_data = {
.rc = 0,
.hook_name = hook_name,
.options = options,
.invoked_hook = options->invoked_hook,
};
- if (options->absolute_path) {
- strbuf_add_absolute_path(&abs_path, hook_path);
- my_hook.hook_path = abs_path.buf;
- }
- cb_data.run_me = &my_hook;
+
+ cb_data.run_me = list_first_entry(hooks, struct hook, list);
if (options->jobs != 1)
BUG("we do not handle %d or any other != 1 job number yet", options->jobs);
@@ -201,15 +252,13 @@ int run_found_hooks(const char *hook_name, const char *hook_path,
&cb_data,
"hook",
hook_name);
- if (options->absolute_path)
- strbuf_release(&abs_path);
return cb_data.rc;
}
int run_hooks(const char *hook_name, struct run_hooks_opt *options)
{
- const char *hook_path;
+ struct list_head *hooks;
int ret;
if (!options)
BUG("a struct run_hooks_opt must be provided to run_hooks");
@@ -217,15 +266,17 @@ int run_hooks(const char *hook_name, struct run_hooks_opt *options)
if (options->path_to_stdin && options->feed_pipe)
BUG("choose only one method to populate stdin");
- hook_path = find_hook(hook_name);
+ hooks = hook_list(hook_name);
/*
* If you need to act on a missing hook, use run_found_hooks()
* instead
*/
- if (!hook_path)
+ if (list_empty(hooks))
return 0;
- ret = run_found_hooks(hook_name, hook_path, options);
+ ret = run_found_hooks(hook_name, hooks, options);
+
+ clear_hook_list(hooks);
return ret;
}
@@ -3,6 +3,7 @@
#include "strbuf.h"
#include "strvec.h"
#include "run-command.h"
+#include "list.h"
/*
* Returns the path to the hook file, or NULL if the hook is missing
@@ -17,6 +18,7 @@ const char *find_hook(const char *name);
int hook_exists(const char *hookname);
struct hook {
+ struct list_head list;
/* The path to the hook */
const char *hook_path;
@@ -27,6 +29,12 @@ struct hook {
void *feed_pipe_cb_data;
};
+/*
+ * Provides a linked list of 'struct hook' detailing commands which should run
+ * in response to the 'hookname' event, in execution order.
+ */
+struct list_head* hook_list(const char *hookname);
+
struct run_hooks_opt
{
/* Environment vars to be set for each hook */
@@ -103,6 +111,7 @@ struct hook_cb_data {
/* rc reflects the cumulative failure state */
int rc;
const char *hook_name;
+ struct list_head *head;
struct hook *run_me;
struct run_hooks_opt *options;
int *invoked_hook;
@@ -120,6 +129,10 @@ int run_hooks(const char *hook_name, struct run_hooks_opt *options);
* Takes an already resolved hook and runs it. Internally the simpler
* run_hooks() will call this.
*/
-int run_found_hooks(const char *hookname, const char *hook_path,
+int run_found_hooks(const char *hookname, struct list_head *hooks,
struct run_hooks_opt *options);
+
+/* Empties the list at 'head', calling 'free_hook()' on each entry */
+void clear_hook_list(struct list_head *head);
+
#endif
To prepare for multihook support, teach hook.[hc] to take a list of hooks at run_hooks and run_found_hooks. Right now the list is always one entry, but in the future we will allow users to supply more than one executable for a single hook event. Signed-off-by: Emily Shaffer <emilyshaffer@google.com> --- builtin/hook.c | 9 +++--- hook.c | 85 ++++++++++++++++++++++++++++++++++++++++---------- hook.h | 15 ++++++++- 3 files changed, 87 insertions(+), 22 deletions(-)