Mercurial > evolve
view hgext3rd/topic/stack.py @ 1961:d9c7fced94fc
stack: prevent crash when topic is rooted on nullid
We exclude null from displayed base. This was causing a crash when trying to
display its description (we'll still have to handle the empty description case,
but it is now less urgent).
author | Pierre-Yves David <pierre-yves.david@fb.com> |
---|---|
date | Mon, 04 Apr 2016 00:28:03 -0700 |
parents | ea5553e47027 |
children | 137f8b04901e |
line wrap: on
line source
# stack.py - code related to stack workflow # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. import collections from mercurial.i18n import _ from mercurial import ( error, node, obsolete, ) def getstack(repo, topic): # XXX need sorting trevs = repo.revs("topic(%s) - obsolete()", topic) return _orderrevs(repo, trevs) def showstack(ui, repo, topic, opts): if not topic: topic = repo.currenttopic if not topic: raise error.Abort(_('no active topic to list')) fm = ui.formatter('topicstack', opts) prev = None entries = [] for idx, r in enumerate(getstack(repo, topic), 1): ctx = repo[r] p1 = ctx.p1() if p1.obsolete(): p1 = repo[_singlesuccessor(repo, p1)] if p1.rev() != prev and p1.node() != node.nullid: entries.append((None, p1)) entries.append((idx, ctx)) prev = r # super crude initial version for idx, ctx in entries[::-1]: if idx is None: symbol = '^' state = 'base' elif repo.revs('%d and parents()', ctx.rev()): symbol = '@' state = 'current' elif repo.revs('%d and unstable()', ctx.rev()): symbol = '$' state = 'unstable' else: symbol = ':' state = 'clean' fm.startitem() if idx is None: fm.plain(' ') else: fm.write('topic.stack.index', 't%d', idx, label='topic.stack.index topic.stack.index.%s' % state) fm.write('topic.stack.state.symbol', '%s', symbol, label='topic.stack.state topic.stack.state.%s' % state) fm.plain(' ') fm.write('topic.stack.desc', '%s', ctx.description().splitlines()[0], label='topic.stack.desc topic.stack.desc.%s' % state) fm.condwrite(state != 'clean' and idx is not None, 'topic.stack.state', ' (%s)', state, label='topic.stack.state topic.stack.state.%s' % state) fm.plain('\n') fm.end() # Copied from evolve 081605c2e9b6 def _orderrevs(repo, revs): """Compute an ordering to solve instability for the given revs revs is a list of unstable revisions. Returns the same revisions ordered to solve their instability from the bottom to the top of the stack that the stabilization process will produce eventually. This ensures the minimal number of stabilizations, as we can stabilize each revision on its final stabilized destination. """ # Step 1: Build the dependency graph dependencies, rdependencies = builddependencies(repo, revs) # Step 2: Build the ordering # Remove the revisions with no dependency(A) and add them to the ordering. # Removing these revisions leads to new revisions with no dependency (the # one depending on A) that we can remove from the dependency graph and add # to the ordering. We progress in a similar fashion until the ordering is # built solvablerevs = [r for r in sorted(dependencies.keys()) if not dependencies[r]] ordering = [] while solvablerevs: rev = solvablerevs.pop() for dependent in rdependencies[rev]: dependencies[dependent].remove(rev) if not dependencies[dependent]: solvablerevs.append(dependent) del dependencies[rev] ordering.append(rev) ordering.extend(sorted(dependencies)) return ordering def builddependencies(repo, revs): """returns dependency graphs giving an order to solve instability of revs (see _orderrevs for more information on usage)""" # For each troubled revision we keep track of what instability if any should # be resolved in order to resolve it. Example: # dependencies = {3: [6], 6:[]} # Means that: 6 has no dependency, 3 depends on 6 to be solved dependencies = {} # rdependencies is the inverted dict of dependencies rdependencies = collections.defaultdict(set) for r in revs: dependencies[r] = set() for p in repo[r].parents(): try: succ = _singlesuccessor(repo, p) except MultipleSuccessorsError as exc: dependencies[r] = exc.successorssets continue if succ in revs: dependencies[r].add(succ) rdependencies[succ].add(r) return dependencies, rdependencies def _singlesuccessor(repo, p): """returns p (as rev) if not obsolete or its unique latest successors fail if there are no such successor""" if not p.obsolete(): return p.rev() obs = repo[p] ui = repo.ui newer = obsolete.successorssets(repo, obs.node()) # search of a parent which is not killed while not newer: ui.debug("stabilize target %s is plain dead," " trying to stabilize on its parent\n" % obs) obs = obs.parents()[0] newer = obsolete.successorssets(repo, obs.node()) if len(newer) > 1 or len(newer[0]) > 1: raise MultipleSuccessorsError(newer) return repo[newer[0][0]].rev() class MultipleSuccessorsError(RuntimeError): """Exception raised by _singlesuccessor when multiple successor sets exists The object contains the list of successorssets in its 'successorssets' attribute to call to easily recover. """ def __init__(self, successorssets): self.successorssets = successorssets