# Git Hooks

Git hooks are a powerful development feature. While it does have its issue (e.g. enforcement issues), I still think it is a handy tool in any case. This page introduces different methods for setting up and some commonly-used scripts.

# Quickstart

There are a number of ways to get started with git hooks.

Use this method to set up git hooks for all your users

Add your scripts to the .githooks directory

mkdir .githooks
nano pre-commit

Make your script executable

chmod +x pre-commit

Next, add this line to your Makefile so that users can automatically set core.hooksPath in a single command

init:
    git config core.hooksPath .githooks

To activate the .githooks directory, run:

make init

Recommended: Commit files to repository so that other users can use the same git hooks.

git add .githooks
git add Makefile
git commit
git push

# Method 2: Set up single repo for single user

Use this method if you do not want to share the git hooks with other users

Firstly, add your scripts to the .git/hooks directory

cd .git/hooks
nano pre-commit

Make your script executable

chmod +x pre-commit

# Method 3: Set up all repos for single user via core.hooksPath

Use this method only if your git hooks are the same for all repositories.

Create a global template directory ~/.git_template/hooks

mkdir ~/.git_template
mkdir ~/.git_template/hooks

Register the hooksPath directory with git config

git config --global core.hooksPath '~/.git_template/hooks'

Add your scripts

cd ~/.git_template/hooks
nano pre-commit

Next, make your script executable

chmod +x pre-commit

# Method 4: Set up all repos for single user via init.templatedir

Use this method only if your git hooks are the same for all repositories. Compared to Method 3, this method only provides a template so changes to git hooks in the template directory will only be effective for new git repositories.

Create a global template directory ~/.git_template

mkdir ~/.git_template
mkdir ~/.git_template/hooks

Register the template directory with git config

git config --global init.templatedir '~/.git_template'

Add your scripts

cd ~/.git_template/hooks
nano pre-commit

Next, make your script executable

chmod +x pre-commit

Now, whenever you run the git init or git clone command, the scripts that you've set up should be added to the local .git/hooks directory. You can also check by running:

ls -l .git/hooks

# Recipes

# Runs linter and tests before committing changes

#!/bin/bash
# Runs linter and tests before committing changes

# Ensures that script exits if error is detected
set -e

# Use virtual environment
source venv/bin/activate

# Checks and tests
make lint
make test

# Prepends issue number before committing

#!/bin/bash
# Prepends the ticket/issue identifier in the commit message
# based on the prefix of the branch. Note that this means all
# branches MUST include the ticket/issue identifier as a prefix.
# 
# Example:
#   abc-123                       -> ABC-123
#   abc-123-this-is-a-new-branch  -> ABC-123
#   feature/abc-123-new-branch    -> ABC-123
# 
# This script was adapted from:
# https://medium.com/@nicklee1/prepending-your-git-commit-messages-with-user-story-ids-3bfea00eab5a

# Include any branches for which you wish to disable this script
if [ -z "$BRANCHES_TO_SKIP" ]; then
  BRANCHES_TO_SKIP=(master develop staging test)
fi

# Get the current branch name and check if it is excluded
BRANCH_NAME=$(git symbolic-ref --short HEAD)
BRANCH_EXCLUDED=$(printf "%s\n" "${BRANCHES_TO_SKIP[@]}" | grep -c "^$BRANCH_NAME$")

# Extract ticket name and check if ticket in commit msg
TICKET_NAME=$(echo $BRANCH_NAME | sed -E -e 's:^([A-Za-z0-9_\/]*\/)*([A-Za-z0-9_]*-[0-9]*)(.*):\2:' -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/')
TICKET_IN_COMMIT=$(grep -c "$TICKET_NAME" $1)

# If it isn't excluded or already in commit message, prepend the ticket name to the given message
if [ -n "$BRANCH_NAME" ] &&  ! [[ $BRANCH_EXCLUDED -eq 1 ]] && ! [[ "$TICKET_IN_COMMIT" -eq 1 ]]; then
  sed -i.bak -e "1s:^:[$TICKET_NAME] :" $1
fi