diff options
Diffstat (limited to 'third_party/git/Documentation/howto/revert-a-faulty-merge.txt')
-rw-r--r-- | third_party/git/Documentation/howto/revert-a-faulty-merge.txt | 273 |
1 files changed, 0 insertions, 273 deletions
diff --git a/third_party/git/Documentation/howto/revert-a-faulty-merge.txt b/third_party/git/Documentation/howto/revert-a-faulty-merge.txt deleted file mode 100644 index 19f59cc88808..000000000000 --- a/third_party/git/Documentation/howto/revert-a-faulty-merge.txt +++ /dev/null @@ -1,273 +0,0 @@ -Date: Fri, 19 Dec 2008 00:45:19 -0800 -From: Linus Torvalds <torvalds@linux-foundation.org>, Junio C Hamano <gitster@pobox.com> -Subject: Re: Odd merge behaviour involving reverts -Abstract: Sometimes a branch that was already merged to the mainline - is later found to be faulty. Linus and Junio give guidance on - recovering from such a premature merge and continuing development - after the offending branch is fixed. -Message-ID: <7vocz8a6zk.fsf@gitster.siamese.dyndns.org> -References: <alpine.LFD.2.00.0812181949450.14014@localhost.localdomain> -Content-type: text/asciidoc - -How to revert a faulty merge -============================ - -Alan <alan@clueserver.org> said: - - I have a master branch. We have a branch off of that that some - developers are doing work on. They claim it is ready. We merge it - into the master branch. It breaks something so we revert the merge. - They make changes to the code. they get it to a point where they say - it is ok and we merge again. - - When examined, we find that code changes made before the revert are - not in the master branch, but code changes after are in the master - branch. - -and asked for help recovering from this situation. - -The history immediately after the "revert of the merge" would look like -this: - - ---o---o---o---M---x---x---W - / - ---A---B - -where A and B are on the side development that was not so good, M is the -merge that brings these premature changes into the mainline, x are changes -unrelated to what the side branch did and already made on the mainline, -and W is the "revert of the merge M" (doesn't W look M upside down?). -IOW, `"diff W^..W"` is similar to `"diff -R M^..M"`. - -Such a "revert" of a merge can be made with: - - $ git revert -m 1 M - -After the developers of the side branch fix their mistakes, the history -may look like this: - - ---o---o---o---M---x---x---W---x - / - ---A---B-------------------C---D - -where 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. - -If you merge the updated side branch (with D at its tip), none of the -changes made in A or B will be in the result, because they were reverted -by W. That is what Alan saw. - -Linus explains the situation: - - Reverting a regular commit just effectively undoes what that commit - did, and is fairly straightforward. But reverting a merge commit also - undoes the _data_ that the commit changed, but it does absolutely - nothing to the effects on _history_ that the merge had. - - So the merge will still exist, and it will still be seen as joining - the two branches together, and future merges will see that merge as - the last shared state - and the revert that reverted the merge brought - in will not affect that at all. - - So a "revert" undoes the data changes, but it's very much _not_ an - "undo" in the sense that it doesn't undo the effects of a commit on - the repository history. - - So if you think of "revert" as "undo", then you're going to always - miss this part of reverts. Yes, it undoes the data, but no, it doesn't - undo history. - -In such a situation, you would want to first revert the previous revert, -which would make the history look like this: - - ---o---o---o---M---x---x---W---x---Y - / - ---A---B-------------------C---D - -where Y is the revert of W. Such a "revert of the revert" can be done -with: - - $ git revert W - -This history would (ignoring possible conflicts between what W and W..Y -changed) be equivalent to not having W or Y at all in the history: - - ---o---o---o---M---x---x-------x---- - / - ---A---B-------------------C---D - -and merging the side branch again will not have conflict arising from an -earlier revert and revert of the revert. - - ---o---o---o---M---x---x-------x-------* - / / - ---A---B-------------------C---D - -Of course the changes made in C and D still can conflict with what was -done by any of the x, but that is just a normal merge conflict. - -On the other hand, if the developers of the side branch discarded their -faulty A and B, and redone the changes on top of the updated mainline -after the revert, the history would have looked like this: - - ---o---o---o---M---x---x---W---x---x - / \ - ---A---B A'--B'--C' - -If you reverted the revert in such a case as in the previous example: - - ---o---o---o---M---x---x---W---x---x---Y---* - / \ / - ---A---B A'--B'--C' - -where Y is the revert of W, A' and B' are rerolled A and B, and there may -also be a further fix-up C' on the side branch. `"diff Y^..Y"` is similar -to `"diff -R W^..W"` (which in turn means it is similar to `"diff M^..M"`), -and `"diff A'^..C'"` by definition would be similar but different from that, -because it is a rerolled series of the earlier change. There will be a -lot of overlapping changes that result in conflicts. So do not do "revert -of revert" blindly without thinking.. - - ---o---o---o---M---x---x---W---x---x - / \ - ---A---B A'--B'--C' - -In the history with rebased side branch, W (and M) are behind the merge -base of the updated branch and the tip of the mainline, and they should -merge without the past faulty merge and its revert getting in the way. - -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). - -For example, think about what reverting a merge (and then reverting the -revert) does to bisectability. Ignore the fact that the revert of a revert -is undoing it - just think of it as a "single commit that does a lot". -Because that is what it does. - -When you have a problem you are chasing down, and you hit a "revert this -merge", what you're hitting is essentially a single commit that contains -all the changes (but obviously in reverse) of all the commits that got -merged. So it's debugging hell, because now you don't have lots of small -changes that you can try to pinpoint which _part_ of it changes. - -But does it all work? Sure it does. You can revert a merge, and from a -purely technical angle, Git did it very naturally and had no real -troubles. It just considered it a change from "state before merge" to -"state after merge", and that was it. Nothing complicated, nothing odd, -nothing really dangerous. Git will do it without even thinking about it. - -So from a technical angle, there's nothing wrong with reverting a merge, -but from a workflow angle it's something that you generally should try to -avoid. - -If at all possible, for example, if you find a problem that got merged -into the main tree, rather than revert the merge, try _really_ hard to -bisect the problem down into the branch you merged, and just fix it, or -try to revert the individual commit that caused it. - -Yes, it's more complex, and no, it's not always going to work (sometimes -the answer is: "oops, I really shouldn't have merged it, because it wasn't -ready yet, and I really need to undo _all_ of the merge"). So then you -really should revert the merge, but when you want to re-do the merge, you -now need to do it by reverting the revert. - -ADDENDUM - -Sometimes you have to rewrite one of a topic branch's commits *and* you can't -change the topic's branching-off point. Consider the following situation: - - P---o---o---M---x---x---W---x - \ / - A---B---C - -where commit W reverted commit M because it turned out that commit B was wrong -and needs to be rewritten, but you need the rewritten topic to still branch -from commit P (perhaps P is a branching-off point for yet another branch, and -you want be able to merge the topic into both branches). - -The natural thing to do in this case is to checkout the A-B-C branch and use -"rebase -i P" to change commit B. However this does not rewrite commit A, -because "rebase -i" by default fast-forwards over any initial commits selected -with the "pick" command. So you end up with this: - - P---o---o---M---x---x---W---x - \ / - A---B---C <-- old branch - \ - B'---C' <-- naively rewritten branch - -To merge A-B'-C' into the mainline branch you would still have to first revert -commit W in order to pick up the changes in A, but then it's likely that the -changes in B' will conflict with the original B changes re-introduced by the -reversion of W. - -However, you can avoid these problems if you recreate the entire branch, -including commit A: - - A'---B'---C' <-- completely rewritten branch - / - P---o---o---M---x---x---W---x - \ / - A---B---C - -You can merge A'-B'-C' into the mainline branch without worrying about first -reverting W. Mainline's history would look like this: - - A'---B'---C'------------------ - / \ - P---o---o---M---x---x---W---x---M2 - \ / - A---B---C - -But if you don't actually need to change commit A, then you need some way to -recreate it as a new commit with the same changes in it. The rebase command's ---no-ff option provides a way to do this: - - $ git rebase [-i] --no-ff P - -The --no-ff option creates a new branch A'-B'-C' with all-new commits (all the -SHA IDs will be different) even if in the interactive case you only actually -modify commit B. You can then merge this new branch directly into the mainline -branch and be sure you'll get all of the branch's changes. - -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 branch - -At this point, you can use --no-ff to recreate the topic branch: - - $ git checkout E - $ git rebase --no-ff P - -yielding - - A'---B'---C'------------D'---E' <-- recreated topic branch - / - P---o---o---M---x---x---W---x - \ / - A---B---C----------------D---E - -You 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 |