You can have nicer commit messages

Use Regex in a git hook.

Vojta Struhár
4 min readJul 28, 2022

Keeping a clean git history requires skill and discipline.

One accidental push to the production branch, subsequent revert, rebase and push can send you spiraling down into a git spaghetti. And your repo is never quite the same afterwards. It happens to all of us.

Git Spaghetti

Writing commit messages is boring.

Especially when you are working on a solo project. There is no annoying colleague to hold you accountable to your mess.

But of course you can help yourself to stay on track. With a tiny bash script and a git hook of course! :)

Battle plan

  1. Pick a message format you want to stick to
  2. Create regex for it
  3. Register a git hook to check the message format for you!

Commit message format

There are many ways on how to structure your commit messages. Some are simple, some complex, some are easy to adopt and some take time to get used to. I will list few ideas for you:

Imperative + Punctuation

Rules:

  • Every message has to start with an imperative keyword:Add , Fix , Remove , Refactor , Update , Document
  • The sentence must make sense and has to end with a dot .

Example:

Implement automatic login for new verified signups

Tags + Summary

This is my strategy of choice. Works really well for bigger projects with multiple different unrelated areas!

Rules:

  • Message starts with comma-separated list of tags enclosed in brackets []
  • After tags comes a short summary sentence of what the commit is about.

Example:

[Login] Added popup with login error.

[Player, UI] Fixed inventory HUD indicator.

Detailed description

Commit message has a subject and a body. This strategy focuses on giving the body some order.

Rules:

  • Each changed subject has its own line.
  • Added things start with +
  • Changed things start with *
  • Removed things start with -

Now that you've picked your favorite format

it's time to create a regular expression that defines it. We are going to be using egrep bash utility, so just make sure that it is compatible. If you don't go crazy mode, it will be fine :)

I will pick the Tags + Summary strategy.

The regex for that format is following

^\[(\w+, )*\w+\] \w+
  • The ^ character marks the beginning of a string (line). So the message has to start with this.
  • \w are word (=alphanumeric + _ ) characters

First tags have to be a word ( \w+ ) followed by a comma and a space. This can occur many ( * ) times. And the last tag has to be a just a word, followed by a closing bracket. The summary should begin with a word, but I'm not checking further (for punctuation, word count, etc.).

Register a git hook

Every .git/ directory inside your git repo contains hooks/ folder with some sample inactive hooks. You know when a hook is inactive when its name ends in .sample .

Let's bring to life the commit-msg.sample hook. You can rename the file to commit-msg or create a new file with that name.

Hook file should be executable ( chmod ugo+x commit-msg ) and contain shebang with your shell. Mine is #!/bin/zsh since I'm on a Mac.

The wannabe commit message

Message text for the future commit is stored somewhere deep in /var in a temporary file. The path to that file is passed to our script as 1st argument!

That means we can just cat the file to get its contents.

Checking our regex then looks something like this:

if ! cat $1 | egrep "^\[(\w+, )*\w+\] \w+" ; then
...
fi

That is — if the message is different from our regex, we want to do something about it.

Reject the commit

If this hook exits with a non-zero code, the commit will not happen and an error will be displayed to you. Inside the if clause, echo your desired error message to stderr and then just exit 1 .

Final notes

It may be a good idea to whitelist a few specific commit messages. Namely messages that are created by git. You cannot control them, but the hook would still prevent them from happening.

Allow commits with messages starting by Merge branch , Squashed , etc. You can do so by putting another check in front of your format one.

cat $1 | egrep "^(Merge branch|Squashed)" && exit 0

Also Git hooks are local to your machine, so you don't have to worry about it interfering with your CI or messing with your team members. You can read about them here!

Congratulations!

You just forced yourself to keep a good habit of taking care of your commit messages! Good job!

(I wrote the words commit message way too many times by now…)

If you liked this article (and maybe learned something new) a clap 👏 would be highly appreciated. Thanks!

--

--

Vojta Struhár

Always on the lookout for a smarter way to code. Mac enjoyer 🍎, Web developer 🕸️, Game developer 👾