--- 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 ` — 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: ``` 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.