Skip to content
bobby_dreamer

Local or Client-side git hooks

shell, unix, git1 min read

Git hooks allow you to run custom scripts whenever certain important events occur in the Git life-cycle, such as committing, merging and pushing. -- tygertec, Youtuber

Hooks are found in .git/hooks and to run them

  1. You have to remove .sample extension
  2. Make them executable chmod +x hook_name
  • Hooks(.git/hooks) are not pushed to source control(git / bitbucket)
  • Hooks can be bypassed by using --no-verify flag
  • Scripting languages can be anything available in the system like #!/bin/sh or #!/bin/bash or python or perl anything.
  • Exit return codes have to be 0(success) / 1(failure)

There are many hooks and here we are going to see two of them

  • pre-commit - This hook is invoked git commit. It takes no parameters, and is invoked before obtaining the proposed commit log message and making a commit. Exiting with a non-zero status from this script causes the git commit command to abort before creating a commit.

  • prepare-commit-msg - This hook is invoked by git-commit right after preparing the default log message, and before the editor is started. It takes one to three parameters. The first is the name of the file that contains the commit log message. The second is the source of the commit message, and can be: message or template or squash or commit. If the exit status is non-zero, git commit will abort. The purpose of the hook is to edit the message file in place, and it is not suppressed by the --no-verify option. A non-zero exit means a failure of the hook and aborts the commit. It should not be used as replacement for pre-commit hook.

pre-commit

This hook does 3 things

  • Checks if the filenames are ASCII Complaint
  • File names should start either with I or B
  • Files should be uppercase
pre-commit
1#!/bin/sh
2#
3# An example hook script to verify what is about to be committed.
4# The hook should exit with non-zero status after issuing an
5# appropriate message if it wants to stop the commit.
6#
7# To enable this hook, rename this file to "pre-commit".
8
9##########################################
10### Filename should be ASCII Complaint ###
11##########################################
12
13if git rev-parse --verify HEAD >/dev/null 2>&1
14then
15 against=HEAD
16else
17 # Initial commit: diff against an empty tree object
18 against=$(git hash-object -t tree /dev/null)
19fi
20
21# If you want to allow non-ASCII filenames set this variable to true.
22allownonascii=$(git config --type=bool hooks.allownonascii)
23
24# Redirect output to stderr.
25exec 1>&2
26
27#######################################################################
28# Cross platform projects tend to avoid non-ASCII filenames; prevent
29# them from being added to the repository. We exploit the fact that the
30# printable range starts at the space character and ends with tilde.
31#######################################################################
32if [ "$allownonascii" != "true" ] &&
33 # Note that the use of brackets around a tr range is ok here, (it's
34 # even required, for portability to Solaris 10's /usr/bin/tr), since
35 # the square bracket bytes happen to fall in the designated range.
36 test $(git diff --cached --name-only --diff-filter=A -z $against |
37 LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
38then
39 cat <<\EOF
40Error: Attempt to add a non-ASCII file name.
41
42This can cause problems if you want to work with people on other platforms.
43
44To be portable it is advisable to rename the file.
45
46If you know what you are doing you can disable this check using:
47
48 git config hooks.allownonascii true
49EOF
50 exit 1
51fi
52
53# If there are whitespace errors, print the offending file names and fail.
54# exec git diff-index --check --cached $against --
55
56#################################################################################
57### File names should start either with I for Implementation or B for backout ###
58#################################################################################
59flag=0
60string=$(git diff --cached --name-only --diff-filter=A -z HEAD | sed 's/\x0/ /g')
61
62## String to array
63IFS=' ' read -r -a array <<< "$string"
64
65## Loop through the above array
66for i in "${array[@]}"
67do
68 if !([[ "$i" == I* ]] || [[ "$i" == B* ]]); then
69 echo "pre-commit check : FAIL : First character should be I or B - $i"
70 flag=1
71 fi
72done
73
74if [ "$flag" == 1 ]; then
75 exit 1
76else
77 echo "pre-commit check : PASS : First character naming : OK"
78fi
79
80#################################
81### Files should be uppercase ###
82#################################
83string=$(git diff --cached --name-only --diff-filter=A -z HEAD | sed 's/\x0/ /g')
84
85# String to array
86IFS=' ' read -r -a array <<< "$string"
87
88flag=0
89## now loop through the above array
90for i in "${array[@]}"
91do
92 val=$(echo $i | sed 's/\.txt//g;s/\.TXT//g;s/\.sql//g;s/\.SQL//g;s/[0-9]//g' | LC_ALL=C tr -d '[A-Z]\0')
93 # String length
94 if [ "${#val}" != 0 ]; then
95 echo "pre-commit check : FAIL : Filename should be uppercase : $i"
96 flag=1
97 fi
98done
99
100if [ "$flag" == 1 ]; then
101 exit 1
102else
103 echo "pre-commit check : PASS : Filename uppercase check : OK"
104fi

prepare-commit-msg

This hook checks whether the commit message starts with ABCD=

prepare-commit-msg
1#!/bin/sh
2#
3# This hook check if the commit message has ABCD= as the starting
4# text. If the hook fails non-zero status is returned and
5# commit operation is aborted.
6#
7# To enable this hook, rename this file to "prepare-commit-msg".
8
9COMMIT_MSG_FILE=$1
10COMMIT_SOURCE=$2
11SHA1=$3
12
13#echo "COMMIT_MSG_FILE = $COMMIT_MSG_FILE"
14#echo "COMMIT_SOURCE = $COMMIT_SOURCE"
15#echo "SHA1 = $SHA1"
16
17if [[ -z "$COMMIT_SOURCE" ]]; then
18 echo "prepare-commit-msg check : FAIL : No commit message"
19 exit 1
20else
21 e_msg=$(cat "$COMMIT_MSG_FILE")
22# echo "${e_msg:0:5}"
23 if [ ${e_msg:0:5} == "ABCD=" ]; then
24 echo "prepare-commit-msg check : PASS : Found ABCD= in commit message"
25 exit 0
26 else
27 echo "prepare-commit-msg check : FAIL : No ABCD= in CAPS found in the beginning of commit message"
28 exit 1
29 fi
30fi

This hook just sort of prepends commit message with month and year

prepare-commit-msg
1#!/bin/sh
2#
3# This prepare-commit hook will prepend month and year to the
4# commit message.
5
6COMMIT_MSG_FILE=$1
7COMMIT_SOURCE=$2
8SHA1=$3
9
10#echo "COMMIT_MSG_FILE = $COMMIT_MSG_FILE"
11#echo "COMMIT_SOURCE = $COMMIT_SOURCE"
12#echo "SHA1 = $SHA1"
13
14if [[ -z "$COMMIT_SOURCE" ]]; then
15 echo "prepare-commit-msg check : FAIL : No commit message"
16 exit 1
17else
18 e_msg=$(cat "$COMMIT_MSG_FILE")
19 read YYYY MMM <<<$(date +'%Y %B')
20# echo $MMM $YYYY :
21 echo "$MMM $YYYY : $e_msg" > "$COMMIT_MSG_FILE"
22 exit 0
23fi

Initially wanted to try pre-receive hook. This hook executes once for the receive operation and thought i can club some of above operations/checks at the server side. But it seems, pre-receive hook is available at only Git Enterprise. Anyhow it was interesting to learn.

Learning, when learning comes