2656 字
约 8 分钟
22
S24-X08 · 并行任务:用 worktree 让几个 Agent 同时开工

一个会话在改 12 个文件,另一个会话也在改同一批文件——这不叫并行,叫互相拆台。真正能并行的,是"共享仓库历史、但不共享工作区"的任务。git worktree 恰好提供这个单位:同一份 .git,多个独立工作目录,各自的 HEAD。

它解决的是隔离问题,不解决冲突问题。分不清这两件事,开三个窗口只会换来三次手忙脚乱。


1. 串行为什么慢:先分清四种瓶颈

瓶颈 表现 并行能不能解
上下文污染 会话 A 的改动被会话 B 看到,判断跟着跑偏 能(独立工作区 + 独立会话)
纯等待 一次只跑一件事,人和机器都在等 能
合并冲突 两个会话改同一文件,最后合并打架 不能,要靠任务切分
环境竞争 抢同一个端口、同一个库、同一个构建目录 不能,要靠资源隔离
依赖安装 每个工作区都要装一遍 不能,只能优化

所以并行的收益来自前两项,成本来自后三项。收益是线性的,成本是超线性的——这就是为什么"能并行三个"和"并行三个更快"是两件事。


2. git worktree 到底共享什么

项 是否共享 说明
对象库(提交、树、blob) 共享 不会因为多开工作区而翻倍占盘
分支引用 共享 一个分支同时只能在一个工作区检出
HEAD 独立 每个工作区停在自己的分支
索引/暂存区 独立 各自的 git add 互不影响
配置与 hooks 共享 来自同一份 .git/config
未跟踪文件(node_modules、.env) 独立 新工作区里是空的
git worktree add ../proj-feat-auth -b feat/auth
git worktree add ../proj-feat-api  -b feat/api
git worktree list
/repo                      abc1234 [main]
/repo/../proj-feat-auth    def5678 [feat/auth]
/repo/../proj-feat-api     0123abc [feat/api]

一条 git 的硬规则:同一个分支不能同时检出到两个工作区。想开第二个窗口干别的活,必须新开分支(-b),否则 git 直接拒绝。


3. 一个 worktree 一个会话

cd ../proj-feat-auth && claude
# 另一个终端
cd ../proj-feat-api && claude

每个会话拥有自己的上下文窗口,互不可见——这正是隔离的价值。但要记住三件"没有跟过来"的东西:

  1. 未提交的个人配置:.claude/settings.local.json 属于工作区里的文件,新 worktree 里没有;
  2. 未跟踪的依赖:node_modules、虚拟环境、构建缓存都是空的;
  3. IDE 的索引:新目录需要单独打开,否则跳转会指回旧路径。
mkdir -p ../proj-feat-auth/.claude
cp .claude/settings.local.json ../proj-feat-auth/.claude/ 2>/dev/null || true

更好的做法是把团队规则放进已提交的 .claude/settings.json,个人差异才用 local 文件——这样新 worktree 天然就带着正确的权限配置。


4. 分支命名与合并策略

策略 做法 适合 代价
一任务一分支 worktree add -b feat/<主题> 独立功能、互不依赖 分支多,要勤清理
共享基线 + 各自 rebase 都从 main 切,完成前 rebase 想要线性历史 rebase 会反复解冲突
集成分支 各自合到 integration,再整体进 main 多人多任务同批上线 多一层合并
短生命周期 完成即合并、即删 worktree 减少长期漂移 要求任务切得够小

命名建议 类型/域-动作:feat/api-pagination、fix/web-login-loop。名字里带上模块名,后面判断"这两个分支会不会撞"时省很多事。

合并顺序也有讲究:先合小改动、先合共享文件多的那个。共享文件先落地,后合并的分支只需要解一次冲突,而不是反复解。


5. 冲突预防:按文件与模块切

切分方式 例子 冲突风险
按目录/模块 apps/web vs services/api 低
按层 前端 / 后端 / 数据迁移 中(共享类型定义)
按文件 一人一文件 低,但难规划
按功能纵切 一个功能从前端到库表 高(同文件多处改)

纵切听起来最"完整",实际冲突最多:类型定义、路由表、i18n 文案、锁文件全是共享热点。

一条实践经验:共享文件指定唯一负责人。类型定义、路由注册、依赖锁文件,只允许一个 worktree 改,其他 worktree 需要时用 patch 交接,或等对方先合并。

git diff --name-only main...feat/auth | sort > /tmp/a.txt
git diff --name-only main...feat/api  | sort > /tmp/b.txt
comm -12 /tmp/a.txt /tmp/b.txt          # 交集就是冲突候选

