Git Vibe Coding 可交互

Git Worktree:从临时热修到 Agent 并行开发

2026年9月15日 约 5,000 字 约 17 分钟阅读

你正在重构登录页,二十多个文件已经改到一半,测试还没恢复。线上突然传来消息:支付按钮坏了,需要马上修。

如果只有一个项目目录,你大概会先停下重构,检查有没有漏掉的未跟踪文件,再 stash、切到 main、拉出热修分支。修完以后,还要按相反顺序把刚才的现场拼回来。另一种办法是重新 clone 一份仓库,但下载、配置和目录管理又显得很重。

Git worktree 提供了第三条路:保留眼前这个凌乱却完整的现场,在旁边再摆一张干净的工作台。

git worktree add -b hotfix/payment ../shop-hotfix main

运行以后,../shop-hotfix 是一个新的目录,里面检出了从 main 出发的 hotfix/payment 分支。原来的登录页改动没有被收起,也没有被覆盖。你可以打开第二个编辑器窗口去修支付问题,结束后再回到原目录,屏幕上的文件仍停在离开时的样子。

这就是 worktree 最初最朴素的价值。十多年后,当同时工作的不再只是“你上午的任务”和“下午插进来的热修”,而是三四个会自主改文件、跑测试、提交代码的 Coding Agent,这张额外的工作台突然从小众技巧变成了并行开发的基础设施。

一、先分清仓库、分支与工作树

Worktree 直译是“工作树”。这个名字容易让人以为它是一种新的分支,或者仓库的完整副本。它其实更接近某个版本在硬盘上展开后的可编辑现场

一个普通的非裸 Git 仓库里,平时就已经有一棵工作树:你能在编辑器里看到的源代码、图片和配置文件。git clonegit init 创建的这一个,官方称为 main worktree,也就是主工作树。通过 git worktree add 增加的目录叫 linked worktree,也就是关联工作树

它们的关系可以先看成“一间档案室,连接多张书桌”。点击下面任一目录,只观察哪些东西共享,哪些东西独立。

ONE REPOSITORY · THREE DESKS

一个仓库怎样长出多个工作目录

点击任一目录查看
共同仓库 · common Git directory 对象库 · refs · 分支历史 · 配置

提交一次,其他 worktree 立即能看见这个提交和分支移动。

这个目录独有文件现场 · HEAD · index
眼前状态稳定版本
当前分支main
这个目录独有文件现场 · HEAD · index
眼前状态登录页改动
当前分支feature/login
这个目录独有文件现场 · HEAD · index
眼前状态支付修复
当前分支hotfix/payment
关键边界:worktree 共享版本历史,但不共享“桌面上的文件现场”。它不是仓库的三份完整复制品,也不是同一目录来回换分支。

这里有三个容易混在一起的概念:

  • Repository,仓库:保存提交对象、分支引用和版本历史的共同档案室。
  • Branch,分支:一个会随提交向前移动的名字,例如 mainfeature/login。它指向历史中的某个提交,不是一份独立文件夹。
  • Worktree,工作树:一个真实目录,里面有某个提交展开后的文件,以及这个目录自己的当前分支、HEAD 和暂存区。

因此,“创建分支”和“创建 worktree”做的事不同。分支给历史增加一个可移动的路标;worktree 给这个路标安排一张可以立即动手的物理工作台。

共享与独立同时存在

多个 worktree 共享同一个对象库和大部分 refs。你在 shop-hotfix/ 中提交一次,新的 commit 对象和 hotfix/payment 分支移动会立刻进入共同仓库;回到 shop/ 运行 git log --all,已经可以看到它。

但每棵 worktree 都有自己的:

  • 工作目录中的文件状态;
  • HEAD,也就是当前查看的位置;
  • index,也就是通常所说的暂存区;
  • 一部分正在进行的操作状态,例如 rebase 或 bisect。

这解释了为什么两个目录可以分别保留未提交修改,也解释了 Git 为什么默认不允许同一个本地分支同时被两个 worktree 检出。如果两张书桌同时推动同一个分支名,分支究竟应该跟着哪一边移动会变得含糊。Git 用这一限制提前挡住了危险状态。

