Mercurial Reverting Back, then Reapplying Changesets - mercurial

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.

Related

Mercurial: retrospectively merging revisions

I have just been asked by a fellow developer whether they need to do anything when that merge window appears. Apparently their default course of action has been to simply close the window immediately.
Anyway, we're now in a situation where they have made minor changes to a number of files where another developer made a large number of changes. They then 'merged' them, essentially backing out all remote changes to the files, committed the code, and pushed to the central repository.
So we're in the situation that there is revision x with changes we want, revision y with changes we want, and revision z that is a merge of x+y, but actually just contains the code from y.
Is there a way now to retrospectively get back into the merge tool?
I'd like to run a command and get a meld window up to let me merge back in the changes from y into the current revision.
First, clone your repository somewhere, and test these instructions in there. I tested them on a very small repository made for the purpose, so I may have overlooked something.
hg clone yourrepo testrepo
cd testrepo
You can check out the code as of revision x, and repeat the merge. That will give you a new head. Then, if you have not made changes since, you can use hg strip to get rid of the bad one. If you don't want to risk the strip, then merge the two heads, telling hg to keep the version of the code in the good version. If you have it checked out, as you will immediately after making it, then give the argument -t internal:local to throw away the changes from the other, bad, head. It's a lot simpler to do than to explain:
hg update x
hg merge -r y
hg commit -m 'Redo the merge.'
Then either:
hg strip z
or:
hg merge -t internal:local -r z
hg commit -m 'Get rid of the bad merge.'
You can look at the state of the graph with the very useful hg view extension.
Once again, I tested this on a toy repository. Back it up first!

How do I revert multiple commits in 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):

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.

TortoiseHG: cannot partially commit a merge

