1 关于提交日志规范
良好的Commit Message有利于代码审查,能更快速查找变更记录,并且可以直接生成Change log。
Commit Message的写法规范: 为了规范代码提交的说明,这里我们使用angular的规范写法:
( ): <空行> <空行> 空行> 空行>
其中 head((): )是必须的,body和footer是可选的。 如果只需要输入header,可以直接使用:
git commit -m复制代码
命令提交。
如果需要输入body、footer这样的多行日志,需要输入:
git commit复制代码
跳出文本编辑器进行编写。
1.1 Header
1.1.1 Type
commit的类别,包涵以下七种:
feat:新功能(feature)fix:修补bugdocs:文档(documentation)style: 格式(不影响代码运行的变动)refactor:重构(即不是新增功能,也不是修改bug的代码变动)test:增加测试chore:构建过程或辅助工具的变动复制代码
1.1.2 Scope
commit的影响范围,比如会影响到哪个模块/性能/哪一层(业务层,持久层,缓存,rpc),如果是特性代码,可以写特性名称
1.1.3 Subject
commit的简短描述,不超过50个字符。
- 使用现在式,祈使句
- 第一个字母无需大写
- 结尾不用加句号
1.2 Body
跟subject一样,使用现在式,祈使句。应该说明提交代码的动机,以及跟前一个版本的对比。
1.3 Footer
Foot包含可以包含以下信息:
1.3.1 描述重大变更的信息
以 BREAKING CHANGE 开头,后面是变更的具体描述,如
BREAKING CHANGE: isolate scope bindings definition has changed and the inject option for the directive controller injection was removed. To migrate the code follow the example below: Before: scope: { myAttr: 'attribute', myBind: 'bind', myExpression: 'expression', myEval: 'evaluate', myAccessor: 'accessor' } After: scope: { myAttr: '@', myBind: '@', myExpression: '&', // myEval - usually not useful, but in cases where the expression is assignable, you can use '=' myAccessor: '=' // in directive's template change myAccessor() to myAccessor } The removed `inject` wasn't generaly useful for directives so there should be no code using it.复制代码
1.3.2 关闭JIRA
如:
Closes DB-1001, DB1002复制代码
1.4 一些 Commit Message 例子:
feat($browser): onUrlChange event (popstate/hashchange/polling)Added new event to $browser:- forward popstate event if available- forward hashchange event if popstate not available- do polling when neither popstate nor hashchange availableBreaks $browser.onHashChange, which was removed (use onUrlChange instead)---------fix($compile): couple of unit tests for IE9Older IEs serialize html uppercased, but IE9 does not...Would be better to expect case insensitive, unfortunately jasmine doesnot allow to user regexps for throw expectations.Closes #392Breaks foo.bar api, foo.baz should be used instead---------eat(directive): ng:disabled, ng:checked, ng:multiple, ng:readonly, ng:selectedNew directives for proper binding these attributes in older browsers (IE).Added coresponding description, live examples and e2e tests.Closes #351---------feat($compile): simplify isolate scope bindingsChanged the isolate scope binding options to: - @attr - attribute binding (including interpolation) - =model - by-directional model binding - &expr - expression execution bindingThis change simplifies the terminology as well asnumber of choices available to the developer. Italso supports local name aliasing from the parent.BREAKING CHANGE: isolate scope bindings definition has changed andthe inject option for the directive controller injection was removed.To migrate the code follow the example below:Before:scope: { myAttr: 'attribute', myBind: 'bind', myExpression: 'expression', myEval: 'evaluate', myAccessor: 'accessor'}After:scope: { myAttr: '@', myBind: '@', myExpression: '&', // myEval - usually not useful, but in cases where the expression is assignable, you can use '=' myAccessor: '=' // in directive's template change myAccessor() to myAccessor}The removed `inject` wasn't generaly useful for directives so there should be no code using it.复制代码
如果是特性开发,则可以这样:
feat(短视频播放优化): 全屏播放动画效果优化复制代码
2 提交日志自动校验
在NodeJS项目中,我们可以通过使用 + 进行提交日志校验,校验不通过的将被拒绝提交。原理是通过NodeJs项目编译,把NodeJS的校验脚本写到.git/hooks/
目录下的勾子文件中,这样每次执行git commit
命令都会执行这个校验,不过这种方式依赖于Node环境,并且每个Git项目都需要引入对应的npm模块进行编译,对于比较多微服务项目的情况来说使用不太方便。
为此,编写了插件, 该插件有如下特点:
- 很方便的在项目中自定义团队的工作流, 把自定义钩子钩子脚本纳入git管理类, 方便团队共享;
- 把钩子脚本的安装集成到
Maven
的生命周期中的某个阶段, 方便的通过项目编译自动安装或者升级脚本; - 提供通用的内置脚本, 方便钩子的配置, 目前只提供了
validate-commit-message
钩子脚本, 用于提交日志的规范, 遵循的格式。
。
配置完这个,当我们通过maven编译完项目之后,下一次提交代码,如果提交日志不符合规范,则会报错:
➜ project-1 git:(master) ✗ git commit -m "test"Commit log error: First commit message line (commit header) does not follow format: type(scope): subject - Refer commit guide: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#复制代码
3 特性开发提交压缩合并
对于独自完成的特性,可能在开发过程中会产生多个提交,为了让提交整洁,需要对这个特性的所有提交进行压缩合并。我们先看看压缩合并之前的代码:
* be6e32d (HEAD->master)feat(测试提交): 修改第二个文件* 4a7615e feat(测试提交): 修改第一个文件* 721064e feat(测试提交): 提交第四个文件* e20968e feat(测试提交): 提交第三个文件* b7160b3 feat(测试提交): 提交第二个文件* 0c90fcl feat(测试提交): 提交第一个文件* e618321 fix(页面展示): 展示错误修复复制代码
如图,可以发现压缩合并测试
特性有多个提交,可以进行合并,现在准备把e618321
前面的提交都进行合并,执行git rebase -i
命令进行压缩合并:
git rebase -i e618321
其中命令后面的hash值是压缩合并开始的那个提交的hash值:
pick 0c90fcl feat (测试提交): 提交第一个文件pick b7160b3 feat(测试提交): 提交第二个文件pick e20968e feat(测试提交): 提交第三个文件pick 721064e feat(测试提交): 提交第四个文件pick 4a7615e feat(测试提交): 修改第一个文件pick be6e32d feat(测试提交): 修改第二个文件# Rebase e618321..be6e32d onto e618321 (6 command(s))## 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# d, drop = remove commit## 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复制代码
注意:这个界面里面的提交是按照提交时间顺序往下排的,最新提交的在最后面,跟git lg
的相反,平时查看日志是最新提交的排在最前面。
我们把低2行到6行改为 s(squash),第一个行改为 r(reword),把第2到6行合并到第一个提交里面,调整下提交信息:
r 0c90fcl feat (测试提交): 提交第一个文件s b7160b3 feat(测试提交): 提交第二个文件s e20968e feat(测试提交): 提交第三个文件s 721064e feat(测试提交): 提交第四个文件s 4a7615e feat(测试提交): 修改第一个文件s be6e32d feat(测试提交): 修改第二个文件复制代码
然后输 :wq 保存并退出编辑模式继续处理。
如果遇到有冲突,需要解决完冲突之后,修改冲突的提交日志。
最后是一个压缩合并之后的提交日志确认界面,看看第一行的日志是否符合我们的需求,没有问题则输入 :wq 保存并退出编辑模式。
# This is acombination of 7commits.# The first commit'smessage is:feat(测试提交): 测试代码# This is the 2nd commit message:feat(测试提交): 提交第二个文件# This is the 3rd commit message:feat(测试提交): 提交第三个文件复制代码
这样我们就合并完成了,再次查看提交日志,发现已经合并程了一个提交:
* eb0121a (HEAD->master) feat(测试提交): 测试代码* e618321 fix(页面展示): 展示错误修复复制代码
注:git lg中 lg = log --graph --oneline --decorate,特别注意,本次演示代码直接在master上面进行,实际上需要在 feature-xxx 分支上面进行。
提交代码和压缩合并的原则:
开发过程中的提交代码原则: 尽量减少commit的次数; 在push之前,需要把本次特性所有的代码都压缩成一个提交; 在提交代码审查之前,最好把本次特产压缩成一个提交
4 合并master代码使用 rebase
假设我们开发一个新的特性,并且已经把代码进行了压缩合并,提交日志如下:
* 69b4ffb (HEAD -> feature-test) feat(测试特性): 测试特性代码* eb0121a (HEAD->master) feat(测试提交): 测试代码* e618321 fix(页面展示): 展示错误修复复制代码
而此时,master代码也有了新的修复代码提交:
* 69b4ffb (HEAD -> master) fix(缓存): 修复产品接口缓存bug* eb0121a (HEAD->master) feat(测试提交): 测试代码* e618321 fix(页面展示): 展示错误修复复制代码
为了保持合并之后,我们当前特性点仍然在提交日志的最新位置,达成效果如下:
我们使用rebase
进行合并代码:
git rebase master
合并之后效果如下,我们当前特性分支的提交仍然在最前面:
* 69b4ffb (HEAD -> feature-test) feat(测试特性): 测试特性代码* 69b4ffb (HEAD -> master) fix(缓存): 修复产品接口缓存bug* eb0121a (HEAD->master) feat(测试提交): 测试代码* e618321 fix(页面展示): 展示错误修复复制代码
rebase代码原则:
特性分支合并master代码使用 rebase,总让当前特性处于最近提交; 如果一个特性有多个同事开发,rebase之前记得让其他同事提交所有代码,rebase之后记得让其他同事强制更新本地代码;
References
首次发布于: