9

I have a bunch of commits, say A, B, C, D, and I want to keep B, C, and D, and make a commit that is (BCD)⁻¹. What's the easiest way to do this, if I have many commits that I want to undo at the same time?

(I seem to recall seeing a stackoverflow question about this that suggested that I hg update to A, and then call hg commit with some arguments, but I can't seem to find that question now.)

Jason Gross
  • 5,928
  • 1
  • 26
  • 53

3 Answers3

6

It sounds like you are looking for backout. See:

hg help backout

I don’t think it can back out multiple commits in one go, so you have to back them out individually:

hg backout D
hg backout C
hg backout B

This will create three commits on top of D that are the reverse of D, and C, and B. If you want to combine these commits into one changeset, you can fold them using rebase --collapse or one of a number of other extensions (e.g. the histedit or mq or collapse extensions).

If you don’t want to back out the individual changes but do it all in one go, you could do the following:

hg update A
hg debugsetparents D
hg commit -m "revert B-D"

It’s ugly, but it works. However this does not record renames in reverse. To be honest though, I wouldn’t recommend doing this, if you need to back out so much that individual backout commands are too much trouble to type it makes me wonder if backing out is really what you should want to be doing for that particular case.

Alternatively, you could do as Jim and Rafael suggested, and decide that B, C and D are on a branch, and update back to A and continue committing there (splitting off history at that point). This may be more appropriate.

Laurens Holst
  • 20,156
  • 2
  • 29
  • 33
  • This will do what I want (if I squash the three commits together in mq), but it'll get unwieldy if there are, say, 50 commits that I want to undo. I'm looking for something closer to the second part of the accepted answer in http://stackoverflow.com/questions/1463340/revert-multiple-git-commits (but for hg), i.e., the hg equivalent of `git reset --hard A && git reset --soft @{1} && git commit -a` – Jason Gross Jul 23 '12 at 16:07
  • I updated the answer with a way to do that in Mercurial. However, like the git answer you linked to, it does not actually reverse the changesets. It really makes me wonder though why you would ever want to revert such a large sequence of changesets. It seems better to just park them on an inactive branch, and continue from the change before that. If you describe your use case a bit in your question, it may help give you a better solution to your problem. – Laurens Holst Jul 25 '12 at 09:11
  • Also, you may want to consider submitting a feature request to Mercurial for the backout command to accept a revision set instead of just single revisions. – Laurens Holst Jul 25 '12 at 09:13
5

The easiest way:

You are in changeset D and you want to terminate that branch

hg commit --close-branch -m "Branch closed"

Now, just go to changeset A and continue your work commiting new stuff

hg up -r A
... change stuff ...
hg ci -m "New stuff"

The branch that has B, C and D will be there, but it will be terminated, it won't show in hg heads. It will be an inactive branch.

That's easy to do and expressive too. If you look at the graph you'll see a separate branch that was closed and the "official" branch will go on. Much better than having those revert and backout changesets making noise in your branch.

Rafael Piccolo
  • 2,328
  • 1
  • 18
  • 29
  • 1
    As explained in [another question](http://stackoverflow.com/questions/3688263/mercurial-beheading-a-head), when you try to push the closed head to another repository, you will get a warning about creating multiple heads. So, use `hg push --force` the first time you push. People who pull the closed head won't get any warnings. – mernst Jun 21 '13 at 15:14
1

You've answered your own question, update back to A and just continue from there. If you need to make a new head at that point, you'll need to make a change to some file or the other as described here: How can I force mercurial to accept an empty commit . A mail thread linked to from that post describes a way to use MqExtension to remove BCD (unless you've pushed them):

Community
  • 1
  • 1
Jim Downing
  • 1,481
  • 12
  • 29
  • This, but only if D is a HEAD. There's no garbage collection in hg, so B, C and D will not be lost. D will just remain a HEAD that you can push or not - up to you. – DanMan Mar 28 '14 at 21:23