aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Ratiu <adrian.ratiu@collabora.com>2026-03-25 21:54:57 +0200
committerJunio C Hamano <gitster@pobox.com>2026-03-25 14:00:46 -0700
commita8b1ba86d494ea8825292c91c243e5d84fd7ee2c (patch)
tree728b1bcaadeff06afdf8a575b797879a3901b799
parent4d10f4a9527e664e001b9747b1daff6681b3f807 (diff)
downloadgit-a8b1ba86d494ea8825292c91c243e5d84fd7ee2c.tar.xz
hook: replace hook_list_clear() -> string_list_clear_func()
Replace the custom function with string_list_clear_func() which is a more common pattern for clearing a string_list. To be able to do this, rework hook_clear() into hook_free(), so it can be passed to string_list_clear_func(). A slight complication is the need to keep a copy of the internal cb data free() pointer, however I think it's worth it since the API becomes cleaner, e.g. no more calls with NULL function args like hook_list_clear(hooks, NULL). In other words, the callers don't need to keep track of hook internal state to determine when cleanup is necessary or not (pass NULL) because each `struct hook` now owns its data_free callback. Suggested-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r--builtin/hook.c2
-rw-r--r--hook.c40
-rw-r--r--hook.h20
3 files changed, 37 insertions, 25 deletions
diff --git a/builtin/hook.c b/builtin/hook.c
index e641614b84..54b737990b 100644
--- a/builtin/hook.c
+++ b/builtin/hook.c
@@ -78,7 +78,7 @@ static int list(int argc, const char **argv, const char *prefix,
}
cleanup:
- hook_list_clear(head, NULL);
+ string_list_clear_func(head, hook_free);
free(head);
return ret;
}
diff --git a/hook.c b/hook.c
index b0226ed716..021110f216 100644
--- a/hook.c
+++ b/hook.c
@@ -52,8 +52,10 @@ const char *find_hook(struct repository *r, const char *name)
return path.buf;
}
-static void hook_clear(struct hook *h, hook_data_free_fn cb_data_free)
+void hook_free(void *p, const char *str UNUSED)
{
+ struct hook *h = p;
+
if (!h)
return;
@@ -64,22 +66,12 @@ static void hook_clear(struct hook *h, hook_data_free_fn cb_data_free)
free((void *)h->u.configured.command);
}
- if (cb_data_free)
- cb_data_free(h->feed_pipe_cb_data);
+ if (h->data_free && h->feed_pipe_cb_data)
+ h->data_free(h->feed_pipe_cb_data);
free(h);
}
-void hook_list_clear(struct string_list *hooks, hook_data_free_fn cb_data_free)
-{
- struct string_list_item *item;
-
- for_each_string_list_item(item, hooks)
- hook_clear(item->util, cb_data_free);
-
- string_list_clear(hooks, 0);
-}
-
/* Helper to detect and add default "traditional" hooks from the hookdir. */
static void list_hooks_add_default(struct repository *r, const char *hookname,
struct string_list *hook_list,
@@ -100,9 +92,15 @@ static void list_hooks_add_default(struct repository *r, const char *hookname,
if (options && options->dir)
hook_path = absolute_path(hook_path);
- /* Setup per-hook internal state cb data */
- if (options && options->feed_pipe_cb_data_alloc)
+ /*
+ * Setup per-hook internal state callback data.
+ * When provided, the alloc/free callbacks are always provided
+ * together, so use them to alloc/free the internal hook state.
+ */
+ if (options && options->feed_pipe_cb_data_alloc) {
h->feed_pipe_cb_data = options->feed_pipe_cb_data_alloc(options->feed_pipe_ctx);
+ h->data_free = options->feed_pipe_cb_data_free;
+ }
h->kind = HOOK_TRADITIONAL;
h->u.traditional.path = xstrdup(hook_path);
@@ -316,10 +314,16 @@ static void list_hooks_add_configured(struct repository *r,
CALLOC_ARRAY(hook, 1);
- if (options && options->feed_pipe_cb_data_alloc)
+ /*
+ * When provided, the alloc/free callbacks are always provided
+ * together, so use them to alloc/free the internal hook state.
+ */
+ if (options && options->feed_pipe_cb_data_alloc) {
hook->feed_pipe_cb_data =
options->feed_pipe_cb_data_alloc(
options->feed_pipe_ctx);
+ hook->data_free = options->feed_pipe_cb_data_free;
+ }
hook->kind = HOOK_CONFIGURED;
hook->u.configured.friendly_name = xstrdup(friendly_name);
@@ -362,7 +366,7 @@ int hook_exists(struct repository *r, const char *name)
{
struct string_list *hooks = list_hooks(r, name, NULL);
int exists = hooks->nr > 0;
- hook_list_clear(hooks, NULL);
+ string_list_clear_func(hooks, hook_free);
free(hooks);
return exists;
}
@@ -516,7 +520,7 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
run_processes_parallel(&opts);
ret = cb_data.rc;
cleanup:
- hook_list_clear(cb_data.hook_command_list, options->feed_pipe_cb_data_free);
+ string_list_clear_func(cb_data.hook_command_list, hook_free);
free(cb_data.hook_command_list);
run_hooks_opt_clear(options);
return ret;
diff --git a/hook.h b/hook.h
index 965794a5b8..a56ac20ccf 100644
--- a/hook.h
+++ b/hook.h
@@ -7,6 +7,9 @@
struct repository;
+typedef void (*hook_data_free_fn)(void *data);
+typedef void *(*hook_data_alloc_fn)(void *init_ctx);
+
/**
* Represents a hook command to be run.
* Hooks can be:
@@ -41,10 +44,15 @@ struct hook {
* Only useful when using `run_hooks_opt.feed_pipe`, otherwise ignore it.
*/
void *feed_pipe_cb_data;
-};
-typedef void (*hook_data_free_fn)(void *data);
-typedef void *(*hook_data_alloc_fn)(void *init_ctx);
+ /**
+ * Callback to free `feed_pipe_cb_data`.
+ *
+ * It is called automatically and points to the `feed_pipe_cb_data_free`
+ * provided via the `run_hook_opt` parameter.
+ */
+ hook_data_free_fn data_free;
+};
struct run_hooks_opt {
/* Environment vars to be set for each hook */
@@ -185,10 +193,10 @@ struct string_list *list_hooks(struct repository *r, const char *hookname,
struct run_hooks_opt *options);
/**
- * Frees the memory allocated for the hook list, including the `struct hook`
- * items and their internal state.
+ * Frees a struct hook stored as the util pointer of a string_list_item.
+ * Suitable for use as a string_list_clear_func_t callback.
*/
-void hook_list_clear(struct string_list *hooks, hook_data_free_fn cb_data_free);
+void hook_free(void *p, const char *str);
/**
* Frees the hook configuration cache stored in `struct repository`.