Husky Complete Guide | Git Hooks for Linting· Formatting
이 글의 핵심
Husky runs scripts automatically on git events — pre-commit linting catches errors before they enter your repo, commitlint enforces commit message standards, and pre-push hooks prevent broken code from reaching CI. Set it up once and enforce quality for the whole team.
What Husky Does
Husky makes git hooks easy:
git commit → pre-commit hook → lint + format staged files
git commit → commit-msg hook → validate commit message format
git push → pre-push hook → run tests
Without Husky, hooks are per-developer local files (.git/hooks/) not shared in the repo. With Husky, they’re in package.json and installed automatically.
1. Installation
# Install Husky
npm install --save-dev husky
# Initialize (creates .husky/ directory + adds prepare script)
npx husky init
This adds to package.json:
{
"scripts": {
"prepare": "husky"
}
}
And creates .husky/pre-commit with a sample hook.
2. Pre-commit Hook — Lint and Format
With lint-staged (recommended)
lint-staged runs linters only on staged files — much faster than linting everything.
npm install --save-dev lint-staged
// package.json
{
"lint-staged": {
"*.{ts,tsx,js,jsx}": [
"eslint --fix",
"prettier --write"
],
"*.{css,scss,json,md}": [
"prettier --write"
]
}
}
# .husky/pre-commit
npx lint-staged
Now every git commit automatically:
- Finds staged
.ts/.tsx/.js/.jsxfiles - Runs ESLint with auto-fix
- Runs Prettier
- Re-stages the fixed files
- Commits
Alternative: direct commands
# .husky/pre-commit
npm run lint
npm run format:check
3. Commit Message Validation — commitlint
Enforce conventional commit format: feat(scope): description
npm install --save-dev @commitlint/cli @commitlint/config-conventional
// commitlint.config.js
export default {
extends: ['@commitlint/config-conventional'],
rules: {
// Customize if needed
'type-enum': [2, 'always', [
'feat', // new feature
'fix', // bug fix
'docs', // documentation
'style', // formatting
'refactor',// code change without feat/fix
'test', // tests
'chore', // build/tooling
'perf', // performance
'ci', // CI changes
'revert', // revert commit
]],
'subject-max-length': [2, 'always', 100],
},
}
# .husky/commit-msg
npx --no -- commitlint --edit $1
Valid commit messages:
feat(auth): add OAuth2 login
fix(api): handle null response from user endpoint
docs: update README installation steps
chore: upgrade dependencies to latest
Invalid (will be rejected):
WIP
update stuff
Fixed the bug
4. Pre-push Hook — Run Tests
Pre-commit should be fast (lint only). Run tests before push:
# .husky/pre-push
npm test -- --passWithNoTests
Or run type checking + tests:
# .husky/pre-push
npm run typecheck && npm test
5. Full Setup Example
// package.json
{
"scripts": {
"prepare": "husky",
"lint": "eslint src --ext .ts,.tsx",
"lint:fix": "eslint src --ext .ts,.tsx --fix",
"format": "prettier --write \"src/**/*.{ts,tsx,css,json}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx,css,json}\"",
"typecheck": "tsc --noEmit",
"test": "vitest run"
},
"lint-staged": {
"src/**/*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"src/**/*.{css,json,md}": [
"prettier --write"
]
}
}
# .husky/pre-commit
npx lint-staged
# .husky/commit-msg
npx --no -- commitlint --edit $1
# .husky/pre-push
npm run typecheck
6. Hooks for Different Scenarios
Block commits with TODO/FIXME
# .husky/pre-commit
if git diff --cached | grep -E '^\+.*TODO|^\+.*FIXME' > /dev/null; then
echo "Error: staged changes contain TODO or FIXME comments"
echo "Resolve them or use git commit --no-verify to skip"
exit 1
fi
npx lint-staged
Prevent direct commits to main
# .husky/pre-commit
branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$branch" = "main" ] || [ "$branch" = "master" ]; then
echo "Direct commits to $branch are not allowed."
echo "Create a feature branch and open a pull request."
exit 1
fi
npx lint-staged
Check for large files
# .husky/pre-commit
MAX_SIZE=500 # KB
for file in $(git diff --cached --name-only); do
if [ -f "$file" ]; then
size=$(du -k "$file" | cut -f1)
if [ $size -gt $MAX_SIZE ]; then
echo "Error: $file is ${size}KB (max ${MAX_SIZE}KB)"
echo "Add large files to .gitignore"
exit 1
fi
fi
done
npx lint-staged
7. CI Integration
Husky hooks should NOT run in CI (CI has its own lint/test steps):
# .github/workflows/ci.yml
- name: Install dependencies
run: npm ci
env:
HUSKY: 0 # disable Husky in CI
Or automatically skip in CI (Husky 9+ does this by default):
# Husky 9+ checks CI environment automatically
# The prepare script skips in CI environments
# (when CI=true env var is set, which GitHub Actions sets automatically)
8. Bypassing Hooks
When genuinely needed (emergency commits, fixing hook issues):
# Skip all hooks for one commit
git commit --no-verify -m "emergency: hotfix"
# Skip pre-push
git push --no-verify
# Disable Husky for the session
HUSKY=0 git commit -m "wip"
# Disable lint-staged for one commit
LINT_STAGED_SKIP=true git commit -m "wip"
Common Issues
Hook not executable
chmod +x .husky/pre-commit
Hooks not running after npm install
# Make sure prepare script exists
npm run prepare
ESLint errors blocking commits in CI
# Make sure HUSKY=0 is set in CI env
lint-staged running on deleted files
{
"lint-staged": {
"*.{ts,tsx}": "eslint --fix" // lint-staged skips deleted files automatically
}
}
Key Takeaways
| Hook | Purpose | Speed |
|---|---|---|
pre-commit | Lint + format staged files | Fast (< 3s with lint-staged) |
commit-msg | Validate commit message | Instant |
pre-push | Type check + tests | Can be slow (use wisely) |
- lint-staged makes pre-commit fast by only linting changed files
- commitlint enforces commit message conventions for readable git history
- Set
HUSKY=0in CI — CI has its own steps, don’t duplicate work - Keep hooks fast — developers bypass slow hooks with
--no-verify - Hooks are version-controlled in
.husky/and installed vianpm install
Frequently Asked Questions (FAQ)
Q. When would I use this in practice?
A. Automate code quality with Husky git hooks. Covers pre-commit linting with lint-staged, commit-msg validation with commi…
Q. What should I read before this?
A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.
Q. Where can I study this more deeply?
A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.
Related Articles (Internal Links)
Other articles related to this topic.
Keywords Covered in This Article (Related Search Terms)
This article covers Husky, Git, ESLint, Prettier, Developer Tools, CI/CD.