Skip to content

Git Rebase: Stacked Branches without Re-resolving Conflicts

This is a common situation in Git when working with stacked feature branches. You want to rebase a branch (say v3) onto main after a previous branch (v2) has already been merged into main, without re-resolving conflicts that were already handled between v2 and v3.


Scenario 1: v2 was merged normally (not squashed)

  • main already contains all changes from v2 (via merge or fast-forward)
  • v3 was branched from v2, and any conflicts between v2 and v3 were already resolved

Problem: You want to rebase v3 directly onto main, but when you do, you are asked by git to resolve conflicts that you've already resolved between v2 and v3.


Step-by-Step Instructions

🧭 1. Ensure you're on v3

git checkout v3

πŸ”€ 2. Rebase v3 onto main, Skipping v2 commits

git rebase --onto main v2

This command means: "Take all commits from v3 after it diverged from v18, and replay them on top of main."

This works because:

  • git will skip the v2 changes (since they're now inmain)
  • Only v3-specific commits are rebased onto main
  • Previously resolved conflicts between v2 and v3 don’t come back

βœ… Verifying the Rebase

After the rebase completes successfully

1. Confirm your branch base

git log --oneline --graph --decorate --all

Ensure v3 is now based off the latest main tip and the history looks clean.

2. Double-check your changes

git diff main..v3

You should only see changes that were unique to v3, not v2

3. Push safely

This safely updates the remote v3 branch with your new rebased commits.

git push --force-with-lease origin v3

Why --force-with-lease?

It’s safer than plain --force because it checks if the remote branch has changed since your last fetch If someone else has pushed to the branch in the meantime, it will refuse to overwrite their work


Example

Before rebase

       A---B---C  (main)
            \
             D---E  (v2)
                  \
                   F---G  (v3)

After merging v2 into main

       A---B---C---'D---E'  (main)
            \
             D---E          (v2)
                  \
                   F---G    (v3)

Now you run

git checkout v3
git rebase --onto main v3

After rebase

       A---B---C---'D---E'---'F---G'  (main)
                                   \
                                    (v3 rebased)

You now have a clean history, no redundant conflict resolution and v3 is based on the lates main.


Scenario 2: Rebase v3 onto main after v2 was squashed-merged

  • v3 was branched off v2
  • v2 was squashed-merged into main
  • You want to rebase v3 onto main, without reapplying or conflicting with the changes already merged from v2

Step-by-Step Instructions

πŸ” 1. Find the common ancestor (fork point) between v2 and v3

git merge-base v2 v3

Let' say this returns abc123. Save that commit hash.

πŸš€ 2. Start the rebase

git checkout v3
git rebase --onto main abc123

This means: "Take all commits from v3 that came after it diverged from v2, and replay them onto main."

⚠️ 3. Resolve Conflicts

You might see

CONFLICT (content): Merge conflict in some/file
I. Manually resolve the conflict

Open the conflicting files using your IDE and fix them.

Since you've likely already resolved this conflict before (in v3 when building on top of v2), reapply the same resolution logic.

II. Stage the Resolved Files
git add path/to/file
III. Continue the rebase
git rebase --continue

git will replay the next commit in v3. Repeat step I - III for each commit that causes a conflict.

❌ 4. If the rebase becomes too messy

You can cancel and go back to how v3 was before the rebase

git rebase --abort

Enable git conflict memory

This tells Git to remember how you resolved a conflict, so if the same one comes up in the future, Git can auto-resolve it for you.

git config --global rerere.enabled true

βœ… 5. Verify Rebase

Check the commit graph
git log --oneline --graph --decorate --all

Make sure v3 is now based on main.

Check that only your v3 changes are present
git diff main..v3

πŸ›Ÿ 6. Push Safely

This safely updates the remote v3 branch with your new rebased commits.

git push --force-with-lease origin v3

Example

Before squash merge and rebase

A---B---C                 (main)
         \
          D---E           (v2)
               \
                F---G     (v3)

After v2 is squash-merged into main (as S)

A---B---'C---S'           (main)
         \
          D---E           (v2)
               \
                F---G     (v3)

S is a squashed version of D + E, so Git sees no shared history.

Rebase v3 onto main, skipping over v2 commits

git rebase --onto main $(git merge-base v2 v3)

Now git replays only F and G on top of main.

A---B---C---S---'F---G'   (main)
                       \
                        (v3 rebased)

I’ve run into this exact situation enough times β€” rebasing stacked branches after a squash merge β€” that I finally decided to write it down. It always trips me up when I forget which branch to use with --onto, or when conflicts pop up that I thought I already resolved.

Hopefully, this note helps future me (and maybe you too) avoid the same confusion. git can be tricky, but with a clear mental model and the right steps, it's not as bad as it feels in the moment.

Until the next rebase panic. πŸ˜