73

I'm trying to help out a coworker who accidentally created one feature branch from another feature branch, rather than creating the second one from master. Here is essentially what we have now…

Master ---A---B---C \ Foo E---F---F---H \ Bar J---K---L---M 

And here is what we'd like to have…

Master ---A---B---C |\ Foo | E---F---F---H | Bar J---K---L---M 

One way I thought of would be to create FooV2 and BarV2 branches, and cherry-pick the individual commits into the appropriate V2 branches. But I'm curious, is there a better way to handle this situation?

3 Answers 3

157

For a more general answer that will help us understand things a bit better than just "run this command", we need a larger example. So, let's pretend you're actually in this situation:

---A---B---C <= Master \ E---F---F---H <= Foo \ J---K---L---M <= Bar \ N---O---P---Q <= Baz 

And here is what we'd like to have…

---A---B---C <= Master |\ | E---F---F---H <= Foo |\ | J---K---L---M <= Bar \ N---O---P---Q <= Baz 

Thankfully, Git has a solution for us in the options to the rebase command!

git rebase --onto [newParent] [oldParent] [branchToMove] 

What this means can be broken down into parts:

  1. rebase - Change the parents of something
  2. --onto - This is the flag that tells git to use this alternate rebase syntax
  3. newParent - This is the branch that the branch you are rebasing will have as it's parent
  4. oldParent - This is the branch that the branch you are rebasing currently has as it's parent
  5. branchToMove - This is the branch that you are moving (rebasing)

The bit about "old parent branch" can be a little confusing, but what it's really doing is it's saying "ignore the changes from my old parent when doing the rebase". The oldParent is how you define where the branch you are moving (ie. branchToMove) starts.

So, what are the commands to execute in our situation?

git rebase --onto Master Bar Baz git rebase --onto Master Foo Bar 

Note that the order of these commands matter because we need to pull each branch off of the end of the "branch chain".

Sign up to request clarification or add additional context in comments.

4 Comments

Takes all the magic out of it, in the best sense possible. Just used this to un-fubar(baz) a feature branch where I had done some work on Foo, then some commits towards Bar, then some more Foo, but all on the same commit line. #fail
@BenMosher: I realize this was a long time ago, but did you actually succeed or fail in using this approach to turn a foo-bar-foo into a clean foo and bar?
@Superole: IIRC, I did. have used --onto several times hence as well. 👍🏻
This seems to achieve the same result as cherry pick.Can someone opine on pros and cons of cherry pick vs rebase --onto?
21

It looks to me like you could:

git checkout J git rebase master 

Edit:

I tried what I suggested and it doesn't work. knittl's suggestion doesn't work (on my box). Here's what did work for me:

git rebase --onto master foo bar 

3 Comments

Thanks a lot; the edited version worked great! In case anyone needs some more details around this, there is a "More Interesting Rebases" section of Pro Git book that covers almost this exact example: git-scm.com/book/en/Git-Branching-Rebasing
git rebase --onto master foo bar means: Take "bar" without "foo" and place it onto master
@schoetbi: Your comment is the VIP in this whole thread. Before seeing it, I did not understand any of the answers.
7

You can rebase your Bar branch onto master:

git rebase --onto C H M 

If some patches conflict, you have to resolve them manually (but you also have to do that when cherry picking). Word of caution: don't rebase when the history has been published already.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.