Git: Merging a Subdirectory from Another Repository

In this brief post I’ll show you how to copy one directory from one repository to another with the hisotry for it intact.

If you are unsure what you are doing then work on clean clones of your source and target repositories.

Let’s get the terminology straight now. We will be going from this state:

  • repo-from
    • tasty-directory
  • repo-to

To this state:

  • repo-from
    • tasty-directory
  • repo-to
    • tasty-directory

Notice that the repositories are on the same directory level.

Shape your Source

cd repo-from
git checkout -b source

We are checking in to a new branch because the stuff we are going to do destroys the branch you are currently on. Also, the name of the branch you are checking into will show up in the merge commit.

git filter-branch --prune-empty --subdirectory-filter tasty-directory --

Now we have the whole contents of tasty-directory in our root directory. You could stop here and just merge te branch in if you want to move the directory to it’s own subdirectory. In our case we want to keep the structure intact so we have to reshape the history a bit to suit our needs.

git filter-branch --prune-empty --tree-filter '
if [[ ! -e tasty-directory ]]; then
    mkdir -p tasty-directory
    git ls-tree --name-only $GIT_COMMIT | xargs -I files mv files tasty-directory
fi' -f

Note that we had to force this command because refs/original/ keeps the state of our branch from before the first filter-branch.

Now you should see a nice subsection of your repositories history that’s concerning only the tasty-directory.

Merging Time

cd ../repo-to
git remote add repo-from ../repo-from
git fetch repo-from

Now we have access to our shaped branch and all of it’s history fetched. All there’s left is to merge, push and forget.

git merge repo-from/source
git push
git remote rm repo-from