How to get a specific version of a file in Mercurial? - mercurial

I am new to Mercurial. Just cannot find the right command. Tried update/checkout with no luck. I am using local repository. Thanks

I think you want hg revert -r<rev> <file> (this will change that file to be as it was at the given revision).

As djc said revert alters a file in place to match a prior revision. If you want it not in place you can use hg cat -r revisionid filename (substituting revisionid and filename of course) which will output the file to stdout, suitable for redirecting anyplace you'd like.

hg revert does indeed solve this problem. But I think that you are confused about a broader range of things than simply the answer to your question and want to try to answer more fully.
hg update is a whole repository command and will not work on individual files. It is unlike the subversion svn update in this way. If you do hg --help update you can see that this is the case because the command takes no file argument. It can be used to move your whole repository to a particular snapshot, but cannot be used to do that to just one file.
If you type hg --help you see a list of commands. It's a rather large and somewhat daunting list, but if you read through it, you'll find this line:
revert restore individual files or directories to an earlier state
Now, if you just want the last state for comparison purposes, there is another command you may be interested in, and that's hg cat. That will allow you to print out the contents of a file at any particular revision. You can then redirect its output into some other file. Then you can have the previous known good version of your file and the old version to compare side-by-side.
The reason why Mercurial has a separate update command is that it is possible to do something in Mercurial that is impossible in Subversion. You can update to an earlier version, make changes, then commit. This will create a branch. The update command has the effect of also changing the parent revision of the current working directory as well as changing the contents of all the files in that directory to that parent revision's versions.
That means revert changes the contents of a file (or even the whole repository if you give the command the right arguments) but leaves the parent revision of the current working copy the same.
You can find out the parent revision (or revisions in the case of a merge) of the current working copy by using the hg parents command.
In Subversion revisions are a strictly linear progression. Mercurial creates branches at the drop of a hat, and they are almost as easy to merge. Revisions form a DAG, not a strictly linear progression.

To extract a specific revision of a specific file you can do this on Windows:
hg cat "<FileToBeExtractedPath>" -r 9 > "<ExtractionPath>"
Here, 9 is the revision number.
Or even better:
hg cat "<FileToBeExtractedPath>" -r 9 -o "<ExtractionPath>"

Came here trying to get the previous version, so here is the exact command:
hg revert -r .~1 <file>

Related

Mercurial - Look into history of file without updating

I'm working in this file and I come across a piece of code which I think has changed at some point in history, and I would like to know where it changed.
It's a pretty big file with a lot of history, so when I use hg diff, I get a enormous list and I don't think it's efficient to search through that.
It would be really neat if I can look into an old revision of the file, to see what the file looked like at a certain point in time. Then I can see how the code worked back then so I can conclude how the bug evolved. Of course, I want to do this without updating the file, because I'm currently working in it and have made changes in it.
So, is there any way you can look into the history of a file without updating it?
There are a few tools to help you:
To get the history of a file you can just use hg log FILE which is probably the best starting point.
You can also use hg annotate FILE which lists every line in the file and says which revision changed it to be like it currently is. It can also take a revision using the --rev REV command tail to look at older versions of the file.
To just list the contents of a file at a given revision you can use hg cat FILE --rev REV.
If it proves too hard to track down the bug using those tools, you can just clone your repository somewhere else and use hg bisect to track it down.
hg bisect lets you find the changeset that intoduced a problem. To start the search run the hg bisect --reset command. It is well document in Mercurial: The Definitive Guide.

How to diff two revisions of a file that got renamed in between and Mercurial doesn't know about the renaming?

