DEBUG.WIKI
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
雨云;给项目一台云服务器
RackNerd;$21.99/年 · 1GB KVM

pre-commit 恢复补丁时报 patch does not apply:先保住未暂存修改

pre-commit 运行中改变暂存区后恢复补丁失败,先保存日志所指补丁与当前仓库状态,再在恢复副本检查应用;--reject 仍需人工合并。

pre-commit 正在检查提交时,又在另一终端执行了 git add;检查失败后,日志提示恢复补丁出错,编辑器里的未暂存修改似乎少了。看到 patch does not apply 时,先停止继续提交和暂存,保存日志中指向的补丁文件,不要清理 pre-commit 缓存或重置工作区。

这篇针对 pre-commit 暂时移开未暂存修改、结束时无法重新应用补丁的情况。pre-commit 2.20.0 已有这条恢复路径,当前实现仍会把相应差异保存成补丁;具体位置以当次日志为准。它不等同于 Git stash 列表中的普通条目。

日志里的 patch 路径是恢复材料

普通提交检查需要让 hook 看到待提交内容。pre-commit 会保存未暂存的已跟踪文件差异,再暂时清除这部分修改,检查结束后尝试应用回来。中途改变索引,会使恢复时的文件状态偏离原先预期。

日志可能先出现 Stashed changes conflicted with hook auto-fixes,随后尝试回滚 hook 的修改并恢复补丁。如果最后仍以 CalledProcessError 和 patch does not apply 结束,不能把前面的“回滚”理解成所有工作都已经恢复。

找到当次日志的补丁路径,确认文件存在,复制到一个权限受控、不会被缓存清理的目录。路径可能位于用户缓存中,文件名包含时间和进程信息;不要照搬别人的 patch 文件名,也不要用目录中“最新文件”代替日志指向的文件。

补丁可能含业务代码或敏感配置,应限制访问并避免公开上传。它主要覆盖当时纳入差异的已跟踪文件,不能当作整个仓库、未跟踪文件和后来新增修改的完整备份。

先保存当前状态,再考虑应用

在仓库根目录只读查看:

git status --untracked-files=all
git diff --cached
git diff

分别检查暂存区和工作区,因为中途的 git add 可能已经改变两者的分界。保留当前重要文件、未跟踪文件、索引状态、当次日志和原补丁的独立副本,再做恢复;仅备份一份 git diff,可能漏掉暂存内容或未跟踪文件。

不清楚哪些内容已经恢复时,先在完整的仓库恢复副本中比较文件与补丁。副本应具有与恢复任务对应的基础提交和文件状态,不能随便新建一个空目录就应用同一份补丁。

下面只检查补丁是否能够完整应用,不修改文件;路径是占位示例,在恢复副本的仓库根目录运行:

git apply --check /path/to/saved-precommit.patch

检查通过后,阅读补丁,确认它没有与当前已有修改重复,再在该副本中应用:

git apply /path/to/saved-precommit.patch

普通 git apply 默认处理工作区,不等于恢复原先的暂存布局。不要在不了解索引状态时顺手加 --index;这个选项对索引和工作区的一致性另有要求。

--reject 可以留下部分结果,仍需逐个合并

检查失败时,先看错误文件和上下文是否已被后续修改、hook 或暂存操作改变。不要反复把同一补丁应用到已经改变的原工作区,也不要为“准备干净环境”执行硬重置。

在已保存原始材料的恢复副本里,可以评估部分应用:

git apply --reject /path/to/saved-precommit.patch

--reject 会应用能够匹配的部分,把不能应用的片段留在对应的 .rej 文件中。因此命令报错后,文件仍可能已经改变;不能按“失败就完全没动”处理。逐个对照拒绝片段、原补丁和当前文件,人工合并尚未恢复的修改,并确认可应用部分没有重复加入。

--reject 不保证恢复全部内容,也不能与 --3way 一起使用。没有 .rej 或命令返回成功,只能说明这一轮补丁处理的结果,不能替代对所有需要保留的工作进行核对。

恢复副本确认完整后,再按受控方式把审定结果合并回原仓库。保留原补丁和两边副本,直到文件、暂存划分及项目检查都完成;不要为了让状态列表好看而先删除恢复材料。

下一次检查期间保持索引稳定

核对原本未暂存的修改、这次有意暂存的内容和 hook 改动是否分别保留下来。按需要重新安排暂存内容,在没有其他进程编辑或暂存的情况下重新执行项目的提交检查,确认检查结束后修改仍完整。

自动保存、其他编辑器任务或脚本也可能在检查期间改变文件与索引。把这段操作安排为一个完整步骤:检查开始后等待它结束,再编辑和 git add。单纯升级 pre-commit,不能保证自动恢复一份在运行中被改动了基础状态的补丁。

如果恢复结果不满意,回到保存的仓库副本和补丁继续比较。不要使用 reset --hard、git clean 或删除缓存来“撤销失败”;这些操作可能把尚未确认的修改及唯一的补丁一并丢掉。