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)
mainalready contains all changes fromv2(via merge or fast-forward)v3was branched fromv2, and any conflicts betweenv2andv3were 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
π 2. Rebase v3 onto main, Skipping v2 commits
This command means: "Take all commits from
v3after it diverged fromv18, and replay them on top ofmain."
This works because:
gitwill skip thev2changes (since they're now inmain)- Only
v3-specific commits are rebased ontomain - Previously resolved conflicts between v2 and v3 donβt come back
β Verifying the Rebase
After the rebase completes successfully
1. Confirm your branch base
Ensure
v3is now based off the latestmaintip and the history looks clean.
2. Double-check your changes
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.
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
After merging v2 into main
Now you run
After rebase
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
v3was branched offv2v2was squashed-merged intomain
Problem: You want to rebase v3 onto main, but git doesnβt recognize that the
changes from v2 are already on main - so it tries to reapply them. This
leads to conflicts for changes that are already present, forcing you to resolve
them again.
Step-by-Step Instructions
π 1. Find the common ancestor (fork point) between v2 and v3
Let' say this returns
abc123. Save that commit hash.
π 2. Start the rebase
This means: "Take all commits from
v3that came after it diverged fromv2, and replay them ontomain."
β οΈ 3. Resolve Conflicts
You might see
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
III. Continue the rebase
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
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
Make sure
v3is now based onmain.
Check that only your v3 changes are present
π 6. Push Safely
This safely updates the remote v3 branch with your new rebased commits.
Example
Before squash merge and rebase
After v2 is squash-merged into main (as S)
Sis a squashed version ofD+E, so Git sees no shared history.
Rebase v3 onto main, skipping over v2 commits
Now git replays only F and G on top of main.
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. π