一句话收束:仓库负责共享历史,worktree 负责隔离现场,分支负责标记每项工作的历史方向。

二、Worktree 从边缘脚本走进 Git

Worktree 不是为了 AI 发明的。它来自一个很早就存在的开发矛盾:Git 的分支很轻,但硬盘上的工作目录一次只能呈现一个检出状态。

早期用户已经会用 GIT_DIRGIT_WORK_TREE 或手工符号链接,把同一仓库的元数据连接到多个目录。Git 源码也曾附带一个位于 contrib/workdir/ 的辅助脚本 git-new-workdircontrib 可以理解为随项目提供、但没有完全成为核心命令的一组贡献工具。这个脚本能共享对象和 refs,却依赖符号链接,仓库本身也不充分了解还有哪些工作目录正在借用它,安全边界比较脆弱。

2015 年发布的 Git 2.5 加入了替代方案。发布说明没有把它描述成凭空出现的新创意,而是明确说:它用于替代 git-new-workdir,不再依赖符号链接,并让共享关系中的双方都知道彼此存在。这个版本当时仍把多工作树标为实验性功能,界面也可能继续变化。

最初的 Git 2.5 worktree 文档 只有少量核心能力:add 用来增加工作树,prune 用来清理失效的管理记录。随后 Git 逐步补齐了生命周期管理:可以列出、锁定、移动、删除、修复 worktree,也加入了更稳妥的脚本输出和每棵工作树独立配置等能力。

这段历史里最重要的不是记住版本号,而是看清它的演化方向:

  1. 用户先用脚本解决“同一历史、多个目录”的真实需求。
  2. Git 把隐蔽的符号链接关系变成仓库明确管理的关联关系。
  3. 功能从“能再检出一份”发展为完整的创建、检查、移动、修复与清理流程。

Git 官方文档至今仍以一个突发热修作为典型例子:重构现场很乱,不想冒险 stash,就新建一棵临时 worktree 修复紧急问题。也就是说,worktree 的原生使命不是制造更多分支,而是让已经很轻的分支能够同时出现在不同目录里

三、它与切分支、stash、重复 clone 的差别

这四种做法都可能让你“处理另一项工作”,但代价分布完全不同。

做法工作目录Git 对象库未提交现场适合场景
git switch复用一个共享必须能安全切换当前目录很干净,任务依次进行
git stash + 切换复用一个共享临时打包后再恢复短暂插入、改动容易完整收起
git clone新增一个通常各自保存天然隔离需要完全独立的仓库配置或远端关系
git worktree add新增一个共享天然隔离同一仓库的多个分支需要同时工作

Worktree 比重复 clone 轻,主要不是因为源代码文件消失了。每个 worktree 仍要在硬盘上展开一份被检出的文件;大型依赖目录也可能需要分别安装。省下的是重复的 Git 对象库,例如相同的 commit、tree 和 blob,以及重新 clone 和同步历史的成本。

它也不等同于 stash。Stash 像把桌面物品装进一个有标签的箱子,再清空原桌面;worktree 则是保留原桌面,直接打开第二张桌子。前者仍然是串行切换,后者允许两个现场同时存在。

Worktree 不是容器

文件目录隔离很容易让人产生过度联想。Worktree 不是 Docker 容器、虚拟机或权限沙箱。多个 worktree 仍可能共享:

  • 同一台机器的端口,例如都想监听 3000
  • 同一个开发数据库、缓存或云端测试环境;
  • 用户目录里的全局配置、凭据和工具缓存;
  • 仓库级 Git 配置与 hooks;
  • 被代码写到 worktree 之外的文件。

所以 worktree 隔离的是仓库内的文件现场和 Git 操作状态,不是进程能接触到的一切资源。这条边界到了 Agent 并行阶段尤其重要。

四、用五步完成一次安全工作流

多数日常使用不需要记很多参数。沿着“查看、创建、工作、汇合、清理”走一遍,已经覆盖了最常见的热修与并行任务。

