场景:主线是master分支,从某个分岔点切出topic分支,进行了commit A,commit B,然后将topic merge 到 master。大家继续在 master 上 commit,到某一个时间点,发现commit B有问题,这时候你会怎么做?
---o---o---o---M---x---x--- master
/
---A---B topic如果我们对merge进行revert,执行命令$ git revert -m 1 M,生成一个W commit:
---o---o---o---M---x---x---W
/
---A---Btopic分支上进行了对 B的修复,形成了commit C, commit D:
---o---o---o---M---x---x---W---x master
/
---A---B-------------------C---D topicwhere C and D are to fix what was broken in A and B, and you may already have some other changes on the mainline after W.
如果此时将topic merge 到 master,会有什么问题吗?
A,B 的 修改会丢失。
为什么会出现这种情况: revert只对 指定的 commit 进行了undo,但是并未对 历史 进行 undo。 即使对merge进行了revert,merge的 commit历史 仍然存在,未来再次merge时,会以上次merge作为基础。
在这种情况下我们要对 revert 进行 revert(Y 是 W 的revert):
---o---o---o---M---x---x---W---x---Y
/
---A---B-------------------C---D效果相当于:
---o---o---o---M---x---x-------x----
/
---A---B-------------------C---D可以将topic merge 到master
---o---o---o---M---x---x-------x-------*
/ /
---A---B-------------------C---D警惕一种情况,在revert merge后,topic分支被丢弃,新起一个topic’分支,添加了原A, B 的修改。
---o---o---o---M---x---x---W---x---x
/ \
---A---B A'--B'--C'如果在这种revert进行revert:
---o---o---o---M---x---x---W---x---x---Y---*
/ \ /
---A---B A'--B'--C'将topic’ merge 到 master 时,会出现大量代码重叠,冲突。要避免这种情况。
---o---o---o---M---x---x---W---x---x---x---*
/ \ /
---A---B A'--B'--C'这种情况下,直接合并即可,主线上相当于既没有 faulty merge,也没有其revert。
To recap, these are two very different scenarios, and they want two very different resolution strategies:
If the faulty side branch was fixed by adding corrections on top, then doing a revert of the previous revert would be the right thing to do.
If the faulty side branch whose effects were discarded by an earlier revert of a merge was rebuilt from scratch (i.e. rebasing and fixing, as you seem to have interpreted), then re-merging the result without doing anything else fancy would be the right thing to do. (See the ADDENDUM below for how to rebuild a branch from scratch without changing its original branching-off point.)
However, there are things to keep in mind when reverting a merge (and reverting such a revert).
考虑到revert,并不是单纯的做了undo,而是一次可能包含大量更改的提交。操作时要衡量利弊。一个revert包含大量更改时,不便于日志查看及调试。当然,技术上来说,git revert本身没有问题。
如果条件允许,尽可能还是在原分支,进行修复,再次merge,而不是对merge进行revert。或者只对merge前的某个commit进行revert。
发生revert后,还可以通过重建分支,方便日后正常merge:
P---o---o---M---x---x---W---x
\ /
A---B---C若通过执行git rebase -i选择 pick A commit,回到A状态,增补正确的代码,形成B’,C’:
P---o---o---M---x---x---W---x
\ /
A---B---C <-- old branch
\
B'---C' <-- naively rewritten branch这时会有一个两难的境地,如果合并 A-B’-C’到 主线,会丢失A的更改,如果对W进行revert,B‘跟B会冲突。
这是重建分支不够彻底导致的,如果我们把commit A进行改写,就可以正常合并了:
A'---B'---C' <-- completely rewritten branch
/
P---o---o---M---x---x---W---x
\ /
A---B---C合并后:
A'---B'---C'------------------
/ \
P---o---o---M---x---x---W---x---M2
\ /
A---B---C通过$ git rebase [-i] --no-ff P可以改写 commit SHA ID
You can also use –no-ff in cases where you just add extra commits to the topic to fix it up. Let’s revisit the situation discussed at the start of this howto:
P---o---o---M---x---x---W---x
\ /
A---B---C----------------D---E <-- fixed-up topic branchAt this point, you can use –no-ff to recreate the topic branch:
$ git checkout E
$ git rebase --no-ff Pyielding
A'---B'---C'------------D'---E' <-- recreated topic branch
/
P---o---o---M---x---x---W---x
\ /
A---B---C----------------D---EYou can merge the recreated branch into the mainline without reverting commit W, and mainline’s history will look like this:
A'---B'---C'------------D'---E'
/ \
P---o---o---M---x---x---W---x---M2
\ /
A---B---C