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