##// END OF EJS Templates
revset: predicate to avoid lookup errors...
Wagner Bruna -
r11944:df52ff09 default
parent child Browse files
Show More
@@ -1,169 +1,173 b''
1 1 Mercurial supports a functional language for selecting a set of
2 2 revisions.
3 3
4 4 The language supports a number of predicates which are joined by infix
5 5 operators. Parenthesis can be used for grouping.
6 6
7 7 Identifiers such as branch names must be quoted with single or double
8 8 quotes if they contain characters outside of
9 9 ``[._a-zA-Z0-9\x80-\xff]`` or if they match one of the predefined
10 10 predicates. Special characters can be used in quoted identifiers by
11 11 escaping them, e.g., ``\n`` is interpreted as a newline.
12 12
13 13 There is a single prefix operator:
14 14
15 15 ``not x``
16 16 Changesets not in x. Short form is ``! x``.
17 17
18 18 These are the supported infix operators:
19 19
20 20 ``x::y``
21 21 A DAG range, meaning all changesets that are descendants of x and
22 22 ancestors of y, including x and y themselves. If the first endpoint
23 23 is left out, this is equivalent to ``ancestors(y)``, if the second
24 24 is left out it is equivalent to ``descendants(x)``.
25 25
26 26 An alternative syntax is ``x..y``.
27 27
28 28 ``x:y``
29 29 All changesets with revision numbers between x and y, both
30 30 inclusive. Either endpoint can be left out, they default to 0 and
31 31 tip.
32 32
33 33 ``x and y``
34 34 The intersection of changesets in x and y. Short form is ``x & y``.
35 35
36 36 ``x or y``
37 37 The union of changesets in x and y. There are two alternative short
38 38 forms: ``x | y`` and ``x + y``.
39 39
40 40 ``x - y``
41 41 Changesets in x but not in y.
42 42
43 43 The following predicates are supported:
44 44
45 45 ``adds(pattern)``
46 46 Changesets that add a file matching pattern.
47 47
48 48 ``all()``
49 49 All changesets, the same as ``0:tip``.
50 50
51 51 ``ancestor(single, single)``
52 52 Greatest common ancestor of the two changesets.
53 53
54 54 ``ancestors(set)``
55 55 Changesets that are ancestors of a changeset in set.
56 56
57 57 ``author(string)``
58 58 Alias for ``user(string)``.
59 59
60 60 ``branch(set)``
61 61 All changesets belonging to the branches of changesets in set.
62 62
63 63 ``children(set)``
64 64 Child changesets of changesets in set.
65 65
66 66 ``closed()``
67 67 Changeset is closed.
68 68
69 69 ``contains(pattern)``
70 70 Revision contains pattern.
71 71
72 72 ``date(interval)``
73 73 Changesets within the interval, see :hg:`help dates`.
74 74
75 75 ``descendants(set)``
76 76 Changesets which are descendants of changesets in set.
77 77
78 78 ``file(pattern)``
79 79 Changesets affecting files matched by pattern.
80 80
81 81 ``follow()``
82 82 An alias for ``::.`` (ancestors of the working copy's first parent).
83 83
84 84 ``grep(regex)``
85 85 Like ``keyword(string)`` but accepts a regex.
86 86
87 87 ``head()``
88 88 Changeset is a head.
89 89
90 90 ``heads(set)``
91 91 Members of set with no children in set.
92 92
93 93 ``keyword(string)``
94 94 Search commit message, user name, and names of changed files for
95 95 string.
96 96
97 97 ``limit(set, n)``
98 98 First n members of set.
99 99
100 100 ``max(set)``
101 101 Changeset with highest revision number in set.
102 102
103 103 ``min(set)``
104 104 Changeset with lowest revision number in set.
105 105
106 106 ``merge()``
107 107 Changeset is a merge changeset.
108 108
109 109 ``modifies(pattern)``
110 110 Changesets modifying files matched by pattern.
111 111
112 112 ``outgoing([path])``
113 113 Changesets not found in the specified destination repository, or the
114 114 default push location.
115 115
116 116 ``p1(set)``
117 117 First parent of changesets in set.
118 118
119 119 ``p2(set)``
120 120 Second parent of changesets in set.
121 121
122 122 ``parents(set)``
123 123 The set of all parents for all changesets in set.
124 124
125 ``present(set)``
126 An empty set, if any revision in set isn't found; otherwise,
127 all revisions in set.
128
125 129 ``removes(pattern)``
126 130 Changesets which remove files matching pattern.
127 131
128 132 ``reverse(set)``
129 133 Reverse order of set.
130 134
131 135 ``roots(set)``
132 136 Changesets with no parent changeset in set.
133 137
134 138 ``sort(set[, [-]key...])``
135 139 Sort set by keys. The default sort order is ascending, specify a key
136 140 as ``-key`` to sort in descending order.
137 141
138 142 The keys can be:
139 143
140 144 - ``rev`` for the revision number,
141 145 - ``branch`` for the branch name,
142 146 - ``desc`` for the commit message (description),
143 147 - ``user`` for user name (``author`` can be used as an alias),
144 148 - ``date`` for the commit date
145 149
146 150 ``tagged()``
147 151 Changeset is tagged.
148 152
149 153 ``user(string)``
150 154 User name is string.
151 155
152 156 Command line equivalents for :hg:`log`::
153 157
154 158 -f -> ::.
155 159 -d x -> date(x)
156 160 -k x -> keyword(x)
157 161 -m -> merge()
158 162 -u x -> user(x)
159 163 -b x -> branch(x)
160 164 -P x -> !::x
161 165 -l x -> limit(expr, x)
162 166
163 167 Some sample queries::
164 168
165 169 hg log -r 'branch(default)'
166 170 hg log -r 'branch(default) and 1.5:: and not merge()'
167 171 hg log -r '1.3::1.5 and keyword(bug) and file("hgext/*")'
168 172 hg log -r 'sort(date("May 2008"), user)'
169 173 hg log -r '(keyword(bug) or keyword(issue)) and not ancestors(tagged())'
@@ -1,581 +1,588 b''
1 1 # revset.py - revision set queries for mercurial
2 2 #
3 3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import re
9 9 import parser, util, error, discovery
10 10 import match as _match
11 11 from i18n import _
12 12
13 13 elements = {
14 14 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
15 15 "-": (19, ("negate", 19), ("minus", 19)),
16 16 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
17 17 ("dagrangepost", 17)),
18 18 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
19 19 ("dagrangepost", 17)),
20 20 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
21 21 "not": (10, ("not", 10)),
22 22 "!": (10, ("not", 10)),
23 23 "and": (5, None, ("and", 5)),
24 24 "&": (5, None, ("and", 5)),
25 25 "or": (4, None, ("or", 4)),
26 26 "|": (4, None, ("or", 4)),
27 27 "+": (4, None, ("or", 4)),
28 28 ",": (2, None, ("list", 2)),
29 29 ")": (0, None, None),
30 30 "symbol": (0, ("symbol",), None),
31 31 "string": (0, ("string",), None),
32 32 "end": (0, None, None),
33 33 }
34 34
35 35 keywords = set(['and', 'or', 'not'])
36 36
37 37 def tokenize(program):
38 38 pos, l = 0, len(program)
39 39 while pos < l:
40 40 c = program[pos]
41 41 if c.isspace(): # skip inter-token whitespace
42 42 pass
43 43 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
44 44 yield ('::', None, pos)
45 45 pos += 1 # skip ahead
46 46 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
47 47 yield ('..', None, pos)
48 48 pos += 1 # skip ahead
49 49 elif c in "():,-|&+!": # handle simple operators
50 50 yield (c, None, pos)
51 51 elif c in '"\'': # handle quoted strings
52 52 pos += 1
53 53 s = pos
54 54 while pos < l: # find closing quote
55 55 d = program[pos]
56 56 if d == '\\': # skip over escaped characters
57 57 pos += 2
58 58 continue
59 59 if d == c:
60 60 yield ('string', program[s:pos].decode('string-escape'), s)
61 61 break
62 62 pos += 1
63 63 else:
64 64 raise error.ParseError(_("unterminated string"), s)
65 65 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
66 66 s = pos
67 67 pos += 1
68 68 while pos < l: # find end of symbol
69 69 d = program[pos]
70 70 if not (d.isalnum() or d in "._" or ord(d) > 127):
71 71 break
72 72 if d == '.' and program[pos - 1] == '.': # special case for ..
73 73 pos -= 1
74 74 break
75 75 pos += 1
76 76 sym = program[s:pos]
77 77 if sym in keywords: # operator keywords
78 78 yield (sym, None, s)
79 79 else:
80 80 yield ('symbol', sym, s)
81 81 pos -= 1
82 82 else:
83 83 raise error.ParseError(_("syntax error"), pos)
84 84 pos += 1
85 85 yield ('end', None, pos)
86 86
87 87 # helpers
88 88
89 89 def getstring(x, err):
90 90 if x and (x[0] == 'string' or x[0] == 'symbol'):
91 91 return x[1]
92 92 raise error.ParseError(err)
93 93
94 94 def getlist(x):
95 95 if not x:
96 96 return []
97 97 if x[0] == 'list':
98 98 return getlist(x[1]) + [x[2]]
99 99 return [x]
100 100
101 101 def getargs(x, min, max, err):
102 102 l = getlist(x)
103 103 if len(l) < min or len(l) > max:
104 104 raise error.ParseError(err)
105 105 return l
106 106
107 107 def getset(repo, subset, x):
108 108 if not x:
109 109 raise error.ParseError(_("missing argument"))
110 110 return methods[x[0]](repo, subset, *x[1:])
111 111
112 112 # operator methods
113 113
114 114 def stringset(repo, subset, x):
115 115 x = repo[x].rev()
116 116 if x == -1 and len(subset) == len(repo):
117 117 return [-1]
118 118 if x in subset:
119 119 return [x]
120 120 return []
121 121
122 122 def symbolset(repo, subset, x):
123 123 if x in symbols:
124 124 raise error.ParseError(_("can't use %s here") % x)
125 125 return stringset(repo, subset, x)
126 126
127 127 def rangeset(repo, subset, x, y):
128 128 m = getset(repo, subset, x)
129 129 if not m:
130 130 m = getset(repo, range(len(repo)), x)
131 131
132 132 n = getset(repo, subset, y)
133 133 if not n:
134 134 n = getset(repo, range(len(repo)), y)
135 135
136 136 if not m or not n:
137 137 return []
138 138 m, n = m[0], n[-1]
139 139
140 140 if m < n:
141 141 r = range(m, n + 1)
142 142 else:
143 143 r = range(m, n - 1, -1)
144 144 s = set(subset)
145 145 return [x for x in r if x in s]
146 146
147 147 def andset(repo, subset, x, y):
148 148 return getset(repo, getset(repo, subset, x), y)
149 149
150 150 def orset(repo, subset, x, y):
151 151 s = set(getset(repo, subset, x))
152 152 s |= set(getset(repo, [r for r in subset if r not in s], y))
153 153 return [r for r in subset if r in s]
154 154
155 155 def notset(repo, subset, x):
156 156 s = set(getset(repo, subset, x))
157 157 return [r for r in subset if r not in s]
158 158
159 159 def listset(repo, subset, a, b):
160 160 raise error.ParseError(_("can't use a list in this context"))
161 161
162 162 def func(repo, subset, a, b):
163 163 if a[0] == 'symbol' and a[1] in symbols:
164 164 return symbols[a[1]](repo, subset, b)
165 165 raise error.ParseError(_("not a function: %s") % a[1])
166 166
167 167 # functions
168 168
169 169 def p1(repo, subset, x):
170 170 ps = set()
171 171 cl = repo.changelog
172 172 for r in getset(repo, subset, x):
173 173 ps.add(cl.parentrevs(r)[0])
174 174 return [r for r in subset if r in ps]
175 175
176 176 def p2(repo, subset, x):
177 177 ps = set()
178 178 cl = repo.changelog
179 179 for r in getset(repo, subset, x):
180 180 ps.add(cl.parentrevs(r)[1])
181 181 return [r for r in subset if r in ps]
182 182
183 183 def parents(repo, subset, x):
184 184 ps = set()
185 185 cl = repo.changelog
186 186 for r in getset(repo, subset, x):
187 187 ps.update(cl.parentrevs(r))
188 188 return [r for r in subset if r in ps]
189 189
190 190 def maxrev(repo, subset, x):
191 191 s = getset(repo, subset, x)
192 192 if s:
193 193 m = max(s)
194 194 if m in subset:
195 195 return [m]
196 196 return []
197 197
198 198 def minrev(repo, subset, x):
199 199 s = getset(repo, subset, x)
200 200 if s:
201 201 m = min(s)
202 202 if m in subset:
203 203 return [m]
204 204 return []
205 205
206 206 def limit(repo, subset, x):
207 207 l = getargs(x, 2, 2, _("limit wants two arguments"))
208 208 try:
209 209 lim = int(getstring(l[1], _("limit wants a number")))
210 210 except ValueError:
211 211 raise error.ParseError(_("limit expects a number"))
212 212 return getset(repo, subset, l[0])[:lim]
213 213
214 214 def children(repo, subset, x):
215 215 cs = set()
216 216 cl = repo.changelog
217 217 s = set(getset(repo, subset, x))
218 218 for r in xrange(0, len(repo)):
219 219 for p in cl.parentrevs(r):
220 220 if p in s:
221 221 cs.add(r)
222 222 return [r for r in subset if r in cs]
223 223
224 224 def branch(repo, subset, x):
225 225 s = getset(repo, range(len(repo)), x)
226 226 b = set()
227 227 for r in s:
228 228 b.add(repo[r].branch())
229 229 s = set(s)
230 230 return [r for r in subset if r in s or repo[r].branch() in b]
231 231
232 232 def ancestor(repo, subset, x):
233 233 l = getargs(x, 2, 2, _("ancestor wants two arguments"))
234 234 r = range(len(repo))
235 235 a = getset(repo, r, l[0])
236 236 b = getset(repo, r, l[1])
237 237 if len(a) != 1 or len(b) != 1:
238 238 raise error.ParseError(_("ancestor arguments must be single revisions"))
239 239 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
240 240
241 241 return [r for r in an if r in subset]
242 242
243 243 def ancestors(repo, subset, x):
244 244 args = getset(repo, range(len(repo)), x)
245 245 if not args:
246 246 return []
247 247 s = set(repo.changelog.ancestors(*args)) | set(args)
248 248 return [r for r in subset if r in s]
249 249
250 250 def descendants(repo, subset, x):
251 251 args = getset(repo, range(len(repo)), x)
252 252 if not args:
253 253 return []
254 254 s = set(repo.changelog.descendants(*args)) | set(args)
255 255 return [r for r in subset if r in s]
256 256
257 257 def follow(repo, subset, x):
258 258 getargs(x, 0, 0, _("follow takes no arguments"))
259 259 p = repo['.'].rev()
260 260 s = set(repo.changelog.ancestors(p)) | set([p])
261 261 return [r for r in subset if r in s]
262 262
263 263 def date(repo, subset, x):
264 264 ds = getstring(x, _("date wants a string"))
265 265 dm = util.matchdate(ds)
266 266 return [r for r in subset if dm(repo[r].date()[0])]
267 267
268 268 def keyword(repo, subset, x):
269 269 kw = getstring(x, _("keyword wants a string")).lower()
270 270 l = []
271 271 for r in subset:
272 272 c = repo[r]
273 273 t = " ".join(c.files() + [c.user(), c.description()])
274 274 if kw in t.lower():
275 275 l.append(r)
276 276 return l
277 277
278 278 def grep(repo, subset, x):
279 279 gr = re.compile(getstring(x, _("grep wants a string")))
280 280 l = []
281 281 for r in subset:
282 282 c = repo[r]
283 283 for e in c.files() + [c.user(), c.description()]:
284 284 if gr.search(e):
285 285 l.append(r)
286 286 continue
287 287 return l
288 288
289 289 def author(repo, subset, x):
290 290 n = getstring(x, _("author wants a string")).lower()
291 291 return [r for r in subset if n in repo[r].user().lower()]
292 292
293 293 def hasfile(repo, subset, x):
294 294 pat = getstring(x, _("file wants a pattern"))
295 295 m = _match.match(repo.root, repo.getcwd(), [pat])
296 296 s = []
297 297 for r in subset:
298 298 for f in repo[r].files():
299 299 if m(f):
300 300 s.append(r)
301 301 continue
302 302 return s
303 303
304 304 def contains(repo, subset, x):
305 305 pat = getstring(x, _("contains wants a pattern"))
306 306 m = _match.match(repo.root, repo.getcwd(), [pat])
307 307 s = []
308 308 if m.files() == [pat]:
309 309 for r in subset:
310 310 if pat in repo[r]:
311 311 s.append(r)
312 312 continue
313 313 else:
314 314 for r in subset:
315 315 for f in repo[r].manifest():
316 316 if m(f):
317 317 s.append(r)
318 318 continue
319 319 return s
320 320
321 321 def checkstatus(repo, subset, pat, field):
322 322 m = _match.match(repo.root, repo.getcwd(), [pat])
323 323 s = []
324 324 fast = (m.files() == [pat])
325 325 for r in subset:
326 326 c = repo[r]
327 327 if fast:
328 328 if pat not in c.files():
329 329 continue
330 330 else:
331 331 for f in c.files():
332 332 if m(f):
333 333 break
334 334 else:
335 335 continue
336 336 files = repo.status(c.p1().node(), c.node())[field]
337 337 if fast:
338 338 if pat in files:
339 339 s.append(r)
340 340 continue
341 341 else:
342 342 for f in files:
343 343 if m(f):
344 344 s.append(r)
345 345 continue
346 346 return s
347 347
348 348 def modifies(repo, subset, x):
349 349 pat = getstring(x, _("modifies wants a pattern"))
350 350 return checkstatus(repo, subset, pat, 0)
351 351
352 352 def adds(repo, subset, x):
353 353 pat = getstring(x, _("adds wants a pattern"))
354 354 return checkstatus(repo, subset, pat, 1)
355 355
356 356 def removes(repo, subset, x):
357 357 pat = getstring(x, _("removes wants a pattern"))
358 358 return checkstatus(repo, subset, pat, 2)
359 359
360 360 def merge(repo, subset, x):
361 361 getargs(x, 0, 0, _("merge takes no arguments"))
362 362 cl = repo.changelog
363 363 return [r for r in subset if cl.parentrevs(r)[1] != -1]
364 364
365 365 def closed(repo, subset, x):
366 366 getargs(x, 0, 0, _("closed takes no arguments"))
367 367 return [r for r in subset if repo[r].extra().get('close')]
368 368
369 369 def head(repo, subset, x):
370 370 getargs(x, 0, 0, _("head takes no arguments"))
371 371 hs = set()
372 372 for b, ls in repo.branchmap().iteritems():
373 373 hs.update(repo[h].rev() for h in ls)
374 374 return [r for r in subset if r in hs]
375 375
376 376 def reverse(repo, subset, x):
377 377 l = getset(repo, subset, x)
378 378 l.reverse()
379 379 return l
380 380
381 def present(repo, subset, x):
382 try:
383 return getset(repo, subset, x)
384 except error.RepoLookupError:
385 return []
386
381 387 def sort(repo, subset, x):
382 388 l = getargs(x, 1, 2, _("sort wants one or two arguments"))
383 389 keys = "rev"
384 390 if len(l) == 2:
385 391 keys = getstring(l[1], _("sort spec must be a string"))
386 392
387 393 s = l[0]
388 394 keys = keys.split()
389 395 l = []
390 396 def invert(s):
391 397 return "".join(chr(255 - ord(c)) for c in s)
392 398 for r in getset(repo, subset, s):
393 399 c = repo[r]
394 400 e = []
395 401 for k in keys:
396 402 if k == 'rev':
397 403 e.append(r)
398 404 elif k == '-rev':
399 405 e.append(-r)
400 406 elif k == 'branch':
401 407 e.append(c.branch())
402 408 elif k == '-branch':
403 409 e.append(invert(c.branch()))
404 410 elif k == 'desc':
405 411 e.append(c.description())
406 412 elif k == '-desc':
407 413 e.append(invert(c.description()))
408 414 elif k in 'user author':
409 415 e.append(c.user())
410 416 elif k in '-user -author':
411 417 e.append(invert(c.user()))
412 418 elif k == 'date':
413 419 e.append(c.date()[0])
414 420 elif k == '-date':
415 421 e.append(-c.date()[0])
416 422 else:
417 423 raise error.ParseError(_("unknown sort key %r") % k)
418 424 e.append(r)
419 425 l.append(e)
420 426 l.sort()
421 427 return [e[-1] for e in l]
422 428
423 429 def getall(repo, subset, x):
424 430 getargs(x, 0, 0, _("all takes no arguments"))
425 431 return subset
426 432
427 433 def heads(repo, subset, x):
428 434 s = getset(repo, subset, x)
429 435 ps = set(parents(repo, subset, x))
430 436 return [r for r in s if r not in ps]
431 437
432 438 def roots(repo, subset, x):
433 439 s = getset(repo, subset, x)
434 440 cs = set(children(repo, subset, x))
435 441 return [r for r in s if r not in cs]
436 442
437 443 def outgoing(repo, subset, x):
438 444 import hg # avoid start-up nasties
439 445 l = getargs(x, 0, 1, _("outgoing wants a repository path"))
440 446 dest = l and getstring(l[0], _("outgoing wants a repository path")) or ''
441 447 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
442 448 dest, branches = hg.parseurl(dest)
443 449 other = hg.repository(hg.remoteui(repo, {}), dest)
444 450 repo.ui.pushbuffer()
445 451 o = discovery.findoutgoing(repo, other)
446 452 repo.ui.popbuffer()
447 453 cl = repo.changelog
448 454 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, None)[0]])
449 455 return [r for r in subset if r in o]
450 456
451 457 def tagged(repo, subset, x):
452 458 getargs(x, 0, 0, _("tagged takes no arguments"))
453 459 cl = repo.changelog
454 460 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
455 461 return [r for r in subset if r in s]
456 462
457 463 symbols = {
458 464 "adds": adds,
459 465 "all": getall,
460 466 "ancestor": ancestor,
461 467 "ancestors": ancestors,
462 468 "author": author,
463 469 "branch": branch,
464 470 "children": children,
465 471 "closed": closed,
466 472 "contains": contains,
467 473 "date": date,
468 474 "descendants": descendants,
469 475 "file": hasfile,
470 476 "follow": follow,
471 477 "grep": grep,
472 478 "head": head,
473 479 "heads": heads,
474 480 "keyword": keyword,
475 481 "limit": limit,
476 482 "max": maxrev,
477 483 "min": minrev,
478 484 "merge": merge,
479 485 "modifies": modifies,
480 486 "outgoing": outgoing,
481 487 "p1": p1,
482 488 "p2": p2,
483 489 "parents": parents,
490 "present": present,
484 491 "removes": removes,
485 492 "reverse": reverse,
486 493 "roots": roots,
487 494 "sort": sort,
488 495 "tagged": tagged,
489 496 "user": author,
490 497 }
491 498
492 499 methods = {
493 500 "range": rangeset,
494 501 "string": stringset,
495 502 "symbol": symbolset,
496 503 "and": andset,
497 504 "or": orset,
498 505 "not": notset,
499 506 "list": listset,
500 507 "func": func,
501 508 }
502 509
503 510 def optimize(x, small):
504 511 if x == None:
505 512 return 0, x
506 513
507 514 smallbonus = 1
508 515 if small:
509 516 smallbonus = .5
510 517
511 518 op = x[0]
512 519 if op == 'minus':
513 520 return optimize(('and', x[1], ('not', x[2])), small)
514 521 elif op == 'dagrange':
515 522 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
516 523 ('func', ('symbol', 'ancestors'), x[2])), small)
517 524 elif op == 'dagrangepre':
518 525 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
519 526 elif op == 'dagrangepost':
520 527 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
521 528 elif op == 'rangepre':
522 529 return optimize(('range', ('string', '0'), x[1]), small)
523 530 elif op == 'rangepost':
524 531 return optimize(('range', x[1], ('string', 'tip')), small)
525 532 elif op == 'negate':
526 533 return optimize(('string',
527 534 '-' + getstring(x[1], _("can't negate that"))), small)
528 535 elif op in 'string symbol negate':
529 536 return smallbonus, x # single revisions are small
530 537 elif op == 'and' or op == 'dagrange':
531 538 wa, ta = optimize(x[1], True)
532 539 wb, tb = optimize(x[2], True)
533 540 w = min(wa, wb)
534 541 if wa > wb:
535 542 return w, (op, tb, ta)
536 543 return w, (op, ta, tb)
537 544 elif op == 'or':
538 545 wa, ta = optimize(x[1], False)
539 546 wb, tb = optimize(x[2], False)
540 547 if wb < wa:
541 548 wb, wa = wa, wb
542 549 return max(wa, wb), (op, ta, tb)
543 550 elif op == 'not':
544 551 o = optimize(x[1], not small)
545 552 return o[0], (op, o[1])
546 553 elif op == 'group':
547 554 return optimize(x[1], small)
548 555 elif op in 'range list':
549 556 wa, ta = optimize(x[1], small)
550 557 wb, tb = optimize(x[2], small)
551 558 return wa + wb, (op, ta, tb)
552 559 elif op == 'func':
553 560 f = getstring(x[1], _("not a symbol"))
554 561 wa, ta = optimize(x[2], small)
555 562 if f in "grep date user author keyword branch file":
556 563 w = 10 # slow
557 564 elif f in "modifies adds removes outgoing":
558 565 w = 30 # slower
559 566 elif f == "contains":
560 567 w = 100 # very slow
561 568 elif f == "ancestor":
562 569 w = 1 * smallbonus
563 570 elif f == "reverse limit":
564 571 w = 0
565 572 elif f in "sort":
566 573 w = 10 # assume most sorts look at changelog
567 574 else:
568 575 w = 1
569 576 return w + wa, (op, x[1], ta)
570 577 return 1, x
571 578
572 579 parse = parser.parser(tokenize, elements).parse
573 580
574 581 def match(spec):
575 582 if not spec:
576 583 raise error.ParseError(_("empty query"))
577 584 tree = parse(spec)
578 585 weight, tree = optimize(tree, True)
579 586 def mfunc(repo, subset):
580 587 return getset(repo, subset, tree)
581 588 return mfunc
General Comments 0
You need to be logged in to leave comments. Login now