I've previously (i.e. several changesets ago) committed a fairly big changeset that contains both good changes I'd like to keep, and some I'd like to undo. Yes, I shouldn't have packed so much into a single changeset anyway, but now the damage is done.
AFAIK, Mercurial's backout command can only work on an entire changeset. I can certainly figure out how to do this manually in a few steps, but I'm wondering: is there an easy way of getting only part of a changeset reversed?
(If it matters, the changes I'd like to undo are file deletions.)
I should have tried before posting, but this may be useful for others as well:
Backout doesn't automatically commit. So the approach that worked for me is to run hg backout, then simply revert the files that I don't want to be changed back.
Depending from conditions (amount of "must-be-backout'ed" and "must be preserved" or file-patterns for both cases) you can use hg backout with -I (include mask) or -X (exclude mask) option
hg backout -r CSET -I FILE backout only changes for FILE
hg backout -r CSET -X FILE backout all changes except in FILE
If you want to undo file deletions, then it would be better to use hg revert instead of hg backout:
hg status --change CSET --removed -n0 | xargs -0 hg revert --rev CSET^1
Related
I have a mercurial repo with no uncommitted changes. I was on revision 846, and decided my last 2 commits were junk and wanted to carry on as if from revision 844. So I was going to type:
hg revert -r 844 --all
Unfortunately I mistyped, and wrote:
hg revert -r 44 --all
So my whole repository has changed dramatically, including directory structure. I don't see any .orig files, so I don't think the answer in:
How to undo a revert in mercurial
helps me.
hg log still has all of my commits up to revision 846 - do you think I can revert back to revision 846?
Advice very welcome
hg revert just sets your working copy to the revision that you specify so if you didn't do a commit after the revert then your repository has not changed. To fix it you can just do hg update -C and then delete all the .orig files.
After that you can do the correct revert statement to remove the last two revisions.
If you did do the commit then the command that you wanted to do (hg revert -r 844 --all) would still get you to the point that you want by undoing the revert commit as well as the two commits that you originally intended to undo.
I issued hg qnew without realizing that it includes any outstanding changes into the patch. I'd like to back that out and pick only specific changes using hg qrecord. How can I undo qnew?
Your answer definitely works — with newer Mercurial's you can use hg strip --keep to avoid doing the import step:
$ hg strip --keep .
$ hg qdelete patch-name
The --keep flag makes strip ignore the working copy while working, that is, it deletes the commit (like hg qpop would do) but it doesn't undo the changes to the files. After stripping you still have the patch in your series (unapplied) and you can then delete it.
I've found an anwer here:
hg qpop
hg import --no-commit .hg/patches/patch-name
hg qdelete patch-name
Please add a better way, if you know.
Update: Based on Aldo's answer, there is another way:
hg qnew test
# We can undo the above qnew as:
hg qrefresh -X '*'
hg qpop -f
hg qdelete test
If you just want to undo the latest qnew retaining all your local changes, one option is:
qcrefresh 123
hg qpop -f
hg qdelete <name of the patch>
Notice that 123 is just a random string: you are telling mercurial to only include the (hopefully nonexistsnt) 123 file in the current patch.
Newer versions of Mercurial When you issue will issue a warning about the fact 123 file does not exist, but this is exactly what we want here.
If you want to retain some of the changes in the current path, you can use the qcrefresh command from the crecord extension, which allows to graphically select the changes to be included in the current patch. You need to download it from Bitbucket, extract the archive and configure it in .hgrc:
[extensions]
crecord = <path/to/crecord/package>
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).
Let's say that I have a named branch 'B1' which I'm doing feature development on.
I am at a good stopping point before a demo though not done with the feature so I:
hg up default
hg merge B1
hg ci -m "merged in feature drop"
hg push
Now I continue working for a half an hour or so and go to commit only to realize that I forgot to update back to B1 and that my current working directory is on default - uhoh. In theory I should be able to just mark my working directory parent as the tip of B1 - is there an easy way to do this?
I could of course commit, update back to B1, and merge my changes back, but then there's an unstable changeset in default and this happens often enough to me that I would like a real solution.
Two ways. First, the obvious way:
hg diff > foo
hg up -C b1
hg import --no-commit foo
rm foo
Second, the magical way:
hg up -r 'ancestor(., b1)' # take working dir back to the fork point
hg up b1 # take it forward to the branch head
This way involves merges. Depending on how much your branches have diverged, this may be painless. Or it may be complicated, and you may make a mess of your changes that you haven't saved anywhere. Which is why even magicians like myself prefer to do it the first way.
I would use the shelve extension. I think it’s distributed along with TortoiseHg, you can also use it from the UI:
hg shelve --all
hg up B1
hg unshelve
Rebase extension allow you to change parent for any commit for wrongly commited changeset.
If you want just change branch for future commit - MQ (as mentioned) or Shelve
Typically for this sort of dynamic approach, I favor mercurial queues.
In your situation, what I would do would be to create a patch on default with the changes, pop the patch off, switch over to B1, and apply the patch.
It goes something like:
hg qnew OOPSPATCH
hg qrefresh
hg qpop
hg up B1
hg qpush
<hack hack>
hg qrefresh
hg qfinish
All you need is simple hg up -m B1
From hg up --help:
options:
…
-m --merge merge uncommitted changes
…
I have a large commit of many files on one branch, I need to transfer the modifications of a single file in that changeset to another branch. How can I do this? I am mostly using TortoiseHg but commandline solutions are also fine.
If I go to the changeset in TortoiseHg and select the file I can see the diffs I want to transfer, but not a way to actually apply them.
You can get the patch for just that file using:
hg log -r THEREVISIONWITHLOTSOFCHANGES -p -I path/to/justthatfile > justthatfile.patch
which you can then import on whatever branch you want by doing:
hg update anotherbranch
hg import --no-commit justthatfile.patch
hg commit
The most basic solution is to dump the patch of the file, apply it to the current working revision, and commit it (assuming you're at the root of the repository):
$ hg up <revision-to-apply-the-patch-to>
$ hg diff -c <revision-containing-the-patch> <files-to-include> | patch -p0
$ hg ci -m "Transplanting selected changes from <revision-contain...>"
The drawback of this method is that it isn't very obvious what you've done from a revision history perspective. A good commit message helps here, but the history graph gives no hint about the process of transplanting some changes. In that case merging and reverting may be a better solution:
$ hg up <revision-to-apply-the-patch-to>
$ hg merge -r <revision-containing-the-patch>
$ hg revert --no-backup <files-to-exclude>
$ hg ci -m "Merge in changes of <files-to-include>"
Probably there are more solutions to do this -- these two came to my mind first.