Is it possible to revert recent changes? - mercurial

I've made unwanted changes to several files recently. Is it possible to revert them based on the time (for example revert all changes made in last 10 minutes)

Option 1: Rollback or strip
In case you already commit (and not pushed) some of the unwanted changes, it is easy:
$ hg strip <firts-unwanted-revision>
This requires the mqstrip extension to be enabled.
Alternatively check if the rollback command is sufficient for your case.
Option 2: Revert selected files only
Otherwise you can only revert files to the state of the last commit. Mercurial (as Git, SVN, and most other version control systems) only tracks changes when you commit. Everything in between is out of Mercurial's control. That's why it is generally best to commit early and often.
However, you could make use of the modification time of your files and only revert those that have been touched in the last 10 minutes. Be aware that this reverts all changes per file, i.e. also changes made in these files more than 10 minutes ago. It only safes your correctly edited files where the last change has been more than 10 minutes ago.
$ hg revert <files-touched-within-last-10-minutes>
On Unix systems, you get the list of all files modified within the last 10 minutes using the find command:
$ find -mmin -10
Option 3: Commit good changes, discard bad ones
Finally there is a 3rd option which works good when your bad changes did not overwrite good changes but exist next to them. Here a good strategy is to
only commit the good changes (using the record or crecord command), and then
revert all remaining changes (e.g. with hg up -r . -C)
The HG-Book has more hints on dealing with unwanted changes.

Related

Remove file from multiple (all) commits in a branch in Mercurial?

I have seen Remove file from a commit in Mercurial - but I have very little experience with Mercurial, so I'd like to ask and make sure if this is doable, and how.
There is a software that is developed in Mercurial, and I wanted to try and add a feature. So when I cloned the repo, first I did was added my own branch, and started hacking there. Already in first or second commit of this branch, I added multiple files hg add test1.bmp, hg add test2.bmp and so on, and committed.
Then I kept on hacking other files (haven't touched these test1.bmp, test2.bmp etc since), and made about 3 or 4 commits after the commit where the bmps are added. Nothing has been pushed anywhere - my local copy of the Mercurial repo and my branch in it is the only place where these files are referenced. And just now I realize I shouldn't have added those files at all.
Is it possible to remove these files from all of the commits where they feature, since the first commit where they were added?
Or maybe I should formulate the question in singular - is it possible to remove a file from all of the commits where it features, since the first commit where it was added? (if it is possible for one file, then I can just repeat the process for any additional file I'd like to remove)
Yes this is possible.
However, this is a more advanced use of mercurial and will require you enabling at lease one extension.
As a new user, my recommendations would be to simply use mercurial basic management and remove the file(s) of concern:
hg remove <file>
You can then commit the change.
While this will not remove the file(s) from all changesets, it will remove it
going forward. This honestly is the recommended way of managing working content.
It should be noted that this is likely your only option if you have pushed changes to the parent repository. If you have pushed your changes to the parent repository, you will also have to edit it which makes things much more complicated and the potential for a serious mistake more likely.
If you truly want to remove it from previous changesets, you will need enable the histedit extension (included with mercurial).
[extensions]
histedit =
Since you are a relatively new user to mercurial, I strongly recommend that you backup your repository and experiment there before attempting this on you working copy.
The process I recommend for this is as follows:
Note: this only works as described for changesets that have not been pushed and have phase = draft (or secret)
1) Identify all changes where file(s) are added or modified
You will need to know each changeset where the file(s) where modified or added. You will need to modify each of these changesets in the reverse order that they where added.
You can use:
hg log file
to list changesets where the file was modified or added.
2) Slowly edit each affected changeset (working your way backwards)
Use the histedit to display all show all changesets that can be modified/edited in your default editor.
hg histedit
Find the first changeset you identified and change it from pick to edit and save the change.
This will drop you into histedit edit mode. This is the state just before the changeset was committed. This means you can make changes such as modifying, unedifying. Adding or removing content. In this case we want to undo any changes to the file(s) in question.
hg revert file
or
hg remove file (if this is the changeset where the file as added)
3) recommit changeset
Once you have reversed the file changes/additions, you need to inform histedit
that you are done.
hg histedit --continue
This will cause the changeset to be recommitted with the modifications you made including editing the commit message.
Repeat this for each changeset until completed.
Note: While you can select multiple changes to edit in histedit, I recommend doing one at a time to help reduce complexity. Remember to do the in the reverse order that they where committed to help reduce / eliminate any merge conflicts.
Again a recommend practicing on a repository copy until you have the process firmly understood. Since you are modifying repository history, this can have potential negative effects of a mistake is made.
I highly recommend reading this: https://book.mercurial-scm.org/read/changing-history.html.

