[PATCH] revset aliases

Alexander Solovyov alexander at solovyov.net
Sun May 1 03:21:03 CDT 2011


# HG changeset patch
# User Alexander Solovyov <alexander at solovyov.net>
# Date 1304181014 -7200
# Node ID db915c57691d5f8badf682ee6f0739a395168900
# Parent  e83ced8b6464ff8f4c6cd9e4b780ba4b5d6208e0
revset aliases

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -174,7 +174,7 @@ def revrange(repo, revs):
             pass
 
         # fall through to new-style queries if old-style fails
-        m = revset.match(spec)
+        m = revset.match(repo.ui, spec)
         for r in m(repo, range(len(repo))):
             if r not in seen:
                 l.append(r)
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -1296,7 +1296,10 @@ def debugrevspec(ui, repo, expr):
     if ui.verbose:
         tree = revset.parse(expr)[0]
         ui.note(tree, "\n")
-    func = revset.match(expr)
+        newtree = revset.findaliases(ui, tree)
+        if newtree != tree:
+            ui.note(newtree, "\n")
+    func = revset.match(ui, expr)
     for c in func(repo, range(len(repo))):
         ui.write("%s\n" % c)
 
diff --git a/mercurial/help/revsets.txt b/mercurial/help/revsets.txt
--- a/mercurial/help/revsets.txt
+++ b/mercurial/help/revsets.txt
@@ -61,6 +61,17 @@ The following predicates are supported:
 
 .. predicatesmarker
 
+This language also supports aliases, both symbolic and predicates, which you can
+put in an ``.hgrc`` with a following syntax::
+
+  [revsetalias]
+  h = heads()
+  d($1) = sort($1, date)
+  rs($1, $2) = reverse(sort($1, $2))
+
+You can use any syntax for arguments, simple substitution is used to replace
+them.
+
 Command line equivalents for :hg:`log`::
 
   -f    ->  ::.
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -889,14 +889,89 @@ def optimize(x, small):
         return w + wa, (op, x[1], ta)
     return 1, x
 
+class revsetalias(object):
+    funcre = re.compile('^([^(]+)\(([^)]+)\)$')
+    args = ()
+
+    def __init__(self, token, value):
+        '''Aliases like:
+
+        h = heads(default)
+        b($1) = ancestors($1) - ancestors(default)
+        '''
+        if isinstance(token, tuple):
+            self.type, self.name = token
+        else:
+            m = self.funcre.search(token)
+            if m:
+                self.type = 'func'
+                self.name = m.group(1)
+                self.args = [x.strip() for x in m.group(2).split(',')]
+            else:
+                self.type = 'symbol'
+                self.name = token
+
+        if isinstance(value, str):
+            for arg in self.args:
+                value = value.replace(arg, repr(arg))
+            self.replacement, pos = parse(value)
+            if pos != len(value):
+                raise error.ParseError('invalid token', pos)
+        else:
+            self.replacement = value
+
+    def match(self, tree):
+        if not tree:
+            return False
+        if tree == (self.type, self.name):
+            return True
+        if tree[0] != self.type:
+            return False
+        if len(tree) > 1 and tree[1] != ('symbol', self.name):
+            return False
+        # 'func' + funcname + args
+        if ((self.args and len(tree) != 3) or
+            (len(self.args) == 1 and tree[2][0] == 'list') or
+            (len(self.args) > 1 and (tree[2][0] != 'list' or
+                                     len(tree[2]) - 1 != len(self.args)))):
+            raise error.ParseError('invalid amount of arguments', len(tree) - 2)
+        return True
+
+    def replace(self, tree):
+        if tree == (self.type, self.name):
+            return self.replacement
+        result = self.replacement
+        def getsubtree(i):
+            if tree[2][0] == 'list':
+                return tree[2][i+1]
+            return tree[i+2]
+        for i, v in enumerate(self.args):
+            valalias = revsetalias(('string', v), getsubtree(i))
+            result = valalias.process(result)
+        return result
+
+    def process(self, tree):
+        if self.match(tree):
+            return self.replace(tree)
+        if isinstance(tree, tuple):
+            return tuple(map(self.process, tree))
+        return tree
+
+def findaliases(ui, tree):
+    for k, v in ui.configitems('revsetalias'):
+        alias = revsetalias(k, v)
+        tree = alias.process(tree)
+    return tree
+
 parse = parser.parser(tokenize, elements).parse
 
-def match(spec):
+def match(ui, spec):
     if not spec:
         raise error.ParseError(_("empty query"))
     tree, pos = parse(spec)
     if (pos != len(spec)):
         raise error.ParseError("invalid token", pos)
+    tree = findaliases(ui, tree)
     weight, tree = optimize(tree, True)
     def mfunc(repo, subset):
         return getset(repo, subset, tree)
diff --git a/tests/test-revset.t b/tests/test-revset.t
--- a/tests/test-revset.t
+++ b/tests/test-revset.t
@@ -2,7 +2,7 @@
   $ export HGENCODING
 
   $ try() {
-  >   hg debugrevspec --debug $@
+  >   hg debugrevspec --debug "$@"
   > }
 
   $ log() {
@@ -411,3 +411,27 @@ parentrevspec
   $ log 'tip^foo'
   hg: parse error: ^ expects a number 0, 1, or 2
   [255]
+
+aliases:
+
+  $ echo '[revsetalias]' >> .hg/hgrc
+  $ echo 'm = merge()' >> .hg/hgrc
+  $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
+  $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
+
+  $ try m
+  ('symbol', 'm')
+  ('func', ('symbol', 'merge'), None)
+  6
+  $ try 'd(2:5)'
+  ('func', ('symbol', 'd'), ('range', ('symbol', '2'), ('symbol', '5')))
+  ('func', ('symbol', 'reverse'), ('func', ('symbol', 'sort'), ('list', ('range', ('symbol', '2'), ('symbol', '5')), ('symbol', 'date'))))
+  4
+  5
+  3
+  2
+  $ try 'rs(2 or 3, date)'
+  ('func', ('symbol', 'rs'), ('list', ('or', ('symbol', '2'), ('symbol', '3')), ('symbol', 'date')))
+  ('func', ('symbol', 'reverse'), ('func', ('symbol', 'sort'), ('list', ('or', ('symbol', '2'), ('symbol', '3')), ('symbol', 'date'))))
+  3
+  2


More information about the Mercurial-devel mailing list