Git hooks
Disclaimer: when I say git hooks I’m talking about commiting-workflow hooks.
What are git hooks?
Git hooks are scripts that automatically run before (or after) you make changes to a repository. For example, you can automatically run tests, lint checks, automatic formatting and other niceties that help you push better code to the project.
How hooks work
By default, git hooks live in .git/hooks
inside the
repo. For example, to automatically run a command before committing,
simply create a .git/hooks/pre-commit
script:
#!/bin/sh
echo "You're about to commit a change 👀"
# Change input to keyboard so user can interact
exec < /dev/tty
read -p "Are you really really sure? (y/n) "
[[ $REPLY == "y" ]] && exit 0 || exit 1
This will prompt the user to confirm if they really really want to commit the changes. Menancing, but I’m not sure if effective.
Tracking git hooks
The problem is that .git/hooks
is a private directory,
which means hooks will only be available locally and not for everyone
using the repo. Let’s change that and tell git to look for hooks in the
.githooks
directory:
git config --local core.hooksPath .githooks
Then, simply add .githooks
to the repo.
Making all contributors use the hooks
Now everyone has the directory in the repo, but git is looking for
the hooks in the wrong place: the default directory. To use them, each
contributor must manually configure their repo to use
.githooks
as their hooks path. Bummer.
However, to make life easier for everyone, you can add the config command to your project’s setup script.
For example, if your project has a package.json
you can
add a prepare
script:
"scripts": {
...
"prepare": "git config --local core.hooksPath .githooks"
}
This will make git look for hooks in the correct place as soon as
they yarn install
.
Useful git hooks
Now that we’ve put everyone in the same page and we’re all using the same hooks, we can create useful things.
Example 1: Force everyone to use commitizen when commiting
Not cool, but… When commiting, commitzen will open and help the user
form a conventional commit message. Add to
.git/hooks/prepare-commit-msg
:
#!/bin/sh
# Change input to keyboard so user can interact
exec < /dev/tty
# Run commitizen
yarn cz --hook || true
Example 2: Lint commit messages with commitlint
Much saner. If the user tries to commit changes with a
non-conventional message, abort (if they want to, they can use commitzen
to help them). Add to .githooks/commit-msg
:
#!/bin/sh
# Run commitlint against current commit
yarn commitlint --edit "${1}"
Example 3: Lint code
When commiting, check files for lint errors with eslint. Add to
.githooks/pre-commit
:
#!/bin/sh
# Lint all jsx and js files
eslint --ext jsx,.js ./
Example 4: Run prettier only on staged files
When commiting, run code formatting only on staged files. Add to
.githooks/pre-commit
:
#!/bin/sh
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g')
[ -z "$STAGED_FILES" ] && exit 0
# Prettify staged files
echo "$STAGED_FILES" | xargs ./node_modules/.bin/prettier --ignore-unknown --write
# Add back the files to staging
echo "$STAGED_FILES" | xargs git add
exit 0
List of all commit hooks
To get a list of all hooks you can use and when they are executed
just RTFM @ man githooks
or online