Can I revert a range of lines in a file with mercurial? - mercurial

I often notice a few unwanted changes when I review my working copy (with hg status and hg diff) right before a commit. For example, I might have temporarily added or remove some code just for the duration of a debugging session.
I know I can use hg revert to remove unwanted changes, but this removes all the changes in the entire file. Is there a way to revert just a part of a file?

I'm not sure if you can revert individual lines explicitly, but what I do in situation like yours is to commit the good code and revert the rest (the bad code). This workflow is easy using Mercurial's record or crecord extension (I recommend the latter one).

I've been doing this with the interactive ncurses style ui hg revert -i that lets you walk around and select the parts you want to destroy, either file, diff chunk or line by line, as you please, depending on how deep you unfold your changes.
I am not sure if this is a standard hg feature or not, you can verify easily enough if yours has it:
> hg revert --help --verbose | grep -- -interactive
-i --interactive interactively select the changes (EXPERIMENTAL)
Just remember that the changes you mark (X) will be what gets nuked, not what you retain.

Assuming you have the record and shelve extensions enabled, you can do as follows:
hg record -m "garbage" # pick out and commit the change you want to revert
hg shelve --all # temporarily hide other changes
hg strip tip # remove the garbage changeset
hg unshelve # restore the changes you want to keep

One way is using a graphical diff tool like kdiff3. If you feed it the diff and choose "merge current file" you can go line by line and pick what you want.
The better way is to commit more often. If you make a habit to commit right before adding debugging code, then either commit or revert your debug code before adding your "real" code, it makes it very easy to remove your debug code because it has its own revision. Alternately, you can put your debugging code in a separate branch altogether.

The TortoiseHg GUI's "Commit" window has a "Hunk Selection" tab that allows selection of specific sections of a file's changes to be committed.

I've got a different answer for your problem, which is the same problem I have. This is a great use case for mercurial queues!
When I am about to start adding debugging code to a change that I think is ready, I do the following:
hg qnew -m "fix for bug #123" fix.patch # basically a local-only commit
hg qnew -m "debugging" dbg.patch # prepare the next changeset at the tip
[add my debugging]
hg qrefresh # update the changeset at the tip
[...]
hg qpop # pop the debugging off the repo history
It takes a little bit of getting used to -- you end up having to reorder your patches to then fold whatever fixes you made into the original work patch.
Also, check out Bill Barry's attic extension. This page talks about how to use it for a few different workflows and how that compares to using mq. https://www.mercurial-scm.org/wiki/AtticExtension

If the commit where you want the change is e.g. ba1c841aaff4,
the easiest is to use:
hg meld -r ba1c841aaff4^ <filename>
Now click on a right arrow (pointing to the right) in the middle to revert your lines, save the file and close meld. kdiff3 (old and unintuitive) can be an alternative.
Side note: in order to use meld you need to configure it in our ~/.hgrc file:
[extdiff]
cmd.meld =
[merge-tools]
meld.args=$base $local $other

Download and install meld https://meldmerge.org/
(You should have Mercurial installed locally or TortoiseHg)
Launch Meld and select Version Control View and then select you will be asked to choose a parent directory of the file you want to modify.
Select the file which has the uncommitted changes
Choose which lines you want to revert to the last commit by selecting the arrow. The document on the left is from the last commit and the right document is the file with the uncommitted changes.

Related

Remember uncommitted changes

I have some uncommitted changes C in my repo. I would like remember that changes in any way and get clean code (without that changes), make a little change and commit it. Now, I would like to recover my changes C and continue working on it. I know that I can deal with it using a lot of ways, but that ways are irritating. How to do it using mercurial?
So, to be more precise I need something like a stack:
Working on the code. Remember changes C on the stack.
hg update --clean
Make a change C2. Commit it.
Pop from stack a changeset C and work on it. But, now the repositorium contains committed change C2 and uncommitted C. It may cause that I need to merge but I expect that this merge will be invisible from the point of view repositorium.
While you certainly can work with mercurial queues, there's IMHO an easier and nicer way: change your default phase to secret and work with those commits like normal commits. Commits in phase secret are mutable and will not be exposed by push and pull commands acting on the repo.
This process has the advantage that you do not need to change your workflow - whether you work with commits you want to share (phase draft or public), or whether you still consider them work-in-progress and keep them locally only.
Additionally if you enable the evolve extension, you gain several benefits: it becomes even easier to amend commits and evolve (thus rebase) all child commits which depend on it.
The big advantage over the use of the mercurial queues is that you can make full use of the inbuild merge features - thus if the underlaying code changes, rebasing the new changesets is WAY easier and natural than using queues and hg shelve.
See the introduction to hg phases and changeset evolution which needs the evolve extension.
Enable the Mercurial Queues extension in your mercurial.ini or .hgrc file:
[extensions]
mq =
Then you can,
hg qnew save # save work in progress as a temporary commit
hg qpop # remove that commit
Make some more changes....
hg ci -m "new changes"
hg qpush # push the saved commit back.
hg qfinish -a # convert all temp commits to full commits.
You can also enable the shelve extension:
[extensions]
shelve =
Then you can:
hg shelve # "put away" current uncommitted changes.
*do other work*
hg unshelve # bring the shelved changes back
See hg help mq and hg help shelve for more info.
I've tried shelve, mq as described in other answers but to be honest I generally stick with:
hg diff > saved.patch # This assumes you've not aliased diff to a UI!!!
hg update -C
.. work
hg patch -f --no-commit saved.patch # I alias this for less typing
.. continue
Less book-keeping involved, its never gone wrong unlike shelve, and the patch itself is more easily portable. Just use common-sense and either make sure the patch applies fully, or use the --partial option and manually complete the patch.

