Choosing Between Mercurial Rebase and Merge for Feature Branch Integration
A decision guide for choosing Mercurial rebase vs merge when integrating feature branches. Covers constraints, a comparison table, trade-offs, and a hybrid rebase-then-merge workflow with commands and verification checks.
03 Jul 2025, 06:19 UTC

The Decision You Face
When a feature branch is ready to join the main line, you must decide whether to rebase it onto the target branch or merge it with a merge commit. The choice affects history readability, collaboration safety, and future bisect behavior. This guide lays out the constraints, compares the two operations side by side, and shows how to validate the result on a local repository.
Constraints That Shape the Choice
- Shared vs. private history: Rebase rewrites commits. If the branch has already been pushed to a repository that others have cloned, rebasing forces every collaborator to re-sync manually. Merge adds a commit without rewriting, so it is safe for shared branches.
- Extension availability: Rebase requires the
rebaseextension (bundled but disabled by default). Merge works out of the box on any Mercurial installation. - History philosophy: Teams that value a linear, bisect-friendly graph prefer rebase. Teams that want an immutable record of every integration point prefer merge.
- Repository size and tooling: Very large repositories with thousands of merge commits can make
hg log -Gharder to navigate. Linear history from rebasing keeps the graph narrow.
Quick Comparison
| Factor | Rebase | Merge |
|---|---|---|
| History shape | Linear; feature commits appear as if written on top of the target | DAG with explicit merge commit linking two lines of development |
| Safety for shared branches | Unsafe — rewrites public commits | Safe — only adds a new commit |
| Extension required | Yes (rebase) | No |
| Conflict resolution | Repeated per rebased commit | Single resolution at merge commit |
| Bisect behavior | Clean linear walk | Must traverse both parents |
| Rollback complexity | Requires hg strip or hg rebase --abort before push | Simple hg strip of the merge commit (if not pushed) |
Trade-offs in Practice
When Rebase Wins
- Short-lived feature branches that exist only on your workstation or a private fork.
- Teams that run automated bisect scripts and need a straight line of commits.
- Preparing a clean pull request: rebase onto the target branch, then push the rebased head for review.
When Merge Wins
- Long-lived release branches, integration branches, or any branch already visible to collaborators.
- Environments where the
rebaseextension cannot be enabled (e.g., restricted CI images). - Audit requirements that demand an unaltered record of when and how branches were combined.
Hybrid Workflow
Many projects rebase feature branches locally onto the latest main line, then perform a final merge into the shared branch. This preserves a linear feature history while still recording the integration event. In Mercurial, the merge commit is created explicitly; there is no fast-forward concept because Mercurial never moves a branch pointer without a new commit.
Concrete Validation: Rebase Then Merge on a Local Repo
The following steps assume a clean clone of your project. Run them in a terminal with write access to the repository directory. No special permissions beyond your user account are needed.
1. Enable the Rebase Extension
# Edit ~/.hgrc or .hg/hgrc in the repo
[extensions]
rebase =
Verify with hg help rebase — it should list the command without "(disabled)".
2. Create a Feature Branch and Commits
hg branch feature-ui
# make changes
hg commit -m "feat: add login button"
hg commit -m "feat: wire login handler"
3. Advance the Main Line (Simulate Upstream Work)
hg update default
# make unrelated changes
hg commit -m "chore: update dependencies"
4. Rebase Feature onto Default
hg update feature-ui
hg rebase -d default
What happens: Mercurial replays the feature commits on top of the current default tip. If conflicts appear, resolve them per commit, then run hg rebase --continue. Abort with hg rebase --abort if you want to start over.
5. Inspect the Graph
hg log -G -l 10
Expected result: a single line of commits showing the feature commits directly after the dependency update, with no merge node.
6. Merge into Shared Branch
hg update default
hg merge feature-ui
hg commit -m "merge: integrate feature-ui into default"
Because the feature branch was already rebased, this merge resolves trivially. The merge commit records the integration point.
7. Final Verification
hg log -G -l 15
Expected graph shape:
- Linear sequence: dependency update, then the two feature commits.
- One merge commit with two parents: the linear tip and the feature head.
If you see that structure, the hybrid workflow succeeded.
Limitations and Gotchas
- Rebase on published commits: If you accidentally rebase a branch that exists on a shared server, you must force-push (
hg push --force) and every collaborator must runhg pull --rebaseor re-clone. Document this risk in your contribution guide. - Large conflict sets: Rebasing a branch with dozens of commits that each touch the same files can mean resolving the same conflict repeatedly. In that case, a single merge is less tedious.
- Extension version differences: The bundled
rebaseextension behavior is stable across Mercurial 5.x–6.x, but third-party extensions (e.g.,evolve) may change rebasing semantics. Test in a throwaway clone first. - No fast-forward: Mercurial never fast-forwards; every merge creates a commit. If you want a purely linear history without a merge commit, you must rebase the feature branch and then move the branch pointer manually (advanced, rarely needed).
How to Check the Result in Your CI
Add a lint step that runs hg log -G --template '{rev}:{branch}|{desc|firstline}\n' -l 20 and fails if a merge commit appears on a branch named feature-* (indicating someone merged instead of rebasing before push). Adjust the pattern to match your branch naming convention.
Summary Checklist
- Is the branch already shared? → Merge.
- Do you need a linear history for bisect? → Rebase locally, then merge with a commit.
- Is the
rebaseextension enabled? → If not, merge or enable it. - Are conflicts numerous and repetitive? → Merge once.
- After the operation, run
hg log -Gand confirm the graph matches the intended shape.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.