SAFE WORKFLOW

从创建到清理的一次完整操作

逐步点击命令
终端
git worktree list
发生的变化先看清哪些目录、提交和分支已经被占用。
shop/ → main
终端
git worktree add -b hotfix/payment ../shop-hotfix main
发生的变化从 main 新建分支,并把它检出到旁边的新目录。
shop/ → main
shop-hotfix/ → hotfix/payment
终端
cd ../shop-hotfix
# 修改、测试、提交
发生的变化原目录保持原样;新提交进入共享对象库。
hotfix/payment → f031c4
终端
git switch main
git merge hotfix/payment
发生的变化像普通分支一样评审、合并或 cherry-pick。
main 包含支付修复
终端
git worktree remove ../shop-hotfix
git branch -d hotfix/payment
发生的变化先移除工作目录,再按需删除已经合并的分支。
只剩 shop/ → main
示例目录与提交号是教学用虚构数据;命令语义来自 Git 官方文档。路径放在仓库目录之外,更容易避免把 worktree 误纳入项目。

查看已有工作树

git worktree list

输出会列出路径、当前提交和分支。它应该成为创建和清理前的第一个动作,因为一个你想使用的分支可能已经在另一个目录中工作。

如果脚本需要稳定格式,可以使用:

git worktree list --porcelain

创建新分支与新目录

最清楚的写法是同时给出新分支、目录和起点:

git worktree add -b hotfix/payment ../shop-hotfix main

它做了三件事:从 main 创建 hotfix/payment,创建 ../shop-hotfix 目录,再把新分支检出到这个目录。

如果分支已经存在,可以直接检出:

git worktree add ../shop-login feature/login

只想临时运行旧版本测试,不打算推动某个分支时,可以创建 detached HEAD 的工作树:

git worktree add --detach ../shop-check v2.3.0

Detached HEAD 的中文直觉是“当前指向一个提交,而不是跟着某个分支名”。可以查看和试验;若产生了值得保留的提交,记得及时创建分支。

工作与汇合仍然使用普通 Git

进入新目录以后,修改、暂存、提交、推送都和平时一样:

cd ../shop-hotfix
npm test
git add src/payment.ts tests/payment.test.ts
git commit -m "fix: restore payment submission"

Worktree 没有发明新的合并方式。任务完成后,仍然通过 pull request、mergerebasecherry-pick 把结果带回目标分支。它改变的是工作发生在哪里,不是历史如何组合。

用 Git 删除,不要直接拖进废纸篓

完成后运行:

git worktree remove ../shop-hotfix
git branch -d hotfix/payment

第一条删除关联工作目录,第二条按需删除已经合并的分支。Git 会拒绝直接移除含有未提交改动的 worktree,这是有意设置的保护。

若你已经手工删除了目录,仓库里可能留下失效记录。先预览,再清理:

git worktree prune --dry-run
git worktree prune

移动了主仓库或关联目录,导致连接失效时,优先使用 git worktree repair,不要手工猜测 .git/worktrees/ 中的内部文件。

一次可复述的路径是:先列清单,再从明确基线创建;独立完成并提交;用普通分支流程汇合;确认干净后由 Git 清理目录。

五、Agent 时代重新发现了 Worktree

人独自写代码时,常见节奏是一次专注一项工作。登录页没写完,支付热修只能插队。Worktree 能减少切换成本,但很多人并不经常需要两张同时活跃的工作台,因此它长期像一个“知道很好、偶尔才用”的工具。

Coding Agent 改变了瓶颈。生成和修改代码的执行者突然可以同时有多个:一个实现登录页,一个修支付问题,一个升级依赖。此时限制并行度的,不再只是人的手速,而是它们能不能同时写同一个仓库而不踩乱彼此的文件现场

下面保持三个任务完全相同,只切换“共用一个目录”与“每个任务一棵 worktree”。

AGENT CONCURRENCY LAB

目录数量怎样改变并行方式

只切换隔离方式
SERIAL三个任务共用同一个文件现场。
Agent A · 登录页运行中

