ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

提交信息规范化:Commitizen、husky 与 commitlint 实战

提交信息规范化:Commitizen、husky 与 commitlint 实战 干这一行久了最烦的其实不是写代码而是看 git log。一堆“修改代码”、“更新文件”、“fix bug”堆在一起主任让我查某个需求是什么时候改的我能翻半天。项目越大、人越多提交信息越乱回滚和溯源就越痛苦。后来我花了一个下午把所有队员的提交习惯统一到了一套标准格式上用的就是 Commitizen。说白了Commitizen 是一套让提交信息结构化、规范化的交互式工具它把“git commit”变成了一个带选项的引导流程告诉你该填什么、怎么填从源头杜绝了随意写提交信息的问题。这篇文章我会把从零配置到日常使用的完整流程写清楚包括不同适配器怎么选、husky 怎么配合、踩过的坑有哪些适合正在做团队规范或者被各种乱起八糟提交折磨的人。1. 为什么要做提交信息标准化1.1 混乱提交信息的真实成本先不急着说工具聊一个非常现实的问题提交信息写得不规范到底坑在哪最简单的一个场景线上出了个 bug你根据报错信息定位到某个文件然后你需要知道这段代码是什么时候被改的、为什么被改、改的人是谁。如果提交信息是“修改代码”或者“更新文件”你等于没有任何线索只能自己去翻 diff、猜逻辑。如果提交信息是“fix: 修复用户登录时验证码过期导致无法提交订单的问题”你一眼就知道这次改动涉及什么、意图是什么甚至不用打开代码。再说团队协作。一个小队五六个人还好一个人乱写也就影响自己。一旦超过十个人提交信息就是团队共同的“阅读材料”。代码 review、问题回溯、版本发布生成 changelog全都依赖提交信息的质量。一个团队如果提交风格各自为政review 起来会觉得特别累你的时间和队员的时间都在无形中被消耗掉了。Commitizen 解决的就是这个问题它强制你在提交时走一个交互流程把你写的 commit 信息拆成了多个部分每个部分都有提示和校验规则最终生成一条符合约定规范的提交记录。这种约束不是靠自觉而是靠工具流程所以只要大家统一配置了产出的提交信息格式就是一致的。1.2 一套被广泛认可的提交规范Conventional CommitsCommitizen 本身只是一个壳真正决定格式的是适配器。目前最主流的规范叫 Conventional Commits约定式提交它虽然是英文社区先兴起的但在国内团队里也非常流行。规范的核心逻辑很简单提交信息的格式大概是这样的type(scope): subject其中type表示提交的类型常见的有feat新功能、fix修复bug、docs文档、style格式调整、refactor重构、perf性能优化、test测试、chore构建或辅助工具等。scope是影响范围比如是哪个模块、哪个组件可选。subject是对改动的简洁描述。为什么这条规范能流行起来核心在于它好用、好理解、好扩展。type保证了提交信息的可分类性你可以在 git log 里用grep直接把所有 feature 找出来subject强制你用一句话把意图讲清楚写不了长篇大论。基于这条规范工具还可以自动生成 changelog、根据提交类型自动升级版本号生态非常完整。Commitizen 在交互式流程中做的事情就是逐个问你“这次提交属于哪种类型”、“影响范围是什么”、“具体改动是什么”。如果你用了对应的校验工具比如 commitlint还能在提交时再次校验防止有人绕过 Commitizen 直接git commit然后用不规范的格式提交。2. Commitizen 工具选型与方案解析2.1 Commitizen、适配器、校验器之间的关系Commitizen 不是一个大而全的单一工具而是一个工具链的组合。刚开始接触的人容易被各种名词绕晕理清它们的关系其实很简单。Commitizencz核心它替换掉git commit给你一个交互式提示界面。核心本身不带任何规范它是完全插件化的。适配器Adapter真正决定提交格式的核心。你选了 Conventional Commits 规范就装对应的适配器按提示填内容最后生成符合规范的 commit 消息。校验器Lint比如 commitlint它是审核提交信息的工具可以放在 husky 钩子里确保任何方式的提交都符合规范。它是独立于 Commitizen 的用来做最后一道防线。用生活化的话来类比Commitizen 是一台自动问答机适配器是问题清单校验器是收单处的检查员。问答机负责引导人按清单答题检查员负责确保每个人交上来的单子都符合要求。这里要特别提醒一个选择问题传统的适配器cz-conventional-changelog虽然经典但是提问组织方式有一些老了而且对新格式支持一般。现在好用的适配器我推荐cz-git它功能更强支持中文、支持自定义提示语配置也更简单生成的效果非常稳。后面我的配置示例会以cz-git为主。2.2 为什么推荐用 commitlint husky 做双保险Commitizen 只能保证“用它的提交都规范”但它管不住不用的。有队员习惯了直接写git commit -m xxx或者复制一段命令就提交了那规范还是会被打破。所以我的方案里Commitizen 负责提供正规交互通道commitlint husky 负责拦截所有通道的提交。husky 是 Git 钩子工具可以在pre-commit或commit-msg阶段执行命令。我在commit-msg钩子里挂上 commitlint 检查无论队员从哪个入口提交信息不合法就直接被拒绝。这样做的好处有几个强制力足够。提交被拦截不过关就不能 commit久而久之大家就记住了规范。审查成本低。不合规的提交直接过不了本地不会等到 push 之后才发现。自动化程度高。配置一次团队所有人都生效不用再改。有一些人觉得这种强制约束太死板但以我做团队规范管理的经验来说真正高效的团队恰恰是“在规则之上自由”。把格式的琐碎约束交给工具人脑解放出来想真正的代码逻辑反而更轻松。3. 完整实操从环境准备到团队统一配置3.1 安装依赖与初始化配置先说明一下我目前偏好的技术栈基于 Node.js 生态使用 npm 管理依赖这是 Commitizen 最常见的运行环境。第一步在项目根目录安装 Commitizen 以及其他相关依赖。通常我们会把它们放在 devDependencies 里因为它们是纯开发阶段用的工具。npm install --save-dev commitizen cz-git commitlint commitlint/cli commitlint/config-conventional husky这里有一个需要注意的版本兼容问题较新的 Commitizen比如 v4 以上和 cz-git 的配合已经比较成熟不用再像以前那样做复杂的 adapter 配置注册。如果出现版本混乱优先查看官方 README确认是否需要额外的适配器注册步骤。第二步初始化 Commitizen。如果使用cz-conventional-changelog需要执行commitizen init cz-conventional-changelog --save-dev --save-exact它会自动修改 package.json。但我用cz-git更推荐手动配置这样更可控。在 package.json 中增加一个config字段{ scripts: { commit: git-cz }, config: { commitizen: { path: node_modules/cz-git } } }在.czrc文件中项目根目录配置 cz-git 的具体行为。这里可以写得非常细包括类型的映射、emoji 开关、最大长度限制等。比如我自己的.czrc配置类似这样{ types: [ { name: feat: ✨ 新功能, value: feat }, { name: fix: 修复 Bug, value: fix } ], scopes: [common, components, hooks, router, store], useEmoji: true, maxSubjectLength: 100 }注意这个配置文件只是定义了你提问的选项。提交信息真正生效时还是需要符合 Conventional Commits 的格式这一点不能被误解。3.2 配置 commitlint 与 husky 钩子Commitizen 只是建了一条高品质的提交通道但要确保通道是“唯一通道”必须把 commitlint 加进来。在项目根目录新建commitlint.config.jsexport default { extends: [commitlint/config-conventional], rules: { subject-max-length: [2, always, 100], scope-max-length: [2, always, 15] } };配置项里的subject-max-length限制主题的字符长度防止有人把提交信息写得像一篇小作文scope-max-length限制影响范围避免出现超长模块名。根据团队习惯这些限制可以微调。接着初始化 husky并添加 commit-msg 钩子npx husky init npx husky add .husky/commit-msg npx --no -- commitlint --edit $1husky init会生成一个.husky目录。钩子脚本文件要用 shell 命令执行。关键点在于commit-msg钩子的参数是提交信息所在的临时文件我们把这个文件传给commitlint --edit它就能读取最新的提交信息并校验。做成之后测试一下效果。直接提交一条不规范的信息git commit -m 随便写了个提交提交会被拦截提示信息类似subject may not be empty或者type must be one of [feat, fix, ...]。这就是最后一道防线在生效。3.3 两种提交姿势交互式提交与直写提交配置完成之后日常开发有两种行之有效的提交姿势。第一种基于 Commitizen 的交互式提交这是主力姿势。队员只需要执行npm run commit就会看到交互式引导界面。依次选择提交类型、影响范围、编写主题、填写正文等等。每一步都有清晰的提示文案而且可以设置默认值大部分情况按回车就能快速完成。第二种如果你脑子已经有非常清晰的提交信息可以直接写标准格式的命令git commit -m fix: 换算模块金额精度问题前提是 commitlint 校验能通过。这种方式更快但不适合新手因为新手很容易漏掉 type比如写成git commit -m 修复错误这种立刻被拦截。这两种方式并存算是我项目实施时最喜欢的效果给了快速通道但校验标准统一。队员们可以根据具体情况选择自己的提交方式而不是所有人都被强制拉进交互式流程。3.4 团队落地时的几个协作要点落地到团队时有几个容易被忽略的点需要特别强调。第一个是cz-git的配置文件最好跟着项目走而不是放在个人全局。~/.czrc可能会影响多个项目但不同项目的 scopes 和 types 可能不同。推荐把 .czrc 放在项目根目录并提交到 Git 仓库这样每个人 clone 下来就能直接使用。第二个是包管理器锁版本。在 package.json 中把 commitizen、cz-git、commitlint 相关的包用精确版本号保存save-dev --save-exact避免队员安装到不同大版本导致行为不一致。我的做法是NO_SPAM就不用想了直接用 package-lock.json 锁定完整依赖树。第三个是文档同步。配置好一套工具链如果不告诉队员怎么用很容易被遗忘。建议在项目的 README 或 CONTRIBUTING 文档里增加一个小节说明提交规范、如何用npm run commit、哪些场景用哪种 type并给出几个示例。4. 常见问题与排查技巧实录4.1 Commitizen 交互界面没反应或直接退出这一类问题通常出在配置路径上。如果你选用 cz-git而 package.json 的config.commitizen.path写成了node_modules/cz-git但项目里没有安装它那么交互界面就会退出随后报Cannot find module cz-git。这种问题的排查思路很简单先确认依赖是否安装成功再确认路径是否正确。在项目根目录运行npm ls cz-git如果没有输出正确版本那就重新安装。如果路径写的是包名cz-git而不是完整路径有可能某些版本解析不到建议写成完整路径node_modules/cz-git。另外一个隐蔽的坑是如果项目里配置了type: module尾部的 ESM 模块解析逻辑可能会影响旧版本的 cz-git导致提示无法加载。遇到这种情况升级到最新版本通常就能解决。4.2 commit-msg 钩子把符合规范的消息也拦了这个问题非常经典。很多队伍配置好 commitlint 之后发现一些格式明明没问题的提交也被拒绝提示信息是 subject 为空。这往往是因为subject和type的解析顺序被打乱了。如果你写feat: 新增用户权限,但中间冒号后面有个多余的空格或者没有空格commitlint 解析就可能认为subject不合法。我实践下来最稳定的写法是标准格式feat(scope): 主题描述其中冒号后面必须有一个空格。另外配置commitlint/config-conventional时因为它内部默认要求 subject 非空所以如果你的 type 后面直接接了一个中文冒号或全角冒号也会被拦截。遇到类似问题时先手动跑一下校验命令看具体输出echo feat: 新增用户权限 | npx commitlint这样能直接看出是哪条规则不满足。这是我认为排查 commit 问题最有效的手段。4.3 同时存在多个 cz 适配器时冲突有些老项目在历史上可能已经安装过cz-conventional-changelog和cz-customizable现在又装了cz-git系统可能会弹出多个适配器选择界面或者直接默认使用第一个但生成的格式不是你想要的配置。解决办法是把 package.json 中config.commitizen.path明确指向你要用的适配器。如果在交互式界面里没有弹出来你可以删掉其他适配器只留下目标适配器。有时候适配器之间还会因为依赖了同一个底层库的不同版本而产生冲突如果出现报错建议把 node_modules 删除后重新安装一遍。4.4 Commitizen 支持的中文配置技巧很多国内团队希望交互式提问的文案是中文这样新人上手成本低。cz-git 支持高度自定义提示文案你可以在.czrc中配置messages字段例如{ messages: { type: 选择你要提交的更改类型, scope: 选择影响范围可跳过, subject: 写一个简短、明确的副标题描述, body: 补充更详细的说明可跳过 } }这样做的好处非常明显新同事不需要去查英文字典理解feat、fix的含义界面上直接展示中文问题同时保留feat这些英文类型值确保生成的提交信息符合规范不会因为中文化而破坏格式。如果你的 UI 语言包在配好之后没有生效检查一下是否把配置写进了.czrc的根字段而不是某一层嵌套字段路径错了就会静默失效。5. 更进一步生成 changelog 与版本发布5.1 规范化的数据才有自动化的资格提交信息规范化的真正红利在于你可以在此基础上搭建自动生成 changelog、自动发布版本号的流程。这是 Commitizen 配置完成后很多人会忽略的扩展价值但恰恰是最值钱的部分。主流的做法是用standard-version或semantic-release两者都能解析 Git 提交信息。从默认规则来看feat类型对应 minor 版本升级fix类型对应 patch 版本升级带有 BREAKING CHANGE 标识的提交对应 major 版本升级。以standard-version为例只需要在 package.json 中加一个 script{ scripts: { release: standard-version } }它会自动读取规范化的提交记录生成CHANGELOG.md并且根据规则自动更新version字段最后创建 tag。整个过程不需要手动改版本号团队只需要保证提交信息符合规范版本管理就被自动化了。5.2 定制你的 type 集合与提交历史分析Commitizen 的默认 type 集合是从 Conventional Commits 规范里来的但实际项目里可能有更多自己关心的类型。比如你想区分“ architecture 重构”和“局部代码整理”可以增加一个refactor和一个perf之外的style类型。但我的建议是不要加太多type 越多越难判断反而会让人选择疲劳。我更建议在 scopes 上做文章。把 scopes 定义为项目的模块清单比如components、hooks、store、router、api等。这样提交历史中你可以快速统计哪些模块改动频率最高、哪些模块最近变化比较大甚至可以在代码评审时快速筛选出某个模块的历史提交大幅提高审查效率。6. 一些值得长期坚持的提交习惯写完整个流程最后想分享几个我在实际推行标准化提交过程中的真实体会。第一标准化的过程不是一蹴而就的一定要给团队留出适应期。刚开始推 Commitizen 时总会有人忘记npm run commit直接git commit -m写一堆乱信息。这时候 commitlint 生效消息被拦截他会回来问你怎么办。你直接告诉他规范写法他改一次就记住了。大概一两周后所有入都能顺畅按规范走。第二提交信息与其写得多不如写得准确。我之前见过队员为了“显得认真”把一个一行改动写了三百字的提交信息但这并没有让 commit 更好读。好的提交信息是能让占用你两分钟后的人直接明白这是做什么的而不是展示你能写多少。Commitizen 限制 subject 长度恰好就是在帮你对抗这种废话文学。第三CI 阶段也可以加入同样的校验。本地钩子可以绕过的场景有限比如某些人会带上--no-verify参数强行跳过钩子。为了彻底堵住这个口子可以在 CI 流水线里也执行一次commitlint。虽然这一般只用在团队管理上但对规范有执着的人值得去做。第四不要把工具神化。Commitizen 能解决提交格式问题但解决不了模板化提交带来的信息熵增加。如果大家都在写fix: 修复 bug也是不符合要求的。规范的终点应该是让提交信息传递真实且有价值的改动意图。人始终是核心工具只是辅助把人的表达梳理成机器和人都能理解的结构。最后说一个具体的小技巧。如果你在 Windows 环境使用 cz-git终端可能会出现中文显示乱码这是因为 Windows 的代码页默认不是 UTF-8。在 PowerShell 里执行[Console]::OutputEncoding [System.Text.Encoding]::UTF8或者把 Windows Terminal 的默认编码改成 UTF-8中文提示就正常了。这是我踩过几次坑之后才总结出来的希望你能直接避开。
返回列表