☰
caveman:用Conventional Commits自动生成规范的Git提交信息
2026/10/8 21:32:07 网站建设 项目流程

手里的项目正好需要一个规范的 Git 提交信息,但每次手动写都觉得“都可以,但都没那么标准”。后来我找到caveman这个命令行小工具,实测用了一段时间,确实省心很多。它能把复杂的git diff自动转成 Conventional Commits 格式的提交信息,靠正则和一份关键词评分规则去判断这次改动是新增功能、修了 bug、改了文档还是整理依赖。这篇文章就从它的原理、参数、实操流程到坑位排查,完整拆一遍,方便你直接照着上手。

1. caveman 是什么,又是为谁准备的

1.1 先解决“提交信息怎么才算规范”的问题

在日常开发里,提交信息写得乱七八糟是常态。有人写fix,有人写update,还有人直接aaa或者留空。代码本身可能没问题,但过两个月回看历史,想找“那次登录模块的改动”就得翻半天。

caveman解决的就是这件事。它是一个跑在终端里的 Git 辅助命令,核心功能是帮你分析当前的代码改动,然后生成一句符合 Conventional Commits 规范的提交信息,比如feat: added support for timezone conversion或者bugfix: fixed division by zero in parser。它的名字起得很有意思:像一个穴居人那样“原始”地检查每一行改动,但产出的结果却是有结构的。

我实际用下来的感受是:它不是那种“替代你思考”的傻瓜工具,而是“帮你把散乱的改动归纳成一个可读结论”的助手。适合个人开发者、小团队、以及还没上 commitlint 这类强校验工具中间态的项目。

1.2 它为什么选择 Conventional Commits 这套规范

你可能会问:为什么是 Conventional Commits,而不是其他提交规范?这套规范的核心是让提交信息以feat、fix、docs、chore这类带明确语义的前缀开头,后面再跟一段简洁的描述。好处很明显:机器能识别,人也能看懂,市面上大多数 changelog 生成工具、语义化版本发布工具都能直接消费这类信息。

caveman的设计者把精力放在“自动判定前缀”上,而不是让你自己选。它内部维护了一套关键词和正则规则:当你改动里出现import、render、handle这类词时,它会倾向判定为feat;出现bug、fix、error时会倾向判定为bugfix;改动集中在.md、.rst、.txt时直接归为docs;剩下依赖、配置文件的变动就落到chore。这个过程有个评分机制,不是碰到某个词就立刻下结论,而是把多个证据累加起来,再挑分数最高的类型。

这套思路用“加权证据”代替“人工三选一”,我在实际用的时候觉得比自己动脑判断更稳定。因为改动一多,人的判断反而容易摇摆,机器只要规则设计合理,结论基本一致。

2. 安装与基础配置,全程五分钟搞定

2.1 不同平台的安装方式

caveman是用 Python 写的,所以最简单的路子就是用pip装。

# 全局安装 pip install --user caveman3 # 如果用 Homebrew,也可以走源码编译 brew install caveman

pip装的话,建议加--user参数,避免污染系统级的 Python 目录。装完验证一下:

caveman --version

能正常输出版本号就说明环境没毛病。要注意它依赖 Git 运行环境,所以你机器上必须先装好 Git,而且只能在 Git 仓库目录里调用它,否则它会直接报错退出。

2.2 首次运行需要先git add

这个工具的思路是“分析暂存区里的改动”,而不是整个工作区。所以正确的使用顺序是:

git add -A caveman

它会读取暂存区里所有文本文件的差异,逐行扫描改动内容,然后输出一个候选提交信息。官方推荐的做法是把它的输出直接接给git commit:

git add -A caveman | git commit -F -

-F -的意思是让 Git 从标准输入读提交信息。我试过很多次,这个组合最顺手,等于一条命令完成“分析 + 提交”。

如果你是嫌typing麻烦的人,可以在 shell 里配个 alias:

alias gc='git add -A && caveman | git commit -F -'

2.3 核心参数逐一看

caveman的参数不算多,但每个都很关键:

参数作用我的建议
--score评分的宽松程度,可选lax、medium、strict个人项目用medium,团队强校验用strict
--release-type指定项目类型,可选packagist、manifest.json等有 composer.json 的项目建议指定,能识别依赖变更
--commit-per-file为每个文件分别生成独立的提交信息改动很散时很好用,能拆出多条提交
-p额外生成一条用于标记版本号的普通提交配 release 流程时用得上
--correctly-mapped让文件名映射更准确遇到“改了几个文件却判成同一类型”时开

