diff mercurial/templatefuncs.py @ 44600:7cd5c0968139

templater: add subsetparents(rev, revset) function Naming suggestions are welcome. And this could be flagged as an (ADVANCED) function since the primary use case is to draw a graph. This provides all data needed for drawing revisions graph filtered by revset, and allows us to implement a GUI graph viewer in some languages better than Python. A frontend grapher will be quite similar to our graphmod since subsetparents() just returns parent-child relations in the filtered sub graph. Frontend example: https://hg.sr.ht/~yuja/hgv/browse/default/core/hgchangesetgrapher.cpp However, the resulting graph will be simpler than the one "hg log -G" would generate because redundant edges are eliminated. This should be the same graph rendering strategy as TortoiseHg. This function could be implemented as a revset predicate, but that would mean the scanning state couldn't be cached and thus slow. Test cases are split to new file since test-template-functions.t is quite big and we'll need a new branchy repository anyway.
author Yuya Nishihara <yuya@tcha.org>
date Sun, 15 Mar 2020 16:11:58 +0900
parents 1f81f680912f
children 89a2afe31e82
line wrap: on
line diff
--- a/mercurial/templatefuncs.py	Sun Mar 15 16:00:45 2020 +0900
+++ b/mercurial/templatefuncs.py	Sun Mar 15 16:11:58 2020 +0900
@@ -16,6 +16,7 @@
 )
 from . import (
     color,
+    dagop,
     diffutil,
     encoding,
     error,
@@ -842,6 +843,45 @@
     return b''
 
 
+@templatefunc(
+    b'subsetparents(rev, revset)',
+    argspec=b'rev revset',
+    requires={b'repo', b'cache'},
+)
+def subsetparents(context, mapping, args):
+    """Look up parents of the rev in the sub graph given by the revset."""
+    if b'rev' not in args or b'revset' not in args:
+        # i18n: "subsetparents" is a keyword
+        raise error.ParseError(_(b"subsetparents expects two arguments"))
+
+    repo = context.resource(mapping, b'repo')
+
+    rev = templateutil.evalinteger(context, mapping, args[b'rev'])
+
+    # TODO: maybe subsetparents(rev) should be allowed. the default revset
+    # will be the revisions specified by -rREV argument.
+    q = templateutil.evalwrapped(context, mapping, args[b'revset'])
+    if not isinstance(q, templateutil.revslist):
+        # i18n: "subsetparents" is a keyword
+        raise error.ParseError(_(b"subsetparents expects a queried revset"))
+    subset = q.tovalue(context, mapping)
+    key = q.cachekey
+
+    if key:
+        # cache only if revset query isn't dynamic
+        cache = context.resource(mapping, b'cache')
+        walkercache = cache.setdefault(b'subsetparentswalker', {})
+        if key in walkercache:
+            walker = walkercache[key]
+        else:
+            walker = dagop.subsetparentswalker(repo, subset)
+            walkercache[key] = walker
+    else:
+        # for one-shot use, specify startrev to limit the search space
+        walker = dagop.subsetparentswalker(repo, subset, startrev=rev)
+    return templateutil.revslist(repo, walker.parentsset(rev))
+
+
 @templatefunc(b'word(number, text[, separator])')
 def word(context, mapping, args):
     """Return the nth word from a string."""