Transfer Git Branch Across Repos

Quick guide for moving a commits in a git branch to a separate repository.

Recently at work we decided to combine several projects into a single repository. Each project will continue to be published separately, but coordinating cross-project features should be easier. Somebody on the team managed combining the code bases, including outstanding branches.

Somehow I missed the note about when the consolidation would happen, and a local branch I was working on didn't get moved to the monorepo. I had to find a way to move the branch myself. I knew git had facilities to handle this kind of situation, but I had to look up and piece together the particulars.

I started with local copies of both projects in sibling directories (old is $LEGACY; new is $MONO). AllMost of the code from $LEGACY was moved into $MONO/packages/legacy and hasn't changed significately. I made sure both projects were up-to-date with our remote server before starting. Below is the process I followed and a few thoughts.

First move into the legacy directory.

cd $LEGACY

Create a numbered patch file for each commit on the target branch. feature-whatever-idk should be rebased onto master. Create the patches when you are ready.

git format-patch --numbered master..feature-whatever-idk

You will end up with one patch file per commit in feature-whatever-idk. Each patch will include the commit metadata (author, message), a diff and the git version that created it. The patch file names will be adapted from the commit subject (first line in message) and prefixed with a zero-padded integer.

Move the patches to $MONO.

mv 000* ../$MONO
cd ../$MONO

Now checkout master and create a new branch for the patches.

git checkout master
git checkout --branch feature-whatever-idk-but-mono

I could use git apply-patch to apply each patch individually, but I'd have to re-commit each one and copy-paste the commit messages. I want to replay the patches instead, like a cross-repo git rebase.

git am --directory packages/legacy 000*

(I don't know what am stands for, but this functionality was meant to allow passing patches across email. Maybe "apply mail"?) Recall the $LEGACY code was moved into a deep subdirectory, so the file name paths won't match up. I considered editing each patch to prepend the new directory information, but then I stumbled across the --directory argument. It tells git to prepend packages/legacy to all the paths in the patches.

In my case there was some inconsequential conflicts in the first patch, and I had to abort (git am --abort) and start over a few times. Ultimately I edited the patch to remove the conflicting files; everything else applied cleanly.

Remember to clear the file name from the top of the patch if you need to remove its diff. Also be sure to not delete the git version at the bottom of the patch when editing the last file. It looks like this:

<this line is part of the diff, but the next two lines are for the version>
--
2.6.3