##// END OF EJS Templates
ancestor: drop an unused local variable assignment...
Matt Harbison -
r44420:5ce6daa6 default
parent child Browse files
Show More
@@ -1,431 +1,431 b''
1 # ancestor.py - generic DAG ancestor algorithm for mercurial
1 # ancestor.py - generic DAG ancestor algorithm for mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import heapq
10 import heapq
11
11
12 from .node import nullrev
12 from .node import nullrev
13 from . import (
13 from . import (
14 dagop,
14 dagop,
15 policy,
15 policy,
16 pycompat,
16 pycompat,
17 )
17 )
18
18
19 parsers = policy.importmod('parsers')
19 parsers = policy.importmod('parsers')
20
20
21
21
22 def commonancestorsheads(pfunc, *nodes):
22 def commonancestorsheads(pfunc, *nodes):
23 """Returns a set with the heads of all common ancestors of all nodes,
23 """Returns a set with the heads of all common ancestors of all nodes,
24 heads(::nodes[0] and ::nodes[1] and ...) .
24 heads(::nodes[0] and ::nodes[1] and ...) .
25
25
26 pfunc must return a list of parent vertices for a given vertex.
26 pfunc must return a list of parent vertices for a given vertex.
27 """
27 """
28 if not isinstance(nodes, set):
28 if not isinstance(nodes, set):
29 nodes = set(nodes)
29 nodes = set(nodes)
30 if nullrev in nodes:
30 if nullrev in nodes:
31 return set()
31 return set()
32 if len(nodes) <= 1:
32 if len(nodes) <= 1:
33 return nodes
33 return nodes
34
34
35 allseen = (1 << len(nodes)) - 1
35 allseen = (1 << len(nodes)) - 1
36 seen = [0] * (max(nodes) + 1)
36 seen = [0] * (max(nodes) + 1)
37 for i, n in enumerate(nodes):
37 for i, n in enumerate(nodes):
38 seen[n] = 1 << i
38 seen[n] = 1 << i
39 poison = 1 << (i + 1)
39 poison = 1 << (i + 1)
40
40
41 gca = set()
41 gca = set()
42 interesting = len(nodes)
42 interesting = len(nodes)
43 nv = len(seen) - 1
43 nv = len(seen) - 1
44 while nv >= 0 and interesting:
44 while nv >= 0 and interesting:
45 v = nv
45 v = nv
46 nv -= 1
46 nv -= 1
47 if not seen[v]:
47 if not seen[v]:
48 continue
48 continue
49 sv = seen[v]
49 sv = seen[v]
50 if sv < poison:
50 if sv < poison:
51 interesting -= 1
51 interesting -= 1
52 if sv == allseen:
52 if sv == allseen:
53 gca.add(v)
53 gca.add(v)
54 sv |= poison
54 sv |= poison
55 if v in nodes:
55 if v in nodes:
56 # history is linear
56 # history is linear
57 return {v}
57 return {v}
58 if sv < poison:
58 if sv < poison:
59 for p in pfunc(v):
59 for p in pfunc(v):
60 sp = seen[p]
60 sp = seen[p]
61 if p == nullrev:
61 if p == nullrev:
62 continue
62 continue
63 if sp == 0:
63 if sp == 0:
64 seen[p] = sv
64 seen[p] = sv
65 interesting += 1
65 interesting += 1
66 elif sp != sv:
66 elif sp != sv:
67 seen[p] |= sv
67 seen[p] |= sv
68 else:
68 else:
69 for p in pfunc(v):
69 for p in pfunc(v):
70 if p == nullrev:
70 if p == nullrev:
71 continue
71 continue
72 sp = seen[p]
72 sp = seen[p]
73 if sp and sp < poison:
73 if sp and sp < poison:
74 interesting -= 1
74 interesting -= 1
75 seen[p] = sv
75 seen[p] = sv
76 return gca
76 return gca
77
77
78
78
79 def ancestors(pfunc, *orignodes):
79 def ancestors(pfunc, *orignodes):
80 """
80 """
81 Returns the common ancestors of a and b that are furthest from a
81 Returns the common ancestors of a and b that are furthest from a
82 root (as measured by longest path).
82 root (as measured by longest path).
83
83
84 pfunc must return a list of parent vertices for a given vertex.
84 pfunc must return a list of parent vertices for a given vertex.
85 """
85 """
86
86
87 def deepest(nodes):
87 def deepest(nodes):
88 interesting = {}
88 interesting = {}
89 count = max(nodes) + 1
89 count = max(nodes) + 1
90 depth = [0] * count
90 depth = [0] * count
91 seen = [0] * count
91 seen = [0] * count
92 mapping = []
92 mapping = []
93 for (i, n) in enumerate(sorted(nodes)):
93 for (i, n) in enumerate(sorted(nodes)):
94 depth[n] = 1
94 depth[n] = 1
95 b = 1 << i
95 b = 1 << i
96 seen[n] = b
96 seen[n] = b
97 interesting[b] = 1
97 interesting[b] = 1
98 mapping.append((b, n))
98 mapping.append((b, n))
99 nv = count - 1
99 nv = count - 1
100 while nv >= 0 and len(interesting) > 1:
100 while nv >= 0 and len(interesting) > 1:
101 v = nv
101 v = nv
102 nv -= 1
102 nv -= 1
103 dv = depth[v]
103 dv = depth[v]
104 if dv == 0:
104 if dv == 0:
105 continue
105 continue
106 sv = seen[v]
106 sv = seen[v]
107 for p in pfunc(v):
107 for p in pfunc(v):
108 if p == nullrev:
108 if p == nullrev:
109 continue
109 continue
110 dp = depth[p]
110 dp = depth[p]
111 nsp = sp = seen[p]
111 nsp = sp = seen[p]
112 if dp <= dv:
112 if dp <= dv:
113 depth[p] = dv + 1
113 depth[p] = dv + 1
114 if sp != sv:
114 if sp != sv:
115 interesting[sv] += 1
115 interesting[sv] += 1
116 nsp = seen[p] = sv
116 nsp = seen[p] = sv
117 if sp:
117 if sp:
118 interesting[sp] -= 1
118 interesting[sp] -= 1
119 if interesting[sp] == 0:
119 if interesting[sp] == 0:
120 del interesting[sp]
120 del interesting[sp]
121 elif dv == dp - 1:
121 elif dv == dp - 1:
122 nsp = sp | sv
122 nsp = sp | sv
123 if nsp == sp:
123 if nsp == sp:
124 continue
124 continue
125 seen[p] = nsp
125 seen[p] = nsp
126 interesting.setdefault(nsp, 0)
126 interesting.setdefault(nsp, 0)
127 interesting[nsp] += 1
127 interesting[nsp] += 1
128 interesting[sp] -= 1
128 interesting[sp] -= 1
129 if interesting[sp] == 0:
129 if interesting[sp] == 0:
130 del interesting[sp]
130 del interesting[sp]
131 interesting[sv] -= 1
131 interesting[sv] -= 1
132 if interesting[sv] == 0:
132 if interesting[sv] == 0:
133 del interesting[sv]
133 del interesting[sv]
134
134
135 if len(interesting) != 1:
135 if len(interesting) != 1:
136 return []
136 return []
137
137
138 k = 0
138 k = 0
139 for i in interesting:
139 for i in interesting:
140 k |= i
140 k |= i
141 return set(n for (i, n) in mapping if k & i)
141 return set(n for (i, n) in mapping if k & i)
142
142
143 gca = commonancestorsheads(pfunc, *orignodes)
143 gca = commonancestorsheads(pfunc, *orignodes)
144
144
145 if len(gca) <= 1:
145 if len(gca) <= 1:
146 return gca
146 return gca
147 return deepest(gca)
147 return deepest(gca)
148
148
149
149
150 class incrementalmissingancestors(object):
150 class incrementalmissingancestors(object):
151 '''persistent state used to calculate missing ancestors incrementally
151 '''persistent state used to calculate missing ancestors incrementally
152
152
153 Although similar in spirit to lazyancestors below, this is a separate class
153 Although similar in spirit to lazyancestors below, this is a separate class
154 because trying to support contains and missingancestors operations with the
154 because trying to support contains and missingancestors operations with the
155 same internal data structures adds needless complexity.'''
155 same internal data structures adds needless complexity.'''
156
156
157 def __init__(self, pfunc, bases):
157 def __init__(self, pfunc, bases):
158 self.bases = set(bases)
158 self.bases = set(bases)
159 if not self.bases:
159 if not self.bases:
160 self.bases.add(nullrev)
160 self.bases.add(nullrev)
161 self.pfunc = pfunc
161 self.pfunc = pfunc
162
162
163 def hasbases(self):
163 def hasbases(self):
164 '''whether the common set has any non-trivial bases'''
164 '''whether the common set has any non-trivial bases'''
165 return self.bases and self.bases != {nullrev}
165 return self.bases and self.bases != {nullrev}
166
166
167 def addbases(self, newbases):
167 def addbases(self, newbases):
168 '''grow the ancestor set by adding new bases'''
168 '''grow the ancestor set by adding new bases'''
169 self.bases.update(newbases)
169 self.bases.update(newbases)
170
170
171 def basesheads(self):
171 def basesheads(self):
172 return dagop.headrevs(self.bases, self.pfunc)
172 return dagop.headrevs(self.bases, self.pfunc)
173
173
174 def removeancestorsfrom(self, revs):
174 def removeancestorsfrom(self, revs):
175 '''remove all ancestors of bases from the set revs (in place)'''
175 '''remove all ancestors of bases from the set revs (in place)'''
176 bases = self.bases
176 bases = self.bases
177 pfunc = self.pfunc
177 pfunc = self.pfunc
178 revs.difference_update(bases)
178 revs.difference_update(bases)
179 # nullrev is always an ancestor
179 # nullrev is always an ancestor
180 revs.discard(nullrev)
180 revs.discard(nullrev)
181 if not revs:
181 if not revs:
182 return
182 return
183 # anything in revs > start is definitely not an ancestor of bases
183 # anything in revs > start is definitely not an ancestor of bases
184 # revs <= start needs to be investigated
184 # revs <= start needs to be investigated
185 start = max(bases)
185 start = max(bases)
186 keepcount = sum(1 for r in revs if r > start)
186 keepcount = sum(1 for r in revs if r > start)
187 if len(revs) == keepcount:
187 if len(revs) == keepcount:
188 # no revs to consider
188 # no revs to consider
189 return
189 return
190
190
191 for curr in pycompat.xrange(start, min(revs) - 1, -1):
191 for curr in pycompat.xrange(start, min(revs) - 1, -1):
192 if curr not in bases:
192 if curr not in bases:
193 continue
193 continue
194 revs.discard(curr)
194 revs.discard(curr)
195 bases.update(pfunc(curr))
195 bases.update(pfunc(curr))
196 if len(revs) == keepcount:
196 if len(revs) == keepcount:
197 # no more potential revs to discard
197 # no more potential revs to discard
198 break
198 break
199
199
200 def missingancestors(self, revs):
200 def missingancestors(self, revs):
201 '''return all the ancestors of revs that are not ancestors of self.bases
201 '''return all the ancestors of revs that are not ancestors of self.bases
202
202
203 This may include elements from revs.
203 This may include elements from revs.
204
204
205 Equivalent to the revset (::revs - ::self.bases). Revs are returned in
205 Equivalent to the revset (::revs - ::self.bases). Revs are returned in
206 revision number order, which is a topological order.'''
206 revision number order, which is a topological order.'''
207 revsvisit = set(revs)
207 revsvisit = set(revs)
208 basesvisit = self.bases
208 basesvisit = self.bases
209 pfunc = self.pfunc
209 pfunc = self.pfunc
210 bothvisit = revsvisit.intersection(basesvisit)
210 bothvisit = revsvisit.intersection(basesvisit)
211 revsvisit.difference_update(bothvisit)
211 revsvisit.difference_update(bothvisit)
212 if not revsvisit:
212 if not revsvisit:
213 return []
213 return []
214
214
215 start = max(max(revsvisit), max(basesvisit))
215 start = max(max(revsvisit), max(basesvisit))
216 # At this point, we hold the invariants that:
216 # At this point, we hold the invariants that:
217 # - revsvisit is the set of nodes we know are an ancestor of at least
217 # - revsvisit is the set of nodes we know are an ancestor of at least
218 # one of the nodes in revs
218 # one of the nodes in revs
219 # - basesvisit is the same for bases
219 # - basesvisit is the same for bases
220 # - bothvisit is the set of nodes we know are ancestors of at least one
220 # - bothvisit is the set of nodes we know are ancestors of at least one
221 # of the nodes in revs and one of the nodes in bases. bothvisit and
221 # of the nodes in revs and one of the nodes in bases. bothvisit and
222 # revsvisit are mutually exclusive, but bothvisit is a subset of
222 # revsvisit are mutually exclusive, but bothvisit is a subset of
223 # basesvisit.
223 # basesvisit.
224 # Now we walk down in reverse topo order, adding parents of nodes
224 # Now we walk down in reverse topo order, adding parents of nodes
225 # already visited to the sets while maintaining the invariants. When a
225 # already visited to the sets while maintaining the invariants. When a
226 # node is found in both revsvisit and basesvisit, it is removed from
226 # node is found in both revsvisit and basesvisit, it is removed from
227 # revsvisit and added to bothvisit. When revsvisit becomes empty, there
227 # revsvisit and added to bothvisit. When revsvisit becomes empty, there
228 # are no more ancestors of revs that aren't also ancestors of bases, so
228 # are no more ancestors of revs that aren't also ancestors of bases, so
229 # exit.
229 # exit.
230
230
231 missing = []
231 missing = []
232 for curr in pycompat.xrange(start, nullrev, -1):
232 for curr in pycompat.xrange(start, nullrev, -1):
233 if not revsvisit:
233 if not revsvisit:
234 break
234 break
235
235
236 if curr in bothvisit:
236 if curr in bothvisit:
237 bothvisit.remove(curr)
237 bothvisit.remove(curr)
238 # curr's parents might have made it into revsvisit through
238 # curr's parents might have made it into revsvisit through
239 # another path
239 # another path
240 for p in pfunc(curr):
240 for p in pfunc(curr):
241 revsvisit.discard(p)
241 revsvisit.discard(p)
242 basesvisit.add(p)
242 basesvisit.add(p)
243 bothvisit.add(p)
243 bothvisit.add(p)
244 continue
244 continue
245
245
246 if curr in revsvisit:
246 if curr in revsvisit:
247 missing.append(curr)
247 missing.append(curr)
248 revsvisit.remove(curr)
248 revsvisit.remove(curr)
249 thisvisit = revsvisit
249 thisvisit = revsvisit
250 othervisit = basesvisit
250 othervisit = basesvisit
251 elif curr in basesvisit:
251 elif curr in basesvisit:
252 thisvisit = basesvisit
252 thisvisit = basesvisit
253 othervisit = revsvisit
253 othervisit = revsvisit
254 else:
254 else:
255 # not an ancestor of revs or bases: ignore
255 # not an ancestor of revs or bases: ignore
256 continue
256 continue
257
257
258 for p in pfunc(curr):
258 for p in pfunc(curr):
259 if p == nullrev:
259 if p == nullrev:
260 pass
260 pass
261 elif p in othervisit or p in bothvisit:
261 elif p in othervisit or p in bothvisit:
262 # p is implicitly in thisvisit. This means p is or should be
262 # p is implicitly in thisvisit. This means p is or should be
263 # in bothvisit
263 # in bothvisit
264 revsvisit.discard(p)
264 revsvisit.discard(p)
265 basesvisit.add(p)
265 basesvisit.add(p)
266 bothvisit.add(p)
266 bothvisit.add(p)
267 else:
267 else:
268 # visit later
268 # visit later
269 thisvisit.add(p)
269 thisvisit.add(p)
270
270
271 missing.reverse()
271 missing.reverse()
272 return missing
272 return missing
273
273
274
274
275 # Extracted from lazyancestors.__iter__ to avoid a reference cycle
275 # Extracted from lazyancestors.__iter__ to avoid a reference cycle
276 def _lazyancestorsiter(parentrevs, initrevs, stoprev, inclusive):
276 def _lazyancestorsiter(parentrevs, initrevs, stoprev, inclusive):
277 seen = {nullrev}
277 seen = {nullrev}
278 heappush = heapq.heappush
278 heappush = heapq.heappush
279 heappop = heapq.heappop
279 heappop = heapq.heappop
280 heapreplace = heapq.heapreplace
280 heapreplace = heapq.heapreplace
281 see = seen.add
281 see = seen.add
282
282
283 if inclusive:
283 if inclusive:
284 visit = [-r for r in initrevs]
284 visit = [-r for r in initrevs]
285 seen.update(initrevs)
285 seen.update(initrevs)
286 heapq.heapify(visit)
286 heapq.heapify(visit)
287 else:
287 else:
288 visit = []
288 visit = []
289 heapq.heapify(visit)
289 heapq.heapify(visit)
290 for r in initrevs:
290 for r in initrevs:
291 p1, p2 = parentrevs(r)
291 p1, p2 = parentrevs(r)
292 if p1 not in seen:
292 if p1 not in seen:
293 heappush(visit, -p1)
293 heappush(visit, -p1)
294 see(p1)
294 see(p1)
295 if p2 not in seen:
295 if p2 not in seen:
296 heappush(visit, -p2)
296 heappush(visit, -p2)
297 see(p2)
297 see(p2)
298
298
299 while visit:
299 while visit:
300 current = -visit[0]
300 current = -visit[0]
301 if current < stoprev:
301 if current < stoprev:
302 break
302 break
303 yield current
303 yield current
304 # optimize out heapq operation if p1 is known to be the next highest
304 # optimize out heapq operation if p1 is known to be the next highest
305 # revision, which is quite common in linear history.
305 # revision, which is quite common in linear history.
306 p1, p2 = parentrevs(current)
306 p1, p2 = parentrevs(current)
307 if p1 not in seen:
307 if p1 not in seen:
308 if current - p1 == 1:
308 if current - p1 == 1:
309 visit[0] = -p1
309 visit[0] = -p1
310 else:
310 else:
311 heapreplace(visit, -p1)
311 heapreplace(visit, -p1)
312 see(p1)
312 see(p1)
313 else:
313 else:
314 heappop(visit)
314 heappop(visit)
315 if p2 not in seen:
315 if p2 not in seen:
316 heappush(visit, -p2)
316 heappush(visit, -p2)
317 see(p2)
317 see(p2)
318
318
319
319
320 class lazyancestors(object):
320 class lazyancestors(object):
321 def __init__(self, pfunc, revs, stoprev=0, inclusive=False):
321 def __init__(self, pfunc, revs, stoprev=0, inclusive=False):
322 """Create a new object generating ancestors for the given revs. Does
322 """Create a new object generating ancestors for the given revs. Does
323 not generate revs lower than stoprev.
323 not generate revs lower than stoprev.
324
324
325 This is computed lazily starting from revs. The object supports
325 This is computed lazily starting from revs. The object supports
326 iteration and membership.
326 iteration and membership.
327
327
328 cl should be a changelog and revs should be an iterable. inclusive is
328 cl should be a changelog and revs should be an iterable. inclusive is
329 a boolean that indicates whether revs should be included. Revs lower
329 a boolean that indicates whether revs should be included. Revs lower
330 than stoprev will not be generated.
330 than stoprev will not be generated.
331
331
332 Result does not include the null revision."""
332 Result does not include the null revision."""
333 self._parentrevs = pfunc
333 self._parentrevs = pfunc
334 self._initrevs = revs = [r for r in revs if r >= stoprev]
334 self._initrevs = [r for r in revs if r >= stoprev]
335 self._stoprev = stoprev
335 self._stoprev = stoprev
336 self._inclusive = inclusive
336 self._inclusive = inclusive
337
337
338 self._containsseen = set()
338 self._containsseen = set()
339 self._containsiter = _lazyancestorsiter(
339 self._containsiter = _lazyancestorsiter(
340 self._parentrevs, self._initrevs, self._stoprev, self._inclusive
340 self._parentrevs, self._initrevs, self._stoprev, self._inclusive
341 )
341 )
342
342
343 def __nonzero__(self):
343 def __nonzero__(self):
344 """False if the set is empty, True otherwise."""
344 """False if the set is empty, True otherwise."""
345 try:
345 try:
346 next(iter(self))
346 next(iter(self))
347 return True
347 return True
348 except StopIteration:
348 except StopIteration:
349 return False
349 return False
350
350
351 __bool__ = __nonzero__
351 __bool__ = __nonzero__
352
352
353 def __iter__(self):
353 def __iter__(self):
354 """Generate the ancestors of _initrevs in reverse topological order.
354 """Generate the ancestors of _initrevs in reverse topological order.
355
355
356 If inclusive is False, yield a sequence of revision numbers starting
356 If inclusive is False, yield a sequence of revision numbers starting
357 with the parents of each revision in revs, i.e., each revision is
357 with the parents of each revision in revs, i.e., each revision is
358 *not* considered an ancestor of itself. Results are emitted in reverse
358 *not* considered an ancestor of itself. Results are emitted in reverse
359 revision number order. That order is also topological: a child is
359 revision number order. That order is also topological: a child is
360 always emitted before its parent.
360 always emitted before its parent.
361
361
362 If inclusive is True, the source revisions are also yielded. The
362 If inclusive is True, the source revisions are also yielded. The
363 reverse revision number order is still enforced."""
363 reverse revision number order is still enforced."""
364 return _lazyancestorsiter(
364 return _lazyancestorsiter(
365 self._parentrevs, self._initrevs, self._stoprev, self._inclusive
365 self._parentrevs, self._initrevs, self._stoprev, self._inclusive
366 )
366 )
367
367
368 def __contains__(self, target):
368 def __contains__(self, target):
369 """Test whether target is an ancestor of self._initrevs."""
369 """Test whether target is an ancestor of self._initrevs."""
370 seen = self._containsseen
370 seen = self._containsseen
371 if target in seen:
371 if target in seen:
372 return True
372 return True
373 iter = self._containsiter
373 iter = self._containsiter
374 if iter is None:
374 if iter is None:
375 # Iterator exhausted
375 # Iterator exhausted
376 return False
376 return False
377 # Only integer target is valid, but some callers expect 'None in self'
377 # Only integer target is valid, but some callers expect 'None in self'
378 # to be False. So we explicitly allow it.
378 # to be False. So we explicitly allow it.
379 if target is None:
379 if target is None:
380 return False
380 return False
381
381
382 see = seen.add
382 see = seen.add
383 try:
383 try:
384 while True:
384 while True:
385 rev = next(iter)
385 rev = next(iter)
386 see(rev)
386 see(rev)
387 if rev == target:
387 if rev == target:
388 return True
388 return True
389 if rev < target:
389 if rev < target:
390 return False
390 return False
391 except StopIteration:
391 except StopIteration:
392 # Set to None to indicate fast-path can be used next time, and to
392 # Set to None to indicate fast-path can be used next time, and to
393 # free up memory.
393 # free up memory.
394 self._containsiter = None
394 self._containsiter = None
395 return False
395 return False
396
396
397
397
398 class rustlazyancestors(object):
398 class rustlazyancestors(object):
399 def __init__(self, index, revs, stoprev=0, inclusive=False):
399 def __init__(self, index, revs, stoprev=0, inclusive=False):
400 self._index = index
400 self._index = index
401 self._stoprev = stoprev
401 self._stoprev = stoprev
402 self._inclusive = inclusive
402 self._inclusive = inclusive
403 # no need to prefilter out init revs that are smaller than stoprev,
403 # no need to prefilter out init revs that are smaller than stoprev,
404 # it's done by rustlazyancestors constructor.
404 # it's done by rustlazyancestors constructor.
405 # we need to convert to a list, because our ruslazyancestors
405 # we need to convert to a list, because our ruslazyancestors
406 # constructor (from C code) doesn't understand anything else yet
406 # constructor (from C code) doesn't understand anything else yet
407 self._initrevs = initrevs = list(revs)
407 self._initrevs = initrevs = list(revs)
408
408
409 self._containsiter = parsers.rustlazyancestors(
409 self._containsiter = parsers.rustlazyancestors(
410 index, initrevs, stoprev, inclusive
410 index, initrevs, stoprev, inclusive
411 )
411 )
412
412
413 def __nonzero__(self):
413 def __nonzero__(self):
414 """False if the set is empty, True otherwise.
414 """False if the set is empty, True otherwise.
415
415
416 It's better to duplicate this essentially trivial method than
416 It's better to duplicate this essentially trivial method than
417 to subclass lazyancestors
417 to subclass lazyancestors
418 """
418 """
419 try:
419 try:
420 next(iter(self))
420 next(iter(self))
421 return True
421 return True
422 except StopIteration:
422 except StopIteration:
423 return False
423 return False
424
424
425 def __iter__(self):
425 def __iter__(self):
426 return parsers.rustlazyancestors(
426 return parsers.rustlazyancestors(
427 self._index, self._initrevs, self._stoprev, self._inclusive
427 self._index, self._initrevs, self._stoprev, self._inclusive
428 )
428 )
429
429
430 def __contains__(self, target):
430 def __contains__(self, target):
431 return target in self._containsiter
431 return target in self._containsiter
General Comments 0
You need to be logged in to leave comments. Login now