Frail and faltering follower of Jesus

Test driving pre-commit

By Gavin Davies

pre-commit is “a framework for managing and maintaining multi-language pre-commit hooks”. I tried it out.

The git tool offers many hooks - you can ls -al .git/hooks in any repo to see:

$ ls -al .git/hooks
total 104
drwxr-xr-x  14 gavin  staff   448 25 Oct  2021 .
drwxr-xr-x  15 gavin  staff   480 13 Aug 19:15 ..
-rwxr-xr-x   1 gavin  staff   478 25 Oct  2021 applypatch-msg.sample
-rwxr-xr-x   1 gavin  staff   896 25 Oct  2021 commit-msg.sample
-rwxr-xr-x   1 gavin  staff  3327 25 Oct  2021 fsmonitor-watchman.sample
-rwxr-xr-x   1 gavin  staff   189 25 Oct  2021 post-update.sample
-rwxr-xr-x   1 gavin  staff   424 25 Oct  2021 pre-applypatch.sample
-rwxr-xr-x   1 gavin  staff  1638 25 Oct  2021 pre-commit.sample
-rwxr-xr-x   1 gavin  staff   416 25 Oct  2021 pre-merge-commit.sample
-rwxr-xr-x   1 gavin  staff  1348 25 Oct  2021 pre-push.sample
-rwxr-xr-x   1 gavin  staff  4898 25 Oct  2021 pre-rebase.sample
-rwxr-xr-x   1 gavin  staff   544 25 Oct  2021 pre-receive.sample
-rwxr-xr-x   1 gavin  staff  1492 25 Oct  2021 prepare-commit-msg.sample
-rwxr-xr-x   1 gavin  staff  3610 25 Oct  2021 update.sample

In the above example, I don’t have any hooks set up; there’s just a bunch of samples. I’ve often used pre-push to stop myself pushing outright garbage.

But let’s say I want to prevent myself from committing some dump stuff. I mean, sure, we have CI and all that, but for small, light checks, we can run before even making the commit in the first place…

Better still, we can apply our autoformatting code style at point of commit, without ever having to explicitly run the tool.

AUTOMATION. ROBOTS! A better class of problem!

So, we could write our own hooks, for example a pre commit hook as described above… But do we really have to write it all ourselves? Wouldn’t it be nice to pull some off the shelf, like we do for everything else these days?

Enter pre-commit

pre-commit is “a framework for managing and maintaining multi-language pre-commit hooks”. The rationale for its existence is that it’s painful to manage hooks across projects. After all, it’s a generic problem - everyone needs linters, for example!

I won’t duplicate their documentation, this article is more about figuring out where pre-commit could be situated in a developer’s tool chain

I installed it on the repo for this website and added just one hook to trim trailing whitespace.

I hacked a PHP file, added some whitespace at the end, and committed it, and hey presto, it was auto-trimmed!

$ git commit -amx
Trim Trailing Whitespace.................................................Failed
- hook id: trailing-whitespace
- exit code: 1
- files were modified by this hook

Fixing remove-dead-tag.php

Clearly, in IRL, I would never use x as a commit message! :p Man was just fiddlin’!

Any gotchas?

Not really gotchas, more something worth knowing… The pre-commit hook will only operate on staged files, i.e. files in the set you git add. So, when you add a new plugin, it won’t run on all the files in the repo unless you pre-commit run --all-files.

You can skip pre-commit by supplying the --no-verify flag to git, should you need to, although it may be neater to use the SKIP flag (see their docs.

Writing your own plugins

One of the most powerful features is that you can write your own hooks, and use them cross-repo simply by specifying them by repo in the Yaml. Tops!

Loadsa languages are supported, but unfortunately my goto language PHP is not included. Boo! However, Node and Python are, so man’s happy with that.

In the current role I’m working in, I am considering developing a few pre-commit hooks to help keep our Terraform consistent, doesn’t look too hard to wire in.

Worth a bash?

Yeah, for sure. It’s complementary to CI, .editorconfig, pull request reviews… It’s another tool in that space to help you write more consistent code, and to raise your game in terms of the quality of mistakes you’re making.

And if you are getting to grips with Git, see my free eBook, Git Workflow Discipline :-)