Splitting single change set in 2 different commits [duplicate]

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

Is there an hg equivalent of `git stash save -p` that can split chunks into smaller chunks?

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

How to reinstate most recent commit in Mercurial?

I've searched the docs of Mercurial and still am confused. What I'm wanting to do is just reinstate the last commit I made i.e. I want my project to go back to being exactly the same as it was when I made the last commit. I see hg revert, rollback, etc. and still am not understanding which is correct for this situation. Which should I use?
The hg rollback command is used to undo the last action that modified Mercurial's internal store, usually a pull or commit. So, if you want to undo your last commit hg rollback will work.
But it sounds like you want to undo all your uncommitted changes. You have two options. The hg revert --all command will undo all uncommitted changes. Each changed file is saved/backed-up with a .orig extension before being reverted.
If you don't need to preserve your changes in .orig files, run hg update -C. This clears out all uncomitted changes, without preserving anything.
If you have commited changesets and wish to remove them, I like the strip extension, strip extension. With it, you can remove explicit changesets from your history.
However, strip is an unforgiving command, i.e. if you get it wrong there is no retrieval unless you have a backup of the repo. You might prefer the prune command which comes with the evolve extension. Using prune, you can mark changesets as obsolete and they will no longer normally be visible in logs or tortoise. [You can make them visible by adding --hidden on an hg log command line, or in tortoise by enabling the Filter toolbar (from the view menu) and selecting 'Show/Hide hidden changesets'.]

How do you delete a commit in Mercurial?

I want to completely delete a Mercurial commit as if it was never entered in the repository and move back to my prior commit.
Is this possible?
If it was your last commit and you haven't pushed it anywhere, you can do that with rollback. Otherwise, no. Not really. Time to change your passwords.
Edit: It has been pointed out that you can clone from an older revision and merge in the changes you want to keep. That's also true, unless you have pushed it to a repo you don't control. Once you push, your data is very likely to be very hard to get back.
You can try to remove mq info about your commit.
For this you need to go File->Settings->Extensions.
There check mq and restart gui.
After that just right click on unneeded commit and
ModifyHistory->Strip
To edit the history I would use the Histedit Extension extension.
hg histedit 45:c3a3a271d11c
However keep in mind this only makes sense in a situation where you have not yet pushed the commits to the public repository, you own the public repository and/or you can account for all the clones out there. If you receive the following error:
abort: can't rebase immutable changeset 43ab8134e7af
It means that Mecurial thinks this is a public changeset (see phases) that has already been pushed - you can force it to be a draft again doing:
hg phase -f -d 45:c3a3a271d11c
I encounter this fairly often. I make a commit and then pull to push. But then there is something incoming that makes my newly made commit unnecessary. A plain hg rollback isn't enough because it only undoes the pull...
This is the thing to do:
hg strip <rev>
Things are painless when you don't push your changesets anywhere.
If it's more than one commit and/or you already pushed it somewhere else, you can clone your repository and specify the last changeset that should be cloned.
See my answer here how to do this:
Mercurial: Fix a borked history
If you only committed locally and didn't push, you can just create a clone locally (as described in my link) and you're done.
If you already pushed to some remote repository, you would have to replace that with your clone.
Of course it depends if you are able (or allowed) to do this.
You can use "hg backout" to do a reverse merge basically. All options are discussed in the freely available book "Mercurial: The Definitive Guide":
http://hgbook.red-bean.com/read/finding-and-fixing-mistakes.html
If using tortoise you can use modify history > strip...
Yes. Unless I am mistaken, as of v2.3 (rel. 2012/08/01) you can use the HisteditExtension with a drop command to drop a commit, along with strip or backout to remove changes.
A simple Google search on the feature: https://www.google.com/webhp#q=histedit+drop
In 2022 I do use evolve extension. It is one of the best extensions for this purpose.
To prune unwanted changeset, if you for example did a quick hack to get the code working:
$ echo 'debug hack' >> file1.c
$ hg commit -m 'debug hack'
Now you have a proper patch you can do hg prune .:
$ hg prune .
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
working directory is now at 2a39221aaebb
1 changesets pruned
If you push the change to the remote repository you will find only obsolescence markers:
$ hg push
searching for changes
no changes found
remote: 1 new obsolescence markers
To check the changes to your local repo you can pull from the remote one:
$ hg pull
pulling from ssh://userid#server/repo
searching for changes
no changes found