Use case: You have repository A with remote location rA, and repository B (which may or may not have remote location rB). You want to do one of two things:
- preserve all commits of both repositories, but replace everything from A with the contents of B, and use rA as your remote location
- actually combine the two repositories, as if they are two branches that you want to merge, using rA as the remote location
NB: Check out git subtree
/git submodule
and this Stack Overflow question before going through the steps below. This gist is just a record of how I solved this problem on my own one day.
Before starting, make sure your local and remote repositories are up-to-date with all changes you need. The following steps use the general idea of changing the remote origin and renaming the local master branch of one of the repos in order to combine the two master branches.
Change the remote origin of B to that of A:
$ cd path/to/B
$ git remote rm origin
$ git remote add origin url_to_rA
Rename the local master branch of B:
$ git checkout master
$ git branch -m master-holder
Pull all the code of A from rA into your local B repo.
$ git fetch
$ git checkout master
$ git pull origin master
Now the master branch of A is master in B. The old master of B is master-holder.
Delete all the things! (i.e, scrap everything from A.) If you actually want to merge both repos, this step is unnecessary.
$ git rm -rf *
$ git commit -m "Delete all the things."
Merge master-holder into master. (If you didn't do the delete step above, you have to option of git checkout master-holder; git rebase master
instead.) For more recent versions of git, you'll probably have to add the --allow-unrelated-histories
flag (thanks to @sadzik).
git merge master-holder --allow-unrelated-histories
git log
should show all the commits from A, the delete commit, the merge commit, and finally all the commits from B.
Push everything to rA
git push origin master
Now your local copy of B has become a "unified" repository, which includes all the commits from A and B. rA is used as the remote repo. You no longer need your local copy of A or the remote repo rB (although keeping rB around for a bit longer isn't a bad idea).
I think it's got to do with how git handles merging unrelated histories. Here's a test I ran with two simple repos:
Which when merged as described become:
EXAMPLE COMMANDS TO GENERATE THIS REPO STATE
$ mkdir a $ mkdir b $ cd a $ git init Initialized empty Git repository in /Users/michael/Documents/test-stuff/test-repo-merge/a/.git/ $ echo data > file1 $ cat file1 data $ git add . $ git commit -m "First commit A" [master (root-commit) 49021aa] First commit A 1 file changed, 1 insertion(+) create mode 100644 file1 $ cd ../b/ $ git init Initialized empty Git repository in /Users/michael/Documents/test-stuff/test-repo-merge/b/.git/ $ echo data > file2 $ git add . $ git commit -m "First commit B" [master (root-commit) 772ec4a] First commit B 1 file changed, 1 insertion(+) create mode 100644 file2 $ cd ../a/ $ echo data > file3 $ git add . $ git commit -m "Second commit A" [master 2ffa2bd] Second commit A 1 file changed, 1 insertion(+) create mode 100644 file3 $ cd ../b/ $ echo data > file4 $ git add . $ git commit -m "Second commit B" [master eab377d] Second commit B 1 file changed, 1 insertion(+) create mode 100644 file4 $ git remote add other ../a $ git fetch other warning: no common commits remote: Enumerating objects: 5, done. remote: Counting objects: 100% (5/5), done. remote: Compressing objects: 100% (3/3), done. remote: Total 5 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (5/5), done. From ../a * [new branch] master -> other/master $ git merge other/master fatal: refusing to merge unrelated histories $ git merge other/master --allow-unrelated-histories Merge made by the 'recursive' strategy. file1 | 1 + file3 | 1 + 2 files changed, 2 insertions(+) create mode 100644 file1 create mode 100644 file3 $ git log --pretty=oneline d15171726f4678556680648d9911cfba8daf3fba (HEAD -> master) Merge remote-tracking branch 'other/master' eab377dbf9e00c20244d7f73e387d8b353d3f730 Second commit B 2ffa2bdc459392c10e0a286173bb9dd33cdb930e (other/master) Second commit A 772ec4a01176c68e6e5e9013f60dd17462acb394 First commit B 49021aa931f7583db4421e08b14b067e10bea7f3 First commit A $ git checkout 49021aa931f7583db4421e08b14b067e10bea7f3 HEAD is now at 49021aa First commit A $ ls file1 $ git checkout 772ec4a01176c68e6e5e9013f60dd17462acb394 Previous HEAD position was 49021aa First commit A HEAD is now at 772ec4a First commit B $ ls file2 $ git checkout master Previous HEAD position was 772ec4a First commit B Switched to branch 'master' $ ls file1 file2 file3 file4 ```All the files exist on the final commit, but the intermediate commits don't contain files from the other repo because the histories are unrelated.
Now if you do
git rebase master-holder
, or in the example heregit rebase other/master
, you'll end up with:and commits B1' and B2' with have all the files from A1 and A2, which I think is what you want. Note that this won't preserve a linear commit history across both repos since the rebase replays the changes of B1 and B2 and you end up with new commits, but it does end up in a more useful state for your case.
So to summarize: you want to use
git rebase
instead ofgit merge --allow-unrelated-histories