故障处理

Codex 改坏了怎么撤销?保留原有修改的检查与还原边界

想只撤销 Codex 的改动,又怕覆盖自己的修改?先保全工作区与暂存状态,再区分未提交、已提交和混合改动,按文件或差异块核对还原;没有基线、无法分离时明确停止条件。

Codex 把代码改坏了,你想撤销它刚才的修改,但项目里还有自己没提交的工作。先停止继续写入、保全当前现场,再判断哪些变化有证据可以撤销。 不要直接点“全部还原”,也不要让一句“帮我恢复原样”变成对整个工作区的重置。

本文对应“Codex 改坏了怎么还原”“只撤销 Codex 的改动,保留我的修改”和 codex revert keeping my changes。完成后应能说清:要保留什么、从哪个版本还原什么,以及如何验证。如果双方修改已经重叠,又没有可核验的原版本,正确结果可能是停止自动还原、保全并人工重建,而不是声称精准撤销成功。

先确认你要撤销的是什么

当前情况 应走的路径
修改还没提交,能与自己的工作明确区分 第 1、2 步后,按第 3 步逐块还原
Codex 的修改已经暂存 第 3 步先分清“取消暂存”与“撤销文件内容”
有一个只包含待撤改动的普通提交 第 4 步审查后用新提交反向撤销
同一差异块混有双方修改,或提交也混在一起 第 5 步保留现场,人工重建,不整块丢弃
已经误点还原,自己的内容也消失了 第 5 步查已有副本、历史版本与提交记录
没有 Git 仓库或没有可比较的版本 保全文件后按第 5 步处理,不先初始化仓库制造“原版”

如果连修改在哪个 checkout 都没确认,先查 worktree 与改动定位。本文处理的是已经找到改动之后的取舍与还原,不把“主目录里没变化”当作撤销完成。

会话回退、审查范围和文件还原不是同一件事

OpenAI 官方审查说明明确:桌面审查面板反映仓库状态,可能同时包含 Codex、你自己和其他未提交的改动;Unstaged、Staged、Branch、Last turn 是不同查看范围。视图名称不能代替逐项归属核对。 官方同时提供整份 diff、单文件和单个 hunk 的暂存与还原操作,使用前仍要检查当前选区。

不要把撤回一条消息、恢复聊天或退出任务当作文件已恢复的证据。例如开发者接口 app-server 的 thread/revert只回退指定会话历史,不还原本地文件;这也不是让普通用户输入的 CLI 命令。不同客户端按实际界面确认,不照搬“双 Esc”之类其他产品的操作。

1. 停止继续写入,保全文件和暂存状态

适用条件: 当前内容还有需要保留的部分,尚未执行还原或清理。

先停止当前 Codex 写入任务及会继续改文件的格式化、生成或同步操作。编辑器里尚未保存的内容,先另存到独立的本地救援位置;不要为保存缓冲区覆盖唯一的磁盘版本。核对实际执行主机与项目目录,确保备份的是本次任务使用的那一份。

使用能显示隐藏文件的文件管理器或已有备份工具,把项目复制到仓库之外的新目录,保留原现场不动,并明确记录保全范围:

  • 工作区当前文件,包括需要保留的未跟踪文件、忽略文件和二进制文件;不能只复制 Git 列出的变化。
  • 已删除文件的删除状态、当前分支/HEAD,以及暂存区版本;普通独立仓库要同时保全隐藏的 .git 目录。
  • 符号链接的链接本身和需要保留的实际目标;仅复制链接并不保存外部目标内容。

linked worktree、子模块或 .git 是指针文件时,单拷项目目录不等于完整仓库备份。 依据 Git worktree 的管理结构,还需分别保全实际 Git 管理目录、共享仓库及相关工作区;不能把副本中的指针仍指向原仓库当作独立恢复环境。在不确定这些关系时,先保全可见文件和编辑器内容,暂停 Git 写操作,找熟悉该仓库的人核对。

备份可能包含本地密钥,使用已有授权的本地安全位置,不上传公开服务。复制完成后,实际打开副本中的一个原有修改文件、一个未跟踪文件及必要的忽略文件,核对内容;有暂存内容的,还要能在独立恢复副本中查看相应版本。无法确认副本覆盖范围时,不进入还原。

不要把 git stash 当成上述操作的等价替代。Git stash 文档说明,它会保存并移走工作区/索引改动;默认不包含未跟踪与忽略文件,-u 和 -a 的范围也不同。此时先运行 stash 会改变现场,不能仅凭出现一个 stash 条目就宣告完整保全。git diff 导出的补丁也不包含未跟踪文件正文,不是一份完整备份。

2. 只读核对版本,列出“保留”和“待撤”清单

