【git revert】报错commit is a merge but no -m option was given.

场景

线上分支:master

你开发的分支:dev1

同时开发的分支:dev2

dev1分支开发的代码已经上线,并且已经merge到master

同时dev2分支也已上线,并且已经merge到master

这时发现dev1的巨大bug,线上版本要把这个分支的代码全部移除。

【git revert】报错commit is a merge but no -m option was given._第1张图片

想要达到的效果

我们要撤销所有dev1的合并,并且保留dev2的代码。

同时本地dev1的分支不想删除这些代码,还有在这基础上开发。

master分支

如果使用reset,那么线上的几个提交记录都不会保留,达不到我们想要的效果。

这里使用git revert。

首先我们要撤销所有dev1的更改,找到dev1的两次commit id

git revert 63db9b1228c9e38a015513f834a42fa55002fca8
git revert a407174c5df3e47e1866663e4c3fe611419eb5a8

此时master已经达到我们想要的效果:

【git revert】报错commit is a merge but no -m option was given._第2张图片

开发分支

这时回到我们的dev1分支,修复bug,我还要保留以前提交的代码。

但是在上线前总要先merge master,但是master的两次revert是领先你的,一旦merge后你的代码就没有了。

下面是当前dev1的提交情况

【git revert】报错commit is a merge but no -m option was given._第3张图片

所以我们要在merge master后,再使用revert撤销这次merge。

但是这时你发现,在merge完master之后你又在这个分支提交了新代码,这时revert就会报错:

git revert ce479b597de6025da4a67ddd4a94d1b8034d8c67

error: commit ce479b597de6025da4a67ddd4a94d1b8034d8c67 is a merge but no -m option was given.
fatal: revert failed

这是因为撤销的是一次合并,git不知道要保存这两个分支中哪个的修改。

-m 1 表示保留当前分支的更改

-m 2 表示保留master更改

我们目的是为了保留dev1的代码,所以要保留当前代码,即使用 -m 1

git revert -m 1
ce479b597de6025da4a67ddd4a94d1b8034d8c67
[dev1 bb363fa] Revert "Merge branch 'master' into dev1"
 2 files changed, 0 insertions(+), 0 deletions(-)
 rename dev2 add => b (100%)
 create mode 100644 c

执行完上面的代码,我们就会发现,代码又回来了,和master没有回滚前的代码一样。

修完bug,再把当前代码合并到master,然后你就会发现,dev2提交的代码被你的merge干掉了???

这是因为你的那次rever合并采用了你的分支代码,但是你的dev1分支并没有dev2的代码…

所以我们应该在master回滚前,回到dev1分支,先merge一次最新代码,再执行后面的操作。

总结

总结起来流程很简单。

1.保持你要开发的分支同步了master最新代码。

2.revert所有该分支的提交。

3.回到你的分支merge master。

4.revert merge master的那次提交。

[Git如何回滚一次错误的合并]

revert 撤销

git revert被用来撤销一个已经提交的快照。但实现上和reset是完全不同的。通过搞清楚如何撤销这个提交引入的更改,然后在最后加上一个撤销了更改的 新 提交,而不是从项目历史中移除这个提交。

git revert 

