Git hooks are scripts that run automatically every time a particular event occurs in a Git repository. A Git hook allows you to customize Git’s internal behavior and they also trigger customizable actions at key points in the development life cycle of your project.
Git hooks have several common uses that include:
- Encouraging a commit policy.
- Altering a project environment, depending on the state of the repository.
- Implementing continuous integration workflows.
Since Git hooks are very customizable, you can use them to automate or optimize virtually any aspect of your development workflow.
In this article, I will give you a quick overview of Git hooks and how they work. Then, I will show you some common Git hooks and the definitions of what they do.
Git Hook Overview
Just like many other Version Control Systems, Git provides a way for you to fire off custom scripts when certain important actions occur in the repository. In turn, this makes them easy to install and configure.
There are two groups of 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.
Installing Git Hooks
Hooks reside in the .git/hooks directory of every Git repository. Git automatically populates this directory with example scripts when you initialize a repository. If you take a look inside .git/hooks, you will find the following files:
[ht_message mstyle=”info” title=”” show_icon=”” id=”” class=”” style=”” ]applypatch-msg.sample pre-push.sample
These represent most of the available hooks, but the .sample extension prevents them from executing by default. To “install” a hook, all you have to do is remove the .sample extension. Or, if you’re writing a new script from scratch, you can simply add a new file matching one of the above filenames, minus the .sample extension.
Try the example below and see how you do. Install a simple prepare-commit-msg hook. Remove the .sample extension from this script, and add the following to the file:
[ht_message mstyle=”info” title=”” show_icon=”” id=”” class=”” style=”” ]#!/bin/sh
echo “# Please include a useful commit message!” > $1[/ht_message]
Hooks need to be executable, so you might have to change the file permissions of the script if you are creating from scratch. For example, to make sure that prepare-commit-msg is executable, you would run the following command:
[ht_message mstyle=”info” title=”” show_icon=”” id=”” class=”” style=”” ]chmod +x prepare-commit-msg[/ht_message]
The built-in sample scripts above are very useful references, as they document the parameters that are passed in to each hook (they vary from hook to hook).
Let’s take a look at the client-side Git hooks and see what each one does.
pre-commit: This Git hook is used to inspect the snapshot that’s about to be committed, to see if you’ve forgotten something, to make sure tests run, or to examine whatever you need to inspect in the code.
prepare-commit-msg: This Git hook is run before the commit message editor is fired up but after the default message is created. It lets you edit the default message before the commit author sees it. This hook takes a few parameters: the path to the file that holds the commit message so far, the type of commit, and the commit SHA-1 if this is an amended commit.
commit-msg: This Git hook takes one parameter, which again is the path to a temporary file that contains the commit message written by the developer. If this script exits non-zero, Git aborts the commit process, so you can use it to validate your project state or commit message before allowing a commit to go through.
post-commit: This Git hook runs after the entire commit process is completed. It doesn’t take any parameters, but you can easily get the last commit by running git log -1 HEAD. Generally, this script is used for notification or something similar.
Email Workflow Hooks
applypatch-msg: This Git hook takes a single argument: the name of the temporary file that contains the proposed commit message. Git aborts the patch if this script exits non-zero. You can use this to make sure a commit message is properly formatted or to normalize the message by having the script edit it in place.
pre-applypatch: This Git hook is a little confusing because it is run after the patch is applied but before a commit is made, so you can use it to inspect the snapshot before making the commit.
post-applypatch: This Git hook runs after the commit is made. You can use it to notify a group or the author of the patch you pulled in that you’ve done so. You can’t stop the patching process with this script.
Other Client Hooks
pre-rebase: This Git hook runs before you rebase anything and can halt the process by exiting non-zero. You can use this hook to disallow rebasing any commits that have already been pushed.
post-rewrite: is run by commands that replace commits, such as git commit –amend and git rebase (though not by git filter-branch). Its single argument is which command triggered the rewrite, and it receives a list of rewrites on stdin. This hook has many of the same uses as the post-checkout and post-merge hooks.
post-merge: This Git hook runs after a successful merge command. You can use it to restore data in the working tree that Git can’t track, such as permissions data. This hook can likewise validate the presence of files external to Git control that you may want copied in when the working tree changes.
pre-push: This Git hook runs during git push. It runs after the remote refs have been updated but before any objects have been transferred. It receives the name and location of the remote as parameters and a list of to-be-updated refs through stdin. You can use it to validate a set of ref updates before a push occurs (a non-zero exit code will abort the push).
pre-receive: This Git hook is the first script to run when handling a push from a client is pre-receive. It takes a list of references that are being pushed from stdin; if it exits non-zero, none of them are accepted. You can use this hook to do things like making sure none of the updated references are non-fast-forwards. Also use it to do access control for all the refs and files they’re modifying with the push.
update: This Git hook is very similar to the pre-receive script, except that it’s run once for each branch the pusher is trying to update. If the pusher is trying to push to multiple branches, pre-receive runs only once, whereas update runs once per branch they’re pushing to. Instead of reading from stdin, this script takes three arguments: the name of the reference (branch), the SHA-1 that reference pointed to before the push, and the SHA-1 the user is trying to push. If the update script exits non-zero, only that reference is rejected; other references can still be updated.
post-receive: This Git hook runs after the entire process is completed and can be used to update other services or notify users. It takes the same stdin data as the pre-receive hook. Examples include:
- Emailing a List
- Notifying a Continuous Integration Server
- Updating a Ticket-Tracking System
You can even parse the commit messages to see if any tickets need to be opened, modified, or closed. This script can’t stop the push process, but the client doesn’t disconnect until it has completed. Be careful if you try to do anything that may take a long time.
Managing Git Hooks
Here are some fantastic options for managing your Git hooks. Take a look and see if they are of any interest to you.
Awesome Git Hooks
Awesome Git Hooks is a collection library of excellent Git hooks to have at hand.
Git Hooks is a full git hook manager at your disposal.
Overcommit is a very well-maintained, up-to-date, flexible Git hook manager.
Git Hooks Summary
Now we know that Git hooks can be used to alter internal behavior and receive notifications when certain events occur in a repository. Git hooks are ordinary scripts that reside in the .git/hooks repository, and they are pretty easy to install and customize if you are familiar with Git.
This article also gave you the client-side and server-side hooks available and a definition of what they all do.