Git Hook For Commit Message Style

Created: Sept. 25, 2021 |  Modified: Sept. 25, 2021 |  Categories:  Scripting   Source Control   Productivity  

Description:

This hook can be used to check commit message styling. Failure of this script will prevent the commit from being made and display an appropriate error message.


Use the hook globally:

  1. Create a directory for git hooks and tell git where to find it:
    git config --global init.templatedir '~/.git-templates'
    mkdir -p ~/.git-templates/hooks

  2. Create a file called commit-msg in ~/.git-templates/hooks/ and add the script shown below to the file.

  3. Make sure the hook is executable:
    chmod a+x ~/.git-templates/hooks/commit-msg

  4. Re-initialize git in each existing repo you'd like to use this hook in (if a script with the same name already exists, it won't be overwritten and thus won't work):
    git init


Use the hook in a single repository:

  1. Add the commit-msg file (contents shown below) to the repository's .git/hooks/ directory.

  2. Make sure the hook is executable:
    chmod a+x ~/<path_to_repo>/.git/hooks/commit-msg

  3. Re-initialize git:
    git init


commit-msg script:

#!/usr/bin/env bash

# hook to ensure commit message meets style requirements:
# + commit title is <= 50 characters
# + lines in commit body are <= 72 characters
# + subject line is capitalized
# + subject line does not end with punctuation
# + subject line is separated from body with a blank line

function abort_commit {
    echo -e "\nCOMMIT FAILED"
    echo "To fix commit message and retry, run:  git commit -e --file=$(git rev-parse --git-dir)/COMMIT_EDITMSG"
    exit 1
}

title_line=false
title_newline=false

while read line; do
    # skip comments
    if [ "${line:0:1}" == "#" ]; then
        continue
    fi
    if [ "$title_line" == false ]; then
        if [ ${#line} -gt 50 ]; then
            echo "Commit title is limited to 50 characters."
            echo "The following commit title has ${#line} characters:"
            echo "${line}"
            abort_commit
        fi
        if [[ ${line: -1} == *[[:punct:]]* ]]; then
            echo "Commit title cannot end with punctuation."
            echo "Found:"
            echo "${line}"
            abort_commit
        fi
        if echo "$line" | grep -q -E "\b[a-z]"; then
            echo "All words in commit title must be capitalized."
            echo "Found:"
            echo "${line}"
            abort_commit
        fi
        title_line=true
    elif [ "$title_newline" == false ]; then
        if [ "$line" != "" ]; then
            echo "Commit title must be followed by a blank line."
            echo "Found:"
            echo "${line}"
            abort_commit
        fi
        title_newline=true
    elif [ ${#line} -gt 72 ]; then
        echo "Commit body lines are limited to 72 characters."
        echo "The following line has ${#line} characters:"
        echo "${line}"
        abort_commit
    fi
done < "${1}"

exit 0