By default git allows you to include anything in a commit message. This freedom is nice, but when I need to look back on the commit history it is very useful to be able to connect commits to specific issues.

This could be done by including the issue number in every commit message. (Even if in your setting it is called ticket number or bug number.). e.g. #42.

Git provides ways to enforce that you every commit has such a string in it, but this enforcement can only be done on the server. Too late if I made several commit on the client without the required part.

You can, however, ask git on your own computer to help you enforce this rule.

In your workspace on your computer the whole git database is in the .git subdirectory of your workspace. Inside there is a directory called .git/hooks/ with a bunch of file with th extension "sample". These are or example scripts to have actions at various stages of your work life-cycle.

Here is what I did: I create a file called .git/hooks/commit-msg with the following content:

#!/bin/sh

test "" = "$(grep '^#\d* - ' "$1")" && {
   echo >&2 "******  Start the commit message with a # character followed by the task-id!"
   exit 1
}

exit 0

Made it executable with

chmod +x .git/hooks/commit-msg

From this point on, in this repository I'll have to make sure the commit message starts with # followed by a number, followed by a - . The rest is optional.

How the commit-msg hook works:

Before a commit is recorded the commit message is saved in a temporary file and this script is executed passing the name of the file as the first parameter. If the exit code of this script is 0 (meaning success) then the commit can go on. If the exit code is any other number (meaning failure) then the commit is aborted. The above code checks if the content of the file (of which the name is located in variable $1 contains the correct format.

Enforce task-by-name

If you don't have a bug-tracking system, or you don't always want to require a bug number, I'd still suggest to require some kind of identification to the commits that will make it easier later to connect commits that are related to the same task.

This slight modification requires some kind of a word consisting of letters, digits, and the underscore immediately after the # tag.

#!/bin/sh

test "" = "$(grep '^#\w* - ' "$1")" && {
   echo >&2 "******  Start the commit message with a # character followed by the task!"
   exit 1
}

exit 0

So a commit message can look like:

#refactoring - merging 4 cases of copy-paste into a function call

Select from specific names

The above still allows for typos in the identifier that is mainly a problem as it will make it much harder to list the related commits.

The next one, written in Perl, that should still be called .git/hooks/commit-msg has a list of acceptable task names. It checks if the text starts with one of those.

examples/commit-msg.pl

#!/usr/bin/perl
use strict;
use warnings;
use autodie;

my @tasks  = qw(refactoring design bug);

my $filename = shift;
open my $fh, '<', $filename;
my $line = <$fh>;
my ($task) = $line =~ /^#(\w+) - /;
my $error;

if ($task) {
    if (grep { $task eq $_ } @tasks) {
        # OK
    } else {
        $error = "Invalid task '$task'.";
    }
} else {
    $error = 'Invalid message format.'; 
}

if ($error) {
    die "****** $error\nThe message needs to start with '#TASK - ' where TASK is one of the following:\n@tasks\n";
}

Using this will require you to update the .git/hooks/commit-msg file with the current task names, but will enforce the specific names.