Copy part of a Git repository to another repository with all history preserved

You want to copy part of repository A to repository B with all history of the part you copy preserved. We assume the part you want to move is within a subdirectory. This can be done in two steps.

Step 1: Retain only the part you wish to copy in repository A using history modification

Clone repository A. Detach from the upstream branches if you want to prevent accidental push to the remote repository. Remove all files except those within the subdirectory you want to separate, say some/module here:

git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter some/module -- --all

After this the content of the subdirectory some/module will be in the root of the local repository. If you want to keep it in the same directory structure as before, move it to the directory where it originally was:

git filter-branch -f --index-filter 'git ls-files -s | sed "s-\t\"*-&some/module/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD

On Mac, this won’t work because BSD sed doesn’t recognise \t – use gsed or enter Ctrl-V followed by Tab key instead of \t.

Optionally, to annotate the commit messages:

git filter-branch -f --msg-filter 'sed "s/^/[some prefix] /"' HEAD

Step 2: Merge the modified repository A to repository B

Go to repository B to which you want to move the separated subdirectory. Pull the modified version of repository A and that’s it:

git pull path/to/A HEAD

Alternative way using git-subtree

If you have git-subtree installed, the first step can be done simply by splitting the subdirectory into a new branch:

git subtree split --prefix some/module --branch some/module --annotate '[prefix to commit messages] '

Annotation is optional. If you wish to move the files back to their original directory, run the same git filter-branch above:

git filter-branch -f --index-filter 'git ls-files -s | sed "s-\t\"*-&some/module/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' some/module

When pulling the modified repository A, be sure you pull the very branch you created by git-subtree:

git pull path/to/A some/module
Advertisements
Copy part of a Git repository to another repository with all history preserved

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s