2

I'd like to merge two repos A and B into a common repo C. A and B both have a lot of files at the root folder level, So I'd like to create folders A and B in the root folder of repo C, and put the content of each original repo in its matching folder.

Is there do this without using git mv in the process? The problem is that I'm using Xcode which unfortunately doesn't follow the history of renamed files, so I'd like to avoid moves if possible. In effect, I'd like to merge each repo directly into its subfolder.

Danra
  • 9,546
  • 5
  • 59
  • 117

3 Answers3

1

There are many ways of achieving this, and twice as many blog-posts and SO answers to this question. The easiest solution is probably to use the git-stitch-repo tool.

Another option (which I haven't tried) is to follow this blog post. But it is pretty gory and will need some adjustments if you have dot-files that you want to move into the sub-directories.

Michael Wild
  • 24,977
  • 3
  • 43
  • 43
  • I haven't been able to make this work on Mac (Mountain Lion). Getting: Can't locate Git/FastExport/Stitch.pm in @INC (@INC contains: /Library/Perl/5.12/darwin-thread-multi-2level /Library/Perl/5.12 /Network/Library/Perl/5.12/darwin-thread-multi-2level /Network/Library/Perl/5.12 /Library/Perl/Updates/5.12.4 /System/Library/Perl/5.12/darwin-thread-multi-2level /System/Library/Perl/5.12 /System/Library/Perl/Extras/5.12/darwin-thread-multi-2level /System/Library/Perl/Extras/5.12 .) at script/git-stitch-repo line 6. BEGIN failed--compilation aborted at script/git-stitch-repo line 6. – Danra Mar 18 '13 at 14:23
  • 1
    How did you install it? `cpan install Git::FastExport::Stitch`? – Michael Wild Mar 18 '13 at 14:36
  • No, just downloaded it from the page. When trying cpan install, I get many similar errors about `Git.pm` not being found, for example: Can't locate Git.pm in @INC (@INC contains: /Users/dan/.cpan/build/Git-FastExport-0.08-gNondI/blib/lib /Users/dan/.cpan/build/Git-FastExport-0.08-gNondI/blib/arch <...snipped...> /System/Library/Perl/Extras/5.12 .) at t/Utils.pm line 6. – Danra Mar 18 '13 at 15:13
  • You need to make sure that the `Git.pm` from your Git installation is on your `PERL5LIB` path variable. Not knowing how you installed Git, it is difficult to give advice, however. Usually it is something like `$PREFIX/lib/perl5/site_perl`, where $PREFIX is the installation prefix of your Git installation, such as `/usr` or `/usr/local`. – Michael Wild Mar 18 '13 at 15:32
  • Thanks! I've been able to install it by adding `/Applications/Xcode.app/Contents/Developer/usr/share/git-core/perl ` to the `/Library/Perl/5.12/AppendToPath` file, and running `git fast-import` rather than `git-fast-import`. However, looking at the result I'm not sure this is what I was looking for. The repos are completely disjoint - So I'm not interested in the master branch being merged. I *think* it is merged since I see the 'master-B' branch containing both folders, not just one. Also the `--select` parameter implies there's a merge going on? – Danra Mar 18 '13 at 16:20
  • You don't want to use `git fast-import`, that is another tool from the same module. You're looking for the `git stitch-repo` command. – Michael Wild Mar 18 '13 at 20:35
  • I was referring to the `git-fast-import` used in the stitch command mentioned in the article: `git-stitch-repo ../A:A ../B:B | git-fast-import` – Danra Mar 19 '13 at 07:47
  • I've been able to build on the blog post to get a working solution. Posting an answer based on it. Thanks! – Danra Mar 20 '13 at 07:22
1
  • Subtree merging — a simple solution. Will just merge a set of files relocating them at the same time under a different prefix (a directory). Will not do anything to the past history of those files.
  • The git-subtree script with its add command. Will create synthetic history as if the originating code has always been located in the specified directory.
kostix
  • 51,517
  • 14
  • 93
  • 176
  • Most answers I saw which mentioned subtrees had many comments about it being broken with regards to logs - Are you aware of these? – Danra Mar 18 '13 at 14:05
  • I believe I followed the instructions in the first method to the letter - and it looks like no history exists at all for the files under the subtree, all the files are considered new. – Danra Mar 18 '13 at 14:43
  • Unfortunately the second method also effectively adds all the files as new, despite keeping a general history of the repository. Therefore, again, no history exists at all for all the files under the subtree, as far as I can tell :( – Danra Mar 18 '13 at 17:01
1

I've been able to do the merge by building on the blog post suggested by Michael. The original post only takes care of merging the master branch - I've expanded it to pull all branches from the merged repos so no history is lost.

To reiterate, I've found many other suggested methods of doing this sort of merge, but none which both save the history and don't rename paths (causing Xcode to fail showing history).

# Init a git repo which will contain the merge of the two repos, each in its own subdirectory
cd ~/dev
mkdir Merged
cd Merged
git init

# Dummy initial commit to create a master branch
git commit --allow-empty -m "Initial commit"

# Clone first repo
cd ..
git clone <RepoA url>
cd RepoA

# Checkout all branches
for remote in `git branch -r | grep -v master `; do git checkout --track $remote ; done

# Prepend subdirectory to all committed files paths in all branches
git filter-branch -f --prune-empty --tree-filter ' mkdir -p .sub;
  find . -mindepth 1 -exec mv {} .sub;
  mv .sub RepoA
  ' -- --glob=refs/heads/*

# Garbage cleanup
git gc --aggressive

# Same steps for second repo
cd ..
git clone <RepoB URL>
cd RepoB

# Checkout all branches  
for remote in `git branch -r | grep -v master `; do git checkout --track $remote ; done

# Prepend subdirectory to all committed files paths in all branches
git filter-branch -f --prune-empty --tree-filter ' mkdir -p .sub;
  find . -mindepth 1 -exec mv {} .sub;
  mv .sub RepoB
  ' -- --glob=refs/heads/*

# Garbage cleanup
git gc --aggressive

# Merge modified repos into unified repo
cd ../Merged
git remote add RepoA ../RepoA
git remote add RepoB ../RepoB
git fetch --all
for remote in `git branch -r`; do git checkout -b $remote --track $remote ; done

# Merge wanted branches (usually master) from each original repo into the master branch of the unified repo
git checkout master
git merge RepoA/master
git merge RepoB/master

# Remove remotes
git remote rm RepoA
git remote rm RepoB

# Garbage cleanup
git gc --aggressive

# All done

# Optionally push into a new empty remote repository
git remote add RepoMerged <Merged Repo URL>
git push --all RepoMerged
Danra
  • 9,546
  • 5
  • 59
  • 117