TECH

用 Git Worktree 跑并行任务:AI Coding 场景下的隔离、协作与收尾

解释 Git Worktree 在 AI Coding 中的真正角色:什么时候该开独立工位,什么时候没必要开,以及任务结束后如何合并分支、删除旧 worktree 与清理残留。

topic: ai-way type: 文章 origin: 原创 score: 8 addedAt: 2026-03-06

修订记录

版本时间说明
v12026-03-06初稿:从 README 迁出 worktree 内容,重写为 AI Coding 并行任务指南

Why:为什么 AI Coding 特别需要 worktree

单人开发时代,很多人对分支已经够用了:切到 feat/a 做一会儿,再切回 main 修一个小问题,忍一忍也能做完。

但在 AI Coding 场景里,真正稀缺的往往不是“能不能切分支”,而是下面三件事:

  1. 会话边界是否清晰。
  2. 当前工作区是否干净。
  3. 两个任务能不能并行推进,而不是互相打断。

一旦开始用 AI 同时推进两个任务,问题就不一样了。真正麻烦的往往不是 Git 命令本身,而是下面这些现场会缠在一起:

  1. 当前目录已经改到一半,又插进来一个紧急 bug。
  2. 一个 AI 会话还占着现有上下文,另一个任务却已经要开始。
  3. 同一个工作目录里来回切分支,很容易把未完成改动、终端状态和会话目标搅在一起。

这时候,git worktree 才显出价值:给每个任务一个独立目录、一个独立分支、一个独立会话。这样做最直接的收益,是少做很多来回切换和收拾现场的动作。

所以这篇文章真正想解决的,是当手头有两个任务同时推进时,怎样把分支、目录和会话拆开,避免互相污染。命令只是其中一部分。

What:worktree 到底是什么,它又不是什么

一句话说,git worktree 就是给同一个仓库开多个独立工位。

可以把它理解成:

  1. 分支负责表达“代码历史和变更主题”。
  2. worktree 负责表达“今天在哪个物理工作区里干活”。

所以它不是分支的替代品,而是分支的工作区载体。

这也能解释一个常见困惑:为什么有时候会用 worktree,有时候又不会?

原因很简单,worktree 不是必须步骤,而是“需要隔离时才启用”的能力。以下几种情况,通常值得开:

  1. 当前工作区已经有未完成改动,但又要插入第二个任务。
  2. 两个任务改动面不同,想拆成两个独立提交或两个独立评审单元。
  3. 希望给不同 AI 会话明确边界,避免上下文串味。
  4. 需要同时打开两个分支做比对、验证或回归。

反过来,以下情况通常没必要:

  1. 当前仓库很干净,只做一个线性任务。
  2. 只是改一两个文件的小修复,十分钟内能收口。
  3. 两个改动强耦合,本质上就是同一条变更链。

再回答一个更贴近实际的问题:在 VS Code + Codex + superpowers 这类工作流里,worktree 是自动的吗?

结论是:Git 本身不会自动替你创建 worktree;是否使用,取决于当前工作流有没有显式选择“隔离工作区”这条路径。

以这套工作流为例,可以把它理解成三层:

  1. Git 能力层:只有执行了 git worktree add ...,新的 worktree 才真的存在。
  2. Agent 工作流层:某些 skill 或流程会建议在开始 feature、执行 plan、需要隔离时创建 worktree;没有走到这一步,就继续在当前目录工作。
  3. 个人操作层:如果明确知道任务要并行、要分会话、要防污染,最好在任务开始前主动决定是否开新工位。

所以它不是某个“一次开启、永久生效”的全局开关,而更像是每次启动任务时做的一次工作方式选择。

How:如何用 worktree 支撑多个任务并行跑

下面给一条最小可用路径,适合日常 AI Coding。

第一步:先判断任务是否真的适合并行

最适合拆成两个 worktree 的,一般是这类组合:

  1. 任务 A:后端接口、脚本、数据迁移一类的主线改动。
  2. 任务 B:E2E、文档、回归验证、样式修补这类旁路改动。

判断标准不是“能不能并行”,而是“并行后会不会互相踩”。如果两个任务高度依赖同一批未完成代码,那么最好还是顺序推进。

第二步:一个任务,一个分支,一个 worktree

先看已有工位:

git worktree list

然后创建新工位。下面以项目内隐藏目录 .worktrees/ 为例:

mkdir -p .worktrees

git worktree add .worktrees/feat-api-refactor -b feat/api-refactor
git worktree add .worktrees/feat-e2e-stability -b feat/e2e-stability

如果团队不希望在仓库内放 .worktrees/,也可以放到仓库同级目录:

git worktree add ../project-feat-api-refactor -b feat/api-refactor

有两个注意点:

  1. 如果使用 .worktrees/ 这类项目内目录,应该先确认它被 .gitignore 忽略。
  2. 同一个分支不能同时检出到两个 worktree;Git 默认会拒绝这种操作。

第三步:一个窗口一个会话,不要混跑

创建好工位后,真正稳定的做法不是“有了两个目录就算完成”,而是继续把执行面拆开:

  1. 一个 worktree 对应一个 VS Code 窗口。
  2. 一个 worktree 对应一个终端上下文。
  3. 一个 worktree 对应一段独立的 Codex 或 Agent 会话。

这样做的好处是,任务 A 的上下文不会污染任务 B。对于 AI 工具来说,这一步比“会不会用 Git 命令”更重要。

第四步:各自开发,各自提交

在每个工位中独立完成改动、测试和提交。例如:

cd .worktrees/feat-api-refactor
git status
git add .
git commit -m "feat: refactor api workflow"

这里最容易写错的一点是:提交完成,不代表 worktree 生命周期结束。

