轻量级 Git Hooks 管理工具 Husky

1. 概述

1.1 什么是 Husky

Husky 是一个轻量级的 Git Hooks 管理工具,专为 Node.js 项目设计。它利用 Git 的 core.hooksPath 特性,让团队可以在版本控制中共享 Git Hooks 配置。

核心特性:

特性 说明
轻量 仅 2kB(gzipped),零依赖
快速 执行时间约 1ms
跨平台 支持 macOS、Linux、Windows
全面支持 支持全部 13 种客户端 Git Hooks
生态完善 与 lint-staged、commitlint 等工具无缝集成

1.2 工作原理

sql 复制代码
项目目录/
├── .husky/
│   ├── pre-commit      # pre-commit hook 脚本
│   └── commit-msg      # commit-msg hook 脚本
├── package.json        # 包含 prepare 脚本
└── ...
  1. 安装时 :设置 Git 的 core.hooksPath 指向 .husky 目录
  2. 触发时 :Git 事件触发 .husky 目录中对应的脚本
  3. 执行时:脚本运行指定命令,失败则阻止 Git 操作

2. 安装和配置

2.1 快速开始(推荐)

使用 husky init 一键完成安装和初始化:

bash 复制代码
# npm
npx husky init

# pnpm
pnpm exec husky init

# yarn
yarn dlx husky init

# bun
bunx husky init

该命令会自动:

  • 安装 husky 作为开发依赖
  • 创建 .husky/ 目录
  • package.json 中添加 prepare 脚本
  • 创建示例 pre-commit hook

2.2 手动安装

bash 复制代码
# 1. 安装
npm install husky --save-dev

# 2. 初始化
npx husky init

package.json 中的 prepare 脚本:

json 复制代码
{
  "scripts": {
    "prepare": "husky"
  }
}

prepare 脚本在 npm install 后自动执行,确保团队成员克隆项目后自动配置 hooks。

2.3 添加 Hook

创建 hook 非常简单,只需在 .husky/ 目录下创建对应名称的文件:

bash 复制代码
# 方法 1:使用 echo
echo "npm test" > .husky/pre-commit

# 方法 2:手动创建文件
# 直接编辑 .husky/pre-commit 文件

Hook 脚本示例:

bash 复制代码
# .husky/pre-commit
npm run lint
npm test

注意 :Husky v9 的 hook 脚本不需要 shebang 行(如 #!/bin/sh),Husky 会自动处理。

2.4 在 CI/Docker 中禁用

CI 环境通常不需要 Git hooks:

bash 复制代码
# 方法 1:环境变量(推荐)
HUSKY=0 npm ci

# 方法 2:GitHub Actions
- run: npm ci
  env:
    HUSKY: 0

# 方法 3:package.json 容错处理
{
  "scripts": {
    "prepare": "husky || true"
  }
}

3. 常用 Git Hooks

3.1 pre-commit

提交前执行,用于代码检查和格式化。

bash 复制代码
# .husky/pre-commit
npx lint-staged

3.2 commit-msg

验证提交信息格式。

bash 复制代码
# .husky/commit-msg
npx --no -- commitlint --edit $1

自定义验证示例:

bash 复制代码
# .husky/commit-msg
commit_msg=$(cat "$1")
pattern="^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50}"

if ! echo "$commit_msg" | grep -qE "$pattern"; then
    echo "错误:提交信息格式不正确"
    echo "格式:<type>(<scope>): <subject>"
    exit 1
fi

3.3 pre-push

推送前执行,用于运行测试。

bash 复制代码
# .husky/pre-push
npm test
npm run build

3.4 post-merge

合并/拉取后执行,用于自动更新依赖。

bash 复制代码
# .husky/post-merge
changed=$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)

if echo "$changed" | grep -q "package-lock.json"; then
    echo "依赖变化,正在安装..."
    npm ci
fi

4. 与其他工具集成

4.1 lint-staged

只对暂存文件运行检查,大幅提升速度。

bash 复制代码
npm install lint-staged --save-dev

package.json 配置:

json 复制代码
{
  "lint-staged": {
    "*.{js,ts,jsx,tsx}": ["eslint --fix", "prettier --write"],
    "*.{css,scss}": "prettier --write",
    "*.md": "prettier --write"
  }
}

Husky 配置:

bash 复制代码
# .husky/pre-commit
npx lint-staged

4.2 commitlint

检查提交信息是否符合 Conventional Commits 规范。

bash 复制代码
npm install @commitlint/cli @commitlint/config-conventional --save-dev

commitlint.config.js:

javascript 复制代码
module.exports = {
  extends: ['@commitlint/config-conventional'],
};

Husky 配置:

bash 复制代码
# .husky/commit-msg
npx --no -- commitlint --edit $1