适用条件: 已有可核验的保全副本,任务已经停止写入。下面在原仓库的根目录运行,适用于已存在的 Git 仓库;不要修改 Git 全局配置。

git rev-parse --show-toplevel
git rev-parse HEAD
git --no-optional-locks status --short --untracked-files=all
git --no-pager diff --no-ext-diff --no-textconv --
git --no-pager diff --cached --no-ext-diff --no-textconv --

按 Git status与 Git diff的含义分别阅读:普通 diff 比较工作区与暂存区,--cached 比较暂存区与 HEAD。?? 是未跟踪文件,不会因为普通 diff 没输出就被证明没有变化;忽略文件也要对照自己的保全清单。没有 HEAD、正在合并/变基或出现冲突时,先处理相应状态,不继续套用下文的普通还原路径。

把结果列成三类:明确要保留、证据确认可撤、暂时无法归属。 优先对照任务前的提交、已保存快照、编辑器历史和可核验 patch。会话记录可帮助定位工具操作,文件时间戳只能辅助排查;二者都不能自动标注每一行是谁修改的。

例如,你修改了文件第 5 行,Codex 修改第 80 行,两块差异可以分别核对。若你和 Codex 都改了同一个函数的一行,diff 只给出与所选来源的最终差异,不会保留完整编辑顺序。不确定的内容留在“无法归属”清单,不靠猜测执行还原。

3. 未提交的改动:逐块核对,分清来源与目标

只有工作区中的某些差异需要撤销

适用条件: 文件已跟踪;当前暂存区中该部分是你确认要保留的版本;你能确认每个待选块相对暂存区的差异都可以丢弃。若暂存区也已经包含错误版本,或同一块含有要保留的后续手改,不满足条件。

先查看一个具体文件。下列 src/example.ts 是示例路径,必须换成已核对的实际仓库相对路径:

git --no-pager diff --no-ext-diff --no-textconv -- "src/example.ts"

确认后再执行这个会修改文件内容的交互命令;不要与上面的只读检查一口气运行:

git restore --worktree --patch -- "src/example.ts"

Git restore 官方说明中,不带 --source、不带 --staged 时,默认从暂存区恢复工作区。这里的 --patch 让你选择差异块;在提示中只对已核准丢弃的块选择 y,保留的块选 n,拿不准就 q 退出。-p 不识别 Codex 作者;一个块混有双方修改时,不能因部分内容来自 Codex 就整块选 y。

预期结果: 所选变化从工作区移除,暂存区不因此改变,未选的原有修改保留。退出后立刻重复第 2 步并对照副本;若看到新变化、选区不清或自己的内容消失,停止后续操作,从副本对照恢复必要内容,不反复试整文件还原。

Codex 的修改已经暂存

适用条件: 已保存暂存区版本,且能从 --cached 差异中独立确认待撤的块;当前没有冲突。

git restore --staged --patch -- "src/example.ts"

这一步默认以 HEAD 为来源,只改变暂存区,不撤销工作区里的文件内容。所以取消暂存后,错误代码仍在编辑器里,是预期现象。重新查看普通 diff;只有新出现的待撤块仍可独立确认、且暂存区现在是目标版本时,才按上一节对工作区逐块还原。原有未暂存修改若与它合并成一个块,停下,转第 5 步。

不要为了省一步,把 --staged、--worktree 和 --source=HEAD 一起加到整个文件上。HEAD 可能比你任务前尚未提交的修改更旧,整文件替换会一起覆盖这些工作。新建未跟踪文件没有可供 restore 使用的暂存区版本:先保全、查是否已经被你继续编辑或引用,再人工处理已确认不需要的具体文件;不批量 clean。

使用桌面审查面板时

按 官方审查面板说明,选择正确仓库和 Staged/Unstaged 等范围,展开具体文件,读完选区后才使用单个 hunk 的还原。若只能看清整文件、不能确认块内全部内容可丢弃,就退出该操作并人工比较。不要用 Revert all 代替归属判断,也不要把 Stage/Unstage 当作文件内容已恢复。

4. 已经提交:只对范围干净的普通提交使用 revert

适用条件: 待撤的是一个已核对的、非 merge 的普通提交,只包含本次确定要撤的改动;当前工作区和暂存区干净,不在其他 Git 操作中。若提交混有自己的工作,整笔 revert 也会反向撤掉它,不满足本节条件。

先替换占位值并审查完整提交,不能只凭提交标题或作者认定范围:

codex_bad_commit='替换为已核对的提交哈希'
git --no-pager show --stat "$codex_bad_commit"
git --no-pager show --format=fuller --no-ext-diff --no-textconv "$codex_bad_commit" --
git --no-optional-locks status --short --untracked-files=all

检查通过后,下面的操作会生成一个新的反向提交;已有提交历史保留。先在独立恢复副本验证,确认后才在正确原仓库执行;本步骤不包含推送或部署:

