You write:
One important fact in my scenario is that both branches are completely unrelated. No commit of either branch affects patches of the other branches' commits => There will be no conflicts.
Of course, the "splicing" operation you suggest is probably a bad idea if you anticipate that many conflicts will arise. Let's assume that, indeed, nothing bad will happen.
If, at the beginning, your repo looks like this
A---B------C--D [branch1]
X---Y--Z [branch2]
you can follow the procedure outlined below to automatically "splice" commits from both branches into a single branch, while maintaining chronological order.
"Splicing" two unrelated branches
Make sure you're in a clean working state; then check out branch1
and merge branch2
into it:
git checkout branch1
git merge branch2
That will yield
A---B------C--D---E [HEAD=branch1]
/
X---Y--Z------- [branch2]
Now, I know that's not what you want, but bear with me for a second. We will use merge commit E
to have access to "the ancestry on both sides" at once.
Check out branch2
and reset it to commit A
.
git checkout branch2
git reset --hard A
You'll be in the following situation:
A [HEAD=branch2]
\
---B------C--D---E [branch1]
/
X---Y--Z-------
Generate a list (in chronological order) of all the non-merge commits reachable from branch1
but not from branch2
:
git rev-list --no-merges --reverse branch2..branch1
This should yield the following list of commits: X
, B
, Y
, Z
, C
, D
; commit E
, which was created in Step 1 will not be in that list, because we used the --no-merges
flag.
Cherry-pick those commits on top of branch2
(A).
git cherry-pick `git rev-list --no-merges --reverse branch2..branch1`
Your repo will then look as follows:
A--X'--B'--Y'--Z'--C'--D' [HEAD=branch2]
\
---B-----C-D---E [branch1]
/
X---Y-Z------
Delete branch1
:
git branch -D branch1
Edit: As you correctly remarked, because branch1
is not fully merged into the current branch (branch2
), using just -d
won't do, here; you need to use the -D
flag instead.
Your repo will then simply be
A--X'--B'--Y'--Z'--C'--D' [HEAD=branch2]
(Optionally) Rename branch2
:
git branch -m branch2 <more_meaningful_name>
Generalization to more than two branches
Let's assume you have n completely unrelated branches: branch1
, branch2
, ..., branchn
, where branch1
corresponds to the branch whose root commit is the oldest commit in the entire repository; Let's call that commit A
.
A ----- o ---- o [branch1]
o ----- o ---- o -- o [branch2]
...
o ----- o - o [branchn]
If you don't know which commit is A
, you can identify it by running
git rev-list --reverse --max-parents=0 --all
The commit ID of A
will be the first listed in the output of that command. And you can identify which branch is branch1
by running:
git branch -r --contains `git rev-list --reverse --max-parents=0 --all | head -1`
Then the procedure outlines in the two-branch case becomes:
Create a commit that has access to the ancestry of all branches, by merging all branches other than branch1
into branch1
.
(same as in two-branch case)
- (same as in two-branch case)
- (same as in two-branch case)
- Delete all branches other than
branch2
.
- (same as in two-branch case)