# Git Hooks

Hooks in Git are executable scripts that are triggered when certain events happen in Git. It's a way to customize Git's internal behaviour, automate tasks, enforce policies, and bring consistency across the team.

For example, hooks can check that passwords or access tokens are not committed, validate that commit messages conform to an agreed format, prevent unauthorized developers from pushing to a branch, update local directories after a checkout, and so on.

Git supports both client-side and server-side hooks. Hooks can be written in any programming language though it's common to use Bash, Perl, Python or Ruby.

## Discussion

• What use cases can benefit from Git Hooks?

The figure illustrates local add/commit followed by a push to the remote repository. Also shown are the relevant hooks. Each hook is given a specific name. Hooks are invoked by naming convention before or after specific events.

The pre-commit hook is called when the commit procedure begins. This hook can format the code against the project's styling guide, run linting or execute unit tests. By checking for missing semicolons, trailing whitespace, and debug statements code reviews can focus on more important issues. Hooks prepare-commit-msg and commit-msg can be used to format/validate the commit message.

After a successful commit, the hook post-commit could be used to send notifications. More commonly, when collaboration is via a remote repository, notifications are sent from the post-receive hook that's triggered after all references are updated. This hook can also deploy the latest changes to production. The update hook that runs once per branch can be used to enforce access control, that is, only authorized users can push to a branch.

• What are client-side and server-side hooks?

Client-side hooks run on developer machines whereas server-side hooks run on the server that hosts the repository. Committing, merging, rebasing, applying patches, and checkout are common Git operations that developers do on their development machines. Any of these commands can trigger their relevant client-side hooks. Server-side hooks are triggered by the git-receive-pack command, which is a result of developers pushing changes to the remote repository on a cloud-hosted Git server.

Developers have full control of client-side hooks. Developers can choose to disable all client-side hooks if they wish. If a project's best practices need to be strictly enforced, server-side hooks are the way to do it.

Some Git repository providers impose restrictions on the use of server-side hooks on their cloud-based servers. For example, GitHub's free tier doesn't allow developers to define server-side hooks though GitHub Enterprise Server supports them. However, there are alternatives to achieve server-side automation such as webhooks, GitHub Apps/Actions, GitLab CI/CD, GitLab push rules, etc.

• What are pre- and post- Git Hooks?

Client-side and server-side hooks can be either pre- and post- hooks. The pre- hooks can be used to validate stuff before allowing the requested operation. For example, pre-commit hook executes at the start of a commit workflow. If the commit is deemed invalid for any reason, the hook can return a non-zero value. This terminates the commit operation. By returning zero, the hook states that the commit operation can continue, including the execution of other relevant hooks.

The post- hooks execute after the requested operation is done. Such a hook's return value doesn't matter since the operation is already complete. For example, post-commit executes after a successful commit. These hooks are typically used to send emails or other notifications. They may also be used to trigger CI/CD pipelines. For example, post-receive could be used to deploy the updated code to production.

• What options help skip the execution of hooks?

It's possible to skip some hooks if their associated commands are called with certain options. Hooks pre-commit, pre-merge-commit, prepare-commit-msg, and commit-msg can be skipped if commit and merge commands are invoked with --no-verify option.

The hook post-checkout can be skipped if clone and worktree add commands are called with --no-checkout option. However, this hook is always called for checkout and switch commands.

• What essentials should a developer know to use Git Hooks?

By default, hooks are stored in .git/hooks within the project's repository. Git populates this folder with sample scripts that developers can use as a starting point. However, the file suffix .sample must be removed and scripts must be made executable. Developers can rewrite the scripts in their preferred language. Accordingly, the shebang (#! first line in script) must be updated with the correct path to the language.

If these sample scripts are deleted by mistake, we can retrieve them from Git templates folder. This may be /usr/local/git-core/templates/hooks (Linux) or C:\Program Files\Git\mingw64\share\git-core\templates\hooks (Windows). In fact, this copying from templates folder to the project's .git/ folder is what happens when we run git init.

While hooks are triggered in response to specific events, we can also manually trigger the execution of a hook. The command for this is git hook run <hook-name> <hook-args>. For example, we could trigger one hook from another hook. This command is a low-level internal helper command.

Beginners can start by reading the official documentation on Git Hooks and Chapter 14 of Loeliger's book on Git.

• What does it take to create and maintain reusable Git Hooks?

It's a good practice to commit hooks as part of the repository so that all developers reuse and execute the same hooks. However, it's not permitted to store them within a .git/hooks folder within the repository. One approach is store them in another folder, say hooks, and then create a soft link from .git/hooks to hooks folder after cloning the repository. Another approach is to configure the path to the hooks with the command git config core.hooksPath <path>.

To reuse across projects, hooks can be in a separate repository and cloned locally. Configure the hooks path globally to that folder: git config --global core.hooksPath <path>. This configuration variable has been available since Git version 2.9.0.

