--- a/hgext/evolve.py Fri Mar 18 23:49:32 2016 -0700
+++ b/hgext/evolve.py Tue Mar 22 14:08:16 2016 -0700
@@ -1514,6 +1514,110 @@
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 = 12, 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: %s (unstable parent)\n' % unpar[:hashlen])
+ for obspar in obspars if showunstable else []:
+ fm.plain(' unstable: %s (obsolete parent)\n' % obspar[:hashlen])
+ for imprec in imprecs if showbumped else []:
+ fm.plain(' bumped: %s (immutable precursor)\n' % imprec[:hashlen])
+
+ if dsets and showdivergent:
+ for dset in dsets:
+ fm.plain(' divergent: ')
+ 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(" (precursor %s)\n" % comprec)
+ fm.plain("\n")
+
+ # 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')),
@@ -1529,6 +1633,7 @@
('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):
@@ -1596,9 +1701,13 @@
(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']
@@ -1608,6 +1717,10 @@
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)')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-evolve-list.t Tue Mar 22 14:08:16 2016 -0700
@@ -0,0 +1,75 @@
+Set up some configs
+ $ cat >> $HGRCPATH <<EOF
+ > [extensions]
+ > rebase=
+ > EOF
+ $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+
+Test the instability listing
+ $ hg init r2
+ $ cd r2
+ $ echo a > a && hg ci -Am a
+ adding a
+ $ echo b > b && hg ci -Am b
+ adding b
+ $ echo c > c && hg ci -Am c
+ adding c
+ $ hg up 0
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ echo a >> a && hg ci --amend -m a
+ 2 new unstable changesets
+ $ hg evolve --list
+ d2ae7f538514: b
+ unstable: cb9a9f314b8b (obsolete parent)
+
+ 177f92b77385: c
+ unstable: d2ae7f538514 (unstable parent)
+
+ $ cd ..
+
+Test the bumpedness listing
+ $ hg init r3
+ $ cd r3
+ $ echo a > a && hg ci -Am a
+ adding a
+ $ echo b > b && hg ci --amend -m ab
+ $ hg phase --public --rev 0 --hidden
+ 1 new bumped changesets
+ $ hg evolve --list
+ 88cc282e27fc: ab
+ bumped: cb9a9f314b8b (immutable precursor)
+
+ $ cd ..
+
+Test the divergence listing
+ $ hg init r1
+ $ cd r1
+ $ echo a > a && hg ci -Am a
+ adding a
+ $ hg up 0
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo b > b && hg ci -Am b
+ adding b
+ $ hg up 0
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo c > c && hg ci -Am c
+ adding c
+ created new head
+ $ hg up 0
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo d > d && hg ci -Am d
+ adding d
+ created new head
+ $ hg rebase -s 1 -d 2
+ rebasing 1:d2ae7f538514 "b"
+ $ hg rebase -s 1 -d 3 --hidden --config experimental.allowdivergence=True
+ rebasing 1:d2ae7f538514 "b"
+ 2 new divergent changesets
+ $ hg evolve --list
+ c882616e9d84: b
+ divergent: a922b3733e98 (precursor d2ae7f538514)
+
+ a922b3733e98: b
+ divergent: c882616e9d84 (precursor d2ae7f538514)
+
+ $ cd ..