跳转到内容

02 — 侧 Git 快照系统:工作区回滚

侧 Git 快照系统:工作区回滚

学习目标

理解 CodeWhale 的侧 Git 快照设计:独立于用户 .git 的工作区快照、每轮前后双快照策略,以及 /restorerevert_turn 命令。

项目实践

快照存储路径

~/.codewhale/snapshots/<project_hash>/<worktree_hash>/.git
  • project_hash:工作区路径的哈希(区分不同项目)
  • worktree_hash:快照时间点工作区的哈希
  • 每个快照是一个完整的独立 .git 仓库,包含完整的 commit 历史

双快照策略

每次 Agent/YOLO turn 执行:

关键:快照存储在用户 .git 之外的独立路径,不污染项目的版本控制

回滚命令

命令行为影响会话历史
/restore N恢复到第 N 轮前的快照不改写
revert_turn回滚最后一轮(使用 pre-turn 快照)不改写
Esc-Esc backtrack回退转记到之前的用户 prompt改写的仅是转记

区别

  • /restorerevert_turn 恢复文件状态,不改写对话历史
  • Esc-Esc 在 TUI 中回退转记,把 prompt 放回 composer 供编辑

快照生命周期

v0.8.46+ 增加了:

  • 快照标签包含来源用户 prompt,便于识别
  • 快照孤儿清理(orphan cleanup)
  • npm install 保护(防止误删快照)

与用户 .git 的交互

当工作区本身是一个 Git 仓库时:

  • 侧快照不影响用户的 .git 历史
  • Agent 修改文件后,用户的 git diff 能看到变化
  • /restore 恢复文件但不 git reset 用户的仓库

实现路径

  • crates/tui/src/tools/revert_turn.rs — 回滚工具实现
  • crates/tui/src/tools/git.rs — Git 操作封装
  • 快照使用独立的 Git 仓库而非 git stash/git diff

问题与规避

陷阱:快照占用磁盘空间

每轮两个快照,每个快照是一个完整的 Git 仓库。长会话中快照数量可能达到数百个。

规避

  • 快照包含去重的 Git 对象——相同文件内容不重复存储
  • 孤儿清理机制检测并删除不再引用的快照
  • 用户可手动清理 ~/.codewhale/snapshots/ 目录

陷阱:大文件仓库快照缓慢

对于包含大量文件(如 node_modules)的仓库,快照创建可能很慢。

规避

  • Git 快照只记录变化的文件,不是全量复制
  • 初始快照可能需要较长时间,后续快照是增量的
  • 建议用户在工作区中 .gitignore 大目录

陷阱:快照与 .gitignore 冲突

侧快照使用独立的 Git 仓库,用户的 .gitignore 不会自动应用到快照中。

规避:快照仓库有自己的 .gitignore,排除常见的构建产物和依赖目录。

设计取舍

为什么用侧 Git 而非 git stash/diff

维度侧 Gitgit stash/diff
污染用户仓库是(stash 列表膨胀)
完整历史是(每个快照完整 commit)否(diff 可能不完整)
跨仓库是(非 Git 仓库也可快照)否(需要 Git 仓库)
实现复杂度中(独立仓库管理)低(现有 Git 功能)
恢复粒度轮次级(按 turn 编号)diff 级

CodeWhale 选择侧 Git 的核心原因:不污染用户的 .git。对于活跃的 Git 仓库,频繁 git stash 会导致 stash 列表膨胀到不可用。

为什么保留 pre 和 post 两个快照

只保留 post-turn 快照无法回滚到 turn 前的状态(如 Agent 做出了错误修改)。pre-turn 快照提供了安全的回滚点。

代价是存储空间翻倍,但 Git 对象去重减少了实际增量。

参考来源

  • README: “Every turn records a side-git snapshot outside your repo’s .git
  • crates/tui/src/tools/revert_turn.rs — 回滚工具实现
  • crates/tui/src/tools/git.rs — Git 操作封装