How to copy a git repository into another repositoryBy Mark on July 4th, 2011 in Technobabble
Tags: development, git, setup, workflow
While Beanstalk looks great, it does have an annoyingly low limit on the number of repositories we can have. To save space on old projects, we decided to merge old repos (client work) into a single repo, with each project in its own directory.
Using git, this is pretty easy – but you need some git-fu to make it work. That’s where this post comes in.
The git documentation on the subtree merge strategy helped us figure this out, but like most other git documentation, you’re better off with me, here, reading this.
First, create your new repository. If you use a repository host, you can just clone a blank repository following your host’s instructions, or create it locally first:
mkdir new-repo-dir cd new-repo-dir git init
Now, for each repository you want to add, add it as a remote of your new repository:
git remote add -f (remote name) /path/or/URL/to/remote
(remote name) with a unique name for that repository, and you can either use a path (if the other repo is on your local machine) or a URL to specify the location of the remote.
-f switch tells git not only to add the remote, but to
fetch the remote. This will pull the remote’s database of commits into your new repository’s git database.
However, if you look at the new repo’s directory, it’s still empty. While git now knows about the changesets and commits, it hasn’t applied them locally. First, you need to merge those commits as part of your new history.
git merge -s ours --no-commit (remote name)/master
This command tells git to merge the
master branch of your existing repo into the new repo. The
--no-commit switch tells git to perform the merge & stage the changes, but not actually commit it yet. The
-s switch tells git to use the
ours strategy – ours being the local repository. If you had any files that conflicted between repositories, this means that the “ours” one will be the file used. Make sure you don’t have directory/file conflicts beforehand!
Note that this method only merges the branch you specify (in our case,
master). If you have unmerged changes on other branches, they will not be merged into the new repository. This is a limitation of our approach, but for our purposes, it wasn’t a problem – we had everything in
master. Also note you would lose specific branch history – only the merge commit (into
master) would remain to indicate the branch’s changes.
The next step is the magic:
git read-tree --prefix=foo/bar/ -u (remote name)/master
This tells git to read in the tree of
master branch commits, but to prefix the files & directories with the path provided on the
--prefix switch. I recommend performing this command from the root of your new project and using a relative path. The path will be created if it doesn’t exist.
Finally, commit the merge as a normal commit on your new repository:
git commit -m "Merged (remote name) into subdirectory /foo/bar"