Putting unrelated histories behind each other

118 Views Asked by At

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?

2

There are 2 best solutions below

3
ElpieKay On BEST ANSWER

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.

0
Guildenstern On

You need to

  1. Create a new commit from the root commit of the second history: set the first and only parent to the last commit of the first history (git-replace(1))
  2. Recalculate the graph of the second history (git-filter-repo(1))

Then you will have one history/graph.

# Assuming branches `a` and `b` from the two repositories
first_commit_b=$(git log --reverse --format='%H' b | head -1)
git replace --graft $first_commit_b a
# Third-party tool https://github.com/newren/git-filter-repo
git filter-repo --force
# Should show the whole history
git log b

Demo

cd /tmp
tmp=$(mktemp -d)
cd $tmp
git init
git commit --allow-empty -m1
git commit --allow-empty -m2
git commit --allow-empty -m3
git switch --orphan other
git commit --allow-empty -m4
git commit --allow-empty -m5
git commit --allow-empty -m6
git replace --graft ':/4' ':/3'
git filter-repo --force
# Expected: 6
git log --oneline other | wc -l