##// END OF EJS Templates
hg: use capable instead of capabilities
hg: use capable instead of capabilities

File last commit:

r14509:4f695345 merge default
r14552:3417954c default
Show More
revset.py
1029 lines | 30.6 KiB | text/x-python | PythonLexer
Matt Mackall
revset: introduce revset core
r11275 # revset.py - revision set queries for mercurial
#
# Copyright 2010 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
import re
Matt Mackall
help: consolidate topic hooks in help.py...
r14318 import parser, util, error, discovery, hbisect
Matt Mackall
bookmarks: move revset support to core
r13359 import bookmarks as bookmarksmod
Martin Geisler
Consistently import foo as foomod when foo to avoid shadowing...
r12085 import match as matchmod
Patrick Mezard
help: extract items doc generation function
r13593 from i18n import _
Matt Mackall
revset: introduce revset core
r11275
elements = {
"(": (20, ("group", 1, ")"), ("func", 1, ")")),
Kevin Gessner
revset: add ^ and ~ operators from parentrevspec extension...
r14070 "~": (18, None, ("ancestor", 18)),
"^": (18, None, ("parent", 18), ("parentpost", 18)),
Matt Mackall
revset: lower precedence of minus infix (issue2361)
r12616 "-": (5, ("negate", 19), ("minus", 5)),
Matt Mackall
revset: add support for prefix and suffix versions of : and ::
r11278 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
("dagrangepost", 17)),
"..": (17, ("dagrangepre", 17), ("dagrange", 17),
("dagrangepost", 17)),
":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
Matt Mackall
revset: introduce revset core
r11275 "not": (10, ("not", 10)),
"!": (10, ("not", 10)),
"and": (5, None, ("and", 5)),
"&": (5, None, ("and", 5)),
"or": (4, None, ("or", 4)),
"|": (4, None, ("or", 4)),
"+": (4, None, ("or", 4)),
",": (2, None, ("list", 2)),
")": (0, None, None),
"symbol": (0, ("symbol",), None),
"string": (0, ("string",), None),
"end": (0, None, None),
}
keywords = set(['and', 'or', 'not'])
def tokenize(program):
pos, l = 0, len(program)
while pos < l:
c = program[pos]
if c.isspace(): # skip inter-token whitespace
pass
Matt Mackall
revset: add support for prefix and suffix versions of : and ::
r11278 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
Matt Mackall
revset: raise ParseError exceptions
r11289 yield ('::', None, pos)
Matt Mackall
revset: add support for prefix and suffix versions of : and ::
r11278 pos += 1 # skip ahead
Matt Mackall
revset: introduce revset core
r11275 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
Matt Mackall
revset: raise ParseError exceptions
r11289 yield ('..', None, pos)
Matt Mackall
revset: introduce revset core
r11275 pos += 1 # skip ahead
Kevin Gessner
revset: add ^ and ~ operators from parentrevspec extension...
r14070 elif c in "():,-|&+!~^": # handle simple operators
Matt Mackall
revset: raise ParseError exceptions
r11289 yield (c, None, pos)
Brodie Rao
revset: support raw string literals...
r12408 elif (c in '"\'' or c == 'r' and
program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
if c == 'r':
pos += 1
c = program[pos]
decode = lambda x: x
else:
decode = lambda x: x.decode('string-escape')
Matt Mackall
revset: introduce revset core
r11275 pos += 1
s = pos
while pos < l: # find closing quote
d = program[pos]
if d == '\\': # skip over escaped characters
pos += 2
continue
if d == c:
Brodie Rao
revset: support raw string literals...
r12408 yield ('string', decode(program[s:pos]), s)
Matt Mackall
revset: introduce revset core
r11275 break
pos += 1
else:
Martin Geisler
revset: all your error messages are belong to _
r11383 raise error.ParseError(_("unterminated string"), s)
Matt Mackall
revset: allow extended characters in symbols
r11404 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
Matt Mackall
revset: introduce revset core
r11275 s = pos
pos += 1
while pos < l: # find end of symbol
d = program[pos]
Matt Mackall
revset: allow extended characters in symbols
r11404 if not (d.isalnum() or d in "._" or ord(d) > 127):
Matt Mackall
revset: introduce revset core
r11275 break
if d == '.' and program[pos - 1] == '.': # special case for ..
pos -= 1
break
pos += 1
sym = program[s:pos]
if sym in keywords: # operator keywords
Matt Mackall
revset: raise ParseError exceptions
r11289 yield (sym, None, s)
Matt Mackall
revset: introduce revset core
r11275 else:
Matt Mackall
revset: raise ParseError exceptions
r11289 yield ('symbol', sym, s)
Matt Mackall
revset: introduce revset core
r11275 pos -= 1
else:
Martin Geisler
revset: all your error messages are belong to _
r11383 raise error.ParseError(_("syntax error"), pos)
Matt Mackall
revset: introduce revset core
r11275 pos += 1
Matt Mackall
revset: raise ParseError exceptions
r11289 yield ('end', None, pos)
Matt Mackall
revset: introduce revset core
r11275
# helpers
def getstring(x, err):
Matt Mackall
revset: fix up contains/getstring when no args passed
r11406 if x and (x[0] == 'string' or x[0] == 'symbol'):
Matt Mackall
revset: introduce revset core
r11275 return x[1]
Matt Mackall
revset: raise ParseError exceptions
r11289 raise error.ParseError(err)
Matt Mackall
revset: introduce revset core
r11275
def getlist(x):
if not x:
return []
if x[0] == 'list':
return getlist(x[1]) + [x[2]]
return [x]
Matt Mackall
revset: improve filter argument handling
r11339 def getargs(x, min, max, err):
Matt Mackall
revset: introduce revset core
r11275 l = getlist(x)
Matt Mackall
revset: improve filter argument handling
r11339 if len(l) < min or len(l) > max:
Matt Mackall
revset: raise ParseError exceptions
r11289 raise error.ParseError(err)
Matt Mackall
revset: introduce revset core
r11275 return l
def getset(repo, subset, x):
if not x:
Martin Geisler
revset: all your error messages are belong to _
r11383 raise error.ParseError(_("missing argument"))
Matt Mackall
revset: introduce revset core
r11275 return methods[x[0]](repo, subset, *x[1:])
# operator methods
def stringset(repo, subset, x):
x = repo[x].rev()
Matt Mackall
revset: fix up tests
r11282 if x == -1 and len(subset) == len(repo):
return [-1]
Idan Kamara
revset: optimize stringset when subset == entire repo...
r13938 if len(subset) == len(repo) or x in subset:
Matt Mackall
revset: introduce revset core
r11275 return [x]
return []
def symbolset(repo, subset, x):
if x in symbols:
Martin Geisler
revset: all your error messages are belong to _
r11383 raise error.ParseError(_("can't use %s here") % x)
Matt Mackall
revset: introduce revset core
r11275 return stringset(repo, subset, x)
def rangeset(repo, subset, x, y):
Matt Mackall
revset: deal with empty sets in range endpoints...
r11456 m = getset(repo, subset, x)
if not m:
m = getset(repo, range(len(repo)), x)
n = getset(repo, subset, y)
if not n:
n = getset(repo, range(len(repo)), y)
if not m or not n:
return []
m, n = m[0], n[-1]
Matt Mackall
revset: introduce revset core
r11275 if m < n:
Matt Mackall
revset: deal with empty sets in range endpoints...
r11456 r = range(m, n + 1)
else:
r = range(m, n - 1, -1)
s = set(subset)
return [x for x in r if x in s]
Matt Mackall
revset: introduce revset core
r11275
def andset(repo, subset, x, y):
return getset(repo, getset(repo, subset, x), y)
def orset(repo, subset, x, y):
Augie Fackler
revsets: preserve ordering with the or operator...
r13932 xl = getset(repo, subset, x)
s = set(xl)
yl = getset(repo, [r for r in subset if r not in s], y)
return xl + yl
Matt Mackall
revset: introduce revset core
r11275
def notset(repo, subset, x):
s = set(getset(repo, subset, x))
return [r for r in subset if r not in s]
def listset(repo, subset, a, b):
Martin Geisler
revset: all your error messages are belong to _
r11383 raise error.ParseError(_("can't use a list in this context"))
Matt Mackall
revset: introduce revset core
r11275
def func(repo, subset, a, b):
if a[0] == 'symbol' and a[1] in symbols:
return symbols[a[1]](repo, subset, b)
Martin Geisler
revset: all your error messages are belong to _
r11383 raise error.ParseError(_("not a function: %s") % a[1])
Matt Mackall
revset: introduce revset core
r11275
# functions
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 def adds(repo, subset, x):
"""``adds(pattern)``
Changesets that add a file matching pattern.
"""
# i18n: "adds" is a keyword
pat = getstring(x, _("adds requires a pattern"))
return checkstatus(repo, subset, pat, 1)
def ancestor(repo, subset, x):
"""``ancestor(single, single)``
Greatest common ancestor of the two changesets.
"""
# i18n: "ancestor" is a keyword
l = getargs(x, 2, 2, _("ancestor requires two arguments"))
r = range(len(repo))
a = getset(repo, r, l[0])
b = getset(repo, r, l[1])
if len(a) != 1 or len(b) != 1:
# i18n: "ancestor" is a keyword
raise error.ParseError(_("ancestor arguments must be single revisions"))
an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
return [r for r in an if r in subset]
def ancestors(repo, subset, x):
"""``ancestors(set)``
Changesets that are ancestors of a changeset in set.
"""
args = getset(repo, range(len(repo)), x)
if not args:
return []
s = set(repo.changelog.ancestors(*args)) | set(args)
return [r for r in subset if r in s]
Kevin Gessner
revset: add ^ and ~ operators from parentrevspec extension...
r14070 def ancestorspec(repo, subset, x, n):
"""``set~n``
Changesets that are the Nth ancestor (first parents only) of a changeset in set.
"""
try:
n = int(n[1])
except ValueError:
raise error.ParseError(_("~ expects a number"))
ps = set()
cl = repo.changelog
for r in getset(repo, subset, x):
for i in range(n):
r = cl.parentrevs(r)[0]
ps.add(r)
return [r for r in subset if r in ps]
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 def author(repo, subset, x):
"""``author(string)``
Alias for ``user(string)``.
"""
# i18n: "author" is a keyword
n = getstring(x, _("author requires a string")).lower()
return [r for r in subset if n in repo[r].user().lower()]
def bisected(repo, subset, x):
"""``bisected(string)``
Changesets marked in the specified bisect state (good, bad, skip).
"""
state = getstring(x, _("bisect requires a string")).lower()
if state not in ('good', 'bad', 'skip', 'unknown'):
Brodie Rao
revset: fix undefined name ParseError
r14057 raise error.ParseError(_('invalid bisect state'))
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
return [r for r in subset if r in marked]
def bookmark(repo, subset, x):
"""``bookmark([name])``
The named bookmark or all bookmarks.
"""
# i18n: "bookmark" is a keyword
args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
if args:
bm = getstring(args[0],
# i18n: "bookmark" is a keyword
_('the argument to bookmark must be a string'))
bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
if not bmrev:
raise util.Abort(_("bookmark '%s' does not exist") % bm)
bmrev = repo[bmrev].rev()
return [r for r in subset if r == bmrev]
bms = set([repo[r].rev()
for r in bookmarksmod.listbookmarks(repo).values()])
return [r for r in subset if r in bms]
def branch(repo, subset, x):
"""``branch(string or set)``
All changesets belonging to the given branch or the branches of the given
changesets.
"""
try:
b = getstring(x, '')
if b in repo.branchmap():
return [r for r in subset if repo[r].branch() == b]
except error.ParseError:
# not a string, but another revspec, e.g. tip()
pass
s = getset(repo, range(len(repo)), x)
b = set()
for r in s:
b.add(repo[r].branch())
s = set(s)
return [r for r in subset if r in s or repo[r].branch() in b]
def checkstatus(repo, subset, pat, field):
m = matchmod.match(repo.root, repo.getcwd(), [pat])
s = []
fast = (m.files() == [pat])
for r in subset:
c = repo[r]
if fast:
if pat not in c.files():
continue
else:
for f in c.files():
if m(f):
break
else:
continue
files = repo.status(c.p1().node(), c.node())[field]
if fast:
if pat in files:
s.append(r)
else:
for f in files:
if m(f):
s.append(r)
break
return s
def children(repo, subset, x):
"""``children(set)``
Child changesets of changesets in set.
"""
cs = set()
cl = repo.changelog
s = set(getset(repo, range(len(repo)), x))
for r in xrange(0, len(repo)):
for p in cl.parentrevs(r):
if p in s:
cs.add(r)
return [r for r in subset if r in cs]
def closed(repo, subset, x):
"""``closed()``
Changeset is closed.
"""
# i18n: "closed" is a keyword
getargs(x, 0, 0, _("closed takes no arguments"))
return [r for r in subset if repo[r].extra().get('close')]
def contains(repo, subset, x):
"""``contains(pattern)``
Martin Geisler
merge with stable
r14357 Revision contains a file matching pattern. See :hg:`help patterns`
for information about file patterns.
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 """
# i18n: "contains" is a keyword
pat = getstring(x, _("contains requires a pattern"))
m = matchmod.match(repo.root, repo.getcwd(), [pat])
s = []
if m.files() == [pat]:
for r in subset:
if pat in repo[r]:
s.append(r)
else:
for r in subset:
for f in repo[r].manifest():
if m(f):
s.append(r)
break
return s
def date(repo, subset, x):
"""``date(interval)``
Changesets within the interval, see :hg:`help dates`.
"""
# i18n: "date" is a keyword
ds = getstring(x, _("date requires a string"))
dm = util.matchdate(ds)
return [r for r in subset if dm(repo[r].date()[0])]
def descendants(repo, subset, x):
"""``descendants(set)``
Changesets which are descendants of changesets in set.
"""
args = getset(repo, range(len(repo)), x)
if not args:
return []
s = set(repo.changelog.descendants(*args)) | set(args)
return [r for r in subset if r in s]
Matt Mackall
revset: introduce filelog() to emulate log's fast path...
r14342 def filelog(repo, subset, x):
"""``filelog(pattern)``
Changesets connected to the specified filelog.
"""
pat = getstring(x, _("filelog requires a pattern"))
m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath')
s = set()
if not m.anypats():
for f in m.files():
fl = repo.file(f)
for fr in fl:
s.add(fl.linkrev(fr))
else:
for f in repo[None]:
if m(f):
fl = repo.file(f)
for fr in fl:
s.add(fl.linkrev(fr))
return [r for r in subset if r in s]
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 def follow(repo, subset, x):
Matt Mackall
revset: add follow(filename) to follow a filename's history across copies
r14343 """``follow([file])``
An alias for ``::.`` (ancestors of the working copy's first parent).
If a filename is specified, the history of the given file is followed,
including copies.
"""
# i18n: "follow" is a keyword
l = getargs(x, 0, 1, _("follow takes no arguments or a filename"))
p = repo['.'].rev()
if l:
x = getstring(l[0], "follow expected a filename")
s = set(ctx.rev() for ctx in repo['.'][x].ancestors())
else:
s = set(repo.changelog.ancestors(p))
s |= set([p])
return [r for r in subset if r in s]
def followfile(repo, subset, f):
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 """``follow()``
An alias for ``::.`` (ancestors of the working copy's first parent).
"""
# i18n: "follow" is a keyword
getargs(x, 0, 0, _("follow takes no arguments"))
p = repo['.'].rev()
s = set(repo.changelog.ancestors(p)) | set([p])
return [r for r in subset if r in s]
def getall(repo, subset, x):
"""``all()``
All changesets, the same as ``0:tip``.
"""
# i18n: "all" is a keyword
getargs(x, 0, 0, _("all takes no arguments"))
return subset
def grep(repo, subset, x):
"""``grep(regex)``
Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
Martin Geisler
merge with stable
r14357 to ensure special escape characters are handled correctly. Unlike
``keyword(string)``, the match is case-sensitive.
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 """
try:
# i18n: "grep" is a keyword
gr = re.compile(getstring(x, _("grep requires a string")))
except re.error, e:
raise error.ParseError(_('invalid match pattern: %s') % e)
l = []
for r in subset:
c = repo[r]
for e in c.files() + [c.user(), c.description()]:
if gr.search(e):
l.append(r)
break
return l
def hasfile(repo, subset, x):
"""``file(pattern)``
Changesets affecting files matched by pattern.
"""
# i18n: "file" is a keyword
pat = getstring(x, _("file requires a pattern"))
m = matchmod.match(repo.root, repo.getcwd(), [pat])
s = []
for r in subset:
for f in repo[r].files():
if m(f):
s.append(r)
break
return s
def head(repo, subset, x):
"""``head()``
Changeset is a named branch head.
"""
# i18n: "head" is a keyword
getargs(x, 0, 0, _("head takes no arguments"))
hs = set()
for b, ls in repo.branchmap().iteritems():
hs.update(repo[h].rev() for h in ls)
return [r for r in subset if r in hs]
def heads(repo, subset, x):
"""``heads(set)``
Members of set with no children in set.
"""
s = getset(repo, subset, x)
ps = set(parents(repo, subset, x))
return [r for r in s if r not in ps]
def keyword(repo, subset, x):
"""``keyword(string)``
Search commit message, user name, and names of changed files for
Martin Geisler
merge with stable
r14357 string. The match is case-insensitive.
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 """
# i18n: "keyword" is a keyword
kw = getstring(x, _("keyword requires a string")).lower()
l = []
for r in subset:
c = repo[r]
t = " ".join(c.files() + [c.user(), c.description()])
if kw in t.lower():
l.append(r)
return l
def limit(repo, subset, x):
"""``limit(set, n)``
First n members of set.
"""
# i18n: "limit" is a keyword
l = getargs(x, 2, 2, _("limit requires two arguments"))
try:
# i18n: "limit" is a keyword
lim = int(getstring(l[1], _("limit requires a number")))
except ValueError:
# i18n: "limit" is a keyword
raise error.ParseError(_("limit expects a number"))
Mads Kiilerich
revset: avoid over-aggresive optimizations of non-filtering functions (issue2549)...
r14153 ss = set(subset)
os = getset(repo, range(len(repo)), l[0])[:lim]
return [r for r in os if r in ss]
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915
Matt Mackall
revsets: add a last function...
r14061 def last(repo, subset, x):
"""``last(set, n)``
Last n members of set.
"""
# i18n: "last" is a keyword
l = getargs(x, 2, 2, _("last requires two arguments"))
try:
# i18n: "last" is a keyword
lim = int(getstring(l[1], _("last requires a number")))
except ValueError:
# i18n: "last" is a keyword
raise error.ParseError(_("last expects a number"))
Mads Kiilerich
revset: avoid over-aggresive optimizations of non-filtering functions (issue2549)...
r14153 ss = set(subset)
os = getset(repo, range(len(repo)), l[0])[-lim:]
return [r for r in os if r in ss]
Matt Mackall
revsets: add a last function...
r14061
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 def maxrev(repo, subset, x):
"""``max(set)``
Changeset with highest revision number in set.
"""
Mads Kiilerich
revset: avoid over-aggresive optimizations of non-filtering functions (issue2549)...
r14153 os = getset(repo, range(len(repo)), x)
if os:
m = max(os)
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 if m in subset:
return [m]
return []
def merge(repo, subset, x):
"""``merge()``
Changeset is a merge changeset.
"""
# i18n: "merge" is a keyword
getargs(x, 0, 0, _("merge takes no arguments"))
cl = repo.changelog
return [r for r in subset if cl.parentrevs(r)[1] != -1]
def minrev(repo, subset, x):
"""``min(set)``
Changeset with lowest revision number in set.
"""
Mads Kiilerich
revset: avoid over-aggresive optimizations of non-filtering functions (issue2549)...
r14153 os = getset(repo, range(len(repo)), x)
if os:
m = min(os)
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 if m in subset:
return [m]
return []
def modifies(repo, subset, x):
"""``modifies(pattern)``
Changesets modifying files matched by pattern.
"""
# i18n: "modifies" is a keyword
pat = getstring(x, _("modifies requires a pattern"))
return checkstatus(repo, subset, pat, 0)
Augie Fackler
revset: add id() and rev() to allow explicitly referring to changes by hash or rev
r12716 def node(repo, subset, x):
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """``id(string)``
Wagner Bruna
revset: fix missing dot in docstring
r12859 Revision non-ambiguously specified by the given hex string prefix.
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """
Martin Geisler
revset: add translator comments to i18n strings
r12815 # i18n: "id" is a keyword
Benoit Boissinot
revset: use 'requires' instead of 'wants' in error message
r12736 l = getargs(x, 1, 1, _("id requires one argument"))
Martin Geisler
revset: add translator comments to i18n strings
r12815 # i18n: "id" is a keyword
Benoit Boissinot
revset: use 'requires' instead of 'wants' in error message
r12736 n = getstring(l[0], _("id requires a string"))
Augie Fackler
revset: add id() and rev() to allow explicitly referring to changes by hash or rev
r12716 if len(n) == 40:
rn = repo[n].rev()
else:
rn = repo.changelog.rev(repo.changelog._partialmatch(n))
return [r for r in subset if r == rn]
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 def outgoing(repo, subset, x):
"""``outgoing([path])``
Changesets not found in the specified destination repository, or the
default push location.
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 import hg # avoid start-up nasties
# i18n: "outgoing" is a keyword
l = getargs(x, 0, 1, _("outgoing requires a repository path"))
# i18n: "outgoing" is a keyword
dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
dest, branches = hg.parseurl(dest)
revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
if revs:
revs = [repo.lookup(rev) for rev in revs]
other = hg.repository(hg.remoteui(repo, {}), dest)
repo.ui.pushbuffer()
Peter Arrenbrecht
discovery: resurrect findoutgoing as findcommonoutgoing for extension hooks...
r14213 common, outheads = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 repo.ui.popbuffer()
cl = repo.changelog
Peter Arrenbrecht
discovery: resurrect findoutgoing as findcommonoutgoing for extension hooks...
r14213 o = set([cl.rev(r) for r in repo.changelog.findmissing(common, outheads)])
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 return [r for r in subset if r in o]
Augie Fackler
revset: add id() and rev() to allow explicitly referring to changes by hash or rev
r12716
Matt Mackall
revset: introduce revset core
r11275 def p1(repo, subset, x):
Kevin Bullock
revsets: let p1() and p2() return parents of working dir...
r12928 """``p1([set])``
First parent of changesets in set, or the working directory.
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """
Kevin Bullock
revsets: let p1() and p2() return parents of working dir...
r12928 if x is None:
Matt Mackall
misc: replace .parents()[0] with p1()
r13878 p = repo[x].p1().rev()
Patrick Mezard
revset: fix p1, p2 and parents in dirstate case (a5f7f1e9340e)...
r12935 return [r for r in subset if r == p]
Kevin Bullock
revsets: let p1() and p2() return parents of working dir...
r12928
Matt Mackall
revset: introduce revset core
r11275 ps = set()
cl = repo.changelog
Wagner Bruna
revset: disable subset optimization for parents() and children() (issue2437)...
r12786 for r in getset(repo, range(len(repo)), x):
Matt Mackall
revset: introduce revset core
r11275 ps.add(cl.parentrevs(r)[0])
return [r for r in subset if r in ps]
def p2(repo, subset, x):
Kevin Bullock
revsets: let p1() and p2() return parents of working dir...
r12928 """``p2([set])``
Second parent of changesets in set, or the working directory.
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """
Kevin Bullock
revsets: let p1() and p2() return parents of working dir...
r12928 if x is None:
ps = repo[x].parents()
try:
Patrick Mezard
revset: fix p1, p2 and parents in dirstate case (a5f7f1e9340e)...
r12935 p = ps[1].rev()
return [r for r in subset if r == p]
Kevin Bullock
revsets: let p1() and p2() return parents of working dir...
r12928 except IndexError:
return []
Matt Mackall
revset: introduce revset core
r11275 ps = set()
cl = repo.changelog
Wagner Bruna
revset: disable subset optimization for parents() and children() (issue2437)...
r12786 for r in getset(repo, range(len(repo)), x):
Matt Mackall
revset: introduce revset core
r11275 ps.add(cl.parentrevs(r)[1])
return [r for r in subset if r in ps]
def parents(repo, subset, x):
Kevin Bullock
revsets: let parents() return parents of working dir...
r12929 """``parents([set])``
The set of all parents for all changesets in set, or the working directory.
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """
Kevin Bullock
revsets: let parents() return parents of working dir...
r12929 if x is None:
Patrick Mezard
revset: fix p1, p2 and parents in dirstate case (a5f7f1e9340e)...
r12935 ps = tuple(p.rev() for p in repo[x].parents())
return [r for r in subset if r in ps]
Kevin Bullock
revsets: let parents() return parents of working dir...
r12929
Matt Mackall
revset: introduce revset core
r11275 ps = set()
cl = repo.changelog
Wagner Bruna
revset: disable subset optimization for parents() and children() (issue2437)...
r12786 for r in getset(repo, range(len(repo)), x):
Matt Mackall
revset: introduce revset core
r11275 ps.update(cl.parentrevs(r))
return [r for r in subset if r in ps]
Kevin Gessner
revset: add ^ and ~ operators from parentrevspec extension...
r14070 def parentspec(repo, subset, x, n):
"""``set^0``
The set.
``set^1`` (or ``set^``), ``set^2``
First or second parent, respectively, of all changesets in set.
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """
Brodie Rao
revset: handle re.compile() errors in grep()...
r12320 try:
Kevin Gessner
revset: add ^ and ~ operators from parentrevspec extension...
r14070 n = int(n[1])
Kevin Gessner
revset: add missing whitespace
r14072 if n not in (0, 1, 2):
Kevin Gessner
revset: add ^ and ~ operators from parentrevspec extension...
r14070 raise ValueError
except ValueError:
raise error.ParseError(_("^ expects a number 0, 1, or 2"))
ps = set()
Matt Mackall
revset: introduce revset core
r11275 cl = repo.changelog
Kevin Gessner
revset: add ^ and ~ operators from parentrevspec extension...
r14070 for r in getset(repo, subset, x):
if n == 0:
ps.add(r)
elif n == 1:
ps.add(cl.parentrevs(r)[0])
elif n == 2:
parents = cl.parentrevs(r)
if len(parents) > 1:
ps.add(parents[1])
return [r for r in subset if r in ps]
Matt Mackall
revset: introduce revset core
r11275
Wagner Bruna
revset: predicate to avoid lookup errors...
r11944 def present(repo, subset, x):
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """``present(set)``
An empty set, if any revision in set isn't found; otherwise,
all revisions in set.
"""
Wagner Bruna
revset: predicate to avoid lookup errors...
r11944 try:
return getset(repo, subset, x)
except error.RepoLookupError:
return []
Matt Mackall
revset: introduce revset core
r11275 def removes(repo, subset, x):
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """``removes(pattern)``
Changesets which remove files matching pattern.
"""
Martin Geisler
revset: add translator comments to i18n strings
r12815 # i18n: "removes" is a keyword
Benoit Boissinot
revset: use 'requires' instead of 'wants' in error message
r12736 pat = getstring(x, _("removes requires a pattern"))
Matt Mackall
revset: introduce revset core
r11275 return checkstatus(repo, subset, pat, 2)
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 def rev(repo, subset, x):
"""``rev(number)``
Revision with the given numeric identifier.
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 # i18n: "rev" is a keyword
l = getargs(x, 1, 1, _("rev requires one argument"))
try:
# i18n: "rev" is a keyword
l = int(getstring(l[0], _("rev requires a number")))
except ValueError:
# i18n: "rev" is a keyword
raise error.ParseError(_("rev expects a number"))
return [r for r in subset if r == l]
Matt Mackall
revset: introduce revset core
r11275
def reverse(repo, subset, x):
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """``reverse(set)``
Reverse order of set.
"""
Matt Mackall
revset: introduce revset core
r11275 l = getset(repo, subset, x)
l.reverse()
return l
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 def roots(repo, subset, x):
"""``roots(set)``
Changesets with no parent changeset in set.
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 s = getset(repo, subset, x)
cs = set(children(repo, subset, x))
return [r for r in s if r not in cs]
Wagner Bruna
revset: predicate to avoid lookup errors...
r11944
Matt Mackall
revset: introduce revset core
r11275 def sort(repo, subset, x):
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """``sort(set[, [-]key...])``
Sort set by keys. The default sort order is ascending, specify a key
as ``-key`` to sort in descending order.
The keys can be:
- ``rev`` for the revision number,
- ``branch`` for the branch name,
- ``desc`` for the commit message (description),
- ``user`` for user name (``author`` can be used as an alias),
- ``date`` for the commit date
"""
Martin Geisler
revset: add translator comments to i18n strings
r12815 # i18n: "sort" is a keyword
Benoit Boissinot
revset: use 'requires' instead of 'wants' in error message
r12736 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
Matt Mackall
revset: introduce revset core
r11275 keys = "rev"
if len(l) == 2:
Martin Geisler
revset: all your error messages are belong to _
r11383 keys = getstring(l[1], _("sort spec must be a string"))
Matt Mackall
revset: introduce revset core
r11275
s = l[0]
keys = keys.split()
l = []
def invert(s):
return "".join(chr(255 - ord(c)) for c in s)
for r in getset(repo, subset, s):
c = repo[r]
e = []
for k in keys:
if k == 'rev':
e.append(r)
elif k == '-rev':
e.append(-r)
elif k == 'branch':
e.append(c.branch())
elif k == '-branch':
e.append(invert(c.branch()))
elif k == 'desc':
e.append(c.description())
elif k == '-desc':
e.append(invert(c.description()))
elif k in 'user author':
e.append(c.user())
elif k in '-user -author':
e.append(invert(c.user()))
elif k == 'date':
e.append(c.date()[0])
elif k == '-date':
e.append(-c.date()[0])
else:
Martin Geisler
revset: all your error messages are belong to _
r11383 raise error.ParseError(_("unknown sort key %r") % k)
Matt Mackall
revset: introduce revset core
r11275 e.append(r)
l.append(e)
l.sort()
return [e[-1] for e in l]
Augie Fackler
revset: rename tagged() to tag() and allow it to take an optional tag name
r12715 def tag(repo, subset, x):
Martin Geisler
revset: the name is optional for the tag predicate
r14356 """``tag([name])``
Patrick Mezard
revsets: generate predicate help dynamically
r12821 The specified tag by name, or all tagged revisions if no name is given.
"""
Martin Geisler
revset: add translator comments to i18n strings
r12815 # i18n: "tag" is a keyword
Augie Fackler
revset: rename tagged() to tag() and allow it to take an optional tag name
r12715 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
Matt Mackall
revset: add tagged predicate
r11280 cl = repo.changelog
Augie Fackler
revset: rename tagged() to tag() and allow it to take an optional tag name
r12715 if args:
tn = getstring(args[0],
Martin Geisler
revset: add translator comments to i18n strings
r12815 # i18n: "tag" is a keyword
Augie Fackler
revset: rename tagged() to tag() and allow it to take an optional tag name
r12715 _('the argument to tag must be a string'))
Idan Kamara
revset: abort when tag or bookmark doesn't exist
r13914 if not repo.tags().get(tn, None):
raise util.Abort(_("tag '%s' does not exist") % tn)
Augie Fackler
revset: rename tagged() to tag() and allow it to take an optional tag name
r12715 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
else:
s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
Matt Mackall
revset: add tagged predicate
r11280 return [r for r in subset if r in s]
Patrick Mezard
revsets: generate predicate help dynamically
r12821 def tagged(repo, subset, x):
return tag(repo, subset, x)
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 def user(repo, subset, x):
"""``user(string)``
Martin Geisler
merge with stable
r14357 User name contains string. The match is case-insensitive.
Matt Mackall
bookmarks: move revset support to core
r13359 """
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 return author(repo, subset, x)
Matt Mackall
bookmarks: move revset support to core
r13359
Matt Mackall
revset: introduce revset core
r11275 symbols = {
Matt Mackall
revset: sort the predicate list
r11284 "adds": adds,
"all": getall,
Matt Mackall
revset: introduce revset core
r11275 "ancestor": ancestor,
"ancestors": ancestors,
Matt Mackall
revset: sort the predicate list
r11284 "author": author,
Benoit Boissinot
revset: add a revset command to get bisect state.
r13602 "bisected": bisected,
Matt Mackall
bookmarks: move revset support to core
r13359 "bookmark": bookmark,
Matt Mackall
revset: introduce revset core
r11275 "branch": branch,
Matt Mackall
revset: sort the predicate list
r11284 "children": children,
"closed": closed,
"contains": contains,
"date": date,
"descendants": descendants,
"file": hasfile,
Matt Mackall
revset: introduce filelog() to emulate log's fast path...
r14342 "filelog": filelog,
Matt Mackall
revset: sort the predicate list
r11284 "follow": follow,
"grep": grep,
"head": head,
"heads": heads,
Matt Mackall
revset: introduce revset core
r11275 "keyword": keyword,
Matt Mackall
revsets: add a last function...
r14061 "last": last,
Matt Mackall
revset: sort the predicate list
r11284 "limit": limit,
"max": maxrev,
Nicolas Dumazet
revset: add min function
r11708 "min": minrev,
Matt Mackall
revset: sort the predicate list
r11284 "merge": merge,
"modifies": modifies,
Augie Fackler
revset: add id() and rev() to allow explicitly referring to changes by hash or rev
r12716 "id": node,
Matt Mackall
revset: sort the predicate list
r11284 "outgoing": outgoing,
Matt Mackall
revset: introduce revset core
r11275 "p1": p1,
"p2": p2,
"parents": parents,
Wagner Bruna
revset: predicate to avoid lookup errors...
r11944 "present": present,
Matt Mackall
revset: sort the predicate list
r11284 "removes": removes,
"reverse": reverse,
Augie Fackler
revset: add id() and rev() to allow explicitly referring to changes by hash or rev
r12716 "rev": rev,
Matt Mackall
revset: introduce revset core
r11275 "roots": roots,
Matt Mackall
revset: sort the predicate list
r11284 "sort": sort,
Augie Fackler
revset: rename tagged() to tag() and allow it to take an optional tag name
r12715 "tag": tag,
Patrick Mezard
revsets: generate predicate help dynamically
r12821 "tagged": tagged,
"user": user,
Matt Mackall
revset: introduce revset core
r11275 }
methods = {
"range": rangeset,
"string": stringset,
"symbol": symbolset,
"and": andset,
"or": orset,
"not": notset,
"list": listset,
"func": func,
Kevin Gessner
revset: add ^ and ~ operators from parentrevspec extension...
r14070 "ancestor": ancestorspec,
"parent": parentspec,
"parentpost": p1,
Matt Mackall
revset: introduce revset core
r11275 }
Matt Mackall
revset: optimize the parse tree directly...
r11279 def optimize(x, small):
Martin Geisler
code style: prefer 'is' and 'is not' tests with singletons
r13031 if x is None:
Matt Mackall
revset: optimize the parse tree directly...
r11279 return 0, x
Matt Mackall
revset: introduce revset core
r11275 smallbonus = 1
if small:
smallbonus = .5
op = x[0]
Matt Mackall
revset: fix - handling in the optimizer
r11283 if op == 'minus':
Matt Mackall
revset: optimize the parse tree directly...
r11279 return optimize(('and', x[1], ('not', x[2])), small)
elif op == 'dagrange':
return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
('func', ('symbol', 'ancestors'), x[2])), small)
elif op == 'dagrangepre':
return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
elif op == 'dagrangepost':
return optimize(('func', ('symbol', 'descendants'), x[1]), small)
elif op == 'rangepre':
return optimize(('range', ('string', '0'), x[1]), small)
elif op == 'rangepost':
return optimize(('range', x[1], ('string', 'tip')), small)
Matt Mackall
revset: make negate work for sort specs
r11467 elif op == 'negate':
return optimize(('string',
'-' + getstring(x[1], _("can't negate that"))), small)
Matt Mackall
revset: optimize the parse tree directly...
r11279 elif op in 'string symbol negate':
return smallbonus, x # single revisions are small
Matt Mackall
revset: introduce revset core
r11275 elif op == 'and' or op == 'dagrange':
Matt Mackall
revset: optimize the parse tree directly...
r11279 wa, ta = optimize(x[1], True)
wb, tb = optimize(x[2], True)
w = min(wa, wb)
if wa > wb:
return w, (op, tb, ta)
return w, (op, ta, tb)
elif op == 'or':
wa, ta = optimize(x[1], False)
wb, tb = optimize(x[2], False)
if wb < wa:
wb, wa = wa, wb
return max(wa, wb), (op, ta, tb)
Matt Mackall
revset: introduce revset core
r11275 elif op == 'not':
Matt Mackall
revset: optimize the parse tree directly...
r11279 o = optimize(x[1], not small)
return o[0], (op, o[1])
Kevin Gessner
revset: add ^ and ~ operators from parentrevspec extension...
r14070 elif op == 'parentpost':
o = optimize(x[1], small)
return o[0], (op, o[1])
Matt Mackall
revset: introduce revset core
r11275 elif op == 'group':
Matt Mackall
revset: optimize the parse tree directly...
r11279 return optimize(x[1], small)
Kevin Gessner
revset: add ^ and ~ operators from parentrevspec extension...
r14070 elif op in 'range list parent ancestorspec':
Matt Mackall
revset: optimize the parse tree directly...
r11279 wa, ta = optimize(x[1], small)
wb, tb = optimize(x[2], small)
return wa + wb, (op, ta, tb)
Matt Mackall
revset: introduce revset core
r11275 elif op == 'func':
Martin Geisler
revset: all your error messages are belong to _
r11383 f = getstring(x[1], _("not a symbol"))
Matt Mackall
revset: optimize the parse tree directly...
r11279 wa, ta = optimize(x[2], small)
Matt Mackall
revset: teach optimizer that closed is slowish
r13873 if f in "grep date user author keyword branch file outgoing closed":
Matt Mackall
revset: optimize the parse tree directly...
r11279 w = 10 # slow
Matt Mackall
revsets: reduce cost of outgoing in the optimizer
r12351 elif f in "modifies adds removes":
Matt Mackall
revset: optimize the parse tree directly...
r11279 w = 30 # slower
Matt Mackall
revset: introduce revset core
r11275 elif f == "contains":
Matt Mackall
revset: optimize the parse tree directly...
r11279 w = 100 # very slow
Matt Mackall
revset: introduce revset core
r11275 elif f == "ancestor":
Matt Mackall
revset: optimize the parse tree directly...
r11279 w = 1 * smallbonus
Mads Kiilerich
revset: fix typo when assigning weight to reverse and limit
r13506 elif f in "reverse limit":
Matt Mackall
revset: optimize the parse tree directly...
r11279 w = 0
Matt Mackall
revset: introduce revset core
r11275 elif f in "sort":
Matt Mackall
revset: optimize the parse tree directly...
r11279 w = 10 # assume most sorts look at changelog
Matt Mackall
revset: introduce revset core
r11275 else:
Matt Mackall
revset: optimize the parse tree directly...
r11279 w = 1
return w + wa, (op, x[1], ta)
return 1, x
Matt Mackall
revset: introduce revset core
r11275
Alexander Solovyov
revset aliases
r14098 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
Matt Mackall
revset: introduce revset core
r11275 parse = parser.parser(tokenize, elements).parse
Alexander Solovyov
revset aliases
r14098 def match(ui, spec):
Matt Mackall
revset: nicer exception for empty queries
r11385 if not spec:
raise error.ParseError(_("empty query"))
Bernhard Leiner
revset: report a parse error if a revset is not parsed completely (issue2654)
r14496 tree, pos = parse(spec)
if (pos != len(spec)):
raise error.ParseError("invalid token", pos)
Alexander Solovyov
revset aliases
r14098 tree = findaliases(ui, tree)
Matt Mackall
revset: optimize the parse tree directly...
r11279 weight, tree = optimize(tree, True)
Matt Mackall
revset: introduce revset core
r11275 def mfunc(repo, subset):
return getset(repo, subset, tree)
return mfunc
Patrick Mezard
revsets: generate predicate help dynamically
r12821
Patrick Mezard
hggettext: handle i18nfunctions declaration for docstrings translations
r12823 # tell hggettext to extract docstrings from these functions:
i18nfunctions = symbols.values()