Improving Your Go Development Workflow With Git Hooks

Elliot Forbes Elliot Forbes ⏰ 6 Minutes 📅 Aug 2, 2019

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.

$ git commit -m "Test Commit"
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:

.git/hooks/pre-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:

git add -A
C:\Projects\test>git status On branch master          modified:   main.go 
git commit -m "Updates"
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!

Further Reading: