# HG changeset patch # User Matt Mackall # Date 1187328916 18000 # Node ID c1dbc9ae8f2ba6d15cf42146083ccd523cd04346 # Parent 2da57dc04aa85e3ea11b473bd6f6b4217b18c058# Parent 0d5d0384492742821912e3b0277e030d8121f626 Merge with crew diff --git a/hg b/hg --- 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() diff --git a/hgext/alias.py b/hgext/alias.py --- 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(' '): diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- 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.''' diff --git a/mercurial/commands.py b/mercurial/commands.py --- 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:])) diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py 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 +# +# 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() diff --git a/mercurial/hg.py b/mercurial/hg.py --- 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) diff --git a/mercurial/httprepo.py b/mercurial/httprepo.py --- 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") diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- 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. diff --git a/mercurial/sshrepo.py b/mercurial/sshrepo.py --- 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 diff --git a/mercurial/verify.py b/mercurial/verify.py --- 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) diff --git a/tests/test-dispatch.py b/tests/test-dispatch.py --- 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") diff --git a/tests/test-ui-config b/tests/test-ui-config --- 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',