Eyes, JAPAN Blog > Customizing git with hooks

Customizing git with hooks

denvazh

この記事は1年以上前に書かれたもので、内容が古い可能性がありますのでご注意ください。

What are we talking about?

It is always nice if certain software ( and hardware as well ) provide good enough functionality by default. However, as soon as any decent human can potentially come up with enormous amount of ideas, for certain software/hardware it might be a tough case to deal with. Hopefully, git is flexible enough to provide great default functionality as well as possibility to customize certain actions with custom hooks.

Getting started

Reason why I started looking into hooks was very simple. Once in a while I have some ideas, and I want to start working on them right away. So I just create repository and start writing some test code to actually see the potential of the idea. Once in a while I might do it on a different remote server (usually virtual one), which absolutely lacks any decent development environment settings(such as colors, editors, username, e-mail and etc.). That’s where I started to think, if it would be nice to have some nice script to check those settings before making any commit.
Note: you still have to make basic setup of the project repository at least, but I will omit this important issue this time.

Writing “hooks”

For a simple test script I’ve chosen very easy function to implement: to check whether user had set username and e-mail in his ~/.gitconfig config file.
But before we start, you should know how those hooks works. In few words: there are number of pre-definied actions which is possible to customize. In .git folder of any project you can find hooks folder bundled with default sample hooks scripts (in fact, we will be using this as well). All action hooks have generic template: %timing%-%action%, where %action%
is a command you usually use with git, for example commit, and %timing% – moment when hook will be fired up ( this can be either pre, post or any other). We will be using pre-commit hook, because we would like git to run some script right before committing code to the project.
So let’s create small nice script for some project you already have (or you just can create new one with git init).

First, we will create file in .git/hooks and name it pre-commit.
Secondly, we will be using bash 🙂 so we add this

#!/usr/bin/env bash

to the first line of the file.
Yay! Now, we finally can start writing the script.

Let’s setup some basic variables where we store error codes for tests we run, and then add also option for default email domain:

COMPANY_DOMAIN="some.domain"
USER_ERR=0
EMAIL_ERR=0

To read defined user information it is possible to use git config -l command to list all possible settings in the system and then parse output for the lines we want:

USER="$(git config -l | grep "user.name" | awk -F"=" '{print $2}')"
EMAIL="$(git config -l | grep "user.email" | awk -F"=" '{print $2}')"

When we got information we wanted, we can run tests and see whether user have everything in order:

if [ -z "${USER}" ]; then
         USER_ERR=1
fi
if [ -z "${EMAIL}" ]; then
         EMAIL_ERR=1
else
         if [ ! -z "${COMPANY_DOMAIN}" ]; then
              MAIL=$(echo "${EMAIL}" | awk -F"@" '{print $2}')
              if [ "${MAIL}" != "${COMPANY_DOMAIN}" ]; then
                   EMAIL_ERR=2
              fi
         fi
fi

After this part of the code is finished all error flags should be set.
Note: if COMPANY_DOMAIN variable is set (and not zero length) then it will try to make domain check, and in case its different from user settings, it will set EMAIL_ERR flag to 2.

Now we can have some output to inform user about what happened if there was any error, or do nothing if everything was in order.

if [[ $USER_ERR -ne 0 || $EMAIL_ERR -ne 0 ]]; then
        echo "Error: aborting commit. See why below."
        if [ $USER_ERR -eq 1 ]; then
                echo -e "\nReason: username was not set"
                helpUser
        fi

        if [ $EMAIL_ERR -eq 1 ]; then
                echo -e "\nReason: email was not set"
                helpEmail
        fi

        if [[ ! -z "${COMPANY_DOMAIN}" && $EMAIL_ERR -eq 2 ]]; then
                echo -e "\nReason: email domain do not match expected one."
                echo "Entered email domain: $(echo "${EMAIL}" | awk -F"@" '{print $2}')"
                echo "Expected email domain: ${COMPANY_DOMAIN}"
        fi
        exit 1        
else
        exit 0
fi

Note: exit codes are important! when running hooks git is tracking exit codes of the script to decide whether to go on, or to about running action.

Code above also has these commands: helpUser, helpEmail. Those are custom functions you can define in bash:

function helpUser(){
        echo -e "To setup username (it is recommended to set it up globally) run the following command:"
        echo 'git config --global user.name "%Firstname Lastname%"'
        echo -e "where %Firstname Lastname% is your first name and last name"
}

function helpEmail(){
        echo -e "To setup e-mail (it is recommended to set it up globally) run the following command:"
        echo 'git config --global user.email "%[email protected]%"'
        echo -e "where %[email protected]% is your e-mail address"
}

That’s it! We made our first git-hook! But…let’s see how it works:)

Using git hook

Let’s try case, when script exits with error code 0.
For this I have both, e-mail and username already defined.

$: git config -l | grep user
user.name=Denis Vazhenin
[email protected]

In this case, if I run git commit everything should work fine (unless something terrible happens, like arrival of ufo or something…
but this is out of scope of this blog post).

Next, we delete (or move elsewhere) file ~/.gitconfig, and enjoy some nice error messages we created few moments ago:

$: git config -l | grep user

Let’s try. If we try to commit, we will see the following:

$ git commit
Error: aborting commit. See why below.

Reason: username was not set
To setup username (it is recommended to set it up globally) run the following command:
git config --global user.name "%Firstname Lastname%"
where %Firstname Lastname% is your first name and last name

Reason: email was not set
To setup e-mail (it is recommended to set it up globally) run the following command:
git config --global user.email "%[email protected]%"
where %[email protected]% is your e-mail address

For the last case we need to setup wrong domain for user email:

$: git config --global user.email "[email protected]"

Now error message changes to:

$ git commit
Error: aborting commit. See reason below.

Reason: username was not set
To setup username (it is recommended to set it up globally) run the following command:
git config --global user.name "%Firstname Lastname%"
where %Firstname Lastname% is your first name and last name

Reason: email domain do not match expected one.
Entered email domain: denis.denis
Expected email domain: some.domain

Conclusion

We have successfully created hook for git-commit action and made our first steps in the world of git customization.

Comments are closed.