changeset 28:af82295c9167

7901672: trees extension broken after upgrade to mercurial 3.8.1 Reviewed-by: ihse, jjg
author erikj
date Fri, 02 Jun 2017 18:22:29 -0700
parents 05c34e801d96
children a789f1e84919
files trees.py
diffstat 1 files changed, 110 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/trees.py	Fri Apr 21 14:52:54 2017 -0700
+++ b/trees.py	Fri Jun 02 18:22:29 2017 -0700
@@ -131,9 +131,47 @@
 2.0-rc 2.0 2.0.2 2.1-rc 2.1 2.1.2 2.2-rc 2.2 2.2.3
 2.3-rc 2.3 2.3.2 2.4-rc 2.4 2.4.2 2.5-rc 2.5 2.5.4
 2.6-rc 2.6 2.6.3 2.7-rc 2.7 2.7.2 2.8-rc 2.8 2.8.2
-2.9-rc 2.9
+2.9-rc 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 3.0-rc 3.1
+3.1.1 3.1.2 3.1-rc 3.2 3.2.1 3.2.2 3.2.3 3.2.4
+3.2-rc 3.3 3.3.1 3.3.2 3.3.3 3.3-rc 3.4 3.4.1 3.4.2
+3.4-rc 3.5 3.5.1 3.5.2 3.5-rc 3.6 3.6.1 3.6.2 3.6.3
+3.6-rc 3.7 3.7.1 3.7.2 3.7.3 3.7-rc 3.8 3.8.1 3.8.2
+3.8.3 3.8.4 3.8-rc 3.9 3.9.1 3.9.2 3.9-rc 4.0-rc
+4.0 4.1
 '''
 
+# From Mercurial 1.9, the preferred way to define commands is using the @command
+# decorator. From version 3.8, the old way of directly defining the command table
+# was deprecated and in 4.1 it's no longer supported at all.
+#
+# By defining a wrapper function, compatibility with most versions is kept. The
+# wrapper first tries to call the actual decorator if it is defined. If it isn't
+# or if it doesn't support all the arguments supplied to it, it falls back to
+# minimal working implementation for our use.
+#
+# Part of the command definition needs to be deferred until other plugins have
+# been loaded so the decorator only adds minimal data here. The rest is filled in
+# further down in extsetup.
+cmdtable = {}
+cmdutil_command = None
+if hasattr(cmdutil, 'command'):
+    cmdutil_command = cmdutil.command(cmdtable)
+
+def command(name, options=(), synopsis=None, norepo=False, optionalrepo=False):
+    if cmdutil_command:
+        try:
+            return cmdutil_command(name, options, synopsis, norepo, optionalrepo)
+        except:
+            # Mercurial versions older than 3.1 does not support all the options
+            # used here. If it fails, fall back to workaround.
+            pass
+    def decorator(func):
+        func.norepo = norepo
+        func.optionalrepo = optionalrepo
+        cmdtable[name] = func, list(options), synopsis
+        return func
+    return decorator
+
 def _checklocal(repo):
     if not isinstance(repo, localrepo.localrepository):
         raise util.Abort(_('repository is not local'))
@@ -397,6 +435,7 @@
 # Need to indirect through hg_clone for compatibility w/various hg versions.
 hg_clone = None
 
+@command("^tclone", norepo=True)
 def clone(ui, source, dest=None, *subtreeargs, **opts):
     '''copy one or more existing repositories to create a tree'''
     global hg_clone
@@ -428,7 +467,10 @@
             return rc
     return rc
 
-def command(ui, repo, cmd, *args, **opts):
+# This function cannot be named "command" since it clashes with the @command
+# decorator.
+@command('tcommand|tcmd')
+def command_cmd(ui, repo, cmd, *args, **opts):
     """Run a command in each repo in the tree.
 
     Change directory to the root of each repo and run the command.
@@ -445,6 +487,7 @@
     l = __builtin__.list((cmd,) + args)
     return _command(ui, repo, l, opts.get('stop'), opts)
 
+@command('tcommit|tci')
 def commit(ui, repo, *pats, **opts):
     """commit all files"""
     _checklocal(repo)
@@ -567,6 +610,7 @@
         return nestconfig(ui, repo, subtrees, opts)
     return _writeconfig(repo, _ns(ui, opts), subtrees)
 
+@command('tconfig', optionalrepo=True)
 def config(ui, repo, *subtrees, **opts):
     """list or change the subtrees configuration
 
@@ -626,11 +670,13 @@
         ui.write(subtree + '\n')
     return 0
 
+@command('tdiff')
 def diff(ui, repo, *args, **opts):
     """diff repository (or selected files)"""
     _checklocal(repo)
     return _docmd1(_origcmd('diff'), ui, repo, *args, **opts)
 
+@command('theads')
 def heads(ui, repo, *branchrevs, **opts):
     """show current repository heads or show branch heads"""
     _checklocal(repo)
@@ -640,6 +686,7 @@
     # return 0 if any of the repos have matching heads; 1 otherwise.
     return int(rc == repocount)
 
+@command('tincoming')
 def incoming(ui, repo, remote="default", **opts):
     """show new changesets found in source"""
     _checklocal(repo)
@@ -661,7 +708,9 @@
             ui.warn('repo %s is missing subtree %s\n' % (repo.root, subtree))
     return l
 
-def list(ui, repo, **opts):
+# This function cannot be named list since it clashes with the python builtin
+@command('tlist')
+def list_cmd(ui, repo, **opts):
     """list the repo and configured subtrees, recursively
 
     The initial list of subtrees is obtained from the command line (if present)
@@ -684,11 +733,13 @@
         ui.write(subtree + '\n')
     return 0
 
+@command('^tlog|thistory')
 def log(ui, repo, *args, **opts):
     '''show revision history of entire repository or files'''
     _checklocal(repo)
     return _docmd1(_origcmd('log'), ui, repo, *args, **opts)
 
+@command('tmerge')
 def merge(ui, repo, node=None, **opts):
     '''merge working directory with another revision'''
     _checklocal(repo)
@@ -702,6 +753,7 @@
 
     return _docmd1(condmerge, ui, repo, node, **opts)
 
+@command('toutgoing')
 def outgoing(ui, repo, remote=None, **opts):
     '''show changesets not found in the destination'''
     _checklocal(repo)
@@ -712,6 +764,7 @@
     # return 0 if any of the repos have outgoing changes; 1 otherwise.
     return int(rc == repocount)
 
+@command('tparents')
 def parents(ui, repo, filename=None, **opts):
     _checklocal(repo)
     return _docmd1(_origcmd('parents'), ui, repo, filename, **opts)
@@ -725,11 +778,13 @@
         _paths(cmd, lr.ui, lr, search, **opts)
     return 0
 
+@command('tpaths')
 def paths(ui, repo, search=None, **opts):
     '''show aliases for remote repositories'''
     _checklocal(repo)
     return _paths(_origcmd('paths'), ui, repo, search, **opts)
 
+@command('^tpull')
 def pull(ui, repo, remote="default", **opts):
     '''pull changes from the specified source'''
     _checklocal(repo)
@@ -742,6 +797,7 @@
     # return 0 if any subtree pulled successfully.
     return int(rc == repocount)
 
+@command('^tpush')
 def push(ui, repo, remote=None, **opts):
     '''push changes to the specified destination'''
     _checklocal(repo)
@@ -753,21 +809,30 @@
     # 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)
 
-def summary(ui, repo, **opts):
-    """summarize working directory state"""
-    _checklocal(repo)
-    return _docmd1(_origcmd('summary'), ui, repo, **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)
     return _docmd1(_origcmd('tag'), ui, repo, name1, *names, **opts)
 
+@command('ttip')
 def tip(ui, repo, **opts):
     '''show the tip revision'''
     _checklocal(repo)
@@ -788,6 +853,7 @@
         rc += trc != None and trc or 0
     return rc
 
+@command('^tupdate')
 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
            **opts):
     '''update working directory (or switch revisions)'''
@@ -796,16 +862,19 @@
                  **opts)
     return rc and 1 or 0
 
+@command('tversion', norepo=True)
 def version(ui, **opts):
     '''show version information'''
     ui.status('trees extension (version 0.7)\n')
 
+@command('tdefapth')
 def defpath(ui, repo, peer=None, peer_push=None, **opts):
     '''examine and manipulate default path settings for a tree.'''
     def walker(r):
         return _list(ui, r, opts)
     return defpath_mod.defpath(ui, repo, peer, peer_push, walker, opts)
 
+@command('tdebugkeys', norepo=True)
 def debugkeys(ui, src, **opts):
     '''list the tree configuration using mercurial's pushkey mechanism.
 
