程云来 / 杭州
返回文章·工程实践
·约 8 分钟

Vibe Coding 里的 worktree:两个 AI,怎样各改各的代码?

只建分支为什么不够?用真实 Git 实验追踪工作目录、暂存区与共享历史,再看成果合并时冲突会在哪里出现。


设想同时开两个 AI 编程聊天:一个帮博客写小记,另一个调整网络小册。两项任务各有上下文,甚至各自约定了分支名,但工具都在同一个项目目录里工作。

前一个聊天刚改完 README,后一个又读写了同一个文件。等到提交时,文章、组件和导航混在一起,得重新辨认哪些修改属于哪项任务。

聊天分开了,磁盘上的文件却还是同一份。Git worktree 解决的正是这个问题:让同一个仓库同时拥有多个工作目录,各自编辑、暂存和提交,再明确地整合成果。

已经有分支,为什么还要多一个目录?

分支记录一条开发线当前指向哪次提交。只在原目录创建两个分支,磁盘上依然只有一份正在使用的文件;切换分支,会改变这个目录检出的内容。

Worktree 增加的是可以同时使用的工作目录。给写小记和改小册分别准备一份目录后,两个 AI 就能各自打开 README,而不用轮流切换原目录的版本。Git worktree 官方说明

这几个概念可以按职责区分:

对象保存什么在并行任务中起什么作用
分支开发线当前指向的提交给任务成果一个可引用的名字
工作目录实际可读写的文件让两项任务的编辑分别落盘
暂存区 index下一次提交准备收录的内容分开组织两项任务的提交
HEAD当前检出的分支或提交确定每份目录正在什么位置工作

通常可以让一个独立任务对应一个目录和一个分支。但 worktree 也能直接检出某次提交,处于 detached HEAD 状态;它与命名分支并不是必须一一绑定的关系。

先跑一次:同名文件会不会互相覆盖?

配套实验用真实 Git 创建一个临时仓库,把 note.txt 的初始内容提交为 draft。随后从同一个提交建立 alpha 和 beta 两个 worktree,分别写入不同内容。

在本站仓库根目录运行下面的公开入口,需要 Python 3.10+、Git 2.31+,只用 Python 标准库。2026-11-04 实测环境为 Python 3.13.3、Git 2.50.1(Apple Git-155):

bash
python3 examples/git-worktree/user_code/main.py

实验不调用模型。它按固定顺序扮演两项任务,在系统临时目录执行真实 Git 命令,完成后自动清理。实验提交使用专用虚构身份,不修改用户的 Git 配置。

两项任务提交后,文件和分支的位置是:

目录检出的分支note.txt 的内容
repomaindraft
alphatask/alphaA: protocol note
betatask/betaB: network note

脚本会输出八行 PASS。其中两个检查尤其值得看:alpha 暂存修改后,beta 的暂存区仍保存 draft;alpha 提交后,beta 已经可以读取那次提交对象,但自己的 HEAD 和文件都没有自动前进。

这两个观察分别证明了提交准备各自独立和仓库历史共享。它们来自真实本地 Git 操作,不是字典模拟,也不代表两个在线 Agent 已经完成了协作。

多份目录,怎样连到同一个仓库?

新增 worktree 时,Git 检出文件,并为它保存一份专属管理信息。新目录顶层的 .git 通常是文本文件,指向主仓库 .git/worktrees/ 下的对应位置。

例如执行 git worktree add -b task/alpha ../alpha main 后,可以把主要结构理解为:

text
repo/.git/
├── objects/                 共享的 Git 对象库
├── refs/heads/              共享的分支引用
└── worktrees/alpha/
    ├── HEAD                 alpha 的检出位置
    └── index                alpha 的暂存区

alpha/
├── .git                     指向上面的管理目录
└── note.txt                 alpha 实际编辑的文件

这里省略了其他管理文件。每份 worktree 的 HEAD、index 分开保存,提交对象和大部分引用共享,仓库配置默认也共享。因而不能把“文件独立”理解成“所有 Git 状态都独立”。结构依据见 Git 仓库布局。

