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

Git 解决冲突后按文件提交,报 cannot do a partial commit during a merge

Git 合并尚未完成时不能按路径做部分提交。先核对暂存区和未解决冲突,再用不带路径的 git commit 完成合并,避免把额外修改一并提交。

合并或使用 merge 模式的 git pull 遇到冲突,手工改好文件后,像平时一样指定文件提交:

git commit -m "Resolve report conflict" -- src/report.php

Git 却返回:

fatal: cannot do a partial commit during a merge.

这里拒绝的是“合并尚未结束时按路径做部分提交”。即使文件中的冲突标记已经删干净,这种提交方式仍然不成立。需要先把完整的合并结果准备到暂存区,再执行不带文件路径的提交。

以下针对普通 Git merge 尚未完成的状态,也适用于 pull 选择 merge 后停在冲突处的情况。若 git status 显示正在 rebase 或 cherry-pick,应遵循对应操作的继续流程,不要照搬合并收尾步骤。

指定路径,会改变 git commit 读取内容的方式

不带路径的 git commit 根据当前暂存区生成提交。命令后面列出路径时,默认进入 --only 的行为:使用指定路径在工作区中的内容,忽略其他路径已经暂存的变更。

普通开发时,这可以用来提交选中的文件;但合并提交需要记录完整的合并结果及父提交关系,不能用逐文件提交把同一次合并拆开。git commit -- src/report.php 和 git commit . 都带了路径,后者的点号也不能当成“普通提交所有暂存内容”的简写。

改成 git commit -a 虽然可能避开部分提交错误,但会自动暂存已跟踪文件的修改和删除。如果工作区还有不属于本次合并的编辑,它们也可能被带进去。先核对暂存内容,再普通提交,可以更清楚地控制提交范围。

先分清未解决冲突、暂存结果和额外修改

在仓库中查看:

git status
git diff --name-only --diff-filter=U
git diff --cached --stat
git diff --cached
git diff

第二条列出索引中仍处于未合并状态的路径。它没有输出,表示没有这种索引记录;不能仅凭这一点断定业务上的冲突已经处理正确。手工删除冲突标记后,通常还需要暂存解决结果;有些合并工具会替你完成暂存,因此以状态为准。

git diff --cached 默认相对当前 HEAD 展示暂存区差异。合并时,其他分支带来的、无需人工解决的文件也可能已经在暂存区中。这些文件不是因为你编辑过才出现,而是 Git 已把自动合并成功的结果写入索引。

所以暂存区里出现“别人改的文件”并不说明仓库把提交作者弄错了,也不能仅因它们不是自己手工改的就全部撤销暂存。合并本来就要整合两边的变更;新建合并提交不会抹掉另一侧已有的提交历史。

最后一条 git diff 比较工作区与暂存区,帮助你发现尚未暂存的额外编辑。若同一个文件暂存后又被修改,它会同时在已暂存和未暂存部分出现,需要确认哪一个版本才是本次合并的最终结果。

暂存解决结果,整体完成这次合并

确认每个冲突的取舍后,只把已经检查好的解决结果加入暂存区。下面路径是示例,替换成实际冲突文件;有多个文件就逐个处理:

git add -- src/report.php

删除冲突中的文件也需要明确记录所选的删除结果。不要为了让错误消失就机械选择所有文件的 ours 或 theirs,那会改变本次合并保留的业务内容。

再检查一次:

git status
git diff --name-only --diff-filter=U
git diff --cached --check
git diff --cached

没有未解决路径后,仍要阅读暂存差异,并运行项目原本要求的检查。--check 能提示新增的冲突标记和空白错误,不会替你判断程序逻辑。不要把“Git 允许提交”当成“冲突处理正确”。

如果暂存区正是要记录的完整合并结果,执行下面的命令。这里不要再附文件名或点号:

git commit

编辑器会打开合并提交说明。写明整合的目标和重要冲突取舍后保存退出;需要在命令中给说明,也可以使用不带路径的 git commit -m "Merge topic and resolve report conflict"。如果只是提交说明编辑器或钩子报错,按新的具体错误处理,不能假定提交已经完成。

这一步提交整个暂存区,不会自动把所有未暂存修改和未跟踪文件一起提交。但前提是你已确认暂存区里没有混入无关编辑,不能用“不带路径”替代这项审查。

想把自己的额外改动单独提交时

如果额外改动只存在于与合并无关的文件中,而且尚未暂存,可以保留在工作区,先完成已核对的合并提交,再单独暂存这些改动。普通提交不会自动暂存它们。

如果额外改动已经和冲突解决混在同一个文件,git add 会暂存该文件当前的完整内容。不要贸然把整个文件恢复为 HEAD,或把自动合并的文件全部移出暂存区;这样可能使最终的合并提交遗漏另一侧的有效变更。

此时先在仓库外备份有关文件和当前工作区状态,再人工分离额外编辑与必须保留的冲突解决。若已无法可靠区分,保存现有工作后放弃本次合并、重新安排提交,通常比制造一个不完整的合并结果更可控。

git commit -i 中的 -i 是 --include:它在原有暂存内容之外,再暂存命令中列出的路径。它不表示只提交这些文件,也不是把合并拆成多个部分的开关。已有其他暂存内容时,不要因为加了这个选项就以为提交范围缩小了。

核对父提交与剩余工作;需要中止时保留现场

提交成功后查看:

git status
git log -1 --format='%h %s%nParents: %P'

对于普通两分支合并,最后一条的 Parents 后应有两个父提交哈希;状态不应再提示正在合并。工作区可以仍有此前故意留下的未暂存修改,因此“完全干净”不是唯一判断标准。还要核对这些剩余改动没有被意外带入合并提交。

如果决定放弃的是仍在进行中的合并,先备份重要文件及已完成的冲突处理,再考虑:

git merge --abort

它会尝试恢复合并前状态。若开始合并时已经有未提交修改,尤其是合并后又继续改过这些内容,Git 不保证能完整重建原来的状态。不要把它当作无需备份的撤销按钮,也不要在已完成合并提交后用它撤销历史。

不要手工删除 MERGE_HEAD 来消除报错,也不要用 git reset --hard 清场。前者可能让后续提交失去应有的合并关系,后者会丢弃未提交的工作。需要保留这次合并,就应提交完整的合并结果;决定放弃时,先保存工作再中止。