
Conditioning Yourself With Git Hooks
What are Git Hooks?
Git hooks allow users to execute scripts before or after specific Git events. These can be executed either locally or server-side on a git repository. The Git documentation and Atlassian Git Tutorial provide more in-depth explanations about git hooks and how to use them.
In this post, I will cover some of the more common use cases for git hooks and how I use them to condition myself over time with some snippets of code included. There are no limits as to what can be done with git hooks besides your own imagination.
Enforcing Best Practices and Standards
When contributing to any project, there tends to be a set of guidelines outlining the procedure one must follow before having their contributions merged. There may be many other factors involved, but I believe that it is to ensure that the code style remains consistent, maintainable, and is of quality work.
While there are plenty of linter plugins out there for many of the editors we use daily, how often do we actually pay attention to the warnings shown? There may also be times when we don’t notice them at all. They simply don’t force us to address them immediately or stop us from committing them. We usually end up being alerted later on by an external service or during the review process. Instead, you can pre-emptively stop that from happening before you end up heading into a downwards spiral. Git hooks help.
Increased Error Visibility
The pre-commit
hook can be used to force us to address issues before allowing us to pass through the gate and commit the changes we’ve made. This allows us to minimize the amounts of commits made to address simple code styling issues while keeping our git history and code both clean and readable.
#!/bin/bash
# This obtains the list of files staged for the current commit and executes an
# additional script that runs the correct linter depending on the filename and
# would then output the list of files that have errors.
staged_files=`git diff --cached --name-only`
/usr/bin/node /path/to/linter-processing-script.js $staged_files
Preventing Mistakes Beforehand
There are always a number of issues that can happen unintentionally due to a variety of reasons. This can range from simple merge conflicts to quick hotfixes to mental exhaustion. There isn’t a perfect way to prevent some of these issues from happening, but we can always try to minimize when it occurs.
Common Pitfalls
While linters can help us locate syntax issues and debugger statements accidentally left in the code, we can also use the pre-commit
hook to check for unresolved merge conflicts, secret keys, private information, or are you working on the master branch? There are plenty of actions that can be done to help prevent these common mistakes from happening.
#!/bin/bash
branch=$(git rev-parse --abbrev-ref HEAD | grep -e 'master')
if [ $? -eq 0 ]; then
echo "!!!WARNING!! You should not be working on the master branch."
echo "Are you sure you want to continue? [y/N]"
read confirmation
if [ "$confirmation" != "Y" ] || [ "$confirmation" != "y" ]; then
exit 1
fi
fi
conflicts=`git diff --cached --name-only -S '<<<<<< HEAD'`
if [ -n "$conflicts" ]; then
echo "!!COMMIT REJECTED!! You have merge conflicts in the following file(s):"
echo "$conflicts"
exit 1
fi
stashed=`git diff --cached --name-only -S '<<<<<<< Updated upstream'`
if [ -n "$stashed" ]; then
echo "!!COMMIT REJECTED!! You have stashed conflicts in the following file(s):"
echo "$stashed"
exit 1
fi
Protect Yourself and Everyone
The pre-push
hook can be used to “protect” a branch from being pushed to for various reasons. Is it the end of the day? Are you exhausted mentally and/or physically? This is usually around the time you begin to wrap things up. If there isn’t a good reason to start pushing everything up, you can make sure that your sanity is still intact before allowing yourself to push up commits that may potentially break something. Usually, it can wait.
#!/bin/bash
# We want to make sure that we still have our sanity and are sure we want to
# continue working after 6PM. This can be adjusted accordingly.
sanity_time='18'
hour_of_day=`date +%H`
if [ "$hour_of_date" -ge "$sanity_time"]; then
echo "It looks like it's close to the end of the day. Do you still have your sanity? Are you really sure you want to push these commits? [y/N]"
read $confirmation
if [ "$confirmation" != "Y" ] || [ "$confirmation" != "y" ]; then
exit 1
fi
fi
Being a Team Player
I believe that it is very important that commit messages contain meaningful information. This makes it extremely easy for anyone to traverse the git history to find when a bug was introduced and locate the root cause of the issue. We also want to make sure that the code review process remains fast and simple. Cleaning up many of these issues allows everyone to save even more time in the end.
Proper Commit Messages
The commit-msg
hook can be used in combination with a spellchecker to ensure that your commit message is readable and doesn’t contain any errors. If there is an error, we can always use git commit --amend
to fix it before pushing it.
#!/bin/bash
aspell_path="/usr/bin/aspell"
errors=$($aspell_path list < "#1")
if [ -n "$errors" ]; then
echo "There are some possible spelling errors found in the commit message."
echo "Use git commit --amend to verify that your commit message is correct."
fi
The End Game
The main goal is to condition myself to address any possible issues early and to reinforce any coding standards I’m working with so that it becomes second nature. What were the results? The amount of time required to review my code for a merge, looking up code style rules, and making mistakes is shrinking. You aren’t the only one that benefits. So does everyone you work with.