Mercurial: how to amend the last commit?

I'm looking for a counter-part of git commit --amend in Mercurial, i.e. a way to modify the commit which my working copy is linked to. I'm only interested in the last commit, not an arbitrary earlier commit.
The requirements for this amend-procedure are:
if possible, it should not require any extensions. It must not require non-default extensions, i.e. extensions which do not come with an official Mercurial installation.
if the commit to amend is one head of my current branch, no new head should be created. If the commit is not head, a new head may be created.
the procedure should be safe in a way that if for whatever reasons the amending fails, I want to have the same working copy and repository state restored as before the amending. With other words, if the amending itself can fail, there should be a fail-safe procedure to restore the working copy and repository state. I'm referring to "failures" which lie in the nature of the amend-procedure (like e.g. conflicts), not to file-system-related problems (like access restrictions, not being able to lock a file for writing, ...)
Update (1):
the procedure must be automatable, so it can be performed by a GUI client without any user interaction required.
Update (2):
files in the working directory must not be touched (there may be file system locks on certain modified files). This especially means, that a possible approach may at no point require a clean working directory.
With the release of Mercurial 2.2, you can use the --amend option with hg commit to update the last commit with the current working directory
From the command line reference:
The --amend flag can be used to amend the parent of the working directory with a new commit that contains the changes in the parent in addition to those currently reported by hg status, if there are any. The old commit is stored in a backup bundle in .hg/strip-backup (see hg help bundle and hg help unbundle on how to restore it).
Message, user and date are taken from the amended commit unless specified. When a message isn't specified on the command line, the editor will open with the message of the amended commit.
The great thing is that this mechanism is "safe", because it relies on the relatively new "Phases" feature to prevent updates that would change history that's already been made available outside of the local repository.
You have 3 options to edit commits in Mercurial:
hg strip --keep --rev -1 undo the last (1) commit(s), so you can do it again (see this answer for more information).
Using the MQ extension, which is shipped with Mercurial
Even if it isn't shipped with Mercurial, the Histedit extension is worth mentioning
You can also have a look on the Editing History page of the Mercurial wiki.
In short, editing history is really hard and discouraged. And if you've already pushed your changes, there's barely nothing you can do, except if you have total control of all the other clones.
I'm not really familiar with the git commit --amend command, but AFAIK, Histedit is what seems to be the closest approach, but sadly it isn't shipped with Mercurial. MQ is really complicated to use, but you can do nearly anything with it.
GUI equivalent for hg commit --amend:
This also works from TortoiseHG's GUI (I'm using v2.5):
Swich to the 'Commit' view or, in the workbench view, select the 'working directory' entry.
The 'Commit' button has an option named 'Amend current revision' (click the button's drop-down arrow to find it).
||
||
\/
Caveat emptor:
This extra option will only be enabled if the mercurial version is at least
2.2.0, and if the current revision is not public, is not a patch and has no
children. [...]
Clicking the button will call
'commit --amend' to 'amend' the revision.
More info about this on the THG dev channel
I'm tuning into what krtek has written. More specifically solution 1:
Assumptions:
you've committed one (!) changeset but have not pushed it yet
you want to modify this changeset (e.g. add, remove or change files and/or the commit message)
Solution:
use hg rollback to undo the last commit
commit again with the new changes in place
The rollback really undoes the last operation. Its way of working is quite simple: normal operations in HG will only append to files; this includes a commit. Mercurial keeps track of the file lengths of the last transaction and can therefore completely undo one step by truncating the files back to their old lengths.
Assuming that you have not yet propagated your changes, here is what you can do.
Add to your .hgrc:
[extensions]
mq =
In your repository:
hg qimport -r0:tip
hg qpop -a
Of course you need not start with revision zero or pop all patches, for the last just one pop (hg qpop) suffices (see below).
remove the last entry in the .hg/patches/series file, or the patches you do not like. Reordering is possible too.
hg qpush -a; hg qfinish -a
remove the .diff files (unapplied patches) still in .hg/patches (should be one in your case).
If you don't want to take back all of your patch, you can edit it by using hg qimport -r0:tip (or similar), then edit stuff and use hg qrefresh to merge the changes into the topmost patch on your stack. Read hg help qrefresh.
By editing .hg/patches/series, you can even remove several patches, or reorder some. If your last revision is 99, you may just use hg qimport -r98:tip; hg qpop; [edit series file]; hg qpush -a; hg qfinish -a.
Of course, this procedure is highly discouraged and risky. Make a backup of everything before you do this!
As a sidenote, I've done it zillions of times on private-only repositories.
Recent versions of Mercurial include the evolve extension which provides the hg amend command. This allows amending a commit without losing the pre-amend history in your version control.
hg amend [OPTION]... [FILE]...
aliases: refresh
combine a changeset with updates and replace it with a new one
Commits a new changeset incorporating both the changes to the given files
and all the changes from the current parent changeset into the repository.
See 'hg commit' for details about committing changes.
If you don't specify -m, the parent's message will be reused.
Behind the scenes, Mercurial first commits the update as a regular child
of the current parent. Then it creates a new commit on the parent's
parents with the updated contents. Then it changes the working copy parent
to this new combined changeset. Finally, the old changeset and its update
are hidden from 'hg log' (unless you use --hidden with log).
See https://www.mercurial-scm.org/doc/evolution/user-guide.html#example-3-amend-a-changeset-with-evolve for a complete description of the evolve extension.
Might not solve all the problems in the original question, but since this seems to be the de facto post on how mercurial can amend to previous commit, I'll add my 2 cents worth of information.
If you are like me, and only wish to modify the previous commit message (fix a typo etc) without adding any files, this will work
hg commit -X 'glob:**' --amend
Without any include or exclude patterns hg commit will by default include all files in working directory. Applying pattern -X 'glob:**' will exclude all possible files, allowing only to modify the commit message.
Functionally it is same as git commit --amend when there are no files in index/stage.
Another solution could be use the uncommit command to exclude specific file from current commit.
hg uncommit [file/directory]
This is very helpful when you want to keep current commit and deselect some files from commit (especially helpful for files/directories have been deleted).

Mercurial: update to a revision, but not modify local files

Suppose the repository has 20 revisions. The current revision on the record is revision 10, while the actual local files are based on the latest revision 20. I do not want to branch from revision 10. I want to continue the revision 20.
Currently what I'm doing is to copy all the files out of the revision directory, run hg update -C (which updates the revision number), remove all the files in the repository directory, copy all the files back, then commit. You can tell this is annoying and time wasteful. Is there a way to only update the revision number, but not modify any local file?
Thanks in advance.
EDIT: there are many use cases (I mainly use TortoiseHg).
1) I mistakenly choose "No" when prompted to remove files from repository during a commit. I can use the strip from mq to remove the revision, but that will revert all commited files. Instead, I want to "re-parent" to the previous revision, commit again, then strip the unwanted revision. This may happen to anybody anytime, not just me, not just once.
2) I had a development machine A. I started the project on it, and switch to machine B after several months. The code on A becomes old, and the code on B has uncommited changed. If I want to continue development on A, I need to copy the code to A via Flash or network, etc. Now I want to commit on A without branching (because the code on A is actually based on the current tip). This happens maybe less frequently, but still not "once" problem, and more possibly when involved in a big project.
3) The repository has 2 branches C and D. There are roughly 2000 files. The current tip is in C. I want to overwrite some files in D with the same-named files in C (because C is more implemented) while preserving other files unchanged, then make the new tip in D. I have several options. I could update to D, compare the last revision of D with the current tip, make change and commit. The update would take some time, and the difficulty of comparison heavily relies on the comparison tool. I could also compare first, make change, backup the entire directory, update to D, then restore the entire directory. This is even more cumbersome. But if I could "re-parent" to D, I just need to make change and commit. This does not rely on comparison tool because the commit window in TortoiseHg has diff list.
There could be more use cases if you want to think. I hope Mercurial provides such "re-parent" feature (supposedly a new option for hg update), or at least convenient alternative.
You might find this article useful. The original link no longer exists, so it now references the Internet Archive's copy:
https://web.archive.org/web/20170805042314/http://hgtip.com/tips/advanced/2010-04-23-debug-command-tricks
In particular, to "reparent" your current working directory, issue:
hg debugsetparent 20
hg debugrebuildstate
You can use hg debugsetparents to set the parent revision of your working copy.

