##// END OF EJS Templates
revset: introduce "_parsealiasdecl" to parse alias declarations strictly...
FUJIWARA Katsunori -
r23845:0a7fd54d default
parent child Browse files
Show More
@@ -1,3173 +1,3288 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 heapq
11 import heapq
12 import match as matchmod
12 import match as matchmod
13 from i18n import _
13 from i18n import _
14 import encoding
14 import encoding
15 import obsolete as obsmod
15 import obsolete as obsmod
16 import pathutil
16 import pathutil
17 import repoview
17 import repoview
18
18
19 def _revancestors(repo, revs, followfirst):
19 def _revancestors(repo, revs, followfirst):
20 """Like revlog.ancestors(), but supports followfirst."""
20 """Like revlog.ancestors(), but supports followfirst."""
21 cut = followfirst and 1 or None
21 cut = followfirst and 1 or None
22 cl = repo.changelog
22 cl = repo.changelog
23
23
24 def iterate():
24 def iterate():
25 revqueue, revsnode = None, None
25 revqueue, revsnode = None, None
26 h = []
26 h = []
27
27
28 revs.sort(reverse=True)
28 revs.sort(reverse=True)
29 revqueue = util.deque(revs)
29 revqueue = util.deque(revs)
30 if revqueue:
30 if revqueue:
31 revsnode = revqueue.popleft()
31 revsnode = revqueue.popleft()
32 heapq.heappush(h, -revsnode)
32 heapq.heappush(h, -revsnode)
33
33
34 seen = set([node.nullrev])
34 seen = set([node.nullrev])
35 while h:
35 while h:
36 current = -heapq.heappop(h)
36 current = -heapq.heappop(h)
37 if current not in seen:
37 if current not in seen:
38 if revsnode and current == revsnode:
38 if revsnode and current == revsnode:
39 if revqueue:
39 if revqueue:
40 revsnode = revqueue.popleft()
40 revsnode = revqueue.popleft()
41 heapq.heappush(h, -revsnode)
41 heapq.heappush(h, -revsnode)
42 seen.add(current)
42 seen.add(current)
43 yield current
43 yield current
44 for parent in cl.parentrevs(current)[:cut]:
44 for parent in cl.parentrevs(current)[:cut]:
45 if parent != node.nullrev:
45 if parent != node.nullrev:
46 heapq.heappush(h, -parent)
46 heapq.heappush(h, -parent)
47
47
48 return generatorset(iterate(), iterasc=False)
48 return generatorset(iterate(), iterasc=False)
49
49
50 def _revdescendants(repo, revs, followfirst):
50 def _revdescendants(repo, revs, followfirst):
51 """Like revlog.descendants() but supports followfirst."""
51 """Like revlog.descendants() but supports followfirst."""
52 cut = followfirst and 1 or None
52 cut = followfirst and 1 or None
53
53
54 def iterate():
54 def iterate():
55 cl = repo.changelog
55 cl = repo.changelog
56 first = min(revs)
56 first = min(revs)
57 nullrev = node.nullrev
57 nullrev = node.nullrev
58 if first == nullrev:
58 if first == nullrev:
59 # Are there nodes with a null first parent and a non-null
59 # Are there nodes with a null first parent and a non-null
60 # second one? Maybe. Do we care? Probably not.
60 # second one? Maybe. Do we care? Probably not.
61 for i in cl:
61 for i in cl:
62 yield i
62 yield i
63 else:
63 else:
64 seen = set(revs)
64 seen = set(revs)
65 for i in cl.revs(first + 1):
65 for i in cl.revs(first + 1):
66 for x in cl.parentrevs(i)[:cut]:
66 for x in cl.parentrevs(i)[:cut]:
67 if x != nullrev and x in seen:
67 if x != nullrev and x in seen:
68 seen.add(i)
68 seen.add(i)
69 yield i
69 yield i
70 break
70 break
71
71
72 return generatorset(iterate(), iterasc=True)
72 return generatorset(iterate(), iterasc=True)
73
73
74 def _revsbetween(repo, roots, heads):
74 def _revsbetween(repo, roots, heads):
75 """Return all paths between roots and heads, inclusive of both endpoint
75 """Return all paths between roots and heads, inclusive of both endpoint
76 sets."""
76 sets."""
77 if not roots:
77 if not roots:
78 return baseset()
78 return baseset()
79 parentrevs = repo.changelog.parentrevs
79 parentrevs = repo.changelog.parentrevs
80 visit = list(heads)
80 visit = list(heads)
81 reachable = set()
81 reachable = set()
82 seen = {}
82 seen = {}
83 minroot = min(roots)
83 minroot = min(roots)
84 roots = set(roots)
84 roots = set(roots)
85 # open-code the post-order traversal due to the tiny size of
85 # open-code the post-order traversal due to the tiny size of
86 # sys.getrecursionlimit()
86 # sys.getrecursionlimit()
87 while visit:
87 while visit:
88 rev = visit.pop()
88 rev = visit.pop()
89 if rev in roots:
89 if rev in roots:
90 reachable.add(rev)
90 reachable.add(rev)
91 parents = parentrevs(rev)
91 parents = parentrevs(rev)
92 seen[rev] = parents
92 seen[rev] = parents
93 for parent in parents:
93 for parent in parents:
94 if parent >= minroot and parent not in seen:
94 if parent >= minroot and parent not in seen:
95 visit.append(parent)
95 visit.append(parent)
96 if not reachable:
96 if not reachable:
97 return baseset()
97 return baseset()
98 for rev in sorted(seen):
98 for rev in sorted(seen):
99 for parent in seen[rev]:
99 for parent in seen[rev]:
100 if parent in reachable:
100 if parent in reachable:
101 reachable.add(rev)
101 reachable.add(rev)
102 return baseset(sorted(reachable))
102 return baseset(sorted(reachable))
103
103
104 elements = {
104 elements = {
105 "(": (21, ("group", 1, ")"), ("func", 1, ")")),
105 "(": (21, ("group", 1, ")"), ("func", 1, ")")),
106 "##": (20, None, ("_concat", 20)),
106 "##": (20, None, ("_concat", 20)),
107 "~": (18, None, ("ancestor", 18)),
107 "~": (18, None, ("ancestor", 18)),
108 "^": (18, None, ("parent", 18), ("parentpost", 18)),
108 "^": (18, None, ("parent", 18), ("parentpost", 18)),
109 "-": (5, ("negate", 19), ("minus", 5)),
109 "-": (5, ("negate", 19), ("minus", 5)),
110 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
110 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
111 ("dagrangepost", 17)),
111 ("dagrangepost", 17)),
112 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
112 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
113 ("dagrangepost", 17)),
113 ("dagrangepost", 17)),
114 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
114 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
115 "not": (10, ("not", 10)),
115 "not": (10, ("not", 10)),
116 "!": (10, ("not", 10)),
116 "!": (10, ("not", 10)),
117 "and": (5, None, ("and", 5)),
117 "and": (5, None, ("and", 5)),
118 "&": (5, None, ("and", 5)),
118 "&": (5, None, ("and", 5)),
119 "%": (5, None, ("only", 5), ("onlypost", 5)),
119 "%": (5, None, ("only", 5), ("onlypost", 5)),
120 "or": (4, None, ("or", 4)),
120 "or": (4, None, ("or", 4)),
121 "|": (4, None, ("or", 4)),
121 "|": (4, None, ("or", 4)),
122 "+": (4, None, ("or", 4)),
122 "+": (4, None, ("or", 4)),
123 ",": (2, None, ("list", 2)),
123 ",": (2, None, ("list", 2)),
124 ")": (0, None, None),
124 ")": (0, None, None),
125 "symbol": (0, ("symbol",), None),
125 "symbol": (0, ("symbol",), None),
126 "string": (0, ("string",), None),
126 "string": (0, ("string",), None),
127 "end": (0, None, None),
127 "end": (0, None, None),
128 }
128 }
129
129
130 keywords = set(['and', 'or', 'not'])
130 keywords = set(['and', 'or', 'not'])
131
131
132 # default set of valid characters for the initial letter of symbols
132 # default set of valid characters for the initial letter of symbols
133 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
133 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
134 if c.isalnum() or c in '._@' or ord(c) > 127)
134 if c.isalnum() or c in '._@' or ord(c) > 127)
135
135
136 # default set of valid characters for non-initial letters of symbols
136 # default set of valid characters for non-initial letters of symbols
137 _symletters = set(c for c in [chr(i) for i in xrange(256)]
137 _symletters = set(c for c in [chr(i) for i in xrange(256)]
138 if c.isalnum() or c in '-._/@' or ord(c) > 127)
138 if c.isalnum() or c in '-._/@' or ord(c) > 127)
139
139
140 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
140 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
141 '''
141 '''
142 Parse a revset statement into a stream of tokens
142 Parse a revset statement into a stream of tokens
143
143
144 ``syminitletters`` is the set of valid characters for the initial
144 ``syminitletters`` is the set of valid characters for the initial
145 letter of symbols.
145 letter of symbols.
146
146
147 By default, character ``c`` is recognized as valid for initial
147 By default, character ``c`` is recognized as valid for initial
148 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
148 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
149
149
150 ``symletters`` is the set of valid characters for non-initial
150 ``symletters`` is the set of valid characters for non-initial
151 letters of symbols.
151 letters of symbols.
152
152
153 By default, character ``c`` is recognized as valid for non-initial
153 By default, character ``c`` is recognized as valid for non-initial
154 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
154 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
155
155
156 Check that @ is a valid unquoted token character (issue3686):
156 Check that @ is a valid unquoted token character (issue3686):
157 >>> list(tokenize("@::"))
157 >>> list(tokenize("@::"))
158 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
158 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
159
159
160 '''
160 '''
161 if syminitletters is None:
161 if syminitletters is None:
162 syminitletters = _syminitletters
162 syminitletters = _syminitletters
163 if symletters is None:
163 if symletters is None:
164 symletters = _symletters
164 symletters = _symletters
165
165
166 pos, l = 0, len(program)
166 pos, l = 0, len(program)
167 while pos < l:
167 while pos < l:
168 c = program[pos]
168 c = program[pos]
169 if c.isspace(): # skip inter-token whitespace
169 if c.isspace(): # skip inter-token whitespace
170 pass
170 pass
171 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
171 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
172 yield ('::', None, pos)
172 yield ('::', None, pos)
173 pos += 1 # skip ahead
173 pos += 1 # skip ahead
174 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
174 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
175 yield ('..', None, pos)
175 yield ('..', None, pos)
176 pos += 1 # skip ahead
176 pos += 1 # skip ahead
177 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
177 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
178 yield ('##', None, pos)
178 yield ('##', None, pos)
179 pos += 1 # skip ahead
179 pos += 1 # skip ahead
180 elif c in "():,-|&+!~^%": # handle simple operators
180 elif c in "():,-|&+!~^%": # handle simple operators
181 yield (c, None, pos)
181 yield (c, None, pos)
182 elif (c in '"\'' or c == 'r' and
182 elif (c in '"\'' or c == 'r' and
183 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
183 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
184 if c == 'r':
184 if c == 'r':
185 pos += 1
185 pos += 1
186 c = program[pos]
186 c = program[pos]
187 decode = lambda x: x
187 decode = lambda x: x
188 else:
188 else:
189 decode = lambda x: x.decode('string-escape')
189 decode = lambda x: x.decode('string-escape')
190 pos += 1
190 pos += 1
191 s = pos
191 s = pos
192 while pos < l: # find closing quote
192 while pos < l: # find closing quote
193 d = program[pos]
193 d = program[pos]
194 if d == '\\': # skip over escaped characters
194 if d == '\\': # skip over escaped characters
195 pos += 2
195 pos += 2
196 continue
196 continue
197 if d == c:
197 if d == c:
198 yield ('string', decode(program[s:pos]), s)
198 yield ('string', decode(program[s:pos]), s)
199 break
199 break
200 pos += 1
200 pos += 1
201 else:
201 else:
202 raise error.ParseError(_("unterminated string"), s)
202 raise error.ParseError(_("unterminated string"), s)
203 # gather up a symbol/keyword
203 # gather up a symbol/keyword
204 elif c in syminitletters:
204 elif c in syminitletters:
205 s = pos
205 s = pos
206 pos += 1
206 pos += 1
207 while pos < l: # find end of symbol
207 while pos < l: # find end of symbol
208 d = program[pos]
208 d = program[pos]
209 if d not in symletters:
209 if d not in symletters:
210 break
210 break
211 if d == '.' and program[pos - 1] == '.': # special case for ..
211 if d == '.' and program[pos - 1] == '.': # special case for ..
212 pos -= 1
212 pos -= 1
213 break
213 break
214 pos += 1
214 pos += 1
215 sym = program[s:pos]
215 sym = program[s:pos]
216 if sym in keywords: # operator keywords
216 if sym in keywords: # operator keywords
217 yield (sym, None, s)
217 yield (sym, None, s)
218 elif '-' in sym:
218 elif '-' in sym:
219 # some jerk gave us foo-bar-baz, try to check if it's a symbol
219 # some jerk gave us foo-bar-baz, try to check if it's a symbol
220 if lookup and lookup(sym):
220 if lookup and lookup(sym):
221 # looks like a real symbol
221 # looks like a real symbol
222 yield ('symbol', sym, s)
222 yield ('symbol', sym, s)
223 else:
223 else:
224 # looks like an expression
224 # looks like an expression
225 parts = sym.split('-')
225 parts = sym.split('-')
226 for p in parts[:-1]:
226 for p in parts[:-1]:
227 if p: # possible consecutive -
227 if p: # possible consecutive -
228 yield ('symbol', p, s)
228 yield ('symbol', p, s)
229 s += len(p)
229 s += len(p)
230 yield ('-', None, pos)
230 yield ('-', None, pos)
231 s += 1
231 s += 1
232 if parts[-1]: # possible trailing -
232 if parts[-1]: # possible trailing -
233 yield ('symbol', parts[-1], s)
233 yield ('symbol', parts[-1], s)
234 else:
234 else:
235 yield ('symbol', sym, s)
235 yield ('symbol', sym, s)
236 pos -= 1
236 pos -= 1
237 else:
237 else:
238 raise error.ParseError(_("syntax error"), pos)
238 raise error.ParseError(_("syntax error"), pos)
239 pos += 1
239 pos += 1
240 yield ('end', None, pos)
240 yield ('end', None, pos)
241
241
242 def parseerrordetail(inst):
242 def parseerrordetail(inst):
243 """Compose error message from specified ParseError object
243 """Compose error message from specified ParseError object
244 """
244 """
245 if len(inst.args) > 1:
245 if len(inst.args) > 1:
246 return _('at %s: %s') % (inst.args[1], inst.args[0])
246 return _('at %s: %s') % (inst.args[1], inst.args[0])
247 else:
247 else:
248 return inst.args[0]
248 return inst.args[0]
249
249
250 # helpers
250 # helpers
251
251
252 def getstring(x, err):
252 def getstring(x, err):
253 if x and (x[0] == 'string' or x[0] == 'symbol'):
253 if x and (x[0] == 'string' or x[0] == 'symbol'):
254 return x[1]
254 return x[1]
255 raise error.ParseError(err)
255 raise error.ParseError(err)
256
256
257 def getlist(x):
257 def getlist(x):
258 if not x:
258 if not x:
259 return []
259 return []
260 if x[0] == 'list':
260 if x[0] == 'list':
261 return getlist(x[1]) + [x[2]]
261 return getlist(x[1]) + [x[2]]
262 return [x]
262 return [x]
263
263
264 def getargs(x, min, max, err):
264 def getargs(x, min, max, err):
265 l = getlist(x)
265 l = getlist(x)
266 if len(l) < min or (max >= 0 and len(l) > max):
266 if len(l) < min or (max >= 0 and len(l) > max):
267 raise error.ParseError(err)
267 raise error.ParseError(err)
268 return l
268 return l
269
269
270 def isvalidsymbol(tree):
271 """Examine whether specified ``tree`` is valid ``symbol`` or not
272 """
273 return tree[0] == 'symbol' and len(tree) > 1
274
275 def getsymbol(tree):
276 """Get symbol name from valid ``symbol`` in ``tree``
277
278 This assumes that ``tree`` is already examined by ``isvalidsymbol``.
279 """
280 return tree[1]
281
282 def isvalidfunc(tree):
283 """Examine whether specified ``tree`` is valid ``func`` or not
284 """
285 return tree[0] == 'func' and len(tree) > 1 and isvalidsymbol(tree[1])
286
287 def getfuncname(tree):
288 """Get function name from valid ``func`` in ``tree``
289
290 This assumes that ``tree`` is already examined by ``isvalidfunc``.
291 """
292 return getsymbol(tree[1])
293
294 def getfuncargs(tree):
295 """Get list of function arguments from valid ``func`` in ``tree``
296
297 This assumes that ``tree`` is already examined by ``isvalidfunc``.
298 """
299 if len(tree) > 2:
300 return getlist(tree[2])
301 else:
302 return []
303
270 def getset(repo, subset, x):
304 def getset(repo, subset, x):
271 if not x:
305 if not x:
272 raise error.ParseError(_("missing argument"))
306 raise error.ParseError(_("missing argument"))
273 s = methods[x[0]](repo, subset, *x[1:])
307 s = methods[x[0]](repo, subset, *x[1:])
274 if util.safehasattr(s, 'isascending'):
308 if util.safehasattr(s, 'isascending'):
275 return s
309 return s
276 return baseset(s)
310 return baseset(s)
277
311
278 def _getrevsource(repo, r):
312 def _getrevsource(repo, r):
279 extra = repo[r].extra()
313 extra = repo[r].extra()
280 for label in ('source', 'transplant_source', 'rebase_source'):
314 for label in ('source', 'transplant_source', 'rebase_source'):
281 if label in extra:
315 if label in extra:
282 try:
316 try:
283 return repo[extra[label]].rev()
317 return repo[extra[label]].rev()
284 except error.RepoLookupError:
318 except error.RepoLookupError:
285 pass
319 pass
286 return None
320 return None
287
321
288 # operator methods
322 # operator methods
289
323
290 def stringset(repo, subset, x):
324 def stringset(repo, subset, x):
291 x = repo[x].rev()
325 x = repo[x].rev()
292 if x == -1 and len(subset) == len(repo):
326 if x == -1 and len(subset) == len(repo):
293 return baseset([-1])
327 return baseset([-1])
294 if x in subset:
328 if x in subset:
295 return baseset([x])
329 return baseset([x])
296 return baseset()
330 return baseset()
297
331
298 def symbolset(repo, subset, x):
332 def symbolset(repo, subset, x):
299 if x in symbols:
333 if x in symbols:
300 raise error.ParseError(_("can't use %s here") % x)
334 raise error.ParseError(_("can't use %s here") % x)
301 return stringset(repo, subset, x)
335 return stringset(repo, subset, x)
302
336
303 def rangeset(repo, subset, x, y):
337 def rangeset(repo, subset, x, y):
304 m = getset(repo, fullreposet(repo), x)
338 m = getset(repo, fullreposet(repo), x)
305 n = getset(repo, fullreposet(repo), y)
339 n = getset(repo, fullreposet(repo), y)
306
340
307 if not m or not n:
341 if not m or not n:
308 return baseset()
342 return baseset()
309 m, n = m.first(), n.last()
343 m, n = m.first(), n.last()
310
344
311 if m < n:
345 if m < n:
312 r = spanset(repo, m, n + 1)
346 r = spanset(repo, m, n + 1)
313 else:
347 else:
314 r = spanset(repo, m, n - 1)
348 r = spanset(repo, m, n - 1)
315 return r & subset
349 return r & subset
316
350
317 def dagrange(repo, subset, x, y):
351 def dagrange(repo, subset, x, y):
318 r = spanset(repo)
352 r = spanset(repo)
319 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
353 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
320 return xs & subset
354 return xs & subset
321
355
322 def andset(repo, subset, x, y):
356 def andset(repo, subset, x, y):
323 return getset(repo, getset(repo, subset, x), y)
357 return getset(repo, getset(repo, subset, x), y)
324
358
325 def orset(repo, subset, x, y):
359 def orset(repo, subset, x, y):
326 xl = getset(repo, subset, x)
360 xl = getset(repo, subset, x)
327 yl = getset(repo, subset - xl, y)
361 yl = getset(repo, subset - xl, y)
328 return xl + yl
362 return xl + yl
329
363
330 def notset(repo, subset, x):
364 def notset(repo, subset, x):
331 return subset - getset(repo, subset, x)
365 return subset - getset(repo, subset, x)
332
366
333 def listset(repo, subset, a, b):
367 def listset(repo, subset, a, b):
334 raise error.ParseError(_("can't use a list in this context"))
368 raise error.ParseError(_("can't use a list in this context"))
335
369
336 def func(repo, subset, a, b):
370 def func(repo, subset, a, b):
337 if a[0] == 'symbol' and a[1] in symbols:
371 if a[0] == 'symbol' and a[1] in symbols:
338 return symbols[a[1]](repo, subset, b)
372 return symbols[a[1]](repo, subset, b)
339 raise error.ParseError(_("not a function: %s") % a[1])
373 raise error.ParseError(_("not a function: %s") % a[1])
340
374
341 # functions
375 # functions
342
376
343 def adds(repo, subset, x):
377 def adds(repo, subset, x):
344 """``adds(pattern)``
378 """``adds(pattern)``
345 Changesets that add a file matching pattern.
379 Changesets that add a file matching pattern.
346
380
347 The pattern without explicit kind like ``glob:`` is expected to be
381 The pattern without explicit kind like ``glob:`` is expected to be
348 relative to the current directory and match against a file or a
382 relative to the current directory and match against a file or a
349 directory.
383 directory.
350 """
384 """
351 # i18n: "adds" is a keyword
385 # i18n: "adds" is a keyword
352 pat = getstring(x, _("adds requires a pattern"))
386 pat = getstring(x, _("adds requires a pattern"))
353 return checkstatus(repo, subset, pat, 1)
387 return checkstatus(repo, subset, pat, 1)
354
388
355 def ancestor(repo, subset, x):
389 def ancestor(repo, subset, x):
356 """``ancestor(*changeset)``
390 """``ancestor(*changeset)``
357 A greatest common ancestor of the changesets.
391 A greatest common ancestor of the changesets.
358
392
359 Accepts 0 or more changesets.
393 Accepts 0 or more changesets.
360 Will return empty list when passed no args.
394 Will return empty list when passed no args.
361 Greatest common ancestor of a single changeset is that changeset.
395 Greatest common ancestor of a single changeset is that changeset.
362 """
396 """
363 # i18n: "ancestor" is a keyword
397 # i18n: "ancestor" is a keyword
364 l = getlist(x)
398 l = getlist(x)
365 rl = spanset(repo)
399 rl = spanset(repo)
366 anc = None
400 anc = None
367
401
368 # (getset(repo, rl, i) for i in l) generates a list of lists
402 # (getset(repo, rl, i) for i in l) generates a list of lists
369 for revs in (getset(repo, rl, i) for i in l):
403 for revs in (getset(repo, rl, i) for i in l):
370 for r in revs:
404 for r in revs:
371 if anc is None:
405 if anc is None:
372 anc = repo[r]
406 anc = repo[r]
373 else:
407 else:
374 anc = anc.ancestor(repo[r])
408 anc = anc.ancestor(repo[r])
375
409
376 if anc is not None and anc.rev() in subset:
410 if anc is not None and anc.rev() in subset:
377 return baseset([anc.rev()])
411 return baseset([anc.rev()])
378 return baseset()
412 return baseset()
379
413
380 def _ancestors(repo, subset, x, followfirst=False):
414 def _ancestors(repo, subset, x, followfirst=False):
381 heads = getset(repo, spanset(repo), x)
415 heads = getset(repo, spanset(repo), x)
382 if not heads:
416 if not heads:
383 return baseset()
417 return baseset()
384 s = _revancestors(repo, heads, followfirst)
418 s = _revancestors(repo, heads, followfirst)
385 return subset & s
419 return subset & s
386
420
387 def ancestors(repo, subset, x):
421 def ancestors(repo, subset, x):
388 """``ancestors(set)``
422 """``ancestors(set)``
389 Changesets that are ancestors of a changeset in set.
423 Changesets that are ancestors of a changeset in set.
390 """
424 """
391 return _ancestors(repo, subset, x)
425 return _ancestors(repo, subset, x)
392
426
393 def _firstancestors(repo, subset, x):
427 def _firstancestors(repo, subset, x):
394 # ``_firstancestors(set)``
428 # ``_firstancestors(set)``
395 # Like ``ancestors(set)`` but follows only the first parents.
429 # Like ``ancestors(set)`` but follows only the first parents.
396 return _ancestors(repo, subset, x, followfirst=True)
430 return _ancestors(repo, subset, x, followfirst=True)
397
431
398 def ancestorspec(repo, subset, x, n):
432 def ancestorspec(repo, subset, x, n):
399 """``set~n``
433 """``set~n``
400 Changesets that are the Nth ancestor (first parents only) of a changeset
434 Changesets that are the Nth ancestor (first parents only) of a changeset
401 in set.
435 in set.
402 """
436 """
403 try:
437 try:
404 n = int(n[1])
438 n = int(n[1])
405 except (TypeError, ValueError):
439 except (TypeError, ValueError):
406 raise error.ParseError(_("~ expects a number"))
440 raise error.ParseError(_("~ expects a number"))
407 ps = set()
441 ps = set()
408 cl = repo.changelog
442 cl = repo.changelog
409 for r in getset(repo, fullreposet(repo), x):
443 for r in getset(repo, fullreposet(repo), x):
410 for i in range(n):
444 for i in range(n):
411 r = cl.parentrevs(r)[0]
445 r = cl.parentrevs(r)[0]
412 ps.add(r)
446 ps.add(r)
413 return subset & ps
447 return subset & ps
414
448
415 def author(repo, subset, x):
449 def author(repo, subset, x):
416 """``author(string)``
450 """``author(string)``
417 Alias for ``user(string)``.
451 Alias for ``user(string)``.
418 """
452 """
419 # i18n: "author" is a keyword
453 # i18n: "author" is a keyword
420 n = encoding.lower(getstring(x, _("author requires a string")))
454 n = encoding.lower(getstring(x, _("author requires a string")))
421 kind, pattern, matcher = _substringmatcher(n)
455 kind, pattern, matcher = _substringmatcher(n)
422 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())))
456 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())))
423
457
424 def bisect(repo, subset, x):
458 def bisect(repo, subset, x):
425 """``bisect(string)``
459 """``bisect(string)``
426 Changesets marked in the specified bisect status:
460 Changesets marked in the specified bisect status:
427
461
428 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
462 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
429 - ``goods``, ``bads`` : csets topologically good/bad
463 - ``goods``, ``bads`` : csets topologically good/bad
430 - ``range`` : csets taking part in the bisection
464 - ``range`` : csets taking part in the bisection
431 - ``pruned`` : csets that are goods, bads or skipped
465 - ``pruned`` : csets that are goods, bads or skipped
432 - ``untested`` : csets whose fate is yet unknown
466 - ``untested`` : csets whose fate is yet unknown
433 - ``ignored`` : csets ignored due to DAG topology
467 - ``ignored`` : csets ignored due to DAG topology
434 - ``current`` : the cset currently being bisected
468 - ``current`` : the cset currently being bisected
435 """
469 """
436 # i18n: "bisect" is a keyword
470 # i18n: "bisect" is a keyword
437 status = getstring(x, _("bisect requires a string")).lower()
471 status = getstring(x, _("bisect requires a string")).lower()
438 state = set(hbisect.get(repo, status))
472 state = set(hbisect.get(repo, status))
439 return subset & state
473 return subset & state
440
474
441 # Backward-compatibility
475 # Backward-compatibility
442 # - no help entry so that we do not advertise it any more
476 # - no help entry so that we do not advertise it any more
443 def bisected(repo, subset, x):
477 def bisected(repo, subset, x):
444 return bisect(repo, subset, x)
478 return bisect(repo, subset, x)
445
479
446 def bookmark(repo, subset, x):
480 def bookmark(repo, subset, x):
447 """``bookmark([name])``
481 """``bookmark([name])``
448 The named bookmark or all bookmarks.
482 The named bookmark or all bookmarks.
449
483
450 If `name` starts with `re:`, the remainder of the name is treated as
484 If `name` starts with `re:`, the remainder of the name is treated as
451 a regular expression. To match a bookmark that actually starts with `re:`,
485 a regular expression. To match a bookmark that actually starts with `re:`,
452 use the prefix `literal:`.
486 use the prefix `literal:`.
453 """
487 """
454 # i18n: "bookmark" is a keyword
488 # i18n: "bookmark" is a keyword
455 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
489 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
456 if args:
490 if args:
457 bm = getstring(args[0],
491 bm = getstring(args[0],
458 # i18n: "bookmark" is a keyword
492 # i18n: "bookmark" is a keyword
459 _('the argument to bookmark must be a string'))
493 _('the argument to bookmark must be a string'))
460 kind, pattern, matcher = _stringmatcher(bm)
494 kind, pattern, matcher = _stringmatcher(bm)
461 bms = set()
495 bms = set()
462 if kind == 'literal':
496 if kind == 'literal':
463 bmrev = repo._bookmarks.get(pattern, None)
497 bmrev = repo._bookmarks.get(pattern, None)
464 if not bmrev:
498 if not bmrev:
465 raise util.Abort(_("bookmark '%s' does not exist") % bm)
499 raise util.Abort(_("bookmark '%s' does not exist") % bm)
466 bms.add(repo[bmrev].rev())
500 bms.add(repo[bmrev].rev())
467 else:
501 else:
468 matchrevs = set()
502 matchrevs = set()
469 for name, bmrev in repo._bookmarks.iteritems():
503 for name, bmrev in repo._bookmarks.iteritems():
470 if matcher(name):
504 if matcher(name):
471 matchrevs.add(bmrev)
505 matchrevs.add(bmrev)
472 if not matchrevs:
506 if not matchrevs:
473 raise util.Abort(_("no bookmarks exist that match '%s'")
507 raise util.Abort(_("no bookmarks exist that match '%s'")
474 % pattern)
508 % pattern)
475 for bmrev in matchrevs:
509 for bmrev in matchrevs:
476 bms.add(repo[bmrev].rev())
510 bms.add(repo[bmrev].rev())
477 else:
511 else:
478 bms = set([repo[r].rev()
512 bms = set([repo[r].rev()
479 for r in repo._bookmarks.values()])
513 for r in repo._bookmarks.values()])
480 bms -= set([node.nullrev])
514 bms -= set([node.nullrev])
481 return subset & bms
515 return subset & bms
482
516
483 def branch(repo, subset, x):
517 def branch(repo, subset, x):
484 """``branch(string or set)``
518 """``branch(string or set)``
485 All changesets belonging to the given branch or the branches of the given
519 All changesets belonging to the given branch or the branches of the given
486 changesets.
520 changesets.
487
521
488 If `string` starts with `re:`, the remainder of the name is treated as
522 If `string` starts with `re:`, the remainder of the name is treated as
489 a regular expression. To match a branch that actually starts with `re:`,
523 a regular expression. To match a branch that actually starts with `re:`,
490 use the prefix `literal:`.
524 use the prefix `literal:`.
491 """
525 """
492 import branchmap
526 import branchmap
493 urepo = repo.unfiltered()
527 urepo = repo.unfiltered()
494 ucl = urepo.changelog
528 ucl = urepo.changelog
495 getbi = branchmap.revbranchcache(urepo).branchinfo
529 getbi = branchmap.revbranchcache(urepo).branchinfo
496
530
497 try:
531 try:
498 b = getstring(x, '')
532 b = getstring(x, '')
499 except error.ParseError:
533 except error.ParseError:
500 # not a string, but another revspec, e.g. tip()
534 # not a string, but another revspec, e.g. tip()
501 pass
535 pass
502 else:
536 else:
503 kind, pattern, matcher = _stringmatcher(b)
537 kind, pattern, matcher = _stringmatcher(b)
504 if kind == 'literal':
538 if kind == 'literal':
505 # note: falls through to the revspec case if no branch with
539 # note: falls through to the revspec case if no branch with
506 # this name exists
540 # this name exists
507 if pattern in repo.branchmap():
541 if pattern in repo.branchmap():
508 return subset.filter(lambda r: matcher(getbi(ucl, r)[0]))
542 return subset.filter(lambda r: matcher(getbi(ucl, r)[0]))
509 else:
543 else:
510 return subset.filter(lambda r: matcher(getbi(ucl, r)[0]))
544 return subset.filter(lambda r: matcher(getbi(ucl, r)[0]))
511
545
512 s = getset(repo, spanset(repo), x)
546 s = getset(repo, spanset(repo), x)
513 b = set()
547 b = set()
514 for r in s:
548 for r in s:
515 b.add(getbi(ucl, r)[0])
549 b.add(getbi(ucl, r)[0])
516 c = s.__contains__
550 c = s.__contains__
517 return subset.filter(lambda r: c(r) or getbi(ucl, r)[0] in b)
551 return subset.filter(lambda r: c(r) or getbi(ucl, r)[0] in b)
518
552
519 def bumped(repo, subset, x):
553 def bumped(repo, subset, x):
520 """``bumped()``
554 """``bumped()``
521 Mutable changesets marked as successors of public changesets.
555 Mutable changesets marked as successors of public changesets.
522
556
523 Only non-public and non-obsolete changesets can be `bumped`.
557 Only non-public and non-obsolete changesets can be `bumped`.
524 """
558 """
525 # i18n: "bumped" is a keyword
559 # i18n: "bumped" is a keyword
526 getargs(x, 0, 0, _("bumped takes no arguments"))
560 getargs(x, 0, 0, _("bumped takes no arguments"))
527 bumped = obsmod.getrevs(repo, 'bumped')
561 bumped = obsmod.getrevs(repo, 'bumped')
528 return subset & bumped
562 return subset & bumped
529
563
530 def bundle(repo, subset, x):
564 def bundle(repo, subset, x):
531 """``bundle()``
565 """``bundle()``
532 Changesets in the bundle.
566 Changesets in the bundle.
533
567
534 Bundle must be specified by the -R option."""
568 Bundle must be specified by the -R option."""
535
569
536 try:
570 try:
537 bundlerevs = repo.changelog.bundlerevs
571 bundlerevs = repo.changelog.bundlerevs
538 except AttributeError:
572 except AttributeError:
539 raise util.Abort(_("no bundle provided - specify with -R"))
573 raise util.Abort(_("no bundle provided - specify with -R"))
540 return subset & bundlerevs
574 return subset & bundlerevs
541
575
542 def checkstatus(repo, subset, pat, field):
576 def checkstatus(repo, subset, pat, field):
543 hasset = matchmod.patkind(pat) == 'set'
577 hasset = matchmod.patkind(pat) == 'set'
544
578
545 mcache = [None]
579 mcache = [None]
546 def matches(x):
580 def matches(x):
547 c = repo[x]
581 c = repo[x]
548 if not mcache[0] or hasset:
582 if not mcache[0] or hasset:
549 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
583 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
550 m = mcache[0]
584 m = mcache[0]
551 fname = None
585 fname = None
552 if not m.anypats() and len(m.files()) == 1:
586 if not m.anypats() and len(m.files()) == 1:
553 fname = m.files()[0]
587 fname = m.files()[0]
554 if fname is not None:
588 if fname is not None:
555 if fname not in c.files():
589 if fname not in c.files():
556 return False
590 return False
557 else:
591 else:
558 for f in c.files():
592 for f in c.files():
559 if m(f):
593 if m(f):
560 break
594 break
561 else:
595 else:
562 return False
596 return False
563 files = repo.status(c.p1().node(), c.node())[field]
597 files = repo.status(c.p1().node(), c.node())[field]
564 if fname is not None:
598 if fname is not None:
565 if fname in files:
599 if fname in files:
566 return True
600 return True
567 else:
601 else:
568 for f in files:
602 for f in files:
569 if m(f):
603 if m(f):
570 return True
604 return True
571
605
572 return subset.filter(matches)
606 return subset.filter(matches)
573
607
574 def _children(repo, narrow, parentset):
608 def _children(repo, narrow, parentset):
575 cs = set()
609 cs = set()
576 if not parentset:
610 if not parentset:
577 return baseset(cs)
611 return baseset(cs)
578 pr = repo.changelog.parentrevs
612 pr = repo.changelog.parentrevs
579 minrev = min(parentset)
613 minrev = min(parentset)
580 for r in narrow:
614 for r in narrow:
581 if r <= minrev:
615 if r <= minrev:
582 continue
616 continue
583 for p in pr(r):
617 for p in pr(r):
584 if p in parentset:
618 if p in parentset:
585 cs.add(r)
619 cs.add(r)
586 return baseset(cs)
620 return baseset(cs)
587
621
588 def children(repo, subset, x):
622 def children(repo, subset, x):
589 """``children(set)``
623 """``children(set)``
590 Child changesets of changesets in set.
624 Child changesets of changesets in set.
591 """
625 """
592 s = getset(repo, fullreposet(repo), x)
626 s = getset(repo, fullreposet(repo), x)
593 cs = _children(repo, subset, s)
627 cs = _children(repo, subset, s)
594 return subset & cs
628 return subset & cs
595
629
596 def closed(repo, subset, x):
630 def closed(repo, subset, x):
597 """``closed()``
631 """``closed()``
598 Changeset is closed.
632 Changeset is closed.
599 """
633 """
600 # i18n: "closed" is a keyword
634 # i18n: "closed" is a keyword
601 getargs(x, 0, 0, _("closed takes no arguments"))
635 getargs(x, 0, 0, _("closed takes no arguments"))
602 return subset.filter(lambda r: repo[r].closesbranch())
636 return subset.filter(lambda r: repo[r].closesbranch())
603
637
604 def contains(repo, subset, x):
638 def contains(repo, subset, x):
605 """``contains(pattern)``
639 """``contains(pattern)``
606 The revision's manifest contains a file matching pattern (but might not
640 The revision's manifest contains a file matching pattern (but might not
607 modify it). See :hg:`help patterns` for information about file patterns.
641 modify it). See :hg:`help patterns` for information about file patterns.
608
642
609 The pattern without explicit kind like ``glob:`` is expected to be
643 The pattern without explicit kind like ``glob:`` is expected to be
610 relative to the current directory and match against a file exactly
644 relative to the current directory and match against a file exactly
611 for efficiency.
645 for efficiency.
612 """
646 """
613 # i18n: "contains" is a keyword
647 # i18n: "contains" is a keyword
614 pat = getstring(x, _("contains requires a pattern"))
648 pat = getstring(x, _("contains requires a pattern"))
615
649
616 def matches(x):
650 def matches(x):
617 if not matchmod.patkind(pat):
651 if not matchmod.patkind(pat):
618 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
652 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
619 if pats in repo[x]:
653 if pats in repo[x]:
620 return True
654 return True
621 else:
655 else:
622 c = repo[x]
656 c = repo[x]
623 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
657 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
624 for f in c.manifest():
658 for f in c.manifest():
625 if m(f):
659 if m(f):
626 return True
660 return True
627 return False
661 return False
628
662
629 return subset.filter(matches)
663 return subset.filter(matches)
630
664
631 def converted(repo, subset, x):
665 def converted(repo, subset, x):
632 """``converted([id])``
666 """``converted([id])``
633 Changesets converted from the given identifier in the old repository if
667 Changesets converted from the given identifier in the old repository if
634 present, or all converted changesets if no identifier is specified.
668 present, or all converted changesets if no identifier is specified.
635 """
669 """
636
670
637 # There is exactly no chance of resolving the revision, so do a simple
671 # There is exactly no chance of resolving the revision, so do a simple
638 # string compare and hope for the best
672 # string compare and hope for the best
639
673
640 rev = None
674 rev = None
641 # i18n: "converted" is a keyword
675 # i18n: "converted" is a keyword
642 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
676 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
643 if l:
677 if l:
644 # i18n: "converted" is a keyword
678 # i18n: "converted" is a keyword
645 rev = getstring(l[0], _('converted requires a revision'))
679 rev = getstring(l[0], _('converted requires a revision'))
646
680
647 def _matchvalue(r):
681 def _matchvalue(r):
648 source = repo[r].extra().get('convert_revision', None)
682 source = repo[r].extra().get('convert_revision', None)
649 return source is not None and (rev is None or source.startswith(rev))
683 return source is not None and (rev is None or source.startswith(rev))
650
684
651 return subset.filter(lambda r: _matchvalue(r))
685 return subset.filter(lambda r: _matchvalue(r))
652
686
653 def date(repo, subset, x):
687 def date(repo, subset, x):
654 """``date(interval)``
688 """``date(interval)``
655 Changesets within the interval, see :hg:`help dates`.
689 Changesets within the interval, see :hg:`help dates`.
656 """
690 """
657 # i18n: "date" is a keyword
691 # i18n: "date" is a keyword
658 ds = getstring(x, _("date requires a string"))
692 ds = getstring(x, _("date requires a string"))
659 dm = util.matchdate(ds)
693 dm = util.matchdate(ds)
660 return subset.filter(lambda x: dm(repo[x].date()[0]))
694 return subset.filter(lambda x: dm(repo[x].date()[0]))
661
695
662 def desc(repo, subset, x):
696 def desc(repo, subset, x):
663 """``desc(string)``
697 """``desc(string)``
664 Search commit message for string. The match is case-insensitive.
698 Search commit message for string. The match is case-insensitive.
665 """
699 """
666 # i18n: "desc" is a keyword
700 # i18n: "desc" is a keyword
667 ds = encoding.lower(getstring(x, _("desc requires a string")))
701 ds = encoding.lower(getstring(x, _("desc requires a string")))
668
702
669 def matches(x):
703 def matches(x):
670 c = repo[x]
704 c = repo[x]
671 return ds in encoding.lower(c.description())
705 return ds in encoding.lower(c.description())
672
706
673 return subset.filter(matches)
707 return subset.filter(matches)
674
708
675 def _descendants(repo, subset, x, followfirst=False):
709 def _descendants(repo, subset, x, followfirst=False):
676 roots = getset(repo, spanset(repo), x)
710 roots = getset(repo, spanset(repo), x)
677 if not roots:
711 if not roots:
678 return baseset()
712 return baseset()
679 s = _revdescendants(repo, roots, followfirst)
713 s = _revdescendants(repo, roots, followfirst)
680
714
681 # Both sets need to be ascending in order to lazily return the union
715 # Both sets need to be ascending in order to lazily return the union
682 # in the correct order.
716 # in the correct order.
683 base = subset & roots
717 base = subset & roots
684 desc = subset & s
718 desc = subset & s
685 result = base + desc
719 result = base + desc
686 if subset.isascending():
720 if subset.isascending():
687 result.sort()
721 result.sort()
688 elif subset.isdescending():
722 elif subset.isdescending():
689 result.sort(reverse=True)
723 result.sort(reverse=True)
690 else:
724 else:
691 result = subset & result
725 result = subset & result
692 return result
726 return result
693
727
694 def descendants(repo, subset, x):
728 def descendants(repo, subset, x):
695 """``descendants(set)``
729 """``descendants(set)``
696 Changesets which are descendants of changesets in set.
730 Changesets which are descendants of changesets in set.
697 """
731 """
698 return _descendants(repo, subset, x)
732 return _descendants(repo, subset, x)
699
733
700 def _firstdescendants(repo, subset, x):
734 def _firstdescendants(repo, subset, x):
701 # ``_firstdescendants(set)``
735 # ``_firstdescendants(set)``
702 # Like ``descendants(set)`` but follows only the first parents.
736 # Like ``descendants(set)`` but follows only the first parents.
703 return _descendants(repo, subset, x, followfirst=True)
737 return _descendants(repo, subset, x, followfirst=True)
704
738
705 def destination(repo, subset, x):
739 def destination(repo, subset, x):
706 """``destination([set])``
740 """``destination([set])``
707 Changesets that were created by a graft, transplant or rebase operation,
741 Changesets that were created by a graft, transplant or rebase operation,
708 with the given revisions specified as the source. Omitting the optional set
742 with the given revisions specified as the source. Omitting the optional set
709 is the same as passing all().
743 is the same as passing all().
710 """
744 """
711 if x is not None:
745 if x is not None:
712 sources = getset(repo, spanset(repo), x)
746 sources = getset(repo, spanset(repo), x)
713 else:
747 else:
714 sources = getall(repo, spanset(repo), x)
748 sources = getall(repo, spanset(repo), x)
715
749
716 dests = set()
750 dests = set()
717
751
718 # subset contains all of the possible destinations that can be returned, so
752 # subset contains all of the possible destinations that can be returned, so
719 # iterate over them and see if their source(s) were provided in the arg set.
753 # iterate over them and see if their source(s) were provided in the arg set.
720 # Even if the immediate src of r is not in the arg set, src's source (or
754 # Even if the immediate src of r is not in the arg set, src's source (or
721 # further back) may be. Scanning back further than the immediate src allows
755 # further back) may be. Scanning back further than the immediate src allows
722 # transitive transplants and rebases to yield the same results as transitive
756 # transitive transplants and rebases to yield the same results as transitive
723 # grafts.
757 # grafts.
724 for r in subset:
758 for r in subset:
725 src = _getrevsource(repo, r)
759 src = _getrevsource(repo, r)
726 lineage = None
760 lineage = None
727
761
728 while src is not None:
762 while src is not None:
729 if lineage is None:
763 if lineage is None:
730 lineage = list()
764 lineage = list()
731
765
732 lineage.append(r)
766 lineage.append(r)
733
767
734 # The visited lineage is a match if the current source is in the arg
768 # The visited lineage is a match if the current source is in the arg
735 # set. Since every candidate dest is visited by way of iterating
769 # set. Since every candidate dest is visited by way of iterating
736 # subset, any dests further back in the lineage will be tested by a
770 # subset, any dests further back in the lineage will be tested by a
737 # different iteration over subset. Likewise, if the src was already
771 # different iteration over subset. Likewise, if the src was already
738 # selected, the current lineage can be selected without going back
772 # selected, the current lineage can be selected without going back
739 # further.
773 # further.
740 if src in sources or src in dests:
774 if src in sources or src in dests:
741 dests.update(lineage)
775 dests.update(lineage)
742 break
776 break
743
777
744 r = src
778 r = src
745 src = _getrevsource(repo, r)
779 src = _getrevsource(repo, r)
746
780
747 return subset.filter(dests.__contains__)
781 return subset.filter(dests.__contains__)
748
782
749 def divergent(repo, subset, x):
783 def divergent(repo, subset, x):
750 """``divergent()``
784 """``divergent()``
751 Final successors of changesets with an alternative set of final successors.
785 Final successors of changesets with an alternative set of final successors.
752 """
786 """
753 # i18n: "divergent" is a keyword
787 # i18n: "divergent" is a keyword
754 getargs(x, 0, 0, _("divergent takes no arguments"))
788 getargs(x, 0, 0, _("divergent takes no arguments"))
755 divergent = obsmod.getrevs(repo, 'divergent')
789 divergent = obsmod.getrevs(repo, 'divergent')
756 return subset & divergent
790 return subset & divergent
757
791
758 def draft(repo, subset, x):
792 def draft(repo, subset, x):
759 """``draft()``
793 """``draft()``
760 Changeset in draft phase."""
794 Changeset in draft phase."""
761 # i18n: "draft" is a keyword
795 # i18n: "draft" is a keyword
762 getargs(x, 0, 0, _("draft takes no arguments"))
796 getargs(x, 0, 0, _("draft takes no arguments"))
763 phase = repo._phasecache.phase
797 phase = repo._phasecache.phase
764 target = phases.draft
798 target = phases.draft
765 condition = lambda r: phase(repo, r) == target
799 condition = lambda r: phase(repo, r) == target
766 return subset.filter(condition, cache=False)
800 return subset.filter(condition, cache=False)
767
801
768 def extinct(repo, subset, x):
802 def extinct(repo, subset, x):
769 """``extinct()``
803 """``extinct()``
770 Obsolete changesets with obsolete descendants only.
804 Obsolete changesets with obsolete descendants only.
771 """
805 """
772 # i18n: "extinct" is a keyword
806 # i18n: "extinct" is a keyword
773 getargs(x, 0, 0, _("extinct takes no arguments"))
807 getargs(x, 0, 0, _("extinct takes no arguments"))
774 extincts = obsmod.getrevs(repo, 'extinct')
808 extincts = obsmod.getrevs(repo, 'extinct')
775 return subset & extincts
809 return subset & extincts
776
810
777 def extra(repo, subset, x):
811 def extra(repo, subset, x):
778 """``extra(label, [value])``
812 """``extra(label, [value])``
779 Changesets with the given label in the extra metadata, with the given
813 Changesets with the given label in the extra metadata, with the given
780 optional value.
814 optional value.
781
815
782 If `value` starts with `re:`, the remainder of the value is treated as
816 If `value` starts with `re:`, the remainder of the value is treated as
783 a regular expression. To match a value that actually starts with `re:`,
817 a regular expression. To match a value that actually starts with `re:`,
784 use the prefix `literal:`.
818 use the prefix `literal:`.
785 """
819 """
786
820
787 # i18n: "extra" is a keyword
821 # i18n: "extra" is a keyword
788 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
822 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
789 # i18n: "extra" is a keyword
823 # i18n: "extra" is a keyword
790 label = getstring(l[0], _('first argument to extra must be a string'))
824 label = getstring(l[0], _('first argument to extra must be a string'))
791 value = None
825 value = None
792
826
793 if len(l) > 1:
827 if len(l) > 1:
794 # i18n: "extra" is a keyword
828 # i18n: "extra" is a keyword
795 value = getstring(l[1], _('second argument to extra must be a string'))
829 value = getstring(l[1], _('second argument to extra must be a string'))
796 kind, value, matcher = _stringmatcher(value)
830 kind, value, matcher = _stringmatcher(value)
797
831
798 def _matchvalue(r):
832 def _matchvalue(r):
799 extra = repo[r].extra()
833 extra = repo[r].extra()
800 return label in extra and (value is None or matcher(extra[label]))
834 return label in extra and (value is None or matcher(extra[label]))
801
835
802 return subset.filter(lambda r: _matchvalue(r))
836 return subset.filter(lambda r: _matchvalue(r))
803
837
804 def filelog(repo, subset, x):
838 def filelog(repo, subset, x):
805 """``filelog(pattern)``
839 """``filelog(pattern)``
806 Changesets connected to the specified filelog.
840 Changesets connected to the specified filelog.
807
841
808 For performance reasons, visits only revisions mentioned in the file-level
842 For performance reasons, visits only revisions mentioned in the file-level
809 filelog, rather than filtering through all changesets (much faster, but
843 filelog, rather than filtering through all changesets (much faster, but
810 doesn't include deletes or duplicate changes). For a slower, more accurate
844 doesn't include deletes or duplicate changes). For a slower, more accurate
811 result, use ``file()``.
845 result, use ``file()``.
812
846
813 The pattern without explicit kind like ``glob:`` is expected to be
847 The pattern without explicit kind like ``glob:`` is expected to be
814 relative to the current directory and match against a file exactly
848 relative to the current directory and match against a file exactly
815 for efficiency.
849 for efficiency.
816
850
817 If some linkrev points to revisions filtered by the current repoview, we'll
851 If some linkrev points to revisions filtered by the current repoview, we'll
818 work around it to return a non-filtered value.
852 work around it to return a non-filtered value.
819 """
853 """
820
854
821 # i18n: "filelog" is a keyword
855 # i18n: "filelog" is a keyword
822 pat = getstring(x, _("filelog requires a pattern"))
856 pat = getstring(x, _("filelog requires a pattern"))
823 s = set()
857 s = set()
824 cl = repo.changelog
858 cl = repo.changelog
825
859
826 if not matchmod.patkind(pat):
860 if not matchmod.patkind(pat):
827 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
861 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
828 files = [f]
862 files = [f]
829 else:
863 else:
830 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
864 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
831 files = (f for f in repo[None] if m(f))
865 files = (f for f in repo[None] if m(f))
832
866
833 for f in files:
867 for f in files:
834 backrevref = {} # final value for: filerev -> changerev
868 backrevref = {} # final value for: filerev -> changerev
835 lowestchild = {} # lowest known filerev child of a filerev
869 lowestchild = {} # lowest known filerev child of a filerev
836 delayed = [] # filerev with filtered linkrev, for post-processing
870 delayed = [] # filerev with filtered linkrev, for post-processing
837 lowesthead = None # cache for manifest content of all head revisions
871 lowesthead = None # cache for manifest content of all head revisions
838 fl = repo.file(f)
872 fl = repo.file(f)
839 for fr in list(fl):
873 for fr in list(fl):
840 rev = fl.linkrev(fr)
874 rev = fl.linkrev(fr)
841 if rev not in cl:
875 if rev not in cl:
842 # changerev pointed in linkrev is filtered
876 # changerev pointed in linkrev is filtered
843 # record it for post processing.
877 # record it for post processing.
844 delayed.append((fr, rev))
878 delayed.append((fr, rev))
845 continue
879 continue
846 for p in fl.parentrevs(fr):
880 for p in fl.parentrevs(fr):
847 if 0 <= p and p not in lowestchild:
881 if 0 <= p and p not in lowestchild:
848 lowestchild[p] = fr
882 lowestchild[p] = fr
849 backrevref[fr] = rev
883 backrevref[fr] = rev
850 s.add(rev)
884 s.add(rev)
851
885
852 # Post-processing of all filerevs we skipped because they were
886 # Post-processing of all filerevs we skipped because they were
853 # filtered. If such filerevs have known and unfiltered children, this
887 # filtered. If such filerevs have known and unfiltered children, this
854 # means they have an unfiltered appearance out there. We'll use linkrev
888 # means they have an unfiltered appearance out there. We'll use linkrev
855 # adjustment to find one of these appearances. The lowest known child
889 # adjustment to find one of these appearances. The lowest known child
856 # will be used as a starting point because it is the best upper-bound we
890 # will be used as a starting point because it is the best upper-bound we
857 # have.
891 # have.
858 #
892 #
859 # This approach will fail when an unfiltered but linkrev-shadowed
893 # This approach will fail when an unfiltered but linkrev-shadowed
860 # appearance exists in a head changeset without unfiltered filerev
894 # appearance exists in a head changeset without unfiltered filerev
861 # children anywhere.
895 # children anywhere.
862 while delayed:
896 while delayed:
863 # must be a descending iteration. To slowly fill lowest child
897 # must be a descending iteration. To slowly fill lowest child
864 # information that is of potential use by the next item.
898 # information that is of potential use by the next item.
865 fr, rev = delayed.pop()
899 fr, rev = delayed.pop()
866 lkr = rev
900 lkr = rev
867
901
868 child = lowestchild.get(fr)
902 child = lowestchild.get(fr)
869
903
870 if child is None:
904 if child is None:
871 # search for existence of this file revision in a head revision.
905 # search for existence of this file revision in a head revision.
872 # There are three possibilities:
906 # There are three possibilities:
873 # - the revision exists in a head and we can find an
907 # - the revision exists in a head and we can find an
874 # introduction from there,
908 # introduction from there,
875 # - the revision does not exist in a head because it has been
909 # - the revision does not exist in a head because it has been
876 # changed since its introduction: we would have found a child
910 # changed since its introduction: we would have found a child
877 # and be in the other 'else' clause,
911 # and be in the other 'else' clause,
878 # - all versions of the revision are hidden.
912 # - all versions of the revision are hidden.
879 if lowesthead is None:
913 if lowesthead is None:
880 lowesthead = {}
914 lowesthead = {}
881 for h in repo.heads():
915 for h in repo.heads():
882 fnode = repo[h].manifest().get(f)
916 fnode = repo[h].manifest().get(f)
883 if fnode is not None:
917 if fnode is not None:
884 lowesthead[fl.rev(fnode)] = h
918 lowesthead[fl.rev(fnode)] = h
885 headrev = lowesthead.get(fr)
919 headrev = lowesthead.get(fr)
886 if headrev is None:
920 if headrev is None:
887 # content is nowhere unfiltered
921 # content is nowhere unfiltered
888 continue
922 continue
889 rev = repo[headrev][f].introrev()
923 rev = repo[headrev][f].introrev()
890 else:
924 else:
891 # the lowest known child is a good upper bound
925 # the lowest known child is a good upper bound
892 childcrev = backrevref[child]
926 childcrev = backrevref[child]
893 # XXX this does not guarantee returning the lowest
927 # XXX this does not guarantee returning the lowest
894 # introduction of this revision, but this gives a
928 # introduction of this revision, but this gives a
895 # result which is a good start and will fit in most
929 # result which is a good start and will fit in most
896 # cases. We probably need to fix the multiple
930 # cases. We probably need to fix the multiple
897 # introductions case properly (report each
931 # introductions case properly (report each
898 # introduction, even for identical file revisions)
932 # introduction, even for identical file revisions)
899 # once and for all at some point anyway.
933 # once and for all at some point anyway.
900 for p in repo[childcrev][f].parents():
934 for p in repo[childcrev][f].parents():
901 if p.filerev() == fr:
935 if p.filerev() == fr:
902 rev = p.rev()
936 rev = p.rev()
903 break
937 break
904 if rev == lkr: # no shadowed entry found
938 if rev == lkr: # no shadowed entry found
905 # XXX This should never happen unless some manifest points
939 # XXX This should never happen unless some manifest points
906 # to biggish file revisions (like a revision that uses a
940 # to biggish file revisions (like a revision that uses a
907 # parent that never appears in the manifest ancestors)
941 # parent that never appears in the manifest ancestors)
908 continue
942 continue
909
943
910 # Fill the data for the next iteration.
944 # Fill the data for the next iteration.
911 for p in fl.parentrevs(fr):
945 for p in fl.parentrevs(fr):
912 if 0 <= p and p not in lowestchild:
946 if 0 <= p and p not in lowestchild:
913 lowestchild[p] = fr
947 lowestchild[p] = fr
914 backrevref[fr] = rev
948 backrevref[fr] = rev
915 s.add(rev)
949 s.add(rev)
916
950
917 return subset & s
951 return subset & s
918
952
919 def first(repo, subset, x):
953 def first(repo, subset, x):
920 """``first(set, [n])``
954 """``first(set, [n])``
921 An alias for limit().
955 An alias for limit().
922 """
956 """
923 return limit(repo, subset, x)
957 return limit(repo, subset, x)
924
958
925 def _follow(repo, subset, x, name, followfirst=False):
959 def _follow(repo, subset, x, name, followfirst=False):
926 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
960 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
927 c = repo['.']
961 c = repo['.']
928 if l:
962 if l:
929 x = getstring(l[0], _("%s expected a filename") % name)
963 x = getstring(l[0], _("%s expected a filename") % name)
930 if x in c:
964 if x in c:
931 cx = c[x]
965 cx = c[x]
932 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
966 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
933 # include the revision responsible for the most recent version
967 # include the revision responsible for the most recent version
934 s.add(cx.introrev())
968 s.add(cx.introrev())
935 else:
969 else:
936 return baseset()
970 return baseset()
937 else:
971 else:
938 s = _revancestors(repo, baseset([c.rev()]), followfirst)
972 s = _revancestors(repo, baseset([c.rev()]), followfirst)
939
973
940 return subset & s
974 return subset & s
941
975
942 def follow(repo, subset, x):
976 def follow(repo, subset, x):
943 """``follow([file])``
977 """``follow([file])``
944 An alias for ``::.`` (ancestors of the working copy's first parent).
978 An alias for ``::.`` (ancestors of the working copy's first parent).
945 If a filename is specified, the history of the given file is followed,
979 If a filename is specified, the history of the given file is followed,
946 including copies.
980 including copies.
947 """
981 """
948 return _follow(repo, subset, x, 'follow')
982 return _follow(repo, subset, x, 'follow')
949
983
950 def _followfirst(repo, subset, x):
984 def _followfirst(repo, subset, x):
951 # ``followfirst([file])``
985 # ``followfirst([file])``
952 # Like ``follow([file])`` but follows only the first parent of
986 # Like ``follow([file])`` but follows only the first parent of
953 # every revision or file revision.
987 # every revision or file revision.
954 return _follow(repo, subset, x, '_followfirst', followfirst=True)
988 return _follow(repo, subset, x, '_followfirst', followfirst=True)
955
989
956 def getall(repo, subset, x):
990 def getall(repo, subset, x):
957 """``all()``
991 """``all()``
958 All changesets, the same as ``0:tip``.
992 All changesets, the same as ``0:tip``.
959 """
993 """
960 # i18n: "all" is a keyword
994 # i18n: "all" is a keyword
961 getargs(x, 0, 0, _("all takes no arguments"))
995 getargs(x, 0, 0, _("all takes no arguments"))
962 return subset
996 return subset
963
997
964 def grep(repo, subset, x):
998 def grep(repo, subset, x):
965 """``grep(regex)``
999 """``grep(regex)``
966 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1000 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
967 to ensure special escape characters are handled correctly. Unlike
1001 to ensure special escape characters are handled correctly. Unlike
968 ``keyword(string)``, the match is case-sensitive.
1002 ``keyword(string)``, the match is case-sensitive.
969 """
1003 """
970 try:
1004 try:
971 # i18n: "grep" is a keyword
1005 # i18n: "grep" is a keyword
972 gr = re.compile(getstring(x, _("grep requires a string")))
1006 gr = re.compile(getstring(x, _("grep requires a string")))
973 except re.error, e:
1007 except re.error, e:
974 raise error.ParseError(_('invalid match pattern: %s') % e)
1008 raise error.ParseError(_('invalid match pattern: %s') % e)
975
1009
976 def matches(x):
1010 def matches(x):
977 c = repo[x]
1011 c = repo[x]
978 for e in c.files() + [c.user(), c.description()]:
1012 for e in c.files() + [c.user(), c.description()]:
979 if gr.search(e):
1013 if gr.search(e):
980 return True
1014 return True
981 return False
1015 return False
982
1016
983 return subset.filter(matches)
1017 return subset.filter(matches)
984
1018
985 def _matchfiles(repo, subset, x):
1019 def _matchfiles(repo, subset, x):
986 # _matchfiles takes a revset list of prefixed arguments:
1020 # _matchfiles takes a revset list of prefixed arguments:
987 #
1021 #
988 # [p:foo, i:bar, x:baz]
1022 # [p:foo, i:bar, x:baz]
989 #
1023 #
990 # builds a match object from them and filters subset. Allowed
1024 # builds a match object from them and filters subset. Allowed
991 # prefixes are 'p:' for regular patterns, 'i:' for include
1025 # prefixes are 'p:' for regular patterns, 'i:' for include
992 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1026 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
993 # a revision identifier, or the empty string to reference the
1027 # a revision identifier, or the empty string to reference the
994 # working directory, from which the match object is
1028 # working directory, from which the match object is
995 # initialized. Use 'd:' to set the default matching mode, default
1029 # initialized. Use 'd:' to set the default matching mode, default
996 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1030 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
997
1031
998 # i18n: "_matchfiles" is a keyword
1032 # i18n: "_matchfiles" is a keyword
999 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
1033 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
1000 pats, inc, exc = [], [], []
1034 pats, inc, exc = [], [], []
1001 rev, default = None, None
1035 rev, default = None, None
1002 for arg in l:
1036 for arg in l:
1003 # i18n: "_matchfiles" is a keyword
1037 # i18n: "_matchfiles" is a keyword
1004 s = getstring(arg, _("_matchfiles requires string arguments"))
1038 s = getstring(arg, _("_matchfiles requires string arguments"))
1005 prefix, value = s[:2], s[2:]
1039 prefix, value = s[:2], s[2:]
1006 if prefix == 'p:':
1040 if prefix == 'p:':
1007 pats.append(value)
1041 pats.append(value)
1008 elif prefix == 'i:':
1042 elif prefix == 'i:':
1009 inc.append(value)
1043 inc.append(value)
1010 elif prefix == 'x:':
1044 elif prefix == 'x:':
1011 exc.append(value)
1045 exc.append(value)
1012 elif prefix == 'r:':
1046 elif prefix == 'r:':
1013 if rev is not None:
1047 if rev is not None:
1014 # i18n: "_matchfiles" is a keyword
1048 # i18n: "_matchfiles" is a keyword
1015 raise error.ParseError(_('_matchfiles expected at most one '
1049 raise error.ParseError(_('_matchfiles expected at most one '
1016 'revision'))
1050 'revision'))
1017 rev = value
1051 rev = value
1018 elif prefix == 'd:':
1052 elif prefix == 'd:':
1019 if default is not None:
1053 if default is not None:
1020 # i18n: "_matchfiles" is a keyword
1054 # i18n: "_matchfiles" is a keyword
1021 raise error.ParseError(_('_matchfiles expected at most one '
1055 raise error.ParseError(_('_matchfiles expected at most one '
1022 'default mode'))
1056 'default mode'))
1023 default = value
1057 default = value
1024 else:
1058 else:
1025 # i18n: "_matchfiles" is a keyword
1059 # i18n: "_matchfiles" is a keyword
1026 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
1060 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
1027 if not default:
1061 if not default:
1028 default = 'glob'
1062 default = 'glob'
1029
1063
1030 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1064 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1031 exclude=exc, ctx=repo[rev], default=default)
1065 exclude=exc, ctx=repo[rev], default=default)
1032
1066
1033 def matches(x):
1067 def matches(x):
1034 for f in repo[x].files():
1068 for f in repo[x].files():
1035 if m(f):
1069 if m(f):
1036 return True
1070 return True
1037 return False
1071 return False
1038
1072
1039 return subset.filter(matches)
1073 return subset.filter(matches)
1040
1074
1041 def hasfile(repo, subset, x):
1075 def hasfile(repo, subset, x):
1042 """``file(pattern)``
1076 """``file(pattern)``
1043 Changesets affecting files matched by pattern.
1077 Changesets affecting files matched by pattern.
1044
1078
1045 For a faster but less accurate result, consider using ``filelog()``
1079 For a faster but less accurate result, consider using ``filelog()``
1046 instead.
1080 instead.
1047
1081
1048 This predicate uses ``glob:`` as the default kind of pattern.
1082 This predicate uses ``glob:`` as the default kind of pattern.
1049 """
1083 """
1050 # i18n: "file" is a keyword
1084 # i18n: "file" is a keyword
1051 pat = getstring(x, _("file requires a pattern"))
1085 pat = getstring(x, _("file requires a pattern"))
1052 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1086 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1053
1087
1054 def head(repo, subset, x):
1088 def head(repo, subset, x):
1055 """``head()``
1089 """``head()``
1056 Changeset is a named branch head.
1090 Changeset is a named branch head.
1057 """
1091 """
1058 # i18n: "head" is a keyword
1092 # i18n: "head" is a keyword
1059 getargs(x, 0, 0, _("head takes no arguments"))
1093 getargs(x, 0, 0, _("head takes no arguments"))
1060 hs = set()
1094 hs = set()
1061 for b, ls in repo.branchmap().iteritems():
1095 for b, ls in repo.branchmap().iteritems():
1062 hs.update(repo[h].rev() for h in ls)
1096 hs.update(repo[h].rev() for h in ls)
1063 return baseset(hs).filter(subset.__contains__)
1097 return baseset(hs).filter(subset.__contains__)
1064
1098
1065 def heads(repo, subset, x):
1099 def heads(repo, subset, x):
1066 """``heads(set)``
1100 """``heads(set)``
1067 Members of set with no children in set.
1101 Members of set with no children in set.
1068 """
1102 """
1069 s = getset(repo, subset, x)
1103 s = getset(repo, subset, x)
1070 ps = parents(repo, subset, x)
1104 ps = parents(repo, subset, x)
1071 return s - ps
1105 return s - ps
1072
1106
1073 def hidden(repo, subset, x):
1107 def hidden(repo, subset, x):
1074 """``hidden()``
1108 """``hidden()``
1075 Hidden changesets.
1109 Hidden changesets.
1076 """
1110 """
1077 # i18n: "hidden" is a keyword
1111 # i18n: "hidden" is a keyword
1078 getargs(x, 0, 0, _("hidden takes no arguments"))
1112 getargs(x, 0, 0, _("hidden takes no arguments"))
1079 hiddenrevs = repoview.filterrevs(repo, 'visible')
1113 hiddenrevs = repoview.filterrevs(repo, 'visible')
1080 return subset & hiddenrevs
1114 return subset & hiddenrevs
1081
1115
1082 def keyword(repo, subset, x):
1116 def keyword(repo, subset, x):
1083 """``keyword(string)``
1117 """``keyword(string)``
1084 Search commit message, user name, and names of changed files for
1118 Search commit message, user name, and names of changed files for
1085 string. The match is case-insensitive.
1119 string. The match is case-insensitive.
1086 """
1120 """
1087 # i18n: "keyword" is a keyword
1121 # i18n: "keyword" is a keyword
1088 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1122 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1089
1123
1090 def matches(r):
1124 def matches(r):
1091 c = repo[r]
1125 c = repo[r]
1092 return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
1126 return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
1093 c.description()])
1127 c.description()])
1094
1128
1095 return subset.filter(matches)
1129 return subset.filter(matches)
1096
1130
1097 def limit(repo, subset, x):
1131 def limit(repo, subset, x):
1098 """``limit(set, [n])``
1132 """``limit(set, [n])``
1099 First n members of set, defaulting to 1.
1133 First n members of set, defaulting to 1.
1100 """
1134 """
1101 # i18n: "limit" is a keyword
1135 # i18n: "limit" is a keyword
1102 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
1136 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
1103 try:
1137 try:
1104 lim = 1
1138 lim = 1
1105 if len(l) == 2:
1139 if len(l) == 2:
1106 # i18n: "limit" is a keyword
1140 # i18n: "limit" is a keyword
1107 lim = int(getstring(l[1], _("limit requires a number")))
1141 lim = int(getstring(l[1], _("limit requires a number")))
1108 except (TypeError, ValueError):
1142 except (TypeError, ValueError):
1109 # i18n: "limit" is a keyword
1143 # i18n: "limit" is a keyword
1110 raise error.ParseError(_("limit expects a number"))
1144 raise error.ParseError(_("limit expects a number"))
1111 ss = subset
1145 ss = subset
1112 os = getset(repo, spanset(repo), l[0])
1146 os = getset(repo, spanset(repo), l[0])
1113 result = []
1147 result = []
1114 it = iter(os)
1148 it = iter(os)
1115 for x in xrange(lim):
1149 for x in xrange(lim):
1116 try:
1150 try:
1117 y = it.next()
1151 y = it.next()
1118 if y in ss:
1152 if y in ss:
1119 result.append(y)
1153 result.append(y)
1120 except (StopIteration):
1154 except (StopIteration):
1121 break
1155 break
1122 return baseset(result)
1156 return baseset(result)
1123
1157
1124 def last(repo, subset, x):
1158 def last(repo, subset, x):
1125 """``last(set, [n])``
1159 """``last(set, [n])``
1126 Last n members of set, defaulting to 1.
1160 Last n members of set, defaulting to 1.
1127 """
1161 """
1128 # i18n: "last" is a keyword
1162 # i18n: "last" is a keyword
1129 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1163 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1130 try:
1164 try:
1131 lim = 1
1165 lim = 1
1132 if len(l) == 2:
1166 if len(l) == 2:
1133 # i18n: "last" is a keyword
1167 # i18n: "last" is a keyword
1134 lim = int(getstring(l[1], _("last requires a number")))
1168 lim = int(getstring(l[1], _("last requires a number")))
1135 except (TypeError, ValueError):
1169 except (TypeError, ValueError):
1136 # i18n: "last" is a keyword
1170 # i18n: "last" is a keyword
1137 raise error.ParseError(_("last expects a number"))
1171 raise error.ParseError(_("last expects a number"))
1138 ss = subset
1172 ss = subset
1139 os = getset(repo, spanset(repo), l[0])
1173 os = getset(repo, spanset(repo), l[0])
1140 os.reverse()
1174 os.reverse()
1141 result = []
1175 result = []
1142 it = iter(os)
1176 it = iter(os)
1143 for x in xrange(lim):
1177 for x in xrange(lim):
1144 try:
1178 try:
1145 y = it.next()
1179 y = it.next()
1146 if y in ss:
1180 if y in ss:
1147 result.append(y)
1181 result.append(y)
1148 except (StopIteration):
1182 except (StopIteration):
1149 break
1183 break
1150 return baseset(result)
1184 return baseset(result)
1151
1185
1152 def maxrev(repo, subset, x):
1186 def maxrev(repo, subset, x):
1153 """``max(set)``
1187 """``max(set)``
1154 Changeset with highest revision number in set.
1188 Changeset with highest revision number in set.
1155 """
1189 """
1156 os = getset(repo, spanset(repo), x)
1190 os = getset(repo, spanset(repo), x)
1157 if os:
1191 if os:
1158 m = os.max()
1192 m = os.max()
1159 if m in subset:
1193 if m in subset:
1160 return baseset([m])
1194 return baseset([m])
1161 return baseset()
1195 return baseset()
1162
1196
1163 def merge(repo, subset, x):
1197 def merge(repo, subset, x):
1164 """``merge()``
1198 """``merge()``
1165 Changeset is a merge changeset.
1199 Changeset is a merge changeset.
1166 """
1200 """
1167 # i18n: "merge" is a keyword
1201 # i18n: "merge" is a keyword
1168 getargs(x, 0, 0, _("merge takes no arguments"))
1202 getargs(x, 0, 0, _("merge takes no arguments"))
1169 cl = repo.changelog
1203 cl = repo.changelog
1170 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1)
1204 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1)
1171
1205
1172 def branchpoint(repo, subset, x):
1206 def branchpoint(repo, subset, x):
1173 """``branchpoint()``
1207 """``branchpoint()``
1174 Changesets with more than one child.
1208 Changesets with more than one child.
1175 """
1209 """
1176 # i18n: "branchpoint" is a keyword
1210 # i18n: "branchpoint" is a keyword
1177 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1211 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1178 cl = repo.changelog
1212 cl = repo.changelog
1179 if not subset:
1213 if not subset:
1180 return baseset()
1214 return baseset()
1181 baserev = min(subset)
1215 baserev = min(subset)
1182 parentscount = [0]*(len(repo) - baserev)
1216 parentscount = [0]*(len(repo) - baserev)
1183 for r in cl.revs(start=baserev + 1):
1217 for r in cl.revs(start=baserev + 1):
1184 for p in cl.parentrevs(r):
1218 for p in cl.parentrevs(r):
1185 if p >= baserev:
1219 if p >= baserev:
1186 parentscount[p - baserev] += 1
1220 parentscount[p - baserev] += 1
1187 return subset.filter(lambda r: parentscount[r - baserev] > 1)
1221 return subset.filter(lambda r: parentscount[r - baserev] > 1)
1188
1222
1189 def minrev(repo, subset, x):
1223 def minrev(repo, subset, x):
1190 """``min(set)``
1224 """``min(set)``
1191 Changeset with lowest revision number in set.
1225 Changeset with lowest revision number in set.
1192 """
1226 """
1193 os = getset(repo, spanset(repo), x)
1227 os = getset(repo, spanset(repo), x)
1194 if os:
1228 if os:
1195 m = os.min()
1229 m = os.min()
1196 if m in subset:
1230 if m in subset:
1197 return baseset([m])
1231 return baseset([m])
1198 return baseset()
1232 return baseset()
1199
1233
1200 def modifies(repo, subset, x):
1234 def modifies(repo, subset, x):
1201 """``modifies(pattern)``
1235 """``modifies(pattern)``
1202 Changesets modifying files matched by pattern.
1236 Changesets modifying files matched by pattern.
1203
1237
1204 The pattern without explicit kind like ``glob:`` is expected to be
1238 The pattern without explicit kind like ``glob:`` is expected to be
1205 relative to the current directory and match against a file or a
1239 relative to the current directory and match against a file or a
1206 directory.
1240 directory.
1207 """
1241 """
1208 # i18n: "modifies" is a keyword
1242 # i18n: "modifies" is a keyword
1209 pat = getstring(x, _("modifies requires a pattern"))
1243 pat = getstring(x, _("modifies requires a pattern"))
1210 return checkstatus(repo, subset, pat, 0)
1244 return checkstatus(repo, subset, pat, 0)
1211
1245
1212 def named(repo, subset, x):
1246 def named(repo, subset, x):
1213 """``named(namespace)``
1247 """``named(namespace)``
1214 The changesets in a given namespace.
1248 The changesets in a given namespace.
1215
1249
1216 If `namespace` starts with `re:`, the remainder of the string is treated as
1250 If `namespace` starts with `re:`, the remainder of the string is treated as
1217 a regular expression. To match a namespace that actually starts with `re:`,
1251 a regular expression. To match a namespace that actually starts with `re:`,
1218 use the prefix `literal:`.
1252 use the prefix `literal:`.
1219 """
1253 """
1220 # i18n: "named" is a keyword
1254 # i18n: "named" is a keyword
1221 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1255 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1222
1256
1223 ns = getstring(args[0],
1257 ns = getstring(args[0],
1224 # i18n: "named" is a keyword
1258 # i18n: "named" is a keyword
1225 _('the argument to named must be a string'))
1259 _('the argument to named must be a string'))
1226 kind, pattern, matcher = _stringmatcher(ns)
1260 kind, pattern, matcher = _stringmatcher(ns)
1227 namespaces = set()
1261 namespaces = set()
1228 if kind == 'literal':
1262 if kind == 'literal':
1229 if pattern not in repo.names:
1263 if pattern not in repo.names:
1230 raise util.Abort(_("namespace '%s' does not exist") % ns)
1264 raise util.Abort(_("namespace '%s' does not exist") % ns)
1231 namespaces.add(repo.names[pattern])
1265 namespaces.add(repo.names[pattern])
1232 else:
1266 else:
1233 for name, ns in repo.names.iteritems():
1267 for name, ns in repo.names.iteritems():
1234 if matcher(name):
1268 if matcher(name):
1235 namespaces.add(ns)
1269 namespaces.add(ns)
1236 if not namespaces:
1270 if not namespaces:
1237 raise util.Abort(_("no namespace exists that match '%s'")
1271 raise util.Abort(_("no namespace exists that match '%s'")
1238 % pattern)
1272 % pattern)
1239
1273
1240 names = set()
1274 names = set()
1241 for ns in namespaces:
1275 for ns in namespaces:
1242 for name in ns.listnames(repo):
1276 for name in ns.listnames(repo):
1243 names.update(ns.nodes(repo, name))
1277 names.update(ns.nodes(repo, name))
1244
1278
1245 names -= set([node.nullrev])
1279 names -= set([node.nullrev])
1246 return subset & names
1280 return subset & names
1247
1281
1248 def node_(repo, subset, x):
1282 def node_(repo, subset, x):
1249 """``id(string)``
1283 """``id(string)``
1250 Revision non-ambiguously specified by the given hex string prefix.
1284 Revision non-ambiguously specified by the given hex string prefix.
1251 """
1285 """
1252 # i18n: "id" is a keyword
1286 # i18n: "id" is a keyword
1253 l = getargs(x, 1, 1, _("id requires one argument"))
1287 l = getargs(x, 1, 1, _("id requires one argument"))
1254 # i18n: "id" is a keyword
1288 # i18n: "id" is a keyword
1255 n = getstring(l[0], _("id requires a string"))
1289 n = getstring(l[0], _("id requires a string"))
1256 if len(n) == 40:
1290 if len(n) == 40:
1257 rn = repo[n].rev()
1291 rn = repo[n].rev()
1258 else:
1292 else:
1259 rn = None
1293 rn = None
1260 pm = repo.changelog._partialmatch(n)
1294 pm = repo.changelog._partialmatch(n)
1261 if pm is not None:
1295 if pm is not None:
1262 rn = repo.changelog.rev(pm)
1296 rn = repo.changelog.rev(pm)
1263
1297
1264 if rn is None:
1298 if rn is None:
1265 return baseset()
1299 return baseset()
1266 result = baseset([rn])
1300 result = baseset([rn])
1267 return result & subset
1301 return result & subset
1268
1302
1269 def obsolete(repo, subset, x):
1303 def obsolete(repo, subset, x):
1270 """``obsolete()``
1304 """``obsolete()``
1271 Mutable changeset with a newer version."""
1305 Mutable changeset with a newer version."""
1272 # i18n: "obsolete" is a keyword
1306 # i18n: "obsolete" is a keyword
1273 getargs(x, 0, 0, _("obsolete takes no arguments"))
1307 getargs(x, 0, 0, _("obsolete takes no arguments"))
1274 obsoletes = obsmod.getrevs(repo, 'obsolete')
1308 obsoletes = obsmod.getrevs(repo, 'obsolete')
1275 return subset & obsoletes
1309 return subset & obsoletes
1276
1310
1277 def only(repo, subset, x):
1311 def only(repo, subset, x):
1278 """``only(set, [set])``
1312 """``only(set, [set])``
1279 Changesets that are ancestors of the first set that are not ancestors
1313 Changesets that are ancestors of the first set that are not ancestors
1280 of any other head in the repo. If a second set is specified, the result
1314 of any other head in the repo. If a second set is specified, the result
1281 is ancestors of the first set that are not ancestors of the second set
1315 is ancestors of the first set that are not ancestors of the second set
1282 (i.e. ::<set1> - ::<set2>).
1316 (i.e. ::<set1> - ::<set2>).
1283 """
1317 """
1284 cl = repo.changelog
1318 cl = repo.changelog
1285 # i18n: "only" is a keyword
1319 # i18n: "only" is a keyword
1286 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1320 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1287 include = getset(repo, spanset(repo), args[0])
1321 include = getset(repo, spanset(repo), args[0])
1288 if len(args) == 1:
1322 if len(args) == 1:
1289 if not include:
1323 if not include:
1290 return baseset()
1324 return baseset()
1291
1325
1292 descendants = set(_revdescendants(repo, include, False))
1326 descendants = set(_revdescendants(repo, include, False))
1293 exclude = [rev for rev in cl.headrevs()
1327 exclude = [rev for rev in cl.headrevs()
1294 if not rev in descendants and not rev in include]
1328 if not rev in descendants and not rev in include]
1295 else:
1329 else:
1296 exclude = getset(repo, spanset(repo), args[1])
1330 exclude = getset(repo, spanset(repo), args[1])
1297
1331
1298 results = set(cl.findmissingrevs(common=exclude, heads=include))
1332 results = set(cl.findmissingrevs(common=exclude, heads=include))
1299 return subset & results
1333 return subset & results
1300
1334
1301 def origin(repo, subset, x):
1335 def origin(repo, subset, x):
1302 """``origin([set])``
1336 """``origin([set])``
1303 Changesets that were specified as a source for the grafts, transplants or
1337 Changesets that were specified as a source for the grafts, transplants or
1304 rebases that created the given revisions. Omitting the optional set is the
1338 rebases that created the given revisions. Omitting the optional set is the
1305 same as passing all(). If a changeset created by these operations is itself
1339 same as passing all(). If a changeset created by these operations is itself
1306 specified as a source for one of these operations, only the source changeset
1340 specified as a source for one of these operations, only the source changeset
1307 for the first operation is selected.
1341 for the first operation is selected.
1308 """
1342 """
1309 if x is not None:
1343 if x is not None:
1310 dests = getset(repo, spanset(repo), x)
1344 dests = getset(repo, spanset(repo), x)
1311 else:
1345 else:
1312 dests = getall(repo, spanset(repo), x)
1346 dests = getall(repo, spanset(repo), x)
1313
1347
1314 def _firstsrc(rev):
1348 def _firstsrc(rev):
1315 src = _getrevsource(repo, rev)
1349 src = _getrevsource(repo, rev)
1316 if src is None:
1350 if src is None:
1317 return None
1351 return None
1318
1352
1319 while True:
1353 while True:
1320 prev = _getrevsource(repo, src)
1354 prev = _getrevsource(repo, src)
1321
1355
1322 if prev is None:
1356 if prev is None:
1323 return src
1357 return src
1324 src = prev
1358 src = prev
1325
1359
1326 o = set([_firstsrc(r) for r in dests])
1360 o = set([_firstsrc(r) for r in dests])
1327 o -= set([None])
1361 o -= set([None])
1328 return subset & o
1362 return subset & o
1329
1363
1330 def outgoing(repo, subset, x):
1364 def outgoing(repo, subset, x):
1331 """``outgoing([path])``
1365 """``outgoing([path])``
1332 Changesets not found in the specified destination repository, or the
1366 Changesets not found in the specified destination repository, or the
1333 default push location.
1367 default push location.
1334 """
1368 """
1335 import hg # avoid start-up nasties
1369 import hg # avoid start-up nasties
1336 # i18n: "outgoing" is a keyword
1370 # i18n: "outgoing" is a keyword
1337 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1371 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1338 # i18n: "outgoing" is a keyword
1372 # i18n: "outgoing" is a keyword
1339 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1373 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1340 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1374 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1341 dest, branches = hg.parseurl(dest)
1375 dest, branches = hg.parseurl(dest)
1342 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1376 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1343 if revs:
1377 if revs:
1344 revs = [repo.lookup(rev) for rev in revs]
1378 revs = [repo.lookup(rev) for rev in revs]
1345 other = hg.peer(repo, {}, dest)
1379 other = hg.peer(repo, {}, dest)
1346 repo.ui.pushbuffer()
1380 repo.ui.pushbuffer()
1347 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1381 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1348 repo.ui.popbuffer()
1382 repo.ui.popbuffer()
1349 cl = repo.changelog
1383 cl = repo.changelog
1350 o = set([cl.rev(r) for r in outgoing.missing])
1384 o = set([cl.rev(r) for r in outgoing.missing])
1351 return subset & o
1385 return subset & o
1352
1386
1353 def p1(repo, subset, x):
1387 def p1(repo, subset, x):
1354 """``p1([set])``
1388 """``p1([set])``
1355 First parent of changesets in set, or the working directory.
1389 First parent of changesets in set, or the working directory.
1356 """
1390 """
1357 if x is None:
1391 if x is None:
1358 p = repo[x].p1().rev()
1392 p = repo[x].p1().rev()
1359 if p >= 0:
1393 if p >= 0:
1360 return subset & baseset([p])
1394 return subset & baseset([p])
1361 return baseset()
1395 return baseset()
1362
1396
1363 ps = set()
1397 ps = set()
1364 cl = repo.changelog
1398 cl = repo.changelog
1365 for r in getset(repo, spanset(repo), x):
1399 for r in getset(repo, spanset(repo), x):
1366 ps.add(cl.parentrevs(r)[0])
1400 ps.add(cl.parentrevs(r)[0])
1367 ps -= set([node.nullrev])
1401 ps -= set([node.nullrev])
1368 return subset & ps
1402 return subset & ps
1369
1403
1370 def p2(repo, subset, x):
1404 def p2(repo, subset, x):
1371 """``p2([set])``
1405 """``p2([set])``
1372 Second parent of changesets in set, or the working directory.
1406 Second parent of changesets in set, or the working directory.
1373 """
1407 """
1374 if x is None:
1408 if x is None:
1375 ps = repo[x].parents()
1409 ps = repo[x].parents()
1376 try:
1410 try:
1377 p = ps[1].rev()
1411 p = ps[1].rev()
1378 if p >= 0:
1412 if p >= 0:
1379 return subset & baseset([p])
1413 return subset & baseset([p])
1380 return baseset()
1414 return baseset()
1381 except IndexError:
1415 except IndexError:
1382 return baseset()
1416 return baseset()
1383
1417
1384 ps = set()
1418 ps = set()
1385 cl = repo.changelog
1419 cl = repo.changelog
1386 for r in getset(repo, spanset(repo), x):
1420 for r in getset(repo, spanset(repo), x):
1387 ps.add(cl.parentrevs(r)[1])
1421 ps.add(cl.parentrevs(r)[1])
1388 ps -= set([node.nullrev])
1422 ps -= set([node.nullrev])
1389 return subset & ps
1423 return subset & ps
1390
1424
1391 def parents(repo, subset, x):
1425 def parents(repo, subset, x):
1392 """``parents([set])``
1426 """``parents([set])``
1393 The set of all parents for all changesets in set, or the working directory.
1427 The set of all parents for all changesets in set, or the working directory.
1394 """
1428 """
1395 if x is None:
1429 if x is None:
1396 ps = set(p.rev() for p in repo[x].parents())
1430 ps = set(p.rev() for p in repo[x].parents())
1397 else:
1431 else:
1398 ps = set()
1432 ps = set()
1399 cl = repo.changelog
1433 cl = repo.changelog
1400 for r in getset(repo, spanset(repo), x):
1434 for r in getset(repo, spanset(repo), x):
1401 ps.update(cl.parentrevs(r))
1435 ps.update(cl.parentrevs(r))
1402 ps -= set([node.nullrev])
1436 ps -= set([node.nullrev])
1403 return subset & ps
1437 return subset & ps
1404
1438
1405 def parentspec(repo, subset, x, n):
1439 def parentspec(repo, subset, x, n):
1406 """``set^0``
1440 """``set^0``
1407 The set.
1441 The set.
1408 ``set^1`` (or ``set^``), ``set^2``
1442 ``set^1`` (or ``set^``), ``set^2``
1409 First or second parent, respectively, of all changesets in set.
1443 First or second parent, respectively, of all changesets in set.
1410 """
1444 """
1411 try:
1445 try:
1412 n = int(n[1])
1446 n = int(n[1])
1413 if n not in (0, 1, 2):
1447 if n not in (0, 1, 2):
1414 raise ValueError
1448 raise ValueError
1415 except (TypeError, ValueError):
1449 except (TypeError, ValueError):
1416 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1450 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1417 ps = set()
1451 ps = set()
1418 cl = repo.changelog
1452 cl = repo.changelog
1419 for r in getset(repo, fullreposet(repo), x):
1453 for r in getset(repo, fullreposet(repo), x):
1420 if n == 0:
1454 if n == 0:
1421 ps.add(r)
1455 ps.add(r)
1422 elif n == 1:
1456 elif n == 1:
1423 ps.add(cl.parentrevs(r)[0])
1457 ps.add(cl.parentrevs(r)[0])
1424 elif n == 2:
1458 elif n == 2:
1425 parents = cl.parentrevs(r)
1459 parents = cl.parentrevs(r)
1426 if len(parents) > 1:
1460 if len(parents) > 1:
1427 ps.add(parents[1])
1461 ps.add(parents[1])
1428 return subset & ps
1462 return subset & ps
1429
1463
1430 def present(repo, subset, x):
1464 def present(repo, subset, x):
1431 """``present(set)``
1465 """``present(set)``
1432 An empty set, if any revision in set isn't found; otherwise,
1466 An empty set, if any revision in set isn't found; otherwise,
1433 all revisions in set.
1467 all revisions in set.
1434
1468
1435 If any of specified revisions is not present in the local repository,
1469 If any of specified revisions is not present in the local repository,
1436 the query is normally aborted. But this predicate allows the query
1470 the query is normally aborted. But this predicate allows the query
1437 to continue even in such cases.
1471 to continue even in such cases.
1438 """
1472 """
1439 try:
1473 try:
1440 return getset(repo, subset, x)
1474 return getset(repo, subset, x)
1441 except error.RepoLookupError:
1475 except error.RepoLookupError:
1442 return baseset()
1476 return baseset()
1443
1477
1444 def public(repo, subset, x):
1478 def public(repo, subset, x):
1445 """``public()``
1479 """``public()``
1446 Changeset in public phase."""
1480 Changeset in public phase."""
1447 # i18n: "public" is a keyword
1481 # i18n: "public" is a keyword
1448 getargs(x, 0, 0, _("public takes no arguments"))
1482 getargs(x, 0, 0, _("public takes no arguments"))
1449 phase = repo._phasecache.phase
1483 phase = repo._phasecache.phase
1450 target = phases.public
1484 target = phases.public
1451 condition = lambda r: phase(repo, r) == target
1485 condition = lambda r: phase(repo, r) == target
1452 return subset.filter(condition, cache=False)
1486 return subset.filter(condition, cache=False)
1453
1487
1454 def remote(repo, subset, x):
1488 def remote(repo, subset, x):
1455 """``remote([id [,path]])``
1489 """``remote([id [,path]])``
1456 Local revision that corresponds to the given identifier in a
1490 Local revision that corresponds to the given identifier in a
1457 remote repository, if present. Here, the '.' identifier is a
1491 remote repository, if present. Here, the '.' identifier is a
1458 synonym for the current local branch.
1492 synonym for the current local branch.
1459 """
1493 """
1460
1494
1461 import hg # avoid start-up nasties
1495 import hg # avoid start-up nasties
1462 # i18n: "remote" is a keyword
1496 # i18n: "remote" is a keyword
1463 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1497 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1464
1498
1465 q = '.'
1499 q = '.'
1466 if len(l) > 0:
1500 if len(l) > 0:
1467 # i18n: "remote" is a keyword
1501 # i18n: "remote" is a keyword
1468 q = getstring(l[0], _("remote requires a string id"))
1502 q = getstring(l[0], _("remote requires a string id"))
1469 if q == '.':
1503 if q == '.':
1470 q = repo['.'].branch()
1504 q = repo['.'].branch()
1471
1505
1472 dest = ''
1506 dest = ''
1473 if len(l) > 1:
1507 if len(l) > 1:
1474 # i18n: "remote" is a keyword
1508 # i18n: "remote" is a keyword
1475 dest = getstring(l[1], _("remote requires a repository path"))
1509 dest = getstring(l[1], _("remote requires a repository path"))
1476 dest = repo.ui.expandpath(dest or 'default')
1510 dest = repo.ui.expandpath(dest or 'default')
1477 dest, branches = hg.parseurl(dest)
1511 dest, branches = hg.parseurl(dest)
1478 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1512 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1479 if revs:
1513 if revs:
1480 revs = [repo.lookup(rev) for rev in revs]
1514 revs = [repo.lookup(rev) for rev in revs]
1481 other = hg.peer(repo, {}, dest)
1515 other = hg.peer(repo, {}, dest)
1482 n = other.lookup(q)
1516 n = other.lookup(q)
1483 if n in repo:
1517 if n in repo:
1484 r = repo[n].rev()
1518 r = repo[n].rev()
1485 if r in subset:
1519 if r in subset:
1486 return baseset([r])
1520 return baseset([r])
1487 return baseset()
1521 return baseset()
1488
1522
1489 def removes(repo, subset, x):
1523 def removes(repo, subset, x):
1490 """``removes(pattern)``
1524 """``removes(pattern)``
1491 Changesets which remove files matching pattern.
1525 Changesets which remove files matching pattern.
1492
1526
1493 The pattern without explicit kind like ``glob:`` is expected to be
1527 The pattern without explicit kind like ``glob:`` is expected to be
1494 relative to the current directory and match against a file or a
1528 relative to the current directory and match against a file or a
1495 directory.
1529 directory.
1496 """
1530 """
1497 # i18n: "removes" is a keyword
1531 # i18n: "removes" is a keyword
1498 pat = getstring(x, _("removes requires a pattern"))
1532 pat = getstring(x, _("removes requires a pattern"))
1499 return checkstatus(repo, subset, pat, 2)
1533 return checkstatus(repo, subset, pat, 2)
1500
1534
1501 def rev(repo, subset, x):
1535 def rev(repo, subset, x):
1502 """``rev(number)``
1536 """``rev(number)``
1503 Revision with the given numeric identifier.
1537 Revision with the given numeric identifier.
1504 """
1538 """
1505 # i18n: "rev" is a keyword
1539 # i18n: "rev" is a keyword
1506 l = getargs(x, 1, 1, _("rev requires one argument"))
1540 l = getargs(x, 1, 1, _("rev requires one argument"))
1507 try:
1541 try:
1508 # i18n: "rev" is a keyword
1542 # i18n: "rev" is a keyword
1509 l = int(getstring(l[0], _("rev requires a number")))
1543 l = int(getstring(l[0], _("rev requires a number")))
1510 except (TypeError, ValueError):
1544 except (TypeError, ValueError):
1511 # i18n: "rev" is a keyword
1545 # i18n: "rev" is a keyword
1512 raise error.ParseError(_("rev expects a number"))
1546 raise error.ParseError(_("rev expects a number"))
1513 if l not in fullreposet(repo):
1547 if l not in fullreposet(repo):
1514 return baseset()
1548 return baseset()
1515 return subset & baseset([l])
1549 return subset & baseset([l])
1516
1550
1517 def matching(repo, subset, x):
1551 def matching(repo, subset, x):
1518 """``matching(revision [, field])``
1552 """``matching(revision [, field])``
1519 Changesets in which a given set of fields match the set of fields in the
1553 Changesets in which a given set of fields match the set of fields in the
1520 selected revision or set.
1554 selected revision or set.
1521
1555
1522 To match more than one field pass the list of fields to match separated
1556 To match more than one field pass the list of fields to match separated
1523 by spaces (e.g. ``author description``).
1557 by spaces (e.g. ``author description``).
1524
1558
1525 Valid fields are most regular revision fields and some special fields.
1559 Valid fields are most regular revision fields and some special fields.
1526
1560
1527 Regular revision fields are ``description``, ``author``, ``branch``,
1561 Regular revision fields are ``description``, ``author``, ``branch``,
1528 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1562 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1529 and ``diff``.
1563 and ``diff``.
1530 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1564 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1531 contents of the revision. Two revisions matching their ``diff`` will
1565 contents of the revision. Two revisions matching their ``diff`` will
1532 also match their ``files``.
1566 also match their ``files``.
1533
1567
1534 Special fields are ``summary`` and ``metadata``:
1568 Special fields are ``summary`` and ``metadata``:
1535 ``summary`` matches the first line of the description.
1569 ``summary`` matches the first line of the description.
1536 ``metadata`` is equivalent to matching ``description user date``
1570 ``metadata`` is equivalent to matching ``description user date``
1537 (i.e. it matches the main metadata fields).
1571 (i.e. it matches the main metadata fields).
1538
1572
1539 ``metadata`` is the default field which is used when no fields are
1573 ``metadata`` is the default field which is used when no fields are
1540 specified. You can match more than one field at a time.
1574 specified. You can match more than one field at a time.
1541 """
1575 """
1542 # i18n: "matching" is a keyword
1576 # i18n: "matching" is a keyword
1543 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1577 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1544
1578
1545 revs = getset(repo, fullreposet(repo), l[0])
1579 revs = getset(repo, fullreposet(repo), l[0])
1546
1580
1547 fieldlist = ['metadata']
1581 fieldlist = ['metadata']
1548 if len(l) > 1:
1582 if len(l) > 1:
1549 fieldlist = getstring(l[1],
1583 fieldlist = getstring(l[1],
1550 # i18n: "matching" is a keyword
1584 # i18n: "matching" is a keyword
1551 _("matching requires a string "
1585 _("matching requires a string "
1552 "as its second argument")).split()
1586 "as its second argument")).split()
1553
1587
1554 # Make sure that there are no repeated fields,
1588 # Make sure that there are no repeated fields,
1555 # expand the 'special' 'metadata' field type
1589 # expand the 'special' 'metadata' field type
1556 # and check the 'files' whenever we check the 'diff'
1590 # and check the 'files' whenever we check the 'diff'
1557 fields = []
1591 fields = []
1558 for field in fieldlist:
1592 for field in fieldlist:
1559 if field == 'metadata':
1593 if field == 'metadata':
1560 fields += ['user', 'description', 'date']
1594 fields += ['user', 'description', 'date']
1561 elif field == 'diff':
1595 elif field == 'diff':
1562 # a revision matching the diff must also match the files
1596 # a revision matching the diff must also match the files
1563 # since matching the diff is very costly, make sure to
1597 # since matching the diff is very costly, make sure to
1564 # also match the files first
1598 # also match the files first
1565 fields += ['files', 'diff']
1599 fields += ['files', 'diff']
1566 else:
1600 else:
1567 if field == 'author':
1601 if field == 'author':
1568 field = 'user'
1602 field = 'user'
1569 fields.append(field)
1603 fields.append(field)
1570 fields = set(fields)
1604 fields = set(fields)
1571 if 'summary' in fields and 'description' in fields:
1605 if 'summary' in fields and 'description' in fields:
1572 # If a revision matches its description it also matches its summary
1606 # If a revision matches its description it also matches its summary
1573 fields.discard('summary')
1607 fields.discard('summary')
1574
1608
1575 # We may want to match more than one field
1609 # We may want to match more than one field
1576 # Not all fields take the same amount of time to be matched
1610 # Not all fields take the same amount of time to be matched
1577 # Sort the selected fields in order of increasing matching cost
1611 # Sort the selected fields in order of increasing matching cost
1578 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1612 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1579 'files', 'description', 'substate', 'diff']
1613 'files', 'description', 'substate', 'diff']
1580 def fieldkeyfunc(f):
1614 def fieldkeyfunc(f):
1581 try:
1615 try:
1582 return fieldorder.index(f)
1616 return fieldorder.index(f)
1583 except ValueError:
1617 except ValueError:
1584 # assume an unknown field is very costly
1618 # assume an unknown field is very costly
1585 return len(fieldorder)
1619 return len(fieldorder)
1586 fields = list(fields)
1620 fields = list(fields)
1587 fields.sort(key=fieldkeyfunc)
1621 fields.sort(key=fieldkeyfunc)
1588
1622
1589 # Each field will be matched with its own "getfield" function
1623 # Each field will be matched with its own "getfield" function
1590 # which will be added to the getfieldfuncs array of functions
1624 # which will be added to the getfieldfuncs array of functions
1591 getfieldfuncs = []
1625 getfieldfuncs = []
1592 _funcs = {
1626 _funcs = {
1593 'user': lambda r: repo[r].user(),
1627 'user': lambda r: repo[r].user(),
1594 'branch': lambda r: repo[r].branch(),
1628 'branch': lambda r: repo[r].branch(),
1595 'date': lambda r: repo[r].date(),
1629 'date': lambda r: repo[r].date(),
1596 'description': lambda r: repo[r].description(),
1630 'description': lambda r: repo[r].description(),
1597 'files': lambda r: repo[r].files(),
1631 'files': lambda r: repo[r].files(),
1598 'parents': lambda r: repo[r].parents(),
1632 'parents': lambda r: repo[r].parents(),
1599 'phase': lambda r: repo[r].phase(),
1633 'phase': lambda r: repo[r].phase(),
1600 'substate': lambda r: repo[r].substate,
1634 'substate': lambda r: repo[r].substate,
1601 'summary': lambda r: repo[r].description().splitlines()[0],
1635 'summary': lambda r: repo[r].description().splitlines()[0],
1602 'diff': lambda r: list(repo[r].diff(git=True),)
1636 'diff': lambda r: list(repo[r].diff(git=True),)
1603 }
1637 }
1604 for info in fields:
1638 for info in fields:
1605 getfield = _funcs.get(info, None)
1639 getfield = _funcs.get(info, None)
1606 if getfield is None:
1640 if getfield is None:
1607 raise error.ParseError(
1641 raise error.ParseError(
1608 # i18n: "matching" is a keyword
1642 # i18n: "matching" is a keyword
1609 _("unexpected field name passed to matching: %s") % info)
1643 _("unexpected field name passed to matching: %s") % info)
1610 getfieldfuncs.append(getfield)
1644 getfieldfuncs.append(getfield)
1611 # convert the getfield array of functions into a "getinfo" function
1645 # convert the getfield array of functions into a "getinfo" function
1612 # which returns an array of field values (or a single value if there
1646 # which returns an array of field values (or a single value if there
1613 # is only one field to match)
1647 # is only one field to match)
1614 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1648 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1615
1649
1616 def matches(x):
1650 def matches(x):
1617 for rev in revs:
1651 for rev in revs:
1618 target = getinfo(rev)
1652 target = getinfo(rev)
1619 match = True
1653 match = True
1620 for n, f in enumerate(getfieldfuncs):
1654 for n, f in enumerate(getfieldfuncs):
1621 if target[n] != f(x):
1655 if target[n] != f(x):
1622 match = False
1656 match = False
1623 if match:
1657 if match:
1624 return True
1658 return True
1625 return False
1659 return False
1626
1660
1627 return subset.filter(matches)
1661 return subset.filter(matches)
1628
1662
1629 def reverse(repo, subset, x):
1663 def reverse(repo, subset, x):
1630 """``reverse(set)``
1664 """``reverse(set)``
1631 Reverse order of set.
1665 Reverse order of set.
1632 """
1666 """
1633 l = getset(repo, subset, x)
1667 l = getset(repo, subset, x)
1634 l.reverse()
1668 l.reverse()
1635 return l
1669 return l
1636
1670
1637 def roots(repo, subset, x):
1671 def roots(repo, subset, x):
1638 """``roots(set)``
1672 """``roots(set)``
1639 Changesets in set with no parent changeset in set.
1673 Changesets in set with no parent changeset in set.
1640 """
1674 """
1641 s = getset(repo, spanset(repo), x)
1675 s = getset(repo, spanset(repo), x)
1642 subset = baseset([r for r in s if r in subset])
1676 subset = baseset([r for r in s if r in subset])
1643 cs = _children(repo, subset, s)
1677 cs = _children(repo, subset, s)
1644 return subset - cs
1678 return subset - cs
1645
1679
1646 def secret(repo, subset, x):
1680 def secret(repo, subset, x):
1647 """``secret()``
1681 """``secret()``
1648 Changeset in secret phase."""
1682 Changeset in secret phase."""
1649 # i18n: "secret" is a keyword
1683 # i18n: "secret" is a keyword
1650 getargs(x, 0, 0, _("secret takes no arguments"))
1684 getargs(x, 0, 0, _("secret takes no arguments"))
1651 phase = repo._phasecache.phase
1685 phase = repo._phasecache.phase
1652 target = phases.secret
1686 target = phases.secret
1653 condition = lambda r: phase(repo, r) == target
1687 condition = lambda r: phase(repo, r) == target
1654 return subset.filter(condition, cache=False)
1688 return subset.filter(condition, cache=False)
1655
1689
1656 def sort(repo, subset, x):
1690 def sort(repo, subset, x):
1657 """``sort(set[, [-]key...])``
1691 """``sort(set[, [-]key...])``
1658 Sort set by keys. The default sort order is ascending, specify a key
1692 Sort set by keys. The default sort order is ascending, specify a key
1659 as ``-key`` to sort in descending order.
1693 as ``-key`` to sort in descending order.
1660
1694
1661 The keys can be:
1695 The keys can be:
1662
1696
1663 - ``rev`` for the revision number,
1697 - ``rev`` for the revision number,
1664 - ``branch`` for the branch name,
1698 - ``branch`` for the branch name,
1665 - ``desc`` for the commit message (description),
1699 - ``desc`` for the commit message (description),
1666 - ``user`` for user name (``author`` can be used as an alias),
1700 - ``user`` for user name (``author`` can be used as an alias),
1667 - ``date`` for the commit date
1701 - ``date`` for the commit date
1668 """
1702 """
1669 # i18n: "sort" is a keyword
1703 # i18n: "sort" is a keyword
1670 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1704 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1671 keys = "rev"
1705 keys = "rev"
1672 if len(l) == 2:
1706 if len(l) == 2:
1673 # i18n: "sort" is a keyword
1707 # i18n: "sort" is a keyword
1674 keys = getstring(l[1], _("sort spec must be a string"))
1708 keys = getstring(l[1], _("sort spec must be a string"))
1675
1709
1676 s = l[0]
1710 s = l[0]
1677 keys = keys.split()
1711 keys = keys.split()
1678 l = []
1712 l = []
1679 def invert(s):
1713 def invert(s):
1680 return "".join(chr(255 - ord(c)) for c in s)
1714 return "".join(chr(255 - ord(c)) for c in s)
1681 revs = getset(repo, subset, s)
1715 revs = getset(repo, subset, s)
1682 if keys == ["rev"]:
1716 if keys == ["rev"]:
1683 revs.sort()
1717 revs.sort()
1684 return revs
1718 return revs
1685 elif keys == ["-rev"]:
1719 elif keys == ["-rev"]:
1686 revs.sort(reverse=True)
1720 revs.sort(reverse=True)
1687 return revs
1721 return revs
1688 for r in revs:
1722 for r in revs:
1689 c = repo[r]
1723 c = repo[r]
1690 e = []
1724 e = []
1691 for k in keys:
1725 for k in keys:
1692 if k == 'rev':
1726 if k == 'rev':
1693 e.append(r)
1727 e.append(r)
1694 elif k == '-rev':
1728 elif k == '-rev':
1695 e.append(-r)
1729 e.append(-r)
1696 elif k == 'branch':
1730 elif k == 'branch':
1697 e.append(c.branch())
1731 e.append(c.branch())
1698 elif k == '-branch':
1732 elif k == '-branch':
1699 e.append(invert(c.branch()))
1733 e.append(invert(c.branch()))
1700 elif k == 'desc':
1734 elif k == 'desc':
1701 e.append(c.description())
1735 e.append(c.description())
1702 elif k == '-desc':
1736 elif k == '-desc':
1703 e.append(invert(c.description()))
1737 e.append(invert(c.description()))
1704 elif k in 'user author':
1738 elif k in 'user author':
1705 e.append(c.user())
1739 e.append(c.user())
1706 elif k in '-user -author':
1740 elif k in '-user -author':
1707 e.append(invert(c.user()))
1741 e.append(invert(c.user()))
1708 elif k == 'date':
1742 elif k == 'date':
1709 e.append(c.date()[0])
1743 e.append(c.date()[0])
1710 elif k == '-date':
1744 elif k == '-date':
1711 e.append(-c.date()[0])
1745 e.append(-c.date()[0])
1712 else:
1746 else:
1713 raise error.ParseError(_("unknown sort key %r") % k)
1747 raise error.ParseError(_("unknown sort key %r") % k)
1714 e.append(r)
1748 e.append(r)
1715 l.append(e)
1749 l.append(e)
1716 l.sort()
1750 l.sort()
1717 return baseset([e[-1] for e in l])
1751 return baseset([e[-1] for e in l])
1718
1752
1719 def _stringmatcher(pattern):
1753 def _stringmatcher(pattern):
1720 """
1754 """
1721 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1755 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1722 returns the matcher name, pattern, and matcher function.
1756 returns the matcher name, pattern, and matcher function.
1723 missing or unknown prefixes are treated as literal matches.
1757 missing or unknown prefixes are treated as literal matches.
1724
1758
1725 helper for tests:
1759 helper for tests:
1726 >>> def test(pattern, *tests):
1760 >>> def test(pattern, *tests):
1727 ... kind, pattern, matcher = _stringmatcher(pattern)
1761 ... kind, pattern, matcher = _stringmatcher(pattern)
1728 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1762 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1729
1763
1730 exact matching (no prefix):
1764 exact matching (no prefix):
1731 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1765 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1732 ('literal', 'abcdefg', [False, False, True])
1766 ('literal', 'abcdefg', [False, False, True])
1733
1767
1734 regex matching ('re:' prefix)
1768 regex matching ('re:' prefix)
1735 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1769 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1736 ('re', 'a.+b', [False, False, True])
1770 ('re', 'a.+b', [False, False, True])
1737
1771
1738 force exact matches ('literal:' prefix)
1772 force exact matches ('literal:' prefix)
1739 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1773 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1740 ('literal', 're:foobar', [False, True])
1774 ('literal', 're:foobar', [False, True])
1741
1775
1742 unknown prefixes are ignored and treated as literals
1776 unknown prefixes are ignored and treated as literals
1743 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1777 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1744 ('literal', 'foo:bar', [False, False, True])
1778 ('literal', 'foo:bar', [False, False, True])
1745 """
1779 """
1746 if pattern.startswith('re:'):
1780 if pattern.startswith('re:'):
1747 pattern = pattern[3:]
1781 pattern = pattern[3:]
1748 try:
1782 try:
1749 regex = re.compile(pattern)
1783 regex = re.compile(pattern)
1750 except re.error, e:
1784 except re.error, e:
1751 raise error.ParseError(_('invalid regular expression: %s')
1785 raise error.ParseError(_('invalid regular expression: %s')
1752 % e)
1786 % e)
1753 return 're', pattern, regex.search
1787 return 're', pattern, regex.search
1754 elif pattern.startswith('literal:'):
1788 elif pattern.startswith('literal:'):
1755 pattern = pattern[8:]
1789 pattern = pattern[8:]
1756 return 'literal', pattern, pattern.__eq__
1790 return 'literal', pattern, pattern.__eq__
1757
1791
1758 def _substringmatcher(pattern):
1792 def _substringmatcher(pattern):
1759 kind, pattern, matcher = _stringmatcher(pattern)
1793 kind, pattern, matcher = _stringmatcher(pattern)
1760 if kind == 'literal':
1794 if kind == 'literal':
1761 matcher = lambda s: pattern in s
1795 matcher = lambda s: pattern in s
1762 return kind, pattern, matcher
1796 return kind, pattern, matcher
1763
1797
1764 def tag(repo, subset, x):
1798 def tag(repo, subset, x):
1765 """``tag([name])``
1799 """``tag([name])``
1766 The specified tag by name, or all tagged revisions if no name is given.
1800 The specified tag by name, or all tagged revisions if no name is given.
1767
1801
1768 If `name` starts with `re:`, the remainder of the name is treated as
1802 If `name` starts with `re:`, the remainder of the name is treated as
1769 a regular expression. To match a tag that actually starts with `re:`,
1803 a regular expression. To match a tag that actually starts with `re:`,
1770 use the prefix `literal:`.
1804 use the prefix `literal:`.
1771 """
1805 """
1772 # i18n: "tag" is a keyword
1806 # i18n: "tag" is a keyword
1773 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1807 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1774 cl = repo.changelog
1808 cl = repo.changelog
1775 if args:
1809 if args:
1776 pattern = getstring(args[0],
1810 pattern = getstring(args[0],
1777 # i18n: "tag" is a keyword
1811 # i18n: "tag" is a keyword
1778 _('the argument to tag must be a string'))
1812 _('the argument to tag must be a string'))
1779 kind, pattern, matcher = _stringmatcher(pattern)
1813 kind, pattern, matcher = _stringmatcher(pattern)
1780 if kind == 'literal':
1814 if kind == 'literal':
1781 # avoid resolving all tags
1815 # avoid resolving all tags
1782 tn = repo._tagscache.tags.get(pattern, None)
1816 tn = repo._tagscache.tags.get(pattern, None)
1783 if tn is None:
1817 if tn is None:
1784 raise util.Abort(_("tag '%s' does not exist") % pattern)
1818 raise util.Abort(_("tag '%s' does not exist") % pattern)
1785 s = set([repo[tn].rev()])
1819 s = set([repo[tn].rev()])
1786 else:
1820 else:
1787 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1821 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1788 else:
1822 else:
1789 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1823 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1790 return subset & s
1824 return subset & s
1791
1825
1792 def tagged(repo, subset, x):
1826 def tagged(repo, subset, x):
1793 return tag(repo, subset, x)
1827 return tag(repo, subset, x)
1794
1828
1795 def unstable(repo, subset, x):
1829 def unstable(repo, subset, x):
1796 """``unstable()``
1830 """``unstable()``
1797 Non-obsolete changesets with obsolete ancestors.
1831 Non-obsolete changesets with obsolete ancestors.
1798 """
1832 """
1799 # i18n: "unstable" is a keyword
1833 # i18n: "unstable" is a keyword
1800 getargs(x, 0, 0, _("unstable takes no arguments"))
1834 getargs(x, 0, 0, _("unstable takes no arguments"))
1801 unstables = obsmod.getrevs(repo, 'unstable')
1835 unstables = obsmod.getrevs(repo, 'unstable')
1802 return subset & unstables
1836 return subset & unstables
1803
1837
1804
1838
1805 def user(repo, subset, x):
1839 def user(repo, subset, x):
1806 """``user(string)``
1840 """``user(string)``
1807 User name contains string. The match is case-insensitive.
1841 User name contains string. The match is case-insensitive.
1808
1842
1809 If `string` starts with `re:`, the remainder of the string is treated as
1843 If `string` starts with `re:`, the remainder of the string is treated as
1810 a regular expression. To match a user that actually contains `re:`, use
1844 a regular expression. To match a user that actually contains `re:`, use
1811 the prefix `literal:`.
1845 the prefix `literal:`.
1812 """
1846 """
1813 return author(repo, subset, x)
1847 return author(repo, subset, x)
1814
1848
1815 # for internal use
1849 # for internal use
1816 def _list(repo, subset, x):
1850 def _list(repo, subset, x):
1817 s = getstring(x, "internal error")
1851 s = getstring(x, "internal error")
1818 if not s:
1852 if not s:
1819 return baseset()
1853 return baseset()
1820 ls = [repo[r].rev() for r in s.split('\0')]
1854 ls = [repo[r].rev() for r in s.split('\0')]
1821 s = subset
1855 s = subset
1822 return baseset([r for r in ls if r in s])
1856 return baseset([r for r in ls if r in s])
1823
1857
1824 # for internal use
1858 # for internal use
1825 def _intlist(repo, subset, x):
1859 def _intlist(repo, subset, x):
1826 s = getstring(x, "internal error")
1860 s = getstring(x, "internal error")
1827 if not s:
1861 if not s:
1828 return baseset()
1862 return baseset()
1829 ls = [int(r) for r in s.split('\0')]
1863 ls = [int(r) for r in s.split('\0')]
1830 s = subset
1864 s = subset
1831 return baseset([r for r in ls if r in s])
1865 return baseset([r for r in ls if r in s])
1832
1866
1833 # for internal use
1867 # for internal use
1834 def _hexlist(repo, subset, x):
1868 def _hexlist(repo, subset, x):
1835 s = getstring(x, "internal error")
1869 s = getstring(x, "internal error")
1836 if not s:
1870 if not s:
1837 return baseset()
1871 return baseset()
1838 cl = repo.changelog
1872 cl = repo.changelog
1839 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
1873 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
1840 s = subset
1874 s = subset
1841 return baseset([r for r in ls if r in s])
1875 return baseset([r for r in ls if r in s])
1842
1876
1843 symbols = {
1877 symbols = {
1844 "adds": adds,
1878 "adds": adds,
1845 "all": getall,
1879 "all": getall,
1846 "ancestor": ancestor,
1880 "ancestor": ancestor,
1847 "ancestors": ancestors,
1881 "ancestors": ancestors,
1848 "_firstancestors": _firstancestors,
1882 "_firstancestors": _firstancestors,
1849 "author": author,
1883 "author": author,
1850 "bisect": bisect,
1884 "bisect": bisect,
1851 "bisected": bisected,
1885 "bisected": bisected,
1852 "bookmark": bookmark,
1886 "bookmark": bookmark,
1853 "branch": branch,
1887 "branch": branch,
1854 "branchpoint": branchpoint,
1888 "branchpoint": branchpoint,
1855 "bumped": bumped,
1889 "bumped": bumped,
1856 "bundle": bundle,
1890 "bundle": bundle,
1857 "children": children,
1891 "children": children,
1858 "closed": closed,
1892 "closed": closed,
1859 "contains": contains,
1893 "contains": contains,
1860 "converted": converted,
1894 "converted": converted,
1861 "date": date,
1895 "date": date,
1862 "desc": desc,
1896 "desc": desc,
1863 "descendants": descendants,
1897 "descendants": descendants,
1864 "_firstdescendants": _firstdescendants,
1898 "_firstdescendants": _firstdescendants,
1865 "destination": destination,
1899 "destination": destination,
1866 "divergent": divergent,
1900 "divergent": divergent,
1867 "draft": draft,
1901 "draft": draft,
1868 "extinct": extinct,
1902 "extinct": extinct,
1869 "extra": extra,
1903 "extra": extra,
1870 "file": hasfile,
1904 "file": hasfile,
1871 "filelog": filelog,
1905 "filelog": filelog,
1872 "first": first,
1906 "first": first,
1873 "follow": follow,
1907 "follow": follow,
1874 "_followfirst": _followfirst,
1908 "_followfirst": _followfirst,
1875 "grep": grep,
1909 "grep": grep,
1876 "head": head,
1910 "head": head,
1877 "heads": heads,
1911 "heads": heads,
1878 "hidden": hidden,
1912 "hidden": hidden,
1879 "id": node_,
1913 "id": node_,
1880 "keyword": keyword,
1914 "keyword": keyword,
1881 "last": last,
1915 "last": last,
1882 "limit": limit,
1916 "limit": limit,
1883 "_matchfiles": _matchfiles,
1917 "_matchfiles": _matchfiles,
1884 "max": maxrev,
1918 "max": maxrev,
1885 "merge": merge,
1919 "merge": merge,
1886 "min": minrev,
1920 "min": minrev,
1887 "modifies": modifies,
1921 "modifies": modifies,
1888 "named": named,
1922 "named": named,
1889 "obsolete": obsolete,
1923 "obsolete": obsolete,
1890 "only": only,
1924 "only": only,
1891 "origin": origin,
1925 "origin": origin,
1892 "outgoing": outgoing,
1926 "outgoing": outgoing,
1893 "p1": p1,
1927 "p1": p1,
1894 "p2": p2,
1928 "p2": p2,
1895 "parents": parents,
1929 "parents": parents,
1896 "present": present,
1930 "present": present,
1897 "public": public,
1931 "public": public,
1898 "remote": remote,
1932 "remote": remote,
1899 "removes": removes,
1933 "removes": removes,
1900 "rev": rev,
1934 "rev": rev,
1901 "reverse": reverse,
1935 "reverse": reverse,
1902 "roots": roots,
1936 "roots": roots,
1903 "sort": sort,
1937 "sort": sort,
1904 "secret": secret,
1938 "secret": secret,
1905 "matching": matching,
1939 "matching": matching,
1906 "tag": tag,
1940 "tag": tag,
1907 "tagged": tagged,
1941 "tagged": tagged,
1908 "user": user,
1942 "user": user,
1909 "unstable": unstable,
1943 "unstable": unstable,
1910 "_list": _list,
1944 "_list": _list,
1911 "_intlist": _intlist,
1945 "_intlist": _intlist,
1912 "_hexlist": _hexlist,
1946 "_hexlist": _hexlist,
1913 }
1947 }
1914
1948
1915 # symbols which can't be used for a DoS attack for any given input
1949 # symbols which can't be used for a DoS attack for any given input
1916 # (e.g. those which accept regexes as plain strings shouldn't be included)
1950 # (e.g. those which accept regexes as plain strings shouldn't be included)
1917 # functions that just return a lot of changesets (like all) don't count here
1951 # functions that just return a lot of changesets (like all) don't count here
1918 safesymbols = set([
1952 safesymbols = set([
1919 "adds",
1953 "adds",
1920 "all",
1954 "all",
1921 "ancestor",
1955 "ancestor",
1922 "ancestors",
1956 "ancestors",
1923 "_firstancestors",
1957 "_firstancestors",
1924 "author",
1958 "author",
1925 "bisect",
1959 "bisect",
1926 "bisected",
1960 "bisected",
1927 "bookmark",
1961 "bookmark",
1928 "branch",
1962 "branch",
1929 "branchpoint",
1963 "branchpoint",
1930 "bumped",
1964 "bumped",
1931 "bundle",
1965 "bundle",
1932 "children",
1966 "children",
1933 "closed",
1967 "closed",
1934 "converted",
1968 "converted",
1935 "date",
1969 "date",
1936 "desc",
1970 "desc",
1937 "descendants",
1971 "descendants",
1938 "_firstdescendants",
1972 "_firstdescendants",
1939 "destination",
1973 "destination",
1940 "divergent",
1974 "divergent",
1941 "draft",
1975 "draft",
1942 "extinct",
1976 "extinct",
1943 "extra",
1977 "extra",
1944 "file",
1978 "file",
1945 "filelog",
1979 "filelog",
1946 "first",
1980 "first",
1947 "follow",
1981 "follow",
1948 "_followfirst",
1982 "_followfirst",
1949 "head",
1983 "head",
1950 "heads",
1984 "heads",
1951 "hidden",
1985 "hidden",
1952 "id",
1986 "id",
1953 "keyword",
1987 "keyword",
1954 "last",
1988 "last",
1955 "limit",
1989 "limit",
1956 "_matchfiles",
1990 "_matchfiles",
1957 "max",
1991 "max",
1958 "merge",
1992 "merge",
1959 "min",
1993 "min",
1960 "modifies",
1994 "modifies",
1961 "obsolete",
1995 "obsolete",
1962 "only",
1996 "only",
1963 "origin",
1997 "origin",
1964 "outgoing",
1998 "outgoing",
1965 "p1",
1999 "p1",
1966 "p2",
2000 "p2",
1967 "parents",
2001 "parents",
1968 "present",
2002 "present",
1969 "public",
2003 "public",
1970 "remote",
2004 "remote",
1971 "removes",
2005 "removes",
1972 "rev",
2006 "rev",
1973 "reverse",
2007 "reverse",
1974 "roots",
2008 "roots",
1975 "sort",
2009 "sort",
1976 "secret",
2010 "secret",
1977 "matching",
2011 "matching",
1978 "tag",
2012 "tag",
1979 "tagged",
2013 "tagged",
1980 "user",
2014 "user",
1981 "unstable",
2015 "unstable",
1982 "_list",
2016 "_list",
1983 "_intlist",
2017 "_intlist",
1984 "_hexlist",
2018 "_hexlist",
1985 ])
2019 ])
1986
2020
1987 methods = {
2021 methods = {
1988 "range": rangeset,
2022 "range": rangeset,
1989 "dagrange": dagrange,
2023 "dagrange": dagrange,
1990 "string": stringset,
2024 "string": stringset,
1991 "symbol": symbolset,
2025 "symbol": symbolset,
1992 "and": andset,
2026 "and": andset,
1993 "or": orset,
2027 "or": orset,
1994 "not": notset,
2028 "not": notset,
1995 "list": listset,
2029 "list": listset,
1996 "func": func,
2030 "func": func,
1997 "ancestor": ancestorspec,
2031 "ancestor": ancestorspec,
1998 "parent": parentspec,
2032 "parent": parentspec,
1999 "parentpost": p1,
2033 "parentpost": p1,
2000 "only": only,
2034 "only": only,
2001 "onlypost": only,
2035 "onlypost": only,
2002 }
2036 }
2003
2037
2004 def optimize(x, small):
2038 def optimize(x, small):
2005 if x is None:
2039 if x is None:
2006 return 0, x
2040 return 0, x
2007
2041
2008 smallbonus = 1
2042 smallbonus = 1
2009 if small:
2043 if small:
2010 smallbonus = .5
2044 smallbonus = .5
2011
2045
2012 op = x[0]
2046 op = x[0]
2013 if op == 'minus':
2047 if op == 'minus':
2014 return optimize(('and', x[1], ('not', x[2])), small)
2048 return optimize(('and', x[1], ('not', x[2])), small)
2015 elif op == 'only':
2049 elif op == 'only':
2016 return optimize(('func', ('symbol', 'only'),
2050 return optimize(('func', ('symbol', 'only'),
2017 ('list', x[1], x[2])), small)
2051 ('list', x[1], x[2])), small)
2018 elif op == 'dagrangepre':
2052 elif op == 'dagrangepre':
2019 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
2053 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
2020 elif op == 'dagrangepost':
2054 elif op == 'dagrangepost':
2021 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
2055 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
2022 elif op == 'rangepre':
2056 elif op == 'rangepre':
2023 return optimize(('range', ('string', '0'), x[1]), small)
2057 return optimize(('range', ('string', '0'), x[1]), small)
2024 elif op == 'rangepost':
2058 elif op == 'rangepost':
2025 return optimize(('range', x[1], ('string', 'tip')), small)
2059 return optimize(('range', x[1], ('string', 'tip')), small)
2026 elif op == 'negate':
2060 elif op == 'negate':
2027 return optimize(('string',
2061 return optimize(('string',
2028 '-' + getstring(x[1], _("can't negate that"))), small)
2062 '-' + getstring(x[1], _("can't negate that"))), small)
2029 elif op in 'string symbol negate':
2063 elif op in 'string symbol negate':
2030 return smallbonus, x # single revisions are small
2064 return smallbonus, x # single revisions are small
2031 elif op == 'and':
2065 elif op == 'and':
2032 wa, ta = optimize(x[1], True)
2066 wa, ta = optimize(x[1], True)
2033 wb, tb = optimize(x[2], True)
2067 wb, tb = optimize(x[2], True)
2034
2068
2035 # (::x and not ::y)/(not ::y and ::x) have a fast path
2069 # (::x and not ::y)/(not ::y and ::x) have a fast path
2036 def isonly(revs, bases):
2070 def isonly(revs, bases):
2037 return (
2071 return (
2038 revs[0] == 'func'
2072 revs[0] == 'func'
2039 and getstring(revs[1], _('not a symbol')) == 'ancestors'
2073 and getstring(revs[1], _('not a symbol')) == 'ancestors'
2040 and bases[0] == 'not'
2074 and bases[0] == 'not'
2041 and bases[1][0] == 'func'
2075 and bases[1][0] == 'func'
2042 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
2076 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
2043
2077
2044 w = min(wa, wb)
2078 w = min(wa, wb)
2045 if isonly(ta, tb):
2079 if isonly(ta, tb):
2046 return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2]))
2080 return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2]))
2047 if isonly(tb, ta):
2081 if isonly(tb, ta):
2048 return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2]))
2082 return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2]))
2049
2083
2050 if wa > wb:
2084 if wa > wb:
2051 return w, (op, tb, ta)
2085 return w, (op, tb, ta)
2052 return w, (op, ta, tb)
2086 return w, (op, ta, tb)
2053 elif op == 'or':
2087 elif op == 'or':
2054 wa, ta = optimize(x[1], False)
2088 wa, ta = optimize(x[1], False)
2055 wb, tb = optimize(x[2], False)
2089 wb, tb = optimize(x[2], False)
2056 if wb < wa:
2090 if wb < wa:
2057 wb, wa = wa, wb
2091 wb, wa = wa, wb
2058 return max(wa, wb), (op, ta, tb)
2092 return max(wa, wb), (op, ta, tb)
2059 elif op == 'not':
2093 elif op == 'not':
2060 o = optimize(x[1], not small)
2094 o = optimize(x[1], not small)
2061 return o[0], (op, o[1])
2095 return o[0], (op, o[1])
2062 elif op == 'parentpost':
2096 elif op == 'parentpost':
2063 o = optimize(x[1], small)
2097 o = optimize(x[1], small)
2064 return o[0], (op, o[1])
2098 return o[0], (op, o[1])
2065 elif op == 'group':
2099 elif op == 'group':
2066 return optimize(x[1], small)
2100 return optimize(x[1], small)
2067 elif op in 'dagrange range list parent ancestorspec':
2101 elif op in 'dagrange range list parent ancestorspec':
2068 if op == 'parent':
2102 if op == 'parent':
2069 # x^:y means (x^) : y, not x ^ (:y)
2103 # x^:y means (x^) : y, not x ^ (:y)
2070 post = ('parentpost', x[1])
2104 post = ('parentpost', x[1])
2071 if x[2][0] == 'dagrangepre':
2105 if x[2][0] == 'dagrangepre':
2072 return optimize(('dagrange', post, x[2][1]), small)
2106 return optimize(('dagrange', post, x[2][1]), small)
2073 elif x[2][0] == 'rangepre':
2107 elif x[2][0] == 'rangepre':
2074 return optimize(('range', post, x[2][1]), small)
2108 return optimize(('range', post, x[2][1]), small)
2075
2109
2076 wa, ta = optimize(x[1], small)
2110 wa, ta = optimize(x[1], small)
2077 wb, tb = optimize(x[2], small)
2111 wb, tb = optimize(x[2], small)
2078 return wa + wb, (op, ta, tb)
2112 return wa + wb, (op, ta, tb)
2079 elif op == 'func':
2113 elif op == 'func':
2080 f = getstring(x[1], _("not a symbol"))
2114 f = getstring(x[1], _("not a symbol"))
2081 wa, ta = optimize(x[2], small)
2115 wa, ta = optimize(x[2], small)
2082 if f in ("author branch closed date desc file grep keyword "
2116 if f in ("author branch closed date desc file grep keyword "
2083 "outgoing user"):
2117 "outgoing user"):
2084 w = 10 # slow
2118 w = 10 # slow
2085 elif f in "modifies adds removes":
2119 elif f in "modifies adds removes":
2086 w = 30 # slower
2120 w = 30 # slower
2087 elif f == "contains":
2121 elif f == "contains":
2088 w = 100 # very slow
2122 w = 100 # very slow
2089 elif f == "ancestor":
2123 elif f == "ancestor":
2090 w = 1 * smallbonus
2124 w = 1 * smallbonus
2091 elif f in "reverse limit first _intlist":
2125 elif f in "reverse limit first _intlist":
2092 w = 0
2126 w = 0
2093 elif f in "sort":
2127 elif f in "sort":
2094 w = 10 # assume most sorts look at changelog
2128 w = 10 # assume most sorts look at changelog
2095 else:
2129 else:
2096 w = 1
2130 w = 1
2097 return w + wa, (op, x[1], ta)
2131 return w + wa, (op, x[1], ta)
2098 return 1, x
2132 return 1, x
2099
2133
2100 _aliasarg = ('func', ('symbol', '_aliasarg'))
2134 _aliasarg = ('func', ('symbol', '_aliasarg'))
2101 def _getaliasarg(tree):
2135 def _getaliasarg(tree):
2102 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
2136 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
2103 return X, None otherwise.
2137 return X, None otherwise.
2104 """
2138 """
2105 if (len(tree) == 3 and tree[:2] == _aliasarg
2139 if (len(tree) == 3 and tree[:2] == _aliasarg
2106 and tree[2][0] == 'string'):
2140 and tree[2][0] == 'string'):
2107 return tree[2][1]
2141 return tree[2][1]
2108 return None
2142 return None
2109
2143
2110 def _checkaliasarg(tree, known=None):
2144 def _checkaliasarg(tree, known=None):
2111 """Check tree contains no _aliasarg construct or only ones which
2145 """Check tree contains no _aliasarg construct or only ones which
2112 value is in known. Used to avoid alias placeholders injection.
2146 value is in known. Used to avoid alias placeholders injection.
2113 """
2147 """
2114 if isinstance(tree, tuple):
2148 if isinstance(tree, tuple):
2115 arg = _getaliasarg(tree)
2149 arg = _getaliasarg(tree)
2116 if arg is not None and (not known or arg not in known):
2150 if arg is not None and (not known or arg not in known):
2117 raise error.ParseError(_("not a function: %s") % '_aliasarg')
2151 raise error.ParseError(_("not a function: %s") % '_aliasarg')
2118 for t in tree:
2152 for t in tree:
2119 _checkaliasarg(t, known)
2153 _checkaliasarg(t, known)
2120
2154
2155 # the set of valid characters for the initial letter of symbols in
2156 # alias declarations and definitions
2157 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2158 if c.isalnum() or c in '._@$' or ord(c) > 127)
2159
2160 def _tokenizealias(program, lookup=None):
2161 """Parse alias declaration/definition into a stream of tokens
2162
2163 This allows symbol names to use also ``$`` as an initial letter
2164 (for backward compatibility), and callers of this function should
2165 examine whether ``$`` is used also for unexpected symbols or not.
2166 """
2167 return tokenize(program, lookup=lookup,
2168 syminitletters=_aliassyminitletters)
2169
2170 def _parsealiasdecl(decl):
2171 """Parse alias declaration ``decl``
2172
2173 This returns ``(name, tree, args, errorstr)`` tuple:
2174
2175 - ``name``: of declared alias (may be ``decl`` itself at error)
2176 - ``tree``: parse result (or ``None`` at error)
2177 - ``args``: list of alias argument names (or None for symbol declaration)
2178 - ``errorstr``: detail about detected error (or None)
2179
2180 >>> _parsealiasdecl('foo')
2181 ('foo', ('symbol', 'foo'), None, None)
2182 >>> _parsealiasdecl('$foo')
2183 ('$foo', None, None, "'$' not for alias arguments")
2184 >>> _parsealiasdecl('foo::bar')
2185 ('foo::bar', None, None, 'invalid format')
2186 >>> _parsealiasdecl('foo bar')
2187 ('foo bar', None, None, 'at 4: invalid token')
2188 >>> _parsealiasdecl('foo()')
2189 ('foo', ('func', ('symbol', 'foo')), [], None)
2190 >>> _parsealiasdecl('$foo()')
2191 ('$foo()', None, None, "'$' not for alias arguments")
2192 >>> _parsealiasdecl('foo($1, $2)')
2193 ('foo', ('func', ('symbol', 'foo')), ['$1', '$2'], None)
2194 >>> _parsealiasdecl('foo(bar_bar, baz.baz)')
2195 ('foo', ('func', ('symbol', 'foo')), ['bar_bar', 'baz.baz'], None)
2196 >>> _parsealiasdecl('foo($1, $2, nested($1, $2))')
2197 ('foo($1, $2, nested($1, $2))', None, None, 'invalid argument list')
2198 >>> _parsealiasdecl('foo(bar($1, $2))')
2199 ('foo(bar($1, $2))', None, None, 'invalid argument list')
2200 >>> _parsealiasdecl('foo("string")')
2201 ('foo("string")', None, None, 'invalid argument list')
2202 >>> _parsealiasdecl('foo($1, $2')
2203 ('foo($1, $2', None, None, 'at 10: unexpected token: end')
2204 >>> _parsealiasdecl('foo("string')
2205 ('foo("string', None, None, 'at 5: unterminated string')
2206 """
2207 p = parser.parser(_tokenizealias, elements)
2208 try:
2209 tree, pos = p.parse(decl)
2210 if (pos != len(decl)):
2211 raise error.ParseError(_('invalid token'), pos)
2212
2213 if isvalidsymbol(tree):
2214 # "name = ...." style
2215 name = getsymbol(tree)
2216 if name.startswith('$'):
2217 return (decl, None, None, _("'$' not for alias arguments"))
2218 return (name, ('symbol', name), None, None)
2219
2220 if isvalidfunc(tree):
2221 # "name(arg, ....) = ...." style
2222 name = getfuncname(tree)
2223 if name.startswith('$'):
2224 return (decl, None, None, _("'$' not for alias arguments"))
2225 args = []
2226 for arg in getfuncargs(tree):
2227 if not isvalidsymbol(arg):
2228 return (decl, None, None, _("invalid argument list"))
2229 args.append(getsymbol(arg))
2230 return (name, ('func', ('symbol', name)), args, None)
2231
2232 return (decl, None, None, _("invalid format"))
2233 except error.ParseError, inst:
2234 return (decl, None, None, parseerrordetail(inst))
2235
2121 class revsetalias(object):
2236 class revsetalias(object):
2122 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
2237 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
2123 args = None
2238 args = None
2124
2239
2125 # error message at parsing, or None
2240 # error message at parsing, or None
2126 error = None
2241 error = None
2127 # whether own `error` information is already shown or not.
2242 # whether own `error` information is already shown or not.
2128 # this avoids showing same warning multiple times at each `findaliases`.
2243 # this avoids showing same warning multiple times at each `findaliases`.
2129 warned = False
2244 warned = False
2130
2245
2131 def __init__(self, name, value):
2246 def __init__(self, name, value):
2132 '''Aliases like:
2247 '''Aliases like:
2133
2248
2134 h = heads(default)
2249 h = heads(default)
2135 b($1) = ancestors($1) - ancestors(default)
2250 b($1) = ancestors($1) - ancestors(default)
2136 '''
2251 '''
2137 m = self.funcre.search(name)
2252 m = self.funcre.search(name)
2138 if m:
2253 if m:
2139 self.name = m.group(1)
2254 self.name = m.group(1)
2140 self.tree = ('func', ('symbol', m.group(1)))
2255 self.tree = ('func', ('symbol', m.group(1)))
2141 self.args = [x.strip() for x in m.group(2).split(',')]
2256 self.args = [x.strip() for x in m.group(2).split(',')]
2142 for arg in self.args:
2257 for arg in self.args:
2143 # _aliasarg() is an unknown symbol only used separate
2258 # _aliasarg() is an unknown symbol only used separate
2144 # alias argument placeholders from regular strings.
2259 # alias argument placeholders from regular strings.
2145 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
2260 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
2146 else:
2261 else:
2147 self.name = name
2262 self.name = name
2148 self.tree = ('symbol', name)
2263 self.tree = ('symbol', name)
2149
2264
2150 try:
2265 try:
2151 self.replacement, pos = parse(value)
2266 self.replacement, pos = parse(value)
2152 if pos != len(value):
2267 if pos != len(value):
2153 raise error.ParseError(_('invalid token'), pos)
2268 raise error.ParseError(_('invalid token'), pos)
2154 # Check for placeholder injection
2269 # Check for placeholder injection
2155 _checkaliasarg(self.replacement, self.args)
2270 _checkaliasarg(self.replacement, self.args)
2156 except error.ParseError, inst:
2271 except error.ParseError, inst:
2157 self.error = _('failed to parse the definition of revset alias'
2272 self.error = _('failed to parse the definition of revset alias'
2158 ' "%s": %s') % (self.name, parseerrordetail(inst))
2273 ' "%s": %s') % (self.name, parseerrordetail(inst))
2159
2274
2160 def _getalias(aliases, tree):
2275 def _getalias(aliases, tree):
2161 """If tree looks like an unexpanded alias, return it. Return None
2276 """If tree looks like an unexpanded alias, return it. Return None
2162 otherwise.
2277 otherwise.
2163 """
2278 """
2164 if isinstance(tree, tuple) and tree:
2279 if isinstance(tree, tuple) and tree:
2165 if tree[0] == 'symbol' and len(tree) == 2:
2280 if tree[0] == 'symbol' and len(tree) == 2:
2166 name = tree[1]
2281 name = tree[1]
2167 alias = aliases.get(name)
2282 alias = aliases.get(name)
2168 if alias and alias.args is None and alias.tree == tree:
2283 if alias and alias.args is None and alias.tree == tree:
2169 return alias
2284 return alias
2170 if tree[0] == 'func' and len(tree) > 1:
2285 if tree[0] == 'func' and len(tree) > 1:
2171 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
2286 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
2172 name = tree[1][1]
2287 name = tree[1][1]
2173 alias = aliases.get(name)
2288 alias = aliases.get(name)
2174 if alias and alias.args is not None and alias.tree == tree[:2]:
2289 if alias and alias.args is not None and alias.tree == tree[:2]:
2175 return alias
2290 return alias
2176 return None
2291 return None
2177
2292
2178 def _expandargs(tree, args):
2293 def _expandargs(tree, args):
2179 """Replace _aliasarg instances with the substitution value of the
2294 """Replace _aliasarg instances with the substitution value of the
2180 same name in args, recursively.
2295 same name in args, recursively.
2181 """
2296 """
2182 if not tree or not isinstance(tree, tuple):
2297 if not tree or not isinstance(tree, tuple):
2183 return tree
2298 return tree
2184 arg = _getaliasarg(tree)
2299 arg = _getaliasarg(tree)
2185 if arg is not None:
2300 if arg is not None:
2186 return args[arg]
2301 return args[arg]
2187 return tuple(_expandargs(t, args) for t in tree)
2302 return tuple(_expandargs(t, args) for t in tree)
2188
2303
2189 def _expandaliases(aliases, tree, expanding, cache):
2304 def _expandaliases(aliases, tree, expanding, cache):
2190 """Expand aliases in tree, recursively.
2305 """Expand aliases in tree, recursively.
2191
2306
2192 'aliases' is a dictionary mapping user defined aliases to
2307 'aliases' is a dictionary mapping user defined aliases to
2193 revsetalias objects.
2308 revsetalias objects.
2194 """
2309 """
2195 if not isinstance(tree, tuple):
2310 if not isinstance(tree, tuple):
2196 # Do not expand raw strings
2311 # Do not expand raw strings
2197 return tree
2312 return tree
2198 alias = _getalias(aliases, tree)
2313 alias = _getalias(aliases, tree)
2199 if alias is not None:
2314 if alias is not None:
2200 if alias.error:
2315 if alias.error:
2201 raise util.Abort(alias.error)
2316 raise util.Abort(alias.error)
2202 if alias in expanding:
2317 if alias in expanding:
2203 raise error.ParseError(_('infinite expansion of revset alias "%s" '
2318 raise error.ParseError(_('infinite expansion of revset alias "%s" '
2204 'detected') % alias.name)
2319 'detected') % alias.name)
2205 expanding.append(alias)
2320 expanding.append(alias)
2206 if alias.name not in cache:
2321 if alias.name not in cache:
2207 cache[alias.name] = _expandaliases(aliases, alias.replacement,
2322 cache[alias.name] = _expandaliases(aliases, alias.replacement,
2208 expanding, cache)
2323 expanding, cache)
2209 result = cache[alias.name]
2324 result = cache[alias.name]
2210 expanding.pop()
2325 expanding.pop()
2211 if alias.args is not None:
2326 if alias.args is not None:
2212 l = getlist(tree[2])
2327 l = getlist(tree[2])
2213 if len(l) != len(alias.args):
2328 if len(l) != len(alias.args):
2214 raise error.ParseError(
2329 raise error.ParseError(
2215 _('invalid number of arguments: %s') % len(l))
2330 _('invalid number of arguments: %s') % len(l))
2216 l = [_expandaliases(aliases, a, [], cache) for a in l]
2331 l = [_expandaliases(aliases, a, [], cache) for a in l]
2217 result = _expandargs(result, dict(zip(alias.args, l)))
2332 result = _expandargs(result, dict(zip(alias.args, l)))
2218 else:
2333 else:
2219 result = tuple(_expandaliases(aliases, t, expanding, cache)
2334 result = tuple(_expandaliases(aliases, t, expanding, cache)
2220 for t in tree)
2335 for t in tree)
2221 return result
2336 return result
2222
2337
2223 def findaliases(ui, tree, showwarning=None):
2338 def findaliases(ui, tree, showwarning=None):
2224 _checkaliasarg(tree)
2339 _checkaliasarg(tree)
2225 aliases = {}
2340 aliases = {}
2226 for k, v in ui.configitems('revsetalias'):
2341 for k, v in ui.configitems('revsetalias'):
2227 alias = revsetalias(k, v)
2342 alias = revsetalias(k, v)
2228 aliases[alias.name] = alias
2343 aliases[alias.name] = alias
2229 tree = _expandaliases(aliases, tree, [], {})
2344 tree = _expandaliases(aliases, tree, [], {})
2230 if showwarning:
2345 if showwarning:
2231 # warn about problematic (but not referred) aliases
2346 # warn about problematic (but not referred) aliases
2232 for name, alias in sorted(aliases.iteritems()):
2347 for name, alias in sorted(aliases.iteritems()):
2233 if alias.error and not alias.warned:
2348 if alias.error and not alias.warned:
2234 showwarning(_('warning: %s\n') % (alias.error))
2349 showwarning(_('warning: %s\n') % (alias.error))
2235 alias.warned = True
2350 alias.warned = True
2236 return tree
2351 return tree
2237
2352
2238 def foldconcat(tree):
2353 def foldconcat(tree):
2239 """Fold elements to be concatenated by `##`
2354 """Fold elements to be concatenated by `##`
2240 """
2355 """
2241 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2356 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2242 return tree
2357 return tree
2243 if tree[0] == '_concat':
2358 if tree[0] == '_concat':
2244 pending = [tree]
2359 pending = [tree]
2245 l = []
2360 l = []
2246 while pending:
2361 while pending:
2247 e = pending.pop()
2362 e = pending.pop()
2248 if e[0] == '_concat':
2363 if e[0] == '_concat':
2249 pending.extend(reversed(e[1:]))
2364 pending.extend(reversed(e[1:]))
2250 elif e[0] in ('string', 'symbol'):
2365 elif e[0] in ('string', 'symbol'):
2251 l.append(e[1])
2366 l.append(e[1])
2252 else:
2367 else:
2253 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2368 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2254 raise error.ParseError(msg)
2369 raise error.ParseError(msg)
2255 return ('string', ''.join(l))
2370 return ('string', ''.join(l))
2256 else:
2371 else:
2257 return tuple(foldconcat(t) for t in tree)
2372 return tuple(foldconcat(t) for t in tree)
2258
2373
2259 def parse(spec, lookup=None):
2374 def parse(spec, lookup=None):
2260 p = parser.parser(tokenize, elements)
2375 p = parser.parser(tokenize, elements)
2261 return p.parse(spec, lookup=lookup)
2376 return p.parse(spec, lookup=lookup)
2262
2377
2263 def match(ui, spec, repo=None):
2378 def match(ui, spec, repo=None):
2264 if not spec:
2379 if not spec:
2265 raise error.ParseError(_("empty query"))
2380 raise error.ParseError(_("empty query"))
2266 lookup = None
2381 lookup = None
2267 if repo:
2382 if repo:
2268 lookup = repo.__contains__
2383 lookup = repo.__contains__
2269 tree, pos = parse(spec, lookup)
2384 tree, pos = parse(spec, lookup)
2270 if (pos != len(spec)):
2385 if (pos != len(spec)):
2271 raise error.ParseError(_("invalid token"), pos)
2386 raise error.ParseError(_("invalid token"), pos)
2272 if ui:
2387 if ui:
2273 tree = findaliases(ui, tree, showwarning=ui.warn)
2388 tree = findaliases(ui, tree, showwarning=ui.warn)
2274 tree = foldconcat(tree)
2389 tree = foldconcat(tree)
2275 weight, tree = optimize(tree, True)
2390 weight, tree = optimize(tree, True)
2276 def mfunc(repo, subset):
2391 def mfunc(repo, subset):
2277 if util.safehasattr(subset, 'isascending'):
2392 if util.safehasattr(subset, 'isascending'):
2278 result = getset(repo, subset, tree)
2393 result = getset(repo, subset, tree)
2279 else:
2394 else:
2280 result = getset(repo, baseset(subset), tree)
2395 result = getset(repo, baseset(subset), tree)
2281 return result
2396 return result
2282 return mfunc
2397 return mfunc
2283
2398
2284 def formatspec(expr, *args):
2399 def formatspec(expr, *args):
2285 '''
2400 '''
2286 This is a convenience function for using revsets internally, and
2401 This is a convenience function for using revsets internally, and
2287 escapes arguments appropriately. Aliases are intentionally ignored
2402 escapes arguments appropriately. Aliases are intentionally ignored
2288 so that intended expression behavior isn't accidentally subverted.
2403 so that intended expression behavior isn't accidentally subverted.
2289
2404
2290 Supported arguments:
2405 Supported arguments:
2291
2406
2292 %r = revset expression, parenthesized
2407 %r = revset expression, parenthesized
2293 %d = int(arg), no quoting
2408 %d = int(arg), no quoting
2294 %s = string(arg), escaped and single-quoted
2409 %s = string(arg), escaped and single-quoted
2295 %b = arg.branch(), escaped and single-quoted
2410 %b = arg.branch(), escaped and single-quoted
2296 %n = hex(arg), single-quoted
2411 %n = hex(arg), single-quoted
2297 %% = a literal '%'
2412 %% = a literal '%'
2298
2413
2299 Prefixing the type with 'l' specifies a parenthesized list of that type.
2414 Prefixing the type with 'l' specifies a parenthesized list of that type.
2300
2415
2301 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2416 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2302 '(10 or 11):: and ((this()) or (that()))'
2417 '(10 or 11):: and ((this()) or (that()))'
2303 >>> formatspec('%d:: and not %d::', 10, 20)
2418 >>> formatspec('%d:: and not %d::', 10, 20)
2304 '10:: and not 20::'
2419 '10:: and not 20::'
2305 >>> formatspec('%ld or %ld', [], [1])
2420 >>> formatspec('%ld or %ld', [], [1])
2306 "_list('') or 1"
2421 "_list('') or 1"
2307 >>> formatspec('keyword(%s)', 'foo\\xe9')
2422 >>> formatspec('keyword(%s)', 'foo\\xe9')
2308 "keyword('foo\\\\xe9')"
2423 "keyword('foo\\\\xe9')"
2309 >>> b = lambda: 'default'
2424 >>> b = lambda: 'default'
2310 >>> b.branch = b
2425 >>> b.branch = b
2311 >>> formatspec('branch(%b)', b)
2426 >>> formatspec('branch(%b)', b)
2312 "branch('default')"
2427 "branch('default')"
2313 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2428 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2314 "root(_list('a\\x00b\\x00c\\x00d'))"
2429 "root(_list('a\\x00b\\x00c\\x00d'))"
2315 '''
2430 '''
2316
2431
2317 def quote(s):
2432 def quote(s):
2318 return repr(str(s))
2433 return repr(str(s))
2319
2434
2320 def argtype(c, arg):
2435 def argtype(c, arg):
2321 if c == 'd':
2436 if c == 'd':
2322 return str(int(arg))
2437 return str(int(arg))
2323 elif c == 's':
2438 elif c == 's':
2324 return quote(arg)
2439 return quote(arg)
2325 elif c == 'r':
2440 elif c == 'r':
2326 parse(arg) # make sure syntax errors are confined
2441 parse(arg) # make sure syntax errors are confined
2327 return '(%s)' % arg
2442 return '(%s)' % arg
2328 elif c == 'n':
2443 elif c == 'n':
2329 return quote(node.hex(arg))
2444 return quote(node.hex(arg))
2330 elif c == 'b':
2445 elif c == 'b':
2331 return quote(arg.branch())
2446 return quote(arg.branch())
2332
2447
2333 def listexp(s, t):
2448 def listexp(s, t):
2334 l = len(s)
2449 l = len(s)
2335 if l == 0:
2450 if l == 0:
2336 return "_list('')"
2451 return "_list('')"
2337 elif l == 1:
2452 elif l == 1:
2338 return argtype(t, s[0])
2453 return argtype(t, s[0])
2339 elif t == 'd':
2454 elif t == 'd':
2340 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2455 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2341 elif t == 's':
2456 elif t == 's':
2342 return "_list('%s')" % "\0".join(s)
2457 return "_list('%s')" % "\0".join(s)
2343 elif t == 'n':
2458 elif t == 'n':
2344 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2459 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2345 elif t == 'b':
2460 elif t == 'b':
2346 return "_list('%s')" % "\0".join(a.branch() for a in s)
2461 return "_list('%s')" % "\0".join(a.branch() for a in s)
2347
2462
2348 m = l // 2
2463 m = l // 2
2349 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2464 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2350
2465
2351 ret = ''
2466 ret = ''
2352 pos = 0
2467 pos = 0
2353 arg = 0
2468 arg = 0
2354 while pos < len(expr):
2469 while pos < len(expr):
2355 c = expr[pos]
2470 c = expr[pos]
2356 if c == '%':
2471 if c == '%':
2357 pos += 1
2472 pos += 1
2358 d = expr[pos]
2473 d = expr[pos]
2359 if d == '%':
2474 if d == '%':
2360 ret += d
2475 ret += d
2361 elif d in 'dsnbr':
2476 elif d in 'dsnbr':
2362 ret += argtype(d, args[arg])
2477 ret += argtype(d, args[arg])
2363 arg += 1
2478 arg += 1
2364 elif d == 'l':
2479 elif d == 'l':
2365 # a list of some type
2480 # a list of some type
2366 pos += 1
2481 pos += 1
2367 d = expr[pos]
2482 d = expr[pos]
2368 ret += listexp(list(args[arg]), d)
2483 ret += listexp(list(args[arg]), d)
2369 arg += 1
2484 arg += 1
2370 else:
2485 else:
2371 raise util.Abort('unexpected revspec format character %s' % d)
2486 raise util.Abort('unexpected revspec format character %s' % d)
2372 else:
2487 else:
2373 ret += c
2488 ret += c
2374 pos += 1
2489 pos += 1
2375
2490
2376 return ret
2491 return ret
2377
2492
2378 def prettyformat(tree):
2493 def prettyformat(tree):
2379 def _prettyformat(tree, level, lines):
2494 def _prettyformat(tree, level, lines):
2380 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2495 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2381 lines.append((level, str(tree)))
2496 lines.append((level, str(tree)))
2382 else:
2497 else:
2383 lines.append((level, '(%s' % tree[0]))
2498 lines.append((level, '(%s' % tree[0]))
2384 for s in tree[1:]:
2499 for s in tree[1:]:
2385 _prettyformat(s, level + 1, lines)
2500 _prettyformat(s, level + 1, lines)
2386 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2501 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2387
2502
2388 lines = []
2503 lines = []
2389 _prettyformat(tree, 0, lines)
2504 _prettyformat(tree, 0, lines)
2390 output = '\n'.join((' '*l + s) for l, s in lines)
2505 output = '\n'.join((' '*l + s) for l, s in lines)
2391 return output
2506 return output
2392
2507
2393 def depth(tree):
2508 def depth(tree):
2394 if isinstance(tree, tuple):
2509 if isinstance(tree, tuple):
2395 return max(map(depth, tree)) + 1
2510 return max(map(depth, tree)) + 1
2396 else:
2511 else:
2397 return 0
2512 return 0
2398
2513
2399 def funcsused(tree):
2514 def funcsused(tree):
2400 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2515 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2401 return set()
2516 return set()
2402 else:
2517 else:
2403 funcs = set()
2518 funcs = set()
2404 for s in tree[1:]:
2519 for s in tree[1:]:
2405 funcs |= funcsused(s)
2520 funcs |= funcsused(s)
2406 if tree[0] == 'func':
2521 if tree[0] == 'func':
2407 funcs.add(tree[1][1])
2522 funcs.add(tree[1][1])
2408 return funcs
2523 return funcs
2409
2524
2410 class abstractsmartset(object):
2525 class abstractsmartset(object):
2411
2526
2412 def __nonzero__(self):
2527 def __nonzero__(self):
2413 """True if the smartset is not empty"""
2528 """True if the smartset is not empty"""
2414 raise NotImplementedError()
2529 raise NotImplementedError()
2415
2530
2416 def __contains__(self, rev):
2531 def __contains__(self, rev):
2417 """provide fast membership testing"""
2532 """provide fast membership testing"""
2418 raise NotImplementedError()
2533 raise NotImplementedError()
2419
2534
2420 def __iter__(self):
2535 def __iter__(self):
2421 """iterate the set in the order it is supposed to be iterated"""
2536 """iterate the set in the order it is supposed to be iterated"""
2422 raise NotImplementedError()
2537 raise NotImplementedError()
2423
2538
2424 # Attributes containing a function to perform a fast iteration in a given
2539 # Attributes containing a function to perform a fast iteration in a given
2425 # direction. A smartset can have none, one, or both defined.
2540 # direction. A smartset can have none, one, or both defined.
2426 #
2541 #
2427 # Default value is None instead of a function returning None to avoid
2542 # Default value is None instead of a function returning None to avoid
2428 # initializing an iterator just for testing if a fast method exists.
2543 # initializing an iterator just for testing if a fast method exists.
2429 fastasc = None
2544 fastasc = None
2430 fastdesc = None
2545 fastdesc = None
2431
2546
2432 def isascending(self):
2547 def isascending(self):
2433 """True if the set will iterate in ascending order"""
2548 """True if the set will iterate in ascending order"""
2434 raise NotImplementedError()
2549 raise NotImplementedError()
2435
2550
2436 def isdescending(self):
2551 def isdescending(self):
2437 """True if the set will iterate in descending order"""
2552 """True if the set will iterate in descending order"""
2438 raise NotImplementedError()
2553 raise NotImplementedError()
2439
2554
2440 def min(self):
2555 def min(self):
2441 """return the minimum element in the set"""
2556 """return the minimum element in the set"""
2442 if self.fastasc is not None:
2557 if self.fastasc is not None:
2443 for r in self.fastasc():
2558 for r in self.fastasc():
2444 return r
2559 return r
2445 raise ValueError('arg is an empty sequence')
2560 raise ValueError('arg is an empty sequence')
2446 return min(self)
2561 return min(self)
2447
2562
2448 def max(self):
2563 def max(self):
2449 """return the maximum element in the set"""
2564 """return the maximum element in the set"""
2450 if self.fastdesc is not None:
2565 if self.fastdesc is not None:
2451 for r in self.fastdesc():
2566 for r in self.fastdesc():
2452 return r
2567 return r
2453 raise ValueError('arg is an empty sequence')
2568 raise ValueError('arg is an empty sequence')
2454 return max(self)
2569 return max(self)
2455
2570
2456 def first(self):
2571 def first(self):
2457 """return the first element in the set (user iteration perspective)
2572 """return the first element in the set (user iteration perspective)
2458
2573
2459 Return None if the set is empty"""
2574 Return None if the set is empty"""
2460 raise NotImplementedError()
2575 raise NotImplementedError()
2461
2576
2462 def last(self):
2577 def last(self):
2463 """return the last element in the set (user iteration perspective)
2578 """return the last element in the set (user iteration perspective)
2464
2579
2465 Return None if the set is empty"""
2580 Return None if the set is empty"""
2466 raise NotImplementedError()
2581 raise NotImplementedError()
2467
2582
2468 def __len__(self):
2583 def __len__(self):
2469 """return the length of the smartsets
2584 """return the length of the smartsets
2470
2585
2471 This can be expensive on smartset that could be lazy otherwise."""
2586 This can be expensive on smartset that could be lazy otherwise."""
2472 raise NotImplementedError()
2587 raise NotImplementedError()
2473
2588
2474 def reverse(self):
2589 def reverse(self):
2475 """reverse the expected iteration order"""
2590 """reverse the expected iteration order"""
2476 raise NotImplementedError()
2591 raise NotImplementedError()
2477
2592
2478 def sort(self, reverse=True):
2593 def sort(self, reverse=True):
2479 """get the set to iterate in an ascending or descending order"""
2594 """get the set to iterate in an ascending or descending order"""
2480 raise NotImplementedError()
2595 raise NotImplementedError()
2481
2596
2482 def __and__(self, other):
2597 def __and__(self, other):
2483 """Returns a new object with the intersection of the two collections.
2598 """Returns a new object with the intersection of the two collections.
2484
2599
2485 This is part of the mandatory API for smartset."""
2600 This is part of the mandatory API for smartset."""
2486 return self.filter(other.__contains__, cache=False)
2601 return self.filter(other.__contains__, cache=False)
2487
2602
2488 def __add__(self, other):
2603 def __add__(self, other):
2489 """Returns a new object with the union of the two collections.
2604 """Returns a new object with the union of the two collections.
2490
2605
2491 This is part of the mandatory API for smartset."""
2606 This is part of the mandatory API for smartset."""
2492 return addset(self, other)
2607 return addset(self, other)
2493
2608
2494 def __sub__(self, other):
2609 def __sub__(self, other):
2495 """Returns a new object with the substraction of the two collections.
2610 """Returns a new object with the substraction of the two collections.
2496
2611
2497 This is part of the mandatory API for smartset."""
2612 This is part of the mandatory API for smartset."""
2498 c = other.__contains__
2613 c = other.__contains__
2499 return self.filter(lambda r: not c(r), cache=False)
2614 return self.filter(lambda r: not c(r), cache=False)
2500
2615
2501 def filter(self, condition, cache=True):
2616 def filter(self, condition, cache=True):
2502 """Returns this smartset filtered by condition as a new smartset.
2617 """Returns this smartset filtered by condition as a new smartset.
2503
2618
2504 `condition` is a callable which takes a revision number and returns a
2619 `condition` is a callable which takes a revision number and returns a
2505 boolean.
2620 boolean.
2506
2621
2507 This is part of the mandatory API for smartset."""
2622 This is part of the mandatory API for smartset."""
2508 # builtin cannot be cached. but do not needs to
2623 # builtin cannot be cached. but do not needs to
2509 if cache and util.safehasattr(condition, 'func_code'):
2624 if cache and util.safehasattr(condition, 'func_code'):
2510 condition = util.cachefunc(condition)
2625 condition = util.cachefunc(condition)
2511 return filteredset(self, condition)
2626 return filteredset(self, condition)
2512
2627
2513 class baseset(abstractsmartset):
2628 class baseset(abstractsmartset):
2514 """Basic data structure that represents a revset and contains the basic
2629 """Basic data structure that represents a revset and contains the basic
2515 operation that it should be able to perform.
2630 operation that it should be able to perform.
2516
2631
2517 Every method in this class should be implemented by any smartset class.
2632 Every method in this class should be implemented by any smartset class.
2518 """
2633 """
2519 def __init__(self, data=()):
2634 def __init__(self, data=()):
2520 if not isinstance(data, list):
2635 if not isinstance(data, list):
2521 data = list(data)
2636 data = list(data)
2522 self._list = data
2637 self._list = data
2523 self._ascending = None
2638 self._ascending = None
2524
2639
2525 @util.propertycache
2640 @util.propertycache
2526 def _set(self):
2641 def _set(self):
2527 return set(self._list)
2642 return set(self._list)
2528
2643
2529 @util.propertycache
2644 @util.propertycache
2530 def _asclist(self):
2645 def _asclist(self):
2531 asclist = self._list[:]
2646 asclist = self._list[:]
2532 asclist.sort()
2647 asclist.sort()
2533 return asclist
2648 return asclist
2534
2649
2535 def __iter__(self):
2650 def __iter__(self):
2536 if self._ascending is None:
2651 if self._ascending is None:
2537 return iter(self._list)
2652 return iter(self._list)
2538 elif self._ascending:
2653 elif self._ascending:
2539 return iter(self._asclist)
2654 return iter(self._asclist)
2540 else:
2655 else:
2541 return reversed(self._asclist)
2656 return reversed(self._asclist)
2542
2657
2543 def fastasc(self):
2658 def fastasc(self):
2544 return iter(self._asclist)
2659 return iter(self._asclist)
2545
2660
2546 def fastdesc(self):
2661 def fastdesc(self):
2547 return reversed(self._asclist)
2662 return reversed(self._asclist)
2548
2663
2549 @util.propertycache
2664 @util.propertycache
2550 def __contains__(self):
2665 def __contains__(self):
2551 return self._set.__contains__
2666 return self._set.__contains__
2552
2667
2553 def __nonzero__(self):
2668 def __nonzero__(self):
2554 return bool(self._list)
2669 return bool(self._list)
2555
2670
2556 def sort(self, reverse=False):
2671 def sort(self, reverse=False):
2557 self._ascending = not bool(reverse)
2672 self._ascending = not bool(reverse)
2558
2673
2559 def reverse(self):
2674 def reverse(self):
2560 if self._ascending is None:
2675 if self._ascending is None:
2561 self._list.reverse()
2676 self._list.reverse()
2562 else:
2677 else:
2563 self._ascending = not self._ascending
2678 self._ascending = not self._ascending
2564
2679
2565 def __len__(self):
2680 def __len__(self):
2566 return len(self._list)
2681 return len(self._list)
2567
2682
2568 def isascending(self):
2683 def isascending(self):
2569 """Returns True if the collection is ascending order, False if not.
2684 """Returns True if the collection is ascending order, False if not.
2570
2685
2571 This is part of the mandatory API for smartset."""
2686 This is part of the mandatory API for smartset."""
2572 if len(self) <= 1:
2687 if len(self) <= 1:
2573 return True
2688 return True
2574 return self._ascending is not None and self._ascending
2689 return self._ascending is not None and self._ascending
2575
2690
2576 def isdescending(self):
2691 def isdescending(self):
2577 """Returns True if the collection is descending order, False if not.
2692 """Returns True if the collection is descending order, False if not.
2578
2693
2579 This is part of the mandatory API for smartset."""
2694 This is part of the mandatory API for smartset."""
2580 if len(self) <= 1:
2695 if len(self) <= 1:
2581 return True
2696 return True
2582 return self._ascending is not None and not self._ascending
2697 return self._ascending is not None and not self._ascending
2583
2698
2584 def first(self):
2699 def first(self):
2585 if self:
2700 if self:
2586 if self._ascending is None:
2701 if self._ascending is None:
2587 return self._list[0]
2702 return self._list[0]
2588 elif self._ascending:
2703 elif self._ascending:
2589 return self._asclist[0]
2704 return self._asclist[0]
2590 else:
2705 else:
2591 return self._asclist[-1]
2706 return self._asclist[-1]
2592 return None
2707 return None
2593
2708
2594 def last(self):
2709 def last(self):
2595 if self:
2710 if self:
2596 if self._ascending is None:
2711 if self._ascending is None:
2597 return self._list[-1]
2712 return self._list[-1]
2598 elif self._ascending:
2713 elif self._ascending:
2599 return self._asclist[-1]
2714 return self._asclist[-1]
2600 else:
2715 else:
2601 return self._asclist[0]
2716 return self._asclist[0]
2602 return None
2717 return None
2603
2718
2604 class filteredset(abstractsmartset):
2719 class filteredset(abstractsmartset):
2605 """Duck type for baseset class which iterates lazily over the revisions in
2720 """Duck type for baseset class which iterates lazily over the revisions in
2606 the subset and contains a function which tests for membership in the
2721 the subset and contains a function which tests for membership in the
2607 revset
2722 revset
2608 """
2723 """
2609 def __init__(self, subset, condition=lambda x: True):
2724 def __init__(self, subset, condition=lambda x: True):
2610 """
2725 """
2611 condition: a function that decide whether a revision in the subset
2726 condition: a function that decide whether a revision in the subset
2612 belongs to the revset or not.
2727 belongs to the revset or not.
2613 """
2728 """
2614 self._subset = subset
2729 self._subset = subset
2615 self._condition = condition
2730 self._condition = condition
2616 self._cache = {}
2731 self._cache = {}
2617
2732
2618 def __contains__(self, x):
2733 def __contains__(self, x):
2619 c = self._cache
2734 c = self._cache
2620 if x not in c:
2735 if x not in c:
2621 v = c[x] = x in self._subset and self._condition(x)
2736 v = c[x] = x in self._subset and self._condition(x)
2622 return v
2737 return v
2623 return c[x]
2738 return c[x]
2624
2739
2625 def __iter__(self):
2740 def __iter__(self):
2626 return self._iterfilter(self._subset)
2741 return self._iterfilter(self._subset)
2627
2742
2628 def _iterfilter(self, it):
2743 def _iterfilter(self, it):
2629 cond = self._condition
2744 cond = self._condition
2630 for x in it:
2745 for x in it:
2631 if cond(x):
2746 if cond(x):
2632 yield x
2747 yield x
2633
2748
2634 @property
2749 @property
2635 def fastasc(self):
2750 def fastasc(self):
2636 it = self._subset.fastasc
2751 it = self._subset.fastasc
2637 if it is None:
2752 if it is None:
2638 return None
2753 return None
2639 return lambda: self._iterfilter(it())
2754 return lambda: self._iterfilter(it())
2640
2755
2641 @property
2756 @property
2642 def fastdesc(self):
2757 def fastdesc(self):
2643 it = self._subset.fastdesc
2758 it = self._subset.fastdesc
2644 if it is None:
2759 if it is None:
2645 return None
2760 return None
2646 return lambda: self._iterfilter(it())
2761 return lambda: self._iterfilter(it())
2647
2762
2648 def __nonzero__(self):
2763 def __nonzero__(self):
2649 for r in self:
2764 for r in self:
2650 return True
2765 return True
2651 return False
2766 return False
2652
2767
2653 def __len__(self):
2768 def __len__(self):
2654 # Basic implementation to be changed in future patches.
2769 # Basic implementation to be changed in future patches.
2655 l = baseset([r for r in self])
2770 l = baseset([r for r in self])
2656 return len(l)
2771 return len(l)
2657
2772
2658 def sort(self, reverse=False):
2773 def sort(self, reverse=False):
2659 self._subset.sort(reverse=reverse)
2774 self._subset.sort(reverse=reverse)
2660
2775
2661 def reverse(self):
2776 def reverse(self):
2662 self._subset.reverse()
2777 self._subset.reverse()
2663
2778
2664 def isascending(self):
2779 def isascending(self):
2665 return self._subset.isascending()
2780 return self._subset.isascending()
2666
2781
2667 def isdescending(self):
2782 def isdescending(self):
2668 return self._subset.isdescending()
2783 return self._subset.isdescending()
2669
2784
2670 def first(self):
2785 def first(self):
2671 for x in self:
2786 for x in self:
2672 return x
2787 return x
2673 return None
2788 return None
2674
2789
2675 def last(self):
2790 def last(self):
2676 it = None
2791 it = None
2677 if self._subset.isascending:
2792 if self._subset.isascending:
2678 it = self.fastdesc
2793 it = self.fastdesc
2679 elif self._subset.isdescending:
2794 elif self._subset.isdescending:
2680 it = self.fastdesc
2795 it = self.fastdesc
2681 if it is None:
2796 if it is None:
2682 # slowly consume everything. This needs improvement
2797 # slowly consume everything. This needs improvement
2683 it = lambda: reversed(list(self))
2798 it = lambda: reversed(list(self))
2684 for x in it():
2799 for x in it():
2685 return x
2800 return x
2686 return None
2801 return None
2687
2802
2688 class addset(abstractsmartset):
2803 class addset(abstractsmartset):
2689 """Represent the addition of two sets
2804 """Represent the addition of two sets
2690
2805
2691 Wrapper structure for lazily adding two structures without losing much
2806 Wrapper structure for lazily adding two structures without losing much
2692 performance on the __contains__ method
2807 performance on the __contains__ method
2693
2808
2694 If the ascending attribute is set, that means the two structures are
2809 If the ascending attribute is set, that means the two structures are
2695 ordered in either an ascending or descending way. Therefore, we can add
2810 ordered in either an ascending or descending way. Therefore, we can add
2696 them maintaining the order by iterating over both at the same time
2811 them maintaining the order by iterating over both at the same time
2697 """
2812 """
2698 def __init__(self, revs1, revs2, ascending=None):
2813 def __init__(self, revs1, revs2, ascending=None):
2699 self._r1 = revs1
2814 self._r1 = revs1
2700 self._r2 = revs2
2815 self._r2 = revs2
2701 self._iter = None
2816 self._iter = None
2702 self._ascending = ascending
2817 self._ascending = ascending
2703 self._genlist = None
2818 self._genlist = None
2704 self._asclist = None
2819 self._asclist = None
2705
2820
2706 def __len__(self):
2821 def __len__(self):
2707 return len(self._list)
2822 return len(self._list)
2708
2823
2709 def __nonzero__(self):
2824 def __nonzero__(self):
2710 return bool(self._r1) or bool(self._r2)
2825 return bool(self._r1) or bool(self._r2)
2711
2826
2712 @util.propertycache
2827 @util.propertycache
2713 def _list(self):
2828 def _list(self):
2714 if not self._genlist:
2829 if not self._genlist:
2715 self._genlist = baseset(self._iterator())
2830 self._genlist = baseset(self._iterator())
2716 return self._genlist
2831 return self._genlist
2717
2832
2718 def _iterator(self):
2833 def _iterator(self):
2719 """Iterate over both collections without repeating elements
2834 """Iterate over both collections without repeating elements
2720
2835
2721 If the ascending attribute is not set, iterate over the first one and
2836 If the ascending attribute is not set, iterate over the first one and
2722 then over the second one checking for membership on the first one so we
2837 then over the second one checking for membership on the first one so we
2723 dont yield any duplicates.
2838 dont yield any duplicates.
2724
2839
2725 If the ascending attribute is set, iterate over both collections at the
2840 If the ascending attribute is set, iterate over both collections at the
2726 same time, yielding only one value at a time in the given order.
2841 same time, yielding only one value at a time in the given order.
2727 """
2842 """
2728 if self._ascending is None:
2843 if self._ascending is None:
2729 def gen():
2844 def gen():
2730 for r in self._r1:
2845 for r in self._r1:
2731 yield r
2846 yield r
2732 inr1 = self._r1.__contains__
2847 inr1 = self._r1.__contains__
2733 for r in self._r2:
2848 for r in self._r2:
2734 if not inr1(r):
2849 if not inr1(r):
2735 yield r
2850 yield r
2736 gen = gen()
2851 gen = gen()
2737 else:
2852 else:
2738 iter1 = iter(self._r1)
2853 iter1 = iter(self._r1)
2739 iter2 = iter(self._r2)
2854 iter2 = iter(self._r2)
2740 gen = self._iterordered(self._ascending, iter1, iter2)
2855 gen = self._iterordered(self._ascending, iter1, iter2)
2741 return gen
2856 return gen
2742
2857
2743 def __iter__(self):
2858 def __iter__(self):
2744 if self._ascending is None:
2859 if self._ascending is None:
2745 if self._genlist:
2860 if self._genlist:
2746 return iter(self._genlist)
2861 return iter(self._genlist)
2747 return iter(self._iterator())
2862 return iter(self._iterator())
2748 self._trysetasclist()
2863 self._trysetasclist()
2749 if self._ascending:
2864 if self._ascending:
2750 it = self.fastasc
2865 it = self.fastasc
2751 else:
2866 else:
2752 it = self.fastdesc
2867 it = self.fastdesc
2753 if it is None:
2868 if it is None:
2754 # consume the gen and try again
2869 # consume the gen and try again
2755 self._list
2870 self._list
2756 return iter(self)
2871 return iter(self)
2757 return it()
2872 return it()
2758
2873
2759 def _trysetasclist(self):
2874 def _trysetasclist(self):
2760 """populate the _asclist attribute if possible and necessary"""
2875 """populate the _asclist attribute if possible and necessary"""
2761 if self._genlist is not None and self._asclist is None:
2876 if self._genlist is not None and self._asclist is None:
2762 self._asclist = sorted(self._genlist)
2877 self._asclist = sorted(self._genlist)
2763
2878
2764 @property
2879 @property
2765 def fastasc(self):
2880 def fastasc(self):
2766 self._trysetasclist()
2881 self._trysetasclist()
2767 if self._asclist is not None:
2882 if self._asclist is not None:
2768 return self._asclist.__iter__
2883 return self._asclist.__iter__
2769 iter1 = self._r1.fastasc
2884 iter1 = self._r1.fastasc
2770 iter2 = self._r2.fastasc
2885 iter2 = self._r2.fastasc
2771 if None in (iter1, iter2):
2886 if None in (iter1, iter2):
2772 return None
2887 return None
2773 return lambda: self._iterordered(True, iter1(), iter2())
2888 return lambda: self._iterordered(True, iter1(), iter2())
2774
2889
2775 @property
2890 @property
2776 def fastdesc(self):
2891 def fastdesc(self):
2777 self._trysetasclist()
2892 self._trysetasclist()
2778 if self._asclist is not None:
2893 if self._asclist is not None:
2779 return self._asclist.__reversed__
2894 return self._asclist.__reversed__
2780 iter1 = self._r1.fastdesc
2895 iter1 = self._r1.fastdesc
2781 iter2 = self._r2.fastdesc
2896 iter2 = self._r2.fastdesc
2782 if None in (iter1, iter2):
2897 if None in (iter1, iter2):
2783 return None
2898 return None
2784 return lambda: self._iterordered(False, iter1(), iter2())
2899 return lambda: self._iterordered(False, iter1(), iter2())
2785
2900
2786 def _iterordered(self, ascending, iter1, iter2):
2901 def _iterordered(self, ascending, iter1, iter2):
2787 """produce an ordered iteration from two iterators with the same order
2902 """produce an ordered iteration from two iterators with the same order
2788
2903
2789 The ascending is used to indicated the iteration direction.
2904 The ascending is used to indicated the iteration direction.
2790 """
2905 """
2791 choice = max
2906 choice = max
2792 if ascending:
2907 if ascending:
2793 choice = min
2908 choice = min
2794
2909
2795 val1 = None
2910 val1 = None
2796 val2 = None
2911 val2 = None
2797
2912
2798 choice = max
2913 choice = max
2799 if ascending:
2914 if ascending:
2800 choice = min
2915 choice = min
2801 try:
2916 try:
2802 # Consume both iterators in an ordered way until one is
2917 # Consume both iterators in an ordered way until one is
2803 # empty
2918 # empty
2804 while True:
2919 while True:
2805 if val1 is None:
2920 if val1 is None:
2806 val1 = iter1.next()
2921 val1 = iter1.next()
2807 if val2 is None:
2922 if val2 is None:
2808 val2 = iter2.next()
2923 val2 = iter2.next()
2809 next = choice(val1, val2)
2924 next = choice(val1, val2)
2810 yield next
2925 yield next
2811 if val1 == next:
2926 if val1 == next:
2812 val1 = None
2927 val1 = None
2813 if val2 == next:
2928 if val2 == next:
2814 val2 = None
2929 val2 = None
2815 except StopIteration:
2930 except StopIteration:
2816 # Flush any remaining values and consume the other one
2931 # Flush any remaining values and consume the other one
2817 it = iter2
2932 it = iter2
2818 if val1 is not None:
2933 if val1 is not None:
2819 yield val1
2934 yield val1
2820 it = iter1
2935 it = iter1
2821 elif val2 is not None:
2936 elif val2 is not None:
2822 # might have been equality and both are empty
2937 # might have been equality and both are empty
2823 yield val2
2938 yield val2
2824 for val in it:
2939 for val in it:
2825 yield val
2940 yield val
2826
2941
2827 def __contains__(self, x):
2942 def __contains__(self, x):
2828 return x in self._r1 or x in self._r2
2943 return x in self._r1 or x in self._r2
2829
2944
2830 def sort(self, reverse=False):
2945 def sort(self, reverse=False):
2831 """Sort the added set
2946 """Sort the added set
2832
2947
2833 For this we use the cached list with all the generated values and if we
2948 For this we use the cached list with all the generated values and if we
2834 know they are ascending or descending we can sort them in a smart way.
2949 know they are ascending or descending we can sort them in a smart way.
2835 """
2950 """
2836 self._ascending = not reverse
2951 self._ascending = not reverse
2837
2952
2838 def isascending(self):
2953 def isascending(self):
2839 return self._ascending is not None and self._ascending
2954 return self._ascending is not None and self._ascending
2840
2955
2841 def isdescending(self):
2956 def isdescending(self):
2842 return self._ascending is not None and not self._ascending
2957 return self._ascending is not None and not self._ascending
2843
2958
2844 def reverse(self):
2959 def reverse(self):
2845 if self._ascending is None:
2960 if self._ascending is None:
2846 self._list.reverse()
2961 self._list.reverse()
2847 else:
2962 else:
2848 self._ascending = not self._ascending
2963 self._ascending = not self._ascending
2849
2964
2850 def first(self):
2965 def first(self):
2851 for x in self:
2966 for x in self:
2852 return x
2967 return x
2853 return None
2968 return None
2854
2969
2855 def last(self):
2970 def last(self):
2856 self.reverse()
2971 self.reverse()
2857 val = self.first()
2972 val = self.first()
2858 self.reverse()
2973 self.reverse()
2859 return val
2974 return val
2860
2975
2861 class generatorset(abstractsmartset):
2976 class generatorset(abstractsmartset):
2862 """Wrap a generator for lazy iteration
2977 """Wrap a generator for lazy iteration
2863
2978
2864 Wrapper structure for generators that provides lazy membership and can
2979 Wrapper structure for generators that provides lazy membership and can
2865 be iterated more than once.
2980 be iterated more than once.
2866 When asked for membership it generates values until either it finds the
2981 When asked for membership it generates values until either it finds the
2867 requested one or has gone through all the elements in the generator
2982 requested one or has gone through all the elements in the generator
2868 """
2983 """
2869 def __init__(self, gen, iterasc=None):
2984 def __init__(self, gen, iterasc=None):
2870 """
2985 """
2871 gen: a generator producing the values for the generatorset.
2986 gen: a generator producing the values for the generatorset.
2872 """
2987 """
2873 self._gen = gen
2988 self._gen = gen
2874 self._asclist = None
2989 self._asclist = None
2875 self._cache = {}
2990 self._cache = {}
2876 self._genlist = []
2991 self._genlist = []
2877 self._finished = False
2992 self._finished = False
2878 self._ascending = True
2993 self._ascending = True
2879 if iterasc is not None:
2994 if iterasc is not None:
2880 if iterasc:
2995 if iterasc:
2881 self.fastasc = self._iterator
2996 self.fastasc = self._iterator
2882 self.__contains__ = self._asccontains
2997 self.__contains__ = self._asccontains
2883 else:
2998 else:
2884 self.fastdesc = self._iterator
2999 self.fastdesc = self._iterator
2885 self.__contains__ = self._desccontains
3000 self.__contains__ = self._desccontains
2886
3001
2887 def __nonzero__(self):
3002 def __nonzero__(self):
2888 for r in self:
3003 for r in self:
2889 return True
3004 return True
2890 return False
3005 return False
2891
3006
2892 def __contains__(self, x):
3007 def __contains__(self, x):
2893 if x in self._cache:
3008 if x in self._cache:
2894 return self._cache[x]
3009 return self._cache[x]
2895
3010
2896 # Use new values only, as existing values would be cached.
3011 # Use new values only, as existing values would be cached.
2897 for l in self._consumegen():
3012 for l in self._consumegen():
2898 if l == x:
3013 if l == x:
2899 return True
3014 return True
2900
3015
2901 self._cache[x] = False
3016 self._cache[x] = False
2902 return False
3017 return False
2903
3018
2904 def _asccontains(self, x):
3019 def _asccontains(self, x):
2905 """version of contains optimised for ascending generator"""
3020 """version of contains optimised for ascending generator"""
2906 if x in self._cache:
3021 if x in self._cache:
2907 return self._cache[x]
3022 return self._cache[x]
2908
3023
2909 # Use new values only, as existing values would be cached.
3024 # Use new values only, as existing values would be cached.
2910 for l in self._consumegen():
3025 for l in self._consumegen():
2911 if l == x:
3026 if l == x:
2912 return True
3027 return True
2913 if l > x:
3028 if l > x:
2914 break
3029 break
2915
3030
2916 self._cache[x] = False
3031 self._cache[x] = False
2917 return False
3032 return False
2918
3033
2919 def _desccontains(self, x):
3034 def _desccontains(self, x):
2920 """version of contains optimised for descending generator"""
3035 """version of contains optimised for descending generator"""
2921 if x in self._cache:
3036 if x in self._cache:
2922 return self._cache[x]
3037 return self._cache[x]
2923
3038
2924 # Use new values only, as existing values would be cached.
3039 # Use new values only, as existing values would be cached.
2925 for l in self._consumegen():
3040 for l in self._consumegen():
2926 if l == x:
3041 if l == x:
2927 return True
3042 return True
2928 if l < x:
3043 if l < x:
2929 break
3044 break
2930
3045
2931 self._cache[x] = False
3046 self._cache[x] = False
2932 return False
3047 return False
2933
3048
2934 def __iter__(self):
3049 def __iter__(self):
2935 if self._ascending:
3050 if self._ascending:
2936 it = self.fastasc
3051 it = self.fastasc
2937 else:
3052 else:
2938 it = self.fastdesc
3053 it = self.fastdesc
2939 if it is not None:
3054 if it is not None:
2940 return it()
3055 return it()
2941 # we need to consume the iterator
3056 # we need to consume the iterator
2942 for x in self._consumegen():
3057 for x in self._consumegen():
2943 pass
3058 pass
2944 # recall the same code
3059 # recall the same code
2945 return iter(self)
3060 return iter(self)
2946
3061
2947 def _iterator(self):
3062 def _iterator(self):
2948 if self._finished:
3063 if self._finished:
2949 return iter(self._genlist)
3064 return iter(self._genlist)
2950
3065
2951 # We have to use this complex iteration strategy to allow multiple
3066 # We have to use this complex iteration strategy to allow multiple
2952 # iterations at the same time. We need to be able to catch revision
3067 # iterations at the same time. We need to be able to catch revision
2953 # removed from _consumegen and added to genlist in another instance.
3068 # removed from _consumegen and added to genlist in another instance.
2954 #
3069 #
2955 # Getting rid of it would provide an about 15% speed up on this
3070 # Getting rid of it would provide an about 15% speed up on this
2956 # iteration.
3071 # iteration.
2957 genlist = self._genlist
3072 genlist = self._genlist
2958 nextrev = self._consumegen().next
3073 nextrev = self._consumegen().next
2959 _len = len # cache global lookup
3074 _len = len # cache global lookup
2960 def gen():
3075 def gen():
2961 i = 0
3076 i = 0
2962 while True:
3077 while True:
2963 if i < _len(genlist):
3078 if i < _len(genlist):
2964 yield genlist[i]
3079 yield genlist[i]
2965 else:
3080 else:
2966 yield nextrev()
3081 yield nextrev()
2967 i += 1
3082 i += 1
2968 return gen()
3083 return gen()
2969
3084
2970 def _consumegen(self):
3085 def _consumegen(self):
2971 cache = self._cache
3086 cache = self._cache
2972 genlist = self._genlist.append
3087 genlist = self._genlist.append
2973 for item in self._gen:
3088 for item in self._gen:
2974 cache[item] = True
3089 cache[item] = True
2975 genlist(item)
3090 genlist(item)
2976 yield item
3091 yield item
2977 if not self._finished:
3092 if not self._finished:
2978 self._finished = True
3093 self._finished = True
2979 asc = self._genlist[:]
3094 asc = self._genlist[:]
2980 asc.sort()
3095 asc.sort()
2981 self._asclist = asc
3096 self._asclist = asc
2982 self.fastasc = asc.__iter__
3097 self.fastasc = asc.__iter__
2983 self.fastdesc = asc.__reversed__
3098 self.fastdesc = asc.__reversed__
2984
3099
2985 def __len__(self):
3100 def __len__(self):
2986 for x in self._consumegen():
3101 for x in self._consumegen():
2987 pass
3102 pass
2988 return len(self._genlist)
3103 return len(self._genlist)
2989
3104
2990 def sort(self, reverse=False):
3105 def sort(self, reverse=False):
2991 self._ascending = not reverse
3106 self._ascending = not reverse
2992
3107
2993 def reverse(self):
3108 def reverse(self):
2994 self._ascending = not self._ascending
3109 self._ascending = not self._ascending
2995
3110
2996 def isascending(self):
3111 def isascending(self):
2997 return self._ascending
3112 return self._ascending
2998
3113
2999 def isdescending(self):
3114 def isdescending(self):
3000 return not self._ascending
3115 return not self._ascending
3001
3116
3002 def first(self):
3117 def first(self):
3003 if self._ascending:
3118 if self._ascending:
3004 it = self.fastasc
3119 it = self.fastasc
3005 else:
3120 else:
3006 it = self.fastdesc
3121 it = self.fastdesc
3007 if it is None:
3122 if it is None:
3008 # we need to consume all and try again
3123 # we need to consume all and try again
3009 for x in self._consumegen():
3124 for x in self._consumegen():
3010 pass
3125 pass
3011 return self.first()
3126 return self.first()
3012 if self:
3127 if self:
3013 return it().next()
3128 return it().next()
3014 return None
3129 return None
3015
3130
3016 def last(self):
3131 def last(self):
3017 if self._ascending:
3132 if self._ascending:
3018 it = self.fastdesc
3133 it = self.fastdesc
3019 else:
3134 else:
3020 it = self.fastasc
3135 it = self.fastasc
3021 if it is None:
3136 if it is None:
3022 # we need to consume all and try again
3137 # we need to consume all and try again
3023 for x in self._consumegen():
3138 for x in self._consumegen():
3024 pass
3139 pass
3025 return self.first()
3140 return self.first()
3026 if self:
3141 if self:
3027 return it().next()
3142 return it().next()
3028 return None
3143 return None
3029
3144
3030 def spanset(repo, start=None, end=None):
3145 def spanset(repo, start=None, end=None):
3031 """factory function to dispatch between fullreposet and actual spanset
3146 """factory function to dispatch between fullreposet and actual spanset
3032
3147
3033 Feel free to update all spanset call sites and kill this function at some
3148 Feel free to update all spanset call sites and kill this function at some
3034 point.
3149 point.
3035 """
3150 """
3036 if start is None and end is None:
3151 if start is None and end is None:
3037 return fullreposet(repo)
3152 return fullreposet(repo)
3038 return _spanset(repo, start, end)
3153 return _spanset(repo, start, end)
3039
3154
3040
3155
3041 class _spanset(abstractsmartset):
3156 class _spanset(abstractsmartset):
3042 """Duck type for baseset class which represents a range of revisions and
3157 """Duck type for baseset class which represents a range of revisions and
3043 can work lazily and without having all the range in memory
3158 can work lazily and without having all the range in memory
3044
3159
3045 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3160 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3046 notable points:
3161 notable points:
3047 - when x < y it will be automatically descending,
3162 - when x < y it will be automatically descending,
3048 - revision filtered with this repoview will be skipped.
3163 - revision filtered with this repoview will be skipped.
3049
3164
3050 """
3165 """
3051 def __init__(self, repo, start=0, end=None):
3166 def __init__(self, repo, start=0, end=None):
3052 """
3167 """
3053 start: first revision included the set
3168 start: first revision included the set
3054 (default to 0)
3169 (default to 0)
3055 end: first revision excluded (last+1)
3170 end: first revision excluded (last+1)
3056 (default to len(repo)
3171 (default to len(repo)
3057
3172
3058 Spanset will be descending if `end` < `start`.
3173 Spanset will be descending if `end` < `start`.
3059 """
3174 """
3060 if end is None:
3175 if end is None:
3061 end = len(repo)
3176 end = len(repo)
3062 self._ascending = start <= end
3177 self._ascending = start <= end
3063 if not self._ascending:
3178 if not self._ascending:
3064 start, end = end + 1, start +1
3179 start, end = end + 1, start +1
3065 self._start = start
3180 self._start = start
3066 self._end = end
3181 self._end = end
3067 self._hiddenrevs = repo.changelog.filteredrevs
3182 self._hiddenrevs = repo.changelog.filteredrevs
3068
3183
3069 def sort(self, reverse=False):
3184 def sort(self, reverse=False):
3070 self._ascending = not reverse
3185 self._ascending = not reverse
3071
3186
3072 def reverse(self):
3187 def reverse(self):
3073 self._ascending = not self._ascending
3188 self._ascending = not self._ascending
3074
3189
3075 def _iterfilter(self, iterrange):
3190 def _iterfilter(self, iterrange):
3076 s = self._hiddenrevs
3191 s = self._hiddenrevs
3077 for r in iterrange:
3192 for r in iterrange:
3078 if r not in s:
3193 if r not in s:
3079 yield r
3194 yield r
3080
3195
3081 def __iter__(self):
3196 def __iter__(self):
3082 if self._ascending:
3197 if self._ascending:
3083 return self.fastasc()
3198 return self.fastasc()
3084 else:
3199 else:
3085 return self.fastdesc()
3200 return self.fastdesc()
3086
3201
3087 def fastasc(self):
3202 def fastasc(self):
3088 iterrange = xrange(self._start, self._end)
3203 iterrange = xrange(self._start, self._end)
3089 if self._hiddenrevs:
3204 if self._hiddenrevs:
3090 return self._iterfilter(iterrange)
3205 return self._iterfilter(iterrange)
3091 return iter(iterrange)
3206 return iter(iterrange)
3092
3207
3093 def fastdesc(self):
3208 def fastdesc(self):
3094 iterrange = xrange(self._end - 1, self._start - 1, -1)
3209 iterrange = xrange(self._end - 1, self._start - 1, -1)
3095 if self._hiddenrevs:
3210 if self._hiddenrevs:
3096 return self._iterfilter(iterrange)
3211 return self._iterfilter(iterrange)
3097 return iter(iterrange)
3212 return iter(iterrange)
3098
3213
3099 def __contains__(self, rev):
3214 def __contains__(self, rev):
3100 hidden = self._hiddenrevs
3215 hidden = self._hiddenrevs
3101 return ((self._start <= rev < self._end)
3216 return ((self._start <= rev < self._end)
3102 and not (hidden and rev in hidden))
3217 and not (hidden and rev in hidden))
3103
3218
3104 def __nonzero__(self):
3219 def __nonzero__(self):
3105 for r in self:
3220 for r in self:
3106 return True
3221 return True
3107 return False
3222 return False
3108
3223
3109 def __len__(self):
3224 def __len__(self):
3110 if not self._hiddenrevs:
3225 if not self._hiddenrevs:
3111 return abs(self._end - self._start)
3226 return abs(self._end - self._start)
3112 else:
3227 else:
3113 count = 0
3228 count = 0
3114 start = self._start
3229 start = self._start
3115 end = self._end
3230 end = self._end
3116 for rev in self._hiddenrevs:
3231 for rev in self._hiddenrevs:
3117 if (end < rev <= start) or (start <= rev < end):
3232 if (end < rev <= start) or (start <= rev < end):
3118 count += 1
3233 count += 1
3119 return abs(self._end - self._start) - count
3234 return abs(self._end - self._start) - count
3120
3235
3121 def isascending(self):
3236 def isascending(self):
3122 return self._ascending
3237 return self._ascending
3123
3238
3124 def isdescending(self):
3239 def isdescending(self):
3125 return not self._ascending
3240 return not self._ascending
3126
3241
3127 def first(self):
3242 def first(self):
3128 if self._ascending:
3243 if self._ascending:
3129 it = self.fastasc
3244 it = self.fastasc
3130 else:
3245 else:
3131 it = self.fastdesc
3246 it = self.fastdesc
3132 for x in it():
3247 for x in it():
3133 return x
3248 return x
3134 return None
3249 return None
3135
3250
3136 def last(self):
3251 def last(self):
3137 if self._ascending:
3252 if self._ascending:
3138 it = self.fastdesc
3253 it = self.fastdesc
3139 else:
3254 else:
3140 it = self.fastasc
3255 it = self.fastasc
3141 for x in it():
3256 for x in it():
3142 return x
3257 return x
3143 return None
3258 return None
3144
3259
3145 class fullreposet(_spanset):
3260 class fullreposet(_spanset):
3146 """a set containing all revisions in the repo
3261 """a set containing all revisions in the repo
3147
3262
3148 This class exists to host special optimization.
3263 This class exists to host special optimization.
3149 """
3264 """
3150
3265
3151 def __init__(self, repo):
3266 def __init__(self, repo):
3152 super(fullreposet, self).__init__(repo)
3267 super(fullreposet, self).__init__(repo)
3153
3268
3154 def __and__(self, other):
3269 def __and__(self, other):
3155 """As self contains the whole repo, all of the other set should also be
3270 """As self contains the whole repo, all of the other set should also be
3156 in self. Therefore `self & other = other`.
3271 in self. Therefore `self & other = other`.
3157
3272
3158 This boldly assumes the other contains valid revs only.
3273 This boldly assumes the other contains valid revs only.
3159 """
3274 """
3160 # other not a smartset, make is so
3275 # other not a smartset, make is so
3161 if not util.safehasattr(other, 'isascending'):
3276 if not util.safehasattr(other, 'isascending'):
3162 # filter out hidden revision
3277 # filter out hidden revision
3163 # (this boldly assumes all smartset are pure)
3278 # (this boldly assumes all smartset are pure)
3164 #
3279 #
3165 # `other` was used with "&", let's assume this is a set like
3280 # `other` was used with "&", let's assume this is a set like
3166 # object.
3281 # object.
3167 other = baseset(other - self._hiddenrevs)
3282 other = baseset(other - self._hiddenrevs)
3168
3283
3169 other.sort(reverse=self.isdescending())
3284 other.sort(reverse=self.isdescending())
3170 return other
3285 return other
3171
3286
3172 # tell hggettext to extract docstrings from these functions:
3287 # tell hggettext to extract docstrings from these functions:
3173 i18nfunctions = symbols.values()
3288 i18nfunctions = symbols.values()
General Comments 0
You need to be logged in to leave comments. Login now