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