11 encoding, |
11 encoding, |
12 error, |
12 error, |
13 pycompat, |
13 pycompat, |
14 util, |
14 util, |
15 ) |
15 ) |
16 from .utils import ( |
16 from .utils import stringutil |
17 stringutil, |
17 |
18 ) |
|
19 |
18 |
20 def _typename(o): |
19 def _typename(o): |
21 return pycompat.sysbytes(type(o).__name__).lstrip('_') |
20 return pycompat.sysbytes(type(o).__name__).lstrip('_') |
22 |
21 |
|
22 |
23 class abstractsmartset(object): |
23 class abstractsmartset(object): |
24 |
|
25 def __nonzero__(self): |
24 def __nonzero__(self): |
26 """True if the smartset is not empty""" |
25 """True if the smartset is not empty""" |
27 raise NotImplementedError() |
26 raise NotImplementedError() |
28 |
27 |
29 __bool__ = __nonzero__ |
28 __bool__ = __nonzero__ |
123 def __sub__(self, other): |
122 def __sub__(self, other): |
124 """Returns a new object with the substraction of the two collections. |
123 """Returns a new object with the substraction of the two collections. |
125 |
124 |
126 This is part of the mandatory API for smartset.""" |
125 This is part of the mandatory API for smartset.""" |
127 c = other.__contains__ |
126 c = other.__contains__ |
128 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other), |
127 return self.filter( |
129 cache=False) |
128 lambda r: not c(r), condrepr=('<not %r>', other), cache=False |
|
129 ) |
130 |
130 |
131 def filter(self, condition, condrepr=None, cache=True): |
131 def filter(self, condition, condrepr=None, cache=True): |
132 """Returns this smartset filtered by condition as a new smartset. |
132 """Returns this smartset filtered by condition as a new smartset. |
133 |
133 |
134 `condition` is a callable which takes a revision number and returns a |
134 `condition` is a callable which takes a revision number and returns a |
161 if y is None: |
161 if y is None: |
162 break |
162 break |
163 ys.append(y) |
163 ys.append(y) |
164 return baseset(ys, datarepr=('slice=%d:%d %r', start, stop, self)) |
164 return baseset(ys, datarepr=('slice=%d:%d %r', start, stop, self)) |
165 |
165 |
|
166 |
166 class baseset(abstractsmartset): |
167 class baseset(abstractsmartset): |
167 """Basic data structure that represents a revset and contains the basic |
168 """Basic data structure that represents a revset and contains the basic |
168 operation that it should be able to perform. |
169 operation that it should be able to perform. |
169 |
170 |
170 Every method in this class should be implemented by any smartset class. |
171 Every method in this class should be implemented by any smartset class. |
220 >>> type(rs).__name__ |
221 >>> type(rs).__name__ |
221 'baseset' |
222 'baseset' |
222 >>> rs._istopo |
223 >>> rs._istopo |
223 True |
224 True |
224 """ |
225 """ |
|
226 |
225 def __init__(self, data=(), datarepr=None, istopo=False): |
227 def __init__(self, data=(), datarepr=None, istopo=False): |
226 """ |
228 """ |
227 datarepr: a tuple of (format, obj, ...), a function or an object that |
229 datarepr: a tuple of (format, obj, ...), a function or an object that |
228 provides a printable representation of the given data. |
230 provides a printable representation of the given data. |
229 """ |
231 """ |
340 return self._asclist[0] |
342 return self._asclist[0] |
341 return None |
343 return None |
342 |
344 |
343 def _fastsetop(self, other, op): |
345 def _fastsetop(self, other, op): |
344 # try to use native set operations as fast paths |
346 # try to use native set operations as fast paths |
345 if (type(other) is baseset and r'_set' in other.__dict__ and r'_set' in |
347 if ( |
346 self.__dict__ and self._ascending is not None): |
348 type(other) is baseset |
347 s = baseset(data=getattr(self._set, op)(other._set), |
349 and r'_set' in other.__dict__ |
348 istopo=self._istopo) |
350 and r'_set' in self.__dict__ |
|
351 and self._ascending is not None |
|
352 ): |
|
353 s = baseset( |
|
354 data=getattr(self._set, op)(other._set), istopo=self._istopo |
|
355 ) |
349 s._ascending = self._ascending |
356 s._ascending = self._ascending |
350 else: |
357 else: |
351 s = getattr(super(baseset, self), op)(other) |
358 s = getattr(super(baseset, self), op)(other) |
352 return s |
359 return s |
353 |
360 |
381 if self._ascending is not None: |
388 if self._ascending is not None: |
382 l = self._asclist |
389 l = self._asclist |
383 s = pycompat.byterepr(l) |
390 s = pycompat.byterepr(l) |
384 return '<%s%s %s>' % (_typename(self), d, s) |
391 return '<%s%s %s>' % (_typename(self), d, s) |
385 |
392 |
|
393 |
386 class filteredset(abstractsmartset): |
394 class filteredset(abstractsmartset): |
387 """Duck type for baseset class which iterates lazily over the revisions in |
395 """Duck type for baseset class which iterates lazily over the revisions in |
388 the subset and contains a function which tests for membership in the |
396 the subset and contains a function which tests for membership in the |
389 revset |
397 revset |
390 """ |
398 """ |
|
399 |
391 def __init__(self, subset, condition=lambda x: True, condrepr=None): |
400 def __init__(self, subset, condition=lambda x: True, condrepr=None): |
392 """ |
401 """ |
393 condition: a function that decide whether a revision in the subset |
402 condition: a function that decide whether a revision in the subset |
394 belongs to the revset or not. |
403 belongs to the revset or not. |
395 condrepr: a tuple of (format, obj, ...), a function or an object that |
404 condrepr: a tuple of (format, obj, ...), a function or an object that |
425 return None |
434 return None |
426 return lambda: self._iterfilter(it()) |
435 return lambda: self._iterfilter(it()) |
427 |
436 |
428 def __nonzero__(self): |
437 def __nonzero__(self): |
429 fast = None |
438 fast = None |
430 candidates = [self.fastasc if self.isascending() else None, |
439 candidates = [ |
431 self.fastdesc if self.isdescending() else None, |
440 self.fastasc if self.isascending() else None, |
432 self.fastasc, |
441 self.fastdesc if self.isdescending() else None, |
433 self.fastdesc] |
442 self.fastasc, |
|
443 self.fastdesc, |
|
444 ] |
434 for candidate in candidates: |
445 for candidate in candidates: |
435 if candidate is not None: |
446 if candidate is not None: |
436 fast = candidate |
447 fast = candidate |
437 break |
448 break |
438 |
449 |
496 xs = [pycompat.byterepr(self._subset)] |
507 xs = [pycompat.byterepr(self._subset)] |
497 s = stringutil.buildrepr(self._condrepr) |
508 s = stringutil.buildrepr(self._condrepr) |
498 if s: |
509 if s: |
499 xs.append(s) |
510 xs.append(s) |
500 return '<%s %s>' % (_typename(self), ', '.join(xs)) |
511 return '<%s %s>' % (_typename(self), ', '.join(xs)) |
|
512 |
501 |
513 |
502 def _iterordered(ascending, iter1, iter2): |
514 def _iterordered(ascending, iter1, iter2): |
503 """produce an ordered iteration from two iterators with the same order |
515 """produce an ordered iteration from two iterators with the same order |
504 |
516 |
505 The ascending is used to indicated the iteration direction. |
517 The ascending is used to indicated the iteration direction. |
533 # might have been equality and both are empty |
545 # might have been equality and both are empty |
534 yield val2 |
546 yield val2 |
535 for val in it: |
547 for val in it: |
536 yield val |
548 yield val |
537 |
549 |
|
550 |
538 class addset(abstractsmartset): |
551 class addset(abstractsmartset): |
539 """Represent the addition of two sets |
552 """Represent the addition of two sets |
540 |
553 |
541 Wrapper structure for lazily adding two structures without losing much |
554 Wrapper structure for lazily adding two structures without losing much |
542 performance on the __contains__ method |
555 performance on the __contains__ method |
604 >>> rs = addset(generatorset(xs), ys, ascending=False) |
617 >>> rs = addset(generatorset(xs), ys, ascending=False) |
605 >>> assert rs.fastdesc is None |
618 >>> assert rs.fastdesc is None |
606 >>> [x for x in rs] |
619 >>> [x for x in rs] |
607 [5, 4, 3, 2, 0] |
620 [5, 4, 3, 2, 0] |
608 """ |
621 """ |
|
622 |
609 def __init__(self, revs1, revs2, ascending=None): |
623 def __init__(self, revs1, revs2, ascending=None): |
610 self._r1 = revs1 |
624 self._r1 = revs1 |
611 self._r2 = revs2 |
625 self._r2 = revs2 |
612 self._iter = None |
626 self._iter = None |
613 self._ascending = ascending |
627 self._ascending = ascending |
639 same time, yielding only one value at a time in the given order. |
653 same time, yielding only one value at a time in the given order. |
640 """ |
654 """ |
641 if self._ascending is None: |
655 if self._ascending is None: |
642 if self._genlist: |
656 if self._genlist: |
643 return iter(self._genlist) |
657 return iter(self._genlist) |
|
658 |
644 def arbitraryordergen(): |
659 def arbitraryordergen(): |
645 for r in self._r1: |
660 for r in self._r1: |
646 yield r |
661 yield r |
647 inr1 = self._r1.__contains__ |
662 inr1 = self._r1.__contains__ |
648 for r in self._r2: |
663 for r in self._r2: |
649 if not inr1(r): |
664 if not inr1(r): |
650 yield r |
665 yield r |
|
666 |
651 return arbitraryordergen() |
667 return arbitraryordergen() |
652 # try to use our own fast iterator if it exists |
668 # try to use our own fast iterator if it exists |
653 self._trysetasclist() |
669 self._trysetasclist() |
654 if self._ascending: |
670 if self._ascending: |
655 attr = 'fastasc' |
671 attr = 'fastasc' |
745 @encoding.strmethod |
761 @encoding.strmethod |
746 def __repr__(self): |
762 def __repr__(self): |
747 d = {None: '', False: '-', True: '+'}[self._ascending] |
763 d = {None: '', False: '-', True: '+'}[self._ascending] |
748 return '<%s%s %r, %r>' % (_typename(self), d, self._r1, self._r2) |
764 return '<%s%s %r, %r>' % (_typename(self), d, self._r1, self._r2) |
749 |
765 |
|
766 |
750 class generatorset(abstractsmartset): |
767 class generatorset(abstractsmartset): |
751 """Wrap a generator for lazy iteration |
768 """Wrap a generator for lazy iteration |
752 |
769 |
753 Wrapper structure for generators that provides lazy membership and can |
770 Wrapper structure for generators that provides lazy membership and can |
754 be iterated more than once. |
771 be iterated more than once. |
758 >>> xs = generatorset([0, 1, 4], iterasc=True) |
775 >>> xs = generatorset([0, 1, 4], iterasc=True) |
759 >>> assert xs.last() == xs.last() |
776 >>> assert xs.last() == xs.last() |
760 >>> xs.last() # cached |
777 >>> xs.last() # cached |
761 4 |
778 4 |
762 """ |
779 """ |
|
780 |
763 def __new__(cls, gen, iterasc=None): |
781 def __new__(cls, gen, iterasc=None): |
764 if iterasc is None: |
782 if iterasc is None: |
765 typ = cls |
783 typ = cls |
766 elif iterasc: |
784 elif iterasc: |
767 typ = _generatorsetasc |
785 typ = _generatorsetasc |
828 # |
846 # |
829 # Getting rid of it would provide an about 15% speed up on this |
847 # Getting rid of it would provide an about 15% speed up on this |
830 # iteration. |
848 # iteration. |
831 genlist = self._genlist |
849 genlist = self._genlist |
832 nextgen = self._consumegen() |
850 nextgen = self._consumegen() |
833 _len, _next = len, next # cache global lookup |
851 _len, _next = len, next # cache global lookup |
|
852 |
834 def gen(): |
853 def gen(): |
835 i = 0 |
854 i = 0 |
836 while True: |
855 while True: |
837 if i < _len(genlist): |
856 if i < _len(genlist): |
838 yield genlist[i] |
857 yield genlist[i] |
909 @encoding.strmethod |
929 @encoding.strmethod |
910 def __repr__(self): |
930 def __repr__(self): |
911 d = {False: '-', True: '+'}[self._ascending] |
931 d = {False: '-', True: '+'}[self._ascending] |
912 return '<%s%s>' % (_typename(self), d) |
932 return '<%s%s>' % (_typename(self), d) |
913 |
933 |
|
934 |
914 class _generatorsetasc(generatorset): |
935 class _generatorsetasc(generatorset): |
915 """Special case of generatorset optimized for ascending generators.""" |
936 """Special case of generatorset optimized for ascending generators.""" |
916 |
937 |
917 fastasc = generatorset._iterator |
938 fastasc = generatorset._iterator |
918 |
939 |
946 if l < x: |
968 if l < x: |
947 break |
969 break |
948 |
970 |
949 self._cache[x] = False |
971 self._cache[x] = False |
950 return False |
972 return False |
|
973 |
951 |
974 |
952 def spanset(repo, start=0, end=None): |
975 def spanset(repo, start=0, end=None): |
953 """Create a spanset that represents a range of repository revisions |
976 """Create a spanset that represents a range of repository revisions |
954 |
977 |
955 start: first revision included the set (default to 0) |
978 start: first revision included the set (default to 0) |
962 ascending = start <= end |
985 ascending = start <= end |
963 if not ascending: |
986 if not ascending: |
964 start, end = end + 1, start + 1 |
987 start, end = end + 1, start + 1 |
965 return _spanset(start, end, ascending, repo.changelog.filteredrevs) |
988 return _spanset(start, end, ascending, repo.changelog.filteredrevs) |
966 |
989 |
|
990 |
967 class _spanset(abstractsmartset): |
991 class _spanset(abstractsmartset): |
968 """Duck type for baseset class which represents a range of revisions and |
992 """Duck type for baseset class which represents a range of revisions and |
969 can work lazily and without having all the range in memory |
993 can work lazily and without having all the range in memory |
970 |
994 |
971 Note that spanset(x, y) behave almost like xrange(x, y) except for two |
995 Note that spanset(x, y) behave almost like xrange(x, y) except for two |
972 notable points: |
996 notable points: |
973 - when x < y it will be automatically descending, |
997 - when x < y it will be automatically descending, |
974 - revision filtered with this repoview will be skipped. |
998 - revision filtered with this repoview will be skipped. |
975 |
999 |
976 """ |
1000 """ |
|
1001 |
977 def __init__(self, start, end, ascending, hiddenrevs): |
1002 def __init__(self, start, end, ascending, hiddenrevs): |
978 self._start = start |
1003 self._start = start |
979 self._end = end |
1004 self._end = end |
980 self._ascending = ascending |
1005 self._ascending = ascending |
981 self._hiddenrevs = hiddenrevs |
1006 self._hiddenrevs = hiddenrevs |
1016 return self._iterfilter(iterrange) |
1041 return self._iterfilter(iterrange) |
1017 return iter(iterrange) |
1042 return iter(iterrange) |
1018 |
1043 |
1019 def __contains__(self, rev): |
1044 def __contains__(self, rev): |
1020 hidden = self._hiddenrevs |
1045 hidden = self._hiddenrevs |
1021 return ((self._start <= rev < self._end) |
1046 return (self._start <= rev < self._end) and not ( |
1022 and not (hidden and rev in hidden)) |
1047 hidden and rev in hidden |
|
1048 ) |
1023 |
1049 |
1024 def __nonzero__(self): |
1050 def __nonzero__(self): |
1025 for r in self: |
1051 for r in self: |
1026 return True |
1052 return True |
1027 return False |
1053 return False |
1079 @encoding.strmethod |
1105 @encoding.strmethod |
1080 def __repr__(self): |
1106 def __repr__(self): |
1081 d = {False: '-', True: '+'}[self._ascending] |
1107 d = {False: '-', True: '+'}[self._ascending] |
1082 return '<%s%s %d:%d>' % (_typename(self), d, self._start, self._end) |
1108 return '<%s%s %d:%d>' % (_typename(self), d, self._start, self._end) |
1083 |
1109 |
|
1110 |
1084 class fullreposet(_spanset): |
1111 class fullreposet(_spanset): |
1085 """a set containing all revisions in the repo |
1112 """a set containing all revisions in the repo |
1086 |
1113 |
1087 This class exists to host special optimization and magic to handle virtual |
1114 This class exists to host special optimization and magic to handle virtual |
1088 revisions such as "null". |
1115 revisions such as "null". |
1089 """ |
1116 """ |
1090 |
1117 |
1091 def __init__(self, repo): |
1118 def __init__(self, repo): |
1092 super(fullreposet, self).__init__(0, len(repo), True, |
1119 super(fullreposet, self).__init__( |
1093 repo.changelog.filteredrevs) |
1120 0, len(repo), True, repo.changelog.filteredrevs |
|
1121 ) |
1094 |
1122 |
1095 def __and__(self, other): |
1123 def __and__(self, other): |
1096 """As self contains the whole repo, all of the other set should also be |
1124 """As self contains the whole repo, all of the other set should also be |
1097 in self. Therefore `self & other = other`. |
1125 in self. Therefore `self & other = other`. |
1098 |
1126 |