mercurial/sshrepo.py
author Thomas Arendsen Hein <thomas@intevation.de>
Tue, 11 Apr 2006 08:42:07 +0200
changeset 2057 fef2d653beaf
parent 2046 d14497cbd668
child 2176 9b42304d9896
permissions -rw-r--r--
Never exit directly from commands.dispatch(), but pass return code to caller. Usually the caller is commands.run(). Some extensions still use sys.exit(), this is catched, too. Fixed wrong return statement in commands.recover() yielding a zero exit code.

# sshrepo.py - ssh repository proxy class for mercurial
#
# Copyright 2005 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.

from node import *
from remoterepo import *
from i18n import gettext as _
from demandload import *
demandload(globals(), "hg os re stat")

class sshrepository(remoterepository):
    def __init__(self, ui, path):
        self.url = path
        self.ui = ui

        m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
        if not m:
            raise hg.RepoError(_("couldn't parse destination %s") % path)

        self.user = m.group(2)
        self.host = m.group(3)
        self.port = m.group(5)
        self.path = m.group(7) or "."

        args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
        args = self.port and ("%s -p %s") % (args, self.port) or args

        sshcmd = self.ui.config("ui", "ssh", "ssh")
        remotecmd = self.ui.config("ui", "remotecmd", "hg")
        cmd = '%s %s "%s -R %s serve --stdio"'
        cmd = cmd % (sshcmd, args, remotecmd, self.path)

        ui.note('running %s\n' % cmd)
        self.pipeo, self.pipei, self.pipee = os.popen3(cmd, 'b')

        # skip any noise generated by remote shell
        r = self.do_cmd("between", pairs=("%s-%s" % ("0"*40, "0"*40)))
        l1 = ""
        l2 = "dummy"
        max_noise = 500
        while l2 and max_noise:
            l2 = r.readline()
            self.readerr()
            if l1 == "1\n" and l2 == "\n":
                break
            if l1:
                ui.debug(_("remote: "), l1)
            l1 = l2
            max_noise -= 1
        else:
            if l1:
                ui.debug(_("remote: "), l1)
            raise hg.RepoError(_("no response from remote hg"))

    def readerr(self):
        while 1:
            size = os.fstat(self.pipee.fileno())[stat.ST_SIZE]
            if size == 0: break
            l = self.pipee.readline()
            if not l: break
            self.ui.status(_("remote: "), l)

    def __del__(self):
        try:
            self.pipeo.close()
            self.pipei.close()
            # read the error descriptor until EOF
            for l in self.pipee:
                self.ui.status(_("remote: "), l)
            self.pipee.close()
        except:
            pass

    def dev(self):
        return -1

    def do_cmd(self, cmd, **args):
        self.ui.debug(_("sending %s command\n") % cmd)
        self.pipeo.write("%s\n" % cmd)
        for k, v in args.items():
            self.pipeo.write("%s %d\n" % (k, len(v)))
            self.pipeo.write(v)
        self.pipeo.flush()

        return self.pipei

    def call(self, cmd, **args):
        r = self.do_cmd(cmd, **args)
        l = r.readline()
        self.readerr()
        try:
            l = int(l)
        except:
            raise hg.RepoError(_("unexpected response '%s'") % l)
        return r.read(l)

    def lock(self):
        self.call("lock")
        return remotelock(self)

    def unlock(self):
        self.call("unlock")

    def heads(self):
        d = self.call("heads")
        try:
            return map(bin, d[:-1].split(" "))
        except:
            raise hg.RepoError(_("unexpected response '%s'") % (d[:400] + "..."))

    def branches(self, nodes):
        n = " ".join(map(hex, nodes))
        d = self.call("branches", nodes=n)
        try:
            br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
            return br
        except:
            raise hg.RepoError(_("unexpected response '%s'") % (d[:400] + "..."))

    def between(self, pairs):
        n = "\n".join(["-".join(map(hex, p)) for p in pairs])
        d = self.call("between", pairs=n)
        try:
            p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
            return p
        except:
            raise hg.RepoError(_("unexpected response '%s'") % (d[:400] + "..."))

    def changegroup(self, nodes, kind):
        n = " ".join(map(hex, nodes))
        f = self.do_cmd("changegroup", roots=n)
        return self.pipei

    def addchangegroup(self, cg):
        d = self.call("addchangegroup")
        if d:
            raise hg.RepoError(_("push refused: %s"), d)

        while 1:
            d = cg.read(4096)
            if not d: break
            self.pipeo.write(d)
            self.readerr()

        self.pipeo.flush()

        self.readerr()
        l = int(self.pipei.readline())
        r = self.pipei.read(l)
        if not r:
            return 1
        return int(r)