为什么 revert 一个 merge commit 必须加 -m?-m 1 和 -m 2 的结果有什么不同?revert 之后再合并同一个分支为什么会丢提交?这篇造出一个合并,逐项验证。
$ git log --graph --format='%s' | sed -E 's/^/ /'
* M3 合并之后的提交
* 合并 feature(M)
|\
| * F2 功能的第二步
| * F1 功能的第一步
* | M2 主线上的提交
|/
* M1 基础
$ echo "M^1 = $(git log -1 --format=%s HEAD~1^1)"; echo "M^2 = $(git log -1 --format=%s HEAD~1^2)"
M^1 = M2 主线上的提交
M^2 = F2 功能的第二步
当前文件: base.txt f1.txt f2.txt m2.txt m3.txt合并提交 M 有两个 parent:M^1 是合并时所在的分支(这里是 main 的 M2),M^2 是被合并进来的分支的末端(feature 的 F2)。合并之后 main 上还可以继续有提交(M3)。
$ git revert --no-edit HEAD~1 2>&1 | head -3 error: commit 2d8d61a21d5ec41aad93d68dc4351875e8a4b429 is a merge but no -m option was given. fatal: revert failed
revert 的做法是“撤销这个提交相对它的 parent 的差异”。合并提交有两个 parent,相对不同 parent 的差异完全不同,所以必须用 -m <n> 指定主线 parent,否则报 is a merge but no -m option was given。
文件: base.txt m2.txt m3.txt (f1.txt、f2.txt 没了;m2.txt、m3.txt 还在) $ git log -1 --format=%B | head -3 Revert "合并 feature(M)" This reverts commit 2d8d61a21d5ec41aad93d68dc4351875e8a4b429, reversing 把现在的 HEAD 和 M 的 ^1(M2)比较,差异文件只有: m3.txt(M3 在 M 之后的改动保留了,f1、f2 的改动被撤销)
以第一个 parent(合并时所在的分支,通常是 main)为主线,撤销被合并进来的分支带来的内容。这是撤销一个已合并功能时最常用的写法;合并之后 main 上的其它提交保留。
文件: base.txt f1.txt f2.txt m3.txt (m2.txt 没了,f1/f2 还在)
以第二个 parent(被合并的分支)为主线,撤销的是 main 带来的内容,结果通常不是想要的。
$ git merge --no-edit feature 2>&1 | head -3
Merge made by the 'ort' strategy.
f3.txt | 1 +
1 file changed, 1 insertion(+)
文件: base.txt f3.txt m2.txt m3.txt ← 只有 f3.txt 回来了,f1.txt、f2.txt 没有回来!
$ git log --oneline --graph | sed -E 's/^([*|\\/ ]+) [0-9a-f]{7} /\1 /'
* Merge branch 'feature'
|\
| * F3 功能的第三步(revert 之后新增)
* | Revert "合并 feature(M)"
* | M3 合并之后的提交
* | 合并 feature(M)
|\|
| * F2 功能的第二步
| * F1 功能的第一步
* | M2 主线上的提交
|/
* M1 基础revert 只是新增一个反向修改的提交,不改变提交图里的祖先关系。被撤销的分支提交仍是 main 的祖先,所以之后再合并同一个分支时,git 认为它们已经合并过,只带来之后新增的提交。
revert 了 revert 之后: base.txt f1.txt f2.txt m2.txt m3.txt (f1、f2 回来了) $ git merge --no-edit feature 2>&1 | head -2 Merge made by the 'ort' strategy. f3.txt | 1 + 再合并 feature: base.txt f1.txt f2.txt f3.txt m2.txt m3.txt
先对那次 revert 提交再做一次 revert(把被撤销的改动恢复),之后的合并就会正常带来新增的提交。或者把 feature 分支 rebase / 重建,让它的提交成为新的提交。