Adding Git Branch name to commit message
Most teams I've worked in name branches after the ticket: feature/ittfc-1234, bugfix/proj-987, that sort of thing. Most of those teams also expect the ticket reference to appear in the commit message.
So everyone types it out, every time, and half of them forget.
It's a job for a computer. Git's prepare-commit-msg hook can prepend the branch name to every commit message, and once you've set it up as a global template you can forget about it.
The result looks like this:
[ittfc-1234] Fix rounding on basket totals
Squash that branch into main later and the ticket reference survives in the history, which is the point of the exercise.
Setting it up
1. Create a folder for your git templates
# Windows
mkdir %userprofile%.git-templates
# macOS / Linux
mkdir -p ~/.git-templates/hooks
2. Point git at it globally
# Windows
git config --global init.templatedir %userprofile%.git-templates
# macOS / Linux
git config --global init.templateDir ~/.git-templates
3. Create the hook
The file needs to be called prepare-commit-msg and live in a hooks subfolder of your template directory.
#!/bin/bash
# Branches that shouldn't have the name prepended
if [ -z "$BRANCHES_TO_SKIP" ]; then
BRANCHES_TO_SKIP=(main master develop test)
fi
BRANCH_NAME=$(git symbolic-ref --short HEAD)
BRANCH_NAME="${BRANCH_NAME##*/}"
BRANCH_EXCLUDED=$(printf "%sn" "${BRANCHES_TO_SKIP[@]}" | grep -c "^$BRANCH_NAME$")
BRANCH_IN_COMMIT=$(grep -c "[$BRANCH_NAME]" $1)
if [ -n "$BRANCH_NAME" ] && ! [[ $BRANCH_EXCLUDED -eq 1 ]] && ! [[ $BRANCH_IN_COMMIT -ge 1 ]]; then
sed -i.bak -e "1s/^/[$BRANCH_NAME] /" $1
fi
4. Make it executable
chmod +x prepare-commit-msg
5. Re-run git init in an existing repo to pick it up
cd my-existing-repo
git init
Proving it works
mkdir /tmp/git-repo-test && cd /tmp/git-repo-test
git init
git commit -m "commit on master" --allow-empty
git checkout -b feature/ittfc-1234
git commit -m "useless commit message" --allow-empty
git log -n 2
commit 3874c47eeb788531e3302cbae51e5d7e8500 (HEAD -> feature/ittfc-1234)
Author: Alex Brown <[email protected]>
Date: Tue May 21 11:21:24 2019 +0100
[ittfc-1234] useless commit message
commit 4c6f03f05a7e377d647b0ca6b9beeaa91c2630a6 (master)
Author: Alex Brown <[email protected]>
Date: Tue May 21 11:21:15 2019 +0100
commit on master
How it works
A few lines in that script do more than they look like they do.
BRANCH_NAME="${BRANCH_NAME##*/}" strips everything up to and including the last forward slash. feature/ittfc-1234 becomes ittfc-1234. Note that feature/JIRA-99/some-description becomes some-description, which is probably not what you want, so adjust if your team nests branch names.
BRANCH_IN_COMMIT guards against the tag being added twice. It matters more than you'd expect, because prepare-commit-msg also runs on amends and on the reword step of an interactive rebase. Without it, a single --amend leaves you with [ittfc-1234] [ittfc-1234] Fix thing.
BRANCHES_TO_SKIP only gets set when it's empty, so you can override it per repo by exporting the variable instead of editing the hook.
The gotcha that catches everyone
Git applies init.templatedir at git init and git clone time. It copies the template contents into .git/, once.
Two consequences:
Existing repos won't pick it up until you re-run git init in them. That's safe. It doesn't touch your history or your working tree, it re-initialises the .git plumbing and copies the template in. New clones get it from the start.
Updating the hook doesn't update your existing repos. They have their own copy. Change the script and you'll need to re-run git init everywhere, or copy the file across by hand.
If that second point bothers you, core.hooksPath points every repo at one shared directory rather than copying:
git config --global core.hooksPath ~/.git-hooks
One source of truth, at the cost of overriding any per-repo hooks. If your team uses Husky in specific repos, a global core.hooksPath will stomp on it, so pick your poison.
Worth saying
None of this substitutes for a decent commit message. [ittfc-1234] useless commit message is still a useless commit message. It does mean that six months later, running git log --oneline over a file and wondering why on earth it's written like that, you have a ticket number to go and read.