##// END OF EJS Templates
smartset: convert set to list lazily...
Jun Wu -
r31015:1076f7eb default
parent child Browse files
Show More
@@ -1,973 +1,986 b''
1 # smartset.py - data structure for revision set
1 # smartset.py - data structure for revision set
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 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 from . import (
10 from . import (
11 util,
11 util,
12 )
12 )
13
13
14 def _formatsetrepr(r):
14 def _formatsetrepr(r):
15 """Format an optional printable representation of a set
15 """Format an optional printable representation of a set
16
16
17 ======== =================================
17 ======== =================================
18 type(r) example
18 type(r) example
19 ======== =================================
19 ======== =================================
20 tuple ('<not %r>', other)
20 tuple ('<not %r>', other)
21 str '<branch closed>'
21 str '<branch closed>'
22 callable lambda: '<branch %r>' % sorted(b)
22 callable lambda: '<branch %r>' % sorted(b)
23 object other
23 object other
24 ======== =================================
24 ======== =================================
25 """
25 """
26 if r is None:
26 if r is None:
27 return ''
27 return ''
28 elif isinstance(r, tuple):
28 elif isinstance(r, tuple):
29 return r[0] % r[1:]
29 return r[0] % r[1:]
30 elif isinstance(r, str):
30 elif isinstance(r, str):
31 return r
31 return r
32 elif callable(r):
32 elif callable(r):
33 return r()
33 return r()
34 else:
34 else:
35 return repr(r)
35 return repr(r)
36
36
37 class abstractsmartset(object):
37 class abstractsmartset(object):
38
38
39 def __nonzero__(self):
39 def __nonzero__(self):
40 """True if the smartset is not empty"""
40 """True if the smartset is not empty"""
41 raise NotImplementedError()
41 raise NotImplementedError()
42
42
43 def __contains__(self, rev):
43 def __contains__(self, rev):
44 """provide fast membership testing"""
44 """provide fast membership testing"""
45 raise NotImplementedError()
45 raise NotImplementedError()
46
46
47 def __iter__(self):
47 def __iter__(self):
48 """iterate the set in the order it is supposed to be iterated"""
48 """iterate the set in the order it is supposed to be iterated"""
49 raise NotImplementedError()
49 raise NotImplementedError()
50
50
51 # Attributes containing a function to perform a fast iteration in a given
51 # Attributes containing a function to perform a fast iteration in a given
52 # direction. A smartset can have none, one, or both defined.
52 # direction. A smartset can have none, one, or both defined.
53 #
53 #
54 # Default value is None instead of a function returning None to avoid
54 # Default value is None instead of a function returning None to avoid
55 # initializing an iterator just for testing if a fast method exists.
55 # initializing an iterator just for testing if a fast method exists.
56 fastasc = None
56 fastasc = None
57 fastdesc = None
57 fastdesc = None
58
58
59 def isascending(self):
59 def isascending(self):
60 """True if the set will iterate in ascending order"""
60 """True if the set will iterate in ascending order"""
61 raise NotImplementedError()
61 raise NotImplementedError()
62
62
63 def isdescending(self):
63 def isdescending(self):
64 """True if the set will iterate in descending order"""
64 """True if the set will iterate in descending order"""
65 raise NotImplementedError()
65 raise NotImplementedError()
66
66
67 def istopo(self):
67 def istopo(self):
68 """True if the set will iterate in topographical order"""
68 """True if the set will iterate in topographical order"""
69 raise NotImplementedError()
69 raise NotImplementedError()
70
70
71 def min(self):
71 def min(self):
72 """return the minimum element in the set"""
72 """return the minimum element in the set"""
73 if self.fastasc is None:
73 if self.fastasc is None:
74 v = min(self)
74 v = min(self)
75 else:
75 else:
76 for v in self.fastasc():
76 for v in self.fastasc():
77 break
77 break
78 else:
78 else:
79 raise ValueError('arg is an empty sequence')
79 raise ValueError('arg is an empty sequence')
80 self.min = lambda: v
80 self.min = lambda: v
81 return v
81 return v
82
82
83 def max(self):
83 def max(self):
84 """return the maximum element in the set"""
84 """return the maximum element in the set"""
85 if self.fastdesc is None:
85 if self.fastdesc is None:
86 return max(self)
86 return max(self)
87 else:
87 else:
88 for v in self.fastdesc():
88 for v in self.fastdesc():
89 break
89 break
90 else:
90 else:
91 raise ValueError('arg is an empty sequence')
91 raise ValueError('arg is an empty sequence')
92 self.max = lambda: v
92 self.max = lambda: v
93 return v
93 return v
94
94
95 def first(self):
95 def first(self):
96 """return the first element in the set (user iteration perspective)
96 """return the first element in the set (user iteration perspective)
97
97
98 Return None if the set is empty"""
98 Return None if the set is empty"""
99 raise NotImplementedError()
99 raise NotImplementedError()
100
100
101 def last(self):
101 def last(self):
102 """return the last element in the set (user iteration perspective)
102 """return the last element in the set (user iteration perspective)
103
103
104 Return None if the set is empty"""
104 Return None if the set is empty"""
105 raise NotImplementedError()
105 raise NotImplementedError()
106
106
107 def __len__(self):
107 def __len__(self):
108 """return the length of the smartsets
108 """return the length of the smartsets
109
109
110 This can be expensive on smartset that could be lazy otherwise."""
110 This can be expensive on smartset that could be lazy otherwise."""
111 raise NotImplementedError()
111 raise NotImplementedError()
112
112
113 def reverse(self):
113 def reverse(self):
114 """reverse the expected iteration order"""
114 """reverse the expected iteration order"""
115 raise NotImplementedError()
115 raise NotImplementedError()
116
116
117 def sort(self, reverse=True):
117 def sort(self, reverse=True):
118 """get the set to iterate in an ascending or descending order"""
118 """get the set to iterate in an ascending or descending order"""
119 raise NotImplementedError()
119 raise NotImplementedError()
120
120
121 def __and__(self, other):
121 def __and__(self, other):
122 """Returns a new object with the intersection of the two collections.
122 """Returns a new object with the intersection of the two collections.
123
123
124 This is part of the mandatory API for smartset."""
124 This is part of the mandatory API for smartset."""
125 if isinstance(other, fullreposet):
125 if isinstance(other, fullreposet):
126 return self
126 return self
127 return self.filter(other.__contains__, condrepr=other, cache=False)
127 return self.filter(other.__contains__, condrepr=other, cache=False)
128
128
129 def __add__(self, other):
129 def __add__(self, other):
130 """Returns a new object with the union of the two collections.
130 """Returns a new object with the union of the two collections.
131
131
132 This is part of the mandatory API for smartset."""
132 This is part of the mandatory API for smartset."""
133 return addset(self, other)
133 return addset(self, other)
134
134
135 def __sub__(self, other):
135 def __sub__(self, other):
136 """Returns a new object with the substraction of the two collections.
136 """Returns a new object with the substraction of the two collections.
137
137
138 This is part of the mandatory API for smartset."""
138 This is part of the mandatory API for smartset."""
139 c = other.__contains__
139 c = other.__contains__
140 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
140 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
141 cache=False)
141 cache=False)
142
142
143 def filter(self, condition, condrepr=None, cache=True):
143 def filter(self, condition, condrepr=None, cache=True):
144 """Returns this smartset filtered by condition as a new smartset.
144 """Returns this smartset filtered by condition as a new smartset.
145
145
146 `condition` is a callable which takes a revision number and returns a
146 `condition` is a callable which takes a revision number and returns a
147 boolean. Optional `condrepr` provides a printable representation of
147 boolean. Optional `condrepr` provides a printable representation of
148 the given `condition`.
148 the given `condition`.
149
149
150 This is part of the mandatory API for smartset."""
150 This is part of the mandatory API for smartset."""
151 # builtin cannot be cached. but do not needs to
151 # builtin cannot be cached. but do not needs to
152 if cache and util.safehasattr(condition, 'func_code'):
152 if cache and util.safehasattr(condition, 'func_code'):
153 condition = util.cachefunc(condition)
153 condition = util.cachefunc(condition)
154 return filteredset(self, condition, condrepr)
154 return filteredset(self, condition, condrepr)
155
155
156 class baseset(abstractsmartset):
156 class baseset(abstractsmartset):
157 """Basic data structure that represents a revset and contains the basic
157 """Basic data structure that represents a revset and contains the basic
158 operation that it should be able to perform.
158 operation that it should be able to perform.
159
159
160 Every method in this class should be implemented by any smartset class.
160 Every method in this class should be implemented by any smartset class.
161 """
161 """
162 def __init__(self, data=(), datarepr=None, istopo=False):
162 def __init__(self, data=(), datarepr=None, istopo=False):
163 """
163 """
164 datarepr: a tuple of (format, obj, ...), a function or an object that
164 datarepr: a tuple of (format, obj, ...), a function or an object that
165 provides a printable representation of the given data.
165 provides a printable representation of the given data.
166 """
166 """
167 self._ascending = None
167 self._ascending = None
168 self._istopo = istopo
168 self._istopo = istopo
169 if not isinstance(data, list):
169 if not isinstance(data, list):
170 if isinstance(data, set):
170 if isinstance(data, set):
171 self._set = data
171 self._set = data
172 # set has no order we pick one for stability purpose
172 # set has no order we pick one for stability purpose
173 self._ascending = True
173 self._ascending = True
174 # converting set to list has a cost, do it lazily
175 data = None
176 else:
174 data = list(data)
177 data = list(data)
178 if data is not None:
175 self._list = data
179 self._list = data
176 self._datarepr = datarepr
180 self._datarepr = datarepr
177
181
178 @util.propertycache
182 @util.propertycache
179 def _set(self):
183 def _set(self):
180 return set(self._list)
184 return set(self._list)
181
185
182 @util.propertycache
186 @util.propertycache
183 def _asclist(self):
187 def _asclist(self):
184 asclist = self._list[:]
188 asclist = self._list[:]
185 asclist.sort()
189 asclist.sort()
186 return asclist
190 return asclist
187
191
192 @util.propertycache
193 def _list(self):
194 # _list is only lazily constructed if we have _set
195 assert '_set' in self.__dict__
196 return list(self._set)
197
188 def __iter__(self):
198 def __iter__(self):
189 if self._ascending is None:
199 if self._ascending is None:
190 return iter(self._list)
200 return iter(self._list)
191 elif self._ascending:
201 elif self._ascending:
192 return iter(self._asclist)
202 return iter(self._asclist)
193 else:
203 else:
194 return reversed(self._asclist)
204 return reversed(self._asclist)
195
205
196 def fastasc(self):
206 def fastasc(self):
197 return iter(self._asclist)
207 return iter(self._asclist)
198
208
199 def fastdesc(self):
209 def fastdesc(self):
200 return reversed(self._asclist)
210 return reversed(self._asclist)
201
211
202 @util.propertycache
212 @util.propertycache
203 def __contains__(self):
213 def __contains__(self):
204 return self._set.__contains__
214 return self._set.__contains__
205
215
206 def __nonzero__(self):
216 def __nonzero__(self):
207 return bool(self._list)
217 return bool(len(self))
208
218
209 def sort(self, reverse=False):
219 def sort(self, reverse=False):
210 self._ascending = not bool(reverse)
220 self._ascending = not bool(reverse)
211 self._istopo = False
221 self._istopo = False
212
222
213 def reverse(self):
223 def reverse(self):
214 if self._ascending is None:
224 if self._ascending is None:
215 self._list.reverse()
225 self._list.reverse()
216 else:
226 else:
217 self._ascending = not self._ascending
227 self._ascending = not self._ascending
218 self._istopo = False
228 self._istopo = False
219
229
220 def __len__(self):
230 def __len__(self):
231 if '_list' in self.__dict__:
221 return len(self._list)
232 return len(self._list)
233 else:
234 return len(self._set)
222
235
223 def isascending(self):
236 def isascending(self):
224 """Returns True if the collection is ascending order, False if not.
237 """Returns True if the collection is ascending order, False if not.
225
238
226 This is part of the mandatory API for smartset."""
239 This is part of the mandatory API for smartset."""
227 if len(self) <= 1:
240 if len(self) <= 1:
228 return True
241 return True
229 return self._ascending is not None and self._ascending
242 return self._ascending is not None and self._ascending
230
243
231 def isdescending(self):
244 def isdescending(self):
232 """Returns True if the collection is descending order, False if not.
245 """Returns True if the collection is descending order, False if not.
233
246
234 This is part of the mandatory API for smartset."""
247 This is part of the mandatory API for smartset."""
235 if len(self) <= 1:
248 if len(self) <= 1:
236 return True
249 return True
237 return self._ascending is not None and not self._ascending
250 return self._ascending is not None and not self._ascending
238
251
239 def istopo(self):
252 def istopo(self):
240 """Is the collection is in topographical order or not.
253 """Is the collection is in topographical order or not.
241
254
242 This is part of the mandatory API for smartset."""
255 This is part of the mandatory API for smartset."""
243 if len(self) <= 1:
256 if len(self) <= 1:
244 return True
257 return True
245 return self._istopo
258 return self._istopo
246
259
247 def first(self):
260 def first(self):
248 if self:
261 if self:
249 if self._ascending is None:
262 if self._ascending is None:
250 return self._list[0]
263 return self._list[0]
251 elif self._ascending:
264 elif self._ascending:
252 return self._asclist[0]
265 return self._asclist[0]
253 else:
266 else:
254 return self._asclist[-1]
267 return self._asclist[-1]
255 return None
268 return None
256
269
257 def last(self):
270 def last(self):
258 if self:
271 if self:
259 if self._ascending is None:
272 if self._ascending is None:
260 return self._list[-1]
273 return self._list[-1]
261 elif self._ascending:
274 elif self._ascending:
262 return self._asclist[-1]
275 return self._asclist[-1]
263 else:
276 else:
264 return self._asclist[0]
277 return self._asclist[0]
265 return None
278 return None
266
279
267 def __repr__(self):
280 def __repr__(self):
268 d = {None: '', False: '-', True: '+'}[self._ascending]
281 d = {None: '', False: '-', True: '+'}[self._ascending]
269 s = _formatsetrepr(self._datarepr)
282 s = _formatsetrepr(self._datarepr)
270 if not s:
283 if not s:
271 l = self._list
284 l = self._list
272 # if _list has been built from a set, it might have a different
285 # if _list has been built from a set, it might have a different
273 # order from one python implementation to another.
286 # order from one python implementation to another.
274 # We fallback to the sorted version for a stable output.
287 # We fallback to the sorted version for a stable output.
275 if self._ascending is not None:
288 if self._ascending is not None:
276 l = self._asclist
289 l = self._asclist
277 s = repr(l)
290 s = repr(l)
278 return '<%s%s %s>' % (type(self).__name__, d, s)
291 return '<%s%s %s>' % (type(self).__name__, d, s)
279
292
280 class filteredset(abstractsmartset):
293 class filteredset(abstractsmartset):
281 """Duck type for baseset class which iterates lazily over the revisions in
294 """Duck type for baseset class which iterates lazily over the revisions in
282 the subset and contains a function which tests for membership in the
295 the subset and contains a function which tests for membership in the
283 revset
296 revset
284 """
297 """
285 def __init__(self, subset, condition=lambda x: True, condrepr=None):
298 def __init__(self, subset, condition=lambda x: True, condrepr=None):
286 """
299 """
287 condition: a function that decide whether a revision in the subset
300 condition: a function that decide whether a revision in the subset
288 belongs to the revset or not.
301 belongs to the revset or not.
289 condrepr: a tuple of (format, obj, ...), a function or an object that
302 condrepr: a tuple of (format, obj, ...), a function or an object that
290 provides a printable representation of the given condition.
303 provides a printable representation of the given condition.
291 """
304 """
292 self._subset = subset
305 self._subset = subset
293 self._condition = condition
306 self._condition = condition
294 self._condrepr = condrepr
307 self._condrepr = condrepr
295
308
296 def __contains__(self, x):
309 def __contains__(self, x):
297 return x in self._subset and self._condition(x)
310 return x in self._subset and self._condition(x)
298
311
299 def __iter__(self):
312 def __iter__(self):
300 return self._iterfilter(self._subset)
313 return self._iterfilter(self._subset)
301
314
302 def _iterfilter(self, it):
315 def _iterfilter(self, it):
303 cond = self._condition
316 cond = self._condition
304 for x in it:
317 for x in it:
305 if cond(x):
318 if cond(x):
306 yield x
319 yield x
307
320
308 @property
321 @property
309 def fastasc(self):
322 def fastasc(self):
310 it = self._subset.fastasc
323 it = self._subset.fastasc
311 if it is None:
324 if it is None:
312 return None
325 return None
313 return lambda: self._iterfilter(it())
326 return lambda: self._iterfilter(it())
314
327
315 @property
328 @property
316 def fastdesc(self):
329 def fastdesc(self):
317 it = self._subset.fastdesc
330 it = self._subset.fastdesc
318 if it is None:
331 if it is None:
319 return None
332 return None
320 return lambda: self._iterfilter(it())
333 return lambda: self._iterfilter(it())
321
334
322 def __nonzero__(self):
335 def __nonzero__(self):
323 fast = None
336 fast = None
324 candidates = [self.fastasc if self.isascending() else None,
337 candidates = [self.fastasc if self.isascending() else None,
325 self.fastdesc if self.isdescending() else None,
338 self.fastdesc if self.isdescending() else None,
326 self.fastasc,
339 self.fastasc,
327 self.fastdesc]
340 self.fastdesc]
328 for candidate in candidates:
341 for candidate in candidates:
329 if candidate is not None:
342 if candidate is not None:
330 fast = candidate
343 fast = candidate
331 break
344 break
332
345
333 if fast is not None:
346 if fast is not None:
334 it = fast()
347 it = fast()
335 else:
348 else:
336 it = self
349 it = self
337
350
338 for r in it:
351 for r in it:
339 return True
352 return True
340 return False
353 return False
341
354
342 def __len__(self):
355 def __len__(self):
343 # Basic implementation to be changed in future patches.
356 # Basic implementation to be changed in future patches.
344 # until this gets improved, we use generator expression
357 # until this gets improved, we use generator expression
345 # here, since list comprehensions are free to call __len__ again
358 # here, since list comprehensions are free to call __len__ again
346 # causing infinite recursion
359 # causing infinite recursion
347 l = baseset(r for r in self)
360 l = baseset(r for r in self)
348 return len(l)
361 return len(l)
349
362
350 def sort(self, reverse=False):
363 def sort(self, reverse=False):
351 self._subset.sort(reverse=reverse)
364 self._subset.sort(reverse=reverse)
352
365
353 def reverse(self):
366 def reverse(self):
354 self._subset.reverse()
367 self._subset.reverse()
355
368
356 def isascending(self):
369 def isascending(self):
357 return self._subset.isascending()
370 return self._subset.isascending()
358
371
359 def isdescending(self):
372 def isdescending(self):
360 return self._subset.isdescending()
373 return self._subset.isdescending()
361
374
362 def istopo(self):
375 def istopo(self):
363 return self._subset.istopo()
376 return self._subset.istopo()
364
377
365 def first(self):
378 def first(self):
366 for x in self:
379 for x in self:
367 return x
380 return x
368 return None
381 return None
369
382
370 def last(self):
383 def last(self):
371 it = None
384 it = None
372 if self.isascending():
385 if self.isascending():
373 it = self.fastdesc
386 it = self.fastdesc
374 elif self.isdescending():
387 elif self.isdescending():
375 it = self.fastasc
388 it = self.fastasc
376 if it is not None:
389 if it is not None:
377 for x in it():
390 for x in it():
378 return x
391 return x
379 return None #empty case
392 return None #empty case
380 else:
393 else:
381 x = None
394 x = None
382 for x in self:
395 for x in self:
383 pass
396 pass
384 return x
397 return x
385
398
386 def __repr__(self):
399 def __repr__(self):
387 xs = [repr(self._subset)]
400 xs = [repr(self._subset)]
388 s = _formatsetrepr(self._condrepr)
401 s = _formatsetrepr(self._condrepr)
389 if s:
402 if s:
390 xs.append(s)
403 xs.append(s)
391 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
404 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
392
405
393 def _iterordered(ascending, iter1, iter2):
406 def _iterordered(ascending, iter1, iter2):
394 """produce an ordered iteration from two iterators with the same order
407 """produce an ordered iteration from two iterators with the same order
395
408
396 The ascending is used to indicated the iteration direction.
409 The ascending is used to indicated the iteration direction.
397 """
410 """
398 choice = max
411 choice = max
399 if ascending:
412 if ascending:
400 choice = min
413 choice = min
401
414
402 val1 = None
415 val1 = None
403 val2 = None
416 val2 = None
404 try:
417 try:
405 # Consume both iterators in an ordered way until one is empty
418 # Consume both iterators in an ordered way until one is empty
406 while True:
419 while True:
407 if val1 is None:
420 if val1 is None:
408 val1 = next(iter1)
421 val1 = next(iter1)
409 if val2 is None:
422 if val2 is None:
410 val2 = next(iter2)
423 val2 = next(iter2)
411 n = choice(val1, val2)
424 n = choice(val1, val2)
412 yield n
425 yield n
413 if val1 == n:
426 if val1 == n:
414 val1 = None
427 val1 = None
415 if val2 == n:
428 if val2 == n:
416 val2 = None
429 val2 = None
417 except StopIteration:
430 except StopIteration:
418 # Flush any remaining values and consume the other one
431 # Flush any remaining values and consume the other one
419 it = iter2
432 it = iter2
420 if val1 is not None:
433 if val1 is not None:
421 yield val1
434 yield val1
422 it = iter1
435 it = iter1
423 elif val2 is not None:
436 elif val2 is not None:
424 # might have been equality and both are empty
437 # might have been equality and both are empty
425 yield val2
438 yield val2
426 for val in it:
439 for val in it:
427 yield val
440 yield val
428
441
429 class addset(abstractsmartset):
442 class addset(abstractsmartset):
430 """Represent the addition of two sets
443 """Represent the addition of two sets
431
444
432 Wrapper structure for lazily adding two structures without losing much
445 Wrapper structure for lazily adding two structures without losing much
433 performance on the __contains__ method
446 performance on the __contains__ method
434
447
435 If the ascending attribute is set, that means the two structures are
448 If the ascending attribute is set, that means the two structures are
436 ordered in either an ascending or descending way. Therefore, we can add
449 ordered in either an ascending or descending way. Therefore, we can add
437 them maintaining the order by iterating over both at the same time
450 them maintaining the order by iterating over both at the same time
438
451
439 >>> xs = baseset([0, 3, 2])
452 >>> xs = baseset([0, 3, 2])
440 >>> ys = baseset([5, 2, 4])
453 >>> ys = baseset([5, 2, 4])
441
454
442 >>> rs = addset(xs, ys)
455 >>> rs = addset(xs, ys)
443 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
456 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
444 (True, True, False, True, 0, 4)
457 (True, True, False, True, 0, 4)
445 >>> rs = addset(xs, baseset([]))
458 >>> rs = addset(xs, baseset([]))
446 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
459 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
447 (True, True, False, 0, 2)
460 (True, True, False, 0, 2)
448 >>> rs = addset(baseset([]), baseset([]))
461 >>> rs = addset(baseset([]), baseset([]))
449 >>> bool(rs), 0 in rs, rs.first(), rs.last()
462 >>> bool(rs), 0 in rs, rs.first(), rs.last()
450 (False, False, None, None)
463 (False, False, None, None)
451
464
452 iterate unsorted:
465 iterate unsorted:
453 >>> rs = addset(xs, ys)
466 >>> rs = addset(xs, ys)
454 >>> # (use generator because pypy could call len())
467 >>> # (use generator because pypy could call len())
455 >>> list(x for x in rs) # without _genlist
468 >>> list(x for x in rs) # without _genlist
456 [0, 3, 2, 5, 4]
469 [0, 3, 2, 5, 4]
457 >>> assert not rs._genlist
470 >>> assert not rs._genlist
458 >>> len(rs)
471 >>> len(rs)
459 5
472 5
460 >>> [x for x in rs] # with _genlist
473 >>> [x for x in rs] # with _genlist
461 [0, 3, 2, 5, 4]
474 [0, 3, 2, 5, 4]
462 >>> assert rs._genlist
475 >>> assert rs._genlist
463
476
464 iterate ascending:
477 iterate ascending:
465 >>> rs = addset(xs, ys, ascending=True)
478 >>> rs = addset(xs, ys, ascending=True)
466 >>> # (use generator because pypy could call len())
479 >>> # (use generator because pypy could call len())
467 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
480 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
468 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
481 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
469 >>> assert not rs._asclist
482 >>> assert not rs._asclist
470 >>> len(rs)
483 >>> len(rs)
471 5
484 5
472 >>> [x for x in rs], [x for x in rs.fastasc()]
485 >>> [x for x in rs], [x for x in rs.fastasc()]
473 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
486 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
474 >>> assert rs._asclist
487 >>> assert rs._asclist
475
488
476 iterate descending:
489 iterate descending:
477 >>> rs = addset(xs, ys, ascending=False)
490 >>> rs = addset(xs, ys, ascending=False)
478 >>> # (use generator because pypy could call len())
491 >>> # (use generator because pypy could call len())
479 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
492 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
480 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
493 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
481 >>> assert not rs._asclist
494 >>> assert not rs._asclist
482 >>> len(rs)
495 >>> len(rs)
483 5
496 5
484 >>> [x for x in rs], [x for x in rs.fastdesc()]
497 >>> [x for x in rs], [x for x in rs.fastdesc()]
485 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
498 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
486 >>> assert rs._asclist
499 >>> assert rs._asclist
487
500
488 iterate ascending without fastasc:
501 iterate ascending without fastasc:
489 >>> rs = addset(xs, generatorset(ys), ascending=True)
502 >>> rs = addset(xs, generatorset(ys), ascending=True)
490 >>> assert rs.fastasc is None
503 >>> assert rs.fastasc is None
491 >>> [x for x in rs]
504 >>> [x for x in rs]
492 [0, 2, 3, 4, 5]
505 [0, 2, 3, 4, 5]
493
506
494 iterate descending without fastdesc:
507 iterate descending without fastdesc:
495 >>> rs = addset(generatorset(xs), ys, ascending=False)
508 >>> rs = addset(generatorset(xs), ys, ascending=False)
496 >>> assert rs.fastdesc is None
509 >>> assert rs.fastdesc is None
497 >>> [x for x in rs]
510 >>> [x for x in rs]
498 [5, 4, 3, 2, 0]
511 [5, 4, 3, 2, 0]
499 """
512 """
500 def __init__(self, revs1, revs2, ascending=None):
513 def __init__(self, revs1, revs2, ascending=None):
501 self._r1 = revs1
514 self._r1 = revs1
502 self._r2 = revs2
515 self._r2 = revs2
503 self._iter = None
516 self._iter = None
504 self._ascending = ascending
517 self._ascending = ascending
505 self._genlist = None
518 self._genlist = None
506 self._asclist = None
519 self._asclist = None
507
520
508 def __len__(self):
521 def __len__(self):
509 return len(self._list)
522 return len(self._list)
510
523
511 def __nonzero__(self):
524 def __nonzero__(self):
512 return bool(self._r1) or bool(self._r2)
525 return bool(self._r1) or bool(self._r2)
513
526
514 @util.propertycache
527 @util.propertycache
515 def _list(self):
528 def _list(self):
516 if not self._genlist:
529 if not self._genlist:
517 self._genlist = baseset(iter(self))
530 self._genlist = baseset(iter(self))
518 return self._genlist
531 return self._genlist
519
532
520 def __iter__(self):
533 def __iter__(self):
521 """Iterate over both collections without repeating elements
534 """Iterate over both collections without repeating elements
522
535
523 If the ascending attribute is not set, iterate over the first one and
536 If the ascending attribute is not set, iterate over the first one and
524 then over the second one checking for membership on the first one so we
537 then over the second one checking for membership on the first one so we
525 dont yield any duplicates.
538 dont yield any duplicates.
526
539
527 If the ascending attribute is set, iterate over both collections at the
540 If the ascending attribute is set, iterate over both collections at the
528 same time, yielding only one value at a time in the given order.
541 same time, yielding only one value at a time in the given order.
529 """
542 """
530 if self._ascending is None:
543 if self._ascending is None:
531 if self._genlist:
544 if self._genlist:
532 return iter(self._genlist)
545 return iter(self._genlist)
533 def arbitraryordergen():
546 def arbitraryordergen():
534 for r in self._r1:
547 for r in self._r1:
535 yield r
548 yield r
536 inr1 = self._r1.__contains__
549 inr1 = self._r1.__contains__
537 for r in self._r2:
550 for r in self._r2:
538 if not inr1(r):
551 if not inr1(r):
539 yield r
552 yield r
540 return arbitraryordergen()
553 return arbitraryordergen()
541 # try to use our own fast iterator if it exists
554 # try to use our own fast iterator if it exists
542 self._trysetasclist()
555 self._trysetasclist()
543 if self._ascending:
556 if self._ascending:
544 attr = 'fastasc'
557 attr = 'fastasc'
545 else:
558 else:
546 attr = 'fastdesc'
559 attr = 'fastdesc'
547 it = getattr(self, attr)
560 it = getattr(self, attr)
548 if it is not None:
561 if it is not None:
549 return it()
562 return it()
550 # maybe half of the component supports fast
563 # maybe half of the component supports fast
551 # get iterator for _r1
564 # get iterator for _r1
552 iter1 = getattr(self._r1, attr)
565 iter1 = getattr(self._r1, attr)
553 if iter1 is None:
566 if iter1 is None:
554 # let's avoid side effect (not sure it matters)
567 # let's avoid side effect (not sure it matters)
555 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
568 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
556 else:
569 else:
557 iter1 = iter1()
570 iter1 = iter1()
558 # get iterator for _r2
571 # get iterator for _r2
559 iter2 = getattr(self._r2, attr)
572 iter2 = getattr(self._r2, attr)
560 if iter2 is None:
573 if iter2 is None:
561 # let's avoid side effect (not sure it matters)
574 # let's avoid side effect (not sure it matters)
562 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
575 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
563 else:
576 else:
564 iter2 = iter2()
577 iter2 = iter2()
565 return _iterordered(self._ascending, iter1, iter2)
578 return _iterordered(self._ascending, iter1, iter2)
566
579
567 def _trysetasclist(self):
580 def _trysetasclist(self):
568 """populate the _asclist attribute if possible and necessary"""
581 """populate the _asclist attribute if possible and necessary"""
569 if self._genlist is not None and self._asclist is None:
582 if self._genlist is not None and self._asclist is None:
570 self._asclist = sorted(self._genlist)
583 self._asclist = sorted(self._genlist)
571
584
572 @property
585 @property
573 def fastasc(self):
586 def fastasc(self):
574 self._trysetasclist()
587 self._trysetasclist()
575 if self._asclist is not None:
588 if self._asclist is not None:
576 return self._asclist.__iter__
589 return self._asclist.__iter__
577 iter1 = self._r1.fastasc
590 iter1 = self._r1.fastasc
578 iter2 = self._r2.fastasc
591 iter2 = self._r2.fastasc
579 if None in (iter1, iter2):
592 if None in (iter1, iter2):
580 return None
593 return None
581 return lambda: _iterordered(True, iter1(), iter2())
594 return lambda: _iterordered(True, iter1(), iter2())
582
595
583 @property
596 @property
584 def fastdesc(self):
597 def fastdesc(self):
585 self._trysetasclist()
598 self._trysetasclist()
586 if self._asclist is not None:
599 if self._asclist is not None:
587 return self._asclist.__reversed__
600 return self._asclist.__reversed__
588 iter1 = self._r1.fastdesc
601 iter1 = self._r1.fastdesc
589 iter2 = self._r2.fastdesc
602 iter2 = self._r2.fastdesc
590 if None in (iter1, iter2):
603 if None in (iter1, iter2):
591 return None
604 return None
592 return lambda: _iterordered(False, iter1(), iter2())
605 return lambda: _iterordered(False, iter1(), iter2())
593
606
594 def __contains__(self, x):
607 def __contains__(self, x):
595 return x in self._r1 or x in self._r2
608 return x in self._r1 or x in self._r2
596
609
597 def sort(self, reverse=False):
610 def sort(self, reverse=False):
598 """Sort the added set
611 """Sort the added set
599
612
600 For this we use the cached list with all the generated values and if we
613 For this we use the cached list with all the generated values and if we
601 know they are ascending or descending we can sort them in a smart way.
614 know they are ascending or descending we can sort them in a smart way.
602 """
615 """
603 self._ascending = not reverse
616 self._ascending = not reverse
604
617
605 def isascending(self):
618 def isascending(self):
606 return self._ascending is not None and self._ascending
619 return self._ascending is not None and self._ascending
607
620
608 def isdescending(self):
621 def isdescending(self):
609 return self._ascending is not None and not self._ascending
622 return self._ascending is not None and not self._ascending
610
623
611 def istopo(self):
624 def istopo(self):
612 # not worth the trouble asserting if the two sets combined are still
625 # not worth the trouble asserting if the two sets combined are still
613 # in topographical order. Use the sort() predicate to explicitly sort
626 # in topographical order. Use the sort() predicate to explicitly sort
614 # again instead.
627 # again instead.
615 return False
628 return False
616
629
617 def reverse(self):
630 def reverse(self):
618 if self._ascending is None:
631 if self._ascending is None:
619 self._list.reverse()
632 self._list.reverse()
620 else:
633 else:
621 self._ascending = not self._ascending
634 self._ascending = not self._ascending
622
635
623 def first(self):
636 def first(self):
624 for x in self:
637 for x in self:
625 return x
638 return x
626 return None
639 return None
627
640
628 def last(self):
641 def last(self):
629 self.reverse()
642 self.reverse()
630 val = self.first()
643 val = self.first()
631 self.reverse()
644 self.reverse()
632 return val
645 return val
633
646
634 def __repr__(self):
647 def __repr__(self):
635 d = {None: '', False: '-', True: '+'}[self._ascending]
648 d = {None: '', False: '-', True: '+'}[self._ascending]
636 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
649 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
637
650
638 class generatorset(abstractsmartset):
651 class generatorset(abstractsmartset):
639 """Wrap a generator for lazy iteration
652 """Wrap a generator for lazy iteration
640
653
641 Wrapper structure for generators that provides lazy membership and can
654 Wrapper structure for generators that provides lazy membership and can
642 be iterated more than once.
655 be iterated more than once.
643 When asked for membership it generates values until either it finds the
656 When asked for membership it generates values until either it finds the
644 requested one or has gone through all the elements in the generator
657 requested one or has gone through all the elements in the generator
645 """
658 """
646 def __init__(self, gen, iterasc=None):
659 def __init__(self, gen, iterasc=None):
647 """
660 """
648 gen: a generator producing the values for the generatorset.
661 gen: a generator producing the values for the generatorset.
649 """
662 """
650 self._gen = gen
663 self._gen = gen
651 self._asclist = None
664 self._asclist = None
652 self._cache = {}
665 self._cache = {}
653 self._genlist = []
666 self._genlist = []
654 self._finished = False
667 self._finished = False
655 self._ascending = True
668 self._ascending = True
656 if iterasc is not None:
669 if iterasc is not None:
657 if iterasc:
670 if iterasc:
658 self.fastasc = self._iterator
671 self.fastasc = self._iterator
659 self.__contains__ = self._asccontains
672 self.__contains__ = self._asccontains
660 else:
673 else:
661 self.fastdesc = self._iterator
674 self.fastdesc = self._iterator
662 self.__contains__ = self._desccontains
675 self.__contains__ = self._desccontains
663
676
664 def __nonzero__(self):
677 def __nonzero__(self):
665 # Do not use 'for r in self' because it will enforce the iteration
678 # Do not use 'for r in self' because it will enforce the iteration
666 # order (default ascending), possibly unrolling a whole descending
679 # order (default ascending), possibly unrolling a whole descending
667 # iterator.
680 # iterator.
668 if self._genlist:
681 if self._genlist:
669 return True
682 return True
670 for r in self._consumegen():
683 for r in self._consumegen():
671 return True
684 return True
672 return False
685 return False
673
686
674 def __contains__(self, x):
687 def __contains__(self, x):
675 if x in self._cache:
688 if x in self._cache:
676 return self._cache[x]
689 return self._cache[x]
677
690
678 # Use new values only, as existing values would be cached.
691 # Use new values only, as existing values would be cached.
679 for l in self._consumegen():
692 for l in self._consumegen():
680 if l == x:
693 if l == x:
681 return True
694 return True
682
695
683 self._cache[x] = False
696 self._cache[x] = False
684 return False
697 return False
685
698
686 def _asccontains(self, x):
699 def _asccontains(self, x):
687 """version of contains optimised for ascending generator"""
700 """version of contains optimised for ascending generator"""
688 if x in self._cache:
701 if x in self._cache:
689 return self._cache[x]
702 return self._cache[x]
690
703
691 # Use new values only, as existing values would be cached.
704 # Use new values only, as existing values would be cached.
692 for l in self._consumegen():
705 for l in self._consumegen():
693 if l == x:
706 if l == x:
694 return True
707 return True
695 if l > x:
708 if l > x:
696 break
709 break
697
710
698 self._cache[x] = False
711 self._cache[x] = False
699 return False
712 return False
700
713
701 def _desccontains(self, x):
714 def _desccontains(self, x):
702 """version of contains optimised for descending generator"""
715 """version of contains optimised for descending generator"""
703 if x in self._cache:
716 if x in self._cache:
704 return self._cache[x]
717 return self._cache[x]
705
718
706 # Use new values only, as existing values would be cached.
719 # Use new values only, as existing values would be cached.
707 for l in self._consumegen():
720 for l in self._consumegen():
708 if l == x:
721 if l == x:
709 return True
722 return True
710 if l < x:
723 if l < x:
711 break
724 break
712
725
713 self._cache[x] = False
726 self._cache[x] = False
714 return False
727 return False
715
728
716 def __iter__(self):
729 def __iter__(self):
717 if self._ascending:
730 if self._ascending:
718 it = self.fastasc
731 it = self.fastasc
719 else:
732 else:
720 it = self.fastdesc
733 it = self.fastdesc
721 if it is not None:
734 if it is not None:
722 return it()
735 return it()
723 # we need to consume the iterator
736 # we need to consume the iterator
724 for x in self._consumegen():
737 for x in self._consumegen():
725 pass
738 pass
726 # recall the same code
739 # recall the same code
727 return iter(self)
740 return iter(self)
728
741
729 def _iterator(self):
742 def _iterator(self):
730 if self._finished:
743 if self._finished:
731 return iter(self._genlist)
744 return iter(self._genlist)
732
745
733 # We have to use this complex iteration strategy to allow multiple
746 # We have to use this complex iteration strategy to allow multiple
734 # iterations at the same time. We need to be able to catch revision
747 # iterations at the same time. We need to be able to catch revision
735 # removed from _consumegen and added to genlist in another instance.
748 # removed from _consumegen and added to genlist in another instance.
736 #
749 #
737 # Getting rid of it would provide an about 15% speed up on this
750 # Getting rid of it would provide an about 15% speed up on this
738 # iteration.
751 # iteration.
739 genlist = self._genlist
752 genlist = self._genlist
740 nextrev = self._consumegen().next
753 nextrev = self._consumegen().next
741 _len = len # cache global lookup
754 _len = len # cache global lookup
742 def gen():
755 def gen():
743 i = 0
756 i = 0
744 while True:
757 while True:
745 if i < _len(genlist):
758 if i < _len(genlist):
746 yield genlist[i]
759 yield genlist[i]
747 else:
760 else:
748 yield nextrev()
761 yield nextrev()
749 i += 1
762 i += 1
750 return gen()
763 return gen()
751
764
752 def _consumegen(self):
765 def _consumegen(self):
753 cache = self._cache
766 cache = self._cache
754 genlist = self._genlist.append
767 genlist = self._genlist.append
755 for item in self._gen:
768 for item in self._gen:
756 cache[item] = True
769 cache[item] = True
757 genlist(item)
770 genlist(item)
758 yield item
771 yield item
759 if not self._finished:
772 if not self._finished:
760 self._finished = True
773 self._finished = True
761 asc = self._genlist[:]
774 asc = self._genlist[:]
762 asc.sort()
775 asc.sort()
763 self._asclist = asc
776 self._asclist = asc
764 self.fastasc = asc.__iter__
777 self.fastasc = asc.__iter__
765 self.fastdesc = asc.__reversed__
778 self.fastdesc = asc.__reversed__
766
779
767 def __len__(self):
780 def __len__(self):
768 for x in self._consumegen():
781 for x in self._consumegen():
769 pass
782 pass
770 return len(self._genlist)
783 return len(self._genlist)
771
784
772 def sort(self, reverse=False):
785 def sort(self, reverse=False):
773 self._ascending = not reverse
786 self._ascending = not reverse
774
787
775 def reverse(self):
788 def reverse(self):
776 self._ascending = not self._ascending
789 self._ascending = not self._ascending
777
790
778 def isascending(self):
791 def isascending(self):
779 return self._ascending
792 return self._ascending
780
793
781 def isdescending(self):
794 def isdescending(self):
782 return not self._ascending
795 return not self._ascending
783
796
784 def istopo(self):
797 def istopo(self):
785 # not worth the trouble asserting if the two sets combined are still
798 # not worth the trouble asserting if the two sets combined are still
786 # in topographical order. Use the sort() predicate to explicitly sort
799 # in topographical order. Use the sort() predicate to explicitly sort
787 # again instead.
800 # again instead.
788 return False
801 return False
789
802
790 def first(self):
803 def first(self):
791 if self._ascending:
804 if self._ascending:
792 it = self.fastasc
805 it = self.fastasc
793 else:
806 else:
794 it = self.fastdesc
807 it = self.fastdesc
795 if it is None:
808 if it is None:
796 # we need to consume all and try again
809 # we need to consume all and try again
797 for x in self._consumegen():
810 for x in self._consumegen():
798 pass
811 pass
799 return self.first()
812 return self.first()
800 return next(it(), None)
813 return next(it(), None)
801
814
802 def last(self):
815 def last(self):
803 if self._ascending:
816 if self._ascending:
804 it = self.fastdesc
817 it = self.fastdesc
805 else:
818 else:
806 it = self.fastasc
819 it = self.fastasc
807 if it is None:
820 if it is None:
808 # we need to consume all and try again
821 # we need to consume all and try again
809 for x in self._consumegen():
822 for x in self._consumegen():
810 pass
823 pass
811 return self.first()
824 return self.first()
812 return next(it(), None)
825 return next(it(), None)
813
826
814 def __repr__(self):
827 def __repr__(self):
815 d = {False: '-', True: '+'}[self._ascending]
828 d = {False: '-', True: '+'}[self._ascending]
816 return '<%s%s>' % (type(self).__name__, d)
829 return '<%s%s>' % (type(self).__name__, d)
817
830
818 class spanset(abstractsmartset):
831 class spanset(abstractsmartset):
819 """Duck type for baseset class which represents a range of revisions and
832 """Duck type for baseset class which represents a range of revisions and
820 can work lazily and without having all the range in memory
833 can work lazily and without having all the range in memory
821
834
822 Note that spanset(x, y) behave almost like xrange(x, y) except for two
835 Note that spanset(x, y) behave almost like xrange(x, y) except for two
823 notable points:
836 notable points:
824 - when x < y it will be automatically descending,
837 - when x < y it will be automatically descending,
825 - revision filtered with this repoview will be skipped.
838 - revision filtered with this repoview will be skipped.
826
839
827 """
840 """
828 def __init__(self, repo, start=0, end=None):
841 def __init__(self, repo, start=0, end=None):
829 """
842 """
830 start: first revision included the set
843 start: first revision included the set
831 (default to 0)
844 (default to 0)
832 end: first revision excluded (last+1)
845 end: first revision excluded (last+1)
833 (default to len(repo)
846 (default to len(repo)
834
847
835 Spanset will be descending if `end` < `start`.
848 Spanset will be descending if `end` < `start`.
836 """
849 """
837 if end is None:
850 if end is None:
838 end = len(repo)
851 end = len(repo)
839 self._ascending = start <= end
852 self._ascending = start <= end
840 if not self._ascending:
853 if not self._ascending:
841 start, end = end + 1, start +1
854 start, end = end + 1, start +1
842 self._start = start
855 self._start = start
843 self._end = end
856 self._end = end
844 self._hiddenrevs = repo.changelog.filteredrevs
857 self._hiddenrevs = repo.changelog.filteredrevs
845
858
846 def sort(self, reverse=False):
859 def sort(self, reverse=False):
847 self._ascending = not reverse
860 self._ascending = not reverse
848
861
849 def reverse(self):
862 def reverse(self):
850 self._ascending = not self._ascending
863 self._ascending = not self._ascending
851
864
852 def istopo(self):
865 def istopo(self):
853 # not worth the trouble asserting if the two sets combined are still
866 # not worth the trouble asserting if the two sets combined are still
854 # in topographical order. Use the sort() predicate to explicitly sort
867 # in topographical order. Use the sort() predicate to explicitly sort
855 # again instead.
868 # again instead.
856 return False
869 return False
857
870
858 def _iterfilter(self, iterrange):
871 def _iterfilter(self, iterrange):
859 s = self._hiddenrevs
872 s = self._hiddenrevs
860 for r in iterrange:
873 for r in iterrange:
861 if r not in s:
874 if r not in s:
862 yield r
875 yield r
863
876
864 def __iter__(self):
877 def __iter__(self):
865 if self._ascending:
878 if self._ascending:
866 return self.fastasc()
879 return self.fastasc()
867 else:
880 else:
868 return self.fastdesc()
881 return self.fastdesc()
869
882
870 def fastasc(self):
883 def fastasc(self):
871 iterrange = xrange(self._start, self._end)
884 iterrange = xrange(self._start, self._end)
872 if self._hiddenrevs:
885 if self._hiddenrevs:
873 return self._iterfilter(iterrange)
886 return self._iterfilter(iterrange)
874 return iter(iterrange)
887 return iter(iterrange)
875
888
876 def fastdesc(self):
889 def fastdesc(self):
877 iterrange = xrange(self._end - 1, self._start - 1, -1)
890 iterrange = xrange(self._end - 1, self._start - 1, -1)
878 if self._hiddenrevs:
891 if self._hiddenrevs:
879 return self._iterfilter(iterrange)
892 return self._iterfilter(iterrange)
880 return iter(iterrange)
893 return iter(iterrange)
881
894
882 def __contains__(self, rev):
895 def __contains__(self, rev):
883 hidden = self._hiddenrevs
896 hidden = self._hiddenrevs
884 return ((self._start <= rev < self._end)
897 return ((self._start <= rev < self._end)
885 and not (hidden and rev in hidden))
898 and not (hidden and rev in hidden))
886
899
887 def __nonzero__(self):
900 def __nonzero__(self):
888 for r in self:
901 for r in self:
889 return True
902 return True
890 return False
903 return False
891
904
892 def __len__(self):
905 def __len__(self):
893 if not self._hiddenrevs:
906 if not self._hiddenrevs:
894 return abs(self._end - self._start)
907 return abs(self._end - self._start)
895 else:
908 else:
896 count = 0
909 count = 0
897 start = self._start
910 start = self._start
898 end = self._end
911 end = self._end
899 for rev in self._hiddenrevs:
912 for rev in self._hiddenrevs:
900 if (end < rev <= start) or (start <= rev < end):
913 if (end < rev <= start) or (start <= rev < end):
901 count += 1
914 count += 1
902 return abs(self._end - self._start) - count
915 return abs(self._end - self._start) - count
903
916
904 def isascending(self):
917 def isascending(self):
905 return self._ascending
918 return self._ascending
906
919
907 def isdescending(self):
920 def isdescending(self):
908 return not self._ascending
921 return not self._ascending
909
922
910 def first(self):
923 def first(self):
911 if self._ascending:
924 if self._ascending:
912 it = self.fastasc
925 it = self.fastasc
913 else:
926 else:
914 it = self.fastdesc
927 it = self.fastdesc
915 for x in it():
928 for x in it():
916 return x
929 return x
917 return None
930 return None
918
931
919 def last(self):
932 def last(self):
920 if self._ascending:
933 if self._ascending:
921 it = self.fastdesc
934 it = self.fastdesc
922 else:
935 else:
923 it = self.fastasc
936 it = self.fastasc
924 for x in it():
937 for x in it():
925 return x
938 return x
926 return None
939 return None
927
940
928 def __repr__(self):
941 def __repr__(self):
929 d = {False: '-', True: '+'}[self._ascending]
942 d = {False: '-', True: '+'}[self._ascending]
930 return '<%s%s %d:%d>' % (type(self).__name__, d,
943 return '<%s%s %d:%d>' % (type(self).__name__, d,
931 self._start, self._end - 1)
944 self._start, self._end - 1)
932
945
933 class fullreposet(spanset):
946 class fullreposet(spanset):
934 """a set containing all revisions in the repo
947 """a set containing all revisions in the repo
935
948
936 This class exists to host special optimization and magic to handle virtual
949 This class exists to host special optimization and magic to handle virtual
937 revisions such as "null".
950 revisions such as "null".
938 """
951 """
939
952
940 def __init__(self, repo):
953 def __init__(self, repo):
941 super(fullreposet, self).__init__(repo)
954 super(fullreposet, self).__init__(repo)
942
955
943 def __and__(self, other):
956 def __and__(self, other):
944 """As self contains the whole repo, all of the other set should also be
957 """As self contains the whole repo, all of the other set should also be
945 in self. Therefore `self & other = other`.
958 in self. Therefore `self & other = other`.
946
959
947 This boldly assumes the other contains valid revs only.
960 This boldly assumes the other contains valid revs only.
948 """
961 """
949 # other not a smartset, make is so
962 # other not a smartset, make is so
950 if not util.safehasattr(other, 'isascending'):
963 if not util.safehasattr(other, 'isascending'):
951 # filter out hidden revision
964 # filter out hidden revision
952 # (this boldly assumes all smartset are pure)
965 # (this boldly assumes all smartset are pure)
953 #
966 #
954 # `other` was used with "&", let's assume this is a set like
967 # `other` was used with "&", let's assume this is a set like
955 # object.
968 # object.
956 other = baseset(other - self._hiddenrevs)
969 other = baseset(other - self._hiddenrevs)
957
970
958 other.sort(reverse=self.isdescending())
971 other.sort(reverse=self.isdescending())
959 return other
972 return other
960
973
961 def prettyformat(revs):
974 def prettyformat(revs):
962 lines = []
975 lines = []
963 rs = repr(revs)
976 rs = repr(revs)
964 p = 0
977 p = 0
965 while p < len(rs):
978 while p < len(rs):
966 q = rs.find('<', p + 1)
979 q = rs.find('<', p + 1)
967 if q < 0:
980 if q < 0:
968 q = len(rs)
981 q = len(rs)
969 l = rs.count('<', 0, p) - rs.count('>', 0, p)
982 l = rs.count('<', 0, p) - rs.count('>', 0, p)
970 assert l >= 0
983 assert l >= 0
971 lines.append((l, rs[p:q].rstrip()))
984 lines.append((l, rs[p:q].rstrip()))
972 p = q
985 p = q
973 return '\n'.join(' ' * l + s for l, s in lines)
986 return '\n'.join(' ' * l + s for l, s in lines)
General Comments 0
You need to be logged in to leave comments. Login now