Home > front end >  How to merge Git repos with common code BUT NO common ancestry
How to merge Git repos with common code BUT NO common ancestry

Time:09-13

How can I merge two Git repos with common code but no common ancestry?

Here is what happened:

  1. repoRoot was created
  2. repoRoot was lost but there was a file only backup
  3. repoNew was created from the same files but with no history
  4. in repoNew all code was moved to a new subFolder
  5. repoRoot was found again

What we are desiring now is to have ONE repo with

  1. First the history of repoRoot
  2. Followed by the history of repoNew

Preferably with all branches that existed in both intact

Here are two sample repos

CodePudding user response:

The easiest would be a graft/replace. That will allow you to connect both histories without much trouble, to the best of my knowledge. Check https://stackoverflow.com/a/6802005/2437508

But if you don't mind rewriting the later part of history, you could rewrite the history of the second repo on top of the first.... you just need to find the two revisions that share the same content so that you work from there.

Let's assume you have a local repo that can see both repos as remotes, old and new.

The first revision in branches of new is branch new/first, which matches in content with old/main. If we wanted to rewrite new/main on top of old/main:

git rebase --rebase-merges --onto old/main new/first new/main

That should do.

Another case could be that the last revision in old master is very similar to the first revision in the new master.... and you still want to have continuity between both branches. In that case, you need to hack it a little bit so that it is transparent. We need to first create an equivalent of the first revision of the new master that points to the last revision of the old master, and then rebase on top of this revision.... that could be done like this:

git checkout first-revision-of-new-master
git reset --soft old-master
git commit -C first-revision-of-new-master # create an equivalent of the first in new master but that points to the _old_ master as its parent
# now we can rebase on top of this revision
git rebase --rebase-merges --onto HEAD first-revision-of-new-master new-master

Finally, if it is not the last of old master and the first in new master that have the same tree but some other revisions, the trick could still be pulled off like this. Let's call these revisions sameTreeOldRevision and sameTreeNewRevision (again: they have the same tree):

git rebase --rebase-merges --onto sameTreeOldRevision sameTreeNewRevision new-master

update

This is using the sanboxed repos:

14:42 $ git log --graph --oneline new/master
*   ff73670 (new/master) Merge branch 'dev'
|\  
| *   2078106 (new/dev) Merge branch 'dev2' into dev
| |\  
| | * 6e134c9 (new/dev2) dev2 branch
| |/  
| * 418b317 dev bfranch
* | f5f4abb moved files
|/  
* e860f9a new repot commit 2
* 80ece84 initial commit
✔ ~/blah/temp [:633fdcf|✔] 
14:42 $ git log --graph --oneline old/master
*   9086ffd (old/master) Merge branch 'dev'
|\  
| * c5e3e07 (old/dev) branch 2
|/  
* 2227b33 branch
* 06d3c7b commit 3
* e60c1e0 Commit 2
* 23d81c0 Commit 1
✔ ~/blah/temp [:633fdcf|✔] 
14:42 $ git cat-file -p 80ece84
tree 4f5da62292695e4d8268f73e6f8c011ed5adef09
author Patrick Wolf <[email protected]> 1662689224 -0700
committer Patrick Wolf <[email protected]> 1662689224 -0700

initial commit
✔ ~/blah/temp [:633fdcf|✔] 
14:42 $ git cat-file -p old/master
tree 4f5da62292695e4d8268f73e6f8c011ed5adef09 <----- same tree as the other rev so it's a perfect tree match, great!
parent 2227b3399653f606b7bac328a348577fe629d88b
parent c5e3e070f8bab8c90d9d62972033f6150fc24c32
author Patrick Wolf <[email protected]> 1662689186 -0700
committer Patrick Wolf <[email protected]> 1662689186 -0700

Merge branch 'dev'
✔ ~/blah/temp [:633fdcf|✔] 
14:43 $ git rebase --onto old/master 80ece84 new/master --rebase-merges
Successfully rebased and updated detached HEAD.
✔ ~/blah/temp [:7e26ae0|✔] 
14:43 $ git log --oneline --graph
*   7e26ae0 (HEAD) Merge branch 'dev'
|\  
| *   f6e0a1e Merge branch 'dev2' into dev
| |\  
| | * b264f15 dev2 branch
| |/  
| * 69268af dev bfranch
* | c615483 moved files
|/  
* ddeb1d1 new repot commit 2
*   9086ffd (old/master) Merge branch 'dev'
|\  
| * c5e3e07 (old/dev) branch 2
|/  
* 2227b33 branch
* 06d3c7b commit 3
* e60c1e0 Commit 2
* 23d81c0 Commit 1

At this point, I haven't pushed into any remote.... but you can see that I have placed an equivalent of the new master on top of the old master. At this point you might run:

git push -f new @:master
  • Related