This page describes Mercurial 0.9.5 and older releases and is no longer relevant for Mercurial 1.0

Before Mercurial 1.0, a script named hgmerge was installed by default. As of Mercurial 1.0, hgmerge is no longer installed (and it should not be distributed with Mercurial), but hg still uses it, if it's present on the system and no other merge tools are configured. See MergeToolConfiguration for the new mechanism.

The hgmerge script tries to invoke several merge programs in turn. If it cannot find any of them, hgmerge runs a text editor on a version of the file with the conflicts annotated.

The merge program is defined in .hgrc with

[ui]
merge = your-merge-program

Note that the order of the argument can differ between hg and your merge program, hgmerge is called

hgmerge local base other

The files have to be merged into local.

For example kdiff3 is called

kdiff3 base local other -o output

Python merge script

There is a python based merge script available which will detect the merge tools you have available on your computer and use them appropriately (similar to the hgmerge shell script which is packaged with Mercurial). Since it's written in python it is portable to all the platforms that run Mercurial. On Windows, the script will even look in the registry for tools which are installed but not in the system path (Note: registry searching requires the pywin32 package).

To use this merge script on a *nix system, simply copy the script somewhere in your path, make it executable, then add these lines to your ~/.hgrc:

; Unix/Linux ~/.hgrc example
[ui]
merge = hgmerge.py

[hgmerge]
interactive = kdiff3
noninteractive = diff3

Configuration of the script is now documented separately.

On Windows, you will have to specify merge = python \path\to\hgmerge.py in the [ui] of your Mercurial.ini, presuming you have a python interpreter installed on your computer. If you have no python interpreter, then either install one or else you might be interested in trying the more user-friendly front end to Hg, TortoiseHg instead.

; Windows c:\Documents and Settings\USERNAME\Mercurial.ini or %HOME%\Mercurial.ini example
[ui]
merge = python c:\Mercurial\hgmerge.py

[hgmerge]
interactive = kdiff3
noninteractive = diff3

For this to work, the programs python.exe, kdiff3.exe, and diff3.exe are assumed to be on your PATH. See above for links to obtain various merge programs like kdiff3 or diff3. It also assumes you've saved the hgmerge.py file to c:\Mercurial\ .

Windows

On Windows you can create a batch file called hgmerge.cmd which is located somewhere in the path, e.g. in C:\Program Files\Mercurial. In this case do not set the merge entry in the ui section of your hgrc file. Having the batch file is enough.

<!> Note that you must create something, as no hgmerge script is currently provided on Windows.

The scripts are not intended for the user: If you call them directly they will exit your command prompt. This is necessary to make hg detect failed merges. If you really want to call this script, use "cmd /c hgmerge" or "start hgmerge".

KDiff3

Example script for KDiff3 (works with XP and later versions):

@echo off
setlocal
:: Look in the registry for KDiff3 location
for /f "skip=2 tokens=3*" %%A in (
    '"reg query "HKEY_CURRENT_USER\SOFTWARE\KDiff3" /ve 2> nul"' ) do set KDiff3Path=%%B
if "%KDiff3Path%"=="" (goto :notfound) else (goto :kdiff3)

:kdiff3
"%KDiff3Path%\kdiff3.exe" --auto --L1 Base --L2 Local --L3 Other %2 %1 %3 -o %1
if %errorlevel% neq 0 (exit 1) else (exit 0)

:notfound
echo hgmerge: cannot find KDiff3 location in the registry.
exit 1

Note: this script has problems when whitespace present in kdiff3 path. For example I have kdiff3 installed in C:\Program Files\KDiff3\.

Until someone fix the script to determine path correctly in such case, we may just hardcode full kdiff3 path in the script instead of querying registry:

@echo off
"C:\Program Files\KDiff3\kdiff3.exe" --auto --L1 Base --L2 Local --L3 Other %2 %1 %3 -o %1
if %errorlevel% neq 0 (exit 1) else (exit 0)

TortoiseMerge

Example script for TortoiseMerge (works with XP and later versions):

@echo off
setlocal
:: Look in the registry for TortoiseMerge location
for /f "skip=2 tokens=2*" %%A in (
    '"reg query "HKEY_LOCAL_MACHINE\SOFTWARE\TortoiseSVN" /v TMergePath 2> nul"' ) do set TMergePath=%%B
if "%TMergePath%"=="" (goto :notfound) else (goto :tortoisemerge)

:tortoisemerge
"%TMergePath%" /base:%2 /mine:%1 /theirs:%3 /merged:%1
if %errorlevel% neq 0 (exit 1) else (exit 0)

:notfound
echo hgmerge: cannot find TortoiseMerge location in the registry.
exit 1

In order to get it to TortoiseMerge to work with Cygwin's version of Mercurial try this:

@echo off
setlocal
:: Look in the registry for TortoiseMerge location
for /f "skip=2 tokens=2*" %%A in (
    '"reg query "HKEY_LOCAL_MACHINE\SOFTWARE\TortoiseSVN" /v TMergePath 2> nul"' ) do set TMergePath=%%B
