How to automate your work using Git’s add-on scripts?
Tags: git, automategit, gitaddon, gitscripts
Published: 2022-12-05 05:30:00

Do you know you can automate a part of the work that you do every time during your development?

This includes running lint, test cases, etc. before committing and pushing the code. Git provides an excellent option to achieve this.

That’s called **Git Hooks**

Git Hooks, being an advanced topic do you know why I call it as **Dark part of Git?**

Because, being a powerful feature it was not widely adopted by many software development teams (including me, but I follow an alternative. Read further to know what is it & why I’m using that).

Let’s try to explore the below items in this blog

 1. What is Git Hooks?

 2. Why most development teams are not adopting Hooks in their development process?

### What is Git Hooks?

Git Hooks is a feature of git that allows to run custom scripts on the occurrence of certain events at the git lifecycle. Some of the events include before-commit, after-commit, before-push, etc.

### Why do we need Git Hooks?

Hooks is the tool that makes it much easier for developers to adhere to certain guidelines. These guidelines include running lint before committing, running test cases before pushing the code, etc. Git Hooks can be configured to make the action fail (by returning a non-zero exit code) if something was broken or not good (test case/lint failure).

### Where can I find the Hooks configured on my repo?

Git Hooks comes pre-installed with the git repository. Whenever you initialize a git repo ( `git init` ) or clone a repo from any remote server ( `git clone` ). You can find all the hooks inside .`git/hooks` directory from the root repo.

Every hook will be configured as a file. By default, git creates 12 sample hooks file listed below. The files that end with `.sample` are example hook files generated by git.

Okay. That’s fine. I hope your next question will be,
> # What are all the available hooks which I can use?

### Client Side Hooks

 1. prepare-commit-msg

 2. pre-commit

 3. applypatch-msg

 4. commit-msg

 5. fsmonitor-watchman

 6. pre-applypatch

 7. pre-merge-commit

 8. pre-push

 9. pre-rebase

### Server Side Hooks

 1. pre-receive

 2. update

 3. post-update

