Overview
Git Hooks 管理工具实战对比:lefthook vs husky

Git Hooks 管理工具实战对比:lefthook vs husky

June 1, 2026
9 min read

引子:门铃与快递员的故事

你有没有遇到过这种情况——快递员按门铃,你开门接过快递,关上门才发现包装破了,东西摔坏了。如果能在快递员离开前就发现这个问题就好了。

git hooks 就是那个“按门铃“的机制。它在 git 执行某些操作的关键节点插入一个检查点,让你有机会在事情发生前做点什么——比如检查代码风格、跑单元测试、确保提交信息符合规范。

但 git hooks 本身只是一个框架,你需要手动创建hook文件、编写脚本、管理多台机器上的配置——这很快就会变成一团乱麻。所以社区出现了各种工具来简化这个过程,其中最流行的两个就是 huskylefthook

今天这篇文章,我们从头开始,一步步配置两个工具,最后给出一个实操性的对比结论。无论你是刚接触 git 的新手,还是想了解这两个工具区别的老手,看完都会有收获。

第一章:git hooks 基础

什么是 git hooks

git hooks 是 git 内置的脚本触发机制。当你在终端执行 git commitgit push 等命令时,git 会在特定时机自动运行预设的脚本。这些脚本让你有机会在 git 操作完成前(或完成后)插入自定义逻辑。

一个经典的例子是 pre-commit hook:每次你执行 git commit,git 会先运行这个 hook 里的脚本。如果脚本返回非零值(表示检查失败),commit就会被取消。这样就能确保有问题的代码不会进入版本历史。

常见 hooks 种类

git 支持的 hooks 很多,下面是最常用的几种:

提交相关

  • pre-commit:在 git commit 执行前运行,最常用的场景是做代码风格检查和单元测试
  • prepare-commit-msg:在提交信息编辑器打开前运行,可以用来自动生成提交信息模板
  • commit-msg:在提交信息填写完成后运行,常用于验证提交信息格式(如是否符合Conventional Commits规范)
  • post-commit:在 commit 完成后运行,通常用于通知、记录等

推送相关

  • pre-push:在 git push 执行前运行,可以用来检查远程分支的最新状态
  • post-push:在 push 完成后运行,常用于触发CI/CD pipeline

其他

  • pre-rebase:在 rebase 执行前运行
  • post-merge:在 merge 完成后运行,常用于恢复文件权限、生成文档等

为什么需要管理工具

直接写 git hooks 脚本并不难,但当团队变大、项目变复杂时,问题就来了:

配置分散:每个开发者的 .git/hooks 目录是本地目录,无法提交到 git 仓库。新同事加入时需要手动复制脚本,一致性无法保证。

跨平台问题:shell 脚本在 Linux/Mac 和 Windows 上的行为可能不一致。Windows 的 git bash 支持 shell 语法,但原生的 Windows CMD 和 PowerShell 就另说了。

调试困难:当 hook 脚本执行失败时,错误信息可能不够清晰,难以定位问题。

脚本复用性差:如果你有多个项目,每个项目都需要类似的 hooks,复制粘贴代码会导致维护噩梦。

husky 和 lefthook 就是为了解决这些问题而生的。它们让你把 hooks 配置写在代码仓库里,所有人共享同一份配置,同时提供跨平台支持和更友好的使用体验。

第二章:husky 入门

什么是 husky

husky 是目前最流行的 git hooks 管理工具之一,由 typicode 开发维护。它的设计理念是“让 git hooks 像代码一样管理“——你把 hooks 配置写在项目里,通过 husky 自动安装到 .git/hooks 目录。

husky 的特点是简单直接:它本质上就是一个帮你创建和管理 .git/hooks 目录里那些原始文件的工具。安装后,你可以在项目根目录看到一个 .husky 目录,里面就是你定义的 hooks。

安装 husky

husky 通过 npm(或 yarn/pnpm)安装。先确认你的项目已经初始化了 git 仓库:

Terminal window
git init # 如果还没有初始化的话

然后安装 husky 作为开发依赖:

Terminal window
npm install husky --save-dev

安装完成后,用 husky 官方推荐的初始化命令初始化:

Terminal window
npx husky install

这个命令会做两件事:

  1. 在项目根目录创建 .husky 目录
  2. 修改 git 配置,让 git 知道去 .husky 目录找 hooks

初始化完成后,你每次 git commit,git 就会自动执行 .husky 目录下的 hooks。

配置第一个 pre-commit hook

我们用一个实际场景来演示:让 husky 在每次 commit 前运行 ESLint 检查代码风格。

