diff -r bdd2e18b54c5 -r ee64e677c3cf mercurial/dispatch.py --- a/mercurial/dispatch.py Wed Nov 22 22:18:06 2017 +0800 +++ b/mercurial/dispatch.py Thu Nov 30 15:48:42 2017 -0500 @@ -55,6 +55,9 @@ self.fout = fout self.ferr = ferr + # remember options pre-parsed by _earlyreqopt*() + self.earlyoptions = {} + # reposetups which run before extensions, useful for chg to pre-fill # low-level repo state (for example, changelog) before extensions. self.prereposetups = prereposetups or [] @@ -147,7 +150,7 @@ try: if not req.ui: req.ui = uimod.ui.load() - if '--traceback' in req.args: + if _earlyreqoptbool(req, 'traceback', ['--traceback']): req.ui.setconfig('ui', 'traceback', 'on', '--traceback') # set ui streams from the request @@ -261,7 +264,8 @@ # read --config before doing anything else # (e.g. to change trust settings for reading .hg/hgrc) - cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args)) + cfgs = _parseconfig(req.ui, + _earlyreqopt(req, 'config', ['--config'])) if req.repo: # copy configs that were passed on the cmdline (--config) to @@ -275,7 +279,7 @@ if not debugger or ui.plain(): # if we are in HGPLAIN mode, then disable custom debugging debugger = 'pdb' - elif '--debugger' in req.args: + elif _earlyreqoptbool(req, 'debugger', ['--debugger']): # This import can be slow for fancy debuggers, so only # do it when absolutely necessary, i.e. when actual # debugging has been requested @@ -289,7 +293,7 @@ debugmortem[debugger] = debugmod.post_mortem # enter the debugger before command execution - if '--debugger' in req.args: + if _earlyreqoptbool(req, 'debugger', ['--debugger']): ui.warn(_("entering debugger - " "type c to continue starting hg or h for help\n")) @@ -305,7 +309,7 @@ ui.flush() except: # re-raises # enter the debugger when we hit an exception - if '--debugger' in req.args: + if _earlyreqoptbool(req, 'debugger', ['--debugger']): traceback.print_exc() debugmortem[debugger](sys.exc_info()[2]) raise @@ -465,7 +469,7 @@ self.cmdname = cmd = args.pop(0) self.givenargs = args - for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"): + for invalidarg in commands.earlyoptflags: if _earlygetopt([invalidarg], args): self.badalias = (_("error in definition for alias '%s': %s may " "only be given on the command line") @@ -640,11 +644,11 @@ return configs -def _earlygetopt(aliases, args): +def _earlygetopt(aliases, args, strip=True): """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. + The options and values are removed from args if strip=True. >>> args = [b'x', b'--cwd', b'foo', b'y'] >>> _earlygetopt([b'--cwd'], args), args @@ -654,13 +658,33 @@ >>> _earlygetopt([b'--cwd'], args), args (['bar'], ['x', 'y']) + >>> args = [b'x', b'--cwd=bar', b'y'] + >>> _earlygetopt([b'--cwd'], args, strip=False), args + (['bar'], ['x', '--cwd=bar', 'y']) + >>> args = [b'x', b'-R', b'foo', b'y'] >>> _earlygetopt([b'-R'], args), args (['foo'], ['x', 'y']) + >>> args = [b'x', b'-R', b'foo', b'y'] + >>> _earlygetopt([b'-R'], args, strip=False), args + (['foo'], ['x', '-R', 'foo', 'y']) + >>> args = [b'x', b'-Rbar', b'y'] >>> _earlygetopt([b'-R'], args), args (['bar'], ['x', 'y']) + + >>> args = [b'x', b'-Rbar', b'y'] + >>> _earlygetopt([b'-R'], args, strip=False), args + (['bar'], ['x', '-Rbar', 'y']) + + >>> args = [b'x', b'-R=bar', b'y'] + >>> _earlygetopt([b'-R'], args), args + (['=bar'], ['x', 'y']) + + >>> args = [b'x', b'-R', b'--', b'y'] + >>> _earlygetopt([b'-R'], args), args + ([], ['x', '-R', '--', 'y']) """ try: argcount = args.index("--") @@ -671,28 +695,77 @@ pos = 0 while pos < argcount: fullarg = arg = args[pos] - equals = arg.find('=') + equals = -1 + if arg.startswith('--'): + equals = arg.find('=') if equals > -1: arg = arg[:equals] if arg in aliases: - del args[pos] if equals > -1: values.append(fullarg[equals + 1:]) - argcount -= 1 + if strip: + del args[pos] + argcount -= 1 + else: + pos += 1 else: if pos + 1 >= argcount: # ignore and let getopt report an error if there is no value break - values.append(args.pop(pos)) - argcount -= 2 + values.append(args[pos + 1]) + if strip: + del args[pos:pos + 2] + argcount -= 2 + else: + pos += 2 elif arg[:2] in shortopts: # short option can have no following space, e.g. hg log -Rfoo - values.append(args.pop(pos)[2:]) - argcount -= 1 + values.append(args[pos][2:]) + if strip: + del args[pos] + argcount -= 1 + else: + pos += 1 else: pos += 1 return values +def _earlyreqopt(req, name, aliases): + """Peek a list option without using a full options table""" + values = _earlygetopt(aliases, req.args, strip=False) + req.earlyoptions[name] = values + return values + +def _earlyreqoptstr(req, name, aliases): + """Peek a string option without using a full options table""" + value = (_earlygetopt(aliases, req.args, strip=False) or [''])[-1] + req.earlyoptions[name] = value + return value + +def _earlyreqoptbool(req, name, aliases): + """Peek a boolean option without using a full options table + + >>> req = request([b'x', b'--debugger']) + >>> _earlyreqoptbool(req, b'debugger', [b'--debugger']) + True + + >>> req = request([b'x', b'--', b'--debugger']) + >>> _earlyreqoptbool(req, b'debugger', [b'--debugger']) + """ + try: + argcount = req.args.index("--") + except ValueError: + argcount = len(req.args) + value = None + pos = 0 + while pos < argcount: + arg = req.args[pos] + if arg in aliases: + value = True + pos += 1 + req.earlyoptions[name] = value + return value + def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions): # run pre-hook, and abort if it fails hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs), @@ -727,8 +800,8 @@ lui = ui.copy() lui.readconfig(os.path.join(path, ".hg", "hgrc"), path) - if rpath and rpath[-1]: - path = lui.expandpath(rpath[-1]) + if rpath: + path = lui.expandpath(rpath) lui = ui.copy() lui.readconfig(os.path.join(path, ".hg", "hgrc"), path) @@ -759,6 +832,9 @@ fn = entry[0] if cmd and util.safehasattr(fn, 'shell'): + # shell alias shouldn't receive early options which are consumed by hg + args = args[:] + _earlygetopt(commands.earlyoptflags, args, strip=True) d = lambda: fn(ui, *args[1:]) return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {}) @@ -768,11 +844,11 @@ ui = req.ui # check for cwd - cwd = _earlygetopt(['--cwd'], args) + cwd = _earlyreqoptstr(req, 'cwd', ['--cwd']) if cwd: - os.chdir(cwd[-1]) + os.chdir(cwd) - rpath = _earlygetopt(["-R", "--repository", "--repo"], args) + rpath = _earlyreqoptstr(req, 'repository', ["-R", "--repository", "--repo"]) path, lui = _getlocal(ui, rpath) uis = {ui, lui} @@ -780,7 +856,7 @@ if req.repo: uis.add(req.repo.ui) - if '--profile' in args: + if _earlyreqoptbool(req, 'profile', ['--profile']): for ui_ in uis: ui_.setconfig('profiling', 'enabled', 'true', '--profile') @@ -812,14 +888,17 @@ fullargs = args cmd, func, args, options, cmdoptions = _parse(lui, args) - if options["config"]: + if options["config"] != req.earlyoptions["config"]: raise error.Abort(_("option --config may not be abbreviated!")) - if options["cwd"]: + if options["cwd"] != req.earlyoptions["cwd"]: raise error.Abort(_("option --cwd may not be abbreviated!")) - if options["repository"]: + if options["repository"] != req.earlyoptions["repository"]: raise error.Abort(_( "option -R has to be separated from other options (e.g. not " "-qR) and --repository may only be abbreviated as --repo!")) + if options["debugger"] != req.earlyoptions["debugger"]: + raise error.Abort(_("option --debugger may not be abbreviated!")) + # don't validate --profile/--traceback, which can be enabled from now if options["encoding"]: encoding.encoding = options["encoding"] @@ -908,7 +987,7 @@ except error.RequirementError: raise except error.RepoError: - if rpath and rpath[-1]: # invalid -R path + if rpath: # invalid -R path raise if not func.optionalrepo: if func.inferrepo and args and not path: