02 — 侧 Git 快照系统:工作区回滚
侧 Git 快照系统:工作区回滚
学习目标
理解 CodeWhale 的侧 Git 快照设计:独立于用户 .git 的工作区快照、每轮前后双快照策略,以及 /restore 和 revert_turn 命令。
项目实践
快照存储路径
~/.codewhale/snapshots/<project_hash>/<worktree_hash>/.gitproject_hash:工作区路径的哈希(区分不同项目)worktree_hash:快照时间点工作区的哈希- 每个快照是一个完整的独立
.git仓库,包含完整的 commit 历史
双快照策略
每次 Agent/YOLO turn 执行:
关键:快照存储在用户 .git 之外的独立路径,不污染项目的版本控制。
回滚命令
| 命令 | 行为 | 影响会话历史 |
|---|---|---|
/restore N | 恢复到第 N 轮前的快照 | 不改写 |
revert_turn | 回滚最后一轮(使用 pre-turn 快照) | 不改写 |
Esc-Esc backtrack | 回退转记到之前的用户 prompt | 改写的仅是转记 |
区别:
/restore和revert_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
| 维度 | 侧 Git | git 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 操作封装