##// END OF EJS Templates
revset: abort when tag or bookmark doesn't exist
Idan Kamara -
r13914:27573f2d default
parent child Browse files
Show More
@@ -1,841 +1,844 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, help, hbisect
9 import parser, util, error, discovery, help, hbisect
10 import bookmarks as bookmarksmod
10 import bookmarks as bookmarksmod
11 import match as matchmod
11 import match as matchmod
12 from i18n import _
12 from i18n import _
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].p1().rev()
210 p = repo[x].p1().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(string or set)``
301 """``branch(string or set)``
302 All changesets belonging to the given branch or the branches of the given
302 All changesets belonging to the given branch or the branches of the given
303 changesets.
303 changesets.
304 """
304 """
305 try:
305 try:
306 b = getstring(x, '')
306 b = getstring(x, '')
307 if b in repo.branchmap():
307 if b in repo.branchmap():
308 return [r for r in subset if repo[r].branch() == b]
308 return [r for r in subset if repo[r].branch() == b]
309 except error.ParseError:
309 except error.ParseError:
310 # not a string, but another revspec, e.g. tip()
310 # not a string, but another revspec, e.g. tip()
311 pass
311 pass
312
312
313 s = getset(repo, range(len(repo)), x)
313 s = getset(repo, range(len(repo)), x)
314 b = set()
314 b = set()
315 for r in s:
315 for r in s:
316 b.add(repo[r].branch())
316 b.add(repo[r].branch())
317 s = set(s)
317 s = set(s)
318 return [r for r in subset if r in s or repo[r].branch() in b]
318 return [r for r in subset if r in s or repo[r].branch() in b]
319
319
320 def ancestor(repo, subset, x):
320 def ancestor(repo, subset, x):
321 """``ancestor(single, single)``
321 """``ancestor(single, single)``
322 Greatest common ancestor of the two changesets.
322 Greatest common ancestor of the two changesets.
323 """
323 """
324 # i18n: "ancestor" is a keyword
324 # i18n: "ancestor" is a keyword
325 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
325 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
326 r = range(len(repo))
326 r = range(len(repo))
327 a = getset(repo, r, l[0])
327 a = getset(repo, r, l[0])
328 b = getset(repo, r, l[1])
328 b = getset(repo, r, l[1])
329 if len(a) != 1 or len(b) != 1:
329 if len(a) != 1 or len(b) != 1:
330 # i18n: "ancestor" is a keyword
330 # i18n: "ancestor" is a keyword
331 raise error.ParseError(_("ancestor arguments must be single revisions"))
331 raise error.ParseError(_("ancestor arguments must be single revisions"))
332 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
332 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
333
333
334 return [r for r in an if r in subset]
334 return [r for r in an if r in subset]
335
335
336 def ancestors(repo, subset, x):
336 def ancestors(repo, subset, x):
337 """``ancestors(set)``
337 """``ancestors(set)``
338 Changesets that are ancestors of a changeset in set.
338 Changesets that are ancestors of a changeset in set.
339 """
339 """
340 args = getset(repo, range(len(repo)), x)
340 args = getset(repo, range(len(repo)), x)
341 if not args:
341 if not args:
342 return []
342 return []
343 s = set(repo.changelog.ancestors(*args)) | set(args)
343 s = set(repo.changelog.ancestors(*args)) | set(args)
344 return [r for r in subset if r in s]
344 return [r for r in subset if r in s]
345
345
346 def descendants(repo, subset, x):
346 def descendants(repo, subset, x):
347 """``descendants(set)``
347 """``descendants(set)``
348 Changesets which are descendants of changesets in set.
348 Changesets which are descendants of changesets in set.
349 """
349 """
350 args = getset(repo, range(len(repo)), x)
350 args = getset(repo, range(len(repo)), x)
351 if not args:
351 if not args:
352 return []
352 return []
353 s = set(repo.changelog.descendants(*args)) | set(args)
353 s = set(repo.changelog.descendants(*args)) | set(args)
354 return [r for r in subset if r in s]
354 return [r for r in subset if r in s]
355
355
356 def follow(repo, subset, x):
356 def follow(repo, subset, x):
357 """``follow()``
357 """``follow()``
358 An alias for ``::.`` (ancestors of the working copy's first parent).
358 An alias for ``::.`` (ancestors of the working copy's first parent).
359 """
359 """
360 # i18n: "follow" is a keyword
360 # i18n: "follow" is a keyword
361 getargs(x, 0, 0, _("follow takes no arguments"))
361 getargs(x, 0, 0, _("follow takes no arguments"))
362 p = repo['.'].rev()
362 p = repo['.'].rev()
363 s = set(repo.changelog.ancestors(p)) | set([p])
363 s = set(repo.changelog.ancestors(p)) | set([p])
364 return [r for r in subset if r in s]
364 return [r for r in subset if r in s]
365
365
366 def date(repo, subset, x):
366 def date(repo, subset, x):
367 """``date(interval)``
367 """``date(interval)``
368 Changesets within the interval, see :hg:`help dates`.
368 Changesets within the interval, see :hg:`help dates`.
369 """
369 """
370 # i18n: "date" is a keyword
370 # i18n: "date" is a keyword
371 ds = getstring(x, _("date requires a string"))
371 ds = getstring(x, _("date requires a string"))
372 dm = util.matchdate(ds)
372 dm = util.matchdate(ds)
373 return [r for r in subset if dm(repo[r].date()[0])]
373 return [r for r in subset if dm(repo[r].date()[0])]
374
374
375 def keyword(repo, subset, x):
375 def keyword(repo, subset, x):
376 """``keyword(string)``
376 """``keyword(string)``
377 Search commit message, user name, and names of changed files for
377 Search commit message, user name, and names of changed files for
378 string.
378 string.
379 """
379 """
380 # i18n: "keyword" is a keyword
380 # i18n: "keyword" is a keyword
381 kw = getstring(x, _("keyword requires a string")).lower()
381 kw = getstring(x, _("keyword requires a string")).lower()
382 l = []
382 l = []
383 for r in subset:
383 for r in subset:
384 c = repo[r]
384 c = repo[r]
385 t = " ".join(c.files() + [c.user(), c.description()])
385 t = " ".join(c.files() + [c.user(), c.description()])
386 if kw in t.lower():
386 if kw in t.lower():
387 l.append(r)
387 l.append(r)
388 return l
388 return l
389
389
390 def grep(repo, subset, x):
390 def grep(repo, subset, x):
391 """``grep(regex)``
391 """``grep(regex)``
392 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
392 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
393 to ensure special escape characters are handled correctly.
393 to ensure special escape characters are handled correctly.
394 """
394 """
395 try:
395 try:
396 # i18n: "grep" is a keyword
396 # i18n: "grep" is a keyword
397 gr = re.compile(getstring(x, _("grep requires a string")))
397 gr = re.compile(getstring(x, _("grep requires a string")))
398 except re.error, e:
398 except re.error, e:
399 raise error.ParseError(_('invalid match pattern: %s') % e)
399 raise error.ParseError(_('invalid match pattern: %s') % e)
400 l = []
400 l = []
401 for r in subset:
401 for r in subset:
402 c = repo[r]
402 c = repo[r]
403 for e in c.files() + [c.user(), c.description()]:
403 for e in c.files() + [c.user(), c.description()]:
404 if gr.search(e):
404 if gr.search(e):
405 l.append(r)
405 l.append(r)
406 break
406 break
407 return l
407 return l
408
408
409 def author(repo, subset, x):
409 def author(repo, subset, x):
410 """``author(string)``
410 """``author(string)``
411 Alias for ``user(string)``.
411 Alias for ``user(string)``.
412 """
412 """
413 # i18n: "author" is a keyword
413 # i18n: "author" is a keyword
414 n = getstring(x, _("author requires a string")).lower()
414 n = getstring(x, _("author requires a string")).lower()
415 return [r for r in subset if n in repo[r].user().lower()]
415 return [r for r in subset if n in repo[r].user().lower()]
416
416
417 def user(repo, subset, x):
417 def user(repo, subset, x):
418 """``user(string)``
418 """``user(string)``
419 User name is string.
419 User name is string.
420 """
420 """
421 return author(repo, subset, x)
421 return author(repo, subset, x)
422
422
423 def hasfile(repo, subset, x):
423 def hasfile(repo, subset, x):
424 """``file(pattern)``
424 """``file(pattern)``
425 Changesets affecting files matched by pattern.
425 Changesets affecting files matched by pattern.
426 """
426 """
427 # i18n: "file" is a keyword
427 # i18n: "file" is a keyword
428 pat = getstring(x, _("file requires a pattern"))
428 pat = getstring(x, _("file requires a pattern"))
429 m = matchmod.match(repo.root, repo.getcwd(), [pat])
429 m = matchmod.match(repo.root, repo.getcwd(), [pat])
430 s = []
430 s = []
431 for r in subset:
431 for r in subset:
432 for f in repo[r].files():
432 for f in repo[r].files():
433 if m(f):
433 if m(f):
434 s.append(r)
434 s.append(r)
435 break
435 break
436 return s
436 return s
437
437
438 def contains(repo, subset, x):
438 def contains(repo, subset, x):
439 """``contains(pattern)``
439 """``contains(pattern)``
440 Revision contains pattern.
440 Revision contains pattern.
441 """
441 """
442 # i18n: "contains" is a keyword
442 # i18n: "contains" is a keyword
443 pat = getstring(x, _("contains requires a pattern"))
443 pat = getstring(x, _("contains requires a pattern"))
444 m = matchmod.match(repo.root, repo.getcwd(), [pat])
444 m = matchmod.match(repo.root, repo.getcwd(), [pat])
445 s = []
445 s = []
446 if m.files() == [pat]:
446 if m.files() == [pat]:
447 for r in subset:
447 for r in subset:
448 if pat in repo[r]:
448 if pat in repo[r]:
449 s.append(r)
449 s.append(r)
450 else:
450 else:
451 for r in subset:
451 for r in subset:
452 for f in repo[r].manifest():
452 for f in repo[r].manifest():
453 if m(f):
453 if m(f):
454 s.append(r)
454 s.append(r)
455 break
455 break
456 return s
456 return s
457
457
458 def checkstatus(repo, subset, pat, field):
458 def checkstatus(repo, subset, pat, field):
459 m = matchmod.match(repo.root, repo.getcwd(), [pat])
459 m = matchmod.match(repo.root, repo.getcwd(), [pat])
460 s = []
460 s = []
461 fast = (m.files() == [pat])
461 fast = (m.files() == [pat])
462 for r in subset:
462 for r in subset:
463 c = repo[r]
463 c = repo[r]
464 if fast:
464 if fast:
465 if pat not in c.files():
465 if pat not in c.files():
466 continue
466 continue
467 else:
467 else:
468 for f in c.files():
468 for f in c.files():
469 if m(f):
469 if m(f):
470 break
470 break
471 else:
471 else:
472 continue
472 continue
473 files = repo.status(c.p1().node(), c.node())[field]
473 files = repo.status(c.p1().node(), c.node())[field]
474 if fast:
474 if fast:
475 if pat in files:
475 if pat in files:
476 s.append(r)
476 s.append(r)
477 else:
477 else:
478 for f in files:
478 for f in files:
479 if m(f):
479 if m(f):
480 s.append(r)
480 s.append(r)
481 break
481 break
482 return s
482 return s
483
483
484 def modifies(repo, subset, x):
484 def modifies(repo, subset, x):
485 """``modifies(pattern)``
485 """``modifies(pattern)``
486 Changesets modifying files matched by pattern.
486 Changesets modifying files matched by pattern.
487 """
487 """
488 # i18n: "modifies" is a keyword
488 # i18n: "modifies" is a keyword
489 pat = getstring(x, _("modifies requires a pattern"))
489 pat = getstring(x, _("modifies requires a pattern"))
490 return checkstatus(repo, subset, pat, 0)
490 return checkstatus(repo, subset, pat, 0)
491
491
492 def adds(repo, subset, x):
492 def adds(repo, subset, x):
493 """``adds(pattern)``
493 """``adds(pattern)``
494 Changesets that add a file matching pattern.
494 Changesets that add a file matching pattern.
495 """
495 """
496 # i18n: "adds" is a keyword
496 # i18n: "adds" is a keyword
497 pat = getstring(x, _("adds requires a pattern"))
497 pat = getstring(x, _("adds requires a pattern"))
498 return checkstatus(repo, subset, pat, 1)
498 return checkstatus(repo, subset, pat, 1)
499
499
500 def removes(repo, subset, x):
500 def removes(repo, subset, x):
501 """``removes(pattern)``
501 """``removes(pattern)``
502 Changesets which remove files matching pattern.
502 Changesets which remove files matching pattern.
503 """
503 """
504 # i18n: "removes" is a keyword
504 # i18n: "removes" is a keyword
505 pat = getstring(x, _("removes requires a pattern"))
505 pat = getstring(x, _("removes requires a pattern"))
506 return checkstatus(repo, subset, pat, 2)
506 return checkstatus(repo, subset, pat, 2)
507
507
508 def merge(repo, subset, x):
508 def merge(repo, subset, x):
509 """``merge()``
509 """``merge()``
510 Changeset is a merge changeset.
510 Changeset is a merge changeset.
511 """
511 """
512 # i18n: "merge" is a keyword
512 # i18n: "merge" is a keyword
513 getargs(x, 0, 0, _("merge takes no arguments"))
513 getargs(x, 0, 0, _("merge takes no arguments"))
514 cl = repo.changelog
514 cl = repo.changelog
515 return [r for r in subset if cl.parentrevs(r)[1] != -1]
515 return [r for r in subset if cl.parentrevs(r)[1] != -1]
516
516
517 def closed(repo, subset, x):
517 def closed(repo, subset, x):
518 """``closed()``
518 """``closed()``
519 Changeset is closed.
519 Changeset is closed.
520 """
520 """
521 # i18n: "closed" is a keyword
521 # i18n: "closed" is a keyword
522 getargs(x, 0, 0, _("closed takes no arguments"))
522 getargs(x, 0, 0, _("closed takes no arguments"))
523 return [r for r in subset if repo[r].extra().get('close')]
523 return [r for r in subset if repo[r].extra().get('close')]
524
524
525 def head(repo, subset, x):
525 def head(repo, subset, x):
526 """``head()``
526 """``head()``
527 Changeset is a named branch head.
527 Changeset is a named branch head.
528 """
528 """
529 # i18n: "head" is a keyword
529 # i18n: "head" is a keyword
530 getargs(x, 0, 0, _("head takes no arguments"))
530 getargs(x, 0, 0, _("head takes no arguments"))
531 hs = set()
531 hs = set()
532 for b, ls in repo.branchmap().iteritems():
532 for b, ls in repo.branchmap().iteritems():
533 hs.update(repo[h].rev() for h in ls)
533 hs.update(repo[h].rev() for h in ls)
534 return [r for r in subset if r in hs]
534 return [r for r in subset if r in hs]
535
535
536 def reverse(repo, subset, x):
536 def reverse(repo, subset, x):
537 """``reverse(set)``
537 """``reverse(set)``
538 Reverse order of set.
538 Reverse order of set.
539 """
539 """
540 l = getset(repo, subset, x)
540 l = getset(repo, subset, x)
541 l.reverse()
541 l.reverse()
542 return l
542 return l
543
543
544 def present(repo, subset, x):
544 def present(repo, subset, x):
545 """``present(set)``
545 """``present(set)``
546 An empty set, if any revision in set isn't found; otherwise,
546 An empty set, if any revision in set isn't found; otherwise,
547 all revisions in set.
547 all revisions in set.
548 """
548 """
549 try:
549 try:
550 return getset(repo, subset, x)
550 return getset(repo, subset, x)
551 except error.RepoLookupError:
551 except error.RepoLookupError:
552 return []
552 return []
553
553
554 def sort(repo, subset, x):
554 def sort(repo, subset, x):
555 """``sort(set[, [-]key...])``
555 """``sort(set[, [-]key...])``
556 Sort set by keys. The default sort order is ascending, specify a key
556 Sort set by keys. The default sort order is ascending, specify a key
557 as ``-key`` to sort in descending order.
557 as ``-key`` to sort in descending order.
558
558
559 The keys can be:
559 The keys can be:
560
560
561 - ``rev`` for the revision number,
561 - ``rev`` for the revision number,
562 - ``branch`` for the branch name,
562 - ``branch`` for the branch name,
563 - ``desc`` for the commit message (description),
563 - ``desc`` for the commit message (description),
564 - ``user`` for user name (``author`` can be used as an alias),
564 - ``user`` for user name (``author`` can be used as an alias),
565 - ``date`` for the commit date
565 - ``date`` for the commit date
566 """
566 """
567 # i18n: "sort" is a keyword
567 # i18n: "sort" is a keyword
568 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
568 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
569 keys = "rev"
569 keys = "rev"
570 if len(l) == 2:
570 if len(l) == 2:
571 keys = getstring(l[1], _("sort spec must be a string"))
571 keys = getstring(l[1], _("sort spec must be a string"))
572
572
573 s = l[0]
573 s = l[0]
574 keys = keys.split()
574 keys = keys.split()
575 l = []
575 l = []
576 def invert(s):
576 def invert(s):
577 return "".join(chr(255 - ord(c)) for c in s)
577 return "".join(chr(255 - ord(c)) for c in s)
578 for r in getset(repo, subset, s):
578 for r in getset(repo, subset, s):
579 c = repo[r]
579 c = repo[r]
580 e = []
580 e = []
581 for k in keys:
581 for k in keys:
582 if k == 'rev':
582 if k == 'rev':
583 e.append(r)
583 e.append(r)
584 elif k == '-rev':
584 elif k == '-rev':
585 e.append(-r)
585 e.append(-r)
586 elif k == 'branch':
586 elif k == 'branch':
587 e.append(c.branch())
587 e.append(c.branch())
588 elif k == '-branch':
588 elif k == '-branch':
589 e.append(invert(c.branch()))
589 e.append(invert(c.branch()))
590 elif k == 'desc':
590 elif k == 'desc':
591 e.append(c.description())
591 e.append(c.description())
592 elif k == '-desc':
592 elif k == '-desc':
593 e.append(invert(c.description()))
593 e.append(invert(c.description()))
594 elif k in 'user author':
594 elif k in 'user author':
595 e.append(c.user())
595 e.append(c.user())
596 elif k in '-user -author':
596 elif k in '-user -author':
597 e.append(invert(c.user()))
597 e.append(invert(c.user()))
598 elif k == 'date':
598 elif k == 'date':
599 e.append(c.date()[0])
599 e.append(c.date()[0])
600 elif k == '-date':
600 elif k == '-date':
601 e.append(-c.date()[0])
601 e.append(-c.date()[0])
602 else:
602 else:
603 raise error.ParseError(_("unknown sort key %r") % k)
603 raise error.ParseError(_("unknown sort key %r") % k)
604 e.append(r)
604 e.append(r)
605 l.append(e)
605 l.append(e)
606 l.sort()
606 l.sort()
607 return [e[-1] for e in l]
607 return [e[-1] for e in l]
608
608
609 def getall(repo, subset, x):
609 def getall(repo, subset, x):
610 """``all()``
610 """``all()``
611 All changesets, the same as ``0:tip``.
611 All changesets, the same as ``0:tip``.
612 """
612 """
613 # i18n: "all" is a keyword
613 # i18n: "all" is a keyword
614 getargs(x, 0, 0, _("all takes no arguments"))
614 getargs(x, 0, 0, _("all takes no arguments"))
615 return subset
615 return subset
616
616
617 def heads(repo, subset, x):
617 def heads(repo, subset, x):
618 """``heads(set)``
618 """``heads(set)``
619 Members of set with no children in set.
619 Members of set with no children in set.
620 """
620 """
621 s = getset(repo, subset, x)
621 s = getset(repo, subset, x)
622 ps = set(parents(repo, subset, x))
622 ps = set(parents(repo, subset, x))
623 return [r for r in s if r not in ps]
623 return [r for r in s if r not in ps]
624
624
625 def roots(repo, subset, x):
625 def roots(repo, subset, x):
626 """``roots(set)``
626 """``roots(set)``
627 Changesets with no parent changeset in set.
627 Changesets with no parent changeset in set.
628 """
628 """
629 s = getset(repo, subset, x)
629 s = getset(repo, subset, x)
630 cs = set(children(repo, subset, x))
630 cs = set(children(repo, subset, x))
631 return [r for r in s if r not in cs]
631 return [r for r in s if r not in cs]
632
632
633 def outgoing(repo, subset, x):
633 def outgoing(repo, subset, x):
634 """``outgoing([path])``
634 """``outgoing([path])``
635 Changesets not found in the specified destination repository, or the
635 Changesets not found in the specified destination repository, or the
636 default push location.
636 default push location.
637 """
637 """
638 import hg # avoid start-up nasties
638 import hg # avoid start-up nasties
639 # i18n: "outgoing" is a keyword
639 # i18n: "outgoing" is a keyword
640 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
640 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
641 # i18n: "outgoing" is a keyword
641 # i18n: "outgoing" is a keyword
642 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
642 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
643 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
643 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
644 dest, branches = hg.parseurl(dest)
644 dest, branches = hg.parseurl(dest)
645 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
645 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
646 if revs:
646 if revs:
647 revs = [repo.lookup(rev) for rev in revs]
647 revs = [repo.lookup(rev) for rev in revs]
648 other = hg.repository(hg.remoteui(repo, {}), dest)
648 other = hg.repository(hg.remoteui(repo, {}), dest)
649 repo.ui.pushbuffer()
649 repo.ui.pushbuffer()
650 o = discovery.findoutgoing(repo, other)
650 o = discovery.findoutgoing(repo, other)
651 repo.ui.popbuffer()
651 repo.ui.popbuffer()
652 cl = repo.changelog
652 cl = repo.changelog
653 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
653 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
654 return [r for r in subset if r in o]
654 return [r for r in subset if r in o]
655
655
656 def tag(repo, subset, x):
656 def tag(repo, subset, x):
657 """``tag(name)``
657 """``tag(name)``
658 The specified tag by name, or all tagged revisions if no name is given.
658 The specified tag by name, or all tagged revisions if no name is given.
659 """
659 """
660 # i18n: "tag" is a keyword
660 # i18n: "tag" is a keyword
661 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
661 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
662 cl = repo.changelog
662 cl = repo.changelog
663 if args:
663 if args:
664 tn = getstring(args[0],
664 tn = getstring(args[0],
665 # i18n: "tag" is a keyword
665 # i18n: "tag" is a keyword
666 _('the argument to tag must be a string'))
666 _('the argument to tag must be a string'))
667 if not repo.tags().get(tn, None):
668 raise util.Abort(_("tag '%s' does not exist") % tn)
667 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
669 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
668 else:
670 else:
669 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
671 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
670 return [r for r in subset if r in s]
672 return [r for r in subset if r in s]
671
673
672 def tagged(repo, subset, x):
674 def tagged(repo, subset, x):
673 return tag(repo, subset, x)
675 return tag(repo, subset, x)
674
676
675 def bookmark(repo, subset, x):
677 def bookmark(repo, subset, x):
676 """``bookmark([name])``
678 """``bookmark([name])``
677 The named bookmark or all bookmarks.
679 The named bookmark or all bookmarks.
678 """
680 """
679 # i18n: "bookmark" is a keyword
681 # i18n: "bookmark" is a keyword
680 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
682 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
681 if args:
683 if args:
682 bm = getstring(args[0],
684 bm = getstring(args[0],
683 # i18n: "bookmark" is a keyword
685 # i18n: "bookmark" is a keyword
684 _('the argument to bookmark must be a string'))
686 _('the argument to bookmark must be a string'))
685 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
687 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
686 if bmrev:
688 if not bmrev:
687 bmrev = repo[bmrev].rev()
689 raise util.Abort(_("bookmark '%s' does not exist") % bm)
690 bmrev = repo[bmrev].rev()
688 return [r for r in subset if r == bmrev]
691 return [r for r in subset if r == bmrev]
689 bms = set([repo[r].rev()
692 bms = set([repo[r].rev()
690 for r in bookmarksmod.listbookmarks(repo).values()])
693 for r in bookmarksmod.listbookmarks(repo).values()])
691 return [r for r in subset if r in bms]
694 return [r for r in subset if r in bms]
692
695
693 def bisected(repo, subset, x):
696 def bisected(repo, subset, x):
694 """``bisected(string)``
697 """``bisected(string)``
695 Changesets marked in the specified bisect state (good, bad, skip).
698 Changesets marked in the specified bisect state (good, bad, skip).
696 """
699 """
697 state = getstring(x, _("bisect requires a string")).lower()
700 state = getstring(x, _("bisect requires a string")).lower()
698 if state not in ('good', 'bad', 'skip', 'unknown'):
701 if state not in ('good', 'bad', 'skip', 'unknown'):
699 raise ParseError(_('invalid bisect state'))
702 raise ParseError(_('invalid bisect state'))
700 marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
703 marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
701 return [r for r in subset if r in marked]
704 return [r for r in subset if r in marked]
702
705
703 symbols = {
706 symbols = {
704 "adds": adds,
707 "adds": adds,
705 "all": getall,
708 "all": getall,
706 "ancestor": ancestor,
709 "ancestor": ancestor,
707 "ancestors": ancestors,
710 "ancestors": ancestors,
708 "author": author,
711 "author": author,
709 "bisected": bisected,
712 "bisected": bisected,
710 "bookmark": bookmark,
713 "bookmark": bookmark,
711 "branch": branch,
714 "branch": branch,
712 "children": children,
715 "children": children,
713 "closed": closed,
716 "closed": closed,
714 "contains": contains,
717 "contains": contains,
715 "date": date,
718 "date": date,
716 "descendants": descendants,
719 "descendants": descendants,
717 "file": hasfile,
720 "file": hasfile,
718 "follow": follow,
721 "follow": follow,
719 "grep": grep,
722 "grep": grep,
720 "head": head,
723 "head": head,
721 "heads": heads,
724 "heads": heads,
722 "keyword": keyword,
725 "keyword": keyword,
723 "limit": limit,
726 "limit": limit,
724 "max": maxrev,
727 "max": maxrev,
725 "min": minrev,
728 "min": minrev,
726 "merge": merge,
729 "merge": merge,
727 "modifies": modifies,
730 "modifies": modifies,
728 "id": node,
731 "id": node,
729 "outgoing": outgoing,
732 "outgoing": outgoing,
730 "p1": p1,
733 "p1": p1,
731 "p2": p2,
734 "p2": p2,
732 "parents": parents,
735 "parents": parents,
733 "present": present,
736 "present": present,
734 "removes": removes,
737 "removes": removes,
735 "reverse": reverse,
738 "reverse": reverse,
736 "rev": rev,
739 "rev": rev,
737 "roots": roots,
740 "roots": roots,
738 "sort": sort,
741 "sort": sort,
739 "tag": tag,
742 "tag": tag,
740 "tagged": tagged,
743 "tagged": tagged,
741 "user": user,
744 "user": user,
742 }
745 }
743
746
744 methods = {
747 methods = {
745 "range": rangeset,
748 "range": rangeset,
746 "string": stringset,
749 "string": stringset,
747 "symbol": symbolset,
750 "symbol": symbolset,
748 "and": andset,
751 "and": andset,
749 "or": orset,
752 "or": orset,
750 "not": notset,
753 "not": notset,
751 "list": listset,
754 "list": listset,
752 "func": func,
755 "func": func,
753 }
756 }
754
757
755 def optimize(x, small):
758 def optimize(x, small):
756 if x is None:
759 if x is None:
757 return 0, x
760 return 0, x
758
761
759 smallbonus = 1
762 smallbonus = 1
760 if small:
763 if small:
761 smallbonus = .5
764 smallbonus = .5
762
765
763 op = x[0]
766 op = x[0]
764 if op == 'minus':
767 if op == 'minus':
765 return optimize(('and', x[1], ('not', x[2])), small)
768 return optimize(('and', x[1], ('not', x[2])), small)
766 elif op == 'dagrange':
769 elif op == 'dagrange':
767 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
770 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
768 ('func', ('symbol', 'ancestors'), x[2])), small)
771 ('func', ('symbol', 'ancestors'), x[2])), small)
769 elif op == 'dagrangepre':
772 elif op == 'dagrangepre':
770 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
773 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
771 elif op == 'dagrangepost':
774 elif op == 'dagrangepost':
772 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
775 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
773 elif op == 'rangepre':
776 elif op == 'rangepre':
774 return optimize(('range', ('string', '0'), x[1]), small)
777 return optimize(('range', ('string', '0'), x[1]), small)
775 elif op == 'rangepost':
778 elif op == 'rangepost':
776 return optimize(('range', x[1], ('string', 'tip')), small)
779 return optimize(('range', x[1], ('string', 'tip')), small)
777 elif op == 'negate':
780 elif op == 'negate':
778 return optimize(('string',
781 return optimize(('string',
779 '-' + getstring(x[1], _("can't negate that"))), small)
782 '-' + getstring(x[1], _("can't negate that"))), small)
780 elif op in 'string symbol negate':
783 elif op in 'string symbol negate':
781 return smallbonus, x # single revisions are small
784 return smallbonus, x # single revisions are small
782 elif op == 'and' or op == 'dagrange':
785 elif op == 'and' or op == 'dagrange':
783 wa, ta = optimize(x[1], True)
786 wa, ta = optimize(x[1], True)
784 wb, tb = optimize(x[2], True)
787 wb, tb = optimize(x[2], True)
785 w = min(wa, wb)
788 w = min(wa, wb)
786 if wa > wb:
789 if wa > wb:
787 return w, (op, tb, ta)
790 return w, (op, tb, ta)
788 return w, (op, ta, tb)
791 return w, (op, ta, tb)
789 elif op == 'or':
792 elif op == 'or':
790 wa, ta = optimize(x[1], False)
793 wa, ta = optimize(x[1], False)
791 wb, tb = optimize(x[2], False)
794 wb, tb = optimize(x[2], False)
792 if wb < wa:
795 if wb < wa:
793 wb, wa = wa, wb
796 wb, wa = wa, wb
794 return max(wa, wb), (op, ta, tb)
797 return max(wa, wb), (op, ta, tb)
795 elif op == 'not':
798 elif op == 'not':
796 o = optimize(x[1], not small)
799 o = optimize(x[1], not small)
797 return o[0], (op, o[1])
800 return o[0], (op, o[1])
798 elif op == 'group':
801 elif op == 'group':
799 return optimize(x[1], small)
802 return optimize(x[1], small)
800 elif op in 'range list':
803 elif op in 'range list':
801 wa, ta = optimize(x[1], small)
804 wa, ta = optimize(x[1], small)
802 wb, tb = optimize(x[2], small)
805 wb, tb = optimize(x[2], small)
803 return wa + wb, (op, ta, tb)
806 return wa + wb, (op, ta, tb)
804 elif op == 'func':
807 elif op == 'func':
805 f = getstring(x[1], _("not a symbol"))
808 f = getstring(x[1], _("not a symbol"))
806 wa, ta = optimize(x[2], small)
809 wa, ta = optimize(x[2], small)
807 if f in "grep date user author keyword branch file outgoing closed":
810 if f in "grep date user author keyword branch file outgoing closed":
808 w = 10 # slow
811 w = 10 # slow
809 elif f in "modifies adds removes":
812 elif f in "modifies adds removes":
810 w = 30 # slower
813 w = 30 # slower
811 elif f == "contains":
814 elif f == "contains":
812 w = 100 # very slow
815 w = 100 # very slow
813 elif f == "ancestor":
816 elif f == "ancestor":
814 w = 1 * smallbonus
817 w = 1 * smallbonus
815 elif f in "reverse limit":
818 elif f in "reverse limit":
816 w = 0
819 w = 0
817 elif f in "sort":
820 elif f in "sort":
818 w = 10 # assume most sorts look at changelog
821 w = 10 # assume most sorts look at changelog
819 else:
822 else:
820 w = 1
823 w = 1
821 return w + wa, (op, x[1], ta)
824 return w + wa, (op, x[1], ta)
822 return 1, x
825 return 1, x
823
826
824 parse = parser.parser(tokenize, elements).parse
827 parse = parser.parser(tokenize, elements).parse
825
828
826 def match(spec):
829 def match(spec):
827 if not spec:
830 if not spec:
828 raise error.ParseError(_("empty query"))
831 raise error.ParseError(_("empty query"))
829 tree, pos = parse(spec)
832 tree, pos = parse(spec)
830 if (pos != len(spec)):
833 if (pos != len(spec)):
831 raise error.ParseError("invalid token", pos)
834 raise error.ParseError("invalid token", pos)
832 weight, tree = optimize(tree, True)
835 weight, tree = optimize(tree, True)
833 def mfunc(repo, subset):
836 def mfunc(repo, subset):
834 return getset(repo, subset, tree)
837 return getset(repo, subset, tree)
835 return mfunc
838 return mfunc
836
839
837 def makedoc(topic, doc):
840 def makedoc(topic, doc):
838 return help.makeitemsdoc(topic, doc, '.. predicatesmarker', symbols)
841 return help.makeitemsdoc(topic, doc, '.. predicatesmarker', symbols)
839
842
840 # tell hggettext to extract docstrings from these functions:
843 # tell hggettext to extract docstrings from these functions:
841 i18nfunctions = symbols.values()
844 i18nfunctions = symbols.values()
General Comments 0
You need to be logged in to leave comments. Login now