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

login
register
mail settings
Submitter Kostia Balytskyi
Date March 16, 2016, 2:14 p.m.
Message ID <5a68b6de50853d94d8b5.1458137643@dev1902.lla1.facebook.com>
Download mbox | patch
Permalink /patch/13914/
State Changes Requested
Headers show

Comments

Kostia Balytskyi - March 16, 2016, 2:14 p.m.
# HG changeset patch
# User Kostia Balytskyi <ikostia@fb.com>
# Date 1458136898 25200
#      Wed Mar 16 07:01:38 2016 -0700
# Node ID 5a68b6de50853d94d8b54412dfaf3d88defaf36e
# Parent  081605c2e9b6bb4d917295aae06b8428f64d0df1
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 :)


Sample output:
```
ikostia@dev1902.lla1:~/temprepos/supertroubledrepo$ hg evolve --list
01a3e66b: e (amended)
  unstable: unstable parent 1995fc65
  divergent with:
    (84e1c6ae, d3b90e9c); common precursor: 3efa43a7
    (add9a356); common precursor: 3efa43a7
add9a356: e (rebased)
  divergent with:
    (84e1c6ae, d3b90e9c); common precursor: 3efa43a7
    (01a3e66b); common precursor: 3efa43a7
84e1c6ae: e (e+f split)
  unstable: unstable parent 1995fc65
  divergent with:
    (01a3e66b); common precursor: 3efa43a7
    (add9a356); common precursor: 3efa43a7
d3b90e9c: f (e+f split)
  unstable: unstable parent 84e1c6ae
  divergent with:
    (01a3e66b); common precursor: 3efa43a7
    (add9a356); common precursor: 3efa43a7
8febfaee: c
  unstable: obsolete parent 43765473
  bumped: immutable precursor b36d99df
1995fc65: d: commit with a long happy message, ababagalamaga, ababagal...
  unstable: unstable parent 8febfaee
```

Patch

diff --git a/hgext/evolve.py b/hgext/evolve.py
--- a/hgext/evolve.py
+++ b/hgext/evolve.py
@@ -1509,6 +1509,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')),
@@ -1524,6 +1635,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):
@@ -1591,9 +1703,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']
@@ -1603,6 +1719,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)')