If you're a software developer, you probably know about Git. You might use it for version control at work or for side projects. One interesting feature you may not have used yet, at least not intentionally, is a Git hook.
Git hooks are custom scripts that you can use to automate tasks that will be triggered either immediately before or after a Git command is executed.
The Git book explains it best in their Customizing Git - Git Hooks section.
Like many other Version Control Systems, Git has a way to fire off custom scripts when certain important actions occur. There are two groups of these hooks: client-side and server-side. Client-side hooks are triggered by operations such as committing and merging, while server-side hooks run on network operations such as receiving pushed commits. You can use these hooks for all sorts of reasons.
In this article, we will cover two things.
- A simple way to add a new Git hook.
- How to setup Git hooks for a project in a way that allows everyone to use them automatically. This means no one else will need to manually set anything up for the hooks to work.
Do you ever push your code, only to realize you forgot to run that darn formatting command? There's a Git hook for that!
Do you leave little comments in your code to remind yourself of things to fix, only to commit those comments by accident? Don't worry, there's a Git hook for that too!
Ever wonder why your code doesn't work after checking out a new branch, only to realize it's because you forgot to update submodules (again)? Let's "Git" hooked on Git hooks. (Ha!)
Okay, let's see what it looks like to go through each step of adding a Git hook to a project.
First we need to think of a task that we could automate. Say you like to write "FIXME" as a comment next to whatever needs to be fixed in the code. You plan to fix them before committing. Once you are finished, you remove the comment.
You probably remember to fix most of them, but once in a while you lose track and accidentally commit one or two of the comments or unfinished code sections. How can we prevent this from happening?
We can add a Git hook that will prevent us from successfully commiting code if it detects the phrase "FIXME" in any of the modified files.
First we need to write the hook. The simplest version might look something like this.
This is our Git hook script. A script can be written in many different languages. This one happens to be written in Bash.
You always use a shebang at the top of the hook script to specify which language you are writing it in. A shebang always starts with
Our shebang is
#!/bin/bash because we are writing our script in Bash. Other common shebangs you might see are:
We also need to specify the phrase that we want to search for in the code. Whatever phrase you put as the value for the variable "SEARCH_TERM" is what the script will detect and then prevent from being committed.
The last block of code is where we do the legwork of searching for the term and the logic for handling that. If it detects the search term anywhere in the modified files, it will display the messages we've written and then exit with a status of 1.
Exit 1 means that something went wrong, so the script stops and Git will not proceed with the commit.
If the search term is not detected however, then the logic inside our if-statement is ignored and it reaches the end of our script.
At this point it is implied that it exits with a status of 0. Exit 0 means that the script finished successfully, and so Git will proceed with the commit.
Now that our hook is written, we just need to place it in the designated hooks folder in our project that uses Git.
The folder we want to navigate to is the .git/hooks folder inside our local repository. The .git folder is hidden by default, but every repository that uses Git has one. Once there, we just need to place our new script inside.
We create a new file here and paste the code snippet inside. When we save it, we will name the file "pre-commit", without any file extension. Doing this will tell Git that this file is a Git hook script, and is specifically a pre-commit hook.
You'll notice there are several types of Git hooks in the .git/hooks folder by default. Each file has an example of something you can do with that type of Git hook. For a full list of the types of Git hooks available, check out the Git hooks documentation.
Great! Now we're ready to see it in action. Let's say we have a file in our project with the comment "FIXME" above a block of code we're working on.
Pretending we haven't noticed that code comment, we stage our file and then commit it. Oops! Luckily, our Git hook is ready to save the day. Let's see what happens.
Our pre-commit hook was triggered automatically, and scanned the file for the phrase "FIXME". It detected the phrase and forced our commit to fail.
Good thing we left a useful error message so we weren't left totally confused. Now we can either finish fixing whatever the code issue was, or if it's done then we just need to remove the comment.
After we remove that comment, we can successfully commit the code.
A couple of things you might be wondering. Does this work if we write "FIXME" in lowercase? What if we write it like "FIXME:" instead? Also, is this hook checking for comments only, or if the phrase found its way into a block of text inside an HTML <p> tag, would it still stop the commit?
The phrase is case-sensitive based on how we wrote our hook, so in this case it needs to be all uppercase. Any characters surrounding it won't matter though, as long as it says exactly "FIXME". Our hook is only checking for that phrase, so it will work even if it is found outside of a code comment.
This is a simple example, but you can create more complex hooks to automate trickier Git workflows too.
You can find a variety of Git hook examples on a GitHub repository that I have linked at the bottom of this article under the Conclusion section.
Manually adding a Git hook to your project is fine, especially if it's a solo project. But what about your team's project? How can we allow everyone to benefit from your Git hooks?
It's actually pretty straightforward if you use Husky, a tool specifically developed to make sharing Git hooks easy.
Let's say you want to add automatic code formatting to your project, to help keep the code looking consistent. We can use Prettier, a popular formatting tool. Here are the steps you'd take.
- Install Prettier with
npm i prettier --save-dev
- Install Husky and lint-staged with
npx mrm lint-staged
- Add this as another script under your package.json scripts:
"prettier": "prettier --write '**/*.js'"
It's that easy! Once these changes are added to the project, the prettier formatter will automatically run on any modified files that do not match the formatting settings.
Note that the files this example checks are any that match the glob pattern
You can also execute
npm run prettier manually to run it on all current files that match the glob pattern in the project. This is useful if you want to use prettier but already have a lot of existing code that needs the formatting updated.
You can find more information on automating Prettier with Git hooks on the Prettier pre-commit hook documentation.
I wanted a place to keep a list of all the useful Git hooks I've come across or written over time, and that place is my Awesome Git Hooks repository on GitHub. Feel free to check there for Git hook ideas.
Have a useful Git hook to share? Please consider contributing it to the GitHub repo, if it's not already on there. Let's help each other make the most out of Git hooks!
If you don't already use Git hooks in your day to day coding, I hope you'll give it a try. Automating your Git workflow will save you time and can prevent all kinds of mistakes from happening.
Have you used Git hooks before? Which ones do you use the most? Let me know what you think about hooks in the comments below!
This article was originally published on my personal website's blog, Joy Bytes.