git提交规范

一个好的COMMIT MESSAGE非常重要:

  1. 可以让其它开发人员清楚知道每个commit的变更内容
  2. 可以基于COMMIT MESSAGE进行过滤查找
  3. 基于规范的COMMIT MESSAGE可以生成Change Log
  4. 可以依据某些类型的Commit Message触发构建或者发布流程
  5. 根据提交的类型来确定语义化版本号

业界使用最多的就是Angular规范。其每个提交都是有意义的,并且提交的格式都是规范的,同样可以识别规范,从而帮助我们自动化完成某些工作。就如前面所说的生成Change log等等。

在Angular规范中,COMMIT MESSAGE包含三部分:Header,Body,Footer。格式如下:

<type>[optional scope]: <description>
// 空行
[optional body]
// 空行
[optional footer(s)]

Header是必须的,body和footer可以省略。==两个空行不可以省略,scope必须用()括起来,冒号紧接着前面的内容,冒号后面必须有一个空格==。 一个符合规范的提交如下:

fix($compile): couple of unit tests for IE9
# Please enter the Commit Message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
# ...
 
Older IEs serialize html uppercased, but IE9 does not...
Would be better to expect case insensitive, unfortunately jasmine does
not allow to user regexps for throw expectations.
 
Closes #392
Breaks foo.bar api, foo.baz should be used instead

包括type,subject和可选的scope。

对于type类型,看下图: 对于代码的变更,如果不符合feat、fix、perf、style四种,那么就作为refactor。对于非代码类的变更,如果不符合test、ci、docs,就归于chore。

scope用来说明commit的影响范围,必须是名词,范围不能太小,否则难以维护。

subject是对commit的简短描述,必须以动词开头,使用现在时,第一个字母必须小写,句子的末尾不能够加英文句号

Body

对本次提交的更详细的描述。格式比较自由,但是也要以动词开头,使用现在时。需要包括修改的动机,与上一次提交的改动点。

用来说明本次提交造成的后果。 在实际应用中,Footer通常用来说明不兼容的改动和关闭的issue列表

BREAKING CHANGE: <breaking change summary>
// 空行
<breaking change description + migration instructions>
// 空行
// 空行
Fixes #<issue number>
  • 不兼容的改动:以BREAKING CHANGE开头,跟上不兼容的摘要。其它部分需要说明变动的描述,理由和迁移方法。
  • 关闭issue列表:新建一行,以Closes开头列出,例如Closes #123Closes #123, #456, #789

Revert Commit

这是一种特殊的情况,即当前的commit还原了先前的commit,那么以revert:开头,后面跟着还原的commit的Header。Body必须写为This reverts commit <HASH>,其中HASH就是还原的提交的SHA标志。

例如:

revert: feat(iam-apiserver): add 'Host' option
 
This reverts commit 079360c7cfc830ea8a6e13f4c8b8114febc9b48a.

重要内容

提交频率

不要随意提交commit。随意提交会让COMMIT MESSAGE变得难懂。 提交有两种方式:

  1. 完成一个Buf的修复、开发完一个小功能、开发完一个完整的功能。
  2. 规定一个时间定期提交。例如下班前。

按照上面的工作方式,本地的COMMIT比较多,比较杂乱。因此在把代码远程合并和进行Pull Request之前,执行git rebase -i合并之前的所有commit。

合并提交

将多个commit合并成一个commit。可以让commit记录变得简介有意义 。

完成这个功能使用的就是git rebase命令。用来重写commit历史。

通常使用git rebase -i <commit ID>命令,其中-i参数表示交互的意思,即进入到交互页面,对commit进行一些操作。

会列出给出commit ID之后(不包括,越下面越新)的所有提交,每个commit前有一个操作命令,默认是pick,通过修改其命令,来对此commit执行不同的变更操作。

git rebase支持的变更操作如下:

