43

I would like to check the commit message before Git commit.

I use a pre-commit hook to do that, but I couldn't find the way to get the commit message in the .git/pre-commit script. How could I get it?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
fish potato
  • 5,319
  • 6
  • 27
  • 32

7 Answers7

49

In the pre-commit hook, the commit message usually hasn't been created yet 1. You probably want to use one of the prepare-commit-msg or commit-msg hooks instead. There's a nice section in Pro Git on the order in which these hooks are run, and what you typically might do with them.

1. The exception is that the committer might have supplied a commit message with -m, but the message still isn't accessible to the pre-commit hook, whereas it is to prepare-commit-msg or commit-msg

Deanna
  • 23,876
  • 7
  • 71
  • 156
Mark Longair
  • 446,582
  • 72
  • 411
  • 327
  • 1
    Please note that these are client side scripts, for server side script, one may need to use `pre-receive`. – Walty Yeung Mar 06 '14 at 08:55
  • ` the commit message hasn't been created yet` yes it has .. when the user typed `git commit -m "foobar"` –  May 19 '19 at 06:22
  • @OlegzandrDenman - OK, fair enough - I've reworded the answer and added a footnote about that. – Mark Longair May 20 '19 at 09:40
24

I implemented this in the commit-msg hook. See the documentation.

commit-msg

This hook is invoked by git commit, and can be bypassed with the --no-verify option.
It takes a single parameter, the name of the file that holds the proposed commit log message.
Exiting with a non-zero status causes the git commit to abort.

Under my_git_project/.git/hooks, I added the file commit-msg (has to be this name). I added the following Bash contents inside this file which did the validation.

#!/usr/bin/env bash
INPUT_FILE=$1
START_LINE=`head -n1 $INPUT_FILE`
PATTERN="^(MYPROJ)-[[:digit:]]+: "
if ! [[ "$START_LINE" =~ $PATTERN ]]; then
  echo "Bad commit message, see example: MYPROJ-123: commit message"
  exit 1
fi
Brown Bear
  • 19,655
  • 10
  • 58
  • 76
Neo
  • 4,640
  • 5
  • 39
  • 53
  • Its not working. I can't get commit message in commit-msg hook. COMMIT_FILE=$1 COMMIT_MSG=$(cat $1) – iKushal Aug 16 '20 at 08:41
  • Isn't it supposed to be commit-msg instead of commit.msg ? – AFract Aug 26 '21 at 13:40
  • file has to be named commit-msg, not commit.msg. – commbot Oct 11 '21 at 04:46
  • how do i get commit message in variable ? cat $1 is not working... – Pawan Deore Jul 11 '22 at 12:28
  • It works, thank you, but only if I get rid of the quotes in PATTERN, see https://stackoverflow.com/questions/2172352/in-bash-how-can-i-check-if-a-string-begins-with-some-value#comment84627580_2172365. `PATTERN=^\[.*-\d*\].*` – PJ127 Jan 31 '23 at 12:32
3

The hook name should be:

commit-msg , otherwise it won't get invoked:

JammingThebBits
  • 732
  • 11
  • 31
  • yes and apparently the commit message is the first argument passed to `commit-msg`, which is also the same as the contents of the file `.git/COMMIT_EDITMSG` – Alexander Mills May 19 '19 at 06:41
  • Yes, it also says so in the (sample) file *commit-msg.sample*, line 9: *'To enable this hook, rename this file to "commit-msg".'* – Peter Mortensen Nov 24 '21 at 17:56
2

I have created a commit-msg script in Bash having the commit syntax <CURRENT_BRANCH_NAME>-<4_DIGIT_TICKETID>-<COMMIT_DECRIPTION>. This syntax can be used for Azure DevOps ticket ID-based commits by the developer.

#!/bin/sh

# The below input_file is file ".git/COMMIT_EDITMSG" where commits are stored
INPUT_FILE=$1

# It will copy the commit string from ".git/COMMIT_EDITMSG"
START_LINE=`head -n1 $INPUT_FILE`

# Initial index value
sum=0

# Add commit in an array variable separated by -
IFS='- ' read -r -a array_value <<< "$START_LINE"

# Count array index
for i in ${!array_value[@]}
do
    sum=`expr $sum + $i`
done

# Verify commit
if [ ${sum} == 3 ]; then

BRANCH_NAME=`git branch | awk '/\*/ { print $2; }'`
TICKET_DIGIT=`awk -F '[0-9]' '{print NF-1}' <<< "${array_value[1]}"`

   if [ ${array_value[0]} != ${BRANCH_NAME} ];  then
        echo "please enter current branch name"
        exit 1
   fi

   if [ "${TICKET_DIGIT}" != "4" ];  then
        echo "INVALID TICKET ID"
        exit 1
   else
      echo "verify ticket ID ${array_value[1]}"
   fi

else
   echo "pattern must be <CURRENT_BRANCH_NAME>-<4_DIGIT_TICKETID>-<COMMIT_DECRIPTION> without space and don't use - in commit_description"
   exit 1
fi
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
1

Usually it is done by commit-msg.
This hook gives us a file which contains the commit message.
Here is a sample to reject a commit in which we have test word:

declare -r msg=$(< $1);
if grep -i test <<< "$msg" > /dev/null 2>&1; then
    echo you are not allowd to have test commit.
    exit 1;
else
    echo looks good.
    exit 0;
fi

So exit 1 does not allow git to continue and exit 0 of the hook allows git to continue.


Note
Here $1 is the name of that file which is : .git/COMMIT_EDITMSG


enter image description here

Shakiba Moshiri
  • 21,040
  • 2
  • 34
  • 44
0

If you want to access the commit message from hooks other than commit-msg, you can read the file .git/COMMIT_EDITMSG.

Simple Python example: (Git hooks can run Python with the #!/usr/bin/env python shebang)

#!/usr/bin/env python

from pathlib import Path

print(Path(".git/COMMIT_EDITMSG").read_text())

Just make sure to get the relative path right, based on your current working directory

Mandera
  • 2,647
  • 3
  • 21
  • 26
-1

You can do the following in a pre-receive hook (for server side) using Python, and that will display the revision information.

import sys
import subprocess
old, new, branch = sys.stdin.read().split()
proc = subprocess.Popen(["git", "rev-list", "--oneline","--first-parent" , "%s..%s" %(old, new)], stdout=subprocess.PIPE)
commitMessage=str(proc.stdout.readlines()[0])  
Smart Manoj
  • 5,230
  • 4
  • 34
  • 59
ugurarpaci
  • 58
  • 2