[PATCH RFC] revset: add pattern matching to 'tag' revset expression
Simon King
simon at simonking.org.uk
Fri May 25 16:23:02 CDT 2012
# HG changeset patch
# User Simon King <simon at simonking.org.uk>
# Date 1337980885 -3600
# Node ID acd9d3574a98a80e9db12b89a6f338d1541f3a1d
# Parent 2ac08d8b21aa7b6e0a062afed5a3f357ccef67f9
revset: add pattern matching to 'tag' revset expression
The 'tag' revset expression now takes an optional second parameter, which
is either 're' or 'glob'. When one of these is supplied, the tag name is
treated as a pattern, and any tags which match the pattern will be returned
from the expression.
Outstanding questions:
1. Is an optional parameter the right way to support this? Would it be better
to have a separate expression, or use 're:' and 'glob:' prefixes on the
tag name parameter instead?
2. Is it OK to use the python fnmatch module for glob patterns? Mercurial's
match module seems much more geared towards filename matching; even the
_globre function treats path separators specially.
3. Patterns must match from the start of the tag name. I think this would be
the least surprising behaviour, but perhaps others may disagree.
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -6,6 +6,7 @@
# GNU General Public License version 2 or any later version.
import re
+import fnmatch
import parser, util, error, discovery, hbisect, phases
import node
import bookmarks as bookmarksmod
@@ -1120,19 +1121,42 @@
return [e[-1] for e in l]
def tag(repo, subset, x):
- """``tag([name])``
+ """``tag([name[, patterntype]])``
The specified tag by name, or all tagged revisions if no name is given.
+
+ If patterntype is passed, it may be one of 're' or 'glob'. Revisions
+ with tags matching the pattern will be returned.
"""
# i18n: "tag" is a keyword
- args = getargs(x, 0, 1, _("tag takes one or no arguments"))
+ args = getargs(x, 0, 2, _("tag takes at most 2 arguments"))
cl = repo.changelog
if args:
tn = getstring(args[0],
# i18n: "tag" is a keyword
_('the argument to tag must be a string'))
- if not repo.tags().get(tn, None):
- raise util.Abort(_("tag '%s' does not exist") % tn)
- s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
+ if len(args) == 1:
+ if not repo.tags().get(tn, None):
+ raise util.Abort(_("tag '%s' does not exist") % tn)
+ s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
+ else:
+ # i18n: "tag" is a keyword
+ typemsg = _("the second argument to tag must be 're' or 'glob'")
+ patterntype = getstring(args[1], typemsg)
+ if patterntype == 're':
+ try:
+ regexp = re.compile(tn)
+ except re.error, e:
+ raise error.ParseError(_("invalid match pattern: %s") % e)
+ elif patterntype == 'glob':
+ regexp = re.compile(fnmatch.translate(tn))
+ else:
+ raise util.Abort(typemsg)
+ s = set()
+ for t, n in repo.tagslist():
+ if regexp.match(t):
+ s.add(cl.rev(n))
+ if not s:
+ raise util.Abort(_('no tags match pattern "%s"') % tn)
else:
s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
return [r for r in subset if r in s]
diff --git a/tests/test-revset.t b/tests/test-revset.t
--- a/tests/test-revset.t
+++ b/tests/test-revset.t
@@ -369,6 +369,16 @@
6
$ log 'tag(tip)'
9
+ $ log 'tag("1.*", "glob")'
+ 6
+ $ log 'tag("0.*", "glob")'
+ abort: no tags match pattern "0.*"
+ [255]
+ $ log 'tag("foo", "re")'
+ abort: no tags match pattern "foo"
+ [255]
+ $ log 'tag("[0-9].[0-9]", "re")'
+ 6
$ log 'tag(unknown)'
abort: tag 'unknown' does not exist
[255]
More information about the Mercurial-devel
mailing list