How do I revert multiple commits in Mercurial? - mercurial

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.)

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.

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.

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):

Related

How can I backout of a Mercurial changeset in a working directory and not commit it to the repo?

Backout works by applying a changeset that's the opposite of the
changeset to be backed out. That new changeset is committed to the
repository, and eventually merged.
https://www.mercurial-scm.org/wiki/Backout
How can I backout without commiting the changeset? I just want the changeset reverted in a working directory.
Try hg backout --no-commit REV
This will perform the backout but leave the changes uncommited.
Assuming you have already committed a new changeset but not yet pushed it; let's say your history looks like this:
A--B--C--★
where C is the recently committed changeset you wish to do away with, but leave its modifications in the working folder. And ★ is the working directory (not an actual changeset itself).
There is more than one way to do this. One approach is the following...
hg up B
This leaves your history looking like this:
A--B--★
\
C
Then do
hg revert -r C
which in effect copies whatever changes were in C into your working folder.
Then you could do (optional)
hg strip C
which eradicates C from history:
A--B--★
An advantage of this approach is that it removes C entirely, like it never existed.
(I mentioned that using strip is optional in this sense: if you did leave C in place, it causes little harm. And you'd never need to push it if it is marked secret. But personally I would clean it up by stripping.)

Mercurial: How to deal with one branch that has two heads

What if one branch has two heads? I came to this situation weeks ago for some reason I didn't merged them back then and just continued developing on one head. Now I want to get rid of the other head. What should I do? Should I just merge them after so many changesets?
So you have this:
o--o--o--A--B <- older unnecessary head
\
1--2--3--4--5--6 <- newer ‘good’ head
...and you don't need A and B, absolutely, 100% sure. If you aren't sure, and there are possibly salvageable things in A and B, better merge the heads to combine the changes. As Aaron said, Mercurial is good at it.
Now you have two options:
get rid of the old head, or
do a dummy merge of two heads that ignores head B.
If changesets A and B are present in other repositories you don't control, e.g. if other people pulled A and B into their repositories, or you pushed A and B to a public repository (say, Bitbucket), then you've released A and B into the wild, and can't get rid of them. You should do a dummy merge:
$ hg up 6
$ hg --config ui.merge=internal:local merge
This will ignore any changes from A and B when merging.
If, on the other hand, A and B are private, you can either strip them:
$ hg strip A
(Rev A and descendants stripped; enable the MQ extension to make strip available.)
Or, make a new clone of your repository without changesets A and B:
$ hg clone myrepo myrepo2-clone -r 6
(Only rev 6 and ancestors added to the clone.)
A head is created if you commit some changes (add a changeset to an existing changeset). If that happens several times for the same changeset, then several heads are created. This is nothing unusual or bad.
The usual solution is to merge them. Mercurial is very good at merging. Chances are that the result will still compile and run without any manual work.
Just run hg merge and then hg status and hg diff to see what changed between the two. If you like the result, commit it. If you don't like it, update to either head to clean your workspace.
To get rid of a head, the docs explain how to do that.
Using MQ, you can also turn the second head into an independent branch (so you can keep it around but it won't bother you). See this answer: How can I create a branch for a non-tip revision in Mercurial?
If you need to keep the changes on the old branch, you should merge them, and it should not be a problem.

Mercurial moving commits in another branch

My coworker accidentally made two commits in the default branch instead of creating new his own development branch.
How can I change this situation and moves these two commits to a new branch?
Imagine the following scenario:
D
|
C
| "I want to move C and D here"
B/
|
A
Steps:
hg update B
hg branch "mybranch"
hg commit --message "Create my branch"
hg update mybranch
hg graft -r C
hg graft -r D
hg strip -r C (this should be the revision C had originally)
The strip command is provided by an extension that you need to enable. You can follow a guide on how to enable it on the Mercurial Wiki.
hg update default
A major question
Have the accidental commits reached other repositories or is it just in his own? If so, you can skip to the section below 'Maybe the cat is still in the bag' otherwise you may have a fair bit of work to do.
You are not alone
See here for more discussion on how to correct the problem elsewhere on Stack Overflow. What is described is the 'proper' way to to it
export a patch
create the branch
import the patch
delete the earlier commits.
Maybe the cat is still in the bag
If the changes are only in the local copy, then a simpler solution is to
create the new branch
switch to it
merge the changes onto that either with your fav merge tool (go Meld) or with hg graft
use the hg strip command to delete the changes on the old brach
push the changes to the world
pretend like nothing ever happened, whistle a happy tune ...
The two answers above are both correct but, assuming one has not yet pushed the commits, there's a third way.
I just successfully used the rebase command to move a string of commits to a topic branch I had forgotten to create in the first place.
I first updated to the revision from which I wanted to create the branch on which my commmits were supposed to be, then I rebased the earliest of my commits from the wrong branch on this new one and ta-da, done.
Takes more time to explain it than to do it with TortoiseHg or even the command line, really.

Mercurial Reverting Back, then Reapplying Changesets

I have commits A, B, C, D, and E. I realize that something very bad happened in commit B, so I want to revert back to A, make the change correctly this time that screwed up B before, and then reapply C, D, and E automatically.
You may be wondering why I don't revert back to B and make the fix there, then remerge back in to E (is this ever a good idea?). The reason is not well understood by me, but it has something to do with the problem occurring in a set of special visual studio files (that should only be edited via some GUI screens in visual studio) that don't play well with simply correcting the file after an error occurred... I would give more details if I knew them
Just make a backout of what you did in B and commit it as F. This way, history will be intact, and your peers will get the change without having to know about it.
If B is a service release, do make the change there and merge it into F afterwards.
This can be done using Mercurial Queues (mq). You want to:
Import changesets B through E to mq
Unapply changesets C through E
Fix changeset B and refresh the patch
Reapply C through E
Finalize the patches
This is done as follows:
cd <project>
hg qinit
hg qimport --rev B:E
hg qpop --all
hg qpush <patch name for B>
...fix the problems you found in B
hg qrefresh
hg qpush --all
hg qfinish --applied
This all assumes that B through E have not been pushed to any public repositories. If they have already been pushed, then your best bet is to simply fix the problem in a new changeset (F).
You can also use the HisteditExtension (instead of MqExtension). MqExtension is much more powerful, but it's also much more complicated, I think. HisteditExtension is a bit more like git rebase --interactive.
# Ordinarily it would be something like (I'd normally do -r -5, instead):
hg histedit d0844102a010
Your text editor will be opened with a file that looks something like this:
pick d0844102a010 A
pick a9448f0ba534 B
pick b754f9f2513b C
pick 736f7f2363ff D
pick 05bb58f48597 E
# Edit history between d0844102a010 and 05bb58f48597
#
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
# f, fold = use commit, but fold into previous commit
# d, drop = remove commit from history
#
Each line corresponds to a commit. The first word refers to a command to be applied to that commit. The default "pick" just keeps the commit as is, unchanged. Use "edit" to make changes (including commit log changes), "fold" to combine it with the previous commit, and "drop" to delete it entirely.
In your case, you'd probably just need to change the first line to "edit".
Remember that you must use hg histedit --continue instead of hg commit (if you're "editing" or if there's a merge conflict). :) If you get conflicts and things aren't looking good and you just want to cancel then you can use hg histedit --abort.
# Fix up files...
vim foo bar baz
# Finished; apply the changes (and pray for a clean merge ;).
hg histedit --continue
Edit history at own risk, of course. I recommend that you create a backup tarball or zip of your source tree prior to editing history until you're familiar with the commands.

How to compare sets of changesets between 2 Mercurial branches?

I've got a (remote) Hg repository with a couple branches. I want to verify that branch A has every changeset that branch B has (it may have more, and that's OK).
Is there an easy way to do this with just hg?
I can write a little shell script to do it, but it seems like the sort of thing that might come up a lot, so maybe there's an easy built-in way.
This will show any ancestors of changeset b which are not an ancestor of changeset a:
hg log -r "ancestors(b) and not ancestors(a)"
This should show you which changes still need to be merged from B to A if you give the head of branch B for b, and the head of branch A for a.