|
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 |