Last updated: 2026-05-18

Rebasing

Rebasing is Git's second major tool for integrating changes from one branch into another — the alternative to merging. While merging preserves the complete history of how work diverged and came back together, rebasing rewrites history to tell a cleaner, more linear story. Understanding when and how to use each is one of the marks of Git fluency.


The Problem Rebasing Solves

When two branches diverge and you want to integrate their work, merging is the straightforward approach — it creates a merge commit that joins both histories. The result is accurate but can leave a complex, non-linear history that is harder to read.

Before merge:
      A -- B -- C  (main)
           \
            D -- E  (feature)

After merge:
      A -- B -- C -- M  (main, M = merge commit)
           \        /
            D ------E

Rebasing offers an alternative: instead of creating a merge commit, it takes the commits from your branch and replays them on top of another branch as if the work had started there all along.

After rebase:
      A -- B -- C -- D' -- E'  (main, linear history)

The D' and E' are new commits — they contain the same changes as D and E but are built on top of C rather than B. The result is a perfectly linear history with no merge commit.


Basic Rebasing — git rebase

The standard rebase command takes the commits on your current branch and replays them onto a target branch:

git switch feature
git rebase main

What Git does step by step:

  1. Finds the common ancestor of feature and main (commit B in the example above).
  2. Saves the changes introduced by each commit on feature to temporary patch files.
  3. Resets feature to point to the tip of main.
  4. Replays each saved patch, creating new commits on top of main.

After the rebase, a fast-forward merge on main brings it up to date:

git switch main
git merge feature      # Fast-forward — no merge commit needed

The end result is identical content to a merge, but with a perfectly straight-line history.


Rebase vs. Merge — The Core Trade-off

Both commands integrate changes. The difference is in what they do to history.

MergeRebase
HistoryPreserves the true divergence and joining of branchesRewrites history to appear linear
Merge commitCreates oneDoes not create one
ReadabilityCan become complex in active repositoriesEasier to read with git log
SafetyAlways safe — never rewrites existing commitsCan cause serious problems if misused on shared branches
Use caseIntegrating completed work into a shared branchKeeping a feature branch up to date with main

The end snapshot — the actual state of the code — is identical after either operation. Only the history differs.


Updating a Feature Branch with Rebase

One of the most common uses of rebase is keeping a long-running feature branch current with main as other work lands there. Rather than creating a merge commit every time you pull in upstream changes, you rebase your branch on top of the updated main:

git switch feature
git rebase main

This replays your feature branch's commits on top of the latest main, keeping your branch ahead of main without introducing merge commits into the feature's history. When the feature is eventually merged, the history reads cleanly.


Interactive Rebase — git rebase -i

Interactive rebase is where rebasing becomes genuinely powerful as an editing tool. It allows you to rewrite, reorder, squash, split, or drop commits before sharing them — cleaning up your local history before it becomes permanent.

git rebase -i HEAD~3    # Interactively edit the last 3 commits

This opens an editor showing the commits in order (oldest first):

pick f7f3f6d Add login form component
pick 310154e Add form validation
pick a5f4a0d Fix typo in form label

# Commands:
# p, pick   = use commit as-is
# r, reword = use commit, but edit the commit message
# e, edit   = use commit, but stop to amend it
# s, squash = meld into the previous commit
# f, fixup  = like squash, but discard this commit's message
# d, drop   = remove commit entirely

You edit the list, save, and Git executes your instructions.

Reword a commit message

Change pick to reword (or r):

reword f7f3f6d Add login form component
pick 310154e Add form validation

Git replays the commits and pauses at each reword to let you edit the message.

Squash commits together

Change pick to squash (or s) on the commits you want folded into the one above them:

pick f7f3f6d Add login form component
squash 310154e Add form validation
squash a5f4a0d Fix typo in form label

Git combines all three into a single commit and opens an editor for you to write a combined message. Use fixup instead of squash to silently discard the message of the squashed commit.

Drop a commit entirely

Change pick to drop (or d), or simply delete the line:

pick f7f3f6d Add login form component
drop 310154e Add form validation

The dropped commit is removed from history as if it never existed.

Reorder commits

Rearrange the lines in the editor. Git replays commits in the order they appear — top to bottom:

pick 310154e Add form validation
pick f7f3f6d Add login form component

Rebasing onto a Different Base — --onto

The --onto flag provides precise control when you want to rebase a branch onto a branch other than the one it diverged from. This is useful when a topic branch was created off another topic branch and you want to move just part of the work.

git rebase --onto main server client

This says: take the commits on client that are not on server, and replay them onto main. The server branch is untouched; only client is moved.


The Golden Rule of Rebasing

Never rebase commits that exist outside your local repository and that others may have based work on.

This is the single most important rule in Git.

When you rebase, you are abandoning existing commits and creating new ones that are similar but different — they have different SHA-1 hashes. If you have already pushed those commits to a shared remote and someone else has pulled them and built work on top of them, rebasing and force-pushing replaces the commits they are working from. Their local history no longer matches the remote, creating a confusing and painful situation that is difficult to untangle.

The practical consequence:

  • Rebase freely on local commits that have never been pushed. Clean up your history before you share it.
  • Never rebase shared branches — branches that others are actively working from.
  • If you rebase a branch and need to push it, you must force-push (git push --force). Force-pushing on a shared branch is a warning sign that you may be breaking collaborators' work.

git pull --rebase

By default, git pull performs a fetch followed by a merge. If you prefer a clean linear history, you can pull with rebase instead:

git pull --rebase

This fetches the remote changes and rebases your local commits on top of them rather than creating a merge commit. To make this the default behaviour globally:

git config --global pull.rebase true

When to Use Rebase vs. Merge

SituationRecommended
Keeping a feature branch current with mainRebase
Cleaning up local commits before pushingInteractive rebase
Integrating a completed feature into mainMerge (or rebase + fast-forward merge)
Shared branch that others are working fromAlways merge — never rebase
Preparing a clean series of patches to submitRebase
Preserving the exact history of how work happenedMerge

The simplest guideline: rebase local changes before pushing to clean up your work; never rebase anything you have already pushed to a shared branch.


Summary

CommandWhat It Does
git rebase <branch>Replays current branch commits on top of <branch>
git rebase -i HEAD~nInteractively edits the last n commits
git rebase --onto <base> <upstream> <branch>Replays <branch> commits (relative to <upstream>) onto <base>
git pull --rebaseFetches and rebases instead of fetching and merging
git rebase --continueContinues a rebase after resolving conflicts
git rebase --abortCancels a rebase and restores the original state