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