There's also a framework named pre-commit to manage pre-commit hooks. This framework includes dozens of tested pre-commit hooks written in Python that developers can reuse in their own projects. These include syntax checking (check-json, check-yaml), fixing problems (fix-byte-order-marker, trailing-whitespace), protecting secrets (detect-private-key, detect-aws-credentials), enforcing policies (no-commit-to-branch, forbid-new-submodules), and more.

The githooks.com lists many useful hooks and related tools that developers can use.

• How do I configure my project environment to use Git Hooks?

No special configuration is needed other than updating .git/hooks folder or configuring core.hooksPath variable. However, many developer tools attempt to simplify this further.

In Maven, a plugin can setup the hooks path. In Gradle, we can have a dependent build script to do this setup. In Node.js, Husky project allows us to wire up all the hooks in the package.json file. In PHP, hooks can be set up in composer.json file via the post-install-cmd configuration.

In different languages, frameworks are available to manage hooks: pre-commit (Python), Overcommit (Ruby), Lefthook (Ruby or Node.js).

• What are some criticisms of Git Hooks?

Hooks change Git's default behaviour. If a hook does something unusual, it may confuse developers new to the project. Moreover, hooks can be buggy and make things counterproductive. For this reason, some recommend using Git aliases and invoking scripts directly rather than relying on hooks.

Git doesn't provide any easy mechanism to share hooks among developers or from one repository to another. No doubt this is more secure but at the expense of convenience and reusability. Hooks problem with installation, maintainability, and reusability is to some extent solved by frameworks such as pre-commit.

Hooks are powerful. Developers may be tempted to run unit tests before each commit. This can slow things quite a bit and discourage developers from committing often. This can lead of data loss.

Server-side hooks are not universally enabled by cloud-hosted Git repository service providers.

## Milestones

2005

Linus Torvalds creates Git as a replacement to BitKeeper that's a distributed version control system. Even in Git v1.0.0 (December), hooks are included as a feature.

Mar
2007

Hook post-receive is introduced to replace post-update. The latter is expected to be deprecated from v1.6.0. The hook post-receive is more useful since it has access to both old and new references; post-update knows only the new references.

Sep
2007

Git 1.5.4 is released. In this release, git merge command calls the post-merge hook following a successful merge.

Feb
2008

Git 1.6.0 is released. This release ships sample hook scripts with the suffix .sample since on some filesystems it's not sufficient to ship them as non-executables. In Windows, all scripts are executable by default. Developers can rename the scripts if they wish to enable the hooks.

Dec
2008

Git 1.6.1 is released. In this release, git rebase command when used without the --no-verify option calls the pre-rebase hook. This hook can be used to prevent the rebasing of a branch.

May
2009

Git 1.6.3 is released. In this release, git clone command when used without the --no-checkout option calls the post-checkout hook. This hook is normally called after an update to the worktree, such as with the git checkout command.

Dec
2014

A critical vulnerability is discovered pertaining to Git hooks. Usually it's not possible to create a top-level folder named .git in a repository. However, in case-sensitive filesystems it's possible to create a folder named .GIT, .Git, etc. In Windows, which is case-insensitive, such a folder overwrites the default .git folder. By including malicious hooks, a hacker could execute arbitrary code on the client's machine. This threat is greatest for developers using third party or untrusted repositories. This vulnerability is patched within a week.

Apr
2015

Git 2.4.0 is released. This release adds a new server-side hook named push-to-checkout. By default, pushes to checked out branch would fail. With this hook, we can customize the behaviour by merging with or overwriting server-side changes.

Apr
2016

GitHub Enterprise Server 2.6 is released. This includes the pre-receive server-side hook that can be used to enforce push policies and optimize workflows. However, GitHub has included server-side hooks since 2013, it's first hook tasked with warning users when a repository is renamed.

Jun
2016

Git 2.9.0 is released. The hooks directory can now be configured via the core.hooksPath configuration variable.

Nov
2019

Git 2.24.0 is released. This release adds a new hook named pre-merge-commit. This hook is called after a successful merge but before obtaining the commit message. It can be bypassed with --no-verify option to the merge command.

Feb
2022

On GitHub, it's noticed that pushes to repositories can take as long as 880ms on average. The implementation is in Ruby. A lot of time is spent loading Ruby dependencies. By rewriting the hook as a Go service, average processing time is brought down to 10ms. This improvement also becomes part of GitHub Enterprise Server 3.4.

Author
No. of Edits
No. of Chats
DevCoins
5
0
1232
1805
Words
0
Likes
2392
Hits

## Cite As

Devopedia. 2022. "Git Hooks." Version 5, May 9. Accessed 2022-10-09. https://devopedia.org/git-hooks
Contributed by
1 author

Last updated on
2022-05-09 12:17:03
• Site Map