提交只是形成了一个清晰 checkpoint,方便评审、回滚、继续迭代。只要这个工位还承担后续任务,就可以继续保留。

但还有一个同样重要的事实:在 worktree 里 commit,只是把改动提交到了这个 worktree 对应的分支上,不会自动进入 main。

如果目标是“把结果交回主分支”,后面仍然要走一次合并动作。常见方式有三种:

  1. git merge
  2. git rebase 后快进合入
  3. git cherry-pick

无论选哪种,核心都一样:commit 解决的是“先把改动记在分支上”,merge 解决的是“让主分支真正拿到这次改动”。

第五步:任务结束后再决定“留还是删”

真正应该问的不是“我已经 commit 了,要不要删 worktree”,而是:

  1. 这个分支后面还要继续改吗?
  2. 这个改动已经进入评审或合并流程了吗?
  3. 我还需要保留这个目录作为独立上下文吗?

常见收尾路径如下:

  1. 还要继续改:保留 worktree。
  2. 已经合并完成、后面没后续:删除 worktree。
  3. 暂时中断,但过几天还要回来:可以保留 worktree,不必急着清。

Principles:什么时候该用,什么时候没必要用

经验上,可以把 worktree 当成一种“高价值隔离工具”,而不是默认动作。

适合使用的场景

  1. 当前目录有未提交改动,又来了一个插队任务。
  2. 需要把两项任务拆成两个独立 PR 或两个独立评审批次。
  3. AI 会话已经很长,希望给新任务一个干净上下文。
  4. 需要对两个分支同时做运行、对比或回归。

不必强行使用的场景

  1. 任务很小,当前分支也很干净。
  2. 两个改动本质上属于同一次提交。
  3. 团队当前流程更依赖快速收口,而不是长期并行。

三个容易混淆的点

  1. worktree != branch 分支是历史线,worktree 是工位。

  2. 删 worktree != 删分支 git worktree remove 只移除工位和对应元数据,不会自动删除分支。

  3. commit != worktree 结束 commit 是里程碑,不是工位的死亡时间。

如果只记一条原则,可以记这句:worktree 的生命周期,跟任务生命周期走,而不是跟某一次 commit 走。

Operations:任务跑完后,状态是什么,该怎么处理

这部分专门回答日常使用中最容易卡住的问题。

场景 1:任务做完了,但还没合并

这时最正常的状态就是:

  1. worktree 还在。
  2. 分支还在。
  3. 改动已经提交,等待 code review 或等待与主线对齐。

这不是脏状态,而是一个很正常的“待合并”状态。

这里最容易混淆的一点是:此时 main 还没有这些代码。

因为提交发生在 feat/... 分支上,不发生在 main 上。只要还没 merge、rebase 或 cherry-pick 回主线,主分支就看不到这次结果。

场景 2:只删 worktree,不合主分支,会发生什么

需要分两种情况看:

  1. 如果只是删除 worktree,但分支还在,代码通常不会立刻丢,因为提交还挂在那个分支上;只是 main 依然看不到。
  2. 如果 worktree 删了,分支也删了,而且这些提交没有被别的分支引用,那这条改动线才会有真正丢失风险。

所以,“删工位”不等于“把结果交回主线”。如果目标是把结果留在主分支,合并这一步不是可选项,而是交付动作本身。

场景 3:任务已经合并完成,准备清理

推荐顺序如下:

# 回到主工作区或任一正常工作区
git switch main
git pull --ff-only
git merge --no-ff feat/api-refactor

# 删除已经合并的分支
git branch -d feat/api-refactor

# 删除对应工位
git worktree remove .worktrees/feat-api-refactor

如果团队走 PR 流程,也可以把“合并”这一步放到 GitHub、GitLab 或其他托管平台完成。平台合并后,本地再做两件事:

git switch main
git fetch --all --prune
git branch -d feat/api-refactor
git worktree remove .worktrees/feat-api-refactor

场景 4:想直接删工位,但里面还有未提交改动

默认不行。git worktree remove 只允许删除干净的 worktree。

如果目录里还有已修改或未跟踪文件,Git 会拒绝删除。只有在你非常确定这些改动不要了时,才考虑:

git worktree remove --force .worktrees/feat-api-refactor

建议把 --force 理解成“放弃这个工位里的未收口现场”,而不是普通清理命令。

场景 5:目录被手工删掉了,Git 里还挂着记录

这时常见现象是 git worktree list 里还能看到一个已经不存在的路径,通常会标成 prunable。

清理方式:

git worktree prune

这一步清的是仓库里的 worktree 管理元数据,不是删除真实代码文件。

场景 6:仓库挪位置了,worktree 记录失联

如果主仓库或 linked worktree 被手工移动,路径关系可能失效。这时可以尝试:

git worktree repair

这个命令的作用是重建主仓库与 linked worktree 之间的管理连接。

一套可复用的最小工作流

如果希望把这件事变成团队可执行习惯,可以直接采用下面这条规则:

  1. 一个独立任务,对应一个分支。
  2. 一个独立任务,如果需要并行或隔离,再对应一个 worktree。
  3. 一个独立 worktree,对应一个 VS Code 窗口和一段 AI 会话。
  4. 合并完成后,再决定是否删分支、删 worktree。

这样,worktree 就不再是“偶尔才想起的 Git 冷门命令”,而会变成 AI Coding 时代一套很实用的并行协作手法。

结语

很多关于 worktree 的困惑,本质上都不是命令问题,而是工作流边界问题:

  1. 什么时候需要隔离?
  2. 隔离的是代码,还是会话,还是验证环境?
  3. 隔离结束后,哪些东西该保留,哪些东西该清理?

把这三个问题想清楚,worktree 就会从“看起来很高级的小技巧”,变成一套可持续复用的任务编排工具。

评论