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