Git Hooks 驱动的文档同步

2 min read
Zekari
Git自动化工程实践

文档在提交时就已经过时了

代码提交的那一刻,文档就开始偏离真相。

不是所有文档都会立即过时。类型定义和函数签名随代码一起变化,它们是活的。但那些从代码生成的参考文档——API清单、组件列表、数据库Schema——它们需要额外的步骤才能更新。

这个额外的步骤是问题所在。

如果依赖人记住"提交代码后运行文档生成命令",这个系统已经失败了。记忆靠不住,纪律是稀缺资源。任何需要人主动记住的流程都会在某个疲惫的深夜被遗忘。

文档的三种生命形态 告诉我们哪些文档可以自动化。但如何自动化?触发点在哪里?

Git Hooks 是最近的触发点

自动化需要触发器。文档更新需要知道"代码变了"这个事实。

有几种可能的触发点:

CI/CD 流程中生成文档。 代码推送到远程后,CI 系统检测变更、运行测试、构建部署。这时候生成文档,然后推回仓库。

问题是距离太远。代码已经离开本地,到了远程服务器。如果文档生成失败,开发者不会立即知道。如果生成的文档需要修改,又要额外的提交。这个循环拉长了反馈时间。

定时任务自动生成。 每天定时扫描代码库,重新生成所有文档。

问题是频率不对。每天一次太慢,每次提交代码都可能让文档过时。每小时一次又太浪费,大部分时间代码没变。

Git Hooks 在提交时触发。 代码提交前或提交后,本地自动运行文档生成脚本。

这是最近的触发点。代码刚变化,文档立即更新。开发者还在上下文中,能立即看到生成结果。如果有问题,还能修改后重新提交。

💡 Click the maximize icon to view in fullscreen

设计一个基于 Hooks 的文档系统

核心思路很简单:在 Git Hooks 中插入文档生成逻辑。

第一层:检测变更。 不是每次提交都需要更新所有文档。如果这次提交只改了 README,不需要重新生成 API 文档。

检测哪些文件变了。如果是组件文件,触发组件文档生成。如果是数据库迁移,触发 Schema 文档更新。如果是普通 markdown,跳过。

这需要一个映射关系:文件类型 → 文档生成器。

第二层:并行生成。 文档生成很慢。扫描所有代码文件、解析注释、渲染模板,可能需要几十秒。

如果顺序执行,提交过程会被卡住。开发者等得不耐烦,下次就会跳过 Hooks。

并行执行多个生成器。API 文档和组件文档同时生成,数据库文档独立运行。只要机器有多核,就能节省时间。

第三层:失败处理。 文档生成可能失败。代码注释格式不对,生成器报错,网络请求超时。

失败了怎么办?

阻止提交太严格。一个小的文档错误不应该阻止代码提交。但完全忽略又太松散,错误会堆积。

折中方案:显示警告,但允许继续。记录失败日志,开发者可以稍后修复。对于关键文档(比如 API 参考),可以设置为强制通过。

第四层:自动添加。 文档生成后,需要加入提交。

这一步容易被忽略。如果生成器只是更新了文件,但没有 git add,这些变更不会被包含在提交中。下次提交时又会重新生成,陷入循环。

Hooks 脚本需要显式地把生成的文档添加到暂存区。这样它们和代码一起被提交,保持同步。

💡 Click the maximize icon to view in fullscreen

Pre-commit 还是 Post-commit

Hooks 有不同的时机。Pre-commit 在提交前运行,Post-commit 在提交后运行。

Pre-commit 的好处是文档和代码在同一个提交中。开发者看到的历史是干净的,每个提交都包含对应的文档更新。

坏处是拉长了提交时间。如果文档生成需要 30 秒,开发者要等 30 秒才能完成提交。这会破坏流畅性。

Post-commit 的好处是不阻塞提交。代码先提交,文档在后台生成,完成后自动创建一个新的提交。

坏处是历史变得复杂。代码提交后面总是跟着一个"Update documentation"的提交。如果频繁提交,这些文档提交会让历史变得嘈杂。

我选择 Pre-commit。

理由是语义完整性。一个提交应该包含它引起的所有变更,包括文档。分裂成两个提交会让审查变得困难——你需要同时看两个提交才能理解完整的变更。

