DTeam 团队日志

Doer、Delivery、Dream

规范化git commit信息

冯宇 Posted at — Apr 25, 2019 阅读

前言

在 git 的使用中,一种最佳实践是使用格式化的 commit 信息,这样方便自动化工具进行处理,可以快速生成 Release Notes,甚至可以直接对接 CI 工具进行更进一步的规范化发布流程。那么如何规范化 git commit 信息呢?本文将重点讨论这个。

2020-05-21 更新:为了更加轻量化,使用git-cz替换掉原来主要推荐的commitlizen部分,在最后添加了一个小结部分概览,方便快速在本地开发环境速查命令及使用方式。

一个最基本的 git commit 最佳实践

Git-Commit-Best-Practices这个项目总结了一个最基本的 git commit 实践:

而针对其中的Formatting Rules部分,我们将详细讲解下 Angular 的 git commit 规范格式。

Angular 项目的 git commit 规范

首先我们先了解一下Angular项目如何规范化自己的 commit 信息的吧。

Angular 项目可以说是业界最广为流传的 git commit 最佳实践的项目。Angular 的贡献要求必须 git commit 符合自己定义的模板。先来看看 Angular 的 commit 记录长什么样子吧: https://github.com/angular/angular/commits/master

image.png

这样的话 Angular 项目组可以很方便的生成Release Notes

image.png

Angular 的 git commit 实践

完整的 Angular 的 commit 教程参见的 CONTRIBUTING: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-guidelines

Angular 定义的 commit 基本格式如下:

<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

除了第一行的 Header 部分必填外,其余均可选。注意 Header, Body, Footer 中间有空白行分割。

image.png

Header 部分只有一行,包括三个字段:type(必需)、scope(可选)和subject(必需)。

type用于说明 commit 的类别,只允许使用下面 7 个标识:

通常featfix会被放入 changelog 中,其他(docschorestylerefactortest)通常不会放入 changelog 中。

scope用于说明 commit 影响的范围,可选值。通常是文件、路径、功能等。对于 Angular 项目已经定死了 scope: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#scope

subject是 commit 目的的简短描述,不超过 50 个字符。如用英语表述:

Body部分是对本次 commit 的详细描述,可以分成多行。可选项。范例:

More detailed explanatory text, if necessary. Wrap it to
about 72 characters or so.

Further paragraphs come after blank lines.

- Bullet points are okay, too
- Use a hanging indent

Footer 部分只用于两种情况:

Break Changes 范例:

BREAKING CHANGE: isolate scope bindings definition has changed.

    To migrate the code follow the example below:

    Before:

    scope: {
      myAttr: 'attribute',
    }

    After:

    scope: {
      myAttr: '@',
    }

    The removed `inject` wasn't generaly useful for directives so there should be no code using it.

Closes 范例:

Closes #123, #245, #992

自动化工具处理 git commit

手写上述的 commit 格式很明显并不怎么方便,而且还有出错的可能性(尽管 git 支持template功能,但是实际使用的时候仍然不是特别方便),为什么不交给自动化工具完成呢?

下面就是重点要介绍的规范化 git commit 消息的工具git-cz

git-cz是一个简化版的commitizen+cz-conventional-changelog组合,提供了开箱即用的功能,默认使用Angular规范,默认模板不填写scope部分内容。

NOTE: Windows 环境下你需要使用cmdpowershell运行交互式终端,在cygwin/mingw32/msys2等模拟 posix 运行环境下无法正常执行交互式终端菜单。

NOTE: git-cz非常轻量,只提供 Angular 规范的支持。如果你想使用其他规范的 commit,请使用commitizen配合对应的Adapter

开始使用

对于 NodeJS 项目,commitizen 可以将自己的一些脚本添加到package.json中,方便npm script生命周期管理。

快速将一个项目初始化为 commitizen 友好的项目(需要确保当前工程中必须存在package.json文件):

# 将git-cz命令行安装到全局
npm install -g git-cz

# 也可以使用npx命令直接运行
npx git-cz

此时git czgit-cz命令将出现类似于下面的交互式终端:

cz-cli@3.1.1, cz-conventional-changelog@2.1.0


Line 1 will be cropped at 100 characters. All other lines will be wrapped after 100 characters.

