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 developers of the side branch fix 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 (See the ADDENDUM below for how to rebuild a branch from scratch 146 without changing its original branching-off point.) 147 148However, there are things to keep in mind when reverting a merge (and 149reverting such a revert). 150 151For example, think about what reverting a merge (and then reverting the 152revert) does to bisectability. Ignore the fact that the revert of a revert 153is undoing it - just think of it as a "single commit that does a lot". 154Because that is what it does. 155 156When you have a problem you are chasing down, and you hit a "revert this 157merge", what you're hitting is essentially a single commit that contains 158all the changes (but obviously in reverse) of all the commits that got 159merged. So it's debugging hell, because now you don't have lots of small 160changes that you can try to pinpoint which _part_ of it changes. 161 162But does it all work? Sure it does. You can revert a merge, and from a 163purely technical angle, git did it very naturally and had no real 164troubles. It just considered it a change from "state before merge" to 165"state after merge", and that was it. Nothing complicated, nothing odd, 166nothing really dangerous. Git will do it without even thinking about it. 167 168So from a technical angle, there's nothing wrong with reverting a merge, 169but from a workflow angle it's something that you generally should try to 170avoid. 171 172If at all possible, for example, if you find a problem that got merged 173into the main tree, rather than revert the merge, try _really_ hard to 174bisect the problem down into the branch you merged, and just fix it, or 175try to revert the individual commit that caused it. 176 177Yes, it's more complex, and no, it's not always going to work (sometimes 178the answer is: "oops, I really shouldn't have merged it, because it wasn't 179ready yet, and I really need to undo _all_ of the merge"). So then you 180really should revert the merge, but when you want to re-do the merge, you 181now need to do it by reverting the revert. 182 183ADDENDUM 184 185Sometimes you have to rewrite one of a topic branch's commits *and* you can't 186change the topic's branching-off point. Consider the following situation: 187 188 P---o---o---M---x---x---W---x 189 \ / 190 A---B---C 191 192where commit W reverted commit M because it turned out that commit B was wrong 193and needs to be rewritten, but you need the rewritten topic to still branch 194from commit P (perhaps P is a branching-off point for yet another branch, and 195you want be able to merge the topic into both branches). 196 197The natural thing to do in this case is to checkout the A-B-C branch and use 198"rebase -i P" to change commit B. However this does not rewrite commit A, 199because "rebase -i" by default fast-forwards over any initial commits selected 200with the "pick" command. So you end up with this: 201 202 P---o---o---M---x---x---W---x 203 \ / 204 A---B---C <-- old branch 205 \ 206 B'---C' <-- naively rewritten branch 207 208To merge A-B'-C' into the mainline branch you would still have to first revert 209commit W in order to pick up the changes in A, but then it's likely that the 210changes in B' will conflict with the original B changes re-introduced by the 211reversion of W. 212 213However, you can avoid these problems if you recreate the entire branch, 214including commit A: 215 216 A'---B'---C' <-- completely rewritten branch 217 / 218 P---o---o---M---x---x---W---x 219 \ / 220 A---B---C 221 222You can merge A'-B'-C' into the mainline branch without worrying about first 223reverting W. Mainline's history would look like this: 224 225 A'---B'---C'------------------ 226 / \ 227 P---o---o---M---x---x---W---x---M2 228 \ / 229 A---B---C 230 231But if you don't actually need to change commit A, then you need some way to 232recreate it as a new commit with the same changes in it. The rebase command's 233--no-ff option provides a way to do this: 234 235 $ git rebase [-i] --no-ff P 236 237The --no-ff option creates a new branch A'-B'-C' with all-new commits (all the 238SHA IDs will be different) even if in the interactive case you only actually 239modify commit B. You can then merge this new branch directly into the mainline 240branch and be sure you'll get all of the branch's changes. 241 242You can also use --no-ff in cases where you just add extra commits to the topic 243to fix it up. Let's revisit the situation discussed at the start of this howto: 244 245 P---o---o---M---x---x---W---x 246 \ / 247 A---B---C----------------D---E <-- fixed-up topic branch 248 249At this point, you can use --no-ff to recreate the topic branch: 250 251 $ git checkout E 252 $ git rebase --no-ff P 253 254yielding 255 256 A'---B'---C'------------D'---E' <-- recreated topic branch 257 / 258 P---o---o---M---x---x---W---x 259 \ / 260 A---B---C----------------D---E 261 262You can merge the recreated branch into the mainline without reverting commit W, 263and mainline's history will look like this: 264 265 A'---B'---C'------------D'---E' 266 / \ 267 P---o---o---M---x---x---W---x---M2 268 \ / 269 A---B---C