Using Mercurial Phases and the Evolve Extension to Rewrite Local History Safely
Learn how Mercurial’s phase system protects shared history and how the evolve extension lets you amend draft changesets while automatically rebasing descendants.
17 Dec 2025, 10:58 UTC

The Problem: Accidentally Rewriting Shared History
When you work on a feature branch locally, you often realize a commit needs a small fix—perhaps a typo, a missing file, or a tweak to the commit message. If you simply run hg commit --amend on a changeset that has already been pushed to a shared repository, you risk rewriting public history. That can break clones for teammates and cause confusing merge Mercurial will reject the operation, but low‑level commands can bypass the safety net if you’re not careful.
Thesis: Phases Guard Public History; Evolve Lets You Rewrite Drafts Safely
Mercurial tracks each changeset with a phase—public, draft, or secret. New local commits start in draft. When you push or pull from a repository that others also use, Mercurial promotes those changesets to public. The phase system prevents commands like hg strip or hg rebase from touching public changesets unless you explicitly override it.
The evolve extension builds on this model. It provides high‑level commands (hg amend, hg fold, hg split, hg evolve) that work only on draft changesets. When you amend a parent changeset, hg evolve automatically rebases its descendants onto the updated parent, keeping the history linear and eliminating manual fix‑ups.
Worked Example: Amending a Draft Commit and Evolving Its Children
- Set up a scratch repository (run where you have write permission):
hg init example-repo cd example-repo echo 'initial' > file.txt hg add file.txt hg commit -m 'Initial commit' - Create a draft stack (three local commits, none pushed):
echo 'change A' >> file.txt hg commit -m 'Add A' echo 'change B' >> file.txt hg commit -m 'Add B' echo 'change C' >> file.txt hg commit -m 'Add C' - Verify the phases (all should be
draft):
You’ll see output like:hg phase0: draft Initial commit 1: draft Add A 2: draft Add B 3: draft Add C
- Amend the middle commit (for instance, fix a typo in its message):
This creates a new changeset that obsoletes the old one; the old changeset becomeshg amend -m 'Add A (fixed)' -r 1obsoletebut remains in the repository until evolved. - Run evolve to rebase the child changesets:
After this,hg evolvehg log --graphshows a linear chain where the amended commit is the parent of the former "Add B" and "Add C" commits, now rebased onto the updated parent. - Confirm the result:
You should see all three commits still markedhg log --graph -T '{rev}:{phase} {desc|firstline}\n'draft(none have been pushed yet).
Trade‑offs and Limitations
- Cognitive load: You must remember to run
hg evolve after any amendment, fold, or split. Forgetting it leaves obsolete changesets and can create "unstable heads" that confuse newcomers. - Extension availability:
evolveis not part of the core Mercurial distribution; you need to enable it in~/.hgrcor the repository’s.hg/hgrc:
Verify with[extensions] evolve =hg help extensionsbefore relying on it in automation. - Collaborator responsibility: If you push rewritten draft changesets to a shared repo and others pull them, they must also run
hg evolve(orhg pull --rebasewith evolve enabled) to avoid seeing obsolete heads. - Phase bypass risk: Low‑level commands like
hg strip -rcan still remove public changesets if you force them. Treat phases as a guard, not an absolute lock.
Actionable Closing
To safely rewrite local history in Mercurial:
- Keep work-in‑progress commits in the
draftphase—avoid pushing them until they are ready. - Enable the
evolveextension once per environment. - When you need to adjust a draft changeset, use
hg amend,hg fold, orhg splitas appropriate. - Immediately run
hg evolveto propagate the change to any descendant commits. - Before sharing, run
hg phase --force -r publiconly when you are certain the changeset should become immutable.
By combining Mercurial’s built‑in phase protection with the evolve extension’s automatic rebasing, you can iterate locally with confidence, knowing that shared history stays intact and your teammates won’t be surprised by unexpected heads.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.