Mercurial: back out last change so codebase is clean?

I've just messed something up in Mercurial.
How do I back out the last change so that the codebase is clean - specifically so that if someone else does 'hg pull', they'll have a working codebase?
Two ways:
hg rollback as mentioned by Fred; if you've already pushed, too late. hg rollback is only suitable for local use. hg strip (also as mentioned by Fred) works the same way and is (excepting its support for backing up) equivalent to doing hg rollback a number of times, till you get back to that revision.
hg backout for when you have already pushed and just want to revert the effects of the commit (if you've accidentally pushed out sensitive data, you'll need to take more drastic measures, but if you just want it to work, use this).
If the commit is not the last-committed revision, say so and we can get into deeper stuff (or search - it's been answered before).
Another way is to clone from a specific revision. Say checkin 6 was the mistake. You can clone your repository up to revision 5:
hg clone -r 5 myrepobad myrepoclean
now in myrepoclean you are back to where you were before the bad checkin. Obviously you need to be aware that anyone who has pulled the bad checkin is now liable to push it back in.
$ hg rollback --help
hg rollback
roll back the last transaction (dangerous)
This command should be used with care. There is only one level of rollback,
and there is no way to undo a rollback. It will also restore the dirstate at
the time of the last transaction, losing any dirstate changes since that
time. This command does not alter the working directory.
...
You should also look at the strip command.

Branch from the past in mercurial

I'm a single developer using Mercurial to create a program. I have so far committed at every step of developing, and I have messed things up and want to work on something else.
How shall I go to a previous state (ie. rollback) and then create a branch, maintaining the newer states?
Does rollback ever delete anything? Should that be all I do? Just rollback untill I'm at the place where I want, edit, then commit? Will I be able to merge the changesets later, or at least look at them? Of course I can just save this repository, rollback, and then create new changesets, then merge the two repositories?
I'm new to SCM and of course DSCM, so be gentle :)
You don't want to use hg rollback, that only erases exactly one change (the most recent). See You Can Only Roll Back Once in the hg book.
To make a branch from an earlier revision, you need to find the revision number you want to branch from and run:
hg clone -r <revnum> <source repository> <destination repository>
You can then do your changes in the newly-cloned repository (which will not contain any of the history that came after the revision number you specified) and then merge those changes back into the original later, if you wish.
The other answer will work but it is excessive. All you need to do is:
hg up <revnum>
and your local working folder will be updated to the state of files at the specified revision. You can just work from there.
As soon as you commit it will create an anonymous branch automatically. Your original commits remain preserved but out of your way.
This is the simplest possible approach (IMHO) which I would recommend starting with.