Documentation / howto / revert-a-faulty-merge.txton commit Sync with 1.6.1.1 (c42b1ad)
   1Date: Fri, 19 Dec 2008 00:45:19 -0800
   2From: Linus Torvalds <torvalds@linux-foundation.org>, Junio C Hamano <gitster@pobox.com>
   3Subject: Re: Odd merge behaviour involving reverts
   4Abstract: Sometimes a branch that was already merged to the mainline
   5 is later found to be faulty.  Linus and Junio give guidance on
   6 recovering from such a premature merge and continuing development
   7 after the offending branch is fixed.
   8Message-ID: <7vocz8a6zk.fsf@gitster.siamese.dyndns.org>
   9References: <alpine.LFD.2.00.0812181949450.14014@localhost.localdomain>
  10
  11Alan <alan@clueserver.org> said:
  12
  13    I have a master branch.  We have a branch off of that that some
  14    developers are doing work on.  They claim it is ready. We merge it
  15    into the master branch.  It breaks something so we revert the merge.
  16    They make changes to the code.  they get it to a point where they say
  17    it is ok and we merge again.
  18
  19    When examined, we find that code changes made before the revert are
  20    not in the master branch, but code changes after are in the master
  21    branch.
  22
  23and asked for help recovering from this situation.
  24
  25The history immediately after the "revert of the merge" would look like
  26this:
  27
  28 ---o---o---o---M---x---x---W
  29               /
  30       ---A---B
  31
  32where A and B are on the side development that was not so good, M is the
  33merge that brings these premature changes into the mainline, x are changes
  34unrelated to what the side branch did and already made on the mainline,
  35and W is the "revert of the merge M" (doesn't W look M upside down?).
  36IOW, "diff W^..W" is similar to "diff -R M^..M".
  37
  38Such a "revert" of a merge can be made with:
  39
  40    $ git revert -m 1 M
  41
  42After the develpers of the side branch fixes their mistakes, the history
  43may look like this:
  44
  45 ---o---o---o---M---x---x---W---x
  46               /
  47       ---A---B-------------------C---D
  48
  49where C and D are to fix what was broken in A and B, and you may already
  50have some other changes on the mainline after W.
  51
  52If you merge the updated side branch (with D at its tip), none of the
  53changes made in A nor B will be in the result, because they were reverted
  54by W.  That is what Alan saw.
  55
  56Linus explains the situation:
  57
  58    Reverting a regular commit just effectively undoes what that commit
  59    did, and is fairly straightforward. But reverting a merge commit also
  60    undoes the _data_ that the commit changed, but it does absolutely
  61    nothing to the effects on _history_ that the merge had.
  62
  63    So the merge will still exist, and it will still be seen as joining
  64    the two branches together, and future merges will see that merge as
  65    the last shared state - and the revert that reverted the merge brought
  66    in will not affect that at all.
  67
  68    So a "revert" undoes the data changes, but it's very much _not_ an
  69    "undo" in the sense that it doesn't undo the effects of a commit on
  70    the repository history.
  71
  72    So if you think of "revert" as "undo", then you're going to always
  73    miss this part of reverts. Yes, it undoes the data, but no, it doesn't
  74    undo history.
  75
  76In such a situation, you would want to first revert the previous revert,
  77which would make the history look like this:
  78
  79 ---o---o---o---M---x---x---W---x---Y
  80               /
  81       ---A---B-------------------C---D
  82
  83where Y is the revert of W.  Such a "revert of the revert" can be done
  84with:
  85
  86    $ git revert W
  87
  88This history would (ignoring possible conflicts between what W and W..Y
  89changed) be equivalent to not having W nor Y at all in the history:
  90
  91 ---o---o---o---M---x---x-------x----
  92               /
  93       ---A---B-------------------C---D
  94
  95and merging the side branch again will not have conflict arising from an
  96earlier revert and revert of the revert.
  97
  98 ---o---o---o---M---x---x-------x-------*
  99               /                       /
 100       ---A---B-------------------C---D
 101
 102Of course the changes made in C and D still can conflict with what was
 103done by any of the x, but that is just a normal merge conflict.
 104
 105On the other hand, if the developers of the side branch discarded their
 106faulty A and B, and redone the changes on top of the updated mainline
 107after the revert, the history would have looked like this:
 108
 109 ---o---o---o---M---x---x---W---x---x
 110               /                 \
 111       ---A---B                   A'--B'--C'
 112
 113If you reverted the revert in such a case as in the previous example:
 114
 115 ---o---o---o---M---x---x---W---x---x---Y---*
 116               /                 \         /
 117       ---A---B                   A'--B'--C'
 118
 119where Y is the revert of W, A' and B'are rerolled A and B, and there may
 120also be a further fix-up C' on the side branch.  "diff Y^..Y" is similar
 121to "diff -R W^..W" (which in turn means it is similar to "diff M^..M"),
 122and "diff A'^..C'" by definition would be similar but different from that,
 123because it is a rerolled series of the earlier change.  There will be a
 124lot of overlapping changes that result in conflicts.  So do not do "revert
 125of revert" blindly without thinking..
 126
 127 ---o---o---o---M---x---x---W---x---x
 128               /                 \
 129       ---A---B                   A'--B'--C'
 130
 131In the history with rebased side branch, W (and M) are behind the merge
 132base of the updated branch and the tip of the mainline, and they should
 133merge without the past faulty merge and its revert getting in the way.
 134
 135To recap, these are two very different scenarios, and they want two very
 136different resolution strategies:
 137
 138 - If the faulty side branch was fixed by adding corrections on top, then
 139   doing a revert of the previous revert would be the right thing to do.
 140
 141 - If the faulty side branch whose effects were discarded by an earlier
 142   revert of a merge was rebuilt from scratch (i.e. rebasing and fixing,
 143   as you seem to have interpreted), then re-merging the result without
 144   doing anything else fancy would be the right thing to do.
 145
 146However, there are things to keep in mind when reverting a merge (and
 147reverting such a revert).
 148
 149For example, think about what reverting a merge (and then reverting the
 150revert) does to bisectability. Ignore the fact that the revert of a revert
 151is undoing it - just think of it as a "single commit that does a lot".
 152Because that is what it does.
 153
 154When you have a problem you are chasing down, and you hit a "revert this
 155merge", what you're hitting is essentially a single commit that contains
 156all the changes (but obviously in reverse) of all the commits that got
 157merged. So it's debugging hell, because now you don't have lots of small
 158changes that you can try to pinpoint which _part_ of it changes.
 159
 160But does it all work? Sure it does. You can revert a merge, and from a
 161purely technical angle, git did it very naturally and had no real
 162troubles. It just considered it a change from "state before merge" to
 163"state after merge", and that was it. Nothing complicated, nothing odd,
 164nothing really dangerous. Git will do it without even thinking about it.
 165
 166So from a technical angle, there's nothing wrong with reverting a merge,
 167but from a workflow angle it's something that you generally should try to
 168avoid.
 169
 170If at all possible, for example, if you find a problem that got merged
 171into the main tree, rather than revert the merge, try _really_ hard to
 172bisect the problem down into the branch you merged, and just fix it, or
 173try to revert the individual commit that caused it.
 174
 175Yes, it's more complex, and no, it's not always going to work (sometimes
 176the answer is: "oops, I really shouldn't have merged it, because it wasn't
 177ready yet, and I really need to undo _all_ of the merge"). So then you
 178really should revert the merge, but when you want to re-do the merge, you
 179now need to do it by reverting the revert.