{"id":102892,"date":"2019-09-16T07:00:00","date_gmt":"2019-09-16T14:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102892"},"modified":"2019-09-16T06:21:20","modified_gmt":"2019-09-16T13:21:20","slug":"20190916-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190916-00\/?p=102892","title":{"rendered":"How do I split a file into two while preserving git line history?"},"content":{"rendered":"<p>Some time ago, I showed how to <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190514-00\/?p=102493\"> combine two files into one while preserving line history<\/a>. Today, we&#8217;re going to do the opposite: Split a file into two smaller files, while preserving line history.<\/p>\n<p>Let&#8217;s set up a scratch repo to demonstrate. I&#8217;ve omitted the command prompts so you can copy-paste this into your shell of choice and play along at home. (The timestamps and commit hashes will naturally be different.)<\/p>\n<pre style=\"white-space: pre-wrap;\">git init\r\n\r\n&gt;foods echo apple\r\n&gt;&gt;foods echo celery\r\n&gt;&gt;foods echo cheese\r\ngit add foods\r\ngit commit --author=\"Alice &lt;alice&gt;\" -m created\r\n\r\n&gt;&gt;foods echo eggs\r\n&gt;&gt;foods echo grape\r\n&gt;&gt;foods echo lettuce\r\ngit commit --author=\"Bob &lt;bob&gt;\"   -am middle\r\n\r\n&gt;&gt;foods echo milk\r\n&gt;&gt;foods echo orange\r\n&gt;&gt;foods echo peas\r\ngit commit --author=\"Carol &lt;carol&gt;\" -am last\r\n\r\ngit tag ready\r\n<\/pre>\n<p>With this starting point, the <code>git blame<\/code> output says<\/p>\n<pre style=\"white-space: pre-wrap;\">^e7a114d (Alice 2019-09-16 07:00:00 -0700 1) apple\r\n^e7a114d (Alice 2019-09-16 07:00:00 -0700 2) celery\r\n^e7a114d (Alice 2019-09-16 07:00:00 -0700 3) cheese\r\n86348be4 (Bob   2019-09-16 07:00:01 -0700 4) eggs\r\n86348be4 (Bob   2019-09-16 07:00:01 -0700 5) grape\r\n86348be4 (Bob   2019-09-16 07:00:01 -0700 6) lettuce\r\n34eb5bd1 (Carol 2019-09-16 07:00:02 -0700 7) milk\r\n34eb5bd1 (Carol 2019-09-16 07:00:02 -0700 8) orange\r\n34eb5bd1 (Carol 2019-09-16 07:00:02 -0700 9) peas\r\n<\/pre>\n<p>As we noted when we learned how to combine two files, the na\u00efve way of splitting the file will treat the larger file as a continuation of the original (assuming you haven&#8217;t hit the rename limit), and the smaller file will be treated as a brand new file. The blame of the smaller file will blame you, the person who split them, instead of blaming the person who introduced each line.<\/p>\n<p>To get git to follow the line attributes, we have to make each of the result files look like a rename of the original. We can do this by creating each piece in a different branch, then merging them all together.<\/p>\n<p>In a new <code>fruits<\/code> branch, the first step is to do a pure rename, so that git will recognize that the <code>fruits<\/code> file is a continuation of the <code>foods<\/code> file.<\/p>\n<pre style=\"white-space: pre-wrap;\">git checkout -b fruits\r\ngit mv foods fruits\r\ngit commit --author=\"Greg &lt;greg&gt;\" -m \"split foods to fruits\"\r\n<\/pre>\n<p>Now you can edit the <code>fruits<\/code> file to contain just the part you want to split out. In this case, we want the fruits (duh).<\/p>\n<pre style=\"white-space: pre-wrap;\">&gt;fruits echo apple\r\n&gt;&gt;fruits echo grape\r\n&gt;&gt;fruits echo orange\r\ngit commit --author=\"Greg &lt;greg&gt;\" -am \"split foods to fruits\"\r\n\r\ngit checkout -\r\n<\/pre>\n<p>Repeat for the veggies.<\/p>\n<pre style=\"white-space: pre-wrap;\">git checkout -b veggies\r\ngit mv foods veggies\r\ngit commit --author=\"Greg &lt;greg&gt;\" -m \"split foods to veggies\"\r\n\r\n&gt;veggies echo celery\r\n&gt;&gt;veggies echo lettuce\r\n&gt;&gt;veggies echo peas\r\ngit commit --author=\"Greg &lt;greg&gt;\" -am \"split foods to veggies\"\r\n\r\ngit checkout -\r\n<\/pre>\n<p>The last file (dairy) can be done directly in the original branch.<\/p>\n<pre style=\"white-space: pre-wrap;\">git mv foods dairy\r\ngit commit --author=\"Greg &lt;greg&gt;\" -m \"split foods to dairy\"\r\n\r\n&gt;dairy echo cheese\r\n&gt;&gt;dairy echo eggs\r\n&gt;&gt;dairy echo milk\r\ngit commit --author=\"Greg &lt;greg&gt;\" -am \"split foods to dairy\"\r\n<\/pre>\n<p>And now we octopus merge all the branches together.<\/p>\n<pre style=\"white-space: pre-wrap;\">git merge fruits veggies\r\n<\/pre>\n<p>This time, the octopus merge succeeds. All branches agree that the <code>foods<\/code> file be deleted, so there are no merge conflicts.<\/p>\n<pre style=\"white-space: pre-wrap;\">Trying simple merge with fruits\r\nTrying simple merge with veggies\r\nMerge made by the 'octopus' strategy.\r\n fruits  | 3 +++\r\n veggies | 3 +++\r\n 2 files changed, 6 insertions(+)\r\n create mode 100644 fruits\r\n create mode 100644 veggies\r\n<\/pre>\n<p>And lo and behold, all three resulting files preserved the original line histories. Greg doesn&#8217;t show up anywhere.<\/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 dairy\r\n\r\n^e7a114d foods (Alice 2019-09-16 07:00:00 -0700 1) cheese\r\n86348be4 foods (Bob   2019-09-16 07:00:01 -0700 2) eggs\r\n34eb5bd1 foods (Carol 2019-09-16 07:00:02 -0700 3) milk\r\n<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The octopus delivers.<\/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-102892","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>The octopus delivers.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102892","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=102892"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102892\/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=102892"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102892"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102892"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}