Search code examples
gitgit-mergegit-rebasegit-rewrite-history

Putting unrelated histories behind each other


For some reason (due to complicated conversion from SVN) I have two Git repositories:

  • git repo A contains all history up to 31st December 2021
  • git repo B contains all history from 1st January 2022 on

Now I would like to "merge" them in a way that I have the whole history, i.e. first the main branch of repo A and then the main branch from repo B.

I have not really understood yet how to do this properly.

  • If I just merge the two repos, then I get a merge commit at the end, which is also pretty meaningless because I do not want to have the content of repo A merged into B, since B is more recent.
  • If I rebase the main of B on the main of A I get kind of what I wanted but I am not sure whether the newly generated commits (from the ones of repo B) may have content I don't want, e.g. old files.

Can someone tell me how to do this properly?


Solution

  • Create a working repository.

    git init foo
    cd foo
    

    Fetch the two branches into the repository.

    git fetch <url_to_A> BranchA:BranchA
    git fetch <url_to_B> BranchB:BranchB
    

    Rebase BranchB onto BranchA, preserving BranchB's log graph.

    # WARNING, THIS MIGHT NOT WORK
    git switch BranchA
    git rebase --onto BranchA --root BranchB --empty=keep --rebase-merges
    

    The current BranchB contains the histories of BranchA and the old BranchB.

    Suppose the head of BranchA is X and the root of BranchB is Y. The above git rebase might not work as expected. It could still merge X and Y. I don't know what your branches are like. If it does not work, here's another method that I'm sure can work.

    After fetching the 2 branches, first create an equivalent Y on BranchA.

    git switch BranchA
    git merge $(git commit-tree -p BranchA -m blah Y^{tree})
    # reuse the commit message of Y
    git commit --amend -C Y
    

    Then rebase the commits after Y till the head of BranchB onto BranchA, preserving BranchB's original log graph.

    git rebase --empty=keep --rebase-merges --onto BranchA Y BranchB
    

    The 2nd method first creates a commit Y' based on X. It does not merge X and Y. It has the same contents with Y. git checkout Y and git checkout Y' result in the same files.