1. 先搞清楚这两个选项到底在争什么
很多人第一次在 Codex 里看到Current checkout和New worktree这两个选项时,手指会停在鼠标上犹豫几秒。界面上没有任何解释,文档里也只是一笔带过,选错了倒不会炸库,但接下来半小时的操作节奏会完全不同。我自己最开始用的时候,凭直觉一直选 Current checkout,直到有一次同时要验证两个互斥的改动方案,才被迫去研究 New worktree 到底是怎么回事。
先把结论摆在前面:这两个选项的本质区别,是你接下来的改动落在哪个工作目录上。Current checkout 就是你现在打开的这个目录,New worktree 则是基于当前仓库另开一个独立的工作目录。听起来像是"复制一份代码"这么简单,但真正影响体验的是它们背后的 Git 机制——一个是原地修改,一个是利用 Git 的 worktree 能力做隔离。
为什么这个选择值得单独写一篇?因为 Codex 这类工具的工作模式是"你给指令,它去改代码",而改代码这件事一旦和 Git 的工作区状态纠缠在一起,就会衍生出一堆连锁反应:未提交的改动会不会被覆盖、能不能同时跑两个任务、切换分支时会不会冲突、任务做完后怎么清理。这些问题的答案,全都取决于你一开始选了哪个。
我见过不少人把这两个选项当成"随便选一个都行"的按钮,结果要么是辛苦写了半天的改动被新任务冲掉,要么是磁盘里堆了一堆忘记删除的临时目录。所以这篇就按我自己的使用经验,把这两个选项的适用场景、底层逻辑、实操细节和踩坑记录完整梳理一遍。不管你是刚接触 Codex 的新手,还是已经用了一段时间但一直没搞明白区别的老用户,应该都能找到对你有用的部分。
2. Current checkout 的真实工作方式与适用边界
2.1 它其实就是"就地开工"
Current checkout 的逻辑非常直白:Codex 直接在你当前打开的这个仓库目录里干活。你看到的文件、你正在编辑的分支、你还没提交的改动,全都在它的操作范围内。它不会给你另开一个目录,也不会帮你把当前状态备份一份,就是在这个已经存在的空间里继续叠加新的修改。
这种模式最大的好处是零切换成本。你不需要在多个目录之间跳来跳去,不需要重新配置编辑器的工作区,也不需要重新装依赖(如果依赖是装在项目目录里的话)。任务做完,改动就在你眼前,直接 review、直接提交,整个流程是一条直线。
我日常大部分场景都用 Current checkout,尤其是那种"我知道要改什么、改动范围可控、做完就提交"的任务。比如修一个明确的 bug、加一个小的工具函数、调整一段配置,这些用 Current checkout 是最顺手的。它就像你在自己书桌上改稿子,笔和纸都是现成的。
2.2 什么时候它会变成麻烦
Current checkout 的问题不在于它本身,而在于它和你的未提交改动共享同一个空间。这是最容易被忽略的一点。
假设你手头有一批改了一半的代码,还没提交,这时候你让 Codex 用 Current checkout 去做一个新任务。Codex 在改代码的过程中,很可能会碰到你正在改的那些文件。如果它的改动和你的改动落在同一片区域,冲突就来了——轻则它覆盖了你的修改,重则两边的内容混在一起,你分不清哪段是谁写的。
我自己踩过一次比较典型的坑:当时在调一个模块的日志输出,改了几个文件还没提交,顺手让 Codex 用 Current checkout 去加一个无关的小功能。结果它为了"保持代码风格一致",把我正在改的那个日志文件也顺手格式化了,我半天的调整全没了。虽然能从 Git 的暂存区或者编辑器的本地历史里找回一部分,但那种感觉非常糟糕。
所以 Current checkout 有一条隐含的使用前提:你当前的工作区最好是干净的,或者至少你清楚哪些文件正在被改动,并且这些文件不会和 Codex 的任务重叠。如果你做不到这一点,就应该考虑 New worktree。
2.3 一个容易被忽视的细节:分支状态
还有一个细节值得单独说:Current checkout 会继承你当前所在的分支。如果你现在在某个功能分支上,Codex 的改动就直接落在这个分支上。这本身没问题,但如果你接下来打算切分支,或者这个分支还有别的用途,就要提前想清楚。
我一般的做法是,用 Current checkout 之前先确认三件事:当前分支是不是我想让改动落地的分支、工作区有没有未提交的改动、这些改动会不会和即将进行的任务冲突。这三件事花不了十秒钟,但能避免很多返工。
提示:如果你不确定当前工作区是否干净,在让 Codex 动手之前先跑一次状态查看,把未提交的文件列出来扫一眼。这个习惯能帮你挡掉大部分"改动被覆盖"的事故。
3. New worktree 到底新在哪里
3.1 它借的是 Git worktree 的能力
New worktree 这个名字里的 worktree,指的就是 Git 原生的worktree机制。简单说,Git 允许同一个仓库同时挂载多个工作目录,每个目录可以检出不同的分支,但它们共享同一份版本历史。你在其中一个目录里的提交,在另一个目录里也能看到(因为历史是共享的),但工作区的文件是各自独立的。
Codex 的 New worktree 就是基于这个机制:它会在你当前仓库之外,另开一个工作目录,通常是一个临时路径,然后在这个新目录里执行任务。你原来的目录完全不受影响,该是什么状态还是什么状态。
这个设计的价值在于隔离。新目录里的改动、新目录里的分支、新目录里的未提交内容,全都和你的主目录隔开。Codex 在里面怎么折腾,都不会碰到你手头正在改的东西。
3.2 隔离带来的三个实际好处
第一个好处是可以并行。你可以在主目录里继续做自己的事,同时让 Codex 在 worktree 里跑另一个任务,两边互不干扰。这对那种"我想同时验证两个方案"的场景特别有用。以前要这么做,得手动 clone 一份仓库或者 stash 来 stash 去,现在一个选项就解决了。
第二个好处是失败成本低。如果 Codex 在 worktree 里的改动不理想,你直接把这个目录删掉就行,主目录干干净净,一点痕迹都不留。用 Current checkout 的话,改动是直接落在你主目录里的,清理起来要麻烦得多,尤其是当它改了一堆文件的时候。
第三个好处是分支干净。New worktree 通常会基于一个干净的分支状态开始,不会把你主目录里那些乱七八糟的未提交改动带进去。这意味着 Codex 看到的代码是"纯净"的,它做出的判断和改动也更可预测。
3.3 代价是什么
天下没有免费的午餐,New worktree 的代价主要体现在两个方面。
一是环境成本。新目录是一个全新的工作目录,如果你的项目需要安装依赖、构建产物、配置本地环境变量,这些在新目录里可能都要重来一遍。对于依赖很重的项目(比如前端项目动辄几百兆的 node_modules),这个成本不能忽略。我遇到过好几次,worktree 建好了,结果因为依赖没装,Codex 跑起来各种报错,最后还得手动去补环境。
二是管理成本。worktree 用完是要清理的。如果你忘了删,磁盘里会慢慢堆积一堆临时目录。Git 本身提供了查看和清理 worktree 的命令,但前提是你得记得去用。我现在的习惯是,任务一结束就顺手清理,绝不拖到"以后再说"。
4. 按任务类型做选择:一张对照表
光讲原理还是容易犯迷糊,我把自己常用的判断逻辑整理成了一张表,按任务类型来对照,基本能覆盖八成以上的场景。
| 任务类型 | 推荐选项 | 核心理由 |
|---|---|---|
| 修一个明确的 bug,改动范围小 | Current checkout | 就地改完就地提交,流程最短 |
| 加一个小功能,不涉及大范围重构 | Current checkout | 同上,且不需要额外环境 |
| 当前工作区有未提交改动 | New worktree | 避免改动被覆盖或混淆 |
| 想同时验证两个互斥方案 | New worktree | 隔离是刚需,主目录保持不动 |
| 探索性任务,不确定结果如何 | New worktree | 失败直接删目录,零成本回退 |
| 依赖很重、环境搭建麻烦的项目 | Current checkout | 省去重装依赖的时间 |
| 需要长时间运行、中途可能中断 | New worktree | 不阻塞主目录的日常使用 |
| 改动需要立刻 review 并提交 | Current checkout | 改动就在眼前,无需切换 |
这张表不是死规矩,但它背后的判断维度是清晰的:看隔离需求和环境成本哪个更重要。隔离需求强(有未提交改动、要并行、要试错),就选 New worktree;环境成本高(依赖重、配置麻烦),就倾向 Current checkout。两者都强的时候,就得权衡一下,我一般会优先保证隔离,因为环境问题是可以解决的,改动被覆盖的损失往往更大。
4.1 一个具体的决策例子
举个我最近遇到的场景。当时我在主目录里改一个数据处理模块,改到一半,突然发现另一个模块有个明显的逻辑错误需要修。这两个改动互不相关,但都在同一个仓库里。
如果我直接用 Current checkout 去修那个逻辑错误,Codex 很可能会碰到我正在改的文件(因为两个模块有共享的工具函数),风险不小。所以我选了 New worktree,让 Codex 在新目录里修那个逻辑错误。修完之后,我在新目录里 review、提交,然后把那个提交 cherry-pick 回主分支。整个过程主目录的改动一点没受影响,两边都干净。
这个例子里,隔离的价值体现得很明显。如果当时图省事用 Current checkout,很可能就是一场混乱。
4.2 反过来,什么时候我坚决不用 New worktree
有一类项目我基本不会用 New worktree:依赖特别重、环境配置特别繁琐的。比如某些需要编译原生模块的项目,或者需要连接本地数据库、消息队列才能跑起来的项目。这类项目在新目录里重新搭一遍环境,时间成本太高,有时候还会因为路径变化导致配置失效。
这种项目我宁愿先把主目录的改动提交或者暂存起来,把工作区弄干净,然后用 Current checkout。虽然多了一步"清理工作区"的操作,但比重新搭环境划算得多。
5. 实操中的完整流程与命令细节
5.1 用 Current checkout 之前该做什么
用 Current checkout 之前,我固定会做两件事。
第一件是查看工作区状态。这一步的目的是确认没有"意外的未提交改动"。有时候你自己都忘了昨天改了什么,扫一眼能避免很多问题。如果发现有未提交的改动,先判断它们会不会和即将进行的任务冲突。会冲突的话,要么先提交,要么先暂存。
第二件是确认当前分支。如果你不希望改动落在当前分支上,就得先切到目标分支。这一步很容易被跳过,但一旦落错分支,后面要挪动提交就麻烦了。
# 查看当前工作区状态 git status # 查看当前所在分支 git branch --show-current # 如果有未提交改动且暂时不想提交,可以先暂存 git stash push -m "临时保存:日志模块调整"暂存这个操作我用了很多次,它相当于给你的未提交改动拍个快照,然后把工作区恢复干净。等 Codex 的任务做完、提交之后,再把暂存的内容恢复回来。这样既保证了工作区干净,又不会丢失手头的改动。
5.2 用 New worktree 时的环境处理
New worktree 建好之后,第一件事是确认环境。我一般会按这个顺序检查:依赖有没有装、配置文件在不在、构建产物需不需要重新生成。
对于依赖,如果项目用的是常见的包管理器,通常在新目录里跑一次安装命令就行。但要注意,有些项目的依赖安装脚本会写死路径,或者依赖主目录里的某些文件,这种情况下新目录里可能会失败。遇到这种问题,我的处理方式是手动把必要的配置复制过去,或者临时改一下脚本里的路径。
# 在新 worktree 目录里安装依赖(以常见包管理器为例) cd /path/to/new-worktree npm install # 如果项目有构建步骤,先跑一次构建 npm run build环境确认没问题之后,再让 Codex 开始干活。这个顺序很重要,如果环境没弄好就让它跑,它可能会因为各种报错而做出奇怪的改动,反而增加清理成本。
5.3 任务结束后的清理
New worktree 用完一定要清理,这是我踩过坑之后养成的习惯。清理分两步:先确认里面的改动都已经处理完(该提交的提交了,该丢弃的丢弃了),然后删除这个 worktree。
# 查看当前仓库有哪些 worktree git worktree list # 删除指定的 worktree git worktree remove /path/to/new-worktree # 如果目录里有未提交改动导致删不掉,可以强制删除 git worktree remove --force /path/to/new-worktree强制删除这个选项要慎用,它会连同未提交的改动一起丢掉。用之前一定确认里面没有你还需要的改动。
注意:worktree 的清理不只是删目录,还要让 Git 知道这个 worktree 已经不存在了。用
git worktree remove而不是直接rm -rf,后者会留下 Git 的元数据残留,时间长了git worktree list里会出现一堆幽灵条目。
6. 那些文档里不会写的坑
6.1 未提交改动被覆盖的真实案例
前面提过一次改动被覆盖的经历,这里展开说下细节,因为它太典型了。
当时的情况是:我在主目录里改一个配置文件,改到一半,让 Codex 用 Current checkout 去加一个日志功能。Codex 在实现日志功能时,需要读取配置文件来获取日志级别,于是它"顺手"把配置文件也改了——加了一个默认的日志级别字段。问题是,它改的那一行,正好是我正在编辑的那一行。结果就是,我的修改和它的修改混在一起,而且因为它的改动是后写入的,我的部分内容被覆盖了。
事后复盘,根本原因是我没有保证工作区干净。如果当时用 New worktree,或者先把配置文件的改动提交/暂存,这个事故就不会发生。所以我现在有一条铁律:只要工作区有未提交改动,就默认用 New worktree,除非我百分之百确定改动不会重叠。
6.2 worktree 里的分支陷阱
New worktree 会检出某个分支,这里有个容易踩的坑:如果你指定的分支已经在主目录里被检出了,Git 默认不允许同一个分支在两个 worktree 里同时检出。这时候 Codex 可能会自动创建一个新分支,或者报错。
自动创建新分支这个行为本身没问题,但如果你没注意到,后面提交的时候可能会发现改动落在了一个你没预期的分支上。我的做法是,用 New worktree 时主动指定一个明确的新分支名,而不是让它自己决定。
# 创建 worktree 时指定新分支 git worktree add -b feature/temp-task /path/to/new-worktree这样分支名是可控的,后面不管是合并还是删除,都清楚自己在操作什么。
6.3 磁盘空间的隐形消耗
worktree 是完整的工作目录,意味着它会占用和主目录差不多的磁盘空间(取决于项目大小)。如果你的项目有几个 G,每开一个 worktree 就多几个 G。我有一段时间没注意清理,磁盘空间悄悄少了一大截,查了半天才发现是 worktree 堆积。
现在的习惯是,每次用完 worktree,当天就清理。如果任务需要跨天,我会在任务清单里记一笔,提醒自己第二天处理完就删。
6.4 编辑器工作区的重新配置
如果你用 IDE 打开项目,New worktree 是一个新路径,IDE 需要重新打开这个目录,相关的插件配置、调试配置、运行配置可能都要重新设一遍。对于配置复杂的项目,这个成本不低。
我的应对方式是,把常用的运行和调试配置做成项目内的共享配置(比如放在项目目录里的配置文件),这样新 worktree 打开时能直接复用,不用手动重配。这个习惯帮我省了不少时间。
7. 我个人的选择习惯与几条经验
用到现在,我的选择逻辑已经比较固定了,总结下来就是几条简单的规则。
规则一:工作区不干净,一律 New worktree。这条没有例外。改动被覆盖的代价太高,不值得为了省一点环境搭建时间而冒险。
规则二:探索性任务,一律 New worktree。探索意味着结果不确定,可能改了一堆东西最后发现方向不对。这种任务用 New worktree,失败直接删目录,主目录一点不受影响。
规则三:依赖重、环境搭建麻烦的项目,优先 Current checkout。前提是工作区干净。这种情况下重新搭环境的成本太高,不值得。
规则四:任务做完立刻清理。不管是提交还是删除,不要拖。拖着的后果就是磁盘堆积和状态混乱。
还有一条不算规则但很重要的经验:不要在两个选项之间反复横跳。我见过有人一个任务里先用 Current checkout 改了一半,觉得不对又切到 New worktree 重来,结果两边都有改动,最后自己都搞不清哪个是最新的。选定一个就做到底,中途要换的话,先把当前状态处理干净。
最后说一个我最近才用顺手的技巧:如果任务比较复杂,我会先用 New worktree 做一版,验证思路可行之后,再把改动整理成干净的提交,应用到主目录。这样主目录里留下的都是经过验证的改动,不会有中间过程的噪音。这个流程比直接在 Current checkout 里试错要清爽得多,尤其适合那种"我知道大概怎么做但细节需要调"的任务。
这套习惯不是一天形成的,中间踩过的坑、丢过的改动、清理过的临时目录,都是学费。希望这些经验能帮你少走点弯路,把精力花在真正重要的代码上,而不是和工具的工作区状态较劲。