1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
|
---
name: isurus-conventions
description: Use when working in the Isurus repository (or any repo identified as Isurus by the presence of `Isurus` in `README.md` or `cmd/isurus/`). Encodes Isurus-specific Mercurial conventions: non-publishing repo, bookmarks-as-PRs, commit message style, pre-PR Makefile checklist, opening PRs in the Isurus web UI.
---
# Isurus conventions (Mercurial overlay)
## When this applies
Use when working in the Isurus repository. Detect Isurus by:
- Repo root contains `cmd/isurus/`, or
- Top-level `README.md` contains the word `Isurus`, or
- Repo is cloned from `ssh://hg@hg.leafscale.com/leafscale/isurus`.
This skill is **purely additive**. The procedures live in `mercurial-basics`, `mercurial-bookmarks-and-prs`, `mercurial-sync-and-merge`, `mercurial-history-rewriting`, and `mercurial-finishing-a-branch`. This file supplies Isurus-specific values those procedures plug into.
## Repository facts
| Fact | Value |
|---|---|
| VCS | Mercurial 7.x (never git) |
| Clone URL | `ssh://hg@hg.leafscale.com/leafscale/isurus` |
| Long-lived branches | `default`, `stable` |
| Phase model | **Non-publishing** — pushed changesets stay `draft` until merged |
| Feature/PR mechanism | **Bookmarks** (push with `-B <name>`) |
| Forge | Isurus (web UI at `https://hg.leafscale.com/`) |
## Commit messages in this repo
- Imperative summary, **under 72 characters**.
- Prefix with a category: `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `scripts:`, `hgignore:`. Match what `hg log` shows for recent commits if uncertain.
- Body (optional) explains *why*; the diff shows *what*.
- One blank line between summary and body.
- **Never pass `-u`** to `hg commit` — identity is in `~/.hgrc`.
## Pre-PR check sequence
Run from the repo root, in this order:
```sh
make fmt # gofmt — leaves no diff if all files are formatted
make vet # go vet — catches suspicious constructs
make check # full test suite against PostgreSQL test DB
```
`make check` requires the `isurus-test` database on localhost:
- DB name: `isurus-test`
- User: `isurus-test`
- Password: `isurus-test`
Override with `ISURUS_TEST_DSN`. See `make test-setup` for one-time DB creation.
Optional but recommended (skip if not installed):
```sh
make lint # golangci-lint
```
The PR checklist from `CONTRIBUTING.md` (must all be true before opening PR):
- `make check` passes
- `go vet ./...` is clean
- Code is formatted (`make fmt`)
- New public functions have short godoc comments where non-obvious
- No new `TODO`/`FIXME` markers (fix or file an issue instead)
- Documentation updated if behavior or configuration changed
- Commit messages explain *why*
## Pull request flow
1. `hg push -B <bookmark>` to the canonical remote.
2. Open a PR in the Isurus web UI (`https://hg.leafscale.com/<owner>/<repo>`).
3. Title: same as the commit message summary.
4. Description: *why* + link to related issues.
5. Reviewer feedback → `hg amend` → `hg push -B <bookmark> -f`.
The non-publishing repo makes `-f` after amend the **expected** workflow, not a sign of trouble.
## What this overlay does NOT cover
- General Mercurial commands → `mercurial-basics`.
- Bookmark mechanics → `mercurial-bookmarks-and-prs`.
- Pull / merge / conflicts → `mercurial-sync-and-merge`.
- Amend / uncommit / phase rules → `mercurial-history-rewriting`.
- The end-to-end "finish a branch" sequence → `mercurial-finishing-a-branch`.
This file only changes the *values* those skills use (URLs, branch names, check commands). The procedures stay the same.
---
name: mercurial-basics
description: Use when working in any Mercurial repository for inspection or local commits — `hg status`, `diff`, `log`, `add`, `commit`, `parents`, `heads`, `branches`, `bookmarks`, `phase`. Triggers on any `hg ...` command or when `.hg/` is in the working directory.
---
# Mercurial basics
## When this applies
Use when:
- The working directory contains `.hg/` (or any ancestor does).
- About to run any `hg` command.
- About to perform an operation that has both git and hg equivalents and the repo is hg.
Do not use for:
- Pulling, merging, conflict resolution → see `mercurial-sync-and-merge`.
- Bookmarks and the PR workflow → see `mercurial-bookmarks-and-prs`.
- Amending or rewriting history → see `mercurial-history-rewriting`.
## Identity comes from ~/.hgrc
**Never pass `-u` to `hg commit`.** Identity is configured globally in `~/.hgrc`:
```ini
[ui]
username = Name <email@example.com>
```
`hg commit -u "..."` is for impersonation and will create commits with the wrong author. Always:
```sh
hg commit -m "your message"
```
## Inspection commands
| Need | Command |
|---|---|
| What changed in working dir | `hg status` |
| Diff of unstaged changes | `hg diff` |
| Recent commits | `hg log` (or `hg log -l 10` for limit) |
| Working dir parent revision | `hg parents` |
| All current heads | `hg heads` |
| Named branches | `hg branches` |
| Bookmarks (≈ feature branches) | `hg bookmarks` |
| Phase of a revision | `hg phase -r <rev>` |
| Who wrote each line | `hg annotate <file>` (git users: `blame`) |
## Reading `hg log` output
Default format shows changeset hash, branch (if not `default`), tag, user, date, and summary:
```
changeset: 42:a1b2c3d4e5f6
branch: myfeature
tag: tip
bookmark: work-in-progress
user: Alice <alice@example.com>
date: Mon May 02 09:00:00 2026 -0500
summary: feat: add the thing
```
For terser output: `hg log --template '{node|short} {bookmarks} {branch} {desc|firstline}\n'`
## Commit messages
The Isurus convention (and a generally good rule) is:
- **First line:** imperative summary, **under 72 characters**. Examples: `feat: add language stats to repo sidebar`, `fix: handle nil reviewer in PR template`, `chore: bump dependency X to v2`.
- **Body (optional):** explain *why* the change is being made. The diff already shows *what*. One blank line between summary and body.
- **Category prefix** (`feat:`, `fix:`, `docs:`, `chore:`, `test:`, `refactor:`) is convention in this codebase — match what `hg log` shows.
Keep each commit focused. If a single change touches unrelated areas, split into separate commits.
## Adding files
```sh
hg add path/to/file # explicit
hg add # add all untracked files (use carefully)
```
Mercurial does not have a staging area. Files are tracked or not; commits include all changes to tracked files. To commit only some changes, use `hg commit path/to/file1 path/to/file2`.
## .hgignore syntax
`.hgignore` lives at the repo root. Default syntax is `glob`:
```
syntax: glob
# Build artifacts
dist/
*.o
# Switch syntax for a section
syntax: rootglob
binary-at-root-only
# Switch back
syntax: glob
**/*.swp
```
`syntax: rootglob` matches only at the repo root (useful for binary names that must not match files in subdirectories).
## Phase awareness
Every changeset has a phase: `draft`, `public`, or `secret`. The phase governs what's safe to rewrite:
- **draft** — local or pushed-but-not-merged. Safe to amend, uncommit, rewrite.
- **public** — published and considered immutable.
- **secret** — never pushed.
In a non-publishing repo (like Isurus), pushed changesets stay draft until merged into the canonical branch. This is what makes `hg amend` safe during code review. See `mercurial-history-rewriting` for the rewriting rules.
To check: `hg phase -r <rev>`. To see your tip: `hg phase -r tip`.
## Common workflow examples
Inspect what's changed and commit:
```sh
hg status
hg diff
hg add new-file.go
hg commit -m "feat: add new thing"
```
See recent history on a bookmark:
```sh
hg log -l 5 -B my-feature
```
Diff between two revisions:
```sh
hg diff -r REV1 -r REV2
```
---
name: mercurial-bookmarks-and-prs
description: Use when starting feature work, switching between in-progress work, or pushing a feature for review in a Mercurial repo. Bookmarks are the unit of feature work and the unit of pull requests in this workflow.
---
# Mercurial bookmarks and pull requests
## When this applies
Use when:
- Starting a new feature or fix that will become a pull request.
- Switching between multiple in-progress features.
- About to push work for review.
- A bookmark is in the wrong place and needs fixing.
Do not use for:
- Long-lived branches like `default`, `stable` — those are named branches, see "Bookmark vs named branch" below for the distinction.
- Pulling/merging upstream changes → see `mercurial-sync-and-merge`.
- Amending commits before push → see `mercurial-history-rewriting`.
## Bookmark vs named branch
| | Bookmark | Named branch |
|---|---|---|
| Purpose | Feature/PR pointer | Long-lived line of development |
| Lifetime | Days to weeks; deleted after merge | Months to years |
| Examples | `add-language-stats`, `fix-nil-reviewer` | `default`, `stable`, `release-2.0` |
| Moves on commit? | Yes (the active one moves) | The new commit stays on the branch |
| Can be deleted? | Yes (`hg bookmark -d`) | Effectively no |
**Rule of thumb:** if you'd open a PR for it, it's a bookmark. If it's a maintained release line, it's a named branch.
### When the user says "branch"
Most users come to Mercurial from Git, where "branch" means a lightweight pointer that can be deleted. In Mercurial that concept is a **bookmark**, not a named branch. Map their words accordingly:
- **"create a feature branch", "start a branch", "switch branches", "branch off X"** → bookmark, every time.
- **"create a release branch", "branch for v2.0"** → named branch (long-lived release line).
- **Ambiguous** → ask, but default to bookmark for anything PR-shaped.
**Override repo precedent.** If you find existing named branches that look like feature work (e.g. an `add-language-stats` named branch alongside `default`), do *not* infer that the convention is to use named branches. That precedent is almost always a previous mistake (often by an LLM that didn't load this skill). Use a bookmark for the new work and surface the inconsistency:
> "Heads up: this repo has named branches (`add-language-stats`, `fix-nil-reviewer`) that look like feature work, which is unusual — feature work in Mercurial is typically a bookmark. I'm using a bookmark for the new feature; let me know if you'd prefer to match the existing pattern."
The only time existing named branches *should* steer you toward a named branch is when they are clearly long-lived release lines (`stable`, `release-1.0`, `release-2.0`).
## Starting a feature
```sh
hg pull -u # sync to latest default
hg bookmark my-feature # create bookmark at current rev
# ... edit files ...
hg add new-file.go
hg commit -m "feat: add new thing"
hg push -B my-feature # push the bookmark to the remote
```
After `hg commit`, the bookmark `my-feature` advances to the new changeset automatically (it's the active bookmark).
## Listing bookmarks
```sh
hg bookmarks
```
Output marks the active bookmark with `*`:
```
add-language-stats 42:a1b2c3d4e5f6
* my-feature 47:b9c8d7e6f5a4
work-in-progress 39:c5d4e3f2a1b0
```
## Switching bookmarks
```sh
hg update my-feature # switch to the bookmark and make it active
```
`hg update` checks out the changeset the bookmark points to AND activates the bookmark (so future commits move it forward).
If you only want to look at a revision without activating any bookmark there:
```sh
hg update -r <revision>
```
## Pushing for review
The Isurus convention (non-publishing repo + bookmarks = PRs):
```sh
hg push -B my-feature
```
`-B my-feature` pushes the bookmark by name. Without `-B`, `hg push` pushes the current head's changesets but does not push the bookmark itself, which means the remote won't know to associate them with a PR.
After pushing, open the PR in the Isurus web UI (or the equivalent forge UI). See `isurus-conventions` for Isurus specifics.
## Updating a PR after review feedback
In a non-publishing repo, pushed changesets stay `draft` until the PR is merged. That means amending after a push is **safe**:
```sh
# ... make the requested changes ...
hg amend # rewrites the tip changeset in place
hg push -B my-feature -f # -f because the rewrite changed the hash
```
The `-f` is required because the new amended changeset has a different hash than what's on the remote; without `-f` the push is rejected. This is the *intended* workflow — not a sign you're doing something dangerous, because the changesets are still draft.
## Deleting a bookmark after merge
Once the PR is merged into `default`:
```sh
hg pull -u # pull in the merged changes
hg bookmark -d my-feature # delete locally
hg push --delete my-feature # delete on the remote (if not already gone)
```
## Recovering a misplaced bookmark
If the bookmark is on the wrong changeset (e.g., you forgot it was active and committed something unrelated):
```sh
hg bookmarks # see where it is
hg bookmark my-feature -r <correct> # move it
```
The `-r` flag moves an existing bookmark to a specific revision without checking out anything.
## Common pitfalls
- **Forgetting `-B` on push:** the changesets land but no bookmark = no PR association. Re-push with `hg push -B <name>`.
- **Forgetting `-f` after amend:** push is rejected with "remote has changesets you don't have." Verify it's your own amended changeset (not a real upstream change), then `-f`.
- **Active bookmark surprise:** `hg update -r <rev>` *without* a bookmark name leaves no bookmark active; subsequent commits create anonymous heads. Always activate a bookmark you intend to commit to.
---
name: mercurial-finishing-a-branch
description: Use when an implementation is complete and ready to be proposed as a pull request — runs the pre-PR checklist (build, vet, format, test) and walks through pushing the bookmark and opening a PR. The Mercurial counterpart to superpowers' `finishing-a-development-branch`.
---
# Mercurial: finishing a development branch
## When this applies
Use when:
- Implementation is complete and the user is ready to propose a PR.
- The user says "let's ship this" / "this is done" / "let's open a PR" in an hg repo.
Do not use for:
- Mid-development commits → see `mercurial-basics`.
- Recovering from a bad commit → see `mercurial-history-rewriting`.
## The flow
Six steps. Do them in order; do not skip.
### 1. Verify the working directory is clean
```sh
hg status
```
Expected: empty output. If anything is shown:
- New files you intended to include → `hg add` and amend the tip commit.
- New files you didn't mean to track → add to `.hgignore` and commit that change separately.
- Modified files → either amend or commit them as a separate change.
Do not push with uncommitted changes lingering.
### 2. Verify you're on the right bookmark
```sh
hg bookmarks
```
The active bookmark is marked with `*`. Confirm it's the feature bookmark, not e.g. an accidental commit on no bookmark.
If a feature bookmark exists but isn't active:
```sh
hg update <bookmark-name>
```
If commits were made without an active bookmark (anonymous head):
```sh
hg bookmark <feature-name> -r .
```
(See `mercurial-bookmarks-and-prs` for the bookmark mechanics.)
### 3. Verify history is clean
```sh
hg log -l 10 --template '{node|short} {phase} {desc|firstline}\n'
```
Look for:
- All commits should be `draft` (in non-publishing repos like Isurus).
- Commit messages follow the convention (imperative summary, <72 chars, "category: summary" prefix where the project uses one).
- No "wip", "fix typo", "address review" debris — squash via `hg amend` / `hg uncommit`.
If cleanup is needed → see `mercurial-history-rewriting`. The non-publishing repo means amend is safe even on already-pushed changesets.
### 4. Run the project's pre-PR checks
Detect the project's check command:
```sh
ls Makefile 2>/dev/null && grep -E '^(check|test|fmt|vet|lint):' Makefile
```
Common patterns:
- **Has `Makefile` with `check` target:** `make check` (full test suite). Then run `make fmt` and `make vet` if those exist.
- **Go project, no Makefile:** `go test ./...`, `go vet ./...`, `gofmt -l .` (anything output = unformatted).
- **Other languages:** match the project's documented test/lint commands.
For the Isurus repository specifically, see `isurus-conventions` — it documents the exact `make` targets used.
If checks fail, **fix or surface** before continuing. Do not push code that fails the project's own gate.
### 5. Push the bookmark
```sh
hg push -B <bookmark-name>
```
The `-B <bookmark-name>` is critical — without it the changesets push but the remote doesn't know to associate them with a bookmark/PR.
If the push is rejected because the bookmark was previously pushed and has since been amended:
```sh
hg push -B <bookmark-name> -f
```
`-f` is the expected workflow in non-publishing repos after `hg amend`. See `mercurial-bookmarks-and-prs` for context.
### 6. Open the pull request
In the forge web UI:
1. Navigate to the repository.
2. The forge typically shows a banner offering to open a PR for the just-pushed bookmark — click it.
3. Title the PR using the same format as the commit message summary.
4. PR description: explain *why*, link any related issues. The diff shows *what*; don't repeat it in the description.
For Isurus-specific URL patterns and PR conventions → see `isurus-conventions`.
## After the PR is open
When reviewer feedback comes in:
- Make the requested changes locally.
- `hg amend` to fold the fix into the existing changeset (in non-publishing repos).
- `hg push -B <bookmark-name> -f`.
- Reply to the review comments confirming the fix is pushed.
When the PR is merged:
- `hg pull -u` to bring the merged changes locally.
- `hg bookmark -d <bookmark-name>` to delete the local bookmark.
- Optionally `hg push --delete <bookmark-name>` if the forge doesn't auto-delete on merge.
---
name: mercurial-history-rewriting
description: Use when rewriting Mercurial history — amending commits, uncommitting, splitting commits, or recovering from a mistake. Critical: only safe on draft changesets, never on public ones.
---
# Mercurial history rewriting
## When this applies
Use when:
- About to amend a commit (fix typo, add forgotten file, edit the message).
- Need to uncommit / split a changeset.
- Recovering from a mistaken commit.
Do not use for:
- Routine commits → see `mercurial-basics`.
- Bookmark/PR push flow → see `mercurial-bookmarks-and-prs`.
- Conflicts after merging → see `mercurial-sync-and-merge`.
## The fundamental rule: draft only
**Only rewrite changesets in `draft` phase. Never rewrite `public` ones.**
Check before rewriting:
```sh
hg phase -r tip # or any specific revision
```
If the phase is `public`, **stop and surface the situation to the user.** Rewriting public history breaks every other clone of the repo. Mercurial will refuse by default with: `cannot amend public changesets`.
In Isurus the repo is non-publishing, so pushed changesets stay `draft` until merged into the canonical branch. That makes `hg amend` safe during code review. After merge, the changesets become `public` and are off-limits.
## `hg amend` — fix the tip changeset in place
The most common rewrite. Edit files, then:
```sh
hg amend # fold working-dir changes into tip; keeps message
hg amend -m "new message" # also rewrite the message
hg amend file1 file2 # only fold those files
```
After amend, the new changeset has a different hash. If it was already pushed, you'll need `-f` next push:
```sh
hg push -B my-feature -f
```
This is normal in a non-publishing repo. See `mercurial-bookmarks-and-prs` for the post-amend push pattern.
## `hg uncommit` — undo a commit, keeping changes in working dir
```sh
hg uncommit # the entire tip changeset
hg uncommit file1 file2 # only those files (rest stay committed)
```
Use cases:
- The commit message is wrong and you want to redo it from scratch.
- You committed too much and want to split.
After uncommit, the files are back in the working dir as uncommitted changes. Re-commit selectively.
## Splitting a commit
Goal: turn one commit into two (or more) more focused ones.
```sh
hg uncommit # uncommit everything
hg commit path/to/group-1 -m "feat: part 1"
hg commit path/to/group-2 -m "feat: part 2"
```
If groups overlap by line within a single file, you may need to use a partial-add tool — that's beyond v1 scope. Manually patch and recommit, or use `hg commit -i` (interactive) if available.
## Recovering from a mistake
### "I committed too soon / wrong message"
```sh
hg amend -m "the right message"
```
### "I committed to the wrong bookmark"
```sh
hg bookmarks # see what's where
hg bookmark wrong-name -r .~1 # move the wrong bookmark back one
hg bookmark right-name -r . # put the right bookmark on tip
```
### "I committed something I shouldn't have"
If only the most recent commit is wrong and changes haven't been pushed:
```sh
hg uncommit # changes return to working dir
# discard them (CAREFUL — destructive):
hg revert --all --no-backup # discard all working-dir changes
```
### "I want to undo the last several local commits"
This is harder in vanilla Mercurial without `evolve`. Options:
1. `hg strip <rev>` — removes a changeset and all descendants. Requires the `strip` extension (`[extensions] strip = ` in `~/.hgrc`). Destructive; backups are saved in `.hg/strip-backup/` by default.
2. Re-clone from the remote, lose local changes.
In Isurus today, prefer option 1 with the strip extension. For complex multi-commit rewrites, this is exactly the case where `evolve` + `topics` would help (deferred to phase 2).
## What `hg rollback` is and isn't
`hg rollback` undoes the most recent transaction (commit, pull, push). It's mostly deprecated because it operates on the *transaction*, not the changeset, and quietly loses information. **Prefer `hg amend` / `hg uncommit` / `hg strip` instead.** The repo's `[ui] rollback = false` is recommended (and is the default in modern Mercurial).
## Public-changeset attempts
If an `hg amend` returns:
```
abort: cannot amend public changesets: <hash>
```
Do not use `--force` to override. Stop and surface the situation: someone (or the workflow) has marked this changeset public, which means it's been integrated. The right answer is a *new* commit on top, not a rewrite.
## A note on evolve / topics
Modern Mercurial (with the `evolve` extension and `topics`) supports rich history rewriting workflows: stable obsolescence markers, topic branches, automatic descendant rebasing. Isurus does not enable these today; if it does in a future version, this skill will grow a sibling (`mercurial-evolve`) or be expanded.
---
name: mercurial-sync-and-merge
description: Use when pulling changes from a remote, updating to a different revision, dealing with multiple heads, or resolving merge conflicts in a Mercurial repo.
---
# Mercurial sync and merge
## When this applies
Use when:
- Pulling changes from a remote.
- Updating the working directory to a different revision.
- The repo has multiple heads on the same branch.
- Resolving merge conflicts.
Do not use for:
- Local commit hygiene → see `mercurial-basics`.
- Moving or deleting bookmarks → see `mercurial-bookmarks-and-prs`.
- Amending after a merge → see `mercurial-history-rewriting`.
## Pull then update
```sh
hg pull # fetch new changesets, do not touch the working dir
hg update # update working dir to tip of the active branch/bookmark
```
Combined:
```sh
hg pull -u # pull then update in one step (most common)
```
If a pull brings in changesets that create a new head on the same branch (someone else committed in parallel), `hg update` will move to the new tip *only if* the working dir is at the previous tip. Otherwise it warns about multiple heads.
## Multiple heads
A "head" is a changeset with no children on the same branch. Normal repos have one head per branch. Two parallel commits → two heads.
See them:
```sh
hg heads # all heads
hg heads default # heads on the default branch
```
Resolution options:
1. **Merge them** — combine the two lines:
```sh
hg update -r <head A>
hg merge <head B>
# resolve any conflicts (see below)
hg commit -m "merge: combine parallel work"
```
2. **Rebase your work onto the other head** (preferred for personal feature work):
```sh
# If your work is the new head and the other is upstream:
hg update -r <upstream head>
# then for each of your local commits, replay using rebase or amend
```
Note: vanilla Mercurial without the `rebase` extension can't `hg rebase` directly. Either enable `rebase` in `~/.hgrc` (`[extensions] rebase = `) or fall back to merge.
For Isurus today: merge is the safe default; rebase is not part of the v1 workflow.
## Merging two heads
```sh
hg update -r <target> # check out the changeset to merge INTO
hg merge -r <source> --tool internal:merge # bring source into the working dir
# files with conflicts are listed in the output
```
**Always pass `--tool internal:merge`** when running `hg merge` from a Claude Code session. The user's `~/.hgrc` may default to `vimdiff`, `kdiff3`, or another interactive tool, none of which work in a non-TTY context — the merge will hang or error. `internal:merge` writes conflict markers (`<<<<<<<` / `=======` / `>>>>>>>`) into the file and returns control immediately, which is exactly what the resolution flow below assumes.
## Conflict resolution
After `hg merge`, files in conflict are marked. Inspect them:
```sh
hg resolve --list # 'U' = unresolved, 'R' = resolved
```
Edit each unresolved file. Conflict markers look like:
```
<<<<<<< local
your version
=======
their version
>>>>>>> other
```
Remove the markers, keep the correct content, save. Then mark resolved:
```sh
hg resolve --mark path/to/file
```
Or after editing all of them:
```sh
hg resolve --mark --all
```
Verify nothing remains:
```sh
hg resolve --list # should print nothing
```
Re-run a merge tool on a specific file (e.g., to retry):
```sh
hg resolve --tool internal:merge3 path/to/file
```
### Shortcuts when one side wins
If the user explicitly picks one side ("keep ours", "take theirs") rather than wanting a hand-merge, use the appropriate `internal:` resolver — it both resolves and marks in one step, no manual editing needed:
```sh
hg resolve --tool internal:local path/to/file # keep working-dir version
hg resolve --tool internal:other path/to/file # take incoming version
```
Then verify with `hg resolve --list` and proceed to commit.
Once all files are resolved:
```sh
hg commit -m "merge: combine X and Y"
```
A merge commit has two parents. `hg log --graph` (or `hg log -G`) shows the merge visually.
## Aborting a merge
If you started a merge and want to back out:
```sh
hg update -C -r <pre-merge revision>
```
`-C` means clean — discard the in-progress merge state. Verify the working dir is clean afterward with `hg status`.
## Common pitfalls
- **`hg pull` without `-u`:** changesets are in the repo but the working dir is unchanged. You're working against stale code without realizing.
- **Two parents after merge with no commit:** if you `hg merge` and then run other commands, the working dir has two parents until you `hg commit`. Until then, `hg parents` shows both.
- **Conflict markers committed:** if you `hg commit` without removing all `<<<<<<<`/`=======`/`>>>>>>>` markers, you commit broken files. Always grep before commit:
```sh
grep -r '<<<<<<<' .
```
|