bigywater.tech
常见报错 · 23

Git CONFLICT (content):rebase 里 --ours/--theirs 和 merge 相反,-X ours 悄悄丢改动,rerere 实测

“CONFLICT (content): Merge conflict in …” 是协作里最常见的 git 报错之一。这篇在本机把 merge、rebase、stash pop、cherry-pick 的冲突都复现一遍,看每一种的提示、冲突标记里谁是谁、状态码含义,以及 -X ours 和 rerere 这类“省事”选项到底做了什么。

相反
rebase 时 --ours 是你变基到的分支,merge 时 --ours 是你所在的分支(同样站在 feature 上测得)
UU / AA / UD
三种冲突的状态码:都改了同处 / 都新增同名文件 / 一边改一边删
悄悄丢掉
git merge -X ours 不报冲突,feature 对同一行的修改在结果里不见了

merge 冲突

实测 · 本机执行输出
    $ git merge feature    (退出码 1)
      Auto-merging f.txt
      CONFLICT (content): Merge conflict in f.txt
      Automatic merge failed; fix conflicts and then commit the result.
    $ git status --short; git status | sed -n 1,8p
      UU f.txt
      On branch main
      You have unmerged paths.
        (fix conflicts and run "git commit")
        (use "git merge --abort" to abort the merge)
      
      Unmerged paths:
        (use "git add <file>..." to mark resolution)
      	both modified:   f.txt
    $ cat f.txt
      title
      line A
      <<<<<<< HEAD
      line B (main)
      =======
      line B (feature)
      >>>>>>> feature
      line C
    $ printf "title\nline A\nline B (both)\nline C\n" > f.txt && git add f.txt && git commit -qm "merge feature" && git log --oneline --graph
      *   c732520 merge feature
      |\  
      | * 0fd0e30 feature: edit B
      * | 901bfb9 main: edit B
      |/  
      * a77f801 base
    $ git merge feature >/dev/null 2>&1; git merge --abort && git status | head -2 && cat f.txt
      On branch main
      nothing to commit, working tree clean
      title
      line A
      line B (main)
      line C

HEAD 一侧是当前分支,另一侧是被合并的分支;解决后 add 并 commit,或者 merge --abort 放弃。

rebase 冲突

实测 · 本机执行输出
    $ git switch -q feature && git rebase main    (退出码 1)
      Auto-merging f.txt
      CONFLICT (content): Merge conflict in f.txt
      Rebasing (1/1)
      error: could not apply 0fd0e30... feature: edit B
      hint: Resolve all conflicts manually, mark them as resolved with
      hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
      hint: You can instead skip this commit: run "git rebase --skip".
      hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
      hint: Disable this message with "git config set advice.mergeConflict false"
      Could not apply 0fd0e30... # feature: edit B
    $ cat f.txt
      title
      line A
      <<<<<<< HEAD
      line B (main)
      =======
      line B (feature)
      >>>>>>> 0fd0e30 (feature: edit B)
      line C
    $ git status | sed -n 1,9p
      interactive rebase in progress; onto 901bfb9
      Last command done (1 command done):
         pick 0fd0e30 # feature: edit B
      No commands remaining.
      You are currently rebasing branch 'feature' on '901bfb9'.
        (fix conflicts and then run "git rebase --continue")
        (use "git rebase --skip" to skip this patch)
        (use "git rebase --abort" to check out the original branch)
    $ git checkout --ours f.txt && cat f.txt && git checkout --theirs f.txt && cat f.txt
      title
      line A
      line B (main)
      line C
      title
      line A
      line B (feature)
      line C
      Updated 1 path from the index
      Updated 1 path from the index
    $ git checkout -m f.txt >/dev/null 2>&1; git rebase --abort && git status | head -1
      On branch feature
    $ git switch -q feature; git merge main >/dev/null 2>&1; echo "—— 同样站在 feature 上,git merge main 的 --ours / --theirs:"; git checkout --ours f.txt && grep "line B" f.txt && git checkout --theirs f.txt && grep "line B" f.txt
      —— 同样站在 feature 上,git merge main 的 --ours / --theirs:
      line B (feature)
      line B (main)
      Updated 1 path from the index
      Updated 1 path from the index
    $ git merge --abort; git rebase main >/dev/null 2>&1; echo "—— 同样站在 feature 上,git rebase main 的 --ours / --theirs:"; git checkout --ours f.txt && grep "line B" f.txt && git checkout --theirs f.txt && grep "line B" f.txt; git rebase --abort
      —— 同样站在 feature 上,git rebase main 的 --ours / --theirs:
      line B (main)
      line B (feature)
      Updated 1 path from the index
      Updated 1 path from the index

收尾命令是 rebase --continue / --skip / --abort;ours 与 theirs 的含义和 merge 相反。

