线上分支:master
你开发的分支:dev1
同时开发的分支:dev2
dev1分支开发的代码已经上线,并且已经merge到master
同时dev2分支也已上线,并且已经merge到master
这时发现dev1的巨大bug,线上版本要把这个分支的代码全部移除。
我们要撤销所有dev1的合并,并且保留dev2的代码。
同时本地dev1的分支不想删除这些代码,还有在这基础上开发。
如果使用reset,那么线上的几个提交记录都不会保留,达不到我们想要的效果。
这里使用git revert。
首先我们要撤销所有dev1的更改,找到dev1的两次commit id
git revert 63db9b1228c9e38a015513f834a42fa55002fca8
git revert a407174c5df3e47e1866663e4c3fe611419eb5a8
此时master已经达到我们想要的效果:
这时回到我们的dev1分支,修复bug,我还要保留以前提交的代码。
但是在上线前总要先merge master,但是master的两次revert是领先你的,一旦merge后你的代码就没有了。
下面是当前dev1的提交情况
所以我们要在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
被用来撤销一个已经提交的快照。但实现上和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
没有被清初。
所以相较于reset
,revert
不会改变项目历史,对那些已经发布到共享仓库的提交来说这是一个安全的操作。其次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)