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

login
register
mail settings
Submitter Kostia Balytskyi
Date March 15, 2016, 4:49 p.m.
Message ID <f80436c109d952e10438.1458060542@dev1902.lla1.facebook.com>
Download mbox | patch
Permalink /patch/13898/
State Superseded
Headers show

Comments

Kostia Balytskyi - March 15, 2016, 4:49 p.m.
# HG changeset patch
# User Kostia Balytskyi <ikostia@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 :)
timeless - March 15, 2016, 8:47 p.m.
You don't want this change:

 @command('^split',
-    [('r', 'rev', [], _("revision to split")),
+    [('r', 'rev', [], _("revision to fold")),
Durham Goode - March 16, 2016, 5:56 a.m.
On 3/15/16 9:49 AM, Kostia Balytskyi wrote:
> # HG changeset patch
> # User Kostia Balytskyi <ikostia@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 :)
Could you include in the commit message an example of the output? It's 
hard to tell what this will actually look like to a user from just 
seeing the code.

Patch

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):