智能体 SaaS 沙盒 Workspace 版本管理设计

目标:用户工作运行在沙盒 workspace 中,里面可能包含代码、Word 文档、图片,甚至用户自己创建的 Git 项目。平台希望对 workspace 做版本管理,但不能让平台版本库干扰用户自己的 Git 使用。

1. 结论

推荐方案不是“自己实现一套 Git”,而是:

平台仍然使用 Git 做版本存储,但不要直接对真实 workspace 执行 git add -A每次需要快照时,先把真实 workspace 同步到一个平台侧的 shadow snapshot 目录,过滤掉用户自己的 .git 等元数据目录,然后在 shadow snapshot 上执行 Git 提交。

真实 workspace
用户可见
可包含用户自己的 .git
rsync / 过滤同步
排除 .git、缓存、大文件策略
shadow snapshot
平台内部目录
干净文件树
平台 Git commit
bare repo
用户不可见

用户在 workspace 根目录执行 git status 时,因为真实 workspace 外面没有平台 .git,所以 Git 不会把 workspace 根目录识别成一个仓库。除非用户自己在当前目录或父目录创建了 Git 仓库,否则会提示 not a git repository

2. 目录结构建议

/sandbox/workspace/                  # 用户真实工作区,用户/Agent 在这里工作
  repo-a/
    .git/                            # 用户自己的 Git 仓库
    src/main.ts
    package.json
  docs/方案.docx
  images/demo.png

/platform/snapshots/ws-123/          # 平台内部 shadow snapshot
  repo-a/
    src/main.ts                      # 注意:没有 repo-a/.git
    package.json
  docs/方案.docx
  images/demo.png

/platform/vcs/ws-123.git             # 平台内部 bare Git repo,用户不可见
/platform/objects/                   # 可选:大文件对象存储
/platform/metadata/                  # 可选:checkpoint、Agent run、nested repo manifest

关键点:平台自己的版本库不在 workspace 内,用户看不到,也不会被用户的 Git 命令误操作。平台只把清洗后的 shadow snapshot 提交到平台 Git 仓库。

3. 你的两个理解是否正确?

理解一:每次快照时,先 rsync 差额同步,再 git commit

正确。 可以这样做 MVP:

# 1. 从真实 workspace 同步到平台 shadow snapshot
rsync -a --delete \
  --exclude='**/.git/**' \
  --exclude='**/.hg/**' \
  --exclude='**/.svn/**' \
  --exclude='**/.jj/**' \
  --exclude='node_modules/' \
  --exclude='.venv/' \
  --exclude='__pycache__/' \
  /sandbox/workspace/ \
  /platform/snapshots/ws-123/

# 2. 在 shadow snapshot 上做平台 Git 提交
git --git-dir=/platform/vcs/ws-123.git \
    --work-tree=/platform/snapshots/ws-123 \
    add -A

git --git-dir=/platform/vcs/ws-123.git \
    --work-tree=/platform/snapshots/ws-123 \
    commit -m "checkpoint after agent run"

rsync -a --delete 的作用是让 shadow snapshot 与真实 workspace 的“可保存文件集合”保持一致。用户删除的文件,也会在 shadow snapshot 中删除,然后 Git commit 会记录删除。

理解二:用户在 workspace 根目录调用 git status,不会看到平台 Git

正确。 只要你满足这几个条件:

因此用户在 workspace 根目录执行:

cd /sandbox/workspace
git status

大概率会看到类似:

fatal: not a git repository (or any of the parent directories): .git

但是如果用户进入自己创建的 Git 项目:

cd /sandbox/workspace/repo-a
git status

那么会正常显示 repo-a 这个用户项目自己的 Git 状态。

4. 为什么不要直接对真实 workspace 执行 git add -A?

即使平台 .git 在 workspace 外部,直接对真实 workspace 执行:

git --git-dir=/platform/vcs/ws-123.git \
    --work-tree=/sandbox/workspace \
    add -A

仍然可能遇到问题。因为真实 workspace 里面可能有用户自己的 Git 项目:

/sandbox/workspace/
  repo-a/
    .git/
    src/main.ts
    package.json

Git 看到 repo-a/.git 时,可能会把 repo-a 识别成 embedded repository,类似 submodule 的语义。这样平台 Git 可能不是递归保存 repo-a/src/main.ts,而是记录一个 commit 指针。

这与平台想要的语义不同。平台想保存的是“用户当前看到的文件系统状态”,而不是“用户内层 Git 仓库的某个 commit 指针”。用户可能还没 commit、没 push、有未跟踪文件,直接记录 Git 指针会带来恢复不完整的风险。

5. 嵌套 Git 项目的默认处理策略

对用户 workspace 里的 Git 项目,建议默认保存工作区内容,但不保存 .git 历史。

内容默认是否保存原因
代码文件、配置文件、README保存这是用户当前可见、可编辑的工作成果。
Word、PPT、图片、PDF保存属于 workspace 成果,需要支持回滚。
.git/objects.git/config、reflog、stash默认不保存可能包含敏感历史、token、大对象、用户私有 Git 状态。
node_modules.venv、build output通常不保存可重建,体积大,容易污染版本库。
大文件、二进制文件保存,但可走对象存储Git 原生不适合大量二进制版本增量。

可以额外记录一份 nested repo manifest:

{
  "checkpoint_id": "ckpt_2026_06_26_001",
  "nested_git_repos": [
    {
      "path": "repo-a",
      "branch": "main",
      "head": "abc1234",
      "dirty": true,
      "has_untracked_files": true,
      "remote_host": "github.com",
      "git_history_saved": false
    }
  ]
}