我第一次用的时候没有加--correctly-mapped,结果三个不同目录的文件被合并成了一条提交信息。后来发现加上这个参数,它会把文件名作为重要权重参与判断,多条改动就能分得更清楚。

3. 原理拆解:它怎么从 diff 里猜出提交类型的

3.1 从“读代码”到“读改动”:逐行扫描的机制

caveman不读整个项目,它只关心git diff --cached的结果。拿到了改动列表以后,它做的事情很像一个文本分析器:把每个文件的每个增删行拆出来,用正则去匹配关键词。

比如以下这种情况:

+function isValidEmail(email) { + return regex.test(email); +}

它的正则会发现function、return、test这类词,特征上更像“新增能力”,于是给feat加分。如果是下面这种:

-if (count == 0) { +if (count <= 0) {

正则会发现if条件的变化、边界调整,同时原行有旧逻辑被替换,这类改动会被计入 bugfix 的证据链。

这个设计的巧妙之处在于:它不看语义,只看“词频 + 位置”。因为语义理解在命令行工具里做不现实,但词频统计非常快,足够应付大多数提交场景。

3.2 四类提交的判定规则

caveman主要产出四类提交:

类型判定思路典型例子
feat出现新增模块、函数、参数、配置项等feat: added new endpoint for user profile
bugfix出现修复、边界处理、异常处理相关改动bugfix: fixed off-by-one error in pagination
docs改动集中在.md、.rst、注释块docs: updated README with setup instructions
chore依赖、构建、工具链相关修改chore: bumped lodash from 4.17.11 to 4.17.15

它内部维护了关键词库,然后通过加权评分来定最终结果。你可以把每个文件想象成一个“投票者”,每个关键词就是一张选票。得票最多的类型胜出。

3.3 评分机制:lax、medium、strict的区别

--score参数管的是“多少证据才能下结论”。我实测下来的体感是:

  • lax:只要有一两张选票就走结论,提交快,但偶尔会误判。
  • medium:需要比较充足的证据才判否定,“模棱两可”时会倾向于chore。
  • strict:必须锁定关键词,证据不足时直接生成一个通用提交信息,宁可保守也不乱猜。

我的建议是:日常开发用medium,如果团队上了commitlint,又嫌它经常拦提交,那就在生成端用strict,从源头保证信息的置信度。

3.4 与 husky、commitlint 的配合

如果你想要更严格的团队流程,可以把它和 Git 钩子组合起来。典型做法是:

  1. 开发完代码后跑caveman --score strict,拿到提交信息。
  2. 在 pre-commit 钩子里跑composer test或者npm test。
  3. 在 commit-msg 钩子里跑 commitlint,校验提交信息格式。

这样等于把提交信息的生成和校验分成了两件事,前者让机器代劳,后者让规则兜底。比我之前只用 commitlint 时“频繁手写、又被拦截”的体验强多了。

4. 实操全过程:从工作区到一条干净提交

4.1 一个真实的混合改动场景

我拿一个实际项目举例。这个项目是一个小的 Web 服务,我一次改了三个部分:

  • src/parser.js里加了一个时间解析函数;
  • README.md里补了一段部署说明;
  • package.json里升级了依赖版本。

如果按以前的习惯,我可能会写“更新了很多东西”,但这不是一个好提交信息。用了caveman之后,我是这么操作的:

git add -A caveman --score medium --correctly-mapped --release-type packagist

它给我的输出类似:

feat: added parseISODate function for timezone handling docs: updated README with deployment instructions chore: bumped dependencies in package.json

注意这里它自动分出了三条提交信息,因为每个文件的改动特征太不一样了。如果我想把它们拆成三次提交,就配合--commit-per-file参数和git commit一起用。

4.2--commit-per-file拆提交的正确姿势

git add -A caveman --commit-per-file

这个命令会遍历暂存区里的文件,然后对每个文件输出一条对应的提交信息。你可以在循环里自动提交:

git add -A caveman --commit-per-file | while read -r msg; do git commit -m "$msg" done

不过这里有个注意点:git add -A之后所有文件已经进了暂存区,如果直接循环提交,后续文件也都在暂存区里,实际上会一次全部提交。更稳妥的做法是按文件数量分开git add,或者干脆先看哪些是一组,手动分成几个批次。

我的习惯是:当改动文件少于 5 个时,直接看caveman生成的信息然后手动挑几条;改动文件多时,我用--commit-per-file配合git restore --staged来细化提交粒度的控制。

4.3 生成 release 标记的-p参数

如果你的项目使用语义化版本发布,-p参数很好用。它会在常规提交之外额外生成一条类似rebrand: Release v1.2.3的提交,用来标记发布点。很多团队的 changelog 生成工具要看这种标记。

git add -A caveman --release-type manifest.json -p

它会在生成的提交信息里带上版本信息,这样后续跑版本发布脚本时,解析 Git 历史就能准确找到上一次发版的位置。

4.4 最适合它的自动化流水线

如果你嫌每次都要手动敲组合命令麻烦,可以把它放进一个简单的脚本里。我自己的做法是建了个.git-commit.sh:

#!/bin/bash git add -A msg=$(caveman --score strict --correctly-mapped) git commit -F - <<< "$msg"

这样一个脚本就能完成绝大部分个人项目的提交需求。团队项目的话,我可以把它接到 CI 的事件脚本里,代码合并后自动生成一条汇总提交信息,省掉最后一步手动总结的力气。

5. 常见问题与排查实录

5.1 问题一:“生成的提交信息类型总是不对”

这是我收到反馈最多的场景。我排查过几次,发现多半是因为改动里同时包含了好几个语义特征。比如一个文件里既改了循环逻辑,又新增了一个辅助函数,caveman可能同时收到 bugfix 和 feat 的选票。这时最后的胜出类型取决于选票权重。

解决方法是加--correctly-mapped参数,然后检查一下项目根目录有没有.caveman.json配置文件。如果有,看看里面的关键词库是不是覆盖到了你这个项目的技术栈。比如 Go 项目的结构体定义、Rust 的 trait 实现,默认关键词肯定没有针对它们优化,所以不同类型的技术栈输出质量会有所差别。

5.2 问题二:“提交历史被误提交后怎么后悔”

caveman只是帮你生成提交信息,真正执行提交的是git commit。如果不小心把不该提交的内容一起提交了,我的习惯是先用git reset撤回来,再重新走一遍git add流程。

# 撤回最近一次提交,但保留改动内容 git reset --soft HEAD~1 # 或者连暂存一起撤回 git reset --mixed HEAD~1

这个操作并不会丢代码,只是把提交记录撤销,让你重新整理。caveman在下次运行时会重新分析暂存区,生成新的提交信息,所以不会因为误操作而“粘住”。

5.3 问题三:“中文文件内容或者路径乱码”

如果你的代码文件是 UTF-8 编码,一般没问题。但某些 Windows 环境下 Git 的默认编码可能不是 UTF-8,路径含中文时caveman的解析可能会乱。

我的建议是先在仓库里设置强制 UTF-8:

git config --global core.quotepath false git config --global i18n.logoutputencoding utf-8

这能让 Git 在输出 diff 时保留中文路径原名,caveman在解析文件名和提取关键词时,就不会因为编码问题把路径内容判进提交信息里去了。

5.4 问题四:“和 commitizen 这类工具到底选谁”

如果你之前用过 commitizen 那一套交互式问答工具,对比一下会更清楚:

工具交互方式适合场景核心区别
caveman命令行自动分析追求速度、改动频繁自动判定类型,不需要手动输入
commitizen交互提示选择团队规范培训期手动选择类型,再补充描述
commitlint钩子校验任何团队只校验不生成,范围不同

从使用体验上说,我平时一个人的项目用caveman效率高,但到了团队协作,如果大家对规范的理解还有分歧,先用commitizen引导几次,等人习惯了再切caveman提速,是一个比较平滑的路径。

5.5 问题五:“为什么它有时候会漏掉单独的文件”

caveman默认只处理 Git 能识别的文本文件。二进制文件、图片、压缩包之类的改动,它不会去“读”内容,自然也无法根据内容生成提交类型。这时它会基于文件名简单判一条chore或者忽略,具体处理方式跟版本有关。

我的做法是:如果这次提交就是换了一个图标资源,那就别纠结提交信息是不是足够“意义明确”,直接手动写一句chore: replaced favicon assets就行。工具能帮 80% 的忙,剩下 20% 还是要人来补齐。

6. 踩过几次坑之后的最终用法总结

我把caveman真正用起来,是在一次把自己 Week 前的提交历史整理得一团糟之后。当时我连续提交了几十条“update”和“fix”,到了要生成 changelog 的时候完全不知道该归到哪一类。后来用caveman重新整理以后,提交历史变得可读了,版本发布的节点也清楚很多。

如果你也想试,我推荐从最保守的组合开始:

git add -A caveman --score medium --correctly-mapped

遇到多文件改动时适当拆拆提交,别把上线的功能和依赖升级挤在同一条信息里。这个工具不复杂,但正确使用它,确实能让 Git 历史变成项目里一笔清晰的资产,而不是一笔糊涂账。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询