--- 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 ``` ## 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 # 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 `. - **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 ` *without* a bookmark name leaves no bookmark active; subsequent commits create anonymous heads. Always activate a bookmark you intend to commit to.