hg extdiff -p and %~dp0 - mercurial

myprogram.cmd is in PATH;
myprogram.cmd uses %~dp0 to determine the folder it is in;
I have included #echo %~dp0 into myprogram.cmd for debugging;
When I call myprogram.cmd from anywhere, it works perfectly, displaying the folder myprogram.cmd is in;
When I call hg extdiff -p myprogram.cmd, it does not work, displaying something like c:\Users\Username\AppData\Local\Temp\extdiff.3n8op2\.
Here is the related part of hgrc file:
[extensions]
hgext.extdiff =
What do I do wrong? Should not %~dp0 return drive and path of the batch file? What do I use instead? Do I have to apply some special configuration to Mercurial repository? Passing the full path of myprogram.cmd to hg extdiff -p is not an option, unless it is done automatically.

The %~dp0 trick is a big lie. It is not actually a magic variable, it's simply a manipulation of %0 (or whichever var you stick the ~dp in front of). It simply takes whatever string is in that variable and tells you what its drive and path components appear to be. If that string is simply a name like "myprogram", it says "well, filenames without drives and paths are assumed to be in the current directory".
So the %~dp0 trick only works if either:
a) you've launched your script by its full name or
b) you happen to be in the directory where it resides
In this case, you run:
hg extdiff -p myprogram
gets turned into the following call to Windows:
CreateProcess(NULL, "cmd.exe /c myprogram some diff args", ..., "c:/some/temp/path", ...)
which is morally equivalent to opening a shell and running:
C:\>cd c:\some\temp\path
C:\some\temp\path>myprogram some diff args
%0 is myprogram
%~dp0 is C:\some\temp\path
I recommend passing the full program name for your tool like this via your .hgrc:
[extdiff]
myprogram=c:/tools/myprogram.cmd
But be warned, this can get confused by the presence of spaces in your filename, you may need to experiment with quoting.

You can try using %~dp$PATH:0, but you need to always specify the extension. For example, for the following test.cmd:
rem test.cmd
#echo dp0 == %~dp0
#echo dp$PATH:0 == %~dp$PATH:0
these are two sample runs from the d:\hg folder:
$ hg extdiff -p test.cmd
dp0 == c:\Users\estefan\AppData\Local\Temp\extdiff.pamj6n\
dp$PATH:0 == k:\home\Scripts\
$ hg extdiff -p test
dp0 == c:\Users\estefan\AppData\Local\Temp\extdiff.dgp0qz\
dp$PATH:0 ==
From http://ss64.com/nt/syntax-args.html:
%~$PATH:1 Search the PATH environment variable and expand %1 to the fully qualified name of the first match found.

Related

How to tell Mercurial not to look for root from ignored sub-directory

So here's the problem. I have my configuration file in my home directory ~ under Mercurial control. Part of the
|-~
|.hg/...
|-Dev
|-Project1/...
|-Project2/...
.hgrc
.hgignore
I have Dev directory excluded from the source control in .hgignore file.
However when I am in the directory ~/Dev/Project1 Mercurial thinks that I am in the under the source control. If I type hg root in any directory that is in the .hgignore or its sub-directory hg still considers it being a part of repository.
Is it a bug or a feature ?
UPDATE
So, here's the simple experiment one could do from the command line:
% mkdir -p /var/HgTest
% cd /var/HgTest
% hg init
% echo "this is a repository file" >> test.txt
% hg commit -Am "added repo file"
% cat <<EOT >> .hgignore
heredoc> syntax:re
heredoc>
heredoc> ^Dev
heredoc> EOT
% hg commit -Am "added .hgignore"
% echo "This is not in repository" >> Dev/notinrepo.txt
Now, Dev directory not in repository, if you type hg st anywhere under /var/HgTest it shows you that repo is clean. However if you go into Dev directory and type hg root it will output /var/HgTest. This is perhaps desired result. However, since the path should be ignored, I would think that hg root should effectively exit with -1 return code and message "not in repository" or something like that.
In my case, having HOME directory under source control effectively makes some of the tools consider every new directory (even under ignored paths) as a part of Mercurial repository located in the HOME directory.
It's a feature for when you are in ~/Dev/Project1/deeply/nested and want to keep mercurial commands within the scope of Project1.
A workaround is to hg init in ~/Dev/Project1. Part of the problem is the bad practice of putting your home directory under version control; I can see no benefit to be gained from it and much cost. As an example, almost everything you do with a browser, or music player, or many other programs is going to alter files in ~/.groovy-game/config or ~/.browser/cache-files; there is no meaningful way to choose a commit point. Because of this it would be better to establish good, incremental snapshot backups for $HOME, even if they are stored on the same machine.
This is not to say that dot-directories in your home should never be versioned. for example, suppose I hack on my ~/.vim files because I am working on the ultimate editing environment, cd ~/.vim; hg init can certainly be useful.
Put another way — so long as there is an .hg repository somewhere in the tree above you, Mercurial will seek it out and read the ignore file and not take action on ignored paths. However, hg root only looks for an .hg directory. In your case, there is always a root, you are in your ~ repository by definition. I don't see how it could be done otherwise; you can't find the ignore file until you've inspected the root.

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.

hgext.extdiff silently does nothing

