32 |
32 |
33 if precnodeid in repo: |
33 if precnodeid in repo: |
34 yield precnodeid |
34 yield precnodeid |
35 else: |
35 else: |
36 stack.append(precnodeid) |
36 stack.append(precnodeid) |
|
37 |
|
38 def _filterprunes(markers): |
|
39 """return a set with no prune markers""" |
|
40 return set(m for m in markers if m[1]) |
|
41 |
|
42 def exclusivemarkers(repo, nodes): |
|
43 """set of markers relevant to "nodes" but no other locally-known nodes |
|
44 |
|
45 This function compute the set of markers "exclusive" to a locally-known |
|
46 node. This means we walk the markers starting from <nodes> until we reach a |
|
47 locally-known precursors outside of <nodes>. Element of <nodes> with |
|
48 locally-known successors outside of <nodes> are ignored (since their |
|
49 precursors markers are also relevant to these successors). |
|
50 |
|
51 For example: |
|
52 |
|
53 # (A0 rewritten as A1) |
|
54 # |
|
55 # A0 <-1- A1 # Marker "1" is exclusive to A1 |
|
56 |
|
57 or |
|
58 |
|
59 # (A0 rewritten as AX; AX rewritten as A1; AX is unkown locally) |
|
60 # |
|
61 # <-1- A0 <-2- AX <-3- A1 # Marker "2,3" are exclusive to A1 |
|
62 |
|
63 or |
|
64 |
|
65 # (A0 has unknown precursors, A0 rewritten as A1 and A2 (divergence)) |
|
66 # |
|
67 # <-2- A1 # Marker "2" is exclusive to A0,A1 |
|
68 # / |
|
69 # <-1- A0 |
|
70 # \ |
|
71 # <-3- A2 # Marker "3" is exclusive to A0,A2 |
|
72 # |
|
73 # in addition: |
|
74 # |
|
75 # Markers "2,3" are exclusive to A1,A2 |
|
76 # Markers "1,2,3" are exclusive to A0,A1,A2 |
|
77 |
|
78 See test/test-obsolete-bundle-strip.t for more examples. |
|
79 |
|
80 An example usage is strip. When stripping a changeset, we also want to |
|
81 strip the markers exclusive to this changeset. Otherwise we would have |
|
82 "dangling"" obsolescence markers from its precursors: Obsolescence markers |
|
83 marking a node as obsolete without any successors available locally. |
|
84 |
|
85 As for relevant markers, the prune markers for children will be followed. |
|
86 Of course, they will only be followed if the pruned children is |
|
87 locally-known. Since the prune markers are relevant to the pruned node. |
|
88 However, while prune markers are considered relevant to the parent of the |
|
89 pruned changesets, prune markers for locally-known changeset (with no |
|
90 successors) are considered exclusive to the pruned nodes. This allows |
|
91 to strip the prune markers (with the rest of the exclusive chain) alongside |
|
92 the pruned changesets. |
|
93 """ |
|
94 # running on a filtered repository would be dangerous as markers could be |
|
95 # reported as exclusive when they are relevant for other filtered nodes. |
|
96 unfi = repo.unfiltered() |
|
97 |
|
98 # shortcut to various useful item |
|
99 nm = unfi.changelog.nodemap |
|
100 precursorsmarkers = unfi.obsstore.precursors |
|
101 successormarkers = unfi.obsstore.successors |
|
102 childrenmarkers = unfi.obsstore.children |
|
103 |
|
104 # exclusive markers (return of the function) |
|
105 exclmarkers = set() |
|
106 # we need fast membership testing |
|
107 nodes = set(nodes) |
|
108 # looking for head in the obshistory |
|
109 # |
|
110 # XXX we are ignoring all issues in regard with cycle for now. |
|
111 stack = [n for n in nodes if not _filterprunes(successormarkers.get(n, ()))] |
|
112 stack.sort() |
|
113 # nodes already stacked |
|
114 seennodes = set(stack) |
|
115 while stack: |
|
116 current = stack.pop() |
|
117 # fetch precursors markers |
|
118 markers = list(precursorsmarkers.get(current, ())) |
|
119 # extend the list with prune markers |
|
120 for mark in successormarkers.get(current, ()): |
|
121 if not mark[1]: |
|
122 markers.append(mark) |
|
123 # and markers from children (looking for prune) |
|
124 for mark in childrenmarkers.get(current, ()): |
|
125 if not mark[1]: |
|
126 markers.append(mark) |
|
127 # traverse the markers |
|
128 for mark in markers: |
|
129 if mark in exclmarkers: |
|
130 # markers already selected |
|
131 continue |
|
132 |
|
133 # If the markers is about the current node, select it |
|
134 # |
|
135 # (this delay the addition of markers from children) |
|
136 if mark[1] or mark[0] == current: |
|
137 exclmarkers.add(mark) |
|
138 |
|
139 # should we keep traversing through the precursors? |
|
140 prec = mark[0] |
|
141 |
|
142 # nodes in the stack or already processed |
|
143 if prec in seennodes: |
|
144 continue |
|
145 |
|
146 # is this a locally known node ? |
|
147 known = prec in nm |
|
148 # if locally-known and not in the <nodes> set the traversal |
|
149 # stop here. |
|
150 if known and prec not in nodes: |
|
151 continue |
|
152 |
|
153 # do not keep going if there are unselected markers pointing to this |
|
154 # nodes. If we end up traversing these unselected markers later the |
|
155 # node will be taken care of at that point. |
|
156 precmarkers = _filterprunes(successormarkers.get(prec)) |
|
157 if precmarkers.issubset(exclmarkers): |
|
158 seennodes.add(prec) |
|
159 stack.append(prec) |
|
160 |
|
161 return exclmarkers |
37 |
162 |
38 def successorssets(repo, initialnode, cache=None): |
163 def successorssets(repo, initialnode, cache=None): |
39 """Return set of all latest successors of initial nodes |
164 """Return set of all latest successors of initial nodes |
40 |
165 |
41 The successors set of a changeset A are the group of revisions that succeed |
166 The successors set of a changeset A are the group of revisions that succeed |