def findcmd(cmd): if cmdutil.findcmd.func_code.co_varnames[ 0] == "ui": # < 1.1.0 return cmdutil.findcmd(self._getUI(), cmd, commands.table) else: # >= 1.1.0 return cmdutil.findcmd(cmd, commands.table)
def pagecmd(orig, ui, options, cmd, cmdfunc): p = ui.config("pager", "pager", os.environ.get("PAGER")) usepager = False always = util.parsebool(options['pager']) auto = options['pager'] == 'auto' if not p: pass elif always: usepager = True elif not auto: usepager = False else: attend = ui.configlist('pager', 'attend', attended) ignore = ui.configlist('pager', 'ignore') cmds, _ = cmdutil.findcmd(cmd, commands.table) for cmd in cmds: var = 'attend-%s' % cmd if ui.config('pager', var): usepager = ui.configbool('pager', var) break if (cmd in attend or (cmd not in ignore and not attend)): usepager = True break if usepager: ui.setconfig('ui', 'formatted', ui.formatted(), 'pager') ui.setconfig('ui', 'interactive', False, 'pager') if util.safehasattr(signal, "SIGPIPE"): signal.signal(signal.SIGPIPE, signal.SIG_DFL) _runpager(ui, p) return orig(ui, options, cmd, cmdfunc)
def _setuppagercmd(ui, options, cmd): if not ui.formatted(): return p = ui.config("pager", "pager", os.environ.get("PAGER")) usepager = False always = util.parsebool(options['pager']) auto = options['pager'] == 'auto' if not p: pass elif always: usepager = True elif not auto: usepager = False else: attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff'] attend = ui.configlist('pager', 'attend', attended) ignore = ui.configlist('pager', 'ignore') cmds, _ = cmdutil.findcmd(cmd, commands.table) for cmd in cmds: var = 'attend-%s' % cmd if ui.config('pager', var): usepager = ui.configbool('pager', var) break if (cmd in attend or (cmd not in ignore and not attend)): usepager = True break if usepager: ui.setconfig('ui', 'formatted', ui.formatted(), 'pager') ui.setconfig('ui', 'interactive', False, 'pager') return p
def _resolve(self): if self._cmd is not None: return try: self._cmd = findcmd(self._ui, self._target, commands.table)[1] if self._cmd == self: raise RecursiveCommand() if self._target in commands.norepo.split(' '): commands.norepo += ' %s' % self._name return except UnknownCommand: msg = '*** [alias] %s: command %s is unknown' % \ (self._name, self._target) except AmbiguousCommand: msg = '*** [alias] %s: command %s is ambiguous' % \ (self._name, self._target) except RecursiveCommand: msg = '*** [alias] %s: circular dependency on %s' % \ (self._name, self._target) def nocmd(*args, **opts): self._ui.warn(msg + '\n') return 1 nocmd.__doc__ = msg self._cmd = (nocmd, [], '') commands.norepo += ' %s' % self._name
def pagecmd(orig, ui, options, cmd, cmdfunc): auto = options[b'pager'] == b'auto' if auto and not ui.pageractive: usepager = False attend = ui.configlist(b'pager', b'attend') ignore = ui.configlist(b'pager', b'ignore') cmds, _ = cmdutil.findcmd(cmd, commands.table) for cmd in cmds: var = b'attend-%s' % cmd if ui.config(b'pager', var, None): usepager = ui.configbool(b'pager', var, True) break if cmd in attend or (cmd not in ignore and not attend): usepager = True break if usepager: # Slight hack: the attend list is supposed to override # the ignore list for the pager extension, but the # core code doesn't know about attend, so we have to # lobotomize the ignore list so that the extension's # behavior is preserved. ui.setconfig(b'pager', b'ignore', b'', b'pager') ui.pager(b'extension-via-attend-' + cmd) else: ui.disablepager() return orig(ui, options, cmd, cmdfunc)
def nointerruptcmd(orig, ui, options, cmd, cmdfunc): # bail if not in interactive terminal if ui.configbool('nointerrupt', 'interactiveonly', True): if not ui.fout.isatty() or ui.plain(): return orig(ui, options, cmd, cmdfunc) cmds, _cmdtableentry = cmdutil.findcmd(cmd, commands.table) if isinstance(_cmdtableentry[0], dispatch.cmdalias): cmds.append(_cmdtableentry[0].cmdname) shouldpreventinterrupt = ui.configbool( 'nointerrupt', 'default-attend', False) for cmd in cmds: var = 'attend-%s' % cmd if ui.config('nointerrupt', var): shouldpreventinterrupt = ui.configbool('nointerrupt', var) break if shouldpreventinterrupt: oldsiginthandler = signal.getsignal(signal.SIGINT) try: msg = ui.config('nointerrupt', 'message', "==========================\n" "Interrupting Mercurial may leave your repo in a bad state.\n" "If you really want to interrupt your current command, press\n" "CTRL-C again.\n" "==========================\n" ) signal.signal(signal.SIGINT, sigintprintwarninghandlerfactory( oldsiginthandler, msg)) except AttributeError: pass return orig(ui, options, cmd, cmdfunc)
def uisetup(ui): try: mq = extensions.find('mq') if mq is None: ui.debug("mqext extension is mostly disabled when mq is disabled\n") return except KeyError: ui.debug("mqext extension is mostly disabled when mq is not installed\n") return # mq not loaded at all # check whether mq is loaded before mqext. If not, do a nasty hack to set # it up first so that mqext can modify what it does and not have the # modifications get clobbered. Mercurial really needs to implement # inter-extension dependencies. aliases, entry = cmdutil.findcmd('init', commands.table) try: if not [ e for e in entry[1] if e[1] == 'mq' ]: orig = mq.uisetup mq.uisetup = lambda ui: deferred_uisetup(orig, ui, mq) return except AttributeError: # argh! Latest mq does not use uisetup anymore. Now it does its stuff # in extsetup (phase 2). Fortunately, it now installs its commands # early enough that the order no longer matters. pass uisetup_post_mq(ui, mq)
def pagecmd(orig, ui, options, cmd, cmdfunc): p = ui.config("pager", "pager", os.environ.get("PAGER")) usepager = False always = util.parsebool(options['pager']) auto = options['pager'] == 'auto' if not p: pass elif always: usepager = True elif not auto: usepager = False else: attend = ui.configlist('pager', 'attend', attended) ignore = ui.configlist('pager', 'ignore') cmds, _ = cmdutil.findcmd(cmd, commands.table) for cmd in cmds: var = 'attend-%s' % cmd if ui.config('pager', var): usepager = ui.configbool('pager', var) break if (cmd in attend or (cmd not in ignore and not attend)): usepager = True break setattr(ui, 'pageractive', usepager) if usepager: ui.setconfig('ui', 'formatted', ui.formatted(), 'pager') ui.setconfig('ui', 'interactive', False, 'pager') if util.safehasattr(signal, "SIGPIPE"): signal.signal(signal.SIGPIPE, signal.SIG_DFL) _runpager(ui, p) return orig(ui, options, cmd, cmdfunc)
def uisetup(ui): try: mq = extensions.find('mq') if mq is None: ui.warn("mqext extension is mostly disabled when mq is disabled") return except KeyError: ui.warn("mqext extension is mostly disabled when mq is not installed") return # mq not loaded at all # check whether mq is loaded before mqext. If not, do a nasty hack to set # it up first so that mqext can modify what it does and not have the # modifications get clobbered. Mercurial really needs to implement # inter-extension dependencies. aliases, entry = cmdutil.findcmd('init', commands.table) try: if not [ e for e in entry[1] if e[1] == 'mq' ]: orig = mq.uisetup mq.uisetup = lambda ui: deferred_uisetup(orig, ui, mq) return except AttributeError: # argh! Latest mq does not use uisetup anymore. Now it does its stuff # in extsetup (phase 2). Fortunately, it now installs its commands # early enough that the order no longer matters. pass uisetup_post_mq(ui, mq)
def decorator(fn): for entry in cmdutil.findcmd("clone", commands.table)[1][1]: if entry[1] == option: return fn # no match found, so skip if SkipTest: return _makeskip(fn.__name__, "test requires clone to accept %s" % option) # no skipping support, so erase decorated method return
def _newcte(origcmd, newfunc, extraopts=[], synopsis=None): '''generate a cmdtable entry based on that for origcmd''' cte = cmdutil.findcmd(origcmd, commands.table)[1] # Filter out --exclude and --include, since those do not work across # repositories (mercurial converts them to abs paths). opts = [o for o in cte[1] if o[1] not in ('exclude', 'include')] if len(cte) > 2: return (newfunc, opts + extraopts, synopsis or cte[2]) return (newfunc, opts + extraopts, synopsis)
def _newcte(origcmd, newfunc, extraopts = [], synopsis = None): '''generate a cmdtable entry based on that for origcmd''' cte = cmdutil.findcmd(origcmd, commands.table)[1] # Filter out --exclude and --include, since those do not work across # repositories (mercurial converts them to abs paths). opts = [o for o in cte[1] if o[1] not in ('exclude', 'include')] if len(cte) > 2: return (newfunc, opts + extraopts, synopsis or cte[2]) return (newfunc, opts + extraopts, synopsis)
def decorator(fn): for entry in cmdutil.findcmd('clone', commands.table)[1][1]: if entry[1] == option: return fn # no match found, so skip if SkipTest: return _makeskip(fn.__name__, 'test requires clone to accept %s' % option) # no skipping support, so erase decorated method return
def autopushwrapper(orig, ui, repo, *args, **opts): """if in bound mode this will attempt a push after calling the wrapped method """ orig(ui, repo, *args, **opts) b = boundrc(ui, repo) if b.isbound(): ui.note(_('commit succeeded; attempting push\n')) pushfunc = cmdutil.findcmd('push', commands.table)[1][0] dest = b.pushloc() pushfunc(ui, repo, dest, **opts)
def helpcmd(name): if with_version: version(ui) ui.write('\n') try: aliases, i = cmdutil.findcmd(name, table, False) except error.AmbiguousCommand, inst: select = lambda c: c.lstrip('^').startswith(inst.args[0]) helplist(_('list of commands:\n\n'), select) return
def uisetup(ui): try: extensions.find('mq') cmdtable.update(_mqcmdtable) except KeyError: pass # ignore --no-commit on hg<3.7 (ce76c4d2b85c) _aliases, entry = cmdutil.findcmd('backout', commands.table) if not any(op for op in entry[1] if op[1] == 'no-commit'): entry[1].append(('', 'no-commit', None, '(EXPERIMENTAL)'))
def _extend_histedit(ui): histedit = extensions.find('histedit') _aliases, entry = cmdutil.findcmd('histedit', histedit.cmdtable) options = entry[1] options.append(('x', 'retry', False, _('retry exec command that failed and try to continue'))) options.append(('', 'show-plan', False, _('show remaining actions list'))) extensions.wrapfunction(histedit, '_histedit', _histedit) extensions.wrapfunction(histedit, 'parserules', parserules)
def extsetup(ui): extensions.wrapfunction(exchange, 'push', wrappedpush) # Mercurial 3.2 introduces a decorator for registering functions to # be called during discovery. Switch to this once we drop support for # 3.1. extensions.wrapfunction(exchange, '_pushdiscovery', wrappedpushdiscovery) # _pushbookmark gets called near the end of push. Sadly, there isn't # a better place to hook that has access to the pushop. extensions.wrapfunction(exchange, '_pushbookmark', wrappedpushbookmark) if os.name == 'posix': extensions.wrapfunction(sshpeer.sshpeer, 'readerr', wrappedreaderr) # Define some extra arguments on the push command. entry = extensions.wrapcommand(commands.table, 'push', pushcommand) entry[1].append(('', 'noreview', False, _('Do not perform a review on push.'))) entry[1].append(('', 'reviewid', '', _('Review identifier'))) entry[1].append(('c', 'changeset', '', _('Review this specific changeset only'))) # Value may be empty. So check config source to see if key is present. if (ui.configsource('extensions', 'rebase') != 'none' and ui.config('extensions', 'rebase') != '!'): # The extensions.afterloaded mechanism is busted. So we can't # reliably wrap the rebase command in case it hasn't loaded yet. So # just load the rebase extension and wrap the function directly # from its commands table. try: cmdutil.findcmd('rebase', commands.table, strict=True) except error.UnknownCommand: extensions.load(ui, 'rebase', '') # Extensions' cmdtable entries aren't merged with commands.table. # Instead, dispatch just looks at each module. So it is safe to wrap # the command on the extension module. rebase = extensions.find('rebase') extensions.wrapcommand(rebase.cmdtable, 'rebase', rebasecommand) templatekw.keywords['reviews'] = template_reviews
def bookmarkcmd(orig, ui, repo, *names, **opts): strip = opts.pop('strip') if not strip: return orig(ui, repo, *names, **opts) # check conflicted opts for name in ['force', 'rev', 'rename', 'inactive', 'track', 'untrack', 'all', 'remote']: if opts.get(name): raise error.Abort(_('--strip cannot be used together with %s') % ('--%s' % name)) # call strip -B, may raise UnknownCommand stripfunc = cmdutil.findcmd('strip', commands.table)[1][0] return stripfunc(ui, repo, bookmark=names, rev=[])
def extsetup(ui): extensions.wrapfunction(exchange, "push", wrappedpush) # Mercurial 3.2 introduces a decorator for registering functions to # be called during discovery. Switch to this once we drop support for # 3.1. extensions.wrapfunction(exchange, "_pushdiscovery", wrappedpushdiscovery) # _pushbookmark gets called near the end of push. Sadly, there isn't # a better place to hook that has access to the pushop. extensions.wrapfunction(exchange, "_pushbookmark", wrappedpushbookmark) if os.name == "posix": extensions.wrapfunction(sshpeer.sshpeer, "readerr", wrappedreaderr) # Define some extra arguments on the push command. entry = extensions.wrapcommand(commands.table, "push", pushcommand) entry[1].append(("", "noreview", False, _("Do not perform a review on push."))) entry[1].append(("", "reviewid", "", _("Review identifier"))) entry[1].append(("c", "changeset", "", _("Review this specific changeset only"))) # Value may be empty. So check config source to see if key is present. if ui.configsource("extensions", "rebase") != "none" and ui.config("extensions", "rebase") != "!": # The extensions.afterloaded mechanism is busted. So we can't # reliably wrap the rebase command in case it hasn't loaded yet. So # just load the rebase extension and wrap the function directly # from its commands table. try: cmdutil.findcmd("rebase", commands.table, strict=True) except error.UnknownCommand: extensions.load(ui, "rebase", "") # Extensions' cmdtable entries aren't merged with commands.table. # Instead, dispatch just looks at each module. So it is safe to wrap # the command on the extension module. rebase = extensions.find("rebase") extensions.wrapcommand(rebase.cmdtable, "rebase", rebasecommand) templatekw.keywords["reviews"] = template_reviews
def wrapcommand(table, command, wrapper): aliases, entry = cmdutil.findcmd(command, table) for alias, e in table.iteritems(): if e is entry: key = alias break origfn = entry[0] def wrap(*args, **kwargs): return wrapper(origfn, *args, **kwargs) wrap.__doc__ = getattr(origfn, '__doc__') wrap.__module__ = getattr(origfn, '__module__') newentry = list(entry) newentry[0] = wrap table[key] = tuple(newentry) return newentry
def uisetup(ui): permitted_opts = [ ('g', 'git', None, _('use git extended diff format')), ('', 'stat', None, _('output diffstat-style summary of changes')), ] + commands.templateopts + commands.walkopts local_opts = [ ('U', 'unified', int, _('number of lines of diff context to show')), ] aliases, entry = cmdutil.findcmd('log', commands.table) allowed_opts = [opt for opt in entry[1] if opt in permitted_opts] + local_opts # manual call of the decorator command('^show', allowed_opts, _('[OPTION]... [REV [FILE]...]'), inferrepo=True)(show)
def debugcomplete(ui, cmd='', **opts): """output list of possible commands""" if opts.get('options'): options = [] otables = [globalopts] if cmd: aliases, entry = cmdutil.findcmd(cmd, table, False) otables.append(entry[1]) for t in otables: for o in t: if o[0]: options.append('-%s' % o[0]) options.append('--%s' % o[1]) ui.write("%s\n" % "\n".join(options)) return cmdlist = cmdutil.findpossible(cmd, table) if ui.verbose: cmdlist = [' '.join(c[0]) for c in cmdlist.values()] ui.write("%s\n" % "\n".join(sorted(cmdlist)))
def uisetup(ui): def printrevset(orig, repo, pats, opts): revs, filematcher = orig(repo, pats, opts) if opts.get(b'print_revset'): expr = logrevset(repo, pats, opts) if expr: tree = revsetlang.parse(expr) tree = revsetlang.analyze(tree) else: tree = [] ui = repo.ui ui.write(b'%s\n' % stringutil.pprint(opts.get(b'rev', []))) ui.write(revsetlang.prettyformat(tree) + b'\n') ui.write(stringutil.prettyrepr(revs) + b'\n') revs = smartset.baseset() # display no revisions return revs, filematcher extensions.wrapfunction(logcmdutil, 'getrevs', printrevset) aliases, entry = cmdutil.findcmd(b'log', commands.table) entry[1].append((b'', b'print-revset', False, b'print generated revset and exit (DEPRECATED)'))
def extsetup(ui): try: extensions.find('histedit') except KeyError: raise error.Abort( _('fbhistedit: please enable histedit extension as well')) defineactions() _extend_histedit(ui) rebase = extensions.find('rebase') extensions.wrapcommand(rebase.cmdtable, 'rebase', _rebase, synopsis='[-i]') aliases, entry = cmdutil.findcmd('rebase', rebase.cmdtable) newentry = list(entry) options = newentry[1] # dirty hack because we need to change an existing switch for idx, opt in enumerate(options): if opt[0] == 'i': del options[idx] options.append(('i', 'interactive', False, 'interactive rebase')) rebase.cmdtable['rebase'] = tuple(newentry)
def pagecmd(orig, ui, options, cmd, cmdfunc): p = ui.config("pager", "pager", os.environ.get("PAGER")) if p: attend = ui.configlist('pager', 'attend', attended) auto = options['pager'] == 'auto' always = util.parsebool(options['pager']) cmds, _ = cmdutil.findcmd(cmd, commands.table) ignore = ui.configlist('pager', 'ignore') for cmd in cmds: if (always or auto and (cmd in attend or (cmd not in ignore and not attend))): ui.setconfig('ui', 'formatted', ui.formatted(), 'pager') ui.setconfig('ui', 'interactive', False, 'pager') if util.safehasattr(signal, "SIGPIPE"): signal.signal(signal.SIGPIPE, signal.SIG_DFL) _runpager(ui, p) break return orig(ui, options, cmd, cmdfunc)
def pagecmd(orig, ui, options, cmd, cmdfunc): p = ui.config("pager", "pager", os.environ.get("PAGER")) if p: attend = ui.configlist('pager', 'attend', attended) auto = options['pager'] == 'auto' always = util.parsebool(options['pager']) cmds, _ = cmdutil.findcmd(cmd, commands.table) ignore = ui.configlist('pager', 'ignore') for cmd in cmds: if (always or auto and (cmd in attend or (cmd not in ignore and not attend))): ui.setconfig('ui', 'formatted', ui.formatted()) ui.setconfig('ui', 'interactive', False) if util.safehasattr(signal, "SIGPIPE"): signal.signal(signal.SIGPIPE, signal.SIG_DFL) _runpager(ui, p) break return orig(ui, options, cmd, cmdfunc)
def qimporthook(orig, ui, repo, *files, **opts): q = repo.mq # checks for an unused patch name. prompts if the patch already exists and # returns the corrected name. def checkpatchname(patch, current_filename=None): name = patch.name # For the first patch imported, the patch has already been temporarily saved # to disk by the time we reach here, with a filename that we cannot control # (eg 973703 for bz://973703). The patch is then renamed by us to the name of # the attachment in Bugzilla (unless patch_format has been overridden). # Unfortunately it's common for people to use the bug number as the filename of # the attachment, which would cause a name collision with this temporary file. # Patches other than the first will not have a current_filename. if name == current_filename: # Add a suffix to the patch filename to avoid a collision. This is preferable # to just skipping the check to see if the file exists, since if we leave the # filename as-is, subsequent qimports of the same bug will abort with an mq # "patch already exists" when it is unable to write its temporary file. ui.status( "Changing patch filename to avoid conflict with temporary file.\n" ) name = "%s_" % name while os.path.exists(q.join(name)): msg = ( "A file named '%s' already exists in your patch directory.\n" "Rename %s '%s' (%d) (r)/overwrite (o)?" % (name, 'patch' if isinstance(patch, bz.Patch) else 'attachment', patch.desc, int(patch.id))) if name in q.series and q.isapplied(name): ui.write("A patch file named '%s' is already applied.\n") choice = 'r' else: choice = ui.prompt(msg, default="r") if choice == 'r': name = ui.prompt( "Enter the new patch name (old one was '%s'):" % name, default=name) else: # overwrite break if name in q.series and q.isapplied(name): ui.fatal("Patch with that name is already applied.") return name # hook for url.open which lets the user edit the returned patch name def previewopen(orig, ui, path): fp = orig(ui, path) class PreviewReader(object): def read(self): return ui.edit(fp.read(), ui.username()) return PreviewReader() # Install the preview hook if necessary. This will preview non-bz:// bugs # and that's OK. if opts['preview']: extensions.wrapfunction(url, "open", previewopen) # mercurial's url.search_re includes the // and that doesn't match what we # want which is bz:dddddd(/ddddd)? files = map(fixuppath, files) # Do the import as normal. The first patch of any bug is actually imported # and the rest are stored in the global delayed_imports. The imported # patches have dumb filenames because there's no way to tell mq to pick the # patch name *after* download. patches = [] bzhandler.set_patch_container(patches) ret = orig(ui, repo, *files, **opts) bzhandler.set_patch_container() if ret or not patches: return ret # The first patch in the list is the one we just imported. patch = patches.pop(0) # If the user passed a name, then mq used that so we don't need to rename if not opts['name']: # cache the lookup of the name. findcmd is not fast. qrename = cmdutil.findcmd("qrename", commands.table)[1][0] # Rename the already imported patch. Any other patches will be # processed later and given their correct names during the import. oldpatchname = q.fullseries[q.fullseriesend()] newpatchname = checkpatchname(patch, current_filename=oldpatchname) if newpatchname != oldpatchname: if newpatchname in q.series: q.delete(repo, [newpatchname], {}) qrename(ui, repo, oldpatchname, newpatchname) # mq always reports the original name, which is confusing so we'll # report the rename. But if ui.verbose is on, qrename will have already # reported it. if not ui.verbose: ui.status("renamed %s -> %s\n" % (oldpatchname, newpatchname)) # now process the delayed imports # these opts are invariant for all patches newopts = {} newopts.update(opts) newopts['force'] = True # loop through the Patches and import them by calculating their url. The # bz:// handler will have cached the lookup so we don't hit the network # here. Each one of these pushes an unapplied patch onto the beginning of # the queue, and unapplied patches are ignored when importing them, so do # these in reverse order. for patch in reversed(patches): newopts['name'] = checkpatchname(patch) path = makebzurl(patch.bug.num, patch.id) ret = orig(ui, repo, path, **newopts) if ret: return ret
if check: c = repo[None] if c.modified() or c.added() or c.removed(): raise util.Abort(_("uncommitted local changes")) if date: rev = cmdutil.finddate(ui, repo, date) _nested_apply(ui, repo, test, False, node=node, rev=rev, date=date, check=check) _nested_apply(ui, repo, commands.update, True, node=node, rev=rev, clean=clean, date=date, check=check) cmdtable = { '^ndiff': (ndiff, cmdutil.findcmd('diff', commands.table)[1][1], _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')), '^nclone': (nclone, cmdutil.findcmd('clone', commands.table)[1][1], _('[OPTION]... SOURCE [DEST]')), '^nshare': (nshare, [('U', 'noupdate', None, _('do not create a working copy'))], ('[-U] SOURCE [DEST]')), 'nincoming|nin': (nincoming, cmdutil.findcmd('incoming', commands.table)[1][1], _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]')), 'noutgoing|nout': (noutgoing, cmdutil.findcmd('outgoing', commands.table)[1][1], _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')), 'npull': (npull, cmdutil.findcmd('pull', commands.table)[1][1],
def extsetup(ui): # Insert preview flag into qimport qimport_cmd = cmdutil.findcmd("qimport", mq.cmdtable) cmdtable = mq.cmdtable qimport_cmd[1][1].append(('p', 'preview', False, "preview commit message")) # re to match our url syntax bz_matcher = re.compile("bz:(?://)?(\d+)(?:/(\w+))?") pb_matcher = re.compile("pb:(?://)?(\d+)") scp_matcher = re.compile(r"scp:(?://)?(.*)") def makebzurl(num, attachid): return "bz://%s%s" % (num, "/" + attachid if attachid else "") def makepburl(num): return "pb://%s" % num def makescpurl(path): return "scp://%s" % urllib.quote(path, safe='') def fixuppath(path): m = bz_matcher.search(path) if m: bug, attachment = m.groups() return makebzurl(bug, attachment) m = pb_matcher.search(path) if m: num, = m.groups() return makepburl(num) m = scp_matcher.search(path) if m: scppath, = m.groups() return makescpurl(scppath) return path # hook the mq import so we can fixup the patchname and handle multiple # patches per url def qimporthook(orig, ui, repo, *files, **opts): q = repo.mq # checks for an unused patch name. prompts if the patch already exists and # returns the corrected name. def checkpatchname(patch, current_filename=None): name = patch.name # For the first patch imported, the patch has already been temporarily saved # to disk by the time we reach here, with a filename that we cannot control # (eg 973703 for bz://973703). The patch is then renamed by us to the name of # the attachment in Bugzilla (unless patch_format has been overridden). # Unfortunately it's common for people to use the bug number as the filename of # the attachment, which would cause a name collision with this temporary file. # Patches other than the first will not have a current_filename. if name == current_filename: # Add a suffix to the patch filename to avoid a collision. This is preferable # to just skipping the check to see if the file exists, since if we leave the # filename as-is, subsequent qimports of the same bug will abort with an mq # "patch already exists" when it is unable to write its temporary file. ui.status("Changing patch filename to avoid conflict with temporary file.\n") name = "%s_" % name while os.path.exists(q.join(name)): msg = ("A file named '%s' already exists in your patch directory.\n" "Rename %s '%s' (%d) (r)/overwrite (o)?" % (name, 'patch' if isinstance(patch, bz.Patch) else 'attachment', patch.desc, int(patch.id))) if name in q.series and q.isapplied(name): ui.write("A patch file named '%s' is already applied.\n") choice = 'r' else: choice = ui.prompt(msg, default="r") if choice == 'r': name = ui.prompt("Enter the new patch name (old one was '%s'):" % name, default=name) else: # overwrite break if name in q.series and q.isapplied(name): ui.fatal("Patch with that name is already applied.") return name # hook for url.open which lets the user edit the returned patch name def previewopen(orig, ui, path): fp = orig(ui, path) class PreviewReader(object): def read(self): return ui.edit(fp.read(), ui.username()) return PreviewReader() # Install the preview hook if necessary. This will preview non-bz:// bugs # and that's OK. if opts['preview']: extensions.wrapfunction(url, "open", previewopen) # mercurial's url.search_re includes the // and that doesn't match what we # want which is bz:dddddd(/ddddd)? files = map(fixuppath, files) # Do the import as normal. The first patch of any bug is actually imported # and the rest are stored in the global delayed_imports. The imported # patches have dumb filenames because there's no way to tell mq to pick the # patch name *after* download. ret = orig(ui, repo, *files, **opts) if ret or bzhandler.last_imported_patch() is None: return ret # If the user passed a name, then mq used that so we don't need to rename if not opts['name']: # cache the lookup of the name. findcmd is not fast. qrename = cmdutil.findcmd("qrename", commands.table)[1][0] # Rename the already imported patch. If there are multiple patches, the # rest will be in bzhandler.delayed_imports, which we'll name correctly # in the first place. oldpatchname = q.fullseries[q.fullseriesend()] newpatchname = checkpatchname(bzhandler.last_imported_patch(), current_filename=oldpatchname) if newpatchname != oldpatchname: if newpatchname in q.series: q.delete(repo, [newpatchname], {}) qrename(ui, repo, oldpatchname, newpatchname) # mq always reports the original name, which is confusing so we'll # report the rename. But if ui.verbose is on, qrename will have already # reported it. if not ui.verbose: ui.status("renamed %s -> %s\n" % (oldpatchname, newpatchname)) # now process the delayed imports # these opts are invariant for all patches newopts = {} newopts.update(opts) newopts['force'] = True # loop through the Patches and import them by calculating their url. The # bz:// handler will have cached the lookup so we don't hit the network # here. Each one of these pushes an unapplied patch onto the beginning of # the queue, and unapplied patches are ignored when importing them, so do # these in reverse order. for patch in reversed(list(bzhandler.delayed_imports)): newopts['name'] = checkpatchname(patch) path = makebzurl(patch.bug.num, patch.id) ret = orig(ui, repo, path, **newopts) if ret: return ret extensions.wrapcommand(cmdtable, 'qimport', qimporthook) # Here we setup the protocol handlers processors = { 'bz': bzhandler.Handler, 'pb': pb.Handler, 'scp': scp.Handler } for s, p in processors.items(): url.handlerfuncs.append(p) hg.schemes[s] = httppeer
def _cmdtableitem(ui, cmd, table): '''Return key, value from table for cmd, or None if not found.''' aliases, entry = cmdutil.findcmd(ui, cmd, table) for candidatekey, candidateentry in table.iteritems(): if candidateentry is entry: return candidatekey, entry
def _origcmd(name): """Return the callable mercurial will invoke for the given command name.""" return cmdutil.findcmd(name, commands.table)[1][0]
def findcmd(cmd): if cmdutil.findcmd.func_code.co_varnames[0] == "ui": # < 1.1.0 return cmdutil.findcmd(self._getUI(), cmd, commands.table) else: # >= 1.1.0 return cmdutil.findcmd(cmd, commands.table)
def qimporthook(orig, ui, repo, *files, **opts): q = repo.mq # checks for an unused patch name. prompts if the patch already exists and # returns the corrected name. def checkpatchname(patch): name = patch.name # Hg v1.4+: "ui.prompt is now a simple prompt and does not accept a list of choices. Use ui.promptchoice instead.". hasPromptchoice = hasattr(ui, 'promptchoice') while os.path.exists(q.join(name)): prompt = "A patch file named '%s' already exists in your patch directory. Rename %s '%s' (%d) (r)/overwrite (o)?" % \ (name, 'patch' if isinstance(patch, bz.Patch) else 'attachment', patch.desc, int(patch.id)) if hasPromptchoice: choice = ui.promptchoice(prompt, ("&readonly", "&overwrite"), 0) choice = ["r", "o"][choice] else: choice = ui.prompt(prompt, ("&readonly", "&overwrite"), "r") if choice == 'r': name = ui.prompt("Enter the new patch name (old one was '%s'):" % name) else: # overwrite break; if name in q.series and q.isapplied(name): ui.warn("Patch was already applied. Changes will not take effect until the patch is reapplied.") return name # hook for url.open which lets the user edit the returned def previewopen(orig, ui, path): fp = orig(ui, path) class PreviewReader(object): def read(self): return ui.edit(fp.read(), ui.username()) return PreviewReader() # Install the preview hook if necessary. This will preview non-bz:// bugs # and that's OK. if opts['preview']: extensions.wrapfunction(url, "open", previewopen) # mercurial's url.search_re includes the // and that doesn't match what we # want which is bz:dddddd(/ddddd)? files = map(fixuppath, files) # Remember where the next patch will be inserted into the series try: # hg 1.9+ insert = q.fullseriesend() except: insert = q.full_series_end() # Do the import as normal. The first patch of any bug is actually imported # and the rest are stored in the global delayed_imports. The imported # patches have dumb filenames because there's no way to tell mq to pick the # patch name *after* download. orig(ui, repo, *files, **opts) # If the user passed a name, then mq used that so we don't need to rename if not opts['name']: # cache the lookup of the name. findcmd is not fast. qrename = cmdutil.findcmd("qrename", commands.table)[1][0] # For all the already imported patches, rename them. Except there will # only be one, since if the url resolves to multiple patches then # everything but the first will go into bzhandler.delayed_imports. for (patch, path) in list(bzhandler.imported_patches): # Find where qimport will have inserted the initial patch try: # hg 1.9+ oldpatchname = q.fullseries[insert] except: oldpatchname = q.full_series[insert] insert += 1 newpatchname = checkpatchname(patch) if newpatchname != oldpatchname: qrename(ui, repo, oldpatchname, newpatchname) # mq always reports the original name, which is confusing so we'll # report the rename. But if ui.verbose is on, qrename will have # already reported it. if not ui.verbose: ui.write("renamed %s -> %s\n" % (oldpatchname, newpatchname)) # now process the delayed imports # these opts are invariant for all patches newopts = {} newopts.update(opts) newopts['force'] = True # loop through the Patches and import them by calculating their url. The # bz:// handler will have cached the lookup so we don't hit the network here for patch in bzhandler.delayed_imports: newopts['name'] = checkpatchname(patch) path = makebzurl(patch.bug.num, patch.id) orig(ui, repo, path, **newopts)
def findcmd(cmd): return cmdutil.findcmd(self._getUI(), cmd, commands.table)
def showstack(ui, repo, displayer): """current line of work""" wdirctx = repo['.'] if wdirctx.rev() == nullrev: raise error.Abort(_('stack view only available when there is a ' 'working directory')) if wdirctx.phase() == phases.public: ui.write(_('(empty stack; working directory parent is a published ' 'changeset)\n')) return # TODO extract "find stack" into a function to facilitate # customization and reuse. baserev = destutil.stackbase(ui, repo) basectx = None if baserev is None: baserev = wdirctx.rev() stackrevs = {wdirctx.rev()} else: stackrevs = set(repo.revs('%d::.', baserev)) ctx = repo[baserev] if ctx.p1().rev() != nullrev: basectx = ctx.p1() # And relevant descendants. branchpointattip = False cl = repo.changelog for rev in cl.descendants([wdirctx.rev()]): ctx = repo[rev] # Will only happen if . is public. if ctx.phase() == phases.public: break stackrevs.add(ctx.rev()) # ctx.children() within a function iterating on descandants # potentially has severe performance concerns because revlog.children() # iterates over all revisions after ctx's node. However, the number of # draft changesets should be a reasonably small number. So even if # this is quadratic, the perf impact should be minimal. if len(ctx.children()) > 1: branchpointattip = True break stackrevs = list(sorted(stackrevs, reverse=True)) # Find likely target heads for the current stack. These are likely # merge or rebase targets. if basectx: # TODO make this customizable? newheads = set(repo.revs('heads(%d::) - %ld - not public()', basectx.rev(), stackrevs)) else: newheads = set() allrevs = set(stackrevs) | newheads | set([baserev]) nodelen = longestshortest(repo, allrevs) try: cmdutil.findcmd('rebase', commands.table) haverebase = True except (error.AmbiguousCommand, error.UnknownCommand): haverebase = False # TODO use templating. # TODO consider using graphmod. But it may not be necessary given # our simplicity and the customizations required. # TODO use proper graph symbols from graphmod tres = formatter.templateresources(ui, repo) shortesttmpl = formatter.maketemplater(ui, '{shortest(node, %d)}' % nodelen, resources=tres) def shortest(ctx): return shortesttmpl.renderdefault({'ctx': ctx, 'node': ctx.hex()}) # We write out new heads to aid in DAG awareness and to help with decision # making on how the stack should be reconciled with commits made since the # branch point. if newheads: # Calculate distance from base so we can render the count and so we can # sort display order by commit distance. revdistance = {} for head in newheads: # There is some redundancy in DAG traversal here and therefore # room to optimize. ancestors = cl.ancestors([head], stoprev=basectx.rev()) revdistance[head] = len(list(ancestors)) sourcectx = repo[stackrevs[-1]] sortedheads = sorted(newheads, key=lambda x: revdistance[x], reverse=True) for i, rev in enumerate(sortedheads): ctx = repo[rev] if i: ui.write(': ') else: ui.write(' ') ui.write(('o ')) displayer.show(ctx, nodelen=nodelen) displayer.flush(ctx) ui.write('\n') if i: ui.write(':/') else: ui.write(' /') ui.write(' (') ui.write(_('%d commits ahead') % revdistance[rev], label='stack.commitdistance') if haverebase: # TODO may be able to omit --source in some scenarios ui.write('; ') ui.write(('hg rebase --source %s --dest %s' % ( shortest(sourcectx), shortest(ctx))), label='stack.rebasehint') ui.write(')\n') ui.write(':\n: ') ui.write(_('(stack head)\n'), label='stack.label') if branchpointattip: ui.write(' \\ / ') ui.write(_('(multiple children)\n'), label='stack.label') ui.write(' |\n') for rev in stackrevs: ctx = repo[rev] symbol = '@' if rev == wdirctx.rev() else 'o' if newheads: ui.write(': ') else: ui.write(' ') ui.write(symbol, ' ') displayer.show(ctx, nodelen=nodelen) displayer.flush(ctx) ui.write('\n') # TODO display histedit hint? if basectx: # Vertically and horizontally separate stack base from parent # to reinforce stack boundary. if newheads: ui.write(':/ ') else: ui.write(' / ') ui.write(_('(stack base)'), '\n', label='stack.label') ui.write(('o ')) displayer.show(basectx, nodelen=nodelen) displayer.flush(basectx) ui.write('\n')
def qimporthook(orig, ui, repo, *files, **opts): q = repo.mq # checks for an unused patch name. prompts if the patch already exists and # returns the corrected name. def checkpatchname(patch, current_filename=None): name = patch.name # For the first patch imported, the patch has already been temporarily saved # to disk by the time we reach here, with a filename that we cannot control # (eg 973703 for bz://973703). The patch is then renamed by us to the name of # the attachment in Bugzilla (unless patch_format has been overridden). # Unfortunately it's common for people to use the bug number as the filename of # the attachment, which would cause a name collision with this temporary file. # Patches other than the first will not have a current_filename. if name == current_filename: # Add a suffix to the patch filename to avoid a collision. This is preferable # to just skipping the check to see if the file exists, since if we leave the # filename as-is, subsequent qimports of the same bug will abort with an mq # "patch already exists" when it is unable to write its temporary file. ui.status("Changing patch filename to avoid conflict with temporary file.\n") name = "%s_" % name while os.path.exists(q.join(name)): msg = ( "A file named '%s' already exists in your patch directory.\n" "Rename %s '%s' (%d) (r)/overwrite (o)?" % (name, "patch" if isinstance(patch, bz.Patch) else "attachment", patch.desc, int(patch.id)) ) if name in q.series and q.isapplied(name): ui.write("A patch file named '%s' is already applied.\n") choice = "r" else: choice = ui.prompt(msg, default="r") if choice == "r": name = ui.prompt("Enter the new patch name (old one was '%s'):" % name, default=name) else: # overwrite break if name in q.series and q.isapplied(name): ui.fatal("Patch with that name is already applied.") return name # hook for url.open which lets the user edit the returned patch name def previewopen(orig, ui, path): fp = orig(ui, path) class PreviewReader(object): def read(self): return ui.edit(fp.read(), ui.username()) return PreviewReader() # Install the preview hook if necessary. This will preview non-bz:// bugs # and that's OK. if opts["preview"]: extensions.wrapfunction(url, "open", previewopen) # mercurial's url.search_re includes the // and that doesn't match what we # want which is bz:dddddd(/ddddd)? files = map(fixuppath, files) # Do the import as normal. The first patch of any bug is actually imported # and the rest are stored in the global delayed_imports. The imported # patches have dumb filenames because there's no way to tell mq to pick the # patch name *after* download. ret = orig(ui, repo, *files, **opts) if ret or bzhandler.last_imported_patch() is None: return ret # If the user passed a name, then mq used that so we don't need to rename if not opts["name"]: # cache the lookup of the name. findcmd is not fast. qrename = cmdutil.findcmd("qrename", commands.table)[1][0] # Rename the already imported patch. If there are multiple patches, the # rest will be in bzhandler.delayed_imports, which we'll name correctly # in the first place. oldpatchname = q.fullseries[q.fullseriesend()] newpatchname = checkpatchname(bzhandler.last_imported_patch(), current_filename=oldpatchname) if newpatchname != oldpatchname: if newpatchname in q.series: q.delete(repo, [newpatchname], {}) qrename(ui, repo, oldpatchname, newpatchname) # mq always reports the original name, which is confusing so we'll # report the rename. But if ui.verbose is on, qrename will have already # reported it. if not ui.verbose: ui.status("renamed %s -> %s\n" % (oldpatchname, newpatchname)) # now process the delayed imports # these opts are invariant for all patches newopts = {} newopts.update(opts) newopts["force"] = True # loop through the Patches and import them by calculating their url. The # bz:// handler will have cached the lookup so we don't hit the network # here. Each one of these pushes an unapplied patch onto the beginning of # the queue, and unapplied patches are ignored when importing them, so do # these in reverse order. for patch in reversed(list(bzhandler.delayed_imports)): newopts["name"] = checkpatchname(patch) path = makebzurl(patch.bug.num, patch.id) ret = orig(ui, repo, path, **newopts) if ret: return ret
def startrev_available(self): entry = cmdutil.findcmd('clone', commands.table)[1] longopts = set(e[1] for e in entry[1]) return 'startrev' in longopts
def extsetup(ui=None): # "Mercurial version 8e6019b16a7d and later (that is post-1.3.1) will pass a # ui argument to extsetup." # 'None': support pre/post Hg v1.3.1 versions. # Insert preview flag into qimport: # For HG 1.3.1 and earlier, commands.table has the commands for mq # For HG 1.4, commands.table does not have the commands for mq so we # use mq.cmdtable # # Note that we cannot just use mq.cmdtable always because each command entry # is a tuple so wrapping the qimport function will not update the right # table # # Hence our strategy is to try commands.table and fall back to mq.cmdtable # rather than do an explicit version check. try: qimport_cmd = cmdutil.findcmd("qimport", commands.table) cmdtable = commands.table except error.UnknownCommand: qimport_cmd = cmdutil.findcmd("qimport", mq.cmdtable) cmdtable = mq.cmdtable qimport_cmd[1][1].append(('p', 'preview', False, "preview commit message")) # re to match our url syntax bz_matcher = re.compile("bz:(?://)?(\d+)(?:/(\w+))?") pb_matcher = re.compile("pb:(?://)?(\d+)") scp_matcher= re.compile(r"scp:(?://)?(.*)") def makebzurl(num, attachid): return "bz://%s%s" % (num, "/" + attachid if attachid else "") def makepburl(num): return "pb://%s" % num def makescpurl(path): return "scp://%s" % urllib.quote(path, safe='') def fixuppath(path): m = bz_matcher.search(path) if m: bug, attachment = m.groups() return makebzurl(bug, attachment) m = pb_matcher.search(path) if m: num, = m.groups() return makepburl(num) m = scp_matcher.search(path) if m: scppath, = m.groups() return makescpurl(scppath) return path # hook the mq import so we can fixup the patchname and handle multiple # patches per url def qimporthook(orig, ui, repo, *files, **opts): q = repo.mq # checks for an unused patch name. prompts if the patch already exists and # returns the corrected name. def checkpatchname(patch): name = patch.name # Hg v1.4+: "ui.prompt is now a simple prompt and does not accept a list of choices. Use ui.promptchoice instead.". hasPromptchoice = hasattr(ui, 'promptchoice') while os.path.exists(q.join(name)): prompt = "A patch file named '%s' already exists in your patch directory. Rename %s '%s' (%d) (r)/overwrite (o)?" % \ (name, 'patch' if isinstance(patch, bz.Patch) else 'attachment', patch.desc, int(patch.id)) if hasPromptchoice: choice = ui.promptchoice(prompt, ("&readonly", "&overwrite"), 0) choice = ["r", "o"][choice] else: choice = ui.prompt(prompt, ("&readonly", "&overwrite"), "r") if choice == 'r': name = ui.prompt("Enter the new patch name (old one was '%s'):" % name) else: # overwrite break; if name in q.series and q.isapplied(name): ui.warn("Patch was already applied. Changes will not take effect until the patch is reapplied.") return name # hook for url.open which lets the user edit the returned def previewopen(orig, ui, path): fp = orig(ui, path) class PreviewReader(object): def read(self): return ui.edit(fp.read(), ui.username()) return PreviewReader() # Install the preview hook if necessary. This will preview non-bz:// bugs # and that's OK. if opts['preview']: extensions.wrapfunction(url, "open", previewopen) # mercurial's url.search_re includes the // and that doesn't match what we # want which is bz:dddddd(/ddddd)? files = map(fixuppath, files) # Remember where the next patch will be inserted into the series try: # hg 1.9+ insert = q.fullseriesend() except: insert = q.full_series_end() # Do the import as normal. The first patch of any bug is actually imported # and the rest are stored in the global delayed_imports. The imported # patches have dumb filenames because there's no way to tell mq to pick the # patch name *after* download. orig(ui, repo, *files, **opts) # If the user passed a name, then mq used that so we don't need to rename if not opts['name']: # cache the lookup of the name. findcmd is not fast. qrename = cmdutil.findcmd("qrename", commands.table)[1][0] # For all the already imported patches, rename them. Except there will # only be one, since if the url resolves to multiple patches then # everything but the first will go into bzhandler.delayed_imports. for (patch, path) in list(bzhandler.imported_patches): # Find where qimport will have inserted the initial patch try: # hg 1.9+ oldpatchname = q.fullseries[insert] except: oldpatchname = q.full_series[insert] insert += 1 newpatchname = checkpatchname(patch) if newpatchname != oldpatchname: qrename(ui, repo, oldpatchname, newpatchname) # mq always reports the original name, which is confusing so we'll # report the rename. But if ui.verbose is on, qrename will have # already reported it. if not ui.verbose: ui.write("renamed %s -> %s\n" % (oldpatchname, newpatchname)) # now process the delayed imports # these opts are invariant for all patches newopts = {} newopts.update(opts) newopts['force'] = True # loop through the Patches and import them by calculating their url. The # bz:// handler will have cached the lookup so we don't hit the network here for patch in bzhandler.delayed_imports: newopts['name'] = checkpatchname(patch) path = makebzurl(patch.bug.num, patch.id) orig(ui, repo, path, **newopts) extensions.wrapcommand(cmdtable, 'qimport', qimporthook) # Here we setup the protocol handlers processors = { 'bz' : bzhandler.Handler, 'pb' : pb.Handler, 'scp' : scp.Handler } # Mercurial 1.4 has an easy way to do this for bz://dddddd urls if hasattr(url, 'handlerfuncs') and hasattr(hg, 'schemes'): for s, p in processors.items(): url.handlerfuncs.append(p) hg.schemes[s] = httppeer else: # monkey patching for 1.3.1 :( # patch in bz: and pb: url support def bzopener(orig, ui, authinfo=None): result = orig(ui, authinfo) for p in processors: result.add_handler(p(ui, authinfo)) return result extensions.wrapfunction(url, "opener", bzopener)
def qimporthook(orig, ui, repo, *files, **opts): q = repo.mq # checks for an unused patch name. prompts if the patch already exists and # returns the corrected name. def checkpatchname(patch): name = patch.name # Hg v1.4+: "ui.prompt is now a simple prompt and does not accept a list of choices. Use ui.promptchoice instead.". hasPromptchoice = hasattr(ui, 'promptchoice') while os.path.exists(q.join(name)): prompt = "A patch file named '%s' already exists in your patch directory. Rename %s '%s' (%d) (r)/overwrite (o)?" % \ (name, 'patch' if isinstance(patch, bz.Patch) else 'attachment', patch.desc, int(patch.id)) if hasPromptchoice: choice = ui.promptchoice(prompt, ("&readonly", "&overwrite"), 0) choice = ["r", "o"][choice] else: choice = ui.prompt(prompt, ("&readonly", "&overwrite"), "r") if choice == 'r': name = ui.prompt( "Enter the new patch name (old one was '%s'):" % name) else: # overwrite break if name in q.series and q.isapplied(name): ui.warn( "Patch was already applied. Changes will not take effect until the patch is reapplied." ) return name # hook for url.open which lets the user edit the returned def previewopen(orig, ui, path): fp = orig(ui, path) class PreviewReader(object): def read(self): return ui.edit(fp.read(), ui.username()) return PreviewReader() # Install the preview hook if necessary. This will preview non-bz:// bugs # and that's OK. if opts['preview']: extensions.wrapfunction(url, "open", previewopen) # mercurial's url.search_re includes the // and that doesn't match what we # want which is bz:dddddd(/ddddd)? files = map(fixuppath, files) # Remember where the next patch will be inserted into the series try: # hg 1.9+ insert = q.fullseriesend() except: insert = q.full_series_end() # Do the import as normal. The first patch of any bug is actually imported # and the rest are stored in the global delayed_imports. The imported # patches have dumb filenames because there's no way to tell mq to pick the # patch name *after* download. orig(ui, repo, *files, **opts) # If the user passed a name, then mq used that so we don't need to rename if not opts['name']: # cache the lookup of the name. findcmd is not fast. qrename = cmdutil.findcmd("qrename", commands.table)[1][0] # For all the already imported patches, rename them. Except there will # only be one, since if the url resolves to multiple patches then # everything but the first will go into bzhandler.delayed_imports. for (patch, path) in list(bzhandler.imported_patches): # Find where qimport will have inserted the initial patch try: # hg 1.9+ oldpatchname = q.fullseries[insert] except: oldpatchname = q.full_series[insert] insert += 1 newpatchname = checkpatchname(patch) if newpatchname != oldpatchname: qrename(ui, repo, oldpatchname, newpatchname) # mq always reports the original name, which is confusing so we'll # report the rename. But if ui.verbose is on, qrename will have # already reported it. if not ui.verbose: ui.write("renamed %s -> %s\n" % (oldpatchname, newpatchname)) # now process the delayed imports # these opts are invariant for all patches newopts = {} newopts.update(opts) newopts['force'] = True # loop through the Patches and import them by calculating their url. The # bz:// handler will have cached the lookup so we don't hit the network here for patch in bzhandler.delayed_imports: newopts['name'] = checkpatchname(patch) path = makebzurl(patch.bug.num, patch.id) orig(ui, repo, path, **newopts)
def extsetup(ui=None): # "Mercurial version 8e6019b16a7d and later (that is post-1.3.1) will pass a # ui argument to extsetup." # 'None': support pre/post Hg v1.3.1 versions. # Insert preview flag into qimport: # For HG 1.3.1 and earlier, commands.table has the commands for mq # For HG 1.4, commands.table does not have the commands for mq so we # use mq.cmdtable # # Note that we cannot just use mq.cmdtable always because each command entry # is a tuple so wrapping the qimport function will not update the right # table # # Hence our strategy is to try commands.table and fall back to mq.cmdtable # rather than do an explicit version check. try: qimport_cmd = cmdutil.findcmd("qimport", commands.table) cmdtable = commands.table except error.UnknownCommand: qimport_cmd = cmdutil.findcmd("qimport", mq.cmdtable) cmdtable = mq.cmdtable qimport_cmd[1][1].append(('p', 'preview', False, "preview commit message")) # re to match our url syntax bz_matcher = re.compile("bz:(?://)?(\d+)(?:/(\w+))?") pb_matcher = re.compile("pb:(?://)?(\d+)") scp_matcher = re.compile(r"scp:(?://)?(.*)") def makebzurl(num, attachid): return "bz://%s%s" % (num, "/" + attachid if attachid else "") def makepburl(num): return "pb://%s" % num def makescpurl(path): return "scp://%s" % urllib.quote(path, safe='') def fixuppath(path): m = bz_matcher.search(path) if m: bug, attachment = m.groups() return makebzurl(bug, attachment) m = pb_matcher.search(path) if m: num, = m.groups() return makepburl(num) m = scp_matcher.search(path) if m: scppath, = m.groups() return makescpurl(scppath) return path # hook the mq import so we can fixup the patchname and handle multiple # patches per url def qimporthook(orig, ui, repo, *files, **opts): q = repo.mq # checks for an unused patch name. prompts if the patch already exists and # returns the corrected name. def checkpatchname(patch): name = patch.name # Hg v1.4+: "ui.prompt is now a simple prompt and does not accept a list of choices. Use ui.promptchoice instead.". hasPromptchoice = hasattr(ui, 'promptchoice') while os.path.exists(q.join(name)): prompt = "A patch file named '%s' already exists in your patch directory. Rename %s '%s' (%d) (r)/overwrite (o)?" % \ (name, 'patch' if isinstance(patch, bz.Patch) else 'attachment', patch.desc, int(patch.id)) if hasPromptchoice: choice = ui.promptchoice(prompt, ("&readonly", "&overwrite"), 0) choice = ["r", "o"][choice] else: choice = ui.prompt(prompt, ("&readonly", "&overwrite"), "r") if choice == 'r': name = ui.prompt( "Enter the new patch name (old one was '%s'):" % name) else: # overwrite break if name in q.series and q.isapplied(name): ui.warn( "Patch was already applied. Changes will not take effect until the patch is reapplied." ) return name # hook for url.open which lets the user edit the returned def previewopen(orig, ui, path): fp = orig(ui, path) class PreviewReader(object): def read(self): return ui.edit(fp.read(), ui.username()) return PreviewReader() # Install the preview hook if necessary. This will preview non-bz:// bugs # and that's OK. if opts['preview']: extensions.wrapfunction(url, "open", previewopen) # mercurial's url.search_re includes the // and that doesn't match what we # want which is bz:dddddd(/ddddd)? files = map(fixuppath, files) # Remember where the next patch will be inserted into the series try: # hg 1.9+ insert = q.fullseriesend() except: insert = q.full_series_end() # Do the import as normal. The first patch of any bug is actually imported # and the rest are stored in the global delayed_imports. The imported # patches have dumb filenames because there's no way to tell mq to pick the # patch name *after* download. orig(ui, repo, *files, **opts) # If the user passed a name, then mq used that so we don't need to rename if not opts['name']: # cache the lookup of the name. findcmd is not fast. qrename = cmdutil.findcmd("qrename", commands.table)[1][0] # For all the already imported patches, rename them. Except there will # only be one, since if the url resolves to multiple patches then # everything but the first will go into bzhandler.delayed_imports. for (patch, path) in list(bzhandler.imported_patches): # Find where qimport will have inserted the initial patch try: # hg 1.9+ oldpatchname = q.fullseries[insert] except: oldpatchname = q.full_series[insert] insert += 1 newpatchname = checkpatchname(patch) if newpatchname != oldpatchname: qrename(ui, repo, oldpatchname, newpatchname) # mq always reports the original name, which is confusing so we'll # report the rename. But if ui.verbose is on, qrename will have # already reported it. if not ui.verbose: ui.write("renamed %s -> %s\n" % (oldpatchname, newpatchname)) # now process the delayed imports # these opts are invariant for all patches newopts = {} newopts.update(opts) newopts['force'] = True # loop through the Patches and import them by calculating their url. The # bz:// handler will have cached the lookup so we don't hit the network here for patch in bzhandler.delayed_imports: newopts['name'] = checkpatchname(patch) path = makebzurl(patch.bug.num, patch.id) orig(ui, repo, path, **newopts) extensions.wrapcommand(cmdtable, 'qimport', qimporthook) # Here we setup the protocol handlers processors = { 'bz': bzhandler.Handler, 'pb': pb.Handler, 'scp': scp.Handler } # Mercurial 1.4 has an easy way to do this for bz://dddddd urls if hasattr(url, 'handlerfuncs') and hasattr(hg, 'schemes'): for s, p in processors.items(): url.handlerfuncs.append(p) hg.schemes[s] = httppeer else: # monkey patching for 1.3.1 :( # patch in bz: and pb: url support def bzopener(orig, ui, authinfo=None): result = orig(ui, authinfo) for p in processors: result.add_handler(p(ui, authinfo)) return result extensions.wrapfunction(url, "opener", bzopener)
def findcmd(cmd): return cmdutil.findcmd(self._getUI(), cmd)
repocount = len(_list(ui, repo, st and {'subtrees': st} or {})) rc = _docmd2(_origcmd('push'), ui, repo, remote, adjust, **opts) # return 0 if all pushes were successful; 1 if none of the repos had # anything to push. return int(rc == repocount) @command('^tstatus') def status(ui, repo, *args, **opts): '''show changed files in the working directory''' _checklocal(repo) return _docmd1(_origcmd('status'), ui, repo, *args, **opts) try: cmdutil.findcmd('summary', commands.table) @command('tsummary') def summary(ui, repo, **opts): """summarize working directory state""" _checklocal(repo) return _docmd1(_origcmd('summary'), ui, repo, **opts) except: # The summary command is not present in early versions of mercurial pass @command('ttag') def tag(ui, repo, name1, *names, **opts): '''add one or more tags for the current or given revision''' _checklocal(repo)
options = {} cmdoptions = {} try: args = fancyopts.fancyopts(args, globalopts, options) except fancyopts.getopt.GetoptError, inst: raise error.CommandError(None, inst) if args: alias, args = args[0], args[1:] elif options['help']: help_(ui, None) sys.exit() else: alias, args = 'workbench', [] aliases, i = cmdutil.findcmd(alias, table, ui.config("ui", "strict")) for a in aliases: if a.startswith(alias): alias = a break cmd = aliases[0] c = list(i[1]) # combine global options into local for o in globalopts: c.append((o[0], o[1], options[o[1]], o[3])) try: args = fancyopts.fancyopts(args, c, cmdoptions, True) except fancyopts.getopt.GetoptError, inst: raise error.CommandError(cmd, inst)
options = {} cmdoptions = {} try: args = fancyopts.fancyopts(args, globalopts, options) except fancyopts.getopt.GetoptError, inst: raise error.CommandError(None, inst) if args: alias, args = args[0], args[1:] elif options['help']: help_(ui, None) sys.exit() else: alias, args = 'workbench', [] aliases, i = cmdutil.findcmd(alias, table, ui.config("ui", "strict")) for a in aliases: if a.startswith(alias): alias = a break cmd = aliases[0] c = list(i[1]) # combine global options into local for o in 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 error.CommandError(cmd, inst)
def dmmerge(ui, repo, branch, **kwargs): branch_map = get_bookmark_map(repo)[0] name = branch_map[branch] if name == DEFAULT_BRANCH: name = '@' cmdutil.findcmd('merge', commands.table)[1][0](ui, repo, name, **kwargs)