{"id":725,"date":"2014-11-25T08:06:24","date_gmt":"2014-11-25T13:06:24","guid":{"rendered":"http:\/\/blog.fritzhardy.com\/?p=725"},"modified":"2014-12-11T09:14:03","modified_gmt":"2014-12-11T14:14:03","slug":"git-splitting-and-merging-repositories","status":"publish","type":"post","link":"https:\/\/blog.fritzhardy.com\/?p=725","title":{"rendered":"Git: Splitting and Merging Repositories"},"content":{"rendered":"<p>Projects evolve, code boundaries change.  Git commit history shall be preserved.<\/p>\n<p><strong>Split Repo<\/strong><br \/>\nSplit files out into a new repo preserving all history.  Existing repository: perl.  File to preserve: psntrophy.pl.<\/p>\n<p>First, clone the existing repository to a new location, as it will be carved down to just the file(s) we need, thereby becoming the new desired repository:<\/p>\n<pre>\r\ncd tmp\r\ngit clone ~\/workspace\/perl\r\ncd perl\r\ngit remote remove origin\r\n<\/pre>\n<p>Remove everything but the matching file by use of filter-branch.  There are other approaches, particularly for subdirectories, but I found this to work best (all one line):<\/p>\n<pre>\r\ngit filter-branch -f --prune-empty \r\n     --index-filter 'git ls-tree -z --name-only --full-tree HEAD | \r\n     grep -zv \"^psntrophy.pl$\" | \r\n     xargs -0 git rm --cached -r -f --ignore-unmatch' -- --all\r\n<\/pre>\n<p>Rebase to eliminate commits related to renames that are not related to this file, and only remain because git ls-tree did not find these non-existent files for our filter:<\/p>\n<pre>\r\ngit rebase --onto bf1fbad2b6f766b80c2a07edacd7084c6d3fd252 e490b6cb41ca541b7e468148a62f63435366836f\r\ngit rebase --onto 31fe475fe0c7314a2f307fa1c201cd2f9eca6992 bf1fbad2b6f766b80c2a07edacd7084c6d3fd252\r\n<\/pre>\n<p>Garbage collection on 694 objects at 4MB:<\/p>\n<pre>\r\ngit gc\r\nCounting objects: 694, done.\r\nDelta compression using up to 2 threads.\r\nCompressing objects: 100% (674\/674), done.\r\nWriting objects: 100% (694\/694), done.\r\nTotal 694 (delta 253), reused 0 (delta 0)\r\n\r\ndu -csh .git\/objects\/\r\n592K\t.git\/objects\/\r\n592K\ttotal\r\n<\/pre>\n<p><strong>Merge Repos<\/strong><br \/>\nMerge several repositories, each into subdirectories of a new repository, preserving all history.  New repository: unix.  Project to merge: ccli.<\/p>\n<p>First, clone the project we wish to merge in so as to preserve the original in case something goes awry:<\/p>\n<pre>\r\ncd tmp\r\ngit clone ~\/workspace\/ccli\r\ncd ccli\r\ngit remote remove origin\r\ncd ..\r\n<\/pre>\n<p>Now back at top-level tmp, create a new target repository and start with a an initial dummy commit<\/p>\n<pre>\r\ngit init unix\r\ncd unix\r\necho \"initial\" > deleteme.txt\r\ngit add deleteme.txt\r\ngit commit -m \"Initial commit.\"\r\ngit rm .\/deleteme.txt\r\ngit commit -m \"Initial delete.\"\r\n<\/pre>\n<p>Add a remote to the ccli repo:<\/p>\n<pre>\r\ngit remote add ccli ..\/ccli\r\ngit fetch ccli\r\ngit merge ccli\/master\r\n<\/pre>\n<p>Move files and commit:<\/p>\n<pre>\r\nmkdir ccli\r\nfor i in $(ls -1 | grep -v ccli); do git mv $i ccli; done\r\ngit mv .ccli_env_example ccli\r\ngit commit -m \"Move ccli project into ccli subdir\"\r\ngit remote remove ccli\r\n<\/pre>\n<p>Repeat for each project to merge in.<\/p>\n<p>NOTE: Alternative is to fetch each project into separate branches, merge the branches. Cleaner, but end result same.<br \/>\nNOTE: Requires &#8211;follow flag to track full file history in git log, as this is a move like any other. There may be a history rewrite alternative.<br \/>\nNOTE: Approaches with submodules keep subdir distinct so future changes from upstream can be brought in. This is not what we want, as we wish to glue things together permanently.<\/p>\n<p><strong>Upload<\/strong><br \/>\nShould we desire to share our work by pushing it to a new remote repository, such as GitHub:<\/p>\n<pre>\r\ngit remote add origin https:\/\/github.com\/fritzhardy\/psnextract.git\r\ngit push -u origin master\r\n<\/pre>\n<p><strong>Resources<\/strong><\/p>\n<ul>\n<li>\n<a href=\"http:\/\/stackoverflow.com\/questions\/2797191\/how-to-split-a-git-repository-while-preserving-subdirectories\">http:\/\/stackoverflow.com\/questions\/2797191\/how-to-split-a-git-repository-while-preserving-subdirectories<\/a>\n<\/li>\n<li>\n<a href=\"http:\/\/stackoverflow.com\/questions\/37219\/how-do-you-remove-a-specific-revision-in-the-git-history\">http:\/\/stackoverflow.com\/questions\/37219\/how-do-you-remove-a-specific-revision-in-the-git-history<\/a> (rebase notes in responses)\n<\/li>\n<li>\n<a href=\"http:\/\/saintgimp.org\/2013\/01\/22\/merging-two-git-repositories-into-one-repository-without-losing-file-history\">http:\/\/saintgimp.org\/2013\/01\/22\/merging-two-git-repositories-into-one-repository-without-losing-file-history<\/a>\n<\/li>\n<li>\n<a href=\"http:\/\/julipedia.meroh.net\/2014\/02\/how-to-merge-multiple-git-repositories.html\">http:\/\/julipedia.meroh.net\/2014\/02\/how-to-merge-multiple-git-repositories.html<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/help.github.com\/articles\/adding-an-existing-project-to-github-using-the-command-line\">https:\/\/help.github.com\/articles\/adding-an-existing-project-to-github-using-the-command-line<\/a>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Projects evolve, code boundaries change. Git commit history shall be preserved. Split Repo Split files out into a new repo preserving all history. Existing repository: perl. File to preserve: psntrophy.pl. First, clone the existing repository to a new location, as it will be carved down to just the file(s) we need, thereby becoming the new [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-725","post","type-post","status-publish","format-standard","hentry","category-technology"],"_links":{"self":[{"href":"https:\/\/blog.fritzhardy.com\/index.php?rest_route=\/wp\/v2\/posts\/725","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.fritzhardy.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.fritzhardy.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.fritzhardy.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.fritzhardy.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=725"}],"version-history":[{"count":9,"href":"https:\/\/blog.fritzhardy.com\/index.php?rest_route=\/wp\/v2\/posts\/725\/revisions"}],"predecessor-version":[{"id":735,"href":"https:\/\/blog.fritzhardy.com\/index.php?rest_route=\/wp\/v2\/posts\/725\/revisions\/735"}],"wp:attachment":[{"href":"https:\/\/blog.fritzhardy.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=725"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.fritzhardy.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=725"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.fritzhardy.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=725"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}