这条命令值得在开工前跑一次。交集为空,可以放心并行;交集里有三四个文件,就先调整切分方式。


6. 资源竞争:端口、数据库、构建目录

资源 冲突表现 隔离做法
端口 EADDRINUSE,或两个实例抢同一个 每个 worktree 一个 PORT,写进各自的 .env.local
数据库 数据互相污染、迁移互踩 每 worktree 独立库名或独立 schema
构建产物 产物互覆盖、缓存键冲突 独立 dist/、独立缓存目录
依赖 node_modules 重装、lock 冲突 各自安装;lock 只在一处改
测试数据 固定 fixture 被改坏 每次运行生成随机前缀
日志文件 两个进程写同一个文件 日志路径带分支名
# 每个 worktree 的 .env.local(不进 git)
PORT=3101
DATABASE_URL=postgres://localhost:5432/app_feat_auth
CACHE_DIR=.cache/feat-auth
LOG_FILE=logs/feat-auth.log

数据库这条最容易出事:迁移只跑了一半的库,两个分支都读它,报错信息会指向完全无关的代码。开工前把 DATABASE_URL 写对,比事后 debug 便宜一百倍。


7. 清理与回收

命令 作用 注意
git worktree list 列出所有工作区 定期看一眼,别积累
git worktree remove <path> 删除工作区 有未提交改动会被拒绝,需先处理
git worktree prune 清理失效元数据 目录被 rm -rf 后必须跑
git branch -d <name> 删除已合并分支 用 -d 而不是 -D,让 git 帮你判断
git worktree remove ../proj-feat-auth
git worktree prune
git branch -d feat/auth
git worktree list

prune 是唯一容易忘的一步:直接 rm -rf 掉目录,git worktree list 里还会留着幽灵条目,之后想用同名路径重建就会报"已存在"。养成"先 remove、再 prune"的习惯。


8. 什么时候不该并行

场景 为什么 替代做法
强耦合改动(接口重构) 每一步都依赖上一步的结果 串行,或先定接口再分头实现
需要频繁互看 两边的上下文本来就是互补的 合并成一个会话
同一文件的连续修改 必然冲突,且冲突在语义层 串行
方向未定的探索 还不知道要改哪些文件 先探明边界,再并行
单个小任务 开工作区 + 装依赖的开销超过收益 直接串行
需要真实用户数据验证 数据只有一份 串行,或用只读副本

一个常被忽略的判断:并行度应该受"你能同时审查多少产出"限制,而不是受机器限制。三个会话同时吐代码,人只有一双手 review,最后还是要排队。


9. 排错:六个高频坑

现象 原因 处理
新工作区里命令全报 module not found 依赖没装,node_modules 不共享 在新目录跑一次安装
IDE 跳转到旧文件 索引指向主工作区 用 IDE 单独打开 worktree 目录,别打开父目录
测试互相污染 共用同一个库 独立库名/schema,或测试用事务回滚
fatal: 'feat/x' is already checked out 同一分支被两个工作区占用 用 -b 新建分支
worktree list 里有不存在的路径 直接删了目录没 prune git worktree prune
提交到了错的分支 终端提示符没显示分支 提示符里带上分支名,提交前 git status

第 2 条最费时间:IDE 打开的是父目录,go to definition 全跳到主工作区的同名文件上,改了半天发现改的不是运行的那份。一个 worktree 一个 IDE 窗口,是唯一不容易错的用法。


10. 边界:这些需求 worktree 解决不了

需求 更合适的做法 理由
同一文件的多处并行修改 不并行,或先拆分文件 语义冲突无法机械合并
系统级隔离(不同运行时版本) 容器 / 虚拟机 worktree 只隔离文件与分支
大规模并行构建/测试 CI 的分布式 runner 本机资源是瓶颈
需要共享同一份运行中的服务 起一份服务,多个客户端连 端口与状态只有一份
团队多人协作同一分支 远程分支 + 常规协作流程 worktree 是本机概念

一句话:worktree 隔离的是"文件和分支",不是"进程和数据"。


11. 自测

  1. worktree 之间哪些东西共享、哪些独立?至少各说三项。
  2. 为什么同一个分支不能同时检出到两个 worktree,怎么绕开?
  3. 开工前用什么命令判断两个分支会撞车?
  4. 新 worktree 里最容易漏掉的三样东西是什么?
  5. rm -rf 删掉工作区目录后,git worktree list 还显示它,怎么处理?
  6. 哪些任务类型不该并行,判断依据是什么?
S24-X08 · 并行任务:用 worktree 让几个 Agent 同时开工
https://www.clxhxhhr.top/posts/5700/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。