[PATCH 1 of 4 evolve-ext v4] evolve--list: initial implementation with sample template

Kostia Balytskyi ikostia at fb.com
Sun Mar 20 18:11:44 EDT 2016


# HG changeset patch
# User Kostia Balytskyi <ikostia at fb.com>
# Date 1458511346 25200
#      Sun Mar 20 15:02:26 2016 -0700
# Node ID cee17a44777f68aa55aa101e7ef0ad3fe9350059
# 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 at 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
```

diff --git a/hgext/evolve.py b/hgext/evolve.py
--- a/hgext/evolve.py
+++ b/hgext/evolve.py
@@ -1509,6 +1509,110 @@ def _orderrevs(repo, revs):
     ordering.extend(sorted(dependencies))
     return ordering
 
+def divergentsets(repo, ctx):
+    """Compute sets of commits divergent with a given one"""
+    cache = {}
+    succsets = {}
+    base = {}
+    for n in obsolete.allprecursors(repo.obsstore, [ctx.node()]):
+        if n == ctx.node():
+            # a node can't be a base for divergence 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
+    divergence = []
+    for divset, b in base.iteritems():
+        divergence.append({
+            'divergentnodes': divset,
+            'commonprecursor': b
+        })
+
+    return divergence
+
+def _preparelistctxs(items, condition):
+    return [item.hex() for item in items if condition(item)]
+
+def _formatctx(fm, ctx):
+    fm.data(node=ctx.hex())
+    fm.data(desc=ctx.description())
+    fm.data(date=ctx.date())
+    fm.data(user=ctx.user())
+
+def listtroubles(ui, repo, troublecategories, **opts):
+    """Print all the troubles for the repo (or given revset)"""
+    troublecategories = troublecategories or ['divergent', 'unstable', 'bumped']
+    showunstable = 'unstable' in troublecategories
+    showbumped = 'bumped' in troublecategories
+    showdivergent = 'divergent' in troublecategories
+
+    revs = repo.revs('+'.join("%s()" % t for t in troublecategories))
+    if opts.get('rev'):
+        revs = revs & repo.revs(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(%n)", ctx.node()),
+                                   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 % node.hex(n)[:hashlen])
+                comprec = node.hex(dset['commonprecursor'])[:hashlen]
+                fm.plain("); common precursor: %s\n" % comprec)
+
+        # templater-friendly section
+        _formatctx(fm, ctx)
+        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 +1628,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, 'provide details on troubled changesets in the repo'),
     ] + mergetoolopts,
     _('[OPTIONS]...'))
 def evolve(ui, repo, **opts):
@@ -1591,9 +1696,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 +1712,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)')


More information about the Mercurial-devel mailing list