WinddSnow

Git-Hooks-and-Husky-Commitlint-Conventional-Commits

字数统计: 3.7k阅读时长: 15 min
2026/07/31

第85课:Git Hooks 与 Husky——提交前检查、commitlint、规范提交信息

版本控制是现代软件工程的基石,而 Git Hooks 是嵌入在 Git 工作流中的自动化脚本机制。利用 Git Hooks,团队可以在代码提交、推送、合并等关键节点自动执行检查——如运行 linter、执行测试、校验提交信息格式。然而,原生 Git Hooks 配置繁琐(需手动编写 shell 脚本并放入 .git/hooks 目录),且无法纳入版本控制与团队共享。Husky 解决了这一问题:它将 Git Hooks 的管理抽象为简单的 npm 配置,使 hooks 可以随项目代码一起提交、安装和更新。结合 commitlintConventional Commits 规范,Husky 还能强制团队使用统一的提交信息格式,为自动化版本发布和 CHANGELOG 生成奠定基础。本节课将系统讲解 Git Hooks 的工作原理、Husky 的安装与配置、commitlint 的规则定制,以及一套完整的提交规范工作流。


1. Git Hooks 的工作原理

Git 在执行特定操作(如 commitpushmerge)前后会查找并执行对应名称的脚本文件。这些脚本分为两类:

  • 客户端 Hooks:在本地执行,如 pre-commit(提交前)、prepare-commit-msg(准备提交信息时)、commit-msg(提交信息编辑完成后)、pre-push(推送前)。
  • 服务端 Hooks:在远程仓库执行,如 pre-receive(接收推送前)、post-receive(接收推送后)。

当执行 git commit 时,Git 会在 .git/hooks/ 目录下查找名为 pre-commit 的可执行文件,如果存在则执行它。若脚本以非零状态码退出,提交操作将被中止。这一机制允许开发者在提交前对代码做最后的自动检查。

1.1 原生 Git Hooks 的问题

  • 存放在 .git/hooks/ 目录下,该目录不在版本控制范围内——团队成员无法共享同一套 hooks。
  • 编写和调试 shell 脚本对前端开发者不够友好。
  • 每个项目需要手动复制 hooks 脚本。

Husky 通过 npm 的 prepare 生命周期和 Git 的 core.hooksPath 配置,将 hooks 的管理转移到项目根目录的 .husky/ 文件夹中,从而可以被 Git 跟踪和共享。


2. Husky:现代化的 Git Hooks 管理

2.1 安装与初始化

安装(Husky v9+,当前推荐版本):

1
2
npm install -D husky
npx husky init

npx husky init 会完成两件事:

  • 在项目根目录创建 .husky/ 文件夹,内含一个 pre-commit 示例文件。
  • package.jsonscripts 中添加 "prepare": "husky",确保每次 npm install 后自动启用 Git Hooks。

启用原理prepare 是 npm 的生命周期脚本,会在 npm install(无参数)时自动执行。husky 命令将 Git 的 core.hooksPath 设置为 .husky/,此后 Git 将从该目录读取 hooks 脚本,而非 .git/hooks/

2.2 添加 Hooks

使用 husky 命令行添加新的 hook:

1
2
npx husky add .husky/pre-commit "npm test"
npx husky add .husky/commit-msg "npx commitlint --edit \$1"

或者直接编辑 .husky/ 目录下的文件。Hooks 文件是可执行的 shell 脚本。例如,.husky/pre-commit 内容:

1
2
3
4
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged
  • 第一行指定 shell 解释器。
  • 第二行加载 Husky 的内部辅助函数(可选)。
  • 后续行是实际要执行的命令。

2.3 常用 Hooks 场景

Hook 名称 触发时机 典型用途
pre-commit git commit 之前,提交信息编辑前 运行 lint-staged,检查代码格式、运行单元测试。
commit-msg 提交信息编辑完成后,提交真正发生前 使用 commitlint 校验提交信息是否符合规范。
pre-push git push 之前 运行完整的测试套件,防止推送失败代码。
post-merge git merge 成功后 自动安装依赖(npm install),或通知开发者依赖已变更。

commit-msg hook 中,Git 会将保存提交信息的临时文件路径作为第一个参数传递。commitlint --edit $1 中的 $1 即引用该参数,读取提交信息进行校验。


3. 规范提交信息:Conventional Commits 与 commitlint

3.1 Conventional Commits 规范

Conventional Commits 是一种轻量级的提交信息约定,格式为:

1
2
3
4
5
<type>[optional scope]: <description>