配套脚本通过 git rev-parse --git-common-dir 和 git rev-parse --git-path index 核对这种关系,不硬编码管理目录的实际名称。创建多个 worktree 无需各自维护一套独立历史,但每份检出的文件、依赖和构建缓存仍然占用磁盘。

Git 默认还会拒绝把同一个分支同时检出到第二份 worktree。两个目录若都推进同一个分支引用,就会使它们各自文件和暂存区与分支位置的关系变得难以管理。并行工作通常采用不同分支,或者直接检出提交;不要靠绕过检查来组织任务。

各自改好了,为什么合并时还会冲突?

回到实验:alpha 和 beta 都修改了 note.txt 的同一行。在各自目录里编辑时,两份内容都被保留下来。等到主目录先合入 alpha,再合入 beta,Git 才需要回答:最终这一行应该采用哪个版本?

第二次合并会报告冲突。脚本用 git diff --name-only --diff-filter=U 确认冲突文件为 note.txt,再执行 git merge --abort,验证主目录恢复为合并前的 alpha 版本。这个恢复检查从干净的工作目录开始;不要把它推广成“带着任意未提交修改也一定能完整撤回”。Git merge 的冲突与恢复说明

随后脚本再次合并,按预先规定的教学决定,把两条内容都保留下来:

text
A: protocol note
B: network note

解决冲突、暂存并提交后,脚本确认两条任务分支都已成为 main 的祖先,再移除干净的 linked worktree。这个固定选择只服务于两条笔记的实验;真实代码需要根据需求决定如何组合,并重新运行检查。

因此,worktree 让两份编辑能够先各自成立,把协调放到显式整合时进行。它无法保证两项设计兼容。即使 Git 能自动合并,一项任务改了接口语义、另一项仍依赖旧语义,程序也可能出错。

AI 编程工具接上了哪一层?

有了目录,还需要让任务实际使用它。文件工具应相对于任务目录读写,启动测试和构建的命令也应设置对应的 cwd。如果聊天标着 alpha,工具却仍在主目录执行,创建 worktree 本身不会改变落点。

AI 工具可以把“选择起点、建立目录、准备环境、关联聊天、审阅成果”接成流程。以 Codex 为例,2026-11-04 核对的官方文档说明:Worktree 模式默认直接检出提交,处于 detached HEAD 状态,之后可以创建分支再提交、推送。目录隔离来自 Git,聊天与目录的关联则由应用管理。Codex Worktrees 官方文档

还要核对起点如何准备。手动运行 Git 从指定提交创建目录时,原目录尚未提交的修改、被忽略的 .env 不会顺便完整复制过去;配套实验实际验证了这两项。Codex 等工具可能另外搬运本地修改或准备环境,这属于应用附加行为,不能用来推断原生 Git 的复制规则。

开了 worktree,哪些东西仍会互相影响?

工作目录只是隔离的一部分。为博客并行启动两份开发服务时,它们仍可能都监听 3001;程序连接同一个数据库时,也仍然在修改同一份外部数据。需要为这些资源另行分配端口、数据库或测试数据。

Worktree 也不限制进程访问其他目录。AI 的文件权限、命令权限和聊天记忆,由它所处的运行环境管理。目录路径告诉工具“在哪工作”,不会自动构成安全沙箱。

收尾则要分别确认文件和提交:未提交成果是否保存,新的提交是否已经审阅、合入或明确保留。git status 干净只表示没有待提交的文件变化,不表示新提交已进入主分支。实验也验证了 Git 会拒绝直接移除有已跟踪文件修改的目录;正式任务仍需主动检查未跟踪文件、忽略文件和成果引用。

对博客这样的项目,我会把独立小记、网络章节和首页改版分配到不同 worktree。同一任务的连续调整留在原目录;一个功能如果必须先等另一个接口确定,就先确定交接约定。并行的收益来自任务能独立推进,而目录隔离让每项任务留下可单独审阅的成果。

如果还想看 Harness 怎样把任务 ID 绑定到目录,再让工具带着对应 cwd 执行,可以继续读Worktree Isolation 思想篇及其实践篇。