背景
在实际的开发工作中,使用git总会遇到一堆问题,本文将结合具体例子,讲述在何种条件下会出发git revert失败以及解决的方案和措施。
准备工作
首先,创建一个git 仓库(repo),本人是在Mac环境下,Windows下打开git 命令行,但代码是一样的:
1 | mkdir git-revert |
首先创建两个 commit 来模拟 master 上现有的 commit 记录:
1 | echo 'file1' > file1 |
现在我们需要开发一个新功能,所以需要基于master分支拉了一个新分支dev,我们创建并切换到dev分支:
1 | git branch dev |
接下来我们添加两个commit来完成dev分支:
1 | echo 'file3' > file3 |
在dev分支开发过程中,master 分支上通常会有其他人新的 commit提交,于是我们回到 master,来模拟一下这些 commit:
1 | git checkout master |
这个时候,dev分支测试通过了,需要合并到master分支上:
1 | git merge dev |
如图所示:
后来,master 上又多了一些 commit:
1 | echo 'file7' > file7 |
由于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 | -m parent-number |
翻译过来就是:
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, 反之亦然;