##// END OF EJS Templates
discovery: enforce filtering into revlogbaseddag._internalizeall...
Pierre-Yves David -
r20224:34d4a037 default
parent child Browse files
Show More
@@ -1,277 +1,279 b''
1 1 # dagutil.py - dag utilities for mercurial
2 2 #
3 3 # Copyright 2010 Benoit Boissinot <bboissin@gmail.com>
4 4 # and Peter Arrenbrecht <peter@arrenbrecht.ch>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 from node import nullrev
10 10 from i18n import _
11 11
12 12
13 13 class basedag(object):
14 14 '''generic interface for DAGs
15 15
16 16 terms:
17 17 "ix" (short for index) identifies a nodes internally,
18 18 "id" identifies one externally.
19 19
20 20 All params are ixs unless explicitly suffixed otherwise.
21 21 Pluralized params are lists or sets.
22 22 '''
23 23
24 24 def __init__(self):
25 25 self._inverse = None
26 26
27 27 def nodeset(self):
28 28 '''set of all node idxs'''
29 29 raise NotImplementedError
30 30
31 31 def heads(self):
32 32 '''list of head ixs'''
33 33 raise NotImplementedError
34 34
35 35 def parents(self, ix):
36 36 '''list of parents ixs of ix'''
37 37 raise NotImplementedError
38 38
39 39 def inverse(self):
40 40 '''inverse DAG, where parents becomes children, etc.'''
41 41 raise NotImplementedError
42 42
43 43 def ancestorset(self, starts, stops=None):
44 44 '''
45 45 set of all ancestors of starts (incl), but stop walk at stops (excl)
46 46 '''
47 47 raise NotImplementedError
48 48
49 49 def descendantset(self, starts, stops=None):
50 50 '''
51 51 set of all descendants of starts (incl), but stop walk at stops (excl)
52 52 '''
53 53 return self.inverse().ancestorset(starts, stops)
54 54
55 55 def headsetofconnecteds(self, ixs):
56 56 '''
57 57 subset of connected list of ixs so that no node has a descendant in it
58 58
59 59 By "connected list" we mean that if an ancestor and a descendant are in
60 60 the list, then so is at least one path connecting them.
61 61 '''
62 62 raise NotImplementedError
63 63
64 64 def externalize(self, ix):
65 65 '''return a list of (or set if given a set) of node ids'''
66 66 return self._externalize(ix)
67 67
68 68 def externalizeall(self, ixs):
69 69 '''return a list of (or set if given a set) of node ids'''
70 70 ids = self._externalizeall(ixs)
71 71 if isinstance(ixs, set):
72 72 return set(ids)
73 73 return list(ids)
74 74
75 75 def internalize(self, id):
76 76 '''return a list of (or set if given a set) of node ixs'''
77 77 return self._internalize(id)
78 78
79 79 def internalizeall(self, ids, filterunknown=False):
80 80 '''return a list of (or set if given a set) of node ids'''
81 81 ixs = self._internalizeall(ids, filterunknown)
82 82 if isinstance(ids, set):
83 83 return set(ixs)
84 84 return list(ixs)
85 85
86 86
87 87 class genericdag(basedag):
88 88 '''generic implementations for DAGs'''
89 89
90 90 def ancestorset(self, starts, stops=None):
91 91 stops = stops and set(stops) or set()
92 92 seen = set()
93 93 pending = list(starts)
94 94 while pending:
95 95 n = pending.pop()
96 96 if n not in seen and n not in stops:
97 97 seen.add(n)
98 98 pending.extend(self.parents(n))
99 99 return seen
100 100
101 101 def headsetofconnecteds(self, ixs):
102 102 hds = set(ixs)
103 103 if not hds:
104 104 return hds
105 105 for n in ixs:
106 106 for p in self.parents(n):
107 107 hds.discard(p)
108 108 assert hds
109 109 return hds
110 110
111 111
112 112 class revlogbaseddag(basedag):
113 113 '''generic dag interface to a revlog'''
114 114
115 115 def __init__(self, revlog, nodeset):
116 116 basedag.__init__(self)
117 117 self._revlog = revlog
118 118 self._heads = None
119 119 self._nodeset = nodeset
120 120
121 121 def nodeset(self):
122 122 return self._nodeset
123 123
124 124 def heads(self):
125 125 if self._heads is None:
126 126 self._heads = self._getheads()
127 127 return self._heads
128 128
129 129 def _externalize(self, ix):
130 130 return self._revlog.index[ix][7]
131 131 def _externalizeall(self, ixs):
132 132 idx = self._revlog.index
133 133 return [idx[i][7] for i in ixs]
134 134
135 135 def _internalize(self, id):
136 136 ix = self._revlog.rev(id)
137 137 if ix == nullrev:
138 138 raise LookupError(id, self._revlog.indexfile, _('nullid'))
139 139 return ix
140 140 def _internalizeall(self, ids, filterunknown):
141 141 rl = self._revlog
142 142 if filterunknown:
143 143 return [r for r in map(rl.nodemap.get, ids)
144 if r is not None and r != nullrev]
144 if (r is not None
145 and r != nullrev
146 and r not in rl.filteredrevs)]
145 147 return map(self._internalize, ids)
146 148
147 149
148 150 class revlogdag(revlogbaseddag):
149 151 '''dag interface to a revlog'''
150 152
151 153 def __init__(self, revlog):
152 154 revlogbaseddag.__init__(self, revlog, set(revlog))
153 155
154 156 def _getheads(self):
155 157 return [r for r in self._revlog.headrevs() if r != nullrev]
156 158
157 159 def parents(self, ix):
158 160 rlog = self._revlog
159 161 idx = rlog.index
160 162 revdata = idx[ix]
161 163 prev = revdata[5]
162 164 if prev != nullrev:
163 165 prev2 = revdata[6]
164 166 if prev2 == nullrev:
165 167 return [prev]
166 168 return [prev, prev2]
167 169 prev2 = revdata[6]
168 170 if prev2 != nullrev:
169 171 return [prev2]
170 172 return []
171 173
172 174 def inverse(self):
173 175 if self._inverse is None:
174 176 self._inverse = inverserevlogdag(self)
175 177 return self._inverse
176 178
177 179 def ancestorset(self, starts, stops=None):
178 180 rlog = self._revlog
179 181 idx = rlog.index
180 182 stops = stops and set(stops) or set()
181 183 seen = set()
182 184 pending = list(starts)
183 185 while pending:
184 186 rev = pending.pop()
185 187 if rev not in seen and rev not in stops:
186 188 seen.add(rev)
187 189 revdata = idx[rev]
188 190 for i in [5, 6]:
189 191 prev = revdata[i]
190 192 if prev != nullrev:
191 193 pending.append(prev)
192 194 return seen
193 195
194 196 def headsetofconnecteds(self, ixs):
195 197 if not ixs:
196 198 return set()
197 199 rlog = self._revlog
198 200 idx = rlog.index
199 201 headrevs = set(ixs)
200 202 for rev in ixs:
201 203 revdata = idx[rev]
202 204 for i in [5, 6]:
203 205 prev = revdata[i]
204 206 if prev != nullrev:
205 207 headrevs.discard(prev)
206 208 assert headrevs
207 209 return headrevs
208 210
209 211 def linearize(self, ixs):
210 212 '''linearize and topologically sort a list of revisions
211 213
212 214 The linearization process tries to create long runs of revs where
213 215 a child rev comes immediately after its first parent. This is done by
214 216 visiting the heads of the given revs in inverse topological order,
215 217 and for each visited rev, visiting its second parent, then its first
216 218 parent, then adding the rev itself to the output list.
217 219 '''
218 220 sorted = []
219 221 visit = list(self.headsetofconnecteds(ixs))
220 222 visit.sort(reverse=True)
221 223 finished = set()
222 224
223 225 while visit:
224 226 cur = visit.pop()
225 227 if cur < 0:
226 228 cur = -cur - 1
227 229 if cur not in finished:
228 230 sorted.append(cur)
229 231 finished.add(cur)
230 232 else:
231 233 visit.append(-cur - 1)
232 234 visit += [p for p in self.parents(cur)
233 235 if p in ixs and p not in finished]
234 236 assert len(sorted) == len(ixs)
235 237 return sorted
236 238
237 239
238 240 class inverserevlogdag(revlogbaseddag, genericdag):
239 241 '''inverse of an existing revlog dag; see revlogdag.inverse()'''
240 242
241 243 def __init__(self, orig):
242 244 revlogbaseddag.__init__(self, orig._revlog, orig._nodeset)
243 245 self._orig = orig
244 246 self._children = {}
245 247 self._roots = []
246 248 self._walkfrom = len(self._revlog) - 1
247 249
248 250 def _walkto(self, walkto):
249 251 rev = self._walkfrom
250 252 cs = self._children
251 253 roots = self._roots
252 254 idx = self._revlog.index
253 255 while rev >= walkto:
254 256 data = idx[rev]
255 257 isroot = True
256 258 for prev in [data[5], data[6]]: # parent revs
257 259 if prev != nullrev:
258 260 cs.setdefault(prev, []).append(rev)
259 261 isroot = False
260 262 if isroot:
261 263 roots.append(rev)
262 264 rev -= 1
263 265 self._walkfrom = rev
264 266
265 267 def _getheads(self):
266 268 self._walkto(nullrev)
267 269 return self._roots
268 270
269 271 def parents(self, ix):
270 272 if ix is None:
271 273 return []
272 274 if ix <= self._walkfrom:
273 275 self._walkto(ix)
274 276 return self._children.get(ix, [])
275 277
276 278 def inverse(self):
277 279 return self._orig
General Comments 0
You need to be logged in to leave comments. Login now