Global Hooks with Git

Introduction

Recently I began to wonder if there was a way to have a collection of git hooks that could run on all of my git projects to save me the trouble of creating/editing hooks in each of my projects. Yes, it is possible to do this for new projects, via the git config --global init.templatedir command, but I have a lot of pre-existing git projects that I wanted to add a pre-push hook to, and I wanted to be able to make alterations to this hook after the fact that would affect all my projects. Apparently, I'm not the only person who needed this feature, because it was added in git release 2.9.

Details

Adding the global hooks was easy enough, thanks to this stackoverflow answer, but I did run into a couple of caveats after I had the global hooks path set.

git config

To add the path to your global git configuration run the following command:

git config --global core.hooksPath /path/to/my/centralized/hooks

Hook file

The next step is to add your hook file and make it executable.

touch /path/to/my/centralized/hooks/pre-push
chmod a+x /path/to/my/centralized/hooks/pre-push

And then add what ever logic you'd like the hook to execute. I use sonarqube to provide Continuos Inspection to my projects, so I wanted the sonar-scanner to run on every push. (I highly recommend sonarqube for keeping bugs and code smells out, you should check it out if you're not familiar with it!). I use the fish shell, so you might need a different shebang.

# /path/to/my/centralized/hooks/pre-push
#! /usr/bin/fish

command sonar-scanner

So I created my hook and did a test push, to see that it worked as expected. However, I do have some documentation only and small script projects that don't use the sonar-scanner. I was getting some ugly error messages when pushing these projects. It didn't fail the push, because I wasn't checking the return code on sonar-scanner command, but I still wanted to silence these messages, or not run sonar-scanner at all on projects that didn't need it. That was easy enough to do by checking for the presence of the sonar-project.properties file before running the scanner.

# /path/to/my/centralized/hooks/pre-push
#! /usr/bin/fish
if test -e ./sonar-project.properties
    command sonar-scanner
end

Ok, so now that is running only when it should, but I bumped into another issue. I had some projects that had project specific pre-push hooks that I expected to still be running before a push was executed. I had assumed that adding these global hooks would be in addition to the project hooks, not overriding them. However, that is not the behavior I was wanting; I still wanted any project specific hooks to be executed. The fix for this was to check for the existence of a project level git hook and fire it if it exists. I decided it would make more sense for my use case to fire it before the global hook logic was executed.

# /path/to/my/centralized/hooks/pre-push
#! /usr/bin/fish

if test -e ./.git/hooks/pre-push
    command sh ./.git/hooks/pre-push
end

if test -e ./sonar-project.properties
    command sonar-scanner
end

Conclusion

So there I had my global hook, that only ran the sonar-scanner if it was necessary and made sure to also execute any local hooks. I'm sure I've only scratched the surface of the power of this, and I'm looking forward to adding more functionality in the very near future. I'm going to investigate failing the push if the sonarqube quality gate fails, next. To improve my CI/CD work flow. Have any suggestions or ideas for better hooks? Please leave them in the comments below.

If you liked this post, you can subscribe to the rss feed or follow me @ToddEidson on Twitter to be notified of future blog posts.

Date Published: 17 February, 2019

Tags: git

About Todd

Full stack application developer. Life-long learner. Pragmatic programmer. Believer in clean coding. Proponent for extensible and reusable code. Hobbies include (very) amateur photography, collecting old jazz records and going to live music performances.

North Central Ohio, US
toddeidson[dot]info

Obligatory Disclaimer

All opinions are my own, probably wrong, and subject to change without notice.

© 2017-2019 Todd Eidson. All content is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.

Hosted on linode