? Select the type of change that you're committing: (Use arrow keys)
❯ feat:     A new feature
  fix:      A bug fix
  docs:     Documentation only changes
  style:    Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
  refactor: A code change that neither fixes a bug nor adds a feature
  perf:     A code change that improves performance
  test:     Adding missing tests or correcting existing tests
(Move up and down to reveal more choices)

image

按照之前的 Angular commit 规范格式交互式输入信息即可,是不是比手写方便了许多?

以后,只要是需要git commit的地方,通通替换为git czgit-cz即可这样交互式的输入符合 Angular commit 规范的 git log 了。其余 commit 的参数也兼容,比如-a, --amend等等。

自动检测 commit 是否符合规范

尽管我们可以在 CI 测试阶段检测 commit 是否符合这个规范,但是能在本地就有反馈不是更快吗?因此我们可以考虑在git commithook中加入commitlint检测,不符合 commit 规范的提交在本地就无法提交进去。

对于 NodeJS 项目来说,有个非常简单的使用 git hook 的项目husky,使用者无需手工定义繁琐的 git hook 脚本,直接在package.json中定义要执行的 hook 命令即可。

在项目依赖中添加husky:

npm i -D husky

然后在package.json配置中添加husky的 git hook 配置:

{
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -x @commitlint/config-conventional -E HUSKY_GIT_PARAMS"
    }
  }
}

然后安装@commitlint/cli@commitlint/config-conventional这两个包(建议安装到全局,这样所有项目都可以用):

npm install -g @commitlint/cli @commitlint/config-conventional

这样如果使用普通的 git commit 提交了不符合 commit 规范的消息,就会被直接打回:

 git commit -a -m '添加husky和commitlint依赖'
husky > commit-msg (node v10.15.3)

