hgext3rd/topic/stack.py
changeset 1901 85390446f8c1
parent 1898 2b65c5a6591c
child 1904 f52c02bf47b7
equal deleted inserted replaced
1899:b65f39791f92 1901:85390446f8c1
       
     1 # stack.py - code related to stack workflow
       
     2 #
       
     3 # This software may be used and distributed according to the terms of the
       
     4 # GNU General Public License version 2 or any later version.
       
     5 import collections
       
     6 from mercurial.i18n import _
       
     7 from mercurial import error
       
     8 from mercurial import extensions
       
     9 from mercurial import obsolete
       
    10 
       
    11 def _getstack(repo, topic):
       
    12     # XXX need sorting
       
    13     trevs = repo.revs("topic(%s) - obsolete()", topic)
       
    14     return _orderrevs(repo, trevs)
       
    15 
       
    16 def showstack(ui, repo, topic):
       
    17     if not topic:
       
    18         topic = repo.currenttopic
       
    19     if not topic:
       
    20         raise error.Abort(_('no active topic to list'))
       
    21     for idx, r in enumerate(_getstack(repo, topic)):
       
    22         # super crude initial version
       
    23         l = "%d: %s\n" % (idx, repo[r].description().splitlines()[0])
       
    24         ui.write(l)
       
    25 
       
    26 # Copied from evolve 081605c2e9b6
       
    27 
       
    28 def _orderrevs(repo, revs):
       
    29     """Compute an ordering to solve instability for the given revs
       
    30 
       
    31     revs is a list of unstable revisions.
       
    32 
       
    33     Returns the same revisions ordered to solve their instability from the
       
    34     bottom to the top of the stack that the stabilization process will produce
       
    35     eventually.
       
    36 
       
    37     This ensures the minimal number of stabilizations, as we can stabilize each
       
    38     revision on its final stabilized destination.
       
    39     """
       
    40     # Step 1: Build the dependency graph
       
    41     dependencies, rdependencies = builddependencies(repo, revs)
       
    42     # Step 2: Build the ordering
       
    43     # Remove the revisions with no dependency(A) and add them to the ordering.
       
    44     # Removing these revisions leads to new revisions with no dependency (the
       
    45     # one depending on A) that we can remove from the dependency graph and add
       
    46     # to the ordering. We progress in a similar fashion until the ordering is
       
    47     # built
       
    48     solvablerevs = collections.deque([r for r in sorted(dependencies.keys())
       
    49                                       if not dependencies[r]])
       
    50     ordering = []
       
    51     while solvablerevs:
       
    52         rev = solvablerevs.popleft()
       
    53         for dependent in rdependencies[rev]:
       
    54             dependencies[dependent].remove(rev)
       
    55             if not dependencies[dependent]:
       
    56                 solvablerevs.append(dependent)
       
    57         del dependencies[rev]
       
    58         ordering.append(rev)
       
    59 
       
    60     ordering.extend(sorted(dependencies))
       
    61     return ordering
       
    62 
       
    63 def builddependencies(repo, revs):
       
    64     """returns dependency graphs giving an order to solve instability of revs
       
    65     (see _orderrevs for more information on usage)"""
       
    66 
       
    67     # For each troubled revision we keep track of what instability if any should
       
    68     # be resolved in order to resolve it. Example:
       
    69     # dependencies = {3: [6], 6:[]}
       
    70     # Means that: 6 has no dependency, 3 depends on 6 to be solved
       
    71     dependencies = {}
       
    72     # rdependencies is the inverted dict of dependencies
       
    73     rdependencies = collections.defaultdict(set)
       
    74 
       
    75     for r in revs:
       
    76         dependencies[r] = set()
       
    77         for p in repo[r].parents():
       
    78             try:
       
    79                 succ = _singlesuccessor(repo, p)
       
    80             except MultipleSuccessorsError as exc:
       
    81                 dependencies[r] = exc.successorssets
       
    82                 continue
       
    83             if succ in revs:
       
    84                 dependencies[r].add(succ)
       
    85                 rdependencies[succ].add(r)
       
    86     return dependencies, rdependencies
       
    87 
       
    88 def _singlesuccessor(repo, p):
       
    89     """returns p (as rev) if not obsolete or its unique latest successors
       
    90 
       
    91     fail if there are no such successor"""
       
    92 
       
    93     if not p.obsolete():
       
    94         return p.rev()
       
    95     obs = repo[p]
       
    96     ui = repo.ui
       
    97     newer = obsolete.successorssets(repo, obs.node())
       
    98     # search of a parent which is not killed
       
    99     while not newer:
       
   100         ui.debug("stabilize target %s is plain dead,"
       
   101                  " trying to stabilize on its parent\n" %
       
   102                  obs)
       
   103         obs = obs.parents()[0]
       
   104         newer = obsolete.successorssets(repo, obs.node())
       
   105     if len(newer) > 1 or len(newer[0]) > 1:
       
   106         raise MultipleSuccessorsError(newer)
       
   107 
       
   108     return repo[newer[0][0]].rev()
       
   109 
       
   110 class MultipleSuccessorsError(RuntimeError):
       
   111     """Exception raised by _singlesuccessor when multiple successor sets exists
       
   112 
       
   113     The object contains the list of successorssets in its 'successorssets'
       
   114     attribute to call to easily recover.
       
   115     """
       
   116 
       
   117     def __init__(self, successorssets):
       
   118         self.successorssets = successorssets