git revert --no-edit "$codex_bad_commit"

预期结果: 新提交反向应用目标提交的变化,之后仍需按第 6 步验证行为与其他工作。失败时保留错误,不加 --force 重试。若本次 revert 进入冲突且你尚未开始额外编辑,可以用 git revert --abort 取消本次操作并检查回到操作前状态;若冲突后又有需要保留的手改,先另存它们,再考虑取消。

Git revert 官方文档定义的是反向提交操作,不是挑选某位作者所有改动。merge 提交、多笔相互依赖的修改、已被别人继续开发的代码,都需要另行审查,不能盲选父提交或整段回滚。已推送到共享分支的错误提交也不要默认 reset 后强推;先与协作者确认反向修复范围。

5. 无法分离或已经丢失:停止自动还原,按证据重建

以下任一情况都应停下: 同一差异块混有双方修改且没有可核验基线;不清楚还原来源是否含自己的工作;未保存内容只有一份;备份尚未验证;合并冲突、二进制/重命名关系无法确认;或操作将涉及另一棵工作树、子模块与外部文件。

可以按下面的顺序继续,目标是恢复已经有证据的内容,不是让工具猜一个“原样”:

  1. 保留当前磁盘版本、编辑器缓冲区及已有副本,分别命名,避免相互覆盖。已经误还原时也先停写,不清理仓库或历史。
  2. 查任务前提交、事前副本、编辑器本地历史/时间线及已有 stash。先打开历史版本到单独文件或比较视图,核对确实有要保留的内容;不要直接点击全项目恢复。
  3. 在独立副本中按“保留行为”和“待撤行为”逐项重建一个文件,先小范围测试,再把审查过的具体修改带回原项目。不同版本同一行的修改不能凭时间戳机械合并。

Git reflog记录的是本地引用指向的变化,可帮助找以前的提交,不是每次保存文件的版本历史。没有提交、暂存对象、编辑器历史或备份的内容,不能承诺找回;也不要把创建新分支或现在才做一次空提交当作已经补回任务前基线。

若让 Codex 辅助,先给只读任务,写明实际文件和保留目标;这是一次正常请求,会使用当前账号额度:

这一轮只分析,不修改、暂存、还原、提交或删除任何内容。
目标文件:<已核对的实际路径>。
必须保留的行为:<逐项列出我的修改及已有证据>。
待撤的行为:<本次错误变化及对应 patch 或快照>。
请分别比较工作区、暂存区和指定基线,列出可确认可撤与无法归属的部分。
会话记录只作线索;不要按时间戳猜作者,不调用全量还原、reset --hard 或 clean。
如果同一差异块无法分离,请说明缺少什么证据,并给出人工重建与验证建议。
输出供我审查的方案,等待我明确选择具体操作后再修改。

求助时只提供脱敏路径、状态、必要 diff、客户端/Git 版本及已执行的命令;不上传完整救援副本或密钥。若最终只能确认部分内容,明确标记“已恢复部分、其余未确认”,不要称为完整恢复。

6. 验收:保留的内容还在,待撤的变化确实消失

适用条件: 已完成一次小范围还原或人工重建,尚未继续新一轮开发。

重新运行第 2 步的状态与两类 diff,对照事前写下的清单,而不是以工作区变干净作为唯一目标:

  • 自己的每项修改仍在,包括尚未暂存、未跟踪、必要的忽略文件,以及应保留的暂存区内容;必要时逐文件与救援副本对读。
  • 本次核准撤销的变化已经移除;有意保留的其他内容没有被整文件覆盖。取消暂存本身不算撤销完成。
  • 在原项目运行与这次变化有关的测试、构建或复现步骤,确认错误行为消失、自己的功能仍正常。选用项目已有命令,不凭空编造 npm test 适用于所有项目。
  • 保全副本仍可读取,来源、目标、操作记录和未解决项能对应;验收前不删除副本或 stash。测试通过也不能代替文件范围核对。

如果“恢复成功”只是模型口头回复,回到实际文件与差异;若文件读取本身有疑问,见 当前文件读取验证。遇到 Git 写入拒绝,转 index.lock 与权限排查,不要为完成撤销直接扩大权限。下一次希望先看方案再动文件,可参考 计划模式与改动验证,同时在任务前保留可核验的版本。

本文于 2026-10-17 核对 OpenAI 与 Git 官方资料,并在独立测试仓库验证状态检查、逐块还原、取消暂存、反向提交及混合改动的边界。没有对用户真实项目执行还原,没有调用模型或验证所有客户端的 UI;命令行为验证与桌面产品操作范围分别说明。

社区讨论

补充你的经验或问题

评论由 GitHub Discussions 承载;参与讨论需要登录 GitHub。