sshmercurialputtytortoisehg

Different SSH ports/keys for different hosts, with TortoisePLink


TortoiseHg (Mercurial) normally uses TortoisePLink/plink to connect via SSH. Different SSH hosts have different settings, including private keys and ports to connect to.

In its global hgrc settings, TortoiseHg lets you specify different logins and passwords for different hosts, but not private keys nor ports.

How to override SSH port/private key globally, but only for some hosts and not others?


Solution

  • I'll list a number of imperfect approaches for completeness, then give the one I've found that I think is better:

    ssh setting

    Mercurial ssh setting can be used to pass additional params to plink globally:

    ssh=path\to\TortoisePLink.exe -i "private1.key" -i "private2.key" -P port
    

    But the same ssh string is used for all hosts, so different ports cannot be configured. And while you can list several private keys, some servers will refuse all but the first one.

    putty's saved sessions

    plink comes from putty package, and putty can store server configurations (sessions) in the registry, HKEY_CURRENT_USER\SOFTWARE\SimonTatham\PuTTY\Sessions. plink can use these if you use a session name in the host place, e.g. ssh://session-name/path/to/repository.

    But for some reason plink ignores stored port settings and will use the default SSH port. Probably a bug, but it's unfixed.

    Local hgrcs

    You can override ssh for each repository by creating a local .hgrc file, but it's unwieldy if you have lots of repos.

    Explicit ports

    You can specify ports explicitly in the remote repository address:

    ssh://server.name:port/path/to/repository
    

    Same problems as with local hgrcs.

    PLink script wrapper

    After struggling with this for a while, I have finally thought of something that seems to work. We can wrap TortoisePLink in a script that adds per-server params:

    ssh=path\to\TortoisePLinkWrapper.cmd <all the normal common params>
    

    Create a file called username@host.name.cfg for every host you want to override, and its contents should be additional params to pass to TortoisePLink, e.g.:

    -P 1234 -i "path\to\key.file"
    

    Place this in the same dir:

    @echo off
    set SERVER_PARAMS=
    set SERVER_NAME=
    call :find_server %*
    if "%SERVER_NAME%"=="" goto :call_plink
    set "SERVER_CONFIG_FILE=%~dp0%SERVER_NAME%.cfg"
    if NOT EXIST "%SERVER_CONFIG_FILE%" goto :call_plink
    set /p SERVER_PARAMS=<"%SERVER_CONFIG_FILE%"
    
    :call_plink
    rem TortoisePLink ignores some params if you pass them after the ssh command, so add extra commands before the bulk of them
    "%ProgramFiles%\TortoiseHg\lib\TortoisePLink.exe" %SERVER_PARAMS% %*
    exit /B
    
    :find_server
    if [%1]==[] exit /B
    rem Known arguments which eat some more positions
    rem https://putty.org.ru/htmldoc/chapter7.html
    if [%1]==[-P] shift & shift & goto :find_server
    if [%1]==[-l] shift & shift & goto :find_server
    if [%1]==[-pw] shift & shift & goto :find_server
    if [%1]==[-proxycmd] shift & shift & goto :find_server
    if [%1]==[-sercfg] shift & shift & goto :find_server
    if [%1]==[-D] shift & shift & goto :find_server
    if [%1]==[-L] shift & shift & goto :find_server
    if [%1]==[-R] shift & shift & goto :find_server
    if [%1]==[-i] shift & shift & goto :find_server
    if [%1]==[-hostkey] shift & shift & goto :find_server
    if [%1]==[-m] shift & shift & goto :find_server
    if [%1]==[-nc] shift & shift & goto :find_server
    if [%1]==[-sshlog] shift & shift & goto :find_server
    if [%1]==[-sshrawlog] shift & shift & goto :find_server
    set "PARAM=%~1"
    rem Other flag argument:
    if "%PARAM:~0,1%"=="-" shift & goto :find_server
    rem First positional argument
    set "SERVER_NAME=%~1"
    exit /B