neurons firing from a keyboard

thoughts about devops, technology, and faster business from a random guy from dallas.

You probably don't need Husky

Reading Time: Approximately 3 minutes.
View this post on GitHub.

Git hooks are an incredible and incredibly simple way of fixing loose ends before you commit and/or push your code without installing additional software.

Unfortunately, like most of the Git CLI, they can appear difficult and un-approachable at first glance. For one, they assume that you’re storing your scripts within the hidden and un-tracked .git directory! I hope you like symlinks!

Lots of projects use tools like Husky as an easier-to-use alternative that’s easy to scale across teams and very friendly within CI.

I considered going down this route for a personal project that I maintain, but backed out of it once I realized that I’d have to install npm to get it working. Something about having to install Node and deal with node_modules to, ultimately, do symlinks with extra steps rubbed me the wrong way.

Fortunately, I found a much easier alternative that’s easy to set up and scale: git.hooksPath!

Getting started with git.hooksPath: the easy way

git.hooksPath allows you to tell Git where your hooks path lives relative to the repository’s root. While I haven’t tested it, you can, theoretically, set it to a directory outside of your repository with dots.

This awesome quality-of-life feature requires Git 2.9 to be installed on your machine. If you’re on a Mac, you probably have Git v2.39 out of the box, which has this feature.

Check out the next section if you have an older version of Git or would prefer to manage your hooks with symlinks,

To set this up easily for every repo you’ll add to your machine, add this to your .bashrc or .bash_profile:

git config --global core.hooksPath='.githooks';

(Just for fun: here’s the difference between these two files!)

Then, in your repository, create a directory called .githooks and add an empty file into it to ensure that it’s tracked by Git:

# in your repository...
mkdir .githooks && touch .githooks/.gitkeep

git add, git commit, and git push these changes, as usual.

Congrats! Your Git client-side hooks are now tracked in version control!

git.hooksPath: the hard way

You can also emulate this capability with symlinks with a slightly higher performance penalty if you use Bash or zsh as your default shell.

Add this to your .bashrc or .bash_profile instead:

symlink_git_hooks() {
  test -d ".git" || return 0
  test "$(readlink -f $PWD/.git/hooks)" == "$PWD/.githooks" && return 0
  mv $PWD/.git/hooks $PWD/.git/hooks.old &&
    ln -s "$PWD/.githooks" "$PWD/.git/hooks"
}

PROMPT_COMMAND='symlink_git_hooks;'

This uses Bash and zsh’s excellent PROMPT_COMMAND feature to check that Git’s default hooks directory, .git/hooks, points to whatever directory you want in your codebase, like .githooks.

Since this fails-fast if .git isn’t present in the directory, this should incur a minimal performance hit whenever you execute commands. However, you’ll experience a tiny bit of slowness when you enter Git repos for the first time.

make: The secret weapon for Git hooks

Congrats! You can now easily do things you always forget to do before you push your code, like bumping the project’s version, running tests or updating configs. (If you’re not tracking your projects/infrastructure config in source code, you’re missing out!)

You can supercharge your Git hooks by combining them with your local build system like Make/CMake or, ironically, npm.

This way, you can use your build system as a one-stop shop for all of the actions that are done in your repository.

New contributors to your project will thank you! That includes you, six months from now!


There you have it! Git hooks for fun and profit. Happy hacking!