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