跳到正文
Git 工作树

如何用 Git 工作树并行运行 AI 编程智能体

为每个智能体分配一个独立的 Git 工作树:一个位于自己分支上、与仓库共享历史的独立工作目录。用 git worktree add 创建它,在里面启动智能体,工作完成后合并分支,再删除工作树。MAO 的图层在画布上完成同样的步骤,并在合并前先做检查。

  • 工作树是同一个仓库在自己分支上的第二个检出目录
  • Git 不允许在两个工作树中检出同一个分支
  • 新工作树中没有 .env、node_modules 等被忽略的文件
作者:更新于 阅读约 9 分钟

要并行运行 AI 编程智能体而不让它们互相覆盖,就为每个智能体分配一个独立的 Git 工作树:一个位于自己分支上、共享同一个仓库的独立工作目录。用 git worktree add -b 创建它,在里面启动智能体(Claude Code、Codex、Gemini CLI 或其他任何智能体),工作完成后合并分支,再删除工作树。在 MAO 中,图层就是这样一个工作树:从导航栏创建,合入前先经过检查。

什么是 Git 工作树?

工作树是挂在同一个仓库上的额外工作目录,有自己检出的分支。

Git 自己的文档是这样描述的:一个仓库可以支持多个工作树,因此你可以同时检出多个分支。git clone 或 git init 生成的检出目录是主工作树;之后添加的每一个都是链接工作树。链接工作树与仓库共享一切,只有 HEAD 和索引这类每个工作树各自的文件除外。

实际效果是:每个工作树都有自己的文件、自己的分支和自己暂存的改动,而提交、分支和对象库是共享的。在一个工作树中做的提交,所有其他工作树立刻就能看到,不需要 push 或 fetch。

为什么并行的编程智能体需要各自的工作树?

因为在同一个检出目录中的智能体会编辑同一批文件,并共用一个分支。

两个智能体在同一个目录中工作时,一个智能体改到一半的内容就是另一个智能体的起点;一次测试运行会混进两个智能体都没打算合在一起的改动;而在某个终端里执行一次 git switch,会改变所有智能体脚下的文件。每个智能体一个工作树时,每个智能体在自己的分支上编辑自己的那份文件,由你通过合并分支来决定合并哪些内容。

如何为每个智能体设置一个工作树?

在主检出目录中为每个智能体添加一个工作树和一个新分支,然后在各自的文件夹中启动每个智能体。

  1. 在主检出目录中,为每个智能体创建一个工作树和一个新分支。路径就是文件夹所在的位置;放在仓库的同级目录,可以让它不混进项目文件。
  2. 把项目运行所需的被忽略文件(例如 .env)复制进去,并在每个工作树中安装依赖。
  3. 在每个工作树中各启动一个智能体,每个使用自己的终端。
  4. 随时用 git worktree list 查看现有的工作树。
git worktree add ../myapp-api -b agent/api
git worktree add ../myapp-tests -b agent/tests

cp .env ../myapp-api/
cp .env ../myapp-tests/

cd ../myapp-api && claude      # agent 1
cd ../myapp-tests && claude    # agent 2

git worktree list

git worktree list 先列出主工作树,再列出每个链接工作树,以及它检出的提交和分支:

/home/me/myapp        d3d2dd1 [main]
/home/me/myapp-api    d3d2dd1 [agent/api]
/home/me/myapp-tests  d3d2dd1 [agent/tests]

在脚本中,git worktree list --porcelain 输出的格式在各个 Git 版本之间保持稳定。要让智能体使用一个已经存在的分支,去掉 -b 即可:git worktree add ../myapp-fix fix/login。

如何合并智能体的工作并清理?

在主检出目录中合并智能体的分支,然后删除工作树和分支。

  1. 让智能体把工作提交到它的分支上。
  2. 在主检出目录中,像合并其他分支一样合并它。
  3. 删除工作树。Git 只删除干净的工作树:没有未跟踪的文件,已跟踪的文件也没有改动。
  4. 删除已合并的分支。只要还有工作树检出着这个分支,Git 就拒绝删除它,所以要先删除工作树。
cd ../myapp
git merge agent/api
git merge agent/tests

git worktree remove ../myapp-api
git worktree remove ../myapp-tests
git branch -d agent/api agent/tests

如果 git worktree remove 因为工作树中有改动或未跟踪的文件而拒绝执行,就提交或移走你想保留的内容,或者加上 --force 丢弃它们。如果工作树文件夹是被手动删除的,git worktree list 会把它标记为 prunable,git worktree prune 会清除残留的记录(git worktree prune -n 只显示将要清除的内容)。

使用工作树时会遇到哪些问题?

大多数问题来自已经被检出的分支、Git 不跟踪的文件,以及各个工作树仍然共享的东西。

  • 同一个分支不能检出两次。 git worktree add 拒绝使用另一个工作树已经检出的分支,在工作树中执行 git switch 也一样。主检出目录所在的 main 也包括在内,所以要在主检出目录中合并,而不是把某个工作树切换到 main。用 -b 为每个智能体创建新分支。
  • 被忽略和未跟踪的文件不会被复制。 新工作树只包含 Git 跟踪的文件。.env、node_modules、构建产物和本地配置都留在它们被创建的那个检出目录中,所以要在每个工作树中复制或重新安装。
  • 被忽略的文件会被直接删除,不会询问。 只有改动过或未跟踪的文件会阻止 git worktree remove。一个工作树里如果只有你复制进去的被忽略的 .env,它会和工作树一起被删除(在 Git 2.56.0 上测试)。
  • 端口冲突。 每个工作树都是项目的完整副本,所以在其中两个里启动开发服务器,就会启动两个服务器。如果两者监听同一个固定端口,第二个会启动失败。为每个工作树设置自己的端口,例如写在它自己的 .env 中。
  • 直接删除文件夹会留下记录。 用 rm 删除工作树目录后,它的记录会留在仓库中,直到运行 git worktree prune 或被 Git 自动过期清除。请改用 git worktree remove。
  • 对子模块的支持不完整。 Git 的文档称多个检出目录仍属实验性功能,对子模块的支持不完整,并且不建议对超级项目做多个检出。包含子模块的工作树需要加 --force 才能删除。