This is not the entire list of all available Git hooks. These are the sample hook files that will be available by default on all git repositories. Head over to [Git's official documentation](https://git-scm.com/docs/githooks) to have a look at all supported hooks.

### How to configure a hook?

Let’s take a look at a couple of examples of using it

**prepare-commit-msg**

The `prepare-commit-msg` hook is run before the commit message editor is fired up but after the default message is created. It lets you edit the default message before the commit author sees it. This hook is generally used to template the commit message.

The parameters of this hook are,

* Path to the file that holds the commit message

* Type of commit

* Commit SHA-1, if this is an amended commit.

For example, I used to prepend my name in all the commits I made. We follow this across our organization.

![Example list of commits having my name ([Arunachalam]) at the beginning](https://cdn-images-1.medium.com/max/2000/1*C29ZuoZLQE4ADZGJfXgR2w.png)

As mentioned earlier, we need to remove .sample from the file name. Find the code below to template your name while committing
{% gist https://gist.github.com/5minslearn/9fe774ee12bf7afabcc8e7f668689de9.js %}
We need to make the script executable. So, running chmod +x on the file

![Made prepare-commit-msg hook file executable](https://cdn-images-1.medium.com/max/2000/1*QIQ7XbkAGWEKKJ6q3yr8xA.png)

Let’s try to commit our change,

![Change a file to test prepare-commit-msg hook](https://cdn-images-1.medium.com/max/2000/1*pzoX3dgXGvjp2CXyMMwL8A.png)

The Nano text editor (system default) opens up immediately after I ran git commit . From the below screenshot, you can see my name is already written to the commit message file (COMMIT_EDITMSG) and I just need to add the commit message.

![My name was prefilled as a template when trying to commit](https://cdn-images-1.medium.com/max/2000/1*ErSrd8dweTbcOZfNgog55A.png)

![After saving and exiting the commit message (COMMIT_EDITMSG) file](https://cdn-images-1.medium.com/max/2000/1*_QWqiwl2OkJmCDTd0QCtfg.png)

That’s it. We have successfully setup prepare-commit-msg hook.

Let’s have a quick look at another simple example with a failure scenario.

The action will be failed if the hook(except a few) returns non-zero values.

Let’s make `pre-push` hook fail by returning a non-zero value
{% gist https://gist.github.com/5minslearn/04786e2c1725ac6553abe70749acda8d.js %}
I’ve 35 commits in my repo (haven’t pushed from the beginning of my blogging series). So, I’m not making any new commits and going to push the existing commits.

![Failed to push due to non-zero exit code on pre-push hook](https://cdn-images-1.medium.com/max/2000/1*yVsMJSaxQR7AfW_Wgs2kCw.png)

Let’s make `pre-push` hook to pass by returning zero
{% gist https://gist.github.com/5minslearn/2f3c991ab6579d16912ef6fe61fde086.js %}
Let’s try to push the commits again and verify if it works.

![Commits pushed to the remote repo](https://cdn-images-1.medium.com/max/2000/1*Zyu7HLCm_Ik2aqxmCVgz9g.png)

Hooray!!!! Our commits are pushed to the remote repo.

The only concern is the error message which we received when exiting with a non-zero value was not so user-friendly. It did not say that it has failed due to an error thrown from pre-push hook.

    error: failed to push some refs to '[git@github.com](mailto:git@github.com):arungogosoon/Git-Project.git'

Let’s jump into the next section of our blog

### Why most development teams are not adopting Hooks in their development process?

There are many reasons to answer this question. However, I’m outlining a few of the most common causes.

 1. Hooks are local to any given Git repository and are not synced with the remote repository. This means, if you try to clone again, you’ll not see your hooks there. So, it’ll be challenging to manage them across the development team. But, we can overcome this with 2 workarounds.
 
1. Git allows us to move the hooks folder outside of .git folder. We can create a symlink (shortcut) from .git/hooks folder to the folder where we moved the hooks files. With this approach, we can track the changes in the hooks file and it’ll be synced with the entire team. 
2. By using some kind of alternative packages like husky . [husky](https://typicode.github.io/husky/#/) is a npm package which can be added innode project. husky helps us to configure commands to be executed on git hooks in the project configuration file. i.e., package.json file

 2. Though we find workarounds to sync the hooks file, the developer can easily skip the hooks validation by adding `--no-verify` with every git command. With this in mind, it’s best to think of Git hooks as a *convenient developer tool* rather than a *strictly enforced development policy*. Unless a server-side verification is set up, these cannot be verified.

 3. While git hooks are a compelling guardrail to help enforce quality, like any guardrail, they also come with some downsides including slowing down well-disciplined developers, and withholding opportunities for less-disciplined developers to gain that discipline. When a hook is configured to run test cases on every commit, it reduces developer productivity due to the long-running test cases. This happens especially with the compiling & transpiling languages (Eg. Java, Typescript, etc. ). Rather than focusing effort on guardrails to prevent developers from doing the “wrong thing”, focus on building discipline so that developers will learn to do the “right thing”.

### Q & A

I have limited experience of using `git hooks` with my team.

But, I have an interesting question raised by Kumar from my team.

He asked, “I’m bad at shell scripting, but good at scripting in Python. Is there a way, I can run this hook in python?”

Even I don’t when he asks this question. But, we did a quick search immediately and found that it’s possible.

Just by replacing `#!/bin/sh` with the python interpreter location ( `#!/usr/bin/env python` ) will work. We even tested this and this works fine.

### References

* [https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks)

* [https://git-scm.com/docs/githooks](https://git-scm.com/docs/githooks)

* [https://www.atlassian.com/git/tutorials/git-hooks](https://www.atlassian.com/git/tutorials/git-hooks)

* [https://www.thoughtworks.com/en-in/insights/blog/pre-commit-don-t-git-hooked](https://www.thoughtworks.com/en-in/insights/blog/pre-commit-don-t-git-hooked)

Hope you enjoyed reading this article. Give a ❤️ if you like this article. Subscribe to our [newsletter](https://5minslearn.gogosoon.com/?ref=devdotto_git_addon_scripts) to receive more such insightful articles that get delivered straight to your inbox.