TL;DR the command you want is:
git rebase --onto [the new HEAD base] [the old head base - check git log] [the-branch-to-rebase-from-one-base-to-another]
And my main motivation to putting it here is to easily find it again in the future as I always forget the syntax.
(This is a re-post from my old blog on drupalgardens, but it is still helpful.)
To make all of this simpler think of:
- Two red dishes on top of two blue dishes
- One yellow dish
- Those two red dishes on top of the one yellow dish
- Carefully go with the finger down to the bottom of the two red dishes, which is the first blue dish
- Take the two red dishes
- Transfer them over to the one yellow dish
That is what rebase --onto does:
git rebase --onto [yellow dish] [from: first blue dish] [the two red dishes]
Note: The following is meant for an intermediate audience that is familiar with general rebasing in GIT
It happened! A branch - you had based your work - on has diverged upstream, but you still have work in progress, which you want to preserve.
So it looks like this for you:
"Your Commit A" - "Your Commit B" - "Upstream Commit: This is the last upstream commit" - "Upstream Commit: This is another upstream commit"
But now the branch you based your work on squashed their work together or removed a file due to privacy concerns and now upstream has:
"Upstream Commit: This is the last upstream commit and this is another upstream commit together in one."
So all that you want is to move your two commits onto the new base.
The cherry-pick way
Lets assume your branch is called 'work' and you based your work upon 'upstream/develop'.
What you could do is to checkout the develop branch, reset it to the new version and cherry-pick your two commits:
# Reset the develop branch git checkout develop git reset --hard upstream/develop # Checkout a new branch git checkout -b work2
# Now find the two commit IDs git log work # Cherry-pick those git cherry-pick 28d3fc5 git cherry-pick a67b2c # And reset your work branch to the new work git checkout work git reset --hard work2 git branch -d work2
(Note: cherry-pick now also supports commit ranges, so this can be further simplified.)
The rebase way
Usually git knows - when you call rebase - how to rebase as it has a common history. It gets only problematic if the history diverges as happened above.
In that case you will need to tell git to rebase which on what.
So when you normally use:
git fetch upstream git rebase upstream/develop
You would now use with the new base:
git fetch upstream git rebase --onto upstream/develop [old base] work
and thats it.
To get the old base just use the commit ID of the first commit that does not contain changes you made.
A real drupal.org contribution example
This is super helpful when having to juggle several branches for e.g. the Drupal 8 core queue that depend on each other.
Lets say you worked on issue-X and issue-Y and issue-Y is built on top of issue-X.
Now you work mostly on issue-Y, but now you work more on issue-X and for your personal merit decide to make the history a little nicer, so now issue-X has diverted.
But a simple:
git checkout issue-Y git rebase --onto issue-X [old base of X in issue-Y] issue-Y
can again save the day. The same is sometimes needed when the branches are based of of different core versions.