正在修改 src/auth.ts

Agent B · 支付热修等待

需要 A 先提交或 stash

Agent C · 依赖升级等待

切分支会覆盖眼前文件

隔离靠排队。Agent 数量增加了,文件系统仍只有一个可写现场。
PARALLEL每个任务有自己的目录、分支和 index。
Agent A · 登录页运行中

wt-login → agent/login

Agent B · 支付热修运行中

wt-hotfix → agent/payment

Agent C · 依赖升级运行中

wt-deps → agent/deps

执行可以并行,决定仍需汇合:评审 diff、运行测试、处理分支间的语义冲突。

简化模型:它展示文件隔离,不代表任务一定能安全并行。两个 Agent 即使在不同目录修改同一段逻辑,合并时仍可能冲突。

如果三个 Agent 共用一个目录,即使它们声称各自在不同分支上工作,进程看到的仍是同一组文件和同一个 index。A 切分支会改变 B 正在读取的文件;B 暂存文件可能把 C 的改动带进提交;一个 Agent 运行格式化,也可能重写另一个 Agent 正在编辑的文件。为了安全,它们只能排队,或者依赖非常脆弱的协作约定。

一任务一 worktree 后,每个 Agent 得到:

  • 一个稳定的起始提交;
  • 一个独立分支;
  • 一套独立的源文件与暂存区;
  • 可以单独检查、测试和丢弃的 diff。

这也是为什么当代 Agent 工具越来越常把 worktree 当成会话隔离单元。GitHub 在介绍并行 Agent 会话时,也明确把“每个会话位于自己的 worktree 和分支”作为文件、对话与任务状态隔离的基础; GitHub Desktop 的 worktree 支持 同样把 Agent 并行列为直接使用场景。

这里发生的不是 worktree 本质改变了,而是生产关系变了:以前它让一个人不必收拾上一张桌子;现在它让多个执行者同时拥有各自的桌子。

六、Vibe Coding 中更重要的是可审阅的边界

Vibe coding 常把需求表达、实现选择和大量文件修改压缩进一次对话。速度上升后,新的成本会落在监督:哪一个 Agent 改了什么,它从哪里出发,结果能不能独立测试,不满意时能不能完整丢弃。

Worktree 在这里提供的价值不只是“更快”,还有一条清楚的责任边界:

任务描述

起始提交 + 独立分支 + 独立 worktree

Agent 修改、测试、提交

人查看 diff 与验证结果

合并 / 要求返工 / 整棵丢弃

当一次实验失败,你可以删除它的 worktree 和分支,而不用从混杂的主目录中辨认哪些文件属于谁。当两个 Agent 给出不同方案,你也可以分别启动应用、比较 diff 和测试结果,再选择其一。这让“让 Agent 试试看”从不可控的直接改写,变成一个有入口、有出口的实验单元。

一个实用的任务命名方式

假设仓库是 shop,可以把 Agent 工作树集中放在相邻目录:

projects/
├── shop/                    # main,人负责汇合与最终验证
├── shop-wt-login/           # agent/login
├── shop-wt-payment/         # agent/payment-hotfix
└── shop-wt-deps/            # agent/deps-upgrade

分支和目录都带上短任务名,目的是让 git worktree list 的结果一眼可读。目录不必使用 wt,分支也不必使用 agent/;稳定一致比具体格式重要。

给 Agent 的任务边界至少可以包含:

只在当前 worktree 内修改。
从当前分支开始,不切换或重置其他分支。
不要改写 worktree 之外的文件。
完成后运行指定测试,提交变更,并总结风险与未完成项。
不要合并到 main;等待人工评审。

这些约束不是因为 worktree 不可靠,而是因为它只负责文件现场。Agent 是否选对方案、是否修改了外部服务、是否擅自扩大范围,仍要靠任务说明、权限和评审控制。

并行之前先检查任务是否真的独立

能创建三棵 worktree,不代表三项任务适合并行。可以先看它们可能触碰的边界:

