Moving a project from one git repository to another while retaining it’s history

I recently had to move a project from one git repository to another existing repository under different source tree and I wanted to retain the history of each file. This is rather easy once you know how to do it but you can easily jack up things if you don’t know. So I wanted to write clear instructions for the next time I have to do this and hopefully it also helps someone else.

So I’m going to use Liferay as the example for this so that it’s as clear as it can be. We have two repositories liferay-portal and liferay-plugins which are both hosted on Github. Lets assume as we are modularizing things we want to move the akismet-portlet plugin from liferay-plugins repository to liferay-portal repository under modules/apps/akismet. The akismet-portlet is currently under portlets in the liferay-plugins repository. We could simply copy it there but then we would loose the whole history of that project. So we’ll use some git magic to pull the akismet-portlet into the new path under liferay-portal repository. This example assumes you are working with the master branch but the same steps works with any branch.

1.  Clone the liferay-plugins repository

It’s important to create a fresh clone of the repository as what we are about to do to it will make it unusable. There is a way to recover though I learned that the hard way.

git clone [email protected]:liferay/liferay-plugins.git tmp-plugins-repo
cd tmp-plugins-repo

2. Extract the akismet-portlet and it’s history

In this step we’ll checkout the branch from which we want to move the project and then we’ll rewrite the branch so that it only contains commits to the project we want to move. Word of warning if you skipped step one go back to it since this is destructive operation. 

git checkout master
git filter-branch --subdirectory-filter portlets/akismet-portlet -- --all

When you’ve ran the the git filter-branch you’ll notice that the files from portlets/akismet-portlet are now in the root and nothing else in the repository appears to exist.

3. Move the project to it’s new path in the new repository

Next we need to move the files to the path they are going to be in the liferay-portal repository which is under modules/apps/akismet.

mkdir -p modules/apps/akismet/akismet-portlet
git mv -k * modules/apps/akismet/akismet-portlet
git commit -a "Moved akismet-portlet to modules/apps/akismet/akismet-portlet"

Now the files are in their right place and the changes have been committed to the repository.

4. Pull the akismet-portlet from liferay-plugins to liferay-portal repository

Now you need to clone the liferay-portal repository if you haven’t done it yet. I’m assuming that it is next to the tmp-plugins-repo.

git remote add tmp-plugins-repo ../tmp-plugins-repo
git checkout -b akismet-portlet-move
git pull tmp-plugins-repo master

Now you are ready to merge it to master or send a pull request to someone who will merge it. The only thing is that it has to be merged and can’t be rebased.

Now I used Liferay’s Github repository as example but at Liferay when we move things around we always do it with plain copy followed by massive commit that looses all history. If you don’t care about file history then that works just fine but don’t come crying to me when you try to track down why a certain line of code was added.