##// END OF EJS Templates
revset: introduce and use _revsbetween...
Bryan O'Sullivan -
r16862:b6efeb27 default
parent child Browse files
Show More
@@ -1,1658 +1,1685
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, hbisect, phases
9 import parser, util, error, discovery, hbisect, phases
10 import node
10 import node
11 import bookmarks as bookmarksmod
11 import bookmarks as bookmarksmod
12 import match as matchmod
12 import match as matchmod
13 from i18n import _
13 from i18n import _
14 import encoding
14 import encoding
15
15
16 def _revancestors(repo, revs, followfirst):
16 def _revancestors(repo, revs, followfirst):
17 """Like revlog.ancestors(), but supports followfirst."""
17 """Like revlog.ancestors(), but supports followfirst."""
18 cut = followfirst and 1 or None
18 cut = followfirst and 1 or None
19 cl = repo.changelog
19 cl = repo.changelog
20 visit = util.deque(revs)
20 visit = util.deque(revs)
21 seen = set([node.nullrev])
21 seen = set([node.nullrev])
22 while visit:
22 while visit:
23 for parent in cl.parentrevs(visit.popleft())[:cut]:
23 for parent in cl.parentrevs(visit.popleft())[:cut]:
24 if parent not in seen:
24 if parent not in seen:
25 visit.append(parent)
25 visit.append(parent)
26 seen.add(parent)
26 seen.add(parent)
27 yield parent
27 yield parent
28
28
29 def _revdescendants(repo, revs, followfirst):
29 def _revdescendants(repo, revs, followfirst):
30 """Like revlog.descendants() but supports followfirst."""
30 """Like revlog.descendants() but supports followfirst."""
31 cut = followfirst and 1 or None
31 cut = followfirst and 1 or None
32 cl = repo.changelog
32 cl = repo.changelog
33 first = min(revs)
33 first = min(revs)
34 nullrev = node.nullrev
34 nullrev = node.nullrev
35 if first == nullrev:
35 if first == nullrev:
36 # Are there nodes with a null first parent and a non-null
36 # Are there nodes with a null first parent and a non-null
37 # second one? Maybe. Do we care? Probably not.
37 # second one? Maybe. Do we care? Probably not.
38 for i in cl:
38 for i in cl:
39 yield i
39 yield i
40 return
40 return
41
41
42 seen = set(revs)
42 seen = set(revs)
43 for i in xrange(first + 1, len(cl)):
43 for i in xrange(first + 1, len(cl)):
44 for x in cl.parentrevs(i)[:cut]:
44 for x in cl.parentrevs(i)[:cut]:
45 if x != nullrev and x in seen:
45 if x != nullrev and x in seen:
46 seen.add(i)
46 seen.add(i)
47 yield i
47 yield i
48 break
48 break
49
49
50 def _revsbetween(repo, roots, heads):
51 """Return all paths between roots and heads, inclusive of both endpoint
52 sets."""
53 if not roots:
54 return []
55 parentrevs = repo.changelog.parentrevs
56 visit = heads[:]
57 reachable = set()
58 seen = {}
59 minroot = min(roots)
60 roots = set(roots)
61 # open-code the post-order traversal due to the tiny size of
62 # sys.getrecursionlimit()
63 while visit:
64 rev = visit.pop()
65 if rev in roots:
66 reachable.add(rev)
67 parents = parentrevs(rev)
68 seen[rev] = parents
69 for parent in parents:
70 if parent >= minroot and parent not in seen:
71 visit.append(parent)
72 if not reachable:
73 return []
74 for rev in sorted(seen):
75 for parent in seen[rev]:
76 if parent in reachable:
77 reachable.add(rev)
78 return sorted(reachable)
79
50 elements = {
80 elements = {
51 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
81 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
52 "~": (18, None, ("ancestor", 18)),
82 "~": (18, None, ("ancestor", 18)),
53 "^": (18, None, ("parent", 18), ("parentpost", 18)),
83 "^": (18, None, ("parent", 18), ("parentpost", 18)),
54 "-": (5, ("negate", 19), ("minus", 5)),
84 "-": (5, ("negate", 19), ("minus", 5)),
55 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
85 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
56 ("dagrangepost", 17)),
86 ("dagrangepost", 17)),
57 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
87 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
58 ("dagrangepost", 17)),
88 ("dagrangepost", 17)),
59 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
89 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
60 "not": (10, ("not", 10)),
90 "not": (10, ("not", 10)),
61 "!": (10, ("not", 10)),
91 "!": (10, ("not", 10)),
62 "and": (5, None, ("and", 5)),
92 "and": (5, None, ("and", 5)),
63 "&": (5, None, ("and", 5)),
93 "&": (5, None, ("and", 5)),
64 "or": (4, None, ("or", 4)),
94 "or": (4, None, ("or", 4)),
65 "|": (4, None, ("or", 4)),
95 "|": (4, None, ("or", 4)),
66 "+": (4, None, ("or", 4)),
96 "+": (4, None, ("or", 4)),
67 ",": (2, None, ("list", 2)),
97 ",": (2, None, ("list", 2)),
68 ")": (0, None, None),
98 ")": (0, None, None),
69 "symbol": (0, ("symbol",), None),
99 "symbol": (0, ("symbol",), None),
70 "string": (0, ("string",), None),
100 "string": (0, ("string",), None),
71 "end": (0, None, None),
101 "end": (0, None, None),
72 }
102 }
73
103
74 keywords = set(['and', 'or', 'not'])
104 keywords = set(['and', 'or', 'not'])
75
105
76 def tokenize(program):
106 def tokenize(program):
77 pos, l = 0, len(program)
107 pos, l = 0, len(program)
78 while pos < l:
108 while pos < l:
79 c = program[pos]
109 c = program[pos]
80 if c.isspace(): # skip inter-token whitespace
110 if c.isspace(): # skip inter-token whitespace
81 pass
111 pass
82 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
112 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
83 yield ('::', None, pos)
113 yield ('::', None, pos)
84 pos += 1 # skip ahead
114 pos += 1 # skip ahead
85 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
115 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
86 yield ('..', None, pos)
116 yield ('..', None, pos)
87 pos += 1 # skip ahead
117 pos += 1 # skip ahead
88 elif c in "():,-|&+!~^": # handle simple operators
118 elif c in "():,-|&+!~^": # handle simple operators
89 yield (c, None, pos)
119 yield (c, None, pos)
90 elif (c in '"\'' or c == 'r' and
120 elif (c in '"\'' or c == 'r' and
91 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
121 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
92 if c == 'r':
122 if c == 'r':
93 pos += 1
123 pos += 1
94 c = program[pos]
124 c = program[pos]
95 decode = lambda x: x
125 decode = lambda x: x
96 else:
126 else:
97 decode = lambda x: x.decode('string-escape')
127 decode = lambda x: x.decode('string-escape')
98 pos += 1
128 pos += 1
99 s = pos
129 s = pos
100 while pos < l: # find closing quote
130 while pos < l: # find closing quote
101 d = program[pos]
131 d = program[pos]
102 if d == '\\': # skip over escaped characters
132 if d == '\\': # skip over escaped characters
103 pos += 2
133 pos += 2
104 continue
134 continue
105 if d == c:
135 if d == c:
106 yield ('string', decode(program[s:pos]), s)
136 yield ('string', decode(program[s:pos]), s)
107 break
137 break
108 pos += 1
138 pos += 1
109 else:
139 else:
110 raise error.ParseError(_("unterminated string"), s)
140 raise error.ParseError(_("unterminated string"), s)
111 # gather up a symbol/keyword
141 # gather up a symbol/keyword
112 elif c.isalnum() or c in '._' or ord(c) > 127:
142 elif c.isalnum() or c in '._' or ord(c) > 127:
113 s = pos
143 s = pos
114 pos += 1
144 pos += 1
115 while pos < l: # find end of symbol
145 while pos < l: # find end of symbol
116 d = program[pos]
146 d = program[pos]
117 if not (d.isalnum() or d in "._/" or ord(d) > 127):
147 if not (d.isalnum() or d in "._/" or ord(d) > 127):
118 break
148 break
119 if d == '.' and program[pos - 1] == '.': # special case for ..
149 if d == '.' and program[pos - 1] == '.': # special case for ..
120 pos -= 1
150 pos -= 1
121 break
151 break
122 pos += 1
152 pos += 1
123 sym = program[s:pos]
153 sym = program[s:pos]
124 if sym in keywords: # operator keywords
154 if sym in keywords: # operator keywords
125 yield (sym, None, s)
155 yield (sym, None, s)
126 else:
156 else:
127 yield ('symbol', sym, s)
157 yield ('symbol', sym, s)
128 pos -= 1
158 pos -= 1
129 else:
159 else:
130 raise error.ParseError(_("syntax error"), pos)
160 raise error.ParseError(_("syntax error"), pos)
131 pos += 1
161 pos += 1
132 yield ('end', None, pos)
162 yield ('end', None, pos)
133
163
134 # helpers
164 # helpers
135
165
136 def getstring(x, err):
166 def getstring(x, err):
137 if x and (x[0] == 'string' or x[0] == 'symbol'):
167 if x and (x[0] == 'string' or x[0] == 'symbol'):
138 return x[1]
168 return x[1]
139 raise error.ParseError(err)
169 raise error.ParseError(err)
140
170
141 def getlist(x):
171 def getlist(x):
142 if not x:
172 if not x:
143 return []
173 return []
144 if x[0] == 'list':
174 if x[0] == 'list':
145 return getlist(x[1]) + [x[2]]
175 return getlist(x[1]) + [x[2]]
146 return [x]
176 return [x]
147
177
148 def getargs(x, min, max, err):
178 def getargs(x, min, max, err):
149 l = getlist(x)
179 l = getlist(x)
150 if len(l) < min or (max >= 0 and len(l) > max):
180 if len(l) < min or (max >= 0 and len(l) > max):
151 raise error.ParseError(err)
181 raise error.ParseError(err)
152 return l
182 return l
153
183
154 def getset(repo, subset, x):
184 def getset(repo, subset, x):
155 if not x:
185 if not x:
156 raise error.ParseError(_("missing argument"))
186 raise error.ParseError(_("missing argument"))
157 return methods[x[0]](repo, subset, *x[1:])
187 return methods[x[0]](repo, subset, *x[1:])
158
188
159 # operator methods
189 # operator methods
160
190
161 def stringset(repo, subset, x):
191 def stringset(repo, subset, x):
162 x = repo[x].rev()
192 x = repo[x].rev()
163 if x == -1 and len(subset) == len(repo):
193 if x == -1 and len(subset) == len(repo):
164 return [-1]
194 return [-1]
165 if len(subset) == len(repo) or x in subset:
195 if len(subset) == len(repo) or x in subset:
166 return [x]
196 return [x]
167 return []
197 return []
168
198
169 def symbolset(repo, subset, x):
199 def symbolset(repo, subset, x):
170 if x in symbols:
200 if x in symbols:
171 raise error.ParseError(_("can't use %s here") % x)
201 raise error.ParseError(_("can't use %s here") % x)
172 return stringset(repo, subset, x)
202 return stringset(repo, subset, x)
173
203
174 def rangeset(repo, subset, x, y):
204 def rangeset(repo, subset, x, y):
175 m = getset(repo, subset, x)
205 m = getset(repo, subset, x)
176 if not m:
206 if not m:
177 m = getset(repo, range(len(repo)), x)
207 m = getset(repo, range(len(repo)), x)
178
208
179 n = getset(repo, subset, y)
209 n = getset(repo, subset, y)
180 if not n:
210 if not n:
181 n = getset(repo, range(len(repo)), y)
211 n = getset(repo, range(len(repo)), y)
182
212
183 if not m or not n:
213 if not m or not n:
184 return []
214 return []
185 m, n = m[0], n[-1]
215 m, n = m[0], n[-1]
186
216
187 if m < n:
217 if m < n:
188 r = range(m, n + 1)
218 r = range(m, n + 1)
189 else:
219 else:
190 r = range(m, n - 1, -1)
220 r = range(m, n - 1, -1)
191 s = set(subset)
221 s = set(subset)
192 return [x for x in r if x in s]
222 return [x for x in r if x in s]
193
223
194 def dagrange(repo, subset, x, y):
224 def dagrange(repo, subset, x, y):
195 if subset:
225 if subset:
196 r = range(len(repo))
226 r = range(len(repo))
197 m = getset(repo, r, x)
227 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
198 n = getset(repo, r, y)
199 cl = repo.changelog
200 xs = map(cl.rev, cl.nodesbetween(map(cl.node, m), map(cl.node, n))[0])
201 s = set(subset)
228 s = set(subset)
202 return [r for r in xs if r in s]
229 return [r for r in xs if r in s]
203 return []
230 return []
204
231
205 def andset(repo, subset, x, y):
232 def andset(repo, subset, x, y):
206 return getset(repo, getset(repo, subset, x), y)
233 return getset(repo, getset(repo, subset, x), y)
207
234
208 def orset(repo, subset, x, y):
235 def orset(repo, subset, x, y):
209 xl = getset(repo, subset, x)
236 xl = getset(repo, subset, x)
210 s = set(xl)
237 s = set(xl)
211 yl = getset(repo, [r for r in subset if r not in s], y)
238 yl = getset(repo, [r for r in subset if r not in s], y)
212 return xl + yl
239 return xl + yl
213
240
214 def notset(repo, subset, x):
241 def notset(repo, subset, x):
215 s = set(getset(repo, subset, x))
242 s = set(getset(repo, subset, x))
216 return [r for r in subset if r not in s]
243 return [r for r in subset if r not in s]
217
244
218 def listset(repo, subset, a, b):
245 def listset(repo, subset, a, b):
219 raise error.ParseError(_("can't use a list in this context"))
246 raise error.ParseError(_("can't use a list in this context"))
220
247
221 def func(repo, subset, a, b):
248 def func(repo, subset, a, b):
222 if a[0] == 'symbol' and a[1] in symbols:
249 if a[0] == 'symbol' and a[1] in symbols:
223 return symbols[a[1]](repo, subset, b)
250 return symbols[a[1]](repo, subset, b)
224 raise error.ParseError(_("not a function: %s") % a[1])
251 raise error.ParseError(_("not a function: %s") % a[1])
225
252
226 # functions
253 # functions
227
254
228 def adds(repo, subset, x):
255 def adds(repo, subset, x):
229 """``adds(pattern)``
256 """``adds(pattern)``
230 Changesets that add a file matching pattern.
257 Changesets that add a file matching pattern.
231 """
258 """
232 # i18n: "adds" is a keyword
259 # i18n: "adds" is a keyword
233 pat = getstring(x, _("adds requires a pattern"))
260 pat = getstring(x, _("adds requires a pattern"))
234 return checkstatus(repo, subset, pat, 1)
261 return checkstatus(repo, subset, pat, 1)
235
262
236 def ancestor(repo, subset, x):
263 def ancestor(repo, subset, x):
237 """``ancestor(single, single)``
264 """``ancestor(single, single)``
238 Greatest common ancestor of the two changesets.
265 Greatest common ancestor of the two changesets.
239 """
266 """
240 # i18n: "ancestor" is a keyword
267 # i18n: "ancestor" is a keyword
241 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
268 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
242 r = range(len(repo))
269 r = range(len(repo))
243 a = getset(repo, r, l[0])
270 a = getset(repo, r, l[0])
244 b = getset(repo, r, l[1])
271 b = getset(repo, r, l[1])
245 if len(a) != 1 or len(b) != 1:
272 if len(a) != 1 or len(b) != 1:
246 # i18n: "ancestor" is a keyword
273 # i18n: "ancestor" is a keyword
247 raise error.ParseError(_("ancestor arguments must be single revisions"))
274 raise error.ParseError(_("ancestor arguments must be single revisions"))
248 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
275 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
249
276
250 return [r for r in an if r in subset]
277 return [r for r in an if r in subset]
251
278
252 def _ancestors(repo, subset, x, followfirst=False):
279 def _ancestors(repo, subset, x, followfirst=False):
253 args = getset(repo, range(len(repo)), x)
280 args = getset(repo, range(len(repo)), x)
254 if not args:
281 if not args:
255 return []
282 return []
256 s = set(_revancestors(repo, args, followfirst)) | set(args)
283 s = set(_revancestors(repo, args, followfirst)) | set(args)
257 return [r for r in subset if r in s]
284 return [r for r in subset if r in s]
258
285
259 def ancestors(repo, subset, x):
286 def ancestors(repo, subset, x):
260 """``ancestors(set)``
287 """``ancestors(set)``
261 Changesets that are ancestors of a changeset in set.
288 Changesets that are ancestors of a changeset in set.
262 """
289 """
263 return _ancestors(repo, subset, x)
290 return _ancestors(repo, subset, x)
264
291
265 def _firstancestors(repo, subset, x):
292 def _firstancestors(repo, subset, x):
266 # ``_firstancestors(set)``
293 # ``_firstancestors(set)``
267 # Like ``ancestors(set)`` but follows only the first parents.
294 # Like ``ancestors(set)`` but follows only the first parents.
268 return _ancestors(repo, subset, x, followfirst=True)
295 return _ancestors(repo, subset, x, followfirst=True)
269
296
270 def ancestorspec(repo, subset, x, n):
297 def ancestorspec(repo, subset, x, n):
271 """``set~n``
298 """``set~n``
272 Changesets that are the Nth ancestor (first parents only) of a changeset
299 Changesets that are the Nth ancestor (first parents only) of a changeset
273 in set.
300 in set.
274 """
301 """
275 try:
302 try:
276 n = int(n[1])
303 n = int(n[1])
277 except (TypeError, ValueError):
304 except (TypeError, ValueError):
278 raise error.ParseError(_("~ expects a number"))
305 raise error.ParseError(_("~ expects a number"))
279 ps = set()
306 ps = set()
280 cl = repo.changelog
307 cl = repo.changelog
281 for r in getset(repo, subset, x):
308 for r in getset(repo, subset, x):
282 for i in range(n):
309 for i in range(n):
283 r = cl.parentrevs(r)[0]
310 r = cl.parentrevs(r)[0]
284 ps.add(r)
311 ps.add(r)
285 return [r for r in subset if r in ps]
312 return [r for r in subset if r in ps]
286
313
287 def author(repo, subset, x):
314 def author(repo, subset, x):
288 """``author(string)``
315 """``author(string)``
289 Alias for ``user(string)``.
316 Alias for ``user(string)``.
290 """
317 """
291 # i18n: "author" is a keyword
318 # i18n: "author" is a keyword
292 n = encoding.lower(getstring(x, _("author requires a string")))
319 n = encoding.lower(getstring(x, _("author requires a string")))
293 kind, pattern, matcher = _substringmatcher(n)
320 kind, pattern, matcher = _substringmatcher(n)
294 return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
321 return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
295
322
296 def bisect(repo, subset, x):
323 def bisect(repo, subset, x):
297 """``bisect(string)``
324 """``bisect(string)``
298 Changesets marked in the specified bisect status:
325 Changesets marked in the specified bisect status:
299
326
300 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
327 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
301 - ``goods``, ``bads`` : csets topologicaly good/bad
328 - ``goods``, ``bads`` : csets topologicaly good/bad
302 - ``range`` : csets taking part in the bisection
329 - ``range`` : csets taking part in the bisection
303 - ``pruned`` : csets that are goods, bads or skipped
330 - ``pruned`` : csets that are goods, bads or skipped
304 - ``untested`` : csets whose fate is yet unknown
331 - ``untested`` : csets whose fate is yet unknown
305 - ``ignored`` : csets ignored due to DAG topology
332 - ``ignored`` : csets ignored due to DAG topology
306 - ``current`` : the cset currently being bisected
333 - ``current`` : the cset currently being bisected
307 """
334 """
308 status = getstring(x, _("bisect requires a string")).lower()
335 status = getstring(x, _("bisect requires a string")).lower()
309 state = set(hbisect.get(repo, status))
336 state = set(hbisect.get(repo, status))
310 return [r for r in subset if r in state]
337 return [r for r in subset if r in state]
311
338
312 # Backward-compatibility
339 # Backward-compatibility
313 # - no help entry so that we do not advertise it any more
340 # - no help entry so that we do not advertise it any more
314 def bisected(repo, subset, x):
341 def bisected(repo, subset, x):
315 return bisect(repo, subset, x)
342 return bisect(repo, subset, x)
316
343
317 def bookmark(repo, subset, x):
344 def bookmark(repo, subset, x):
318 """``bookmark([name])``
345 """``bookmark([name])``
319 The named bookmark or all bookmarks.
346 The named bookmark or all bookmarks.
320
347
321 If `name` starts with `re:`, the remainder of the name is treated as
348 If `name` starts with `re:`, the remainder of the name is treated as
322 a regular expression. To match a bookmark that actually starts with `re:`,
349 a regular expression. To match a bookmark that actually starts with `re:`,
323 use the prefix `literal:`.
350 use the prefix `literal:`.
324 """
351 """
325 # i18n: "bookmark" is a keyword
352 # i18n: "bookmark" is a keyword
326 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
353 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
327 if args:
354 if args:
328 bm = getstring(args[0],
355 bm = getstring(args[0],
329 # i18n: "bookmark" is a keyword
356 # i18n: "bookmark" is a keyword
330 _('the argument to bookmark must be a string'))
357 _('the argument to bookmark must be a string'))
331 kind, pattern, matcher = _stringmatcher(bm)
358 kind, pattern, matcher = _stringmatcher(bm)
332 if kind == 'literal':
359 if kind == 'literal':
333 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
360 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
334 if not bmrev:
361 if not bmrev:
335 raise util.Abort(_("bookmark '%s' does not exist") % bm)
362 raise util.Abort(_("bookmark '%s' does not exist") % bm)
336 bmrev = repo[bmrev].rev()
363 bmrev = repo[bmrev].rev()
337 return [r for r in subset if r == bmrev]
364 return [r for r in subset if r == bmrev]
338 else:
365 else:
339 matchrevs = set()
366 matchrevs = set()
340 for name, bmrev in bookmarksmod.listbookmarks(repo).iteritems():
367 for name, bmrev in bookmarksmod.listbookmarks(repo).iteritems():
341 if matcher(name):
368 if matcher(name):
342 matchrevs.add(bmrev)
369 matchrevs.add(bmrev)
343 if not matchrevs:
370 if not matchrevs:
344 raise util.Abort(_("no bookmarks exist that match '%s'")
371 raise util.Abort(_("no bookmarks exist that match '%s'")
345 % pattern)
372 % pattern)
346 bmrevs = set()
373 bmrevs = set()
347 for bmrev in matchrevs:
374 for bmrev in matchrevs:
348 bmrevs.add(repo[bmrev].rev())
375 bmrevs.add(repo[bmrev].rev())
349 return [r for r in subset if r in bmrevs]
376 return [r for r in subset if r in bmrevs]
350
377
351 bms = set([repo[r].rev()
378 bms = set([repo[r].rev()
352 for r in bookmarksmod.listbookmarks(repo).values()])
379 for r in bookmarksmod.listbookmarks(repo).values()])
353 return [r for r in subset if r in bms]
380 return [r for r in subset if r in bms]
354
381
355 def branch(repo, subset, x):
382 def branch(repo, subset, x):
356 """``branch(string or set)``
383 """``branch(string or set)``
357 All changesets belonging to the given branch or the branches of the given
384 All changesets belonging to the given branch or the branches of the given
358 changesets.
385 changesets.
359
386
360 If `string` starts with `re:`, the remainder of the name is treated as
387 If `string` starts with `re:`, the remainder of the name is treated as
361 a regular expression. To match a branch that actually starts with `re:`,
388 a regular expression. To match a branch that actually starts with `re:`,
362 use the prefix `literal:`.
389 use the prefix `literal:`.
363 """
390 """
364 try:
391 try:
365 b = getstring(x, '')
392 b = getstring(x, '')
366 except error.ParseError:
393 except error.ParseError:
367 # not a string, but another revspec, e.g. tip()
394 # not a string, but another revspec, e.g. tip()
368 pass
395 pass
369 else:
396 else:
370 kind, pattern, matcher = _stringmatcher(b)
397 kind, pattern, matcher = _stringmatcher(b)
371 if kind == 'literal':
398 if kind == 'literal':
372 # note: falls through to the revspec case if no branch with
399 # note: falls through to the revspec case if no branch with
373 # this name exists
400 # this name exists
374 if pattern in repo.branchmap():
401 if pattern in repo.branchmap():
375 return [r for r in subset if matcher(repo[r].branch())]
402 return [r for r in subset if matcher(repo[r].branch())]
376 else:
403 else:
377 return [r for r in subset if matcher(repo[r].branch())]
404 return [r for r in subset if matcher(repo[r].branch())]
378
405
379 s = getset(repo, range(len(repo)), x)
406 s = getset(repo, range(len(repo)), x)
380 b = set()
407 b = set()
381 for r in s:
408 for r in s:
382 b.add(repo[r].branch())
409 b.add(repo[r].branch())
383 s = set(s)
410 s = set(s)
384 return [r for r in subset if r in s or repo[r].branch() in b]
411 return [r for r in subset if r in s or repo[r].branch() in b]
385
412
386 def checkstatus(repo, subset, pat, field):
413 def checkstatus(repo, subset, pat, field):
387 m = None
414 m = None
388 s = []
415 s = []
389 hasset = matchmod.patkind(pat) == 'set'
416 hasset = matchmod.patkind(pat) == 'set'
390 fname = None
417 fname = None
391 for r in subset:
418 for r in subset:
392 c = repo[r]
419 c = repo[r]
393 if not m or hasset:
420 if not m or hasset:
394 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
421 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
395 if not m.anypats() and len(m.files()) == 1:
422 if not m.anypats() and len(m.files()) == 1:
396 fname = m.files()[0]
423 fname = m.files()[0]
397 if fname is not None:
424 if fname is not None:
398 if fname not in c.files():
425 if fname not in c.files():
399 continue
426 continue
400 else:
427 else:
401 for f in c.files():
428 for f in c.files():
402 if m(f):
429 if m(f):
403 break
430 break
404 else:
431 else:
405 continue
432 continue
406 files = repo.status(c.p1().node(), c.node())[field]
433 files = repo.status(c.p1().node(), c.node())[field]
407 if fname is not None:
434 if fname is not None:
408 if fname in files:
435 if fname in files:
409 s.append(r)
436 s.append(r)
410 else:
437 else:
411 for f in files:
438 for f in files:
412 if m(f):
439 if m(f):
413 s.append(r)
440 s.append(r)
414 break
441 break
415 return s
442 return s
416
443
417 def _children(repo, narrow, parentset):
444 def _children(repo, narrow, parentset):
418 cs = set()
445 cs = set()
419 pr = repo.changelog.parentrevs
446 pr = repo.changelog.parentrevs
420 for r in narrow:
447 for r in narrow:
421 for p in pr(r):
448 for p in pr(r):
422 if p in parentset:
449 if p in parentset:
423 cs.add(r)
450 cs.add(r)
424 return cs
451 return cs
425
452
426 def children(repo, subset, x):
453 def children(repo, subset, x):
427 """``children(set)``
454 """``children(set)``
428 Child changesets of changesets in set.
455 Child changesets of changesets in set.
429 """
456 """
430 s = set(getset(repo, range(len(repo)), x))
457 s = set(getset(repo, range(len(repo)), x))
431 cs = _children(repo, subset, s)
458 cs = _children(repo, subset, s)
432 return [r for r in subset if r in cs]
459 return [r for r in subset if r in cs]
433
460
434 def closed(repo, subset, x):
461 def closed(repo, subset, x):
435 """``closed()``
462 """``closed()``
436 Changeset is closed.
463 Changeset is closed.
437 """
464 """
438 # i18n: "closed" is a keyword
465 # i18n: "closed" is a keyword
439 getargs(x, 0, 0, _("closed takes no arguments"))
466 getargs(x, 0, 0, _("closed takes no arguments"))
440 return [r for r in subset if repo[r].closesbranch()]
467 return [r for r in subset if repo[r].closesbranch()]
441
468
442 def contains(repo, subset, x):
469 def contains(repo, subset, x):
443 """``contains(pattern)``
470 """``contains(pattern)``
444 Revision contains a file matching pattern. See :hg:`help patterns`
471 Revision contains a file matching pattern. See :hg:`help patterns`
445 for information about file patterns.
472 for information about file patterns.
446 """
473 """
447 # i18n: "contains" is a keyword
474 # i18n: "contains" is a keyword
448 pat = getstring(x, _("contains requires a pattern"))
475 pat = getstring(x, _("contains requires a pattern"))
449 m = None
476 m = None
450 s = []
477 s = []
451 if not matchmod.patkind(pat):
478 if not matchmod.patkind(pat):
452 for r in subset:
479 for r in subset:
453 if pat in repo[r]:
480 if pat in repo[r]:
454 s.append(r)
481 s.append(r)
455 else:
482 else:
456 for r in subset:
483 for r in subset:
457 c = repo[r]
484 c = repo[r]
458 if not m or matchmod.patkind(pat) == 'set':
485 if not m or matchmod.patkind(pat) == 'set':
459 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
486 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
460 for f in c.manifest():
487 for f in c.manifest():
461 if m(f):
488 if m(f):
462 s.append(r)
489 s.append(r)
463 break
490 break
464 return s
491 return s
465
492
466 def date(repo, subset, x):
493 def date(repo, subset, x):
467 """``date(interval)``
494 """``date(interval)``
468 Changesets within the interval, see :hg:`help dates`.
495 Changesets within the interval, see :hg:`help dates`.
469 """
496 """
470 # i18n: "date" is a keyword
497 # i18n: "date" is a keyword
471 ds = getstring(x, _("date requires a string"))
498 ds = getstring(x, _("date requires a string"))
472 dm = util.matchdate(ds)
499 dm = util.matchdate(ds)
473 return [r for r in subset if dm(repo[r].date()[0])]
500 return [r for r in subset if dm(repo[r].date()[0])]
474
501
475 def desc(repo, subset, x):
502 def desc(repo, subset, x):
476 """``desc(string)``
503 """``desc(string)``
477 Search commit message for string. The match is case-insensitive.
504 Search commit message for string. The match is case-insensitive.
478 """
505 """
479 # i18n: "desc" is a keyword
506 # i18n: "desc" is a keyword
480 ds = encoding.lower(getstring(x, _("desc requires a string")))
507 ds = encoding.lower(getstring(x, _("desc requires a string")))
481 l = []
508 l = []
482 for r in subset:
509 for r in subset:
483 c = repo[r]
510 c = repo[r]
484 if ds in encoding.lower(c.description()):
511 if ds in encoding.lower(c.description()):
485 l.append(r)
512 l.append(r)
486 return l
513 return l
487
514
488 def _descendants(repo, subset, x, followfirst=False):
515 def _descendants(repo, subset, x, followfirst=False):
489 args = getset(repo, range(len(repo)), x)
516 args = getset(repo, range(len(repo)), x)
490 if not args:
517 if not args:
491 return []
518 return []
492 s = set(_revdescendants(repo, args, followfirst)) | set(args)
519 s = set(_revdescendants(repo, args, followfirst)) | set(args)
493 return [r for r in subset if r in s]
520 return [r for r in subset if r in s]
494
521
495 def descendants(repo, subset, x):
522 def descendants(repo, subset, x):
496 """``descendants(set)``
523 """``descendants(set)``
497 Changesets which are descendants of changesets in set.
524 Changesets which are descendants of changesets in set.
498 """
525 """
499 return _descendants(repo, subset, x)
526 return _descendants(repo, subset, x)
500
527
501 def _firstdescendants(repo, subset, x):
528 def _firstdescendants(repo, subset, x):
502 # ``_firstdescendants(set)``
529 # ``_firstdescendants(set)``
503 # Like ``descendants(set)`` but follows only the first parents.
530 # Like ``descendants(set)`` but follows only the first parents.
504 return _descendants(repo, subset, x, followfirst=True)
531 return _descendants(repo, subset, x, followfirst=True)
505
532
506 def draft(repo, subset, x):
533 def draft(repo, subset, x):
507 """``draft()``
534 """``draft()``
508 Changeset in draft phase."""
535 Changeset in draft phase."""
509 getargs(x, 0, 0, _("draft takes no arguments"))
536 getargs(x, 0, 0, _("draft takes no arguments"))
510 pc = repo._phasecache
537 pc = repo._phasecache
511 return [r for r in subset if pc.phase(repo, r) == phases.draft]
538 return [r for r in subset if pc.phase(repo, r) == phases.draft]
512
539
513 def extra(repo, subset, x):
540 def extra(repo, subset, x):
514 """``extra(label, [value])``
541 """``extra(label, [value])``
515 Changesets with the given label in the extra metadata, with the given
542 Changesets with the given label in the extra metadata, with the given
516 optional value.
543 optional value.
517
544
518 If `value` starts with `re:`, the remainder of the value is treated as
545 If `value` starts with `re:`, the remainder of the value is treated as
519 a regular expression. To match a value that actually starts with `re:`,
546 a regular expression. To match a value that actually starts with `re:`,
520 use the prefix `literal:`.
547 use the prefix `literal:`.
521 """
548 """
522
549
523 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
550 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
524 label = getstring(l[0], _('first argument to extra must be a string'))
551 label = getstring(l[0], _('first argument to extra must be a string'))
525 value = None
552 value = None
526
553
527 if len(l) > 1:
554 if len(l) > 1:
528 value = getstring(l[1], _('second argument to extra must be a string'))
555 value = getstring(l[1], _('second argument to extra must be a string'))
529 kind, value, matcher = _stringmatcher(value)
556 kind, value, matcher = _stringmatcher(value)
530
557
531 def _matchvalue(r):
558 def _matchvalue(r):
532 extra = repo[r].extra()
559 extra = repo[r].extra()
533 return label in extra and (value is None or matcher(extra[label]))
560 return label in extra and (value is None or matcher(extra[label]))
534
561
535 return [r for r in subset if _matchvalue(r)]
562 return [r for r in subset if _matchvalue(r)]
536
563
537 def filelog(repo, subset, x):
564 def filelog(repo, subset, x):
538 """``filelog(pattern)``
565 """``filelog(pattern)``
539 Changesets connected to the specified filelog.
566 Changesets connected to the specified filelog.
540 """
567 """
541
568
542 pat = getstring(x, _("filelog requires a pattern"))
569 pat = getstring(x, _("filelog requires a pattern"))
543 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
570 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
544 ctx=repo[None])
571 ctx=repo[None])
545 s = set()
572 s = set()
546
573
547 if not matchmod.patkind(pat):
574 if not matchmod.patkind(pat):
548 for f in m.files():
575 for f in m.files():
549 fl = repo.file(f)
576 fl = repo.file(f)
550 for fr in fl:
577 for fr in fl:
551 s.add(fl.linkrev(fr))
578 s.add(fl.linkrev(fr))
552 else:
579 else:
553 for f in repo[None]:
580 for f in repo[None]:
554 if m(f):
581 if m(f):
555 fl = repo.file(f)
582 fl = repo.file(f)
556 for fr in fl:
583 for fr in fl:
557 s.add(fl.linkrev(fr))
584 s.add(fl.linkrev(fr))
558
585
559 return [r for r in subset if r in s]
586 return [r for r in subset if r in s]
560
587
561 def first(repo, subset, x):
588 def first(repo, subset, x):
562 """``first(set, [n])``
589 """``first(set, [n])``
563 An alias for limit().
590 An alias for limit().
564 """
591 """
565 return limit(repo, subset, x)
592 return limit(repo, subset, x)
566
593
567 def _follow(repo, subset, x, name, followfirst=False):
594 def _follow(repo, subset, x, name, followfirst=False):
568 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
595 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
569 c = repo['.']
596 c = repo['.']
570 if l:
597 if l:
571 x = getstring(l[0], _("%s expected a filename") % name)
598 x = getstring(l[0], _("%s expected a filename") % name)
572 if x in c:
599 if x in c:
573 cx = c[x]
600 cx = c[x]
574 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
601 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
575 # include the revision responsible for the most recent version
602 # include the revision responsible for the most recent version
576 s.add(cx.linkrev())
603 s.add(cx.linkrev())
577 else:
604 else:
578 return []
605 return []
579 else:
606 else:
580 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
607 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
581
608
582 return [r for r in subset if r in s]
609 return [r for r in subset if r in s]
583
610
584 def follow(repo, subset, x):
611 def follow(repo, subset, x):
585 """``follow([file])``
612 """``follow([file])``
586 An alias for ``::.`` (ancestors of the working copy's first parent).
613 An alias for ``::.`` (ancestors of the working copy's first parent).
587 If a filename is specified, the history of the given file is followed,
614 If a filename is specified, the history of the given file is followed,
588 including copies.
615 including copies.
589 """
616 """
590 return _follow(repo, subset, x, 'follow')
617 return _follow(repo, subset, x, 'follow')
591
618
592 def _followfirst(repo, subset, x):
619 def _followfirst(repo, subset, x):
593 # ``followfirst([file])``
620 # ``followfirst([file])``
594 # Like ``follow([file])`` but follows only the first parent of
621 # Like ``follow([file])`` but follows only the first parent of
595 # every revision or file revision.
622 # every revision or file revision.
596 return _follow(repo, subset, x, '_followfirst', followfirst=True)
623 return _follow(repo, subset, x, '_followfirst', followfirst=True)
597
624
598 def getall(repo, subset, x):
625 def getall(repo, subset, x):
599 """``all()``
626 """``all()``
600 All changesets, the same as ``0:tip``.
627 All changesets, the same as ``0:tip``.
601 """
628 """
602 # i18n: "all" is a keyword
629 # i18n: "all" is a keyword
603 getargs(x, 0, 0, _("all takes no arguments"))
630 getargs(x, 0, 0, _("all takes no arguments"))
604 return subset
631 return subset
605
632
606 def grep(repo, subset, x):
633 def grep(repo, subset, x):
607 """``grep(regex)``
634 """``grep(regex)``
608 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
635 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
609 to ensure special escape characters are handled correctly. Unlike
636 to ensure special escape characters are handled correctly. Unlike
610 ``keyword(string)``, the match is case-sensitive.
637 ``keyword(string)``, the match is case-sensitive.
611 """
638 """
612 try:
639 try:
613 # i18n: "grep" is a keyword
640 # i18n: "grep" is a keyword
614 gr = re.compile(getstring(x, _("grep requires a string")))
641 gr = re.compile(getstring(x, _("grep requires a string")))
615 except re.error, e:
642 except re.error, e:
616 raise error.ParseError(_('invalid match pattern: %s') % e)
643 raise error.ParseError(_('invalid match pattern: %s') % e)
617 l = []
644 l = []
618 for r in subset:
645 for r in subset:
619 c = repo[r]
646 c = repo[r]
620 for e in c.files() + [c.user(), c.description()]:
647 for e in c.files() + [c.user(), c.description()]:
621 if gr.search(e):
648 if gr.search(e):
622 l.append(r)
649 l.append(r)
623 break
650 break
624 return l
651 return l
625
652
626 def _matchfiles(repo, subset, x):
653 def _matchfiles(repo, subset, x):
627 # _matchfiles takes a revset list of prefixed arguments:
654 # _matchfiles takes a revset list of prefixed arguments:
628 #
655 #
629 # [p:foo, i:bar, x:baz]
656 # [p:foo, i:bar, x:baz]
630 #
657 #
631 # builds a match object from them and filters subset. Allowed
658 # builds a match object from them and filters subset. Allowed
632 # prefixes are 'p:' for regular patterns, 'i:' for include
659 # prefixes are 'p:' for regular patterns, 'i:' for include
633 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
660 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
634 # a revision identifier, or the empty string to reference the
661 # a revision identifier, or the empty string to reference the
635 # working directory, from which the match object is
662 # working directory, from which the match object is
636 # initialized. Use 'd:' to set the default matching mode, default
663 # initialized. Use 'd:' to set the default matching mode, default
637 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
664 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
638
665
639 # i18n: "_matchfiles" is a keyword
666 # i18n: "_matchfiles" is a keyword
640 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
667 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
641 pats, inc, exc = [], [], []
668 pats, inc, exc = [], [], []
642 hasset = False
669 hasset = False
643 rev, default = None, None
670 rev, default = None, None
644 for arg in l:
671 for arg in l:
645 s = getstring(arg, _("_matchfiles requires string arguments"))
672 s = getstring(arg, _("_matchfiles requires string arguments"))
646 prefix, value = s[:2], s[2:]
673 prefix, value = s[:2], s[2:]
647 if prefix == 'p:':
674 if prefix == 'p:':
648 pats.append(value)
675 pats.append(value)
649 elif prefix == 'i:':
676 elif prefix == 'i:':
650 inc.append(value)
677 inc.append(value)
651 elif prefix == 'x:':
678 elif prefix == 'x:':
652 exc.append(value)
679 exc.append(value)
653 elif prefix == 'r:':
680 elif prefix == 'r:':
654 if rev is not None:
681 if rev is not None:
655 raise error.ParseError(_('_matchfiles expected at most one '
682 raise error.ParseError(_('_matchfiles expected at most one '
656 'revision'))
683 'revision'))
657 rev = value
684 rev = value
658 elif prefix == 'd:':
685 elif prefix == 'd:':
659 if default is not None:
686 if default is not None:
660 raise error.ParseError(_('_matchfiles expected at most one '
687 raise error.ParseError(_('_matchfiles expected at most one '
661 'default mode'))
688 'default mode'))
662 default = value
689 default = value
663 else:
690 else:
664 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
691 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
665 if not hasset and matchmod.patkind(value) == 'set':
692 if not hasset and matchmod.patkind(value) == 'set':
666 hasset = True
693 hasset = True
667 if not default:
694 if not default:
668 default = 'glob'
695 default = 'glob'
669 m = None
696 m = None
670 s = []
697 s = []
671 for r in subset:
698 for r in subset:
672 c = repo[r]
699 c = repo[r]
673 if not m or (hasset and rev is None):
700 if not m or (hasset and rev is None):
674 ctx = c
701 ctx = c
675 if rev is not None:
702 if rev is not None:
676 ctx = repo[rev or None]
703 ctx = repo[rev or None]
677 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
704 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
678 exclude=exc, ctx=ctx, default=default)
705 exclude=exc, ctx=ctx, default=default)
679 for f in c.files():
706 for f in c.files():
680 if m(f):
707 if m(f):
681 s.append(r)
708 s.append(r)
682 break
709 break
683 return s
710 return s
684
711
685 def hasfile(repo, subset, x):
712 def hasfile(repo, subset, x):
686 """``file(pattern)``
713 """``file(pattern)``
687 Changesets affecting files matched by pattern.
714 Changesets affecting files matched by pattern.
688 """
715 """
689 # i18n: "file" is a keyword
716 # i18n: "file" is a keyword
690 pat = getstring(x, _("file requires a pattern"))
717 pat = getstring(x, _("file requires a pattern"))
691 return _matchfiles(repo, subset, ('string', 'p:' + pat))
718 return _matchfiles(repo, subset, ('string', 'p:' + pat))
692
719
693 def head(repo, subset, x):
720 def head(repo, subset, x):
694 """``head()``
721 """``head()``
695 Changeset is a named branch head.
722 Changeset is a named branch head.
696 """
723 """
697 # i18n: "head" is a keyword
724 # i18n: "head" is a keyword
698 getargs(x, 0, 0, _("head takes no arguments"))
725 getargs(x, 0, 0, _("head takes no arguments"))
699 hs = set()
726 hs = set()
700 for b, ls in repo.branchmap().iteritems():
727 for b, ls in repo.branchmap().iteritems():
701 hs.update(repo[h].rev() for h in ls)
728 hs.update(repo[h].rev() for h in ls)
702 return [r for r in subset if r in hs]
729 return [r for r in subset if r in hs]
703
730
704 def heads(repo, subset, x):
731 def heads(repo, subset, x):
705 """``heads(set)``
732 """``heads(set)``
706 Members of set with no children in set.
733 Members of set with no children in set.
707 """
734 """
708 s = getset(repo, subset, x)
735 s = getset(repo, subset, x)
709 ps = set(parents(repo, subset, x))
736 ps = set(parents(repo, subset, x))
710 return [r for r in s if r not in ps]
737 return [r for r in s if r not in ps]
711
738
712 def keyword(repo, subset, x):
739 def keyword(repo, subset, x):
713 """``keyword(string)``
740 """``keyword(string)``
714 Search commit message, user name, and names of changed files for
741 Search commit message, user name, and names of changed files for
715 string. The match is case-insensitive.
742 string. The match is case-insensitive.
716 """
743 """
717 # i18n: "keyword" is a keyword
744 # i18n: "keyword" is a keyword
718 kw = encoding.lower(getstring(x, _("keyword requires a string")))
745 kw = encoding.lower(getstring(x, _("keyword requires a string")))
719 l = []
746 l = []
720 for r in subset:
747 for r in subset:
721 c = repo[r]
748 c = repo[r]
722 t = " ".join(c.files() + [c.user(), c.description()])
749 t = " ".join(c.files() + [c.user(), c.description()])
723 if kw in encoding.lower(t):
750 if kw in encoding.lower(t):
724 l.append(r)
751 l.append(r)
725 return l
752 return l
726
753
727 def limit(repo, subset, x):
754 def limit(repo, subset, x):
728 """``limit(set, [n])``
755 """``limit(set, [n])``
729 First n members of set, defaulting to 1.
756 First n members of set, defaulting to 1.
730 """
757 """
731 # i18n: "limit" is a keyword
758 # i18n: "limit" is a keyword
732 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
759 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
733 try:
760 try:
734 lim = 1
761 lim = 1
735 if len(l) == 2:
762 if len(l) == 2:
736 # i18n: "limit" is a keyword
763 # i18n: "limit" is a keyword
737 lim = int(getstring(l[1], _("limit requires a number")))
764 lim = int(getstring(l[1], _("limit requires a number")))
738 except (TypeError, ValueError):
765 except (TypeError, ValueError):
739 # i18n: "limit" is a keyword
766 # i18n: "limit" is a keyword
740 raise error.ParseError(_("limit expects a number"))
767 raise error.ParseError(_("limit expects a number"))
741 ss = set(subset)
768 ss = set(subset)
742 os = getset(repo, range(len(repo)), l[0])[:lim]
769 os = getset(repo, range(len(repo)), l[0])[:lim]
743 return [r for r in os if r in ss]
770 return [r for r in os if r in ss]
744
771
745 def last(repo, subset, x):
772 def last(repo, subset, x):
746 """``last(set, [n])``
773 """``last(set, [n])``
747 Last n members of set, defaulting to 1.
774 Last n members of set, defaulting to 1.
748 """
775 """
749 # i18n: "last" is a keyword
776 # i18n: "last" is a keyword
750 l = getargs(x, 1, 2, _("last requires one or two arguments"))
777 l = getargs(x, 1, 2, _("last requires one or two arguments"))
751 try:
778 try:
752 lim = 1
779 lim = 1
753 if len(l) == 2:
780 if len(l) == 2:
754 # i18n: "last" is a keyword
781 # i18n: "last" is a keyword
755 lim = int(getstring(l[1], _("last requires a number")))
782 lim = int(getstring(l[1], _("last requires a number")))
756 except (TypeError, ValueError):
783 except (TypeError, ValueError):
757 # i18n: "last" is a keyword
784 # i18n: "last" is a keyword
758 raise error.ParseError(_("last expects a number"))
785 raise error.ParseError(_("last expects a number"))
759 ss = set(subset)
786 ss = set(subset)
760 os = getset(repo, range(len(repo)), l[0])[-lim:]
787 os = getset(repo, range(len(repo)), l[0])[-lim:]
761 return [r for r in os if r in ss]
788 return [r for r in os if r in ss]
762
789
763 def maxrev(repo, subset, x):
790 def maxrev(repo, subset, x):
764 """``max(set)``
791 """``max(set)``
765 Changeset with highest revision number in set.
792 Changeset with highest revision number in set.
766 """
793 """
767 os = getset(repo, range(len(repo)), x)
794 os = getset(repo, range(len(repo)), x)
768 if os:
795 if os:
769 m = max(os)
796 m = max(os)
770 if m in subset:
797 if m in subset:
771 return [m]
798 return [m]
772 return []
799 return []
773
800
774 def merge(repo, subset, x):
801 def merge(repo, subset, x):
775 """``merge()``
802 """``merge()``
776 Changeset is a merge changeset.
803 Changeset is a merge changeset.
777 """
804 """
778 # i18n: "merge" is a keyword
805 # i18n: "merge" is a keyword
779 getargs(x, 0, 0, _("merge takes no arguments"))
806 getargs(x, 0, 0, _("merge takes no arguments"))
780 cl = repo.changelog
807 cl = repo.changelog
781 return [r for r in subset if cl.parentrevs(r)[1] != -1]
808 return [r for r in subset if cl.parentrevs(r)[1] != -1]
782
809
783 def minrev(repo, subset, x):
810 def minrev(repo, subset, x):
784 """``min(set)``
811 """``min(set)``
785 Changeset with lowest revision number in set.
812 Changeset with lowest revision number in set.
786 """
813 """
787 os = getset(repo, range(len(repo)), x)
814 os = getset(repo, range(len(repo)), x)
788 if os:
815 if os:
789 m = min(os)
816 m = min(os)
790 if m in subset:
817 if m in subset:
791 return [m]
818 return [m]
792 return []
819 return []
793
820
794 def modifies(repo, subset, x):
821 def modifies(repo, subset, x):
795 """``modifies(pattern)``
822 """``modifies(pattern)``
796 Changesets modifying files matched by pattern.
823 Changesets modifying files matched by pattern.
797 """
824 """
798 # i18n: "modifies" is a keyword
825 # i18n: "modifies" is a keyword
799 pat = getstring(x, _("modifies requires a pattern"))
826 pat = getstring(x, _("modifies requires a pattern"))
800 return checkstatus(repo, subset, pat, 0)
827 return checkstatus(repo, subset, pat, 0)
801
828
802 def node_(repo, subset, x):
829 def node_(repo, subset, x):
803 """``id(string)``
830 """``id(string)``
804 Revision non-ambiguously specified by the given hex string prefix.
831 Revision non-ambiguously specified by the given hex string prefix.
805 """
832 """
806 # i18n: "id" is a keyword
833 # i18n: "id" is a keyword
807 l = getargs(x, 1, 1, _("id requires one argument"))
834 l = getargs(x, 1, 1, _("id requires one argument"))
808 # i18n: "id" is a keyword
835 # i18n: "id" is a keyword
809 n = getstring(l[0], _("id requires a string"))
836 n = getstring(l[0], _("id requires a string"))
810 if len(n) == 40:
837 if len(n) == 40:
811 rn = repo[n].rev()
838 rn = repo[n].rev()
812 else:
839 else:
813 rn = None
840 rn = None
814 pm = repo.changelog._partialmatch(n)
841 pm = repo.changelog._partialmatch(n)
815 if pm is not None:
842 if pm is not None:
816 rn = repo.changelog.rev(pm)
843 rn = repo.changelog.rev(pm)
817
844
818 return [r for r in subset if r == rn]
845 return [r for r in subset if r == rn]
819
846
820 def outgoing(repo, subset, x):
847 def outgoing(repo, subset, x):
821 """``outgoing([path])``
848 """``outgoing([path])``
822 Changesets not found in the specified destination repository, or the
849 Changesets not found in the specified destination repository, or the
823 default push location.
850 default push location.
824 """
851 """
825 import hg # avoid start-up nasties
852 import hg # avoid start-up nasties
826 # i18n: "outgoing" is a keyword
853 # i18n: "outgoing" is a keyword
827 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
854 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
828 # i18n: "outgoing" is a keyword
855 # i18n: "outgoing" is a keyword
829 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
856 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
830 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
857 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
831 dest, branches = hg.parseurl(dest)
858 dest, branches = hg.parseurl(dest)
832 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
859 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
833 if revs:
860 if revs:
834 revs = [repo.lookup(rev) for rev in revs]
861 revs = [repo.lookup(rev) for rev in revs]
835 other = hg.peer(repo, {}, dest)
862 other = hg.peer(repo, {}, dest)
836 repo.ui.pushbuffer()
863 repo.ui.pushbuffer()
837 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
864 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
838 repo.ui.popbuffer()
865 repo.ui.popbuffer()
839 cl = repo.changelog
866 cl = repo.changelog
840 o = set([cl.rev(r) for r in outgoing.missing])
867 o = set([cl.rev(r) for r in outgoing.missing])
841 return [r for r in subset if r in o]
868 return [r for r in subset if r in o]
842
869
843 def p1(repo, subset, x):
870 def p1(repo, subset, x):
844 """``p1([set])``
871 """``p1([set])``
845 First parent of changesets in set, or the working directory.
872 First parent of changesets in set, or the working directory.
846 """
873 """
847 if x is None:
874 if x is None:
848 p = repo[x].p1().rev()
875 p = repo[x].p1().rev()
849 return [r for r in subset if r == p]
876 return [r for r in subset if r == p]
850
877
851 ps = set()
878 ps = set()
852 cl = repo.changelog
879 cl = repo.changelog
853 for r in getset(repo, range(len(repo)), x):
880 for r in getset(repo, range(len(repo)), x):
854 ps.add(cl.parentrevs(r)[0])
881 ps.add(cl.parentrevs(r)[0])
855 return [r for r in subset if r in ps]
882 return [r for r in subset if r in ps]
856
883
857 def p2(repo, subset, x):
884 def p2(repo, subset, x):
858 """``p2([set])``
885 """``p2([set])``
859 Second parent of changesets in set, or the working directory.
886 Second parent of changesets in set, or the working directory.
860 """
887 """
861 if x is None:
888 if x is None:
862 ps = repo[x].parents()
889 ps = repo[x].parents()
863 try:
890 try:
864 p = ps[1].rev()
891 p = ps[1].rev()
865 return [r for r in subset if r == p]
892 return [r for r in subset if r == p]
866 except IndexError:
893 except IndexError:
867 return []
894 return []
868
895
869 ps = set()
896 ps = set()
870 cl = repo.changelog
897 cl = repo.changelog
871 for r in getset(repo, range(len(repo)), x):
898 for r in getset(repo, range(len(repo)), x):
872 ps.add(cl.parentrevs(r)[1])
899 ps.add(cl.parentrevs(r)[1])
873 return [r for r in subset if r in ps]
900 return [r for r in subset if r in ps]
874
901
875 def parents(repo, subset, x):
902 def parents(repo, subset, x):
876 """``parents([set])``
903 """``parents([set])``
877 The set of all parents for all changesets in set, or the working directory.
904 The set of all parents for all changesets in set, or the working directory.
878 """
905 """
879 if x is None:
906 if x is None:
880 ps = tuple(p.rev() for p in repo[x].parents())
907 ps = tuple(p.rev() for p in repo[x].parents())
881 return [r for r in subset if r in ps]
908 return [r for r in subset if r in ps]
882
909
883 ps = set()
910 ps = set()
884 cl = repo.changelog
911 cl = repo.changelog
885 for r in getset(repo, range(len(repo)), x):
912 for r in getset(repo, range(len(repo)), x):
886 ps.update(cl.parentrevs(r))
913 ps.update(cl.parentrevs(r))
887 return [r for r in subset if r in ps]
914 return [r for r in subset if r in ps]
888
915
889 def parentspec(repo, subset, x, n):
916 def parentspec(repo, subset, x, n):
890 """``set^0``
917 """``set^0``
891 The set.
918 The set.
892 ``set^1`` (or ``set^``), ``set^2``
919 ``set^1`` (or ``set^``), ``set^2``
893 First or second parent, respectively, of all changesets in set.
920 First or second parent, respectively, of all changesets in set.
894 """
921 """
895 try:
922 try:
896 n = int(n[1])
923 n = int(n[1])
897 if n not in (0, 1, 2):
924 if n not in (0, 1, 2):
898 raise ValueError
925 raise ValueError
899 except (TypeError, ValueError):
926 except (TypeError, ValueError):
900 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
927 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
901 ps = set()
928 ps = set()
902 cl = repo.changelog
929 cl = repo.changelog
903 for r in getset(repo, subset, x):
930 for r in getset(repo, subset, x):
904 if n == 0:
931 if n == 0:
905 ps.add(r)
932 ps.add(r)
906 elif n == 1:
933 elif n == 1:
907 ps.add(cl.parentrevs(r)[0])
934 ps.add(cl.parentrevs(r)[0])
908 elif n == 2:
935 elif n == 2:
909 parents = cl.parentrevs(r)
936 parents = cl.parentrevs(r)
910 if len(parents) > 1:
937 if len(parents) > 1:
911 ps.add(parents[1])
938 ps.add(parents[1])
912 return [r for r in subset if r in ps]
939 return [r for r in subset if r in ps]
913
940
914 def present(repo, subset, x):
941 def present(repo, subset, x):
915 """``present(set)``
942 """``present(set)``
916 An empty set, if any revision in set isn't found; otherwise,
943 An empty set, if any revision in set isn't found; otherwise,
917 all revisions in set.
944 all revisions in set.
918
945
919 If any of specified revisions is not present in the local repository,
946 If any of specified revisions is not present in the local repository,
920 the query is normally aborted. But this predicate allows the query
947 the query is normally aborted. But this predicate allows the query
921 to continue even in such cases.
948 to continue even in such cases.
922 """
949 """
923 try:
950 try:
924 return getset(repo, subset, x)
951 return getset(repo, subset, x)
925 except error.RepoLookupError:
952 except error.RepoLookupError:
926 return []
953 return []
927
954
928 def public(repo, subset, x):
955 def public(repo, subset, x):
929 """``public()``
956 """``public()``
930 Changeset in public phase."""
957 Changeset in public phase."""
931 getargs(x, 0, 0, _("public takes no arguments"))
958 getargs(x, 0, 0, _("public takes no arguments"))
932 pc = repo._phasecache
959 pc = repo._phasecache
933 return [r for r in subset if pc.phase(repo, r) == phases.public]
960 return [r for r in subset if pc.phase(repo, r) == phases.public]
934
961
935 def remote(repo, subset, x):
962 def remote(repo, subset, x):
936 """``remote([id [,path]])``
963 """``remote([id [,path]])``
937 Local revision that corresponds to the given identifier in a
964 Local revision that corresponds to the given identifier in a
938 remote repository, if present. Here, the '.' identifier is a
965 remote repository, if present. Here, the '.' identifier is a
939 synonym for the current local branch.
966 synonym for the current local branch.
940 """
967 """
941
968
942 import hg # avoid start-up nasties
969 import hg # avoid start-up nasties
943 # i18n: "remote" is a keyword
970 # i18n: "remote" is a keyword
944 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
971 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
945
972
946 q = '.'
973 q = '.'
947 if len(l) > 0:
974 if len(l) > 0:
948 # i18n: "remote" is a keyword
975 # i18n: "remote" is a keyword
949 q = getstring(l[0], _("remote requires a string id"))
976 q = getstring(l[0], _("remote requires a string id"))
950 if q == '.':
977 if q == '.':
951 q = repo['.'].branch()
978 q = repo['.'].branch()
952
979
953 dest = ''
980 dest = ''
954 if len(l) > 1:
981 if len(l) > 1:
955 # i18n: "remote" is a keyword
982 # i18n: "remote" is a keyword
956 dest = getstring(l[1], _("remote requires a repository path"))
983 dest = getstring(l[1], _("remote requires a repository path"))
957 dest = repo.ui.expandpath(dest or 'default')
984 dest = repo.ui.expandpath(dest or 'default')
958 dest, branches = hg.parseurl(dest)
985 dest, branches = hg.parseurl(dest)
959 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
986 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
960 if revs:
987 if revs:
961 revs = [repo.lookup(rev) for rev in revs]
988 revs = [repo.lookup(rev) for rev in revs]
962 other = hg.peer(repo, {}, dest)
989 other = hg.peer(repo, {}, dest)
963 n = other.lookup(q)
990 n = other.lookup(q)
964 if n in repo:
991 if n in repo:
965 r = repo[n].rev()
992 r = repo[n].rev()
966 if r in subset:
993 if r in subset:
967 return [r]
994 return [r]
968 return []
995 return []
969
996
970 def removes(repo, subset, x):
997 def removes(repo, subset, x):
971 """``removes(pattern)``
998 """``removes(pattern)``
972 Changesets which remove files matching pattern.
999 Changesets which remove files matching pattern.
973 """
1000 """
974 # i18n: "removes" is a keyword
1001 # i18n: "removes" is a keyword
975 pat = getstring(x, _("removes requires a pattern"))
1002 pat = getstring(x, _("removes requires a pattern"))
976 return checkstatus(repo, subset, pat, 2)
1003 return checkstatus(repo, subset, pat, 2)
977
1004
978 def rev(repo, subset, x):
1005 def rev(repo, subset, x):
979 """``rev(number)``
1006 """``rev(number)``
980 Revision with the given numeric identifier.
1007 Revision with the given numeric identifier.
981 """
1008 """
982 # i18n: "rev" is a keyword
1009 # i18n: "rev" is a keyword
983 l = getargs(x, 1, 1, _("rev requires one argument"))
1010 l = getargs(x, 1, 1, _("rev requires one argument"))
984 try:
1011 try:
985 # i18n: "rev" is a keyword
1012 # i18n: "rev" is a keyword
986 l = int(getstring(l[0], _("rev requires a number")))
1013 l = int(getstring(l[0], _("rev requires a number")))
987 except (TypeError, ValueError):
1014 except (TypeError, ValueError):
988 # i18n: "rev" is a keyword
1015 # i18n: "rev" is a keyword
989 raise error.ParseError(_("rev expects a number"))
1016 raise error.ParseError(_("rev expects a number"))
990 return [r for r in subset if r == l]
1017 return [r for r in subset if r == l]
991
1018
992 def matching(repo, subset, x):
1019 def matching(repo, subset, x):
993 """``matching(revision [, field])``
1020 """``matching(revision [, field])``
994 Changesets in which a given set of fields match the set of fields in the
1021 Changesets in which a given set of fields match the set of fields in the
995 selected revision or set.
1022 selected revision or set.
996
1023
997 To match more than one field pass the list of fields to match separated
1024 To match more than one field pass the list of fields to match separated
998 by spaces (e.g. ``author description``).
1025 by spaces (e.g. ``author description``).
999
1026
1000 Valid fields are most regular revision fields and some special fields.
1027 Valid fields are most regular revision fields and some special fields.
1001
1028
1002 Regular revision fields are ``description``, ``author``, ``branch``,
1029 Regular revision fields are ``description``, ``author``, ``branch``,
1003 ``date``, ``files``, ``phase``, ``parents``, ``substate`` and ``user``.
1030 ``date``, ``files``, ``phase``, ``parents``, ``substate`` and ``user``.
1004 Note that ``author`` and ``user`` are synonyms.
1031 Note that ``author`` and ``user`` are synonyms.
1005
1032
1006 Special fields are ``summary`` and ``metadata``:
1033 Special fields are ``summary`` and ``metadata``:
1007 ``summary`` matches the first line of the description.
1034 ``summary`` matches the first line of the description.
1008 ``metadata`` is equivalent to matching ``description user date``
1035 ``metadata`` is equivalent to matching ``description user date``
1009 (i.e. it matches the main metadata fields).
1036 (i.e. it matches the main metadata fields).
1010
1037
1011 ``metadata`` is the default field which is used when no fields are
1038 ``metadata`` is the default field which is used when no fields are
1012 specified. You can match more than one field at a time.
1039 specified. You can match more than one field at a time.
1013 """
1040 """
1014 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1041 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1015
1042
1016 revs = getset(repo, xrange(len(repo)), l[0])
1043 revs = getset(repo, xrange(len(repo)), l[0])
1017
1044
1018 fieldlist = ['metadata']
1045 fieldlist = ['metadata']
1019 if len(l) > 1:
1046 if len(l) > 1:
1020 fieldlist = getstring(l[1],
1047 fieldlist = getstring(l[1],
1021 _("matching requires a string "
1048 _("matching requires a string "
1022 "as its second argument")).split()
1049 "as its second argument")).split()
1023
1050
1024 # Make sure that there are no repeated fields, and expand the
1051 # Make sure that there are no repeated fields, and expand the
1025 # 'special' 'metadata' field type
1052 # 'special' 'metadata' field type
1026 fields = []
1053 fields = []
1027 for field in fieldlist:
1054 for field in fieldlist:
1028 if field == 'metadata':
1055 if field == 'metadata':
1029 fields += ['user', 'description', 'date']
1056 fields += ['user', 'description', 'date']
1030 else:
1057 else:
1031 if field == 'author':
1058 if field == 'author':
1032 field = 'user'
1059 field = 'user'
1033 fields.append(field)
1060 fields.append(field)
1034 fields = set(fields)
1061 fields = set(fields)
1035 if 'summary' in fields and 'description' in fields:
1062 if 'summary' in fields and 'description' in fields:
1036 # If a revision matches its description it also matches its summary
1063 # If a revision matches its description it also matches its summary
1037 fields.discard('summary')
1064 fields.discard('summary')
1038
1065
1039 # We may want to match more than one field
1066 # We may want to match more than one field
1040 # Not all fields take the same amount of time to be matched
1067 # Not all fields take the same amount of time to be matched
1041 # Sort the selected fields in order of increasing matching cost
1068 # Sort the selected fields in order of increasing matching cost
1042 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1069 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1043 'files', 'description', 'substate']
1070 'files', 'description', 'substate']
1044 def fieldkeyfunc(f):
1071 def fieldkeyfunc(f):
1045 try:
1072 try:
1046 return fieldorder.index(f)
1073 return fieldorder.index(f)
1047 except ValueError:
1074 except ValueError:
1048 # assume an unknown field is very costly
1075 # assume an unknown field is very costly
1049 return len(fieldorder)
1076 return len(fieldorder)
1050 fields = list(fields)
1077 fields = list(fields)
1051 fields.sort(key=fieldkeyfunc)
1078 fields.sort(key=fieldkeyfunc)
1052
1079
1053 # Each field will be matched with its own "getfield" function
1080 # Each field will be matched with its own "getfield" function
1054 # which will be added to the getfieldfuncs array of functions
1081 # which will be added to the getfieldfuncs array of functions
1055 getfieldfuncs = []
1082 getfieldfuncs = []
1056 _funcs = {
1083 _funcs = {
1057 'user': lambda r: repo[r].user(),
1084 'user': lambda r: repo[r].user(),
1058 'branch': lambda r: repo[r].branch(),
1085 'branch': lambda r: repo[r].branch(),
1059 'date': lambda r: repo[r].date(),
1086 'date': lambda r: repo[r].date(),
1060 'description': lambda r: repo[r].description(),
1087 'description': lambda r: repo[r].description(),
1061 'files': lambda r: repo[r].files(),
1088 'files': lambda r: repo[r].files(),
1062 'parents': lambda r: repo[r].parents(),
1089 'parents': lambda r: repo[r].parents(),
1063 'phase': lambda r: repo[r].phase(),
1090 'phase': lambda r: repo[r].phase(),
1064 'substate': lambda r: repo[r].substate,
1091 'substate': lambda r: repo[r].substate,
1065 'summary': lambda r: repo[r].description().splitlines()[0],
1092 'summary': lambda r: repo[r].description().splitlines()[0],
1066 }
1093 }
1067 for info in fields:
1094 for info in fields:
1068 getfield = _funcs.get(info, None)
1095 getfield = _funcs.get(info, None)
1069 if getfield is None:
1096 if getfield is None:
1070 raise error.ParseError(
1097 raise error.ParseError(
1071 _("unexpected field name passed to matching: %s") % info)
1098 _("unexpected field name passed to matching: %s") % info)
1072 getfieldfuncs.append(getfield)
1099 getfieldfuncs.append(getfield)
1073 # convert the getfield array of functions into a "getinfo" function
1100 # convert the getfield array of functions into a "getinfo" function
1074 # which returns an array of field values (or a single value if there
1101 # which returns an array of field values (or a single value if there
1075 # is only one field to match)
1102 # is only one field to match)
1076 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1103 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1077
1104
1078 matches = set()
1105 matches = set()
1079 for rev in revs:
1106 for rev in revs:
1080 target = getinfo(rev)
1107 target = getinfo(rev)
1081 for r in subset:
1108 for r in subset:
1082 match = True
1109 match = True
1083 for n, f in enumerate(getfieldfuncs):
1110 for n, f in enumerate(getfieldfuncs):
1084 if target[n] != f(r):
1111 if target[n] != f(r):
1085 match = False
1112 match = False
1086 break
1113 break
1087 if match:
1114 if match:
1088 matches.add(r)
1115 matches.add(r)
1089 return [r for r in subset if r in matches]
1116 return [r for r in subset if r in matches]
1090
1117
1091 def reverse(repo, subset, x):
1118 def reverse(repo, subset, x):
1092 """``reverse(set)``
1119 """``reverse(set)``
1093 Reverse order of set.
1120 Reverse order of set.
1094 """
1121 """
1095 l = getset(repo, subset, x)
1122 l = getset(repo, subset, x)
1096 l.reverse()
1123 l.reverse()
1097 return l
1124 return l
1098
1125
1099 def roots(repo, subset, x):
1126 def roots(repo, subset, x):
1100 """``roots(set)``
1127 """``roots(set)``
1101 Changesets in set with no parent changeset in set.
1128 Changesets in set with no parent changeset in set.
1102 """
1129 """
1103 s = set(getset(repo, xrange(len(repo)), x))
1130 s = set(getset(repo, xrange(len(repo)), x))
1104 subset = [r for r in subset if r in s]
1131 subset = [r for r in subset if r in s]
1105 cs = _children(repo, subset, s)
1132 cs = _children(repo, subset, s)
1106 return [r for r in subset if r not in cs]
1133 return [r for r in subset if r not in cs]
1107
1134
1108 def secret(repo, subset, x):
1135 def secret(repo, subset, x):
1109 """``secret()``
1136 """``secret()``
1110 Changeset in secret phase."""
1137 Changeset in secret phase."""
1111 getargs(x, 0, 0, _("secret takes no arguments"))
1138 getargs(x, 0, 0, _("secret takes no arguments"))
1112 pc = repo._phasecache
1139 pc = repo._phasecache
1113 return [r for r in subset if pc.phase(repo, r) == phases.secret]
1140 return [r for r in subset if pc.phase(repo, r) == phases.secret]
1114
1141
1115 def sort(repo, subset, x):
1142 def sort(repo, subset, x):
1116 """``sort(set[, [-]key...])``
1143 """``sort(set[, [-]key...])``
1117 Sort set by keys. The default sort order is ascending, specify a key
1144 Sort set by keys. The default sort order is ascending, specify a key
1118 as ``-key`` to sort in descending order.
1145 as ``-key`` to sort in descending order.
1119
1146
1120 The keys can be:
1147 The keys can be:
1121
1148
1122 - ``rev`` for the revision number,
1149 - ``rev`` for the revision number,
1123 - ``branch`` for the branch name,
1150 - ``branch`` for the branch name,
1124 - ``desc`` for the commit message (description),
1151 - ``desc`` for the commit message (description),
1125 - ``user`` for user name (``author`` can be used as an alias),
1152 - ``user`` for user name (``author`` can be used as an alias),
1126 - ``date`` for the commit date
1153 - ``date`` for the commit date
1127 """
1154 """
1128 # i18n: "sort" is a keyword
1155 # i18n: "sort" is a keyword
1129 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1156 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1130 keys = "rev"
1157 keys = "rev"
1131 if len(l) == 2:
1158 if len(l) == 2:
1132 keys = getstring(l[1], _("sort spec must be a string"))
1159 keys = getstring(l[1], _("sort spec must be a string"))
1133
1160
1134 s = l[0]
1161 s = l[0]
1135 keys = keys.split()
1162 keys = keys.split()
1136 l = []
1163 l = []
1137 def invert(s):
1164 def invert(s):
1138 return "".join(chr(255 - ord(c)) for c in s)
1165 return "".join(chr(255 - ord(c)) for c in s)
1139 for r in getset(repo, subset, s):
1166 for r in getset(repo, subset, s):
1140 c = repo[r]
1167 c = repo[r]
1141 e = []
1168 e = []
1142 for k in keys:
1169 for k in keys:
1143 if k == 'rev':
1170 if k == 'rev':
1144 e.append(r)
1171 e.append(r)
1145 elif k == '-rev':
1172 elif k == '-rev':
1146 e.append(-r)
1173 e.append(-r)
1147 elif k == 'branch':
1174 elif k == 'branch':
1148 e.append(c.branch())
1175 e.append(c.branch())
1149 elif k == '-branch':
1176 elif k == '-branch':
1150 e.append(invert(c.branch()))
1177 e.append(invert(c.branch()))
1151 elif k == 'desc':
1178 elif k == 'desc':
1152 e.append(c.description())
1179 e.append(c.description())
1153 elif k == '-desc':
1180 elif k == '-desc':
1154 e.append(invert(c.description()))
1181 e.append(invert(c.description()))
1155 elif k in 'user author':
1182 elif k in 'user author':
1156 e.append(c.user())
1183 e.append(c.user())
1157 elif k in '-user -author':
1184 elif k in '-user -author':
1158 e.append(invert(c.user()))
1185 e.append(invert(c.user()))
1159 elif k == 'date':
1186 elif k == 'date':
1160 e.append(c.date()[0])
1187 e.append(c.date()[0])
1161 elif k == '-date':
1188 elif k == '-date':
1162 e.append(-c.date()[0])
1189 e.append(-c.date()[0])
1163 else:
1190 else:
1164 raise error.ParseError(_("unknown sort key %r") % k)
1191 raise error.ParseError(_("unknown sort key %r") % k)
1165 e.append(r)
1192 e.append(r)
1166 l.append(e)
1193 l.append(e)
1167 l.sort()
1194 l.sort()
1168 return [e[-1] for e in l]
1195 return [e[-1] for e in l]
1169
1196
1170 def _stringmatcher(pattern):
1197 def _stringmatcher(pattern):
1171 """
1198 """
1172 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1199 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1173 returns the matcher name, pattern, and matcher function.
1200 returns the matcher name, pattern, and matcher function.
1174 missing or unknown prefixes are treated as literal matches.
1201 missing or unknown prefixes are treated as literal matches.
1175
1202
1176 helper for tests:
1203 helper for tests:
1177 >>> def test(pattern, *tests):
1204 >>> def test(pattern, *tests):
1178 ... kind, pattern, matcher = _stringmatcher(pattern)
1205 ... kind, pattern, matcher = _stringmatcher(pattern)
1179 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1206 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1180
1207
1181 exact matching (no prefix):
1208 exact matching (no prefix):
1182 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1209 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1183 ('literal', 'abcdefg', [False, False, True])
1210 ('literal', 'abcdefg', [False, False, True])
1184
1211
1185 regex matching ('re:' prefix)
1212 regex matching ('re:' prefix)
1186 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1213 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1187 ('re', 'a.+b', [False, False, True])
1214 ('re', 'a.+b', [False, False, True])
1188
1215
1189 force exact matches ('literal:' prefix)
1216 force exact matches ('literal:' prefix)
1190 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1217 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1191 ('literal', 're:foobar', [False, True])
1218 ('literal', 're:foobar', [False, True])
1192
1219
1193 unknown prefixes are ignored and treated as literals
1220 unknown prefixes are ignored and treated as literals
1194 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1221 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1195 ('literal', 'foo:bar', [False, False, True])
1222 ('literal', 'foo:bar', [False, False, True])
1196 """
1223 """
1197 if pattern.startswith('re:'):
1224 if pattern.startswith('re:'):
1198 pattern = pattern[3:]
1225 pattern = pattern[3:]
1199 try:
1226 try:
1200 regex = re.compile(pattern)
1227 regex = re.compile(pattern)
1201 except re.error, e:
1228 except re.error, e:
1202 raise error.ParseError(_('invalid regular expression: %s')
1229 raise error.ParseError(_('invalid regular expression: %s')
1203 % e)
1230 % e)
1204 return 're', pattern, regex.search
1231 return 're', pattern, regex.search
1205 elif pattern.startswith('literal:'):
1232 elif pattern.startswith('literal:'):
1206 pattern = pattern[8:]
1233 pattern = pattern[8:]
1207 return 'literal', pattern, pattern.__eq__
1234 return 'literal', pattern, pattern.__eq__
1208
1235
1209 def _substringmatcher(pattern):
1236 def _substringmatcher(pattern):
1210 kind, pattern, matcher = _stringmatcher(pattern)
1237 kind, pattern, matcher = _stringmatcher(pattern)
1211 if kind == 'literal':
1238 if kind == 'literal':
1212 matcher = lambda s: pattern in s
1239 matcher = lambda s: pattern in s
1213 return kind, pattern, matcher
1240 return kind, pattern, matcher
1214
1241
1215 def tag(repo, subset, x):
1242 def tag(repo, subset, x):
1216 """``tag([name])``
1243 """``tag([name])``
1217 The specified tag by name, or all tagged revisions if no name is given.
1244 The specified tag by name, or all tagged revisions if no name is given.
1218 """
1245 """
1219 # i18n: "tag" is a keyword
1246 # i18n: "tag" is a keyword
1220 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1247 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1221 cl = repo.changelog
1248 cl = repo.changelog
1222 if args:
1249 if args:
1223 pattern = getstring(args[0],
1250 pattern = getstring(args[0],
1224 # i18n: "tag" is a keyword
1251 # i18n: "tag" is a keyword
1225 _('the argument to tag must be a string'))
1252 _('the argument to tag must be a string'))
1226 kind, pattern, matcher = _stringmatcher(pattern)
1253 kind, pattern, matcher = _stringmatcher(pattern)
1227 if kind == 'literal':
1254 if kind == 'literal':
1228 # avoid resolving all tags
1255 # avoid resolving all tags
1229 tn = repo._tagscache.tags.get(pattern, None)
1256 tn = repo._tagscache.tags.get(pattern, None)
1230 if tn is None:
1257 if tn is None:
1231 raise util.Abort(_("tag '%s' does not exist") % pattern)
1258 raise util.Abort(_("tag '%s' does not exist") % pattern)
1232 s = set([repo[tn].rev()])
1259 s = set([repo[tn].rev()])
1233 else:
1260 else:
1234 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1261 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1235 if not s:
1262 if not s:
1236 raise util.Abort(_("no tags exist that match '%s'") % pattern)
1263 raise util.Abort(_("no tags exist that match '%s'") % pattern)
1237 else:
1264 else:
1238 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1265 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1239 return [r for r in subset if r in s]
1266 return [r for r in subset if r in s]
1240
1267
1241 def tagged(repo, subset, x):
1268 def tagged(repo, subset, x):
1242 return tag(repo, subset, x)
1269 return tag(repo, subset, x)
1243
1270
1244 def user(repo, subset, x):
1271 def user(repo, subset, x):
1245 """``user(string)``
1272 """``user(string)``
1246 User name contains string. The match is case-insensitive.
1273 User name contains string. The match is case-insensitive.
1247
1274
1248 If `string` starts with `re:`, the remainder of the string is treated as
1275 If `string` starts with `re:`, the remainder of the string is treated as
1249 a regular expression. To match a user that actually contains `re:`, use
1276 a regular expression. To match a user that actually contains `re:`, use
1250 the prefix `literal:`.
1277 the prefix `literal:`.
1251 """
1278 """
1252 return author(repo, subset, x)
1279 return author(repo, subset, x)
1253
1280
1254 # for internal use
1281 # for internal use
1255 def _list(repo, subset, x):
1282 def _list(repo, subset, x):
1256 s = getstring(x, "internal error")
1283 s = getstring(x, "internal error")
1257 if not s:
1284 if not s:
1258 return []
1285 return []
1259 if not isinstance(subset, set):
1286 if not isinstance(subset, set):
1260 subset = set(subset)
1287 subset = set(subset)
1261 ls = [repo[r].rev() for r in s.split('\0')]
1288 ls = [repo[r].rev() for r in s.split('\0')]
1262 return [r for r in ls if r in subset]
1289 return [r for r in ls if r in subset]
1263
1290
1264 symbols = {
1291 symbols = {
1265 "adds": adds,
1292 "adds": adds,
1266 "all": getall,
1293 "all": getall,
1267 "ancestor": ancestor,
1294 "ancestor": ancestor,
1268 "ancestors": ancestors,
1295 "ancestors": ancestors,
1269 "_firstancestors": _firstancestors,
1296 "_firstancestors": _firstancestors,
1270 "author": author,
1297 "author": author,
1271 "bisect": bisect,
1298 "bisect": bisect,
1272 "bisected": bisected,
1299 "bisected": bisected,
1273 "bookmark": bookmark,
1300 "bookmark": bookmark,
1274 "branch": branch,
1301 "branch": branch,
1275 "children": children,
1302 "children": children,
1276 "closed": closed,
1303 "closed": closed,
1277 "contains": contains,
1304 "contains": contains,
1278 "date": date,
1305 "date": date,
1279 "desc": desc,
1306 "desc": desc,
1280 "descendants": descendants,
1307 "descendants": descendants,
1281 "_firstdescendants": _firstdescendants,
1308 "_firstdescendants": _firstdescendants,
1282 "draft": draft,
1309 "draft": draft,
1283 "extra": extra,
1310 "extra": extra,
1284 "file": hasfile,
1311 "file": hasfile,
1285 "filelog": filelog,
1312 "filelog": filelog,
1286 "first": first,
1313 "first": first,
1287 "follow": follow,
1314 "follow": follow,
1288 "_followfirst": _followfirst,
1315 "_followfirst": _followfirst,
1289 "grep": grep,
1316 "grep": grep,
1290 "head": head,
1317 "head": head,
1291 "heads": heads,
1318 "heads": heads,
1292 "id": node_,
1319 "id": node_,
1293 "keyword": keyword,
1320 "keyword": keyword,
1294 "last": last,
1321 "last": last,
1295 "limit": limit,
1322 "limit": limit,
1296 "_matchfiles": _matchfiles,
1323 "_matchfiles": _matchfiles,
1297 "max": maxrev,
1324 "max": maxrev,
1298 "merge": merge,
1325 "merge": merge,
1299 "min": minrev,
1326 "min": minrev,
1300 "modifies": modifies,
1327 "modifies": modifies,
1301 "outgoing": outgoing,
1328 "outgoing": outgoing,
1302 "p1": p1,
1329 "p1": p1,
1303 "p2": p2,
1330 "p2": p2,
1304 "parents": parents,
1331 "parents": parents,
1305 "present": present,
1332 "present": present,
1306 "public": public,
1333 "public": public,
1307 "remote": remote,
1334 "remote": remote,
1308 "removes": removes,
1335 "removes": removes,
1309 "rev": rev,
1336 "rev": rev,
1310 "reverse": reverse,
1337 "reverse": reverse,
1311 "roots": roots,
1338 "roots": roots,
1312 "sort": sort,
1339 "sort": sort,
1313 "secret": secret,
1340 "secret": secret,
1314 "matching": matching,
1341 "matching": matching,
1315 "tag": tag,
1342 "tag": tag,
1316 "tagged": tagged,
1343 "tagged": tagged,
1317 "user": user,
1344 "user": user,
1318 "_list": _list,
1345 "_list": _list,
1319 }
1346 }
1320
1347
1321 methods = {
1348 methods = {
1322 "range": rangeset,
1349 "range": rangeset,
1323 "dagrange": dagrange,
1350 "dagrange": dagrange,
1324 "string": stringset,
1351 "string": stringset,
1325 "symbol": symbolset,
1352 "symbol": symbolset,
1326 "and": andset,
1353 "and": andset,
1327 "or": orset,
1354 "or": orset,
1328 "not": notset,
1355 "not": notset,
1329 "list": listset,
1356 "list": listset,
1330 "func": func,
1357 "func": func,
1331 "ancestor": ancestorspec,
1358 "ancestor": ancestorspec,
1332 "parent": parentspec,
1359 "parent": parentspec,
1333 "parentpost": p1,
1360 "parentpost": p1,
1334 }
1361 }
1335
1362
1336 def optimize(x, small):
1363 def optimize(x, small):
1337 if x is None:
1364 if x is None:
1338 return 0, x
1365 return 0, x
1339
1366
1340 smallbonus = 1
1367 smallbonus = 1
1341 if small:
1368 if small:
1342 smallbonus = .5
1369 smallbonus = .5
1343
1370
1344 op = x[0]
1371 op = x[0]
1345 if op == 'minus':
1372 if op == 'minus':
1346 return optimize(('and', x[1], ('not', x[2])), small)
1373 return optimize(('and', x[1], ('not', x[2])), small)
1347 elif op == 'dagrangepre':
1374 elif op == 'dagrangepre':
1348 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1375 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1349 elif op == 'dagrangepost':
1376 elif op == 'dagrangepost':
1350 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1377 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1351 elif op == 'rangepre':
1378 elif op == 'rangepre':
1352 return optimize(('range', ('string', '0'), x[1]), small)
1379 return optimize(('range', ('string', '0'), x[1]), small)
1353 elif op == 'rangepost':
1380 elif op == 'rangepost':
1354 return optimize(('range', x[1], ('string', 'tip')), small)
1381 return optimize(('range', x[1], ('string', 'tip')), small)
1355 elif op == 'negate':
1382 elif op == 'negate':
1356 return optimize(('string',
1383 return optimize(('string',
1357 '-' + getstring(x[1], _("can't negate that"))), small)
1384 '-' + getstring(x[1], _("can't negate that"))), small)
1358 elif op in 'string symbol negate':
1385 elif op in 'string symbol negate':
1359 return smallbonus, x # single revisions are small
1386 return smallbonus, x # single revisions are small
1360 elif op == 'and':
1387 elif op == 'and':
1361 wa, ta = optimize(x[1], True)
1388 wa, ta = optimize(x[1], True)
1362 wb, tb = optimize(x[2], True)
1389 wb, tb = optimize(x[2], True)
1363 w = min(wa, wb)
1390 w = min(wa, wb)
1364 if wa > wb:
1391 if wa > wb:
1365 return w, (op, tb, ta)
1392 return w, (op, tb, ta)
1366 return w, (op, ta, tb)
1393 return w, (op, ta, tb)
1367 elif op == 'or':
1394 elif op == 'or':
1368 wa, ta = optimize(x[1], False)
1395 wa, ta = optimize(x[1], False)
1369 wb, tb = optimize(x[2], False)
1396 wb, tb = optimize(x[2], False)
1370 if wb < wa:
1397 if wb < wa:
1371 wb, wa = wa, wb
1398 wb, wa = wa, wb
1372 return max(wa, wb), (op, ta, tb)
1399 return max(wa, wb), (op, ta, tb)
1373 elif op == 'not':
1400 elif op == 'not':
1374 o = optimize(x[1], not small)
1401 o = optimize(x[1], not small)
1375 return o[0], (op, o[1])
1402 return o[0], (op, o[1])
1376 elif op == 'parentpost':
1403 elif op == 'parentpost':
1377 o = optimize(x[1], small)
1404 o = optimize(x[1], small)
1378 return o[0], (op, o[1])
1405 return o[0], (op, o[1])
1379 elif op == 'group':
1406 elif op == 'group':
1380 return optimize(x[1], small)
1407 return optimize(x[1], small)
1381 elif op in 'dagrange range list parent ancestorspec':
1408 elif op in 'dagrange range list parent ancestorspec':
1382 if op == 'parent':
1409 if op == 'parent':
1383 # x^:y means (x^) : y, not x ^ (:y)
1410 # x^:y means (x^) : y, not x ^ (:y)
1384 post = ('parentpost', x[1])
1411 post = ('parentpost', x[1])
1385 if x[2][0] == 'dagrangepre':
1412 if x[2][0] == 'dagrangepre':
1386 return optimize(('dagrange', post, x[2][1]), small)
1413 return optimize(('dagrange', post, x[2][1]), small)
1387 elif x[2][0] == 'rangepre':
1414 elif x[2][0] == 'rangepre':
1388 return optimize(('range', post, x[2][1]), small)
1415 return optimize(('range', post, x[2][1]), small)
1389
1416
1390 wa, ta = optimize(x[1], small)
1417 wa, ta = optimize(x[1], small)
1391 wb, tb = optimize(x[2], small)
1418 wb, tb = optimize(x[2], small)
1392 return wa + wb, (op, ta, tb)
1419 return wa + wb, (op, ta, tb)
1393 elif op == 'func':
1420 elif op == 'func':
1394 f = getstring(x[1], _("not a symbol"))
1421 f = getstring(x[1], _("not a symbol"))
1395 wa, ta = optimize(x[2], small)
1422 wa, ta = optimize(x[2], small)
1396 if f in ("author branch closed date desc file grep keyword "
1423 if f in ("author branch closed date desc file grep keyword "
1397 "outgoing user"):
1424 "outgoing user"):
1398 w = 10 # slow
1425 w = 10 # slow
1399 elif f in "modifies adds removes":
1426 elif f in "modifies adds removes":
1400 w = 30 # slower
1427 w = 30 # slower
1401 elif f == "contains":
1428 elif f == "contains":
1402 w = 100 # very slow
1429 w = 100 # very slow
1403 elif f == "ancestor":
1430 elif f == "ancestor":
1404 w = 1 * smallbonus
1431 w = 1 * smallbonus
1405 elif f in "reverse limit first":
1432 elif f in "reverse limit first":
1406 w = 0
1433 w = 0
1407 elif f in "sort":
1434 elif f in "sort":
1408 w = 10 # assume most sorts look at changelog
1435 w = 10 # assume most sorts look at changelog
1409 else:
1436 else:
1410 w = 1
1437 w = 1
1411 return w + wa, (op, x[1], ta)
1438 return w + wa, (op, x[1], ta)
1412 return 1, x
1439 return 1, x
1413
1440
1414 _aliasarg = ('func', ('symbol', '_aliasarg'))
1441 _aliasarg = ('func', ('symbol', '_aliasarg'))
1415 def _getaliasarg(tree):
1442 def _getaliasarg(tree):
1416 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1443 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1417 return X, None otherwise.
1444 return X, None otherwise.
1418 """
1445 """
1419 if (len(tree) == 3 and tree[:2] == _aliasarg
1446 if (len(tree) == 3 and tree[:2] == _aliasarg
1420 and tree[2][0] == 'string'):
1447 and tree[2][0] == 'string'):
1421 return tree[2][1]
1448 return tree[2][1]
1422 return None
1449 return None
1423
1450
1424 def _checkaliasarg(tree, known=None):
1451 def _checkaliasarg(tree, known=None):
1425 """Check tree contains no _aliasarg construct or only ones which
1452 """Check tree contains no _aliasarg construct or only ones which
1426 value is in known. Used to avoid alias placeholders injection.
1453 value is in known. Used to avoid alias placeholders injection.
1427 """
1454 """
1428 if isinstance(tree, tuple):
1455 if isinstance(tree, tuple):
1429 arg = _getaliasarg(tree)
1456 arg = _getaliasarg(tree)
1430 if arg is not None and (not known or arg not in known):
1457 if arg is not None and (not known or arg not in known):
1431 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1458 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1432 for t in tree:
1459 for t in tree:
1433 _checkaliasarg(t, known)
1460 _checkaliasarg(t, known)
1434
1461
1435 class revsetalias(object):
1462 class revsetalias(object):
1436 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1463 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1437 args = None
1464 args = None
1438
1465
1439 def __init__(self, name, value):
1466 def __init__(self, name, value):
1440 '''Aliases like:
1467 '''Aliases like:
1441
1468
1442 h = heads(default)
1469 h = heads(default)
1443 b($1) = ancestors($1) - ancestors(default)
1470 b($1) = ancestors($1) - ancestors(default)
1444 '''
1471 '''
1445 m = self.funcre.search(name)
1472 m = self.funcre.search(name)
1446 if m:
1473 if m:
1447 self.name = m.group(1)
1474 self.name = m.group(1)
1448 self.tree = ('func', ('symbol', m.group(1)))
1475 self.tree = ('func', ('symbol', m.group(1)))
1449 self.args = [x.strip() for x in m.group(2).split(',')]
1476 self.args = [x.strip() for x in m.group(2).split(',')]
1450 for arg in self.args:
1477 for arg in self.args:
1451 # _aliasarg() is an unknown symbol only used separate
1478 # _aliasarg() is an unknown symbol only used separate
1452 # alias argument placeholders from regular strings.
1479 # alias argument placeholders from regular strings.
1453 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1480 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1454 else:
1481 else:
1455 self.name = name
1482 self.name = name
1456 self.tree = ('symbol', name)
1483 self.tree = ('symbol', name)
1457
1484
1458 self.replacement, pos = parse(value)
1485 self.replacement, pos = parse(value)
1459 if pos != len(value):
1486 if pos != len(value):
1460 raise error.ParseError(_('invalid token'), pos)
1487 raise error.ParseError(_('invalid token'), pos)
1461 # Check for placeholder injection
1488 # Check for placeholder injection
1462 _checkaliasarg(self.replacement, self.args)
1489 _checkaliasarg(self.replacement, self.args)
1463
1490
1464 def _getalias(aliases, tree):
1491 def _getalias(aliases, tree):
1465 """If tree looks like an unexpanded alias, return it. Return None
1492 """If tree looks like an unexpanded alias, return it. Return None
1466 otherwise.
1493 otherwise.
1467 """
1494 """
1468 if isinstance(tree, tuple) and tree:
1495 if isinstance(tree, tuple) and tree:
1469 if tree[0] == 'symbol' and len(tree) == 2:
1496 if tree[0] == 'symbol' and len(tree) == 2:
1470 name = tree[1]
1497 name = tree[1]
1471 alias = aliases.get(name)
1498 alias = aliases.get(name)
1472 if alias and alias.args is None and alias.tree == tree:
1499 if alias and alias.args is None and alias.tree == tree:
1473 return alias
1500 return alias
1474 if tree[0] == 'func' and len(tree) > 1:
1501 if tree[0] == 'func' and len(tree) > 1:
1475 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1502 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1476 name = tree[1][1]
1503 name = tree[1][1]
1477 alias = aliases.get(name)
1504 alias = aliases.get(name)
1478 if alias and alias.args is not None and alias.tree == tree[:2]:
1505 if alias and alias.args is not None and alias.tree == tree[:2]:
1479 return alias
1506 return alias
1480 return None
1507 return None
1481
1508
1482 def _expandargs(tree, args):
1509 def _expandargs(tree, args):
1483 """Replace _aliasarg instances with the substitution value of the
1510 """Replace _aliasarg instances with the substitution value of the
1484 same name in args, recursively.
1511 same name in args, recursively.
1485 """
1512 """
1486 if not tree or not isinstance(tree, tuple):
1513 if not tree or not isinstance(tree, tuple):
1487 return tree
1514 return tree
1488 arg = _getaliasarg(tree)
1515 arg = _getaliasarg(tree)
1489 if arg is not None:
1516 if arg is not None:
1490 return args[arg]
1517 return args[arg]
1491 return tuple(_expandargs(t, args) for t in tree)
1518 return tuple(_expandargs(t, args) for t in tree)
1492
1519
1493 def _expandaliases(aliases, tree, expanding, cache):
1520 def _expandaliases(aliases, tree, expanding, cache):
1494 """Expand aliases in tree, recursively.
1521 """Expand aliases in tree, recursively.
1495
1522
1496 'aliases' is a dictionary mapping user defined aliases to
1523 'aliases' is a dictionary mapping user defined aliases to
1497 revsetalias objects.
1524 revsetalias objects.
1498 """
1525 """
1499 if not isinstance(tree, tuple):
1526 if not isinstance(tree, tuple):
1500 # Do not expand raw strings
1527 # Do not expand raw strings
1501 return tree
1528 return tree
1502 alias = _getalias(aliases, tree)
1529 alias = _getalias(aliases, tree)
1503 if alias is not None:
1530 if alias is not None:
1504 if alias in expanding:
1531 if alias in expanding:
1505 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1532 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1506 'detected') % alias.name)
1533 'detected') % alias.name)
1507 expanding.append(alias)
1534 expanding.append(alias)
1508 if alias.name not in cache:
1535 if alias.name not in cache:
1509 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1536 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1510 expanding, cache)
1537 expanding, cache)
1511 result = cache[alias.name]
1538 result = cache[alias.name]
1512 expanding.pop()
1539 expanding.pop()
1513 if alias.args is not None:
1540 if alias.args is not None:
1514 l = getlist(tree[2])
1541 l = getlist(tree[2])
1515 if len(l) != len(alias.args):
1542 if len(l) != len(alias.args):
1516 raise error.ParseError(
1543 raise error.ParseError(
1517 _('invalid number of arguments: %s') % len(l))
1544 _('invalid number of arguments: %s') % len(l))
1518 l = [_expandaliases(aliases, a, [], cache) for a in l]
1545 l = [_expandaliases(aliases, a, [], cache) for a in l]
1519 result = _expandargs(result, dict(zip(alias.args, l)))
1546 result = _expandargs(result, dict(zip(alias.args, l)))
1520 else:
1547 else:
1521 result = tuple(_expandaliases(aliases, t, expanding, cache)
1548 result = tuple(_expandaliases(aliases, t, expanding, cache)
1522 for t in tree)
1549 for t in tree)
1523 return result
1550 return result
1524
1551
1525 def findaliases(ui, tree):
1552 def findaliases(ui, tree):
1526 _checkaliasarg(tree)
1553 _checkaliasarg(tree)
1527 aliases = {}
1554 aliases = {}
1528 for k, v in ui.configitems('revsetalias'):
1555 for k, v in ui.configitems('revsetalias'):
1529 alias = revsetalias(k, v)
1556 alias = revsetalias(k, v)
1530 aliases[alias.name] = alias
1557 aliases[alias.name] = alias
1531 return _expandaliases(aliases, tree, [], {})
1558 return _expandaliases(aliases, tree, [], {})
1532
1559
1533 parse = parser.parser(tokenize, elements).parse
1560 parse = parser.parser(tokenize, elements).parse
1534
1561
1535 def match(ui, spec):
1562 def match(ui, spec):
1536 if not spec:
1563 if not spec:
1537 raise error.ParseError(_("empty query"))
1564 raise error.ParseError(_("empty query"))
1538 tree, pos = parse(spec)
1565 tree, pos = parse(spec)
1539 if (pos != len(spec)):
1566 if (pos != len(spec)):
1540 raise error.ParseError(_("invalid token"), pos)
1567 raise error.ParseError(_("invalid token"), pos)
1541 if ui:
1568 if ui:
1542 tree = findaliases(ui, tree)
1569 tree = findaliases(ui, tree)
1543 weight, tree = optimize(tree, True)
1570 weight, tree = optimize(tree, True)
1544 def mfunc(repo, subset):
1571 def mfunc(repo, subset):
1545 return getset(repo, subset, tree)
1572 return getset(repo, subset, tree)
1546 return mfunc
1573 return mfunc
1547
1574
1548 def formatspec(expr, *args):
1575 def formatspec(expr, *args):
1549 '''
1576 '''
1550 This is a convenience function for using revsets internally, and
1577 This is a convenience function for using revsets internally, and
1551 escapes arguments appropriately. Aliases are intentionally ignored
1578 escapes arguments appropriately. Aliases are intentionally ignored
1552 so that intended expression behavior isn't accidentally subverted.
1579 so that intended expression behavior isn't accidentally subverted.
1553
1580
1554 Supported arguments:
1581 Supported arguments:
1555
1582
1556 %r = revset expression, parenthesized
1583 %r = revset expression, parenthesized
1557 %d = int(arg), no quoting
1584 %d = int(arg), no quoting
1558 %s = string(arg), escaped and single-quoted
1585 %s = string(arg), escaped and single-quoted
1559 %b = arg.branch(), escaped and single-quoted
1586 %b = arg.branch(), escaped and single-quoted
1560 %n = hex(arg), single-quoted
1587 %n = hex(arg), single-quoted
1561 %% = a literal '%'
1588 %% = a literal '%'
1562
1589
1563 Prefixing the type with 'l' specifies a parenthesized list of that type.
1590 Prefixing the type with 'l' specifies a parenthesized list of that type.
1564
1591
1565 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1592 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1566 '(10 or 11):: and ((this()) or (that()))'
1593 '(10 or 11):: and ((this()) or (that()))'
1567 >>> formatspec('%d:: and not %d::', 10, 20)
1594 >>> formatspec('%d:: and not %d::', 10, 20)
1568 '10:: and not 20::'
1595 '10:: and not 20::'
1569 >>> formatspec('%ld or %ld', [], [1])
1596 >>> formatspec('%ld or %ld', [], [1])
1570 "_list('') or 1"
1597 "_list('') or 1"
1571 >>> formatspec('keyword(%s)', 'foo\\xe9')
1598 >>> formatspec('keyword(%s)', 'foo\\xe9')
1572 "keyword('foo\\\\xe9')"
1599 "keyword('foo\\\\xe9')"
1573 >>> b = lambda: 'default'
1600 >>> b = lambda: 'default'
1574 >>> b.branch = b
1601 >>> b.branch = b
1575 >>> formatspec('branch(%b)', b)
1602 >>> formatspec('branch(%b)', b)
1576 "branch('default')"
1603 "branch('default')"
1577 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1604 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1578 "root(_list('a\\x00b\\x00c\\x00d'))"
1605 "root(_list('a\\x00b\\x00c\\x00d'))"
1579 '''
1606 '''
1580
1607
1581 def quote(s):
1608 def quote(s):
1582 return repr(str(s))
1609 return repr(str(s))
1583
1610
1584 def argtype(c, arg):
1611 def argtype(c, arg):
1585 if c == 'd':
1612 if c == 'd':
1586 return str(int(arg))
1613 return str(int(arg))
1587 elif c == 's':
1614 elif c == 's':
1588 return quote(arg)
1615 return quote(arg)
1589 elif c == 'r':
1616 elif c == 'r':
1590 parse(arg) # make sure syntax errors are confined
1617 parse(arg) # make sure syntax errors are confined
1591 return '(%s)' % arg
1618 return '(%s)' % arg
1592 elif c == 'n':
1619 elif c == 'n':
1593 return quote(node.hex(arg))
1620 return quote(node.hex(arg))
1594 elif c == 'b':
1621 elif c == 'b':
1595 return quote(arg.branch())
1622 return quote(arg.branch())
1596
1623
1597 def listexp(s, t):
1624 def listexp(s, t):
1598 l = len(s)
1625 l = len(s)
1599 if l == 0:
1626 if l == 0:
1600 return "_list('')"
1627 return "_list('')"
1601 elif l == 1:
1628 elif l == 1:
1602 return argtype(t, s[0])
1629 return argtype(t, s[0])
1603 elif t == 'd':
1630 elif t == 'd':
1604 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1631 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1605 elif t == 's':
1632 elif t == 's':
1606 return "_list('%s')" % "\0".join(s)
1633 return "_list('%s')" % "\0".join(s)
1607 elif t == 'n':
1634 elif t == 'n':
1608 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1635 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1609 elif t == 'b':
1636 elif t == 'b':
1610 return "_list('%s')" % "\0".join(a.branch() for a in s)
1637 return "_list('%s')" % "\0".join(a.branch() for a in s)
1611
1638
1612 m = l // 2
1639 m = l // 2
1613 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1640 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1614
1641
1615 ret = ''
1642 ret = ''
1616 pos = 0
1643 pos = 0
1617 arg = 0
1644 arg = 0
1618 while pos < len(expr):
1645 while pos < len(expr):
1619 c = expr[pos]
1646 c = expr[pos]
1620 if c == '%':
1647 if c == '%':
1621 pos += 1
1648 pos += 1
1622 d = expr[pos]
1649 d = expr[pos]
1623 if d == '%':
1650 if d == '%':
1624 ret += d
1651 ret += d
1625 elif d in 'dsnbr':
1652 elif d in 'dsnbr':
1626 ret += argtype(d, args[arg])
1653 ret += argtype(d, args[arg])
1627 arg += 1
1654 arg += 1
1628 elif d == 'l':
1655 elif d == 'l':
1629 # a list of some type
1656 # a list of some type
1630 pos += 1
1657 pos += 1
1631 d = expr[pos]
1658 d = expr[pos]
1632 ret += listexp(list(args[arg]), d)
1659 ret += listexp(list(args[arg]), d)
1633 arg += 1
1660 arg += 1
1634 else:
1661 else:
1635 raise util.Abort('unexpected revspec format character %s' % d)
1662 raise util.Abort('unexpected revspec format character %s' % d)
1636 else:
1663 else:
1637 ret += c
1664 ret += c
1638 pos += 1
1665 pos += 1
1639
1666
1640 return ret
1667 return ret
1641
1668
1642 def prettyformat(tree):
1669 def prettyformat(tree):
1643 def _prettyformat(tree, level, lines):
1670 def _prettyformat(tree, level, lines):
1644 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1671 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1645 lines.append((level, str(tree)))
1672 lines.append((level, str(tree)))
1646 else:
1673 else:
1647 lines.append((level, '(%s' % tree[0]))
1674 lines.append((level, '(%s' % tree[0]))
1648 for s in tree[1:]:
1675 for s in tree[1:]:
1649 _prettyformat(s, level + 1, lines)
1676 _prettyformat(s, level + 1, lines)
1650 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1677 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1651
1678
1652 lines = []
1679 lines = []
1653 _prettyformat(tree, 0, lines)
1680 _prettyformat(tree, 0, lines)
1654 output = '\n'.join((' '*l + s) for l, s in lines)
1681 output = '\n'.join((' '*l + s) for l, s in lines)
1655 return output
1682 return output
1656
1683
1657 # tell hggettext to extract docstrings from these functions:
1684 # tell hggettext to extract docstrings from these functions:
1658 i18nfunctions = symbols.values()
1685 i18nfunctions = symbols.values()
General Comments 0
You need to be logged in to leave comments. Login now