stash pop 冲突

实测 · 本机执行输出
    $ echo "line B (local work)" > f.txt && sed -i.bak "s/line B (local work)/line A\nline B (local)/" f.txt; rm f.txt.bak; git stash -q; git switch -q feature; git stash list
      stash@{0}: WIP on main: 901bfb9 main: edit B
    $ git stash pop    (退出码 1)
      Auto-merging f.txt
      CONFLICT (content): Merge conflict in f.txt
      On branch feature
      Unmerged paths:
        (use "git restore --staged <file>..." to unstage)
        (use "git add <file>..." to mark resolution)
      	both modified:   f.txt
      
      no changes added to commit (use "git add" and/or "git commit -a")
      The stash entry is kept in case you need it again.
    $ git stash list; git status --short
      stash@{0}: WIP on main: 901bfb9 main: edit B
      UU f.txt
    $ git checkout -q HEAD -- f.txt && git stash drop && git stash list | wc -l | tr -d " "
      Dropped refs/stash@{0} (c3bfa5099c38111eb6d6cb64818590b16c2405e4)
      0

冲突时 stash 条目保留,解决后要自己 drop。

冲突的种类

实测 · 本机执行输出
  两边改同一行:
      Auto-merging f.txt
      CONFLICT (content): Merge conflict in f.txt
      Automatic merge failed; fix conflicts and then commit the result.
      UU f.txt
  两边都新增同名文件:
      Auto-merging new.txt
      CONFLICT (add/add): Merge conflict in new.txt
      Automatic merge failed; fix conflicts and then commit the result.
      AA new.txt
  一边改、一边删:
      CONFLICT (modify/delete): f.txt deleted in feature and modified in HEAD.  Version HEAD of f.txt left in tree.
      Automatic merge failed; fix conflicts and then commit the result.
      UD f.txt
  二进制文件两边都改:
      warning: Cannot merge binary files: bin.dat (HEAD vs. feature)
      Auto-merging bin.dat
      CONFLICT (content): Merge conflict in bin.dat
      Automatic merge failed; fix conflicts and then commit the result.
      UU bin.dat
  cherry-pick 同一行:
      Auto-merging f.txt
      CONFLICT (content): Merge conflict in f.txt
      error: could not apply 8397524... feat
      UU f.txt

content、add/add、modify/delete、二进制各有不同的提示和状态码。

省事的选项

实测 · 本机执行输出
    $ git -c merge.conflictStyle=zdiff3 merge feature >/dev/null 2>&1; cat f.txt; git merge --abort
      title
      line A
      <<<<<<< HEAD
      line B (main)
      ||||||| a77f801
      line B
      =======
      line B (feature)
      >>>>>>> feature
      line C
    $ git merge -X ours feature 2>&1 | head -3; grep "line B" f.txt; git log --oneline -3
      Auto-merging f.txt
      Merge made by the 'ort' strategy.
      line B (main)
      fd3ee28 Merge branch 'feature'
      901bfb9 main: edit B
      0fd0e30 feature: edit B
    $ git config rerere.enabled true; git merge feature 2>&1 | head -4; printf "title\nline A\nline B (both)\nline C\n" > f.txt && git add f.txt && git commit -qm "merge feature"
      Auto-merging f.txt
      CONFLICT (content): Merge conflict in f.txt
      Recorded preimage for 'f.txt'
      Automatic merge failed; fix conflicts and then commit the result.
      Recorded resolution for 'f.txt'.
    $ git reset -q --hard HEAD~1; git merge feature 2>&1 | head -4; cat f.txt; git status --short
      Auto-merging f.txt
      CONFLICT (content): Merge conflict in f.txt
      Resolved 'f.txt' using previous resolution.
      Automatic merge failed; fix conflicts and then commit the result.
      title
      line A
      line B (both)
      line C
      UU f.txt

zdiff3 让冲突块多一段共同祖先;-X ours 会丢另一边的改动;rerere 记住解决办法。

分步演示

合并冲突
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64;报错文本都是真实复现时原样捕获的(命令行工具统一用 LC_ALL=C 取英文原文,路径换成 );所有服务都是本机上自己起的、只监听 127.0.0.1,不访问外网;计时用 time.perf_counter,数字每次运行会有小幅抖动。本篇用 git 2.54.0(Apple Git-157);仓库都在临时目录里,配置隔离,提交时间固定
    • 没有验证:其他 git 版本里提示文字的差异;rename/rename、directory/file 等更少见的冲突;子模块冲突;merge 的 ort 以外的策略(recursive、octopus);.gitattributes 里 merge=union 等自定义合并驱动;图形化合并工具;rerere 的清理规则
    京ICP备2026067502号-1京公网安备11010502063487号