
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>
91 lines
2.1 KiB
C
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;
|
|
}
|