Git 作为版本控制系统的优点是不容置疑的,但尽管 Git 在跟踪您和您的团队成员对存储库所做的提交方面做得非常出色,但它本身并不能保证这些提交的质量。Git 不会阻止您提交包含 lint 错误的代码,也不会阻止您编写毫无信息意义的提交信息,更不会阻止您提交格式糟糕的代码。
幸运的是,借助 Git Hooks,我们只需几行代码即可纠正这种情况。在本教程中,我将引导您了解如何实现 Git Hooks,这些 Hooks 只有在提交满足所有已设定的构成可接受提交的条件时才允许您进行提交。如果它不满足一个或多个条件,则会显示一条错误消息,其中包含有关要执行哪些操作才能使提交通过检查的信息。通过这种方式,我们可以保持代码库的提交历史整洁有序,从而使我们的团队成员(更不用说我们未来的自己)的生活变得更加轻松愉快。
此外,我们还将确保在提交代码之前对其进行格式化。这个提议有什么不喜欢的呢?好吧,让我们开始吧。
先决条件
为了能够遵循本教程,您应该对 Node.js、npm 和 Git 有基本的了解。如果您从未听说过名为 package.json 的东西,并且 git commit -m [message]
听起来像是超级秘密的东西的代码,那么我建议您在继续阅读之前访问 此 和 此 网站。
我们的行动计划
首先,我们将安装使实施预提交钩子变得轻而易举的依赖项。一旦我们拥有了工具箱,我们将设置三个检查,我们的提交必须在进行之前通过这些检查
- 代码应没有 lint 错误。
- 任何相关的单元测试都应通过。
- 提交信息应遵循预定的格式。
然后,如果提交通过了以上所有检查,则应在提交之前对代码进行格式化。需要注意的重要一点是,这些检查仅在已暂存以进行提交的文件上运行。这是一件好事,因为如果不是这样,对整个代码库进行 lint 并运行所有单元测试会在时间上增加相当大的开销。
在本教程中,我们将为一些使用 TypeScript 作为前端样板,然后使用 Jest 进行单元测试,并使用 Prettier 进行代码格式化的样板项目实现上面讨论的检查。无论您使用的是什么技术栈,实施预提交钩子的过程都是相同的,因此,请不要仅仅因为我正在使用它就觉得有必要跳上 TypeScript 的列车;如果您更喜欢 Mocha 而不是 Jest,那么请使用 Mocha 进行单元测试。
安装依赖项
首先,我们将安装 Husky,它允许我们在提交之前执行任何我们认为合适的检查。在项目的根目录下,运行
npm i husky --save-dev
但是,如前所述,我们只想对已暂存以进行提交的文件运行检查,为此,我们需要安装另一个包,即 lint-staged
npm i lint-staged --save-dev
最后但并非最不重要的一点是,我们将安装 commitlint,它将允许我们强制执行提交信息的特定格式。我选择了一种预先打包的格式,即常规格式,因为我认为它鼓励简洁明了的提交信息。您可以在 此处 阅读更多相关信息。
npm install @commitlint/{config-conventional,cli} --save-dev
## If you are on a device that is running windows
npm install @commitlint/config-conventional @commitlint/cli --save-dev
安装 commitlint 包后,您需要创建一个配置,告诉 commitlint 使用常规格式。您可以使用以下命令从终端执行此操作
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
太好了!现在我们可以继续进行有趣的部分了,也就是说实施我们的检查!
实施我们的预提交钩子
以下是我的样板项目 package.json 中脚本的概述。我们将在提交之前开箱即用地运行其中的两个脚本,即 lint
和 prettier
脚本。您可能想知道为什么我们也不会运行 test
脚本,因为我们将实现一个检查以确保任何相关的单元测试都通过。答案是,如果您不想在提交时运行所有单元测试,则需要对 Jest 进行更具体的设置。
"scripts": {
"start": "webpack-dev-server --config ./webpack.dev.js --mode development",
"build": "webpack --config ./webpack.prod.js --mode production",
"test": "jest",
"lint": "tsc --noEmit",
"prettier": "prettier --single-quote --print-width 80 "./**/*.{js,ts}" --write"
}
从我们添加到下面 package.json 文件中的代码中可以看出,为 lint 和 prettier 脚本创建预提交钩子并不比告诉 Husky 在提交之前需要运行 lint-staged 更复杂。然后,您告诉 lint-staged 对所有暂存的 JavaScript 和 TypeScript 文件运行 lint 和 prettier 脚本,就是这样!
"scripts": {
"start": "webpack-dev-server --config ./webpack.dev.js --mode development",
"build": "webpack --config ./webpack.prod.js --mode production",
"test": "jest",
"lint": "tsc --noEmit",
"prettier": "prettier --single-quote --print-width 80 "./**/*.{js,ts}" --write"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"./**/*.{ts}": [
"npm run lint",
"npm run prettier"
]
}
此时,如果您试图通过向期望数字的函数传递字符串来激怒 TypeScript 编译器,然后尝试提交此代码,我们的 lint 检查将阻止您的提交,并告诉您错误以及在哪里可以找到它。这样,您可以纠正错误,虽然我认为这本身就很强大,但我们还没有完成!
通过将 "jest --bail --coverage --findRelatedTests"
添加到我们对 lint-staged 的配置中,我们还确保如果任何相关的单元测试未通过,则不会进行提交。再加上 lint 检查,这相当于在修理屋顶上的破损瓦片时系上两根安全带。
如何确保所有提交信息都符合 commitlint 常规格式?提交信息不是文件,因此我们无法使用 lint-staged 处理它们,因为 lint-staged 仅对已暂存以进行提交的文件发挥作用。相反,我们必须回到 Husky 的配置并添加另一个钩子,在这种情况下,我们的 package.json 将如下所示
"scripts": {
"start": "webpack-dev-server --config ./webpack.dev.js --mode development",
"build": "webpack --config ./webpack.prod.js --mode production",
"test": "jest",
"lint": "tsc --noEmit",
"prettier": "prettier --single-quote --print-width 80 "./**/*.{js,ts}" --write"
},
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS", //Our new hook!
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"./**/*.{ts}": [
"npm run lint",
"jest --bail --coverage --findRelatedTests",
"npm run prettier"
]
}
如果您的提交信息不遵循 commitlint 常规格式,您将无法进行提交:再见,格式糟糕且模糊的提交信息!
如果您整理好您的代码,并编写了一些通过 lint 和单元测试检查的代码,并且您的提交信息格式正确,则 lint-staged 将在提交之前对已暂存以进行提交的文件运行 Prettier 脚本,这感觉像是锦上添花。此时,我认为我们可以为自己感到非常自豪;甚至有点沾沾自喜。
实施预提交钩子并不比这更复杂,但这样做的好处是巨大的。虽然我总是对在工作流程中添加另一个步骤持怀疑态度,但使用预提交钩子为我节省了无数麻烦,如果允许我以某种伪诗意的语气结束本教程,我永远不会回到在黑暗中进行提交的状态。
达雷尔,好文章。
我们的团队执行了上述所有操作,除了应用 Git 提交信息格式。
它节省了大量时间。
最困难的事情是就将哪些 lint 规则应用于我们的代码达成一致,但一旦它们设置好,此设置就可以重复用于每个项目(并在适当的地方“调整”)。
对您的 Git 提交进行 lint 是提高 Git 历史记录质量的好方法。不应低估此带来的好处。为您的团队提供可读、可调试和可搜索的提交信息,极大地提高了生产力。
顺便说一句,我想提一下,如果您使用 Ruby 或想要一个更强大的 linter,我建议您查看 Git Cop:https://github.com/bkuhlmann/git-cop。它是保持 Git 提交质量高的绝佳编码伙伴。它可以通过 Git Hooks 使用。更好的是,它可以用作 CI 构建工具!