[optional body]

[optional footer]

常用 type

type 说明 示例
feat 新功能。 feat: add user login API
fix Bug 修复。 fix: correct date format in report
docs 文档变更。 docs: update API reference
style 不影响代码含义的格式变更(空格、分号等)。 style: format code with prettier
refactor 代码重构(既非新功能也非 Bug 修复)。 refactor: extract common validation logic
perf 性能优化。 perf: reduce bundle size by 30%
test 添加或修改测试。 test: add unit tests for auth module
chore 构建过程或辅助工具的变更。 chore: update dependency to v2
ci CI/CD 配置变更。 ci: add GitHub Actions workflow
build 影响构建系统或外部依赖的变更。 build: upgrade webpack to v5

规则要点

  • 提交信息的第一行(主题行)不能超过 72 个字符。
  • typedescription 之间用 : (冒号+空格)分隔。
  • description 使用祈使语气(如 add 而非 added)。
  • 末尾不加句号。
  • 破坏性变更(BREAKING CHANGE)可以在 footer 中用 BREAKING CHANGE: description 标记,或直接在 type/scope 后加 !(如 feat!: drop support for Node 12)。

为什么需要规范提交信息?

  • 自动化版本管理:工具(如 semantic-release)可根据提交类型自动确定下一个版本号(feat → 次版本号递增,fix → 修订号递增,BREAKING CHANGE → 主版本号递增)。
  • 自动生成 CHANGELOG:从提交历史中提取 featfix 条目,生成版本发布说明。
  • 提升可读性:任何团队成员或未来的维护者都能快速理解每次提交的意图。

3.2 commitlint:校验提交信息

commitlint 是一个检查提交信息是否符合配置规则的工具,通常与 Husky 的 commit-msg hook 结合使用。

安装

1
npm install -D @commitlint/cli @commitlint/config-conventional

创建配置文件 commitlint.config.js(或 .commitlintrc.js):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'], // 基于 Conventional Commits 的规则
rules: {
// 自定义规则:type 必须是以下枚举之一
'type-enum': [
2, // 错误级别:0=off, 1=warn, 2=error
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'ci', 'build', 'revert']
],
// 主题行最大长度
'header-max-length': [2, 'always', 72],
// 主题行不能为空
'subject-empty': [2, 'never'],
// type 不能为空
'type-empty': [2, 'never'],
},
};

在 Husky 中添加 commit-msg hook

1
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'

或直接编辑 .husky/commit-msg

1
2
3
4
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no -- commitlint --edit $1

--no 参数告诉 npm 不使用已有的 node_modules(可省略,但作为习惯保留),--edit $1 表示读取 Git 传入的提交信息文件并校验。

现在,当执行 git commit -m "add login" 时,commitlint 会报错:

1
2
3
⧗   input: add login
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]

正确的提交:git commit -m "feat: add login page" 将通过校验。


4. 综合实战:搭建完整的提交规范工作流

以下步骤在一个已初始化 Git 的前端项目中完成全部配置:

4.1 安装依赖

1
npm install -D husky lint-staged @commitlint/cli @commitlint/config-conventional

4.2 初始化 Husky

1
npx husky init

4.3 配置 lint-staged

package.json 中添加:

1
2
3
4
5
6
7
8
9
10
11
{
"lint-staged": {
"*.{js,jsx,ts,tsx,vue}": [
"eslint --fix",
"prettier --write"
],
"*.{css,scss,md,json}": [
"prettier --write"
]
}
}

4.4 创建 pre-commit hook

编辑 .husky/pre-commit

1
2
3
4
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

4.5 创建 commit-msg hook

编辑 .husky/commit-msg

1
2
3
4
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no -- commitlint --edit $1

4.6 配置 commitlint

创建 commitlint.config.js

1
2
3
4
5
6
7
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'ci', 'build', 'revert']],
'header-max-length': [2, 'always', 72],
},
};

4.7 验证

1
2
3
git add .
git commit -m "foo bar" # ❌ 被 commitlint 拦截
git commit -m "feat: add auth" # ✅ 通过校验

5. 最佳实践与常见陷阱

5.1 使用 git commit --no-verify 跳过 Hooks

在某些紧急情况下(如快速修复后立即部署),可以使用 --no-verify 跳过 pre-commit 和 commit-msg hooks。但这不应成为习惯——跳过检查意味着将未验证的代码推送到仓库。

5.2 Husky 在 CI 环境中的行为

