教程 / Codex 与 AI 编程

Codex 里怎么开分支和并行:Worktree、Handoff 与 thread fork

“在 Codex 里开个分支”这句话其实混了三个不同的东西:新对话时的运行环境、把已有对话搬到另一处(Handoff)、以及在接口层把一条 thread 的历史复制成新的 thread(fork)。三者用途不同,能做的事也不同。这篇按层拆开讲,每层都给可照做的操作和会踩的坑。

更新于 2026-10-24由 GPTHubs 团队 整理约 9 分钟读完Codex 与 AI 编程

先分清三种“分支”

官方术语里只有 Handoff 和 Thread fork 被明确定义过。日常说的“开分支”通常指的是第三件事:在 Git 层开一条 branch。所以先把三层对齐,后面就不会互相误解。

层解决什么问题官方叫法在哪儿用
运行环境这次任务在哪份文件树里跑,会不会弄脏你的工作区Local / Worktree / Cloud桌面端新建对话时选
搬移已有的对话和它的代码怎么换地方继续Handoff对话底部的运行位置入口
会话历史从一条已有对话的历史里开出新对话,两条各走各的Thread forkapp-server 协议、SDK
Git 分支代码层面的分叉与合并Branchgit 本身,Codex 不替你发明语义

三种运行环境怎么选

新建对话时可以选 Work in:这台电脑,或者云端。选这台电脑时再选 Local 还是 Worktree。这三个选项决定了改动落在哪儿。

环境改动落在哪适合什么注意
Local你当前的项目目录,直接改单线推进、想随时手工接管、改完立刻跑会和你的未提交改动混在一起
Worktree同一仓库的另一份检出,本机运行并行试两个方案、排队跑后台任务、先隔离再合并需要 Git 仓库;默认 detached HEAD
Cloud云端任务自己的工作区,基于已发布的环境长时间无人值守、需要预装依赖或联网凭据、要批量跑环境要先发布;比本地更费额度

Cloud 的关键差别是“环境”:依赖装什么、能连哪些服务、跑在什么基础镜像上,都要提前配置并发布,之后多个任务复用同一套环境。代价是配置有前置成本,好处是任务之间一致。另外云端任务有自己的一份工作文件,你本机的未提交改动、后台进程、浏览器登录态都不会跟过去。

用 Worktree 并行:一次完整操作

  1. 01

    1. 确认这是 Git 仓库

    不是 Git 仓库时 Worktree 选项没有意义。先 git init 或确认 clone 完整,工作区最好没有一半改一半没改的状态。

  2. 02

    2. 在输入框里选 Worktree

    新建对话时把运行环境选成 Worktree。如果项目配了本地环境的初始化脚本,可以在这里选一个,让新 worktree 自动装依赖。

  3. 03

    3. 选起始分支

    可以基于某个分支,也可以基于当前分支(包括你尚未提交的改动)。Codex 在 worktree 里默认以 detached HEAD 工作,目的是不占用分支名。

  4. 04

    4. 提交任务,等它跑

    这条对话此后就绑定在这个 worktree 上。你可以同时开第二条对话、选另一个 Worktree,两条并行改不同方向,互不污染。

  5. 05

    5. 收口

    满意了就把改动搬回 Local(Handoff),或者直接在 worktree 里建分支、提交、开 PR。不满意就丢掉这个 worktree,成本很低。

等价的手工做法:不用图形界面也能并行
# 手工开一份 worktree,和 Codex 托管的那份互不影响
git worktree add ../feature-a -b feature/a
git worktree add ../feature-b -b feature/b

# 看看现在有哪些 worktree,以及各自占着哪个分支
git worktree list

# 用完清理(先确认里面没有未保存的东西)
git worktree remove ../feature-a

gitignore 掉的文件怎么办

worktree 是新的一份检出,你的 .env、本地配置、证书这些被 gitignore 的文件不会自动出现。Codex 给了一个机制:在仓库根放一个 .worktreeinclude,列出要复制进去的路径。

把本地配置文件带进托管 worktree
# 仓库根目录新建 .worktreeinclude
cat > .worktreeinclude <<'EOF'
.env
.env.local
config/secrets.json
EOF

# 规则要点:
# 1. 只复制被 gitignore 的文件,已被跟踪的文件本来就在检出里
# 2. 跳过符号链接,已存在的不覆盖
# 3. 只对桌面端本机托管的 worktree 生效

Handoff:把对话和代码搬到别处继续

Handoff 解决的是“我已经在 Local 里聊了一半,现在想让它到隔离环境里继续”,或者反过来“云端/远程跑完了,我要在本机接着改”。它搬两样东西:对话本身,以及它的 Git 状态。

从到支持吗说明
LocalWorktree支持Codex 会建或复用目标 worktree,把对话切过去
WorktreeLocal支持把改动带回你原来的检出,最常见的收口路径
本机远程主机支持目标主机上必须已有同一个仓库的项目,且分支要对得上
远程主机本机支持在运行位置里选回这台电脑即可
任意Cloud 环境不支持云端任务从环境发布开始,不能从本地对话直接搬过去
  1. 01

    1. 打开要搬的那条对话

    注意:你没法让一条正在运行的对话把自己搬走。如果它正在生成,先停下,或者换一条对话来发起这次搬移。

  2. 02

    2. 点底部的运行位置

    那里显示当前在哪台机器上跑。点开就是目标列表,本机与已连接的远程主机都在里面。

  3. 03

    3. 选目标并确认分支

    Codex 会显示目标位置和将要使用的分支。远程主机上只列出匹配同一仓库的已保存项目,找不到就说明那边还没建项目。

  4. 04

    4. 确认 Handoff

    它会在目标位置建或复用 worktree,把对话和 Git 状态传过去。如果有响应正在生成,这一步会中断它。

命令行里直接分叉:codex fork

如果你在终端里用 CLI,分叉不需要动文件系统,也不需要重开一条空白对话——它是一个独立子命令。codex fork 会复制一条已有会话的历史,生成新的 session id,之后两条会话各自独立增长。原会话不会被改动。

三种常见用法
# 不带参数:弹出会话选择器,手动挑一条来分叉
codex fork

# 直接分叉最近一条会话,跳过选择器
codex fork --last

# 已知 session id 时精确指定,并可带一句开场提示词
codex fork 0f3c1a2e-8b6d-4a51-9c7e-2d4b8f1a6e30 "从断点继续,先跑一遍测试"
  • fork 出来的是新会话,历史被复制过去,但两边后续各写各的,互不影响
  • 需要接着原会话往下跑而不是分叉时,用 codex resume(--last 续跑最近一条)
  • 分叉只复制对话历史,不复制工作区的 Git 状态;代码层面的隔离仍然靠 worktree

接口层:thread fork 才是真正的“复制历史”

如果你在把 Codex 嵌进自己的工具里,分支就不该靠文件系统了,而是靠 app-server 的 thread 接口。thread fork 的含义是:从一条已有 thread 的存储历史里复制出一份,变成一个独立的 thread id,之后两边各走各的。

向 app-server 发一个 thread/fork 请求
// 请求:把 thr_123 的历史复制成一条新 thread
{
  "id": 12,
  "method": "thread/fork",
  "params": {
    "threadId": "thr_123",
    "lastTurnId": "turn_45",
    "ephemeral": false
  }
}

// 响应:新 thread 保留来源信息,sessionId 指向源 thread
{
  "id": 12,
  "result": {
    "thread": {
      "id": "thr_456",
      "sessionId": "thr_123",
      "forkedFromId": "thr_123"
    }
  }
}
  • lastTurnId:只复制到指定那一轮为止,后面的轮次不带过去,适合“从某个好的状态重新试一次”
  • excludeTurns:排除指定的若干轮,适合把一次跑偏的尝试从历史里剪掉
  • ephemeral 设为 true:只在内存里分叉,不落盘,适合对照实验或临时重试;对分页的 thread 做临时分叉时必须同时用 excludeTurns
  • 源 thread 正好在一轮中间时,fork 会记一条中断标记,而不是等它跑完
  • 分叉出来的 thread 会触发一次 thread/started 事件,事件里的 id 才是你后续要继续用的那个
SDK 侧:续跑同一条 thread,而不是重开一条
import { Codex } from "@openai/codex-sdk";

const codex = new Codex();

// 继续一条已有的 thread(本地执行)
const thread = await codex.resumeThread("thr_456");
const first = await thread.run("先看一遍这个仓库的测试策略");

// 再调用一次 run,就是在同一条 thread 上继续,不是新开对话
const second = await thread.run("按刚才的策略补上缺失的用例");
console.log(second.finalResponse);

常见坑与验收清单

现象原因怎么办
提示分支已被某个 worktree 占用git 不允许同一个分支在两处同时检出在目标 worktree 里换一个分支名,或者先把手上的 worktree 收掉
worktree 里改了半天,回 Local 看不到worktree 是独立检出,改动不会自动回来用 Handoff 搬回 Local,或在 worktree 里提交后合并
以为能从分享快照接着聊线程快照是只读的,不能从快照 fork 回原对话把快照当附件导入一条新对话参考,历史不会接上
Handoff 时正在生成的回复没了搬移会中断当前响应先等它跑完,或者换一条对话发起搬移
本地对话搬不到云端handoff 到云端环境不支持云端任务从发布好的环境新建,用 AGENTS.md 和文件把上下文带过去
想从断点开分支但找不到开关分叉不在对话界面里,是一个独立子命令用 codex fork:默认弹出会话选择器,codex fork --last 直接分叉最近一条,也可传 session id
  1. 并行前把任务切成互不重叠的分支,一次只让一条对话动一份工作区
  2. 每个 worktree 都从干净的起点出发,未提交的改动先 commit 或 stash
  3. 把仓库特有约定写进 AGENTS.md,让每条分支上的对话看到同样的规则
  4. 改完用 review 或 PR 收口,不要靠人肉 diff 记着哪条对话改了什么
  5. 用完清理 worktree,别让几十份检出把磁盘和分支名都占住
  6. 涉及密钥的并行任务,先想清楚哪些文件该被复制进 worktree

常见问题

并行开十条对话会更快吗?

通常不会。瓶颈几乎总在你自己:审阅改动、合并分支、处理冲突都要串行。实践里两到四条并行比较常见,再多就要靠更小的任务切分和更严的 CI 才能撑住。

Worktree 和直接复制一份仓库有什么区别?

worktree 共享同一个 .git 目录,分支、提交、远程都是一套,所以切换和合并成本低,也不会因为复制出一堆独立仓库而失去同步。复制目录则更像两个互不相干的仓库,最后合并会麻烦很多。

ephemeral 的 fork 值得用吗?

在需要反复对照、又不想污染历史时值得用,比如同一份上下文试三种提示词。它不落盘,所以进程结束就没了,不适合要留档的实验。另外对分页过的 thread 做临时分叉时必须显式排除若干轮,这不是可选项。

从快照看到别人的对话,能不能直接接着改?

不能。快照是只读视图,官方明确它不能用来获取原项目的访问权,也不能从快照 fork 回原对话。你只能把它当附件导入一条新对话当参考,历史与工具状态不会跟过来。

官方文档与参考

本文依据上述官方文档整理;价格、额度与功能会随官方更新变化,第三方中转站的行为以其自身说明为准。