133

How can I undo my last accidentally commited (not pushed) change in Mercurial?

If possible, a way to do so with TortoiseHg would be prefered.

Update

In my concrete case I commited a changeset (not pushed). Then I pulled and updated from the server. With these new updates I decided, that my last commit is obsolete and I don't want to sync it. So it seems, that hg rollback is not exactly what I'm searching for, because it would rollback the pull instead of my commit.

Gangnus
  • 24,044
  • 16
  • 90
  • 149
Martin Buberl
  • 45,844
  • 25
  • 100
  • 144
  • 1
    What about making two `hg rollback`, and then pulling again? – VonC Jan 21 '11 at 16:13
  • 1
    The first rollback does undo the pull, the second rollback tells me "no rollback information available". – Martin Buberl Jan 21 '11 at 16:16
  • 1
    I've encountered the same situation, and as far as I know that once you've done any other operation - you can't go back or remove it from history. You would need to re-clone the hg repository. –  Jan 21 '11 at 16:28
  • 3
    `strip --keep`: http://stackoverflow.com/questions/29413851/how-to-revert-last-commits-and-keep-changes-in-mercurial directly solves the task, as does MQ qimport. Alternatively, it may be better simply to `rebase` in the scenario if the local changes are *also* to be kept and there is no conflict. (Use a merge to *keep* some combined changes from both branches when that is the desired graph end-goal, especially on a non-fastforward case.) It may also be valid just to close the branch, which still 'preserves history' (but differently than a merge) depending on the final state desired. – user2864740 Jan 06 '16 at 04:20
  • 2
    As @user2864740 says, `hg strip --keep` is the command do use, but you need to provide a revision, e.g.: `hg strip --keep -r .`. This answer explains well http://stackoverflow.com/a/19064016/1286571 – ForeverWintr Dec 01 '16 at 02:27
  • Possible duplicate of [Mercurial (hg) equivalent of git reset (--mixed or --soft)](http://stackoverflow.com/questions/13112280/mercurial-hg-equivalent-of-git-reset-mixed-or-soft) – ForeverWintr Dec 01 '16 at 02:36
  • @ForeverWintr FWIW: I've switched to [Hg Evolve Extension](https://www.mercurial-scm.org/wiki/EvolveExtension) (rebase + histedit + prune + phase as needed) and have not used or any 'dangerous' command in months. I recommend that *everyone* dealing with Hg familiarizes with this extension as then there is no need to dance around the commit tree and it promotes a many-small-commit + fixup flow. Similar to Git, "orphaned" or "obsolete" or "stripped" commits remain part of the repository (until explicitly, in the case of evolve, removed) although they are otherwise ignored. – user2864740 Dec 01 '16 at 06:30
  • @ForeverWintr I asked that question over 1.5 years before the possible duplicate you mentioned. If so, http://stackoverflow.com/questions/13112280/mercurial-hg-equivalent-of-git-reset-mixed-or-soft is the duplicate. – Martin Buberl Dec 01 '16 at 23:46
  • @MartinBuberl I meant no offence; it's just that that question has a correct answer and this one doesn't. I'm not sure what the official SO method of handling such a situation is. – ForeverWintr Dec 02 '16 at 03:59
  • @ForeverWintr I'd argue that 62 (current count) people think that the answer from VonC is correct within this context. I don't actually think the questions are the same as in being duplicates. No offense taken. – Martin Buberl Dec 02 '16 at 07:56
  • VonC's answer *was* correct in 2011, but he edited it in 2013 to reflect the fact that hg rollback has since been deprecated. He recommends using `hg commit --amend` instead, but doesn't explain how to use it to "undo the last commit" as per your question. – ForeverWintr Dec 02 '16 at 22:44

9 Answers9

84

One way would be hg rollback (deprecated as of Hg2.7, August 2013)

Please use hg commit --amend instead of rollback to correct mistakes in the last commit.

Roll back the last transaction in a repository.

When committing or merging, Mercurial adds the changeset entry last.
Mercurial keeps a transaction log of the name of each file touched and its length prior to the transaction. On abort, it truncates each file to its prior length. This simplicity is one benefit of making revlogs append-only. The transaction journal also allows an undo operation.

See TortoiseHg Recovery section:

alt text

This thread also details the difference between hg rollback and hg strip:
(written by Martin Geisler who also contributes on SO)

  • 'hg rollback' will remove the last transaction. Transactions are a concept often found in databases. In Mercurial we start a transaction when certain operations are run, such as commit, push, pull...
    When the operation finishes succesfully, the transaction is marked as complete. If an error occurs, the transaction is "rolled back" and the repository is left in the same state as before.
    You can manually trigger a rollback with 'hg rollback'. This will undo the last transactional command. If a pull command brought 10 new changesets into the repository on different branches, then 'hg rollback' will remove them all. Please note: there is no backup when you rollback a transaction!

  • 'hg strip' will remove a changeset and all its descendants. The changesets are saved as a bundle, which you can apply again if you need them back.

ForeverWintr suggests in the comments (in 2016, 5 years later)

You can 'un-commit' files by first hg forgetting them, e.g.: hg forget filea; hg commit --amend, but that seems unintuitive.
hg strip --keep is probably a better solution for modern hg.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • When I understand that correct the rollback removes the last transaction. In my case I did a commit, then I pulled from the server. So, does that mean I would rollback the pull instead of commit? – Martin Buberl Jan 21 '11 at 15:57
  • 1
    @Martin Buberl: According to http://www.selenic.com/mercurial/hg.1.html#rollback, pull is considered a transaction. So if you pulled, executing `hg rollback` will undo the pull instead of the commit. – Tim Henigan Jan 21 '11 at 16:03
  • Thanks, I'm going to edit my question a little bit to point that better ou. – Martin Buberl Jan 21 '11 at 16:06
  • 2
    I'm not sure how `hg commit --amend` is supposed to work, but for me it gave me an editor to modify the commit message and no method to change which files were included. I exited the editor without saving and found that my mistaken commit was changed to include _ALL_ changed files! – Tim Tisdall Dec 14 '15 at 15:54
  • 2
    It's not clear to me how to accomplish an answer to the question using `hg commit --amend`. You can 'un-commit' files by first `hg forget`ting them, e.g.: `hg forget filea; hg commit --amend`, but that seems unintuitive. [hg strip --keep](http://stackoverflow.com/a/19064016/1286571) is probably a better solution for modern hg. – ForeverWintr Dec 02 '16 at 23:02
  • As pointed out by Tim Tisdall and ForeverWintr, using `hg commit --amend` to solve the problem is confusing and just doing that won't work in the general case. The `hg strip` answer should be put forward and highlighted more. I prefer upvoting Peter Graham's answer for that reason. – Neptilo Jan 31 '17 at 16:31
  • This doesn't tell you how to use `hg commit --amend` to undo a commit. I want to bring my repo to the state before the last commit (don't care if the "bad" commit is in the history). In SVN I would just do a reverse-merge. How do I do this in Hg? – Jason S Oct 11 '17 at 18:04
  • 1
    @JasonS https://book.mercurial-scm.org/read/changing-history.html#adapting-the-last-changeset is a good read, although in your case, you could also consider hg reset: https://stackoverflow.com/a/31302998/6309 – VonC Oct 11 '17 at 20:51