这样用户回滚时,可以明确提示:平台恢复了工作区文件内容,但默认没有恢复用户项目自己的 Git 历史。

6. 快照流程设计

  1. Agent 任务开始前,创建 before checkpoint
  2. Agent 在真实 workspace 中执行工作。
  3. 任务完成后,创建 after checkpoint
  4. checkpoint 服务用 rsync 把真实 workspace 同步到 shadow snapshot。
  5. 同步时排除用户 Git 元数据、缓存目录、临时文件。
  6. 在 shadow snapshot 上执行 Git add 和 Git commit。
  7. 记录 metadata:task id、actor、时间、嵌套 Git 项目状态、大文件索引。
  8. 前端展示本次 checkpoint 的文件变更和内容 diff。
create_checkpoint(workspace_id, reason):
    workspace = "/sandbox/workspace"
    snapshot = f"/platform/snapshots/{workspace_id}"
    repo = f"/platform/vcs/{workspace_id}.git"

    scan_nested_git_repos(workspace)
    rsync_filtered(workspace, snapshot)
    git_add_all(repo, snapshot)

    if git_has_changes(repo, snapshot):
        git_commit(repo, snapshot, message=reason)
        save_metadata()
    else:
        save_noop_checkpoint_metadata()

7. 回滚流程设计

不建议在用户正在使用的 workspace 上直接粗暴覆盖。更稳的方式:

  1. 从平台 Git 指定 commit checkout 到临时恢复目录。
  2. 从对象存储恢复大文件。
  3. 比较当前 workspace 与目标版本之间的差异。
  4. 向用户展示会新增、删除、覆盖哪些文件。
  5. 用户确认后,将目标版本同步回真实 workspace。
restore_checkpoint(workspace_id, commit):
    target = f"/platform/restore-tmp/{workspace_id}/{commit}"
    workspace = "/sandbox/workspace"
    repo = f"/platform/vcs/{workspace_id}.git"

    git_checkout_to_temp(repo, commit, target)
    restore_large_objects(target)
    preview_diff(workspace, target)

    # after confirmation
    rsync -a --delete target/ workspace/

产品上建议提供两种操作:

8. 大文件、Word、图片的处理建议

Git 可以保存二进制文件,但长期大量保存 Word、PPT、图片、PDF、压缩包会让仓库膨胀。建议分层处理:

文件类型存储策略Diff 策略
代码、Markdown、JSON、YAML直接进入 Git文本 diff
Word、PPT小文件可直接进 Git,大文件进对象存储提取文本后做语义/文本 diff,同时保留原文件
图片对象存储或 Git LFS-like 存储before/after 预览,hash 或 perceptual hash
PDF对象存储或 Git LFS-like 存储文本型 PDF 做文本 diff;扫描型 PDF 做页面图像 diff
node_modules、venv、build默认忽略不展示 diff

9. 高级功能:保存用户自己的 Git 历史

默认不保存用户项目里的 .git 是为了安全和容量。但可以提供高级选项:

Workspace 设置:
[ ] 保存嵌套 Git 仓库历史
[ ] 保存 remote 信息,自动脱敏 token
[ ] 保存 stash
[ ] 保存 ignored files
[ ] 保存 build output / node_modules

如果用户打开“保存嵌套 Git 仓库历史”,可以对每个内层 Git 项目额外生成 bundle:

cd /sandbox/workspace/repo-a
git bundle create /platform/git-bundles/ws-123/repo-a.bundle --all

或者将 .git 压缩、加密后放进对象存储。恢复时再解包。

这个能力不建议默认开启,因为 .git 里可能包含历史敏感文件、remote token、hooks、stash、大对象等。

10. MVP 实现建议

  1. 每个 workspace 创建一个平台 bare Git repo。
  2. 每个 workspace 创建一个 shadow snapshot 目录。
  3. 每次 checkpoint 用 rsync -a --delete 同步。
  4. 排除 **/.git/**node_modules.venv、临时文件。
  5. 对 shadow snapshot 执行 git add -Agit commit
  6. 记录 checkpoint metadata,包括 task id、actor、时间、commit id。
  7. 前端先做文件级 diff,代码文件再做文本 diff。
# 初始化
git init --bare /platform/vcs/ws-123.git
mkdir -p /platform/snapshots/ws-123

# checkpoint
rsync -a --delete \
  --exclude='**/.git/**' \
  --exclude='node_modules/' \
  --exclude='.venv/' \
  /sandbox/workspace/ \
  /platform/snapshots/ws-123/

git --git-dir=/platform/vcs/ws-123.git \
    --work-tree=/platform/snapshots/ws-123 \
    add -A

if ! git --git-dir=/platform/vcs/ws-123.git \
         --work-tree=/platform/snapshots/ws-123 \
         diff --cached --quiet; then
  git --git-dir=/platform/vcs/ws-123.git \
      --work-tree=/platform/snapshots/ws-123 \
      commit -m "checkpoint"
fi

11. 关键注意事项

12. 一句话总结

平台用 Git 管理“清洗后的 workspace 快照”,而不是直接管理真实 workspace;用户自己的 Git 项目可以照常存在于 workspace 中,平台默认保存其当前文件内容,但不保存其 .git 历史。

这不是重写 Git,而是在 Git 前面加一层 snapshot filter,使平台版本管理语义与用户实际工作语义保持一致。