I tried to merge two heads in Mercurial. After merging, I didn't commit and did some more changes. Then I tried to commit and got the following message:
abort: cannot partially commit a merge (do not specify files or patterns)
I'm using TortoiseHG as visual shell, and Beyond Compare for comparing and merging. And I'm relatively new to all of them.
What should I do to finish commit successfully?
Mercurial/TortoiseHg is correct in telling you that you should not partially commit a merge. Partial means that you do not commit all files at once.
The underlying reason for this message is that is gives you the wrong results. When you merge two changesets in Mercurial, you are creating a new changeset with two parent changesets. This merge changeset shows others how you want everybody else to combine the two changesets.
Let us imagine that you start with changesets A and B and want to merge them. This creates a graph like this:
... --- [A]
\
[M]
/
... --- [B]
Pretend that we added the line A! to a.txt in the A changeset and that we added B! to b.txt in the B changeset. Just two independent changes that does not conflict. If Mercurial allowed you to do a partial commit, then you could do this:
hg merge
hg commit -m 'Added A!' a.txt # creates M
hg commit -m 'Added B!' b.txt # creates M'
and the result is this graph:
... --- [A]
\
[M] --- [M']
/
... --- [B]
If you look at b.txt along the path B, M, M', then you will see that the line with B! was introduced in B, removed in M and reintroduced in M'!
This is not what you want from a merge changeset: a partial merge throws away changes from one branch just to introduce them again in a followup commit. Mercurial trusts you when you create M: it really believes that M contains the correct mix of A and B. In particular, if the B! line is removed in M, then it will remain gone when you merge M with other changesets.
So Mercurial is trying to protect you from creating a bad history by not allowing partial merges.
I think you have aliases in hgrc file. Try to remove [alias] section and commit again.
What should I do to finish commit
successfully?
One of the benefits of Mercurial (or git or any other DVCS) is that you can perform a commit at any point in your development process and it will be both fast and private. It will be fast because you should be committing to a local copy of the repository residing on your hard drive, and it will be private because no-one will see your change set until you push them to the server (or other master repository).
Therefore, to partially answer your question, the appropriate thing to do would have been to commit the merge without your addition changes, and then apply and commit your next wave of changes. If you are using TortoiseHG to peform the merge it will actually prompt you to commit the merge before leaving the GUI because this is the intended HG workflow.
That being said, I made some changes on a named branch (ie: new head), merged it back into the default branch, exited the TortoiseHG GUI without committing, made some more changes, and then committed with no problem. I will ask some clarifying questions below your original inquiry.
I had this problem because I had deleted some files. I reverted the files to restore them, then was able to commit. I then deleted the files and did a second commit.
I had the same problem and I was NOT specifying any files and using command-line.
The problem was the [default] section in my .hgrc
[defaults]
commit = -X project/web.config
So it added specific files to every commit-operation by default.
Review your defaults section for potential problems.
I had the same problem and the command I gave was
hg commit -m Merge with 1234
I figured it out after sometime that the commit message "Merge with 1234" has to be given in quotes as the command takes "with" and "1234" as file name params.
please check it in your case.

Mercurial cherry picking changes for commit

Say, I made many changes to my code and only need to commit a few of those changes. Is there a way to do it in mercurial? I know that darcs has a feature like this one.
I know hg transplant can do this between branches, but I need something like this for committing code in the present branch and not when adding change sets from some other branch.
If you are using TortoiseHg 1.x for Windows, this feature is implemented beautifully right out of the box (no extensions required).
Run the TortoiseHg Commit Tool.
Choose a file for which you only
want to commit a subset of its
changes.
Click on the Hunk
Selection tab in the preview pane.
Double-click or use the spacebar to
toggle which change hunks should be
included in the commit.
For TortoiseHg 2.x, the Hunk Selection tab is now gone. In its place, is the Shelve tool. It has a few more features than the old hunk selection. Those new features come at the cost of some added complexity.
Note that there is no need to explicitly enable the Mercurial Shelve extension when using this feature. According to Steve Borho (lead TortoiseHg developer) in response to a different TortoiseHg question: "We have a local copy of the shelve extension and call into it directly."
For TortoiseHg 2.7+, this functionality has been improved and re-introduced. It is now built directly into the Commit tool:
Notice in the file list on the left that the top file is checked to indicate it will be included, the second file is unchecked because it will not be included, and the third file, Sample.txt, is filled (the Null checkbox indicator) because only select changes from that file will be included in the commit.
The change to Sample.txt that will be included is checked in the lower-right change selection portion of the image. The change that will be excluded is unchecked and the diff view is grayed out. Also notice that the icon for the shelve tool is still readily available.
MQ as Chad mentioned are one way. There's also more lightweight solutions:
Record extension which works roughly the same way as darcs record. It's distributed with mercurial.
Shelve extension which allows you to "shelve" certain changes, allowing you to commit only a subset of your changes (the ones that are not shelved)
I feel like I'm missing something because nobody has suggested this already.
The normal "hg commit" command can be used to selectively choose what to commit (you don't have to commit all pending changes in the local working directory).
If you have a set of changes like so:
M ext-web/docroot/WEB-INF/liferay-display.xml
M ext-web/docroot/WEB-INF/liferay-portlet-ext.xml
M ext-web/docroot/WEB-INF/portlet-ext.xml
You can commit just two of those changes with...
hg commit -m "partial commit of working dir changes" ext-web/docroot/WEB-INF/liferay-display.xml ext-web/docroot/WEB-INF/liferay-portlet-ext.xml
Not super convenient from the command line because you have to hand-type the files to selectively commit (vs a GUI check-box process like tortoise) but it's about as straightforward as it gets and requires no extensions. And file-globbing can probably help reduce typing (as it would above, both committed files uniquely share "liferay" in their pathnames.
The Mercurial Queues tutorial is terrible for this use case. All the examples I have seen assume you have yet to make a commit and you are refreshing a single patch. Most of the time this is not the case, and you have 2 or 3 commits that you want to squash together or change in some other way.
Lets say you have this sort of history:
---O---O---A---B---C
The first example is to squash commits A, B, and C. First init mq:
$ hg qinit
Now we need to "import" the commits A, B and C into the patch queue. Lets assume they are the last 3 commits. We can use the "-N" revision syntax to import them like so:
$ hg qimport -r -3:-1
That means import as patches from 3 patches back up to the last commit. You can check the status of these patches with hg qseries. It should show something like this:
$ hg qseries
101.diff
102.diff
103.diff
Where the numbers 101, 102 and 103 correspond to the local revision numbers of the commits A, B and C. Now these patches are applied, which means the changes that they describe are already in the working copy. You can get rid of the changes the working copy and remove them from the history of commits, saving them in patch form only, by using hg qpop. You can either say hg qpop; hg qpop to pop changes C and B off the stack, or specify a patch to "pop to". In this case, it would be something like this:
$ hg qpop 101.diff
now at: 101.diff
You now have the patches for commits B and C in the patch queue, but they are not applied (their changes have been "lost" - they only exist in the patch queue area). Now you can fold these patches into the last one, i.e. we create a new commit that is the equivalent of the sum of the changes A+B+C.
$ hg qfold -e 102.diff 103.diff
This will show your editor so you can change the commit message. By default the message will be the concatenation of the commit messages for the changes A, B and C, separated by asterisks. The nice thing here is that hg qfold will tab-complete the patches if you are using bash and have the hg-completion script sourced. This leaves the history like this, where A+B+C is a single commit that is the combination of the 3 patches that interest us:
---O---O---A+B+C
Another use case is if we have the same sort of history as before, but we want to drop patch B and merge A+C. This is pretty similar to above actually. When you get to the qfold step, you would simply fold in the last commit rather than the last 2 commits:
$ hg qfold -e 103.diff
This leaves the change for B in the patch queue, but it is not applied to the working copy and its commit is not in the history. You can see this by running:
$ hg qunapplied
102.diff
The history now looks like this, where A+C is a single commit that combines changes A and C:
---O---O---A+C
A final use case might be that you need to apply only commit C. You'd do this by running the qimport as above, and you would pop off all patches you didn't want:
$ hg qpop -a
The -a flag means pop off all patches. Now you can apply just the one you do want:
$ hg qpush 103.diff
This leaves you with this history:
---O---O---C
Once you are done with all this, you need to finish off the queue fiddling. This can be done with:
$ hg qfinish -a
So there we are. You can now run hg push and only commit exactly what you want, or hg email a coherent patch to the mailing list.
Some time has passed. Seems the best option now is hg commit --interactive
You can use the record extension, which is distributed with Mercurial.
You need to enable it in your ~/.hgrc file first, by adding it to the [extensions] section:
[extensions]
record=
Then, just type hg record instead of hg commit, and you will be able to select which changes to which files you want to commit.
You can also use the crecord extension which provides a nicer interface to review and select the changes. (It is not distributed with Mercurial, though, and I've seen it occasionally mess up a commit so it's not completely bug-free.)
I believe Mercurial Queues fills this role for Mercurial. There's a pretty good tutorial linked there.
Try qct (Qt Commit Tool). It has a "select changes" feature that starts up a 3-way merge tool for you to undo individual changes. After you commit, those changes you "undid" come back.
I use commit-patch. It's a script that lets you edit the diff before committing. It's really nice with Emacs's diff-mode and vc-mode.
In the past I used crecord, but it has bugs related to unicode (actually the record extension has the bugs, which crecord depends on).
First you must forget everything you ever knew about GUI's and return to the commandline. Next from the commandline do this:
hg stat > filelist.txt
This pipes all your modified files into a text file called filelist.txt
Next edit your filelist to include only the files you wish to commit.
Finally commit using the fileset sytnax:
hg commit "set: 'listfile:test.txt'"