Git hooks are one of the most underrated git features, and have the potential to increase your productivity as a developer. Have you ever wanted to run a command every time you
push? Lucky for you, that’s exactly what git hooks do — they’re custom scripts that run before or after git commands to automate manual tasks. Read on to find out how hooks could improve your workflow.
Why use git hooks?
I came to learn about hooks after embarking on a quest to automate my own workflow. You see, for better or worse, I make use of
FIXME: comments in my code to remind myself to revisit something. My usual process involved doing a global search for “
FIXME:” before committing, however I’m sure you can already see plenty of room for human error.
Hooks came to my rescue. I was able to create a simple pre-commit hook (code at bottom of post) that looks for
FIXME: comments in my attempted commits, and stops the commit it if any are present.
What if I wanted to commit one of those comments? Hooks are easy to override if they become a blocker. By supplying the
--no-verify parameter to the command (e.g.
git push --no-verify), the associated hooks will be skipped over completely, allowing the command to continue.
Since hooks can easily be skipped, it’s better to use a build pipeline if you have a process that must be enforced.
There are 17 hooks in total — some that execute before a command, and some that execute after. You can use hooks to enforce commit message length, ensure adequate test coverage, prevent secrets from being committed, or just print out fun ASCII art. The possibilities are endless.
Setting up hooks
Hooks can be set up in two ways — they can run for every repo on your machine, or only run within a specific repo. There are benefits to either method:
Added in version 2.9.0 of git, global hooks are executed for every repository. These are a great option if you have personal workflows that apply to every repository you interact with. Git will automatically look for global hooks in the configured
hooks folder, and invoke any it finds.
The default location for global hooks is:
$GIT_DIR/hooks. If you want to change the location of that folder, you can run the following commands (replacing
~/.githooks with your desired path):
Repo hooks only run for the repo in which they live, and will be skipped if a global hook of the same type exist. These hooks should be placed in the
.git/hooks directory within the repo. They’re a great option if you have hooks that are very specific to a project.
Keep in mind that you can’t commit hooks with your repo — they stay on your local machine. But if you think about it, committing hooks would be a security nightmare. It could give would-be attackers full access to run malicious scripts on your machine without your knowledge each time you clone a new repo.
Now for the fun part. As mentioned above, there are 17 hooks you can implement. Some of my favorites are:
- pre-commit: occurs before a
git commitis executed, and can prevent the commit from happening.
- post-commit: occurs after a
git commit, and is primarily used for notifications since it cannot affect the outcome of execution.
- pre-push: occurs before a
git push, and can prevent the push from happening.
- prepare-commit-msg: occurs before a
git commit, and can edit a commit message before it’s applied.
Creating a hook is as simple as creating a file with the same name as the hook you wish you implement (e.g.
.git/hooks/pre-commit) and making it executable (
chmod +x pre-commit). ← The executable part is key, otherwise git will ignore the file and move on.
As mentioned above, if the hook occurs before an operation (e.g.
pre-push), you have the power to prevent the command’s operation. To stop execution, the hook must exit with a non-zero status (e.g.
exit 1). Otherwise, you’re free to log messages, or anything else your heart desires.
To sum it all up, I present you with the shell script I created to solve the
FIXME: dilemma from the beginning of this article. Any language can be used to write a hook, but Shell and Python are common choices.
If you want to repurpose this script to search for a different string that shouldn’t be committed, you can modify the
SEARCH_TERM variable to look for any substring in incoming commits.
What do you think — are git hooks useful for your workflow? Feel free to share any hooks you’re using in the comments below.
If you found this article helpful and would like to see more like it, please let me know by leaving some claps. 👏