[PATCH evolve-ext] evolve--list: initial implementation with sample template

Kostia Balytskyi ikostia at fb.com
Tue Mar 15 12:49:02 EDT 2016


# HG changeset patch
# User Kostia Balytskyi <ikostia at fb.com>
# Date 1458060234 25200
#      Tue Mar 15 09:43:54 2016 -0700
# Branch stable
# Node ID f80436c109d952e10438c7cd8dbfeda89264c0ba
# Parent  160968654581eb83f58f2b913124a16f7bef56ee
evolve--list: initial implementation with sample template

Please bear in mind that this was not adjusted to conform
with style requirements. This is just a sample implementation
to get some feeback, so the template constant will be removed.

This implementation does not work with JSON since it needs
multiple levels of depth.

Feedback is welcome and necessary :)

diff --git a/hgext/evolve.py b/hgext/evolve.py
--- a/hgext/evolve.py
+++ b/hgext/evolve.py
@@ -1510,6 +1510,117 @@ def _orderrevs(repo, revs):
     ordering.extend(sorted(dependencies))
     return ordering
 
+def divergentsets(repo, ctx):
+    cache = {}
+    succsets = {}
+    base = {}
+    for n in obsolete.allprecursors(repo.obsstore, [ctx.node()]):
+        if n == ctx.node():
+            # a node can't be a base for divergencies with itself
+            continue
+        nsuccsets = obsolete.successorssets(repo, n, cache)
+        for nsuccset in nsuccsets:
+            if ctx.node() in nsuccset:
+                # we are only interested in *other* successor sets
+                continue
+            if tuple(nsuccset) in base:
+                # we already know the latest base for this divergency
+                continue
+            base[tuple(nsuccset)] = n
+    divergencies = []
+    for divset, b in base.iteritems():
+        divergencies.append({
+            'divergentnodes': map(lambda n: node.hex(n), divset),
+            'commonprecursor': node.hex(b)
+        })
+
+    return divergencies
+
+def _preparelistctxs(items, condition):
+    return [item.hex() for item in items if condition(item)]
+
+
+# to be removed, for templater demonstration purposes only
+troublelisttemplate = "{short(node)}: {desc}\n{troubles % '" \
+    "{ifeq(troubletype, \'unstable\', \'  unstable: {ifeq(sourcetype, \\'unstableparent\\', \\'unstable\\', \\'obsolete\\')} parent {short(sourcenode)}\n\')}" \
+    "{ifeq(troubletype, \'bumped\', \'  bumped: immutable precursor {short(sourcenode)}\n\')}" \
+    "{ifeq(troubletype, \'divergent\', \'  divergent with: ({divergentnodes % \\'{short(node)}, \\'}); common precursor {short(commonprecursor)}\n\')}" \
+    "'}"
+
+def listtroubles(ui, repo, troublecategories, **opts):
+    """Print all the troubles for the repo (or given revset)"""
+    # to be removed; uncomment this is you want plain output
+    #opts.setdefault('template', troublelisttemplate)
+
+    troublecategories = troublecategories or ['divergent', 'unstable', 'bumped']
+    showunstable = 'unstable' in troublecategories
+    showbumped = 'bumped' in troublecategories
+    showdivergent = 'divergent' in troublecategories
+
+    revs = scmutil.revrange(repo, map(lambda c: c+'()', troublecategories))
+    if opts.get('rev'):
+        revs = revs & scmutil.revrange(repo, opts.get('rev'))
+
+    fm = ui.formatter('evolvelist', opts)
+    for rev in revs:
+        ctx = repo[rev]
+        unpars = _preparelistctxs(ctx.parents(), lambda p: p.unstable())
+        obspars = _preparelistctxs(ctx.parents(), lambda p: p.obsolete())
+        imprecs = _preparelistctxs(repo.set("allprecursors(%s)" % ctx.hex()),
+                                   lambda p: not p.mutable())
+        dsets = divergentsets(repo, ctx)
+
+        fm.startitem()
+        # plain formatter section
+        hashlen, desclen = 8, 60
+        desc = ctx.description()
+        desc = desc[:desclen] + '...' if len(desc) > desclen else desc
+        fm.plain('%s: ' % ctx.hex()[:hashlen])
+        fm.plain('%s\n' % desc)
+
+        for unpar in unpars if showunstable else []:
+            fm.plain('  unstable: unstable parent %s\n' % unpar[:hashlen])
+        for obspar in obspars if showunstable else []:
+            fm.plain('  unstable: obsolete parent %s\n' % obspar[:hashlen])
+        for imprec in imprecs if showbumped else []:
+            fm.plain('  bumped: immutable precursor %s\n' % imprec[:hashlen])
+
+        if dsets and showdivergent:
+            fm.plain('  divergent with:\n')
+            for dset in dsets:
+                fm.plain("    (")
+                first = True
+                for n in dset['divergentnodes']:
+                    t = "%s" if first else ", %s"
+                    first = False
+                    fm.plain(t % n[:hashlen])
+                comprec = dset['commonprecursor'][:hashlen]
+                fm.plain("); common precursor: %s\n" % comprec)
+
+        # templater-friendly section
+        fm.data(node=ctx.hex())
+        fm.data(desc=ctx.description())
+        fm.data(date=ctx.date())
+        fm.data(user=ctx.user())
+        troubles = []
+        for unpar in unpars:
+            troubles.append({'troubletype': 'unstable', 'sourcenode': unpar,
+                             'sourcetype': 'unstableparent'})
+        for obspar in obspars:
+            troubles.append({'troubletype': 'unstable', 'sourcenode': obspar,
+                             'sourcetype': 'obsoleteparent'})
+        for imprec in imprecs:
+            troubles.append({'troubletype': 'bumped', 'sourcenode': imprec,
+                             'sourcetype': 'immutableprecursor'})
+        for dset in dsets:
+            divnodes = [{'node': n} for n in dset['divergentnodes']]
+            troubles.append({'troubletype': 'divergent',
+                             'commonprecursor': dset['commonprecursor'],
+                             'divergentnodes': divnodes})
+        fm.data(troubles=troubles)
+
+    fm.end()
+
 @command('^evolve|stabilize|solve',
     [('n', 'dry-run', False,
         _('do not perform actions, just print what would be done')),
@@ -1525,6 +1636,7 @@ def _orderrevs(repo, revs):
     ('a', 'all', False, _('evolve all troubled changesets related to the '
                           'current  working directory and its descendants')),
     ('c', 'continue', False, _('continue an interrupted evolution')),
+    ('l', 'list', False, 'list all the troubled changesets in the repository'),
     ] + mergetoolopts,
     _('[OPTIONS]...'))
 def evolve(ui, repo, **opts):
