Skip to content

Instantly share code, notes, and snippets.

@msrose
Last active October 5, 2024 10:03
Show Gist options
  • Save msrose/2feacb303035d11d2d05 to your computer and use it in GitHub Desktop.
Save msrose/2feacb303035d11d2d05 to your computer and use it in GitHub Desktop.
How to combine two git repositories.

Combining two git repositories

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).

@msrose
Copy link
Author

msrose commented Nov 9, 2019

I think it's got to do with how git handles merging unrelated histories. Here's a test I ran with two simple repos:

A1---A2---
--B1---B2

Which when merged as described become:

A1--B1--A2--B2
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 here git rebase other/master, you'll end up with:

A1--A2--B1'--B2'

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 of git merge --allow-unrelated-histories

@kslong
Copy link

kslong commented Nov 10, 2019

That's more or less what I concluded as well. It's unfortunately not quite what I wanted, but that is what it is.

@Ayushi-Rohela
Copy link

@msrose I want to merge one particular branch say B1 from repository R1 to a branch B2 of repository R2, any ideas on this?

@sayashraaj
Copy link

Hi, will this remove contributions from deleted repo?

@BenjaminTJohnson
Copy link

I like this approach, but it doesn't work with bare repositories, failing at the merge portion.

@Brian-Pho
Copy link

This is exactly what I needed and it worked perfectly. Thanks.

@edazpotato
Copy link

Bro, you actually saved my life. Thank you so, so much!

@sheldonhull
Copy link

Super helpful. Much appreciated!

@zyilmaz
Copy link

zyilmaz commented Jul 10, 2020

It is really helpful. Thank you so much!

@lesovsky
Copy link

lesovsky commented Aug 9, 2020

When i do git rm -rf * I get fatal: pathspec 'internal' did not match any files.

Solved with removing all stuff using "rm -rf ..." and then removing from git using "git rm -rf *".

@IamPhytan
Copy link

@untmdsprt If ever you want to add more complexity, submodules are the way to go

@sheldonhull
Copy link

You are brave :-) I really really don't like dealing with submodules.
I haven't tried subtrees yet but read about them recently and seem interesting to try out.

@MrLixm
Copy link

MrLixm commented Oct 20, 2021

Hey, first thanks a lot, perfect explanations.

Then I got the same error as levosky:
when doing git rm -rf * I get fatal: pathspec 'somefilename' did not match any files.

I was using PyCharm when doing this and this thread redirect me to the good workflow: https://stackoverflow.com/questions/15931238/unable-to-remove-file-that-really-exists-fatal-pathspec-did-not-match-any/51623763

I ended manually deleting (in the file explorer) all the content of github directory (except the .git) and commit right after.

@abhinavmanchanda
Copy link

I wrote a script for this a few years ago. You can check it on my blog.

@Timsword
Copy link

Timsword commented Jan 4, 2022

If you get fatal: refusing to merge unrelated histories just read here.

@Saddam-tech
Copy link

its an awesome tutorial, thank you a lot!

@ysulyma
Copy link

ysulyma commented Mar 15, 2022

Very helpful, cheers!

@guadagn0
Copy link

guadagn0 commented Jun 7, 2022

Very helpful, thank you!

@kotorkovsciy
Copy link

It is really helpful. Thank you so much!

@marvin-hris
Copy link

if you have 2 duplicate repositories like this with same origin main :

  1. https://github.com/user-1/repo_name (first create last month)
  2. https://github.com/user-2/repo_name (active development last push today)

and you want to apply change in repo 2 into repo 1
you just need do this :

  1. cd /path_to_repo_2

  2. open .git directory

  3. open file name "config"

  4. change line of remote origin and save it
    [remote "origin"]
    url = "https://github.com/repo_2/repo_name.git"
    to
    [remote "origin"]
    url = "https://github.com/repo_1/repo_name.git"

  5. git push all of your change into new origin main

done.

note : change remote origin is only for a duplicate repo mistake, i recommended back it up before you doing this.

thanks

@RicardoJeronimo
Copy link

Thank you so much!

@4mubarak
Copy link

This Doesnt work for me Now

@Ehtz
Copy link

Ehtz commented Apr 22, 2024

What i did:

Let's say you want to merge repo A main into repo B main.

  1. Go to internal repo of A and add the remote repo connection to B -> terminal -> git add remote <any name> (git URL repo B)

  2. Then to check if there are 2 remote repos associated to the internal repo: git remote -v

This should return:

any name https://github.com/reponame/app.git (fetch)<- (this is repo B)
any name https://github.com/reponame/app.git (push) <- (this is repo B)
origin https://github.com/reponame/app.git (fetch) <- (this is repo A)
origin name https://github.com/reponame/app.git (push) <- (this is repo A)
  1. Then push the A commits to B repo: git push any name

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