In trying to get my real differencing engine up, I've trimmed to a very minimal setup, but still have no output and no indication of why nothing appears to happen. (Search of SO and Mercurial site (including the mercurial wiki) for extdiff gave me all the ideas I've tried, though perhaps I haven't tried all.)
I have tried a bash script and a .bat file; I have tried each of the scripts located in the "root" of the E: drive, identified as /cygdrive/e/ or as E:/ I have tried with and without quoting the path to the script. I think I've exhausted the combinations and have yet to get any indications of what (if anything) is running. When invoked directly, FdbCmp.bat behaves as expected; it is in a directory on my $PATH and in the Windows Path environment variable.
Other suggestions? It looks like it is simple enough and should "just work"
mercurial.ini includes
[extdiff]
hgext.extdiff
cmd.fdiff0 = "e:/Program Files/DbCmp/FdbCmp.bat"
opts.fdiff0 = $root --file $local --file $other
FdbCmp.bat:
#echo off
echo FdbCmp.bat testing
echo FdbCmp.bat args: ::%1:: ::%2:: ::%3 ::%4:: ::%5:: ::%6:: ::%7:: ::%8:: ::%9::
hg showconfig | grep extdiff returns the expected results (among a few other lines)
extdiff.cmd.fdiff0="e:/Program Files/DbCmp/FdbCmp.bat"
extdiff.opts.fdiff0=$root --file $local --file $other
extensions.hgext.extdiff=
hg fdiff0 returns with $? = 0 (cygwin bash or CMD.EXE) and no output displayed. I expected the FdbCmp.bat file would have printed something.
hg fdiff0 a b c (where files a, b, c do not exist) returns the following. This is expected, as the files don't exist and Hg reports that.
a: The system cannot find the file specified
b: The system cannot find the file specified
c: The system cannot find the file specified
hg fdiff0 file1 file2 file3 where all files exist returns with no error and no output. This is unexpected - FdbCmp.bat should have been invoked and printed its something.
Just testing the .bat file alone gives the expected results:
$ FdbCmp.bat moo cow oink pig
FdbCmp.bat testing
FdbCmp.bat args: ::moo:: ::cow:: ::oink ::pig:: :::: :::: :::: :::: ::::
hg --version is 2.4.6-35ba170c0f82
A re-read of the extdiff section of the documentation shows that options "will be inserted to the command between the program name and the files/directories to diff", unlike the merge-tools behaviour where options and files may be intermixed as required to build an appropriate comand line. The $local, $root, ... variables do not exist within the context of extdiff; they are merge-tools features that do not apply here.
The relevant mercurial.ini section now is
[extensions]
# enable the extdiff extension
hgext.extdiff =
[extdiff]
# define a jpeg differencing script; no options required
cmd.jpgdiff = HgJpgDiff.bat
# HgJpgDiff.bat is in a directory in my $PATH and contains:
# #rem ... various lines to test if we have been handed directories or files to compare
# #rem ...we only compare files, so this is the only active line
# JpgDiff --file %1 --file %2
[diff-patterns]
**.jpg=jpgdiff
and all works as desired now. Echoing parameters to a file helped to debug; nothing was ever displayed on the screen.
Many thanks.
I suppose, you have to re-read "Extdiff extension" wiki page
Anyway, if you want to use instead of hg diff FILE1 FILE2 something like hg fdiff FILE1 FILE2 for diffing, you have (see differences)
Add to global mercurial.ini (or project's .hgrc)
[extensions]
hgext.extdiff =
(maybe just extdiff =, have to test)
Create new [exdiff] section in file and in this section define new command for external diff and command options (diff with 3 files???), maybe like this (FdbCmp.bat added to PATH)
cmd.fdiff = FdbCmp.bat
opts.fdiff = $0 --file $1 --file $2
for my sample above resuling command
hg fdiff /PATH/TO/ B C
must produce as result under the hood
FdbCmp.bat /PATH/TO/ --file B --file C

Mercurial: get contents of a specific revision of a file

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

Automatic conversion of specific binary files to text to compare (diff) in Mercurial

I have several -small- binary files added in my Mercurial repository. The files are the "source" files of one of my development tools (report / form / class definitions).
I made a program that dumps this binary files to a text file to allow easy diffs between them. Is there any way to tell Mercurial that certain file extensions need to run this conversion before running the diff program? Or I have to set my conversion program as the main diff tool and run the conversion -or not- and then run the real diff program?
You can (TBT) use ExtDiff extension for pure Mercurial. In case of TortoiseHG
[diff-patterns]
**.ext = difftool
in hgrc plays the game
I ended up with a small batch previous to the diff program:
#echo off
set f1=%1
set f2=%2
::Temporary dir created by hg to copy the snapshot file
set tdir=%~dp1
::Original repository dir
set repo=%~dp2
::Filename extension
set ext=%~x1
::The binary files comes in pairs: scx/sct \ vcx/vct ...
set ex2=%ext:~0,-1%t
::Check if "dumpable" extension
echo %ext% | grep -iE "(vcx|vct|scx|sct|pjx|pjt|frx|frt)" > nul && goto DumpFile
goto diff
:DumpFile
set f1="%tdir%\_Dump1.prg"
set f2="%tdir%\_Dump2.prg"
::Get the pair file from the repository
hg cat %repo%\%~n1%ex2% -o "%~dpn1%ex2%" -R %repo%
::Do the dump, then the diff
MyDumpProgram.exe %1 %f1%
MyDumpProgram.exe %2 %f2%
goto diff
:diff
ExamDiff.exe %f1% %f2%
pause
and then config the batch in %UserProfile%\.hgrc
[extdiff]
cmd.ediff = d:\Utiles\diff2.bat