@@ -1592,9 +1704,13 @@ def evolve(ui, repo, **opts):
     (the default). You can combine ``--bumped`` or ``--divergent`` with
     ``--rev``, ``--all``, or ``--any``.
 
+    You can also use the evolve command to list the troubles affecting your
+    repository by using the --list flag. You can choose to display only some
+    categories of troubles with the --unstable, --divergent or --bumped flags.
     """
 
     # Options
+    listopt = opts['list']
     contopt = opts['continue']
     anyopt = opts['any']
     allopt = opts['all']
@@ -1604,6 +1720,10 @@ def evolve(ui, repo, **opts):
     revopt = opts['rev']
     troublecategories = ['bumped', 'divergent', 'unstable']
     specifiedcategories = [t for t in troublecategories if opts[t]]
+    if listopt:
+        listtroubles(ui, repo, specifiedcategories, **opts)
+        return
+
     targetcat = 'unstable'
     if 1 < len(specifiedcategories):
         msg = _('cannot specify more than one trouble category to solve (yet)')
@@ -2709,7 +2829,7 @@ def commitwrapper(orig, ui, repo, *arg, 
         lockmod.release(tr, lock, wlock)
 
 @command('^split',
-    [('r', 'rev', [], _("revision to split")),
+    [('r', 'rev', [], _("revision to fold")),
     ] + commitopts + commitopts2,
     _('hg split [OPTION]... [-r] REV'))
 def cmdsplit(ui, repo, *revs, **opts):


More information about the Mercurial-devel mailing list