Re: [PATCH v7 03/17] hook: add list command
From: Jonathan Tan <hidden>
Date: 2021-01-31 03:17:15
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.
I learned later that this isn't true - in patch 6, the commit message and one of the tests therein describe being able to skip a previously inline command by making a hookcmd section of the same name and just specifying "skip = true" there (without any command). Maybe just delete the "at minimum" part.
+static int list(int argc, const char **argv, const char *prefix)
{
- struct option builtin_hook_options[] = {
+ struct list_head *head, *pos;
+ struct hook *item;You asked for review on nits too so here's one: "item" should be declared in the list_for_each block. (That also makes it easier to see that we don't need to free it.)
quoted hunk ↗ jump to hunk
diff --git a/hook.c b/hook.c new file mode 100644 index 0000000000..937dc768c8 --- /dev/null +++ b/hook.c@@ -0,0 +1,115 @@ +#include "cache.h" + +#include "hook.h" +#include "config.h"
Usually we put all the includes together without any intervening blank lines.
+static void append_or_move_hook(struct list_head *head, const char *command)
+{
+ struct list_head *pos = NULL, *tmp = NULL;
+ struct hook *to_add = NULL;
+
+ /*
+ * remove the prior entry with this command; we'll replace it at the
+ * end.
+ */
+ list_for_each_safe(pos, tmp, head) {
+ struct hook *it = list_entry(pos, struct hook, list);
+ if (!strcmp(it->command.buf, command)) {
+ list_del(pos);
+ /* we'll simply move the hook to the end */
+ to_add = it;"break" here?
+ }
+ }
+
+ if (!to_add) {
+ /* adding a new hook, not moving an old one */
+ to_add = xmalloc(sizeof(struct hook));Style is to write sizeof(*to_add), I think. [snip]
+struct hook_config_cb
+{
+ struct strbuf *hookname;
struct declarations have "{" not on a line on its own.
Also, "hookname" could just be a char *?
+ struct list_head *list;
+};
+
+static int hook_config_lookup(const char *key, const char *value, void *cb_data)
+{
+ struct hook_config_cb *data = cb_data;
+ const char *hook_key = data->hookname->buf;
+ struct list_head *head = data->list;
+
+ if (!strcmp(key, hook_key)) {
+ const char *command = value;
+ struct strbuf hookcmd_name = STRBUF_INIT;
+
+ /* Check if a hookcmd with that name exists. */
+ strbuf_addf(&hookcmd_name, "hookcmd.%s.command", command);
+ git_config_get_value(hookcmd_name.buf, &command);So we don't really care whether git_config_get_value returns 0 or 1 as long as it doesn't touch "command" if there is no such hookcmd. That fits with how git_config_get_value() is documented, so that's great. Perhaps better to document as: If a hookcmd.%s.command config exists, replace the command with the value of that config. (If not, do nothing - git_config_get_value() is documented to not overwrite the value argument in this case.)
+
+ if (!command) {
+ strbuf_release(&hookcmd_name);
+ 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)
+ */
+
+ append_or_move_hook(head, command);
+
+ strbuf_release(&hookcmd_name);
+ }
+
+ return 0;
+}
+
+struct list_head* hook_list(const struct strbuf* hookname)"const char *hookname" should suffice? Also, search for "* " and replace with " *" where applicable (also in the .h file).
+{
+ struct strbuf hook_key = STRBUF_INIT;
+ struct list_head *hook_head = xmalloc(sizeof(struct list_head));
+ struct hook_config_cb cb_data = { &hook_key, hook_head };
+
+ INIT_LIST_HEAD(hook_head);
+
+ if (!hookname)
+ return NULL;
+
+ strbuf_addf(&hook_key, "hook.%s.command", hookname->buf);
+
+ git_config(hook_config_lookup, (void*)&cb_data);Do we need this void* cast?
quoted hunk ↗ jump to hunk
+ + strbuf_release(&hook_key); + return hook_head; +}diff --git a/hook.h b/hook.h new file mode 100644 index 0000000000..8ffc4f14b6 --- /dev/null +++ b/hook.h@@ -0,0 +1,26 @@ +#include "config.h" +#include "list.h" +#include "strbuf.h" + +struct hook +{ + struct list_head list; + /* + * Config file which holds the hook.*.command definition. + * (This has nothing to do with the hookcmd.<name>.* configs.) + */ + enum config_scope origin; + /* The literal command to run. */ + struct strbuf command;
"char *" would suffice? The tests look fine.