DEV Community

Cover image for Use Git Hooks to ease your development workflow
Raj Hawaldar
Raj Hawaldar

Posted on • Updated on

Use Git Hooks to ease your development workflow

Git hooks allow us to run custom scripts whenever certain important events occur in the git repository such as commit, push, merge. Basically git hooks are stored in the .git/hook directory of your local repository.

Initially all hook files contain .sample as extension, which indicate those are disabled. These sample hooks are stored in one of the below locations as per your operating system,

  • Linux: /usr/share/git-core/templates

  • Windows: C:\Program Files\Git\mingw64\share\git-core\templates

  • Mac OS: /usr/local/git/libexec/git-core

Whenever you initialize a local git repository these sample hooks templates which are already stored on your machine get copied into your local repository.

All hooks pre-fix with pre label will execute before the particular event happens and hooks with pre-fix post will execute after the event.

For Example: pre-push hook will execute before the push operation.

As hooks are disabled by default, we need to perform below steps to enable them ,

  • Remove .sample extension from the hook name.
  • Make that hook file executable using command chmod +x

Before we start remember this:

  • As we see here hooks present inside .git/hook/ directory which means these are not source control. You can use symbolic link.
  • You can bypass pre-commit git hook locally by adding --no-verify flag.

Lets start with the practical use of git hooks.

Scenario 1: In one of the projects we used to create a feature-branch from the environment branch(master,dev,release). We should not commit our code directly on the environment branches.

Solution: pre-commit hook is run first, so we will use this hook. copy below code snippet in your pre-commit hook.

#!/bin/sh
# Avoid doing commit in environment branches.


BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
forbidden_branchs="^(master|dev|release)$"


message="You should not commit directly into ${BRANCH_NAME} branch.\nPlease create a new branch from '$BRANCH_NAME' and Try again."


if [[ $BRANCH_NAME =~ $forbidden_branchs ]]
then
    echo -e $message
    exit 1
fi
Enter fullscreen mode Exit fullscreen mode

In windows operating system instead of #!bin/sh use #!C:\Program Files\Git\bin\sh.exe path where git installed its shell script.

Now git provides you the freedom to select scripting language. If you are comfortable with other scripting languages like python, ruby, powershell you just need to add the path of its library on the first line of the hook.

Always remember, If your hooks returns zero means that it is succeed. If it exists with any other number it is failed. In the above code snippet we exit code 1 which means hook failed so commit will not happen.

Scenario 2:

Sometimes we amend commits multiple times and in later stages we need one of the previous state of commits. If we write a script which will make stash before committing code every time then we will get state if each and every commit in our stash.

Solution:

if [[ $(git diff --cached --exit-code) ]];then
  BRANCH_NAME=$(git branch --show-current)
  STASH_NAME="pre-commit-on-$BRANCH_NAME-$(date +"%m-%d-%y::%T")"
  git stash save -q --keep-index $STASH_NAME
  echo "Stash:${STASH_NAME} saved!"       
fi
Enter fullscreen mode Exit fullscreen mode

$(git diff --cached --exit-code) will check whether there is any code present in staging area. As there is no point running this code for empty commit. $(git branch --show-current ) gives us current branch name.
We will use the same pre-commit hook. Add below line your pre-commit hook.

Scenario 3:

Before pushing our branches to remote lets create a hook which will execute build and tests before pushing your code to remote branch.

Solution:

We can use pre-push or pre-commit hook for this scenario.

echo "Running Build"
sh run-test-scripts.sh

if [ $? -ne 0 ]; then
  echo "Build must be succeed before commit!"
  exit 1
fi
Enter fullscreen mode Exit fullscreen mode

Here, $? stores the exit value of last command. Add test cases and build commands in run-test-scripts.sh file. You can use this hook for automatic code quality checks.

How to deploy these git hooks ?

As git hooks are stored in .git/hook directory so developers will not be able to commit these hooks. We will create our own .githooks directory in our code base. We will copy our created hooks into this newly created directory and ensure permissions are correctly set on the file.

chmod a+x ~/.githooks/pre-commit

Done!

There is another way, from git version 2.9.0 you can set core.hookspath which will allow you to set a global directory for hooks on your machine. So all your local repository will point to that directory and use hooks present over there.

To set core.hookspath use below command:

git config --global core.hookspath <hooks-directory-path>
Enter fullscreen mode Exit fullscreen mode

Note: Keep your hooks folder outside of local repository. Otherwise if you delete local repository for some reason. Your other project will not able to access the hooks.

With git hooks imagination is the limit! You can find my original article on LinkedIn(here).

Let me know if the article is useful.
Happy coding!

Top comments (5)

Collapse
 
wulymammoth profile image
David

Man, I wish you had included your LinkedIn post examples here, too! They're great! I liked those more than the one you've illustrated here, because GitHub repos have "protected branches" and diminishes this hook's utility. But nonetheless, if not for your post, I wouldn't have remembered that git hooks were a thing I wanted to explore more at some point

Collapse
 
rajhawaldar profile image
Raj Hawaldar

Ok, i will add them in the same post. Thanks for the feedback.

Collapse
 
canro91 profile image
Cesar Aguirre

Great! I use a hook to include the task number from the branch name into commit messages

Collapse
 
rajhawaldar profile image
Raj Hawaldar

Yes, that is cool! Once, I used git hook to validate branch name according to JIRA ticket while committing code.
FYI, I got suggestion on my LinkedIn article to use $git branch --show-current instead of $(git rev-parse --abbrev-ref HEAD).

Collapse
 
rajhawaldar profile image
Raj Hawaldar

Hey folks, I have included remaining scenarios from my LinkedIn post here. Thanks for all the feedback.