diff hgext/show.py @ 31944:99bc93147d87

show: implement underway view This is the beginning of a wip/smartlog view. It is basically a manually constructed (read: fast) revset function to collect "relevant" changesets combined with a custom template and a graph displayer. It obviously needs a lot of work. I'd like to get *something* usable in 4.2 so `hg show` has some value to end-users. Let the bikeshedding begin.
author Gregory Szorc <gregory.szorc@gmail.com>
date Wed, 12 Apr 2017 20:31:15 -0700
parents 3e9f118cc834
children 02c3b1f396de
line wrap: on
line diff
--- a/hgext/show.py	Wed Apr 12 20:28:44 2017 -0700
+++ b/hgext/show.py	Wed Apr 12 20:31:15 2017 -0700
@@ -15,13 +15,17 @@
 from __future__ import absolute_import
 
 from mercurial.i18n import _
+from mercurial.node import nullrev
 from mercurial import (
     cmdutil,
     commands,
     error,
     formatter,
+    graphmod,
     pycompat,
     registrar,
+    revset,
+    revsetlang,
 )
 
 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
@@ -32,6 +36,7 @@
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
+revsetpredicate = registrar.revsetpredicate()
 
 class showcmdfunc(registrar._funcregistrarbase):
     """Register a function to be invoked for an `hg show <thing>`."""
@@ -128,6 +133,70 @@
         fm.data(active=bm == active,
                 longestbookmarklen=longestname)
 
+@revsetpredicate('_underway([commitage[, headage]])')
+def underwayrevset(repo, subset, x):
+    args = revset.getargsdict(x, 'underway', 'commitage headage')
+    if 'commitage' not in args:
+        args['commitage'] = None
+    if 'headage' not in args:
+        args['headage'] = None
+
+    # We assume callers of this revset add a topographical sort on the
+    # result. This means there is no benefit to making the revset lazy
+    # since the topographical sort needs to consume all revs.
+    #
+    # With this in mind, we build up the set manually instead of constructing
+    # a complex revset. This enables faster execution.
+
+    # Mutable changesets (non-public) are the most important changesets
+    # to return. ``not public()`` will also pull in obsolete changesets if
+    # there is a non-obsolete changeset with obsolete ancestors. This is
+    # why we exclude obsolete changesets from this query.
+    rs = 'not public() and not obsolete()'
+    rsargs = []
+    if args['commitage']:
+        rs += ' and date(%s)'
+        rsargs.append(revsetlang.getstring(args['commitage'],
+                                           _('commitage requires a string')))
+
+    mutable = repo.revs(rs, *rsargs)
+    relevant = revset.baseset(mutable)
+
+    # Add parents of mutable changesets to provide context.
+    relevant += repo.revs('parents(%ld)', mutable)
+
+    # We also pull in (public) heads if they a) aren't closing a branch
+    # b) are recent.
+    rs = 'head() and not closed()'
+    rsargs = []
+    if args['headage']:
+        rs += ' and date(%s)'
+        rsargs.append(revsetlang.getstring(args['headage'],
+                                           _('headage requires a string')))
+
+    relevant += repo.revs(rs, *rsargs)
+
+    # Add working directory parent.
+    wdirrev = repo['.'].rev()
+    if wdirrev != nullrev:
+        relevant += revset.baseset(set([wdirrev]))
+
+    return subset & relevant
+
+@showview('underway', fmtopic='underway')
+def showunderway(ui, repo, fm):
+    """changesets that aren't finished"""
+    # TODO support date-based limiting when calling revset.
+    revs = repo.revs('sort(_underway(), topo)')
+
+    revdag = graphmod.dagwalker(repo, revs)
+    displayer = cmdutil.changeset_templater(ui, repo, None, None,
+                                            tmpl=fm._t.load(fm._topic),
+                                            mapfile=None, buffered=True)
+
+    ui.setconfig('experimental', 'graphshorten', True)
+    cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
+
 # Adjust the docstring of the show command so it shows all registered views.
 # This is a bit hacky because it runs at the end of module load. When moved
 # into core or when another extension wants to provide a view, we'll need