[控场AI]
· 12 分钟阅读· 6,249 字

Claude Code 与 Codex 并行开发:git worktree 实战指南

Claude Code 与 Codex 并行开发:git worktree 实战指南

用 git worktree、异源模型互审和配置隔离,让 Claude Code 与 Codex 在同一仓库安全并行开发。

文章介绍了一套让 Claude Code 与 Codex 在同一仓库并行工作的完整方案,围绕三个常见问题展开:文件冲突、使用限额和自审盲区。对应解法分别是:用 git worktree 给每个代理分配独立工作目录,并规避 .env 不随行、stash 跨 worktree 共享、脏 worktree 无法直接删除等陷阱;让来自不同厂商的模型互相审查未提交的 diff,其中 `git add -N .` 是让未跟踪文件进入 diff 的关键;通过 `CLAUDE_CONFIG_DIR` 与 `CODEX_HOME` 分离登录配置以切换备用账号,并用 HANDOFF.md 做跨会话交接。作者还明确区分了实测项与未测项,验证边界透明。

Claude Code 与 Codex 并行开发

为什么要让两个 AI 代理并行开发

同时让 Claude Code 和 Codex 在同一个仓库上工作,很快会撞上三堵墙:两个代理编辑同一批文件,一方改动破坏另一方的工作区;某一个账号触发使用限额,任务做到一半停摆;让写代码的代理自己审查 diff,它往往会漏掉写代码时就已经忽略的问题。

一位开发者在构建 Mac 应用 Offrun 时,系统性地解决了这三个问题,并记录下完整方案。核心思路是:给每个代理分配独立的 git worktree,用另一个代理来审查未提交的 diff,再通过分离登录配置来绕过使用限额。作者在 macOS 上用 git 2.50.1、Claude Code 2.1.296 和 Codex CLI 0.162.1 进行了实测。

下面拆解每个环节的具体做法,以及那些容易踩坑的细节。

每个代理一个 worktree

git worktree 是挂在同一个仓库(同一个 .git)下的额外工作目录。每个分支拥有自己的文件夹,代理们在各自的文件夹里自由编辑,互不干扰。

cd ~/work/app
git worktree add ../app-refactor -b refactor
git worktree add ../app-bugfix   -b bugfix

然后每个文件夹启动一个代理:

cd ../app-refactor && claude      # 第一个终端
cd ../app-bugfix   && codex       # 第二个终端

Claude Code 提供了 -w / --worktree [name] 选项,可以直接为会话创建 worktree。Codex 没有对应功能,所以作者选择用纯 git 命令,两个代理都能用。

一个实用建议:把 worktree 放在仓库旁边而不是仓库里面。如果一定要放在里面,记得把文件夹加入 .gitignore,否则主工作区会把它显示为未跟踪文件。

git worktree 是什么

git worktree 是 Git 2.5 引入的机制,允许同一个仓库(共享同一个 .git 目录)同时检出多个工作目录,每个工作目录绑定一个独立的分支。它解决的正是「一个人在多个分支间来回切换、反复 stash 和 rebuild」的低效问题:传统做法要 git checkout 切分支,工作区文件会被整体替换,编译缓存和依赖也随之失效;worktree 则让多个分支各自拥有独立的文件树、索引和 HEAD,互不干扰。

内部实现上,主仓库的 .git 目录下会新增 worktrees/<name> 子目录,记录每个附加工作目录的 HEAD、index 和锁定状态。因此分支、提交对象、stash、reflog 等共享,而工作区文件、暂存区是隔离的——理解这一点,就能预判后面几节提到的所有陷阱。

需要警惕的 git worktree 陷阱

worktree 虽然好用,但有几个行为很容易让并行代理翻车。

同一分支不能在两个 worktree 中检出

git worktree add ../dup refactor
# fatal: 'refactor' is already used by worktree at '/tmp/.../app-refactor'

Git 拒绝让两个 worktree 共享一个分支,这实际上是一道安全网。如果你想让两个代理从同一个起点尝试不同方案,用 -b 创建第二个分支即可。

.env 和 node_modules 不会跟着来

新建的 worktree 只检出被跟踪的文件。.gitignore 里列出的 .env 不存在,上次 npm install 生成的 node_modules 也不存在。当代理的第一步操作报出「缺少环境变量」或「找不到模块」,通常就是这个原因。

作者在 ~/.zshrc 里写了个小函数,创建 worktree 的同时复制 .env:

wt() {
  local name=$1 root dest
  root=$(git rev-parse --show-toplevel) || return
  dest="${root}-${name}"
  git worktree add "$dest" -b "$name" || return
  [ -f "$root/.env" ] && cp "$root/.env" "$dest/.env"
  echo "$dest"
}

依赖安装没有放进去,因为每个项目差异太大。pnpm 这类带共享存储的包管理器可以在增加 worktree 时控制磁盘占用。

stash 是跨 worktree 共享的

只有一个 .git,所以只有一个 stash。在另一个 worktree 里执行的 git stash,会出现在主 worktree 的 git stash list 里。

代理使用 git stash 和 git stash pop 的频率比想象中高。为了防止一个会话弹出另一个会话的 stash,作者建议在并行工作期间往代理指令文件(CLAUDE.md、AGENTS.md)里加一条规则:不要使用 stash,用临时提交来保存进行中的工作。

有未提交改动的 worktree 无法直接删除

$ git worktree remove ../app-refactor
# fatal: '../app-refactor' contains modified or untracked files, use --force to delete it

--force 能删掉它,但会一并丢弃未提交的改动。执行之前先跑 git -C ../app-refactor status 确认。工作完成后,先合并,再 git worktree remove 和 git branch -d。

作者还提到一个未在本文测试但常见的问题:多个代理启动开发服务器时会争抢同一个端口(比如 3000)。如果端口来自 PORT 环境变量,可以给每个 worktree 分配不同端口,如 PORT=3001。

用另一个代理审查未提交的 diff

让写代码的代理审查自己的 diff,它会把写代码时的假设一起带进审查。作者的观察是,来自不同公司的模型会注意到不同的问题,这正是它作为审查者的价值所在。

Codex 作为审查者

Codex CLI 有 review 子命令。根据其帮助文档,--uncommitted 会一起审查已暂存、未暂存和未跟踪的改动:

cd ../app-refactor
codex review --uncommitted

也可以用 --base main 针对基础分支审查,或用 --commit <SHA> 审查单个提交。

Claude Code 作为审查者

Claude Code 通过 -p(--print)非交互运行,可以把 diff 管道传进去:

git add -N .                # 让未跟踪文件出现在 diff 中
git diff HEAD | claude -p "Review this diff. Point out bugs, missing tests and type mismatches. Don't change any code."

git add -N .(intent-to-add)是必需的,因为 git diff HEAD 会忽略未跟踪文件。作者用一个新文件 new.ts 和 package.json 的修改验证了这一点:不加 add -N,diff 里会静默遗漏所有新文件。

审查结果怎么处理

让审查者只指出问题,不要做其他事。把修复交回给原代理,或者自己动手。如果审查者直接改代码,就回到了第一个问题:两个代理碰同一批文件。先读审查意见,挑出值得处理的部分再传递,避免一条跑偏的评论把工作带歪。

为什么异源模型互审更有效

自审失效的根源是「生成—校验」的认知同源性:同一个模型在写代码时已经隐含地接受了自己的假设(比如某个参数一定不为空、某个分支一定可达),审查时它会沿同样的推理路径再次得出「没问题」的结论,这类错误被称为盲区而非疏忽。换一个来自不同厂商、不同训练数据、不同对齐策略的模型来审,它的先验分布不同,更容易对原模型视为理所当然的写法提出质疑。这在实践中表现为:Claude 写的代码常被 Codex 指出边界条件和并发假设,反之 Codex 的输出常被 Claude 指出类型与可读性问题。需要强调的是,异源互审提升的是「发现率」而非「正确率」,审查意见仍可能误报,因此文章建议审查者只输出问题、由原作者决定是否修复。

为使用限额做规划

Claude 和 Codex 都会在一段时间内限制用量,并行运行会更快触顶。如果持有多个登录账号,一个用完可以切到另一个。

分离配置文件夹,分离登录

两个 CLI 都支持通过环境变量迁移配置和凭证:Claude Code 用 CLAUDE_CONFIG_DIR,Codex 用 CODEX_HOME。指向空文件夹后确认登录状态是独立的:

$ CLAUDE_CONFIG_DIR=/tmp/cfgA claude auth status
{ "loggedIn": false, ... }
$ CODEX_HOME=/tmp/cfgB codex login status
Not logged in

所以可以为每个登录建一个别名:

alias claude-b='CLAUDE_CONFIG_DIR=$HOME/.claude-b claude'
alias codex-b='CODEX_HOME=$HOME/.codex-b codex'

claude-b auth login      # 用第二个账号登录

会话历史也按配置文件夹分开存储,所以 claude-b 不知道 claude 做过什么——这就引出了下一个问题。

配置目录隔离为何能绕过限额

使用限额通常绑定在账号(订阅)而非 API key 或设备上,CLI 会在本地配置目录里持久化 OAuth 令牌、会话记录和缓存。Claude Code 默认读取 ~/.claude(可用 CLAUDE_CONFIG_DIR 覆盖),Codex 默认读取 ~/.codex(可用 CODEX_HOME 覆盖)。把环境变量指向一个空目录,CLI 就当成全新安装,要求重新登录——这就是登录状态互相隔离、可以并行消耗多个账号配额的原因。

需要留意:这种方式依赖服务条款允许你使用持有的多个账号,跨账号共享会话数据(如复制历史文件)既不稳定也可能违规,因此文章才推荐用 HANDOFF.md 这种「显式状态文件」来做交接,而不是去拷贝配置目录。

任务中途切换时,手动留下交接文档

限额触发切换后,新会话不知道旧会话尝试过什么。作者没有测试过在不同配置文件夹之间复制会话历史文件,因此不推荐这种做法。可靠的办法是让代理在限额到来前把状态写进文件:

<!-- HANDOFF.md -->

**HANDOFF.md 的有效写法**

交接文档要能被一个「完全不知情」的新会话直接消化,关键在于四点:目标(当前任务要达成什么)、已完成(具体到文件与验证结果)、失败尝试(避免新会话重复踩坑)、下一步(明确优先级的动作项)。文中示例的「Tried and failed」一节尤其重要——AI 代理倾向于重复尝试看起来合理的方案,把已经证明行不通的路径写下来,能显著减少浪费的 token 和上下文。

另一种更结构化但成本更高的替代是使用 `PROGRESS.md` 加清单式勾选,但文章的实践表明,只要代理被要求在每个工作单元结束后更新 `HANDOFF.md`,纯文本的四段式结构就已足够实现跨账号、跨模型的平滑接续。
## Goal
Move the auth middleware to TypeScript

## Done
- Migrated src/auth/session.ts, 0 type errors

## Tried and failed
- Staying on jose v4 breaks the ESM import. Needs v5

## Next
- Migrate src/auth/token.ts

在 CLAUDE.md(或类似文件)里加一行,要求代理在每完成一个工作单元后更新 HANDOFF.md。然后告诉新登录的会话「读 HANDOFF.md 然后继续」,它就能接上上一个会话的进度。

哪些环节仍然需要手动

以上方案能实现代理并行,但有些事仍然得手动做:打开每个终端标签页查看哪个代理在工作、哪个在等你;逐个 CLI 检查哪个登录接近限额;每次都手动输入创建 worktree、复制 .env、运行审查和清理的命令。

作者为此开发了一个 Mac 应用 Offrun,把这些操作集中到一个界面里,可以用已有的登录运行 Claude Code、Codex 和 Antigravity CLI,每个聊天拥有自己的 git worktree。不过即便不用 Offrun,文中所有方法用纯 git 也能实现——先拿纯 git 试试看,这可能是最务实的建议。

可信度说明

作者在文章末尾附上了验证清单,区分了实际运行过的内容和仅检查过命令形状的内容。同一分支冲突、.env 不随行、stash 共享、有改动无法删除、git diff HEAD 遗漏未跟踪文件——这些都经过了测试仓库验证。wt 函数在 zsh 中运行过,CLAUDE_CONFIG_DIR 和 CODEX_HOME 的登录隔离用空文件夹检查过。codex review --uncommitted 和 claude -w 通过 --help 确认存在。而模型实际审查时的输出内容、端口冲突这两项没有运行测试。这种透明的验证边界,比那些通篇「亲测有效」却不说明测了什么的内容要可靠得多。

分享:

相关推荐