7

Hey, I want to revert some changesets in Hg but I'm having a hard time doing so.

I commited some changes by accident, so I wanted to undo that. After trying a little bit, I was able to hg backout, but it created a new commit. Now, I wanted my repo to became to the initial state (without the last 2 commits and with my changes in the working dir). Apparently there's no way of doing that (something similar to git reset --soft). Is there?

Another thing I tried was to get rid of the latest commit (the one that reverted the original one). It would be something similar to git reset --hard, but again, there's no way to do that. I can hg update -C, but that is more like git checkout, since the commit is still out there.

And also, hg update -C -r X reverts me back to revision X, but when I check hg log there's no way for me to know in which revision I am right now. The only way is to check my bash history, is this right? There's gotta be a way to know this.

Anyway, I've been trying to sort this out for a while but couldn't find anything that would solve it. The references I found comparing git and hg commands are not accurate, since the commands don't have the exact same behavior.

I guess I just want to know what are the REAL equivalences in hg for git reset --hard and git reset --soft...

Thanks.

Martin Geisler
  • 72,968
  • 25
  • 171
  • 229
Gaisorama
  • 81
  • 1
  • 2
  • This isn't an answer to your main question, but to see which revision you're in you do `hg summary`. If you *only* want to see the revision, give it the quiet flag: `hg summary -q`. – Cody Piersall Aug 21 '13 at 19:02

5 Answers5

9

Mercurial intentionally doesn't make this easy. Mercurial's mindset is that code committed should continue to exist -- that the record of what didn't work out is almost as important as a record of what did. It's better to commit the negation of your work (which is what backout does) and still have the record than it is to actually discard it.

If, however, you just can't leave that change around (good reasons: it contains a password you can't change, bad reason: It just didn't work out) you have a few options:

  • strip: I don't like strip it modifies the repository and doesn't put the stuff back in the working dir. Strip is disabled by default for a reason.
  • clone: Just do a hg clone -r LASTCHANGESETYOULIKE oldrepo newrepo and now newrepo has everything up to and including LASTCHANGESETYOULIKE. Archive oldrepo and rename newrepo to oldrepo.
  • rollback: This is the one-level undo of the mercurial world. It would have undone your commit right after you did it, but it doesn't work if you've done any other pulls or commits, which you have.
Ry4an Brase
  • 78,112
  • 7
  • 148
  • 169
  • Cool. Thanks a lot for your answer. It clarified a lot of things. Just one last question: is there any command that would undo a commit into the working dir? (like git reset --soft) -- the reason for that is to make it easier for to use my favorite diff software to check what that commit is about. – Gaisorama Nov 13 '10 at 22:35
  • `hg rollback` is like `git reset --soft HEAD^` – wRAR Nov 14 '10 at 07:14
  • Yup, like wRAR says, `rollback` is the one level undo, so it will undo one commit (provided you haven't pulled since). However, check out th extdiff extension -- it should let you compare the last commit to the working dir easily and without committing just to do it. – Ry4an Brase Nov 15 '10 at 18:14
  • @wRAR `hg rollback` is NOT like `git reset --soft HEAD^`. You can't undo `hg rollback` if it goes badly. – Mihai Sep 11 '11 at 09:04
  • @JohnGietzen, I'm guessing your current, preferred programming language doesn't expose direct writes to arbitrary memory addresses, and not because they're useless but because they're "very sharp". It might not even make them possible. Mercurial makes "immutability" a design decision not because they don't trust you but because when you accept immutability some other very hard things become easier. As the lisp/ml/haskell/clojure people about it. They don't avoid mutability because it's "sharp". See also: http://mercurial.selenic.com/wiki/Phases . I use git and hg, and they're both great. – Ry4an Brase Oct 26 '12 at 02:04
7

Here is how to do git reset --soft. Let us assume you have a graph like this:

... --- [A] --- [B] --- [X] --- [-X]

where X is the bad commit and -X is the backout you made. You now want to get rid of X and -X while leaving the working copy looking like it did when you committed X. You do

$ hg update B
$ hg revert --all --rev X # working copy now looks just like in X
$ hg strip --force X      # get rid of X and -X

If you want, you can make an alias for this with Mercurial 1.7:

[alias]
softreset = !hg update 'p1($1)' &&
             hg revert --all --rev $1 &&
             hg strip --force $1

The dirty working copy after hg revert makes it necessary to use hg strip --force. You use this new command as

$ hg softreset 10

which will remove 10 and any descendents while leaving the changes in 10 in the working copy. You can of course take this further and implement a hg reset command:

[alias]
reset = !test "$1" = "--hard" && hg strip $2 || hg softreset $2

The biggest problem with these aliases is the poor error handling. An extension written in Python would be much more robust and maintainable -- perhaps you could make such a "reset" extension and publish it on Bitbucket :)

Martin Geisler
  • 72,968
  • 25
  • 171
  • 229
3

See hg strip from the mq extension.

wRAR
  • 25,009
  • 4
  • 84
  • 97
1

You've got some good answers already, but a couple additional notes:

  • If you've got uncommitted local changes, shelve will set them aside for you for later retrieval
  • If your changeset has been pushed to another repository, then short of editing both repositories, a backout is your only option
  • A backout allows you to make a new changeset which is the inverse of the original changeset, getting rid of the undesired changes without changing the history of the repository
  • A rollback is normally your best option if you haven't already pushed to a remote repo since it obliterates the changeset while leaving your working copy intact
  • Strip will get rid of a whole branch of changes, but like rollbacks it will only stay gone if you haven't pushed the changes anywhere else
  • The same goes for cloning a repository up to a prior revision - the other tools are easier anyway
Martin Geisler
  • 72,968
  • 25
  • 171
  • 229
Binary Phile
  • 2,538
  • 16
  • 16
0

Not certain about the git things, but if you haven't pushed, you can use hg rollback. If you have pushed, there's not really anything decent you can do about it.

As for the current revision, see hg id.

Chris Morgan
  • 86,207
  • 24
  • 208
  • 215