Rebase only part of a branch

I've got two branches (and master). Branch 2 is based on Branch 1 is based on master. I've submitted Branch 1 for review, it had some changes, I rebased some of those changes into history and merged the result into master.

Now I need to rebase Branch 2 on top of master to prepare it for review/merge.

The problem is that Branch 2 still contains the original commits of Branch 1, which don't exist anymore, so git gets confused. I tried rebase -i to drop the original commits of Branch 1, but the commits of Branch 2 don't base on top of master-before-branch-1.

What I need to do is take branch 2, drop some commits, and rebase just the remaining commits on top of master in a single operation. But I only know how to do these two operations in two distinct steps.

How can I rebase part of my branch onto another branch, dropping all commits that are not in common ancestry, except the ones I specify (e.g. from HEAD~2 up)?

Here's the current state:

master                     new branch 1
- - - - - - - - - - - | - - - - - - - - -
\
\   branch 1
\ _ _ _ _ _ _ _
\
\     branch 2
\ _ _ _ _ _ _ _

What I want to end up with:

master            new branch 1
- - - - - - - | - - - - - - - - - -
\
\
\
\    branch 2
- - - - - - - - -
20662 次浏览

The solution is considerably simpler than I expected. It turns out that you can supply -i to a much larger variety of rebase commands (I thought it was only for rebasing a branch to itself for changing history). So I simply ran git rebase -i master and dropped those extra commits.

The actual command would be:

git rebase --onto newbranch1 branch1 branch2

That will replay on top of new_branch1 all commits after branch1 up to branch2 HEAD.

As Joshua Goldberg puts it in the comments:

 git rebase --onto <place-to-put-it> <last-change-that-should-NOT-move> <change to move>

As Denis Sivtsov illustrates in the comments:

If you need only replay the last commit only from branch, in this case work:

git rebase --onto newbranch1 HEAD~1
git rebase --onto master HEAD~2
  • master - the branch you're rebasing onto
  • 2 - the last n commits from the current branch you need rebased

Source

I find your ASCII graph a bit ambiguous, as branches don't really represent a range of commits - a branch points to a specific commit. I take it you mean something like

H (new-branch-1)
G
| F (HEAD -> branch-2)
| E
| D (branch-1)
| C
|/
B (master)
A

in which case the goal, and result of VonC's answer git rebase --onto new-branch-1 branch-1 branch-2, is

F' (HEAD -> branch-2)
E'
H (new-branch-1)
G
| D (branch-1)
| C
|/
B (master)
A

But your answer git rebase -i master doesn't make sense. Surely you would mean git rebase -i new-branch-1 and in the editor write

drop C
drop D
pick E
pick F

which would achieve the goal above.

(Your answer would result in):

F' (HEAD -> branch-2)
E'
| H (new-branch-1)
| G
|/
| D (branch-1)
| C
|/
B (master)
A

As answered, you can do this using --onto.

I find the git rebase --onto syntax quite confusing. So I created this script for rebasing "nested" branches: Github Gist

In your example, you would call:

moveBranch newBranch2 from newBranch1 to master

#!/bin/bash


## Places a branch to a new base.
## Useful when splitting a long branch to multiple pull requests.
##
##   ---+--------master
##       \
##         --- A ---- B
##
## git-moveBranch.sh B from A to master
##
##   ---+-------- master ---- B
##       \
##         --- A




function printUsageAndExit() {
echo "Usage: moveBranch <branch> from <previous-base> to <new-base>";
exit 1;
}


if [ 5 != $# ] ; then printUsageAndExit; fi
if [ "$2" != "from" ] ; then printUsageAndExit; fi
if [ "$4" != "to" ] ; then printUsageAndExit; fi


WHAT="$1"
FROM="$3"
ONTO="$5"


echo "Running:   git rebase —-onto=\"$ONTO\" \"$FROM\" \"$WHAT\""
# git rebase —-onto <place-to-put-it> <last-change-that-should-NOT-move> <change to move>
git rebase --onto "$ONTO" "$FROM" "$WHAT"