{"id":102894,"date":"2019-09-17T07:00:00","date_gmt":"2019-09-17T14:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102894"},"modified":"2019-09-16T22:52:37","modified_gmt":"2019-09-17T05:52:37","slug":"20190917-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190917-00\/?p=102894","title":{"rendered":"How to split out pieces of a file while preserving git line history: The hard way with commit-tree"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190916-00\/?p=102892\"> Last time<\/a>, we looked at how to split a single file into multiple files while preserving line history. A related scenario is where you want to extract some pieces of a file into separate files, but leave some pieces behind.<\/p>\n<p>Let&#8217;s use the same scratch repo we had last time. You can follow the same copy\/paste script, or you can take your existing scratch repo and <code>git reset --hard ready<\/code> to get it back into its &#8220;ready to start experimenting&#8221; state.<\/p>\n<p>First, we&#8217;re going to do things the hard (but more information-theoretically correct) way, and then we&#8217;ll develop a simpler alternative that gets the same result, though through some potentially-confusing intermediate steps.<\/p>\n<p>Okay, to do things the hard way, we split out each file in its own branch.<\/p>\n<pre style=\"white-space: pre-wrap;\">git checkout -b f2f\r\n\r\ngit mv foods fruits\r\ngit commit --author=\"Greg &lt;greg&gt;\" -m \"create fruits from foods\"\r\n<\/pre>\n<p>We start by renaming <code>foods<\/code> to <code>fruits<\/code>. This ensures that when git traces the history of the <code>fruits<\/code> file, it will follow the history back into the <code>foods<\/code> file.<\/p>\n<p>Next, we split the <code>fruits<\/code> file back into two files: The fruits stay in the <code>fruits<\/code> file, and the rest go back into the <code>foods<\/code> file.<\/p>\n<pre style=\"white-space: pre-wrap;\">&gt;foods echo celery\r\n&gt;&gt;foods echo cheese\r\n&gt;&gt;foods echo eggs\r\n&gt;&gt;foods echo lettuce\r\n&gt;&gt;foods echo milk\r\n&gt;&gt;foods echo peas\r\ngit add foods\r\n\r\n&gt;fruits echo apple\r\n&gt;&gt;fruits echo grape\r\n&gt;&gt;fruits echo orange\r\n\r\ngit commit --author=\"Greg &lt;greg&gt;\" -am \"split fruits from foods\"\r\n\r\ngit checkout -\r\n<\/pre>\n<p>Repeat for the other files you want to split out. Let&#8217;s say we also want to split out the veggies.<\/p>\n<pre style=\"white-space: pre-wrap;\">git checkout -b f2v\r\n\r\ngit mv foods veggies\r\ngit commit --author=\"Greg &lt;greg&gt;\" -m \"create veggies from foods\"\r\n\r\n&gt;foods echo apple\r\n&gt;&gt;foods echo cheese\r\n&gt;&gt;foods echo eggs\r\n&gt;&gt;foods echo grape\r\n&gt;&gt;foods echo milk\r\n&gt;&gt;foods echo orange\r\ngit add foods\r\n\r\n&gt;veggies echo celery\r\n&gt;&gt;veggies echo lettuce\r\n&gt;&gt;veggies echo peas\r\n\r\ngit commit --author=\"Greg &lt;greg&gt;\" -am \"split veggies from foods\"\r\n\r\ngit checkout -\r\n<\/pre>\n<p>Then we octopus the branches together. However, the octopus will fail because the changes don&#8217;t merge cleanly, so we&#8217;ll have to do a manual octopus, <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190515-00\/?p=102495\"> like we did before<\/a>.<\/p>\n<pre style=\"white-space: pre-wrap;\">&gt;foods echo cheese\r\n&gt;&gt;foods echo eggs\r\n&gt;&gt;foods echo milk\r\n\r\n&gt;fruits echo apple\r\n&gt;&gt;fruits echo grape\r\n&gt;&gt;fruits echo orange\r\n\r\n&gt;veggies echo celery\r\n&gt;&gt;veggies echo lettuce\r\n&gt;&gt;veggies echo peas\r\n\r\ngit add foods fruits veggies\r\ngit write-tree\r\n<\/pre>\n<p>The <code>git write-tree<\/code> will emit a tree that represents the state of the index. We set up the index so that it contains the desired final state: The fruits have been put into <code>fruits<\/code>, the veggies into <code>veggies<\/code>, and the leftovers stay in <code>foods<\/code>.<\/p>\n<p>Now to do the manual octopus merge.<\/p>\n<pre style=\"white-space: pre-wrap;\">git commit-tree \u2329tree-hash\u232a -p HEAD -p f2f -p f2v -m \"split out fruits and veggies from foods\"\r\n<\/pre>\n<p>The <code>git commit-tree<\/code> will print a hash. This is the commit that is the result of the octopus merge. We can fast-forward to it.<\/p>\n<pre style=\"white-space: pre-wrap;\">git merge --ff-only \u2329commit-hash\u232a\r\n<\/pre>\n<p>Okay, let&#8217;s see what we ended up with.<\/p>\n<pre style=\"white-space: pre-wrap;\">git blame fruits\r\n\r\n^e7a114d foods (Alice 2019-09-16 07:00:00 -0700 1) apple\r\n86348be4 foods (Bob   2019-09-16 07:00:01 -0700 2) grape\r\n34eb5bd1 foods (Carol 2019-09-16 07:00:02 -0700 3) orange\r\n\r\ngit blame veggies\r\n\r\n^e7a114d foods (Alice 2019-09-16 07:00:00 -0700 1) celery\r\n86348be4 foods (Bob   2019-09-16 07:00:01 -0700 2) lettuce\r\n34eb5bd1 foods (Carol 2019-09-16 07:00:02 -0700 3) peas\r\n\r\ngit blame foods\r\n\r\n^e7a114d (Alice 2019-09-16 07:00:00 -0700 1) cheese\r\n86348be4 (Bob   2019-09-16 07:00:01 -0700 2) eggs\r\n34eb5bd1 (Carol 2019-09-16 07:00:02 -0700 3) milk\r\n<\/pre>\n<p>Next time, we&#8217;ll look at how to do this the easy way.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>More commit-tree tricks.<\/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-102894","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>More commit-tree tricks.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102894","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=102894"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102894\/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=102894"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102894"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102894"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}