Key Takeaways
- Git Hooks are scripts that are executed when certain commands are run, helping to streamline development and improve efficiency. These scripts can be used to perform automatic lint checks as part of the commit process, preventing the commitment of syntactically broken code.
- Commit messages can be spell-checked automatically using a Git Hook, reducing the chance of spelling errors and maintaining professionalism. Additionally, hooks can be used to automatically check for formatting violations to ensure code conforms to adopted standards.
- Git Hooks can also be used during deployment, running tasks such as Composer automatically on a remote repository. This can simplify the process of managing application dependencies and streamline the deployment process.
When certain commands are run, Git searches the .git/hooks directory for suitable hook scripts which are then executed if found. Youâll find a small set of example scripts there (you can activate them by renaming them to remove the .sample prefix and setting their executable bit), and a complete list of hooks can be found in the githooks(5) man page.
This article suggests a handful of hooks which can streamline development and help improve your efficiency.
Lint Checks
Thereâs really no excuse, yet Iâll admit even Iâve done this once or twice before: commit syntactically broken code. But what if a lint check could be performed automatically as part of the commit process to safeguard from this? And if you run a lint check manually before each commit, automating it can protect against the occasional moment of forgetfulness.
The following shell code can be saved as .git/hooks/pre-commit (or appended if pre-commit hook code already exists) to trigger an automatic check whenever a commit is made:
[shell]#!/bin/bash
git diff âcached âname-status âdiff-filter=ACMR | while read STATUS FILE; do
if [[ â$FILEâ =~ ^.+(php|inc)$ ]]; then
php -l â$FILEâ 1> /dev/null
if [ $? -ne 0 ]; then
echo âAborting commit due to files with syntax errorsâ >&2
exit 1
fi
fi
done[/shell]
git diff reports on whatâs changed between commits, and the options above return only the files that have been added (A), copied (C), modified (M), or renamed (R) in the staged commit. The files with an extension of .php or .inc are targeted for a lint check, and a failed check will exit the script with a non-zero return code and thus abort the commit itself. Your code may be buggy, but at least you know itâs got all of its semicolons.
Spell-Check Commit Messages
Developers are supposed to be meticulous and have an eye for detail (at least thatâs what we all put on our CV when looking for a job), so a commit message with bad spelling can be embarrassing.
Commit messages give the reader a broad idea of what changes were made in a commit and thus have the potential to be read by a large audience, from other developers to compliance auditors. Even if you code only for yourself, writing professional messages is a good habit to develop, and automating a âsecond set of eyesâ can help reduce speling erors in your comit messeges.
The following code can be saved as .git/hooks/post-commit (or appended); it calls Aspell and outputs a list of suspect words. If there are errors, you can immediately fix the commit message by running git commit --amend.
[shell]#!/bin/bash
ASPELL=$(which aspell)
if [ $? -ne 0 ]; then
echo âAspell not installed â unable to check spellingâ >&2
exit
fi
AWK=$(which awk)
if [ $? -ne 0 ]; then
echo âAwk not installed â unable to filter spelling errorsâ >&2
WORDS=$($ASPELL list < â$1â)
else
WORDS=$($ASPELL list < â$1â | $AWK â!_[$0]++â)
fi
if [ -n â$WORDSâ ]; then
echo âPossible spelling errors found in commit message: â $WORDS >&2
fi[/shell]
You can also compile a supplemental dictionary using identifiers extracted from your projectâs source code (perhaps triggered with a post-checkout hook) and pass it to Aspell with --extra-dicts to reduce the number of false positives. Parsing code and managing dictionary files is beyond the scope of this article though, so Iâll leave it as an exercise for you to explore later.
Checking Standards
I donât know of any scientific research that definitively proves formatting standards reduce cognitive friction when reading code. Most of the standards irritatingly target only low-hanging fruit anyway: capitalize something this way, place your braces here and not there, etc. Standards that enforce good architectural design and address specific interoperability concerns have more merit in my opinion.
But if youâre working with people who think 81 characters on a line is the eighth deadly sin, it might help smooth political friction by making sure your code conforms to an adopted formatting standard. The following code can be used as a post-commit hook (.git/hooks/post-commit) to automatically check for formatting violations.
[shell]#!/bin/bash
DIR=$(git rev-parse âshow-toplevel)
EMAIL=$(git config user.email)
TMPFILE=$(mktemp)
git diff âcached âname-status âdiff-filter=ACMR | while read STATUS FILE; do
if [[ â$FILEâ =~ ^.+(php|inc)$ ]]; then
phpcs âstandard=zend â$FILEâ >> $TMPFILE
fi
done
if [ -s $TMPFILE ]; then
mailx -s âPHPCS: Coding Standards Violationsâ $EMAIL < $TMPFILE
fi
rm $TMPFILE[/shell]
The script uses PHP CodeSniffer to scan each PHP file in the commit for violations and then sends an email listing the violations that were found. If youâre using one of those buzzword productivity approaches like Getting Things Done to manage your email, you can set up an email filter that routes the received emails directly to your TODO folder to make sure you fix the violations in a timely manner.
Automatically Run Composer
So far weâve seen hooks that focus on commits and the development process, but there are also some useful hooks that can be executed at other points in a Git workflow, even deployment. Suppose you have a remote repository set up on a production server, and pushing to it is your approach to deployment.
Further suppose that you use Composer to manage your applicationâs dependencies and you only keep your own code in your repository. This means you still have additional work to do after a push, either copying the dependencies over manually or ssh-ing into the server to run composer.phar update.
To make your life easier, putting this in the remote repositoryâs .git/hooks/post-receive file for a post-receive hook will run Composer automatically.
[shell]#!/bin/bash
DIR=$(git rev-parse âshow-toplevel)
if [ -e â$DIR/composer.jsonâ ]; then
if [ -d â$DIR/vendorâ ]; then
composer.phar install
else
composer.phar update
fi
fi[/shell]
Conclusion
In this article I shared a handful of hooks that can hopefully streamline how you develop your applications and make you more efficient. Feel free to share whether you are taking advantage of this powerful feature or not, and maybe even some of your own hooks in the comments below.
Here are a few additional resources for hints, tips, and inspiration:
- Git â Customizing Git: Git Hooks
- Markâs Blog â Missing Git Hooks Documentation
- Meta Magic â Managing Project, User, and Global Git Hooks
- Ben Kulbertis â Synchronizing a MySQL Database with Git and Git Hooks
Frequently Asked Questions about Git Hooks
What are Git Hooks and how do they work?
Git Hooks are scripts that Git executes before or after events such as commit, push, and receive. They are a built-in feature of Git and are used to automate tasks in the development workflow. When a certain event occurs in your Git repository, the corresponding hook script is triggered. These scripts are stored in the âhooksâ directory of every Git repository and can be written in any scripting language.
How can I create a Git Hook?
To create a Git Hook, navigate to the â.git/hooksâ directory in your Git repository. Here, youâll find sample scripts for various hooks. To create a new hook, create a new file in this directory, give it a suitable name related to the event it corresponds to (like âpre-commitâ, âpost-commitâ, etc.), and make it executable. Write your script in this file.
Can I use Git Hooks to automate commit messages?
Yes, Git Hooks can be used to automate commit messages. The âprepare-commit-msgâ hook can be used for this purpose. This hook is invoked by âgit commitâ, and can be bypassed with ââno-verifyâ option. It takes a few parameters which provide information about the commit.
How can I share Git Hooks with my team?
By default, Git Hooks are not shared among team members because they reside in the â.gitâ directory, which is not version-controlled. However, you can create a âhooksâ directory in your project root, add your hook scripts there, and then instruct your team members to create symbolic links to these scripts in their â.git/hooksâ directory.
Can I use Git Hooks to enforce coding standards?
Yes, Git Hooks can be used to enforce coding standards. The âpre-commitâ hook is commonly used for this purpose. It is invoked by âgit commitâ, and if this script exits with a non-zero status, the commit is aborted.
How can I debug a Git Hook?
Debugging a Git Hook can be done by redirecting the output of your script to a file. This can be done by adding commands in your script that redirect the output to a file. This way, you can see the output of your script when it is run.
Can I use Git Hooks to automatically push after a commit?
Yes, you can use the âpost-commitâ hook to automatically push after a commit. This hook is invoked by âgit commitâ. It does not take any parameters, and its exit status does not affect the commit.
Can I use Git Hooks to run tests?
Yes, Git Hooks can be used to run tests. The âpre-pushâ hook is commonly used for this purpose. This hook is invoked by âgit pushâ, and if this script exits with a non-zero status, the push is aborted.
Can I use Git Hooks to prevent sensitive data from being committed?
Yes, Git Hooks can be used to prevent sensitive data from being committed. The âpre-commitâ hook can be used to check for patterns of sensitive data such as passwords, API keys, etc., and abort the commit if any are found.
Can I use Git Hooks to integrate with continuous integration/continuous deployment (CI/CD) systems?
Yes, Git Hooks can be used to integrate with CI/CD systems. The âpost-receiveâ hook is commonly used for this purpose. This hook is invoked by âgit receive-packâ on the remote repository, and can be used to trigger builds or deployments in your CI/CD system.