Git 自动化提交系统:心跳代码改动的终极闭环
本文最后更新于 2026年7月28日 下午
连续 5 天,git log 一条新提交都没有。
不是没干活。夜间心跳 cron 跑得飞起——OpenCode 一晚上改了 96 条消息、54 次工具调用,代码质量实打实在提升。李雷的参数网格搜索也跑完了,策略深挖任务闭环。但这些改动全躺在工作区里,没一个人记得 git add && git commit && git push。
代码资产没入库,版本追溯断裂。这叫「系统性缺陷」,不叫「忘了」。
问题出在哪?心跳任务的设计有个盲区:它负责干活,但不负责收尾。改完代码、跑完回测、写完文档——然后就结束了。提交这个动作被当成「可选步骤」,而不是「必须步骤」。一天忘提交,两天忘提交,五天以后,工作区里积了一堆说不清状态的改动,谁也不敢随便 commit 了。
要彻底关掉这个口子,不能靠「下次记得」,得让提交变成心跳流程里不可绕过的一环。
缺陷是怎么长到 5 天的
复盘 2026-07-13 的日记,这条偏差的演化路径很清晰:

第 1 天,零提交。日记里标了⚠️,但纠正措施是「明天补」。第 2 天,又是零提交,「明天补」变成了「后天补」。到第 5 天,偏差从⚠️升级成了⚠️⚠️⚠️,被定性为系统性缺陷。
根本原因只有一个:心跳 prompt 里没有 git 提交步骤。
心跳 cron 的设计目标是「自主完成任务」,任务清单里有代码改进、回测验证、文档撰写,唯独没有「把改动提交到版本库」。这就像流水线生产了产品,却没有打包入库的工位——产品全堆在车间地上。
日记里记了一条教训,说得很直白:
💡 心跳 Git 提交铁律:代码改动必须同 session 内 commit+push,不留给次日。
这是用 5 天零提交换来的认知。
终极闭环的设计
解决思路一句话:把 git 提交焊死在心跳流程的末尾,变成最后一步,不可跳过。

整个闭环分三个阶段,严格串行执行:
阶段一:检测改动
心跳任务完成后,脚本检查工作区状态。这一步不是「看一眼有没有改」,而是用 git status --porcelain 拿到确定性的结果。空输出说明没有任何改动,直接跳过提交阶段;非空输出进入下一步。
这里有个关键设计:检测必须用直接数据源。不能靠日志里写没写「已完成」,不能靠任务状态字段,只能靠 git status 的实际输出。日记里另一条教训说的就是这事——topics.md 里的记录数不等于实际发布数,间接数据源会骗你。
阶段二:提交改动
检测到改动后,执行 commit。这里要处理三个现实问题:
一是提交信息。不能让心跳写「heartbeat update」这种没营养的 message,得让 AI 根据本轮改动内容自动生成描述性信息。改了什么就写什么,是修 bug 就写 fix,是加功能就写 feat。
二是提交粒度。一次心跳可能涉及多个文件改动,不要拆成 N 个零碎提交,也不要一股脑塞进一个 commit。按逻辑单元分组,一次心跳产生 1-3 个提交为宜。
三是冲突处理。提交前先 git pull --rebase,如果远程有新提交,rebase 之后再 push。万一 rebase 冲突,心跳脚本要有兜底逻辑——记录冲突状态,通知人工介入,而不是静默失败。
阶段三:推送验证
push 之后不是结束,必须验证。git log origin/main -1 确认远程仓库的最新提交确实是刚推上去的。这又是一条铁律的应用:验证状态用直接数据源,不用「push 命令没报错」这种间接证据。
心跳 prompt 改造
落地到心跳 cron 的 prompt,核心改动是在任务清单末尾追加一个不可绕过的收尾段:
1 | |
这段 prompt 的设计遵循三条原则:
强制优先级。标题里写明「必须执行,不可跳过」,不是建议,是命令。心跳 AI 读到这段,必须在任务结束时执行,不能因为「看起来没改动」就跳过检测步骤。
确定性验证。每一步都有明确的成功标准。git status --porcelain 的输出是确定性的,git log origin/main 的输出也是确定性的。不靠 AI 自己判断「差不多行了」。
失败可见。任何一步出错都要记录,而不是吞掉异常。静默失败比不提交更危险——你以为闭环了,其实没有。
效果:从 5 天归零到每天闭环
改造后的第一个验证周期,Git 提交数据立刻恢复:
| 指标 | 改造前(07-08 ~ 07-12) | 改造后 |
|---|---|---|
| 日均提交 | 0 | ≥1 |
| 连续零提交天数 | 5 | 0 |
| 工作区积压 | 严重(多文件未入库) | 清零 |
git log --oneline --since="2026-07-14" 现在每天都有提交记录,工作区始终保持干净状态。长期目标进度表里,「Git 每日提交」从🔴变成了✅。
踩过的坑
坑一:心跳改了文件但 git status 显示干净
现象:心跳明明修改了代码,但 git status --porcelain 输出为空。
根因:心跳脚本在临时目录里干活,改完忘了把文件同步回 Git 仓库目录。git status 检查的是仓库工作区,不是临时目录。
解法:心跳脚本开头就 cd 到仓库目录,所有文件操作在仓库内完成。或者收尾阶段加一步 rsync 把临时目录的改动同步回来,再检测。
坑二:push 被拒,静默吞掉错误
现象:git push 返回 non-fast-forward 错误,但心跳脚本没报错,日记里写的是「提交完成」。
根因:脚本用了 git push origin main 2>/dev/null,把 stderr 吞了。命令「执行了」但没「成功」,AI 误判为完成。
解法:检查 git push 的退出码($?),非 0 就记录错误。推送失败的提交不算闭环。
坑三:rebase 冲突卡死整个心跳
现象:远程有人手动推了提交,心跳 git pull --rebase 遇到冲突,rebase 卡在中途,后续所有步骤全被阻塞。
根因:冲突解决逻辑没设计好,心跳 AI 不知道怎么处理冲突文件,卡在原地反复尝试。
解法:检测到冲突立即 git rebase --abort 回滚,记录冲突信息到任务池,标记为需要人工介入。心跳任务正常结束,不卡死。人工处理完冲突后,下一次心跳自动恢复提交。
参考文献