赞
踩
在工作中,我们通常使用git来管理代码,当我们对代码进行某项改动后,都可以通过git commit对代码进行提交。git规定提交时必须要写提交信息,作为改动说明,保存在commit历史中,方便回溯。但你仔细研究过git commit吗?或者换句话说你注意过应该怎样写commit信息和优化commit信息吗?本文将回答这些问题,希望对大家有所帮助。如有错误,请不吝指正。
为了对每次提交进行提交说明,方便之后回溯和团队协作,Git 每次提交代码,都要写 Commit message(提交说明),否则就不允许提交。当参与团队协作时,我们共同维护一个maste或dev的代码,如果其他人改动了仓库代码,我们肯定想知道他到底改动了什么,这次改动会对我们有什么影响。怎么看呢,直接看代码肯定没错,但不是最好的方式。其实最方便的方式就是查看提交历史了。如果提交信息写的足够好的话,不需要看代码我们也清楚改动了什么,这样就可以提高协作效率和沟通成本了。
试想谁不想看到下面这样优雅清晰明了提交历史呢,如果再能自动生成ChangeLog文档自然是极好的。我想没有哪个程序员能抵挡得住这种诱惑吧。
总体来说,规范 commit信息的意义如下:
鉴于此,我们需要写出清晰规范的git commit。那么什么的规范的commit信息呢,请继续看下去。
为引导用户为开源社区做贡献,angular较早提出了进行pr时的commit说明规范(详见Git Commit Guidelines),后被很多开源项目所接受,形成了一套约定式提交规范。首先让我们从一条提交记录出发看一下angular是如何写提交信息的。
如上所示,angular提交信息遵循如下格式:
<type>(<scope>): <subject> # header信息头必须
<BLANK LINE>
<body> # 信息体
<BLANK LINE>
<footer> # 结束部分
header 信息头(必须)<type>(<scope>): <subject>
信息头必须要有type,它严格限定为如下值:
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 or correcting existing tests
chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
另外如果本次提交是回退之前的提交,应该以revert:开头,跟着要回退提交的header信息,在body中写:This reverts commit <hash>
其他开源项目的type类型也类似,像electron的type类型如下所示,详见Commit message guidelines:
fix: A bug fix
feat: A new feature
docs: Documentation changes
test: Adding missing tests or correcting existing tests
build: Changes that affect the build system
ci: Changes to our CI configuration files and scripts
perf: A code change that improves performance
refactor: A code change that neither fixes a bug nor adds a feature
style: Changes that do not affect the meaning of the code (linting)
vendor: Bumping a dependency like libchromiumcontent or node
接下来是scope。它指定了本次提交的影响范围,比如数据层、控制层、视图层等等,视项目不同而定。如果影响范围较大,可以使用通配符*。scope可以忽略。
最后是subject,指出本次提交的主题,是对本次提交的简短描述,不超过50个字符。注意它和前面的冒号之间要有空格,和type一样,必须要有。subject内容有以下规定:
type和subject决定了本次提交信息的质量,是灵魂所在。
body 信息体(可忽略)
是对header中subject的补充,可以写一些本次提交改动了哪里和有什么影响等内容。注意时态同样要使用一般现在时,比如改动要写change,不能用changed或changes。
footer 信息尾部(可忽略)
尾部可以包含一些类似做出了哪些重大改变这样的内容,也可以写关闭了那些issue。另外Breaking Changes要以BREAKING CHANGE:
打头,后面跟空格或两个新行和具体内容。关闭了那些bug要使用Closes
打头,哪怕只关闭了一个,例如Closes #234
angular关于commit message的详细规范请参见Git Commit Message Conventions文档。
看来要写出符合git commit规范的信息也是不容易的,不过社区也为我们提供了一些辅助工具来帮助进行提交,下面根据使用目的来简单介绍一下这些工具。
cz-cli 是一款可以交互式建立提交信息的工具。它帮助我们从type开始一步步建立提交信息,具体效果如图所示,通过上下键控制指向你想要的type类型。
注意:
- windows下使用git bash 运行该工具不能上下选择,可以使用powershell代替。
- 此工具要配合adapter(类似模板)使用,来提供交互提交的样式包括type类型和提交的结构,可自定义。
全局环境下安装使用方法如下:
# 需要同时安装commitizen和cz-conventional-changelog,后者是adapter
$ npm install -g commitizen cz-conventional-changelog
# 配置安装的adapter
$ echo '{ "path": "cz-conventional-changelog" }' > ~/.czrc
# 使用
$ git cz
本地环境下(非全局安装)使用方法如下:
# 安装commitizen $ npm install --save-dev commitizen # 接下来安装适配器 # for npm >= 5.2 $ npx commitizen init cz-conventional-changelog --save-dev --save-exact # for npm < 5.2 $ ./node_modules/.bin/commitizen init cz-conventional-changelog --save-dev --save-exact // package.json script字段中添加commit命令 "scripts": { "commit": "git-cz" } // use $ npm run commit // 如果不希望每次使用npm run commit写提交信息的话,可以使用git hook,把git commit命令替换成交互式提交,注意windows下不生效 "husky": { "hooks": { "prepare-commit-msg": "exec < /dev/tty && git cz --hook", } }
commitlint是一款提交信息校验工具,原理是可用git hooks在真正进行git commit入库操作前对信息进行验证,不符合规则的commit信息将会被阻止提交入库,如下所示。
使用方式也很简单:
# 安装 commitlint cli and conventional config
$ npm install --save-dev @commitlint/{config-conventional,cli}
# Windows下自带命令行工具不会识别/{x,x},所以使用以下语句安装
$ npm install --save-dev @commitlint/config-conventional @commitlint/cli
# 生成commitlint配置文件,这步是必须的,不然提交会报错,达不到效果
$ echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
注意:使用commitlint时需要建立校验文件commitlint.config.js,不然会校验失败
如果大家自己写过git hooks,那么可以把commitlint添加到git commit前的校验中,如果不是很熟的话可以借助一些工具,比如husky。
# 安装husky
$ npm i -D husky
# package.json文件添加如下字段
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
当然如果认为遵守规则很累,可以在git commit时使用 --no-verify参数跳过验证,直接提交。不过这样使用该插件就没意义了。所以尽量保证自己写的commit信息符合规范。
changelog是改动日志,包含我们发布的版本信息,以及每次版本更新的东西。
conventional-changelog-cli是一款changelog生成工具,通过提取commit信息(默认提取类型为feat、fix和包含breaking change的提交信息)为我们自动化生成changelog文档。
全局使用方法如下:
# 安装conventional-changelog-cli
$ npm install -g conventional-changelog-cli
$ cd my-project
# 保留原changelog文档,生成本次改变
$ conventional-changelog -p angular -i CHANGELOG.md -s
# 完全重新生成changelog文档
$ conventional-changelog -p angular -i CHANGELOG.md -s -r 0
如果不想全局安装的话,可以使用npm run script方式运行,如下所示
$ npm i -D conventional-changelog-cli
// package.json script字段中添加version命令
"script": {
"version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md"
}
// use
$ npm run version
conventional-changelog-cli生成changelog是根据到目前为止的所有提交信息同最新一次tag作比较得到的所有feature、bug、breaking change信息进行文档的生成,所以要在每次版本更新并生成changelog文档后对分支打tag,再push到远程分支。
这里摘取conventional-changelog-cli推荐的整体流程:
社区同样提供了一些高级工具来自动化进行上述的某些流程,可以说提供了一条龙服务。具体工具如下:
standard-version: 更新版本、生成changelog、打tag
semantic-release: 更新版本、生成changelog、打tag、推送代码发布
具体使用方法请自行查看文档,这里不再赘述。
前面讲了这么多git commit规范,可能大家要问了,我也准备从现在开始写符合规范的提交信息,那之前项目中的不符合规范的信息怎么办呢?其实之前的提交信息也是有办法修改的,关键就是使用git rebase
命令。此外还有一个问题就是我写了很多条提交信息,但其实都是干的同一件事(这很常见,比如说要修复某个bug,改好之后发现这个bug在其他设备上并没有改好,于是又改好之后提交了一次,但都是改的同一个问题),有办法将他们合并成一条吗?其实有的,关键也在于git rebase
。是不是瞬间感觉这个命令很强大,想要迫不及待的试一下呢。
好吧,不卖关子了,直接来到正题,如何使用git rebase
进行变基操作。先说一下如果直接使用git rebase [target-branch]
,虽然可以帮我们在本分支基于目标分支进行变基操作,但它只会让两个分支的所有提交连成一条线,不能修改具体提交信息,所以需要使用git rebase -i
进行交互式操作。首先了解一下git rebase -i
的几个操作。
如上图所示,git rebase提供了几个操作来对提交信息进行处理,这里挑几个常用的介绍一下。
另外还要知道git rebase --continue
命令用来告诉git继续进行合并,一般在解决冲突后使用(一般来讲如果所有的提交没有冲突的话,可以一直走下去,直到出现成功的提示。但如果两次提交合并涉及到冲突,就需要解决冲突并在git add后使用该命令继续走下去);git rebase --abort
命令用来终止变基操作,一般在不想进行rebase时使用(大部分时间是因为玩脱了,rebase到了一种不可预知的地步,不知道干啥了,就只能终止了)。直到出现类似“Successfully rebased and updated refs/heads/dev.”的消息就说明本次变基成功了。下面具体讲一下如何修改和美化提交信息。
修改提交信息可以分为以下几个步骤,当然具体步骤还要具体情况具体分析,有些可以忽略。
暂存工作状态:使用git stash
将当前工作状态进行暂存,如果没有需要暂存的工作状态请忽略
转到需要修改的提交上:使用git rebase -i [commit-id]^
将 HEAD 移动到需要修改的 commit-id 上,因为该[commit-id]也在修改之列,所以要基于它的上一个提交变基,或者直接使用[commit-id]^
将pick修改为想要进行的操作命令:进行编辑时一般要把所有需要修改的commit的pick改为r或e,提示:如果多的话可以使用vim的全局替换
重新编辑commit信息
附加修改:如果需要在该分支上修改其他内容,可以在改动文件后直接使用git add
将其添加到暂存区,并使用 git commit –amend
追加改动到提交。如果不需要请忽略
继续进行变基操作:使用git rebase –continue
继续,一般如果提交没有冲突或者附加修改的话,不需要手动输入,git会自动进行下一次操作。
变基完成:重复以上步骤,直到出现变基成功文字。如果暂存过工作状态,需要使用git stash pop
恢复之前的工作状态,结束。
我们大多数时候只会使用git merge
来将开发代码合并到主分支。不过这样会导致主分支包含我们合并分支的所有提交信息。长此以往,会让commit history越来越繁杂,但其实合并的代码完全可以用一条提交信息比如feat: 添加某项新特性
来代替。当然还有其他原因会导致commit信息重复且不必要。比如当我们在拿不准bug到底有没有修复成果时可能会多次改动代码进行测试,如果每次改动测试都进行了提交,就会产生多次commit信息,如果不对这些commit做合并的话,在我们将代码合并到开发主分支时就会产生很多繁杂的commit信息,不过由于这些commit信息都是为解决同样的事情服务的,完全可以合到一起。所以如果你嫌弃commit信息太啰嗦冗余,那么就需要对其进行美化了。美化主要有两种方式:git rebase
和 git merge --squash
,这里将介绍它们的使用,并进行对比。
rebase merge
注意:如果要合并的commit历史过多并且对git rebase命令使用不熟练,请慎用,不然可能会被各种冲突逼疯的!
如果我们想将dev的提交信息进行美化并合并到master分支,可以进行如下操作:
git checkout dev
git rebase -i master
,其中具体的操作请参见上一小节,不同的不过是把一些提交的pick改为s而已,即提交信息合并到上一次提交。git checkout master
git merge dev
squash merge
同样以dev分支合并到master为例:
git checkout master
git merge --squash dev
两种方式都能实现提交信息的合并,保持 master 分支干净整洁。不过它们也有很大不同。
squash merge会把分支的所有提交一股脑应用在目标分支上,然后你要修改冲突后再提交,生成一次commit,而这次commit包含dev的所有改动。产生的这次新的commit的作者自然也就是做了本次将dev分支合并到master分支操作的人,他可能并不是原dev分支的作者,也就掩盖了真实的作者。此外无论改动有多少,只会产生一次提交记录。
rebase merge拥有更高的可操作性,它允许我们可以选择保留几条有意义的提交,而不像squash merge只有一条。当然有更高的操作自由度也意味着步骤比较繁琐,不像squash merge简单方便。当然变基操作完成后在主分支上使用git merge
进行合并,原作者信息会得到保留。
总之,如果你想要更快进行提交合并,能容忍原作者信息丢失的话,可以选择squash merge;如果你想要保留多条有意义的提交信息和需要保留原作者信息的话,rebase merge可能要更适合你。
话说回来,其实git rebase
命令非常强大,不仅能做本分支commit信息的修改、合并,还能使用git rebase [startpoint] [endpoint] --onto [branchName]
将本分支的某一段commit集合合并到另一分支。嗯,git很是博大精深呢,要想达到精通,我还有很长一段路要走!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。