##// END OF EJS Templates
revert: add support for reverting subrepos without --no-backup and/or --all...
revert: add support for reverting subrepos without --no-backup and/or --all When a subrepo is reverted but --no-backup is not set, call revert on the subrepo that is being reverted prior to updating it to the revision specified in the parent repo's .hgsubstate file. The --all flag is passed down to the subrepo when it is being reverted. If the --all flag is not set, all files that are modified on the subrepo will be reverted.

File last commit:

r16417:b4b0c693 default
r16430:6883c236 default
Show More
revset.py
1468 lines | 45.4 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
Pierre-Yves David
phases: implements simple revset symbol...
r15819 import parser, util, error, discovery, hbisect, phases
Matt Mackall
revset: avoid demandimport bug...
r16417 import node
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 _
FUJIWARA Katsunori
i18n: use "encoding.lower()" to normalize specified string for revset...
r15726 import encoding
Matt Mackall
revset: introduce revset core
r11275
Patrick Mezard
graphlog: fix --follow-first --rev combinations...
r16409 def _revancestors(repo, revs, followfirst):
"""Like revlog.ancestors(), but supports followfirst."""
cut = followfirst and 1 or None
cl = repo.changelog
visit = list(revs)
Matt Mackall
revset: avoid demandimport bug...
r16417 seen = set([node.nullrev])
Patrick Mezard
graphlog: fix --follow-first --rev combinations...
r16409 while visit:
for parent in cl.parentrevs(visit.pop(0))[:cut]:
if parent not in seen:
visit.append(parent)
seen.add(parent)
yield parent
def _revdescendants(repo, revs, followfirst):
"""Like revlog.descendants() but supports followfirst."""
cut = followfirst and 1 or None
cl = repo.changelog
first = min(revs)
Matt Mackall
revset: avoid demandimport bug...
r16417 nullrev = node.nullrev
if first == nullrev:
Patrick Mezard
graphlog: fix --follow-first --rev combinations...
r16409 # Are there nodes with a null first parent and a non-null
# second one? Maybe. Do we care? Probably not.
for i in cl:
yield i
return
seen = set(revs)
for i in xrange(first + 1, len(cl)):
for x in cl.parentrevs(i)[:cut]:
Matt Mackall
revset: avoid demandimport bug...
r16417 if x != nullrev and x in seen:
Patrick Mezard
graphlog: fix --follow-first --rev combinations...
r16409 seen.add(i)
yield i
break
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 slashes in symbols...
r15949 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)
Patrick Mezard
graphlog: paths/-I/-X handling requires a new revset...
r16161 if len(l) < min or (max >= 0 and 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]
Patrick Mezard
graphlog: fix --follow-first --rev combinations...
r16409 def _ancestors(repo, subset, x, followfirst=False):
args = getset(repo, range(len(repo)), x)
if not args:
return []
s = set(_revancestors(repo, args, followfirst)) | set(args)
return [r for r in subset if r in s]
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 def ancestors(repo, subset, x):
"""``ancestors(set)``
Changesets that are ancestors of a changeset in set.
"""
Patrick Mezard
graphlog: fix --follow-first --rev combinations...
r16409 return _ancestors(repo, subset, x)
def _firstancestors(repo, subset, x):
# ``_firstancestors(set)``
# Like ``ancestors(set)`` but follows only the first parents.
return _ancestors(repo, subset, x, followfirst=True)
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915
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])
Matt Mackall
revsets: actually catch type error on tip^p1(tip) (issue2884)...
r14851 except (TypeError, ValueError):
Kevin Gessner
revset: add ^ and ~ operators from parentrevspec extension...
r14070 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
FUJIWARA Katsunori
i18n: use "encoding.lower()" to normalize specified string for revset...
r15726 n = encoding.lower(getstring(x, _("author requires a string")))
return [r for r in subset if n in encoding.lower(repo[r].user())]
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915
"Yann E. MORIN"
revset: rename bisected() to bisect()...
r15134 def bisect(repo, subset, x):
"""``bisect(string)``
"Yann E. MORIN"
hbisect: add two new revset descriptions: 'goods' and 'bads'...
r15153 Changesets marked in the specified bisect status:
"Yann E. MORIN"
revset.bisect: add new 'range' set to the bisect keyword...
r15136
"Yann E. MORIN"
hbisect: add two new revset descriptions: 'goods' and 'bads'...
r15153 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
- ``goods``, ``bads`` : csets topologicaly good/bad
- ``range`` : csets taking part in the bisection
- ``pruned`` : csets that are goods, bads or skipped
- ``untested`` : csets whose fate is yet unknown
- ``ignored`` : csets ignored due to DAG topology
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 """
"Yann E. MORIN"
revset.bisect: move bisect() code to hbisect.py...
r15135 status = getstring(x, _("bisect requires a string")).lower()
return [r for r in subset if r in hbisect.get(repo, status)]
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915
"Yann E. MORIN"
revset: rename bisected() to bisect()...
r15134 # Backward-compatibility
# - no help entry so that we do not advertise it any more
def bisected(repo, subset, x):
return bisect(repo, subset, x)
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 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):
Matt Mackall
revsets: provide contexts for filesets...
r15964 m = None
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 s = []
Matt Mackall
revsets: provide contexts for filesets...
r15964 fast = not matchmod.patkind(pat)
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 for r in subset:
c = repo[r]
if fast:
if pat not in c.files():
continue
else:
Matt Mackall
revsets: provide contexts for filesets...
r15964 if not m or matchmod.patkind(pat) == 'set':
m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 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
Patrick Mezard
revset: avoid set duplication in roots()
r16396 def _children(repo, narrow, parentset):
Matt Mackall
revset: optimize roots and children
r15899 cs = set()
pr = repo.changelog.parentrevs
Patrick Mezard
revset: do not ignore input revisions in roots()...
r16394 for r in narrow:
Matt Mackall
revset: optimize roots and children
r15899 for p in pr(r):
Patrick Mezard
revset: avoid set duplication in roots()
r16396 if p in parentset:
Matt Mackall
revset: optimize roots and children
r15899 cs.add(r)
return cs
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 def children(repo, subset, x):
"""``children(set)``
Child changesets of changesets in set.
"""
Patrick Mezard
revset: avoid set duplication in roots()
r16396 s = set(getset(repo, range(len(repo)), x))
Matt Mackall
revset: optimize roots and children
r15899 cs = _children(repo, subset, s)
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 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"))
Matt Mackall
revsets: provide contexts for filesets...
r15964 m = None
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 s = []
Matt Mackall
revsets: provide contexts for filesets...
r15964 if not matchmod.patkind(pat):
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 for r in subset:
if pat in repo[r]:
s.append(r)
else:
for r in subset:
Matt Mackall
revsets: provide contexts for filesets...
r15964 c = repo[r]
if not m or matchmod.patkind(pat) == 'set':
m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
for f in c.manifest():
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 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])]
Thomas Arendsen Hein
revset: add desc(string) to search in commit messages...
r14650 def desc(repo, subset, x):
"""``desc(string)``
Search commit message for string. The match is case-insensitive.
"""
# i18n: "desc" is a keyword
FUJIWARA Katsunori
i18n: use "encoding.lower()" to normalize specified string for revset...
r15726 ds = encoding.lower(getstring(x, _("desc requires a string")))
Thomas Arendsen Hein
revset: add desc(string) to search in commit messages...
r14650 l = []
for r in subset:
c = repo[r]
FUJIWARA Katsunori
i18n: use "encoding.lower()" to normalize specified string for revset...
r15726 if ds in encoding.lower(c.description()):
Thomas Arendsen Hein
revset: add desc(string) to search in commit messages...
r14650 l.append(r)
return l
Patrick Mezard
graphlog: fix --follow-first --rev combinations...
r16409 def _descendants(repo, subset, x, followfirst=False):
args = getset(repo, range(len(repo)), x)
if not args:
return []
s = set(_revdescendants(repo, args, followfirst)) | set(args)
return [r for r in subset if r in s]
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 def descendants(repo, subset, x):
"""``descendants(set)``
Changesets which are descendants of changesets in set.
"""
Patrick Mezard
graphlog: fix --follow-first --rev combinations...
r16409 return _descendants(repo, subset, x)
def _firstdescendants(repo, subset, x):
# ``_firstdescendants(set)``
# Like ``descendants(set)`` but follows only the first parents.
return _descendants(repo, subset, x, followfirst=True)
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915
Pierre-Yves David
phases: implements simple revset symbol...
r15819 def draft(repo, subset, x):
"""``draft()``
Changeset in draft phase."""
getargs(x, 0, 0, _("draft takes no arguments"))
return [r for r in subset if repo._phaserev[r] == phases.draft]
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"))
Matt Mackall
revsets: provide contexts for filesets...
r15964 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
ctx=repo[None])
Matt Mackall
revset: introduce filelog() to emulate log's fast path...
r14342 s = set()
Matt Mackall
revsets: provide contexts for filesets...
r15964 if not matchmod.patkind(pat):
Matt Mackall
revset: introduce filelog() to emulate log's fast path...
r14342 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]
Matt Mackall
revsets: add first alias for last
r15117 def first(repo, subset, x):
"""``first(set, [n])``
An alias for limit().
"""
return limit(repo, subset, x)
Patrick Mezard
context: add followfirst arg to filectx and workingfilectx...
r16185 def _follow(repo, subset, x, name, followfirst=False):
l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
c = repo['.']
if l:
x = getstring(l[0], _("%s expected a filename") % name)
if x in c:
cx = c[x]
s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
# include the revision responsible for the most recent version
s.add(cx.linkrev())
else:
return []
else:
Patrick Mezard
graphlog: fix --follow-first --rev combinations...
r16409 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
Patrick Mezard
context: add followfirst arg to filectx and workingfilectx...
r16185
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.
"""
Patrick Mezard
context: add followfirst arg to filectx and workingfilectx...
r16185 return _follow(repo, subset, x, 'follow')
Matt Mackall
revset: add follow(filename) to follow a filename's history across copies
r14343
Patrick Mezard
graphlog: implement --follow-first...
r16174 def _followfirst(repo, subset, x):
# ``followfirst([file])``
# Like ``follow([file])`` but follows only the first parent of
# every revision or file revision.
Patrick Mezard
context: add followfirst arg to filectx and workingfilectx...
r16185 return _follow(repo, subset, x, '_followfirst', followfirst=True)
Matt Mackall
revset: add follow(filename) to follow a filename's history across copies
r14343
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 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
Patrick Mezard
graphlog: paths/-I/-X handling requires a new revset...
r16161 def _matchfiles(repo, subset, x):
# _matchfiles takes a revset list of prefixed arguments:
#
# [p:foo, i:bar, x:baz]
#
# builds a match object from them and filters subset. Allowed
# prefixes are 'p:' for regular patterns, 'i:' for include
Patrick Mezard
graphlog: evaluate FILE/-I/-X filesets on the working dir...
r16181 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
# a revision identifier, or the empty string to reference the
# working directory, from which the match object is
Patrick Mezard
graphlog: correctly handle calls in subdirectories
r16411 # initialized. Use 'd:' to set the default matching mode, default
# to 'glob'. At most one 'r:' and 'd:' argument can be passed.
Patrick Mezard
graphlog: paths/-I/-X handling requires a new revset...
r16161
# i18n: "_matchfiles" is a keyword
l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
pats, inc, exc = [], [], []
hasset = False
Patrick Mezard
graphlog: correctly handle calls in subdirectories
r16411 rev, default = None, None
Patrick Mezard
graphlog: paths/-I/-X handling requires a new revset...
r16161 for arg in l:
s = getstring(arg, _("_matchfiles requires string arguments"))
prefix, value = s[:2], s[2:]
if prefix == 'p:':
pats.append(value)
elif prefix == 'i:':
inc.append(value)
elif prefix == 'x:':
exc.append(value)
Patrick Mezard
graphlog: evaluate FILE/-I/-X filesets on the working dir...
r16181 elif prefix == 'r:':
if rev is not None:
raise error.ParseError(_('_matchfiles expected at most one '
'revision'))
rev = value
Patrick Mezard
graphlog: correctly handle calls in subdirectories
r16411 elif prefix == 'd:':
if default is not None:
raise error.ParseError(_('_matchfiles expected at most one '
'default mode'))
default = value
Patrick Mezard
graphlog: paths/-I/-X handling requires a new revset...
r16161 else:
raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
if not hasset and matchmod.patkind(value) == 'set':
hasset = True
Patrick Mezard
graphlog: correctly handle calls in subdirectories
r16411 if not default:
default = 'glob'
Patrick Mezard
graphlog: paths/-I/-X handling requires a new revset...
r16161 m = None
s = []
for r in subset:
c = repo[r]
Patrick Mezard
graphlog: evaluate FILE/-I/-X filesets on the working dir...
r16181 if not m or (hasset and rev is None):
ctx = c
if rev is not None:
ctx = repo[rev or None]
Patrick Mezard
graphlog: paths/-I/-X handling requires a new revset...
r16161 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
Patrick Mezard
graphlog: correctly handle calls in subdirectories
r16411 exclude=exc, ctx=ctx, default=default)
Patrick Mezard
graphlog: paths/-I/-X handling requires a new revset...
r16161 for f in c.files():
if m(f):
s.append(r)
break
return s
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 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"))
Patrick Mezard
graphlog: paths/-I/-X handling requires a new revset...
r16161 return _matchfiles(repo, subset, ('string', 'p:' + pat))
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915
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
FUJIWARA Katsunori
i18n: use "encoding.lower()" to normalize specified string for revset...
r15726 kw = encoding.lower(getstring(x, _("keyword requires a string")))
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 l = []
for r in subset:
c = repo[r]
t = " ".join(c.files() + [c.user(), c.description()])
FUJIWARA Katsunori
i18n: use "encoding.lower()" to normalize specified string for revset...
r15726 if kw in encoding.lower(t):
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 l.append(r)
return l
def limit(repo, subset, x):
Matt Mackall
revset: add default of 1 to limit and last functions
r15116 """``limit(set, [n])``
First n members of set, defaulting to 1.
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 """
# i18n: "limit" is a keyword
Matt Mackall
revset: add default of 1 to limit and last functions
r15116 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 try:
Matt Mackall
revset: add default of 1 to limit and last functions
r15116 lim = 1
if len(l) == 2:
# i18n: "limit" is a keyword
lim = int(getstring(l[1], _("limit requires a number")))
Matt Mackall
revsets: actually catch type error on tip^p1(tip) (issue2884)...
r14851 except (TypeError, ValueError):
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 # 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):
Matt Mackall
revset: add default of 1 to limit and last functions
r15116 """``last(set, [n])``
Last n members of set, defaulting to 1.
Matt Mackall
revsets: add a last function...
r14061 """
# i18n: "last" is a keyword
Matt Mackall
revset: add default of 1 to limit and last functions
r15116 l = getargs(x, 1, 2, _("last requires one or two arguments"))
Matt Mackall
revsets: add a last function...
r14061 try:
Matt Mackall
revset: add default of 1 to limit and last functions
r15116 lim = 1
if len(l) == 2:
# i18n: "last" is a keyword
lim = int(getstring(l[1], _("last requires a number")))
Matt Mackall
revsets: actually catch type error on tip^p1(tip) (issue2884)...
r14851 except (TypeError, ValueError):
Matt Mackall
revsets: add a last function...
r14061 # 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)
Matt Mackall
revset: avoid demandimport bug...
r16417 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
Mads Kiilerich
revset and fileset: fix typos in parser error messages
r14717 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 # 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]
Matt Mackall
hg: change various repository() users to use peer() where appropriate...
r14556 other = hg.peer(repo, {}, dest)
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 repo.ui.pushbuffer()
Pierre-Yves David
discovery: introduce outgoing object for result of findcommonoutgoing...
r15837 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 repo.ui.popbuffer()
cl = repo.changelog
Pierre-Yves David
discovery: introduce outgoing object for result of findcommonoutgoing...
r15837 o = set([cl.rev(r) for r in outgoing.missing])
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
Matt Mackall
revsets: actually catch type error on tip^p1(tip) (issue2884)...
r14851 except (TypeError, ValueError):
Kevin Gessner
revset: add ^ and ~ operators from parentrevspec extension...
r14070 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 []
Pierre-Yves David
phases: implements simple revset symbol...
r15819 def public(repo, subset, x):
"""``public()``
Changeset in public phase."""
getargs(x, 0, 0, _("public takes no arguments"))
return [r for r in subset if repo._phaserev[r] == phases.public]
Matt Mackall
revset: add remote() predicate to lookup remote revisions
r15936 def remote(repo, subset, x):
FUJIWARA Katsunori
revset: fix documentation for 'remote()' predicate...
r16007 """``remote([id [,path]])``
Matt Mackall
revset: add remote() predicate to lookup remote revisions
r15936 Local revision that corresponds to the given identifier in a
remote repository, if present. Here, the '.' identifier is a
synonym for the current local branch.
"""
import hg # avoid start-up nasties
# i18n: "remote" is a keyword
FUJIWARA Katsunori
revset: fix documentation for 'remote()' predicate...
r16007 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
Matt Mackall
revset: add remote() predicate to lookup remote revisions
r15936
q = '.'
if len(l) > 0:
# i18n: "remote" is a keyword
q = getstring(l[0], _("remote requires a string id"))
if q == '.':
q = repo['.'].branch()
dest = ''
if len(l) > 1:
# i18n: "remote" is a keyword
dest = getstring(l[1], _("remote requires a repository path"))
dest = repo.ui.expandpath(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.peer(repo, {}, dest)
n = other.lookup(q)
if n in repo:
r = repo[n].rev()
FUJIWARA Katsunori
revset: fix 'remote()' failure when remote repo has more revs than local...
r16006 if r in subset:
return [r]
Matt Mackall
revset: add remote() predicate to lookup remote revisions
r15936 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")))
Matt Mackall
revsets: actually catch type error on tip^p1(tip) (issue2884)...
r14851 except (TypeError, ValueError):
Idan Kamara
revset: rearrange code so functions are sorted alphabetically
r13915 # 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
Angel Ezquerra
revset: add "matching" keyword...
r16402 def matching(repo, subset, x):
"""``matching(revision [, field])``
Changesets in which a given set of fields match the set of fields in the
selected revision or set.
To match more than one field pass the list of fields to match separated
by spaces (e.g. 'author description').
Valid fields are most regular revision fields and some special fields:
* regular fields:
- description, author, branch, date, files, phase, parents,
substate, user.
Note that author and user are synonyms.
* special fields: summary, metadata.
- summary: matches the first line of the description.
- metatadata: It is equivalent to matching 'description user date'
(i.e. it matches the main metadata fields).
metadata is the default field which is used when no fields are specified.
You can match more than one field at a time.
"""
l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
revs = getset(repo, xrange(len(repo)), l[0])
fieldlist = ['metadata']
if len(l) > 1:
fieldlist = getstring(l[1],
_("matching requires a string "
"as its second argument")).split()
# Make sure that there are no repeated fields, and expand the
# 'special' 'metadata' field type
fields = []
for field in fieldlist:
if field == 'metadata':
fields += ['user', 'description', 'date']
else:
if field == 'author':
field = 'user'
fields.append(field)
fields = set(fields)
# We may want to match more than one field
# Each field will be matched with its own "getfield" function
# which will be added to the getfieldfuncs array of functions
getfieldfuncs = []
_funcs = {
'user': lambda r: repo[r].user(),
'branch': lambda r: repo[r].branch(),
'date': lambda r: repo[r].date(),
'description': lambda r: repo[r].description(),
'files': lambda r: repo[r].files(),
'parents': lambda r: repo[r].parents(),
'phase': lambda r: repo[r].phase(),
'substate': lambda r: repo[r].substate,
'summary': lambda r: repo[r].description().splitlines()[0],
}
for info in fields:
getfield = _funcs.get(info, None)
if getfield is None:
raise error.ParseError(
_("unexpected field name passed to matching: %s") % info)
getfieldfuncs.append(getfield)
# convert the getfield array of functions into a "getinfo" function
# which returns an array of field values (or a single value if there
# is only one field to match)
if len(getfieldfuncs) == 1:
getinfo = getfieldfuncs[0]
else:
getinfo = lambda r: [f(r) for f in getfieldfuncs]
matches = []
for rev in revs:
target = getinfo(rev)
matches += [r for r in subset if getinfo(r) == target]
if len(revs) > 1:
matches = sorted(set(matches))
return matches
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)``
Patrick Mezard
revset: do not ignore input revisions in roots()...
r16394 Changesets in set with no parent changeset in set.
Patrick Mezard
revsets: generate predicate help dynamically
r12821 """
Patrick Mezard
revset: do not ignore input revisions in roots()...
r16394 s = set(getset(repo, xrange(len(repo)), x))
Patrick Mezard
revset: retrieve a bit less parents in roots()
r16395 subset = [r for r in subset if r in s]
Patrick Mezard
revset: do not ignore input revisions in roots()...
r16394 cs = _children(repo, subset, s)
Patrick Mezard
revset: retrieve a bit less parents in roots()
r16395 return [r for r in subset if r not in cs]
Wagner Bruna
revset: predicate to avoid lookup errors...
r11944
Pierre-Yves David
phases: implements simple revset symbol...
r15819 def secret(repo, subset, x):
"""``secret()``
Changeset in secret phase."""
getargs(x, 0, 0, _("secret takes no arguments"))
return [r for r in subset if repo._phaserev[r] == phases.secret]
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: optimize building large lists in formatrevspec...
r15898 # for internal use
def _list(repo, subset, x):
s = getstring(x, "internal error")
if not s:
return []
if not isinstance(subset, set):
subset = set(subset)
ls = [repo[r].rev() for r in s.split('\0')]
return [r for r in ls if r in subset]
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,
Patrick Mezard
graphlog: fix --follow-first --rev combinations...
r16409 "_firstancestors": _firstancestors,
Matt Mackall
revset: sort the predicate list
r11284 "author": author,
"Yann E. MORIN"
revset: rename bisected() to bisect()...
r15134 "bisect": bisect,
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,
Thomas Arendsen Hein
revset: add desc(string) to search in commit messages...
r14650 "desc": desc,
Matt Mackall
revset: sort the predicate list
r11284 "descendants": descendants,
Patrick Mezard
graphlog: fix --follow-first --rev combinations...
r16409 "_firstdescendants": _firstdescendants,
Pierre-Yves David
phases: implements simple revset symbol...
r15819 "draft": draft,
Matt Mackall
revset: sort the predicate list
r11284 "file": hasfile,
Matt Mackall
revset: introduce filelog() to emulate log's fast path...
r14342 "filelog": filelog,
Matt Mackall
revsets: add first alias for last
r15117 "first": first,
Matt Mackall
revset: sort the predicate list
r11284 "follow": follow,
Patrick Mezard
graphlog: implement --follow-first...
r16174 "_followfirst": _followfirst,
Matt Mackall
revset: sort the predicate list
r11284 "grep": grep,
"head": head,
"heads": heads,
Matt Mackall
revset: avoid demandimport bug...
r16417 "id": node_,
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,
Patrick Mezard
graphlog: paths/-I/-X handling requires a new revset...
r16161 "_matchfiles": _matchfiles,
Matt Mackall
revset: sort the predicate list
r11284 "max": maxrev,
Thomas Arendsen Hein
revset: update sorting of symbols
r14649 "merge": merge,
Nicolas Dumazet
revset: add min function
r11708 "min": minrev,
Matt Mackall
revset: sort the predicate list
r11284 "modifies": modifies,
"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,
Pierre-Yves David
phases: implements simple revset symbol...
r15819 "public": public,
Matt Mackall
revset: add remote() predicate to lookup remote revisions
r15936 "remote": remote,
Matt Mackall
revset: sort the predicate list
r11284 "removes": removes,
Thomas Arendsen Hein
revset: update sorting of symbols
r14649 "rev": rev,
Matt Mackall
revset: sort the predicate list
r11284 "reverse": reverse,
Matt Mackall
revset: introduce revset core
r11275 "roots": roots,
Matt Mackall
revset: sort the predicate list
r11284 "sort": sort,
Pierre-Yves David
phases: implements simple revset symbol...
r15819 "secret": secret,
Angel Ezquerra
revset: add "matching" keyword...
r16402 "matching": matching,
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: optimize building large lists in formatrevspec...
r15898 "_list": _list,
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
revsets: do the right thing with x^:y (issue2884)...
r14842 if op == 'parent':
# x^:y means (x^) : y, not x ^ (:y)
post = ('parentpost', x[1])
if x[2][0] == 'dagrangepre':
return optimize(('dagrange', post, x[2][1]), small)
elif x[2][0] == 'rangepre':
return optimize(('range', post, x[2][1]), small)
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)
Thomas Arendsen Hein
revset: add desc(string) to search in commit messages...
r14650 if f in ("author branch closed date desc file grep keyword "
"outgoing user"):
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
Matt Mackall
revsets: add first alias for last
r15117 elif f in "reverse limit first":
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('^([^(]+)\(([^)]+)\)$')
Mads Kiilerich
revset: fix aliases with 0 or more than 2 parameters...
r14723 args = None
Alexander Solovyov
revset aliases
r14098
Mads Kiilerich
revset: fix aliases with 0 or more than 2 parameters...
r14723 def __init__(self, name, value):
Alexander Solovyov
revset aliases
r14098 '''Aliases like:
h = heads(default)
b($1) = ancestors($1) - ancestors(default)
'''
Patrick Mezard
revset: fix alias substitution recursion (issue3240)...
r16096 m = self.funcre.search(name)
if m:
self.name = m.group(1)
self.tree = ('func', ('symbol', m.group(1)))
self.args = [x.strip() for x in m.group(2).split(',')]
for arg in self.args:
value = value.replace(arg, repr(arg))
else:
self.name = name
self.tree = ('symbol', name)
self.replacement, pos = parse(value)
if pos != len(value):
raise error.ParseError(_('invalid token'), pos)
Alexander Solovyov
revset aliases
r14098
Patrick Mezard
revset: fix alias substitution recursion (issue3240)...
r16096 def _getalias(aliases, tree):
"""If tree looks like an unexpanded alias, return it. Return None
otherwise.
"""
if isinstance(tree, tuple) and tree:
if tree[0] == 'symbol' and len(tree) == 2:
name = tree[1]
alias = aliases.get(name)
if alias and alias.args is None and alias.tree == tree:
return alias
if tree[0] == 'func' and len(tree) > 1:
if tree[1][0] == 'symbol' and len(tree[1]) == 2:
name = tree[1][1]
alias = aliases.get(name)
if alias and alias.args is not None and alias.tree == tree[:2]:
return alias
return None
Alexander Solovyov
revset aliases
r14098
Patrick Mezard
revset: fix alias substitution recursion (issue3240)...
r16096 def _expandargs(tree, args):
"""Replace all occurences of ('string', name) with the
substitution value of the same name in args, recursively.
"""
if not isinstance(tree, tuple):
return tree
if len(tree) == 2 and tree[0] == 'string':
return args.get(tree[1], tree)
return tuple(_expandargs(t, args) for t in tree)
def _expandaliases(aliases, tree, expanding):
"""Expand aliases in tree, recursively.
'aliases' is a dictionary mapping user defined aliases to
revsetalias objects.
"""
if not isinstance(tree, tuple):
# Do not expand raw strings
Alexander Solovyov
revset aliases
r14098 return tree
Patrick Mezard
revset: fix alias substitution recursion (issue3240)...
r16096 alias = _getalias(aliases, tree)
if alias is not None:
if alias in expanding:
raise error.ParseError(_('infinite expansion of revset alias "%s" '
'detected') % alias.name)
expanding.append(alias)
result = alias.replacement
if alias.args is not None:
l = getlist(tree[2])
if len(l) != len(alias.args):
raise error.ParseError(
_('invalid number of arguments: %s') % len(l))
result = _expandargs(result, dict(zip(alias.args, l)))
# Recurse in place, the base expression may have been rewritten
result = _expandaliases(aliases, result, expanding)
expanding.pop()
else:
result = tuple(_expandaliases(aliases, t, expanding)
for t in tree)
return result
Alexander Solovyov
revset aliases
r14098
def findaliases(ui, tree):
Patrick Mezard
revset: fix alias substitution recursion (issue3240)...
r16096 aliases = {}
Alexander Solovyov
revset aliases
r14098 for k, v in ui.configitems('revsetalias'):
alias = revsetalias(k, v)
Patrick Mezard
revset: fix alias substitution recursion (issue3240)...
r16096 aliases[alias.name] = alias
return _expandaliases(aliases, tree, [])
Alexander Solovyov
revset aliases
r14098
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)):
Mads Kiilerich
parsers: fix localization markup of parser errors
r14701 raise error.ParseError(_("invalid token"), pos)
Matt Mackall
revset: allow bypassing alias expansion...
r14900 if ui:
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
Matt Mackall
revset: add formatspec convenience query builder
r14901 def formatspec(expr, *args):
'''
This is a convenience function for using revsets internally, and
escapes arguments appropriately. Aliases are intentionally ignored
so that intended expression behavior isn't accidentally subverted.
Supported arguments:
Matt Mackall
revset: add %r for embedded revset support to formatspec...
r15266 %r = revset expression, parenthesized
Matt Mackall
revset: add formatspec convenience query builder
r14901 %d = int(arg), no quoting
%s = string(arg), escaped and single-quoted
%b = arg.branch(), escaped and single-quoted
%n = hex(arg), single-quoted
%% = a literal '%'
Matt Mackall
revset: add %r for embedded revset support to formatspec...
r15266 Prefixing the type with 'l' specifies a parenthesized list of that type.
Matt Mackall
revset: add 'l' flag to formatspec for args...
r15140
Matt Mackall
revset: fix %r handling in formatspec
r15268 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
'(10 or 11):: and ((this()) or (that()))'
Matt Mackall
revset: add formatspec convenience query builder
r14901 >>> formatspec('%d:: and not %d::', 10, 20)
'10:: and not 20::'
Matt Mackall
revset: deal with empty lists in formatspec
r15325 >>> formatspec('%ld or %ld', [], [1])
Matt Mackall
revset: optimize building large lists in formatrevspec...
r15898 "_list('') or 1"
Matt Mackall
revset: add formatspec convenience query builder
r14901 >>> formatspec('keyword(%s)', 'foo\\xe9')
"keyword('foo\\\\xe9')"
>>> b = lambda: 'default'
>>> b.branch = b
>>> formatspec('branch(%b)', b)
"branch('default')"
Matt Mackall
revset: add 'l' flag to formatspec for args...
r15140 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
Matt Mackall
revset: optimize building large lists in formatrevspec...
r15898 "root(_list('a\\x00b\\x00c\\x00d'))"
Matt Mackall
revset: add formatspec convenience query builder
r14901 '''
def quote(s):
return repr(str(s))
Matt Mackall
revset: add 'l' flag to formatspec for args...
r15140 def argtype(c, arg):
if c == 'd':
return str(int(arg))
elif c == 's':
return quote(arg)
Matt Mackall
revset: add %r for embedded revset support to formatspec...
r15266 elif c == 'r':
parse(arg) # make sure syntax errors are confined
return '(%s)' % arg
Matt Mackall
revset: add 'l' flag to formatspec for args...
r15140 elif c == 'n':
Matt Mackall
revset: avoid demandimport bug...
r16417 return quote(node.hex(arg))
Matt Mackall
revset: add 'l' flag to formatspec for args...
r15140 elif c == 'b':
return quote(arg.branch())
Matt Mackall
revset: balance %l or-expressions (issue3129)
r15595 def listexp(s, t):
l = len(s)
if l == 0:
Matt Mackall
revset: optimize building large lists in formatrevspec...
r15898 return "_list('')"
elif l == 1:
Matt Mackall
revset: balance %l or-expressions (issue3129)
r15595 return argtype(t, s[0])
Matt Mackall
revset: optimize building large lists in formatrevspec...
r15898 elif t == 'd':
return "_list('%s')" % "\0".join(str(int(a)) for a in s)
elif t == 's':
return "_list('%s')" % "\0".join(s)
elif t == 'n':
Matt Mackall
revset: avoid demandimport bug...
r16417 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
Matt Mackall
revset: optimize building large lists in formatrevspec...
r15898 elif t == 'b':
return "_list('%s')" % "\0".join(a.branch() for a in s)
Martin Geisler
Use explicit integer division...
r15791 m = l // 2
Matt Mackall
revset: balance %l or-expressions (issue3129)
r15595 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
Matt Mackall
revset: add formatspec convenience query builder
r14901 ret = ''
pos = 0
arg = 0
while pos < len(expr):
c = expr[pos]
if c == '%':
pos += 1
d = expr[pos]
if d == '%':
ret += d
Matt Mackall
revset: fix %r handling in formatspec
r15268 elif d in 'dsnbr':
Matt Mackall
revset: add 'l' flag to formatspec for args...
r15140 ret += argtype(d, args[arg])
Matt Mackall
revset: add formatspec convenience query builder
r14901 arg += 1
Matt Mackall
revset: add 'l' flag to formatspec for args...
r15140 elif d == 'l':
# a list of some type
pos += 1
d = expr[pos]
Matt Mackall
merge with stable
r15596 ret += listexp(list(args[arg]), d)
Matt Mackall
revset: add formatspec convenience query builder
r14901 arg += 1
else:
raise util.Abort('unexpected revspec format character %s' % d)
else:
ret += c
pos += 1
return ret
Patrick Mezard
debugrevspec: pretty print output...
r16218 def prettyformat(tree):
def _prettyformat(tree, level, lines):
if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
lines.append((level, str(tree)))
else:
lines.append((level, '(%s' % tree[0]))
for s in tree[1:]:
_prettyformat(s, level + 1, lines)
lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
lines = []
_prettyformat(tree, 0, lines)
output = '\n'.join((' '*l + s) for l, s in lines)
return output
Patrick Mezard
hggettext: handle i18nfunctions declaration for docstrings translations
r12823 # tell hggettext to extract docstrings from these functions:
i18nfunctions = symbols.values()