在 CI 环境中,prepare 脚本可能不会运行(取决于 NODE_ENV 和包管理器)。可以通过环境变量 HUSKY=0 禁用 Husky:

1
HUSKY=0 npm install

大多数 CI 平台(如 GitHub Actions、GitLab CI)会自动设置 CI=true,Husky 会检测该变量并跳过安装。

5.3 commitlint 规则不能过于严苛

规则是服务于团队的。如果提交类型列表过于严格(仅允许 3 种 type),会导致开发者在紧急提交时频繁使用 --no-verify。建议保留常用的 6-8 个 type,允许团队在实际需要时扩展。

5.4 Husky 目录必须被 Git 跟踪

确保 .husky/ 目录被提交到 Git 仓库(不要加入 .gitignore),否则其他团队成员在克隆仓库后无法启用 hooks。


课后练习

一、概念自测(选择题 / 填空题)

  1. (单选) Git Hooks 中,pre-commit hook 的触发时机是?
    A. 提交信息编辑完成后,提交真正发生前。
    B. 执行 git commit 命令后,提交信息编辑器打开之前。
    C. 代码推送到远程仓库后。
    D. 合并请求完成后。

  2. (单选) Husky 的主要作用是什么?
    A. 自动生成 CHANGELOG。
    B. 将 Git Hooks 的配置纳入项目版本控制,简化 hook 管理。
    C. 代替 ESLint 进行代码检查。
    D. 生成提交信息的统计报告。

  3. (填空) Conventional Commits 规范中,新增一个登录功能,正确的提交信息格式为:______: add login functionality

  4. (多选) 以下哪些属于 Conventional Commits 推荐的 type
    A. feat
    B. bug
    C. docs
    D. chore

二、AI 编程任务:编写面向 AI 的提示词

场景:你需要为一个已有的 Vue 3 项目配置 Husky + commitlint + lint-staged 工作流。要求如下:

  • 安装所需依赖:huskylint-staged@commitlint/cli@commitlint/config-conventional
  • 初始化 Husky,创建 .husky/pre-commit(运行 npx lint-staged)和 .husky/commit-msg(运行 npx commitlint --edit $1)。
  • 配置 lint-staged:对 .js/.ts/.vue 文件运行 eslint --fixprettier --write,对 .css/.scss/.md 运行 prettier --write
  • 创建 commitlint.config.js,继承 @commitlint/config-conventional,并自定义 header-max-length: 100
  • package.json 中添加 "prepare": "husky" 脚本。

任务要求:请写出一段完整的中文提示词,发送给 AI,使其生成所有需要的配置文件(package.json 相关字段、.husky/pre-commit.husky/commit-msgcommitlint.config.js)。提示词中需明确指定每个 hook 的命令和 lint-staged 的匹配规则。

三、Agent 模式下的提示词示例

你是一个资深前端开发 Agent。请为一个 Vue 3 项目配置 Git Hooks 工作流。需要生成/修改以下内容:

  1. 安装 huskylint-staged@commitlint/cli@commitlint/config-conventional 为 devDependencies。
  2. 运行 npx husky init 初始化(或直接在 .husky/ 目录下生成文件)。
  3. .husky/pre-commit:内容为 npx lint-staged
  4. .husky/commit-msg:内容为 npx --no -- commitlint --edit $1
  5. package.json 中添加 "lint-staged" 字段("*.{js,ts,vue}": ["eslint --fix", "prettier --write"]"*.{css,scss,md}": ["prettier --write"])和 "scripts": { "prepare": "husky" }
  6. commitlint.config.jsextends: ['@commitlint/config-conventional']rules: { 'header-max-length': [2, 'always', 100] }
  7. README.md 中简要说明提交规范和如何跳过 hook(git commit --no-verify)。
    所有文件添加必要注释。完成后列出所有生成的文件内容。

四、面试真题与参考答案

题目(滴滴前端面试题):

请解释 Git Hooks 和 Husky 的关系,以及为什么在现代前端工程中推荐使用 Husky 而非手动编写 Git Hooks?结合 commitlint 说明如何通过 commit-msg hook 强制执行提交信息规范。如果开发者使用 git commit --no-verify 跳过了 hook,团队应如何看待和处理?

参考答案

Git Hooks 是 Git 提供的自动化脚本机制,可以在特定 Git 操作前后自动执行。Husky 是基于 Git Hooks 的封装工具,它将 hooks 脚本从 .git/hooks/ 迁移到项目根目录的 .husky/ 文件夹中,使其可以被 Git 版本控制,团队成员克隆仓库后通过 npm install 自动启用。与手动编写 shell 脚本相比,Husky 配置简单、可共享、与 Node.js 生态无缝集成。

