Skip to content

Instantly share code, notes, and snippets.

@smashwilson
Last active November 7, 2019 23:35
Show Gist options
  • Save smashwilson/015e6a3abfbf7af73d31 to your computer and use it in GitHub Desktop.
Save smashwilson/015e6a3abfbf7af73d31 to your computer and use it in GitHub Desktop.
Move a subdirectory from one git repository to a subdirectory of another, without losing commit history.
# Assumptions:
#
# * After the merge is complete, the directory should exist in the target repository and not exist in the source
# repository. In other words, this is a move, not a cross-reference: we never want to be able to push commits
# back to the source repository, or merge further changes from the source repository. If you want to do that
# instead, a subtree merge is what you're looking for.
#
# * The directory doesn't exist in the target repository yet.
#
# In this example, we're moving the "/openstack-swift/" directory from jclouds/jclouds-labs-openstack to
# "/apis/openstack-swift/" in jclouds/jclouds.
# Get a clone of the target repository.
git clone git@github.com:jclouds/jclouds.git
cd jclouds
# Add the source repository as a remote, and perform the initial fetch.
git remote add -f sourcerepo git@github.com:jclouds/jclouds-labs-openstack.git
# Create a branch based on the source repositories' branch that contains the state you want to copy.
git checkout -b staging-branch sourcerepo/master
# Delete everything that isn't the directory we want to move.
# Don't worry, we're not going to push this back ;-)
git rm -r openstack-glance/ openstack-heat/ openstack-marconi/ \
openstack-neutron/ openstack-autoscale/ rackspace-* \
*.md pom.xml
git commit -m "Delete everything we don't want to merge"
# Move the directory into its final position.
mkdir apis/
git mv openstack-swift/ apis/
git commit -m "Lock S-foils in attack position"
# Change back to master and merge it in.
# You could also do this by submitting a pull request, of course. I had one conflict, in /.mailmap.
git checkout master
git merge staging-branch
# I had one conflict, in /.mailmap. Fix it, stage it, and commit it to finalize the merge.
${EDITOR} .mailmap
git add .mailmap
git commit
# Voila! The directory has moved, and you still have full history:
#
# ~/samples/jclouds (master>) $ git log --follow --oneline apis/openstack-swift/pom.xml
# cb930d9 Move openstack-swift into apis/.
# 491dc97 Revert "Fix poms so that modernizer doesn't fail on snapshot."
# 889243a Fix poms so that modernizer doesn't fail on snapshot.
# d65048d Fix Maven parent.relativePath warnings
# f33e90e Updating project versions to 2.0.0-SNAPSHOT
# be8bc22 Up to 2.0.0-SNAPSHOT after the 1.8.0 release
#
# (Notice that you do need the --follow to trace through the git mv I did.)
# Next: be sure to promptly delete the directory from the source repository by normal means
# (git rm -r dir/ && git commit && git push && pull request), because you don't have an
# easy way to update the target repository with further changes.
@nacx
Copy link

nacx commented Oct 9, 2014

I'm glad I checked this, gists don't notify when someone leaves a comment

:) Agree. I also checked this without having been notified!

You can also do some trickery with subtrees to make this happen

If I'm not wrong (haven't tried, just seen comments after googling for the best process) when using subtrees you're not able to rebase normally the branch. It presents several issues.

Also, rather than copy-paste, this is the kind of thing I'd try to keep around as a script for projects that are likely to need it often (like JClouds).

+1, and thanks for the feedback! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment