13

Suppose I have the following scenario:

 o (master) / o--o (WIP1) / / o--o--o--o--o--o (WIP2) (X) \ o--o (WIP3) 

Is there a git command which creates a new branch so that it contains the subtree after branch X? I want to perform a "large rebase", I want the three WIP branches rebased on master.

I know I can do that with some Bash scripting but I'd like to know how to do that using a git command.

2

4 Answers 4

16

There is no single git command for that. You will have to do some manual work. In your situation:

 o (master) / o--o (WIP1) / / X--o--o--B--o--o (WIP2) \ o--o (WIP3) 

You first rebase WIP1 onto master:

git rebase --onto master X WIP1 

which will lead to this:

 o--o (WIP1) (master) / o--o--o--B’ / / X--o--o--B--o--o (WIP2) \ o--o (WIP3) 

If you now run git rebase --onto master X WIP2, you get this structure:

 o--o (WIP1) (master) / o--o--o--B’ / \ / o--o--B’’--o--o (WIP2) / X--o--o--B--o--o (WIP3) 

This is probably not what you want, so now you should rebase WIP2 and WIP3 on B’:

git rebase --onto B’ B WIP2 git rebase --onto B’ B WIP3 

which will lead to this:

 o--o (WIP1) (master) / o--X--o--o--B’--o--o (WIP2) \ o--o (WIP3) 
Sign up to request clarification or add additional context in comments.

3 Comments

Yes, saw your edit just prior to submitting, but by then I had already drawn all that ascii-art ^^
I had to upvote for the step-by-step ascii charts. Good answer. Why didn't either of the answers ever get the check mark, @cd1?
What about rebase in a more complex way with merge commits?
3

I have flagged this question as duplicate. I will write what I explained the other answer but using your example.

The approach that I use for such use cases is to merge all the branches to be moved into 1 common artificial node, and then use the rebase command with the --preserve-merges option. Merging all the branches will expose 1 end-point that will be used as the final input parameter for rebase --onto. The start point is usually obvious, the origin of the subtree to move.

When merging to get the subtree endpoint, conflicts should be explicity avoided. Therefore the merge commands shall be instructed to solve them automatically with the -Xours option. The merging result is not important since these artificial merge nodes will be discarded after the rebase.

It is recommended to create a new branch pack in order to not lose the original references. In the example above the following commands would be performed:

$ git checkout -b pack WIP1 # create new branch at 'WIP1' $ git merge -s recursive -Xours WIP2 # merges WIP2 into pack (node p2) $ git merge -s recursive -Xours WIP3 # merges WIP3 into pack 

Below can be seen what the tree would become. Two new artificial nodes p2 and pack have been created with the merges.

 o (master) / / (WIP1) (p2) / o-----o-----o----o (pack) / / / / o--o--o--o-----o-----o / (WIP2) (X) \ / o------------o (WIP3) 

Now it's time to rebase. Since now there is a common endpoint for all the branches (pack), it's easy to move the whole subtree with:

$ git rebase --preserve-merges --onto master X pack 

Which produces this:

 (WIP1') (p2') o-----o-----o----o (pack') (master) / / / o----o----o--o--o-----o-----o / (WIP2') (X) \ / o------------o (WIP3') 

Now it's time to rearrange the references. I don't know why, in some cases the references are moved and in others are not. Type this for each reference WIP1, WIP2, WIP3 or whatever you need:

$ git checkout WIP1 $ git reset --hard <WIP1' hash> 

And finally, get rid of the artificial commits that were created for generating a common subtree end node.

$ git branch -D pack $ git branch -D p2 # if there is any 

So the final tree would be:

 (WIP1') o-----o (master) / o----o----o--o--o-----o-----o (WIP2') (X) \ o------------o (WIP3') 

1 Comment

It's possible to streamline this a little bit by using git rebase -i --rebase-merges, so that you can omit the dummy merge commit at the end of the process. Discussed here: stackoverflow.com/a/69396585/344643
3

With the git-branchless suite of tools, you can directly rebase subtrees:

$ git move -b WIP1 -d master 

Disclaimer: I'm the author.


If you want to stick with base Git, you can use the approach in this answer, but with the new --rebase-merges option. Make a dummy merge-commit:

$ git checkout WIP1 $ git merge WIP2 WIP3 ...add all conflicts and resolve... 

And then perform the rebase interactively. The generated rebase plan will be what you want, except that you should remove the last merge command:

$ git rebase -i master --rebase-merges ...example editor contents below... label onto # Branch WIP1 reset abc123 pick abc456 pick abc789 label abcdef reset onto pick bcd123 pick bcd456 pick bcd789 ## REMOVE THIS LINE: # merge -C abcdef 

Comments

1
 o (master) / o--o (WIP1) / / o--p--p--o--o--o (WIP2) (X) (Y) \ o--o (WIP3) 

This should be a rebase --onto (you can see one example in "How to move certain commits to another branch in git?"):

 git rebase --onto master X WIP1 git rebase --onto master X WIP2 git rebase --onto master X WIP3 

From Chronial's test, that would give:

 p'--p'--o--o (WIP2) / o-----o-----p--p--o--o--o (WIP1) (X) (master) (Y') \ p''--p''--o--o (WIP3) 

So the first rebase is ok, but you need to get Y SHA, and:

 git rebase --onto Y' Y WIP2 git rebase --onto Y' Y WIP3 

5 Comments

This will create three branches that diverge off of master, won’t it? Or will this move the structure behind X on top of master (so that the branches divert later)?
@Chronial yes, there is that risk, especially considering a rebase won't replay a commit if that the same content is detected on the destination branch. I'll have to test it out.
just tested it, and they diverge :/
@Chronial Meaning the first two commits after X are only present in WIP1? And WIP2,3 start directly from master?
No, the first commits after X are cloned 3 times – once for each WIP.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.