On Tue, May 14, 2019 at 7:24 AM brian m. carlson <sandals@xxxxxxxxxxxxxxxxxxxx> wrote: > > There are a variety of situations in which a user may want an error > behavior for multiple hooks other than the default. Add a config option, > hook.<name>.errorBehavior to allow users to customize this behavior on a An alternative name is onError, probably more often used for event callbacks. But I don't know, maybe errorBehavior is actually better. > per-hook basis. Provide options for the default behavior (exiting should we fall back to hook.errorBehavior? That allows people to set global policy, then customize just a small set of weird hooks. > early), executing all hooks and succeeding if all hooks succeed, or > executing all hooks and succeeding if any hook succeeds. > Signed-off-by: brian m. carlson <sandals@xxxxxxxxxxxxxxxxxxxx> > --- > config.c | 27 +++++++++++++ > run-command.c | 42 +++++++++++++++++--- > run-command.h | 5 +++ > t/lib-hooks.sh | 106 ++++++++++++++++++++++++++++++++++++++++++++++++- > 4 files changed, 173 insertions(+), 7 deletions(-) > > diff --git a/config.c b/config.c > index c2846df3f1..9cba4061a9 100644 > --- a/config.c > +++ b/config.c > @@ -19,6 +19,7 @@ > #include "utf8.h" > #include "dir.h" > #include "color.h" > +#include "run-command.h" > > struct config_source { > struct config_source *prev; > @@ -1093,6 +1094,29 @@ int git_config_color(char *dest, const char *var, const char *value) > return 0; > } > > +static int git_default_hook_config(const char *key, const char *value) > +{ > + const char *hook; > + size_t key_len; > + uintptr_t behavior; > + > + key += strlen("hook."); > + if (strip_suffix(key, ".errorbehavior", &key_len)) { > + hook = xmemdupz(key, key_len); > + if (!strcmp(value, "stop-on-first")) maybe stop-on-first-error (or if you go with the "onError" name, I think "stop" is enough). I know "stop on/after first hook" does not really make any sense when you think about it. Maybe stop-on-first is sufficient. I was going to suggest strcasecmp. But core.whitespace (also has multiple-word-values) already sets a precedent on strcmp. I think we're good. Or mostly good, I don't know, we still accept False, false and FALSE. > + behavior = HOOK_ERROR_STOP_ON_FIRST; This is basically the logical "and" behavior in a C expression. Which makes me think if anybody's crazy enough to need the "or" counterpart (i.e. run hooks, expect failure, keep going until the first success). I guess it's a crazy mode. We should not care about until a real use case shows up. > + else if (!strcmp(value, "report-any-error")) I couldn't guess based on this name alone, whether we continue or stop after the reporting part. The 7/7 document makes it clear though. So all good. > diff --git a/run-command.c b/run-command.c > index 191d6f6f7e..70fb19a55b 100644 > --- a/run-command.c > +++ b/run-command.c > @@ -1308,6 +1308,8 @@ int async_with_fork(void) > #endif > } > > +struct string_list hook_error_behavior = STRING_LIST_INIT_NODUP; Maybe stick this in 'struct repository'. I know most config variables are still global. But I think we have to move/reorganize them at some point. Most may end up in 'struct repository'. > @@ -1401,18 +1403,48 @@ int for_each_hook(const char *name, > void *data) > { > struct string_list paths = STRING_LIST_INIT_DUP; > - int i, ret = 0; > + int i, hret = 0; > + uintptr_t behavior = HOOK_ERROR_STOP_ON_FIRST; > + struct string_list_item *item; > + /* Use -2 as sentinel because failure to exec is -1. */ > + int ret = -2; > + > + item = string_list_lookup(&hook_error_behavior, name); > + if (item) > + behavior = (uintptr_t)item->util; > > find_hooks(name, &paths); > for (i = 0; i < paths.nr; i++) { > const char *p = paths.items[i].string; > > - ret = handler(name, p, data); > - if (ret) > - break; > + hret = handler(name, p, data); > + switch (behavior) { > + case HOOK_ERROR_STOP_ON_FIRST: > + if (hret) { > + ret = hret; > + goto out; > + } > + break; > + case HOOK_ERROR_REPORT_ANY_SUCCESS: > + if (ret == -2) > + ret = 1; > + if (!hret) > + ret = 0; > + break; > + case HOOK_ERROR_REPORT_ANY_ERROR: > + if (ret == -2) > + ret = 0; > + if (hret) > + ret = hret; > + break; > + default: > + BUG("unknown hook error behavior"); maybe BUG(".. behavior %d", behavior); > + } > } > - > +out: > string_list_clear(&paths, 0); > + if (ret == -2) > + return 0; > return ret; > } > -- Duy