至于速度问题,通过并行生成和智能检测可以缓解。大部分提交不会触发所有文档生成器,实际等待时间可以控制在可接受范围内。

Post-commit 并非完全不可用。在某些情况下它是更好的选择:

  • 文档生成非常耗时(超过 1 分钟)
  • 团队提交频率极高,流畅性优先于历史清晰度
  • 使用 Squash Merge 策略,最终合并时历史会被压缩

选择哪种时机取决于团队的工作方式和优先级。

这个系统的局限

Git Hooks 不是完美方案。它有明显的局限。

第一,本地依赖。 Hooks 运行在开发者机器上,依赖本地环境。如果文档生成器需要某个工具,每个开发者都要安装。环境不一致会导致生成结果不同。

缓解方法是容器化。把生成器打包成 Docker 镜像,Hooks 调用容器而不是直接运行脚本。这增加了复杂度,但提高了一致性。

第二,跳过机制。 Git 提供 --no-verify 选项跳过 Hooks。开发者赶时间或遇到问题时会使用它。

这没有技术手段可以完全阻止。最好的方式是让 Hooks 足够快、足够可靠,减少跳过的动机。

第三,初次配置成本。 新加入团队的开发者需要手动启用 Hooks。Git 不会自动运行仓库中的 Hooks 脚本,出于安全考虑。

需要一个安装脚本,在项目初始化时提示开发者运行。这个脚本配置 Git Hooks 路径,检查依赖,确保环境就绪。

第四,生成速度。 即使并行执行,文档生成仍需时间。项目越大,代码越多,生成越慢。

这是根本限制。唯一的解决方法是增量生成——只更新变化部分的文档。但这需要生成器支持增量模式,增加了实现复杂度。

自动化不是目的

这套系统解决了文档过时的问题。但它不能解决文档质量的问题。

生成的文档只和代码注释一样好。如果注释缺失或不准确,生成的文档也是无用的。自动化只是把手工过程变成了机器过程,不能创造原本不存在的信息。

所以这套系统的前提是代码中有足够的文档注释。如果团队不写注释,自动生成器没有输入,系统无法运行。

写好注释依然需要纪律。但至少,一旦注释写了,文档更新不再需要纪律。这减少了一个失败点。

设计的本质是减少失败点

完美的系统不需要纪律。它应该自然地引导人做正确的事,而不是依赖人记住做正确的事。

Git Hooks 文档同步系统不完美。它有性能问题,有环境依赖,有跳过风险。

但它把文档更新从"记住要做的事"变成了"自动发生的事"。这减少了一个关键的失败点。

系统设计不是追求零缺陷。是识别最脆弱的环节,用自动化替换人的记忆。

Related Posts

Articles you might also find interesting

用 AI Agents 加速测试环境配置

3 min read

测试环境的配置是重复的琐事。环境变量、测试数据库、配置文件——这些步骤消耗时间但不产生直接价值。AI agents 改变了这个等式。

Claude Code测试
Read More

在Claude Code中写单元测试:简单高效的实践

2 min read

测试不是负担,是对话。Claude Code改变了测试的成本结构,让测试回归本质:验证行为,而非追求覆盖率。

Claude Code测试
Read More

让文档跟着代码走

2 min read

文档过时是熵增的必然。对抗衰败的方法不是更频繁的手工维护,而是让文档"活"起来——跟随代码自动更新。三种文档形态,三种生命周期。

文档软件工程
Read More

使用Jest或Vitest作为测试框架有什么区别?

1 min read

测试框架的选择不是功能列表的比较,而是关于工具哲学的选择。Jest代表完整性,Vitest代表原生性。

测试工程实践
Read More

分层修复

3 min read

生产问题没有银弹。P0 止血,P1 加固,P2 优化。优先级不是排序,而是在不确定性下的决策框架。

工程实践问题修复
Read More

查询先于假设

3 min read

数据库迁移后,所有功能失效。问题不在迁移本身,而在假设。真相只存在于查询结果中。

数据库迁移
Read More

缺少Jest依赖时的测试选择

4 min read

测试不一定需要框架。有时候最简单的工具已经足够。Node.js内置的assert模块和基础测试能力,往往比庞大的测试框架更清晰。

测试工程实践
Read More