mirror of
https://github.com/git-for-windows/git.git
synced 2026-06-27 13:21:17 -05:00
The Git project is not exactly the easiest project to get started in: it's written in C and POSIX shell, with bits of Perl, Rust and other languages sprinkled into it. On top of that, the project has grown somewhat organically over time, making the codebase hard to navigate. These are problems that we're aware of, and there have been and still are efforts to clean up some of the technical debt that is natural to exist an a project that is more than 20 years old. Furthermore, we provide resources to newcomers that help them out like our coding guidelines, code of conduct or "MyFirstContribution.adoc". But there is a rather practical problem: finding your way around in our project's tree is not easy. Doing a directory listing in the top-level directory will present you with more than 550 files, which makes it extremely hard for a newcomer to figure out what files they are even supposed to look at. This makes the onboarding experience somewhat harder than it really needs to be. This isn't only a problem for newcomers though, as I myself struggle to find the files I am looking for because of the sheer number of files. Besides the problem of discoverability it also creates a problem of structure. It is not obvious at all which files are part of "libgit.a" and which files are only linked into our final executables. So while we have this split in our build systems, that split is not evident at all in our tree. Introduce a new "lib/" directory and move all of our sources for "libgit.a" into it to fix these issues. It makes the split we have evident and reduces the number of files in our top-level tree from 550 files to ~80 files. This is still a lot of files, but it's significantly easier to navigate already. Furthermore, we can further iterate after this step and think about introducing a better structure for remaining files, as well. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
284 lines
8.2 KiB
C
284 lines
8.2 KiB
C
#ifndef HOOK_H
|
|
#define HOOK_H
|
|
#include "config.h"
|
|
#include "run-command.h"
|
|
#include "string-list.h"
|
|
#include "strmap.h"
|
|
#include "strvec.h"
|
|
|
|
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:
|
|
* 1. "traditional" (found in the hooks directory)
|
|
* 2. "configured" (defined in Git's configuration via hook.<friendly-name>.event).
|
|
* The 'kind' field determines which part of the union 'u' is valid.
|
|
*/
|
|
struct hook {
|
|
enum {
|
|
HOOK_TRADITIONAL,
|
|
HOOK_CONFIGURED,
|
|
} kind;
|
|
union {
|
|
struct {
|
|
const char *path;
|
|
} traditional;
|
|
struct {
|
|
const char *friendly_name;
|
|
const char *command;
|
|
enum config_scope scope;
|
|
bool disabled;
|
|
bool event_disabled;
|
|
} configured;
|
|
} u;
|
|
|
|
/**
|
|
* Whether this hook may run in parallel with other hooks for the same
|
|
* event. Only useful for configured (named) hooks. Traditional hooks
|
|
* always default to 0 (serial). Set via `hook.<name>.parallel = true`.
|
|
*/
|
|
bool parallel;
|
|
|
|
/**
|
|
* Opaque data pointer used to keep internal state across callback calls.
|
|
*
|
|
* It can be accessed directly via the third hook callback arg:
|
|
* struct ... *state = pp_task_cb;
|
|
*
|
|
* The caller is responsible for managing the memory for this data by
|
|
* providing alloc/free callbacks to `run_hooks_opt`.
|
|
*
|
|
* Only useful when using `run_hooks_opt.feed_pipe`, otherwise ignore it.
|
|
*/
|
|
void *feed_pipe_cb_data;
|
|
|
|
/**
|
|
* 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 */
|
|
struct strvec env;
|
|
|
|
/* Args to be passed to each hook */
|
|
struct strvec args;
|
|
|
|
/* Emit an error if the hook is missing */
|
|
unsigned int error_if_missing:1;
|
|
|
|
/**
|
|
* Number of processes to parallelize across.
|
|
*
|
|
* If > 1, output will be buffered and de-interleaved (ungroup=0).
|
|
* If == 1, output will be real-time (ungroup=1).
|
|
* If == 0, the 'hook.jobs' config is used or, if the config is unset,
|
|
* defaults to 1 (serial execution).
|
|
*/
|
|
unsigned int jobs;
|
|
|
|
/**
|
|
* An optional initial working directory for the hook,
|
|
* translates to "struct child_process"'s "dir" member.
|
|
*/
|
|
const char *dir;
|
|
|
|
/**
|
|
* A pointer which if provided will be set to 1 or 0 depending
|
|
* on if a hook was started, regardless of whether or not that
|
|
* was successful. I.e. if the underlying start_command() was
|
|
* successful this will be set to 1.
|
|
*
|
|
* Used for avoiding TOCTOU races in code that would otherwise
|
|
* call hook_exist() after a "maybe hook run" to see if a hook
|
|
* was invoked.
|
|
*/
|
|
int *invoked_hook;
|
|
|
|
/**
|
|
* Send the hook's stdout to stderr.
|
|
*
|
|
* This is the default behavior for all hooks except pre-push,
|
|
* which keeps stdout and stderr separate for backwards compatibility.
|
|
* When parallel execution is requested (jobs > 1), get_hook_jobs()
|
|
* overrides this to 1 for all hooks so run-command can de-interleave
|
|
* their outputs correctly.
|
|
*/
|
|
unsigned int stdout_to_stderr:1;
|
|
|
|
/**
|
|
* Path to file which should be piped to stdin for each hook.
|
|
*/
|
|
const char *path_to_stdin;
|
|
|
|
/**
|
|
* Callback used to incrementally feed a child hook stdin pipe.
|
|
*
|
|
* Useful especially if a hook consumes large quantities of data
|
|
* (e.g. a list of all refs in a client push), so feeding it via
|
|
* in-memory strings or slurping to/from files is inefficient.
|
|
* While the callback allows piecemeal writing, it can also be
|
|
* used for smaller inputs, where it gets called only once.
|
|
*
|
|
* Add hook callback initialization context to `feed_pipe_ctx`.
|
|
* Add hook callback internal state to `feed_pipe_cb_data`.
|
|
*
|
|
*/
|
|
feed_pipe_fn feed_pipe;
|
|
|
|
/**
|
|
* Opaque data pointer used to pass context to `feed_pipe_fn`.
|
|
*
|
|
* It can be accessed via the second callback arg 'pp_cb':
|
|
* ((struct hook_cb_data *) pp_cb)->hook_cb->options->feed_pipe_ctx;
|
|
*
|
|
* The caller is responsible for managing the memory for this data.
|
|
* Only useful when using `run_hooks_opt.feed_pipe`, otherwise ignore it.
|
|
*/
|
|
void *feed_pipe_ctx;
|
|
|
|
/**
|
|
* Some hooks need to create a fresh `feed_pipe_cb_data` internal state,
|
|
* so they can keep track of progress without affecting one another.
|
|
*
|
|
* If provided, this function will be called to alloc & initialize the
|
|
* `feed_pipe_cb_data` for each hook.
|
|
*
|
|
* The `feed_pipe_ctx` pointer can be used to pass initialization data.
|
|
*/
|
|
hook_data_alloc_fn feed_pipe_cb_data_alloc;
|
|
|
|
/**
|
|
* Called to free the memory initialized by `feed_pipe_cb_data_alloc`.
|
|
*
|
|
* Must always be provided when `feed_pipe_cb_data_alloc` is provided.
|
|
*/
|
|
hook_data_free_fn feed_pipe_cb_data_free;
|
|
};
|
|
|
|
/**
|
|
* Default initializer for hooks. Parallelism is opt-in: .jobs = 0 defers to
|
|
* the 'hook.jobs' config, falling back to serial (1) if unset.
|
|
*/
|
|
#define RUN_HOOKS_OPT_INIT { \
|
|
.env = STRVEC_INIT, \
|
|
.args = STRVEC_INIT, \
|
|
.stdout_to_stderr = 1, \
|
|
.jobs = 0, \
|
|
}
|
|
|
|
/**
|
|
* Initializer for hooks that must always run sequentially regardless of
|
|
* 'hook.jobs'. Use this when git knows the hook cannot safely be parallelized
|
|
* .jobs = 1 is non-overridable.
|
|
*/
|
|
#define RUN_HOOKS_OPT_INIT_FORCE_SERIAL { \
|
|
.env = STRVEC_INIT, \
|
|
.args = STRVEC_INIT, \
|
|
.stdout_to_stderr = 1, \
|
|
.jobs = 1, \
|
|
}
|
|
|
|
struct hook_cb_data {
|
|
/* rc reflects the cumulative failure state */
|
|
int rc;
|
|
const char *hook_name;
|
|
|
|
/**
|
|
* A list of hook commands/paths to run for the 'hook_name' event.
|
|
*
|
|
* The 'string' member of each item holds the path (for traditional hooks)
|
|
* or the unique friendly-name for hooks specified in configs.
|
|
* The 'util' member of each item points to the corresponding struct hook.
|
|
*/
|
|
struct string_list *hook_command_list;
|
|
|
|
/* Iterator/cursor for the above list, pointing to the next hook to run. */
|
|
size_t hook_to_run_index;
|
|
|
|
struct run_hooks_opt *options;
|
|
};
|
|
|
|
/**
|
|
* Provides a list of hook commands to run for the 'hookname' event.
|
|
*
|
|
* This function consolidates hooks from two sources:
|
|
* 1. The config-based hooks (not yet implemented).
|
|
* 2. The "traditional" hook found in the repository hooks directory
|
|
* (e.g., .git/hooks/pre-commit).
|
|
*
|
|
* The list is ordered by execution priority.
|
|
*
|
|
* The caller is responsible for freeing the memory of the returned list
|
|
* using string_list_clear() and free().
|
|
*/
|
|
struct string_list *list_hooks(struct repository *r, const char *hookname,
|
|
struct run_hooks_opt *options);
|
|
|
|
/**
|
|
* 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_free(void *p, const char *str);
|
|
|
|
/**
|
|
* Frees the hook configuration cache stored in `struct repository`.
|
|
* Called by repo_clear().
|
|
*/
|
|
void hook_cache_clear(struct strmap *cache);
|
|
|
|
/**
|
|
* Returns true if `name` is a recognized hook event name
|
|
* (e.g. "pre-commit", "post-receive").
|
|
*/
|
|
bool is_known_hook(const char *name);
|
|
|
|
/**
|
|
* Returns the path to the hook file, or NULL if the hook is missing
|
|
* or disabled. Note that this points to static storage that will be
|
|
* overwritten by further calls to find_hook and run_hook_*.
|
|
*/
|
|
const char *find_hook(struct repository *r, const char *name);
|
|
|
|
/**
|
|
* A boolean version of find_hook()
|
|
*/
|
|
int hook_exists(struct repository *r, const char *hookname);
|
|
|
|
/**
|
|
* Takes a `hook_name`, resolves it to a path with find_hook(), and
|
|
* runs the hook for you with the options specified in "struct
|
|
* run_hooks opt". Will free memory associated with the "struct run_hooks_opt".
|
|
*
|
|
* Returns the status code of the run hook, or a negative value on
|
|
* error().
|
|
*/
|
|
int run_hooks_opt(struct repository *r, const char *hook_name,
|
|
struct run_hooks_opt *options);
|
|
|
|
/**
|
|
* A wrapper for run_hooks_opt() which provides a dummy "struct
|
|
* run_hooks_opt" initialized with "RUN_HOOKS_OPT_INIT".
|
|
*/
|
|
int run_hooks(struct repository *r, const char *hook_name);
|
|
|
|
/**
|
|
* Like run_hooks(), a wrapper for run_hooks_opt().
|
|
*
|
|
* In addition to the wrapping behavior provided by run_hooks(), this
|
|
* wrapper takes a list of strings terminated by a NULL
|
|
* argument. These things will be used as positional arguments to the
|
|
* hook. This function behaves like the old run_hook_le() API.
|
|
*/
|
|
LAST_ARG_MUST_BE_NULL
|
|
int run_hooks_l(struct repository *r, const char *hook_name, ...);
|
|
#endif
|