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