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