⧗   input: 添加husky和commitlint依赖
✖   subject may not be empty [subject-empty]type may not be empty [type-empty]
✖   found 2 problems, 0 warnings
    (Need help? -> https://github.com/conventional-changelog/commitlint#what-is-commitlint )


husky > commit-msg hook failed (add --no-verify to bypass)

可以看到git commit触发了husky的 hook,告诉用户这条 commit 记录不符合 Angular 规范。

再次使用git cz提交一次:

 git cz -a
cz-cli@3.1.1, cz-conventional-changelog@2.1.0


Line 1 will be cropped at 100 characters. All other lines will be wrapped after 100 characters.

? Select the type of change that you're committing: build:    Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
? What is the scope of this change (e.g. component or file name)? (press enter to skip)
 package.json
? Write a short, imperative tense description of the change:
 添加husky和commitlint提交前检测
? Provide a longer description of the change: (press enter to skip)

? Are there any breaking changes? No
? Does this change affect any open issues? No

husky > commit-msg (node v10.15.3)

⧗   input: build(package.json): 添加husky和commitlint提交前检测
✔   found 0 problems, 0 warnings
    (Need help? -> https://github.com/conventional-changelog/commitlint#what-is-commitlint )


[master bd79828] build(package.json): 添加husky和commitlint提交前检测
 2 files changed, 1357 insertions(+), 7 deletions(-)

符合 commit 规范的提交可以成功提交到版本库中。

NOTE: 巧妙合理利用 husky 的 hook 功能可以大大规范化开发。比如在pre-commit 这个 hook 中加入各种 lint(eslink, tslink, jslint 等)检测,大大提高代码规范化效率,减少 BUG 产生。

对于非 NodeJS 项目,可以将commitlint安装为全局的命令行工具独立使用。

npm install -g @commitlint/config-conventional @commitlint/cli

每次 commit 之后可以使用下述命令检测:

commitlint -x '@commitlint/config-conventional' -e

-e/--edit参数代表读取 git commit 最后一条记录。如果希望检测最近几条 commit 记录,可以用:

commitlint -x '@commitlint/config-conventional' -f HEAD~1

-f/--from参数可以指明从哪一条 commit 记录开始检测,还可以配合-t/--to参数检测一个 commit 区间段。

NOTE: 如果不介意非 NodeJS 项目下多一堆 NodeJS 项目相关的配置文件,也可以在npm init初始化成 nodejs 项目之后走上述 NodeJS 项目的配置流程。

版本发布

重头戏来了。规范化 commit 记录的作用就是为了方便我们知道每次发布之后到底改了什么内容。利用conventional-changelog这个工具可以很方便的帮我们产生 changelog。

npm install -g conventional-changelog-cli

如果之前每次 commit 都使用规范化的 commit 提交,那么使用:

conventional-changelog -p angular

应该看到这样的 markdown:

# (2019-04-25)

### Bug Fixes

- **third.md:** 添加新的 third.md 文件,添加一个新功能 719c542

### Features

- **new.md:** 添加新的功能项 43d6584
- **README.md:** 初始化项目工程 69c6c9f

### BREAKING CHANGES

- **new.md:** 这个功能打破了一个函数。before: old, new: new

这就是一个基本的CHANGELOG.md雏形,你可以自己复制到CHANGELOG.md并进行相应的修改。也可以直接输出到CHANGELOG.md文件中:

conventional-changelog -i CHANGELOG.md -s -p angular

终端中看到的内容将输出到CHANGELOG.md文件。再次使用上述命令可以将新的 change log 追加到文件中。可以追加-r 0参数代表将 commit 记录从头至尾全部生成 changelog。

更自动化的发布方式 standard-version

conventional-changelog的官方文档中,官方更鼓励使用更上层的工具standard-version来产生CHANGELOG.mdconventional-changelog可以帮助我们快速检查要生成的 CHANGELOG.md 的格式是否符合期望,而standard-version可以自动帮助我们做以下几件事情:

首先安装 standard-version 到全局命令行:

npm i -g standard-version

执行下standard-version,将看到类似于下面这样的输出:

 standard-version
✔ created CHANGELOG.md
✔ outputting changes to CHANGELOG.md
✔ committing CHANGELOG.md
✔ tagging release v2.0.0
ℹ Run `git push --follow-tags origin master && npm publish` to publish

可以非常清楚的从终端上看到standard-version做了哪些事情。检查 git log 可以看到新增了一条 commit 记录:

commit cac4b5cda4f0c2a78928d8306c5c2eab8c590f02 (HEAD -> master, tag: v2.0.0)
Author: Your Name <you@example.com>
Date:   Thu Apr 25 17:15:56 2019 +0800

    chore(release): 2.0.0

项目中也生成了一个CHANGELOG.md文件:

# Change Log

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

# (2019-04-25)

### Bug Fixes

- **third.md:** 添加新的 third.md 文件,添加一个新功能 719c542

### Features

- **new.md:** 添加新的功能项 43d6584
- **other.md:** 新增一个 other.md 文件,记录了新的内容 1a204d9
- **README.md:** 初始化项目工程 69c6c9f

### BREAKING CHANGES

- **new.md:** 这个功能打破了一个函数。before: old, new: new

standard-version 一些基本用法

直接跟空参运行的行为我们已经看到了,一些基本参数介绍下:

CI 发布

直接结合semantic-release,参考我们的文章

CI 集成

在 gitlab ci 中运行以下命令检测当前提交是否符合 conventional-changelog 规范:

npx -p "@commitlint/cli" -p "@commitlint/config-conventional" -p "commitlint-format-junit" commitlint -x @commitlint/config-conventional -o commitlint-format-junit -f ${CI_COMMIT_BEFORE_SHA} > commitlint_result.xml

将 lint result 输出为 JUnit 格式,方便 Gitlab 在 merge request 的时候展示 lint 失败的结果。

vscode 用户

可以安装vscode-commitizen插件,使用ctrl+shift+pcommand+shift+p使用conventional commit提交代码。

image.png

image.png

小结

这里快速总结一下本文的工具部分的安装和使用

基本安装和使用

# 安装git-cz包
npm install -g git-cz

# 以后所有使用git commit的地方都用git-cz或git cz命令提交代码
git cz [-a] [--amend] [...]

commitlint 验证是否符合规范

# 安装commitlint命令行和验证使用的规则config-conventional
npm install -g @commitlint/config-conventional @commitlint/cli

# 添加全局默认配置,以后commitlint命令都不用加 -x "@commitlint/config-conventional"
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > ~/commitlint.config.js

# 验证最新一条提交记录(必须添加上述配置,否则需要加上 -x "@commitlint/config-conventional")
commitlint -e

git hook 自动验证

NodeJS 项目直接使用 husky:

npm install -D husky

然后在package.json添加 husky 配置:

{
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -x @commitlint/config-conventional -E HUSKY_GIT_PARAMS"
    }
  }
}

其他项目可以手工添加 git hook。

参考资料