{"id":104245,"date":"2020-09-21T07:00:00","date_gmt":"2020-09-21T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104245"},"modified":"2020-09-21T09:40:47","modified_gmt":"2020-09-21T16:40:47","slug":"20200921-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200921-00\/?p=104245","title":{"rendered":"Git commit-tree parlor tricks, Part 8: I just rebased my branch, but now I realize that I should have merged"},"content":{"rendered":"<p>Suppose you created a feature branch in git and made some changes. Meanwhile, the main branch has also made some changes:<\/p>\n<div id=\"p20200928_head\" style=\"display: none;\">\u00a0<\/div>\n<div id=\"p20200928_defs\" style=\"height: 0;\">\u00a0<\/div>\n<table style=\"text-align: center;\" title=\"Described in text.\" border=\"0\" cellspacing=\"1\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">A<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">M1<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">M2<\/td>\n<td>&nbsp;<\/td>\n<td>master<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td id=\"p20200928_nwarr\">\u2196\ufe0e<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">F1<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">F2<\/td>\n<td>&nbsp;<\/td>\n<td>feature<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>From a common ancestor commit A, we create a feature branch and make two commits, F1 and F2. Meanwhile, the master branch has received two commits M1 and M2.<\/p>\n<p>You decide to rebase your topic branch onto the main branch. Many merge conflicts later, you finish with this:<\/p>\n<table style=\"text-align: center;\" title=\"Described in text.\" border=\"0\" cellspacing=\"1\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">A<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">M1<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">M2<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>master<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td id=\"p20200928_nwarr\">\u2196\ufe0e<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">F1\u2032<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">F2\u2032<\/td>\n<td>&nbsp;<\/td>\n<td>feature<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The resulting graph is now linear, with the original commits A1, M1 and M2, followed by new commits F1\u2032 and F2\u2032.<\/p>\n<p>And then you realize that what you really meant to do was merge, not rebase. Is there a way to convert the rebase into a merge without having to go back and deal with all those merge conflicts again?<\/p>\n<p>Indeed there is.<\/p>\n<p>We&#8217;ve seen something very similar before, when we <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190509-00\/?p=102485\"> retroactively converted a squash to a merge<\/a>. This is pretty much the same thing: We have a final result, and we want to manufacture a merge that has the same final result.<\/p>\n<pre>git commit-tree HEAD^{tree} -p M2 -p F2 -m comment\r\n<\/pre>\n<p><b>Note<\/b>: If using the Windows <code>cmd<\/code> command prompt, you need to type<\/p>\n<pre>git commit-tree HEAD^^{tree} -p M2 -p F2 -m comment\r\n<\/pre>\n<p>for reasons <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20060517-00\/?p=31173\"> discussed earlier<\/a>.<\/p>\n<p>What we did was manufacture a new commit that contains the same results as F2\u2032, but assigned it the parents M2 and F2. The first parent is the branch you want to pretend that you are merging <i>to<\/i>, and the second parent is the branch you want to pretend that you are merging <i>from<\/i>.<\/p>\n<table style=\"text-align: center;\" title=\"Described in text.\" border=\"0\" cellspacing=\"1\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">A<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">M1<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">M2<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">F2\u2032<\/td>\n<td>&nbsp;<\/td>\n<td>master<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td id=\"p20200928_nwarr\">\u2196\ufe0e<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td id=\"p20200928_swarr\">\u2199\ufe0e<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">F1<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">F2<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>feature<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The output of the <code>git commit-tree<\/code> command is a commit hash. You can now reset to that commit, and all will be forgiven.<\/p>\n<pre>git reset --soft \u2329hash\u232a\r\n<\/pre>\n<p>Resetting in soft mode preserves all the changes you may have staged. Those staged changes are still valid because the starting point hasn&#8217;t changed: The commit you are resetting to has the same tree as the commit you are moving from.<\/p>\n<p><b>Bonus chatter<\/b>: If we had swapped the two parent commits, like this:<\/p>\n<pre>git commit-tree HEAD^{tree} -p F2 -p M2 -m comment\r\n<\/pre>\n<p>then the result would have been<\/p>\n<table style=\"text-align: center;\" title=\"Described in text.\" border=\"0\" cellspacing=\"1\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">A<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">M1<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">M2<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>master<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td id=\"p20200928_nwarr\">\u2196\ufe0e<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td id=\"p20200928_nwarr\">\u2199\ufe0e<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">F1<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">F2<\/td>\n<td id=\"p20200928_larr\">\u2190<\/td>\n<td style=\"border: solid 1px black; width: 50px; font-size: 120%;\">F2\u2032<\/td>\n<td>&nbsp;<\/td>\n<td>feature<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><script>\nwindow.addEventListener(\"load\", function() {\n  var fullFF = getComputedStyle(document.querySelector(\"body\")).fontFamily;\n  var simpleFF = fullFF.replace(\/ Emoji\/g, \"\");\n  \/\/ break up \"style\" to prevent wordpress from injecting random junk\n  document.getElementById(\"p20200928_head\").innerHTML =\n`<s` + `tyle>\nbody { font-family: ${simpleFF}; }\n.emoji { font-family: ${fullFF}; }\n<\/s` + `tyle>`;\n}); \/\/ wacky comment to prevent wordpress from injecting random junk\n(function() {\n  var svg = {\n    defs: `<svg width=\"0\" height=\"0\">\n <defs>\n  <marker id=\"arrowhead\" markerWidth=\"5\" markerHeight=\"5\" refX=\"-2\" refY=\"0\"\n    viewBox=\"-6 -6 12 12\" orient=\"auto\">\n    <polygon points=\"-2,0 -5,5 5,0 -5,-5\" fill=\"black\" stroke=\"black\"\n      stroke-dasharray=\"1 0\" \/>\n  <\/marker>\n  <path id=\"larr\" d=\"M20,10 L3,10\" stroke=\"black\"\n   marker-end=\"url(#arrowhead)\" \/>\n  <path id=\"nwarr\" d=\"M20,20 L3,3\" stroke=\"black\"\n   marker-end=\"url(#arrowhead)\" \/>\n  <path id=\"swarr\" d=\"M20,2 L3,17\" stroke=\"black\"\n   marker-end=\"url(#arrowhead)\" \/>\n  <path id=\"swarrd\" d=\"M20,0 L3,17\" stroke=\"black\" stroke-dasharray=\"4 2\"\n   marker-end=\"url(#arrowhead)\" \/>\n <\/defs>\n<\/svg>`,\n    larr: `<svg width=\"20\" height=\"20\"><use href=\"#larr\"\/><\/svg>`,\n    nwarr: `<svg width=\"20\" height=\"20\"><use href=\"#nwarr\"\/><\/svg>`,\n    swarr: `<svg width=\"20\" height=\"20\"><use href=\"#swarr\"\/><\/svg>`\n  };\n  Object.keys(svg).forEach(function (key) {\n    Array.prototype.forEach.call(document.querySelectorAll(\"#p20200928_\" + key),\n      function (e) {\n        e.innerHTML = svg[key];\n      });\n  });\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>You can rewrite the tree while preserving files.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[26],"class_list":["post-104245","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>You can rewrite the tree while preserving files.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104245","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=104245"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104245\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=104245"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104245"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104245"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}