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