Wilson Mar bio photo

Wilson Mar

Hello. Hire me!

Email me Calendar Skype call 310 320-7878

LinkedIn Twitter Gitter Instagram Youtube

Github Stackoverflow Pinterest

One git commit and you’re hooked

Español (Spanish)   Français (French)   Deutsch (German)   Italiano   Português   Cyrillic Russian   中文 (简体) Chinese (Simplified)   日本語 Japanese   한국어 Korean


Here is a tutorial on how to make Git on a local machine automatically run a “hook” script in response to git commands.

Dimensions to Hooks

Hooks scripts run on two locations:

Git and GitHub have been written to look for a hook program before and after each of these commands:

Just so we can use nerdy language, hooks before each event begin with “pre-“ and hooks after each even begin with “post-“. This illustration (by Sarah Goff-Dupont of Atlassian) identifies common uses across four situations:

Ensure adherance to standards

PROTIP: Some organizations impose a set of hook files on repositories in order to impose some standards, such as:

  • Reject passwords found in files. Doing it on the client is better becuase once some text in a repository, it is necessary to git rebase, which is rather messy.

  • Reject very short commit messages, such as 10 characters or less.

  • Move media files (.mp4, .mp3, .jpg, .png) from repositories that are only “supposed” to contain text to a repository hold media files that humans can’t read. The job of git-lfs (Large File System) is to move and replace binary files with a (texual) link to binary repositories.
  • etc.

Hooks On the client

The trouble with client hooks is that it’s rather intrusive to install them on each developer’s laptop.

The free Community Edition (CE) of IntelliJ IDEA from Jetbrains provides check-boxes to activate built-in Git hook functionality:

git intellij before commit 302x418-87kb

PROTIP: We don’t see how to get to Version Control in IntelliJ on videos because it’s often accessed via a hotkey - Apple command 9 on macs and Alt+9 on Windows.

See this video about VCS improvements in IntelliJ 2016.3.

Let’s use a sample project that already has .idea folder containing files that define configurations for IntelliJ.

IntelliJ has a project wizard to start from scratch on various languages.

Default Git Hook files

PROTIP: Every Git repository is created with a hooks folder containing sample hook files that are named so they don’t execute.

  1. Navigate into any Git repository you want to automate.

    cd .git/hooks

    Git and GitHub looks into this specific folder name for scripts to run when specific events occur.

    PROTIP: Git and GitHub have been written such that it recognizes specific file names for each internal event.

    PROTIP: Each Git repository is created with a set of sample automation files in the folder. But the file names end with “.sample” so that they won’t run.

    Listed alphabetically:


    There are others that Git automatically runs.

    post-rewrite runs after a commit is modified by a git commit --amend or git rebase.
  2. PROTIP: Click each link above for the lastest version online at:


    CAUTION: Nobody uses the default files as they are by just removing the “.sample” part of the file name because they probably don’t do what you want.

  3. Delete the sample files in your repo because it’s best to get the latest version from GitHub.

    No file extension

    Due to their Linux origins, Git and GitHub were written to recognize command files that have no file extension, since in Linux the type of command is defined in the first line within each file (with a “SheBang magic number”) rather than in the file name’s extension as in Windows.

    Multiple languages

    Git will run any script that can run on the command line (and properly installed):

    All commands in a particular hook file must be of the same language.

    Bash hook script

    During installation of git-lfs (GitHub’s Large File System), commands like these are placed in hook files:

    command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-commit.\n"; exit 2; }
    git lfs post-commit "$@"

    The above commands first checks if git-lfs is installed, then performs the git lfs command, with “$@” forwarding the attributes passed into the hook file.

    The hook files used by git-lfs are:

    • pre-push
    • post-checkout
    • post-commit
    • post-merge

    Python hook script

    NOTE: The minimal Python hook script (commit-msg):

    #!/usr/bin/env python
    # post-checkout hook
    import argparse
    def parse_args():
    def main(args=None):
    if __name__ == "__main__":
      args = parse_args()

    TODO: Print out values of arguments supplied when calling the hook.

    PowerShell script

    NOTE: To have Git run a PowerShell script, a shell script invokes an additional PowerShell (.ps1) file added in the hooks folder:

    # pre-commit (no file extension)
    c:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe \
    -ExecutionPolicy RemoteSigned -Command .\.git\hooks\pre-commit.ps1

Demo simple hooks