命令目的
p,pick不对该commit做任何处理
r,reword保留此commit, 但是修改提交信息
e, edit保留此commit,但是rebase时会暂停,允许修改这个commit
s,squash保留此commit,但是会将此commit与上一个commit合并。
f,fixup与squash相同,但是不会保存当前commit的提交信息。
x,exec执行其它shell命令
d,drop删除此commit

通常使用squashfixup来合并提交。 例如使用squash合并

pick 07c5abd Introduce OpenPGP and teach basic usage
s de9b1eb Fix PostChecker::Post#urls
s 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend

会将第二个和第三个提交合并到第一个提交,最终生成两个提交,第一个提交包含三个提交的提交信息。

如果使用fixup来合并:

pick 07c5abd Introduce OpenPGP and teach basic usage
s de9b1eb Fix PostChecker::Post#urls
f 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend

同样会将第二个和第三个提交合并到第一个提交,也会最终产生两个提交,但是第一个提交中的提交信息里,第三行提交会被注释掉。

一个合并提交的完整示例

每次开发一个新功能,通常就新建一个分支,开发完成之后再合并到主分支。

  1. 新建一个分支。
git checkout -b feature/user

此时的提交记录如下:

$ git log --oneline
7157e9e docs(docs): append test line 'update3' to README.md
5a26aa2 docs(docs): append test line 'update2' to README.md
55892fa docs(docs): append test line 'update1' to README.md
89651d4 docs(doc): add README.md
  1. 然后在这个分支下完成功能 在完成功能的期间可能会产生多次提交。例如下列记录
$ git log --oneline
4ee51d6 docs(user): update user/README.md
176ba5d docs(user): update user/README.md
5e829f8 docs(user): add README.md for user
f40929f feat(user): add delete user function
fc70a21 feat(user): add create user function
7157e9e docs(docs): append test line 'update3' to README.md
5a26aa2 docs(docs): append test line 'update2' to README.md
55892fa docs(docs): append test line 'update1' to README.md
89651d4 docs(doc): add README.md

在feature/user分支上产生了5个提交。现在开发完毕,想要将此分支合并到主分支,但是提交记录太多,于是先进行合并。 3. 合并commit 要合并feature/user上的5个提交,就要将commit id设置为要合并的提交的父commit ID,例如合并fc70a21~4ee51d6,git rebase -i的commit id就是7157e9e。

git rebase -i 7157e9e

对需要合并的commit执行squash操作,然后wq保存后,就可以编辑合并后的提交的COMMIT MESSAGE。在wq保存退出后,commit合并结束。 4. 将开发完成的分支合并到主分支上

git checkout master
git merge feature/user

修改Commit Message

对于某次不符合要求的提交,可能需要对其Commit信息进行修改。 可以使用git commit --amend修改最近一次的commit的message。输入此命令,进入交互页面,直接提交即可。 如果要修改某次的提交信息,同样可以git rebase -i命令来修改,不同的是,只需要对需要更改的提交执行r命令,wq后进入交互页面修改提交信息,再次wq操作完成。

修改commit message后,commit id也会发生变化。

如果执行分支操作的时候,当前分支有未提交的代码,那么可以执行git stash暂存工作状态,修改完成后再执行git stash pop恢复工作状态。

Commit Message规范化

既然要求规范化,那么人工都难以避免出错,因此使用工作自动化进行生成和检查Commit Message就是可以预见的。

  • Commit message生成和检查:生成符合Angular规范的Commit message。提交前检查,历史commit message检查。
  • 基于commit message自动生成CHANGELOG 和 语义化版本号。

有下列5中工具来完成这些:

  • commitizen-go:进入交互模式,并根据提示生成Commit Message,然后提交。
  • commit-msg:githooks,指定检查的规则
  • go-gitlint:检查历史提交的Commit Message是否符合Angular规范,可以将此工具添加到CI流程中,确保生成的Commit Message都是符合规范的。
  • gsemver:语义化版本自动生成工具。
  • git-chglog:根据Commit Message生成CHANGELOG。