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