Skip to content

使用 Husky + Commitlint 规范 Git 提交信息

前言

优雅的 Git 提交信息,可以很直观表达仓库的变更,方便团队协作、问题追溯。并且直接可以根据 git log产出 CHANGELOG

怎么样才能算是优雅的 Git 提交信息呢?

「Conventional Commits」团队为我们提供了约定式提交规范,一种用于给提交信息增加人机可读含义的规范。该规范很大程度上基于 Angular Commit Guidelines

当然,光有规范肯定不行,规范是死的,人是活的,如果没有工具约束,规范就是白谈。

以下是关于使用相关工具规范 Git 提交信息的教程。

Husky 使用

关于 Git 钩子

Git 本身就有钩子(hooks)功能,能在特定的重要动作发生时触发自定义脚本。钩子都被存储在 Git 目录下的 hooks 子目录中,即项目根目录的 .git/hooks 中。

但是 Git 的钩子,配置比较繁琐,并且我们需要用到的比如 pre-commit钩子,属于客户端钩子,并不会同步到其他协作人员的 .git目录中。

Husky 的出现,可以让我们很方便地配置 Git 钩子,解决了 Git 原生钩子配置繁琐、协作人员之间不能同步的问题。

以下关于 Husky 的讲解,都是 5.0 及以后的版本。5.0 及以后的版本,初始化后,会在项目根目录下生成 .husky 目录,可以在其中添加自定义钩子配置文件。

安装

npm install husky -D

设置 NPM 的 prepare脚本

拓展知识:prepare 脚本属于生命周期脚本,如果你配置了该脚本,那么在你每次 npm install 的时候,prepare 脚本都会自动执行,当然也可以手动调用:npm run prepare。你可以查阅官方文档,了解其他的生命周期脚本,以及会触发生命周期脚本的命令。

# 写入 prepare 脚本到 package.json
npm set-script prepare "husky install"

# 手动运行一次
npm run prepare

每次运行 npm run prepare,都会在 .husky/_下生成 husky.sh.gitignore两个文件,如果原本有的话,会被覆盖。

.husky 下的自定义钩子文件,都需要引入 .husky/_/husky.sh 脚本,由于 .husky/_ 下的两个文件没有被 Git 管理,有时候切换分支时,.husky/_ 下的文件会丢失,导致 git commit 时报错,这时只需重新执行下 npm run prepare

添加钩子

添加两个钩子:pre-commitcommit-msg,后面需要用到。

npx husky add .husky/pre-commit ""
npx husky add .husky/commit-msg ""

Commitlint 使用

上面我们安装了 Husky,并且添加了两个钩子,但此时还不能约束开发人员的 Git 提交信息。我们需要借助 Commitlint 来辅助校验提交信息。

安装

npm install @commitlint/cli @commitlint/config-conventional -D
  • @commitlint/cli 是 Commitlint 命令行工具本体
  • @commitlint/config-conventional 是基于「约定式提交规范」的配置文件

配置使用「约定式提交规范」

echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

在 Husky 的 commit-msg 钩子中使用

  • 可以通过脚本写入到 .husky/commit-msg
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
  • 也可以直接在 .husky/commit-msg 中添加
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

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

测试

git add .

# 应该失败
git commit -m "测试"

# 应该成功
git commit -m "chore: 使用 Husky + Commitlint 规范 Git 提交信息"

lint-staged 使用

lint-staged 可以配合 Husky 的 pre-commit 钩子,在 Git 提交之前,对暂存的文件运行 linters 校验。

安装

官方推荐的方式是运行 npx mrm@2 lint-staged,默认会把配置写入到 package.json,不过我不喜欢,我选择手动安装。

npm i lint-staged -D

在 Husky 的 pre-commit 钩子中使用

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged

配置

lint-staged 默认会对所有暂存的文件进行 lint。我们可以通过配置文件缩小文件范围,以及配置 lint 的方式。

在根目录下创建 .lintstagedrc 配置文件,我常用的配置如下:

{
  "src/**/*.{js,jsx,md,ts,tsx,json,vue,css}": [
    "prettier --write",
    "eslint --fix"
  ]
}

更多配置可以参考官方文档

熟悉「约定式提交规范」

上面配置了一通插件,也是时候了解下,该怎么样书写规范的 Git 提交信息了。

语法

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

# 译文
<类型>[可选 范围]: <描述>

[可选 正文]

[可选 脚注]
  1. 类型和描述必填,其他可选。
  2. 类型包括(可以参考 Angular 约定):
    • build:影响构建系统或外部依赖关系的更改(示例范围:gulp、Brocoli、npm)
    • ci:对 CI 配置文件和脚本的更改(示例范围:Travis、Circle、BrowserStack、SauceLabs)
    • docs:仅文档的更改
    • feat:新功能开发
    • fix:bug 修复
    • perf:提高性能的代码更改(性能优化)
    • refactor:既不修复错误也不添加功能的代码更改(代码重构)
    • style:不影响代码含义的更改(空白、格式、缺少分号等)
    • test:添加缺失的测试代码或修改现有的测试代码
  3. 范围在 Commitlint 的拓展下,可以支持多个,用 /\, 分割。
  4. 正文可以多行,每行正文都是一段描述性的句子,每行正文前后都需要有空行。
  5. 脚注可以多行,脚注和脚注之前不需要空行。脚注语法:
<Word-token>:<space><value>
<Word-token><space>#<value>

示例

1. 不包含正文的提交说明

docs: 更新 README.md

2. 包含范围的提交说明

feat(userManage): 开发用户管理模块

包含了 ! 字符以提醒注意破坏性变更的提交说明

feat!: 产品发货时向客户发送邮件

包含了范围和破坏性变更 ! 的提交說明

feat(api)!: 产品发货时向客户发送邮件

包含了描述并且脚注中有破坏性变更的提交说明

feat: 允许默认的配置对象扩展其他配置

BREAKING CHANGE: 配置文件中的 `extends` 现在用于扩展其他配置文件

包含了 ! 和 BREAKING CHANGE 脚注的提交说明

chore!: 放弃对 Node 6 的支持

BREAKING CHANGE: 使用的 JavaScript 功能将不再支持 Node 6

包含多行正文和多行脚注的提交说明

fix: 防止请求打架

为当前请求设置请求 ID 和一个唯一标志。取消掉当前请求之前的请求响应。

删除之前用于缓解请求打架问题设置的超时器,现在已经不需要了。

Reviewed-by: Wang
Refs: #demo

Commitizen 安装使用

如果你不能清楚的记得不同的类型对应的含义,又不想频繁翻阅文档,可以选择安装 Commitizen。

初始化

如果你使用的是 **npm 5.2+,**可以直接使用 npx,不需要全局安装 Commitizen。如果你使用的是 **npm 5.2 **以下的版本,请自行查阅官方文档

运行以下命令进行初始化:

npx commitizen init cz-conventional-changelog --save-dev --save-exact

配置

上一步的初始化操作,会帮我们安装 cz-conventional-changelog,并且在 package.json 中写入 config 配置:

"config": {
  "commitizen": {
    "path": "./node_modules/cz-conventional-changelog"
  }
}

但是我不喜欢把配置信息放在 package.json 中,显得杂乱,我选择把配置剥离。

在根目录下创建 .czrc 文件,在其中写入同样的配置,并把 package.json 中的 config 删除。

{
  "path": "./node_modules/cz-conventional-changelog"
}

最后在 package.json 配置一个命令,避免每次都要敲 npx cz 这样不太熟悉的命令:

"scripts": {
  "commit": "cz"
}

使用

# 先暂存需要提交的文件
git add xxx
# or 暂存所有
git add .

# 启动 Commitizen
npm run commit

你会看到以下命令行引导:

只需要跟着引导选择、输入、回车。


以上,希望对你有帮助。