This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
git_hooks [2019/02/19 09:25] rpjday [Default hook installation for a new repository] |
git_hooks [2019/02/19 11:10] rpjday [Default hook installation for a new repository] |
||
---|---|---|---|
Line 9: | Line 9: | ||
* client-side | * client-side | ||
* server-side | * server-side | ||
+ | |||
+ | Note that hooks can get their arguments from any combination of: | ||
+ | |||
+ | * the environment | ||
+ | * command-line arguments | ||
+ | * stdin | ||
===== Default hook installation for a new repository ===== | ===== Default hook installation for a new repository ===== | ||
Line 26: | Line 32: | ||
* ''update.sample*'' | * ''update.sample*'' | ||
- | These are sample scripts supplied by Git that you can use as is, or customize to taste. In order to activate such scripts: | + | These are sample scripts supplied by Git that you can use as is, or customize to taste. In order to activate such scripts once they're copied to the new repository: |
* Make sure they are marked as executable. | * Make sure they are marked as executable. | ||
- | * Remove the ''.sample'' prefix. | + | * Remove the ''.sample'' suffix (exact spelling is important). |
- | ===== Properties of hooks ===== | + | ===== Where hooks are effectively run ===== |
- | * They must be marked executable in order to be active. | + | From ''man githooks'': |
- | * They normally live in ''$GIT_DIR/hooks'', unless overridden via ''core.hooksPath''. | + | |
- | * Running ''git init'' on an existing directory will copy in only new hooks; it will never overwrite existing hooks. | + | |
- | * Hooks can get their arguments via the environment, command-line arguments, and stdin. | + | |
- | + | ||
- | ===== Supporting code ===== | + | |
- | + | ||
- | ==== run_commit_hook() ==== | + | |
<code> | <code> | ||
- | int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...) | + | Before Git invokes a hook, it changes its working directory to |
- | { | + | either $GIT_DIR in a bare repository or the root of the working |
- | struct argv_array hook_env = ARGV_ARRAY_INIT; | + | tree in a non-bare repository. An exception are hooks triggered |
- | va_list args; | + | during a push (pre-receive, update, post-receive, post-update, |
- | int ret; | + | push-to-checkout) which are always executed in $GIT_DIR. |
- | + | ||
- | argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file); | + | |
- | + | ||
- | /* | + | |
- | * Let the hook know that no editor will be launched. | + | |
- | */ | + | |
- | if (!editor_is_used) | + | |
- | argv_array_push(&hook_env, "GIT_EDITOR=:"); | + | |
- | + | ||
- | va_start(args, name); | + | |
- | ret = run_hook_ve(hook_env.argv,name, args); | + | |
- | va_end(args); | + | |
- | argv_array_clear(&hook_env); | + | |
- | + | ||
- | return ret; | + | |
- | } | + | |
</code> | </code> | ||
- | + | ===== commit-related client-side hooks ===== | |
- | ==== builtin/commit.c ==== | + | |
- | + | ||
- | <code> | + | |
- | static int prepare_to_commit(const char *index_file, const char *prefix, | + | |
- | struct commit *current_head, | + | |
- | struct wt_status *s, | + | |
- | struct strbuf *author_ident) | + | |
- | { | + | |
- | struct stat statbuf; | + | |
- | struct strbuf committer_ident = STRBUF_INIT; | + | |
- | int commitable; | + | |
- | struct strbuf sb = STRBUF_INIT; | + | |
- | const char *hook_arg1 = NULL; | + | |
- | const char *hook_arg2 = NULL; | + | |
- | int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE); | + | |
- | int old_display_comment_prefix; | + | |
- | + | ||
- | /* This checks and barfs if author is badly specified */ | + | |
- | determine_author_info(author_ident); | + | |
- | + | ||
- | if (!no_verify && run_commit_hook(use_editor, index_file, "pre-commit", NULL)) | + | |
- | return 0; | + | |
- | + | ||
- | ... snip ... | + | |
- | + | ||
- | if (run_commit_hook(use_editor, index_file, "prepare-commit-msg", | + | |
- | git_path_commit_editmsg(), hook_arg1, hook_arg2, NULL)) | + | |
- | return 0; | + | |
- | + | ||
- | if (use_editor) { | + | |
- | struct argv_array env = ARGV_ARRAY_INIT; | + | |
- | + | ||
- | argv_array_pushf(&env, "GIT_INDEX_FILE=%s", index_file); | + | |
- | if (launch_editor(git_path_commit_editmsg(), NULL, env.argv)) { | + | |
- | fprintf(stderr, | + | |
- | _("Please supply the message using either -m or -F option.\n")); | + | |
- | exit(1); | + | |
- | } | + | |
- | argv_array_clear(&env); | + | |
- | } | + | |
- | + | ||
- | if (!no_verify && | + | |
- | run_commit_hook(use_editor, index_file, "commit-msg", git_path_commit_editmsg(), NULL)) { | + | |
- | return 0; | + | |
- | } | + | |
- | + | ||
- | return 1; | + | |
- | } | + | |
- | </code> | + | |
- | + | ||
- | <code> | + | |
- | int cmd_commit(int argc, const char **argv, const char *prefix) | + | |
- | { | + | |
- | const char *argv_gc_auto[] = {"gc", "--auto", NULL}; | + | |
- | static struct wt_status s; | + | |
- | static struct option builtin_commit_options[] = { | + | |
- | + | ||
- | ... snip ... | + | |
- | + | ||
- | run_commit_hook(use_editor, get_index_file(), "post-commit", NULL); | + | |
- | if (amend && !no_post_rewrite) { | + | |
- | commit_post_rewrite(current_head, &oid); | + | |
- | } | + | |
- | if (!quiet) { | + | |
- | unsigned int flags = 0; | + | |
- | + | ||
- | if (!current_head) | + | |
- | flags |= SUMMARY_INITIAL_COMMIT; | + | |
- | if (author_date_is_interesting()) | + | |
- | flags |= SUMMARY_SHOW_AUTHOR_DATE; | + | |
- | print_commit_summary(prefix, &oid, flags); | + | |
- | } | + | |
- | + | ||
- | UNLEAK(err); | + | |
- | UNLEAK(sb); | + | |
- | return 0; | + | |
- | } | + | |
- | </code> | + | |
- | + | ||
- | ===== The client-side hooks ===== | + | |
==== pre-commit ==== | ==== pre-commit ==== | ||
- | In a nutshell (from ''man githooks''): | + | From ''man githooks'': |
<code> | <code> | ||
- | This hook is invoked by git commit, and can be bypassed with | + | This hook is invoked by git commit(1), and can be bypassed with |
the --no-verify option. It takes no parameters, and is invoked | the --no-verify option. It takes no parameters, and is invoked | ||
before obtaining the proposed commit log message and making a | before obtaining the proposed commit log message and making a | ||
Line 210: | Line 113: | ||
# If there are whitespace errors, print the offending file names and fail. | # If there are whitespace errors, print the offending file names and fail. | ||
- | </code> | ||
- | |||
- | Here's the money command: | ||
- | |||
- | <code> | ||
- | exec git diff-index --check --cached $against -- | ||
</code> | </code> | ||
Line 346: | Line 243: | ||
This hook is meant primarily for notification, and cannot affect | This hook is meant primarily for notification, and cannot affect | ||
the outcome of git commit. | the outcome of git commit. | ||
+ | </code> | ||
+ | |||
+ | There is no sample script provided by Git; you're on your own here. | ||
+ | |||
+ | ===== Overriding the default hooks ===== | ||
+ | |||
+ | ==== git init ==== | ||
+ | |||
+ | When initializing a new repository, the template content used for the new repository comes from one of (in order of precedence): | ||
+ | |||
+ | * the ''%%--%%template=<template directory>'' option | ||
+ | * the value of the ''$GIT_TEMPLATE_DIR'' environment variable | ||
+ | * the ''init.templateDir'' configuration option | ||
+ | * the contents of ''/usr/share/git-core/templates/'' | ||
+ | |||
+ | ==== git clone ==== | ||
+ | |||
+ | When cloning a repository, the only way to override the installation of default hooks (and template content) is via the ''%%--%%template=<template directory>'' command-line option. | ||
+ | ==== During normal operation ==== | ||
+ | |||
+ | From ''man git-config'': | ||
+ | |||
+ | <code> | ||
+ | core.hooksPath | ||
+ | By default Git will look for your hooks in the | ||
+ | $GIT_DIR/hooks directory. Set this to different path, e.g. | ||
+ | /etc/git/hooks, and Git will try to find your hooks in that | ||
+ | directory, e.g. /etc/git/hooks/pre-receive instead of in | ||
+ | $GIT_DIR/hooks/pre-receive. | ||
+ | |||
+ | The path can be either absolute or relative. A relative | ||
+ | path is taken as relative to the directory where the hooks | ||
+ | are run (see the "DESCRIPTION" section of githooks(5)). | ||
+ | |||
+ | This configuration variable is useful in cases where you’d | ||
+ | like to centrally configure your Git hooks instead of | ||
+ | configuring them on a per-repository basis, or as a more | ||
+ | flexible and centralized alternative to having an | ||
+ | init.templateDir where you’ve changed default hooks. | ||
</code> | </code> |