Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git rebase subtree

Tags:

git

branch

rebase

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.

like image 977
cd1 Avatar asked Jan 24 '13 14:01

cd1


3 Answers

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)
like image 71
Chronial Avatar answered Nov 05 '22 22:11

Chronial


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')
like image 4
carnicer Avatar answered Nov 06 '22 00:11

carnicer


   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
like image 2
VonC Avatar answered Nov 05 '22 22:11

VonC