49

hg strip will completely remove a revision (and any descendants) from the repository.

To use strip you'll need to install MqExtension by adding the following lines to your .hgrc (or mercurial.ini):

[extensions]
mq =

In TortoiseHg the strip command is available in the workbench. Right click on a revision and choose 'Modify history' -> 'Strip'.

Since strip changes the the repository's history you should only use it on revisions which haven't been shared with anyone yet. If you are using mercurial 2.1+ you can uses phases to track this information. If a commit is still in the draft phase it hasn't been shared with other repositories so you can safely strip it. (Thanks to Zasurus for pointing this out).

Jens
  • 69,818
  • 15
  • 125
  • 179
Peter Graham
  • 11,323
  • 7
  • 40
  • 42
  • 3
    IF the revision in question is still in the draft phrase (or you have access to ALL of the repo's that it has been push/pulled to and apply the strip to all of them) (which is the case in this description) then this is sooo the best and cleanest option. – GazB Jul 23 '12 at 15:36
  • 5
    If you have TortoiseHg installed, there's a graphical interface for globally activating mq. In the current version: enable "file->settings->global settings tab->extensions->mq" or access the settings file through the button "Edit file". – David May 15 '13 at 14:37
19

Since you can't rollback you should merge that commit into the new head you got when you pulled. If you don't want any of the work you did in it you can easily do that using this tip.

So if you've pulled and updated to their head you can do this:

hg --config ui.merge=internal:local merge

keeps all the changes in the currently checked out revision, and none of the changes in the not-checked-out revision (the one you wrote that you no longer want).

This is a great way to do it because it keeps your history accurate and complete. If 2 years from now someone finds a bug in what you pulled down you can look in your (unused but saved) implementation of the same thing and go, "oh, I did it right". :)

ctuffli
  • 3,559
  • 4
  • 31
  • 43
Ry4an Brase
  • 78,112
  • 7
  • 148
  • 169
6

hg rollback is what you want.

In TortoiseHg, the hg rollback is accomplished in the commit dialog. Open the commit dialog and select "Undo".

alt text

Tim Henigan
  • 60,452
  • 11
  • 85
  • 78
4

In the current version of TortoiseHg Workbench 4.4.1 (07.2018) you can use Repository - Rollback/undo...:
enter image description here

TmTron
  • 17,012
  • 10
  • 94
  • 142
1

Its workaround.

If you not push to server, you will clone into new folder else washout(delete all files) from your repository folder and clone new.

ITsDEv
  • 51
  • 3
1

I believe the more modern and simpler way to do this now is hg uncommit. Note this leaves behind an empty commit which can be useful if you want to reuse the commit message later. If you don't, use hg uncommit --no-keep to not leave the empty commit.

hg uncommit [OPTION]... [FILE]...

uncommit part or all of a local changeset

This command undoes the effect of a local commit, returning the affected
files to their uncommitted state. This means that files modified or
deleted in the changeset will be left unchanged, and so will remain
modified in the working directory.

If no files are specified, the commit will be left empty, unless --no-keep

Sorry, I am not sure what the equivalent is TortoiseHg.

studgeek
  • 14,272
  • 6
  • 84
  • 96
1

I ran into this issue recently, although my situation was slightly different - I had already pushed the commit I wanted to undo. I solved my problem with hg backout <revision-code>. It seems to work in a similar way to git revert. From this answer:

backout: create a new commit that is the inverse of a given commit. Net effect is an undo, but the change remains in your history.

In TortiseHg this can be done by right clicking the commit you wish to undo and selecting Backout.... TortiseHg doc here.

Hg Backout example image

I think there's an argument for creating a new undo commit instead of removing the previous commit outright. From this article about git revert:

Instead of removing the commit from the project history, it figures out how to invert the changes introduced by the commit and appends a new commit with the resulting inverse content. This prevents Git from losing history, which is important for the integrity of your revision history and for reliable collaboration.

pjpscriv
  • 866
  • 11
  • 20
0

after you have pulled and updated your workspace do a thg and right click on the change set you want to get rid of and then click modify history -> strip, it will remove the change set and you will point to default tip.

cabhishek
  • 697
  • 5
  • 9