# HG changeset patch # User Kostia Balytskyi # Date 1458680896 25200 # Node ID 9ae4e79a28f3c09e76567f68be3d6f82e650e55b # Parent 9bcb24c3ba8d2b5a7082ae61ac2193e867234822 evolve--list: initial implementation This implementation does not work with '-T json' since it needs multiple levels of depth. There is various small issue with the current data, the test coverage is a bit low and the output is likely to change, but this is a good step forward. Sample output: 01a3e66ba030: e (amended) unstable: 1995fc658ad6 (unstable parent) divergent: 84e1c6ae319d d3b90e9c84ab (precursor 3efa43a7427b) divergent: add9a356b8cf (precursor 3efa43a7427b) add9a356b8cf: e (rebased) divergent: 84e1c6ae319d d3b90e9c84ab (precursor 3efa43a7427b) divergent: 01a3e66ba030 (precursor 3efa43a7427b) 84e1c6ae319d: e (e+f split) unstable: 1995fc658ad6 (unstable parent) divergent: 01a3e66ba030 (precursor 3efa43a7427b) divergent: add9a356b8cf (precursor 3efa43a7427b) d3b90e9c84ab: f (e+f split) unstable: 84e1c6ae319d (unstable parent) divergent: 01a3e66ba030 (precursor 3efa43a7427b) divergent: add9a356b8cf (precursor 3efa43a7427b) 8febfaee0dd1: c unstable: 43765473b851 (obsolete parent) bumped: b36d99df9f41 (immutable precursor) 1995fc658ad6: d: commit with a long happy message, ababagalamaga, ababagal... unstable: 8febfaee0dd1 (unstable parent) fa8498ad030f: aa unstable: d3b90e9c84ab (unstable parent) diff -r 9bcb24c3ba8d -r 9ae4e79a28f3 hgext/evolve.py --- 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)') diff -r 9bcb24c3ba8d -r 9ae4e79a28f3 tests/test-evolve-list.t --- /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 < [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 ..