简体   繁体   中英

Git rebase subtree

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.

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)
   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

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')

If you want to have this result

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

You should do this:

git rebase --onto master X WIP1         /* move WIP1 on master */
git rebase --onto WIP1 WIP2~3 WIP2      /* move WIP2 on WIP1 */
git rebase --onto WIP2 WIP3~3 WIP3      /* move WIP3 on WIP2 */
git reset --hard WIP3                   /* move master index to WIP3 */

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM