blob: 13846127a726a6b6e2b31a8692548a17e906be18 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
<meta name="generator" content="AsciiDoc 8.6.10" />
<title>How to revert a faulty merge</title>
<style type="text/css">
/* Shared CSS for AsciiDoc xhtml11 and html5 backends */
/* Default font. */
body {
font-family: Georgia,serif;
}
/* Title font. */
h1, h2, h3, h4, h5, h6,
div.title, caption.title,
thead, p.table.header,
#toctitle,
#author, #revnumber, #revdate, #revremark,
#footer {
font-family: Arial,Helvetica,sans-serif;
}
body {
margin: 1em 5% 1em 5%;
}
a {
color: blue;
text-decoration: underline;
}
a:visited {
color: fuchsia;
}
em {
font-style: italic;
color: navy;
}
strong {
font-weight: bold;
color: #083194;
}
h1, h2, h3, h4, h5, h6 {
color: #527bbd;
margin-top: 1.2em;
margin-bottom: 0.5em;
line-height: 1.3;
}
h1, h2, h3 {
border-bottom: 2px solid silver;
}
h2 {
padding-top: 0.5em;
}
h3 {
float: left;
}
h3 + * {
clear: left;
}
h5 {
font-size: 1.0em;
}
div.sectionbody {
margin-left: 0;
}
hr {
border: 1px solid silver;
}
p {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
ul, ol, li > p {
margin-top: 0;
}
ul > li { color: #aaa; }
ul > li > * { color: black; }
.monospaced, code, pre {
font-family: "Courier New", Courier, monospace;
font-size: inherit;
color: navy;
padding: 0;
margin: 0;
}
pre {
white-space: pre-wrap;
}
#author {
color: #527bbd;
font-weight: bold;
font-size: 1.1em;
}
#email {
}
#revnumber, #revdate, #revremark {
}
#footer {
font-size: small;
border-top: 2px solid silver;
padding-top: 0.5em;
margin-top: 4.0em;
}
#footer-text {
float: left;
padding-bottom: 0.5em;
}
#footer-badges {
float: right;
padding-bottom: 0.5em;
}
#preamble {
margin-top: 1.5em;
margin-bottom: 1.5em;
}
div.imageblock, div.exampleblock, div.verseblock,
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
div.admonitionblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
div.admonitionblock {
margin-top: 2.0em;
margin-bottom: 2.0em;
margin-right: 10%;
color: #606060;
}
div.content { /* Block element content. */
padding: 0;
}
/* Block element titles. */
div.title, caption.title {
color: #527bbd;
font-weight: bold;
text-align: left;
margin-top: 1.0em;
margin-bottom: 0.5em;
}
div.title + * {
margin-top: 0;
}
td div.title:first-child {
margin-top: 0.0em;
}
div.content div.title:first-child {
margin-top: 0.0em;
}
div.content + div.title {
margin-top: 0.0em;
}
div.sidebarblock > div.content {
background: #ffffee;
border: 1px solid #dddddd;
border-left: 4px solid #f0f0f0;
padding: 0.5em;
}
div.listingblock > div.content {
border: 1px solid #dddddd;
border-left: 5px solid #f0f0f0;
background: #f8f8f8;
padding: 0.5em;
}
div.quoteblock, div.verseblock {
padding-left: 1.0em;
margin-left: 1.0em;
margin-right: 10%;
border-left: 5px solid #f0f0f0;
color: #888;
}
div.quoteblock > div.attribution {
padding-top: 0.5em;
text-align: right;
}
div.verseblock > pre.content {
font-family: inherit;
font-size: inherit;
}
div.verseblock > div.attribution {
padding-top: 0.75em;
text-align: left;
}
/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
div.verseblock + div.attribution {
text-align: left;
}
div.admonitionblock .icon {
vertical-align: top;
font-size: 1.1em;
font-weight: bold;
text-decoration: underline;
color: #527bbd;
padding-right: 0.5em;
}
div.admonitionblock td.content {
padding-left: 0.5em;
border-left: 3px solid #dddddd;
}
div.exampleblock > div.content {
border-left: 3px solid #dddddd;
padding-left: 0.5em;
}
div.imageblock div.content { padding-left: 0; }
span.image img { border-style: none; vertical-align: text-bottom; }
a.image:visited { color: white; }
dl {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
dt {
margin-top: 0.5em;
margin-bottom: 0;
font-style: normal;
color: navy;
}
dd > *:first-child {
margin-top: 0.1em;
}
ul, ol {
list-style-position: outside;
}
ol.arabic {
list-style-type: decimal;
}
ol.loweralpha {
list-style-type: lower-alpha;
}
ol.upperalpha {
list-style-type: upper-alpha;
}
ol.lowerroman {
list-style-type: lower-roman;
}
ol.upperroman {
list-style-type: upper-roman;
}
div.compact ul, div.compact ol,
div.compact p, div.compact p,
div.compact div, div.compact div {
margin-top: 0.1em;
margin-bottom: 0.1em;
}
tfoot {
font-weight: bold;
}
td > div.verse {
white-space: pre;
}
div.hdlist {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
div.hdlist tr {
padding-bottom: 15px;
}
dt.hdlist1.strong, td.hdlist1.strong {
font-weight: bold;
}
td.hdlist1 {
vertical-align: top;
font-style: normal;
padding-right: 0.8em;
color: navy;
}
td.hdlist2 {
vertical-align: top;
}
div.hdlist.compact tr {
margin: 0;
padding-bottom: 0;
}
.comment {
background: yellow;
}
.footnote, .footnoteref {
font-size: 0.8em;
}
span.footnote, span.footnoteref {
vertical-align: super;
}
#footnotes {
margin: 20px 0 20px 0;
padding: 7px 0 0 0;
}
#footnotes div.footnote {
margin: 0 0 5px 0;
}
#footnotes hr {
border: none;
border-top: 1px solid silver;
height: 1px;
text-align: left;
margin-left: 0;
width: 20%;
min-width: 100px;
}
div.colist td {
padding-right: 0.5em;
padding-bottom: 0.3em;
vertical-align: top;
}
div.colist td img {
margin-top: 0.3em;
}
@media print {
#footer-badges { display: none; }
}
#toc {
margin-bottom: 2.5em;
}
#toctitle {
color: #527bbd;
font-size: 1.1em;
font-weight: bold;
margin-top: 1.0em;
margin-bottom: 0.1em;
}
div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
margin-top: 0;
margin-bottom: 0;
}
div.toclevel2 {
margin-left: 2em;
font-size: 0.9em;
}
div.toclevel3 {
margin-left: 4em;
font-size: 0.9em;
}
div.toclevel4 {
margin-left: 6em;
font-size: 0.9em;
}
span.aqua { color: aqua; }
span.black { color: black; }
span.blue { color: blue; }
span.fuchsia { color: fuchsia; }
span.gray { color: gray; }
span.green { color: green; }
span.lime { color: lime; }
span.maroon { color: maroon; }
span.navy { color: navy; }
span.olive { color: olive; }
span.purple { color: purple; }
span.red { color: red; }
span.silver { color: silver; }
span.teal { color: teal; }
span.white { color: white; }
span.yellow { color: yellow; }
span.aqua-background { background: aqua; }
span.black-background { background: black; }
span.blue-background { background: blue; }
span.fuchsia-background { background: fuchsia; }
span.gray-background { background: gray; }
span.green-background { background: green; }
span.lime-background { background: lime; }
span.maroon-background { background: maroon; }
span.navy-background { background: navy; }
span.olive-background { background: olive; }
span.purple-background { background: purple; }
span.red-background { background: red; }
span.silver-background { background: silver; }
span.teal-background { background: teal; }
span.white-background { background: white; }
span.yellow-background { background: yellow; }
span.big { font-size: 2em; }
span.small { font-size: 0.6em; }
span.underline { text-decoration: underline; }
span.overline { text-decoration: overline; }
span.line-through { text-decoration: line-through; }
div.unbreakable { page-break-inside: avoid; }
/*
* xhtml11 specific
*
* */
div.tableblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
div.tableblock > table {
border: 3px solid #527bbd;
}
thead, p.table.header {
font-weight: bold;
color: #527bbd;
}
p.table {
margin-top: 0;
}
/* Because the table frame attribute is overriden by CSS in most browsers. */
div.tableblock > table[frame="void"] {
border-style: none;
}
div.tableblock > table[frame="hsides"] {
border-left-style: none;
border-right-style: none;
}
div.tableblock > table[frame="vsides"] {
border-top-style: none;
border-bottom-style: none;
}
/*
* html5 specific
*
* */
table.tableblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
thead, p.tableblock.header {
font-weight: bold;
color: #527bbd;
}
p.tableblock {
margin-top: 0;
}
table.tableblock {
border-width: 3px;
border-spacing: 0px;
border-style: solid;
border-color: #527bbd;
border-collapse: collapse;
}
th.tableblock, td.tableblock {
border-width: 1px;
padding: 4px;
border-style: solid;
border-color: #527bbd;
}
table.tableblock.frame-topbot {
border-left-style: hidden;
border-right-style: hidden;
}
table.tableblock.frame-sides {
border-top-style: hidden;
border-bottom-style: hidden;
}
table.tableblock.frame-none {
border-style: hidden;
}
th.tableblock.halign-left, td.tableblock.halign-left {
text-align: left;
}
th.tableblock.halign-center, td.tableblock.halign-center {
text-align: center;
}
th.tableblock.halign-right, td.tableblock.halign-right {
text-align: right;
}
th.tableblock.valign-top, td.tableblock.valign-top {
vertical-align: top;
}
th.tableblock.valign-middle, td.tableblock.valign-middle {
vertical-align: middle;
}
th.tableblock.valign-bottom, td.tableblock.valign-bottom {
vertical-align: bottom;
}
/*
* manpage specific
*
* */
body.manpage h1 {
padding-top: 0.5em;
padding-bottom: 0.5em;
border-top: 2px solid silver;
border-bottom: 2px solid silver;
}
body.manpage h2 {
border-style: none;
}
body.manpage div.sectionbody {
margin-left: 3em;
}
@media print {
body.manpage div#toc { display: none; }
}
</style>
<script type="text/javascript">
/*<![CDATA[*/
var asciidoc = { // Namespace.
/////////////////////////////////////////////////////////////////////
// Table Of Contents generator
/////////////////////////////////////////////////////////////////////
/* Author: Mihai Bazon, September 2002
* http://students.infoiasi.ro/~mishoo
*
* Table Of Content generator
* Version: 0.4
*
* Feel free to use this script under the terms of the GNU General Public
* License, as long as you do not remove or alter this notice.
*/
/* modified by Troy D. Hanson, September 2006. License: GPL */
/* modified by Stuart Rackham, 2006, 2009. License: GPL */
// toclevels = 1..4.
toc: function (toclevels) {
function getText(el) {
var text = "";
for (var i = el.firstChild; i != null; i = i.nextSibling) {
if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
text += i.data;
else if (i.firstChild != null)
text += getText(i);
}
return text;
}
function TocEntry(el, text, toclevel) {
this.element = el;
this.text = text;
this.toclevel = toclevel;
}
function tocEntries(el, toclevels) {
var result = new Array;
var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');
// Function that scans the DOM tree for header elements (the DOM2
// nodeIterator API would be a better technique but not supported by all
// browsers).
var iterate = function (el) {
for (var i = el.firstChild; i != null; i = i.nextSibling) {
if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
var mo = re.exec(i.tagName);
if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
}
iterate(i);
}
}
}
iterate(el);
return result;
}
var toc = document.getElementById("toc");
if (!toc) {
return;
}
// Delete existing TOC entries in case we're reloading the TOC.
var tocEntriesToRemove = [];
var i;
for (i = 0; i < toc.childNodes.length; i++) {
var entry = toc.childNodes[i];
if (entry.nodeName.toLowerCase() == 'div'
&& entry.getAttribute("class")
&& entry.getAttribute("class").match(/^toclevel/))
tocEntriesToRemove.push(entry);
}
for (i = 0; i < tocEntriesToRemove.length; i++) {
toc.removeChild(tocEntriesToRemove[i]);
}
// Rebuild TOC entries.
var entries = tocEntries(document.getElementById("content"), toclevels);
for (var i = 0; i < entries.length; ++i) {
var entry = entries[i];
if (entry.element.id == "")
entry.element.id = "_toc_" + i;
var a = document.createElement("a");
a.href = "#" + entry.element.id;
a.appendChild(document.createTextNode(entry.text));
var div = document.createElement("div");
div.appendChild(a);
div.className = "toclevel" + entry.toclevel;
toc.appendChild(div);
}
if (entries.length == 0)
toc.parentNode.removeChild(toc);
},
/////////////////////////////////////////////////////////////////////
// Footnotes generator
/////////////////////////////////////////////////////////////////////
/* Based on footnote generation code from:
* http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
*/
footnotes: function () {
// Delete existing footnote entries in case we're reloading the footnodes.
var i;
var noteholder = document.getElementById("footnotes");
if (!noteholder) {
return;
}
var entriesToRemove = [];
for (i = 0; i < noteholder.childNodes.length; i++) {
var entry = noteholder.childNodes[i];
if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
entriesToRemove.push(entry);
}
for (i = 0; i < entriesToRemove.length; i++) {
noteholder.removeChild(entriesToRemove[i]);
}
// Rebuild footnote entries.
var cont = document.getElementById("content");
var spans = cont.getElementsByTagName("span");
var refs = {};
var n = 0;
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnote") {
n++;
var note = spans[i].getAttribute("data-note");
if (!note) {
// Use [\s\S] in place of . so multi-line matches work.
// Because JavaScript has no s (dotall) regex flag.
note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
spans[i].innerHTML =
"[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
spans[i].setAttribute("data-note", note);
}
noteholder.innerHTML +=
"<div class='footnote' id='_footnote_" + n + "'>" +
"<a href='#_footnoteref_" + n + "' title='Return to text'>" +
n + "</a>. " + note + "</div>";
var id =spans[i].getAttribute("id");
if (id != null) refs["#"+id] = n;
}
}
if (n == 0)
noteholder.parentNode.removeChild(noteholder);
else {
// Process footnoterefs.
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnoteref") {
var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
href = href.match(/#.*/)[0]; // Because IE return full URL.
n = refs[href];
spans[i].innerHTML =
"[<a href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
}
}
}
},
install: function(toclevels) {
var timerId;
function reinstall() {
asciidoc.footnotes();
if (toclevels) {
asciidoc.toc(toclevels);
}
}
function reinstallAndRemoveTimer() {
clearInterval(timerId);
reinstall();
}
timerId = setInterval(reinstall, 500);
if (document.addEventListener)
document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
else
window.onload = reinstallAndRemoveTimer;
}
}
asciidoc.install();
/*]]>*/
</script>
</head>
<body class="article">
<div id="header">
<h1>How to revert a faulty merge</h1>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph"><p>Alan &lt;<a href="mailto:alan@clueserver.org">alan@clueserver.org</a>&gt; said:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>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.</code></pre>
</div></div>
<div class="literalblock">
<div class="content">
<pre><code>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.</code></pre>
</div></div>
<div class="paragraph"><p>and asked for help recovering from this situation.</p></div>
<div class="paragraph"><p>The history immediately after the "revert of the merge" would look like
this:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>---o---o---o---M---x---x---W
/
---A---B</code></pre>
</div></div>
<div class="paragraph"><p>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&#8217;t W look M upside down?).
IOW, <code>"diff W^..W"</code> is similar to <code>"diff -R M^..M"</code>.</p></div>
<div class="paragraph"><p>Such a "revert" of a merge can be made with:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>$ git revert -m 1 M</code></pre>
</div></div>
<div class="paragraph"><p>After the developers of the side branch fix their mistakes, the history
may look like this:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>---o---o---o---M---x---x---W---x
/
---A---B-------------------C---D</code></pre>
</div></div>
<div class="paragraph"><p>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.</p></div>
<div class="paragraph"><p>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.</p></div>
<div class="paragraph"><p>Linus explains the situation:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>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.</code></pre>
</div></div>
<div class="literalblock">
<div class="content">
<pre><code>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.</code></pre>
</div></div>
<div class="literalblock">
<div class="content">
<pre><code>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.</code></pre>
</div></div>
<div class="literalblock">
<div class="content">
<pre><code>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.</code></pre>
</div></div>
<div class="paragraph"><p>In such a situation, you would want to first revert the previous revert,
which would make the history look like this:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>---o---o---o---M---x---x---W---x---Y
/
---A---B-------------------C---D</code></pre>
</div></div>
<div class="paragraph"><p>where Y is the revert of W. Such a "revert of the revert" can be done
with:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>$ git revert W</code></pre>
</div></div>
<div class="paragraph"><p>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:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>---o---o---o---M---x---x-------x----
/
---A---B-------------------C---D</code></pre>
</div></div>
<div class="paragraph"><p>and merging the side branch again will not have conflict arising from an
earlier revert and revert of the revert.</p></div>
<div class="literalblock">
<div class="content">
<pre><code>---o---o---o---M---x---x-------x-------*
/ /
---A---B-------------------C---D</code></pre>
</div></div>
<div class="paragraph"><p>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.</p></div>
<div class="paragraph"><p>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:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>---o---o---o---M---x---x---W---x---x
/ \
---A---B A'--B'--C'</code></pre>
</div></div>
<div class="paragraph"><p>If you reverted the revert in such a case as in the previous example:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>---o---o---o---M---x---x---W---x---x---Y---*
/ \ /
---A---B A'--B'--C'</code></pre>
</div></div>
<div class="paragraph"><p>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. <code>"diff Y^..Y"</code> is similar
to <code>"diff -R W^..W"</code> (which in turn means it is similar to <code>"diff M^..M"</code>),
and <code>"diff A'^..C'"</code> 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..</p></div>
<div class="literalblock">
<div class="content">
<pre><code>---o---o---o---M---x---x---W---x---x
/ \
---A---B A'--B'--C'</code></pre>
</div></div>
<div class="paragraph"><p>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.</p></div>
<div class="paragraph"><p>To recap, these are two very different scenarios, and they want two very
different resolution strategies:</p></div>
<div class="ulist"><ul>
<li>
<p>
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.
</p>
</li>
<li>
<p>
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.)
</p>
</li>
</ul></div>
<div class="paragraph"><p>However, there are things to keep in mind when reverting a merge (and
reverting such a revert).</p></div>
<div class="paragraph"><p>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.</p></div>
<div class="paragraph"><p>When you have a problem you are chasing down, and you hit a "revert this
merge", what you&#8217;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&#8217;s debugging hell, because now you don&#8217;t have lots of small
changes that you can try to pinpoint which <em>part</em> of it changes.</p></div>
<div class="paragraph"><p>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.</p></div>
<div class="paragraph"><p>So from a technical angle, there&#8217;s nothing wrong with reverting a merge,
but from a workflow angle it&#8217;s something that you generally should try to
avoid.</p></div>
<div class="paragraph"><p>If at all possible, for example, if you find a problem that got merged
into the main tree, rather than revert the merge, try <em>really</em> 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.</p></div>
<div class="paragraph"><p>Yes, it&#8217;s more complex, and no, it&#8217;s not always going to work (sometimes
the answer is: "oops, I really shouldn&#8217;t have merged it, because it wasn&#8217;t
ready yet, and I really need to undo <em>all</em> 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.</p></div>
<div class="paragraph"><p>ADDENDUM</p></div>
<div class="paragraph"><p>Sometimes you have to rewrite one of a topic branch&#8217;s commits <strong>and</strong> you can&#8217;t
change the topic&#8217;s branching-off point. Consider the following situation:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>P---o---o---M---x---x---W---x
\ /
A---B---C</code></pre>
</div></div>
<div class="paragraph"><p>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).</p></div>
<div class="paragraph"><p>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></div>
<div class="literalblock">
<div class="content">
<pre><code>P---o---o---M---x---x---W---x
\ /
A---B---C &lt;-- old branch
\
B'---C' &lt;-- naively rewritten branch</code></pre>
</div></div>
<div class="paragraph"><p>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&#8217;s likely that the
changes in B' will conflict with the original B changes re-introduced by the
reversion of W.</p></div>
<div class="paragraph"><p>However, you can avoid these problems if you recreate the entire branch,
including commit A:</p></div>
<div class="literalblock">
<div class="content">
<pre><code> A'---B'---C' &lt;-- completely rewritten branch
/
P---o---o---M---x---x---W---x
\ /
A---B---C</code></pre>
</div></div>
<div class="paragraph"><p>You can merge A'-B'-C' into the mainline branch without worrying about first
reverting W. Mainline&#8217;s history would look like this:</p></div>
<div class="literalblock">
<div class="content">
<pre><code> A'---B'---C'------------------
/ \
P---o---o---M---x---x---W---x---M2
\ /
A---B---C</code></pre>
</div></div>
<div class="paragraph"><p>But if you don&#8217;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&#8217;s
--no-ff option provides a way to do this:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>$ git rebase [-i] --no-ff P</code></pre>
</div></div>
<div class="paragraph"><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&#8217;ll get all of the branch&#8217;s changes.</p></div>
<div class="paragraph"><p>You can also use --no-ff in cases where you just add extra commits to the topic
to fix it up. Let&#8217;s revisit the situation discussed at the start of this howto:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>P---o---o---M---x---x---W---x
\ /
A---B---C----------------D---E &lt;-- fixed-up topic branch</code></pre>
</div></div>
<div class="paragraph"><p>At this point, you can use --no-ff to recreate the topic branch:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>$ git checkout E
$ git rebase --no-ff P</code></pre>
</div></div>
<div class="paragraph"><p>yielding</p></div>
<div class="literalblock">
<div class="content">
<pre><code> A'---B'---C'------------D'---E' &lt;-- recreated topic branch
/
P---o---o---M---x---x---W---x
\ /
A---B---C----------------D---E</code></pre>
</div></div>
<div class="paragraph"><p>You can merge the recreated branch into the mainline without reverting commit W,
and mainline&#8217;s history will look like this:</p></div>
<div class="literalblock">
<div class="content">
<pre><code> A'---B'---C'------------D'---E'
/ \
P---o---o---M---x---x---W---x---M2
\ /
A---B---C</code></pre>
</div></div>
</div>
</div>
</div>
<div id="footnotes"><hr /></div>
<div id="footer">
<div id="footer-text">
Last updated
2019-02-24 08:32:59 PST
</div>
</div>
</body>
</html>