处理git pull冲突时,第一反应不应该是找某个命令去强制覆盖。先想清楚本地和远端的差异来自哪里,因为不同来源的冲突,处理方式会反转。下面记录的是我在实际排查中常用的一条路径:先检查、再选参数、最后验证。
先别急着敲命令,把冲突状态看明白
当git pull提示冲突时,不要直接敲命令,先执行git status。看输出中哪些文件处于Unmerged状态,并确认本地分支基于哪个远端分支。重点要区分两种情况:本地有没有已提交的commit,或者只是工作区有未暂存改动。如果本地改动尚未提交,可以先用git stash暂存,让pull顺利进行;如果已经提交,则冲突通常发生在合并提交时。这两类情况的处理策略并不相同,先检查清楚再动手。
这里有一个容易忽略的点:git status的输出里,Unmerged文件会显示成“both modified”。如果本地还没有提交,那碰撞的只是工作区内容,stash是最稳妥的止血方式。如果已经有了本地提交,冲突发生在“合并”语义下,后面再用--ours或--theirs才具备操作前提。不要在一开始就想着用哪个命令,先看清楚自己在哪个状态。
用参数调整合并偏好时,先确认本地提交是否有保留价值
如果希望在拉取时自动偏向本地版本,可以在fetch后用git merge -X ours origin/。这个参数的作用是:当两方都有改动时优先选择当前分支的内容,但会保留远端对另一处文件的更新。要注意它只影响合并时的冲突偏好,不改变提交历史。使用前先确认本地是否有需要保留的提交。如果完全以本地为准,也可以直接git pull -X ours origin ,但这样会把远端的新提交一并合入,只是冲突处选择本地版本。
注意-X ours和git checkout --ours不是一回事。前者是合并策略的偏好,遇到冲突行时会自动选择ours;后者是冲突已经产生后,手动把文件整体恢复到ours版本。如果只想保留本地某些文件的改动,而不是所有冲突都偏向本地,建议不要用-X ours,而是走一遍正常的merge,然后对特定文件手动git checkout --ours。用这个参数前,最好把当前的改动提交或者stash,避免本地未提交的内容混进合并结果。
冲突已经发生时,手动恢复文件并提交
如果合并已经完成但出现冲突,可以用git checkout --ours 把文件恢复为本地当前分支的版本,然后git add该文件,最后git commit完成合并。对应地,--theirs会取远端的版本。操作前先用git diff --name-only --diff-filter=U查看冲突文件列表,避免漏掉。注意,--ours和--theirs只在冲突状态下有效;一旦提交后就无法再用它们切换这次合并的版本。所以要在add之前决定好。
这个流程里最容易出错的是漏文件。有的冲突文件没有标出明确的分隔符,比如二进制文件,git只会显示冲突状态但不会在文件里插入<<<<<<<标记。这时候要用git diff --name-only --diff-filter=U来列出所有冲突文件,再逐个处理。如果是二进制文件,git checkout --ours可以直接把它恢复成某个版本,不需要手动改。处理完所有冲突后,可以用git status确认不再有Unmerged文件,再执行git commit。
git status
git diff --name-only --diff-filter=U
git checkout --ours -- <file>
git add <file>
git commit做完git add之后,如果发现选错了版本,在commit之前还能用git checkout --theirs再覆盖回来,然后重新add。但一旦commit完成,这次合并的“ours”和“theirs”上下文就消失了,再想找回远端版本,只能git revert或者重新merge。所以提交前多确认一次,比提交后再补救省事。
不要忽略rebase模式下ours和theirs会反转
如果你平时习惯用git pull --rebase,那在处理冲突时要格外小心。rebase会把本地提交重新应用到远端提交之上,这时冲突中的ours指远端分支,theirs指本地提交。所以想保留本地改动,反而要选--theirs。这个方向与普通merge正好相反。
在rebase冲突时不要盲目套用merge的思路。可以先执行git status,看当前正处于哪个rebasing阶段,再用git diff查看具体冲突内容。如果本地提交较多,手动合并时最好把每个提交都搞清楚,不要想着一次解决所有冲突。也可以使用git checkout --theirs -- <file> 来提取本地内容,但要确认这个“theirs”真的是你自己的提交版本。
stash场景的冲突标记可能随版本变化
本地有未提交的改动时,也可以先git stash,接着git pull拉取成功,再git stash pop把改动放回来。但pop如果出现冲突,stash中的内容通常被当作theirs,当前工作区的已拉取版本被当作ours。此时想保留本地改动,需要在冲突文件中选取theirs版本,例如git checkout --theirs ,然后git add。不过由于不同git版本对stash合并的标记可能有差异,保险做法是先用git diff查看冲突标记,确认哪一边是你要的,再手动合并,不要盲目执行命令。
如果本地改动非常重要,建议在stash之前先git stash branch <branchname>把stash保存到临时分支,这样即使pop失败也能找回。处理pop冲突时,也可以用git mergetool启动图形合并工具,人工确认保留哪一边。
保留本地版本还是保留远端版本,不是参数差异,而是要先确定你站在哪个合并模式里。每次操作前用git status和git diff确认状态,比记住所有参数更可靠。