微服务加前端的多仓库项目接入 Codex App 时,project 根目录该选单个仓库还是整个工作目录?并行任务为什么建议一任务一个 Git worktree?本文梳理 worktree 的机制、Codex 反复检查工作区导致改动被 reset 的风险,给出可照做的创建、隔离、合并流程与检查清单。
刚上手 Codex App 的人常常卡在同一个地方:手上的项目并非单一仓库,根目录下并排放着若干个微服务和一个前端工程;同时又看到「worktree」这个概念,听说可以并行跑多个任务,于是担心并行之后代码冲突不断、合并成本比省下来的时间还高。
这些顾虑大部分来自概念没对齐。worktree 是 Git 自带的能力,跟某个具体工具无关;而「一个任务一个 worktree」这种用法,背后有很实际的原因——不只是为了并行,更是为了隔离。下面把目录选择、worktree 机制、并行风险和处理流程拆开讲。
先弄清楚:worktree 到底解决了什么问题
普通开发中,切换分支是在同一个目录里完成的,git checkout 之后目录内容整体换掉。Git worktree 换了个思路:同一个仓库可以同时关联多个工作目录,每个目录检出不同的分支,互不干扰。
# 在当前仓库基础上,新建一个目录检出 feat/login 分支
cd ~/work/svc-order
git worktree add ../svc-order-login -b feat/login
# 查看当前仓库关联了哪些工作目录
git worktree list
执行后,~/work/svc-order-login 是一个独立目录,有自己的文件、自己的暂存区、自己的 git status,但提交历史与主仓库共享。这对 Codex App 这类工具的意义在于:每个 worktree 相当于一个干净、互不可见的沙盒,可以各开一个任务实例。
有两个 Git 层面的限制要记住:
- 同一个分支默认不能在两个 worktree 里同时检出(除非强制),所以分支命名要区分开。
- 依赖目录不会跨 worktree 共享。前端项目每个 worktree 都得重新装一次
node_modules,磁盘占用和安装时间都要算进去。
多仓库项目:project 该选单个仓库还是根目录
原则只有一条:project 根目录决定了 agent 默认看到什么、以及 Git 上下文从哪里取。所以要按「这次任务需要改几个仓库」来选,而不是按项目物理结构选。
| 场景 | 建议的 project 根 | 说明 |
|---|---|---|
| 任务基本落在单个服务内 | 该服务的仓库目录 | Git 状态、worktree、提交都干净可追踪,最不容易出问题 |
| 一次改动横跨服务与前端(如改接口契约) | 根目录,且根目录本身纳入版本控制 | 需要能同时看到多个仓库,但要额外约束 agent 的改动范围 |
| 根目录只是把若干仓库放在一起、本身无版本控制 | 仍选单个仓库 | 根目录下 Git 能力会退化,git status 看不到全貌,容易误判 |
如果确实要选根目录,务必在根目录放一份 AGENTS.md,写清三件事:各子目录分别是什么服务、本次任务允许改动哪些目录、提交时按仓库分别提交而不是在根目录一次性提交。
需要提醒的是,不同版本客户端对多仓库工作区的支持程度不完全一致,具体行为请以官方最新文档为准,先用一个只读的小任务验证一遍再放开手脚。
为什么强烈建议一个任务一个 worktree
这里有一个容易被忽略的坑,也是并行使用中最容易造成实际损失的地方:这类编码 agent 在工作过程中会频繁检查当前工作区的状态,判断「有没有多余文件、工作区干不干净」。
如果两个实例共用同一个工作目录,A 实例刚写好一半的文件,在 B 实例眼里就是「来路不明的未跟踪改动」。极端情况下,agent 为了让工作区恢复干净,可能执行 git reset 或 git restore,把另一个任务尚未提交的成果直接抹掉。这不是靠写几句提示词就能彻底杜绝的,属于模型行为习惯层面的问题。
worktree 从物理上消除了这种互相可见性:每个实例只看到自己的目录和自己的分支,git status 的结果永远只反映自己的改动。所以哪怕你并不打算并行,用 worktree 做一层隔离也是划算的。
反过来,如果只是想串行地做几件事,或者改动很小,那就在主工作区里老老实实一次跑一个任务,不必为了用 worktree 而用。
实操流程:从开任务到合并
步骤一:为任务建 worktree
cd ~/work/svc-order
git worktree add ../svc-order-feat-login -b feat/login
git worktree add ../svc-order-fix-timeout -b fix/timeout
目录命名带上分支语义,后面一眼能分清哪个目录对应哪个任务。
步骤二:在 worktree 里准备环境和 AGENTS.md
cd ../svc-order-feat-login
pnpm install # 或其他包管理器,依赖不会共享
每个 worktree 内放一份简短的 AGENTS.md,明确边界,例如:
- 本目录只负责订单服务的登录相关改动;
- 不要修改
pnpm-lock.yaml,除非任务明确要求; - 不要执行
git reset --hard、git clean -fd、git restore .; - 完成后先跑
pnpm test,通过再提交。
这类约束能降低误操作概率,但不要指望它百分之百生效,物理隔离才是主要保障。
步骤三:把 Codex App 的 project 指到对应 worktree 目录
一个 worktree 对应一个 project 条目、一个任务实例,不要在同一目录里再开第二个实例。同时并行的任务数量建议控制在两到三个,多了之后合并成本上升得很快。
如果本地机器同时跑多个实例明显吃力,或者任务需要长时间挂着跑,可以把工作区放到一台长期在线的远程 Mac 上,本地只做连接和查看——新加坡的独享 Mac 属于这类可选方案之一,本质上解决的是设备性能和在线时长问题,跟 worktree 的正确用法无关。
步骤四:小步提交,及时合并
worktree 里的分支拖得越久,和主干的差距越大,冲突越难解。建议每完成一个可验证的小功能就提交一次,并尽快合回主干,而不是等所有并行任务都做完再一起合并。
冲突了怎么办:哪些能交给 agent,哪些不能
并行任务之间出现冲突是必然的,关键在于处理顺序:
- 先在 worktree 内完成自测并提交,保证该分支自身是完整可运行的状态;
- 回到主干 rebase 或 merge,让冲突暴露在一个明确的位置;
- 机械性冲突可以交给 agent:同一个文件不同区域的改动、import 顺序、格式化差异,让 agent 按上下文合并通常没问题;
- 语义冲突必须人来判断:同一段业务逻辑被两个任务改了不同方向、接口字段含义变化,这类冲突 agent 只能「让代码能编译」,无法保证行为正确;
- 合并后必须重新跑测试,冲突解决得漂亮不代表功能没坏。
一个实用习惯:合并前先看 git log --oneline main..feat/login 和 git diff main...feat/login --stat,确认这个分支到底改了什么,再决定是否交给 agent 处理。
清理:别忘了收尾
任务合并完成后,worktree 目录会一直留着,越积越多。定期清理:
git worktree remove ../svc-order-feat-login
git branch -d feat/login
git worktree list # 确认剩余情况
git worktree prune # 清理已被手动删除目录的残留记录
如果目录里有未提交内容,remove 会拒绝执行,这其实是个保护,先确认是否真的不需要了。
上手检查清单
- 明确这次任务需要改几个仓库,再决定 project 根目录
- 根目录若无版本控制,不要把它当 project 根
- 一个任务一个 worktree、一个实例,不共用工作目录
- 每个 worktree 单独装依赖,预留磁盘空间
- 每个 worktree 内有约束性的
AGENTS.md,明确禁止破坏性 Git 命令 - 分支命名带任务语义,避免与已有分支重名
- 小步提交、尽早合并,不长期分叉
- 合并后跑测试,再清理 worktree
小结
多仓库项目的目录选择,看的是这次任务跨几个仓库,而不是项目物理上长什么样;worktree 的价值首先是隔离,其次才是并行。把「一个任务一个干净工作目录」当成默认习惯,能规避掉大部分「改动莫名消失」和「并行后冲突解不完」的问题。至于客户端对多仓库的具体支持细节,各版本可能有差异,动手前先用一个小任务验证,以官方最新说明为准。