Improving Your Go Development Workflow With Git Hooks
Git hooks are a pretty powerful way of improving your development workflow without having to remember to run additional scripts or perform additional tasks. In this article, we are going to be taking a look at how you can define your own simple git hooks within a project repository that can automatically perform the task of linting + formating your Go code.
This fairly simple example should give you a flavour as to how you can leverage git hooks for your own devious plans.
Real-Life Example
One example of this is from one of my current projects where the team had to encrypt secrets files before they were getting pushed to the project’s repository. Encrypting the file was a simple one liner, but due to the number of encrypted files we were working with, it became a challenge to remember to encrypt every file that has been changed and more often than not we would run our project and scratch our heads whilst we tried to figure out why it wasn’t picking up the new secrets.
The Solution
When it came to attempting to solve this, we were fairly limited in the tools we could pull in and didn’t want to use something that would impact the current development flow for people in the team who had their own preferences.
This is where git hooks came into play. With hooks, I could define a simple pre-commit
hook script that would automatically perform the task of encrypting any un-encrypted files and adding them to the commit. I could also make this an optional addon to the team’s development flow by creating these hooks in a hooks/
directory so that if they wished to add these git hooks to their workflow, they could with a simple git config core.hooksPath hooks
command.
Creating a Git Hook
Hooks are actually surprisingly simple to make as they are simply just bash scripts that have specific names in particular directory. When you next execute a given git
command, it would automatically execute this bash script provided it was the rightly named file for that particular command.
If you wish to only create git hooks for your particular project on your machine, you can do so by navigating to your current project directory and then into the .git/hooks/
directory within that project.
If you view the contents of that directory you should see something like this:
31/07/2019 22:09 478 applypatch-msg.sample
31/07/2019 22:09 896 commit-msg.sample
31/07/2019 22:09 3,327 fsmonitor-watchman.sample
31/07/2019 22:09 189 post-update.sample
31/07/2019 22:09 424 pre-applypatch.sample
31/07/2019 22:09 1,642 pre-commit.sample
31/07/2019 22:09 1,348 pre-push.sample
31/07/2019 22:09 4,898 pre-rebase.sample
31/07/2019 22:09 544 pre-receive.sample
31/07/2019 22:09 1,492 prepare-commit-msg.sample
31/07/2019 22:09 3,610 update.sample
Each of these contains example git hooks that you can turn on simply by renaming the files and removing the .sample
file endings.
The file we are concerned about for the purpose of this article though is the pre-commit
file.
Within this same directory, let’s create a new file called pre-commit
and within this let’s add the following:
#!/bin/bash
echo "Test Hook"
If you are running on Mac or Linux you will have to set the executable bit for this script using
chmod
. On Windows, this should just work straight out of the box!
chmod +x pre-commit
With this set, try make an empty commit to test this:
git commit --allow-empty -m "Testing Git Hook"
When you run this, you should now see that "Test Hook"
is printed out just before git executes the commit
command.
Awesome! You have successfully created your own git hook! This will now be executed every time you commit something within this given repository.
Improving our Git Hook
Ok, so we have been able to successfully create our first git hook, it’s now time to start improving this so that it actually performs a useful task for us other than just echo some text.
Let’s start by adding some go linting to this script:
#!/bin/bash
echo "Test Hook"
## this will retrieve all of the .go files that have been
## changed since the last commit
STAGED_GO_FILES=$(git diff --cached --name-only -- '*.go')
## we can check to see if this is empty
if [[ $STAGED_GO_FILES == "" ]]; then
echo "No Go Files to Update"
## otherwise we can do stuff with these changed go files
else
for file in $STAGED_GO_FILES; do
echo $file
done
fi
With this in place, try changing a .go
file within your project and attempting a git add -A
following by a git commit -m "Some Message"
.
You will now see that just below our "Test Hooks"
our pre-commit
hook is now printing out the path to the file which has been changed.
Test Hook
test.go
[master 82ab8c6] test
1 file changed, 2 insertions(+)
Aweomse, so it’s able to see that we’ve update a .go
file within our project directory and we are able to echo that file out in our for
loop. Let’s extend our git hook to automatically format this file for us whenever we commit:
#!/bin/bash
echo "Test Hook"
## this will retrieve all of the .go files that have been
## changed since the last commit
STAGED_GO_FILES=$(git diff --cached --name-only -- '*.go')
## we can check to see if this is empty
if [[ $STAGED_GO_FILES == "" ]]; then
echo "No Go Files to Update"
## otherwise we can do stuff with these changed go files
else
for file in $STAGED_GO_FILES; do
## format our file
go fmt $file
## add any potential changes from our formatting to the
## commit
git add $file
done
fi
Now, when we go to save any of our files and subsequently add and then commit them, these will be automatically formatted for us and any changes made by the formatting will automatically be added to the given commit:
C:\Projects\test>git status
On branch master
modified: main.go
Test Hook
main.go
main.go
[master 61e6ed4] Updates
Awesome! We have now been able to create a git hook that automatically improves our Go development workflow and ensures that whatever we commit is properly formatted code!
Distributing Git Hooks Between Teams
Now, unfortunately the changes we made within the hooks/
directory under our project’s .git/
directory will not be tracked
and therefore getting these changes out to various different members of your team becomes a bit of a challenge.
However, what you can do to get around this particular challenge is to create a directory called .githooks/
within your current
project’s directory and store the pre-commit
git hook within that directory. You’ll be able to commit and track this just as you
would any other files within your project and in order to turn on these enable these hooks on other development machines you
simply need to run this command:
$ git config core.hooksPath .githooks
Once you have executed this particular command, you should now see that whenever you try and commit something, the hooks provided within that directory are now enabled!
Conclusion
Awesome, so in this tutorial, we’ve had a look at how you can improve your Go development workflow by using git hooks in conjunction
with existing tools such as go fmt
to ensure that whatever you commit up to your repositories has been properly formated!
This is just a very small taste of what you can achieve with git hooks and hopefully it gives you some ideas as to how you can take these further! If you have any ideas or examples as to how you have improved this workflow, then I would love to hear about them in the comments section below!