mercurial/revset.py
changeset 13915 8f81d6f4047f
parent 13914 27573f2ddfb9
child 13932 34f577007ffe
--- 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,