I accidentally renamed a file outside of Mercurial. When I committed the change, Mercurial treated the change as two unrelated files (ie. a remove and a add). I need to go back to diff the two revisions but I don't know how to do so when Mercurial sees them as two respective files across different revisions. What can I do to diff the files?
You didn't say what operating system you were using. The following will work with bash on Linux:
diff <(hg cat -r rev1 file1) <(hg cat -r rev2 file2)
You can replace diff with another program like vimdiff if you want a visual diff.
If you want to actually fix the history so that Mercurial is aware of the rename (and can use that information in future merges if needed), there's a way to do so documented on the Tips and Tricks page on the Mercurial wiki.
Current contents copied here for ease of use (and in case the link gets broken later):
Steps:
Update your working directory to before you did the rename
Do an actual "hg rename" which will create a new head
Merge that new head into the revision where you did the "manual" rename (not the head revision!)
Then finally merge the head revision into this merge result.
Advice:
Make a clone first and work on that, just in case!
After finishing the steps, use a file compare tool to check that the original and the clone are identical
Check the file history of any moved file to make sure it is now restored
That being said, if all you want to do is compare the contents at the point in time, you can definitely accomplish that without making Mercurial aware of the rename (as mentioned in Stephen Rasku's answer). In fact, you can use a combination of "hg cat" and an external comparison tool to compare any files, not just ones that Mercurial knows about.
Fix history:
Update to first changeset with new-filename, save file outside WC
Update to parent of bad replacement changeset, replace file correctly (with rename tracking), commit, got second head
Rebase all changesets from old anonymous branch on top of fresh good changeset
--close-branch on bad-replacement changeset or delete this unwanted changeset or leave inactive head intact

Rollback of the last command in Mercurial

I just ran:
hg add .*
which matched all ../something files and which was something I certainly did't want! What is the easiest way to undo this command?
Not exactly what I was after, but made my life easier:
hg forget "set:added()"
this unmarks all the files that were added in the working repository. Obviously I also unmarked all the files that were added in preceding commands, so as I said this is not exactly what I was after...
If you've got a lot of uncommitted adds that you don't want to have to do over, you can use a mercurial fileset that only picks out pending adds in subdirectories of the parent directory (use this in the same directory where the erroneous command was issued):
hg forget "set:../** and added()"
Mercurial doesn't remember what path you used to refer to a file, so this will forget all files (under the parent directory) that were just added; but if you were deep in a large repository, it'll limit the scope of what you discard.
Your question is similar to this recent question, but here we have an extra condition on the mistakenly added filenames.
There is not built-in way to undo a hg add command.
The underlying problem is that hg add is manipulating the so-called "dirstate" (short for "working directory state". This is where pending changes such as files scheduled for commit is stored — and this storage is unversioned.
There is an extension, though, that can help you: hg-multiundo will make backups of all files touched by Mercurial, including the dirstate. This means that you can use it to undo things like hg add or even hg revert --no-backup.

using hg revert to revert a group of files in Mercurial

I'm using Mercurial to read and debug a complex project, and my modify of the project can be divided into different group of files clearly. For example, if I modified four files
src1.cc src1.hh src2.cc src2.hh
It's apparent that I can divide them into two file groups such as group src1 includes src1.cc src1.hh and group src2 includes src2.cc src2.hh.
I'm wondering if I can revert a group of files by a simple command like 'hg revert group-name-alias' instead of listing all the filename of the group, which is a awful idea if I have modified many files?
Any help really appreciated!
From what I can understand of your use-case, you can:
Use patterns in the hg revert command. This means that you can
run hg revert src1* to revert all the first group.
Most probably, though, your stuff is in sub-folders and thankfully
you can specify a parent folder to the revert command.
So say your files are really like: foo/src1.cc, foo/src1.hh,
bar/src2.cc, bar/src2.hh. In that case, you can revert all the
second group with hg revert bar, assuming you're in the top folder.
If you're already in the bar folder, you can run hg revert ..
You can specify several patterns.
Use Mercurial queues if each one of your "file groups" is also
a different unit of work (a different bug fix or feature). This is not
so desirable if all files belong to the same unit of work, though.
No. To the best of my knowledge, Mercurial has no mechanism for grouping files.
You could do some trickery with aliases ([alias] revert-group-name = revert src2.cc src2.hh in ~/.hgrc), but aliases can only be prefixes, and can't perform variable expansions.
If your files are simple enough, you could use shell globbing (hg revert src2*), or a shell variable (GROUP_NAME="src2.cc src2.hh", then hg revert $GROUP_NAME).
You could also consider writing a small Mercurial extension. If you know Python, they don't take very long (my first took me about 30 minutes).
If the filenames meet patterns, you can use that pattern:
hg revert src1*
or
hg revert src1*.*
If those files are in a specific directory, you can do this:
hg revert dir\*
If the directory is more than one level deep and you want to get that directory and all its subdirectories, you can use this version of that commend:
hg revert dir\**\*

How can I do a changeset based merge instead of file-by-file based merge with Mercurial?

When performing a merge with Mercurial, it wants to merge conflicting files one at a time which just isn't a productive workflow on large merge sets. Instead, what I would like to do is merge the entire changesets of both heads (like using kdiff3 to diff 2 heads). To me that sounds straightforward but I can't figure out how to achieve it.
So far, the closest I can get is to go through the merge the usual way, leave all the conflicts unresolved (a file at a time...), and then hg vdiff -rHead1 -rHead2 - but vdiff (using kdiff3) doesn't seem to have options for passing the tool an output dir (the current working dir) and instead launches with the output dir as a tempdir (possibly -o is the answer?).
Let me put it another way - I want to use kdiff to merge two heads into my working directory. I want the results in my working dir to be my merge that I can commit.
I must be missing something obvious, I can't be the only one who wants to do this.
I came up with a solution that achieves what I want but I still feel like it's a kludge.
Start with an empty working dir with 2 heads: Mine and Theirs.
Update working dir to Mine:
hg update [My head's rev here]
Perform a merge, but fail all files that Merc can't handle automatically without launching a merge tool and keep "My" files when in conflict:
hg --config "ui.merge=internal:fail" merge
See https://www.mercurial-scm.org/wiki/TipsAndTricks#head-9f405488b6d3b3d092a09aafa28db515ba44c742 for how-to merge/fail details.
Now I've got a working dir with as much as auto could figure out, but any outstanding files still untouched from Mine. (Use hg resolve -l to see the Mercurial's resolution status of current files)
Now I can vdiff my working dir against the Theirs head which gives me the high level, changeset-to-changeset merge that I was looking for.
hg vdiff -r [Theirs head's rev here]
Note: If you're using WinMerge for your vdiffs , then make sure it has an /r switch as an option which will do a subdirectory compare and - if WinMerge config is set to use Tree-View - will give a great tree comparison. From Mercurial.ini:
[extdiff]
cmd.vdiff = C:\Program Files\WinMerge\WinMergeU.exe
opts.vdiff = /e /ub /r /dl other /dr local
Now I can work the entire directory which includes the unresolved files and make project wide changes as necessary (ie. maybe resolving one file requires additional changes in another).
When done use resolve to mark all files resolved for Merc and then commit.
hg resolve -m
Whew! Here's hoping this helps someone else!
I relayed the question to #mercurial on irc.freenode.net a couple of days ago. mpm (the author of Mercurial) gave sort of an answer (it was only half an answer, so I didn't immediately pass it on here). He said that one might be able to do something where you let Mercurial merge the files automatically (and insert the <<<< and >>>> merge markers where there are conflicts).
Then use a merge tool that knows about these markers: this will let you resolve them all at once instead of doing it on a file by file basis. A starting point would be the page on merge tool configuration. It explains that
[ui]
merge = internal:merge
will make Mercurial inserte the merge markers. Here I tested it by making two files x.txt and y.txt which I then modified with conflicting changes in two clones. The merge simply gave:
% hg merge
merging x.txt
warning: conflicts during merge.
merging x.txt failed!
merging y.txt
warning: conflicts during merge.
merging y.txt failed!
0 files updated, 0 files merged, 0 files removed, 2 files unresolved
use 'hg resolve' to retry unresolved file merges or 'hg up --clean' to abandon
All files were processed in one go, I did not have to confirm anything per file like you describe.
The files now contain merge markers like this:
% cat x.txt
foo
<<<<<<< local
hehe
=======
foobar
>>>>>>> other
The next step is to find a tool that can take a directory tree with such files and let you resolve them. I looked at kdiff3, but did not figure out how to use it to operate on a single file alone, it seems very focused on comparing pairs of files/directories.
I'm not sure how much this half answer helps you -- maybe you also got stuck at this point? But I hope it can help others who want to have the merge markers inserted into all files and then resolve the conflicts by hand.
I think this answers your question.
sounds like you want the extdiff command:
i have these in my ~/.hgrc (I prefer meld, but you can change it to kdiff3, etc)
[extensions]
hgext.extdiff =
[extdiff]
# add new command called meld, runs meld (no need to name twice)
cmd.meld =
With extdiff your merges occur in your working directory, and moreover you can pass any extra parameters to your diff program with -o:
$ hg help extdiff
hg extdiff [OPT]... [FILE]...
use external program to diff
repository (or selected files)
Show differences between revisions for the specified files, using
an external program. The default program used is diff, with
default options "-Npru".
To select a different program, use the -p option. The program
will be passed the names of two directories to compare. To pass
additional options to the program, use the -o option. These will
be passed before the names of the directories to compare.
When two revision arguments are given, then changes are
shown between those revisions. If only one revision is
specified then that revision is compared to the working
directory, and, when no revisions are specified, the
working directory files are compared to its parent.
options:
-p --program comparison program to run
-o --option pass option to comparison program
-r --rev revision
-I --include include names matching the given patterns
-X --exclude exclude names matching the given patterns
Try setting ui.merge. See this page for more details.