182

There is a commit that just didn't work, so I want to abandon it without deleting it from history.

I have updated from an earlier revision and committed, thus creating a new head.


I don't have branches, I don't want branches, I just want to simply go on with the new head exactly as it is, nothing fancy, no merge, no worries, just go on forgetting the previous one.

I can't seem to find how to do that, and I'm starting to believe it can't be done. All I find is stuff about branches, or stuff about merging.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
o0'.
  • 11,739
  • 19
  • 60
  • 87
  • 1
    It's in your repo, so it hasn't been deleted from history. You've created a new head, so you can go on making revisions without the mistake. What is preventing you from going on with the new head? – ataylor Sep 10 '10 at 23:14
  • What is with your aversion to branches? – Andres Jaan Tack Sep 11 '10 at 09:19
  • @Andres It's not exactly aversion to branches. I just needed it to work without a stupid extra step of creating one just to close it. – o0'. Sep 14 '10 at 16:13
  • Anyone reading - please note that a branch has already been created in this scenario; note the explanation given in this answer:https://stackoverflow.com/a/3692607/3195477 – StayOnTarget Oct 24 '18 at 12:04

9 Answers9

182

Update your repository to the head with the revision that you want to forget about, then use hg commit --close-branch to mark that (anonymous) branch as closed. Then update to the head of the branch that you do want, and continue working.

You can still see the closed branch if you use the -c option to hg heads, but it won't show up by default and hg merge will know not try to merge with the closed head.

You will need to use hg push --force the first time you push this closed head to another repository since you are actually create additional heads in the remote repository when you push. So tell Mercurial that this is okay with --force. People who pull the closed head wont be bothered by any warnings.

Lorem Ipsum
  • 4,020
  • 4
  • 41
  • 67
Niall C.
  • 10,878
  • 7
  • 69
  • 61
  • 3
    @Niall C. won't that only work if he has marked that as a named branch? I'm assuming from what he's saying he did that it is in default – msarchet Sep 10 '10 at 21:04
  • but... that's not true, I still get both heads listed when I call `hg heads`... I'm using mercurial 1.4.3, is that a newer feature? – o0'. Sep 13 '10 at 14:29
  • 2
    @msarchet: AFAIK, trying it today, --close-branch does NOT work for anonymous branches. It should, but does not. I hope this changes in some future version of Mercurial. Anonymous branches are very nice, but should be as first class as named branches. – Krazy Glew Jan 06 '12 at 07:09
  • 5
    @KrazyGlew: The problem is that an "anonymous" branch is really just a second branch with the same name as the branch it was based on. You're not really trying to close the (named) branch: you're trying to discard the changes you've been making. In other words, `hg branches` should still show the branch name you're on. Rather than trying to close the branch, merge your anonymous branch back into the original branch, discarding all changes. – StriplingWarrior Feb 24 '12 at 21:19
  • 2
    In my case, I have a branch called default (this is standard) and another called default/master (I think due to the fact that the remote depot is actually git). hg update default/master; hg commit --close-branch; hg update default worked for me. – MattD Feb 12 '14 at 01:49
68

I know you don't want to work with branches at this stage, but that's exactly what you've done. When you went back to an earlier version and committed something that worked you created a branch - an unnamed branch, but a branch all the same.


There's no problem with just carrying on just as you are and not worrying about having multiple heads, but if you want to tidy things up so you don't accidentally pick the wrong head one time then you can kill off the old branch.

There's a good section in the Mercurial documentation that takes you through a number of options around Pruning Dead Branches.

I think the best option for you is to mark the old branch as "closed". If your old head is revision "123" then:

hg update -r 123
hg commit --close-branch -m 'Closing old branch'
hg update -C default
StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Nick Pierpoint
  • 17,641
  • 9
  • 46
  • 74
  • 3
    Blurgh - just saw @Niall's answer after I'd entered. Will upvote Niall's and mine can languish in the zero points pool. :) – Nick Pierpoint Sep 11 '10 at 20:58
  • 1
    I like your answer better, it requires groking less of merucrial's terminology (which as near as I can tell seems to be chosen to confuse git users) – tacaswell Oct 27 '12 at 02:46
  • 8
    lol it's the opposite! mercurial's terminology was chosen to sound natural to svn users, while git's one is confusing as hell! anyways, upvoting this answer because it includes the last update -C – Tobia Nov 09 '12 at 18:08
  • 2
    Why do you need `-C` in `hg update`? It would seem that no files would have been modified, so it should work without it. – max Nov 19 '12 at 01:17
  • 1
    as far as I can tell, you don't need a -C anywhere. however, if you do have outstanding changes when you try to update, you'll get an abort. – Eli Albért Mar 11 '13 at 20:11
