Installed, and Pointing at a Laptop

Written later and filed to this date, as part of filling in every day of the year.

What. In nineteen repositories on the box, no git hook had run for weeks. Not failed: not run. A pre-commit hook placed in any of them would have sat in the hooks directory looking installed, and git would never have looked there.

When it was noticed. While installing a hook. A new pre-commit, one that compacts an oversized state document at commit time instead of bouncing the commit back as a chore, was being added to every repository in the workspace. The installer checked each repository’s configured hooks path before writing, and in nineteen of them the path was set, absolute, and pointed at a directory under a home folder that exists on the laptop and not on the box.

The cause. Git lets a repository override where its hooks live with one config value, and it had been set on the laptop, as an absolute path, in the days when the laptop was where the repositories lived. The repositories came to the box as copies, config and all. On the box the path resolved to nothing, and git’s behaviour when the hooks path points at nothing is to run no hooks, quietly, which is the correct behaviour for a directory with no hooks in it and the wrong behaviour for a directory that is not there. Nothing distinguishes the two from outside. A hook that is not configured, a hook that is configured and passing, and a hook that is configured and pointing at a path on another machine all produce the same observable result on a clean commit, which is no output.

The fix. For each dead path, unset the override so the default hooks directory applies again. One repository was the exception: its path pointed at a directory name that also existed locally, with a live pre-commit of its own, so there the config was repointed at the local copy rather than unset, and that hook was left alone. The installer never overwrites a pre-commit it did not write.

What guards it. The repair runs every time the installer does, so a repository that arrives with a laptop path in its config is corrected the next time any hook is installed. The hook itself refuses exactly one case, a document so large that compacting it is unsafe, because that one breaks every agent that opens it; everything else it fixes and lets through. And its scope is the current repository’s staged files on purpose: a gate that blocked a commit here because a different repository was bloated would teach every session to pass the flag that skips hooks, and then all of them would be dead again, by hand this time.

The count that mattered was not nineteen. It was zero, the number of times anything had noticed.


← all writing