Consider the following scenario:
I have developed a small experimental project A in its own Git repo. It has now matured, and I\'d like A to be part of larger projec
If you want to put the files from a branch in repo B in a subtree of repo A and also preserve the history, keep reading. (In the example below, I am assuming that we want repo B's master branch merged into repo A's master branch.)
In repo A, first do the following to make repo B available:
git remote add B ../B # Add repo B as a new remote.
git fetch B
Now we create a brand new branch (with only one commit) in repo A that we call new_b_root
. The resulting commit will have the files that were committed in the first commit of repo B's master branch but put in a subdirectory called path/to/b-files/
.
git checkout --orphan new_b_root master
git rm -rf . # Remove all files.
git cherry-pick -n `git rev-list --max-parents=0 B/master`
mkdir -p path/to/b-files
git mv README path/to/b-files/
git commit --date="$(git log --format='%ai' $(git rev-list --max-parents=0 B/master))"
Explanation: The --orphan
option to the checkout command checks out the files from A's master branch but doesn't create any commit. We could have selected any commit because next we clear out all the files anyway. Then, without committing yet (-n
), we cherry-pick the first commit from B's master branch. (The cherry-pick preserves the original commit message which a straight checkout doesn't seem to do.) Then we create the subtree where we want to put all files from repo B. We then have to move all files that were introduced in the cherry-pick to the subtree. In the example above, there's only a README
file to move. Then we commit our B-repo root commit, and, at the same time, we also preserve the timestamp of the original commit.
Now, we'll create a new B/master
branch on top of the newly created new_b_root
. We call the new branch b
:
git checkout -b b B/master
git rebase -s recursive -Xsubtree=path/to/b-files/ new_b_root
Now, we merge our b
branch into A/master
:
git checkout master
git merge --allow-unrelated-histories --no-commit b
git commit -m 'Merge repo B into repo A.'
Finally, you can remove the B
remote and temporary branches:
git remote remove B
git branch -D new_b_root b
The final graph will have a structure like this: