How to append commits of unrelated branches?

104 Views Asked by At

I want to append two unrelated branches together. Let's say I have something like this:

a---b---c---d (A)


      A---B---C---D---E---F (branch)
     /
e---f---g---h (B)

I want the result to be:

                       A'---B'---C'---D'---E'---F' (branch)
                      /
a---b---c---d---e'---f'---g'---h'

I tried with git rebase --onto d e (HEAD is B) but I get conflicts, and my intention is only to append the commits with THEIR content, not to check two commits and see the differences and solve the conflicts. In other words I want the parent of e witch initially is not set to be d. How can I achieve this?

2

There are 2 best solutions below

0
matt On

You can certainly cut-and-paste to form the architecture that you describe. You will have to proceed in two stages because the A-F branch will not magically come with you.

Your diagram is very unfortunate because you have used some names twice. I will try to disambiguate by using better names. Start with this:

a---b---c---d (A-the-branch)

      A---B---C---D---E---F (branch-the-branch)
     /
e---f---g---h (B-the-branch)

So first:

git rebase --onto d e~1 B-the-branch

That will give you:

a---b---c---d---e'---f'---g'---h' (B-the-branch)

But A-F has been left behind. So now:

get rebase --onto f' A~1 branch-the-branch

But you cannot prevent merge considerations from happening; a rebase is a merge. (More precisely: a rebase is a series of cherry-picks, and a cherry-pick is a merge.) Just to remind you why that is: you cannot in any way edit a commit, including moving it (giving it a different parent). The only way to apparently rearrange the architecture is to make all new commits that are effectively like copies of your original commits. But the only way that Git will make new commits out of whole cloth is by merge logic. You can add

-Xtheirs

to resolve any conflicts automatically in favor of the copied commits, but you cannot not merge.

0
ElpieKay On

The key is to make d become the parent of e. So, create e', the tree of which is the same with the tree of e and the parent of which is d. Replace the letters like d, e in the commands with their real commit sha1 values.

# Save the commit message of e in a temp file
git log -1 e --pretty=%B > tmp_msg.txt

# Create e'
newbase=$(git commit-tree -p d -F tmp_msg.txt e^{tree})
echo $newbase
rm -fv tmp_msg.txt

# Use rebase or cherry-pick to apply other commits onto e'
git checkout $newbase
git cherry-pick f g h
git checkout f'
git cherry-pick f..F
# Or
git rebase --onto e' e h
git rebase --onto f' f F

# Move the branches to the new heads
git update-ref refs/heads/<branch> F'
# Or
git switch <branch>
git reset F' --hard

If the graph of the other commits is more complicated than the example in the question, consider git rebase -i -r --keep-empty to do elaborate operations. You could also use git commit-tree to recreate them one by one like e'.