Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to clone remote repo when using SSH alias

Tags:

ssh

mercurial

I know my ssh configuration works because I can just type ssh myAlias.ssh and connect just fine.

I'm trying to use the command hg clone ssh://myAlias.ssh//path/to/repo and getting a remote: ssh: Could not resolve hostname myAlias.ssh: No such file or directory

Is it possible to use an SSH alias here?

like image 312
Webnet Avatar asked Jan 27 '26 14:01

Webnet


1 Answers

Theory

General principles

Mercurial does work with aliases created in ~/.ssh/config -- I use this feature all the time on Linux and OS X. This works because Mercurial doesn't try to resolve the hostname itself, but rather depends on SSH to do this (and passes the error up, when it encounters one). This is why the error is has the ssh: as part of its prefix. Bitbucket even has help on setting up aliases via .ssh/config. (In the linked example, they use it to manage two separate identities.) If you created an alias via another mechanism, say BASH alias, then this will not work because it is dependent on BASH, which Mercurial does not use.

From the Mercurial source code

I have also gone through the Mercurial source code for clone, including examining sshpeer.py and util.py for the SSH related parts, and Mercurial does indeed pass along the hostname/alias to SSH for correct parsing / interpretation.

The docstring for class url in util.py of the Mercurial (2.6.3) source code:

Reliable URL parser.

This parses URLs and provides attributes for the following
components:

<scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>

Missing components are set to None. The only exception is
fragment, which is set to '' if present but empty.

If parsefragment is False, fragment is included in query. If
parsequery is False, query is included in path. If both are
False, both fragment and query are included in path.

See http://www.ietf.org/rfc/rfc2396.txt for more information.

Note that for backward compatibility reasons, bundle URLs do not
take host names. That means 'bundle://../' has a path of '../'.

Examples:

>>> url('http://www.ietf.org/rfc/rfc2396.txt')
<url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
>>> url('ssh://[::1]:2200//home/joe/repo')
<url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
>>> url('file:///home/joe/repo')
<url scheme: 'file', path: '/home/joe/repo'>
>>> url('file:///c:/temp/foo/')
<url scheme: 'file', path: 'c:/temp/foo/'>
>>> url('bundle:foo')
<url scheme: 'bundle', path: 'foo'>
>>> url('bundle://../foo')
<url scheme: 'bundle', path: '../foo'>
>>> url(r'c:\foo\bar')
<url path: 'c:\\foo\\bar'>
>>> url(r'\\blah\blah\blah')
<url path: '\\\\blah\\blah\\blah'>
>>> url(r'\\blah\blah\blah#baz')
<url path: '\\\\blah\\blah\\blah', fragment: 'baz'>

Authentication credentials:

>>> url('ssh://joe:xyz@x/repo')
<url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
>>> url('ssh://joe@x/repo')
<url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>

Query strings and fragments:

>>> url('http://host/a?b#c')
<url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
>>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
<url scheme: 'http', host: 'host', path: 'a?b#c'> 

However, the __init__ method of class sshpeer in sshpeer.py (which is used for cloning over SSH) introduces the additional restriction that passwords are not allowed in the URL:

    u = util.url(path, parsequery=False, parsefragment=False)
    if u.scheme != 'ssh' or not u.host or u.path is None:
        self._abort(error.RepoError(_("couldn't parse location %s") % path))

    self.user = u.user
    if u.passwd is not None:
        self._abort(error.RepoError(_("password in URL not supported")))

(Password in the URL is allowed for other protocols, but I'll leave finding the relevant blocks of code as an exercise for the reader, or see the documentation on URLs)

Demonstrating this live -- potential help in debugging

We can use the -v option to see exactly how Mercurial interacts with SSH during a clone. But first, some relevant excerpts from my configuration files.

From my .ssh/config file:

Host bitbucket.ssh 
  Hostname bitbucket.org
  User hg

From my .hgrc file:

[ui]
# Irrelevant settings omitted
# enable compression in SSH
ssh = ssh -C

Now, we take a look at what happens during a clone:

livius@localhost ~ $ hg clone -v ssh://bitbucket.ssh/palday/splitauthor
running ssh -C bitbucket.ssh 'hg -R palday/splitauthor serve --stdio'
destination directory: splitauthor
requesting all changes
adding changesets
adding manifests
adding file changes
added 3 changesets with 9 changes to 6 files
updating to branch default
resolving manifests
getting .hgignore
getting COPYING
getting README.rst
getting filter-revisions.awk
getting splitauthor.sh
getting testregex.sh
6 files updated, 0 files merged, 0 files removed, 0 files unresolved

The first output line really says it all: Mercurial passes the hostname that it parses out of the URL -- in this case, your alias -- to SSH, which handles the actual issue of resolving the hostname/alias.

Other Comments and Tips

  • In your example hg clone ssh://myAlias.ssh//path/to/repo, you have a slash too many after the hostname, but I'm assuming this is only in the example. If not, this could be causing your path to be absolute, instead of relative to a user name (as configured in .ssh/config). See also the examples in the documentation on hg clone

  • I also find the exact error message a bit odd. When I try this with an undefined hostname, e.g. an alias that I haven't defined in my .ssh/config, I get the following error on OS X: remote: ssh: Could not resolve hostname server.ssh: nodename nor servname provided, or not known. On Linux, I get remote: ssh: Could not resolve hostname server.ssh: Name or service not known.

    So, my suspicion is that you're doing this on Windows. I don't know how PuTTY and the like handle the configuration file on Windows, which might mean that there's a different syntax and that's what your problem is. Running hg clone -v will also let you see the exact call Mercurial is making, which could also be quite useful in tracking down where things are going wrong.

  • On the Unix-y systems, you can try ssh -T myAlias.ssh to test the connection and your alias pass/fail, or ssh -v myAlias.ssh to get exceptionally verbose output on what's happening during the connection. If ssh -T fails, then it's definitely an issue at a level lower than Mercurial.

  • You can also set set SSH itself to be verbose: Instead of ssh = ssh -C like in my .hgrc snippet above, you can set ssh = ssh -Cv to get verbose debugging output from SSH. This generated about 70 lines of debugging output for me.

like image 155
Livius Avatar answered Jan 30 '26 09:01

Livius



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!