comparison mercurial/obsutil.py @ 33143:d09ae850296d

obsutil: move 'exclusivemarkers' to the new modules We have a new 'obsutil' module now. We move the high level utility there to bring 'obsolete.py' back to a more reasonable size.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Tue, 27 Jun 2017 01:11:56 +0200
parents 4f49810a1011
children d0e5bf12f314
comparison
equal deleted inserted replaced
33142:4f49810a1011 33143:d09ae850296d
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