I've recently started working with MQ as I like the idea of working on isolated patches and committing without affecting the repo until the changeset is refined enough. Before that, I used to work with Mercurial's shelves extension, but found it a bit unstable. What I'm still trying to figure out in MQ is how to keep patches separate from each other and apply them in no particular order, and across different branches. Here's my normal flow -
1. Start working on a new patch:
hg qnew fix-bug-1234 -m "fix bug 1234"
# do some work
hg qrefresh
2. Get a new feature/bug to work on:
hg qpop fix-bug-1234
hg qnew some-feature -m "implement feature X"
# some work on feature X (perhaps in a different branch)
hg qrefresh
3. At this point, I'd like to get back to working on bugfix, and put aside the feature work. I thought it's as simple as:
hg qpop some-feature
hg qpush fix-bug-1234
# wrap up bug fix
hg qfinish fix-bug-1234
# get back to work on feature
However, MQ seems to always use the latest patch created in the series, and apply it regardless of the qpop/qpush command I'm using. I should note that the files I work on are completely separate as well (though they can sometimes be the same).
Am I missing something here? Should I be using hg qqueue for this? Thanks.
You could use guards. They allow you to maintain an ordering of patches without rearranging your series file, and selectively apply only a subset of patches, still in a stack-ordered fashion.
An example in your case would be:
hg qnew bugfix
# ..hack hack..
hg qrefresh
# want to switch over to working on some feature now
hg qpop
hg qnew feature
# ..hack hack..
hg qrefresh
At this point, you're in a situation where patch feature comes before bugfix in your stack. Now you can use guards to select one or the other, and switch between the two:
hg qpop -a
hg qguard feature +featureguard
hg qguard bugfix +bugfixguard
If you want to work on feature:
hg qselect featureguard
hg qpush
applying feature
now at: feature
If you want to work on bugfix:
hg qpop -a
hg qselect bugfixguard
hg qpush
applying bugfix
now at: bugfix
Note that since you selected the positive guard bugfixguard, MQ leap-frogged over feature (because it's positive guard was different than the one selected) and applied the patch bugfix instead (which did match the selected guard).
Some useful tools when working with guards are hg qseries -v, which will display a G instead of the usual U for a guarded, unapplied patch, and hg qselect -l which will display the guards associated with each patch.
Execute hg qpop -a to remove all patches from the stack
Execute hg qpush --move some-patch to apply "some-patch" without applying whatever other patches may be before it in the patch stack
No, you aren't missing anything. The mq extension does make a pretty strong assumption that patch queues are linear. If you're going to be creating multi-patch features/fixes then qqueue would work… But if your features/fixes are just single patches and you want to be able to apply one with out applying the others, it might be easier to just re-arrange .hg/patches/series (which stores the order that patches will be applied).
I do this (and hand-editing patches) enough that I've got a shell alias:
alias viq='vim $(hg root)/.hg/patches/series'
Alternately, if you don't mind applying multiple patches at the same time, you could use qgoto:
$ hg qser
0 U bug-1234
1 U feature-4321
$ hg qgoto feature-4321
$ hg qser
0 A bug-1234
1 A feature-4321
Related
I want to commit only some of the modifications I made to my files and my preferred way to do this is to shelve/stash away the changes I don't want to commit. This allows me to test the changes I will commit before actually commiting them.
The problem I am having is that when I use the "shelve" tool in tortoisehg I can't find any way to split a chunk into two smaller chunks. For example, I have a chunk that looks like this:
## -1,1 +1,2 ##
-hallo world
+hello world
+something else
I would like to shelve away the "something else" so I can commit just the "hallo->hello" fix. However, since tortoisehg is seeing this a single chunk I can either shelve both or none of the changes.
I also tried using the shelve extension via the command line but from what I understood from the documentation it does not offer the feature to shelve away only parts of the files.
You can accomplish your goal from the command line. There are interactive versions of hg commit, hg revert, and hg shelve, and they have low-level options to hack a patch together if needed; you can use them with the -i (or --interactive) command line option.
This means that you can build up a commit with hg commit -i and hg commit --amend -i. If you have the evolve extension installed, you can use hg uncommit to pull changes out of the commit again; if not, you can use hg revert -r .~1 or hg revert -i -r .~1 a file and use hg commit --amend -i again to fix it up.
In order to select individual lines from a patch, you have two options. You can use e to edit the patch (but see below for a more convenient option).
You can then use hg shelve to shelve the remaining changes.
You can in principle also do this with hg shelve -i, however, there's a big caveat; if you edit patches for hg shelve -i, then Mercurial's merge mechanism will get confused and not process the change cleanly, but dump you in a merge tool in order to resolve this apparent conflict (which means lots of extra work to resolve it). Hence why I strongly recommend using hg commit -i and hg commit --amend -i if you want to manipulate things at the line level (hg shelve -i works fine if you don't edit the patches).
For added convenience (that prevents you from editing patches), you can also enable the experimental crecord option by adding the following to your hgrc file:
[experimental]
crecord = true
This will enable a terminal-based hunk editor for hg commit -i, hg revert -i, and hg shelve -i that allows you to select individual lines and hunks (internally, that works roughly the same way as editing patches). Use the ? key to get help in that editor; use f to unfold/fold individual hunks and the space key to select hunks/lines.
Note that line-based selection for this tool in conjunction with hg shelve -i comes with the same caveats as editing patches; i.e. use hg commit -i and hg commit --amend -i instead if you want to do line-based selection. Also, line-based selection for hg revert -i will still revert the entire hunk. (There's a reason why this option is still marked as experimental.)
I don't know a command to do this, but if you are using tortoiseHg, you can do that by selecting modified sections (checkboxes) in the commit preview area.
See Mercurial cherry picking changes for commit
This answer shows how you can demote a commit to a patch, but how can I convert an mq patch to local changes only?
Short answer
Make sure the patch is applied, then:
hg qrefresh nothing
hg qpop --keep-changes
hg qdelete "Name of patch"
Long answer
First, you need to make sure no changes are tracked by the patch. To do that, use
hg qrefresh nothing
nothing is just a random file name that is not in the repository. I usually use hg qref 0 for brevity. hg qrefresh accepts an optional file pattern. If it is given, the patch will track the changes that match the pattern - and only those. When nothing matches the file pattern, no changes will be tracked by the patch, and thus there will be local changes only.
Now you have a useless patch lying around, and you have some local changes. To clean up, you can do
hg qpop --keep-changes
to pop the patch even though there are local changes. Finally, to remove the dead, empty and unapplied patch you can use
hg qrm "Name of patch"
You can't remove an applied patch, which is why you need the hg qpop --keep-changes step.
(Note: hg qrm and hg qremove are aliases of hg qdelete.)
If using TortoiseHg
With TortoiseHg, exporting the patch to the clipboard (Workbench > right-click the patch > Export > Copy Patch), then unapplying the patch, and finally importing from the clipboard with the destination being "Working Directory" seems to work. Here are some screen captures demonstrating this procedure:
An arguably simpler alternative:
hg qfinish qtip
hg strip -k tip
That is, finish the patch and then remove the resulting commit while retaining its changes (the -k option to strip).
I'm transitioning to Mercurial from Subversion, where I'm used to using svnmerge.py to track changes that have already been merged, or which have been blocked from being merged:
# Mark change 123 as having already been merged; it will not be merged again, even if a range
# that contains it is subsequently specified.
svnmerge.py merge -M -r123
#
# Block change 326 from being considered for merges.
svnmerge.py merge -X -r326
#
# Show changes that are available for merging from the source branch.
svnmerge.py avail
#
# Do a catchall merge of the remaining changes. Neither change 123 nor change 326 will be
# considered for merging.
svnmerge.py merge
I want to be able to do something similar for hg pull/push/merge/graft, so that if I know that I never want to merge a given change, I can just block it from consideration, making subsequent cherry-picking, merging, etc., into a more fire-and-forget affair. I have done a lot of googling, but have not found a way to do this.
There also appears to be no way to view a list of as-yet-ungrafted changes.
As I'm often tidying up after other developers and helping them with their merges, it's immensely helpful to be able to do these kinds of things, which one might well consider "inverse cherry-picking;" i.e., marking changes that you do NOT want to merge, and then doing a bulk merge of the remainder.
DAG-based systems like Mercurial ans Git are all or nothing: when you merge two branches, you do a three-way merge of the common ancestor and the two branches.
The three-way merge is only concerned with the final stage of each branch. For instance, it doesn't matter if you make your changes in 10 it 1000 steps — the merge result will be the same.
This implies that the only way to ignore a changeset is to back it out before the merge:
$ hg backout BAD
That will cancel the changeset on the branch, making it appear that it was never made from the perspective of the three-way merge.
If you have a whole branch that you want to merge, but ignore, then you can do a dummy merge:
$ hg merge --tool internal:local --non-interactive
$ hg revert --all --rev .
That goes through the merge, but reverts back to the old state before committing.
The best advice I can give you is to structure your workflow so that the above backouts aren't necessary. This means committing a bugfix on the oldest applicative branch. If a bug is found while creating feature X, then use hg bisect to figure out when the bug was introduced. Now updated back to the oldest branch where you still want to fix the bug:
$ hg update 2.0
# fix bug
$ hg commit -m "Fixed issue-123"
then merge the bugfix into all later branches:
$ hg update 2.1
$ hg merge 2.0
$ hg commit -m "Merge with 2.0 to get bugfix for issue-123"
$ hg update 2.2
$ hg merge 2.1
$ hg commit -m "Merge with 2.1 to get bugfix for issue-123"
If the bugfix no longer applies, then you should still merge, but throw away the unrelated changes:
$ hg update 3.0
$ hg merge 2.2 --tool internal:local --non-interactive
$ hg revert --all --rev .
$ hg commit -m "Dummy merge with 2.2"
That ensures that you can always use
$ hg log -r "::2.2 - ::3.0"
to see changesets on the 2.2 branch that haven't been merged into 3.0 yet.
If I have several commits made to the default branch since the last push, is it possible to go back, and move those commits into a separate named branch?
That is, I have:
A--B--C--D
and I want:
A
\
B--C--D
I hope this makes sense?
Take a look at the Transplant extension.
But personally, I'd do it using MQ, like so:
# assuming revs 1 and 2 are the ones we want to move
hg qimport -r1:2
# qimport creates a patch for each changeset
>hg qapplied
1.diff
2.diff
# pop the patches, to save for later
>hg qpop -a
popping 2.diff
popping 1.diff
patch queue now empty
# switch branches
>hg branch my-branch
marked working directory as branch my-branch
# push our saved changesets, essentially rebasing on the new branch
>hg qpush -a
applying 1.diff
applying 2.diff
now at: 2.diff
# make the patches part of permanent history
>hg qfin -a
You could probably also bend the Rebase extension to suit this purpose, if you prefer.
If the commits are still in only your local repository and have not been pushed to any other one, then yes, you can re-arrange them with fairly minimal trouble. If they have moved beyond just your local repo, however, you will run into a lot of trouble.
To re-arrange commits, you want to use the MQ extension. Here's a tutorial, since it explains things better than I could here.
If you do an hg pull and then an hg update (or an hg merge), is there a way to back this out? Ie: revert your repository to the state prior to doing the hg pull?
I believe you can do hg update -r n where you would specify the changeset prior to the pull as n. Though I'm guessing this will still leave the changesets in your repository but this isn't really what we want. ??
hg strip will remove revisions from a repository. Like all powerful commands, its dangerous, so be careful.
https://www.mercurial-scm.org/wiki/StripExtension
Also see:
https://www.mercurial-scm.org/wiki/EditingHistory
If you catch your mistake immediately (or reasonably soon), you can just use hg strip REV to roll back the latest (one or more) changes. ...
Ok, you can't rollback because you've done a commit. What you can do is use 'hg strip' which is part of mq (after 2.8 strip is in it's own extension), or use mq to remove the changes. Either way I suggest you do everything on another clone, just in case.
To do strip, update to a revision that you want to keep, and then
hg strip <REV>
where <REV> is the first revision you want to remove. It will remove that one and all decendents (including your merge commit).
Alternatively you can
hg qnew (if you don't already have a patch queue)
hg qimport <REV>
which will import a single revision into the patch queue. You can then add more, and then use the mq commands to edit, rearrange, delete, or whatever you want to do with those revisions. qdel deletes the current patch.
Edit: Obviously, you'll need to enable the MQ extension for both of these, unless you're using 2.8 or later. In that case strip is in the strip extension, and mq in the mq extension. Both are shipped with the standard installation.
hg --rollback can be used to undo the last transaction so as long as your hg pull is still the most recent transaction then you can use that. This command should be used with care though. See here for some more details.
you can:
hg update -C <version>
see the mercurial FAQ.
If you want to remove all traces of the pull form your history then you need to use an extension as Bert F suggests (the philosophy in mercurial is to never change history)
if you dont mind history containing your mistake you have two slightly different options hg update -C -r which will create a new branch at the version you specify or hg revert -r which will stay on the same branch but create a new uncommited change undoing everything.