@@ -929,38 +998,45 @@
     except:
         pass
 
-    global cmdtable
-    cmdtable = {
-        '^tclone': _newcte('clone', clone, cloneopts,
-                           _('[OPTION]... SOURCE [DEST [SUBTREE]...]')),
-        'tcommand|tcmd': (command, commandopts, _('command [arg] ...')),
-        'tcommit|tci': _newcte('commit', commit, subtreesopts),
-        'tconfig': (config, configopts, _('[OPTION]... [SUBTREE]...')),
-        'tdiff': _newcte('diff', diff, subtreesopts),
-        'theads': _newcte('heads', heads, subtreesopts),
-        'tincoming': _newcte('incoming', incoming, subtreesopts),
-        'toutgoing': _newcte('outgoing', outgoing, subtreesopts),
-        'tlist': (list, listopts, _('[OPTION]...')),
-        '^tlog|thistory': _newcte('log', log, subtreesopts),
-        'tmerge': _newcte('merge', merge, subtreesopts),
-        'tparents': _newcte('parents', parents, subtreesopts),
-        'tpaths': _newcte('paths', paths, subtreesopts),
-        '^tpull': _newcte('pull', pull, subtreesopts),
-        '^tpush': _newcte('push', push, subtreesopts),
-        '^tstatus': _newcte('status', status, subtreesopts),
-        '^tupdate': _newcte('update', update, subtreesopts),
-        'ttag': _newcte('tag', tag, subtreesopts),
-        'ttip': _newcte('tip', tip, subtreesopts),
-        'tversion': (version, [], ''),
-        'tdebugkeys': (debugkeys, namespaceopt, '')
-    }
+    # The command and function names are duplicated here from the command
+    # decorators above. This could benefit from further cleanup.
+    cmdtable['^tclone'] = _newcte('clone', clone, cloneopts,
+            _('[OPTION]... SOURCE [DEST [SUBTREE]...]'))
+    cmdtable['tcommand|tcmd'] = (command_cmd, commandopts, _('command [arg] ...'))
+    cmdtable['tcommit|tci'] = _newcte('commit', commit, subtreesopts)
+    cmdtable['tconfig'] = (config, configopts, _('[OPTION]... [SUBTREE]...'))
+    cmdtable['tdiff'] = _newcte('diff', diff, subtreesopts)
+    cmdtable['theads'] = _newcte('heads', heads, subtreesopts)
+    cmdtable['tincoming'] = _newcte('incoming', incoming, subtreesopts)
+    cmdtable['toutgoing'] = _newcte('outgoing', outgoing, subtreesopts)
+    cmdtable['tlist'] = (list_cmd, listopts, _('[OPTION]...'))
+    cmdtable['^tlog|thistory'] = _newcte('log', log, subtreesopts)
+    cmdtable['tmerge'] = _newcte('merge', merge, subtreesopts)
+    cmdtable['tparents'] = _newcte('parents', parents, subtreesopts)
+    cmdtable['tpaths'] = _newcte('paths', paths, subtreesopts)
+    cmdtable['^tpull'] = _newcte('pull', pull, subtreesopts)
+    cmdtable['^tpush'] = _newcte('push', push, subtreesopts)
+    cmdtable['^tstatus'] = _newcte('status', status, subtreesopts)
+    try:
+        cmdtable['tsummary'] = _newcte('summary', summary, subtreesopts)
+    except:
+        # The summary command is not present in early versions of mercurial
+        pass
+    cmdtable['^tupdate'] = _newcte('update', update, subtreesopts)
+    cmdtable['ttag'] = _newcte('tag', tag, subtreesopts)
+    cmdtable['ttip'] = _newcte('tip', tip, subtreesopts)
+    cmdtable['tversion'] = (version, [], '')
+    cmdtable['tdebugkeys'] = (debugkeys, namespaceopt, '')
     if defpath_mod:
         cmdtable['tdefpath'] = (defpath, defpath_opts, _(''))
     if getattr(commands, 'summary', None):
         cmdtable['tsummary'] = _newcte('summary', summary, subtreesopts)
 
-commands.norepo += ' tclone tversion tdebugkeys'
-commands.optionalrepo += ' tconfig'
+# hg > 3.8: setting norepo and optionalrepo can only be done through decorators
+# and these attributes are no longer present.
+if hasattr(commands, 'norepo'):
+    commands.norepo += ' tclone tversion tdebugkeys'
+    commands.optionalrepo += ' tconfig'
 
 def genlistkeys(namespace):
     def _listkeys(repo):