背景
某一天下午,温度和湿度都很适宜,写代码也写的很来劲,心情也很好,兴致勃勃的。突然钉钉群响了,点开一看,隔壁同事发了一个关于提交代码的 commit message
范本。
1 | // commit 信息规范 |
我一看,很新颖啊,说的很有道理,值得推广,于是立马在下午的提交代码的过程中,用上了。
但是种类太多了,又拿出了便利贴,把所有类别都抄在了上面,方便实时查看,又觉得确实是种规范,立马又推给了之前公司的同事。(可见我多么认可)
问题
一开始还可以,我很认真的严格按照规范执行,但是我写去写去就觉得 fix: 修复ios12微信键盘失焦页面不下来的问题
,按照小学的作业来看,这个明明是个病句啊。 fix
就是 修复
啊,我为什么要多此一举。
渐渐地就开始懒了,便利贴也脱落了,然后我的画风就变成了这样混搭风
并且我也觉得完全没毛病。写成什么样的形式,最终目的是可读性好。我已经做到了。其他同事做 code review
的时候也是没有问题的。
转变
周会了,主题是规范,其中就扯到了这个 commit message
的规范、形式、和执行。
当场我就google了,查阅相关资料。发现原来这个规范最初是 Angular 规范
,大致长这样
它是有好处的。
提供更多的历史信息,方便快速浏览。
可以过滤某些commit(比如文档改动),便于快速查找信息。
git log <last release> HEAD --grep feature
可以直接从commit生成
Change log
执行规范
鉴于我之前慢慢的就不规范,并且没有约束力,但是确实用上了这个规范是有意义的。
于是想着能不能有像 EsLint.js
这样的强制工具,强制约束我。(果然人还是犯贱的…)
commit message 的格式
commit message
包括三个部分: Header
、 Body
、 Footer
。1
2
3
4
5<type>(<scope>): <subject> // 必填
// 空一行
<body> // 选填
// 空一行
<footer> // 选填
不管是哪一个部分,任何一行都不得超过72个字符(或100个字符)。这是为了避免自动换行影响美观。
Header
Header部分只有一行,包括三个字段:type(必需)、scope(可选)和subject(必需)。
Type
feat:新功能(feature)
fix:修补bug
docs:文档(documentation)
style: 格式(不影响代码运行的变动)
refactor:重构(即不是新增功能,也不是修改bug的代码变动)
test:增加测试
chore:构建过程或辅助工具的变动
tip:
如果 type
为 feat
和 fix
,则该 commit
将肯定出现在 Change log
之中。
其他情况(docs、chore、style、refactor、test
)由你决定,要不要放入 Change log
,建议是不要。
scope
用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。
subject
以动词开头,使用第一人称现在时,比如change,而不是changed或changes
第一个字母小写
结尾不加句号(.)
Body
对本次 commit 的详细描述,可以分成多行。
Footer
关闭
Issue
如果当前commit
针对某个issue
,那么可以在Footer
部分关闭这个issue
。Closes #123, #245, #992
不兼容变动
如果当前代码与上一个版本不兼容,则Footer
部分以BREAKING CHANGE
开头,后面是对变动的描述、以及变动理由和迁移方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17BREAKING 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.
commitlint
commitlint
是一个工具,会检查我们的 commit message
是否满足规范。
通常上,我们这个规范是 type(scope?): subject #scope is optional
,
这个type,常见类型根据 commitlint-config-conventional (based on the the Angular convention)
1 | build:主要目的是修改项目构建系统(例如 glup,webpack,rollup 的配置等)的提交 |
所以第一步,下载两个npm包1
npm install --save-dev @commitlint/config-conventional @commitlint/cli
第二步,根目录新建一个js文件 commitlint.config.js
,
然后内容是 module.exports = {extends: ['@commitlint/config-conventional']}
1
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
当然除了这个规范 @commitlint/config-conventional
还有其他的规范
@commitlint/config-angular
@commitlint/config-conventional
@commitlint/config-lerna-scopes
@commitlint/config-patternplate
conventional-changelog-lint-config-atom
conventional-changelog-lint-config-canonical
当然还可以自定义配置,但是不介绍了。
git hooks
上面介绍的是一个检查 commit message
的工具,要想达到 git commit -m 'xxxx'
的时候触发这个检查动作,
就要利用另外一个工具 git hooks
。
钩子(hooks)是一些在 "$GIT-DIR/hooks"
目录的脚本, 在被特定的事件(certain points)触发后被调用。当 "git init"
命令被调用后, 一些非常有用的示例钩子文件(hooks)被拷到新仓库的hooks目录中;
但是在默认情况下这些钩子(hooks)是不生效的。 把这些钩子文件(hooks)的”.sample”文件名后缀去掉就可以使它们生效了。
也就是在我们的git仓库下会有一个.git配置文件夹,它里面包含git操作相关的一系列 预处理/后处理
脚本。如 pre-commit
、 post-push
之类的。
分为客户端钩子的和服务器端钩子。
如何安装钩子
钩子都被存储在 Git
目录下的 hooks
子目录中。 也即绝大部分项目中的 .git/hooks
。 当我们用 git init
初始化一个新版本库时,Git
默认会在这个目录中放置一些示例脚本。这些脚本除了本身可以被调用外,它们还透露了被触发时所传入的参数。
所有的示例都是 shell
脚本,其中一些还混杂了 Perl
代码,不过,任何正确命名的可执行脚本都可以正常使用 —— 你可以用 Ruby
或 Python
,或其它语言编写它们。 这些示例的名字都是以 .sample
结尾,如果你想启用它们,得先移除这个后缀。
把一个正确命名且可执行的文件放入 Git
目录下的 hooks
子目录中,即可激活该钩子脚本。 这样一来,它就能被 Git
调用。
客户端钩子
提交工作流钩子、电子邮件工作流钩子和其它钩子
提交工作流钩子
- pre-commit
钩子在键入提交信息前运行。
它用于检查即将提交的快照,例如,检查是否有所遗漏,确保测试运行,以及核查代码。
可以利用该钩子,来检查代码风格是否一致(运行类似 lint 的程序)、尾随空白字符是否存在(自带的钩子就是这么做的),或新方法的文档是否适当。
- prepare-commit-msg
钩子在启动提交信息编辑器之前,默认信息被创建之后运行。
它允许你编辑提交者所看到的默认信息。 该钩子接收一些选项:存有当前提交信息的文件的路径、提交类型和修补提交的提交的 SHA-1 校验。
- commit-msg
钩子接收一个参数,此参数即上文提到的,存有当前提交信息的临时文件的路径。
可以用来在提交通过前验证项目状态或提交信息。
我们使用该钩子来核对提交信息是否遵循指定的模板。
- post-commit
钩子在整个提交过程完成后运行。
它不接收任何参数,但你可以很容易地通过运行 git log -1 HEAD 来获得最后一次的提交信息。 该钩子一般用于通知之类的事情。
服务端钩子
放在 Git Server
上的 Hooks
脚本,作为管理员,可以利用这些服务端的脚本来强制确保项目的任何规范。
这些运行在服务端的脚本,会在push命令发生的前后执行。pre系列的脚本可以在任何时候返回非零值来终止某次push,并向push方返回一个错误。
可以比如监听 git push
来做消息通知,对接钉钉机器人,发送push的详细信息。
husky(哈士奇)
husky
是一个对 git hooks
的封装,专门用来处理这种git提交时的自动化处理的插件。不过它只支持客户端钩子。1
npm install --save-dev husky
原理:
实现的机制大致是,在你install该插件时,它会自动往git hooks里埋入很多相应的钩子脚本(git hook能识别的),
这些钩子函数会去读区package.json中的配置信息,当用户做某些git操作触发相应钩子时会自然去调用配置好的任务,从而实现我们的自动化需求,
包括代码检查跟单元测试验证以及更多的自动化任务。
1 | // package.json |
小小提一嘴,还有个 git钩子
插件,叫 pre-commit
。
步骤汇总
在自己的项目的
package.json
里面的devDependencies
中新增插件:1
npm install --save-dev @commitlint/config-conventional @commitlint/cli husky
在项目根目录新建js文件
commitlint.config.js
1 | module.exports = { |
- 在自己的项目的
package.json
里面新增配置
1 | { |