Skip to content

Git: Rebase with dependent branches

Rebasing a branch on another is very common...
Yet by default, the dependent branches of the rebased branch will remain where they are and not follow the rebase.
This notes shows how to deal with those dependent branches.

The problem

At some point in time, you will end up with a situation as described below where you have a branch (main), with a child branch (feature/foo) with a child-child branch (feature/bar).

graph LR
  A[A] --> B[B];
  B[B] --> C[C];
  C[C] --> D[D];
  D[D] --> E[E];
  E[E] -.- F{{main}};
  C[C] --> G[G];
  G[G] --> H[H];
  H[H] --> I[I];
  I[I] -.- J{{feature/foo}};
  H[H] --> K[K];
  K[K] --> L[L];
  L[L] -.- M{{feature/bar}};
  style F fill:#000,stroke:#000,color:#fff;
  style J fill:#000,stroke:#000,color:#fff;
  style M fill:#000,stroke:#000,color:#fff;

Using rebase here is complicated.

Start by rebasing the first child (in this case feature/foo):

git checkout feature/foo
git rebase main

You'll end up with this:

graph LR
  A[A] --> B[B];
  B[B] --> C[C];
  C[C] --> D[D];
  D[D] --> E[E];
  E[E] -.- F{{main}};
  E[E] --> GG[G'];
  GG[G'] --> HH[H'];
  HH[H'] --> II[I'];
  II[I'] -.- JJ{{feature/foo}};
  C[C] --> G[G];
  G[G] --> H[H];
  H[H] --> K[K];
  K[K] --> L[L];
  L[L] -.- M{{feature/bar}};
  style F fill:#000,stroke:#000,color:#fff;
  style JJ fill:#000,stroke:#000,color:#fff;
  style M fill:#000,stroke:#000,color:#fff;

As you can see, feature/foo has been rebased on main... BUT feature/bar has been left where it was.

WRONG

If you were to run a rebase with feature/bar like this:

git checkout feature/bar
git rebase main

You end up with something like this:

graph LR
  A[A] --> B[B];
  B[B] --> C[C];
  C[C] --> D[D];
  D[D] --> E[E];
  E[E] -.- F{{main}};
  E[E] --> GG[G'];
  GG[G'] --> HH[H'];
  HH[H'] --> II[I'];
  II[I'] -.- JJ{{feature/foo}};
  E[E] --> G[G''];
  G[G''] --> H[H''];
  H[H''] --> K[K''];
  K[K''] --> L[L''];
  L[L''] -.- M{{feature/bar}};
  style F fill:#000,stroke:#000,color:#fff;
  style JJ fill:#000,stroke:#000,color:#fff;
  style M fill:#000,stroke:#000,color:#fff;

NOT WHAT YOU'RE LOOKING FOR

Could get worse... Trying to rebase on feature/foo will result in this (after dealing with a lot of conflicts):

git checkout feature/bar
git rebase feature/foo
graph LR
  A[A] --> B[B];
  B[B] --> C[C];
  C[C] --> D[D];
  D[D] --> E[E];
  E[E] -.- F{{main}};
  E[E] --> GG[G'];
  GG[G'] --> HH[H'];
  HH[H'] --> II[I'];
  II[I'] -.- JJ{{feature/foo}};
  II[I'] --> G[G''];
  G[G''] --> H[H''];
  H[H''] --> K[K''];
  K[K''] --> L[L''];
  L[L''] -.- M{{feature/bar}};
  style F fill:#000,stroke:#000,color:#fff;
  style JJ fill:#000,stroke:#000,color:#fff;
  style M fill:#000,stroke:#000,color:#fff;

As you can see, commits G & H have been redone even though they ought to be skipped. STILL NOT WHAT YOU'RE LOOKING FOR

screaming.gif

THE SOLUTION

The solution is to use the --onto feature of git rebase

git checkout feature/bar
git rebase --onto {hash_of_H'} feature/foo@{1}

The @{1} in the above code means: the latest known state of feature/foo before the rebase.

graph LR
  A[A] --> B[B];
  B[B] --> C[C];
  C[C] --> D[D];
  D[D] --> E[E];
  E[E] -.- F{{main}};
  E[E] --> GG[G'];
  GG[G'] --> HH[H'];
  HH[H'] --> II[I'];
  II[I'] -.- JJ{{feature/foo}};
  HH[H'] --> K[K'];
  K[K'] --> L[L'];
  L[L'] -.- M{{feature/bar}};
  style F fill:#000,stroke:#000,color:#fff;
  style JJ fill:#000,stroke:#000,color:#fff;
  style M fill:#000,stroke:#000,color:#fff;

PERFECT !

Note that, if feature/bar had been branched on the last commit of feature/foo:

graph LR
  A[A] --> B[B];
  B[B] --> C[C];
  C[C] --> D[D];
  D[D] --> E[E];
  E[E] -.- F{{main}};
  C[C] --> G[G];
  G[G] --> H[H];
  H[H] -.- J{{feature/foo}};
  H[H] --> K[K];
  K[K] --> L[L];
  L[L] -.- M{{feature/bar}};
  style F fill:#000,stroke:#000,color:#fff;
  style J fill:#000,stroke:#000,color:#fff;
  style M fill:#000,stroke:#000,color:#fff;

The command would have been more simple:

git checkout feature/foo
git rebase main
git checkout feature/bar
git rebase --onto feature/foo feature/foo@{1}
graph LR
  A[A] --> B[B];
  B[B] --> C[C];
  C[C] --> D[D];
  D[D] --> E[E];
  E[E] -.- F{{main}};
  E[E] --> GG[G'];
  GG[G'] --> HH[H'];
  HH[H'] -.- JJ{{feature/foo}};
  HH[H'] --> K[K'];
  K[K'] --> L[L'];
  L[L'] -.- M{{feature/bar}};
  style F fill:#000,stroke:#000,color:#fff;
  style JJ fill:#000,stroke:#000,color:#fff;
  style M fill:#000,stroke:#000,color:#fff;

Comments