← All posts

Tutorial Git

How to Rebase a Feature Branch onto Main Without Losing Commits

Learn how to safely rebase a feature branch onto main, resolve conflicts, and keep every commit intact.

Difficulty Intermediate Time 20 min
A feature branch line smoothly rejoining the tip of the main branch, representing a clean git rebase workflow.

Rebasing a feature branch onto main replays your commits on top of the latest upstream work — giving you a clean, linear history without a messy merge commit. By the end of this guide your feature branch will sit cleanly on top of the current main tip, every one of your commits intact and ready to merge or open a pull request.

Prerequisites

  • Git 2.23 or later installed (git --version to check)
  • A local clone of the repository with both main and your feature branch present
  • No uncommitted changes on your feature branch (or they are stashed)
  • You understand that rebasing rewrites commit SHAs — never rebase a branch others are actively working on

Step 1 — Stash Any Uncommitted Work

Rebase requires a clean working tree. If you have changes in progress that are not yet committed, stash them first so nothing gets lost.

git stash push -m "wip: before rebase onto main"

You will restore these at the end with git stash pop. If your tree is already clean, skip this step.

Step 2 — Fetch the Latest Changes from Origin

Before you rebase, pull down everything that has landed on main since you branched off. Rebasing against a stale local main defeats the purpose.

git fetch origin

This updates origin/main (and all other remote refs) without touching your local branches. You are not switching branches yet.

Step 3 — Check Out Your Feature Branch

Make sure you are on the branch you want to rebase, not on main.

git checkout feature/my-feature

Confirm with git branch — the asterisk should sit next to your feature branch name.

Step 4 — Run the Rebase

Now replay your commits on top of the latest origin/main. Using origin/main directly (rather than local main) means you are working against the freshest upstream state.

git rebase origin/main

Git walks through each of your commits in order, pausing only if a conflict is detected. If there are no conflicts, the rebase completes immediately and you can jump to Step 7.

Step 5 — Resolve Conflicts One Commit at a Time

If Git pauses with a conflict message, do not panic. Rebase surfaces conflicts commit-by-commit, so you fix a small, focused diff each time rather than one giant merge conflict.

  1. Open the conflicted file(s). Look for the standard conflict markers (<<<<<<<, =======, >>>>>>>) and edit the file to the correct final state.
  2. Stage the resolved file:git add path/to/resolved-file.ext
  3. Continue the rebase — do NOT use git commit here:git rebase --continue

Repeat for every paused commit. At any point you can bail out completely and return to your pre-rebase state with:

git rebase --abort

Gotcha: Running git commit instead of git rebase --continue is the most common mistake. It creates a stray commit in the middle of your history. Always use --continue after staging resolved files.

Step 6 — Verify the Result Locally

Before you push, confirm the commit log looks exactly the way you expect. Your commits should appear on top of the latest main commits with no duplicates.

git log --oneline --graph origin/main..HEAD

This shows only the commits that are on your branch but not yet on origin/main. Cross-check that the count matches the number of commits you wrote. If anything looks off, use git reflog to find the pre-rebase state and recover it.

Step 7 — Force-Push the Rebased Branch

Because rebasing rewrites commit SHAs, a normal git push will be rejected. You must force-push. Use the safer --force-with-lease flag instead of -f — it refuses to overwrite if someone else has pushed to the same branch since your last fetch, protecting against accidental data loss.

git push origin feature/my-feature --force-with-lease

Important: Only force-push branches that belong solely to you. If teammates have checked out your feature branch and based work on it, force-pushing will break their local history. Coordinate before pushing.

Step 8 — Restore Stashed Work

If you stashed changes in Step 1, bring them back now.

git stash pop

If the stash conflicts with the rebased branch state, Git will flag it. Resolve those conflicts the same way you resolved rebase conflicts: edit, git add, done.

Wrap-up

You now have a clean, linear history with your feature branch sitting directly on top of the current main tip — all your commits intact. The full flow in one mental model: fetch → checkout feature → rebase onto origin/main → fix any conflicts commit-by-commit → verify the log → force-push with lease. Make this part of your daily Git workflow and pull requests become trivial to review: no merge noise, no diverged histories, just your changes on top of the latest code.