--- a/mercurial/cmdutil.py Sat Apr 30 19:41:53 2011 +0200
+++ b/mercurial/cmdutil.py Sat Apr 30 18:30:14 2011 +0200
@@ -174,7 +174,7 @@
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)
--- a/mercurial/commands.py Sat Apr 30 19:41:53 2011 +0200
+++ b/mercurial/commands.py Sat Apr 30 18:30:14 2011 +0200
@@ -1296,7 +1296,10 @@
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)
--- a/mercurial/help/revsets.txt Sat Apr 30 19:41:53 2011 +0200
+++ b/mercurial/help/revsets.txt Sat Apr 30 18:30:14 2011 +0200
@@ -61,6 +61,26 @@
.. predicatesmarker
+New predicates (known as "aliases") can be defined, using any combination of
+existing predicates or other aliases. An alias definition looks like::
+
+ <alias> = <definition>
+
+in the ``revsetalias`` section of ``.hgrc``. Arguments of the form `$1`, `$2`,
+etc. are substituted from the alias into the definition.
+
+For example,
+
+::
+
+ [revsetalias]
+ h = heads()
+ d($1) = sort($1, date)
+ rs($1, $2) = reverse(sort($1, $2))
+
+defines three aliases, ``h``, ``d``, and ``rs``. ``rs(0:tip, author)`` is
+exactly equivalent to ``reverse(sort(0:tip, author))``.
+
Command line equivalents for :hg:`log`::
-f -> ::.
--- a/mercurial/revset.py Sat Apr 30 19:41:53 2011 +0200
+++ b/mercurial/revset.py Sat Apr 30 18:30:14 2011 +0200
@@ -889,14 +889,89 @@
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)
--- a/tests/test-revset.t Sat Apr 30 19:41:53 2011 +0200
+++ b/tests/test-revset.t Sat Apr 30 18:30:14 2011 +0200
@@ -2,7 +2,7 @@
$ export HGENCODING
$ try() {
- > hg debugrevspec --debug $@
+ > hg debugrevspec --debug "$@"
> }
$ log() {
@@ -411,3 +411,27 @@
$ 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