Claude Code 会自己创建工作树吗?

会。它的工作树参数会创建工作树和分支,并在其中启动会话。

根据 Claude Code 的文档,claude --worktree feature-auth(或 -w)会在仓库根目录的 .claude/worktrees/feature-auth/ 下创建一个工作树,位于名为 worktree-feature-auth 的新分支上。在第二个终端中换一个名字再运行一次,就得到第二个隔离的会话。同一页面上的几点细节:

  • 新工作树从仓库的默认分支创建,除非 worktree.baseRef 设置为 "head"。
  • 项目根目录下的 .worktreeinclude 文件使用 .gitignore 语法,会把匹配的被 Git 忽略的文件(例如 .env)复制到每个新工作树中。
  • 退出时,未命名会话的干净工作树会连同分支一起被删除;有改动或提交的工作树会询问你保留还是删除。
  • 在 frontmatter 中设置了 isolation: worktree 的自定义子智能体,总是在自己的临时工作树中运行。

文档还建议把 .claude/worktrees/ 加入 .gitignore,这样这些工作树就不会在主检出目录中显示为未跟踪的文件。

一个检出目录、工作树、克隆还是 MAO 图层,哪种更好?

工作树和图层让每个智能体拥有自己的文件,同时共享一个仓库;克隆隔离得更彻底,但需要 push 和 fetch 才能共享工作。

方法每个智能体有自己的文件无需 fetch 即可共享提交带回工作的方式清理每个智能体的设置
一个检出目录否,所有智能体编辑同一批文件是,只有一个已经混在一起无需清理无
Git 工作树是,每个一个文件夹和一个分支是,工作树共享一个仓库git merge 该分支git worktree remove,然后 git branch -d一次 git worktree add -b
独立克隆是,每个一个完整仓库否,每个克隆都有自己的仓库和远程跟踪分支push 或 fetch 该分支,然后合并删除克隆的文件夹一次 git clone
MAO 图层是,每个图层都是位于自己分支上的 Git 工作树是,图层是同一个仓库的工作树Land… 先检查,然后合并或快进到 Ground默认在合入后归档,可以恢复New layer:一个图层名和一个分支名

MAO 的图层如何使用工作树?

MAO 中的图层是一个位于自己分支上、有自己画布的 Git 工作树,在导航栏中创建、切换和合入。

  1. 创建。 在导航栏的图层栏中选择 New layer,输入图层名和分支名。你可以复制 Ground 的布局作为起点;复制的节点以草稿开始,绕过权限的启动选项不会被复制。
  2. 工作。 在图层栏中切换到该图层,并在上面放置智能体。切换会改变当前的画布和工作树,但不会合并任何内容。图层的画布保存在 MAO 自己的状态中,而不是工作树中的文件,所以不会出现在 git status 中。
  3. 汇报。 图层上的智能体可以用 mao layer status 发布工作进展。标记为 blocked 或 review 的图层会被标记为需要关注。
  4. 合入。 切换到 Ground,在图层的菜单中选择 Land…。MAO 会检查冲突和未提交的改动,并在合并或快进之前显示目标分支。它从不重置分支、强制更改或暂存你的工作,如果检查之后有分支发生了移动,它会拒绝执行。
  5. 归档。 合入后的图层会被归档,除非你打开 Keep the layer。归档可以撤回;没有删除图层的命令。

智能体在自己的终端中发布所在图层的状态。被标记为编排者的智能体还可以创建、合入、归档和恢复图层,并把新的智能体放到某个图层上:

mao layer status --state review --progress 3/5
mao layer land "api"
mao layer archive "api"
mao layer restore "api"

常见问题

不可以。git worktree add 拒绝使用另一个工作树已经检出的分支,在工作树中执行 git switch 也会拒绝。用 -b 为每个工作树创建一个新分支。

不会。新工作树只包含 Git 跟踪的文件,所以 .env、node_modules 这类被忽略的文件需要在每个工作树中复制或重新安装。Claude Code 的 .worktreeinclude 文件可以把被 Git 忽略的文件复制到 Claude Code 创建的工作树中。

不一样。克隆是一个独立的仓库,有自己的分支和对象,克隆之间通过 push 和 fetch 传递工作。工作树共享一个仓库,所以在一个工作树中的提交会立刻在所有工作树中可见。

Git 会保留这个工作树的记录,git worktree list 会把它标记为可清理。运行 git worktree prune 清除这些记录,下次请使用 git worktree remove。

会。MAO 中的每个图层都是位于自己分支上的 Git 工作树,在你选择 New layer 并输入分支名时创建。MAO 检查冲突和未提交的改动之后,你就可以在画布上把它合入 Ground。

资料来源

检查于 2026-10-28。

有问题?可以在 Discord 提问,或发邮件到 hello@miguer.com。

在你自己的仓库里试试

把你的智能体放到同一块画布上。

7 天试用,包含画布的全部功能。无需银行卡。

下载 MAO