4.3 完整配置示例

package.json:

json 复制代码
{
  "scripts": {
    "prepare": "husky",
    "lint": "eslint src",
    "test": "jest"
  },
  "devDependencies": {
    "@commitlint/cli": "^19.0.0",
    "@commitlint/config-conventional": "^19.0.0",
    "husky": "^9.0.0",
    "lint-staged": "^15.0.0"
  },
  "lint-staged": {
    "*.{js,ts}": ["eslint --fix", "prettier --write"]
  }
}

.husky/pre-commit:

bash 复制代码
npx lint-staged

.husky/commit-msg:

bash 复制代码
npx --no -- commitlint --edit $1

5. 最佳实践

5.1 性能优化

bash 复制代码
# 使用 lint-staged 只检查变更文件
npx lint-staged

# ESLint 启用缓存
eslint --fix --cache

# 耗时任务放到 pre-push 而非 pre-commit

5.2 Monorepo 配置

package.json 不在 Git 根目录时:

json 复制代码
{
  "scripts": {
    "prepare": "cd .. && husky frontend/.husky"
  }
}

5.3 启动文件

配置 Node 版本管理器(nvm、fnm 等)的初始化脚本:

bash 复制代码
# ~/.config/husky/init.sh
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

6. 故障排除

6.1 Hooks 不执行

bash 复制代码
# 1. 检查 hook 文件名是否正确(不能是 precommit 或 pre-commit.sh)
ls .husky/

# 2. 检查 Git hooks 路径
git config core.hooksPath

# 3. 确保 Git 版本 >= 2.9
git --version

# 4. 重新初始化
npx husky init

6.2 Command not found

GUI 应用或某些环境可能找不到 node/npm,配置启动文件:

bash 复制代码
# ~/.config/husky/init.sh
export PATH="/usr/local/bin:$PATH"

6.3 卸载后残留问题

如果卸载 Husky 后 hooks 仍然失败:

bash 复制代码
git config --unset core.hooksPath

6.4 Windows + Yarn 问题

Git Bash 配合 Yarn 可能出现 "stdin is not a tty" 错误:

bash 复制代码
# .husky/common.sh
if [ -t 1 ]; then
  exec < /dev/tty
fi
bash 复制代码
# .husky/pre-commit
. "$(dirname "$0")/common.sh"
yarn lint

6.5 跳过 Hooks

bash 复制代码
# 单次跳过
git commit --no-verify -m "message"
git push --no-verify

# 临时禁用所有 hooks
HUSKY=0 git commit -m "message"

# 多个命令
export HUSKY=0
git add .
git commit -m "message"
unset HUSKY

6.6 测试 Hook

bash 复制代码
# 直接运行脚本
sh .husky/pre-commit

# 添加 exit 1 测试是否能阻止提交
echo "exit 1" >> .husky/pre-commit

# 查看详细执行过程
GIT_TRACE=1 git commit -m "test"

7. 版本迁移

7.1 从 v4 迁移到 v9

v4 配置(旧):

json 复制代码
{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  }
}

v9 配置(新):

bash 复制代码
# .husky/pre-commit
npx lint-staged
bash 复制代码
# .husky/commit-msg
npx --no -- commitlint --edit $1

迁移步骤:

bash 复制代码
# 1. 升级
npm install husky@latest --save-dev

# 2. 删除 package.json 中的 "husky" 字段

# 3. 初始化
npx husky init

# 4. 创建 hooks
echo "npx lint-staged" > .husky/pre-commit
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg

主要变化:

方面 v4 v9
配置位置 package.json .husky/ 目录
Hook 格式 JSON Shell 脚本
环境变量 HUSKY_GIT_PARAMS $1
大小 ~1MB 2kB

参考资源

相关推荐
AI前端老薛16 小时前
CSS实现动画的几种方式
前端·css
携欢17 小时前
portswigger靶场之修改序列化数据类型通关秘籍
android·前端·网络·安全
GuMoYu17 小时前
npm link 测试本地依赖完整指南
前端·npm
代码老祖17 小时前
vue3 vue-pdf-embed实现pdf自定义分页+关键词高亮
前端·javascript
未等与你踏清风17 小时前
Elpis npm 包抽离总结
前端·javascript
代码猎人17 小时前
如何使用for...of遍历对象
前端
秋天的一阵风17 小时前
🎥解决前端 “复现难”:rrweb 录制回放从入门到精通(下)
前端·开源·全栈
林恒smileZAZ17 小时前
【Vue3】我用 Vue 封装了个 ECharts Hooks
前端·vue.js·echarts
颜酱17 小时前
用填充表格法-继续吃透完全背包及其变形
前端·后端·算法