##// END OF EJS Templates
revset: added lazyset implementation to contains revset
Lucas Moscovicz -
r20461:abd8e56a default
parent child Browse files
Show More
@@ -1,2135 +1,2133 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import re
8 import re
9 import parser, util, error, discovery, hbisect, phases
9 import parser, util, error, discovery, hbisect, phases
10 import node
10 import node
11 import match as matchmod
11 import match as matchmod
12 from i18n import _
12 from i18n import _
13 import encoding
13 import encoding
14 import obsolete as obsmod
14 import obsolete as obsmod
15 import pathutil
15 import pathutil
16 import repoview
16 import repoview
17
17
18 def _revancestors(repo, revs, followfirst):
18 def _revancestors(repo, revs, followfirst):
19 """Like revlog.ancestors(), but supports followfirst."""
19 """Like revlog.ancestors(), but supports followfirst."""
20 cut = followfirst and 1 or None
20 cut = followfirst and 1 or None
21 cl = repo.changelog
21 cl = repo.changelog
22 visit = util.deque(revs)
22 visit = util.deque(revs)
23 seen = set([node.nullrev])
23 seen = set([node.nullrev])
24 while visit:
24 while visit:
25 for parent in cl.parentrevs(visit.popleft())[:cut]:
25 for parent in cl.parentrevs(visit.popleft())[:cut]:
26 if parent not in seen:
26 if parent not in seen:
27 visit.append(parent)
27 visit.append(parent)
28 seen.add(parent)
28 seen.add(parent)
29 yield parent
29 yield parent
30
30
31 def _revdescendants(repo, revs, followfirst):
31 def _revdescendants(repo, revs, followfirst):
32 """Like revlog.descendants() but supports followfirst."""
32 """Like revlog.descendants() but supports followfirst."""
33 cut = followfirst and 1 or None
33 cut = followfirst and 1 or None
34 cl = repo.changelog
34 cl = repo.changelog
35 first = min(revs)
35 first = min(revs)
36 nullrev = node.nullrev
36 nullrev = node.nullrev
37 if first == nullrev:
37 if first == nullrev:
38 # Are there nodes with a null first parent and a non-null
38 # Are there nodes with a null first parent and a non-null
39 # second one? Maybe. Do we care? Probably not.
39 # second one? Maybe. Do we care? Probably not.
40 for i in cl:
40 for i in cl:
41 yield i
41 yield i
42 return
42 return
43
43
44 seen = set(revs)
44 seen = set(revs)
45 for i in cl.revs(first + 1):
45 for i in cl.revs(first + 1):
46 for x in cl.parentrevs(i)[:cut]:
46 for x in cl.parentrevs(i)[:cut]:
47 if x != nullrev and x in seen:
47 if x != nullrev and x in seen:
48 seen.add(i)
48 seen.add(i)
49 yield i
49 yield i
50 break
50 break
51
51
52 def _revsbetween(repo, roots, heads):
52 def _revsbetween(repo, roots, heads):
53 """Return all paths between roots and heads, inclusive of both endpoint
53 """Return all paths between roots and heads, inclusive of both endpoint
54 sets."""
54 sets."""
55 if not roots:
55 if not roots:
56 return baseset([])
56 return baseset([])
57 parentrevs = repo.changelog.parentrevs
57 parentrevs = repo.changelog.parentrevs
58 visit = baseset(heads)
58 visit = baseset(heads)
59 reachable = set()
59 reachable = set()
60 seen = {}
60 seen = {}
61 minroot = min(roots)
61 minroot = min(roots)
62 roots = set(roots)
62 roots = set(roots)
63 # open-code the post-order traversal due to the tiny size of
63 # open-code the post-order traversal due to the tiny size of
64 # sys.getrecursionlimit()
64 # sys.getrecursionlimit()
65 while visit:
65 while visit:
66 rev = visit.pop()
66 rev = visit.pop()
67 if rev in roots:
67 if rev in roots:
68 reachable.add(rev)
68 reachable.add(rev)
69 parents = parentrevs(rev)
69 parents = parentrevs(rev)
70 seen[rev] = parents
70 seen[rev] = parents
71 for parent in parents:
71 for parent in parents:
72 if parent >= minroot and parent not in seen:
72 if parent >= minroot and parent not in seen:
73 visit.append(parent)
73 visit.append(parent)
74 if not reachable:
74 if not reachable:
75 return baseset([])
75 return baseset([])
76 for rev in sorted(seen):
76 for rev in sorted(seen):
77 for parent in seen[rev]:
77 for parent in seen[rev]:
78 if parent in reachable:
78 if parent in reachable:
79 reachable.add(rev)
79 reachable.add(rev)
80 return baseset(sorted(reachable))
80 return baseset(sorted(reachable))
81
81
82 elements = {
82 elements = {
83 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
83 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
84 "~": (18, None, ("ancestor", 18)),
84 "~": (18, None, ("ancestor", 18)),
85 "^": (18, None, ("parent", 18), ("parentpost", 18)),
85 "^": (18, None, ("parent", 18), ("parentpost", 18)),
86 "-": (5, ("negate", 19), ("minus", 5)),
86 "-": (5, ("negate", 19), ("minus", 5)),
87 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
87 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
88 ("dagrangepost", 17)),
88 ("dagrangepost", 17)),
89 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
89 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
90 ("dagrangepost", 17)),
90 ("dagrangepost", 17)),
91 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
91 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
92 "not": (10, ("not", 10)),
92 "not": (10, ("not", 10)),
93 "!": (10, ("not", 10)),
93 "!": (10, ("not", 10)),
94 "and": (5, None, ("and", 5)),
94 "and": (5, None, ("and", 5)),
95 "&": (5, None, ("and", 5)),
95 "&": (5, None, ("and", 5)),
96 "or": (4, None, ("or", 4)),
96 "or": (4, None, ("or", 4)),
97 "|": (4, None, ("or", 4)),
97 "|": (4, None, ("or", 4)),
98 "+": (4, None, ("or", 4)),
98 "+": (4, None, ("or", 4)),
99 ",": (2, None, ("list", 2)),
99 ",": (2, None, ("list", 2)),
100 ")": (0, None, None),
100 ")": (0, None, None),
101 "symbol": (0, ("symbol",), None),
101 "symbol": (0, ("symbol",), None),
102 "string": (0, ("string",), None),
102 "string": (0, ("string",), None),
103 "end": (0, None, None),
103 "end": (0, None, None),
104 }
104 }
105
105
106 keywords = set(['and', 'or', 'not'])
106 keywords = set(['and', 'or', 'not'])
107
107
108 def tokenize(program):
108 def tokenize(program):
109 '''
109 '''
110 Parse a revset statement into a stream of tokens
110 Parse a revset statement into a stream of tokens
111
111
112 Check that @ is a valid unquoted token character (issue3686):
112 Check that @ is a valid unquoted token character (issue3686):
113 >>> list(tokenize("@::"))
113 >>> list(tokenize("@::"))
114 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
114 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
115
115
116 '''
116 '''
117
117
118 pos, l = 0, len(program)
118 pos, l = 0, len(program)
119 while pos < l:
119 while pos < l:
120 c = program[pos]
120 c = program[pos]
121 if c.isspace(): # skip inter-token whitespace
121 if c.isspace(): # skip inter-token whitespace
122 pass
122 pass
123 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
123 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
124 yield ('::', None, pos)
124 yield ('::', None, pos)
125 pos += 1 # skip ahead
125 pos += 1 # skip ahead
126 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
126 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
127 yield ('..', None, pos)
127 yield ('..', None, pos)
128 pos += 1 # skip ahead
128 pos += 1 # skip ahead
129 elif c in "():,-|&+!~^": # handle simple operators
129 elif c in "():,-|&+!~^": # handle simple operators
130 yield (c, None, pos)
130 yield (c, None, pos)
131 elif (c in '"\'' or c == 'r' and
131 elif (c in '"\'' or c == 'r' and
132 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
132 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
133 if c == 'r':
133 if c == 'r':
134 pos += 1
134 pos += 1
135 c = program[pos]
135 c = program[pos]
136 decode = lambda x: x
136 decode = lambda x: x
137 else:
137 else:
138 decode = lambda x: x.decode('string-escape')
138 decode = lambda x: x.decode('string-escape')
139 pos += 1
139 pos += 1
140 s = pos
140 s = pos
141 while pos < l: # find closing quote
141 while pos < l: # find closing quote
142 d = program[pos]
142 d = program[pos]
143 if d == '\\': # skip over escaped characters
143 if d == '\\': # skip over escaped characters
144 pos += 2
144 pos += 2
145 continue
145 continue
146 if d == c:
146 if d == c:
147 yield ('string', decode(program[s:pos]), s)
147 yield ('string', decode(program[s:pos]), s)
148 break
148 break
149 pos += 1
149 pos += 1
150 else:
150 else:
151 raise error.ParseError(_("unterminated string"), s)
151 raise error.ParseError(_("unterminated string"), s)
152 # gather up a symbol/keyword
152 # gather up a symbol/keyword
153 elif c.isalnum() or c in '._@' or ord(c) > 127:
153 elif c.isalnum() or c in '._@' or ord(c) > 127:
154 s = pos
154 s = pos
155 pos += 1
155 pos += 1
156 while pos < l: # find end of symbol
156 while pos < l: # find end of symbol
157 d = program[pos]
157 d = program[pos]
158 if not (d.isalnum() or d in "._/@" or ord(d) > 127):
158 if not (d.isalnum() or d in "._/@" or ord(d) > 127):
159 break
159 break
160 if d == '.' and program[pos - 1] == '.': # special case for ..
160 if d == '.' and program[pos - 1] == '.': # special case for ..
161 pos -= 1
161 pos -= 1
162 break
162 break
163 pos += 1
163 pos += 1
164 sym = program[s:pos]
164 sym = program[s:pos]
165 if sym in keywords: # operator keywords
165 if sym in keywords: # operator keywords
166 yield (sym, None, s)
166 yield (sym, None, s)
167 else:
167 else:
168 yield ('symbol', sym, s)
168 yield ('symbol', sym, s)
169 pos -= 1
169 pos -= 1
170 else:
170 else:
171 raise error.ParseError(_("syntax error"), pos)
171 raise error.ParseError(_("syntax error"), pos)
172 pos += 1
172 pos += 1
173 yield ('end', None, pos)
173 yield ('end', None, pos)
174
174
175 # helpers
175 # helpers
176
176
177 def getstring(x, err):
177 def getstring(x, err):
178 if x and (x[0] == 'string' or x[0] == 'symbol'):
178 if x and (x[0] == 'string' or x[0] == 'symbol'):
179 return x[1]
179 return x[1]
180 raise error.ParseError(err)
180 raise error.ParseError(err)
181
181
182 def getlist(x):
182 def getlist(x):
183 if not x:
183 if not x:
184 return []
184 return []
185 if x[0] == 'list':
185 if x[0] == 'list':
186 return getlist(x[1]) + [x[2]]
186 return getlist(x[1]) + [x[2]]
187 return [x]
187 return [x]
188
188
189 def getargs(x, min, max, err):
189 def getargs(x, min, max, err):
190 l = getlist(x)
190 l = getlist(x)
191 if len(l) < min or (max >= 0 and len(l) > max):
191 if len(l) < min or (max >= 0 and len(l) > max):
192 raise error.ParseError(err)
192 raise error.ParseError(err)
193 return l
193 return l
194
194
195 def getset(repo, subset, x):
195 def getset(repo, subset, x):
196 if not x:
196 if not x:
197 raise error.ParseError(_("missing argument"))
197 raise error.ParseError(_("missing argument"))
198 return methods[x[0]](repo, subset, *x[1:])
198 return methods[x[0]](repo, subset, *x[1:])
199
199
200 def _getrevsource(repo, r):
200 def _getrevsource(repo, r):
201 extra = repo[r].extra()
201 extra = repo[r].extra()
202 for label in ('source', 'transplant_source', 'rebase_source'):
202 for label in ('source', 'transplant_source', 'rebase_source'):
203 if label in extra:
203 if label in extra:
204 try:
204 try:
205 return repo[extra[label]].rev()
205 return repo[extra[label]].rev()
206 except error.RepoLookupError:
206 except error.RepoLookupError:
207 pass
207 pass
208 return None
208 return None
209
209
210 # operator methods
210 # operator methods
211
211
212 def stringset(repo, subset, x):
212 def stringset(repo, subset, x):
213 x = repo[x].rev()
213 x = repo[x].rev()
214 if x == -1 and len(subset) == len(repo):
214 if x == -1 and len(subset) == len(repo):
215 return baseset([-1])
215 return baseset([-1])
216 if len(subset) == len(repo) or x in subset:
216 if len(subset) == len(repo) or x in subset:
217 return baseset([x])
217 return baseset([x])
218 return baseset([])
218 return baseset([])
219
219
220 def symbolset(repo, subset, x):
220 def symbolset(repo, subset, x):
221 if x in symbols:
221 if x in symbols:
222 raise error.ParseError(_("can't use %s here") % x)
222 raise error.ParseError(_("can't use %s here") % x)
223 return stringset(repo, subset, x)
223 return stringset(repo, subset, x)
224
224
225 def rangeset(repo, subset, x, y):
225 def rangeset(repo, subset, x, y):
226 cl = baseset(repo.changelog)
226 cl = baseset(repo.changelog)
227 m = getset(repo, cl, x)
227 m = getset(repo, cl, x)
228 n = getset(repo, cl, y)
228 n = getset(repo, cl, y)
229
229
230 if not m or not n:
230 if not m or not n:
231 return baseset([])
231 return baseset([])
232 m, n = m[0], n[-1]
232 m, n = m[0], n[-1]
233
233
234 if m < n:
234 if m < n:
235 r = range(m, n + 1)
235 r = range(m, n + 1)
236 else:
236 else:
237 r = range(m, n - 1, -1)
237 r = range(m, n - 1, -1)
238 s = subset.set()
238 s = subset.set()
239 return baseset([x for x in r if x in s])
239 return baseset([x for x in r if x in s])
240
240
241 def dagrange(repo, subset, x, y):
241 def dagrange(repo, subset, x, y):
242 r = baseset(repo)
242 r = baseset(repo)
243 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
243 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
244 s = subset.set()
244 s = subset.set()
245 return baseset([r for r in xs if r in s])
245 return baseset([r for r in xs if r in s])
246
246
247 def andset(repo, subset, x, y):
247 def andset(repo, subset, x, y):
248 return getset(repo, getset(repo, subset, x), y)
248 return getset(repo, getset(repo, subset, x), y)
249
249
250 def orset(repo, subset, x, y):
250 def orset(repo, subset, x, y):
251 xl = getset(repo, subset, x)
251 xl = getset(repo, subset, x)
252 yl = getset(repo, subset - xl, y)
252 yl = getset(repo, subset - xl, y)
253 return xl + yl
253 return xl + yl
254
254
255 def notset(repo, subset, x):
255 def notset(repo, subset, x):
256 return subset - getset(repo, subset, x)
256 return subset - getset(repo, subset, x)
257
257
258 def listset(repo, subset, a, b):
258 def listset(repo, subset, a, b):
259 raise error.ParseError(_("can't use a list in this context"))
259 raise error.ParseError(_("can't use a list in this context"))
260
260
261 def func(repo, subset, a, b):
261 def func(repo, subset, a, b):
262 if a[0] == 'symbol' and a[1] in symbols:
262 if a[0] == 'symbol' and a[1] in symbols:
263 return symbols[a[1]](repo, subset, b)
263 return symbols[a[1]](repo, subset, b)
264 raise error.ParseError(_("not a function: %s") % a[1])
264 raise error.ParseError(_("not a function: %s") % a[1])
265
265
266 # functions
266 # functions
267
267
268 def adds(repo, subset, x):
268 def adds(repo, subset, x):
269 """``adds(pattern)``
269 """``adds(pattern)``
270 Changesets that add a file matching pattern.
270 Changesets that add a file matching pattern.
271
271
272 The pattern without explicit kind like ``glob:`` is expected to be
272 The pattern without explicit kind like ``glob:`` is expected to be
273 relative to the current directory and match against a file or a
273 relative to the current directory and match against a file or a
274 directory.
274 directory.
275 """
275 """
276 # i18n: "adds" is a keyword
276 # i18n: "adds" is a keyword
277 pat = getstring(x, _("adds requires a pattern"))
277 pat = getstring(x, _("adds requires a pattern"))
278 return checkstatus(repo, subset, pat, 1)
278 return checkstatus(repo, subset, pat, 1)
279
279
280 def ancestor(repo, subset, x):
280 def ancestor(repo, subset, x):
281 """``ancestor(*changeset)``
281 """``ancestor(*changeset)``
282 Greatest common ancestor of the changesets.
282 Greatest common ancestor of the changesets.
283
283
284 Accepts 0 or more changesets.
284 Accepts 0 or more changesets.
285 Will return empty list when passed no args.
285 Will return empty list when passed no args.
286 Greatest common ancestor of a single changeset is that changeset.
286 Greatest common ancestor of a single changeset is that changeset.
287 """
287 """
288 # i18n: "ancestor" is a keyword
288 # i18n: "ancestor" is a keyword
289 l = getlist(x)
289 l = getlist(x)
290 rl = baseset(repo)
290 rl = baseset(repo)
291 anc = None
291 anc = None
292
292
293 # (getset(repo, rl, i) for i in l) generates a list of lists
293 # (getset(repo, rl, i) for i in l) generates a list of lists
294 rev = repo.changelog.rev
294 rev = repo.changelog.rev
295 ancestor = repo.changelog.ancestor
295 ancestor = repo.changelog.ancestor
296 node = repo.changelog.node
296 node = repo.changelog.node
297 for revs in (getset(repo, rl, i) for i in l):
297 for revs in (getset(repo, rl, i) for i in l):
298 for r in revs:
298 for r in revs:
299 if anc is None:
299 if anc is None:
300 anc = r
300 anc = r
301 else:
301 else:
302 anc = rev(ancestor(node(anc), node(r)))
302 anc = rev(ancestor(node(anc), node(r)))
303
303
304 if anc is not None and anc in subset:
304 if anc is not None and anc in subset:
305 return baseset([anc])
305 return baseset([anc])
306 return baseset([])
306 return baseset([])
307
307
308 def _ancestors(repo, subset, x, followfirst=False):
308 def _ancestors(repo, subset, x, followfirst=False):
309 args = getset(repo, baseset(repo), x)
309 args = getset(repo, baseset(repo), x)
310 if not args:
310 if not args:
311 return baseset([])
311 return baseset([])
312 s = set(_revancestors(repo, args, followfirst)) | set(args)
312 s = set(_revancestors(repo, args, followfirst)) | set(args)
313 return baseset([r for r in subset if r in s])
313 return baseset([r for r in subset if r in s])
314
314
315 def ancestors(repo, subset, x):
315 def ancestors(repo, subset, x):
316 """``ancestors(set)``
316 """``ancestors(set)``
317 Changesets that are ancestors of a changeset in set.
317 Changesets that are ancestors of a changeset in set.
318 """
318 """
319 return _ancestors(repo, subset, x)
319 return _ancestors(repo, subset, x)
320
320
321 def _firstancestors(repo, subset, x):
321 def _firstancestors(repo, subset, x):
322 # ``_firstancestors(set)``
322 # ``_firstancestors(set)``
323 # Like ``ancestors(set)`` but follows only the first parents.
323 # Like ``ancestors(set)`` but follows only the first parents.
324 return _ancestors(repo, subset, x, followfirst=True)
324 return _ancestors(repo, subset, x, followfirst=True)
325
325
326 def ancestorspec(repo, subset, x, n):
326 def ancestorspec(repo, subset, x, n):
327 """``set~n``
327 """``set~n``
328 Changesets that are the Nth ancestor (first parents only) of a changeset
328 Changesets that are the Nth ancestor (first parents only) of a changeset
329 in set.
329 in set.
330 """
330 """
331 try:
331 try:
332 n = int(n[1])
332 n = int(n[1])
333 except (TypeError, ValueError):
333 except (TypeError, ValueError):
334 raise error.ParseError(_("~ expects a number"))
334 raise error.ParseError(_("~ expects a number"))
335 ps = set()
335 ps = set()
336 cl = repo.changelog
336 cl = repo.changelog
337 for r in getset(repo, baseset(cl), x):
337 for r in getset(repo, baseset(cl), x):
338 for i in range(n):
338 for i in range(n):
339 r = cl.parentrevs(r)[0]
339 r = cl.parentrevs(r)[0]
340 ps.add(r)
340 ps.add(r)
341 return baseset([r for r in subset if r in ps])
341 return baseset([r for r in subset if r in ps])
342
342
343 def author(repo, subset, x):
343 def author(repo, subset, x):
344 """``author(string)``
344 """``author(string)``
345 Alias for ``user(string)``.
345 Alias for ``user(string)``.
346 """
346 """
347 # i18n: "author" is a keyword
347 # i18n: "author" is a keyword
348 n = encoding.lower(getstring(x, _("author requires a string")))
348 n = encoding.lower(getstring(x, _("author requires a string")))
349 kind, pattern, matcher = _substringmatcher(n)
349 kind, pattern, matcher = _substringmatcher(n)
350 return lazyset(subset, lambda x: matcher(encoding.lower(repo[x].user())))
350 return lazyset(subset, lambda x: matcher(encoding.lower(repo[x].user())))
351
351
352 def bisect(repo, subset, x):
352 def bisect(repo, subset, x):
353 """``bisect(string)``
353 """``bisect(string)``
354 Changesets marked in the specified bisect status:
354 Changesets marked in the specified bisect status:
355
355
356 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
356 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
357 - ``goods``, ``bads`` : csets topologically good/bad
357 - ``goods``, ``bads`` : csets topologically good/bad
358 - ``range`` : csets taking part in the bisection
358 - ``range`` : csets taking part in the bisection
359 - ``pruned`` : csets that are goods, bads or skipped
359 - ``pruned`` : csets that are goods, bads or skipped
360 - ``untested`` : csets whose fate is yet unknown
360 - ``untested`` : csets whose fate is yet unknown
361 - ``ignored`` : csets ignored due to DAG topology
361 - ``ignored`` : csets ignored due to DAG topology
362 - ``current`` : the cset currently being bisected
362 - ``current`` : the cset currently being bisected
363 """
363 """
364 # i18n: "bisect" is a keyword
364 # i18n: "bisect" is a keyword
365 status = getstring(x, _("bisect requires a string")).lower()
365 status = getstring(x, _("bisect requires a string")).lower()
366 state = set(hbisect.get(repo, status))
366 state = set(hbisect.get(repo, status))
367 return baseset([r for r in subset if r in state])
367 return baseset([r for r in subset if r in state])
368
368
369 # Backward-compatibility
369 # Backward-compatibility
370 # - no help entry so that we do not advertise it any more
370 # - no help entry so that we do not advertise it any more
371 def bisected(repo, subset, x):
371 def bisected(repo, subset, x):
372 return bisect(repo, subset, x)
372 return bisect(repo, subset, x)
373
373
374 def bookmark(repo, subset, x):
374 def bookmark(repo, subset, x):
375 """``bookmark([name])``
375 """``bookmark([name])``
376 The named bookmark or all bookmarks.
376 The named bookmark or all bookmarks.
377
377
378 If `name` starts with `re:`, the remainder of the name is treated as
378 If `name` starts with `re:`, the remainder of the name is treated as
379 a regular expression. To match a bookmark that actually starts with `re:`,
379 a regular expression. To match a bookmark that actually starts with `re:`,
380 use the prefix `literal:`.
380 use the prefix `literal:`.
381 """
381 """
382 # i18n: "bookmark" is a keyword
382 # i18n: "bookmark" is a keyword
383 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
383 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
384 if args:
384 if args:
385 bm = getstring(args[0],
385 bm = getstring(args[0],
386 # i18n: "bookmark" is a keyword
386 # i18n: "bookmark" is a keyword
387 _('the argument to bookmark must be a string'))
387 _('the argument to bookmark must be a string'))
388 kind, pattern, matcher = _stringmatcher(bm)
388 kind, pattern, matcher = _stringmatcher(bm)
389 if kind == 'literal':
389 if kind == 'literal':
390 bmrev = repo._bookmarks.get(bm, None)
390 bmrev = repo._bookmarks.get(bm, None)
391 if not bmrev:
391 if not bmrev:
392 raise util.Abort(_("bookmark '%s' does not exist") % bm)
392 raise util.Abort(_("bookmark '%s' does not exist") % bm)
393 bmrev = repo[bmrev].rev()
393 bmrev = repo[bmrev].rev()
394 return lazyset(subset, lambda r: r == bmrev)
394 return lazyset(subset, lambda r: r == bmrev)
395 else:
395 else:
396 matchrevs = set()
396 matchrevs = set()
397 for name, bmrev in repo._bookmarks.iteritems():
397 for name, bmrev in repo._bookmarks.iteritems():
398 if matcher(name):
398 if matcher(name):
399 matchrevs.add(bmrev)
399 matchrevs.add(bmrev)
400 if not matchrevs:
400 if not matchrevs:
401 raise util.Abort(_("no bookmarks exist that match '%s'")
401 raise util.Abort(_("no bookmarks exist that match '%s'")
402 % pattern)
402 % pattern)
403 bmrevs = set()
403 bmrevs = set()
404 for bmrev in matchrevs:
404 for bmrev in matchrevs:
405 bmrevs.add(repo[bmrev].rev())
405 bmrevs.add(repo[bmrev].rev())
406 return subset & bmrevs
406 return subset & bmrevs
407
407
408 bms = set([repo[r].rev()
408 bms = set([repo[r].rev()
409 for r in repo._bookmarks.values()])
409 for r in repo._bookmarks.values()])
410 return lazyset(subset, lambda r: r in bms)
410 return lazyset(subset, lambda r: r in bms)
411
411
412 def branch(repo, subset, x):
412 def branch(repo, subset, x):
413 """``branch(string or set)``
413 """``branch(string or set)``
414 All changesets belonging to the given branch or the branches of the given
414 All changesets belonging to the given branch or the branches of the given
415 changesets.
415 changesets.
416
416
417 If `string` starts with `re:`, the remainder of the name is treated as
417 If `string` starts with `re:`, the remainder of the name is treated as
418 a regular expression. To match a branch that actually starts with `re:`,
418 a regular expression. To match a branch that actually starts with `re:`,
419 use the prefix `literal:`.
419 use the prefix `literal:`.
420 """
420 """
421 try:
421 try:
422 b = getstring(x, '')
422 b = getstring(x, '')
423 except error.ParseError:
423 except error.ParseError:
424 # not a string, but another revspec, e.g. tip()
424 # not a string, but another revspec, e.g. tip()
425 pass
425 pass
426 else:
426 else:
427 kind, pattern, matcher = _stringmatcher(b)
427 kind, pattern, matcher = _stringmatcher(b)
428 if kind == 'literal':
428 if kind == 'literal':
429 # note: falls through to the revspec case if no branch with
429 # note: falls through to the revspec case if no branch with
430 # this name exists
430 # this name exists
431 if pattern in repo.branchmap():
431 if pattern in repo.branchmap():
432 return lazyset(subset, lambda r: matcher(repo[r].branch()))
432 return lazyset(subset, lambda r: matcher(repo[r].branch()))
433 else:
433 else:
434 return lazyset(subset, lambda r: matcher(repo[r].branch()))
434 return lazyset(subset, lambda r: matcher(repo[r].branch()))
435
435
436 s = getset(repo, baseset(repo), x)
436 s = getset(repo, baseset(repo), x)
437 b = set()
437 b = set()
438 for r in s:
438 for r in s:
439 b.add(repo[r].branch())
439 b.add(repo[r].branch())
440 s = s.set()
440 s = s.set()
441 return lazyset(subset, lambda r: r in s or repo[r].branch() in b)
441 return lazyset(subset, lambda r: r in s or repo[r].branch() in b)
442
442
443 def bumped(repo, subset, x):
443 def bumped(repo, subset, x):
444 """``bumped()``
444 """``bumped()``
445 Mutable changesets marked as successors of public changesets.
445 Mutable changesets marked as successors of public changesets.
446
446
447 Only non-public and non-obsolete changesets can be `bumped`.
447 Only non-public and non-obsolete changesets can be `bumped`.
448 """
448 """
449 # i18n: "bumped" is a keyword
449 # i18n: "bumped" is a keyword
450 getargs(x, 0, 0, _("bumped takes no arguments"))
450 getargs(x, 0, 0, _("bumped takes no arguments"))
451 bumped = obsmod.getrevs(repo, 'bumped')
451 bumped = obsmod.getrevs(repo, 'bumped')
452 return subset & bumped
452 return subset & bumped
453
453
454 def bundle(repo, subset, x):
454 def bundle(repo, subset, x):
455 """``bundle()``
455 """``bundle()``
456 Changesets in the bundle.
456 Changesets in the bundle.
457
457
458 Bundle must be specified by the -R option."""
458 Bundle must be specified by the -R option."""
459
459
460 try:
460 try:
461 bundlerevs = repo.changelog.bundlerevs
461 bundlerevs = repo.changelog.bundlerevs
462 except AttributeError:
462 except AttributeError:
463 raise util.Abort(_("no bundle provided - specify with -R"))
463 raise util.Abort(_("no bundle provided - specify with -R"))
464 return subset & bundlerevs
464 return subset & bundlerevs
465
465
466 def checkstatus(repo, subset, pat, field):
466 def checkstatus(repo, subset, pat, field):
467 hasset = matchmod.patkind(pat) == 'set'
467 hasset = matchmod.patkind(pat) == 'set'
468
468
469 def matches(x):
469 def matches(x):
470 m = None
470 m = None
471 fname = None
471 fname = None
472 c = repo[x]
472 c = repo[x]
473 if not m or hasset:
473 if not m or hasset:
474 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
474 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
475 if not m.anypats() and len(m.files()) == 1:
475 if not m.anypats() and len(m.files()) == 1:
476 fname = m.files()[0]
476 fname = m.files()[0]
477 if fname is not None:
477 if fname is not None:
478 if fname not in c.files():
478 if fname not in c.files():
479 return False
479 return False
480 else:
480 else:
481 for f in c.files():
481 for f in c.files():
482 if m(f):
482 if m(f):
483 break
483 break
484 else:
484 else:
485 return False
485 return False
486 files = repo.status(c.p1().node(), c.node())[field]
486 files = repo.status(c.p1().node(), c.node())[field]
487 if fname is not None:
487 if fname is not None:
488 if fname in files:
488 if fname in files:
489 return True
489 return True
490 else:
490 else:
491 for f in files:
491 for f in files:
492 if m(f):
492 if m(f):
493 return True
493 return True
494
494
495 return lazyset(subset, matches)
495 return lazyset(subset, matches)
496
496
497 def _children(repo, narrow, parentset):
497 def _children(repo, narrow, parentset):
498 cs = set()
498 cs = set()
499 if not parentset:
499 if not parentset:
500 return baseset(cs)
500 return baseset(cs)
501 pr = repo.changelog.parentrevs
501 pr = repo.changelog.parentrevs
502 minrev = min(parentset)
502 minrev = min(parentset)
503 for r in narrow:
503 for r in narrow:
504 if r <= minrev:
504 if r <= minrev:
505 continue
505 continue
506 for p in pr(r):
506 for p in pr(r):
507 if p in parentset:
507 if p in parentset:
508 cs.add(r)
508 cs.add(r)
509 return baseset(cs)
509 return baseset(cs)
510
510
511 def children(repo, subset, x):
511 def children(repo, subset, x):
512 """``children(set)``
512 """``children(set)``
513 Child changesets of changesets in set.
513 Child changesets of changesets in set.
514 """
514 """
515 s = getset(repo, baseset(repo), x).set()
515 s = getset(repo, baseset(repo), x).set()
516 cs = _children(repo, subset, s)
516 cs = _children(repo, subset, s)
517 return subset & cs
517 return subset & cs
518
518
519 def closed(repo, subset, x):
519 def closed(repo, subset, x):
520 """``closed()``
520 """``closed()``
521 Changeset is closed.
521 Changeset is closed.
522 """
522 """
523 # i18n: "closed" is a keyword
523 # i18n: "closed" is a keyword
524 getargs(x, 0, 0, _("closed takes no arguments"))
524 getargs(x, 0, 0, _("closed takes no arguments"))
525 return baseset([r for r in subset if repo[r].closesbranch()])
525 return baseset([r for r in subset if repo[r].closesbranch()])
526
526
527 def contains(repo, subset, x):
527 def contains(repo, subset, x):
528 """``contains(pattern)``
528 """``contains(pattern)``
529 Revision contains a file matching pattern. See :hg:`help patterns`
529 Revision contains a file matching pattern. See :hg:`help patterns`
530 for information about file patterns.
530 for information about file patterns.
531
531
532 The pattern without explicit kind like ``glob:`` is expected to be
532 The pattern without explicit kind like ``glob:`` is expected to be
533 relative to the current directory and match against a file exactly
533 relative to the current directory and match against a file exactly
534 for efficiency.
534 for efficiency.
535 """
535 """
536 # i18n: "contains" is a keyword
536 # i18n: "contains" is a keyword
537 pat = getstring(x, _("contains requires a pattern"))
537 pat = getstring(x, _("contains requires a pattern"))
538 s = []
538
539 if not matchmod.patkind(pat):
539 def matches(x):
540 pat = pathutil.canonpath(repo.root, repo.getcwd(), pat)
540 if not matchmod.patkind(pat):
541 for r in subset:
541 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
542 if pat in repo[r]:
542 if pats in repo[x]:
543 s.append(r)
543 return True
544 else:
544 else:
545 m = None
545 c = repo[x]
546 for r in subset:
546 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
547 c = repo[r]
548 if not m or matchmod.patkind(pat) == 'set':
549 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
550 for f in c.manifest():
547 for f in c.manifest():
551 if m(f):
548 if m(f):
552 s.append(r)
549 return True
553 break
550 return False
554 return baseset(s)
551
552 return lazyset(subset, matches)
555
553
556 def converted(repo, subset, x):
554 def converted(repo, subset, x):
557 """``converted([id])``
555 """``converted([id])``
558 Changesets converted from the given identifier in the old repository if
556 Changesets converted from the given identifier in the old repository if
559 present, or all converted changesets if no identifier is specified.
557 present, or all converted changesets if no identifier is specified.
560 """
558 """
561
559
562 # There is exactly no chance of resolving the revision, so do a simple
560 # There is exactly no chance of resolving the revision, so do a simple
563 # string compare and hope for the best
561 # string compare and hope for the best
564
562
565 rev = None
563 rev = None
566 # i18n: "converted" is a keyword
564 # i18n: "converted" is a keyword
567 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
565 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
568 if l:
566 if l:
569 # i18n: "converted" is a keyword
567 # i18n: "converted" is a keyword
570 rev = getstring(l[0], _('converted requires a revision'))
568 rev = getstring(l[0], _('converted requires a revision'))
571
569
572 def _matchvalue(r):
570 def _matchvalue(r):
573 source = repo[r].extra().get('convert_revision', None)
571 source = repo[r].extra().get('convert_revision', None)
574 return source is not None and (rev is None or source.startswith(rev))
572 return source is not None and (rev is None or source.startswith(rev))
575
573
576 return baseset([r for r in subset if _matchvalue(r)])
574 return baseset([r for r in subset if _matchvalue(r)])
577
575
578 def date(repo, subset, x):
576 def date(repo, subset, x):
579 """``date(interval)``
577 """``date(interval)``
580 Changesets within the interval, see :hg:`help dates`.
578 Changesets within the interval, see :hg:`help dates`.
581 """
579 """
582 # i18n: "date" is a keyword
580 # i18n: "date" is a keyword
583 ds = getstring(x, _("date requires a string"))
581 ds = getstring(x, _("date requires a string"))
584 dm = util.matchdate(ds)
582 dm = util.matchdate(ds)
585 return lazyset(subset, lambda x: dm(repo[x].date()[0]))
583 return lazyset(subset, lambda x: dm(repo[x].date()[0]))
586
584
587 def desc(repo, subset, x):
585 def desc(repo, subset, x):
588 """``desc(string)``
586 """``desc(string)``
589 Search commit message for string. The match is case-insensitive.
587 Search commit message for string. The match is case-insensitive.
590 """
588 """
591 # i18n: "desc" is a keyword
589 # i18n: "desc" is a keyword
592 ds = encoding.lower(getstring(x, _("desc requires a string")))
590 ds = encoding.lower(getstring(x, _("desc requires a string")))
593
591
594 def matches(x):
592 def matches(x):
595 c = repo[x]
593 c = repo[x]
596 return ds in encoding.lower(c.description())
594 return ds in encoding.lower(c.description())
597
595
598 return lazyset(subset, matches)
596 return lazyset(subset, matches)
599
597
600 def _descendants(repo, subset, x, followfirst=False):
598 def _descendants(repo, subset, x, followfirst=False):
601 args = getset(repo, baseset(repo), x)
599 args = getset(repo, baseset(repo), x)
602 if not args:
600 if not args:
603 return baseset([])
601 return baseset([])
604 s = set(_revdescendants(repo, args, followfirst)) | set(args)
602 s = set(_revdescendants(repo, args, followfirst)) | set(args)
605 return baseset([r for r in subset if r in s])
603 return baseset([r for r in subset if r in s])
606
604
607 def descendants(repo, subset, x):
605 def descendants(repo, subset, x):
608 """``descendants(set)``
606 """``descendants(set)``
609 Changesets which are descendants of changesets in set.
607 Changesets which are descendants of changesets in set.
610 """
608 """
611 return _descendants(repo, subset, x)
609 return _descendants(repo, subset, x)
612
610
613 def _firstdescendants(repo, subset, x):
611 def _firstdescendants(repo, subset, x):
614 # ``_firstdescendants(set)``
612 # ``_firstdescendants(set)``
615 # Like ``descendants(set)`` but follows only the first parents.
613 # Like ``descendants(set)`` but follows only the first parents.
616 return _descendants(repo, subset, x, followfirst=True)
614 return _descendants(repo, subset, x, followfirst=True)
617
615
618 def destination(repo, subset, x):
616 def destination(repo, subset, x):
619 """``destination([set])``
617 """``destination([set])``
620 Changesets that were created by a graft, transplant or rebase operation,
618 Changesets that were created by a graft, transplant or rebase operation,
621 with the given revisions specified as the source. Omitting the optional set
619 with the given revisions specified as the source. Omitting the optional set
622 is the same as passing all().
620 is the same as passing all().
623 """
621 """
624 if x is not None:
622 if x is not None:
625 args = getset(repo, baseset(repo), x).set()
623 args = getset(repo, baseset(repo), x).set()
626 else:
624 else:
627 args = getall(repo, baseset(repo), x).set()
625 args = getall(repo, baseset(repo), x).set()
628
626
629 dests = set()
627 dests = set()
630
628
631 # subset contains all of the possible destinations that can be returned, so
629 # subset contains all of the possible destinations that can be returned, so
632 # iterate over them and see if their source(s) were provided in the args.
630 # iterate over them and see if their source(s) were provided in the args.
633 # Even if the immediate src of r is not in the args, src's source (or
631 # Even if the immediate src of r is not in the args, src's source (or
634 # further back) may be. Scanning back further than the immediate src allows
632 # further back) may be. Scanning back further than the immediate src allows
635 # transitive transplants and rebases to yield the same results as transitive
633 # transitive transplants and rebases to yield the same results as transitive
636 # grafts.
634 # grafts.
637 for r in subset:
635 for r in subset:
638 src = _getrevsource(repo, r)
636 src = _getrevsource(repo, r)
639 lineage = None
637 lineage = None
640
638
641 while src is not None:
639 while src is not None:
642 if lineage is None:
640 if lineage is None:
643 lineage = list()
641 lineage = list()
644
642
645 lineage.append(r)
643 lineage.append(r)
646
644
647 # The visited lineage is a match if the current source is in the arg
645 # The visited lineage is a match if the current source is in the arg
648 # set. Since every candidate dest is visited by way of iterating
646 # set. Since every candidate dest is visited by way of iterating
649 # subset, any dests further back in the lineage will be tested by a
647 # subset, any dests further back in the lineage will be tested by a
650 # different iteration over subset. Likewise, if the src was already
648 # different iteration over subset. Likewise, if the src was already
651 # selected, the current lineage can be selected without going back
649 # selected, the current lineage can be selected without going back
652 # further.
650 # further.
653 if src in args or src in dests:
651 if src in args or src in dests:
654 dests.update(lineage)
652 dests.update(lineage)
655 break
653 break
656
654
657 r = src
655 r = src
658 src = _getrevsource(repo, r)
656 src = _getrevsource(repo, r)
659
657
660 return baseset([r for r in subset if r in dests])
658 return baseset([r for r in subset if r in dests])
661
659
662 def divergent(repo, subset, x):
660 def divergent(repo, subset, x):
663 """``divergent()``
661 """``divergent()``
664 Final successors of changesets with an alternative set of final successors.
662 Final successors of changesets with an alternative set of final successors.
665 """
663 """
666 # i18n: "divergent" is a keyword
664 # i18n: "divergent" is a keyword
667 getargs(x, 0, 0, _("divergent takes no arguments"))
665 getargs(x, 0, 0, _("divergent takes no arguments"))
668 divergent = obsmod.getrevs(repo, 'divergent')
666 divergent = obsmod.getrevs(repo, 'divergent')
669 return baseset([r for r in subset if r in divergent])
667 return baseset([r for r in subset if r in divergent])
670
668
671 def draft(repo, subset, x):
669 def draft(repo, subset, x):
672 """``draft()``
670 """``draft()``
673 Changeset in draft phase."""
671 Changeset in draft phase."""
674 # i18n: "draft" is a keyword
672 # i18n: "draft" is a keyword
675 getargs(x, 0, 0, _("draft takes no arguments"))
673 getargs(x, 0, 0, _("draft takes no arguments"))
676 pc = repo._phasecache
674 pc = repo._phasecache
677 return lazyset(subset, lambda r: pc.phase(repo, r) == phases.draft)
675 return lazyset(subset, lambda r: pc.phase(repo, r) == phases.draft)
678
676
679 def extinct(repo, subset, x):
677 def extinct(repo, subset, x):
680 """``extinct()``
678 """``extinct()``
681 Obsolete changesets with obsolete descendants only.
679 Obsolete changesets with obsolete descendants only.
682 """
680 """
683 # i18n: "extinct" is a keyword
681 # i18n: "extinct" is a keyword
684 getargs(x, 0, 0, _("extinct takes no arguments"))
682 getargs(x, 0, 0, _("extinct takes no arguments"))
685 extincts = obsmod.getrevs(repo, 'extinct')
683 extincts = obsmod.getrevs(repo, 'extinct')
686 return subset & extincts
684 return subset & extincts
687
685
688 def extra(repo, subset, x):
686 def extra(repo, subset, x):
689 """``extra(label, [value])``
687 """``extra(label, [value])``
690 Changesets with the given label in the extra metadata, with the given
688 Changesets with the given label in the extra metadata, with the given
691 optional value.
689 optional value.
692
690
693 If `value` starts with `re:`, the remainder of the value is treated as
691 If `value` starts with `re:`, the remainder of the value is treated as
694 a regular expression. To match a value that actually starts with `re:`,
692 a regular expression. To match a value that actually starts with `re:`,
695 use the prefix `literal:`.
693 use the prefix `literal:`.
696 """
694 """
697
695
698 # i18n: "extra" is a keyword
696 # i18n: "extra" is a keyword
699 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
697 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
700 # i18n: "extra" is a keyword
698 # i18n: "extra" is a keyword
701 label = getstring(l[0], _('first argument to extra must be a string'))
699 label = getstring(l[0], _('first argument to extra must be a string'))
702 value = None
700 value = None
703
701
704 if len(l) > 1:
702 if len(l) > 1:
705 # i18n: "extra" is a keyword
703 # i18n: "extra" is a keyword
706 value = getstring(l[1], _('second argument to extra must be a string'))
704 value = getstring(l[1], _('second argument to extra must be a string'))
707 kind, value, matcher = _stringmatcher(value)
705 kind, value, matcher = _stringmatcher(value)
708
706
709 def _matchvalue(r):
707 def _matchvalue(r):
710 extra = repo[r].extra()
708 extra = repo[r].extra()
711 return label in extra and (value is None or matcher(extra[label]))
709 return label in extra and (value is None or matcher(extra[label]))
712
710
713 return baseset([r for r in subset if _matchvalue(r)])
711 return baseset([r for r in subset if _matchvalue(r)])
714
712
715 def filelog(repo, subset, x):
713 def filelog(repo, subset, x):
716 """``filelog(pattern)``
714 """``filelog(pattern)``
717 Changesets connected to the specified filelog.
715 Changesets connected to the specified filelog.
718
716
719 For performance reasons, ``filelog()`` does not show every changeset
717 For performance reasons, ``filelog()`` does not show every changeset
720 that affects the requested file(s). See :hg:`help log` for details. For
718 that affects the requested file(s). See :hg:`help log` for details. For
721 a slower, more accurate result, use ``file()``.
719 a slower, more accurate result, use ``file()``.
722
720
723 The pattern without explicit kind like ``glob:`` is expected to be
721 The pattern without explicit kind like ``glob:`` is expected to be
724 relative to the current directory and match against a file exactly
722 relative to the current directory and match against a file exactly
725 for efficiency.
723 for efficiency.
726 """
724 """
727
725
728 # i18n: "filelog" is a keyword
726 # i18n: "filelog" is a keyword
729 pat = getstring(x, _("filelog requires a pattern"))
727 pat = getstring(x, _("filelog requires a pattern"))
730 s = set()
728 s = set()
731
729
732 if not matchmod.patkind(pat):
730 if not matchmod.patkind(pat):
733 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
731 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
734 fl = repo.file(f)
732 fl = repo.file(f)
735 for fr in fl:
733 for fr in fl:
736 s.add(fl.linkrev(fr))
734 s.add(fl.linkrev(fr))
737 else:
735 else:
738 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
736 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
739 for f in repo[None]:
737 for f in repo[None]:
740 if m(f):
738 if m(f):
741 fl = repo.file(f)
739 fl = repo.file(f)
742 for fr in fl:
740 for fr in fl:
743 s.add(fl.linkrev(fr))
741 s.add(fl.linkrev(fr))
744
742
745 return baseset([r for r in subset if r in s])
743 return baseset([r for r in subset if r in s])
746
744
747 def first(repo, subset, x):
745 def first(repo, subset, x):
748 """``first(set, [n])``
746 """``first(set, [n])``
749 An alias for limit().
747 An alias for limit().
750 """
748 """
751 return limit(repo, subset, x)
749 return limit(repo, subset, x)
752
750
753 def _follow(repo, subset, x, name, followfirst=False):
751 def _follow(repo, subset, x, name, followfirst=False):
754 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
752 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
755 c = repo['.']
753 c = repo['.']
756 if l:
754 if l:
757 x = getstring(l[0], _("%s expected a filename") % name)
755 x = getstring(l[0], _("%s expected a filename") % name)
758 if x in c:
756 if x in c:
759 cx = c[x]
757 cx = c[x]
760 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
758 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
761 # include the revision responsible for the most recent version
759 # include the revision responsible for the most recent version
762 s.add(cx.linkrev())
760 s.add(cx.linkrev())
763 else:
761 else:
764 return baseset([])
762 return baseset([])
765 else:
763 else:
766 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
764 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
767
765
768 return baseset([r for r in subset if r in s])
766 return baseset([r for r in subset if r in s])
769
767
770 def follow(repo, subset, x):
768 def follow(repo, subset, x):
771 """``follow([file])``
769 """``follow([file])``
772 An alias for ``::.`` (ancestors of the working copy's first parent).
770 An alias for ``::.`` (ancestors of the working copy's first parent).
773 If a filename is specified, the history of the given file is followed,
771 If a filename is specified, the history of the given file is followed,
774 including copies.
772 including copies.
775 """
773 """
776 return _follow(repo, subset, x, 'follow')
774 return _follow(repo, subset, x, 'follow')
777
775
778 def _followfirst(repo, subset, x):
776 def _followfirst(repo, subset, x):
779 # ``followfirst([file])``
777 # ``followfirst([file])``
780 # Like ``follow([file])`` but follows only the first parent of
778 # Like ``follow([file])`` but follows only the first parent of
781 # every revision or file revision.
779 # every revision or file revision.
782 return _follow(repo, subset, x, '_followfirst', followfirst=True)
780 return _follow(repo, subset, x, '_followfirst', followfirst=True)
783
781
784 def getall(repo, subset, x):
782 def getall(repo, subset, x):
785 """``all()``
783 """``all()``
786 All changesets, the same as ``0:tip``.
784 All changesets, the same as ``0:tip``.
787 """
785 """
788 # i18n: "all" is a keyword
786 # i18n: "all" is a keyword
789 getargs(x, 0, 0, _("all takes no arguments"))
787 getargs(x, 0, 0, _("all takes no arguments"))
790 return subset
788 return subset
791
789
792 def grep(repo, subset, x):
790 def grep(repo, subset, x):
793 """``grep(regex)``
791 """``grep(regex)``
794 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
792 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
795 to ensure special escape characters are handled correctly. Unlike
793 to ensure special escape characters are handled correctly. Unlike
796 ``keyword(string)``, the match is case-sensitive.
794 ``keyword(string)``, the match is case-sensitive.
797 """
795 """
798 try:
796 try:
799 # i18n: "grep" is a keyword
797 # i18n: "grep" is a keyword
800 gr = re.compile(getstring(x, _("grep requires a string")))
798 gr = re.compile(getstring(x, _("grep requires a string")))
801 except re.error, e:
799 except re.error, e:
802 raise error.ParseError(_('invalid match pattern: %s') % e)
800 raise error.ParseError(_('invalid match pattern: %s') % e)
803
801
804 def matches(x):
802 def matches(x):
805 c = repo[x]
803 c = repo[x]
806 for e in c.files() + [c.user(), c.description()]:
804 for e in c.files() + [c.user(), c.description()]:
807 if gr.search(e):
805 if gr.search(e):
808 return True
806 return True
809 return False
807 return False
810
808
811 return lazyset(subset, matches)
809 return lazyset(subset, matches)
812
810
813 def _matchfiles(repo, subset, x):
811 def _matchfiles(repo, subset, x):
814 # _matchfiles takes a revset list of prefixed arguments:
812 # _matchfiles takes a revset list of prefixed arguments:
815 #
813 #
816 # [p:foo, i:bar, x:baz]
814 # [p:foo, i:bar, x:baz]
817 #
815 #
818 # builds a match object from them and filters subset. Allowed
816 # builds a match object from them and filters subset. Allowed
819 # prefixes are 'p:' for regular patterns, 'i:' for include
817 # prefixes are 'p:' for regular patterns, 'i:' for include
820 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
818 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
821 # a revision identifier, or the empty string to reference the
819 # a revision identifier, or the empty string to reference the
822 # working directory, from which the match object is
820 # working directory, from which the match object is
823 # initialized. Use 'd:' to set the default matching mode, default
821 # initialized. Use 'd:' to set the default matching mode, default
824 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
822 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
825
823
826 # i18n: "_matchfiles" is a keyword
824 # i18n: "_matchfiles" is a keyword
827 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
825 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
828 pats, inc, exc = [], [], []
826 pats, inc, exc = [], [], []
829 hasset = False
827 hasset = False
830 rev, default = None, None
828 rev, default = None, None
831 for arg in l:
829 for arg in l:
832 # i18n: "_matchfiles" is a keyword
830 # i18n: "_matchfiles" is a keyword
833 s = getstring(arg, _("_matchfiles requires string arguments"))
831 s = getstring(arg, _("_matchfiles requires string arguments"))
834 prefix, value = s[:2], s[2:]
832 prefix, value = s[:2], s[2:]
835 if prefix == 'p:':
833 if prefix == 'p:':
836 pats.append(value)
834 pats.append(value)
837 elif prefix == 'i:':
835 elif prefix == 'i:':
838 inc.append(value)
836 inc.append(value)
839 elif prefix == 'x:':
837 elif prefix == 'x:':
840 exc.append(value)
838 exc.append(value)
841 elif prefix == 'r:':
839 elif prefix == 'r:':
842 if rev is not None:
840 if rev is not None:
843 # i18n: "_matchfiles" is a keyword
841 # i18n: "_matchfiles" is a keyword
844 raise error.ParseError(_('_matchfiles expected at most one '
842 raise error.ParseError(_('_matchfiles expected at most one '
845 'revision'))
843 'revision'))
846 rev = value
844 rev = value
847 elif prefix == 'd:':
845 elif prefix == 'd:':
848 if default is not None:
846 if default is not None:
849 # i18n: "_matchfiles" is a keyword
847 # i18n: "_matchfiles" is a keyword
850 raise error.ParseError(_('_matchfiles expected at most one '
848 raise error.ParseError(_('_matchfiles expected at most one '
851 'default mode'))
849 'default mode'))
852 default = value
850 default = value
853 else:
851 else:
854 # i18n: "_matchfiles" is a keyword
852 # i18n: "_matchfiles" is a keyword
855 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
853 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
856 if not hasset and matchmod.patkind(value) == 'set':
854 if not hasset and matchmod.patkind(value) == 'set':
857 hasset = True
855 hasset = True
858 if not default:
856 if not default:
859 default = 'glob'
857 default = 'glob'
860
858
861 def matches(x):
859 def matches(x):
862 m = None
860 m = None
863 c = repo[x]
861 c = repo[x]
864 if not m or (hasset and rev is None):
862 if not m or (hasset and rev is None):
865 ctx = c
863 ctx = c
866 if rev is not None:
864 if rev is not None:
867 ctx = repo[rev or None]
865 ctx = repo[rev or None]
868 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
866 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
869 exclude=exc, ctx=ctx, default=default)
867 exclude=exc, ctx=ctx, default=default)
870 for f in c.files():
868 for f in c.files():
871 if m(f):
869 if m(f):
872 return True
870 return True
873 return False
871 return False
874
872
875 return lazyset(subset, matches)
873 return lazyset(subset, matches)
876
874
877 def hasfile(repo, subset, x):
875 def hasfile(repo, subset, x):
878 """``file(pattern)``
876 """``file(pattern)``
879 Changesets affecting files matched by pattern.
877 Changesets affecting files matched by pattern.
880
878
881 For a faster but less accurate result, consider using ``filelog()``
879 For a faster but less accurate result, consider using ``filelog()``
882 instead.
880 instead.
883
881
884 This predicate uses ``glob:`` as the default kind of pattern.
882 This predicate uses ``glob:`` as the default kind of pattern.
885 """
883 """
886 # i18n: "file" is a keyword
884 # i18n: "file" is a keyword
887 pat = getstring(x, _("file requires a pattern"))
885 pat = getstring(x, _("file requires a pattern"))
888 return _matchfiles(repo, subset, ('string', 'p:' + pat))
886 return _matchfiles(repo, subset, ('string', 'p:' + pat))
889
887
890 def head(repo, subset, x):
888 def head(repo, subset, x):
891 """``head()``
889 """``head()``
892 Changeset is a named branch head.
890 Changeset is a named branch head.
893 """
891 """
894 # i18n: "head" is a keyword
892 # i18n: "head" is a keyword
895 getargs(x, 0, 0, _("head takes no arguments"))
893 getargs(x, 0, 0, _("head takes no arguments"))
896 hs = set()
894 hs = set()
897 for b, ls in repo.branchmap().iteritems():
895 for b, ls in repo.branchmap().iteritems():
898 hs.update(repo[h].rev() for h in ls)
896 hs.update(repo[h].rev() for h in ls)
899 return baseset([r for r in subset if r in hs])
897 return baseset([r for r in subset if r in hs])
900
898
901 def heads(repo, subset, x):
899 def heads(repo, subset, x):
902 """``heads(set)``
900 """``heads(set)``
903 Members of set with no children in set.
901 Members of set with no children in set.
904 """
902 """
905 s = getset(repo, subset, x)
903 s = getset(repo, subset, x)
906 ps = parents(repo, subset, x)
904 ps = parents(repo, subset, x)
907 return s - ps
905 return s - ps
908
906
909 def hidden(repo, subset, x):
907 def hidden(repo, subset, x):
910 """``hidden()``
908 """``hidden()``
911 Hidden changesets.
909 Hidden changesets.
912 """
910 """
913 # i18n: "hidden" is a keyword
911 # i18n: "hidden" is a keyword
914 getargs(x, 0, 0, _("hidden takes no arguments"))
912 getargs(x, 0, 0, _("hidden takes no arguments"))
915 hiddenrevs = repoview.filterrevs(repo, 'visible')
913 hiddenrevs = repoview.filterrevs(repo, 'visible')
916 return subset & hiddenrevs
914 return subset & hiddenrevs
917
915
918 def keyword(repo, subset, x):
916 def keyword(repo, subset, x):
919 """``keyword(string)``
917 """``keyword(string)``
920 Search commit message, user name, and names of changed files for
918 Search commit message, user name, and names of changed files for
921 string. The match is case-insensitive.
919 string. The match is case-insensitive.
922 """
920 """
923 # i18n: "keyword" is a keyword
921 # i18n: "keyword" is a keyword
924 kw = encoding.lower(getstring(x, _("keyword requires a string")))
922 kw = encoding.lower(getstring(x, _("keyword requires a string")))
925
923
926 def matches(r):
924 def matches(r):
927 c = repo[r]
925 c = repo[r]
928 return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
926 return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
929 c.description()])
927 c.description()])
930
928
931 return lazyset(subset, matches)
929 return lazyset(subset, matches)
932
930
933 def limit(repo, subset, x):
931 def limit(repo, subset, x):
934 """``limit(set, [n])``
932 """``limit(set, [n])``
935 First n members of set, defaulting to 1.
933 First n members of set, defaulting to 1.
936 """
934 """
937 # i18n: "limit" is a keyword
935 # i18n: "limit" is a keyword
938 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
936 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
939 try:
937 try:
940 lim = 1
938 lim = 1
941 if len(l) == 2:
939 if len(l) == 2:
942 # i18n: "limit" is a keyword
940 # i18n: "limit" is a keyword
943 lim = int(getstring(l[1], _("limit requires a number")))
941 lim = int(getstring(l[1], _("limit requires a number")))
944 except (TypeError, ValueError):
942 except (TypeError, ValueError):
945 # i18n: "limit" is a keyword
943 # i18n: "limit" is a keyword
946 raise error.ParseError(_("limit expects a number"))
944 raise error.ParseError(_("limit expects a number"))
947 ss = subset.set()
945 ss = subset.set()
948 os = getset(repo, baseset(repo), l[0])
946 os = getset(repo, baseset(repo), l[0])
949 bs = baseset([])
947 bs = baseset([])
950 it = iter(os)
948 it = iter(os)
951 for x in xrange(lim):
949 for x in xrange(lim):
952 try:
950 try:
953 y = it.next()
951 y = it.next()
954 if y in ss:
952 if y in ss:
955 bs.append(y)
953 bs.append(y)
956 except (StopIteration):
954 except (StopIteration):
957 break
955 break
958 return bs
956 return bs
959
957
960 def last(repo, subset, x):
958 def last(repo, subset, x):
961 """``last(set, [n])``
959 """``last(set, [n])``
962 Last n members of set, defaulting to 1.
960 Last n members of set, defaulting to 1.
963 """
961 """
964 # i18n: "last" is a keyword
962 # i18n: "last" is a keyword
965 l = getargs(x, 1, 2, _("last requires one or two arguments"))
963 l = getargs(x, 1, 2, _("last requires one or two arguments"))
966 try:
964 try:
967 lim = 1
965 lim = 1
968 if len(l) == 2:
966 if len(l) == 2:
969 # i18n: "last" is a keyword
967 # i18n: "last" is a keyword
970 lim = int(getstring(l[1], _("last requires a number")))
968 lim = int(getstring(l[1], _("last requires a number")))
971 except (TypeError, ValueError):
969 except (TypeError, ValueError):
972 # i18n: "last" is a keyword
970 # i18n: "last" is a keyword
973 raise error.ParseError(_("last expects a number"))
971 raise error.ParseError(_("last expects a number"))
974 ss = subset.set()
972 ss = subset.set()
975 os = getset(repo, baseset(repo), l[0])[-lim:]
973 os = getset(repo, baseset(repo), l[0])[-lim:]
976 return baseset([r for r in os if r in ss])
974 return baseset([r for r in os if r in ss])
977
975
978 def maxrev(repo, subset, x):
976 def maxrev(repo, subset, x):
979 """``max(set)``
977 """``max(set)``
980 Changeset with highest revision number in set.
978 Changeset with highest revision number in set.
981 """
979 """
982 os = getset(repo, baseset(repo), x)
980 os = getset(repo, baseset(repo), x)
983 if os:
981 if os:
984 m = max(os)
982 m = max(os)
985 if m in subset:
983 if m in subset:
986 return baseset([m])
984 return baseset([m])
987 return baseset([])
985 return baseset([])
988
986
989 def merge(repo, subset, x):
987 def merge(repo, subset, x):
990 """``merge()``
988 """``merge()``
991 Changeset is a merge changeset.
989 Changeset is a merge changeset.
992 """
990 """
993 # i18n: "merge" is a keyword
991 # i18n: "merge" is a keyword
994 getargs(x, 0, 0, _("merge takes no arguments"))
992 getargs(x, 0, 0, _("merge takes no arguments"))
995 cl = repo.changelog
993 cl = repo.changelog
996 return lazyset(subset, lambda r: cl.parentrevs(r)[1] != -1)
994 return lazyset(subset, lambda r: cl.parentrevs(r)[1] != -1)
997
995
998 def branchpoint(repo, subset, x):
996 def branchpoint(repo, subset, x):
999 """``branchpoint()``
997 """``branchpoint()``
1000 Changesets with more than one child.
998 Changesets with more than one child.
1001 """
999 """
1002 # i18n: "branchpoint" is a keyword
1000 # i18n: "branchpoint" is a keyword
1003 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1001 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1004 cl = repo.changelog
1002 cl = repo.changelog
1005 if not subset:
1003 if not subset:
1006 return baseset([])
1004 return baseset([])
1007 baserev = min(subset)
1005 baserev = min(subset)
1008 parentscount = [0]*(len(repo) - baserev)
1006 parentscount = [0]*(len(repo) - baserev)
1009 for r in cl.revs(start=baserev + 1):
1007 for r in cl.revs(start=baserev + 1):
1010 for p in cl.parentrevs(r):
1008 for p in cl.parentrevs(r):
1011 if p >= baserev:
1009 if p >= baserev:
1012 parentscount[p - baserev] += 1
1010 parentscount[p - baserev] += 1
1013 return baseset([r for r in subset if (parentscount[r - baserev] > 1)])
1011 return baseset([r for r in subset if (parentscount[r - baserev] > 1)])
1014
1012
1015 def minrev(repo, subset, x):
1013 def minrev(repo, subset, x):
1016 """``min(set)``
1014 """``min(set)``
1017 Changeset with lowest revision number in set.
1015 Changeset with lowest revision number in set.
1018 """
1016 """
1019 os = getset(repo, baseset(repo), x)
1017 os = getset(repo, baseset(repo), x)
1020 if os:
1018 if os:
1021 m = min(os)
1019 m = min(os)
1022 if m in subset:
1020 if m in subset:
1023 return baseset([m])
1021 return baseset([m])
1024 return baseset([])
1022 return baseset([])
1025
1023
1026 def modifies(repo, subset, x):
1024 def modifies(repo, subset, x):
1027 """``modifies(pattern)``
1025 """``modifies(pattern)``
1028 Changesets modifying files matched by pattern.
1026 Changesets modifying files matched by pattern.
1029
1027
1030 The pattern without explicit kind like ``glob:`` is expected to be
1028 The pattern without explicit kind like ``glob:`` is expected to be
1031 relative to the current directory and match against a file or a
1029 relative to the current directory and match against a file or a
1032 directory.
1030 directory.
1033 """
1031 """
1034 # i18n: "modifies" is a keyword
1032 # i18n: "modifies" is a keyword
1035 pat = getstring(x, _("modifies requires a pattern"))
1033 pat = getstring(x, _("modifies requires a pattern"))
1036 return checkstatus(repo, subset, pat, 0)
1034 return checkstatus(repo, subset, pat, 0)
1037
1035
1038 def node_(repo, subset, x):
1036 def node_(repo, subset, x):
1039 """``id(string)``
1037 """``id(string)``
1040 Revision non-ambiguously specified by the given hex string prefix.
1038 Revision non-ambiguously specified by the given hex string prefix.
1041 """
1039 """
1042 # i18n: "id" is a keyword
1040 # i18n: "id" is a keyword
1043 l = getargs(x, 1, 1, _("id requires one argument"))
1041 l = getargs(x, 1, 1, _("id requires one argument"))
1044 # i18n: "id" is a keyword
1042 # i18n: "id" is a keyword
1045 n = getstring(l[0], _("id requires a string"))
1043 n = getstring(l[0], _("id requires a string"))
1046 if len(n) == 40:
1044 if len(n) == 40:
1047 rn = repo[n].rev()
1045 rn = repo[n].rev()
1048 else:
1046 else:
1049 rn = None
1047 rn = None
1050 pm = repo.changelog._partialmatch(n)
1048 pm = repo.changelog._partialmatch(n)
1051 if pm is not None:
1049 if pm is not None:
1052 rn = repo.changelog.rev(pm)
1050 rn = repo.changelog.rev(pm)
1053
1051
1054 return baseset([r for r in subset if r == rn])
1052 return baseset([r for r in subset if r == rn])
1055
1053
1056 def obsolete(repo, subset, x):
1054 def obsolete(repo, subset, x):
1057 """``obsolete()``
1055 """``obsolete()``
1058 Mutable changeset with a newer version."""
1056 Mutable changeset with a newer version."""
1059 # i18n: "obsolete" is a keyword
1057 # i18n: "obsolete" is a keyword
1060 getargs(x, 0, 0, _("obsolete takes no arguments"))
1058 getargs(x, 0, 0, _("obsolete takes no arguments"))
1061 obsoletes = obsmod.getrevs(repo, 'obsolete')
1059 obsoletes = obsmod.getrevs(repo, 'obsolete')
1062 return subset & obsoletes
1060 return subset & obsoletes
1063
1061
1064 def origin(repo, subset, x):
1062 def origin(repo, subset, x):
1065 """``origin([set])``
1063 """``origin([set])``
1066 Changesets that were specified as a source for the grafts, transplants or
1064 Changesets that were specified as a source for the grafts, transplants or
1067 rebases that created the given revisions. Omitting the optional set is the
1065 rebases that created the given revisions. Omitting the optional set is the
1068 same as passing all(). If a changeset created by these operations is itself
1066 same as passing all(). If a changeset created by these operations is itself
1069 specified as a source for one of these operations, only the source changeset
1067 specified as a source for one of these operations, only the source changeset
1070 for the first operation is selected.
1068 for the first operation is selected.
1071 """
1069 """
1072 if x is not None:
1070 if x is not None:
1073 args = getset(repo, baseset(repo), x).set()
1071 args = getset(repo, baseset(repo), x).set()
1074 else:
1072 else:
1075 args = getall(repo, baseset(repo), x).set()
1073 args = getall(repo, baseset(repo), x).set()
1076
1074
1077 def _firstsrc(rev):
1075 def _firstsrc(rev):
1078 src = _getrevsource(repo, rev)
1076 src = _getrevsource(repo, rev)
1079 if src is None:
1077 if src is None:
1080 return None
1078 return None
1081
1079
1082 while True:
1080 while True:
1083 prev = _getrevsource(repo, src)
1081 prev = _getrevsource(repo, src)
1084
1082
1085 if prev is None:
1083 if prev is None:
1086 return src
1084 return src
1087 src = prev
1085 src = prev
1088
1086
1089 o = set([_firstsrc(r) for r in args])
1087 o = set([_firstsrc(r) for r in args])
1090 return baseset([r for r in subset if r in o])
1088 return baseset([r for r in subset if r in o])
1091
1089
1092 def outgoing(repo, subset, x):
1090 def outgoing(repo, subset, x):
1093 """``outgoing([path])``
1091 """``outgoing([path])``
1094 Changesets not found in the specified destination repository, or the
1092 Changesets not found in the specified destination repository, or the
1095 default push location.
1093 default push location.
1096 """
1094 """
1097 import hg # avoid start-up nasties
1095 import hg # avoid start-up nasties
1098 # i18n: "outgoing" is a keyword
1096 # i18n: "outgoing" is a keyword
1099 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1097 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1100 # i18n: "outgoing" is a keyword
1098 # i18n: "outgoing" is a keyword
1101 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1099 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1102 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1100 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1103 dest, branches = hg.parseurl(dest)
1101 dest, branches = hg.parseurl(dest)
1104 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1102 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1105 if revs:
1103 if revs:
1106 revs = [repo.lookup(rev) for rev in revs]
1104 revs = [repo.lookup(rev) for rev in revs]
1107 other = hg.peer(repo, {}, dest)
1105 other = hg.peer(repo, {}, dest)
1108 repo.ui.pushbuffer()
1106 repo.ui.pushbuffer()
1109 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1107 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1110 repo.ui.popbuffer()
1108 repo.ui.popbuffer()
1111 cl = repo.changelog
1109 cl = repo.changelog
1112 o = set([cl.rev(r) for r in outgoing.missing])
1110 o = set([cl.rev(r) for r in outgoing.missing])
1113 return baseset([r for r in subset if r in o])
1111 return baseset([r for r in subset if r in o])
1114
1112
1115 def p1(repo, subset, x):
1113 def p1(repo, subset, x):
1116 """``p1([set])``
1114 """``p1([set])``
1117 First parent of changesets in set, or the working directory.
1115 First parent of changesets in set, or the working directory.
1118 """
1116 """
1119 if x is None:
1117 if x is None:
1120 p = repo[x].p1().rev()
1118 p = repo[x].p1().rev()
1121 return baseset([r for r in subset if r == p])
1119 return baseset([r for r in subset if r == p])
1122
1120
1123 ps = set()
1121 ps = set()
1124 cl = repo.changelog
1122 cl = repo.changelog
1125 for r in getset(repo, baseset(repo), x):
1123 for r in getset(repo, baseset(repo), x):
1126 ps.add(cl.parentrevs(r)[0])
1124 ps.add(cl.parentrevs(r)[0])
1127 return subset & ps
1125 return subset & ps
1128
1126
1129 def p2(repo, subset, x):
1127 def p2(repo, subset, x):
1130 """``p2([set])``
1128 """``p2([set])``
1131 Second parent of changesets in set, or the working directory.
1129 Second parent of changesets in set, or the working directory.
1132 """
1130 """
1133 if x is None:
1131 if x is None:
1134 ps = repo[x].parents()
1132 ps = repo[x].parents()
1135 try:
1133 try:
1136 p = ps[1].rev()
1134 p = ps[1].rev()
1137 return baseset([r for r in subset if r == p])
1135 return baseset([r for r in subset if r == p])
1138 except IndexError:
1136 except IndexError:
1139 return baseset([])
1137 return baseset([])
1140
1138
1141 ps = set()
1139 ps = set()
1142 cl = repo.changelog
1140 cl = repo.changelog
1143 for r in getset(repo, baseset(repo), x):
1141 for r in getset(repo, baseset(repo), x):
1144 ps.add(cl.parentrevs(r)[1])
1142 ps.add(cl.parentrevs(r)[1])
1145 return subset & ps
1143 return subset & ps
1146
1144
1147 def parents(repo, subset, x):
1145 def parents(repo, subset, x):
1148 """``parents([set])``
1146 """``parents([set])``
1149 The set of all parents for all changesets in set, or the working directory.
1147 The set of all parents for all changesets in set, or the working directory.
1150 """
1148 """
1151 if x is None:
1149 if x is None:
1152 ps = tuple(p.rev() for p in repo[x].parents())
1150 ps = tuple(p.rev() for p in repo[x].parents())
1153 return subset & ps
1151 return subset & ps
1154
1152
1155 ps = set()
1153 ps = set()
1156 cl = repo.changelog
1154 cl = repo.changelog
1157 for r in getset(repo, baseset(repo), x):
1155 for r in getset(repo, baseset(repo), x):
1158 ps.update(cl.parentrevs(r))
1156 ps.update(cl.parentrevs(r))
1159 return subset & ps
1157 return subset & ps
1160
1158
1161 def parentspec(repo, subset, x, n):
1159 def parentspec(repo, subset, x, n):
1162 """``set^0``
1160 """``set^0``
1163 The set.
1161 The set.
1164 ``set^1`` (or ``set^``), ``set^2``
1162 ``set^1`` (or ``set^``), ``set^2``
1165 First or second parent, respectively, of all changesets in set.
1163 First or second parent, respectively, of all changesets in set.
1166 """
1164 """
1167 try:
1165 try:
1168 n = int(n[1])
1166 n = int(n[1])
1169 if n not in (0, 1, 2):
1167 if n not in (0, 1, 2):
1170 raise ValueError
1168 raise ValueError
1171 except (TypeError, ValueError):
1169 except (TypeError, ValueError):
1172 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1170 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1173 ps = set()
1171 ps = set()
1174 cl = repo.changelog
1172 cl = repo.changelog
1175 for r in getset(repo, baseset(cl), x):
1173 for r in getset(repo, baseset(cl), x):
1176 if n == 0:
1174 if n == 0:
1177 ps.add(r)
1175 ps.add(r)
1178 elif n == 1:
1176 elif n == 1:
1179 ps.add(cl.parentrevs(r)[0])
1177 ps.add(cl.parentrevs(r)[0])
1180 elif n == 2:
1178 elif n == 2:
1181 parents = cl.parentrevs(r)
1179 parents = cl.parentrevs(r)
1182 if len(parents) > 1:
1180 if len(parents) > 1:
1183 ps.add(parents[1])
1181 ps.add(parents[1])
1184 return subset & ps
1182 return subset & ps
1185
1183
1186 def present(repo, subset, x):
1184 def present(repo, subset, x):
1187 """``present(set)``
1185 """``present(set)``
1188 An empty set, if any revision in set isn't found; otherwise,
1186 An empty set, if any revision in set isn't found; otherwise,
1189 all revisions in set.
1187 all revisions in set.
1190
1188
1191 If any of specified revisions is not present in the local repository,
1189 If any of specified revisions is not present in the local repository,
1192 the query is normally aborted. But this predicate allows the query
1190 the query is normally aborted. But this predicate allows the query
1193 to continue even in such cases.
1191 to continue even in such cases.
1194 """
1192 """
1195 try:
1193 try:
1196 return getset(repo, subset, x)
1194 return getset(repo, subset, x)
1197 except error.RepoLookupError:
1195 except error.RepoLookupError:
1198 return baseset([])
1196 return baseset([])
1199
1197
1200 def public(repo, subset, x):
1198 def public(repo, subset, x):
1201 """``public()``
1199 """``public()``
1202 Changeset in public phase."""
1200 Changeset in public phase."""
1203 # i18n: "public" is a keyword
1201 # i18n: "public" is a keyword
1204 getargs(x, 0, 0, _("public takes no arguments"))
1202 getargs(x, 0, 0, _("public takes no arguments"))
1205 pc = repo._phasecache
1203 pc = repo._phasecache
1206 return lazyset(subset, lambda r: pc.phase(repo, r) == phases.public)
1204 return lazyset(subset, lambda r: pc.phase(repo, r) == phases.public)
1207
1205
1208 def remote(repo, subset, x):
1206 def remote(repo, subset, x):
1209 """``remote([id [,path]])``
1207 """``remote([id [,path]])``
1210 Local revision that corresponds to the given identifier in a
1208 Local revision that corresponds to the given identifier in a
1211 remote repository, if present. Here, the '.' identifier is a
1209 remote repository, if present. Here, the '.' identifier is a
1212 synonym for the current local branch.
1210 synonym for the current local branch.
1213 """
1211 """
1214
1212
1215 import hg # avoid start-up nasties
1213 import hg # avoid start-up nasties
1216 # i18n: "remote" is a keyword
1214 # i18n: "remote" is a keyword
1217 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1215 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1218
1216
1219 q = '.'
1217 q = '.'
1220 if len(l) > 0:
1218 if len(l) > 0:
1221 # i18n: "remote" is a keyword
1219 # i18n: "remote" is a keyword
1222 q = getstring(l[0], _("remote requires a string id"))
1220 q = getstring(l[0], _("remote requires a string id"))
1223 if q == '.':
1221 if q == '.':
1224 q = repo['.'].branch()
1222 q = repo['.'].branch()
1225
1223
1226 dest = ''
1224 dest = ''
1227 if len(l) > 1:
1225 if len(l) > 1:
1228 # i18n: "remote" is a keyword
1226 # i18n: "remote" is a keyword
1229 dest = getstring(l[1], _("remote requires a repository path"))
1227 dest = getstring(l[1], _("remote requires a repository path"))
1230 dest = repo.ui.expandpath(dest or 'default')
1228 dest = repo.ui.expandpath(dest or 'default')
1231 dest, branches = hg.parseurl(dest)
1229 dest, branches = hg.parseurl(dest)
1232 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1230 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1233 if revs:
1231 if revs:
1234 revs = [repo.lookup(rev) for rev in revs]
1232 revs = [repo.lookup(rev) for rev in revs]
1235 other = hg.peer(repo, {}, dest)
1233 other = hg.peer(repo, {}, dest)
1236 n = other.lookup(q)
1234 n = other.lookup(q)
1237 if n in repo:
1235 if n in repo:
1238 r = repo[n].rev()
1236 r = repo[n].rev()
1239 if r in subset:
1237 if r in subset:
1240 return baseset([r])
1238 return baseset([r])
1241 return baseset([])
1239 return baseset([])
1242
1240
1243 def removes(repo, subset, x):
1241 def removes(repo, subset, x):
1244 """``removes(pattern)``
1242 """``removes(pattern)``
1245 Changesets which remove files matching pattern.
1243 Changesets which remove files matching pattern.
1246
1244
1247 The pattern without explicit kind like ``glob:`` is expected to be
1245 The pattern without explicit kind like ``glob:`` is expected to be
1248 relative to the current directory and match against a file or a
1246 relative to the current directory and match against a file or a
1249 directory.
1247 directory.
1250 """
1248 """
1251 # i18n: "removes" is a keyword
1249 # i18n: "removes" is a keyword
1252 pat = getstring(x, _("removes requires a pattern"))
1250 pat = getstring(x, _("removes requires a pattern"))
1253 return checkstatus(repo, subset, pat, 2)
1251 return checkstatus(repo, subset, pat, 2)
1254
1252
1255 def rev(repo, subset, x):
1253 def rev(repo, subset, x):
1256 """``rev(number)``
1254 """``rev(number)``
1257 Revision with the given numeric identifier.
1255 Revision with the given numeric identifier.
1258 """
1256 """
1259 # i18n: "rev" is a keyword
1257 # i18n: "rev" is a keyword
1260 l = getargs(x, 1, 1, _("rev requires one argument"))
1258 l = getargs(x, 1, 1, _("rev requires one argument"))
1261 try:
1259 try:
1262 # i18n: "rev" is a keyword
1260 # i18n: "rev" is a keyword
1263 l = int(getstring(l[0], _("rev requires a number")))
1261 l = int(getstring(l[0], _("rev requires a number")))
1264 except (TypeError, ValueError):
1262 except (TypeError, ValueError):
1265 # i18n: "rev" is a keyword
1263 # i18n: "rev" is a keyword
1266 raise error.ParseError(_("rev expects a number"))
1264 raise error.ParseError(_("rev expects a number"))
1267 return baseset([r for r in subset if r == l])
1265 return baseset([r for r in subset if r == l])
1268
1266
1269 def matching(repo, subset, x):
1267 def matching(repo, subset, x):
1270 """``matching(revision [, field])``
1268 """``matching(revision [, field])``
1271 Changesets in which a given set of fields match the set of fields in the
1269 Changesets in which a given set of fields match the set of fields in the
1272 selected revision or set.
1270 selected revision or set.
1273
1271
1274 To match more than one field pass the list of fields to match separated
1272 To match more than one field pass the list of fields to match separated
1275 by spaces (e.g. ``author description``).
1273 by spaces (e.g. ``author description``).
1276
1274
1277 Valid fields are most regular revision fields and some special fields.
1275 Valid fields are most regular revision fields and some special fields.
1278
1276
1279 Regular revision fields are ``description``, ``author``, ``branch``,
1277 Regular revision fields are ``description``, ``author``, ``branch``,
1280 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1278 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1281 and ``diff``.
1279 and ``diff``.
1282 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1280 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1283 contents of the revision. Two revisions matching their ``diff`` will
1281 contents of the revision. Two revisions matching their ``diff`` will
1284 also match their ``files``.
1282 also match their ``files``.
1285
1283
1286 Special fields are ``summary`` and ``metadata``:
1284 Special fields are ``summary`` and ``metadata``:
1287 ``summary`` matches the first line of the description.
1285 ``summary`` matches the first line of the description.
1288 ``metadata`` is equivalent to matching ``description user date``
1286 ``metadata`` is equivalent to matching ``description user date``
1289 (i.e. it matches the main metadata fields).
1287 (i.e. it matches the main metadata fields).
1290
1288
1291 ``metadata`` is the default field which is used when no fields are
1289 ``metadata`` is the default field which is used when no fields are
1292 specified. You can match more than one field at a time.
1290 specified. You can match more than one field at a time.
1293 """
1291 """
1294 # i18n: "matching" is a keyword
1292 # i18n: "matching" is a keyword
1295 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1293 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1296
1294
1297 revs = getset(repo, baseset(repo.changelog), l[0])
1295 revs = getset(repo, baseset(repo.changelog), l[0])
1298
1296
1299 fieldlist = ['metadata']
1297 fieldlist = ['metadata']
1300 if len(l) > 1:
1298 if len(l) > 1:
1301 fieldlist = getstring(l[1],
1299 fieldlist = getstring(l[1],
1302 # i18n: "matching" is a keyword
1300 # i18n: "matching" is a keyword
1303 _("matching requires a string "
1301 _("matching requires a string "
1304 "as its second argument")).split()
1302 "as its second argument")).split()
1305
1303
1306 # Make sure that there are no repeated fields,
1304 # Make sure that there are no repeated fields,
1307 # expand the 'special' 'metadata' field type
1305 # expand the 'special' 'metadata' field type
1308 # and check the 'files' whenever we check the 'diff'
1306 # and check the 'files' whenever we check the 'diff'
1309 fields = []
1307 fields = []
1310 for field in fieldlist:
1308 for field in fieldlist:
1311 if field == 'metadata':
1309 if field == 'metadata':
1312 fields += ['user', 'description', 'date']
1310 fields += ['user', 'description', 'date']
1313 elif field == 'diff':
1311 elif field == 'diff':
1314 # a revision matching the diff must also match the files
1312 # a revision matching the diff must also match the files
1315 # since matching the diff is very costly, make sure to
1313 # since matching the diff is very costly, make sure to
1316 # also match the files first
1314 # also match the files first
1317 fields += ['files', 'diff']
1315 fields += ['files', 'diff']
1318 else:
1316 else:
1319 if field == 'author':
1317 if field == 'author':
1320 field = 'user'
1318 field = 'user'
1321 fields.append(field)
1319 fields.append(field)
1322 fields = set(fields)
1320 fields = set(fields)
1323 if 'summary' in fields and 'description' in fields:
1321 if 'summary' in fields and 'description' in fields:
1324 # If a revision matches its description it also matches its summary
1322 # If a revision matches its description it also matches its summary
1325 fields.discard('summary')
1323 fields.discard('summary')
1326
1324
1327 # We may want to match more than one field
1325 # We may want to match more than one field
1328 # Not all fields take the same amount of time to be matched
1326 # Not all fields take the same amount of time to be matched
1329 # Sort the selected fields in order of increasing matching cost
1327 # Sort the selected fields in order of increasing matching cost
1330 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1328 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1331 'files', 'description', 'substate', 'diff']
1329 'files', 'description', 'substate', 'diff']
1332 def fieldkeyfunc(f):
1330 def fieldkeyfunc(f):
1333 try:
1331 try:
1334 return fieldorder.index(f)
1332 return fieldorder.index(f)
1335 except ValueError:
1333 except ValueError:
1336 # assume an unknown field is very costly
1334 # assume an unknown field is very costly
1337 return len(fieldorder)
1335 return len(fieldorder)
1338 fields = list(fields)
1336 fields = list(fields)
1339 fields.sort(key=fieldkeyfunc)
1337 fields.sort(key=fieldkeyfunc)
1340
1338
1341 # Each field will be matched with its own "getfield" function
1339 # Each field will be matched with its own "getfield" function
1342 # which will be added to the getfieldfuncs array of functions
1340 # which will be added to the getfieldfuncs array of functions
1343 getfieldfuncs = []
1341 getfieldfuncs = []
1344 _funcs = {
1342 _funcs = {
1345 'user': lambda r: repo[r].user(),
1343 'user': lambda r: repo[r].user(),
1346 'branch': lambda r: repo[r].branch(),
1344 'branch': lambda r: repo[r].branch(),
1347 'date': lambda r: repo[r].date(),
1345 'date': lambda r: repo[r].date(),
1348 'description': lambda r: repo[r].description(),
1346 'description': lambda r: repo[r].description(),
1349 'files': lambda r: repo[r].files(),
1347 'files': lambda r: repo[r].files(),
1350 'parents': lambda r: repo[r].parents(),
1348 'parents': lambda r: repo[r].parents(),
1351 'phase': lambda r: repo[r].phase(),
1349 'phase': lambda r: repo[r].phase(),
1352 'substate': lambda r: repo[r].substate,
1350 'substate': lambda r: repo[r].substate,
1353 'summary': lambda r: repo[r].description().splitlines()[0],
1351 'summary': lambda r: repo[r].description().splitlines()[0],
1354 'diff': lambda r: list(repo[r].diff(git=True),)
1352 'diff': lambda r: list(repo[r].diff(git=True),)
1355 }
1353 }
1356 for info in fields:
1354 for info in fields:
1357 getfield = _funcs.get(info, None)
1355 getfield = _funcs.get(info, None)
1358 if getfield is None:
1356 if getfield is None:
1359 raise error.ParseError(
1357 raise error.ParseError(
1360 # i18n: "matching" is a keyword
1358 # i18n: "matching" is a keyword
1361 _("unexpected field name passed to matching: %s") % info)
1359 _("unexpected field name passed to matching: %s") % info)
1362 getfieldfuncs.append(getfield)
1360 getfieldfuncs.append(getfield)
1363 # convert the getfield array of functions into a "getinfo" function
1361 # convert the getfield array of functions into a "getinfo" function
1364 # which returns an array of field values (or a single value if there
1362 # which returns an array of field values (or a single value if there
1365 # is only one field to match)
1363 # is only one field to match)
1366 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1364 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1367
1365
1368 def matches(x):
1366 def matches(x):
1369 for rev in revs:
1367 for rev in revs:
1370 target = getinfo(rev)
1368 target = getinfo(rev)
1371 match = True
1369 match = True
1372 for n, f in enumerate(getfieldfuncs):
1370 for n, f in enumerate(getfieldfuncs):
1373 if target[n] != f(x):
1371 if target[n] != f(x):
1374 match = False
1372 match = False
1375 if match:
1373 if match:
1376 return True
1374 return True
1377 return False
1375 return False
1378
1376
1379 return lazyset(subset, matches)
1377 return lazyset(subset, matches)
1380
1378
1381 def reverse(repo, subset, x):
1379 def reverse(repo, subset, x):
1382 """``reverse(set)``
1380 """``reverse(set)``
1383 Reverse order of set.
1381 Reverse order of set.
1384 """
1382 """
1385 l = getset(repo, subset, x)
1383 l = getset(repo, subset, x)
1386 l.reverse()
1384 l.reverse()
1387 return l
1385 return l
1388
1386
1389 def roots(repo, subset, x):
1387 def roots(repo, subset, x):
1390 """``roots(set)``
1388 """``roots(set)``
1391 Changesets in set with no parent changeset in set.
1389 Changesets in set with no parent changeset in set.
1392 """
1390 """
1393 s = getset(repo, baseset(repo.changelog), x).set()
1391 s = getset(repo, baseset(repo.changelog), x).set()
1394 subset = baseset([r for r in subset if r in s])
1392 subset = baseset([r for r in subset if r in s])
1395 cs = _children(repo, subset, s)
1393 cs = _children(repo, subset, s)
1396 return subset - cs
1394 return subset - cs
1397
1395
1398 def secret(repo, subset, x):
1396 def secret(repo, subset, x):
1399 """``secret()``
1397 """``secret()``
1400 Changeset in secret phase."""
1398 Changeset in secret phase."""
1401 # i18n: "secret" is a keyword
1399 # i18n: "secret" is a keyword
1402 getargs(x, 0, 0, _("secret takes no arguments"))
1400 getargs(x, 0, 0, _("secret takes no arguments"))
1403 pc = repo._phasecache
1401 pc = repo._phasecache
1404 return lazyset(subset, lambda x: pc.phase(repo, x) == phases.secret)
1402 return lazyset(subset, lambda x: pc.phase(repo, x) == phases.secret)
1405
1403
1406 def sort(repo, subset, x):
1404 def sort(repo, subset, x):
1407 """``sort(set[, [-]key...])``
1405 """``sort(set[, [-]key...])``
1408 Sort set by keys. The default sort order is ascending, specify a key
1406 Sort set by keys. The default sort order is ascending, specify a key
1409 as ``-key`` to sort in descending order.
1407 as ``-key`` to sort in descending order.
1410
1408
1411 The keys can be:
1409 The keys can be:
1412
1410
1413 - ``rev`` for the revision number,
1411 - ``rev`` for the revision number,
1414 - ``branch`` for the branch name,
1412 - ``branch`` for the branch name,
1415 - ``desc`` for the commit message (description),
1413 - ``desc`` for the commit message (description),
1416 - ``user`` for user name (``author`` can be used as an alias),
1414 - ``user`` for user name (``author`` can be used as an alias),
1417 - ``date`` for the commit date
1415 - ``date`` for the commit date
1418 """
1416 """
1419 # i18n: "sort" is a keyword
1417 # i18n: "sort" is a keyword
1420 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1418 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1421 keys = "rev"
1419 keys = "rev"
1422 if len(l) == 2:
1420 if len(l) == 2:
1423 # i18n: "sort" is a keyword
1421 # i18n: "sort" is a keyword
1424 keys = getstring(l[1], _("sort spec must be a string"))
1422 keys = getstring(l[1], _("sort spec must be a string"))
1425
1423
1426 s = l[0]
1424 s = l[0]
1427 keys = keys.split()
1425 keys = keys.split()
1428 l = []
1426 l = []
1429 def invert(s):
1427 def invert(s):
1430 return "".join(chr(255 - ord(c)) for c in s)
1428 return "".join(chr(255 - ord(c)) for c in s)
1431 for r in getset(repo, subset, s):
1429 for r in getset(repo, subset, s):
1432 c = repo[r]
1430 c = repo[r]
1433 e = []
1431 e = []
1434 for k in keys:
1432 for k in keys:
1435 if k == 'rev':
1433 if k == 'rev':
1436 e.append(r)
1434 e.append(r)
1437 elif k == '-rev':
1435 elif k == '-rev':
1438 e.append(-r)
1436 e.append(-r)
1439 elif k == 'branch':
1437 elif k == 'branch':
1440 e.append(c.branch())
1438 e.append(c.branch())
1441 elif k == '-branch':
1439 elif k == '-branch':
1442 e.append(invert(c.branch()))
1440 e.append(invert(c.branch()))
1443 elif k == 'desc':
1441 elif k == 'desc':
1444 e.append(c.description())
1442 e.append(c.description())
1445 elif k == '-desc':
1443 elif k == '-desc':
1446 e.append(invert(c.description()))
1444 e.append(invert(c.description()))
1447 elif k in 'user author':
1445 elif k in 'user author':
1448 e.append(c.user())
1446 e.append(c.user())
1449 elif k in '-user -author':
1447 elif k in '-user -author':
1450 e.append(invert(c.user()))
1448 e.append(invert(c.user()))
1451 elif k == 'date':
1449 elif k == 'date':
1452 e.append(c.date()[0])
1450 e.append(c.date()[0])
1453 elif k == '-date':
1451 elif k == '-date':
1454 e.append(-c.date()[0])
1452 e.append(-c.date()[0])
1455 else:
1453 else:
1456 raise error.ParseError(_("unknown sort key %r") % k)
1454 raise error.ParseError(_("unknown sort key %r") % k)
1457 e.append(r)
1455 e.append(r)
1458 l.append(e)
1456 l.append(e)
1459 l.sort()
1457 l.sort()
1460 return baseset([e[-1] for e in l])
1458 return baseset([e[-1] for e in l])
1461
1459
1462 def _stringmatcher(pattern):
1460 def _stringmatcher(pattern):
1463 """
1461 """
1464 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1462 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1465 returns the matcher name, pattern, and matcher function.
1463 returns the matcher name, pattern, and matcher function.
1466 missing or unknown prefixes are treated as literal matches.
1464 missing or unknown prefixes are treated as literal matches.
1467
1465
1468 helper for tests:
1466 helper for tests:
1469 >>> def test(pattern, *tests):
1467 >>> def test(pattern, *tests):
1470 ... kind, pattern, matcher = _stringmatcher(pattern)
1468 ... kind, pattern, matcher = _stringmatcher(pattern)
1471 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1469 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1472
1470
1473 exact matching (no prefix):
1471 exact matching (no prefix):
1474 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1472 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1475 ('literal', 'abcdefg', [False, False, True])
1473 ('literal', 'abcdefg', [False, False, True])
1476
1474
1477 regex matching ('re:' prefix)
1475 regex matching ('re:' prefix)
1478 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1476 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1479 ('re', 'a.+b', [False, False, True])
1477 ('re', 'a.+b', [False, False, True])
1480
1478
1481 force exact matches ('literal:' prefix)
1479 force exact matches ('literal:' prefix)
1482 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1480 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1483 ('literal', 're:foobar', [False, True])
1481 ('literal', 're:foobar', [False, True])
1484
1482
1485 unknown prefixes are ignored and treated as literals
1483 unknown prefixes are ignored and treated as literals
1486 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1484 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1487 ('literal', 'foo:bar', [False, False, True])
1485 ('literal', 'foo:bar', [False, False, True])
1488 """
1486 """
1489 if pattern.startswith('re:'):
1487 if pattern.startswith('re:'):
1490 pattern = pattern[3:]
1488 pattern = pattern[3:]
1491 try:
1489 try:
1492 regex = re.compile(pattern)
1490 regex = re.compile(pattern)
1493 except re.error, e:
1491 except re.error, e:
1494 raise error.ParseError(_('invalid regular expression: %s')
1492 raise error.ParseError(_('invalid regular expression: %s')
1495 % e)
1493 % e)
1496 return 're', pattern, regex.search
1494 return 're', pattern, regex.search
1497 elif pattern.startswith('literal:'):
1495 elif pattern.startswith('literal:'):
1498 pattern = pattern[8:]
1496 pattern = pattern[8:]
1499 return 'literal', pattern, pattern.__eq__
1497 return 'literal', pattern, pattern.__eq__
1500
1498
1501 def _substringmatcher(pattern):
1499 def _substringmatcher(pattern):
1502 kind, pattern, matcher = _stringmatcher(pattern)
1500 kind, pattern, matcher = _stringmatcher(pattern)
1503 if kind == 'literal':
1501 if kind == 'literal':
1504 matcher = lambda s: pattern in s
1502 matcher = lambda s: pattern in s
1505 return kind, pattern, matcher
1503 return kind, pattern, matcher
1506
1504
1507 def tag(repo, subset, x):
1505 def tag(repo, subset, x):
1508 """``tag([name])``
1506 """``tag([name])``
1509 The specified tag by name, or all tagged revisions if no name is given.
1507 The specified tag by name, or all tagged revisions if no name is given.
1510 """
1508 """
1511 # i18n: "tag" is a keyword
1509 # i18n: "tag" is a keyword
1512 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1510 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1513 cl = repo.changelog
1511 cl = repo.changelog
1514 if args:
1512 if args:
1515 pattern = getstring(args[0],
1513 pattern = getstring(args[0],
1516 # i18n: "tag" is a keyword
1514 # i18n: "tag" is a keyword
1517 _('the argument to tag must be a string'))
1515 _('the argument to tag must be a string'))
1518 kind, pattern, matcher = _stringmatcher(pattern)
1516 kind, pattern, matcher = _stringmatcher(pattern)
1519 if kind == 'literal':
1517 if kind == 'literal':
1520 # avoid resolving all tags
1518 # avoid resolving all tags
1521 tn = repo._tagscache.tags.get(pattern, None)
1519 tn = repo._tagscache.tags.get(pattern, None)
1522 if tn is None:
1520 if tn is None:
1523 raise util.Abort(_("tag '%s' does not exist") % pattern)
1521 raise util.Abort(_("tag '%s' does not exist") % pattern)
1524 s = set([repo[tn].rev()])
1522 s = set([repo[tn].rev()])
1525 else:
1523 else:
1526 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1524 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1527 else:
1525 else:
1528 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1526 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1529 return subset & s
1527 return subset & s
1530
1528
1531 def tagged(repo, subset, x):
1529 def tagged(repo, subset, x):
1532 return tag(repo, subset, x)
1530 return tag(repo, subset, x)
1533
1531
1534 def unstable(repo, subset, x):
1532 def unstable(repo, subset, x):
1535 """``unstable()``
1533 """``unstable()``
1536 Non-obsolete changesets with obsolete ancestors.
1534 Non-obsolete changesets with obsolete ancestors.
1537 """
1535 """
1538 # i18n: "unstable" is a keyword
1536 # i18n: "unstable" is a keyword
1539 getargs(x, 0, 0, _("unstable takes no arguments"))
1537 getargs(x, 0, 0, _("unstable takes no arguments"))
1540 unstables = obsmod.getrevs(repo, 'unstable')
1538 unstables = obsmod.getrevs(repo, 'unstable')
1541 return subset & unstables
1539 return subset & unstables
1542
1540
1543
1541
1544 def user(repo, subset, x):
1542 def user(repo, subset, x):
1545 """``user(string)``
1543 """``user(string)``
1546 User name contains string. The match is case-insensitive.
1544 User name contains string. The match is case-insensitive.
1547
1545
1548 If `string` starts with `re:`, the remainder of the string is treated as
1546 If `string` starts with `re:`, the remainder of the string is treated as
1549 a regular expression. To match a user that actually contains `re:`, use
1547 a regular expression. To match a user that actually contains `re:`, use
1550 the prefix `literal:`.
1548 the prefix `literal:`.
1551 """
1549 """
1552 return author(repo, subset, x)
1550 return author(repo, subset, x)
1553
1551
1554 # for internal use
1552 # for internal use
1555 def _list(repo, subset, x):
1553 def _list(repo, subset, x):
1556 s = getstring(x, "internal error")
1554 s = getstring(x, "internal error")
1557 if not s:
1555 if not s:
1558 return baseset([])
1556 return baseset([])
1559 ls = [repo[r].rev() for r in s.split('\0')]
1557 ls = [repo[r].rev() for r in s.split('\0')]
1560 s = subset.set()
1558 s = subset.set()
1561 return baseset([r for r in ls if r in s])
1559 return baseset([r for r in ls if r in s])
1562
1560
1563 symbols = {
1561 symbols = {
1564 "adds": adds,
1562 "adds": adds,
1565 "all": getall,
1563 "all": getall,
1566 "ancestor": ancestor,
1564 "ancestor": ancestor,
1567 "ancestors": ancestors,
1565 "ancestors": ancestors,
1568 "_firstancestors": _firstancestors,
1566 "_firstancestors": _firstancestors,
1569 "author": author,
1567 "author": author,
1570 "bisect": bisect,
1568 "bisect": bisect,
1571 "bisected": bisected,
1569 "bisected": bisected,
1572 "bookmark": bookmark,
1570 "bookmark": bookmark,
1573 "branch": branch,
1571 "branch": branch,
1574 "branchpoint": branchpoint,
1572 "branchpoint": branchpoint,
1575 "bumped": bumped,
1573 "bumped": bumped,
1576 "bundle": bundle,
1574 "bundle": bundle,
1577 "children": children,
1575 "children": children,
1578 "closed": closed,
1576 "closed": closed,
1579 "contains": contains,
1577 "contains": contains,
1580 "converted": converted,
1578 "converted": converted,
1581 "date": date,
1579 "date": date,
1582 "desc": desc,
1580 "desc": desc,
1583 "descendants": descendants,
1581 "descendants": descendants,
1584 "_firstdescendants": _firstdescendants,
1582 "_firstdescendants": _firstdescendants,
1585 "destination": destination,
1583 "destination": destination,
1586 "divergent": divergent,
1584 "divergent": divergent,
1587 "draft": draft,
1585 "draft": draft,
1588 "extinct": extinct,
1586 "extinct": extinct,
1589 "extra": extra,
1587 "extra": extra,
1590 "file": hasfile,
1588 "file": hasfile,
1591 "filelog": filelog,
1589 "filelog": filelog,
1592 "first": first,
1590 "first": first,
1593 "follow": follow,
1591 "follow": follow,
1594 "_followfirst": _followfirst,
1592 "_followfirst": _followfirst,
1595 "grep": grep,
1593 "grep": grep,
1596 "head": head,
1594 "head": head,
1597 "heads": heads,
1595 "heads": heads,
1598 "hidden": hidden,
1596 "hidden": hidden,
1599 "id": node_,
1597 "id": node_,
1600 "keyword": keyword,
1598 "keyword": keyword,
1601 "last": last,
1599 "last": last,
1602 "limit": limit,
1600 "limit": limit,
1603 "_matchfiles": _matchfiles,
1601 "_matchfiles": _matchfiles,
1604 "max": maxrev,
1602 "max": maxrev,
1605 "merge": merge,
1603 "merge": merge,
1606 "min": minrev,
1604 "min": minrev,
1607 "modifies": modifies,
1605 "modifies": modifies,
1608 "obsolete": obsolete,
1606 "obsolete": obsolete,
1609 "origin": origin,
1607 "origin": origin,
1610 "outgoing": outgoing,
1608 "outgoing": outgoing,
1611 "p1": p1,
1609 "p1": p1,
1612 "p2": p2,
1610 "p2": p2,
1613 "parents": parents,
1611 "parents": parents,
1614 "present": present,
1612 "present": present,
1615 "public": public,
1613 "public": public,
1616 "remote": remote,
1614 "remote": remote,
1617 "removes": removes,
1615 "removes": removes,
1618 "rev": rev,
1616 "rev": rev,
1619 "reverse": reverse,
1617 "reverse": reverse,
1620 "roots": roots,
1618 "roots": roots,
1621 "sort": sort,
1619 "sort": sort,
1622 "secret": secret,
1620 "secret": secret,
1623 "matching": matching,
1621 "matching": matching,
1624 "tag": tag,
1622 "tag": tag,
1625 "tagged": tagged,
1623 "tagged": tagged,
1626 "user": user,
1624 "user": user,
1627 "unstable": unstable,
1625 "unstable": unstable,
1628 "_list": _list,
1626 "_list": _list,
1629 }
1627 }
1630
1628
1631 # symbols which can't be used for a DoS attack for any given input
1629 # symbols which can't be used for a DoS attack for any given input
1632 # (e.g. those which accept regexes as plain strings shouldn't be included)
1630 # (e.g. those which accept regexes as plain strings shouldn't be included)
1633 # functions that just return a lot of changesets (like all) don't count here
1631 # functions that just return a lot of changesets (like all) don't count here
1634 safesymbols = set([
1632 safesymbols = set([
1635 "adds",
1633 "adds",
1636 "all",
1634 "all",
1637 "ancestor",
1635 "ancestor",
1638 "ancestors",
1636 "ancestors",
1639 "_firstancestors",
1637 "_firstancestors",
1640 "author",
1638 "author",
1641 "bisect",
1639 "bisect",
1642 "bisected",
1640 "bisected",
1643 "bookmark",
1641 "bookmark",
1644 "branch",
1642 "branch",
1645 "branchpoint",
1643 "branchpoint",
1646 "bumped",
1644 "bumped",
1647 "bundle",
1645 "bundle",
1648 "children",
1646 "children",
1649 "closed",
1647 "closed",
1650 "converted",
1648 "converted",
1651 "date",
1649 "date",
1652 "desc",
1650 "desc",
1653 "descendants",
1651 "descendants",
1654 "_firstdescendants",
1652 "_firstdescendants",
1655 "destination",
1653 "destination",
1656 "divergent",
1654 "divergent",
1657 "draft",
1655 "draft",
1658 "extinct",
1656 "extinct",
1659 "extra",
1657 "extra",
1660 "file",
1658 "file",
1661 "filelog",
1659 "filelog",
1662 "first",
1660 "first",
1663 "follow",
1661 "follow",
1664 "_followfirst",
1662 "_followfirst",
1665 "head",
1663 "head",
1666 "heads",
1664 "heads",
1667 "hidden",
1665 "hidden",
1668 "id",
1666 "id",
1669 "keyword",
1667 "keyword",
1670 "last",
1668 "last",
1671 "limit",
1669 "limit",
1672 "_matchfiles",
1670 "_matchfiles",
1673 "max",
1671 "max",
1674 "merge",
1672 "merge",
1675 "min",
1673 "min",
1676 "modifies",
1674 "modifies",
1677 "obsolete",
1675 "obsolete",
1678 "origin",
1676 "origin",
1679 "outgoing",
1677 "outgoing",
1680 "p1",
1678 "p1",
1681 "p2",
1679 "p2",
1682 "parents",
1680 "parents",
1683 "present",
1681 "present",
1684 "public",
1682 "public",
1685 "remote",
1683 "remote",
1686 "removes",
1684 "removes",
1687 "rev",
1685 "rev",
1688 "reverse",
1686 "reverse",
1689 "roots",
1687 "roots",
1690 "sort",
1688 "sort",
1691 "secret",
1689 "secret",
1692 "matching",
1690 "matching",
1693 "tag",
1691 "tag",
1694 "tagged",
1692 "tagged",
1695 "user",
1693 "user",
1696 "unstable",
1694 "unstable",
1697 "_list",
1695 "_list",
1698 ])
1696 ])
1699
1697
1700 methods = {
1698 methods = {
1701 "range": rangeset,
1699 "range": rangeset,
1702 "dagrange": dagrange,
1700 "dagrange": dagrange,
1703 "string": stringset,
1701 "string": stringset,
1704 "symbol": symbolset,
1702 "symbol": symbolset,
1705 "and": andset,
1703 "and": andset,
1706 "or": orset,
1704 "or": orset,
1707 "not": notset,
1705 "not": notset,
1708 "list": listset,
1706 "list": listset,
1709 "func": func,
1707 "func": func,
1710 "ancestor": ancestorspec,
1708 "ancestor": ancestorspec,
1711 "parent": parentspec,
1709 "parent": parentspec,
1712 "parentpost": p1,
1710 "parentpost": p1,
1713 }
1711 }
1714
1712
1715 def optimize(x, small):
1713 def optimize(x, small):
1716 if x is None:
1714 if x is None:
1717 return 0, x
1715 return 0, x
1718
1716
1719 smallbonus = 1
1717 smallbonus = 1
1720 if small:
1718 if small:
1721 smallbonus = .5
1719 smallbonus = .5
1722
1720
1723 op = x[0]
1721 op = x[0]
1724 if op == 'minus':
1722 if op == 'minus':
1725 return optimize(('and', x[1], ('not', x[2])), small)
1723 return optimize(('and', x[1], ('not', x[2])), small)
1726 elif op == 'dagrangepre':
1724 elif op == 'dagrangepre':
1727 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1725 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1728 elif op == 'dagrangepost':
1726 elif op == 'dagrangepost':
1729 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1727 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1730 elif op == 'rangepre':
1728 elif op == 'rangepre':
1731 return optimize(('range', ('string', '0'), x[1]), small)
1729 return optimize(('range', ('string', '0'), x[1]), small)
1732 elif op == 'rangepost':
1730 elif op == 'rangepost':
1733 return optimize(('range', x[1], ('string', 'tip')), small)
1731 return optimize(('range', x[1], ('string', 'tip')), small)
1734 elif op == 'negate':
1732 elif op == 'negate':
1735 return optimize(('string',
1733 return optimize(('string',
1736 '-' + getstring(x[1], _("can't negate that"))), small)
1734 '-' + getstring(x[1], _("can't negate that"))), small)
1737 elif op in 'string symbol negate':
1735 elif op in 'string symbol negate':
1738 return smallbonus, x # single revisions are small
1736 return smallbonus, x # single revisions are small
1739 elif op == 'and':
1737 elif op == 'and':
1740 wa, ta = optimize(x[1], True)
1738 wa, ta = optimize(x[1], True)
1741 wb, tb = optimize(x[2], True)
1739 wb, tb = optimize(x[2], True)
1742 w = min(wa, wb)
1740 w = min(wa, wb)
1743 if wa > wb:
1741 if wa > wb:
1744 return w, (op, tb, ta)
1742 return w, (op, tb, ta)
1745 return w, (op, ta, tb)
1743 return w, (op, ta, tb)
1746 elif op == 'or':
1744 elif op == 'or':
1747 wa, ta = optimize(x[1], False)
1745 wa, ta = optimize(x[1], False)
1748 wb, tb = optimize(x[2], False)
1746 wb, tb = optimize(x[2], False)
1749 if wb < wa:
1747 if wb < wa:
1750 wb, wa = wa, wb
1748 wb, wa = wa, wb
1751 return max(wa, wb), (op, ta, tb)
1749 return max(wa, wb), (op, ta, tb)
1752 elif op == 'not':
1750 elif op == 'not':
1753 o = optimize(x[1], not small)
1751 o = optimize(x[1], not small)
1754 return o[0], (op, o[1])
1752 return o[0], (op, o[1])
1755 elif op == 'parentpost':
1753 elif op == 'parentpost':
1756 o = optimize(x[1], small)
1754 o = optimize(x[1], small)
1757 return o[0], (op, o[1])
1755 return o[0], (op, o[1])
1758 elif op == 'group':
1756 elif op == 'group':
1759 return optimize(x[1], small)
1757 return optimize(x[1], small)
1760 elif op in 'dagrange range list parent ancestorspec':
1758 elif op in 'dagrange range list parent ancestorspec':
1761 if op == 'parent':
1759 if op == 'parent':
1762 # x^:y means (x^) : y, not x ^ (:y)
1760 # x^:y means (x^) : y, not x ^ (:y)
1763 post = ('parentpost', x[1])
1761 post = ('parentpost', x[1])
1764 if x[2][0] == 'dagrangepre':
1762 if x[2][0] == 'dagrangepre':
1765 return optimize(('dagrange', post, x[2][1]), small)
1763 return optimize(('dagrange', post, x[2][1]), small)
1766 elif x[2][0] == 'rangepre':
1764 elif x[2][0] == 'rangepre':
1767 return optimize(('range', post, x[2][1]), small)
1765 return optimize(('range', post, x[2][1]), small)
1768
1766
1769 wa, ta = optimize(x[1], small)
1767 wa, ta = optimize(x[1], small)
1770 wb, tb = optimize(x[2], small)
1768 wb, tb = optimize(x[2], small)
1771 return wa + wb, (op, ta, tb)
1769 return wa + wb, (op, ta, tb)
1772 elif op == 'func':
1770 elif op == 'func':
1773 f = getstring(x[1], _("not a symbol"))
1771 f = getstring(x[1], _("not a symbol"))
1774 wa, ta = optimize(x[2], small)
1772 wa, ta = optimize(x[2], small)
1775 if f in ("author branch closed date desc file grep keyword "
1773 if f in ("author branch closed date desc file grep keyword "
1776 "outgoing user"):
1774 "outgoing user"):
1777 w = 10 # slow
1775 w = 10 # slow
1778 elif f in "modifies adds removes":
1776 elif f in "modifies adds removes":
1779 w = 30 # slower
1777 w = 30 # slower
1780 elif f == "contains":
1778 elif f == "contains":
1781 w = 100 # very slow
1779 w = 100 # very slow
1782 elif f == "ancestor":
1780 elif f == "ancestor":
1783 w = 1 * smallbonus
1781 w = 1 * smallbonus
1784 elif f in "reverse limit first":
1782 elif f in "reverse limit first":
1785 w = 0
1783 w = 0
1786 elif f in "sort":
1784 elif f in "sort":
1787 w = 10 # assume most sorts look at changelog
1785 w = 10 # assume most sorts look at changelog
1788 else:
1786 else:
1789 w = 1
1787 w = 1
1790 return w + wa, (op, x[1], ta)
1788 return w + wa, (op, x[1], ta)
1791 return 1, x
1789 return 1, x
1792
1790
1793 _aliasarg = ('func', ('symbol', '_aliasarg'))
1791 _aliasarg = ('func', ('symbol', '_aliasarg'))
1794 def _getaliasarg(tree):
1792 def _getaliasarg(tree):
1795 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1793 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1796 return X, None otherwise.
1794 return X, None otherwise.
1797 """
1795 """
1798 if (len(tree) == 3 and tree[:2] == _aliasarg
1796 if (len(tree) == 3 and tree[:2] == _aliasarg
1799 and tree[2][0] == 'string'):
1797 and tree[2][0] == 'string'):
1800 return tree[2][1]
1798 return tree[2][1]
1801 return None
1799 return None
1802
1800
1803 def _checkaliasarg(tree, known=None):
1801 def _checkaliasarg(tree, known=None):
1804 """Check tree contains no _aliasarg construct or only ones which
1802 """Check tree contains no _aliasarg construct or only ones which
1805 value is in known. Used to avoid alias placeholders injection.
1803 value is in known. Used to avoid alias placeholders injection.
1806 """
1804 """
1807 if isinstance(tree, tuple):
1805 if isinstance(tree, tuple):
1808 arg = _getaliasarg(tree)
1806 arg = _getaliasarg(tree)
1809 if arg is not None and (not known or arg not in known):
1807 if arg is not None and (not known or arg not in known):
1810 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1808 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1811 for t in tree:
1809 for t in tree:
1812 _checkaliasarg(t, known)
1810 _checkaliasarg(t, known)
1813
1811
1814 class revsetalias(object):
1812 class revsetalias(object):
1815 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1813 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1816 args = None
1814 args = None
1817
1815
1818 def __init__(self, name, value):
1816 def __init__(self, name, value):
1819 '''Aliases like:
1817 '''Aliases like:
1820
1818
1821 h = heads(default)
1819 h = heads(default)
1822 b($1) = ancestors($1) - ancestors(default)
1820 b($1) = ancestors($1) - ancestors(default)
1823 '''
1821 '''
1824 m = self.funcre.search(name)
1822 m = self.funcre.search(name)
1825 if m:
1823 if m:
1826 self.name = m.group(1)
1824 self.name = m.group(1)
1827 self.tree = ('func', ('symbol', m.group(1)))
1825 self.tree = ('func', ('symbol', m.group(1)))
1828 self.args = [x.strip() for x in m.group(2).split(',')]
1826 self.args = [x.strip() for x in m.group(2).split(',')]
1829 for arg in self.args:
1827 for arg in self.args:
1830 # _aliasarg() is an unknown symbol only used separate
1828 # _aliasarg() is an unknown symbol only used separate
1831 # alias argument placeholders from regular strings.
1829 # alias argument placeholders from regular strings.
1832 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1830 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1833 else:
1831 else:
1834 self.name = name
1832 self.name = name
1835 self.tree = ('symbol', name)
1833 self.tree = ('symbol', name)
1836
1834
1837 self.replacement, pos = parse(value)
1835 self.replacement, pos = parse(value)
1838 if pos != len(value):
1836 if pos != len(value):
1839 raise error.ParseError(_('invalid token'), pos)
1837 raise error.ParseError(_('invalid token'), pos)
1840 # Check for placeholder injection
1838 # Check for placeholder injection
1841 _checkaliasarg(self.replacement, self.args)
1839 _checkaliasarg(self.replacement, self.args)
1842
1840
1843 def _getalias(aliases, tree):
1841 def _getalias(aliases, tree):
1844 """If tree looks like an unexpanded alias, return it. Return None
1842 """If tree looks like an unexpanded alias, return it. Return None
1845 otherwise.
1843 otherwise.
1846 """
1844 """
1847 if isinstance(tree, tuple) and tree:
1845 if isinstance(tree, tuple) and tree:
1848 if tree[0] == 'symbol' and len(tree) == 2:
1846 if tree[0] == 'symbol' and len(tree) == 2:
1849 name = tree[1]
1847 name = tree[1]
1850 alias = aliases.get(name)
1848 alias = aliases.get(name)
1851 if alias and alias.args is None and alias.tree == tree:
1849 if alias and alias.args is None and alias.tree == tree:
1852 return alias
1850 return alias
1853 if tree[0] == 'func' and len(tree) > 1:
1851 if tree[0] == 'func' and len(tree) > 1:
1854 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1852 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1855 name = tree[1][1]
1853 name = tree[1][1]
1856 alias = aliases.get(name)
1854 alias = aliases.get(name)
1857 if alias and alias.args is not None and alias.tree == tree[:2]:
1855 if alias and alias.args is not None and alias.tree == tree[:2]:
1858 return alias
1856 return alias
1859 return None
1857 return None
1860
1858
1861 def _expandargs(tree, args):
1859 def _expandargs(tree, args):
1862 """Replace _aliasarg instances with the substitution value of the
1860 """Replace _aliasarg instances with the substitution value of the
1863 same name in args, recursively.
1861 same name in args, recursively.
1864 """
1862 """
1865 if not tree or not isinstance(tree, tuple):
1863 if not tree or not isinstance(tree, tuple):
1866 return tree
1864 return tree
1867 arg = _getaliasarg(tree)
1865 arg = _getaliasarg(tree)
1868 if arg is not None:
1866 if arg is not None:
1869 return args[arg]
1867 return args[arg]
1870 return tuple(_expandargs(t, args) for t in tree)
1868 return tuple(_expandargs(t, args) for t in tree)
1871
1869
1872 def _expandaliases(aliases, tree, expanding, cache):
1870 def _expandaliases(aliases, tree, expanding, cache):
1873 """Expand aliases in tree, recursively.
1871 """Expand aliases in tree, recursively.
1874
1872
1875 'aliases' is a dictionary mapping user defined aliases to
1873 'aliases' is a dictionary mapping user defined aliases to
1876 revsetalias objects.
1874 revsetalias objects.
1877 """
1875 """
1878 if not isinstance(tree, tuple):
1876 if not isinstance(tree, tuple):
1879 # Do not expand raw strings
1877 # Do not expand raw strings
1880 return tree
1878 return tree
1881 alias = _getalias(aliases, tree)
1879 alias = _getalias(aliases, tree)
1882 if alias is not None:
1880 if alias is not None:
1883 if alias in expanding:
1881 if alias in expanding:
1884 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1882 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1885 'detected') % alias.name)
1883 'detected') % alias.name)
1886 expanding.append(alias)
1884 expanding.append(alias)
1887 if alias.name not in cache:
1885 if alias.name not in cache:
1888 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1886 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1889 expanding, cache)
1887 expanding, cache)
1890 result = cache[alias.name]
1888 result = cache[alias.name]
1891 expanding.pop()
1889 expanding.pop()
1892 if alias.args is not None:
1890 if alias.args is not None:
1893 l = getlist(tree[2])
1891 l = getlist(tree[2])
1894 if len(l) != len(alias.args):
1892 if len(l) != len(alias.args):
1895 raise error.ParseError(
1893 raise error.ParseError(
1896 _('invalid number of arguments: %s') % len(l))
1894 _('invalid number of arguments: %s') % len(l))
1897 l = [_expandaliases(aliases, a, [], cache) for a in l]
1895 l = [_expandaliases(aliases, a, [], cache) for a in l]
1898 result = _expandargs(result, dict(zip(alias.args, l)))
1896 result = _expandargs(result, dict(zip(alias.args, l)))
1899 else:
1897 else:
1900 result = tuple(_expandaliases(aliases, t, expanding, cache)
1898 result = tuple(_expandaliases(aliases, t, expanding, cache)
1901 for t in tree)
1899 for t in tree)
1902 return result
1900 return result
1903
1901
1904 def findaliases(ui, tree):
1902 def findaliases(ui, tree):
1905 _checkaliasarg(tree)
1903 _checkaliasarg(tree)
1906 aliases = {}
1904 aliases = {}
1907 for k, v in ui.configitems('revsetalias'):
1905 for k, v in ui.configitems('revsetalias'):
1908 alias = revsetalias(k, v)
1906 alias = revsetalias(k, v)
1909 aliases[alias.name] = alias
1907 aliases[alias.name] = alias
1910 return _expandaliases(aliases, tree, [], {})
1908 return _expandaliases(aliases, tree, [], {})
1911
1909
1912 def parse(spec):
1910 def parse(spec):
1913 p = parser.parser(tokenize, elements)
1911 p = parser.parser(tokenize, elements)
1914 return p.parse(spec)
1912 return p.parse(spec)
1915
1913
1916 def match(ui, spec):
1914 def match(ui, spec):
1917 if not spec:
1915 if not spec:
1918 raise error.ParseError(_("empty query"))
1916 raise error.ParseError(_("empty query"))
1919 tree, pos = parse(spec)
1917 tree, pos = parse(spec)
1920 if (pos != len(spec)):
1918 if (pos != len(spec)):
1921 raise error.ParseError(_("invalid token"), pos)
1919 raise error.ParseError(_("invalid token"), pos)
1922 if ui:
1920 if ui:
1923 tree = findaliases(ui, tree)
1921 tree = findaliases(ui, tree)
1924 weight, tree = optimize(tree, True)
1922 weight, tree = optimize(tree, True)
1925 def mfunc(repo, subset):
1923 def mfunc(repo, subset):
1926 return getset(repo, subset, tree)
1924 return getset(repo, subset, tree)
1927 return mfunc
1925 return mfunc
1928
1926
1929 def formatspec(expr, *args):
1927 def formatspec(expr, *args):
1930 '''
1928 '''
1931 This is a convenience function for using revsets internally, and
1929 This is a convenience function for using revsets internally, and
1932 escapes arguments appropriately. Aliases are intentionally ignored
1930 escapes arguments appropriately. Aliases are intentionally ignored
1933 so that intended expression behavior isn't accidentally subverted.
1931 so that intended expression behavior isn't accidentally subverted.
1934
1932
1935 Supported arguments:
1933 Supported arguments:
1936
1934
1937 %r = revset expression, parenthesized
1935 %r = revset expression, parenthesized
1938 %d = int(arg), no quoting
1936 %d = int(arg), no quoting
1939 %s = string(arg), escaped and single-quoted
1937 %s = string(arg), escaped and single-quoted
1940 %b = arg.branch(), escaped and single-quoted
1938 %b = arg.branch(), escaped and single-quoted
1941 %n = hex(arg), single-quoted
1939 %n = hex(arg), single-quoted
1942 %% = a literal '%'
1940 %% = a literal '%'
1943
1941
1944 Prefixing the type with 'l' specifies a parenthesized list of that type.
1942 Prefixing the type with 'l' specifies a parenthesized list of that type.
1945
1943
1946 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1944 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1947 '(10 or 11):: and ((this()) or (that()))'
1945 '(10 or 11):: and ((this()) or (that()))'
1948 >>> formatspec('%d:: and not %d::', 10, 20)
1946 >>> formatspec('%d:: and not %d::', 10, 20)
1949 '10:: and not 20::'
1947 '10:: and not 20::'
1950 >>> formatspec('%ld or %ld', [], [1])
1948 >>> formatspec('%ld or %ld', [], [1])
1951 "_list('') or 1"
1949 "_list('') or 1"
1952 >>> formatspec('keyword(%s)', 'foo\\xe9')
1950 >>> formatspec('keyword(%s)', 'foo\\xe9')
1953 "keyword('foo\\\\xe9')"
1951 "keyword('foo\\\\xe9')"
1954 >>> b = lambda: 'default'
1952 >>> b = lambda: 'default'
1955 >>> b.branch = b
1953 >>> b.branch = b
1956 >>> formatspec('branch(%b)', b)
1954 >>> formatspec('branch(%b)', b)
1957 "branch('default')"
1955 "branch('default')"
1958 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1956 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1959 "root(_list('a\\x00b\\x00c\\x00d'))"
1957 "root(_list('a\\x00b\\x00c\\x00d'))"
1960 '''
1958 '''
1961
1959
1962 def quote(s):
1960 def quote(s):
1963 return repr(str(s))
1961 return repr(str(s))
1964
1962
1965 def argtype(c, arg):
1963 def argtype(c, arg):
1966 if c == 'd':
1964 if c == 'd':
1967 return str(int(arg))
1965 return str(int(arg))
1968 elif c == 's':
1966 elif c == 's':
1969 return quote(arg)
1967 return quote(arg)
1970 elif c == 'r':
1968 elif c == 'r':
1971 parse(arg) # make sure syntax errors are confined
1969 parse(arg) # make sure syntax errors are confined
1972 return '(%s)' % arg
1970 return '(%s)' % arg
1973 elif c == 'n':
1971 elif c == 'n':
1974 return quote(node.hex(arg))
1972 return quote(node.hex(arg))
1975 elif c == 'b':
1973 elif c == 'b':
1976 return quote(arg.branch())
1974 return quote(arg.branch())
1977
1975
1978 def listexp(s, t):
1976 def listexp(s, t):
1979 l = len(s)
1977 l = len(s)
1980 if l == 0:
1978 if l == 0:
1981 return "_list('')"
1979 return "_list('')"
1982 elif l == 1:
1980 elif l == 1:
1983 return argtype(t, s[0])
1981 return argtype(t, s[0])
1984 elif t == 'd':
1982 elif t == 'd':
1985 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1983 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1986 elif t == 's':
1984 elif t == 's':
1987 return "_list('%s')" % "\0".join(s)
1985 return "_list('%s')" % "\0".join(s)
1988 elif t == 'n':
1986 elif t == 'n':
1989 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1987 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1990 elif t == 'b':
1988 elif t == 'b':
1991 return "_list('%s')" % "\0".join(a.branch() for a in s)
1989 return "_list('%s')" % "\0".join(a.branch() for a in s)
1992
1990
1993 m = l // 2
1991 m = l // 2
1994 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1992 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1995
1993
1996 ret = ''
1994 ret = ''
1997 pos = 0
1995 pos = 0
1998 arg = 0
1996 arg = 0
1999 while pos < len(expr):
1997 while pos < len(expr):
2000 c = expr[pos]
1998 c = expr[pos]
2001 if c == '%':
1999 if c == '%':
2002 pos += 1
2000 pos += 1
2003 d = expr[pos]
2001 d = expr[pos]
2004 if d == '%':
2002 if d == '%':
2005 ret += d
2003 ret += d
2006 elif d in 'dsnbr':
2004 elif d in 'dsnbr':
2007 ret += argtype(d, args[arg])
2005 ret += argtype(d, args[arg])
2008 arg += 1
2006 arg += 1
2009 elif d == 'l':
2007 elif d == 'l':
2010 # a list of some type
2008 # a list of some type
2011 pos += 1
2009 pos += 1
2012 d = expr[pos]
2010 d = expr[pos]
2013 ret += listexp(list(args[arg]), d)
2011 ret += listexp(list(args[arg]), d)
2014 arg += 1
2012 arg += 1
2015 else:
2013 else:
2016 raise util.Abort('unexpected revspec format character %s' % d)
2014 raise util.Abort('unexpected revspec format character %s' % d)
2017 else:
2015 else:
2018 ret += c
2016 ret += c
2019 pos += 1
2017 pos += 1
2020
2018
2021 return ret
2019 return ret
2022
2020
2023 def prettyformat(tree):
2021 def prettyformat(tree):
2024 def _prettyformat(tree, level, lines):
2022 def _prettyformat(tree, level, lines):
2025 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2023 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2026 lines.append((level, str(tree)))
2024 lines.append((level, str(tree)))
2027 else:
2025 else:
2028 lines.append((level, '(%s' % tree[0]))
2026 lines.append((level, '(%s' % tree[0]))
2029 for s in tree[1:]:
2027 for s in tree[1:]:
2030 _prettyformat(s, level + 1, lines)
2028 _prettyformat(s, level + 1, lines)
2031 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2029 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2032
2030
2033 lines = []
2031 lines = []
2034 _prettyformat(tree, 0, lines)
2032 _prettyformat(tree, 0, lines)
2035 output = '\n'.join((' '*l + s) for l, s in lines)
2033 output = '\n'.join((' '*l + s) for l, s in lines)
2036 return output
2034 return output
2037
2035
2038 def depth(tree):
2036 def depth(tree):
2039 if isinstance(tree, tuple):
2037 if isinstance(tree, tuple):
2040 return max(map(depth, tree)) + 1
2038 return max(map(depth, tree)) + 1
2041 else:
2039 else:
2042 return 0
2040 return 0
2043
2041
2044 def funcsused(tree):
2042 def funcsused(tree):
2045 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2043 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2046 return set()
2044 return set()
2047 else:
2045 else:
2048 funcs = set()
2046 funcs = set()
2049 for s in tree[1:]:
2047 for s in tree[1:]:
2050 funcs |= funcsused(s)
2048 funcs |= funcsused(s)
2051 if tree[0] == 'func':
2049 if tree[0] == 'func':
2052 funcs.add(tree[1][1])
2050 funcs.add(tree[1][1])
2053 return funcs
2051 return funcs
2054
2052
2055 class baseset(list):
2053 class baseset(list):
2056 """Basic data structure that represents a revset and contains the basic
2054 """Basic data structure that represents a revset and contains the basic
2057 operation that it should be able to perform.
2055 operation that it should be able to perform.
2058 """
2056 """
2059 def __init__(self, data):
2057 def __init__(self, data):
2060 super(baseset, self).__init__(data)
2058 super(baseset, self).__init__(data)
2061 self._set = None
2059 self._set = None
2062
2060
2063 def set(self):
2061 def set(self):
2064 if not self._set:
2062 if not self._set:
2065 self._set = set(self)
2063 self._set = set(self)
2066 return self._set
2064 return self._set
2067
2065
2068 def __sub__(self, x):
2066 def __sub__(self, x):
2069 if isinstance(x, baseset):
2067 if isinstance(x, baseset):
2070 s = x.set()
2068 s = x.set()
2071 else:
2069 else:
2072 s = set(x)
2070 s = set(x)
2073 return baseset(self.set() - s)
2071 return baseset(self.set() - s)
2074
2072
2075 def __and__(self, x):
2073 def __and__(self, x):
2076 if isinstance(x, baseset):
2074 if isinstance(x, baseset):
2077 x = x.set()
2075 x = x.set()
2078 return baseset([y for y in self if y in x])
2076 return baseset([y for y in self if y in x])
2079
2077
2080 def __add__(self, x):
2078 def __add__(self, x):
2081 s = self.set()
2079 s = self.set()
2082 l = [r for r in x if r not in s]
2080 l = [r for r in x if r not in s]
2083 return baseset(list(self) + l)
2081 return baseset(list(self) + l)
2084
2082
2085 class lazyset(object):
2083 class lazyset(object):
2086 """Duck type for baseset class which iterates lazily over the revisions in
2084 """Duck type for baseset class which iterates lazily over the revisions in
2087 the subset and contains a function which tests for membership in the
2085 the subset and contains a function which tests for membership in the
2088 revset
2086 revset
2089 """
2087 """
2090 def __init__(self, subset, condition):
2088 def __init__(self, subset, condition):
2091 self._subset = subset
2089 self._subset = subset
2092 self._condition = condition
2090 self._condition = condition
2093
2091
2094 def __contains__(self, x):
2092 def __contains__(self, x):
2095 return x in self._subset and self._condition(x)
2093 return x in self._subset and self._condition(x)
2096
2094
2097 def __iter__(self):
2095 def __iter__(self):
2098 cond = self._condition
2096 cond = self._condition
2099 for x in self._subset:
2097 for x in self._subset:
2100 if cond(x):
2098 if cond(x):
2101 yield x
2099 yield x
2102
2100
2103 def __and__(self, x):
2101 def __and__(self, x):
2104 return lazyset(self, lambda r: r in x)
2102 return lazyset(self, lambda r: r in x)
2105
2103
2106 def __sub__(self, x):
2104 def __sub__(self, x):
2107 return lazyset(self, lambda r: r not in x)
2105 return lazyset(self, lambda r: r not in x)
2108
2106
2109 def __add__(self, x):
2107 def __add__(self, x):
2110 l = baseset([r for r in self])
2108 l = baseset([r for r in self])
2111 return l + baseset(x)
2109 return l + baseset(x)
2112
2110
2113 def __len__(self):
2111 def __len__(self):
2114 # Basic implementation to be changed in future patches.
2112 # Basic implementation to be changed in future patches.
2115 l = baseset([r for r in self])
2113 l = baseset([r for r in self])
2116 return len(l)
2114 return len(l)
2117
2115
2118 def __getitem__(self, x):
2116 def __getitem__(self, x):
2119 # Basic implementation to be changed in future patches.
2117 # Basic implementation to be changed in future patches.
2120 l = baseset([r for r in self])
2118 l = baseset([r for r in self])
2121 return l[x]
2119 return l[x]
2122
2120
2123 def sort(self, reverse=False):
2121 def sort(self, reverse=False):
2124 # Basic implementation to be changed in future patches.
2122 # Basic implementation to be changed in future patches.
2125 self._subset = baseset(self._subset)
2123 self._subset = baseset(self._subset)
2126 self._subset.sort(reverse=reverse)
2124 self._subset.sort(reverse=reverse)
2127
2125
2128 def reverse(self):
2126 def reverse(self):
2129 self._subset.reverse()
2127 self._subset.reverse()
2130
2128
2131 def set(self):
2129 def set(self):
2132 return set([r for r in self])
2130 return set([r for r in self])
2133
2131
2134 # tell hggettext to extract docstrings from these functions:
2132 # tell hggettext to extract docstrings from these functions:
2135 i18nfunctions = symbols.values()
2133 i18nfunctions = symbols.values()
General Comments 0
You need to be logged in to leave comments. Login now