git rebase a branch which is based on several branches

I have the following minimal example:

---M---A-----D1---D2---D3
   |\       /|
   | B1---B2 |
   \         / 
    -C1---C2-

Here,

  • M is master at the moment,
  • there are features
    • A,
    • B (consisting of B1, B2), and
    • C (consisting of C1, C2)

under development and on top of all those a feature

  • D (consisting of a series D1, D2, D3).

Now feature A was accepted to go into the master branch, therefore I have to rebase everything to A. This means, I want

---M---A-----------D1'---D2'---D3'
       |\         /|
       | B1'---B2' |
       \           / 
        -C1'---C2'-

Of course, it is easy to rebase B (B2) at A, and also C, alone/separately.

But how do I rebase my branch D (including B and C), keeping the merge D1 (as shown above)?

  • Rebasing a Detached HEAD against a master branch
  • Rebasing a branch including all its children
  • Git Merge vs Rebase - Resolving conflicts
  • How to deal with merges of topic branches after a squashed merge of parent
  • Is git pull --rebase safe if you are in a centralized model and never push force?
  • Cherrypicking versus Rebasing
  • Git rebase no argument version vs one argument version
  • Ran git rebase origin/develop and now I have a (develop|REBASE 1/2) - branch
  • 2 Solutions collect form web for “git rebase a branch which is based on several branches”

    You’ll want to pass the flag --preserve-merges into your rebase command. This looks like:

    git checkout D
    git rebase A --preserve-merges
    

    I’m far from an expert in the algorithm used by git to preserve merges, so I don’t know what sort of conflicts you may or may not run into, but this will cause git to attempt to reconstruct your merge commit D1 during the rebase.

    You could rebase branches B and C individually and then use grafts (or git-replace) to re-attach the new rebased branches to the merge commit. using grafts I would do

    # rebase the individual branches
    git rebase M B2 --onto A
    git rebase M C2 --onto A
    # replace old branch heads with rebased branch heads
    echo $(git rev-parse D1) $(git rev-parse B2') $(git rev-parse C2') > .git/info/grafts
    

    The grafts will make your git history appear as if D1 has parents B2′ and C2′. If you remove .git/info/grafts, D1 will have its original parents again.

    To make this permanent, use filter branch to rewrite your history:

    #make it permanent
    git filter-branch -f --tag-name-filter cat D1'
    

    A more elaborate explanation can be found in this answer on prepending the past to a git repository. It also explains how you should use git replace instead of grafts. I gave the example with grafts because I have a bit more experience with those.

    git replace would rather be along the lines:

    # rebase the individual branches
    git rebase M B2 --onto A
    git rebase M C2 --onto A
    # replace old branch heads with rebased branch heads
    git replace B2 B2'
    git replace C2 C2'
    #make it permanent
    git filter-branch -f --tag-name-filter cat D1'
    
    Git Baby is a git and github fan, let's start git clone.