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