← home

Git Spellchecker

Do you sometimes find yourself typing commit messages in a hurry, immediately pushing to GitHub and only noticing too late that you’ve made a bunch of typos? It’s happened to me often enough that I decided my commit messages need a spellchecker.

The good news is, git has your back here. A commit-msg hook makes this really easy. Hooks are simply shell scripts with a name git recognizes and placed in a directory where git is looking for hooks. According to the git docs:

The commit-msg hook takes one parameter, the path to a temporary file that contains the commit message written by the developer. If this script exits non-zero, git aborts the commit process, so you can use it to validate your project state or commit message before allowing a commit to go through.

These hooks can be

There’s not a whole lot of official documentation on git hooks, especially not on how to combine global and local hooks. All I could find is

But after a little bit of personal experimentation, it turns out that as soon as you specify a global hook path and leave the default local hook directory as is (i.e. .git/hooks), the global hook directory shadows the local one. Only the global hooks will called if you installed any or nothing happens if there are none. If, on the other hand, you also specify a local hooks directory (it can even be the default location, i.e. git config --local core.hooksPath .git/hooks) the story is reversed. In that case, local hooks will shadow global ones. It seems git left it to you to make sure the global/local hooks check for the existence of local/global hooks and run those themselves if that’s what you want…

Anyways with all that cleared up and knowing the caveats, I’ll be assuming in this guide that you want to setup that commit-msg hook globally. In that case, first decide where your global git hooks should live. To specify that, run git config --global core.hooksPath path/to/globalHooks. The path could be something like ~/gitHooks or ~/dotfiles/gitHooks. You can also specify multiple directories for your global hooks. Just separate each path by a space.

Next, create a file named commit-msg in your global hook directory:

touch $(git config --global core.hooksPath)/commit-msg

and insert the following.

#!/bin/sh

# Adapted from https://reddit.com/r/bash/comments/49sflp/spellcheck_git_commit_hook.
if ! command -v aspell &>/dev/null; then
  printf "%s\n" "[commit-msg hook] Warning: 'aspell' not installed. Unable to spell check commit message."
else
  commit_msg_file="$1"
  wordList=( $(grep -v "^  " "$commit_msg_file" | aspell list) )
fi

if (( "${#wordList[@]}" > 0 )); then
  printf "%s\n" "[commit-msg hook] Possible spelling errors found in commit message:" "${wordList[@]}"
fi

# Check if running inside a terminal where the user can be prompted to handle
# possible spelling errors. See https://stackoverflow.com/a/911213.
if [ -t 1 ]; then
  # Adapted from https://stackoverflow.com/a/10015707.
  exec < /dev/tty

  while true; do
    read -p "[commit-msg hook] Proceed anyway? (y/n) " yn
    if [ "$yn" = "" ]; then
      yn='y'
    fi
    case $yn in
        [Yy] ) break;;
        [Nn] ) exit 1;;
        * ) echo "Please answer y for yes or n for no.";;
    esac
  done
fi

Finally, make sure you have aspell (or a similar command line spellchecker) installed and in your path. With Homebrew on macOS, it’s as simple brew install aspell. Similarly, on Linux: apt-get install aspell.

That’s it. Next time you type a commit message of questionable spelling, git will provide you with a list of all potential typos and ask if you want to abort to modify the message or proceed anyway.

Note, however, that with the above script, this will only work if you’re committing from terminal. If instead you’re using a UI for version control, there most likely won’t a way for git to interact with the user directly. I asked VS Code if they might provide a way around this in the future but they declined.