git move directory to another repository while keeping the history
I can't help you with git subtree
, but with filter-branch
it's possible.
First you need to create a common repository that will contain both source and destination branches. This can be done by adding a new "remote" beside "origin" and fetching from the new remote.
Use filter-branch
on the source branch to rm -rf
all directories except dir-to-move
. After that you'll have a commit history that can be cleanly rebased or merged into the destination branch. I think the easiest way is to cherry-pick
all non-empty commits from the source branch. The list of these commits can be obtained by running git rev-list --reverse source-branch -- dir-to-move
Of course, if the history of dir-to-move
is non-linear (already contains merge commits), then it won't be preserved by cherry-pick, so git merge
can be used instead.
Example create common repo:
cd repo-2
git remote add source ../repo-1
git fetch source
Example filter branch
cd repo-2
git checkout -b source-master source/master
CMD="rm -rf dir1 dir2 dir3 dir5"
git filter-branch --tree-filter "$CMD"
Example cherry-pick into destination master
cd repo-2
git checkout master
git cherry-pick `git rev-list --reverse source-master -- dir-to-move`
FWIW, the following worked for me after a number of iterations.
Clone both the repos to a temporary work area.
git clone <repourl>/repo-1.git
git clone <repourl>/repo-2.git
cd repo-1
git remote rm origin # delete link to original repository to avoid any accidental remote changes
git filter-branch --subdirectory-filter dir-to-move -- --all # dir-to-move is being moved to another repo. This command goes through history and files, removing anything that is not in the folder. The content of this folder will be moved to root of the repo as a result.
# This folder has to be moved to another folder in the target repo. So, move everything to another folder.
git filter-branch -f --index-filter \
'git ls-files -s | /usr/local/bin/sed -e "s/\t\"*/&dir-to-move\//" |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
git update-index --index-info &&
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
# Above command will go through history and rewrite the history by adding dir-to-move/ to each files. As a result, all files will be moved to a subfolder /dir-to-move. Make sure to use gnu-sed as OSX sed doesn't handle some extensions correctly. For eg. \t
Now switch to target repo and fetch everything from source.
git clone repo-2
git remote add master ../repo-1/
git pull master master --allow-unrelated-histories
git push # push everything to remote
Above steps assume that master branches are used for both source and targets. However, tags and branches were ignored.
My approach to move dir-to-move
to repo 2
. Start by cloning the repo 1
in a new location and cd to the repo 1
folder. In my case I am moving the folder from repo 1
branch develop
to repo 2
where develop
is not existent yet.
git filter-branch --subdirectory-filter dir-to-move -- --all #line 1
git remote -v #line 2
git remote set-url origin repo_2_remote_url #line 3
git remote -v #line 4
git push origin develop #line 5
Explanation for each line:
- Cleans all commits not related to
dir-to-move
, removes all files outside that folder, moves all contents in the root folderrepo 1
. From git docs:
Only look at the history which touches the given subdirectory. The result will contain that directory (and only that) as its project root
- You are still pointing to your origin
repo 1
url - Replace the origin url for your current folder to point to
repo 2
- Check the URLs have been updated correctly
- Push to
repo 2
the (newly created, in this case)develop
branch
You can now clone or pull or fetch your repo 2
repository. You will have under repo 2
folder the contents of dir-to-move
along with relevant history as expected.