comparison hgext/evolve.py @ 1634:9ae4e79a28f3

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)
author Kostia Balytskyi <ikostia@fb.com>
date Tue, 22 Mar 2016 14:08:16 -0700
parents 9bcb24c3ba8d
children 91ba7e0daff6
comparison
equal deleted inserted replaced
1633:9bcb24c3ba8d 1634:9ae4e79a28f3
1512 ordering.append(rev) 1512 ordering.append(rev)
1513 1513
1514 ordering.extend(sorted(dependencies)) 1514 ordering.extend(sorted(dependencies))
1515 return ordering 1515 return ordering
1516 1516
1517 def divergentsets(repo, ctx):
1518 """Compute sets of commits divergent with a given one"""
1519 cache = {}
1520 succsets = {}
1521 base = {}
1522 for n in obsolete.allprecursors(repo.obsstore, [ctx.node()]):
1523 if n == ctx.node():
1524 # a node can't be a base for divergence with itself
1525 continue
1526 nsuccsets = obsolete.successorssets(repo, n, cache)
1527 for nsuccset in nsuccsets:
1528 if ctx.node() in nsuccset:
1529 # we are only interested in *other* successor sets
1530 continue
1531 if tuple(nsuccset) in base:
1532 # we already know the latest base for this divergency
1533 continue
1534 base[tuple(nsuccset)] = n
1535 divergence = []
1536 for divset, b in base.iteritems():
1537 divergence.append({
1538 'divergentnodes': divset,
1539 'commonprecursor': b
1540 })
1541
1542 return divergence
1543
1544 def _preparelistctxs(items, condition):
1545 return [item.hex() for item in items if condition(item)]
1546
1547 def _formatctx(fm, ctx):
1548 fm.data(node=ctx.hex())
1549 fm.data(desc=ctx.description())
1550 fm.data(date=ctx.date())
1551 fm.data(user=ctx.user())
1552
1553 def listtroubles(ui, repo, troublecategories, **opts):
1554 """Print all the troubles for the repo (or given revset)"""
1555 troublecategories = troublecategories or ['divergent', 'unstable', 'bumped']
1556 showunstable = 'unstable' in troublecategories
1557 showbumped = 'bumped' in troublecategories
1558 showdivergent = 'divergent' in troublecategories
1559
1560 revs = repo.revs('+'.join("%s()" % t for t in troublecategories))
1561 if opts.get('rev'):
1562 revs = revs & repo.revs(opts.get('rev'))
1563
1564 fm = ui.formatter('evolvelist', opts)
1565 for rev in revs:
1566 ctx = repo[rev]
1567 unpars = _preparelistctxs(ctx.parents(), lambda p: p.unstable())
1568 obspars = _preparelistctxs(ctx.parents(), lambda p: p.obsolete())
1569 imprecs = _preparelistctxs(repo.set("allprecursors(%n)", ctx.node()),
1570 lambda p: not p.mutable())
1571 dsets = divergentsets(repo, ctx)
1572
1573 fm.startitem()
1574 # plain formatter section
1575 hashlen, desclen = 12, 60
1576 desc = ctx.description()
1577 desc = (desc[:desclen] + '...') if len(desc) > desclen else desc
1578 fm.plain('%s: ' % ctx.hex()[:hashlen])
1579 fm.plain('%s\n' % desc)
1580
1581 for unpar in unpars if showunstable else []:
1582 fm.plain(' unstable: %s (unstable parent)\n' % unpar[:hashlen])
1583 for obspar in obspars if showunstable else []:
1584 fm.plain(' unstable: %s (obsolete parent)\n' % obspar[:hashlen])
1585 for imprec in imprecs if showbumped else []:
1586 fm.plain(' bumped: %s (immutable precursor)\n' % imprec[:hashlen])
1587
1588 if dsets and showdivergent:
1589 for dset in dsets:
1590 fm.plain(' divergent: ')
1591 first = True
1592 for n in dset['divergentnodes']:
1593 t = "%s" if first else " %s"
1594 first = False
1595 fm.plain(t % node.hex(n)[:hashlen])
1596 comprec = node.hex(dset['commonprecursor'])[:hashlen]
1597 fm.plain(" (precursor %s)\n" % comprec)
1598 fm.plain("\n")
1599
1600 # templater-friendly section
1601 _formatctx(fm, ctx)
1602 troubles = []
1603 for unpar in unpars:
1604 troubles.append({'troubletype': 'unstable', 'sourcenode': unpar,
1605 'sourcetype': 'unstableparent'})
1606 for obspar in obspars:
1607 troubles.append({'troubletype': 'unstable', 'sourcenode': obspar,
1608 'sourcetype': 'obsoleteparent'})
1609 for imprec in imprecs:
1610 troubles.append({'troubletype': 'bumped', 'sourcenode': imprec,
1611 'sourcetype': 'immutableprecursor'})
1612 for dset in dsets:
1613 divnodes = [{'node': n} for n in dset['divergentnodes']]
1614 troubles.append({'troubletype': 'divergent',
1615 'commonprecursor': dset['commonprecursor'],
1616 'divergentnodes': divnodes})
1617 fm.data(troubles=troubles)
1618
1619 fm.end()
1620
1517 @command('^evolve|stabilize|solve', 1621 @command('^evolve|stabilize|solve',
1518 [('n', 'dry-run', False, 1622 [('n', 'dry-run', False,
1519 _('do not perform actions, just print what would be done')), 1623 _('do not perform actions, just print what would be done')),
1520 ('', 'confirm', False, 1624 ('', 'confirm', False,
1521 _('ask for confirmation before performing the action')), 1625 _('ask for confirmation before performing the action')),
1527 ('', 'divergent', False, _('solves only divergent changesets')), 1631 ('', 'divergent', False, _('solves only divergent changesets')),
1528 ('', 'unstable', False, _('solves only unstable changesets (default)')), 1632 ('', 'unstable', False, _('solves only unstable changesets (default)')),
1529 ('a', 'all', False, _('evolve all troubled changesets related to the ' 1633 ('a', 'all', False, _('evolve all troubled changesets related to the '
1530 'current working directory and its descendants')), 1634 'current working directory and its descendants')),
1531 ('c', 'continue', False, _('continue an interrupted evolution')), 1635 ('c', 'continue', False, _('continue an interrupted evolution')),
1636 ('l', 'list', False, 'provide details on troubled changesets in the repo'),
1532 ] + mergetoolopts, 1637 ] + mergetoolopts,
1533 _('[OPTIONS]...')) 1638 _('[OPTIONS]...'))
1534 def evolve(ui, repo, **opts): 1639 def evolve(ui, repo, **opts):
1535 """solve troubled changesets in your repository 1640 """solve troubled changesets in your repository
1536 1641
1594 wish to resolve, with ``--bumped`` or ``--divergent``. These options are 1699 wish to resolve, with ``--bumped`` or ``--divergent``. These options are
1595 currently mutually exclusive with each other and with ``--unstable`` 1700 currently mutually exclusive with each other and with ``--unstable``
1596 (the default). You can combine ``--bumped`` or ``--divergent`` with 1701 (the default). You can combine ``--bumped`` or ``--divergent`` with
1597 ``--rev``, ``--all``, or ``--any``. 1702 ``--rev``, ``--all``, or ``--any``.
1598 1703
1704 You can also use the evolve command to list the troubles affecting your
1705 repository by using the --list flag. You can choose to display only some
1706 categories of troubles with the --unstable, --divergent or --bumped flags.
1599 """ 1707 """
1600 1708
1601 # Options 1709 # Options
1710 listopt = opts['list']
1602 contopt = opts['continue'] 1711 contopt = opts['continue']
1603 anyopt = opts['any'] 1712 anyopt = opts['any']
1604 allopt = opts['all'] 1713 allopt = opts['all']
1605 startnode = repo['.'] 1714 startnode = repo['.']
1606 dryrunopt = opts['dry_run'] 1715 dryrunopt = opts['dry_run']
1607 confirmopt = opts['confirm'] 1716 confirmopt = opts['confirm']
1608 revopt = opts['rev'] 1717 revopt = opts['rev']
1609 troublecategories = ['bumped', 'divergent', 'unstable'] 1718 troublecategories = ['bumped', 'divergent', 'unstable']
1610 specifiedcategories = [t for t in troublecategories if opts[t]] 1719 specifiedcategories = [t for t in troublecategories if opts[t]]
1720 if listopt:
1721 listtroubles(ui, repo, specifiedcategories, **opts)
1722 return
1723
1611 targetcat = 'unstable' 1724 targetcat = 'unstable'
1612 if 1 < len(specifiedcategories): 1725 if 1 < len(specifiedcategories):
1613 msg = _('cannot specify more than one trouble category to solve (yet)') 1726 msg = _('cannot specify more than one trouble category to solve (yet)')
1614 raise error.Abort(msg) 1727 raise error.Abort(msg)
1615 elif len(specifiedcategories) == 1: 1728 elif len(specifiedcategories) == 1: