Reputation: 367
I think that my title is not clear enough, so I will describe the problem:
In my git project, I have 3 branches: master
, b1
, and b2
. Here is the history tree:
A---B---C master
\
D---E---F b1
\
G---H---I b2
Suppose I want to rebase the b1
branch and edit the E
commit. After rebasing, commits E
and F
will be replaced by new commits E'
and F'
which have different commit SHAs. Therefore, the history in b1
will be different from history in b2
:
A---B---C master
\
D---E'---F' b1
A---B---C---D---E---F---G---H---I b2
So my question is: how to make sure that b2
follows b1
(automatically gets the same new commits as b1
) after rebasing b1
so that their respective histories stay coherent.
Upvotes: 0
Views: 114
Reputation: 3274
After your first rebase, it's not this:
A---B---C master
\
D---E'---F' b1
A---B---C---D---E---F---G---H---I b2
but rather this:
A---B---C master
\
D---Ea---F' b1
\
E---F---G---H---I b2
Here, Ea
means the amended E
commit.
And you want this, if I understand correctly:
A---B---C master
\
D---Ea---F' b1
\
G'---H'---I' b2
You can achieve this using an interactive rebase:
git checkout b2
git rebase -i b1
In the rebase edit you comment out the lines for commit E:
# pick df8efe6 E
pick a7fcbed G
pick 936b51a H
pick c77ca69 I
# ...
# Commands:
# p, pick = use commit
# ...
# If you remove a line here THAT COMMIT WILL BE LOST.
Responding to your comment, if you want to go all the way from the starting position (ABCDEFGHI) to the desired end positon:
git checkout b2
git rebase -i master
In the editor:
pick 1234567 D
edit a7fcbed E
pick 936b51a F
pick c77ca69 G
pick 1e8d614 H
pick 8daafa7 I
# ...
# p, pick = use commit
# e, edit = use commit, but stop for amending
# ...
When done, correct the b1
branch:
git checkout b1
git reset --hard 936b51a
You can also look at the other answers for inspiration. I'm not sure why you want to achieve all of this in a single command; you still have to amend commit E somewhere in the process. It saves you one interactive rebase to do it this way.
Upvotes: 1
Reputation: 60605
So my question is: how to make sure that b2 follows b1 (automatically gets the same new commits as b1).
You need to rewrite b2's history too, you're rebasing all of the master..b2
commits and rehanging the b1
and b2
labels.
Interactive rebase lets you execute arbitrary commands, notably including git branch
, after each step. So do
git rebase -i master b2
and in the pick list after the current b1 commit say exec git branch -f b1
.
You could automate this, pipe the picklist buffer through
while read command commit rest; do case $command in
*) printf %s\\n "$command${commit:+ $commit${rest:+ $rest}}" ;;&
pick) git for-each-ref refs/heads --points-at $commit \
--format='exec git branch -f %(refname:short)' ;;
esac; done
and it'll add the exec git branch -f
's after every pick for a branch tip. I've just built and tried this. Season to taste, of course.
If you're rebasing deep into a branched history, it's probably better to just construct the exact cherry-pick and branch sequence you want; rebase is a convenience command meant to ease a common task. Like all good tools it can be pushed a little farther than you might expect, but it's got its limits.
Upvotes: 0
Reputation: 52216
If you only have two branches, with a linear history as in your diagram :
I would rebase b2
instead of b1
:
A---B---C master
\
D---E'---F'
\
G'---H'---I' b2
and then update b1
:
git branch -f b1 F'
@Han-KwangNienhuys answer is perfectly valid, and you can apply it if you need to update b2
when b1
has already been rewritten.
Upvotes: 1