任务关系并行建议原因
修改互不相干的模块很适合文件和语义交叉少
一个写功能,一个补独立文档通常适合汇合成本较低
同时改同一份依赖锁文件谨慎文本冲突概率高
同时迁移同一数据库 schema不宜直接并行外部状态和顺序依赖强
B 必须依赖 A 新增的接口先 A 后 B,或明确基线B 的起始版本缺少必要前提

Worktree 把执行冲突推迟成更容易看见的集成冲突,却不会让冲突凭空消失。这已经很有价值:文件不会在生成过程中互相覆盖,失败也局限在单个目录。但最终仍需有人决定多个正确的局部修改能否组成正确的系统。

七、并行工作最常见的六个坑

起点过旧

Agent 从昨天的 main 建立 worktree,今天完成时主线已经前进很远。隔离没有问题,集成成本却被拖到最后。长任务应定期明确是否需要 fetch、rebase 或重新建立基线,更新前先确认工作区状态。

两个任务改同一片语义

Git 只能检测部分文本冲突。A 改了折扣计算,B 在另一个文件里改变了税费顺序,即使自动合并没有冲突,组合后的业务含义也可能错。这叫语义冲突,必须靠测试、评审和系统理解发现。

共享端口与数据库

三个 worktree 同时执行 npm run dev,都监听 3000,后启动的进程会失败。更危险的是共用开发数据库:一个 Agent 迁移 schema,另一个的测试数据随之变化。为每项任务分配端口、数据库名称或临时环境,才是完整并行方案。

依赖占用大量空间

Git 对象库共享,不代表 node_modules、构建产物和缓存一定共享。多个大型 worktree 可能迅速占满磁盘。包管理器的全局内容寻址缓存可以减少下载,但每个项目目录仍可能产生链接、构建缓存和输出文件。

把关联目录放进仓库内部

若把 worktree 建在主目录的未忽略子目录里,主 worktree 可能把整个关联目录视为未跟踪内容。更稳妥的默认选择是相邻目录,例如 ../shop-wt-login;若团队采用仓库内部的统一目录,则必须先设计好忽略规则和工具兼容性。

清目录却忘了清记录

手工删除文件夹可能留下 prunable 记录;长时间保留的临时 worktree 又会让分支和目录清单越来越难读。把 git worktree listremoveprune --dry-run 纳入日常收尾,比偶尔进行一次大扫除可靠。

此外,Git 当前官方文档仍提醒:多工作树与 submodule 的组合支持并不完整,不建议对包含 submodule 的超级项目随意做多个 checkout。遇到这类仓库,应先在小范围验证真实行为,而不是假设每个依赖都会自然隔离。完整命令与最新限制以 git-worktree 官方文档 为准。

八、从热修工具到并行调度原语

现在回放整条路径。

最初的问题是:一个 Git 仓库的分支很轻,但一个工作目录只能呈现一个文件现场。人们先用额外脚本和符号链接连接多个目录;Git 2.5 把这件事变成仓库明确知道、能够管理的 linked worktree。它首先服务于很传统的场景:开发中途处理热修、同时测试另一个版本、保留一个长期构建目录。

Agent 时代没有改写这个机制,只把它的价值放大了:

共同仓库 → 为每项任务创建独立分支与 worktree → 多个 Agent 分别修改和测试 → 通过 diff、提交与测试评审 → 再把结果汇合到主线

这条链路里,worktree 做了两件很具体的事:让并行写文件成为可能,让每项变化可以单独审阅和丢弃。它没有替你拆分任务,没有判断代码对错,也没有隔离端口、数据库和云端资源。

因此,在 vibe coding 中使用 worktree,最稳妥的心态不是“终于可以无限开 Agent”,而是:

给每个并行实验一张独立工作台、一个明确起点和一条独立分支;完成后再把人的注意力放在差异、验证与汇合上。

Worktree 出生时解决的是“不要为了一个热修毁掉手头现场”。到了 Agent 时代,它承担的是同一个更大版本的问题:不要让更多执行者共享一个混乱现场。

延伸阅读