第一步:确保项目有 ESLint 配置

假设你已经有了 ESLint 配置(如果没有,可以先用 npm init @eslint/config 快速生成一个)。确保 package.json 里有检查脚本:

{
"scripts": {
"lint": "eslint . --ext .js,.ts,.tsx,.jsx"
}
}

第二步:创建 pre-commit hook 文件

使用 husky 的 add 命令创建 hook:

Terminal window
npx husky add .husky/pre-commit "npm run lint"

这会在 .husky/pre-commit 文件里写入:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run lint

解释一下这个文件的内容:

  • #!/usr/bin/env sh:shebang,声明这是一个 shell 脚本
  • . "$(dirname -- "$0")/_/husky.sh":加载 husky 的公共函数库,确保环境变量正确设置
  • npm run lint:你要执行的命令

第三步:提交 .husky 目录

建议把 .husky 目录也提交到 git 仓库,这样团队成员克隆代码后只需要运行 npx husky install 就能自动配置好 hooks:

Terminal window
git add .husky
git commit -m "chore: add husky git hooks"

第四步:测试

现在,随便改一个文件,然后尝试 commit:

Terminal window
echo "console.log('test')" > test.js
git add test.js
git commit -m "test: testing husky"

如果代码通过 ESLint 检查,commit 会成功;如果 ESLint 报错了,commit 会被取消,你就需要先修复问题再重试。

husky 的工作原理

husky 的原理其实很简单:

  1. npx husky install 修改 git 的 core.hooksPath 配置,把 hooks 的查找路径从 .git/hooks 改成 .husky
  2. 你通过 npx husky add 命令在 .husky 目录创建 hook 文件
  3. 当 git 执行到对应的时机时,会去 .husky 目录查找并执行对应的 hook 脚本

这个设计的优点是配置可视化:你可以在 .husky 目录里看到所有定义的 hooks,每个 hook 就是一个单独的文件,非常直观。

husky 的常用命令

Terminal window
# 初始化 husky
npx husky install
# 创建或追加 hook
npx husky add .husky/pre-commit "npm run lint"
# 删除某个 hook
rm .husky/pre-commit
# 查看所有 hooks
ls .husky/

husky 的局限性

husky 简单直观,但它有几个局限:

串行执行:husky 的 hook 脚本是顺序执行的。如果你要跑 ESLint + Prettier + TypeScript 检查,每个任务都要等上一个完成才能开始。

Windows 兼容性问题:husky 依赖 shell 环境,在某些 Windows 环境(尤其是非 Git Bash 环境)可能出现兼容性问题。

单项目适用:husky 适合单个仓库的场景。如果你是 monorepo 结构,需要在多个子项目里配置相同的 hooks,维护成本会变高。

这些问题促成了 lefthook 的诞生。

第三章:lefthook 入门

什么是 lefthook

lefthook 是另一个流行的 git hooks 管理工具,由 Evil Martians 团队开发和维护。它的设计理念是“功能强大的 hooks 管理器“,支持并行执行、灵活的配置文件、远程 hooks 共享等功能。

和 husky 相比,lefthook 的定位更像是一个通用的任务运行器——你可以用它来管理不仅仅是 git hooks,而是各种自动化任务。它的配置文件(lefthook.yml)使用 YAML 格式,比 husky 的文件方式更结构化。

安装 lefthook

lefthook 支持多种安装方式,最常用的是通过 npm 安装:

Terminal window
npm install lefthook --save-dev

或者用其他包管理器:

Terminal window
# Yarn
yarn add lefthook --dev
# pnpm
pnpm add lefthook --save-dev
# 全局安装
npm install -g lefthook

安装完成后,需要在项目里初始化:

Terminal window
npx lefthook install

这个命令会在 .git/hooks 目录里安装 lefthook 的入口脚本。和 husky 不同的是,lefthook 安装后会在 .git/hooks 目录创建对应的 hook 文件(如 pre-commit),这些文件会调用 lefthook 来执行你在配置文件里定义的命令。

配置 lefthook

lefthook 的配置写在项目根目录的 lefthook.yml(或 lefthook.yaml)文件中。

基础配置示例:

lefthook.yml
pre-commit:
parallel: true
commands:
lint:
run: npm run lint
type-check:
run: npm run type-check

这个配置定义了 pre-commit hook,要做的事情:

  • parallel: true:并行执行下面的命令
  • commands:要执行的命令列表(这里的每个子项如 linttype-check 是命令名称,可以自定义)
  • run:要执行的命令(支持 npm/yarn/pnpm 脚本)

第一个完整的 lefthook 配置

让我们用一个完整场景来演示:配置 pre-commit 运行 ESLint 和 Prettier,pre-push 运行单元测试。

第一步:确保 package.json 有对应脚本

{
"scripts": {
"lint": "eslint . --ext .js,.ts,.tsx,.jsx",
"format": "prettier --write .",
"test": "jest"
}
}

第二步:创建 lefthook.yml

在项目根目录创建 lefthook.yml

lefthook.yml
pre-commit:
parallel: true
commands:
lint:
run: npm run lint
env:
- CI=true
format:
run: npx prettier --check {staged_files}
env:
- CI=true
pre-push:
parallel: true
commands:
test:
run: npm run test
env:
- CI=true

这个配置演示了两个常用场景:

  • pre-commit:并行运行 ESLint 代码检查和 Prettier 格式检查,只检查暂存区的文件({staged_files} 变量会传入被暂存的文件路径)
  • pre-push:在 push 前运行单元测试,确保代码在推送前通过所有测试

第三步:测试

现在进行 commit,看看 lefthook 是否正常执行:

Terminal window
echo "console.log('test')" > test.js
git add test.js
git commit -m "feat: add test file"

lefthook 会并行运行 lint 和 format 检查,如果所有检查通过,commit 就会成功。

lefthook 的并行执行

lefthook 的一大亮点是支持并行执行。把 parallel: true 加在 hook 层级,该 hook 下的所有命令会同时运行:

pre-commit:
parallel: true
commands:
lint:
run: npm run lint
type-check:
run: npm run type-check
format:
run: npx prettier --check {staged_files}

如果你有多个耗时的检查任务,并行执行可以大幅缩短总耗时。

lefthook 的变量和过滤器

lefthook 提供了有用的变量来引用 git 上下文:

  • {staged_files}:本次提交暂存的文件列表
  • {all_files}:所有被跟踪的文件
  • {push_files}:即将被推送的文件列表
  • {files}:通过 files 过滤器匹配到的文件
  • {cmd}:当前执行的命令
  • {0}, {1}, {2}…:git hook 的位置参数(如 {1} 是 commit-msg hook 的第一个参数,即提交信息文件路径)
  • {lefthook_job_name}:当前 job 的名称

你还可以在命令级别使用 files 过滤器来限定命令只作用于特定文件:

pre-commit:
commands:
lint:
run: npm run lint
files: src/**/*.{js,ts}

这只会在有 JS/TS 文件被修改时运行 lint。

lefthook 的全局安装跳过

有时候你可能想跳过 hooks(比如在 CI 环境或者临时调试时)。 lefthook 支持通过环境变量跳过:

Terminal window
# 跳过所有 hooks
LFHOOK_SKIP=true git commit -m "temp commit"
# 跳过特定 hook
LFHOOK_SKIP=pre-commit git commit -m "temp commit"

你也可以在 lefthook.yml 里通过 skip 注释让 lefthook 在安装时跳过某个 hook:

lefthook.yml
# skip: pre-commit
pre-commit:
commands:
# ...

lefthook 的远程 hooks 共享

lefthook 支持从远程仓库拉取 hooks 配置,这在 monorepo 场景或者团队共享配置时非常有用:

lefthook.yml
remotes:
- git_url: https://github.com/evilmartians/lefthook-git-hooks
ref: v1.0.0
configs:
- lefthook.yml

你可以指定一个远程仓库的 URL 和配置文件路径,lefthook 会自动下载并合并远程的 hooks 配置。

lefthook vs husky:命令对照

操作 husky lefthook
初始化 npx husky install npx lefthook install
添加 hook npx husky add .husky/pre-commit "cmd" 编辑 lefthook.yml
删除 hook rm .husky/pre-commit 编辑 lefthook.yml
跳过 hooks git commit --no-verify -m "msg" LFHOOK_SKIP=true git commit
查看状态 ls .husky/ cat lefthook.yml

第四章:横向对比

配置复杂度对比

husky:基于文件的配置

husky 的配置是目录式的——每个 hook 就是一个文件。要添加一个新的 pre-commit 检查,你需要:

  1. 运行 npx husky add .husky/pre-commit "command"
  2. 或者手动创建文件并写入内容

优点是配置直观,每个 hook 对应一个文件;缺点是如果 hook 脚本很长,文件会变得臃肿,而且没有结构化的元数据管理。

lefthook:基于 YAML 的配置

