Git 解决 cherry-pick 冲突后,提交报 cannot amend
Git 冲突解决并暂存后,cherry-pick 仍可能尚未结束。需要独立提交时使用 --continue;确实要并入未共享的旧提交时,先确认 HEAD、保存工作,再区分 --quit 与 --abort 的影响。
执行 git cherry-pick 遇到冲突,改好文件并完成暂存后,再运行 git commit --amend,Git 仍然拒绝:
fatal: You are in the middle of a cherry-pick -- cannot amend.
暂存冲突解决结果不代表 cherry-pick 已结束。Git 还在等待你完成这次操作,而 --amend 要替换当前分支顶端的已有提交。这两件事对应不同的提交结果,需要先确定你要保留独立的新提交,还是确实要把改动并入原来的提交。
下面针对普通 cherry-pick 因冲突暂停的情况,相关行为可在 Git 2.51.0 中确认,当前命令文档仍提供这些操作。若状态显示正在 rebase 或 merge,不要套用这里的退出命令;也不要直接删除 .git/CHERRY_PICK_HEAD 来让提示消失。
冲突解决了,待生成的提交仍未完成
普通 cherry-pick 会把指定提交引入的变更应用到当前分支,并生成新的提交。发生冲突时,HEAD 停留在最后一个成功提交上,Git 用 CHERRY_PICK_HEAD 记录当前正在挑选的提交。
失败后执行 --amend,目标是当前 HEAD 指向的已有提交,并不是“把解决后的文件补进尚未生成的 cherry-pick 提交”。如果一次挑选了多个提交,HEAD 还可能已经前进到前一个成功挑选的结果,不能认定它仍是整个操作开始前的提交。
在仓库中先查看:
git status
git diff --name-only --diff-filter=U
git diff --cached
git diff
git status 用来确认当前操作;第二条列出索引中尚未解决的路径。git diff --cached 展示已经暂存、准备进入提交的内容,普通 git diff 则帮助发现暂存之后又改过、尚未加入索引的部分。
未解决路径列表为空,只说明索引中没有未合并条目,不保证程序逻辑正确。自动应用成功的文件也可能已经在暂存区,审阅时应看完整差异,不能只看自己手工改过的冲突文件。
要保留独立提交,就继续 cherry-pick
如果目的是完成这次挑选,逐个确认冲突取舍,再暂存实际解决的文件。下面路径是示例,应换成仓库里的路径:
git add -- src/report.php
git diff --name-only --diff-filter=U
git diff --cached --check
git diff --cached
仍有未解决路径时继续处理,不要直接进入下一步。--check 可提示新出现的冲突标记和空白错误,但不检查业务逻辑;还需要完成项目要求的检查。
暂存区确认无误后执行:
git cherry-pick --continue
普通非空的单次挑选会在这里记录解决后的结果。它会产生一个新的提交,这是普通 cherry-pick 的预期行为。如果挑选的是一串提交,命令还会继续处理后面的提交,可能再次停在新的冲突处,不能看到一次成功消息就认定整个序列已结束。
完成后查看 git status,确认没有正在进行的 cherry-pick,并检查提交内容与预期一致。提交说明编辑器或钩子失败时,先处理对应错误;如果提示结果为空,应确认变更是否已存在或冲突取舍是否把它消除了,再决定后续动作,不要改用 --amend 强行收尾。
确实要并入旧提交时,先核对 HEAD 和历史边界
另一种需求是:这次挑选的改动本来就应成为当前本地提交的一部分,不希望保留一个独立提交。只有明确知道当前 HEAD 是哪个提交、它尚未共享、暂存区只包含要并入它的内容时,才考虑修改历史。
先在仓库外保存重要文件与当前处理记录,包括未跟踪文件、冲突解决结果、操作开始时的提交和待挑选列表。分支或提交记录本身不能保存尚未提交的文件。若已经挑选成功了一部分提交,或者还有剩余待处理提交,应先重新规划整个序列;下述步骤只针对单个挑选暂停、HEAD 正是准备修改的那个本地提交的情况。
完成冲突解决并暂存后,再次审阅前面的状态与差异,然后退出当前操作:
git cherry-pick --quit
--quit 清理这次操作的状态,保留当前 HEAD、索引和工作区内容。它不会替你解决剩余冲突,也不会继续尚未处理的提交。退出后先检查:
git status
git diff --name-only --diff-filter=U
git diff --cached
确认没有正在进行的操作、没有未解决路径,而且暂存内容仍是要并入 HEAD 的改动,才执行:
git commit --amend
这会用新提交替换原来的分支顶端提交,提交哈希会变化。默认保留原提交的作者和父提交关系,并允许编辑提交说明;它不会自动保留被挑选提交的作者身份。需要保留独立作者与变更记录时,应使用前面的正常继续流程。
不要把这两个命令当成一条无需检查的组合。执行 --quit 后,即使后面的 amend 因钩子或编辑器失败,cherry-pick 状态也已经退出,不能再依赖原来的 --continue 或 --abort。应保留现场,检查暂存内容后按普通提交流程处理新的错误。
已经推送或被他人使用的提交,优先保留新提交,不要为了少一条历史记录就改写旧提交并强制推送。
下一次从一开始就选择是否生成提交
如果尚未开始挑选,而且明确要先收集变更、稍后自行决定如何提交,可以使用 --no-commit。下面的 SOURCE_COMMIT 是待挑选提交的占位值,执行前替换为实际提交哈希:
git cherry-pick --no-commit SOURCE_COMMIT
它把变更应用到工作区和索引,但不自动创建提交。开始前仍应检查并保存现有工作;这个选项允许索引与 HEAD 不一致,已有暂存内容可能与挑选结果一起留在索引中,所以事后的提交范围必须重新审阅。
应在开始挑选前选择这种方式,不要在已经暂停的普通 cherry-pick 上补跑相同提交。后者可能再次应用变更,无法用来追溯改变第一次操作的意图。
想放弃时,区分 abort 与 quit
仍处于 cherry-pick 中,而且决定放弃整个操作时,先保存想保留的冲突解决及额外编辑,再考虑:
git cherry-pick --abort
--abort 返回整个序列开始前的状态,已经完成的中间挑选结果也可能被撤回。它与保留当前内容、只退出操作状态的 --quit 用途不同,不能互相替换。
执行过 amend 后也不能用 cherry-pick --abort 撤销那个新提交。应根据事前记录,在保存当前工作后按项目的历史恢复流程处理;不要临时用 git reset --hard 清空现场。手工删状态文件同样不合适:多提交操作还可能有其他状态,删除一个文件并不能表达“继续”“放弃”或“保留结果退出”的完整意图。


