Githooks
Githooks: per-repo and global Git hooks with version control
Install / Use
/learn @rycus86/GithooksREADME
Githooks
A simple Shell script to support per-repository Git hooks, checked into the actual repository that uses them.
To make this work, it creates hook templates that are installed into the .git/hooks folders automatically on git init and git clone. When one of them executes, it will try to find matching files in the .githooks directory under the project root, and invoke them one-by-one. There's more to the story though, you can read about it under the Templates or global hooks section.
Check out the blog post for the long read!
Go version
Thanks to @gabyx, this project has now been also ported over to Go in gabyx/githooks. Check it out if you're after some extra features like parallel execution, automatic updates, GUI dialog integration, colored terminal output, and more...
Layout and options
Take this snippet of a project layout as an example:
/
└── .githooks/
└── commit-msg/
├── validate
└── add-text
└── pre-commit/
├── 01-validate
├── 02-lint
├── 03-test
├── docs.md
└── .ignore
└── post-checkout
└── ...
└── .ignore
└── .shared
└── .lfs-required
├── README.md
├── LICENSE
└── ...
All hooks to be executed live under the .githooks top-level folder, that should be checked into the repository. Inside, we can have directories with the name of the hook (like commit-msg and pre-commit above), or a file matching the hook name (like post-checkout in the example). The filenames in the directory do not matter, but the ones starting with a . will be excluded by default. All others are executed in alphabetical order according to the glob / LC_COLLATE rules. You can use the command line helper tool as git hooks list to list all the hooks that apply to the current repository and their current state.
Execution
If a file is executable, it is directly invoked, otherwise it is interpreted with the sh shell. All parameters of the hook are passed to each of the scripts.
Hooks related to commit events will also have a ${STAGED_FILES} environment variable set, that is the list of staged and changed files (according to git diff --cached --diff-filter=ACMR --name-only), one per line and where it makes sense (not post-commit). If you want to iterate over them, and expect spaces in paths, you might want to set IFS like this.
IFS="
"
for STAGED in ${STAGED_FILES}; do
...
done
The ACMR filter in the git diff will include staged files that are added, copied, modified or renamed.
Note: if the list of changes is over 100k characters, then instead of ${STAGED_FILES} you will get the ${STAGED_FILES_REFERENCE} variable set instead which will point to a temporary file containing this list. This is to avoid Argument list too long errors when executing hooks and other parts of the framework. If you have a large enough repository where this is a concern, you should probably start your hook files by examining if this reference is set, like shown below.
if [ -n "${STAGED_FILES_REFERENCE}" ]; then
STAGED_FILES="$(cat "${STAGED_FILES_REFERENCE}")"
fi
for STAGED in ${STAGED_FILES}; do
...
done
Supported hooks
The supported hooks are listed below. Refer to the Git documentation for information on what they do and what parameters they receive.
applypatch-msgpre-applypatchpost-applypatchpre-commitpre-merge-commitprepare-commit-msgcommit-msgpost-commitpre-rebasepost-checkoutpost-mergepre-pushpre-receiveupdatepost-receivepost-updatereference-transactionpush-to-checkoutpre-auto-gcpost-rewritesendemail-validatepost-index-change
The fsmonitor-watchman hook is currently not supported. If you have a use-case for it and want to use it with this tool, please open an issue.
Git Large File Storage support
If the user has installed Git Large File Storage (git-lfs) by calling
git lfs install globally or locally for a repository only, git-lfs installs 4 hooks when initializing (git init) or cloning (git clone) a repository:
post-checkoutpost-commitpost-mergepre-push
Since Githooks overwrites the hooks in .git/hooks, it will also run all Git LFS hooks internally if the git-lfs executable is found on the system path. You can enforce having git-lfs installed on the system by placing a ./githooks/.lfs-required file inside the repository, then if git-lfs is missing, a warning is shown and the hook will exit with code 1. For some post-* hooks this does not mean that the outcome of the git command can be influenced even tough the exit code is 1, for example post-commit hooks can't fail commits. A clone of a repository containing this file might still work but would issue a warning and exit with code 1, a push - however - will fail if git-lfs is missing.
It is advisable for repositories using Git LFS to also have a pre-commit hook (e.g. examples/lfs/pre-commit) checked in which enforces a correct installation of Git LFS.
Ignoring files
The .ignore files allow excluding files from being treated as a hook script. They allow glob filename patterns, empty lines and comments, where the line starts with a # character. In the above example, one of the .ignore files should contain *.md to exclude the pre-commit/docs.md Markdown file. The .githooks/.ignore file applies to each of the hook directories, and should still define filename patterns, *.txt instead of **/*.txt for example. If there is a .ignore file both in the hook type folder and in .githooks, the files whose filename matches any pattern from either of those two files will be excluded. You can also manage .ignore files using the command line helper tool, and running git hooks ignore <pattern>.
Hooks in individual repositories can be disabled as well, running git hooks disable ..., or all of them with git hooks config set disable, check their documentation or help for more information. Finally, all hook execution can be bypassed with a non-empty value in the $GITHOOKS_DISABLE environment variable too.
Shared hook repositories
The hooks are primarily designed to execute programs or scripts in the .githooks folder of a single repository. However, there are use-cases for common hooks, shared between many repositories with similar requirements and functionality. For example, you could make sure Python dependencies are updated on projects that have a requirements.txt file, or an mvn verify is executed on pre-commit for Maven projects, etc.
For this reason, you can place a .shared file inside the .githooks repository, which can hold a list of repositories, one per line, which hold common and shared hooks. Alternatively, you can have a shared repositories set by multiple githooks.shared local or global Git configuration variables, and the hooks in these repositories will execute for all local projects where Githooks is installed. Below are example values for these setting.
$ git config --global --get-all githooks.shared # shared hooks in global config (for all repositories)
https://github.com/shared/hooks-python.git
git@github.com:shared/repo.git@mybranch
$ cd myrepo
$ git config --local --get-all githooks.shared # shared hooks in local config (for specific repository)
ssh://user@github.com/shared/special-hooks.git@v3.3.3
/opt/myspecialhooks
$ cat .githooks/shared
ssh://user@github.com/shared/special-hooks.git@otherbranch
$ git hooks shared list
...
The install script offers to set up shared hooks in the global Git config, but you can do it any time by changing the global configuration variable.
Supported entries for shared hooks are:
-
All URLs Git supports such as:
ssh://github.com/shared/hooks-maven.git@mybranchand also the shortscpformgit@github.com:shared/hooks-maven.gitgit://github.com/shared/hooks-python.gitfile:///local/path/to/bare-repo.git@mybranch
All URLs can include a tag specification syntax at the end like
...@<tag>, where<tag>is a Git tag, branch or commit hash. Thefile://protocol is treated the same as a local path to a bare repository, see local paths below. -
Local paths to bare and non-bare repositories such as:
/local/path/to/checkout(gets used directly)/local/path/to/bare-repo.git(gets cloned internally)
Note that relative paths are relative to the path of the repository executing the hook. These entries are forbidden for shared hooks configured by
.githooks/.sharedper repository because it makes little sense and is a security risk.
Shared hooks repositories specified by URLs and local paths to bare repository will be checked out into the <install-prefix>/.githooks/shared folder (~/.githooks/shared by default), and are updated automatically after a post-merge event (typically a git pull) on any local repositories. Any other local path will be used directly and will not be updated or modified. Updates run only
Related Skills
node-connect
346.8kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
346.8kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
346.8kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
