Wesley Schwengle IncludeIf: A digital space written in pencil Articles Series Answerrit /dev/null About ArticlesConfig based git hooks for fun and profit

TL;DR

Git is going to have (or by now has – via 2.54.0) a new, well, sorta, new feature called git-hooks which you can define in your configuration. This article will explain the how.

The way you configure these hooks is pretty simple:

[hook "your-custom-hook-name"] event = git-hook-name comand = some command here

You can test a hook by running git hook run git-hook-name. All the events are named according to what you can have in .git/hooks/, aka, all the ones found in man 5 githooks.

For more, continue reading.

All them hooks

I, for example have, via my own custom gittooling bum , two hooks that deal with how commit messages are created:

[hook "prepare-commit-message"] event = prepare-commit-msg command = git hook-prepare-commit-msg [hook "commit-message-check"] event = commit-msg command = git hook-commit-msg

Now each project I work on use these hooks when I run git commit. I don’t need to define them in each project or via a shared githook directory.

Caveat emptor

My Javascript projects automatically run a testsuite when I push them:

[hook "npm-test"] event = pre-push command = npm run test "#"

The "#" is needed to exclude additional parameters when the hook is called from an actual pre-push attempt. When running git hook run pre-push everything works well. When running it from the actual push, it adds the remote and the location as seen with GIT_TRACE=1 git push origin:

00:46:53.714453 run-command.c:673 trace: run_command: 'npm run test' origin git@gitlab.com:waterkip/mything.git 00:46:53.714458 run-command.c:765 trace: start_command: /bin/sh -c 'npm run test "$@"' 'npm run test' origin git@gitlab.com:waterkip/mything.git

The documentation is a unclear about it. I asked the git mailing list because I was wondering if I was being stupid or if I misunderstood things. The dumbed down answer was: Git hooks should always be treated as run via the hook framework. And never be used as stand-alone scripts that can be hooked into the hooks. And when I say never, I mean sometimes. It may work because pre-commit hooks don’t read from STDIN, or ignoring the data might be fine too. It depends on your level of comfort and what your hook is trying to do. Hooks often get additional details via STDIN. For example a push hook gets the remote and url as regular arguments. And it gets LOCAL_REF, LOCAL_SHA, REMOTE_REF and REMOTE_SHA via STDIN.

In this case npm run test ignores STDIN, but if your script might read these, you need to be aware that git also sends data in this way. For me the quickest fix was to simply add "#" to the command to ignore the additional arguments. So never do this, k?

Local overrides

All my javascript projects have the hook enabled, but I have some npm repo’s that do not need to have their test run, mainly because they just ship css files and CSS doesn’t really have tests other than “let’s see how it works in the browser”, you can thus tell the hook not to run in your local config:

[hook "npm-test"] enabled = false

Now your hook won’t run in that specific repo.

git-hook dir vs config based hooks

I personally love this new approach to hooks. It allows for setting up multiple hooks without having to add hooks to .git/hooks, which is a good escape hatch in itself – but it shouldn’t have been the default.

The way these hooks are being defined allows people to create hooks via framework, packages, or toolkits without having to take over complete hookdirs. I’m looking at you Husky… I’ve had this problem where our frontend folks decided husky was more important than my hooks and I had to undo a take over from husky. With this new setup husky and I could live life in harmony (in theory at least, my hate for these hook frameworks is probably too deep, but you can dream :)).

You might also like

Git in the trenches: How maintaining sanity is a graph in the making