git blame 能告诉你某一行代码是谁在什么时候改的,但它从来不回答一个更关键的问题:为什么这么改。尤其是当提交者是一个 AI 代理时,这个“为什么”往往只存在于一段很快就会被关掉的聊天记录里。

我最近做了一个小工具,叫 git why。它做的事情很直接:把一次提交背后的推理过程,作为 git trailer 存进提交信息里。没有服务器,没有数据库,克隆仓库时这些“为什么”会跟着代码一起走。

问题出在哪

我现在大部分代码是这样写出来的:我把需求描述给 AI 代理,它生成代码,我审一遍,只要没有明显错误就提交。这个过程确实快,我不否认。

但这个循环里有一个洞,我花了很久才真正说清楚它是什么。一次提交记录的是“改了什么”。git blame 能告诉你“谁改的”,精确到时间戳。可这两者都不记录“为什么”。为什么选这个方案而不是另一个?我们一开始试过什么又放弃了?在代理把需求变成 diff 之前,我到底用自己的话说了什么?

这些推理确实存在过——大概二十分钟。然后聊天窗口一关,它就没了。不是被删掉,只是变得不可达,埋在一个我永远不会再打开的对话记录里。三个月后,我盯着某个函数想不通它为什么长成这样,跑一下 git blame,答案回来了:“你,四月份改的。”行吧,谢谢,真有用。

这不是工具缺口,是记忆缺口。代码库什么都记得,唯独忘了我回来时最需要的那一样东西。

我坚持的几个条件

动手之前我给自己定了几个不能让步的底线,因为我看过太多次“给 X 加个系统”最后变成新的维护负担:

  • 推理要附着在提交本身,不能放在一个需要我手动同步的旁路数据库里
  • 不能有需要跑起来的服务,如果需要守护进程,我就不做了
  • 要能在我本来就在用的工具里看到,比如 git log,不用学新东西
  • 必须能跟着克隆走。如果推理不随仓库一起传播,那它就不是基础设施,只是笔记

最后一条几乎排除了我看过的所有“AI 决策日志”工具。有些做得挺好看,但如果“为什么”住在一个托管面板里,而代码住在 git 里,你就等于造了两个真相来源,只要没人盯着,它们迟早会漂移。而总有人不会盯着。

git 早就给了原语

其实 git 很久以前就解决了“在提交上挂结构化元数据”这个问题。它叫 trailer——就是那些你在提交信息底部见过但没多想的 Key: value 行,比如 Co-Authored-By:Signed-off-by:Reviewed-by:

git 自带处理这些的工具,开箱即用。git interpret-trailers 就是干这个的。所以 git why 没有发明什么新机制,它只是把“提交背后的推理”这件事,塞进了 git 本来就会保留、本来就会随克隆传播的那个位置。

用法也很简单:提交时用 git why 记下推理,之后想看某段代码为什么是现在这样,跑 git why 就能读回来。零依赖,一个 CLI,没有新概念要学。