##// END OF EJS Templates
revset: note case-insensitive matches in keyword and user
Martin Geisler -
r14354:c66ba016 stable
parent child Browse files
Show More
@@ -1,834 +1,834 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
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 import re
8 import re
9 import parser, util, error, discovery
9 import parser, util, error, discovery
10 import bookmarks as bookmarksmod
10 import bookmarks as bookmarksmod
11 import match as matchmod
11 import match as matchmod
12 from i18n import _, gettext
12 from i18n import _, gettext
13
13
14 elements = {
14 elements = {
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
16 "-": (5, ("negate", 19), ("minus", 5)),
16 "-": (5, ("negate", 19), ("minus", 5)),
17 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
17 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
18 ("dagrangepost", 17)),
18 ("dagrangepost", 17)),
19 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
19 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
20 ("dagrangepost", 17)),
20 ("dagrangepost", 17)),
21 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
21 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
22 "not": (10, ("not", 10)),
22 "not": (10, ("not", 10)),
23 "!": (10, ("not", 10)),
23 "!": (10, ("not", 10)),
24 "and": (5, None, ("and", 5)),
24 "and": (5, None, ("and", 5)),
25 "&": (5, None, ("and", 5)),
25 "&": (5, None, ("and", 5)),
26 "or": (4, None, ("or", 4)),
26 "or": (4, None, ("or", 4)),
27 "|": (4, None, ("or", 4)),
27 "|": (4, None, ("or", 4)),
28 "+": (4, None, ("or", 4)),
28 "+": (4, None, ("or", 4)),
29 ",": (2, None, ("list", 2)),
29 ",": (2, None, ("list", 2)),
30 ")": (0, None, None),
30 ")": (0, None, None),
31 "symbol": (0, ("symbol",), None),
31 "symbol": (0, ("symbol",), None),
32 "string": (0, ("string",), None),
32 "string": (0, ("string",), None),
33 "end": (0, None, None),
33 "end": (0, None, None),
34 }
34 }
35
35
36 keywords = set(['and', 'or', 'not'])
36 keywords = set(['and', 'or', 'not'])
37
37
38 def tokenize(program):
38 def tokenize(program):
39 pos, l = 0, len(program)
39 pos, l = 0, len(program)
40 while pos < l:
40 while pos < l:
41 c = program[pos]
41 c = program[pos]
42 if c.isspace(): # skip inter-token whitespace
42 if c.isspace(): # skip inter-token whitespace
43 pass
43 pass
44 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
44 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
45 yield ('::', None, pos)
45 yield ('::', None, pos)
46 pos += 1 # skip ahead
46 pos += 1 # skip ahead
47 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
47 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
48 yield ('..', None, pos)
48 yield ('..', None, pos)
49 pos += 1 # skip ahead
49 pos += 1 # skip ahead
50 elif c in "():,-|&+!": # handle simple operators
50 elif c in "():,-|&+!": # handle simple operators
51 yield (c, None, pos)
51 yield (c, None, pos)
52 elif (c in '"\'' or c == 'r' and
52 elif (c in '"\'' or c == 'r' and
53 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
53 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
54 if c == 'r':
54 if c == 'r':
55 pos += 1
55 pos += 1
56 c = program[pos]
56 c = program[pos]
57 decode = lambda x: x
57 decode = lambda x: x
58 else:
58 else:
59 decode = lambda x: x.decode('string-escape')
59 decode = lambda x: x.decode('string-escape')
60 pos += 1
60 pos += 1
61 s = pos
61 s = pos
62 while pos < l: # find closing quote
62 while pos < l: # find closing quote
63 d = program[pos]
63 d = program[pos]
64 if d == '\\': # skip over escaped characters
64 if d == '\\': # skip over escaped characters
65 pos += 2
65 pos += 2
66 continue
66 continue
67 if d == c:
67 if d == c:
68 yield ('string', decode(program[s:pos]), s)
68 yield ('string', decode(program[s:pos]), s)
69 break
69 break
70 pos += 1
70 pos += 1
71 else:
71 else:
72 raise error.ParseError(_("unterminated string"), s)
72 raise error.ParseError(_("unterminated string"), s)
73 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
73 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
74 s = pos
74 s = pos
75 pos += 1
75 pos += 1
76 while pos < l: # find end of symbol
76 while pos < l: # find end of symbol
77 d = program[pos]
77 d = program[pos]
78 if not (d.isalnum() or d in "._" or ord(d) > 127):
78 if not (d.isalnum() or d in "._" or ord(d) > 127):
79 break
79 break
80 if d == '.' and program[pos - 1] == '.': # special case for ..
80 if d == '.' and program[pos - 1] == '.': # special case for ..
81 pos -= 1
81 pos -= 1
82 break
82 break
83 pos += 1
83 pos += 1
84 sym = program[s:pos]
84 sym = program[s:pos]
85 if sym in keywords: # operator keywords
85 if sym in keywords: # operator keywords
86 yield (sym, None, s)
86 yield (sym, None, s)
87 else:
87 else:
88 yield ('symbol', sym, s)
88 yield ('symbol', sym, s)
89 pos -= 1
89 pos -= 1
90 else:
90 else:
91 raise error.ParseError(_("syntax error"), pos)
91 raise error.ParseError(_("syntax error"), pos)
92 pos += 1
92 pos += 1
93 yield ('end', None, pos)
93 yield ('end', None, pos)
94
94
95 # helpers
95 # helpers
96
96
97 def getstring(x, err):
97 def getstring(x, err):
98 if x and (x[0] == 'string' or x[0] == 'symbol'):
98 if x and (x[0] == 'string' or x[0] == 'symbol'):
99 return x[1]
99 return x[1]
100 raise error.ParseError(err)
100 raise error.ParseError(err)
101
101
102 def getlist(x):
102 def getlist(x):
103 if not x:
103 if not x:
104 return []
104 return []
105 if x[0] == 'list':
105 if x[0] == 'list':
106 return getlist(x[1]) + [x[2]]
106 return getlist(x[1]) + [x[2]]
107 return [x]
107 return [x]
108
108
109 def getargs(x, min, max, err):
109 def getargs(x, min, max, err):
110 l = getlist(x)
110 l = getlist(x)
111 if len(l) < min or len(l) > max:
111 if len(l) < min or len(l) > max:
112 raise error.ParseError(err)
112 raise error.ParseError(err)
113 return l
113 return l
114
114
115 def getset(repo, subset, x):
115 def getset(repo, subset, x):
116 if not x:
116 if not x:
117 raise error.ParseError(_("missing argument"))
117 raise error.ParseError(_("missing argument"))
118 return methods[x[0]](repo, subset, *x[1:])
118 return methods[x[0]](repo, subset, *x[1:])
119
119
120 # operator methods
120 # operator methods
121
121
122 def stringset(repo, subset, x):
122 def stringset(repo, subset, x):
123 x = repo[x].rev()
123 x = repo[x].rev()
124 if x == -1 and len(subset) == len(repo):
124 if x == -1 and len(subset) == len(repo):
125 return [-1]
125 return [-1]
126 if x in subset:
126 if x in subset:
127 return [x]
127 return [x]
128 return []
128 return []
129
129
130 def symbolset(repo, subset, x):
130 def symbolset(repo, subset, x):
131 if x in symbols:
131 if x in symbols:
132 raise error.ParseError(_("can't use %s here") % x)
132 raise error.ParseError(_("can't use %s here") % x)
133 return stringset(repo, subset, x)
133 return stringset(repo, subset, x)
134
134
135 def rangeset(repo, subset, x, y):
135 def rangeset(repo, subset, x, y):
136 m = getset(repo, subset, x)
136 m = getset(repo, subset, x)
137 if not m:
137 if not m:
138 m = getset(repo, range(len(repo)), x)
138 m = getset(repo, range(len(repo)), x)
139
139
140 n = getset(repo, subset, y)
140 n = getset(repo, subset, y)
141 if not n:
141 if not n:
142 n = getset(repo, range(len(repo)), y)
142 n = getset(repo, range(len(repo)), y)
143
143
144 if not m or not n:
144 if not m or not n:
145 return []
145 return []
146 m, n = m[0], n[-1]
146 m, n = m[0], n[-1]
147
147
148 if m < n:
148 if m < n:
149 r = range(m, n + 1)
149 r = range(m, n + 1)
150 else:
150 else:
151 r = range(m, n - 1, -1)
151 r = range(m, n - 1, -1)
152 s = set(subset)
152 s = set(subset)
153 return [x for x in r if x in s]
153 return [x for x in r if x in s]
154
154
155 def andset(repo, subset, x, y):
155 def andset(repo, subset, x, y):
156 return getset(repo, getset(repo, subset, x), y)
156 return getset(repo, getset(repo, subset, x), y)
157
157
158 def orset(repo, subset, x, y):
158 def orset(repo, subset, x, y):
159 s = set(getset(repo, subset, x))
159 s = set(getset(repo, subset, x))
160 s |= set(getset(repo, [r for r in subset if r not in s], y))
160 s |= set(getset(repo, [r for r in subset if r not in s], y))
161 return [r for r in subset if r in s]
161 return [r for r in subset if r in s]
162
162
163 def notset(repo, subset, x):
163 def notset(repo, subset, x):
164 s = set(getset(repo, subset, x))
164 s = set(getset(repo, subset, x))
165 return [r for r in subset if r not in s]
165 return [r for r in subset if r not in s]
166
166
167 def listset(repo, subset, a, b):
167 def listset(repo, subset, a, b):
168 raise error.ParseError(_("can't use a list in this context"))
168 raise error.ParseError(_("can't use a list in this context"))
169
169
170 def func(repo, subset, a, b):
170 def func(repo, subset, a, b):
171 if a[0] == 'symbol' and a[1] in symbols:
171 if a[0] == 'symbol' and a[1] in symbols:
172 return symbols[a[1]](repo, subset, b)
172 return symbols[a[1]](repo, subset, b)
173 raise error.ParseError(_("not a function: %s") % a[1])
173 raise error.ParseError(_("not a function: %s") % a[1])
174
174
175 # functions
175 # functions
176
176
177 def node(repo, subset, x):
177 def node(repo, subset, x):
178 """``id(string)``
178 """``id(string)``
179 Revision non-ambiguously specified by the given hex string prefix.
179 Revision non-ambiguously specified by the given hex string prefix.
180 """
180 """
181 # i18n: "id" is a keyword
181 # i18n: "id" is a keyword
182 l = getargs(x, 1, 1, _("id requires one argument"))
182 l = getargs(x, 1, 1, _("id requires one argument"))
183 # i18n: "id" is a keyword
183 # i18n: "id" is a keyword
184 n = getstring(l[0], _("id requires a string"))
184 n = getstring(l[0], _("id requires a string"))
185 if len(n) == 40:
185 if len(n) == 40:
186 rn = repo[n].rev()
186 rn = repo[n].rev()
187 else:
187 else:
188 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
188 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
189 return [r for r in subset if r == rn]
189 return [r for r in subset if r == rn]
190
190
191 def rev(repo, subset, x):
191 def rev(repo, subset, x):
192 """``rev(number)``
192 """``rev(number)``
193 Revision with the given numeric identifier.
193 Revision with the given numeric identifier.
194 """
194 """
195 # i18n: "rev" is a keyword
195 # i18n: "rev" is a keyword
196 l = getargs(x, 1, 1, _("rev requires one argument"))
196 l = getargs(x, 1, 1, _("rev requires one argument"))
197 try:
197 try:
198 # i18n: "rev" is a keyword
198 # i18n: "rev" is a keyword
199 l = int(getstring(l[0], _("rev requires a number")))
199 l = int(getstring(l[0], _("rev requires a number")))
200 except ValueError:
200 except ValueError:
201 # i18n: "rev" is a keyword
201 # i18n: "rev" is a keyword
202 raise error.ParseError(_("rev expects a number"))
202 raise error.ParseError(_("rev expects a number"))
203 return [r for r in subset if r == l]
203 return [r for r in subset if r == l]
204
204
205 def p1(repo, subset, x):
205 def p1(repo, subset, x):
206 """``p1([set])``
206 """``p1([set])``
207 First parent of changesets in set, or the working directory.
207 First parent of changesets in set, or the working directory.
208 """
208 """
209 if x is None:
209 if x is None:
210 p = repo[x].parents()[0].rev()
210 p = repo[x].parents()[0].rev()
211 return [r for r in subset if r == p]
211 return [r for r in subset if r == p]
212
212
213 ps = set()
213 ps = set()
214 cl = repo.changelog
214 cl = repo.changelog
215 for r in getset(repo, range(len(repo)), x):
215 for r in getset(repo, range(len(repo)), x):
216 ps.add(cl.parentrevs(r)[0])
216 ps.add(cl.parentrevs(r)[0])
217 return [r for r in subset if r in ps]
217 return [r for r in subset if r in ps]
218
218
219 def p2(repo, subset, x):
219 def p2(repo, subset, x):
220 """``p2([set])``
220 """``p2([set])``
221 Second parent of changesets in set, or the working directory.
221 Second parent of changesets in set, or the working directory.
222 """
222 """
223 if x is None:
223 if x is None:
224 ps = repo[x].parents()
224 ps = repo[x].parents()
225 try:
225 try:
226 p = ps[1].rev()
226 p = ps[1].rev()
227 return [r for r in subset if r == p]
227 return [r for r in subset if r == p]
228 except IndexError:
228 except IndexError:
229 return []
229 return []
230
230
231 ps = set()
231 ps = set()
232 cl = repo.changelog
232 cl = repo.changelog
233 for r in getset(repo, range(len(repo)), x):
233 for r in getset(repo, range(len(repo)), x):
234 ps.add(cl.parentrevs(r)[1])
234 ps.add(cl.parentrevs(r)[1])
235 return [r for r in subset if r in ps]
235 return [r for r in subset if r in ps]
236
236
237 def parents(repo, subset, x):
237 def parents(repo, subset, x):
238 """``parents([set])``
238 """``parents([set])``
239 The set of all parents for all changesets in set, or the working directory.
239 The set of all parents for all changesets in set, or the working directory.
240 """
240 """
241 if x is None:
241 if x is None:
242 ps = tuple(p.rev() for p in repo[x].parents())
242 ps = tuple(p.rev() for p in repo[x].parents())
243 return [r for r in subset if r in ps]
243 return [r for r in subset if r in ps]
244
244
245 ps = set()
245 ps = set()
246 cl = repo.changelog
246 cl = repo.changelog
247 for r in getset(repo, range(len(repo)), x):
247 for r in getset(repo, range(len(repo)), x):
248 ps.update(cl.parentrevs(r))
248 ps.update(cl.parentrevs(r))
249 return [r for r in subset if r in ps]
249 return [r for r in subset if r in ps]
250
250
251 def maxrev(repo, subset, x):
251 def maxrev(repo, subset, x):
252 """``max(set)``
252 """``max(set)``
253 Changeset with highest revision number in set.
253 Changeset with highest revision number in set.
254 """
254 """
255 s = getset(repo, subset, x)
255 s = getset(repo, subset, x)
256 if s:
256 if s:
257 m = max(s)
257 m = max(s)
258 if m in subset:
258 if m in subset:
259 return [m]
259 return [m]
260 return []
260 return []
261
261
262 def minrev(repo, subset, x):
262 def minrev(repo, subset, x):
263 """``min(set)``
263 """``min(set)``
264 Changeset with lowest revision number in set.
264 Changeset with lowest revision number in set.
265 """
265 """
266 s = getset(repo, subset, x)
266 s = getset(repo, subset, x)
267 if s:
267 if s:
268 m = min(s)
268 m = min(s)
269 if m in subset:
269 if m in subset:
270 return [m]
270 return [m]
271 return []
271 return []
272
272
273 def limit(repo, subset, x):
273 def limit(repo, subset, x):
274 """``limit(set, n)``
274 """``limit(set, n)``
275 First n members of set.
275 First n members of set.
276 """
276 """
277 # i18n: "limit" is a keyword
277 # i18n: "limit" is a keyword
278 l = getargs(x, 2, 2, _("limit requires two arguments"))
278 l = getargs(x, 2, 2, _("limit requires two arguments"))
279 try:
279 try:
280 # i18n: "limit" is a keyword
280 # i18n: "limit" is a keyword
281 lim = int(getstring(l[1], _("limit requires a number")))
281 lim = int(getstring(l[1], _("limit requires a number")))
282 except ValueError:
282 except ValueError:
283 # i18n: "limit" is a keyword
283 # i18n: "limit" is a keyword
284 raise error.ParseError(_("limit expects a number"))
284 raise error.ParseError(_("limit expects a number"))
285 return getset(repo, subset, l[0])[:lim]
285 return getset(repo, subset, l[0])[:lim]
286
286
287 def children(repo, subset, x):
287 def children(repo, subset, x):
288 """``children(set)``
288 """``children(set)``
289 Child changesets of changesets in set.
289 Child changesets of changesets in set.
290 """
290 """
291 cs = set()
291 cs = set()
292 cl = repo.changelog
292 cl = repo.changelog
293 s = set(getset(repo, range(len(repo)), x))
293 s = set(getset(repo, range(len(repo)), x))
294 for r in xrange(0, len(repo)):
294 for r in xrange(0, len(repo)):
295 for p in cl.parentrevs(r):
295 for p in cl.parentrevs(r):
296 if p in s:
296 if p in s:
297 cs.add(r)
297 cs.add(r)
298 return [r for r in subset if r in cs]
298 return [r for r in subset if r in cs]
299
299
300 def branch(repo, subset, x):
300 def branch(repo, subset, x):
301 """``branch(set)``
301 """``branch(set)``
302 All changesets belonging to the branches of changesets in set.
302 All changesets belonging to the branches of changesets in set.
303 """
303 """
304 s = getset(repo, range(len(repo)), x)
304 s = getset(repo, range(len(repo)), x)
305 b = set()
305 b = set()
306 for r in s:
306 for r in s:
307 b.add(repo[r].branch())
307 b.add(repo[r].branch())
308 s = set(s)
308 s = set(s)
309 return [r for r in subset if r in s or repo[r].branch() in b]
309 return [r for r in subset if r in s or repo[r].branch() in b]
310
310
311 def ancestor(repo, subset, x):
311 def ancestor(repo, subset, x):
312 """``ancestor(single, single)``
312 """``ancestor(single, single)``
313 Greatest common ancestor of the two changesets.
313 Greatest common ancestor of the two changesets.
314 """
314 """
315 # i18n: "ancestor" is a keyword
315 # i18n: "ancestor" is a keyword
316 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
316 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
317 r = range(len(repo))
317 r = range(len(repo))
318 a = getset(repo, r, l[0])
318 a = getset(repo, r, l[0])
319 b = getset(repo, r, l[1])
319 b = getset(repo, r, l[1])
320 if len(a) != 1 or len(b) != 1:
320 if len(a) != 1 or len(b) != 1:
321 # i18n: "ancestor" is a keyword
321 # i18n: "ancestor" is a keyword
322 raise error.ParseError(_("ancestor arguments must be single revisions"))
322 raise error.ParseError(_("ancestor arguments must be single revisions"))
323 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
323 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
324
324
325 return [r for r in an if r in subset]
325 return [r for r in an if r in subset]
326
326
327 def ancestors(repo, subset, x):
327 def ancestors(repo, subset, x):
328 """``ancestors(set)``
328 """``ancestors(set)``
329 Changesets that are ancestors of a changeset in set.
329 Changesets that are ancestors of a changeset in set.
330 """
330 """
331 args = getset(repo, range(len(repo)), x)
331 args = getset(repo, range(len(repo)), x)
332 if not args:
332 if not args:
333 return []
333 return []
334 s = set(repo.changelog.ancestors(*args)) | set(args)
334 s = set(repo.changelog.ancestors(*args)) | set(args)
335 return [r for r in subset if r in s]
335 return [r for r in subset if r in s]
336
336
337 def descendants(repo, subset, x):
337 def descendants(repo, subset, x):
338 """``descendants(set)``
338 """``descendants(set)``
339 Changesets which are descendants of changesets in set.
339 Changesets which are descendants of changesets in set.
340 """
340 """
341 args = getset(repo, range(len(repo)), x)
341 args = getset(repo, range(len(repo)), x)
342 if not args:
342 if not args:
343 return []
343 return []
344 s = set(repo.changelog.descendants(*args)) | set(args)
344 s = set(repo.changelog.descendants(*args)) | set(args)
345 return [r for r in subset if r in s]
345 return [r for r in subset if r in s]
346
346
347 def follow(repo, subset, x):
347 def follow(repo, subset, x):
348 """``follow()``
348 """``follow()``
349 An alias for ``::.`` (ancestors of the working copy's first parent).
349 An alias for ``::.`` (ancestors of the working copy's first parent).
350 """
350 """
351 # i18n: "follow" is a keyword
351 # i18n: "follow" is a keyword
352 getargs(x, 0, 0, _("follow takes no arguments"))
352 getargs(x, 0, 0, _("follow takes no arguments"))
353 p = repo['.'].rev()
353 p = repo['.'].rev()
354 s = set(repo.changelog.ancestors(p)) | set([p])
354 s = set(repo.changelog.ancestors(p)) | set([p])
355 return [r for r in subset if r in s]
355 return [r for r in subset if r in s]
356
356
357 def date(repo, subset, x):
357 def date(repo, subset, x):
358 """``date(interval)``
358 """``date(interval)``
359 Changesets within the interval, see :hg:`help dates`.
359 Changesets within the interval, see :hg:`help dates`.
360 """
360 """
361 # i18n: "date" is a keyword
361 # i18n: "date" is a keyword
362 ds = getstring(x, _("date requires a string"))
362 ds = getstring(x, _("date requires a string"))
363 dm = util.matchdate(ds)
363 dm = util.matchdate(ds)
364 return [r for r in subset if dm(repo[r].date()[0])]
364 return [r for r in subset if dm(repo[r].date()[0])]
365
365
366 def keyword(repo, subset, x):
366 def keyword(repo, subset, x):
367 """``keyword(string)``
367 """``keyword(string)``
368 Search commit message, user name, and names of changed files for
368 Search commit message, user name, and names of changed files for
369 string.
369 string. The match is case-insensitive.
370 """
370 """
371 # i18n: "keyword" is a keyword
371 # i18n: "keyword" is a keyword
372 kw = getstring(x, _("keyword requires a string")).lower()
372 kw = getstring(x, _("keyword requires a string")).lower()
373 l = []
373 l = []
374 for r in subset:
374 for r in subset:
375 c = repo[r]
375 c = repo[r]
376 t = " ".join(c.files() + [c.user(), c.description()])
376 t = " ".join(c.files() + [c.user(), c.description()])
377 if kw in t.lower():
377 if kw in t.lower():
378 l.append(r)
378 l.append(r)
379 return l
379 return l
380
380
381 def grep(repo, subset, x):
381 def grep(repo, subset, x):
382 """``grep(regex)``
382 """``grep(regex)``
383 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
383 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
384 to ensure special escape characters are handled correctly.
384 to ensure special escape characters are handled correctly.
385 """
385 """
386 try:
386 try:
387 # i18n: "grep" is a keyword
387 # i18n: "grep" is a keyword
388 gr = re.compile(getstring(x, _("grep requires a string")))
388 gr = re.compile(getstring(x, _("grep requires a string")))
389 except re.error, e:
389 except re.error, e:
390 raise error.ParseError(_('invalid match pattern: %s') % e)
390 raise error.ParseError(_('invalid match pattern: %s') % e)
391 l = []
391 l = []
392 for r in subset:
392 for r in subset:
393 c = repo[r]
393 c = repo[r]
394 for e in c.files() + [c.user(), c.description()]:
394 for e in c.files() + [c.user(), c.description()]:
395 if gr.search(e):
395 if gr.search(e):
396 l.append(r)
396 l.append(r)
397 continue
397 continue
398 return l
398 return l
399
399
400 def author(repo, subset, x):
400 def author(repo, subset, x):
401 """``author(string)``
401 """``author(string)``
402 Alias for ``user(string)``.
402 Alias for ``user(string)``.
403 """
403 """
404 # i18n: "author" is a keyword
404 # i18n: "author" is a keyword
405 n = getstring(x, _("author requires a string")).lower()
405 n = getstring(x, _("author requires a string")).lower()
406 return [r for r in subset if n in repo[r].user().lower()]
406 return [r for r in subset if n in repo[r].user().lower()]
407
407
408 def user(repo, subset, x):
408 def user(repo, subset, x):
409 """``user(string)``
409 """``user(string)``
410 User name is string.
410 User name contains string. The match is case-insensitive.
411 """
411 """
412 return author(repo, subset, x)
412 return author(repo, subset, x)
413
413
414 def hasfile(repo, subset, x):
414 def hasfile(repo, subset, x):
415 """``file(pattern)``
415 """``file(pattern)``
416 Changesets affecting files matched by pattern.
416 Changesets affecting files matched by pattern.
417 """
417 """
418 # i18n: "file" is a keyword
418 # i18n: "file" is a keyword
419 pat = getstring(x, _("file requires a pattern"))
419 pat = getstring(x, _("file requires a pattern"))
420 m = matchmod.match(repo.root, repo.getcwd(), [pat])
420 m = matchmod.match(repo.root, repo.getcwd(), [pat])
421 s = []
421 s = []
422 for r in subset:
422 for r in subset:
423 for f in repo[r].files():
423 for f in repo[r].files():
424 if m(f):
424 if m(f):
425 s.append(r)
425 s.append(r)
426 continue
426 continue
427 return s
427 return s
428
428
429 def contains(repo, subset, x):
429 def contains(repo, subset, x):
430 """``contains(pattern)``
430 """``contains(pattern)``
431 Revision contains a file matching pattern. See :hg:`help patterns`
431 Revision contains a file matching pattern. See :hg:`help patterns`
432 for information about file patterns.
432 for information about file patterns.
433 """
433 """
434 # i18n: "contains" is a keyword
434 # i18n: "contains" is a keyword
435 pat = getstring(x, _("contains requires a pattern"))
435 pat = getstring(x, _("contains requires a pattern"))
436 m = matchmod.match(repo.root, repo.getcwd(), [pat])
436 m = matchmod.match(repo.root, repo.getcwd(), [pat])
437 s = []
437 s = []
438 if m.files() == [pat]:
438 if m.files() == [pat]:
439 for r in subset:
439 for r in subset:
440 if pat in repo[r]:
440 if pat in repo[r]:
441 s.append(r)
441 s.append(r)
442 continue
442 continue
443 else:
443 else:
444 for r in subset:
444 for r in subset:
445 for f in repo[r].manifest():
445 for f in repo[r].manifest():
446 if m(f):
446 if m(f):
447 s.append(r)
447 s.append(r)
448 continue
448 continue
449 return s
449 return s
450
450
451 def checkstatus(repo, subset, pat, field):
451 def checkstatus(repo, subset, pat, field):
452 m = matchmod.match(repo.root, repo.getcwd(), [pat])
452 m = matchmod.match(repo.root, repo.getcwd(), [pat])
453 s = []
453 s = []
454 fast = (m.files() == [pat])
454 fast = (m.files() == [pat])
455 for r in subset:
455 for r in subset:
456 c = repo[r]
456 c = repo[r]
457 if fast:
457 if fast:
458 if pat not in c.files():
458 if pat not in c.files():
459 continue
459 continue
460 else:
460 else:
461 for f in c.files():
461 for f in c.files():
462 if m(f):
462 if m(f):
463 break
463 break
464 else:
464 else:
465 continue
465 continue
466 files = repo.status(c.p1().node(), c.node())[field]
466 files = repo.status(c.p1().node(), c.node())[field]
467 if fast:
467 if fast:
468 if pat in files:
468 if pat in files:
469 s.append(r)
469 s.append(r)
470 continue
470 continue
471 else:
471 else:
472 for f in files:
472 for f in files:
473 if m(f):
473 if m(f):
474 s.append(r)
474 s.append(r)
475 continue
475 continue
476 return s
476 return s
477
477
478 def modifies(repo, subset, x):
478 def modifies(repo, subset, x):
479 """``modifies(pattern)``
479 """``modifies(pattern)``
480 Changesets modifying files matched by pattern.
480 Changesets modifying files matched by pattern.
481 """
481 """
482 # i18n: "modifies" is a keyword
482 # i18n: "modifies" is a keyword
483 pat = getstring(x, _("modifies requires a pattern"))
483 pat = getstring(x, _("modifies requires a pattern"))
484 return checkstatus(repo, subset, pat, 0)
484 return checkstatus(repo, subset, pat, 0)
485
485
486 def adds(repo, subset, x):
486 def adds(repo, subset, x):
487 """``adds(pattern)``
487 """``adds(pattern)``
488 Changesets that add a file matching pattern.
488 Changesets that add a file matching pattern.
489 """
489 """
490 # i18n: "adds" is a keyword
490 # i18n: "adds" is a keyword
491 pat = getstring(x, _("adds requires a pattern"))
491 pat = getstring(x, _("adds requires a pattern"))
492 return checkstatus(repo, subset, pat, 1)
492 return checkstatus(repo, subset, pat, 1)
493
493
494 def removes(repo, subset, x):
494 def removes(repo, subset, x):
495 """``removes(pattern)``
495 """``removes(pattern)``
496 Changesets which remove files matching pattern.
496 Changesets which remove files matching pattern.
497 """
497 """
498 # i18n: "removes" is a keyword
498 # i18n: "removes" is a keyword
499 pat = getstring(x, _("removes requires a pattern"))
499 pat = getstring(x, _("removes requires a pattern"))
500 return checkstatus(repo, subset, pat, 2)
500 return checkstatus(repo, subset, pat, 2)
501
501
502 def merge(repo, subset, x):
502 def merge(repo, subset, x):
503 """``merge()``
503 """``merge()``
504 Changeset is a merge changeset.
504 Changeset is a merge changeset.
505 """
505 """
506 # i18n: "merge" is a keyword
506 # i18n: "merge" is a keyword
507 getargs(x, 0, 0, _("merge takes no arguments"))
507 getargs(x, 0, 0, _("merge takes no arguments"))
508 cl = repo.changelog
508 cl = repo.changelog
509 return [r for r in subset if cl.parentrevs(r)[1] != -1]
509 return [r for r in subset if cl.parentrevs(r)[1] != -1]
510
510
511 def closed(repo, subset, x):
511 def closed(repo, subset, x):
512 """``closed()``
512 """``closed()``
513 Changeset is closed.
513 Changeset is closed.
514 """
514 """
515 # i18n: "closed" is a keyword
515 # i18n: "closed" is a keyword
516 getargs(x, 0, 0, _("closed takes no arguments"))
516 getargs(x, 0, 0, _("closed takes no arguments"))
517 return [r for r in subset if repo[r].extra().get('close')]
517 return [r for r in subset if repo[r].extra().get('close')]
518
518
519 def head(repo, subset, x):
519 def head(repo, subset, x):
520 """``head()``
520 """``head()``
521 Changeset is a named branch head.
521 Changeset is a named branch head.
522 """
522 """
523 # i18n: "head" is a keyword
523 # i18n: "head" is a keyword
524 getargs(x, 0, 0, _("head takes no arguments"))
524 getargs(x, 0, 0, _("head takes no arguments"))
525 hs = set()
525 hs = set()
526 for b, ls in repo.branchmap().iteritems():
526 for b, ls in repo.branchmap().iteritems():
527 hs.update(repo[h].rev() for h in ls)
527 hs.update(repo[h].rev() for h in ls)
528 return [r for r in subset if r in hs]
528 return [r for r in subset if r in hs]
529
529
530 def reverse(repo, subset, x):
530 def reverse(repo, subset, x):
531 """``reverse(set)``
531 """``reverse(set)``
532 Reverse order of set.
532 Reverse order of set.
533 """
533 """
534 l = getset(repo, subset, x)
534 l = getset(repo, subset, x)
535 l.reverse()
535 l.reverse()
536 return l
536 return l
537
537
538 def present(repo, subset, x):
538 def present(repo, subset, x):
539 """``present(set)``
539 """``present(set)``
540 An empty set, if any revision in set isn't found; otherwise,
540 An empty set, if any revision in set isn't found; otherwise,
541 all revisions in set.
541 all revisions in set.
542 """
542 """
543 try:
543 try:
544 return getset(repo, subset, x)
544 return getset(repo, subset, x)
545 except error.RepoLookupError:
545 except error.RepoLookupError:
546 return []
546 return []
547
547
548 def sort(repo, subset, x):
548 def sort(repo, subset, x):
549 """``sort(set[, [-]key...])``
549 """``sort(set[, [-]key...])``
550 Sort set by keys. The default sort order is ascending, specify a key
550 Sort set by keys. The default sort order is ascending, specify a key
551 as ``-key`` to sort in descending order.
551 as ``-key`` to sort in descending order.
552
552
553 The keys can be:
553 The keys can be:
554
554
555 - ``rev`` for the revision number,
555 - ``rev`` for the revision number,
556 - ``branch`` for the branch name,
556 - ``branch`` for the branch name,
557 - ``desc`` for the commit message (description),
557 - ``desc`` for the commit message (description),
558 - ``user`` for user name (``author`` can be used as an alias),
558 - ``user`` for user name (``author`` can be used as an alias),
559 - ``date`` for the commit date
559 - ``date`` for the commit date
560 """
560 """
561 # i18n: "sort" is a keyword
561 # i18n: "sort" is a keyword
562 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
562 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
563 keys = "rev"
563 keys = "rev"
564 if len(l) == 2:
564 if len(l) == 2:
565 keys = getstring(l[1], _("sort spec must be a string"))
565 keys = getstring(l[1], _("sort spec must be a string"))
566
566
567 s = l[0]
567 s = l[0]
568 keys = keys.split()
568 keys = keys.split()
569 l = []
569 l = []
570 def invert(s):
570 def invert(s):
571 return "".join(chr(255 - ord(c)) for c in s)
571 return "".join(chr(255 - ord(c)) for c in s)
572 for r in getset(repo, subset, s):
572 for r in getset(repo, subset, s):
573 c = repo[r]
573 c = repo[r]
574 e = []
574 e = []
575 for k in keys:
575 for k in keys:
576 if k == 'rev':
576 if k == 'rev':
577 e.append(r)
577 e.append(r)
578 elif k == '-rev':
578 elif k == '-rev':
579 e.append(-r)
579 e.append(-r)
580 elif k == 'branch':
580 elif k == 'branch':
581 e.append(c.branch())
581 e.append(c.branch())
582 elif k == '-branch':
582 elif k == '-branch':
583 e.append(invert(c.branch()))
583 e.append(invert(c.branch()))
584 elif k == 'desc':
584 elif k == 'desc':
585 e.append(c.description())
585 e.append(c.description())
586 elif k == '-desc':
586 elif k == '-desc':
587 e.append(invert(c.description()))
587 e.append(invert(c.description()))
588 elif k in 'user author':
588 elif k in 'user author':
589 e.append(c.user())
589 e.append(c.user())
590 elif k in '-user -author':
590 elif k in '-user -author':
591 e.append(invert(c.user()))
591 e.append(invert(c.user()))
592 elif k == 'date':
592 elif k == 'date':
593 e.append(c.date()[0])
593 e.append(c.date()[0])
594 elif k == '-date':
594 elif k == '-date':
595 e.append(-c.date()[0])
595 e.append(-c.date()[0])
596 else:
596 else:
597 raise error.ParseError(_("unknown sort key %r") % k)
597 raise error.ParseError(_("unknown sort key %r") % k)
598 e.append(r)
598 e.append(r)
599 l.append(e)
599 l.append(e)
600 l.sort()
600 l.sort()
601 return [e[-1] for e in l]
601 return [e[-1] for e in l]
602
602
603 def getall(repo, subset, x):
603 def getall(repo, subset, x):
604 """``all()``
604 """``all()``
605 All changesets, the same as ``0:tip``.
605 All changesets, the same as ``0:tip``.
606 """
606 """
607 # i18n: "all" is a keyword
607 # i18n: "all" is a keyword
608 getargs(x, 0, 0, _("all takes no arguments"))
608 getargs(x, 0, 0, _("all takes no arguments"))
609 return subset
609 return subset
610
610
611 def heads(repo, subset, x):
611 def heads(repo, subset, x):
612 """``heads(set)``
612 """``heads(set)``
613 Members of set with no children in set.
613 Members of set with no children in set.
614 """
614 """
615 s = getset(repo, subset, x)
615 s = getset(repo, subset, x)
616 ps = set(parents(repo, subset, x))
616 ps = set(parents(repo, subset, x))
617 return [r for r in s if r not in ps]
617 return [r for r in s if r not in ps]
618
618
619 def roots(repo, subset, x):
619 def roots(repo, subset, x):
620 """``roots(set)``
620 """``roots(set)``
621 Changesets with no parent changeset in set.
621 Changesets with no parent changeset in set.
622 """
622 """
623 s = getset(repo, subset, x)
623 s = getset(repo, subset, x)
624 cs = set(children(repo, subset, x))
624 cs = set(children(repo, subset, x))
625 return [r for r in s if r not in cs]
625 return [r for r in s if r not in cs]
626
626
627 def outgoing(repo, subset, x):
627 def outgoing(repo, subset, x):
628 """``outgoing([path])``
628 """``outgoing([path])``
629 Changesets not found in the specified destination repository, or the
629 Changesets not found in the specified destination repository, or the
630 default push location.
630 default push location.
631 """
631 """
632 import hg # avoid start-up nasties
632 import hg # avoid start-up nasties
633 # i18n: "outgoing" is a keyword
633 # i18n: "outgoing" is a keyword
634 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
634 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
635 # i18n: "outgoing" is a keyword
635 # i18n: "outgoing" is a keyword
636 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
636 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
637 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
637 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
638 dest, branches = hg.parseurl(dest)
638 dest, branches = hg.parseurl(dest)
639 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
639 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
640 if revs:
640 if revs:
641 revs = [repo.lookup(rev) for rev in revs]
641 revs = [repo.lookup(rev) for rev in revs]
642 other = hg.repository(hg.remoteui(repo, {}), dest)
642 other = hg.repository(hg.remoteui(repo, {}), dest)
643 repo.ui.pushbuffer()
643 repo.ui.pushbuffer()
644 o = discovery.findoutgoing(repo, other)
644 o = discovery.findoutgoing(repo, other)
645 repo.ui.popbuffer()
645 repo.ui.popbuffer()
646 cl = repo.changelog
646 cl = repo.changelog
647 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
647 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
648 return [r for r in subset if r in o]
648 return [r for r in subset if r in o]
649
649
650 def tag(repo, subset, x):
650 def tag(repo, subset, x):
651 """``tag(name)``
651 """``tag(name)``
652 The specified tag by name, or all tagged revisions if no name is given.
652 The specified tag by name, or all tagged revisions if no name is given.
653 """
653 """
654 # i18n: "tag" is a keyword
654 # i18n: "tag" is a keyword
655 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
655 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
656 cl = repo.changelog
656 cl = repo.changelog
657 if args:
657 if args:
658 tn = getstring(args[0],
658 tn = getstring(args[0],
659 # i18n: "tag" is a keyword
659 # i18n: "tag" is a keyword
660 _('the argument to tag must be a string'))
660 _('the argument to tag must be a string'))
661 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
661 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
662 else:
662 else:
663 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
663 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
664 return [r for r in subset if r in s]
664 return [r for r in subset if r in s]
665
665
666 def tagged(repo, subset, x):
666 def tagged(repo, subset, x):
667 return tag(repo, subset, x)
667 return tag(repo, subset, x)
668
668
669 def bookmark(repo, subset, x):
669 def bookmark(repo, subset, x):
670 """``bookmark([name])``
670 """``bookmark([name])``
671 The named bookmark or all bookmarks.
671 The named bookmark or all bookmarks.
672 """
672 """
673 # i18n: "bookmark" is a keyword
673 # i18n: "bookmark" is a keyword
674 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
674 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
675 if args:
675 if args:
676 bm = getstring(args[0],
676 bm = getstring(args[0],
677 # i18n: "bookmark" is a keyword
677 # i18n: "bookmark" is a keyword
678 _('the argument to bookmark must be a string'))
678 _('the argument to bookmark must be a string'))
679 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
679 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
680 if bmrev:
680 if bmrev:
681 bmrev = repo[bmrev].rev()
681 bmrev = repo[bmrev].rev()
682 return [r for r in subset if r == bmrev]
682 return [r for r in subset if r == bmrev]
683 bms = set([repo[r].rev()
683 bms = set([repo[r].rev()
684 for r in bookmarksmod.listbookmarks(repo).values()])
684 for r in bookmarksmod.listbookmarks(repo).values()])
685 return [r for r in subset if r in bms]
685 return [r for r in subset if r in bms]
686
686
687 symbols = {
687 symbols = {
688 "adds": adds,
688 "adds": adds,
689 "all": getall,
689 "all": getall,
690 "ancestor": ancestor,
690 "ancestor": ancestor,
691 "ancestors": ancestors,
691 "ancestors": ancestors,
692 "author": author,
692 "author": author,
693 "bookmark": bookmark,
693 "bookmark": bookmark,
694 "branch": branch,
694 "branch": branch,
695 "children": children,
695 "children": children,
696 "closed": closed,
696 "closed": closed,
697 "contains": contains,
697 "contains": contains,
698 "date": date,
698 "date": date,
699 "descendants": descendants,
699 "descendants": descendants,
700 "file": hasfile,
700 "file": hasfile,
701 "follow": follow,
701 "follow": follow,
702 "grep": grep,
702 "grep": grep,
703 "head": head,
703 "head": head,
704 "heads": heads,
704 "heads": heads,
705 "keyword": keyword,
705 "keyword": keyword,
706 "limit": limit,
706 "limit": limit,
707 "max": maxrev,
707 "max": maxrev,
708 "min": minrev,
708 "min": minrev,
709 "merge": merge,
709 "merge": merge,
710 "modifies": modifies,
710 "modifies": modifies,
711 "id": node,
711 "id": node,
712 "outgoing": outgoing,
712 "outgoing": outgoing,
713 "p1": p1,
713 "p1": p1,
714 "p2": p2,
714 "p2": p2,
715 "parents": parents,
715 "parents": parents,
716 "present": present,
716 "present": present,
717 "removes": removes,
717 "removes": removes,
718 "reverse": reverse,
718 "reverse": reverse,
719 "rev": rev,
719 "rev": rev,
720 "roots": roots,
720 "roots": roots,
721 "sort": sort,
721 "sort": sort,
722 "tag": tag,
722 "tag": tag,
723 "tagged": tagged,
723 "tagged": tagged,
724 "user": user,
724 "user": user,
725 }
725 }
726
726
727 methods = {
727 methods = {
728 "range": rangeset,
728 "range": rangeset,
729 "string": stringset,
729 "string": stringset,
730 "symbol": symbolset,
730 "symbol": symbolset,
731 "and": andset,
731 "and": andset,
732 "or": orset,
732 "or": orset,
733 "not": notset,
733 "not": notset,
734 "list": listset,
734 "list": listset,
735 "func": func,
735 "func": func,
736 }
736 }
737
737
738 def optimize(x, small):
738 def optimize(x, small):
739 if x is None:
739 if x is None:
740 return 0, x
740 return 0, x
741
741
742 smallbonus = 1
742 smallbonus = 1
743 if small:
743 if small:
744 smallbonus = .5
744 smallbonus = .5
745
745
746 op = x[0]
746 op = x[0]
747 if op == 'minus':
747 if op == 'minus':
748 return optimize(('and', x[1], ('not', x[2])), small)
748 return optimize(('and', x[1], ('not', x[2])), small)
749 elif op == 'dagrange':
749 elif op == 'dagrange':
750 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
750 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
751 ('func', ('symbol', 'ancestors'), x[2])), small)
751 ('func', ('symbol', 'ancestors'), x[2])), small)
752 elif op == 'dagrangepre':
752 elif op == 'dagrangepre':
753 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
753 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
754 elif op == 'dagrangepost':
754 elif op == 'dagrangepost':
755 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
755 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
756 elif op == 'rangepre':
756 elif op == 'rangepre':
757 return optimize(('range', ('string', '0'), x[1]), small)
757 return optimize(('range', ('string', '0'), x[1]), small)
758 elif op == 'rangepost':
758 elif op == 'rangepost':
759 return optimize(('range', x[1], ('string', 'tip')), small)
759 return optimize(('range', x[1], ('string', 'tip')), small)
760 elif op == 'negate':
760 elif op == 'negate':
761 return optimize(('string',
761 return optimize(('string',
762 '-' + getstring(x[1], _("can't negate that"))), small)
762 '-' + getstring(x[1], _("can't negate that"))), small)
763 elif op in 'string symbol negate':
763 elif op in 'string symbol negate':
764 return smallbonus, x # single revisions are small
764 return smallbonus, x # single revisions are small
765 elif op == 'and' or op == 'dagrange':
765 elif op == 'and' or op == 'dagrange':
766 wa, ta = optimize(x[1], True)
766 wa, ta = optimize(x[1], True)
767 wb, tb = optimize(x[2], True)
767 wb, tb = optimize(x[2], True)
768 w = min(wa, wb)
768 w = min(wa, wb)
769 if wa > wb:
769 if wa > wb:
770 return w, (op, tb, ta)
770 return w, (op, tb, ta)
771 return w, (op, ta, tb)
771 return w, (op, ta, tb)
772 elif op == 'or':
772 elif op == 'or':
773 wa, ta = optimize(x[1], False)
773 wa, ta = optimize(x[1], False)
774 wb, tb = optimize(x[2], False)
774 wb, tb = optimize(x[2], False)
775 if wb < wa:
775 if wb < wa:
776 wb, wa = wa, wb
776 wb, wa = wa, wb
777 return max(wa, wb), (op, ta, tb)
777 return max(wa, wb), (op, ta, tb)
778 elif op == 'not':
778 elif op == 'not':
779 o = optimize(x[1], not small)
779 o = optimize(x[1], not small)
780 return o[0], (op, o[1])
780 return o[0], (op, o[1])
781 elif op == 'group':
781 elif op == 'group':
782 return optimize(x[1], small)
782 return optimize(x[1], small)
783 elif op in 'range list':
783 elif op in 'range list':
784 wa, ta = optimize(x[1], small)
784 wa, ta = optimize(x[1], small)
785 wb, tb = optimize(x[2], small)
785 wb, tb = optimize(x[2], small)
786 return wa + wb, (op, ta, tb)
786 return wa + wb, (op, ta, tb)
787 elif op == 'func':
787 elif op == 'func':
788 f = getstring(x[1], _("not a symbol"))
788 f = getstring(x[1], _("not a symbol"))
789 wa, ta = optimize(x[2], small)
789 wa, ta = optimize(x[2], small)
790 if f in "grep date user author keyword branch file outgoing":
790 if f in "grep date user author keyword branch file outgoing":
791 w = 10 # slow
791 w = 10 # slow
792 elif f in "modifies adds removes":
792 elif f in "modifies adds removes":
793 w = 30 # slower
793 w = 30 # slower
794 elif f == "contains":
794 elif f == "contains":
795 w = 100 # very slow
795 w = 100 # very slow
796 elif f == "ancestor":
796 elif f == "ancestor":
797 w = 1 * smallbonus
797 w = 1 * smallbonus
798 elif f in "reverse limit":
798 elif f in "reverse limit":
799 w = 0
799 w = 0
800 elif f in "sort":
800 elif f in "sort":
801 w = 10 # assume most sorts look at changelog
801 w = 10 # assume most sorts look at changelog
802 else:
802 else:
803 w = 1
803 w = 1
804 return w + wa, (op, x[1], ta)
804 return w + wa, (op, x[1], ta)
805 return 1, x
805 return 1, x
806
806
807 parse = parser.parser(tokenize, elements).parse
807 parse = parser.parser(tokenize, elements).parse
808
808
809 def match(spec):
809 def match(spec):
810 if not spec:
810 if not spec:
811 raise error.ParseError(_("empty query"))
811 raise error.ParseError(_("empty query"))
812 tree = parse(spec)
812 tree = parse(spec)
813 weight, tree = optimize(tree, True)
813 weight, tree = optimize(tree, True)
814 def mfunc(repo, subset):
814 def mfunc(repo, subset):
815 return getset(repo, subset, tree)
815 return getset(repo, subset, tree)
816 return mfunc
816 return mfunc
817
817
818 def makedoc(topic, doc):
818 def makedoc(topic, doc):
819 """Generate and include predicates help in revsets topic."""
819 """Generate and include predicates help in revsets topic."""
820 predicates = []
820 predicates = []
821 for name in sorted(symbols):
821 for name in sorted(symbols):
822 text = symbols[name].__doc__
822 text = symbols[name].__doc__
823 if not text:
823 if not text:
824 continue
824 continue
825 text = gettext(text.rstrip())
825 text = gettext(text.rstrip())
826 lines = text.splitlines()
826 lines = text.splitlines()
827 lines[1:] = [(' ' + l.strip()) for l in lines[1:]]
827 lines[1:] = [(' ' + l.strip()) for l in lines[1:]]
828 predicates.append('\n'.join(lines))
828 predicates.append('\n'.join(lines))
829 predicates = '\n\n'.join(predicates)
829 predicates = '\n\n'.join(predicates)
830 doc = doc.replace('.. predicatesmarker', predicates)
830 doc = doc.replace('.. predicatesmarker', predicates)
831 return doc
831 return doc
832
832
833 # tell hggettext to extract docstrings from these functions:
833 # tell hggettext to extract docstrings from these functions:
834 i18nfunctions = symbols.values()
834 i18nfunctions = symbols.values()
General Comments 0
You need to be logged in to leave comments. Login now