Install & CLI
brew install lefthook
Install with Homebrew
npm install lefthook --save-dev
Install as a dev dependency
go install github.com/evilmartians/lefthook/v2@latest
Install with Go
gem install lefthook
Install as a gem
pipx install lefthook
Install with pipx
lefthook install
Install hooks, create lefthook.yml if missing
lefthook install <hook> ...
Install only some hooks
lefthook install --force
Install even if core.hooksPath is set
lefthook uninstall
Remove hooks lefthook installed
lefthook add <hook> --dirs
Add a hook and its script directory
lefthook validate
Check the config for errors
lefthook dump
Print the merged configuration
lefthook check-install
Exit 1 if hooks are missing or stale
lefthook self-update
Update the lefthook binary
lefthook version
Print the version
Running
lefthook run <hook>
Run a hook's jobs now
lefthook run <hook> --all-files
Treat all tracked files as input
lefthook run <hook> --file <path>
Run on chosen files only
Repeat the flag for several files. Ignored if --all-files is
also given.
lefthook run <hook> --job <name>
Run only the named job
lefthook run <hook> --tag <tag>
Run only jobs with a tag
LEFTHOOK_VERBOSE=1 lefthook run <hook>
Print verbose output
LEFTHOOK_OUTPUT=meta,summary
Limit which output parts print
LEFTHOOK_EXCLUDE=<tag>,<name>
Exclude tags or commands for one run
LEFTHOOK_CONFIG=<path>
Use another config file
git commit --no-verify
Bypass pre-commit and commit-msg hooks
LEFTHOOK=0 git <cmd>
Disable lefthook for one command
LEFTHOOK=1 npm install
Force hook install when CI=true
Hook & job options
pre-commit:
Hook name as top-level key
Any git hook name works, plus custom names you run with
lefthook run.
jobs: [ {name, run} ]
Ordered list of tasks in a hook
commands: <name>: run:
Older map form of jobs
run: <shell command>
Command to execute (via sh)
parallel: true
Run a hook's jobs concurrently
piped: true
Stop at the first failing job
Cannot be combined with parallel: true.
group: {parallel, jobs}
Nest jobs with their own flow
script: <file> runner: bash
Run a script from .lefthook/<hook>/
root: <dir>
Run the job from a subdirectory
env: {KEY: value}
Set environment variables for a job
tags: [<tag>]
Label a job for filtering
exclude_tags: [<tag>]
Skip jobs with these tags
fail_text: <msg>
Message shown when a job fails
interactive: true
Give the job a TTY; runs after others
use_stdin: true
Pass stdin through (pre-push scripts)
fail_on_changes: always
Fail if a hook modified tracked files
Values: never, always, ci, non-ci.
Files & placeholders
{staged_files}
Files staged for this commit
{push_files}
Commits not yet pushed, as files
{all_files}
Every file tracked by git
{files}
Output of the job's files command
{1} / {2}
Arguments git passed to the hook
{0}
All git hook arguments joined
{cmd}
The job's own run command
{lefthook_job_name}
Current job name
glob: "*.{js,ts}"
Only run for matching files
If a glob matches no staged files, the job is skipped.
glob: ["*.ts", "*.js"]
List of globs
exclude: [<glob>]
Leave matching files out
files: git diff --name-only main
Custom command listing files
stage_fixed: true
git add files after the job (pre-commit)
glob_matcher: doublestar
Make ** match zero directories too
By default ** needs 1 or more directories, so src/**/*.js
misses src/a.js.
Config file
extends: [<file>]
Merge other YAML files (globs ok)
remotes: [{git_url, ref, configs}]
Share config from another repo
min_version: <x.y.z>
Require at least this lefthook
output: [summary, failure]
Choose printed output (false = errors only)
colors: false
Disable colored output
no_tty: true
Never use a TTY
skip_lfs: true
Do not run git-lfs hooks
source_dir: <dir>
Where hook scripts live
# yaml-language-server: $schema=...
Editor schema for lefthook.yml