我正在尝试还原一个合并操作,但是我不知道是否应该使用 git revert -m 1 <merge commit's sha>
或者 git revert -m 2 <merge commit's sha>
。我该如何找出哪个父提交是 -m 1
,哪个是 -m 2
?
我正在尝试还原一个合并操作,但是我不知道是否应该使用 git revert -m 1 <merge commit's sha>
或者 git revert -m 2 <merge commit's sha>
。我该如何找出哪个父提交是 -m 1
,哪个是 -m 2
?
嗯,超短的答案是始终使用-m 1
。 :-) 但这需要一些解释:
父提交有序,像git log
和git show
这样的命令会显示顺序:
所以这里commit c13c783c9d3d7d3eff937b7bf3642d2a7fe32644 Merge: 3f7ebc6ec 39ee4c6c2
3f7ebc6ec
是父级#1,39ee4c6c2
是父级#2。
后缀 ^
操作采用相同的值:
$ git rev-parse c13c783c9d3d7d3eff937b7bf3642d2a7fe32644^1 3f7ebc6ece46f1c23480d094688b8b5f24eb345c
(当然,
...^2
是另一个).绘制图表的程序(包括
git log --graph
)将向您展示它们之间的连接。但更重要的是,任何合并的第一个父提交都是在合并时处于当前状态的提交。
特别地,这意味着如果您在分支 main
上运行 git merge sidebranch
,则您现在(如果一切顺利)或最终(如果您必须手动解决合并)所进行的提交具有作为其第一个父提交的main
分支的上一个末尾。因此,它的第二个父提交是 sidebranch
的末尾。
那么,假设我们从这里开始:
...--o--*--A--B------C <-- main
\
D--E--F--G--H <-- sidebranch
当我们运行git merge
命令时,Git会找到共同的基础提交*
,然后在本质上执行以下操作来创建一个新的合并提交M:
git diff * C
(我们做了哪些更改?)git diff * H
(他们做了哪些更改?)然后将这两组更改合并,并将其应用于*
,从而得到最终结果:
...--o--*--A--B------C--M <-- main
\ /
D--E--F--G--H <-- sidebranch
现在,如果在A-B-C
中所有更改都与D-E-F-G-H
中的更改完全独立无关,那么Git撤消的具体方法并不太重要,只要它保留了A-B-C
的更改,同时放弃了D-E-F-G-H
的更改。
但是,如果B
与F
大部分相同,即B
和F
都修复了一个错误,那该怎么办呢?在这种情况下,我们不想撤销B
和F
的共享更改,因为Git采取了其中一份副本。这就是-m 1
的用处。
当git revert
去撤消某些更改时,它会运行自己的git diff
。它运行的git diff
将要还原的提交与其父提交进行比较。对于任何普通的非合并提交,这很容易:比较B
与A
,或者比较E
与D
等,看看发生了什么,并将其撤消。但是对于合并提交,不确定要与哪个父提交进行比较(虽然它有点明显)。这里的第一个父提交是C
,因此让我们看看如果运行以下命令会得到什么:
git diff C M
C
与M
之间的变化是我们从D-E-F-G-H
的更改中添加到A-B-C
的已有更改中,与*
相比,如果我们将它们进行比较。换句话说:
如果B
和F
完全重叠,C
-vs-M
中的更改是D-E-G-H
:除了重叠部分以外,我们撤消了其他所有更改。
如果F
中的更改多于B
,C
-vs-M
中的更改是D-E-(some-of-F)-G-H
:我们还原这些更改,但不包括B
中的那些。
如果F
中的更改少于B
,C
-vs-M
中只有D-E-G-H
,我们撤消这些更改。
由于第一个父级是C
,我们想要撤销D-E-F-G-H
的更改(不包括我们已经通过A-B-C
拥有的任何更改),因此在此还原中我们要用到-m 1
。