First - apologies for asking this question. There are a lot of topics about it already. But I'm not having much luck with them. My lack of familiarity with git is not much help.
I'm moving a folder from one git repo to another (which already exists). e.g.
repo-1 ---- dir1 ---- dir2 ---- dir3 ---- dir-to-move ---- dir5 repo-2 ---- dir1 ---- dir2 ---- dir3
In the end I want the repos to look like
repo-1 ---- dir1 ---- dir2 ---- dir3 ---- dir-to-move ---- dir5 repo-2 ---- dir1 ---- dir2 ---- dir3 ---- dir-to-move
i.e. For a time dir-to-move will exist in both repos. But eventually I'll migrate the latest changes to repo-2 and remove dir-to-move from repo-1.
My initial research made me believe that I needed to use filter-branch. e.g.
How to move files from one git repo to another preserving history using `git format-patch`and `git am`
I've since learnt that subtree superseded that approach. However it's not doing what I expected. I thought I'd be able to do something like
In repo-1 workspace
git subtree split -P dir-to-move -b split
to filter the split branch down to only dir-to-move and it's history. Then in repo-2 workspace
git remote add repo-1 repo-1-url.git git subtree add --prefix dir-to-move split
This does move the code across. It also, sort of, includes the history
e.g.
cd repo-2 git log
Shows commits from repo-1
but
cd repo-2 git log dir-to-move
Shows only an 'Add dir-to-move from commit ....'
i.e. The history is included but does not show up when checked for the specific files/directories.
How can I do this properly?
To combine two separate Git repositories into one, add the repository to merge in as a remote to the repository to merge into. Then, combine their histories by merging while using the --allow-unrelated-histories command line option.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With