通过 commit-msg hook 配合 commitlint,可以在提交信息保存后、提交真正发生前执行规则校验。如果提交信息不符合 Conventional Commits 格式(如缺少 type、type 不在允许列表中),commitlint 以非零状态码退出,Git 会中止提交,并输出明确的错误提示,引导开发者修正提交信息。

git commit --no-verify 是一个合理的应急逃生口(如紧急修复需要立即部署),但它绕过了所有检查,可能导致不符合规范的代码或提交信息进入仓库。团队应在分支保护规则中在远程端设置二次验证(如 GitHub Branch Protection Rules 或 CI 流水线检查提交信息),并定期审计使用 --no-verify 的提交记录。对于频繁滥用的情况,应进行团队规范和自动化工具使用的宣导,而非简单地禁用该选项。


课后练习答案

一、概念自测答案

  1. B

    • 解析:pre-commitgit commit 执行后、提交信息编辑器打开前触发,用于检查暂存区代码。commit-msg 是提交信息编辑完成后触发(A 描述的是 commit-msg)。
  2. B

    • 解析:Husky 将 Git Hooks 的配置纳入项目仓库,通过 npm scripts 自动启用和管理,解决原生 hooks 难以版本控制和共享的问题。A 是 semantic-release 等工具的职责;C、D 错误。
  3. feat

    • 解析:Conventional Commits 中,新功能使用 feat 类型。
  4. A、C、D

    • 解析:featdocschore 都是标准 type。bug 不是,应该用 fix

二、AI 编程任务参考答案(提示词示例)

示例提示词
“请为我的 Vue 3 项目配置 Husky + commitlint + lint-staged。要求:

  • 安装 huskylint-staged@commitlint/cli@commitlint/config-conventional
  • husky init 后生成 .husky/pre-commit(内容 npx lint-staged)和 .husky/commit-msg(内容 npx --no -- commitlint --edit $1)。
  • package.json 添加 "lint-staged" 配置(JS/TS/Vue 文件运行 eslint --fix + prettier --write,CSS/SCSS/MD 文件运行 prettier --write),添加 "prepare": "husky"
  • commitlint.config.js 继承 @commitlint/config-conventional,设置 header-max-length: 100
  • 直接输出所有配置文件的内容,附简要注释。”
CATALOG
  1. 1. 第85课:Git Hooks 与 Husky——提交前检查、commitlint、规范提交信息
    1. 1.1. 1. Git Hooks 的工作原理
      1. 1.1.1. 1.1 原生 Git Hooks 的问题
    2. 1.2. 2. Husky:现代化的 Git Hooks 管理
      1. 1.2.1. 2.1 安装与初始化
      2. 1.2.2. 2.2 添加 Hooks
      3. 1.2.3. 2.3 常用 Hooks 场景
    3. 1.3. 3. 规范提交信息:Conventional Commits 与 commitlint
      1. 1.3.1. 3.1 Conventional Commits 规范
      2. 1.3.2. 3.2 commitlint:校验提交信息
    4. 1.4. 4. 综合实战:搭建完整的提交规范工作流
      1. 1.4.1. 4.1 安装依赖
      2. 1.4.2. 4.2 初始化 Husky
      3. 1.4.3. 4.3 配置 lint-staged
      4. 1.4.4. 4.4 创建 pre-commit hook
      5. 1.4.5. 4.5 创建 commit-msg hook
      6. 1.4.6. 4.6 配置 commitlint
      7. 1.4.7. 4.7 验证
    5. 1.5. 5. 最佳实践与常见陷阱
      1. 1.5.1. 5.1 使用 git commit --no-verify 跳过 Hooks
      2. 1.5.2. 5.2 Husky 在 CI 环境中的行为
      3. 1.5.3. 5.3 commitlint 规则不能过于严苛
      4. 1.5.4. 5.4 Husky 目录必须被 Git 跟踪
    6. 1.6. 课后练习
      1. 1.6.1. 一、概念自测(选择题 / 填空题)
      2. 1.6.2. 二、AI 编程任务:编写面向 AI 的提示词
      3. 1.6.3. 三、Agent 模式下的提示词示例
      4. 1.6.4. 四、面试真题与参考答案
    7. 1.7. 课后练习答案
      1. 1.7.1. 一、概念自测答案
      2. 1.7.2. 二、AI 编程任务参考答案(提示词示例)