Mercurial: get contents of a specific revision of a file - mercurial

I need to get contents of a specific revision/node of a file in a local repository and write it to a temporary file.
I know it is possible to do through the internal Mercurial API.
Is there a built-in command or an extension?

You can use hg cat:
hg cat -r revisionid filename > tmpfile

The fastest, large and/or binary file friendly way to do this is:
hg cat -r revisionid repoRelativeFilePath -o tempFilePath
The tempFilePath, unless absolutely rooted (ex. 'C:\') will be relative to the repo's root

Related

Get file hash in specific changeset in Mercurial

Is there a way to get file hash in a specific changeset/revision in Mercurial?
Similar to svn proplist command in Subversion.
Thanks
I don't think so, since for mercurial the atomic unit is the changeset, not a single file (and rightly so!).
On the other hand, you can use hg cat and pipe to any hash utility as follows
$ hg cat -r REV FILE | md5
$ hg cat -r REV FILE | shasum
...
But again, are you sure focusing on one file (as opposed to the changeset) is the right approach?

TortoiseHG forget files in all sub folders of specific name

I have a pretty large folder (with many sub folders) on a mercurial repository. I was a bit too fast with my first commit so I added a lot of files that i now realize shouldn't be on version control. I've updated my .hgignore file accordingly but all the old files are still version controlled. Is there a command that I can write in the root directory that forgets all files that are in a folder of a specific name. These folder names exist in a lot of places and i want them all forgotten with one command since it would take a long time to go through them all manually and forget the folders/files
I guess it would maybe look something like this:
hg ignore ../folderName/
Yes... use a pattern to match them like
hg forget FOLDERNAME**
hg commit -m "Forget FOLDERNAME"
hg help forget
hg forget [OPTION]... FILE...
(...)
options ([+] can be repeated):
-I --include PATTERN [+] include names matching the given patterns
or use a one-line script:
for i in $(hg ma | grep FOLDERNAME); do hg forget $i; done
You can read hg help filesets and use one of it's samples
Forget files that are in .hgignore but are already tracked:
hg forget "set:hgignore() and not ignored()"

how can I split a mercurial repository?

The format of the hg mv command is hg rename [OPTION]... SOURCE... DEST
. Path names are relative to the current directory. Thus, when you are at a command prompt at the root directory and specify hg mv -n -I * A\B Z, mercurial will create the directory Z under the root directory, and move A\B\readme.txt to Z\readme.txt.
How can you specify, under Windows, that Z is the repository root directory? I tried using '.' as destination, i.e. hg mv -n -I * A\B . but got a message that A\B\readme.txt will be copied to B\readme.txt, not to readme.txt at the root. I tried using '~' as the destination, but hg mv -n -I * A\B ~ got me a new directory named "~" below the root, obviously not what I wanted.
So my question is: How do I specify the repository root directory as the destination to the mercurial move command?
edit: I'll try to clarify the issue.
I have an OldDev repository containing two products: Product-A and Product-B. Using the '~' symbol to denote OldDev's root folder, OldDev contains two folders: ~/Product-A and ~/Product-B (in addition, of course, to ~/.hg where its metadata is stored).
Each product is composed of a few projects, and each such project is assigned a folder under the product's folder. Thus Product-A has the Project-A, Project-B and Project-C, stored in ~/Product-A/Project-A, ~/Product-A/Project-B and ~/Product-A/Project-C, correspondingly. ~/Product-A/Project-A/xxx.cs is one of (Product-A's) Project-A's files.
Now I want to extract Project-A to its own NewDev repository. As it's the single project in NewDev, it makes no sense to retain the product/project hierarchy, so I want it to be at the root of NewDev: it xxx.cs file, for example, will be #/xxx.cs, where # is the root folder of NewDev (the one contianing NewDev's .hg directory where NewDev's metadata is stored).
To extract Project-A to NewDev I used the the convert extension, as documented in "split a repository in two". I used a mapfile containing the one mapping include Product-A/Project-A.
So far, NewDev is an exact subtree of OldDev. It does not contain ~/Product-B, it does not contain ~/Product-A/Project-B nor ~/Product-A/Project-C. It only contains ~/Product-A/Project-A. The files that remained are located at exactly the same paths as before, but only those files that belong to Product-A's Project-A were retained.
So, I've achieved half of my goals: I split OldDev, with its many products and projects, and created NewDev with only one project (Project-A). However, the files of Project-A are not at # but at their old (OldDev) location #/Product-A/Project-A. I need to move them up two steps so xxx.cs, will be at #/xxx.cs and not at #/Product-A/Project-A/xxx.cs
To move the files I tried to use the hg mv command, but I can't figure how to specify the root (#) as the destination.
Solution: What worked for me, based on Marc Anton Dahmen's answer, is as follows:
convert1.txt: hg convert -s hg -d hg --filemap mapfile1.txt olddev temprepo
mapfile1.txt: include Product-A/Project-A
convert2.txt: hg convert -s hg -d hg --filemap mapfile2.txt temprepo newrepo
mapfile2.txt: rename Product-A/Project-A .
Where the text of convrert1.txt and convert2.txt, of course, shell commands.
You must use the rename directive in your filemap instead of include like so:
rename Project-A .
Moving every file in a repository and the repository data is not an hg mv operation because that cannot change where the repository meta-data is stored.
The wording of your question is still really ambiguous, but I have a decent guess as to what you want to do.
Suppose you have a repo called /some/dir/avi-repo and you really want it to be in /avi-repo. Use clone:
cd /
hg clone /some /avi-repo
Now you have two identical copies of the repo, one in /some/dir/avi-repo and one in /avi-repo. You can delete all of /some/dir/avi-repo now.
Your desire seems a little more complicated than that with a tree like:
/some
---- /.hg # the repository meta-data
---- /dir # no files in here just the sub-dir
-------- /avi-repo
------------/file.c
------------/file.dat
------------/important-file.txt
And you want to move avi-repo to /some/avi-repo. You should be able to do that with the right sequence of mercurial commands, but it is far easier to:
mkdir /temp
cd /temp
hg clone /some /temp/avi-clone
rm -r /some
mkdir /some
hg clone /temp/avi-clone /some
Or some variant of that. The point is that repatriating an entire repository is not a job for hg mv.