lefthook 把所有 hooks 配置集中在一个 lefthook.yml 文件里。你可以在一个文件里管理所有 hooks 的配置,包括并行设置、环境变量、文件过滤器等。

优点是配置集中、结构化、可编程;缺点是初学者可能需要先了解 YAML 语法。

结论:如果你的项目 hook 脚本简单、hook 数量少,husky 的文件式配置更直观;如果项目复杂、hook 数量多,lefthook 的 YAML 配置更易维护。

功能特性对比

特性 husky lefthook
并行执行
配置文件结构化程度 低(文件式) 高(YAML)
Windows 兼容性 ⚠️ 一般 ✅ 更好
monorepo 支持
远程 hooks 共享
交互式终端输出
文件过滤器
社区生态 中等
文档完善程度 完善 完善

性能对比

lefthook 因为支持并行执行,在有多任务场景下会有明显的性能优势。假设你有两个检查任务各需要 5 秒:

husky(串行):5秒 + 5秒 = 10秒

lefthook(并行):max(5秒, 5秒) = 5秒

对于小项目来说这个差异可能不明显,但在大型项目里,多几个检查任务,时间差距就会累积。

生态与社区

husky 是目前最流行的 git hooks 管理工具,拥有更大的社区和更多的使用者。如果你在开发过程中遇到问题,更容易找到解决方案。

lefthook 虽然社区较小,但增长势头不错。它由 Go 语言编写,性能优秀,支持并行执行,也吸引了一批追求效率的开发者。

CI/CD 环境处理

husky:

Terminal window
# 在 CI 中跳过 husky
git commit -m "ci: skip husky" --no-verify

或者在 CI 配置中设置环境变量:

# GitHub Actions
- name: Commit
env:
HUSKY: 0
run: |
git commit -m "ci: skip husky"

lefthook:

Terminal window
# 跳过所有 hooks
LFHOOK_SKIP=true git commit -m "ci: skip hooks"

两个工具都支持在 CI 环境中跳过 hooks,方式都比较优雅。

适用场景总结

场景 推荐工具 原因
小型个人项目 husky 配置简单,够用就好
需要并行执行 lefthook 内置并行支持
monorepo 项目 lefthook 支持远程 hooks 共享和 workspace 配置
团队协作项目 两者皆可 看团队偏好
Windows 为主的项目 lefthook 跨平台兼容更好
追求简单直观 husky 文件式配置一目了然

第五章:实战选型建议

小型个人项目:husky

如果你是个人开发者,项目规模不大,只需要一两个检查任务(比如 ESLint),husky 是更合适的选择

原因:

  • 配置简单,学习成本低
  • 文件式配置直观,不需要理解 YAML
  • 社区大,遇到问题容易搜索到解决方案

典型配置:

Terminal window
npm install husky --save-dev
npx husky install
npx husky add .husky/pre-commit "npm run lint"

团队协作项目:lefthook

如果你是团队开发者,项目有多个检查任务,需要确保团队成员统一配置,lefthook 是更好的选择

原因:

  • YAML 配置可以提交到仓库,确保团队配置一致
  • 并行执行减少等待时间
  • 文件过滤器可以让不同任务只检查相关文件

典型配置:

lefthook.yml
pre-commit:
parallel: true
commands:
lint:
run: npm run lint
files: src/**/*
format:
run: npx prettier --check {staged_files}
files: src/**/*.{js,ts,jsx,tsx}

monorepo 项目:lefthook

如果是 monorepo 结构,有多个子包需要统一管理 hooks,lefthook 是必选

lefthook 支持在根目录配置一个 lefthook.yml 来管理所有子项目的 hooks,也可以通过 remotes 配置从远程仓库拉取共享的 hooks 配置。

典型配置:

lefthook.yml
pre-commit:
parallel: true
commands:
lint:
run: npm run lint
files: packages/*/src/**/*
format:
run: npx prettier --check {staged_files}
files: packages/*/src/**/*

迁移建议

从 husky 迁移到 lefthook:

  1. 先安装 lefthook:npm install lefthook --save-dev
  2. 初始化:npx lefthook install
  3. 根据 .husky 目录里的文件,创建对应的 lefthook.yml 配置
  4. 测试确认工作正常
  5. 移除 husky:npm uninstall husky && rm -rf .husky

从 lefthook 迁移到 husky:

  1. 先安装 husky:npm install husky --save-dev
  2. 初始化:npx husky install
  3. 根据 lefthook.yml 里的配置,创建对应的 .husky hook 文件
  4. 测试确认工作正常
  5. 移除 lefthook:npm uninstall lefthook && rm lefthook.yml