23

First of all, type:

hg heads

Imagine, you have three heads listed:

changeset:   223:d1c3deae6297
user:        Your name  <your@email.com>
date:        Mon Jun 09 02:24:23 2014 +0200
summary:     commit description #3

changeset:   123:91c5402959z3
user:        Your name <your@email.com>
date:        Sat Dec 23 16:05:38 2013 +0200
summary:     commit description #2

changeset:   59:81b9804156a8
user:        Your name <your@email.com>
date:        Sat Sep 14 13:14:40 2013 +0200
summary:     commit description #1

Let's say, you want to keep the last head active (223) and close the rest.

You would then do as follows:

Close head #59

hg up -r 59
hg ci --close-branch -m "clean up heads; approach abandoned"

Close head #123

hg up -r 123
hg ci --close-branch -m "clean up heads; approach abandoned"

Commit the changes

hg push

Don't forget to switch to the right head at the end

hg up -r 223

And you're done.

Artur Barseghyan
  • 12,746
  • 4
  • 52
  • 44
  • This is a nice tutorial, but the example commit messages are a bit meta. I would include better examples so that those learning from you might provide better commit messages. Something like `--close-branch -m "Closing branch - technique #2 abandoned in favor of technique #3"`. – Jason R. Coombs Apr 30 '15 at 14:24
  • 5
    Also, you're sorta done at the end, except that your working copy is still on the head which you just closed. Committing another change would happen on the closed head, reopening it. You'll want to `hg up -r 223` before making any changes. – Jason R. Coombs Apr 30 '15 at 14:26
  • @Jason R. Coombs: Right! – Artur Barseghyan May 04 '15 at 11:52
  • according to @Niall, and my own experience just now, you will need `hg push --force`, not just `hg push` to get past the warning about pushing multiple heads. – craq May 25 '18 at 04:05
  • @craq: From my experience, ```--force``` directive is really the very last resort you should think of. – Artur Barseghyan May 25 '18 at 07:51
  • 1
    @Artur I agree in general. In this case, `hg push` by itself didn't work for me. How do you recommend pushing changes to an external repo if it refuses because of multiple heads? – craq May 25 '18 at 11:05
12

You want to use hg backout. This removes the changes made by the changeset from any child changeset.

Check this out for a good explanation. Mercurial Backout

msarchet
  • 15,104
  • 2
  • 43
  • 66
  • 2
    This is exactly the right answer. Backout add the inverse of a changeset, undoing the working and giving you a commit message to remind yourself why you didn't like the idea. – Ry4an Brase Sep 11 '10 at 02:47
  • 7
    I actually disagree -- abandoning work on one head and starting over from a good starting point seems like a cleaner work pattern than using backouts. Especially since you cannot backout more than one changeset at a time. – Martin Geisler Sep 11 '10 at 09:18
  • 1
    @Martin Geisler, well I agree with this in general, but the OP stated he wanted to abandon the changes without branches – msarchet Sep 12 '10 at 03:06
  • msarchet: okay, I see. I focused more on the part about going on with the new head. For quickly canceling a single changeset, backout (or plain `hg revert --all --rev GOODREV`) are fine options. – Martin Geisler Sep 12 '10 at 11:04
  • 2
    @Martin Geisler yea I'm all for branching just sometimes nuking a bad change is the best – msarchet Sep 12 '10 at 16:54
  • 1
    it might be useful sometimes, but that was not really what I wanted. Thanks anyway :) – o0'. Sep 13 '10 at 21:02
3

An alternative to closing or stripping the unwanted branch would be to merge it in a way that totally discards its effects, but leaves it in history. This approach will allow those unwanted changes to propagate in a push - so only use this if that is the intended effect.

Let's say the changeset history looks like this:

1-2-3-4-5-6    
       \    
        7-8-*

and it is 5 and 6 which are no longer wanted.

You can do this:

hg up 8
hg merge -r 6 -t :local
hg commit ...

which will create this:

1-2-3-4-5-6    
       \   \
        7-8-9-*

The update to 8 ensures you are working at the desired head in history, which you want to keep.

