--- a/mercurial/revset.py Thu Apr 07 19:24:16 2011 +0300
+++ b/mercurial/revset.py Fri Apr 08 17:47:58 2011 +0300
@@ -174,6 +174,322 @@
# functions
+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]
+
+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'):
+ raise ParseError(_('invalid bisect state'))
+ 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)``
+ Revision contains pattern.
+ """
+ # 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]
+
+def follow(repo, subset, x):
+ """``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'...')``
+ to ensure special escape characters are handled correctly.
+ """
+ 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
+ string.
+ """
+ # 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"))
+ return getset(repo, subset, l[0])[:lim]
+
+def maxrev(repo, subset, x):
+ """``max(set)``
+ Changeset with highest revision number in set.
+ """
+ s = getset(repo, subset, x)
+ if s:
+ m = max(s)
+ 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.
+ """
+ s = getset(repo, subset, x)
+ if s:
+ m = min(s)
+ 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)
+
def node(repo, subset, x):
"""``id(string)``
Revision non-ambiguously specified by the given hex string prefix.
@@ -188,19 +504,28 @@
rn = repo.changelog.rev(repo.changelog._partialmatch(n))
return [r for r in subset if r == rn]
-def rev(repo, subset, x):
- """``rev(number)``
- Revision with the given numeric identifier.
+def outgoing(repo, subset, x):
+ """``outgoing([path])``
+ Changesets not found in the specified destination repository, or the
+ default push location.
"""
- # 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]
+ 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()
+ o = discovery.findoutgoing(repo, other)
+ repo.ui.popbuffer()
+ cl = repo.changelog
+ o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
+ return [r for r in subset if r in o]
def p1(repo, subset, x):
"""``p1([set])``
@@ -248,254 +573,15 @@
ps.update(cl.parentrevs(r))
return [r for r in subset if r in ps]
-def maxrev(repo, subset, x):
- """``max(set)``
- Changeset with highest revision number in set.
- """
- s = getset(repo, subset, x)
- if s:
- m = max(s)
- if m in subset:
- return [m]
- return []
-
-def minrev(repo, subset, x):
- """``min(set)``
- Changeset with lowest revision number in set.
- """
- s = getset(repo, subset, x)
- if s:
- m = min(s)
- if m in subset:
- return [m]
- return []
-
-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"))
- return getset(repo, subset, l[0])[:lim]
-
-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 branch(repo, subset, x):
- """``branch(string or set)``
- All changesets belonging to the given branch or the branches of the given
- changesets.
+def present(repo, subset, x):
+ """``present(set)``
+ An empty set, if any revision in set isn't found; otherwise,
+ all revisions in set.
"""
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 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]
-
-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 getset(repo, subset, x)
+ except error.RepoLookupError:
return []
- s = set(repo.changelog.descendants(*args)) | set(args)
- return [r for r in subset if r in s]
-
-def follow(repo, subset, x):
- """``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 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 keyword(repo, subset, x):
- """``keyword(string)``
- Search commit message, user name, and names of changed files for
- string.
- """
- # 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 grep(repo, subset, x):
- """``grep(regex)``
- Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
- to ensure special escape characters are handled correctly.
- """
- 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 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 user(repo, subset, x):
- """``user(string)``
- User name is string.
- """
- return author(repo, subset, x)
-
-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 contains(repo, subset, x):
- """``contains(pattern)``
- Revision contains pattern.
- """
- # 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 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 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)
-
-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 removes(repo, subset, x):
"""``removes(pattern)``
@@ -505,33 +591,19 @@
pat = getstring(x, _("removes requires a pattern"))
return checkstatus(repo, subset, pat, 2)
-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 closed(repo, subset, x):
- """``closed()``
- Changeset is closed.
+def rev(repo, subset, x):
+ """``rev(number)``
+ Revision with the given numeric identifier.
"""
- # 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 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]
+ # 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]
def reverse(repo, subset, x):
"""``reverse(set)``
@@ -541,15 +613,13 @@
l.reverse()
return l
-def present(repo, subset, x):
- """``present(set)``
- An empty set, if any revision in set isn't found; otherwise,
- all revisions in set.
+def roots(repo, subset, x):
+ """``roots(set)``
+ Changesets with no parent changeset in set.
"""
- try:
- return getset(repo, subset, x)
- except error.RepoLookupError:
- return []
+ s = getset(repo, subset, x)
+ cs = set(children(repo, subset, x))
+ return [r for r in s if r not in cs]
def sort(repo, subset, x):
"""``sort(set[, [-]key...])``
@@ -606,53 +676,6 @@
l.sort()
return [e[-1] for e in l]
-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 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 roots(repo, subset, x):
- """``roots(set)``
- Changesets with no parent changeset in set.
- """
- s = getset(repo, subset, x)
- cs = set(children(repo, subset, x))
- return [r for r in s if r not in cs]
-
-def outgoing(repo, subset, x):
- """``outgoing([path])``
- Changesets not found in the specified destination repository, or the
- default push location.
- """
- 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()
- o = discovery.findoutgoing(repo, other)
- repo.ui.popbuffer()
- cl = repo.changelog
- o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
- return [r for r in subset if r in o]
-
def tag(repo, subset, x):
"""``tag(name)``
The specified tag by name, or all tagged revisions if no name is given.
@@ -674,34 +697,11 @@
def tagged(repo, subset, x):
return tag(repo, subset, x)
-def bookmark(repo, subset, x):
- """``bookmark([name])``
- The named bookmark or all bookmarks.
+def user(repo, subset, x):
+ """``user(string)``
+ User name is string.
"""
- # 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 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'):
- raise ParseError(_('invalid bisect state'))
- marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
- return [r for r in subset if r in marked]
+ return author(repo, subset, x)
symbols = {
"adds": adds,