Files
git/hook.c
Emily Shaffer 933f1b2e0c hook: add list command
Teach 'git hook list <hookname>', which checks the known configs in
order to create an ordered list of hooks to run on a given hook event.

Multiple commands can be specified for a given hook by providing
multiple "hook.<hookname>.command = <path-to-hook>" lines. Hooks will be
run in config order. If more properties need to be set on a given hook
in the future, commands can also be specified by providing
"hook.<hookname>.command = <hookcmd-name>", as well as a "[hookcmd
<hookcmd-name>]" subsection; at minimum, this subsection must contain a
"hookcmd.<hookcmd-name>.command = <path-to-hook>" line.

For example:

  $ git config --list | grep ^hook
  hook.pre-commit.command=baz
  hook.pre-commit.command=~/bar.sh
  hookcmd.baz.command=~/baz/from/hookcmd.sh

  $ git hook list pre-commit
  ~/baz/from/hookcmd.sh
  ~/bar.sh

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-05-21 15:37:50 -07:00

91 lines
2.1 KiB
C

#include "cache.h"
#include "hook.h"
#include "config.h"
static LIST_HEAD(hook_head);
void free_hook(struct hook *ptr)
{
if (ptr) {
strbuf_release(&ptr->command);
free(ptr);
}
}
static void emplace_hook(struct list_head *pos, const char *command)
{
struct hook *to_add = malloc(sizeof(struct hook));
to_add->origin = current_config_scope();
strbuf_init(&to_add->command, 0);
strbuf_addstr(&to_add->command, command);
list_add_tail(&to_add->list, pos);
}
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(void)
{
struct list_head *pos, *tmp;
list_for_each_safe(pos, tmp, &hook_head)
remove_hook(pos);
}
static int hook_config_lookup(const char *key, const char *value, void *hook_key_cb)
{
const char *hook_key = hook_key_cb;
if (!strcmp(key, hook_key)) {
const char *command = value;
struct strbuf hookcmd_name = STRBUF_INIT;
struct list_head *pos = NULL, *tmp = NULL;
/* Check if a hookcmd with that name exists. */
strbuf_addf(&hookcmd_name, "hookcmd.%s.command", command);
git_config_get_value(hookcmd_name.buf, &command);
if (!command)
BUG("git_config_get_value overwrote a string it shouldn't have");
/*
* TODO: implement an option-getting callback, e.g.
* get configs by pattern hookcmd.$value.*
* for each key+value, do_callback(key, value, cb_data)
*/
list_for_each_safe(pos, tmp, &hook_head) {
struct hook *hook = list_entry(pos, struct hook, list);
/*
* The list of hooks to run can be reordered by being redeclared
* in the config. Options about hook ordering should be checked
* here.
*/
if (0 == strcmp(hook->command.buf, command))
remove_hook(pos);
}
emplace_hook(pos, command);
}
return 0;
}
struct list_head* hook_list(const struct strbuf* hookname)
{
struct strbuf hook_key = STRBUF_INIT;
if (!hookname)
return NULL;
strbuf_addf(&hook_key, "hook.%s.command", hookname->buf);
git_config(hook_config_lookup, (void*)hook_key.buf);
return &hook_head;
}