comparison mercurial/revset.py @ 15898:6902e13ddd03

revset: optimize building large lists in formatrevspec The large or-expressions we used to build required a substantial amount of subset filtering in orset() which was inefficient. Instead we build a single string which we process in one go with a special internal predicate.
author Matt Mackall <mpm@selenic.com>
date Mon, 16 Jan 2012 01:21:22 -0600
parents cd956049fc14
children 476a981fdf34
comparison
equal deleted inserted replaced
15897:cc021114fc98 15898:6902e13ddd03
861 """``user(string)`` 861 """``user(string)``
862 User name contains string. The match is case-insensitive. 862 User name contains string. The match is case-insensitive.
863 """ 863 """
864 return author(repo, subset, x) 864 return author(repo, subset, x)
865 865
866 # for internal use
867 def _list(repo, subset, x):
868 s = getstring(x, "internal error")
869 if not s:
870 return []
871 if not isinstance(subset, set):
872 subset = set(subset)
873 ls = [repo[r].rev() for r in s.split('\0')]
874 return [r for r in ls if r in subset]
875
866 symbols = { 876 symbols = {
867 "adds": adds, 877 "adds": adds,
868 "all": getall, 878 "all": getall,
869 "ancestor": ancestor, 879 "ancestor": ancestor,
870 "ancestors": ancestors, 880 "ancestors": ancestors,
908 "sort": sort, 918 "sort": sort,
909 "secret": secret, 919 "secret": secret,
910 "tag": tag, 920 "tag": tag,
911 "tagged": tagged, 921 "tagged": tagged,
912 "user": user, 922 "user": user,
923 "_list": _list,
913 } 924 }
914 925
915 methods = { 926 methods = {
916 "range": rangeset, 927 "range": rangeset,
917 "string": stringset, 928 "string": stringset,
1093 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()")) 1104 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1094 '(10 or 11):: and ((this()) or (that()))' 1105 '(10 or 11):: and ((this()) or (that()))'
1095 >>> formatspec('%d:: and not %d::', 10, 20) 1106 >>> formatspec('%d:: and not %d::', 10, 20)
1096 '10:: and not 20::' 1107 '10:: and not 20::'
1097 >>> formatspec('%ld or %ld', [], [1]) 1108 >>> formatspec('%ld or %ld', [], [1])
1098 '(0-0) or 1' 1109 "_list('') or 1"
1099 >>> formatspec('keyword(%s)', 'foo\\xe9') 1110 >>> formatspec('keyword(%s)', 'foo\\xe9')
1100 "keyword('foo\\\\xe9')" 1111 "keyword('foo\\\\xe9')"
1101 >>> b = lambda: 'default' 1112 >>> b = lambda: 'default'
1102 >>> b.branch = b 1113 >>> b.branch = b
1103 >>> formatspec('branch(%b)', b) 1114 >>> formatspec('branch(%b)', b)
1104 "branch('default')" 1115 "branch('default')"
1105 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd']) 1116 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1106 "root((('a' or 'b') or ('c' or 'd')))" 1117 "root(_list('a\\x00b\\x00c\\x00d'))"
1107 ''' 1118 '''
1108 1119
1109 def quote(s): 1120 def quote(s):
1110 return repr(str(s)) 1121 return repr(str(s))
1111 1122
1121 return quote(nodemod.hex(arg)) 1132 return quote(nodemod.hex(arg))
1122 elif c == 'b': 1133 elif c == 'b':
1123 return quote(arg.branch()) 1134 return quote(arg.branch())
1124 1135
1125 def listexp(s, t): 1136 def listexp(s, t):
1126 "balance a list s of type t to limit parse tree depth"
1127 l = len(s) 1137 l = len(s)
1128 if l == 0: 1138 if l == 0:
1129 return '(0-0)' # a minimal way to represent an empty set 1139 return "_list('')"
1130 if l == 1: 1140 elif l == 1:
1131 return argtype(t, s[0]) 1141 return argtype(t, s[0])
1142 elif t == 'd':
1143 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1144 elif t == 's':
1145 return "_list('%s')" % "\0".join(s)
1146 elif t == 'n':
1147 return "_list('%s')" % "\0".join(nodemod.hex(a) for a in s)
1148 elif t == 'b':
1149 return "_list('%s')" % "\0".join(a.branch() for a in s)
1150
1132 m = l // 2 1151 m = l // 2
1133 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t)) 1152 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1134 1153
1135 ret = '' 1154 ret = ''
1136 pos = 0 1155 pos = 0