if "%TMergePath%"=="" (goto :notfound) else (goto :tortoisemerge)

:tortoisemerge
for /f "tokens=1 delims=" %%A in (
    '"cygpath -w %1"' ) do set TMergePath1=%%A
for /f "tokens=1 delims=" %%A in (
    '"cygpath -w %2"' ) do set TMergePath2=%%A
for /f "tokens=1 delims=" %%A in (
    '"cygpath -w %3"' ) do set TMergePath3=%%A
echo %TMergePath1%
"%TMergePath%" /base:"%TMergePath2%" /mine:"%TMergePath1%" /theirs:"%TMergePath3%" /merged:"%TMergePath1%"
if %errorlevel% neq 0 (exit 1) else (exit 0)

:notfound
echo hgmerge: cannot find TortoiseMerge location in the registry.
exit 1

WinMerge

Example script for WinMerge (works with XP and later versions):

@echo off
setlocal
:: Look in the registry for WinMerge location
for /f "skip=2 tokens=2*" %%A in (
    '"reg query "HKEY_CURRENT_USER\Software\Thingamahoochie\WinMerge" /v Executable 2> nul"' ) do set WMergePath=%%B
if "%WMergePath%"=="" (goto :notfound) else (goto :merge)

:merge
"%WMergePath%" /e /ub /wl /dl "Repository" %3 %1
if %errorlevel% neq 0 (exit 1) else (exit 0)

:notfound
echo hgmerge: cannot find WinMerge location in the registry.
exit 1

Variation

A variation on each of the above scripts is to just try to launch the merge program using the default search path. For example the KDiff3 batch file might be modified to look something like this.

@echo off
setlocal
:: Look in the registry for KDiff3 location
for /f "skip=2 tokens=3*" %%A in (
    '"reg query "HKEY_CURRENT_USER\SOFTWARE\KDiff3" /ve 2> nul"' ) do set KDiff3Path=%%B
if "%KDiff3Path%"=="" (goto :trydefaultsearchpath) else (goto :kdiff3)

:kdiff3
"%KDiff3Path%\kdiff3.exe" --auto --L1 Base --L2 Local --L3 Other %2 %1 %3 -o %1
if %errorlevel% neq 0 (exit 1) else (exit 0)

:trydefaultsearchpath
kdiff3.exe --auto --L1 Base --L2 Local --L3 Other %2 %1 %3 -o %1
if %errorlevel% neq 0 (exit 1) else (exit 0)

DiffMerge

Example script for DiffMerge (works on any Windows system with Windows PowerShell installed):

param($localPath, $basePath, $otherPath);

$windowTitle = "Mercurial Merge";
$localTitle = "Mine";
$baseTitle = "Base";
$otherTitle = "Theirs";
$diffMergePath =
        "C:\\Program Files\\SourceGear\\DiffMerge\\DiffMerge.exe";

if (-not [System.IO.File]::Exists($diffMergePath))
{
        Write-Host [System.String]::Format(
                "DiffMerge not found: {0}", $diffMergePath);
        return 1;
}

if (-not [System.IO.File]::Exists($localPath))
{
        Write-Host ("Local file not found: {0}", $localPath);
        return 1;
}

if (-not [System.IO.File]::Exists($basePath))
{
        Write-Host [System.String]::Format("Base file not found: {0}", $basePath);
        return 1;
}

if (-not [System.IO.File]::Exists($otherPath))
{
        Write-Host [System.String]::Format("Other file not found: {0}", $otherPath);
        return 1;
}

$diffMergeProcessStartInfo = New-Object System.Diagnostics.ProcessStartInfo;
$diffMergeProcessStartInfo.FileName = $diffMergePath;
$diffMergeProcessStartInfo.Arguments = [System.String]::Format(
        '--nosplash --merge --caption="{0}" ' +
        '--title1="{1}" ' +
        '--title2="{2}" ' +
        '--title3="{3}" ' +
        '{4} {5} {6}',
                $windowTitle,
                $baseTitle,
                $localTitle,
                $otherTitle,
                $basePath,
                $localPath,
                $otherPath);

$diffMergeProcess = New-Object System.Diagnostics.Process;
$diffMergeProcess.StartInfo = $diffMergeProcessStartInfo;

$beforeMergeLastWriteTime = [System.IO.File]::GetLastWriteTime($localPath);

$exitCode = $diffMergeProcess.Start();
$diffMergeProcess.WaitForExit();

trap [System.Exception]
{
        Write-Host [System.String]::Format(
                "An error occured while starting DiffMerge.")
        return 1;
}

$afterMergeLastWriteTime = [System.IO.File]::GetLastWriteTime($localPath);

if ($afterMergeLastWriteTime -le $beforeMergeLastWriteTime)
{
        Write-Host "File not changed.";
        return 1;
}
else
{
        return 0;
}

hgmerge (last edited 2009-05-19 19:31:05 by localhost)