{"id":102488,"date":"2019-05-10T07:00:00","date_gmt":"2019-05-10T14:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102488"},"modified":"2020-07-02T07:36:26","modified_gmt":"2020-07-02T14:36:26","slug":"20190510-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190510-00\/?p=102488","title":{"rendered":"Mundane git commit-tree tricks, Part 5: Squashing without git rebase"},"content":{"rendered":"<p>Suppose you&#8217;ve made a bunch of changes.<\/p>\n<div id=\"p20190510_head\" style=\"display: none;\">\u00a0<\/div>\n<table class=\"cp3\" style=\"text-align: center;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>X WIP<\/td>\n<td>&nbsp;<\/td>\n<td>almost<\/td>\n<td>&nbsp;<\/td>\n<td>rename<br \/>\nmethod<\/td>\n<td>&nbsp;<\/td>\n<td>silly typo<\/td>\n<td>&nbsp;<\/td>\n<td>X works<\/td>\n<td>&nbsp;<\/td>\n<td>update Y<\/td>\n<td>&nbsp;<\/td>\n<td>update Z<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; width: 4em;\">base<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">A<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">B<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">C<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">D<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">E<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">F<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">G<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>You started by trying to get the <var>X<\/var> component working. I subscribe to the theory of <i><a href=\"https:\/\/en.wikipedia.org\/wiki\/Snowclone\">commit early and commit often<\/a><\/i>. I don&#8217;t wait until all of <var>X<\/var> is done before committing. I&#8217;ll commit every time I reach a point where I have built up enough work that I don&#8217;t want to lose it, especially if I might wind up breaking it in the next stage of work. Think of it as <i>save game<\/i> for source code.<\/p>\n<p>After four tries, you finally got component <var>X<\/var> working. Next step is to update components <var>Y<\/var> and <var>Z<\/var> to use the new component.<\/p>\n<p>Okay, you&#8217;re ready to create your pull request. Now, <a href=\"https:\/\/bitbucket.org\/blog\/the-pull-request-story\"> a pull request is a story<\/a>, so you need to decide how you want to tell the story of your work to others, so that they can review it. For this story, we want to say &#8220;First, I wrote this awesome bug-free <var>X<\/var> component. Then I updated the <var>Y<\/var> component to use the <var>X<\/var> component. Finally, I did the same with <var>Z<\/var>.&#8221; To tell this story, we want to do some internal squashing.<\/p>\n<table class=\"cp3\" style=\"text-align: center;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>X WIP<\/td>\n<td>&nbsp;<\/td>\n<td>almost<\/td>\n<td>&nbsp;<\/td>\n<td>rename<br \/>\nmethod<\/td>\n<td>&nbsp;<\/td>\n<td>silly typo<\/td>\n<td>&nbsp;<\/td>\n<td>X works<\/td>\n<td>&nbsp;<\/td>\n<td>update Y<\/td>\n<td>&nbsp;<\/td>\n<td>update Z<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; width: 4em;\">base<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">A<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">B<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">C<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">D<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">E<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">F<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">G<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u2196\ufe0e<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; width: 4em;\" colspan=\"9\">AE\u2032<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">F\u2032<\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px black; width: 4em;\">G\u2032<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The conventional way to do this is to check out the branch and perform an interactive rebase, squashing together commits <var>A<\/var> through <var>E<\/var> to form a new commit <var>AE\u2032<\/var>, and then picking commits <var>F<\/var> and <var>G<\/var>, producing <var>F\u2032<\/var> and <var>G\u2032<\/var>.<\/p>\n<p>However, the conventional way may not be the convenient way. You may have moved on and checked out a different branch to to some other work, and returning to this branch for some squashing action would churn your working directory, forcing unwanted rebuilds.<\/p>\n<p>Or you might still be on that branch, but rewinding back to <var>base<\/var> is going to churn so many files that it will invalidate all the build collateral you&#8217;ve created, forcing you to rebuild them pointlessly. For example, part of the work in adding the <var>X<\/var> component may have involved changing a centrally-used header file, which means that your entire project will have to rebuild.<\/p>\n<p>Since all of the commits we want to squash are consecutive, we can do all this squashing by simply committing trees.<\/p>\n<pre>git commit-tree E^{tree} -p base -m \"Write component X\"\r\n<\/pre>\n<p><b>Note<\/b>: As before, if using the Windows <code>CMD<\/code> command prompt, you need to double the <tt>^<\/tt> character because it is the <code>CMD<\/code> escape character.<\/p>\n<p>This command prints out a hash, which is our <var>AE\u2032<\/var>.<\/p>\n<p>Now we can stack <var>F<\/var> and <var>G<\/var> on top of it:<\/p>\n<pre>git commit-tree F^{tree} -p <var>AE\u2032<\/var> -m \"Update Y\"\r\n<\/pre>\n<p>This prints a hash, which is our <var>F\u2032<\/var>.<\/p>\n<pre>git commit-tree G^{tree} -p <var>F\u2032<\/var> -m \"Update Z\"\r\n<\/pre>\n<p>This prints a hash, which is our <var>G\u2032<\/var>.<\/p>\n<p>We can now reset the local branch to that commit, and then push it.<\/p>\n<p>If the branch you are &#8220;virtually rebasing&#8221; is the current branch, you can reset to it.<\/p>\n<pre>git reset --soft <var>G\u2032<\/var>\r\n<\/pre>\n<p>Since the trees for <var>G<\/var> and <var>G\u2032<\/var> are identical, this has no effect on your index. Any files that were staged remain staged, with exactly the same changes.<\/p>\n<p>If you are virtually rebasing a non-checked-out branch, then you can update it, and even push it, without checking it out:<\/p>\n<pre>git branch -f that-branch <var>G\u2032<\/var>\r\ngit push -f origin that-branch\r\n<\/pre>\n<p>Or we could bypass our local branch and push directly to the remote.<\/p>\n<pre>git push -f origin <var>G\u2032<\/var>:that-branch\r\n<\/pre>\n<p>The point is that we were able to rewrite a branch without touching any files in the working directory.<\/p>\n<p>\n<script>\nwindow.addEventListener(\"load\", function() {\n  var fullFF = getComputedStyle(document.body).fontFamily;\n  var simpleFF = fullFF.replace(\/ Emoji\/g, \"\");\n  \/\/ break up \"style\" to prevent wordpress from injecting random junk\n  document.getElementById(\"p20190510_head\").innerHTML =\n`<s` + `tyle>\nbody { font-family: ${simpleFF}; }\n.emoji { font-family: ${fullFF}; }\n<\/s` + `tyle>`;\n});\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Working completely from trees.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[26],"class_list":["post-102488","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>Working completely from trees.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102488","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=102488"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102488\/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=102488"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102488"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102488"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}