I have prepared a repository to demo use of local Git hooks to run. The repo contains a “hooks” folder containing script files. Files in the folder are copied into the .git/hooks folder where Git looks.

  1. In an internet browser, go to this repository (which we will later call the “upstream”):


  2. Sign in using your account. The rest of this document assumes “hotwilson” is your account name. So replace “hotwilson” with your own account whereever you see it.

  3. Fork the repo under your own account.

  4. Open a Terminal window.
  5. Make and cd into a folder where you will the repository will be downloaded.

    Again, in this example, you would change “hotwilson” to your own account name.

    cd ~ && mkdir gits && cd gits && mkdir hotwilson && cd hotwilson
  6. Confirm you are indeed where you want to create a new repository:

  7. Clone to download:

    git clone https://github.com/hotwilson/git-utilities.git
    cd git-utilities
  8. Optionally, to enable updates from the original repo:

    git remote add upstream https://github.com/wilsonmar/git-utilities.git
    git remote -v

    Global templates

    NOTE Git looks for hooks in a global template folder applicable to all repositories when either a GIT_TEMPLATE_DIR variable is set in .bash_profile or .gitconfig contains a setting defined by command:

    git config --global init.templatedir /path/to/your/templates/

    Alternatively, symbolic links or symlinks can be used to link custom hooks to the ones in the .git/hooks folder.

  9. Copy these lines and paste in Terminal (at the root folder) to copy demo hook files from the hooks folder into the .gits/hooks folder:

    cp hooks/* .git/hooks
    chmod +x .git/hooks/*

    PROTIP: In Mac and Linux machines, scripts must be authorized to be run using the chmod command.

    PROTIP: It is too difficult and dangerous to edit and add/commit files inside the .git folder, which Git itself uses to track changes. So copying files in is the better approach than error messages such as:

    fatal: This operation must be run in a work tree
  10. Copy these lines and paste in the Terminal to make a change, then add and commit it for the Git Hook to take action:

    echo "random text" >>README.md
    git add .
    git commit -m"try git commit hooks"

    Skip hook invocation

    BTW one can suppress hooks from firing with the --no-verify command attribute:

    git commit --no-verify


    Each of the demo hook files can contain variables. For example:

    echo "prepare-commit-msg: $(cat $COMMIT_MSG_FILE)"
    exit 0

CheatSheet Matrix

Click to expand this cheatsheet (from Daniel Convissor) to see the 15 hooks:


The table lists for each hook what command triggers it, when it runs, what parameters are passed into it, and what happens when the hook exits abnormally (with a 1).

git commit hooks

First of all, there is already some editing to git commit. Git does not allow empty commit messages. If a commit message does not include a message, your favorite editor is opened for you to enter one. If you still haven’t typed anything in your editor, Git aborts the commit.

The flow of processing on the client is illustrated by this (from Johan Abildskov & Jan Krag of Praqma)
git hooks diagram 650x251(click for full screen image)

Upon git commit, client hooks are executed in this order:

  1. The pre-commit hook runs after a git commit is executed, but before the commit message editor is displayed. Since an exit from this with anything other than zero aborts the commit, it is the place to check assets involved in the commit itself (rather than the commit message), to run linters and unit tests on the local laptop. This program has access to the commit date/time, author name and email.

    NOTE: pre-commit hooks do not support hooks with side effects (such as modifying files and adding them to the index with git add).

    A non-zero exit aborts the commit, which can happen if a file being committed contains “#DONTPUSH” (or other keyword recognized by the hook program) developers put in their code as a reminder to hold off pushing to the team repository.

    The following pre-commit code keeps debugging code (such as “console.log” debugging statements) from reaching the shared code base:

    git diff --cached --name-only | \
     grep -E $FILES_PATTERN | \
     GREP_COLOR='4;5;37;41' xargs grep --color --with-filename -n $FORBIDDEN && echo 'COMMIT REJECTED Found "$FORBIDDEN" references. Please remove them before commiting' && exit 1
  2. The prepare-commit-msg hook is invoked after receiving a git commit, just prior to firing up the commit message editor. This hook can edit the commit message in a way that cannot be suppressed. For example, ensuring a capital letter.

    QUESTION: This has access to the commit SHA-1 (when operating on an existing commit)?

  3. The commit-msg hook adjusts the commit message after it has been edited in order to ensure conformity to a standard or to reject based on any criteria. It can abort the commit if it exits with a non-zero value.

  4. The post-commit hook is called after the actual commit is made so that it cannot disrupt the commit. It is mainly used for sending notifications (emails, SMS, Slack, Twitter, etc.). This NOTE describes deploying to a Local Web Server with a Post-Commit Hook.

git push hooks

The order of execution is:

  1. pre-receive run just before pushed files are updated. So it can abort the receive process by exiting with a non-zero status. Thus, it is used to enforce commit policies and reject the entire commit if it is deemed unsatisfactory. (code coverage)

  2. update filters each commit ref made to the remote repository independently. It can be used to reject or accept each ref being pushed.

  3. post-receive is triggered after an update has been done on the remote repository. So it cannot abort the update process. But it can trigger notifications on a successful remote repository update. (send email, run load tests, so that a log of notifications is stored on a remote server.

Sample JavaScript Mocha linter

From https://scotch.io/tutorials/using-git-hooks-in-your-development-workflow#client-side-hooks

# Exits with non zero status if tests fail or linting errors exist
num_of_failures=`mocha -R json | grep failures -m 1 | awk '{print $2}' | sed 's/[,]/''/'`
errors=`jscs -r inline ./test/test.js`
num_of_linting_errors=`jscs -r junit ./test/test.js | grep failures -m 1 | awk '{print $4}' | sed 's/failures=/''/' | sed s/">"/''/ | sed s/\"/''/ | sed s/\"/''/`
if [ $num_of_failures != '0' ]; then
  echo "$num_of_failures tests have failed. You cannot commit until all tests pass.
        Commit exiting with a non-zero status."
  exit 1
if [ $num_of_linting_errors !=  '0' ]; then
  echo "Linting errors present. $errors"
  exit 1

Useful Hook examples

pre-rebase hooks

  1. Use a text editor to create in the hooks folder a “pre-rebase” containing this text described in YouTube video Life’s better with Git hooks - Grumpy Gits [17:58] Nov 16, 2015 by Deepank Vora.

    echo "Custom rebasing message."
    exit 1

    (Unlike Windows, Linux defines file handling in the first line inside the file).

    PROTIP: Non-zero finish aborts the commit.

  2. Enable the new script:

    chmod +x pre-rebase

    This would avoid messages such as:

    -bash: ./pre-rebase: No such file or directory
  3. Run the new script to make sure it works:

  4. Open another Terminal shell window to the working directory of the repo to run the git rebase master command.

    You should see the custom message.

    QUESTION: If instead you see:

    Current branch master is up to date.

    View pre-commit.sample

  5. Use a text editor Set to always open .sample files with a text editor.

    #!/bin/sh at the top of the file means that it’s a Bash shell script.

    (Author Junio C Hamano is the primary developer of Git, now works at Google.)



You can connect to the GitLab API using your API key to perform actions inside GitLab when performing, for example, a commit. This you can do from the client side, but of course, you can also use the server-side hooks, such as post-receive to run programs and scripts after they have reached the remote.

GitLab has a GraphQL API (in alpha as of this writing) among its many APIs

closes or fixes

  1. The following script is saved as .git/hooks/commit-msg (with no file extension):

    URL=`git config --get remote.origin.url`
    PROJECT=`basename ${URL} .git | cut -d':' -f2`
    for issue_id in `grep -o -e "\(closes\|fixes\) #[0-9]\+" $1 | cut
    -d'#' -f2`; do
        curl -X PUT -d "closed=1" \
  2. Edit the program with your gitlab URL.

    If this script sees a commit message text containing “closes #” or “fixes #”, the hook closes the issue identified by the number after the # symbol.

  3. Make the file executable to enable this functionality.

    chmod +x *


The cananoical resources on Git Hooks:

More Resources

Atlassian’s tutorial on Git Hooks - Local hooks

Git Website: how to

023 Introduction to Git Hooks by Dan Gitschooldude

gated-commit with git pre-commit [12:41] by fullstack

Git hooks man page

Andrew Burgess for Tuts [7:28] June 28, 2012 describes run of mocha test processed by a Bash script.

Git hooks made easy

contains contributor code in the git team’s repo

Git Hooks Practical uses on Windows

Git - How to validate commit messages? 27 May 2019 by Marcell Lipp


More on DevOps

This is one of a series on DevOps:

  1. DevOps_2.0
  2. ci-cd (Continuous Integration and Continuous Delivery)
  3. User Stories for DevOps

  4. Git and GitHub vs File Archival
  5. Git Commands and Statuses
  6. Git Commit, Tag, Push
  7. Git Utilities
  8. Data Security GitHub
  9. GitHub API
  10. TFS vs. GitHub

  11. Choices for DevOps Technologies
  12. Java DevOps Workflow
  13. AWS DevOps (CodeCommit, CodePipeline, CodeDeploy)
  14. AWS server deployment options

  15. Cloud services comparisons (across vendors)
  16. Cloud regions (across vendors)
  17. AWS Virtual Private Cloud

  18. Azure Cloud Onramp
  19. Azure Cloud
  20. Azure Cloud Powershell
  21. Bash Windows using Microsoft’s WSL (Windows Subystem for Linux)

  22. Digital Ocean
  23. Cloud Foundry

  24. Packer automation to build Vagrant images
  25. Terraform multi-cloud provisioning automation
  26. Hashicorp Vault and Consul to generate and hold secrets

  27. Powershell Ecosystem
  28. Powershell on MacOS
  29. Powershell Desired System Configuration

  30. Jenkins Server Setup
  31. Jenkins Plug-ins
  32. Jenkins Freestyle jobs
  33. Jenkins2 Pipeline jobs using Groovy code in Jenkinsfile

  34. Docker (Glossary, Ecosystem, Certification)
  35. Docker Setup
  36. Dockerize apps
  37. Docker Registry

  38. Maven on MacOSX

  39. Ansible

  40. MySQL Setup

  41. SonarQube static code scan

  42. API Management Microsoft
  43. API Management Amazon

  44. Scenarios for load