changeset 5187:c1dbc9ae8f2b

Merge with crew
author Matt Mackall <mpm@selenic.com>
date Fri, 17 Aug 2007 00:35:16 -0500
parents 2da57dc04aa8 (diff) 0d5d03844927 (current diff)
children 831ebc408ffb
files mercurial/hg.py
diffstat 12 files changed, 452 insertions(+), 440 deletions(-) [+]
line wrap: on
line diff
--- a/hg
+++ b/hg
@@ -7,5 +7,5 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
-import mercurial.commands
-mercurial.commands.run()
+import mercurial.dispatch
+mercurial.dispatch.run()
--- a/hgext/alias.py
+++ b/hgext/alias.py
@@ -42,7 +42,7 @@ class lazycommand(object):
             return
 
         try:
-            self._cmd = findcmd(self._ui, self._target)[1]
+            self._cmd = findcmd(self._ui, self._target, commands.table)[1]
             if self._cmd == self:
                 raise RecursiveCommand()
             if self._target in commands.norepo.split(' '):
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -7,10 +7,8 @@
 
 from node import *
 from i18n import _
-import os, sys, atexit, signal, pdb, traceback, socket, errno, shlex
-import bisect, stat
-import mdiff, bdiff, util, templater, patch, commands, hg, lock, time
-import fancyopts, revlog, version, extensions, hook
+import os, sys, bisect, stat
+import mdiff, bdiff, util, templater, patch
 
 revrangesep = ':'
 
@@ -18,130 +16,8 @@ class UnknownCommand(Exception):
     """Exception raised if command is not in the command table."""
 class AmbiguousCommand(Exception):
     """Exception raised if command shortcut matches more than one command."""
-class ParseError(Exception):
-    """Exception raised on errors in parsing the command line."""
 
-def runcatch(ui, args):
-    def catchterm(*args):
-        raise util.SignalInterrupt
-
-    for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
-        num = getattr(signal, name, None)
-        if num: signal.signal(num, catchterm)
-
-    try:
-        try:
-            # enter the debugger before command execution
-            if '--debugger' in args:
-                pdb.set_trace()
-            try:
-                return dispatch(ui, args)
-            finally:
-                ui.flush()
-        except:
-            # enter the debugger when we hit an exception
-            if '--debugger' in args:
-                pdb.post_mortem(sys.exc_info()[2])
-            ui.print_exc()
-            raise
-
-    except ParseError, inst:
-        if inst.args[0]:
-            ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
-            commands.help_(ui, inst.args[0])
-        else:
-            ui.warn(_("hg: %s\n") % inst.args[1])
-            commands.help_(ui, 'shortlist')
-    except AmbiguousCommand, inst:
-        ui.warn(_("hg: command '%s' is ambiguous:\n    %s\n") %
-                (inst.args[0], " ".join(inst.args[1])))
-    except UnknownCommand, inst:
-        ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
-        commands.help_(ui, 'shortlist')
-    except hg.RepoError, inst:
-        ui.warn(_("abort: %s!\n") % inst)
-    except lock.LockHeld, inst:
-        if inst.errno == errno.ETIMEDOUT:
-            reason = _('timed out waiting for lock held by %s') % inst.locker
-        else:
-            reason = _('lock held by %s') % inst.locker
-        ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
-    except lock.LockUnavailable, inst:
-        ui.warn(_("abort: could not lock %s: %s\n") %
-               (inst.desc or inst.filename, inst.strerror))
-    except revlog.RevlogError, inst:
-        ui.warn(_("abort: %s!\n") % inst)
-    except util.SignalInterrupt:
-        ui.warn(_("killed!\n"))
-    except KeyboardInterrupt:
-        try:
-            ui.warn(_("interrupted!\n"))
-        except IOError, inst:
-            if inst.errno == errno.EPIPE:
-                if ui.debugflag:
-                    ui.warn(_("\nbroken pipe\n"))
-            else:
-                raise
-    except socket.error, inst:
-        ui.warn(_("abort: %s\n") % inst[1])
-    except IOError, inst:
-        if hasattr(inst, "code"):
-            ui.warn(_("abort: %s\n") % inst)
-        elif hasattr(inst, "reason"):
-            try: # usually it is in the form (errno, strerror)
-                reason = inst.reason.args[1]
-            except: # it might be anything, for example a string
-                reason = inst.reason
-            ui.warn(_("abort: error: %s\n") % reason)
-        elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
-            if ui.debugflag:
-                ui.warn(_("broken pipe\n"))
-        elif getattr(inst, "strerror", None):
-            if getattr(inst, "filename", None):
-                ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
-            else:
-                ui.warn(_("abort: %s\n") % inst.strerror)
-        else:
-            raise
-    except OSError, inst:
-        if getattr(inst, "filename", None):
-            ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
-        else:
-            ui.warn(_("abort: %s\n") % inst.strerror)
-    except util.UnexpectedOutput, inst:
-        ui.warn(_("abort: %s") % inst[0])
-        if not isinstance(inst[1], basestring):
-            ui.warn(" %r\n" % (inst[1],))
-        elif not inst[1]:
-            ui.warn(_(" empty string\n"))
-        else:
-            ui.warn("\n%r\n" % util.ellipsis(inst[1]))
-    except ImportError, inst:
-        m = str(inst).split()[-1]
-        ui.warn(_("abort: could not import module %s!\n" % m))
-        if m in "mpatch bdiff".split():
-            ui.warn(_("(did you forget to compile extensions?)\n"))
-        elif m in "zlib".split():
-            ui.warn(_("(is your Python install correct?)\n"))
-
-    except util.Abort, inst:
-        ui.warn(_("abort: %s\n") % inst)
-    except SystemExit, inst:
-        # Commands shouldn't sys.exit directly, but give a return code.
-        # Just in case catch this and and pass exit code to caller.
-        return inst.code
-    except:
-        ui.warn(_("** unknown exception encountered, details follow\n"))
-        ui.warn(_("** report bug details to "
-                 "http://www.selenic.com/mercurial/bts\n"))
-        ui.warn(_("** or mercurial@selenic.com\n"))
-        ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
-               % version.get_version())
-        raise
-
-    return -1
-
-def findpossible(ui, cmd):
+def findpossible(ui, cmd, table):
     """
     Return cmd -> (aliases, command table entry)
     for each matching command.
@@ -149,7 +25,7 @@ def findpossible(ui, cmd):
     """
     choice = {}
     debugchoice = {}
-    for e in commands.table.keys():
+    for e in table.keys():
         aliases = e.lstrip("^").split("|")
         found = None
         if cmd in aliases:
@@ -161,18 +37,18 @@ def findpossible(ui, cmd):
                     break
         if found is not None:
             if aliases[0].startswith("debug") or found.startswith("debug"):
-                debugchoice[found] = (aliases, commands.table[e])
+                debugchoice[found] = (aliases, table[e])
             else:
-                choice[found] = (aliases, commands.table[e])
+                choice[found] = (aliases, table[e])
 
     if not choice and debugchoice:
         choice = debugchoice
 
     return choice
 
-def findcmd(ui, cmd):
+def findcmd(ui, cmd, table):
     """Return (aliases, command table entry) for command string."""
-    choice = findpossible(ui, cmd)
+    choice = findpossible(ui, cmd, table)
 
     if choice.has_key(cmd):
         return choice[cmd]
@@ -187,247 +63,6 @@ def findcmd(ui, cmd):
 
     raise UnknownCommand(cmd)
 
-def findrepo():
-    p = os.getcwd()
-    while not os.path.isdir(os.path.join(p, ".hg")):
-        oldp, p = p, os.path.dirname(p)
-        if p == oldp:
-            return None
-
-    return p
-
-def parse(ui, args):
-    options = {}
-    cmdoptions = {}
-
-    try:
-        args = fancyopts.fancyopts(args, commands.globalopts, options)
-    except fancyopts.getopt.GetoptError, inst:
-        raise ParseError(None, inst)
-
-    if args:
-        cmd, args = args[0], args[1:]
-        aliases, i = findcmd(ui, cmd)
-        cmd = aliases[0]
-        defaults = ui.config("defaults", cmd)
-        if defaults:
-            args = shlex.split(defaults) + args
-        c = list(i[1])
-    else:
-        cmd = None
-        c = []
-
-    # combine global options into local
-    for o in commands.globalopts:
-        c.append((o[0], o[1], options[o[1]], o[3]))
-
-    try:
-        args = fancyopts.fancyopts(args, c, cmdoptions)
-    except fancyopts.getopt.GetoptError, inst:
-        raise ParseError(cmd, inst)
-
-    # separate global options back out
-    for o in commands.globalopts:
-        n = o[1]
-        options[n] = cmdoptions[n]
-        del cmdoptions[n]
-
-    return (cmd, cmd and i[0] or None, args, options, cmdoptions)
-
-def parseconfig(config):
-    """parse the --config options from the command line"""
-    parsed = []
-    for cfg in config:
-        try:
-            name, value = cfg.split('=', 1)
-            section, name = name.split('.', 1)
-            if not section or not name:
-                raise IndexError
-            parsed.append((section, name, value))
-        except (IndexError, ValueError):
-            raise util.Abort(_('malformed --config option: %s') % cfg)
-    return parsed
-
-def earlygetopt(aliases, args):
-    """Return list of values for an option (or aliases).
-
-    The values are listed in the order they appear in args.
-    The options and values are removed from args.
-    """
-    try:
-        argcount = args.index("--")
-    except ValueError:
-        argcount = len(args)
-    shortopts = [opt for opt in aliases if len(opt) == 2]
-    values = []
-    pos = 0
-    while pos < argcount:
-        if args[pos] in aliases:
-            if pos + 1 >= argcount:
-                # ignore and let getopt report an error if there is no value
-                break
-            del args[pos]
-            values.append(args.pop(pos))
-            argcount -= 2
-        elif args[pos][:2] in shortopts:
-            # short option can have no following space, e.g. hg log -Rfoo
-            values.append(args.pop(pos)[2:])
-            argcount -= 1
-        else:
-            pos += 1
-    return values
-
-def dispatch(ui, args):
-    # read --config before doing anything else
-    # (e.g. to change trust settings for reading .hg/hgrc)
-    config = earlygetopt(['--config'], args)
-    if config:
-        ui.updateopts(config=parseconfig(config))
-
-    # check for cwd
-    cwd = earlygetopt(['--cwd'], args)
-    if cwd:
-        os.chdir(cwd[-1])
-
-    # read the local repository .hgrc into a local ui object
-    path = findrepo() or ""
-    if not path:
-        lui = ui
-    if path:
-        try:
-            lui = commands.ui.ui(parentui=ui)
-            lui.readconfig(os.path.join(path, ".hg", "hgrc"))
-        except IOError:
-            pass
-
-    # now we can expand paths, even ones in .hg/hgrc
-    rpath = earlygetopt(["-R", "--repository", "--repo"], args)
-    if rpath:
-        path = lui.expandpath(rpath[-1])
-        lui = commands.ui.ui(parentui=ui)
-        lui.readconfig(os.path.join(path, ".hg", "hgrc"))
-
-    extensions.loadall(lui)
-    # check for fallback encoding
-    fallback = lui.config('ui', 'fallbackencoding')
-    if fallback:
-        util._fallbackencoding = fallback
-
-    fullargs = args
-    cmd, func, args, options, cmdoptions = parse(lui, args)
-
-    if options["config"]:
-        raise util.Abort(_("Option --config may not be abbreviated!"))
-    if options["cwd"]:
-        raise util.Abort(_("Option --cwd may not be abbreviated!"))
-    if options["repository"]:
-        raise util.Abort(_(
-            "Option -R has to be separated from other options (i.e. not -qR) "
-            "and --repository may only be abbreviated as --repo!"))
-
-    if options["encoding"]:
-        util._encoding = options["encoding"]
-    if options["encodingmode"]:
-        util._encodingmode = options["encodingmode"]
-    if options["time"]:
-        def get_times():
-            t = os.times()
-            if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
-                t = (t[0], t[1], t[2], t[3], time.clock())
-            return t
-        s = get_times()
-        def print_time():
-            t = get_times()
-            ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
-                (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
-        atexit.register(print_time)
-
-    ui.updateopts(options["verbose"], options["debug"], options["quiet"],
-                 not options["noninteractive"], options["traceback"])
-
-    if options['help']:
-        return commands.help_(ui, cmd, options['version'])
-    elif options['version']:
-        return commands.version_(ui)
-    elif not cmd:
-        return commands.help_(ui, 'shortlist')
-
-    repo = None
-    if cmd not in commands.norepo.split():
-        try:
-            repo = hg.repository(ui, path=path)
-            ui = repo.ui
-            if not repo.local():
-                raise util.Abort(_("repository '%s' is not local") % path)
-        except hg.RepoError:
-            if cmd not in commands.optionalrepo.split():
-                if not path:
-                    raise hg.RepoError(_("There is no Mercurial repository here"
-                                         " (.hg not found)"))
-                raise
-        d = lambda: func(ui, repo, *args, **cmdoptions)
-    else:
-        d = lambda: func(ui, *args, **cmdoptions)
-
-    # run pre-hook, and abort if it fails
-    ret = hook.hook(ui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
-    if ret:
-        return ret
-    ret = runcommand(ui, options, cmd, d)
-    # run post-hook, passing command result
-    hook.hook(ui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
-              result = ret)
-    return ret
-
-def runcommand(ui, options, cmd, cmdfunc):
-    def checkargs():
-        try:
-            return cmdfunc()
-        except TypeError, inst:
-            # was this an argument error?
-            tb = traceback.extract_tb(sys.exc_info()[2])
-            if len(tb) != 2: # no
-                raise
-            raise ParseError(cmd, _("invalid arguments"))
-
-    if options['profile']:
-        import hotshot, hotshot.stats
-        prof = hotshot.Profile("hg.prof")
-        try:
-            try:
-                return prof.runcall(checkargs)
-            except:
-                try:
-                    ui.warn(_('exception raised - generating '
-                             'profile anyway\n'))
-                except:
-                    pass
-                raise
-        finally:
-            prof.close()
-            stats = hotshot.stats.load("hg.prof")
-            stats.strip_dirs()
-            stats.sort_stats('time', 'calls')
-            stats.print_stats(40)
-    elif options['lsprof']:
-        try:
-            from mercurial import lsprof
-        except ImportError:
-            raise util.Abort(_(
-                'lsprof not available - install from '
-                'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
-        p = lsprof.Profiler()
-        p.enable(subcalls=True)
-        try:
-            return checkargs()
-        finally:
-            p.disable()
-            stats = lsprof.Stats(p.getstats())
-            stats.sort()
-            stats.pprint(top=10, file=sys.stderr, climit=5)
-    else:
-        return checkargs()
-
 def bail_if_changed(repo):
     modified, added, removed, deleted = repo.status()[:4]
     if modified or added or removed or deleted:
@@ -459,15 +94,6 @@ def setremoteconfig(ui, opts):
     if opts.get('remotecmd'):
         ui.setconfig("ui", "remotecmd", opts['remotecmd'])
 
-def parseurl(url, revs):
-    '''parse url#branch, returning url, branch + revs'''
-
-    if '#' not in url:
-        return url, (revs or None)
-
-    url, rev = url.split('#', 1)
-    return url, revs + [rev]
-
 def revpair(repo, revs):
     '''return pair of nodes, given list of revisions. second item can
     be None, meaning use working dir.'''
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -9,7 +9,7 @@ import demandimport; demandimport.enable
 from node import *
 from i18n import _
 import os, re, sys, urllib
-import ui, hg, util, revlog, bundlerepo, extensions
+import hg, util, revlog, bundlerepo, extensions
 import difflib, patch, time, help, mdiff, tempfile
 import errno, version, socket
 import archival, changegroup, cmdutil, hgweb.server, sshserver
@@ -336,7 +336,7 @@ def bundle(ui, repo, fname, dest=None, *
                         visit.append(p)
     else:
         cmdutil.setremoteconfig(ui, opts)
-        dest, revs = cmdutil.parseurl(
+        dest, revs = hg.parseurl(
             ui.expandpath(dest or 'default-push', dest or 'default'), revs)
         other = hg.repository(ui, dest)
         o = repo.findoutgoing(other, force=opts['force'])
@@ -662,7 +662,7 @@ def debugcomplete(ui, cmd='', **opts):
         options = []
         otables = [globalopts]
         if cmd:
-            aliases, entry = cmdutil.findcmd(ui, cmd)
+            aliases, entry = cmdutil.findcmd(ui, cmd, table)
             otables.append(entry[1])
         for t in otables:
             for o in t:
@@ -672,7 +672,7 @@ def debugcomplete(ui, cmd='', **opts):
         ui.write("%s\n" % "\n".join(options))
         return
 
-    clist = cmdutil.findpossible(ui, cmd).keys()
+    clist = cmdutil.findpossible(ui, cmd, table).keys()
     clist.sort()
     ui.write("%s\n" % "\n".join(clist))
 
@@ -1307,7 +1307,7 @@ def help_(ui, name=None, with_version=Fa
         if with_version:
             version_(ui)
             ui.write('\n')
-        aliases, i = cmdutil.findcmd(ui, name)
+        aliases, i = cmdutil.findcmd(ui, name, table)
         # synopsis
         ui.write("%s\n\n" % i[2])
 
@@ -1475,7 +1475,7 @@ def identify(ui, repo, source=None,
     output = []
 
     if source:
-        source, revs = cmdutil.parseurl(ui.expandpath(source), [])
+        source, revs = hg.parseurl(ui.expandpath(source), [])
         srepo = hg.repository(ui, source)
         if not rev and revs:
             rev = revs[0]
@@ -1638,7 +1638,7 @@ def incoming(ui, repo, source="default",
 
     See pull for valid source format details.
     """
-    source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
+    source, revs = hg.parseurl(ui.expandpath(source), opts['rev'])
     cmdutil.setremoteconfig(ui, opts)
 
     other = hg.repository(ui, source)
@@ -1946,7 +1946,7 @@ def outgoing(ui, repo, dest=None, **opts
 
     See pull for valid destination format details.
     """
-    dest, revs = cmdutil.parseurl(
+    dest, revs = hg.parseurl(
         ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
     cmdutil.setremoteconfig(ui, opts)
     if revs:
@@ -2068,7 +2068,7 @@ def pull(ui, repo, source="default", **o
       Alternatively specify "ssh -C" as your ssh command in your hgrc or
       with the --ssh command line option.
     """
-    source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
+    source, revs = hg.parseurl(ui.expandpath(source), opts['rev'])
     cmdutil.setremoteconfig(ui, opts)
 
     other = hg.repository(ui, source)
@@ -2113,7 +2113,7 @@ def push(ui, repo, dest=None, **opts):
     Pushing to http:// and https:// URLs is only possible, if this
     feature is explicitly enabled on the remote Mercurial server.
     """
-    dest, revs = cmdutil.parseurl(
+    dest, revs = hg.parseurl(
         ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
     cmdutil.setremoteconfig(ui, opts)
 
@@ -3134,14 +3134,3 @@ extensions.commandtable = table
 norepo = ("clone init version help debugancestor debugcomplete debugdata"
           " debugindex debugindexdot debugdate debuginstall")
 optionalrepo = ("paths serve showconfig")
-
-def dispatch(args):
-    try:
-        u = ui.ui(traceback='--traceback' in args)
-    except util.Abort, inst:
-        sys.stderr.write(_("abort: %s\n") % inst)
-        return -1
-    return cmdutil.runcatch(u, args)
-
-def run():
-    sys.exit(dispatch(sys.argv[1:]))
new file mode 100644
--- /dev/null
+++ b/mercurial/dispatch.py
@@ -0,0 +1,390 @@
+# dispatch.py - command dispatching for mercurial
+#
+# Copyright 2005-2007 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 i18n import _
+import os, sys, atexit, signal, pdb, traceback, socket, errno, shlex, time
+import util, commands, hg, lock, fancyopts, revlog, version, extensions, hook
+import cmdutil
+import ui as _ui
+
+class ParseError(Exception):
+    """Exception raised on errors in parsing the command line."""
+
+def run():
+    "run the command in sys.argv"
+    sys.exit(dispatch(sys.argv[1:]))
+
+def dispatch(args):
+    "run the command specified in args"
+    try:
+        u = _ui.ui(traceback='--traceback' in args)
+    except util.Abort, inst:
+        sys.stderr.write(_("abort: %s\n") % inst)
+        return -1
+    return _runcatch(u, args)
+
+def _runcatch(ui, args):
+    def catchterm(*args):
+        raise util.SignalInterrupt
+
+    for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
+        num = getattr(signal, name, None)
+        if num: signal.signal(num, catchterm)
+
+    try:
+        try:
+            # enter the debugger before command execution
+            if '--debugger' in args:
+                pdb.set_trace()
+            try:
+                return _dispatch(ui, args)
+            finally:
+                ui.flush()
+        except:
+            # enter the debugger when we hit an exception
+            if '--debugger' in args:
+                pdb.post_mortem(sys.exc_info()[2])
+            ui.print_exc()
+            raise
+
+    except ParseError, inst:
+        if inst.args[0]:
+            ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
+            commands.help_(ui, inst.args[0])
+        else:
+            ui.warn(_("hg: %s\n") % inst.args[1])
+            commands.help_(ui, 'shortlist')
+    except cmdutil.AmbiguousCommand, inst:
+        ui.warn(_("hg: command '%s' is ambiguous:\n    %s\n") %
+                (inst.args[0], " ".join(inst.args[1])))
+    except cmdutil.UnknownCommand, inst:
+        ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
+        commands.help_(ui, 'shortlist')
+    except hg.RepoError, inst:
+        ui.warn(_("abort: %s!\n") % inst)
+    except lock.LockHeld, inst:
+        if inst.errno == errno.ETIMEDOUT:
+            reason = _('timed out waiting for lock held by %s') % inst.locker
+        else:
+            reason = _('lock held by %s') % inst.locker
+        ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
+    except lock.LockUnavailable, inst:
+        ui.warn(_("abort: could not lock %s: %s\n") %
+               (inst.desc or inst.filename, inst.strerror))
+    except revlog.RevlogError, inst:
+        ui.warn(_("abort: %s!\n") % inst)
+    except util.SignalInterrupt:
+        ui.warn(_("killed!\n"))
+    except KeyboardInterrupt:
+        try:
+            ui.warn(_("interrupted!\n"))
+        except IOError, inst:
+            if inst.errno == errno.EPIPE:
+                if ui.debugflag:
+                    ui.warn(_("\nbroken pipe\n"))
+            else:
+                raise
+    except socket.error, inst:
+        ui.warn(_("abort: %s\n") % inst[1])
+    except IOError, inst:
+        if hasattr(inst, "code"):
+            ui.warn(_("abort: %s\n") % inst)
+        elif hasattr(inst, "reason"):
+            try: # usually it is in the form (errno, strerror)
+                reason = inst.reason.args[1]
+            except: # it might be anything, for example a string
+                reason = inst.reason
+            ui.warn(_("abort: error: %s\n") % reason)
+        elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
+            if ui.debugflag:
+                ui.warn(_("broken pipe\n"))
+        elif getattr(inst, "strerror", None):
+            if getattr(inst, "filename", None):
+                ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
+            else:
+                ui.warn(_("abort: %s\n") % inst.strerror)
+        else:
+            raise
+    except OSError, inst:
+        if getattr(inst, "filename", None):
+            ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
+        else:
+            ui.warn(_("abort: %s\n") % inst.strerror)
+    except util.UnexpectedOutput, inst:
+        ui.warn(_("abort: %s") % inst[0])
+        if not isinstance(inst[1], basestring):
+            ui.warn(" %r\n" % (inst[1],))
+        elif not inst[1]:
+            ui.warn(_(" empty string\n"))
+        else:
+            ui.warn("\n%r\n" % util.ellipsis(inst[1]))
+    except ImportError, inst:
+        m = str(inst).split()[-1]
+        ui.warn(_("abort: could not import module %s!\n" % m))
+        if m in "mpatch bdiff".split():
+            ui.warn(_("(did you forget to compile extensions?)\n"))
+        elif m in "zlib".split():
+            ui.warn(_("(is your Python install correct?)\n"))
+
+    except util.Abort, inst:
+        ui.warn(_("abort: %s\n") % inst)
+    except SystemExit, inst:
+        # Commands shouldn't sys.exit directly, but give a return code.
+        # Just in case catch this and and pass exit code to caller.
+        return inst.code
+    except:
+        ui.warn(_("** unknown exception encountered, details follow\n"))
+        ui.warn(_("** report bug details to "
+                 "http://www.selenic.com/mercurial/bts\n"))
+        ui.warn(_("** or mercurial@selenic.com\n"))
+        ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
+               % version.get_version())
+        raise
+
+    return -1
+
+def _findrepo():
+    p = os.getcwd()
+    while not os.path.isdir(os.path.join(p, ".hg")):
+        oldp, p = p, os.path.dirname(p)
+        if p == oldp:
+            return None
+
+    return p
+
+def _parse(ui, args):
+    options = {}
+    cmdoptions = {}
+
+    try:
+        args = fancyopts.fancyopts(args, commands.globalopts, options)
+    except fancyopts.getopt.GetoptError, inst:
+        raise ParseError(None, inst)
+
+    if args:
+        cmd, args = args[0], args[1:]
+        aliases, i = cmdutil.findcmd(ui, cmd, commands.table)
+        cmd = aliases[0]
+        defaults = ui.config("defaults", cmd)
+        if defaults:
+            args = shlex.split(defaults) + args
+        c = list(i[1])
+    else:
+        cmd = None
+        c = []
+
+    # combine global options into local
+    for o in commands.globalopts:
+        c.append((o[0], o[1], options[o[1]], o[3]))
+
+    try:
+        args = fancyopts.fancyopts(args, c, cmdoptions)
+    except fancyopts.getopt.GetoptError, inst:
+        raise ParseError(cmd, inst)
+
+    # separate global options back out
+    for o in commands.globalopts:
+        n = o[1]
+        options[n] = cmdoptions[n]
+        del cmdoptions[n]
+
+    return (cmd, cmd and i[0] or None, args, options, cmdoptions)
+
+def _parseconfig(config):
+    """parse the --config options from the command line"""
+    parsed = []
+    for cfg in config:
+        try:
+            name, value = cfg.split('=', 1)
+            section, name = name.split('.', 1)
+            if not section or not name:
+                raise IndexError
+            parsed.append((section, name, value))
+        except (IndexError, ValueError):
+            raise util.Abort(_('malformed --config option: %s') % cfg)
+    return parsed
+
+def _earlygetopt(aliases, args):
+    """Return list of values for an option (or aliases).
+
+    The values are listed in the order they appear in args.
+    The options and values are removed from args.
+    """
+    try:
+        argcount = args.index("--")
+    except ValueError:
+        argcount = len(args)
+    shortopts = [opt for opt in aliases if len(opt) == 2]
+    values = []
+    pos = 0
+    while pos < argcount:
+        if args[pos] in aliases:
+            if pos + 1 >= argcount:
+                # ignore and let getopt report an error if there is no value
+                break
+            del args[pos]
+            values.append(args.pop(pos))
+            argcount -= 2
+        elif args[pos][:2] in shortopts:
+            # short option can have no following space, e.g. hg log -Rfoo
+            values.append(args.pop(pos)[2:])
+            argcount -= 1
+        else:
+            pos += 1
+    return values
+
+def _dispatch(ui, args):
+    # read --config before doing anything else
+    # (e.g. to change trust settings for reading .hg/hgrc)
+    config = _earlygetopt(['--config'], args)
+    if config:
+        ui.updateopts(config=_parseconfig(config))
+
+    # check for cwd
+    cwd = _earlygetopt(['--cwd'], args)
+    if cwd:
+        os.chdir(cwd[-1])
+
+    # read the local repository .hgrc into a local ui object
+    path = _findrepo() or ""
+    if not path:
+        lui = ui
+    if path:
+        try:
+            lui = _ui.ui(parentui=ui)
+            lui.readconfig(os.path.join(path, ".hg", "hgrc"))
+        except IOError:
+            pass
+
+    # now we can expand paths, even ones in .hg/hgrc
+    rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
+    if rpath:
+        path = lui.expandpath(rpath[-1])
+        lui = _ui.ui(parentui=ui)
+        lui.readconfig(os.path.join(path, ".hg", "hgrc"))
+
+    extensions.loadall(lui)
+    # check for fallback encoding
+    fallback = lui.config('ui', 'fallbackencoding')
+    if fallback:
+        util._fallbackencoding = fallback
+
+    fullargs = args
+    cmd, func, args, options, cmdoptions = _parse(lui, args)
+
+    if options["config"]:
+        raise util.Abort(_("Option --config may not be abbreviated!"))
+    if options["cwd"]:
+        raise util.Abort(_("Option --cwd may not be abbreviated!"))
+    if options["repository"]:
+        raise util.Abort(_(
+            "Option -R has to be separated from other options (i.e. not -qR) "
+            "and --repository may only be abbreviated as --repo!"))
+
+    if options["encoding"]:
+        util._encoding = options["encoding"]
+    if options["encodingmode"]:
+        util._encodingmode = options["encodingmode"]
+    if options["time"]:
+        def get_times():
+            t = os.times()
+            if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
+                t = (t[0], t[1], t[2], t[3], time.clock())
+            return t
+        s = get_times()
+        def print_time():
+            t = get_times()
+            ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
+                (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
+        atexit.register(print_time)
+
+    ui.updateopts(options["verbose"], options["debug"], options["quiet"],
+                 not options["noninteractive"], options["traceback"])
+
+    if options['help']:
+        return commands.help_(ui, cmd, options['version'])
+    elif options['version']:
+        return commands.version_(ui)
+    elif not cmd:
+        return commands.help_(ui, 'shortlist')
+
+    repo = None
+    if cmd not in commands.norepo.split():
+        try:
+            repo = hg.repository(ui, path=path)
+            ui = repo.ui
+            if not repo.local():
+                raise util.Abort(_("repository '%s' is not local") % path)
+        except hg.RepoError:
+            if cmd not in commands.optionalrepo.split():
+                if not path:
+                    raise hg.RepoError(_("There is no Mercurial repository here"
+                                         " (.hg not found)"))
+                raise
+        d = lambda: func(ui, repo, *args, **cmdoptions)
+    else:
+        d = lambda: func(ui, *args, **cmdoptions)
+
+    # run pre-hook, and abort if it fails
+    ret = hook.hook(ui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
+    if ret:
+        return ret
+    ret = _runcommand(ui, options, cmd, d)
+    # run post-hook, passing command result
+    hook.hook(ui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
+              result = ret)
+    return ret
+
+def _runcommand(ui, options, cmd, cmdfunc):
+    def checkargs():
+        try:
+            return cmdfunc()
+        except TypeError, inst:
+            # was this an argument error?
+            tb = traceback.extract_tb(sys.exc_info()[2])
+            if len(tb) != 2: # no
+                raise
+            raise ParseError(cmd, _("invalid arguments"))
+
+    if options['profile']:
+        import hotshot, hotshot.stats
+        prof = hotshot.Profile("hg.prof")
+        try:
+            try:
+                return prof.runcall(checkargs)
+            except:
+                try:
+                    ui.warn(_('exception raised - generating '
+                             'profile anyway\n'))
+                except:
+                    pass
+                raise
+        finally:
+            prof.close()
+            stats = hotshot.stats.load("hg.prof")
+            stats.strip_dirs()
+            stats.sort_stats('time', 'calls')
+            stats.print_stats(40)
+    elif options['lsprof']:
+        try:
+            from mercurial import lsprof
+        except ImportError:
+            raise util.Abort(_(
+                'lsprof not available - install from '
+                'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
+        p = lsprof.Profiler()
+        p.enable(subcalls=True)
+        try:
+            return checkargs()
+        finally:
+            p.disable()
+            stats = lsprof.Stats(p.getstats())
+            stats.sort()
+            stats.pprint(top=10, file=sys.stderr, climit=5)
+    else:
+        return checkargs()
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -10,7 +10,7 @@ from node import *
 from repo import *
 from i18n import _
 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
-import errno, lock, os, shutil, util, cmdutil, extensions
+import errno, lock, os, shutil, util, extensions
 import merge as _merge
 import verify as _verify
 
@@ -18,6 +18,15 @@ def _local(path):
     return (os.path.isfile(util.drop_scheme('file', path)) and
             bundlerepo or localrepo)
 
+def parseurl(url, revs):
+    '''parse url#branch, returning url, branch + revs'''
+
+    if '#' not in url:
+        return url, (revs or None)
+
+    url, rev = url.split('#', 1)
+    return url, revs + [rev]
+
 schemes = {
     'bundle': bundlerepo,
     'file': _local,
@@ -95,7 +104,7 @@ def clone(ui, source, dest=None, pull=Fa
     """
 
     origsource = source
-    source, rev = cmdutil.parseurl(ui.expandpath(source), rev)
+    source, rev = parseurl(ui.expandpath(source), rev)
 
     if isinstance(source, str):
         src_repo = repository(ui, source)
--- a/mercurial/httprepo.py
+++ b/mercurial/httprepo.py
@@ -9,7 +9,7 @@
 from node import *
 from remoterepo import *
 from i18n import _
-import hg, os, urllib, urllib2, urlparse, zlib, util, httplib
+import repo, os, urllib, urllib2, urlparse, zlib, util, httplib
 import errno, keepalive, tempfile, socket, changegroup
 
 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
@@ -277,7 +277,7 @@ class httprepository(remoterepository):
         if self.caps is None:
             try:
                 self.caps = self.do_read('capabilities').split()
-            except hg.RepoError:
+            except repo.RepoError:
                 self.caps = ()
             self.ui.debug(_('capabilities: %s\n') %
                           (' '.join(self.caps or ['none'])))
@@ -329,7 +329,7 @@ class httprepository(remoterepository):
                 proto.startswith('text/plain') or
                 proto.startswith('application/hg-changegroup')):
             self.ui.debug(_("Requested URL: '%s'\n") % cu)
-            raise hg.RepoError(_("'%s' does not appear to be an hg repository")
+            raise repo.RepoError(_("'%s' does not appear to be an hg repository")
                                % self._url)
 
         if proto.startswith('application/mercurial-'):
@@ -337,10 +337,10 @@ class httprepository(remoterepository):
                 version = proto.split('-', 1)[1]
                 version_info = tuple([int(n) for n in version.split('.')])
             except ValueError:
-                raise hg.RepoError(_("'%s' sent a broken Content-type "
+                raise repo.RepoError(_("'%s' sent a broken Content-type "
                                      "header (%s)") % (self._url, proto))
             if version_info > (0, 1):
-                raise hg.RepoError(_("'%s' uses newer protocol %s") %
+                raise repo.RepoError(_("'%s' uses newer protocol %s") %
                                    (self._url, version))
 
         return resp
@@ -358,7 +358,7 @@ class httprepository(remoterepository):
         success, data = d[:-1].split(' ', 1)
         if int(success):
             return bin(data)
-        raise hg.RepoError(data)
+        raise repo.RepoError(data)
 
     def heads(self):
         d = self.do_read("heads")
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -9,7 +9,7 @@ from node import *
 from i18n import _
 import repo, changegroup
 import changelog, dirstate, filelog, manifest, context, weakref
-import re, lock, transaction, tempfile, stat, mdiff, errno, ui
+import re, lock, transaction, tempfile, stat, errno, ui
 import os, revlog, time, util, extensions, hook
 
 class localrepository(repo.repository):
@@ -1615,12 +1615,9 @@ class localrepository(repo.repository):
                 if r == next_rev[0]:
                     # If the last rev we looked at was the one just previous,
                     # we only need to see a diff.
-                    delta = mdiff.patchtext(mnfst.delta(mnfstnode))
+                    deltamf = mnfst.readdelta(mnfstnode)
                     # For each line in the delta
-                    for dline in delta.splitlines():
-                        # get the filename and filenode for that line
-                        f, fnode = dline.split('\0')
-                        fnode = bin(fnode[:40])
+                    for f, fnode in deltamf.items():
                         f = changedfiles.get(f, None)
                         # And if the file is in the list of files we care
                         # about.
--- a/mercurial/sshrepo.py
+++ b/mercurial/sshrepo.py
@@ -8,7 +8,7 @@
 from node import *
 from remoterepo import *
 from i18n import _
-import hg, os, re, stat, util
+import repo, os, re, stat, util
 
 class sshrepository(remoterepository):
     def __init__(self, ui, path, create=0):
@@ -17,7 +17,7 @@ class sshrepository(remoterepository):
 
         m = re.match(r'^ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?$', path)
         if not m:
-            self.raise_(hg.RepoError(_("couldn't parse location %s") % path))
+            self.raise_(repo.RepoError(_("couldn't parse location %s") % path))
 
         self.user = m.group(2)
         self.host = m.group(3)
@@ -37,7 +37,7 @@ class sshrepository(remoterepository):
             ui.note('running %s\n' % cmd)
             res = os.system(cmd)
             if res != 0:
-                self.raise_(hg.RepoError(_("could not create remote repo")))
+                self.raise_(repo.RepoError(_("could not create remote repo")))
 
         self.validate_repo(ui, sshcmd, args, remotecmd)
 
@@ -69,7 +69,7 @@ class sshrepository(remoterepository):
             lines.append(l)
             max_noise -= 1
         else:
-            self.raise_(hg.RepoError(_("no suitable response from remote hg")))
+            self.raise_(repo.RepoError(_("no suitable response from remote hg")))
 
         self.capabilities = ()
         lines.reverse()
@@ -136,7 +136,7 @@ class sshrepository(remoterepository):
         if int(success):
             return bin(data)
         else:
-            self.raise_(hg.RepoError(data))
+            self.raise_(repo.RepoError(data))
 
     def heads(self):
         d = self.call("heads")
@@ -175,7 +175,7 @@ class sshrepository(remoterepository):
     def unbundle(self, cg, heads, source):
         d = self.call("unbundle", heads=' '.join(map(hex, heads)))
         if d:
-            self.raise_(hg.RepoError(_("push refused: %s") % d))
+            self.raise_(repo.RepoError(_("push refused: %s") % d))
 
         while 1:
             d = cg.read(4096)
@@ -201,7 +201,7 @@ class sshrepository(remoterepository):
     def addchangegroup(self, cg, source, url):
         d = self.call("addchangegroup")
         if d:
-            self.raise_(hg.RepoError(_("push refused: %s") % d))
+            self.raise_(repo.RepoError(_("push refused: %s") % d))
         while 1:
             d = cg.read(4096)
             if not d: break
--- a/mercurial/verify.py
+++ b/mercurial/verify.py
@@ -7,7 +7,7 @@
 
 from node import *
 from i18n import _
-import revlog, mdiff
+import revlog
 
 def verify(repo):
     lock = repo.lock()
@@ -164,6 +164,7 @@ def _verify(repo):
             if flr not in filelinkrevs.get(f, []):
                 err(_("%s:%s points to unexpected changeset %d")
                         % (f, short(n), flr))
+                err(_("expecting one of %s" % filelinkrevs.get(f, [])))
             else:
                 filelinkrevs[f].remove(flr)
 
--- a/tests/test-dispatch.py
+++ b/tests/test-dispatch.py
@@ -1,32 +1,32 @@
 import os
-from mercurial import commands
+from mercurial import dispatch
 
-def dispatch(cmd):
-    """Simple wrapper around commands.dispatch()
+def testdispatch(cmd):
+    """Simple wrapper around dispatch.dispatch()
 
     Prints command and result value, but does not handle quoting.
     """
     print "running: %s" % (cmd,)
-    result = commands.dispatch(cmd.split())
+    result = dispatch.dispatch(cmd.split())
     print "result: %r" % (result,)
 
 
-dispatch("init test1")
+testdispatch("init test1")
 os.chdir('test1')
 
 # create file 'foo', add and commit
 f = file('foo', 'wb')
 f.write('foo\n')
 f.close()
-dispatch("add foo")
-dispatch("commit -m commit1 -d 2000-01-01 foo")
+testdispatch("add foo")
+testdispatch("commit -m commit1 -d 2000-01-01 foo")
 
 # append to file 'foo' and commit
 f = file('foo', 'ab')
 f.write('bar\n')
 f.close()
-dispatch("commit -m commit2 -d 2000-01-02 foo")
+testdispatch("commit -m commit2 -d 2000-01-02 foo")
 
 # check 88803a69b24 (fancyopts modified command table)
-dispatch("log -r 0")
-dispatch("log -r tip")
+testdispatch("log -r 0")
+testdispatch("log -r tip")
--- a/tests/test-ui-config
+++ b/tests/test-ui-config
@@ -1,10 +1,10 @@
 #!/usr/bin/env python
 
 import ConfigParser
-from mercurial import ui, util, cmdutil
+from mercurial import ui, util, dispatch
 
 testui = ui.ui()
-parsed = cmdutil.parseconfig([
+parsed = dispatch._parseconfig([
     'values.string=string value',
     'values.bool1=true',
     'values.bool2=false',