生成一个撤消了 引入的修改的新提交,然后应用到当前分支。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vw4pslS3-1574908387703)(https://segmentfault.com/img/remote/1460000015792398)]

例如:

81f734d commit after bug
        |
3a395af bug
        |
3aa5dfb v3  (<- HEAD)
        |
5aab391 v2
        |
ff7b88e v1
        |
95d7816 init commit

我们在3a395af 引入了一个bug,我们明确是由于3a395af造成的bug的时候,以其我们通过新的提交来fix这个bug,不如git revert, 让他来帮你剔除这个bug。

git revert 3a395af

得到结果

cfb71fc Revert "bug"
        |
81f734d commit after bug
        |
3a395af bug
        |
3aa5dfb v3  (<- HEAD)
        |
5aab391 v2
        |
ff7b88e v1
        |
95d7816 init commit

这个时候bug的改动被撤销了,产生了一个新的commit,但是commit after bug没有被清初。

所以相较于resetrevert不会改变项目历史,对那些已经发布到共享仓库的提交来说这是一个安全的操作。其次git revert可以将提交历史中的任何一个提交撤销、而reset会把历史上某个提交及之后所有的提交都移除掉,这太野蛮了。

另外revert的设计,还有一个考量,那就是撤销一个公共仓库的提交。至于为什么不能用reset,你们可以自己思考一下。
下面我们就用一个麻烦事(回滚一个错误的合并),来讲解这个操作。

合并操作

相对于常规的commit,当使用git merge 合并两个分支的时候,你会得到一个新的merge commit.
当我们git show 的时候会出现类似信息:

commit 6dd0e2b9398ca8cd12bfd1faa1531d86dc41021a
Merge: d24d3b4 11a7112
Author: 前端杂货铺 
...............

Merge: d24d3b4 11a7112 这行表明了两个分支在合并时,所处的parent的版本线索。

比如在上述项目中我们开出了一个dev分支并做了一些操作,现在分支的样子变成了这样:

init -> v1 -> v2 -> v3  (master)
           \      
            d1 -> d2  (dev)

当我们在dev开发的差不多了

#git:(dev)
git checkout master 
#git:(master)
git merge dev

这个时候形成了一个Merge Commit faulty merge

init -> v1 -> v2 -> v3 -- faulty merge  (master)
           \            /
            d1  -->  d2  (dev)

此时faulty merge有两个parent 分别是v3 和 d2。

回滚错误的合并

这个merge之后还继续在dev开发,另一波人也在从别的分支往master合并代码。变成这样:

init -> v1 -> v2 -> v3 -- faulty merge -> v4 -> vc3 (master)
        \  \            /                     /
         \  d1  -->  d2  --> d3 --> d4  (dev)/
          \                                 / 
           c1  -->  c2 -------------------c3 (other)

这个时候你发现, 妈也上次那个merge 好像给共享分支master引入了一个bug。这个bug导致团队其他同学跑不通测试,或者这是一个线上的bug,如果不及时修复老板要骂街了。

这个时候第一想到的肯定是回滚代码,但怎么回滚呢。用reset?不现实,因为太流氓不说,还会把别人的代码也干掉,所以只能用revert。而revert它最初被设计出来就是干这个活的。

怎么操作呢?首先想到的是上面所说的 git revert ,但是貌似不太行。

git revert faulty merge
error: Commit faulty merge is a merge but no -m option was given.
fatal: revert failed

这是因为试图撤销两个分支的合并的时候Git不知道要保留哪一个分支上的修改。所以我们需要告诉git我们保留那个分支m或者mainline.

git revert -m 1 faulty merge

-m后面带的参数值 可以是1或者2,对应着parent的顺序.上面列子:1代表v3,2代表d2
所以该操作会保留master分支的修改,而撤销dev分支合并过来的修改。

提交历史变为

init -> v1 -> v2 -> v3 -- faulty merge -> v4 -> vc3 -> rev3 (master)
          \            /                     
           d1  -->  d2  --> d3 --> d4  (dev)

此处rev3是一个常规commit,其内容包含了之前在faulty merge撤销掉的dev合并过来的commit的【反操作】的合集。

到这个时候还没完,我们要记住,因为我们抛弃过之前dev合并过来的commit,下次dev再往master合并,之前抛弃过的其实是不包含在里面的。那怎么办呢?

恢复之前的回滚

很简单我们把之前master那个带有【反操作】的commit给撤销掉不就好了?

git checkout master
git revert rev3
git merge dev

此时提交历史变成了

init -> v1 -> v2 -> v3 -- faulty merge -> v4 -> vc3 -> rev3 -> rev3` -> final merge (master)
          \            /                                               /
           d1  -->  d2  --> d3 --> d4  --------------------------------(dev)

你可能感兴趣的:(Git)