diff mercurial/localrepo.py @ 1089:142b5d5ec9cc

Break apart hg.py - move the various parts of hg.py into their own files - create node.py to store node manipulation functions
author mpm@selenic.com
date Sat, 27 Aug 2005 14:21:25 -0700
parents mercurial/hg.py@05dc7aba22eb
children 1f89ccbab6ce
line wrap: on
line diff
copy from mercurial/hg.py
copy to mercurial/localrepo.py
--- a/mercurial/hg.py
+++ b/mercurial/localrepo.py
@@ -1,627 +1,22 @@
-# hg.py - repository classes for mercurial
+# localrepo.py - read/write repository 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.
 
-import sys, struct, os
-import util
+import sys, struct, os, util
+from repo import *
 from revlog import *
+from filelog import *
+from manifest import *
+from changelog import *
 from demandload import *
-demandload(globals(), "re lock urllib urllib2 transaction time socket")
-demandload(globals(), "tempfile httprangereader bdiff urlparse")
-demandload(globals(), "bisect errno select stat")
-
-class filelog(revlog):
-    def __init__(self, opener, path):
-        revlog.__init__(self, opener,
-                        os.path.join("data", self.encodedir(path + ".i")),
-                        os.path.join("data", self.encodedir(path + ".d")))
-
-    # This avoids a collision between a file named foo and a dir named
-    # foo.i or foo.d
-    def encodedir(self, path):
-        return (path
-                .replace(".hg/", ".hg.hg/")
-                .replace(".i/", ".i.hg/")
-                .replace(".d/", ".d.hg/"))
-
-    def decodedir(self, path):
-        return (path
-                .replace(".d.hg/", ".d/")
-                .replace(".i.hg/", ".i/")
-                .replace(".hg.hg/", ".hg/"))
-
-    def read(self, node):
-        t = self.revision(node)
-        if not t.startswith('\1\n'):
-            return t
-        s = t.find('\1\n', 2)
-        return t[s+2:]
-
-    def readmeta(self, node):
-        t = self.revision(node)
-        if not t.startswith('\1\n'):
-            return t
-        s = t.find('\1\n', 2)
-        mt = t[2:s]
-        for l in mt.splitlines():
-            k, v = l.split(": ", 1)
-            m[k] = v
-        return m
-
-    def add(self, text, meta, transaction, link, p1=None, p2=None):
-        if meta or text.startswith('\1\n'):
-            mt = ""
-            if meta:
-                mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
-            text = "\1\n" + "".join(mt) + "\1\n" + text
-        return self.addrevision(text, transaction, link, p1, p2)
-
-    def annotate(self, node):
-
-        def decorate(text, rev):
-            return ([rev] * len(text.splitlines()), text)
-
-        def pair(parent, child):
-            for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
-                child[0][b1:b2] = parent[0][a1:a2]
-            return child
-
-        # find all ancestors
-        needed = {node:1}
-        visit = [node]
-        while visit:
-            n = visit.pop(0)
-            for p in self.parents(n):
-                if p not in needed:
-                    needed[p] = 1
-                    visit.append(p)
-                else:
-                    # count how many times we'll use this
-                    needed[p] += 1
-
-        # sort by revision which is a topological order
-        visit = [ (self.rev(n), n) for n in needed.keys() ]
-        visit.sort()
-        hist = {}
-
-        for r,n in visit:
-            curr = decorate(self.read(n), self.linkrev(n))
-            for p in self.parents(n):
-                if p != nullid:
-                    curr = pair(hist[p], curr)
-                    # trim the history of unneeded revs
-                    needed[p] -= 1
-                    if not needed[p]:
-                        del hist[p]
-            hist[n] = curr
-
-        return zip(hist[n][0], hist[n][1].splitlines(1))
-
-class manifest(revlog):
-    def __init__(self, opener):
-        self.mapcache = None
-        self.listcache = None
-        self.addlist = None
-        revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
-
-    def read(self, node):
-        if node == nullid: return {} # don't upset local cache
-        if self.mapcache and self.mapcache[0] == node:
-            return self.mapcache[1]
-        text = self.revision(node)
-        map = {}
-        flag = {}
-        self.listcache = (text, text.splitlines(1))
-        for l in self.listcache[1]:
-            (f, n) = l.split('\0')
-            map[f] = bin(n[:40])
-            flag[f] = (n[40:-1] == "x")
-        self.mapcache = (node, map, flag)
-        return map
-
-    def readflags(self, node):
-        if node == nullid: return {} # don't upset local cache
-        if not self.mapcache or self.mapcache[0] != node:
-            self.read(node)
-        return self.mapcache[2]
-
-    def diff(self, a, b):
-        # this is sneaky, as we're not actually using a and b
-        if self.listcache and self.addlist and self.listcache[0] == a:
-            d = mdiff.diff(self.listcache[1], self.addlist, 1)
-            if mdiff.patch(a, d) != b:
-                sys.stderr.write("*** sortdiff failed, falling back ***\n")
-                return mdiff.textdiff(a, b)
-            return d
-        else:
-            return mdiff.textdiff(a, b)
-
-    def add(self, map, flags, transaction, link, p1=None, p2=None,
-            changed=None):
-        # directly generate the mdiff delta from the data collected during
-        # the bisect loop below
-        def gendelta(delta):
-            i = 0
-            result = []
-            while i < len(delta):
-                start = delta[i][2]
-                end = delta[i][3]
-                l = delta[i][4]
-                if l == None:
-                    l = ""
-                while i < len(delta) - 1 and start <= delta[i+1][2] \
-                          and end >= delta[i+1][2]:
-                    if delta[i+1][3] > end:
-                        end = delta[i+1][3]
-                    if delta[i+1][4]:
-                        l += delta[i+1][4]
-                    i += 1
-                result.append(struct.pack(">lll", start, end, len(l)) +  l)
-                i += 1
-            return result
-
-        # apply the changes collected during the bisect loop to our addlist
-        def addlistdelta(addlist, delta):
-            # apply the deltas to the addlist.  start from the bottom up
-            # so changes to the offsets don't mess things up.
-            i = len(delta)
-            while i > 0:
-                i -= 1
-                start = delta[i][0]
-                end = delta[i][1]
-                if delta[i][4]:
-                    addlist[start:end] = [delta[i][4]]
-                else:
-                    del addlist[start:end]
-            return addlist
-
-        # calculate the byte offset of the start of each line in the
-        # manifest
-        def calcoffsets(addlist):
-            offsets = [0] * (len(addlist) + 1)
-            offset = 0
-            i = 0
-            while i < len(addlist):
-                offsets[i] = offset
-                offset += len(addlist[i])
-                i += 1
-            offsets[i] = offset
-            return offsets
-
-        # if we're using the listcache, make sure it is valid and
-        # parented by the same node we're diffing against
-        if not changed or not self.listcache or not p1 or \
-               self.mapcache[0] != p1:
-            files = map.keys()
-            files.sort()
-
-            self.addlist = ["%s\000%s%s\n" %
-                            (f, hex(map[f]), flags[f] and "x" or '')
-                            for f in files]
-            cachedelta = None
-        else:
-            addlist = self.listcache[1]
-
-            # find the starting offset for each line in the add list
-            offsets = calcoffsets(addlist)
-
-            # combine the changed lists into one list for sorting
-            work = [[x, 0] for x in changed[0]]
-            work[len(work):] = [[x, 1] for x in changed[1]]
-            work.sort()
-
-            delta = []
-            bs = 0
-
-            for w in work:
-                f = w[0]
-                # bs will either be the index of the item or the insert point
-                bs = bisect.bisect(addlist, f, bs)
-                if bs < len(addlist):
-                    fn = addlist[bs][:addlist[bs].index('\0')]
-                else:
-                    fn = None
-                if w[1] == 0:
-                    l = "%s\000%s%s\n" % (f, hex(map[f]),
-                                          flags[f] and "x" or '')
-                else:
-                    l = None
-                start = bs
-                if fn != f:
-                    # item not found, insert a new one
-                    end = bs
-                    if w[1] == 1:
-                        sys.stderr.write("failed to remove %s from manifest\n"
-                                         % f)
-                        sys.exit(1)
-                else:
-                    # item is found, replace/delete the existing line
-                    end = bs + 1
-                delta.append([start, end, offsets[start], offsets[end], l])
-
-            self.addlist = addlistdelta(addlist, delta)
-            if self.mapcache[0] == self.tip():
-                cachedelta = "".join(gendelta(delta))
-            else:
-                cachedelta = None
-
-        text = "".join(self.addlist)
-        if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text:
-            sys.stderr.write("manifest delta failure\n")
-            sys.exit(1)
-        n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
-        self.mapcache = (n, map, flags)
-        self.listcache = (text, self.addlist)
-        self.addlist = None
-
-        return n
-
-class changelog(revlog):
-    def __init__(self, opener):
-        revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
-
-    def extract(self, text):
-        if not text:
-            return (nullid, "", "0", [], "")
-        last = text.index("\n\n")
-        desc = text[last + 2:]
-        l = text[:last].splitlines()
-        manifest = bin(l[0])
-        user = l[1]
-        date = l[2]
-        if " " not in date:
-            date += " 0" # some tools used -d without a timezone
-        files = l[3:]
-        return (manifest, user, date, files, desc)
-
-    def read(self, node):
-        return self.extract(self.revision(node))
-
-    def add(self, manifest, list, desc, transaction, p1=None, p2=None,
-                  user=None, date=None):
-        if not date:
-            if time.daylight: offset = time.altzone
-            else: offset = time.timezone
-            date = "%d %d" % (time.time(), offset)
-        list.sort()
-        l = [hex(manifest), user, date] + list + ["", desc]
-        text = "\n".join(l)
-        return self.addrevision(text, transaction, self.count(), p1, p2)
-
-class dirstate:
-    def __init__(self, opener, ui, root):
-        self.opener = opener
-        self.root = root
-        self.dirty = 0
-        self.ui = ui
-        self.map = None
-        self.pl = None
-        self.copies = {}
-        self.ignorefunc = None
-
-    def wjoin(self, f):
-        return os.path.join(self.root, f)
-
-    def getcwd(self):
-        cwd = os.getcwd()
-        if cwd == self.root: return ''
-        return cwd[len(self.root) + 1:]
-
-    def ignore(self, f):
-        if not self.ignorefunc:
-            bigpat = []
-            try:
-                l = file(self.wjoin(".hgignore"))
-                for pat in l:
-                    p = pat.rstrip()
-                    if p:
-                        try:
-                            re.compile(p)
-                        except:
-                            self.ui.warn("ignoring invalid ignore"
-                                         + " regular expression '%s'\n" % p)
-                        else:
-                            bigpat.append(p)
-            except IOError: pass
-
-            if bigpat:
-                s = "(?:%s)" % (")|(?:".join(bigpat))
-                r = re.compile(s)
-                self.ignorefunc = r.search
-            else:
-                self.ignorefunc = util.never
-
-        return self.ignorefunc(f)
-
-    def __del__(self):
-        if self.dirty:
-            self.write()
-
-    def __getitem__(self, key):
-        try:
-            return self.map[key]
-        except TypeError:
-            self.read()
-            return self[key]
-
-    def __contains__(self, key):
-        if not self.map: self.read()
-        return key in self.map
-
-    def parents(self):
-        if not self.pl:
-            self.read()
-        return self.pl
-
-    def markdirty(self):
-        if not self.dirty:
-            self.dirty = 1
-
-    def setparents(self, p1, p2=nullid):
-        self.markdirty()
-        self.pl = p1, p2
-
-    def state(self, key):
-        try:
-            return self[key][0]
-        except KeyError:
-            return "?"
-
-    def read(self):
-        if self.map is not None: return self.map
-
-        self.map = {}
-        self.pl = [nullid, nullid]
-        try:
-            st = self.opener("dirstate").read()
-            if not st: return
-        except: return
-
-        self.pl = [st[:20], st[20: 40]]
-
-        pos = 40
-        while pos < len(st):
-            e = struct.unpack(">cllll", st[pos:pos+17])
-            l = e[4]
-            pos += 17
-            f = st[pos:pos + l]
-            if '\0' in f:
-                f, c = f.split('\0')
-                self.copies[f] = c
-            self.map[f] = e[:4]
-            pos += l
-
-    def copy(self, source, dest):
-        self.read()
-        self.markdirty()
-        self.copies[dest] = source
-
-    def copied(self, file):
-        return self.copies.get(file, None)
-
-    def update(self, files, state, **kw):
-        ''' current states:
-        n  normal
-        m  needs merging
-        r  marked for removal
-        a  marked for addition'''
-
-        if not files: return
-        self.read()
-        self.markdirty()
-        for f in files:
-            if state == "r":
-                self.map[f] = ('r', 0, 0, 0)
-            else:
-                s = os.stat(os.path.join(self.root, f))
-                st_size = kw.get('st_size', s.st_size)
-                st_mtime = kw.get('st_mtime', s.st_mtime)
-                self.map[f] = (state, s.st_mode, st_size, st_mtime)
-
-    def forget(self, files):
-        if not files: return
-        self.read()
-        self.markdirty()
-        for f in files:
-            try:
-                del self.map[f]
-            except KeyError:
-                self.ui.warn("not in dirstate: %s!\n" % f)
-                pass
-
-    def clear(self):
-        self.map = {}
-        self.markdirty()
-
-    def write(self):
-        st = self.opener("dirstate", "w")
-        st.write("".join(self.pl))
-        for f, e in self.map.items():
-            c = self.copied(f)
-            if c:
-                f = f + "\0" + c
-            e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
-            st.write(e + f)
-        self.dirty = 0
-
-    def filterfiles(self, files):
-        ret = {}
-        unknown = []
-
-        for x in files:
-            if x is '.':
-                return self.map.copy()
-            if x not in self.map:
-                unknown.append(x)
-            else:
-                ret[x] = self.map[x]
-
-        if not unknown:
-            return ret
-
-        b = self.map.keys()
-        b.sort()
-        blen = len(b)
-
-        for x in unknown:
-            bs = bisect.bisect(b, x)
-            if bs != 0 and  b[bs-1] == x:
-                ret[x] = self.map[x]
-                continue
-            while bs < blen:
-                s = b[bs]
-                if len(s) > len(x) and s.startswith(x) and s[len(x)] == '/':
-                    ret[s] = self.map[s]
-                else:
-                    break
-                bs += 1
-        return ret
-
-    def walk(self, files=None, match=util.always, dc=None):
-        self.read()
-
-        # walk all files by default
-        if not files:
-            files = [self.root]
-            if not dc:
-                dc = self.map.copy()
-        elif not dc:
-            dc = self.filterfiles(files)
-
-        known = {'.hg': 1}
-        def seen(fn):
-            if fn in known: return True
-            known[fn] = 1
-        def traverse():
-            for ff in util.unique(files):
-                f = os.path.join(self.root, ff)
-                try:
-                    st = os.stat(f)
-                except OSError, inst:
-                    if ff not in dc: self.ui.warn('%s: %s\n' % (
-                        util.pathto(self.getcwd(), ff),
-                        inst.strerror))
-                    continue
-                if stat.S_ISDIR(st.st_mode):
-                    for dir, subdirs, fl in os.walk(f):
-                        d = dir[len(self.root) + 1:]
-                        nd = util.normpath(d)
-                        if nd == '.': nd = ''
-                        if seen(nd):
-                            subdirs[:] = []
-                            continue
-                        for sd in subdirs:
-                            ds = os.path.join(nd, sd +'/')
-                            if self.ignore(ds) or not match(ds):
-                                subdirs.remove(sd)
-                        subdirs.sort()
-                        fl.sort()
-                        for fn in fl:
-                            fn = util.pconvert(os.path.join(d, fn))
-                            yield 'f', fn
-                elif stat.S_ISREG(st.st_mode):
-                    yield 'f', ff
-                else:
-                    kind = 'unknown'
-                    if stat.S_ISCHR(st.st_mode): kind = 'character device'
-                    elif stat.S_ISBLK(st.st_mode): kind = 'block device'
-                    elif stat.S_ISFIFO(st.st_mode): kind = 'fifo'
-                    elif stat.S_ISLNK(st.st_mode): kind = 'symbolic link'
-                    elif stat.S_ISSOCK(st.st_mode): kind = 'socket'
-                    self.ui.warn('%s: unsupported file type (type is %s)\n' % (
-                        util.pathto(self.getcwd(), ff),
-                        kind))
-
-            ks = dc.keys()
-            ks.sort()
-            for k in ks:
-                yield 'm', k
-
-        # yield only files that match: all in dirstate, others only if
-        # not in .hgignore
-
-        for src, fn in util.unique(traverse()):
-            fn = util.normpath(fn)
-            if seen(fn): continue
-            if fn not in dc and self.ignore(fn):
-                continue
-            if match(fn):
-                yield src, fn
-
-    def changes(self, files=None, match=util.always):
-        self.read()
-        if not files:
-            dc = self.map.copy()
-        else:
-            dc = self.filterfiles(files)
-        lookup, modified, added, unknown = [], [], [], []
-        removed, deleted = [], []
-
-        for src, fn in self.walk(files, match, dc=dc):
-            try:
-                s = os.stat(os.path.join(self.root, fn))
-            except OSError:
-                continue
-            if not stat.S_ISREG(s.st_mode):
-                continue
-            c = dc.get(fn)
-            if c:
-                del dc[fn]
-                if c[0] == 'm':
-                    modified.append(fn)
-                elif c[0] == 'a':
-                    added.append(fn)
-                elif c[0] == 'r':
-                    unknown.append(fn)
-                elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
-                    modified.append(fn)
-                elif c[3] != s.st_mtime:
-                    lookup.append(fn)
-            else:
-                unknown.append(fn)
-
-        for fn, c in [(fn, c) for fn, c in dc.items() if match(fn)]:
-            if c[0] == 'r':
-                removed.append(fn)
-            else:
-                deleted.append(fn)
-        return (lookup, modified, added, removed + deleted, unknown)
-
-# used to avoid circular references so destructors work
-def opener(base):
-    p = base
-    def o(path, mode="r"):
-        if p.startswith("http://"):
-            f = os.path.join(p, urllib.quote(path))
-            return httprangereader.httprangereader(f)
-
-        f = os.path.join(p, path)
-
-        mode += "b" # for that other OS
-
-        if mode[0] != "r":
-            try:
-                s = os.stat(f)
-            except OSError:
-                d = os.path.dirname(f)
-                if not os.path.isdir(d):
-                    os.makedirs(d)
-            else:
-                if s.st_nlink > 1:
-                    file(f + ".tmp", "wb").write(file(f, "rb").read())
-                    util.rename(f+".tmp", f)
-
-        return file(f, mode)
-
-    return o
-
-class RepoError(Exception): pass
+from dirstate import *
+demandload(globals(), "re lock transaction tempfile stat")
 
 class localrepository:
-    def __init__(self, ui, path=None, create=0):
+    def __init__(self, ui, opener, path=None, create=0):
         self.remote = 0
         if path and path.startswith("http://"):
             self.remote = 1
@@ -2014,283 +1409,3 @@ class localrepository:
         if errors:
             self.ui.warn("%d integrity errors encountered!\n" % errors)
             return 1
-
-class remoterepository:
-    def local(self):
-        return False
-
-class httprepository(remoterepository):
-    def __init__(self, ui, path):
-        # fix missing / after hostname
-        s = urlparse.urlsplit(path)
-        partial = s[2]
-        if not partial: partial = "/"
-        self.url = urlparse.urlunsplit((s[0], s[1], partial, '', ''))
-        self.ui = ui
-        no_list = [ "localhost", "127.0.0.1" ]
-        host = ui.config("http_proxy", "host")
-        if host is None:
-            host = os.environ.get("http_proxy")
-        if host and host.startswith('http://'):
-            host = host[7:]
-        user = ui.config("http_proxy", "user")
-        passwd = ui.config("http_proxy", "passwd")
-        no = ui.config("http_proxy", "no")
-        if no is None:
-            no = os.environ.get("no_proxy")
-        if no:
-            no_list = no_list + no.split(",")
-
-        no_proxy = 0
-        for h in no_list:
-            if (path.startswith("http://" + h + "/") or
-                path.startswith("http://" + h + ":") or
-                path == "http://" + h):
-                no_proxy = 1
-
-        # Note: urllib2 takes proxy values from the environment and those will
-        # take precedence
-        for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
-            try:
-                if os.environ.has_key(env):
-                    del os.environ[env]
-            except OSError:
-                pass
-
-        proxy_handler = urllib2.BaseHandler()
-        if host and not no_proxy:
-            proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
-
-        authinfo = None
-        if user and passwd:
-            passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
-            passmgr.add_password(None, host, user, passwd)
-            authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
-
-        opener = urllib2.build_opener(proxy_handler, authinfo)
-        urllib2.install_opener(opener)
-
-    def dev(self):
-        return -1
-
-    def do_cmd(self, cmd, **args):
-        self.ui.debug("sending %s command\n" % cmd)
-        q = {"cmd": cmd}
-        q.update(args)
-        qs = urllib.urlencode(q)
-        cu = "%s?%s" % (self.url, qs)
-        resp = urllib2.urlopen(cu)
-        proto = resp.headers['content-type']
-
-        # accept old "text/plain" and "application/hg-changegroup" for now
-        if not proto.startswith('application/mercurial') and \
-               not proto.startswith('text/plain') and \
-               not proto.startswith('application/hg-changegroup'):
-            raise RepoError("'%s' does not appear to be an hg repository"
-                            % self.url)
-
-        if proto.startswith('application/mercurial'):
-            version = proto[22:]
-            if float(version) > 0.1:
-                raise RepoError("'%s' uses newer protocol %s" %
-                                (self.url, version))
-
-        return resp
-
-    def heads(self):
-        d = self.do_cmd("heads").read()
-        try:
-            return map(bin, d[:-1].split(" "))
-        except:
-            self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
-            raise
-
-    def branches(self, nodes):
-        n = " ".join(map(hex, nodes))
-        d = self.do_cmd("branches", nodes=n).read()
-        try:
-            br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
-            return br
-        except:
-            self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
-            raise
-
-    def between(self, pairs):
-        n = "\n".join(["-".join(map(hex, p)) for p in pairs])
-        d = self.do_cmd("between", pairs=n).read()
-        try:
-            p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
-            return p
-        except:
-            self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
-            raise
-
-    def changegroup(self, nodes):
-        n = " ".join(map(hex, nodes))
-        f = self.do_cmd("changegroup", roots=n)
-        bytes = 0
-
-        class zread:
-            def __init__(self, f):
-                self.zd = zlib.decompressobj()
-                self.f = f
-                self.buf = ""
-            def read(self, l):
-                while l > len(self.buf):
-                    r = self.f.read(4096)
-                    if r:
-                        self.buf += self.zd.decompress(r)
-                    else:
-                        self.buf += self.zd.flush()
-                        break
-                d, self.buf = self.buf[:l], self.buf[l:]
-                return d
-
-        return zread(f)
-
-class remotelock:
-    def __init__(self, repo):
-        self.repo = repo
-    def release(self):
-        self.repo.unlock()
-        self.repo = None
-    def __del__(self):
-        if self.repo:
-            self.release()
-
-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 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)
-
-        self.pipeo, self.pipei, self.pipee = os.popen3(cmd)
-
-    def readerr(self):
-        while 1:
-            r,w,x = select.select([self.pipee], [], [], 0)
-            if not r: break
-            l = self.pipee.readline()
-            if not l: break
-            self.ui.status("remote: ", l)
-
-    def __del__(self):
-        try:
-            self.pipeo.close()
-            self.pipei.close()
-            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 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 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 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 RepoError("unexpected response '%s'" % (d[:400] + "..."))
-
-    def changegroup(self, nodes):
-        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 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())
-        return self.pipei.read(l) != ""
-
-class httpsrepository(httprepository):
-    pass
-
-def repository(ui, path=None, create=0):
-    if path:
-        if path.startswith("http://"):
-            return httprepository(ui, path)
-        if path.startswith("https://"):
-            return httpsrepository(ui, path)
-        if path.startswith("hg://"):
-            return httprepository(ui, path.replace("hg://", "http://"))
-        if path.startswith("old-http://"):
-            return localrepository(ui, path.replace("old-http://", "http://"))
-        if path.startswith("ssh://"):
-            return sshrepository(ui, path)
-
-    return localrepository(ui, path, create)