迁移过程中建议在新旧工具并行运行一段时间,确认没有问题后再移除旧工具。

CI/CD 集成

GitHub Actions:

.github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm ci
# lefthook 跳过
- run: LFHOOK_SKIP=true npm run lint

GitLab CI:

.gitlab-ci.yml
lint:
script:
- npm ci
- LFHOOK_SKIP=true npm run lint

第六章:常见问题

Q: husky 的 .husky 目录需要提交到 git 吗?

建议提交.husky 目录包含了所有 hooks 的定义,提交到 git 可以确保团队成员获得一致的配置。新成员克隆代码后只需运行 npx husky install 即可自动配置好 hooks。

Q: lefthook 能和 husky 共存吗?

技术上可以,但强烈不建议。两个工具都会管理 git hooks 安装,可能会产生冲突。如果你同时启用两者,git 可能会执行重复的检查,或者在卸载其中一个时影响另一个。

建议选择一个工具,并坚持使用。

Q: 在 CI 环境中如何跳过 hooks?

husky:

Terminal window
git commit -m "message" --no-verify

或者设置环境变量 HUSKY=0

lefthook:

Terminal window
LFHOOK_SKIP=true git commit -m "message"

建议使用环境变量方式,更加显式和可控。

Q: pre-commit/prek 和 husky/lefthook 有什么区别?

这是两类定位不同的工具:

  • husky 和 lefthook:是 git hooks 管理器。它们帮你创建和管理 .git/hooks 目录下的原始 hook 文件。它们本身不提供任何检查逻辑,只负责运行你指定的命令。

  • pre-commit 和 prek:是 pre-commit 框架。它们定义了一套 hook 配置格式(.pre-commit-config.yaml),你可以从这个生态里拉取现成的 hook 脚本(如 trailing-whitespacecheck-yaml 等)。prek 是 pre-commit 的 Rust 重新实现,性能更好,不需要预装 Python。

简单来说:

  • 如果你只想运行自己写的脚本(ESLint、Jest 等),用 husky 或 lefthook
  • 如果你想利用 pre-commit 生态的现成检查,用 pre-commit 或 prek
  • 两者不是互斥的,在某些场景下可以结合使用

Q: 如何调试 hooks 不工作的问题?

husky:

  1. 检查 .husky 目录是否存在
  2. 检查 hook 文件是否有执行权限:ls -la .husky/pre-commit
  3. 手动运行 hook 脚本看看是否有错误:sh .husky/pre-commit
  4. 检查 git 的 core.hooksPath 配置:git config core.hooksPath

lefthook:

  1. 检查 lefthook.yml 语法是否正确
  2. lefthook run pre-commit 命令手动运行看是否有错误
  3. 添加 lefthook.yml 配置 debug: true 来看详细日志
  4. 检查 {staged_files} 等变量是否正确解析

Q: 如何只在提交某些文件时触发检查?

lefthook:

使用 files 过滤器:

pre-commit:
commands:
lint:
run: npm run lint
files: src/**/*.{js,ts}

husky:

需要自己在 hook 脚本里加逻辑:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# 只检查暂存区中的 src 文件
git diff --cached --name-only --diff-filter=ACM | grep -qE '^src/.*\.(js|ts)$' && npm run lint

Q: 如何让多个命令在 commit 前并行执行?

lefthook: 直接配置 parallel: true

husky: 需要自己在脚本里加后台任务处理,不推荐。遇到这种需求建议换 lefthook。

第七章:结语

好了,我们从 git hooks 的基础概念开始,一路走到 husky 和 lefthook 的配置实战,再到横向对比和选型建议。该到总结的时候了。

核心要点回顾

  1. git hooks 是 git 内置的触发机制,让你在 commit、push 等操作前后插入自定义逻辑
  2. husky 简单直接,文件式配置,适合小型项目和初学者
  3. lefthook 功能更强,支持并行执行、YAML 配置、远程 hooks,适合复杂项目和团队协作
  4. 选择工具看场景:个人项目用 husky,团队项目、monorepo 用 lefthook
  5. CI 环境中记得跳过 hooks,用环境变量比 --no-verify 更优雅

动手实践

纸上得来终觉浅。如果你是第一次接触这两个工具,我强烈建议你新建一个测试项目,把 husky 和 lefthook 都配一遍,亲自感受一下两者的差异。只有实际操作过,才能真正理解哪个更适合你。

资源链接

祝你的代码质量越来越好,提交信息越来越规范,团队协作越来越顺畅!


如果有任何问题或建议,欢迎在评论区留言讨论。