blob: acf3e477e59f1bab9addde756274afe15d39a8ed [file] [log] [blame]
Nanako Shiraishia128a2c2008-12-20 20:27:02 +09001Date: 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>
Thomas Ackermann1797e5c2012-10-16 19:25:29 +020010Content-type: text/asciidoc
11
12How to revert a faulty merge
13============================
Nanako Shiraishia128a2c2008-12-20 20:27:02 +090014
15Alan <alan@clueserver.org> said:
16
17 I have a master branch. We have a branch off of that that some
18 developers are doing work on. They claim it is ready. We merge it
19 into the master branch. It breaks something so we revert the merge.
20 They make changes to the code. they get it to a point where they say
21 it is ok and we merge again.
22
23 When examined, we find that code changes made before the revert are
24 not in the master branch, but code changes after are in the master
25 branch.
26
27and asked for help recovering from this situation.
28
29The history immediately after the "revert of the merge" would look like
30this:
31
32 ---o---o---o---M---x---x---W
33 /
34 ---A---B
35
36where A and B are on the side development that was not so good, M is the
37merge that brings these premature changes into the mainline, x are changes
38unrelated to what the side branch did and already made on the mainline,
39and W is the "revert of the merge M" (doesn't W look M upside down?).
Ramsay Jones78bef062013-10-11 19:24:14 +010040IOW, `"diff W^..W"` is similar to `"diff -R M^..M"`.
Nanako Shiraishia128a2c2008-12-20 20:27:02 +090041
42Such a "revert" of a merge can be made with:
43
44 $ git revert -m 1 M
45
Mike Ralphsona1070d42009-03-03 19:29:22 +000046After the developers of the side branch fix their mistakes, the history
Nanako Shiraishia128a2c2008-12-20 20:27:02 +090047may look like this:
48
49 ---o---o---o---M---x---x---W---x
50 /
51 ---A---B-------------------C---D
52
53where C and D are to fix what was broken in A and B, and you may already
54have some other changes on the mainline after W.
55
56If you merge the updated side branch (with D at its tip), none of the
57changes made in A nor B will be in the result, because they were reverted
58by W. That is what Alan saw.
59
60Linus explains the situation:
61
62 Reverting a regular commit just effectively undoes what that commit
63 did, and is fairly straightforward. But reverting a merge commit also
64 undoes the _data_ that the commit changed, but it does absolutely
65 nothing to the effects on _history_ that the merge had.
66
67 So the merge will still exist, and it will still be seen as joining
68 the two branches together, and future merges will see that merge as
69 the last shared state - and the revert that reverted the merge brought
70 in will not affect that at all.
71
72 So a "revert" undoes the data changes, but it's very much _not_ an
73 "undo" in the sense that it doesn't undo the effects of a commit on
74 the repository history.
75
76 So if you think of "revert" as "undo", then you're going to always
77 miss this part of reverts. Yes, it undoes the data, but no, it doesn't
78 undo history.
79
80In such a situation, you would want to first revert the previous revert,
81which would make the history look like this:
82
83 ---o---o---o---M---x---x---W---x---Y
84 /
85 ---A---B-------------------C---D
86
87where Y is the revert of W. Such a "revert of the revert" can be done
88with:
89
90 $ git revert W
91
92This history would (ignoring possible conflicts between what W and W..Y
93changed) be equivalent to not having W nor Y at all in the history:
94
95 ---o---o---o---M---x---x-------x----
96 /
97 ---A---B-------------------C---D
98
99and merging the side branch again will not have conflict arising from an
100earlier revert and revert of the revert.
101
102 ---o---o---o---M---x---x-------x-------*
103 / /
104 ---A---B-------------------C---D
105
106Of course the changes made in C and D still can conflict with what was
107done by any of the x, but that is just a normal merge conflict.
108
109On the other hand, if the developers of the side branch discarded their
110faulty A and B, and redone the changes on top of the updated mainline
111after the revert, the history would have looked like this:
112
113 ---o---o---o---M---x---x---W---x---x
114 / \
115 ---A---B A'--B'--C'
116
117If you reverted the revert in such a case as in the previous example:
118
119 ---o---o---o---M---x---x---W---x---x---Y---*
120 / \ /
121 ---A---B A'--B'--C'
122
Mike Ralphsona1070d42009-03-03 19:29:22 +0000123where Y is the revert of W, A' and B' are rerolled A and B, and there may
Ramsay Jones78bef062013-10-11 19:24:14 +0100124also be a further fix-up C' on the side branch. `"diff Y^..Y"` is similar
125to `"diff -R W^..W"` (which in turn means it is similar to `"diff M^..M"`),
126and `"diff A'^..C'"` by definition would be similar but different from that,
Nanako Shiraishia128a2c2008-12-20 20:27:02 +0900127because it is a rerolled series of the earlier change. There will be a
128lot of overlapping changes that result in conflicts. So do not do "revert
129of revert" blindly without thinking..
130
131 ---o---o---o---M---x---x---W---x---x
132 / \
133 ---A---B A'--B'--C'
134
135In the history with rebased side branch, W (and M) are behind the merge
136base of the updated branch and the tip of the mainline, and they should
137merge without the past faulty merge and its revert getting in the way.
138
139To recap, these are two very different scenarios, and they want two very
140different resolution strategies:
141
142 - If the faulty side branch was fixed by adding corrections on top, then
143 doing a revert of the previous revert would be the right thing to do.
144
145 - If the faulty side branch whose effects were discarded by an earlier
146 revert of a merge was rebuilt from scratch (i.e. rebasing and fixing,
147 as you seem to have interpreted), then re-merging the result without
148 doing anything else fancy would be the right thing to do.
Marc Branchaudb4995492010-03-24 16:34:04 -0400149 (See the ADDENDUM below for how to rebuild a branch from scratch
150 without changing its original branching-off point.)
Nanako Shiraishia128a2c2008-12-20 20:27:02 +0900151
152However, there are things to keep in mind when reverting a merge (and
153reverting such a revert).
154
155For example, think about what reverting a merge (and then reverting the
156revert) does to bisectability. Ignore the fact that the revert of a revert
157is undoing it - just think of it as a "single commit that does a lot".
158Because that is what it does.
159
160When you have a problem you are chasing down, and you hit a "revert this
161merge", what you're hitting is essentially a single commit that contains
162all the changes (but obviously in reverse) of all the commits that got
163merged. So it's debugging hell, because now you don't have lots of small
164changes that you can try to pinpoint which _part_ of it changes.
165
166But does it all work? Sure it does. You can revert a merge, and from a
Thomas Ackermann2de9b712013-01-21 20:17:53 +0100167purely technical angle, Git did it very naturally and had no real
Nanako Shiraishia128a2c2008-12-20 20:27:02 +0900168troubles. It just considered it a change from "state before merge" to
169"state after merge", and that was it. Nothing complicated, nothing odd,
170nothing really dangerous. Git will do it without even thinking about it.
171
172So from a technical angle, there's nothing wrong with reverting a merge,
173but from a workflow angle it's something that you generally should try to
174avoid.
175
176If at all possible, for example, if you find a problem that got merged
177into the main tree, rather than revert the merge, try _really_ hard to
178bisect the problem down into the branch you merged, and just fix it, or
179try to revert the individual commit that caused it.
180
181Yes, it's more complex, and no, it's not always going to work (sometimes
182the answer is: "oops, I really shouldn't have merged it, because it wasn't
183ready yet, and I really need to undo _all_ of the merge"). So then you
184really should revert the merge, but when you want to re-do the merge, you
185now need to do it by reverting the revert.
Marc Branchaudb4995492010-03-24 16:34:04 -0400186
187ADDENDUM
188
189Sometimes you have to rewrite one of a topic branch's commits *and* you can't
190change the topic's branching-off point. Consider the following situation:
191
192 P---o---o---M---x---x---W---x
193 \ /
194 A---B---C
195
196where commit W reverted commit M because it turned out that commit B was wrong
197and needs to be rewritten, but you need the rewritten topic to still branch
198from commit P (perhaps P is a branching-off point for yet another branch, and
199you want be able to merge the topic into both branches).
200
201The natural thing to do in this case is to checkout the A-B-C branch and use
202"rebase -i P" to change commit B. However this does not rewrite commit A,
203because "rebase -i" by default fast-forwards over any initial commits selected
204with the "pick" command. So you end up with this:
205
206 P---o---o---M---x---x---W---x
207 \ /
208 A---B---C <-- old branch
209 \
210 B'---C' <-- naively rewritten branch
211
212To merge A-B'-C' into the mainline branch you would still have to first revert
213commit W in order to pick up the changes in A, but then it's likely that the
214changes in B' will conflict with the original B changes re-introduced by the
215reversion of W.
216
217However, you can avoid these problems if you recreate the entire branch,
218including commit A:
219
220 A'---B'---C' <-- completely rewritten branch
221 /
222 P---o---o---M---x---x---W---x
223 \ /
224 A---B---C
225
226You can merge A'-B'-C' into the mainline branch without worrying about first
227reverting W. Mainline's history would look like this:
228
229 A'---B'---C'------------------
230 / \
231 P---o---o---M---x---x---W---x---M2
232 \ /
233 A---B---C
234
235But if you don't actually need to change commit A, then you need some way to
Ralf Wildenhues22e5e582010-08-22 13:12:12 +0200236recreate it as a new commit with the same changes in it. The rebase command's
Marc Branchaudb4995492010-03-24 16:34:04 -0400237--no-ff option provides a way to do this:
238
239 $ git rebase [-i] --no-ff P
240
241The --no-ff option creates a new branch A'-B'-C' with all-new commits (all the
242SHA IDs will be different) even if in the interactive case you only actually
243modify commit B. You can then merge this new branch directly into the mainline
244branch and be sure you'll get all of the branch's changes.
245
246You can also use --no-ff in cases where you just add extra commits to the topic
247to fix it up. Let's revisit the situation discussed at the start of this howto:
248
249 P---o---o---M---x---x---W---x
250 \ /
251 A---B---C----------------D---E <-- fixed-up topic branch
252
253At this point, you can use --no-ff to recreate the topic branch:
254
255 $ git checkout E
256 $ git rebase --no-ff P
257
258yielding
259
260 A'---B'---C'------------D'---E' <-- recreated topic branch
261 /
262 P---o---o---M---x---x---W---x
263 \ /
264 A---B---C----------------D---E
265
266You can merge the recreated branch into the mainline without reverting commit W,
267and mainline's history will look like this:
268
269 A'---B'---C'------------D'---E'
270 / \
271 P---o---o---M---x---x---W---x---M2
272 \ /
273 A---B---C