My Git Commit Hooks
I quite envy a nice commit message. The Rails project has rules about commit messages that are tops. This is a great example from a great developer I know, which got merged into core.
So now on my own projects I have cribbed, tweaked, and automated a tiny bit of of that sweet, sweet commit message honey.
Naming your branch
- Step 1
- I name my branches with a prefix that is one of: “hotfix”, “bug”, “feature”, or “candy”.
- Step 2
- A
/
is totally legit in a branch name.
- A
- Step 3
- The Story ID. I never commit without first creating a story to which the changes will be connected and later, tested.
- Step 4
- A
-
really helps to separate things.
- A
- Step 5
- A dasherized, underscored, or mishmash of both, description of the work in the branch
Here’s an example: bug/102551510-explosion-in-the-fudge-factory-spec-suite-fix
Automating a commit message suffix*
Given a nice branch name like that, I can extract information from it, and automatically add it to my commit messages for $PROFIT.
I just drop this in my project’s .git/hooks/prepare-commit-msg
:
#!/usr/bin/env ruby
# vim: set syntax=ruby
# branches should be named like:
# <story_type>/<story_id>-explosion-in-the-fudge-factory-spec-suite-fix
# where story type is one of "hotfix", "bug", "feature", "candy"
#
branch = `git branch 2> /dev/null | grep -e ^* | awk '{print $2}'`
regex = /^(?<story_type>(hotfix)|(bug)|(feature)|(candy))\/(?<story_id>\d{8,})-.+\Z/
match_data = branch.match(regex)
# NOTE: `match` will return nil if match fails, otherwise an instance of MatchData.
# If not nil then we are assured matches for both regex capture groups
# match_data_or_nil[:story_type] will be one of "hotfix", "bug", "feature", "candy"
# match_data_or_nil[:story_id] will be a numeric string
if !match_data.nil?
commit_msg = IO.read(ARGV[0])
unless commit_msg.include?(match_data[:story_id])
commit_msg = <<EOS
#{commit_msg.strip}
[#{match_data[:story_type]}][#{match_data[:story_id]}]
EOS
File.open(ARGV[0], 'w') do |file|
file.print commit_msg
end
end
end
* Why a suffix? Because the information being added is most useful for searching through commit messages in a long history. Not as useful for reading in a PR on github, which shows the first so many characters from each commit message only, and if prefixed it would push important information below the ellipses fold.
Testing the commit message suffix
Automation is no good without testing or you might just automate badness, and that’s worse than no automation at all.
So I drop this in my .git/hooks/commit-msg
:
#!/usr/bin/env ruby
# vim: set syntax=ruby
denied = <<EOM
Oh snap, think again...
______ _______ ___ _______ _______ _______ _______ ______ __
| _ | | | | || || || || || | | |
| | || | ___| | || ___|| ||_ _|| ___|| _ || |
| |_||_ | |___ | || |___ | | | | | |___ | | | || |
| __ || ___| ___| || ___|| _| | | | ___|| |_| ||__|
| | | || |___ | || |___ | |_ | | | |___ | | __
|___| |_||_______||_______||_______||_______| |___| |_______||______| |__|
Did you forget a Tracker ID?
Example: git commit -m "I just fixed a bug. [12345678]"
EOM
full_text = File.read(ARGV[0])
if full_text =~ /\[\d{8,}\]/
exit 0
else
puts denied
exit 1
end
* Note: In the regex in both scripts I have a test for >= 8 digits. That’s to match the format of my story IDs which are always at least 8 digits. Your story ID format may vary.
** Note: Much of this is based on work originally done by Brett Fishman (github), Director of Engineering at Trumaker & Co *** Note: Brett used a great ascii art generator
comments powered by Disqus