Git 使用规范
一、user.name 配置
Git 的 user.name 配置一定要使用自己的中文名称。命令行操作方式如下:
git config --global user.name "自己的中文名字"
二、Commit message 的作用
Git 每次提交代码,都要写 commit message(提交说明),否则就不允许提交。
$ git commit -m "hello world"
上面代码的 -m
参数,就是用来指定 commit mesage 的。
如果一行不够,可以只执行 git commit
,就会跳出文本编译器,让你写多行。
$ git commit
但是,一般来说,commit message 应该清晰明了,说明本次提交的目的。
目前,社区有多种 Commit message 的写法规范。本文介绍 Angular 规范(见上图),这是目前使用最广的写法,比较合理和系统化,并且有配套的工具。
格式化的 Commit message,有几个好处。
(1)提供更多的历史信息,方便快速浏览。
比如,下面的命令显示上次发布后的变动,每个 commit 占据一行。你只看行首,就知道某次 commit 的目的。
$ git log HEAD --pretty=format:%s
(2)可以过滤某些 commit(比如文档改动),便于快速查找信息。
比如,下面的命令仅仅显示本次发布新增加的功能。
$ git log HEAD --grep feature
(3)可以直接从 commit 生成 Change log。
Change Log 是发布新版本时,用来说明与上一个版本差异的文档,详见后文。
三、Commit message 的格式
每次提交,Commit message 都包括三个部分:Header,Body 和 Footer。
():
// 空一行
// 空一行
其中,Header 是必需的,Body 和 Footer 可以省略。
不管是哪一个部分,任何一行都不得超过 72 个字符(或 100 个字符)。这是为了避免自动换行影响美观。
3.1 Header
Header 部分只有一行,包括三个字段:type
(必需)、scope
(可选)和subject
(必需)。
(1)type
type
用于说明 commit 的类别,只允许使用下面 7 个标识。
- feat:新功能(feature)
- fix:修补bug
- docs:文档(documentation)
- style: 格式(不影响代码运行的变动)
- refactor:重构(即不是新增功能,也不是修改 bug 的代码变动)
- test:增加测试
- chore:构建过程或辅助工具的变动
如果 type
为 feat
和 fix
,则该 commit 将肯定出现在 Change log 之中。其他情况(docs
、chore
、style
、refactor
、test
)由你决定,要不要放入 Change log,建议是不要。
(2)scope
scope
用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。
(3)subject
subject
是 commit 目的的简短描述,不超过 50 个字符。
- 以动词开头,使用第一人称现在时,比如
change
,而不是changed
或changes
- 第一个字母小写
- 结尾不加句号(
.
)
3.2 Body
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
有两个注意点。
(1)使用第一人称现在时,比如使用 change
而不是 changed
或 changes
。
(2)应该说明代码变动的动机,以及与以前行为的对比。
3.3 Footer
Footer 部分只用于两种情况。
(1)不兼容变动
如果当前代码与上一个版本不兼容,则 Footer 部分以 BREAKING CHANGE
开头,后面是对变动的描述、以及变动理由和迁移方法。
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.
(2)关联 Redmine 上的 Issue
如果当前 commit 针对某个 issue,那么应该在 Header 部分关联这个 issue。
git commit -m "commit message (refs: #234)"
也可以一次关联多个 issue。
git commit -m "commit message (refs: #123, #245, #992)"
3.4 Revert
还有一种特殊情况,如果当前 commit 用于撤销以前的 commit,则必须以 revert:
开头,后面跟着被撤销 Commit 的 Header。
revert: feat(pencil): add 'graphiteWidth' option
This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
Body 部分的格式是固定的,必须写成 This reverts commit
,其中的 hash
是被撤销 commit 的 SHA 标识符。
如果当前 commit 与被撤销的 commit,在同一个发布(release)里面,那么它们都不会出现在 Change log 里面。如果两者在不同的发布,那么当前 commit,会出现在 Change log 的 Reverts
小标题下面。
四、Commitizen
Commitizen 是一个撰写合格 Commit message 的工具。
安装命令如下。
$ npm install -g commitizen
然后,在项目目录里,运行下面的命令,使其支持 Angular 的 Commit message 格式。
$ commitizen init cz-conventional-changelog --save --save-exact
以后,凡是用到 git commit
命令,一律改为使用 git cz
。这时,就会出现选项,用来生成符合格式的 Commit message。
五、validate-commit-msg
validate-commit-msg 用于检查 Node 项目的 Commit message 是否符合格式。
它的安装是手动的。首先,拷贝下面这个 JS 文件,放入你的代码库。文件名可以取为 validate-commit-msg.js
。
接着,把这个脚本加入 Git 的 hook。下面是在 package.json
里面使用 ghooks,把这个脚本加为 commit-msg
时运行。
"config": {
"ghooks": {
"commit-msg": "./validate-commit-msg.js"
}
}
然后,每次 git commit
的时候,这个脚本就会自动检查 Commit message 是否合格。如果不合格,就会报错。
$ git add -A
$ git commit -m "edit markdown"
INVALID COMMIT MSG: does not match "(): " ! was: edit markdown
六、生成 Change log
如果你的所有 Commit 都符合 Angular 格式,那么发布新版本时, Change log 就可以用脚本自动生成。
生成的文档包括以下三个部分。
- New features
- Bug fixes
- Breaking changes.
每个部分都会罗列相关的 commit ,并且有指向这些 commit 的链接。当然,生成的文档允许手动修改,所以发布前,你还可以添加其他内容。
conventional-changelog 就是生成 Change log 的工具,运行下面的命令即可。
$ npm install -g conventional-changelog-cli
$ cd my-project
$ conventional-changelog -p angular -i CHANGELOG.md -w
上面命令不会覆盖以前的 Change log,只会在 CHANGELOG.md
的头部加上自从上次发布以来的变动。
如果你想生成所有发布的 Change log,要改为运行下面的命令。
$ conventional-changelog -p angular -i CHANGELOG.md -w -r 0
为了方便使用,可以将其写入 package.json
的 scripts
字段。
{
"scripts": {
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -w -r 0"
}
}
以后,直接运行下面的命令即可。
$ npm run changelog
七、commit 提交规范
每个提交要保证适当的颗粒度、相关性和独立性。
- 以一个小功能、小改进或一个 bug fix 为单位
- 对应的 unit test 程序在同一个 commit
- 无相关的修改不在同一个 commit
- 语法错误的半成品程序不能 commit
八、Git 操作流程
团队开发中,遵循一个合理、清晰的 Git 使用流程,是非常重要的。否则,每个人都提交一堆杂乱无章的 commit,项目很快就会变得难以协调和维护。下面是推荐的 Git 使用规范流程。
[图片上传失败...(image-267134-1556241991938)]
第 1 步:新建分支
首先,每次开发新功能或修复 Bug,都应该新建一个单独的分支。
# 获取主干最新代码
$ git checkout develop
$ git pull --rebase
# 新建一个开发分支 branch-name
$ git checkout -b branch-name develop
注意:
branch-name 的命名规如下:
- 功能开发,branch-name 应为 feat-n。feat 是 feature(功能)的前四个字母,n 是一个数值对应于 Redmine 上功能编号,中间用横线连接。例如:feat-30,如下图所示:
- Bug 修复,branch-name 应为 fix-n。fix 是修复的意思,n 是一个数值对应于 Redmine 上 Bug 编号,中间用横线连接。例如:fix-31.
第 2 步:提交分支 commit
分支修改后,就可以提交 commit 了。
$ git add .
$ git status
$ git commit --verbose
git add 命令的 all 参数,表示保存所有变化(包括新建、修改和删除)。从 Git 2.0 开始,all 是 git add 的默认参数,所以也可以用 git add . 代替。
git status 命令,用来查看发生变动的文件。
git commit 命令的 verbose 参数,会列出 diff 的结果。
第 3 步:撰写提交信息
Git commit message 的撰写规范,请参考:[[kmc:Commit message 和 Change log 编写规范]]
第 4 步:与主干同步
分支的开发过程中,要经常与主干保持同步。
$ git checkout develop
$ git pull --rebase
第 5 步:合并 commit
分支开发完成后,很可能有一堆 commit,但是合并到主干的时候,往往希望只有一个(或最多两三个)commit,这样不仅清晰,也容易管理。
那么,怎样才能将多个 commit 合并呢?这就要用到 git rebase 命令。
$ git checkout branch-name
$ git rebase -i HEAD~n
git rebase 命令的 i 参数表示互动(interactive),这时 git 会打开一个互动界面,进行下一步操作。n 代表变基操作的提交数量。
下面采用 Tute Costa 的例子,来解释怎么合并 commit。
pick 07c5abd Introduce OpenPGP and teach basic usage
pick de9b1eb Fix PostChecker::Post#urls
pick 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend
# Rebase 8db7e8b..fa20af3 onto 8db7e8b
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
上面的互动界面,先列出当前分支最新的 4 个 commit(越下面越新)。每个 commit 前面有一个操作命令,默认是pick,表示该行 commit 被选中,要进行 rebase 操作。
4 个 commit 的下面是一大堆注释,列出可以使用的命令。
- pick:正常选中
- reword:选中,并且修改提交信息;
- edit:选中,rebase 时会暂停,允许你修改这个 commit
- squash:选中,会将当前 commit 与上一个 commit 合并
- fixup:与 squash 相同,但不会保存当前 commit 的提交信息
- exec:执行其他 shell 命令
上面这 6 个命令当中,squash 和 fixup 可以用来合并 commit。先把需要合并的 commit 前面的动词,改成squash(或者s)。
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
这样一改,执行后,当前分支只会剩下两个 commit。第二行和第三行的 commit,都会合并到第一行的 commit。提交信息会同时包含,这三个 commit 的提交信息。
# This is a combination of 3 commits.
# The first commit's message is:
Introduce OpenPGP and teach basic usage
# This is the 2nd commit message:
Fix PostChecker::Post#urls
# This is the 3rd commit message:
Hey kids, stop all the highlighting
如果将第三行的 squash 命令改成 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
运行结果相同,还是会生成两个 commit,第二行和第三行的 commit,都合并到第一行的 commit。但是,新的提交信息里面,第三行 commit 的提交信息,会被注释掉。
# This is a combination of 3 commits.
# The first commit's message is:
Introduce OpenPGP and teach basic usage
# This is the 2nd commit message:
Fix PostChecker::Post#urls
# This is the 3rd commit message:
# Hey kids, stop all the highlighting
第 6 步:推送到远程仓库
合并 commit 后,就可以推送当前分支到远程仓库了。
$ git push --force origin branch-name
git push 命令要加上 force 参数,因为 rebase 以后,分支历史改变了,跟远程分支不一定兼容,有可能要强行推送。
第 7 步:发出 Pull Request
提交到远程仓库以后,就可以发出 Pull Request 将分支合并到 develop 分支,发起 Pull Ruquest 之后,可以做两件事情:Bug 回归测试和 Code Review。
Bug 回归测试。这是可选的,当分支是 Bug 修复分支(fix-m)时,需要做回归测试,回归测试通过,才能 Merge
但是,当分支是功能开发分支(feat-n)时,不需要做 Bug 回归测试,但是开发人员需要自己保证代码是经过测试的
Code Review。不管分支是 Bug 修复分支还是功能开发分支,都需要利用开发规范对提交的代码进行静态检查。
在 Pull Request 页面,项目组的其他开发人员对程序代码和 Git commit 注释,进行检查和审核.
如果有问题,需要在相应分支上做修改代码或者 Git 变基操作
注意,在合并代码之前,开发人员如果收到 develop 分支上其他分支的 Merge 事件,应该主动在本地仓库的分支上执行如下命令:
git rebase develop
git push -f branch-name
第 8 步:合并 Pull Request
一旦检查完毕,没有问题,则进行合并操作。注意,合并操作,不应该由提交代码的开发人员本人进行合并,而是应该由其他人进行合并。而且,在合并之前,应该在 Pull Request 备注文字,例如:本次提交的代码,经检查没有问题,可以合并。
九、如何提高 PR 流程的效率
PR(Pull Request)的意义重大,因为有了 PR 这个缓冲,代码在合并到主干之前可以进行 Code Review,有了 Code Review,开发人员的技术能力就有了提升的空间。否则,不管代码是否规范,直接提交到版本仓库中,没有任何回旋的余地。有了 PR,也可以有效的减少冲突代码的互相覆盖。虽然 PR 有诸多好处,但是很多开发人员的了解 PR 的第一反应就是降低了开发流程的效率。其实 PR 没有真正降低效率,想象一下,如果没有 PR,开发人员提交代码导致了代码的覆盖和丢失,寻找丢失和覆盖的代码是不是效率更低呢?至于如何开展 PR,见上面的 Git 流程介绍。下面谈几点提高 PR 流程效率的技巧:
尽早解决冲突
当发出 PR 后,GitHub 会立刻检查当前分支和长期分支是否有代码冲突(如下图所示)。一旦有冲突发生,是无法完成代码合并的。这个冲突状态是 GitHub 很快能够提示出来的,所以,开发人员在发起 PR 之后,应该在 PR 的页面上等待页面提示是否有冲突的信息出现,如果发现有代码冲突,应该第一时间去解决代码冲突。在确保 PR 中没有冲突的情况下,再继续 PR 的后续流程。主动要求 Code Review
当发出 PR 后,一般情况下,项目所有参与者都会收到邮件提醒,但是邮件提醒是有时间间隔的。所以,如果发出 PR 后,可以主动把 PR 页面的 URL 地址,通过企业微信或者 QQ 发送给项目组成员。或者直接发送给高杨、王树贤或者王顶,要求 Code Review。当然,如果 PR 是 bug 修复,应该同时把 PR 页面的 URL 链接发送给测试人员,要求测试人员进行测试,测试通过后,测试人员应该在 PR 页面上备注测试是否通过的信息。测试和 Code Review 可以同时开展,这样可以提高流程的效率。
总结一下:在企业的任何工作流程中,提高效率的关键要点无非三个:一是减少流程中间的等待时间,二是尽量提高任务并行的程度,减少任务串行的程度,三是尽量减少不必要的重复或者任务。