What are Git hooks?
Git hooks are scripts that are executed every time a certain event occurs in Git. This allows the behavior of Git to be extended at will.
Recap: What is Git again?
Before you can understand Git hooks, you should know what Git actually is. Git is an open source version control software that allows teams of developers to work on a code base simultaneously without overwriting each other's changes. The software was developed in 2005 by Linus Torvalds, who is also the inventor of Linux. Since then, Git has saved countless programmers and project managers around the world many hours of work that would otherwise have been required to merge different versions of the code. You can find out more about this software here.
What do you need Git Hooks for?
Git hooks are particularly suitable for teams that strive for uniform development standards and workflows and want to automate processes. For example, you can write a hook that ensures that a notification is sent by email or Slack after every push to the master branch. Or you can write a hook that checks the formatting of commit messages and cancels the commit if necessary.
In the Drupal cosmos, Git hooks are particularly suitable for checking coding standards. Drupal is open source and has countless maintainers. In order to work better and more efficiently, uniform standards have therefore developed over time that define how Drupal code should be written. This starts with the naming of variables and functions and ends with line spacing and indentation. A hook can now be used to check whether these standards are adhered to before each commit.
What hooks are there?
Hooks are located in the "/.git/hooks" directory of your repository. The prerequisite for this is, of course, that you have initialized a git repository in your project. As the name of the folder suggests, all your hooks are located here and there are already some sample files there by default. As the files end with ".sample" and are not executable, they are ignored by Git. However, they already give you an insight into the hooks that can be used:
- applypatch-msg.sample
- pre-push.sample
- commit-msg.sample
- pre-rebase.sample
- post-update.sample
- prepare-commit-msg.sample
- pre-applypatch.sample
- update.sample
- pre-commit.sample
The names already indicate when the corresponding script is executed. If we now wanted a hook to be applied, we would have to remove the ".sample" file extension and make the script executable via a command in the command line:
chmod +x
However, these are not all the hooks that are available. For example, the "post-receive" hook, which is executed after a "git push" and is therefore well suited for continuous integration, is missing. A list of all available hooks including a description can be found here.
The scope of Git hooks
One problem with hooks is that they are not tracked by Git. This means that the "./git/hooks" folder cannot be committed and therefore cannot be shared between developers. This is problematic because, as described above, the value of hooks is highest in a team environment and it should be ensured that everyone uses the same hooks. The simplest solution for this is to create a new folder in the project (e.g. "my_project/custom_hooks"). This could be tracked by Git and the respective developers can copy the files into the ./git/hooks folder. Transparent communication is then important when hooks are changed. This is because every time a developer makes changes to a hook, the corresponding file must be copied to the /.git/hooks folder again.
Which programming language is used to write Git hooks?
Git hooks can be written in any programming language. However, Shell, Python, Ruby and Perl are most commonly used. The language of the script is determined by the shebang line ("#!"). For example, the shebang line on a Unix-based system would look like this if you want to use Python 3:
#!/usr/bin/env python3
Python is particularly suitable for writing Git hooks, as the various packages available can save the developer a lot of work compared to shell, for example.
Application example
Nowadays, it is project standard to work with ticket systems. In order to be able to "link" changes in the code to tickets, it makes sense to reference the corresponding ticket number in the commit messages. It is then possible to trace exactly when and why the respective changes were made. The first step is, of course, to tell the developers that they should always reference the ticket number. However, this does not protect against careless errors. This is where "commit-msg hook" comes into play. And this is how it works:
- Navigate to the /.git/hooks directory
- Remove the ".sample" file extension from the commit-msg.sample file. Git now recognizes this file as a hook.
- Execute the command "chmod +x commit-msg". This makes the file executable.
- Paste the following code into the file (e.g. with Vim or another text editor):
#!/usr/bin/env bash
# Regex to validate in commit message.
commit_regex='(#[0-9]+|merge)'
error_msg="Aborting commit. Your commit message is missing either a ticket number (e.g., #12345) or 'Merge'"
if ! grep -iqE "$commit_regex" "$1"; then
echo "$error_msg" >&2
exit 1
fi
This script checks whether your commit message contains a ticket number with "#" at the beginning or the word "merge". Something like "Ref #12345 Added new feature" or "Merge develop branch" would therefore be permitted, whereas "I did something" would result in an error message.
With Git hooks, the return value of the script is decisive. The return value 0 is considered a success and the Git process continues. With any other value, e.g. 1, the process is aborted. This is why you will also find the line "exit 1" at the end if the commit message does not pass the test against the regular expression.
Summary
Git hooks are executed when various Git events occur and therefore offer the possibility of creating standardized development processes in teams. They are located in the /.git/hooks folder of the Git repository and can be written in various programming languages. But beware: hooks are not tracked by hooks and must therefore be copied manually into the hooks directory by each developer, or you can build automation for this.