Rewriting Local History with Mercurial Rebase
Move local Mercurial changesets onto a new base with hg rebase: prerequisites, conflict handling, phase checks, and how to recover using obsolete revisions.
12 Jan 2026, 11:48 UTC

When working on a feature branch in Mercurial, the main branch often moves forward while you are coding. Merging creates a merge changeset that clutters history with cross-branch join points, making the project's evolution harder to read.
The rebase command lifts a series of local changesets and replays them on top of a different base, rewriting history as if you started from the latest version of the code. It must only be used on commits that have not been pushed to a public repository.
Prerequisites
- The rebase extension is enabled in your
.hgrcfile: addrebase =under[extensions]. In recent Mercurial versions it ships bundled, but it still must be enabled explicitly. - The changesets you intend to move are in the
draftorsecretphase. Mercurial refuses to rebasepublicchangesets by default, protecting shared history. - Your working directory is clean. Commit or shelve uncommitted changes first.
Check phases before starting (run in the repository root; no special permissions needed):
hg phases -r .If the output shows public, stop: those changesets have been published and must not be rebased.
Procedure for Rebasing a Local Branch
1. Identify the target
Decide where your changes should land, typically the tip of the main branch:
hg log -r main -l 12. Execute the rebase
Move your current changesets onto the main branch tip:
hg rebase -d mainTo move a specific range instead, use -s for the source revision: hg rebase -s <first-rev> -d main.
3. Resolve conflicts if they occur
If your changes overlap with main, Mercurial pauses and leaves the working directory in a merge state:
- Open each conflicted file and resolve the conflict markers.
- Mark files resolved:
hg resolve -m <file>. - Continue:
hg rebase --continue.
Verifying the Result
Check linear history
hg log -GYour feature commits should appear in a straight line on top of the main branch tip.
Check changeset phases
hg phases -vThe rebased changesets should be draft; none should have become public unexpectedly.
Check the working directory
hg status
hg diffStatus should be clean and no unresolved merge markers should remain in files.
Locate obsolete changesets
Mercurial does not delete the original commits; it marks them obsolete and hides them. View them with:
hg log -r 'obsolete()'Recovery and Safety
Aborting mid-operation
If you are stuck in a conflict and want to abandon the rebase entirely:
hg rebase --abortThis restores the working directory to its pre-rebase state.
Reverting after a successful rebase
If the rebase completed but you want the original history back, find the obsolete revision ID and update to it:
hg update -r <original-revision-id>Because obsolete changesets are retained until explicitly stripped, recovery remains possible. Do not run hg strip on them unless you are certain you no longer need them; stripping permanently removes history.
Limitations
Rebasing creates entirely new changeset IDs. If the original commits were already pushed to a shared server, rebasing forces divergence for anyone who pulled them and would require a forced push. Keep rebase strictly for draft-phase, unpushed work. Behavior described here assumes Mercurial 5.x or later with the bundled rebase extension; verify exact flags against hg help rebase for your installed version.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.