背景
在实际的开发工作中,使用git总会遇到一堆问题,本文将结合具体例子,讲述在何种条件下会出发git revert失败以及解决的方案和措施。
准备工作
首先,创建一个git 仓库(repo),本人是在Mac环境下,Windows下打开git 命令行,但代码是一样的:1
2
3mkdir git-revert
cd git-revert
git init
首先创建两个 commit 来模拟 master 上现有的 commit 记录:1
2
3
4echo 'file1' > file1
git add . && git commit -m 'commit 1'
echo 'file2' > file2
git add . && git commit -m 'commit 2'
现在我们需要开发一个新功能,所以需要基于master分支拉了一个新分支dev,我们创建并切换到dev分支:1
2
3git branch dev
git checkout dev
# 或者使用 git checkout -b dev 合上面两步效果一样
接下来我们添加两个commit来完成dev分支:1
2
3
4echo 'file3' > file3
git add . && git commit -m 'dev - commit 1'
echo 'file4' > file4
git add . && git commit -m 'dev - commit 2'
在dev分支开发过程中,master 分支上通常会有其他人新的 commit提交,于是我们回到 master,来模拟一下这些 commit:1
2
3
4
5git checkout master
echo 'file5' > file5
git add . && git commit -m 'commit 3'
echo 'file6' > file6
git add . && git commit -m 'commit 4'
这个时候,dev分支测试通过了,需要合并到master分支上:
1 | git merge dev |
如图所示:
后来,master 上又多了一些 commit:1
2echo 'file7' > file7
git add . && git commit -m 'commit 5'
由于dev分支有问题,不想合并master分支,想revert这次merge commit,那就revert吧。
首先,先git log看一下:
找到merge的commit id来revert,711b06,1
git revert 711b06
问题出现了,revert失败:
分析问题
再次给出错误信息:1
error: commit 711b06365ec6ced517bf2597fa1b7562060e1181 is a merge but no -m option was given.
我们来看看 -m 到底指的是什么, 查看官方文档, 可以看到:1
2
3
4
5-m parent-number
--mainline parent-number
Usually you cannot revert a merge because you do not know which side of the merge should be considered the mainline. This option specifies the parent number (starting from 1) of the mainline and allows revert to reverse the change relative to the specified parent.
Reverting a merge commit declares that you will never want the tree changes brought in by the merge. As a result, later merges will only bring in tree changes introduced by commits that are not ancestors of the previously reverted merge. This may or may not be what you want.
翻译过来就是:
1 | 通常情况下,你无法 revert 一个 merge,因为你不知道 merge 的哪一条线应该被视为主线。这个选项(-m)指定了主线的 parent 的代号(从1开始),并允许以相对于指定的 parent 的进行 revert。 |
由于 merge commit 是将两条线合并到一条线上,因此,合并时的那个commit,将具有两个祖先。所以 git 不知道 base 是选择哪个 parent 进行 diff,所以你要用 -m 属性显示地告诉 git 用哪一个 parent。
那么,如何查看当前的commit有几个祖先呢?1
git show 711b06
Merge 这个字段便标明了当前的parent,分别是 0ffc72f 和 8f1dbff
当你在 B 分支上把 A merge 到 B 中,那么 B 就是merge commit 的 parent1,而 A 是 parent2,所以,master分支是parent1,dev分支是parent2。
解决
有了上一节的分析,我们可以很直接地给出以下可用的代码:1
git revert 711b06 -m 1
输出以下log:
1 | Revert "Merge branch 'dev'" |
:wq 退出看到:
file3 和 file4 是dev上的 commit 引入的文件,被正确地删掉了。
结论
- 对于单一 parent 的 commit,直接使用 git revert commit_id;
- 对于具有多个 parent 的 commit,需要结合 -m 属性:git revert commit_id -m parent_id;
- 对于从 branch 合并到 master 的 merge commit,master 的 parent_id 是1,branch 的 parent_id 是2, 反之亦然;