I want to create a versioned folder as a result of mercurial "hg clone <repo url>"

When cloning a repository in Mercurial, is there a way to create a target folder based on the latest changeset? Example:
$ hg clone http://hg.repo.com:8000/myrepo 'myrepo-$VERSION'
The folder should be named after the version of the project, e.g., myrepo-1.3.
If you are okay with using the changeset hash, then you can start with
$ hg identify -i http://hg.repo.com:8000/myrepo
to get the ID of the tip changeset. You can combine this with clone like this in a Unix shell:
$ hg clone http://hg.repo.com:8000/myrepo \
"myrepo-$(hg -i identify http://hg.repo.com:8000/myrepo)"
To make it more convenient to use, I would create an alias for this:
[alias]
vclone = !DEST=$(basename "$1")-$($HG identify -i "$1");
echo "destination directory: $DEST";
$HG clone "$1" "$DEST"
This let's you do hg vclone foo to get a foo-<ID> clone.
In the alias I took care of computing a suitable basename from the clone URL and to print the destination in the same way that hg clone normally does when you don't give an explicit destination. Finally, I took care to quote the arguments so that you can clone a directory with spaces in the name. (If you also have characters like " in your names, well then you're out of luck without better support for quoting in Mercurial shell aliases.)
You'll have to first clone it into some temporary folder, then inspect the repo's tip to see its revision or whatever trait you want to use in your naming scheme and then rename the previous (temporary) location to whatever it should be now ... it's not available in vanilla Hg to my knowledge.

How to use `hg cat` from an empty working directory?

I have a repo located at x:/projects/repo1. The working directory has been emptied using hg update null. I want to extract the latest version of some files from there to a local directory.
I tried this:
x:\projects\repo1> hg cat -o c:\sql\%s scripts\*.sql -r tip
I get this error:
scripts\*.sql: No such file in rev 14f07c26178b
The same command works fine if the working directory is not empty. Is there a good reason why this does not work? Or do you know another way of extract some files from there to a local directory?
The hg cat command is for single files. If you want multiple files use the hg archive command, which makes zipfiles or directories full of files. Here's your command:
x:\projects\repo1> hg archive --include scripts\*.sql -r tip c:\sql
It seems that hg cat doesn't support wildcard symbols in paths. So you should use the full file name:
hg cat -r tip scripts/foo.sql
When your working copy is up to date with the tip revision, your shell does wildcard substitution for you.
The hg manifest command also might be helpful for getting tracked file listings.
This answer is to your comment on Andrey's answer:
hg manifest takes a --rev argument that you can use to see the list of all files in your repository:
hg manifest --rev tip
To get the list of files matching a pattern at the tip, use:
hg stat --all --include *.sql --rev tip --no-status
hg stat -A -I *.sql --rev tip -n # using abbreviations.
From there you could redirect the output to a file and edit each line into a hg cat command like in your original question. It appears (to me, at least, having done some experimentation) that hg cat uses the contents of the working directory -- rather than the contents of the repository at the revision specified -- for glob-matching, so once you know the exact name of the file, you can hg cat it at any revision.