The -t :local instructs hg to use the merge "tool" called local which tells it to ignore changes from the other branch, i.e., the one NOT represented by the current working folder state. More info.

Thus the unwanted changes in 5 and 6 are preserved in history but do not affect anything more recent.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
2

Both Niall's and Nick's answers are straight on. Because I find myself creating lots of dangling heads, I ended up writing an alias to close heads more easily. By adding this to your .hgrc:

[alias]
behead = !REV=$($HG id -i); $HG update $@ -q && $HG ci --close-branch -m "Closing dead head" && $HG update $REV -q

(if you already have an [alias] section, you can append to it instead)

You can now close a head in one single-command (and without having to update to a different changeset manually) like this:

$ hg behead 123

Note: the alias takes advantage of the fact that Mercurial aliases can be shell commands. This means that this will probably only work on UNIX, not on Windows.

jjst
  • 2,631
  • 2
  • 22
  • 34
2

This is a use case for the Evolve extension. It's currently not bundled with Mercurial, so it is technically a third party extension. But it's being used quite heavily by a bunch of people, including Mercurial developers, is being very actively developed, and isn't going anywhere.

With the Evolve extension, you simply do

hg prune -r revname

and get on with your life. The cset will still be there, but obsoleted. It won't be visible unless you pass the --hidden option to Mercurial commands, and by default won't be pushed to remote repositories. Though I think you can force it if you really want to.

If the cset you are pruning has ancestors you want to keep, then you'll have to run hg evolve to rebase those changesets. hg evolve will do so automatically. Otherwise, you don't have to do anything.

Faheem Mitha
  • 6,096
  • 7
  • 48
  • 83
  • Unfortunately, `hg prune` change doesn't seem to be pushable / doesn't propagate to other clones of the repo. I'm looking for a way to forget / close / delete a very wrong commit that breaks the repo due to wrong revision hash in `.hgsubstate`, any advice? – Violet Giraffe Jul 26 '22 at 20:33
  • @VioletGiraffe The `hg prune` change is definitely pushable to other clones, otherwise it wouldn't be useful, would it? It's probably a configuration issue. But without more information I can't help you. I suggest either posting a separate question on SO, or asking for help on an official forum. IRC is a reasonable place to ask quick questions. #mercurial on Libera.Chat. Or there are official Mercurial user mailing lists, though not used so much. – Faheem Mitha Jul 27 '22 at 11:30
  • It is no less useful than `hg rollback` or `hg strip`, the other two local history-fixing commands. And I'm 100% sure `prune` changes cannot be pushed, I tried a few times and in different ways. Thanks for the advice, I had no idea about the Mercurial IRC chat! – Violet Giraffe Jul 27 '22 at 11:31
  • Both `hg rollback` and `hg strip` are obsolete (if that is the right term for something that one should probably not have been using in the first place). They are destructive commands, so not allowed in Mercurial proper. Part of the point of the Evolve extension is that it conforms to Mercurial's append-only (non-destructive) paradigm. And you can't be 100% sure of this, because what you are saying is incorrect. :-) Maybe talk to people who actually know something about Mercurial? – Faheem Mitha Jul 27 '22 at 17:29
1

You may clone your corrupted repo to a new one without cloning that unwanted head. Then remove old repository, move newly created clone to the original place and continue working with it. This will take some time, but you'll get a perfectly clean repository without a sign of that unwanted revision.

hg clone --rev myGoodResition myDirtyRepo myCleanRepo
kjam
  • 809
  • 1
  • 7
  • 17
-1

I have run into this issue many times when I want to behead a head that was created in error. I always want to see it disappear off the face of the Earth.

On your local copy, get the latest and then:

  1. Find the beginning of a head you want to strip (where a new neck starts to branch off), get the revision number

  2. Strip it.


Source: TipsAndTricks.

Source: PruningDeadBranches#Using_strip.

hg --config extensions.hgext.mq= strip -n <rev>
  1. Make a trivial file update (add a whitespace to a file), commit and push.

Your repo should now have the head stripped. The last step is important as stripping doesn't create any changes you can push to your central repository. Without the last step you only have stripped the head locally.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
sonjz
  • 4,870
  • 3
  • 42
  • 60
  • 1
    But, pushing never deletes anything from the remote repository. It only ever *adds* information. If the changeset is already on the central repository, you either need to use the evolve extension, or somehow strip it on the central server itself. If the changeset is not already on the central repository, then stripping it locally will suffice without any push required. – Ben Jan 20 '16 at 17:01