##// END OF EJS Templates
revset: optimize missing ancestor expressions...
Siddharth Agarwal -
r20499:2efd6084 default
parent child Browse files
Show More
@@ -1,2197 +1,2214 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 match as matchmod
11 import match as matchmod
12 from i18n import _
12 from i18n import _
13 import encoding
13 import encoding
14 import obsolete as obsmod
14 import obsolete as obsmod
15 import pathutil
15 import pathutil
16 import repoview
16 import repoview
17
17
18 def _revancestors(repo, revs, followfirst):
18 def _revancestors(repo, revs, followfirst):
19 """Like revlog.ancestors(), but supports followfirst."""
19 """Like revlog.ancestors(), but supports followfirst."""
20 cut = followfirst and 1 or None
20 cut = followfirst and 1 or None
21 cl = repo.changelog
21 cl = repo.changelog
22 visit = util.deque(revs)
22 visit = util.deque(revs)
23 seen = set([node.nullrev])
23 seen = set([node.nullrev])
24 while visit:
24 while visit:
25 for parent in cl.parentrevs(visit.popleft())[:cut]:
25 for parent in cl.parentrevs(visit.popleft())[:cut]:
26 if parent not in seen:
26 if parent not in seen:
27 visit.append(parent)
27 visit.append(parent)
28 seen.add(parent)
28 seen.add(parent)
29 yield parent
29 yield parent
30
30
31 def _revdescendants(repo, revs, followfirst):
31 def _revdescendants(repo, revs, followfirst):
32 """Like revlog.descendants() but supports followfirst."""
32 """Like revlog.descendants() but supports followfirst."""
33 cut = followfirst and 1 or None
33 cut = followfirst and 1 or None
34 cl = repo.changelog
34 cl = repo.changelog
35 first = min(revs)
35 first = min(revs)
36 nullrev = node.nullrev
36 nullrev = node.nullrev
37 if first == nullrev:
37 if first == nullrev:
38 # Are there nodes with a null first parent and a non-null
38 # Are there nodes with a null first parent and a non-null
39 # second one? Maybe. Do we care? Probably not.
39 # second one? Maybe. Do we care? Probably not.
40 for i in cl:
40 for i in cl:
41 yield i
41 yield i
42 return
42 return
43
43
44 seen = set(revs)
44 seen = set(revs)
45 for i in cl.revs(first + 1):
45 for i in cl.revs(first + 1):
46 for x in cl.parentrevs(i)[:cut]:
46 for x in cl.parentrevs(i)[:cut]:
47 if x != nullrev and x in seen:
47 if x != nullrev and x in seen:
48 seen.add(i)
48 seen.add(i)
49 yield i
49 yield i
50 break
50 break
51
51
52 def _revsbetween(repo, roots, heads):
52 def _revsbetween(repo, roots, heads):
53 """Return all paths between roots and heads, inclusive of both endpoint
53 """Return all paths between roots and heads, inclusive of both endpoint
54 sets."""
54 sets."""
55 if not roots:
55 if not roots:
56 return baseset([])
56 return baseset([])
57 parentrevs = repo.changelog.parentrevs
57 parentrevs = repo.changelog.parentrevs
58 visit = baseset(heads)
58 visit = baseset(heads)
59 reachable = set()
59 reachable = set()
60 seen = {}
60 seen = {}
61 minroot = min(roots)
61 minroot = min(roots)
62 roots = set(roots)
62 roots = set(roots)
63 # open-code the post-order traversal due to the tiny size of
63 # open-code the post-order traversal due to the tiny size of
64 # sys.getrecursionlimit()
64 # sys.getrecursionlimit()
65 while visit:
65 while visit:
66 rev = visit.pop()
66 rev = visit.pop()
67 if rev in roots:
67 if rev in roots:
68 reachable.add(rev)
68 reachable.add(rev)
69 parents = parentrevs(rev)
69 parents = parentrevs(rev)
70 seen[rev] = parents
70 seen[rev] = parents
71 for parent in parents:
71 for parent in parents:
72 if parent >= minroot and parent not in seen:
72 if parent >= minroot and parent not in seen:
73 visit.append(parent)
73 visit.append(parent)
74 if not reachable:
74 if not reachable:
75 return baseset([])
75 return baseset([])
76 for rev in sorted(seen):
76 for rev in sorted(seen):
77 for parent in seen[rev]:
77 for parent in seen[rev]:
78 if parent in reachable:
78 if parent in reachable:
79 reachable.add(rev)
79 reachable.add(rev)
80 return baseset(sorted(reachable))
80 return baseset(sorted(reachable))
81
81
82 elements = {
82 elements = {
83 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
83 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
84 "~": (18, None, ("ancestor", 18)),
84 "~": (18, None, ("ancestor", 18)),
85 "^": (18, None, ("parent", 18), ("parentpost", 18)),
85 "^": (18, None, ("parent", 18), ("parentpost", 18)),
86 "-": (5, ("negate", 19), ("minus", 5)),
86 "-": (5, ("negate", 19), ("minus", 5)),
87 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
87 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
88 ("dagrangepost", 17)),
88 ("dagrangepost", 17)),
89 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
89 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
90 ("dagrangepost", 17)),
90 ("dagrangepost", 17)),
91 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
91 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
92 "not": (10, ("not", 10)),
92 "not": (10, ("not", 10)),
93 "!": (10, ("not", 10)),
93 "!": (10, ("not", 10)),
94 "and": (5, None, ("and", 5)),
94 "and": (5, None, ("and", 5)),
95 "&": (5, None, ("and", 5)),
95 "&": (5, None, ("and", 5)),
96 "or": (4, None, ("or", 4)),
96 "or": (4, None, ("or", 4)),
97 "|": (4, None, ("or", 4)),
97 "|": (4, None, ("or", 4)),
98 "+": (4, None, ("or", 4)),
98 "+": (4, None, ("or", 4)),
99 ",": (2, None, ("list", 2)),
99 ",": (2, None, ("list", 2)),
100 ")": (0, None, None),
100 ")": (0, None, None),
101 "symbol": (0, ("symbol",), None),
101 "symbol": (0, ("symbol",), None),
102 "string": (0, ("string",), None),
102 "string": (0, ("string",), None),
103 "end": (0, None, None),
103 "end": (0, None, None),
104 }
104 }
105
105
106 keywords = set(['and', 'or', 'not'])
106 keywords = set(['and', 'or', 'not'])
107
107
108 def tokenize(program):
108 def tokenize(program):
109 '''
109 '''
110 Parse a revset statement into a stream of tokens
110 Parse a revset statement into a stream of tokens
111
111
112 Check that @ is a valid unquoted token character (issue3686):
112 Check that @ is a valid unquoted token character (issue3686):
113 >>> list(tokenize("@::"))
113 >>> list(tokenize("@::"))
114 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
114 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
115
115
116 '''
116 '''
117
117
118 pos, l = 0, len(program)
118 pos, l = 0, len(program)
119 while pos < l:
119 while pos < l:
120 c = program[pos]
120 c = program[pos]
121 if c.isspace(): # skip inter-token whitespace
121 if c.isspace(): # skip inter-token whitespace
122 pass
122 pass
123 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
123 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
124 yield ('::', None, pos)
124 yield ('::', None, pos)
125 pos += 1 # skip ahead
125 pos += 1 # skip ahead
126 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
126 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
127 yield ('..', None, pos)
127 yield ('..', None, pos)
128 pos += 1 # skip ahead
128 pos += 1 # skip ahead
129 elif c in "():,-|&+!~^": # handle simple operators
129 elif c in "():,-|&+!~^": # handle simple operators
130 yield (c, None, pos)
130 yield (c, None, pos)
131 elif (c in '"\'' or c == 'r' and
131 elif (c in '"\'' or c == 'r' and
132 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
132 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
133 if c == 'r':
133 if c == 'r':
134 pos += 1
134 pos += 1
135 c = program[pos]
135 c = program[pos]
136 decode = lambda x: x
136 decode = lambda x: x
137 else:
137 else:
138 decode = lambda x: x.decode('string-escape')
138 decode = lambda x: x.decode('string-escape')
139 pos += 1
139 pos += 1
140 s = pos
140 s = pos
141 while pos < l: # find closing quote
141 while pos < l: # find closing quote
142 d = program[pos]
142 d = program[pos]
143 if d == '\\': # skip over escaped characters
143 if d == '\\': # skip over escaped characters
144 pos += 2
144 pos += 2
145 continue
145 continue
146 if d == c:
146 if d == c:
147 yield ('string', decode(program[s:pos]), s)
147 yield ('string', decode(program[s:pos]), s)
148 break
148 break
149 pos += 1
149 pos += 1
150 else:
150 else:
151 raise error.ParseError(_("unterminated string"), s)
151 raise error.ParseError(_("unterminated string"), s)
152 # gather up a symbol/keyword
152 # gather up a symbol/keyword
153 elif c.isalnum() or c in '._@' or ord(c) > 127:
153 elif c.isalnum() or c in '._@' or ord(c) > 127:
154 s = pos
154 s = pos
155 pos += 1
155 pos += 1
156 while pos < l: # find end of symbol
156 while pos < l: # find end of symbol
157 d = program[pos]
157 d = program[pos]
158 if not (d.isalnum() or d in "._/@" or ord(d) > 127):
158 if not (d.isalnum() or d in "._/@" or ord(d) > 127):
159 break
159 break
160 if d == '.' and program[pos - 1] == '.': # special case for ..
160 if d == '.' and program[pos - 1] == '.': # special case for ..
161 pos -= 1
161 pos -= 1
162 break
162 break
163 pos += 1
163 pos += 1
164 sym = program[s:pos]
164 sym = program[s:pos]
165 if sym in keywords: # operator keywords
165 if sym in keywords: # operator keywords
166 yield (sym, None, s)
166 yield (sym, None, s)
167 else:
167 else:
168 yield ('symbol', sym, s)
168 yield ('symbol', sym, s)
169 pos -= 1
169 pos -= 1
170 else:
170 else:
171 raise error.ParseError(_("syntax error"), pos)
171 raise error.ParseError(_("syntax error"), pos)
172 pos += 1
172 pos += 1
173 yield ('end', None, pos)
173 yield ('end', None, pos)
174
174
175 # helpers
175 # helpers
176
176
177 def getstring(x, err):
177 def getstring(x, err):
178 if x and (x[0] == 'string' or x[0] == 'symbol'):
178 if x and (x[0] == 'string' or x[0] == 'symbol'):
179 return x[1]
179 return x[1]
180 raise error.ParseError(err)
180 raise error.ParseError(err)
181
181
182 def getlist(x):
182 def getlist(x):
183 if not x:
183 if not x:
184 return []
184 return []
185 if x[0] == 'list':
185 if x[0] == 'list':
186 return getlist(x[1]) + [x[2]]
186 return getlist(x[1]) + [x[2]]
187 return [x]
187 return [x]
188
188
189 def getargs(x, min, max, err):
189 def getargs(x, min, max, err):
190 l = getlist(x)
190 l = getlist(x)
191 if len(l) < min or (max >= 0 and len(l) > max):
191 if len(l) < min or (max >= 0 and len(l) > max):
192 raise error.ParseError(err)
192 raise error.ParseError(err)
193 return l
193 return l
194
194
195 def getset(repo, subset, x):
195 def getset(repo, subset, x):
196 if not x:
196 if not x:
197 raise error.ParseError(_("missing argument"))
197 raise error.ParseError(_("missing argument"))
198 return methods[x[0]](repo, subset, *x[1:])
198 return methods[x[0]](repo, subset, *x[1:])
199
199
200 def _getrevsource(repo, r):
200 def _getrevsource(repo, r):
201 extra = repo[r].extra()
201 extra = repo[r].extra()
202 for label in ('source', 'transplant_source', 'rebase_source'):
202 for label in ('source', 'transplant_source', 'rebase_source'):
203 if label in extra:
203 if label in extra:
204 try:
204 try:
205 return repo[extra[label]].rev()
205 return repo[extra[label]].rev()
206 except error.RepoLookupError:
206 except error.RepoLookupError:
207 pass
207 pass
208 return None
208 return None
209
209
210 # operator methods
210 # operator methods
211
211
212 def stringset(repo, subset, x):
212 def stringset(repo, subset, x):
213 x = repo[x].rev()
213 x = repo[x].rev()
214 if x == -1 and len(subset) == len(repo):
214 if x == -1 and len(subset) == len(repo):
215 return baseset([-1])
215 return baseset([-1])
216 if len(subset) == len(repo) or x in subset:
216 if len(subset) == len(repo) or x in subset:
217 return baseset([x])
217 return baseset([x])
218 return baseset([])
218 return baseset([])
219
219
220 def symbolset(repo, subset, x):
220 def symbolset(repo, subset, x):
221 if x in symbols:
221 if x in symbols:
222 raise error.ParseError(_("can't use %s here") % x)
222 raise error.ParseError(_("can't use %s here") % x)
223 return stringset(repo, subset, x)
223 return stringset(repo, subset, x)
224
224
225 def rangeset(repo, subset, x, y):
225 def rangeset(repo, subset, x, y):
226 cl = baseset(repo.changelog)
226 cl = baseset(repo.changelog)
227 m = getset(repo, cl, x)
227 m = getset(repo, cl, x)
228 n = getset(repo, cl, y)
228 n = getset(repo, cl, y)
229
229
230 if not m or not n:
230 if not m or not n:
231 return baseset([])
231 return baseset([])
232 m, n = m[0], n[-1]
232 m, n = m[0], n[-1]
233
233
234 if m < n:
234 if m < n:
235 r = range(m, n + 1)
235 r = range(m, n + 1)
236 else:
236 else:
237 r = range(m, n - 1, -1)
237 r = range(m, n - 1, -1)
238 s = subset.set()
238 s = subset.set()
239 return baseset([x for x in r if x in s])
239 return baseset([x for x in r if x in s])
240
240
241 def dagrange(repo, subset, x, y):
241 def dagrange(repo, subset, x, y):
242 r = baseset(repo)
242 r = baseset(repo)
243 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
243 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
244 s = subset.set()
244 s = subset.set()
245 return baseset([r for r in xs if r in s])
245 return baseset([r for r in xs if r in s])
246
246
247 def andset(repo, subset, x, y):
247 def andset(repo, subset, x, y):
248 return getset(repo, getset(repo, subset, x), y)
248 return getset(repo, getset(repo, subset, x), y)
249
249
250 def orset(repo, subset, x, y):
250 def orset(repo, subset, x, y):
251 xl = getset(repo, subset, x)
251 xl = getset(repo, subset, x)
252 yl = getset(repo, subset - xl, y)
252 yl = getset(repo, subset - xl, y)
253 return xl + yl
253 return xl + yl
254
254
255 def notset(repo, subset, x):
255 def notset(repo, subset, x):
256 return subset - getset(repo, subset, x)
256 return subset - getset(repo, subset, x)
257
257
258 def listset(repo, subset, a, b):
258 def listset(repo, subset, a, b):
259 raise error.ParseError(_("can't use a list in this context"))
259 raise error.ParseError(_("can't use a list in this context"))
260
260
261 def func(repo, subset, a, b):
261 def func(repo, subset, a, b):
262 if a[0] == 'symbol' and a[1] in symbols:
262 if a[0] == 'symbol' and a[1] in symbols:
263 return symbols[a[1]](repo, subset, b)
263 return symbols[a[1]](repo, subset, b)
264 raise error.ParseError(_("not a function: %s") % a[1])
264 raise error.ParseError(_("not a function: %s") % a[1])
265
265
266 # functions
266 # functions
267
267
268 def adds(repo, subset, x):
268 def adds(repo, subset, x):
269 """``adds(pattern)``
269 """``adds(pattern)``
270 Changesets that add a file matching pattern.
270 Changesets that add a file matching pattern.
271
271
272 The pattern without explicit kind like ``glob:`` is expected to be
272 The pattern without explicit kind like ``glob:`` is expected to be
273 relative to the current directory and match against a file or a
273 relative to the current directory and match against a file or a
274 directory.
274 directory.
275 """
275 """
276 # i18n: "adds" is a keyword
276 # i18n: "adds" is a keyword
277 pat = getstring(x, _("adds requires a pattern"))
277 pat = getstring(x, _("adds requires a pattern"))
278 return checkstatus(repo, subset, pat, 1)
278 return checkstatus(repo, subset, pat, 1)
279
279
280 def ancestor(repo, subset, x):
280 def ancestor(repo, subset, x):
281 """``ancestor(*changeset)``
281 """``ancestor(*changeset)``
282 Greatest common ancestor of the changesets.
282 Greatest common ancestor of the changesets.
283
283
284 Accepts 0 or more changesets.
284 Accepts 0 or more changesets.
285 Will return empty list when passed no args.
285 Will return empty list when passed no args.
286 Greatest common ancestor of a single changeset is that changeset.
286 Greatest common ancestor of a single changeset is that changeset.
287 """
287 """
288 # i18n: "ancestor" is a keyword
288 # i18n: "ancestor" is a keyword
289 l = getlist(x)
289 l = getlist(x)
290 rl = baseset(repo)
290 rl = baseset(repo)
291 anc = None
291 anc = None
292
292
293 # (getset(repo, rl, i) for i in l) generates a list of lists
293 # (getset(repo, rl, i) for i in l) generates a list of lists
294 rev = repo.changelog.rev
294 rev = repo.changelog.rev
295 ancestor = repo.changelog.ancestor
295 ancestor = repo.changelog.ancestor
296 node = repo.changelog.node
296 node = repo.changelog.node
297 for revs in (getset(repo, rl, i) for i in l):
297 for revs in (getset(repo, rl, i) for i in l):
298 for r in revs:
298 for r in revs:
299 if anc is None:
299 if anc is None:
300 anc = r
300 anc = r
301 else:
301 else:
302 anc = rev(ancestor(node(anc), node(r)))
302 anc = rev(ancestor(node(anc), node(r)))
303
303
304 if anc is not None and anc in subset:
304 if anc is not None and anc in subset:
305 return baseset([anc])
305 return baseset([anc])
306 return baseset([])
306 return baseset([])
307
307
308 def _ancestors(repo, subset, x, followfirst=False):
308 def _ancestors(repo, subset, x, followfirst=False):
309 args = getset(repo, baseset(repo), x)
309 args = getset(repo, baseset(repo), x)
310 if not args:
310 if not args:
311 return baseset([])
311 return baseset([])
312 s = set(_revancestors(repo, args, followfirst)) | set(args)
312 s = set(_revancestors(repo, args, followfirst)) | set(args)
313 return baseset([r for r in subset if r in s])
313 return baseset([r for r in subset if r in s])
314
314
315 def ancestors(repo, subset, x):
315 def ancestors(repo, subset, x):
316 """``ancestors(set)``
316 """``ancestors(set)``
317 Changesets that are ancestors of a changeset in set.
317 Changesets that are ancestors of a changeset in set.
318 """
318 """
319 return _ancestors(repo, subset, x)
319 return _ancestors(repo, subset, x)
320
320
321 def _firstancestors(repo, subset, x):
321 def _firstancestors(repo, subset, x):
322 # ``_firstancestors(set)``
322 # ``_firstancestors(set)``
323 # Like ``ancestors(set)`` but follows only the first parents.
323 # Like ``ancestors(set)`` but follows only the first parents.
324 return _ancestors(repo, subset, x, followfirst=True)
324 return _ancestors(repo, subset, x, followfirst=True)
325
325
326 def ancestorspec(repo, subset, x, n):
326 def ancestorspec(repo, subset, x, n):
327 """``set~n``
327 """``set~n``
328 Changesets that are the Nth ancestor (first parents only) of a changeset
328 Changesets that are the Nth ancestor (first parents only) of a changeset
329 in set.
329 in set.
330 """
330 """
331 try:
331 try:
332 n = int(n[1])
332 n = int(n[1])
333 except (TypeError, ValueError):
333 except (TypeError, ValueError):
334 raise error.ParseError(_("~ expects a number"))
334 raise error.ParseError(_("~ expects a number"))
335 ps = set()
335 ps = set()
336 cl = repo.changelog
336 cl = repo.changelog
337 for r in getset(repo, baseset(cl), x):
337 for r in getset(repo, baseset(cl), x):
338 for i in range(n):
338 for i in range(n):
339 r = cl.parentrevs(r)[0]
339 r = cl.parentrevs(r)[0]
340 ps.add(r)
340 ps.add(r)
341 return baseset([r for r in subset if r in ps])
341 return baseset([r for r in subset if r in ps])
342
342
343 def author(repo, subset, x):
343 def author(repo, subset, x):
344 """``author(string)``
344 """``author(string)``
345 Alias for ``user(string)``.
345 Alias for ``user(string)``.
346 """
346 """
347 # i18n: "author" is a keyword
347 # i18n: "author" is a keyword
348 n = encoding.lower(getstring(x, _("author requires a string")))
348 n = encoding.lower(getstring(x, _("author requires a string")))
349 kind, pattern, matcher = _substringmatcher(n)
349 kind, pattern, matcher = _substringmatcher(n)
350 return lazyset(subset, lambda x: matcher(encoding.lower(repo[x].user())))
350 return lazyset(subset, lambda x: matcher(encoding.lower(repo[x].user())))
351
351
352 def bisect(repo, subset, x):
352 def bisect(repo, subset, x):
353 """``bisect(string)``
353 """``bisect(string)``
354 Changesets marked in the specified bisect status:
354 Changesets marked in the specified bisect status:
355
355
356 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
356 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
357 - ``goods``, ``bads`` : csets topologically good/bad
357 - ``goods``, ``bads`` : csets topologically good/bad
358 - ``range`` : csets taking part in the bisection
358 - ``range`` : csets taking part in the bisection
359 - ``pruned`` : csets that are goods, bads or skipped
359 - ``pruned`` : csets that are goods, bads or skipped
360 - ``untested`` : csets whose fate is yet unknown
360 - ``untested`` : csets whose fate is yet unknown
361 - ``ignored`` : csets ignored due to DAG topology
361 - ``ignored`` : csets ignored due to DAG topology
362 - ``current`` : the cset currently being bisected
362 - ``current`` : the cset currently being bisected
363 """
363 """
364 # i18n: "bisect" is a keyword
364 # i18n: "bisect" is a keyword
365 status = getstring(x, _("bisect requires a string")).lower()
365 status = getstring(x, _("bisect requires a string")).lower()
366 state = set(hbisect.get(repo, status))
366 state = set(hbisect.get(repo, status))
367 return baseset([r for r in subset if r in state])
367 return baseset([r for r in subset if r in state])
368
368
369 # Backward-compatibility
369 # Backward-compatibility
370 # - no help entry so that we do not advertise it any more
370 # - no help entry so that we do not advertise it any more
371 def bisected(repo, subset, x):
371 def bisected(repo, subset, x):
372 return bisect(repo, subset, x)
372 return bisect(repo, subset, x)
373
373
374 def bookmark(repo, subset, x):
374 def bookmark(repo, subset, x):
375 """``bookmark([name])``
375 """``bookmark([name])``
376 The named bookmark or all bookmarks.
376 The named bookmark or all bookmarks.
377
377
378 If `name` starts with `re:`, the remainder of the name is treated as
378 If `name` starts with `re:`, the remainder of the name is treated as
379 a regular expression. To match a bookmark that actually starts with `re:`,
379 a regular expression. To match a bookmark that actually starts with `re:`,
380 use the prefix `literal:`.
380 use the prefix `literal:`.
381 """
381 """
382 # i18n: "bookmark" is a keyword
382 # i18n: "bookmark" is a keyword
383 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
383 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
384 if args:
384 if args:
385 bm = getstring(args[0],
385 bm = getstring(args[0],
386 # i18n: "bookmark" is a keyword
386 # i18n: "bookmark" is a keyword
387 _('the argument to bookmark must be a string'))
387 _('the argument to bookmark must be a string'))
388 kind, pattern, matcher = _stringmatcher(bm)
388 kind, pattern, matcher = _stringmatcher(bm)
389 if kind == 'literal':
389 if kind == 'literal':
390 bmrev = repo._bookmarks.get(bm, None)
390 bmrev = repo._bookmarks.get(bm, None)
391 if not bmrev:
391 if not bmrev:
392 raise util.Abort(_("bookmark '%s' does not exist") % bm)
392 raise util.Abort(_("bookmark '%s' does not exist") % bm)
393 bmrev = repo[bmrev].rev()
393 bmrev = repo[bmrev].rev()
394 return lazyset(subset, lambda r: r == bmrev)
394 return lazyset(subset, lambda r: r == bmrev)
395 else:
395 else:
396 matchrevs = set()
396 matchrevs = set()
397 for name, bmrev in repo._bookmarks.iteritems():
397 for name, bmrev in repo._bookmarks.iteritems():
398 if matcher(name):
398 if matcher(name):
399 matchrevs.add(bmrev)
399 matchrevs.add(bmrev)
400 if not matchrevs:
400 if not matchrevs:
401 raise util.Abort(_("no bookmarks exist that match '%s'")
401 raise util.Abort(_("no bookmarks exist that match '%s'")
402 % pattern)
402 % pattern)
403 bmrevs = set()
403 bmrevs = set()
404 for bmrev in matchrevs:
404 for bmrev in matchrevs:
405 bmrevs.add(repo[bmrev].rev())
405 bmrevs.add(repo[bmrev].rev())
406 return subset & bmrevs
406 return subset & bmrevs
407
407
408 bms = set([repo[r].rev()
408 bms = set([repo[r].rev()
409 for r in repo._bookmarks.values()])
409 for r in repo._bookmarks.values()])
410 return lazyset(subset, lambda r: r in bms)
410 return lazyset(subset, lambda r: r in bms)
411
411
412 def branch(repo, subset, x):
412 def branch(repo, subset, x):
413 """``branch(string or set)``
413 """``branch(string or set)``
414 All changesets belonging to the given branch or the branches of the given
414 All changesets belonging to the given branch or the branches of the given
415 changesets.
415 changesets.
416
416
417 If `string` starts with `re:`, the remainder of the name is treated as
417 If `string` starts with `re:`, the remainder of the name is treated as
418 a regular expression. To match a branch that actually starts with `re:`,
418 a regular expression. To match a branch that actually starts with `re:`,
419 use the prefix `literal:`.
419 use the prefix `literal:`.
420 """
420 """
421 try:
421 try:
422 b = getstring(x, '')
422 b = getstring(x, '')
423 except error.ParseError:
423 except error.ParseError:
424 # not a string, but another revspec, e.g. tip()
424 # not a string, but another revspec, e.g. tip()
425 pass
425 pass
426 else:
426 else:
427 kind, pattern, matcher = _stringmatcher(b)
427 kind, pattern, matcher = _stringmatcher(b)
428 if kind == 'literal':
428 if kind == 'literal':
429 # note: falls through to the revspec case if no branch with
429 # note: falls through to the revspec case if no branch with
430 # this name exists
430 # this name exists
431 if pattern in repo.branchmap():
431 if pattern in repo.branchmap():
432 return lazyset(subset, lambda r: matcher(repo[r].branch()))
432 return lazyset(subset, lambda r: matcher(repo[r].branch()))
433 else:
433 else:
434 return lazyset(subset, lambda r: matcher(repo[r].branch()))
434 return lazyset(subset, lambda r: matcher(repo[r].branch()))
435
435
436 s = getset(repo, baseset(repo), x)
436 s = getset(repo, baseset(repo), x)
437 b = set()
437 b = set()
438 for r in s:
438 for r in s:
439 b.add(repo[r].branch())
439 b.add(repo[r].branch())
440 s = s.set()
440 s = s.set()
441 return lazyset(subset, lambda r: r in s or repo[r].branch() in b)
441 return lazyset(subset, lambda r: r in s or repo[r].branch() in b)
442
442
443 def bumped(repo, subset, x):
443 def bumped(repo, subset, x):
444 """``bumped()``
444 """``bumped()``
445 Mutable changesets marked as successors of public changesets.
445 Mutable changesets marked as successors of public changesets.
446
446
447 Only non-public and non-obsolete changesets can be `bumped`.
447 Only non-public and non-obsolete changesets can be `bumped`.
448 """
448 """
449 # i18n: "bumped" is a keyword
449 # i18n: "bumped" is a keyword
450 getargs(x, 0, 0, _("bumped takes no arguments"))
450 getargs(x, 0, 0, _("bumped takes no arguments"))
451 bumped = obsmod.getrevs(repo, 'bumped')
451 bumped = obsmod.getrevs(repo, 'bumped')
452 return subset & bumped
452 return subset & bumped
453
453
454 def bundle(repo, subset, x):
454 def bundle(repo, subset, x):
455 """``bundle()``
455 """``bundle()``
456 Changesets in the bundle.
456 Changesets in the bundle.
457
457
458 Bundle must be specified by the -R option."""
458 Bundle must be specified by the -R option."""
459
459
460 try:
460 try:
461 bundlerevs = repo.changelog.bundlerevs
461 bundlerevs = repo.changelog.bundlerevs
462 except AttributeError:
462 except AttributeError:
463 raise util.Abort(_("no bundle provided - specify with -R"))
463 raise util.Abort(_("no bundle provided - specify with -R"))
464 return subset & bundlerevs
464 return subset & bundlerevs
465
465
466 def checkstatus(repo, subset, pat, field):
466 def checkstatus(repo, subset, pat, field):
467 hasset = matchmod.patkind(pat) == 'set'
467 hasset = matchmod.patkind(pat) == 'set'
468
468
469 def matches(x):
469 def matches(x):
470 m = None
470 m = None
471 fname = None
471 fname = None
472 c = repo[x]
472 c = repo[x]
473 if not m or hasset:
473 if not m or hasset:
474 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
474 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
475 if not m.anypats() and len(m.files()) == 1:
475 if not m.anypats() and len(m.files()) == 1:
476 fname = m.files()[0]
476 fname = m.files()[0]
477 if fname is not None:
477 if fname is not None:
478 if fname not in c.files():
478 if fname not in c.files():
479 return False
479 return False
480 else:
480 else:
481 for f in c.files():
481 for f in c.files():
482 if m(f):
482 if m(f):
483 break
483 break
484 else:
484 else:
485 return False
485 return False
486 files = repo.status(c.p1().node(), c.node())[field]
486 files = repo.status(c.p1().node(), c.node())[field]
487 if fname is not None:
487 if fname is not None:
488 if fname in files:
488 if fname in files:
489 return True
489 return True
490 else:
490 else:
491 for f in files:
491 for f in files:
492 if m(f):
492 if m(f):
493 return True
493 return True
494
494
495 return lazyset(subset, matches)
495 return lazyset(subset, matches)
496
496
497 def _children(repo, narrow, parentset):
497 def _children(repo, narrow, parentset):
498 cs = set()
498 cs = set()
499 if not parentset:
499 if not parentset:
500 return baseset(cs)
500 return baseset(cs)
501 pr = repo.changelog.parentrevs
501 pr = repo.changelog.parentrevs
502 minrev = min(parentset)
502 minrev = min(parentset)
503 for r in narrow:
503 for r in narrow:
504 if r <= minrev:
504 if r <= minrev:
505 continue
505 continue
506 for p in pr(r):
506 for p in pr(r):
507 if p in parentset:
507 if p in parentset:
508 cs.add(r)
508 cs.add(r)
509 return baseset(cs)
509 return baseset(cs)
510
510
511 def children(repo, subset, x):
511 def children(repo, subset, x):
512 """``children(set)``
512 """``children(set)``
513 Child changesets of changesets in set.
513 Child changesets of changesets in set.
514 """
514 """
515 s = getset(repo, baseset(repo), x).set()
515 s = getset(repo, baseset(repo), x).set()
516 cs = _children(repo, subset, s)
516 cs = _children(repo, subset, s)
517 return subset & cs
517 return subset & cs
518
518
519 def closed(repo, subset, x):
519 def closed(repo, subset, x):
520 """``closed()``
520 """``closed()``
521 Changeset is closed.
521 Changeset is closed.
522 """
522 """
523 # i18n: "closed" is a keyword
523 # i18n: "closed" is a keyword
524 getargs(x, 0, 0, _("closed takes no arguments"))
524 getargs(x, 0, 0, _("closed takes no arguments"))
525 return lazyset(subset, lambda r: repo[r].closesbranch())
525 return lazyset(subset, lambda r: repo[r].closesbranch())
526
526
527 def contains(repo, subset, x):
527 def contains(repo, subset, x):
528 """``contains(pattern)``
528 """``contains(pattern)``
529 Revision contains a file matching pattern. See :hg:`help patterns`
529 Revision contains a file matching pattern. See :hg:`help patterns`
530 for information about file patterns.
530 for information about file patterns.
531
531
532 The pattern without explicit kind like ``glob:`` is expected to be
532 The pattern without explicit kind like ``glob:`` is expected to be
533 relative to the current directory and match against a file exactly
533 relative to the current directory and match against a file exactly
534 for efficiency.
534 for efficiency.
535 """
535 """
536 # i18n: "contains" is a keyword
536 # i18n: "contains" is a keyword
537 pat = getstring(x, _("contains requires a pattern"))
537 pat = getstring(x, _("contains requires a pattern"))
538
538
539 def matches(x):
539 def matches(x):
540 if not matchmod.patkind(pat):
540 if not matchmod.patkind(pat):
541 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
541 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
542 if pats in repo[x]:
542 if pats in repo[x]:
543 return True
543 return True
544 else:
544 else:
545 c = repo[x]
545 c = repo[x]
546 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
546 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
547 for f in c.manifest():
547 for f in c.manifest():
548 if m(f):
548 if m(f):
549 return True
549 return True
550 return False
550 return False
551
551
552 return lazyset(subset, matches)
552 return lazyset(subset, matches)
553
553
554 def converted(repo, subset, x):
554 def converted(repo, subset, x):
555 """``converted([id])``
555 """``converted([id])``
556 Changesets converted from the given identifier in the old repository if
556 Changesets converted from the given identifier in the old repository if
557 present, or all converted changesets if no identifier is specified.
557 present, or all converted changesets if no identifier is specified.
558 """
558 """
559
559
560 # There is exactly no chance of resolving the revision, so do a simple
560 # There is exactly no chance of resolving the revision, so do a simple
561 # string compare and hope for the best
561 # string compare and hope for the best
562
562
563 rev = None
563 rev = None
564 # i18n: "converted" is a keyword
564 # i18n: "converted" is a keyword
565 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
565 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
566 if l:
566 if l:
567 # i18n: "converted" is a keyword
567 # i18n: "converted" is a keyword
568 rev = getstring(l[0], _('converted requires a revision'))
568 rev = getstring(l[0], _('converted requires a revision'))
569
569
570 def _matchvalue(r):
570 def _matchvalue(r):
571 source = repo[r].extra().get('convert_revision', None)
571 source = repo[r].extra().get('convert_revision', None)
572 return source is not None and (rev is None or source.startswith(rev))
572 return source is not None and (rev is None or source.startswith(rev))
573
573
574 return lazyset(subset, lambda r: _matchvalue(r))
574 return lazyset(subset, lambda r: _matchvalue(r))
575
575
576 def date(repo, subset, x):
576 def date(repo, subset, x):
577 """``date(interval)``
577 """``date(interval)``
578 Changesets within the interval, see :hg:`help dates`.
578 Changesets within the interval, see :hg:`help dates`.
579 """
579 """
580 # i18n: "date" is a keyword
580 # i18n: "date" is a keyword
581 ds = getstring(x, _("date requires a string"))
581 ds = getstring(x, _("date requires a string"))
582 dm = util.matchdate(ds)
582 dm = util.matchdate(ds)
583 return lazyset(subset, lambda x: dm(repo[x].date()[0]))
583 return lazyset(subset, lambda x: dm(repo[x].date()[0]))
584
584
585 def desc(repo, subset, x):
585 def desc(repo, subset, x):
586 """``desc(string)``
586 """``desc(string)``
587 Search commit message for string. The match is case-insensitive.
587 Search commit message for string. The match is case-insensitive.
588 """
588 """
589 # i18n: "desc" is a keyword
589 # i18n: "desc" is a keyword
590 ds = encoding.lower(getstring(x, _("desc requires a string")))
590 ds = encoding.lower(getstring(x, _("desc requires a string")))
591
591
592 def matches(x):
592 def matches(x):
593 c = repo[x]
593 c = repo[x]
594 return ds in encoding.lower(c.description())
594 return ds in encoding.lower(c.description())
595
595
596 return lazyset(subset, matches)
596 return lazyset(subset, matches)
597
597
598 def _descendants(repo, subset, x, followfirst=False):
598 def _descendants(repo, subset, x, followfirst=False):
599 args = getset(repo, baseset(repo), x)
599 args = getset(repo, baseset(repo), x)
600 if not args:
600 if not args:
601 return baseset([])
601 return baseset([])
602 s = set(_revdescendants(repo, args, followfirst)) | set(args)
602 s = set(_revdescendants(repo, args, followfirst)) | set(args)
603 return baseset([r for r in subset if r in s])
603 return baseset([r for r in subset if r in s])
604
604
605 def descendants(repo, subset, x):
605 def descendants(repo, subset, x):
606 """``descendants(set)``
606 """``descendants(set)``
607 Changesets which are descendants of changesets in set.
607 Changesets which are descendants of changesets in set.
608 """
608 """
609 return _descendants(repo, subset, x)
609 return _descendants(repo, subset, x)
610
610
611 def _firstdescendants(repo, subset, x):
611 def _firstdescendants(repo, subset, x):
612 # ``_firstdescendants(set)``
612 # ``_firstdescendants(set)``
613 # Like ``descendants(set)`` but follows only the first parents.
613 # Like ``descendants(set)`` but follows only the first parents.
614 return _descendants(repo, subset, x, followfirst=True)
614 return _descendants(repo, subset, x, followfirst=True)
615
615
616 def destination(repo, subset, x):
616 def destination(repo, subset, x):
617 """``destination([set])``
617 """``destination([set])``
618 Changesets that were created by a graft, transplant or rebase operation,
618 Changesets that were created by a graft, transplant or rebase operation,
619 with the given revisions specified as the source. Omitting the optional set
619 with the given revisions specified as the source. Omitting the optional set
620 is the same as passing all().
620 is the same as passing all().
621 """
621 """
622 if x is not None:
622 if x is not None:
623 args = getset(repo, baseset(repo), x).set()
623 args = getset(repo, baseset(repo), x).set()
624 else:
624 else:
625 args = getall(repo, baseset(repo), x).set()
625 args = getall(repo, baseset(repo), x).set()
626
626
627 dests = set()
627 dests = set()
628
628
629 # subset contains all of the possible destinations that can be returned, so
629 # subset contains all of the possible destinations that can be returned, so
630 # iterate over them and see if their source(s) were provided in the args.
630 # iterate over them and see if their source(s) were provided in the args.
631 # Even if the immediate src of r is not in the args, src's source (or
631 # Even if the immediate src of r is not in the args, src's source (or
632 # further back) may be. Scanning back further than the immediate src allows
632 # further back) may be. Scanning back further than the immediate src allows
633 # transitive transplants and rebases to yield the same results as transitive
633 # transitive transplants and rebases to yield the same results as transitive
634 # grafts.
634 # grafts.
635 for r in subset:
635 for r in subset:
636 src = _getrevsource(repo, r)
636 src = _getrevsource(repo, r)
637 lineage = None
637 lineage = None
638
638
639 while src is not None:
639 while src is not None:
640 if lineage is None:
640 if lineage is None:
641 lineage = list()
641 lineage = list()
642
642
643 lineage.append(r)
643 lineage.append(r)
644
644
645 # The visited lineage is a match if the current source is in the arg
645 # The visited lineage is a match if the current source is in the arg
646 # set. Since every candidate dest is visited by way of iterating
646 # set. Since every candidate dest is visited by way of iterating
647 # subset, any dests further back in the lineage will be tested by a
647 # subset, any dests further back in the lineage will be tested by a
648 # different iteration over subset. Likewise, if the src was already
648 # different iteration over subset. Likewise, if the src was already
649 # selected, the current lineage can be selected without going back
649 # selected, the current lineage can be selected without going back
650 # further.
650 # further.
651 if src in args or src in dests:
651 if src in args or src in dests:
652 dests.update(lineage)
652 dests.update(lineage)
653 break
653 break
654
654
655 r = src
655 r = src
656 src = _getrevsource(repo, r)
656 src = _getrevsource(repo, r)
657
657
658 return baseset([r for r in subset if r in dests])
658 return baseset([r for r in subset if r in dests])
659
659
660 def divergent(repo, subset, x):
660 def divergent(repo, subset, x):
661 """``divergent()``
661 """``divergent()``
662 Final successors of changesets with an alternative set of final successors.
662 Final successors of changesets with an alternative set of final successors.
663 """
663 """
664 # i18n: "divergent" is a keyword
664 # i18n: "divergent" is a keyword
665 getargs(x, 0, 0, _("divergent takes no arguments"))
665 getargs(x, 0, 0, _("divergent takes no arguments"))
666 divergent = obsmod.getrevs(repo, 'divergent')
666 divergent = obsmod.getrevs(repo, 'divergent')
667 return baseset([r for r in subset if r in divergent])
667 return baseset([r for r in subset if r in divergent])
668
668
669 def draft(repo, subset, x):
669 def draft(repo, subset, x):
670 """``draft()``
670 """``draft()``
671 Changeset in draft phase."""
671 Changeset in draft phase."""
672 # i18n: "draft" is a keyword
672 # i18n: "draft" is a keyword
673 getargs(x, 0, 0, _("draft takes no arguments"))
673 getargs(x, 0, 0, _("draft takes no arguments"))
674 pc = repo._phasecache
674 pc = repo._phasecache
675 return lazyset(subset, lambda r: pc.phase(repo, r) == phases.draft)
675 return lazyset(subset, lambda r: pc.phase(repo, r) == phases.draft)
676
676
677 def extinct(repo, subset, x):
677 def extinct(repo, subset, x):
678 """``extinct()``
678 """``extinct()``
679 Obsolete changesets with obsolete descendants only.
679 Obsolete changesets with obsolete descendants only.
680 """
680 """
681 # i18n: "extinct" is a keyword
681 # i18n: "extinct" is a keyword
682 getargs(x, 0, 0, _("extinct takes no arguments"))
682 getargs(x, 0, 0, _("extinct takes no arguments"))
683 extincts = obsmod.getrevs(repo, 'extinct')
683 extincts = obsmod.getrevs(repo, 'extinct')
684 return subset & extincts
684 return subset & extincts
685
685
686 def extra(repo, subset, x):
686 def extra(repo, subset, x):
687 """``extra(label, [value])``
687 """``extra(label, [value])``
688 Changesets with the given label in the extra metadata, with the given
688 Changesets with the given label in the extra metadata, with the given
689 optional value.
689 optional value.
690
690
691 If `value` starts with `re:`, the remainder of the value is treated as
691 If `value` starts with `re:`, the remainder of the value is treated as
692 a regular expression. To match a value that actually starts with `re:`,
692 a regular expression. To match a value that actually starts with `re:`,
693 use the prefix `literal:`.
693 use the prefix `literal:`.
694 """
694 """
695
695
696 # i18n: "extra" is a keyword
696 # i18n: "extra" is a keyword
697 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
697 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
698 # i18n: "extra" is a keyword
698 # i18n: "extra" is a keyword
699 label = getstring(l[0], _('first argument to extra must be a string'))
699 label = getstring(l[0], _('first argument to extra must be a string'))
700 value = None
700 value = None
701
701
702 if len(l) > 1:
702 if len(l) > 1:
703 # i18n: "extra" is a keyword
703 # i18n: "extra" is a keyword
704 value = getstring(l[1], _('second argument to extra must be a string'))
704 value = getstring(l[1], _('second argument to extra must be a string'))
705 kind, value, matcher = _stringmatcher(value)
705 kind, value, matcher = _stringmatcher(value)
706
706
707 def _matchvalue(r):
707 def _matchvalue(r):
708 extra = repo[r].extra()
708 extra = repo[r].extra()
709 return label in extra and (value is None or matcher(extra[label]))
709 return label in extra and (value is None or matcher(extra[label]))
710
710
711 return lazyset(subset, lambda r: _matchvalue(r))
711 return lazyset(subset, lambda r: _matchvalue(r))
712
712
713 def filelog(repo, subset, x):
713 def filelog(repo, subset, x):
714 """``filelog(pattern)``
714 """``filelog(pattern)``
715 Changesets connected to the specified filelog.
715 Changesets connected to the specified filelog.
716
716
717 For performance reasons, ``filelog()`` does not show every changeset
717 For performance reasons, ``filelog()`` does not show every changeset
718 that affects the requested file(s). See :hg:`help log` for details. For
718 that affects the requested file(s). See :hg:`help log` for details. For
719 a slower, more accurate result, use ``file()``.
719 a slower, more accurate result, use ``file()``.
720
720
721 The pattern without explicit kind like ``glob:`` is expected to be
721 The pattern without explicit kind like ``glob:`` is expected to be
722 relative to the current directory and match against a file exactly
722 relative to the current directory and match against a file exactly
723 for efficiency.
723 for efficiency.
724 """
724 """
725
725
726 # i18n: "filelog" is a keyword
726 # i18n: "filelog" is a keyword
727 pat = getstring(x, _("filelog requires a pattern"))
727 pat = getstring(x, _("filelog requires a pattern"))
728 s = set()
728 s = set()
729
729
730 if not matchmod.patkind(pat):
730 if not matchmod.patkind(pat):
731 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
731 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
732 fl = repo.file(f)
732 fl = repo.file(f)
733 for fr in fl:
733 for fr in fl:
734 s.add(fl.linkrev(fr))
734 s.add(fl.linkrev(fr))
735 else:
735 else:
736 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
736 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
737 for f in repo[None]:
737 for f in repo[None]:
738 if m(f):
738 if m(f):
739 fl = repo.file(f)
739 fl = repo.file(f)
740 for fr in fl:
740 for fr in fl:
741 s.add(fl.linkrev(fr))
741 s.add(fl.linkrev(fr))
742
742
743 return baseset([r for r in subset if r in s])
743 return baseset([r for r in subset if r in s])
744
744
745 def first(repo, subset, x):
745 def first(repo, subset, x):
746 """``first(set, [n])``
746 """``first(set, [n])``
747 An alias for limit().
747 An alias for limit().
748 """
748 """
749 return limit(repo, subset, x)
749 return limit(repo, subset, x)
750
750
751 def _follow(repo, subset, x, name, followfirst=False):
751 def _follow(repo, subset, x, name, followfirst=False):
752 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
752 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
753 c = repo['.']
753 c = repo['.']
754 if l:
754 if l:
755 x = getstring(l[0], _("%s expected a filename") % name)
755 x = getstring(l[0], _("%s expected a filename") % name)
756 if x in c:
756 if x in c:
757 cx = c[x]
757 cx = c[x]
758 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
758 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
759 # include the revision responsible for the most recent version
759 # include the revision responsible for the most recent version
760 s.add(cx.linkrev())
760 s.add(cx.linkrev())
761 else:
761 else:
762 return baseset([])
762 return baseset([])
763 else:
763 else:
764 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
764 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
765
765
766 return baseset([r for r in subset if r in s])
766 return baseset([r for r in subset if r in s])
767
767
768 def follow(repo, subset, x):
768 def follow(repo, subset, x):
769 """``follow([file])``
769 """``follow([file])``
770 An alias for ``::.`` (ancestors of the working copy's first parent).
770 An alias for ``::.`` (ancestors of the working copy's first parent).
771 If a filename is specified, the history of the given file is followed,
771 If a filename is specified, the history of the given file is followed,
772 including copies.
772 including copies.
773 """
773 """
774 return _follow(repo, subset, x, 'follow')
774 return _follow(repo, subset, x, 'follow')
775
775
776 def _followfirst(repo, subset, x):
776 def _followfirst(repo, subset, x):
777 # ``followfirst([file])``
777 # ``followfirst([file])``
778 # Like ``follow([file])`` but follows only the first parent of
778 # Like ``follow([file])`` but follows only the first parent of
779 # every revision or file revision.
779 # every revision or file revision.
780 return _follow(repo, subset, x, '_followfirst', followfirst=True)
780 return _follow(repo, subset, x, '_followfirst', followfirst=True)
781
781
782 def getall(repo, subset, x):
782 def getall(repo, subset, x):
783 """``all()``
783 """``all()``
784 All changesets, the same as ``0:tip``.
784 All changesets, the same as ``0:tip``.
785 """
785 """
786 # i18n: "all" is a keyword
786 # i18n: "all" is a keyword
787 getargs(x, 0, 0, _("all takes no arguments"))
787 getargs(x, 0, 0, _("all takes no arguments"))
788 return subset
788 return subset
789
789
790 def grep(repo, subset, x):
790 def grep(repo, subset, x):
791 """``grep(regex)``
791 """``grep(regex)``
792 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
792 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
793 to ensure special escape characters are handled correctly. Unlike
793 to ensure special escape characters are handled correctly. Unlike
794 ``keyword(string)``, the match is case-sensitive.
794 ``keyword(string)``, the match is case-sensitive.
795 """
795 """
796 try:
796 try:
797 # i18n: "grep" is a keyword
797 # i18n: "grep" is a keyword
798 gr = re.compile(getstring(x, _("grep requires a string")))
798 gr = re.compile(getstring(x, _("grep requires a string")))
799 except re.error, e:
799 except re.error, e:
800 raise error.ParseError(_('invalid match pattern: %s') % e)
800 raise error.ParseError(_('invalid match pattern: %s') % e)
801
801
802 def matches(x):
802 def matches(x):
803 c = repo[x]
803 c = repo[x]
804 for e in c.files() + [c.user(), c.description()]:
804 for e in c.files() + [c.user(), c.description()]:
805 if gr.search(e):
805 if gr.search(e):
806 return True
806 return True
807 return False
807 return False
808
808
809 return lazyset(subset, matches)
809 return lazyset(subset, matches)
810
810
811 def _matchfiles(repo, subset, x):
811 def _matchfiles(repo, subset, x):
812 # _matchfiles takes a revset list of prefixed arguments:
812 # _matchfiles takes a revset list of prefixed arguments:
813 #
813 #
814 # [p:foo, i:bar, x:baz]
814 # [p:foo, i:bar, x:baz]
815 #
815 #
816 # builds a match object from them and filters subset. Allowed
816 # builds a match object from them and filters subset. Allowed
817 # prefixes are 'p:' for regular patterns, 'i:' for include
817 # prefixes are 'p:' for regular patterns, 'i:' for include
818 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
818 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
819 # a revision identifier, or the empty string to reference the
819 # a revision identifier, or the empty string to reference the
820 # working directory, from which the match object is
820 # working directory, from which the match object is
821 # initialized. Use 'd:' to set the default matching mode, default
821 # initialized. Use 'd:' to set the default matching mode, default
822 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
822 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
823
823
824 # i18n: "_matchfiles" is a keyword
824 # i18n: "_matchfiles" is a keyword
825 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
825 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
826 pats, inc, exc = [], [], []
826 pats, inc, exc = [], [], []
827 hasset = False
827 hasset = False
828 rev, default = None, None
828 rev, default = None, None
829 for arg in l:
829 for arg in l:
830 # i18n: "_matchfiles" is a keyword
830 # i18n: "_matchfiles" is a keyword
831 s = getstring(arg, _("_matchfiles requires string arguments"))
831 s = getstring(arg, _("_matchfiles requires string arguments"))
832 prefix, value = s[:2], s[2:]
832 prefix, value = s[:2], s[2:]
833 if prefix == 'p:':
833 if prefix == 'p:':
834 pats.append(value)
834 pats.append(value)
835 elif prefix == 'i:':
835 elif prefix == 'i:':
836 inc.append(value)
836 inc.append(value)
837 elif prefix == 'x:':
837 elif prefix == 'x:':
838 exc.append(value)
838 exc.append(value)
839 elif prefix == 'r:':
839 elif prefix == 'r:':
840 if rev is not None:
840 if rev is not None:
841 # i18n: "_matchfiles" is a keyword
841 # i18n: "_matchfiles" is a keyword
842 raise error.ParseError(_('_matchfiles expected at most one '
842 raise error.ParseError(_('_matchfiles expected at most one '
843 'revision'))
843 'revision'))
844 rev = value
844 rev = value
845 elif prefix == 'd:':
845 elif prefix == 'd:':
846 if default is not None:
846 if default is not None:
847 # i18n: "_matchfiles" is a keyword
847 # i18n: "_matchfiles" is a keyword
848 raise error.ParseError(_('_matchfiles expected at most one '
848 raise error.ParseError(_('_matchfiles expected at most one '
849 'default mode'))
849 'default mode'))
850 default = value
850 default = value
851 else:
851 else:
852 # i18n: "_matchfiles" is a keyword
852 # i18n: "_matchfiles" is a keyword
853 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
853 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
854 if not hasset and matchmod.patkind(value) == 'set':
854 if not hasset and matchmod.patkind(value) == 'set':
855 hasset = True
855 hasset = True
856 if not default:
856 if not default:
857 default = 'glob'
857 default = 'glob'
858
858
859 def matches(x):
859 def matches(x):
860 m = None
860 m = None
861 c = repo[x]
861 c = repo[x]
862 if not m or (hasset and rev is None):
862 if not m or (hasset and rev is None):
863 ctx = c
863 ctx = c
864 if rev is not None:
864 if rev is not None:
865 ctx = repo[rev or None]
865 ctx = repo[rev or None]
866 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
866 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
867 exclude=exc, ctx=ctx, default=default)
867 exclude=exc, ctx=ctx, default=default)
868 for f in c.files():
868 for f in c.files():
869 if m(f):
869 if m(f):
870 return True
870 return True
871 return False
871 return False
872
872
873 return lazyset(subset, matches)
873 return lazyset(subset, matches)
874
874
875 def hasfile(repo, subset, x):
875 def hasfile(repo, subset, x):
876 """``file(pattern)``
876 """``file(pattern)``
877 Changesets affecting files matched by pattern.
877 Changesets affecting files matched by pattern.
878
878
879 For a faster but less accurate result, consider using ``filelog()``
879 For a faster but less accurate result, consider using ``filelog()``
880 instead.
880 instead.
881
881
882 This predicate uses ``glob:`` as the default kind of pattern.
882 This predicate uses ``glob:`` as the default kind of pattern.
883 """
883 """
884 # i18n: "file" is a keyword
884 # i18n: "file" is a keyword
885 pat = getstring(x, _("file requires a pattern"))
885 pat = getstring(x, _("file requires a pattern"))
886 return _matchfiles(repo, subset, ('string', 'p:' + pat))
886 return _matchfiles(repo, subset, ('string', 'p:' + pat))
887
887
888 def head(repo, subset, x):
888 def head(repo, subset, x):
889 """``head()``
889 """``head()``
890 Changeset is a named branch head.
890 Changeset is a named branch head.
891 """
891 """
892 # i18n: "head" is a keyword
892 # i18n: "head" is a keyword
893 getargs(x, 0, 0, _("head takes no arguments"))
893 getargs(x, 0, 0, _("head takes no arguments"))
894 hs = set()
894 hs = set()
895 for b, ls in repo.branchmap().iteritems():
895 for b, ls in repo.branchmap().iteritems():
896 hs.update(repo[h].rev() for h in ls)
896 hs.update(repo[h].rev() for h in ls)
897 return baseset([r for r in subset if r in hs])
897 return baseset([r for r in subset if r in hs])
898
898
899 def heads(repo, subset, x):
899 def heads(repo, subset, x):
900 """``heads(set)``
900 """``heads(set)``
901 Members of set with no children in set.
901 Members of set with no children in set.
902 """
902 """
903 s = getset(repo, subset, x)
903 s = getset(repo, subset, x)
904 ps = parents(repo, subset, x)
904 ps = parents(repo, subset, x)
905 return s - ps
905 return s - ps
906
906
907 def hidden(repo, subset, x):
907 def hidden(repo, subset, x):
908 """``hidden()``
908 """``hidden()``
909 Hidden changesets.
909 Hidden changesets.
910 """
910 """
911 # i18n: "hidden" is a keyword
911 # i18n: "hidden" is a keyword
912 getargs(x, 0, 0, _("hidden takes no arguments"))
912 getargs(x, 0, 0, _("hidden takes no arguments"))
913 hiddenrevs = repoview.filterrevs(repo, 'visible')
913 hiddenrevs = repoview.filterrevs(repo, 'visible')
914 return subset & hiddenrevs
914 return subset & hiddenrevs
915
915
916 def keyword(repo, subset, x):
916 def keyword(repo, subset, x):
917 """``keyword(string)``
917 """``keyword(string)``
918 Search commit message, user name, and names of changed files for
918 Search commit message, user name, and names of changed files for
919 string. The match is case-insensitive.
919 string. The match is case-insensitive.
920 """
920 """
921 # i18n: "keyword" is a keyword
921 # i18n: "keyword" is a keyword
922 kw = encoding.lower(getstring(x, _("keyword requires a string")))
922 kw = encoding.lower(getstring(x, _("keyword requires a string")))
923
923
924 def matches(r):
924 def matches(r):
925 c = repo[r]
925 c = repo[r]
926 return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
926 return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
927 c.description()])
927 c.description()])
928
928
929 return lazyset(subset, matches)
929 return lazyset(subset, matches)
930
930
931 def limit(repo, subset, x):
931 def limit(repo, subset, x):
932 """``limit(set, [n])``
932 """``limit(set, [n])``
933 First n members of set, defaulting to 1.
933 First n members of set, defaulting to 1.
934 """
934 """
935 # i18n: "limit" is a keyword
935 # i18n: "limit" is a keyword
936 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
936 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
937 try:
937 try:
938 lim = 1
938 lim = 1
939 if len(l) == 2:
939 if len(l) == 2:
940 # i18n: "limit" is a keyword
940 # i18n: "limit" is a keyword
941 lim = int(getstring(l[1], _("limit requires a number")))
941 lim = int(getstring(l[1], _("limit requires a number")))
942 except (TypeError, ValueError):
942 except (TypeError, ValueError):
943 # i18n: "limit" is a keyword
943 # i18n: "limit" is a keyword
944 raise error.ParseError(_("limit expects a number"))
944 raise error.ParseError(_("limit expects a number"))
945 ss = subset.set()
945 ss = subset.set()
946 os = getset(repo, baseset(repo), l[0])
946 os = getset(repo, baseset(repo), l[0])
947 bs = baseset([])
947 bs = baseset([])
948 it = iter(os)
948 it = iter(os)
949 for x in xrange(lim):
949 for x in xrange(lim):
950 try:
950 try:
951 y = it.next()
951 y = it.next()
952 if y in ss:
952 if y in ss:
953 bs.append(y)
953 bs.append(y)
954 except (StopIteration):
954 except (StopIteration):
955 break
955 break
956 return bs
956 return bs
957
957
958 def last(repo, subset, x):
958 def last(repo, subset, x):
959 """``last(set, [n])``
959 """``last(set, [n])``
960 Last n members of set, defaulting to 1.
960 Last n members of set, defaulting to 1.
961 """
961 """
962 # i18n: "last" is a keyword
962 # i18n: "last" is a keyword
963 l = getargs(x, 1, 2, _("last requires one or two arguments"))
963 l = getargs(x, 1, 2, _("last requires one or two arguments"))
964 try:
964 try:
965 lim = 1
965 lim = 1
966 if len(l) == 2:
966 if len(l) == 2:
967 # i18n: "last" is a keyword
967 # i18n: "last" is a keyword
968 lim = int(getstring(l[1], _("last requires a number")))
968 lim = int(getstring(l[1], _("last requires a number")))
969 except (TypeError, ValueError):
969 except (TypeError, ValueError):
970 # i18n: "last" is a keyword
970 # i18n: "last" is a keyword
971 raise error.ParseError(_("last expects a number"))
971 raise error.ParseError(_("last expects a number"))
972 ss = subset.set()
972 ss = subset.set()
973 os = getset(repo, baseset(repo), l[0])[-lim:]
973 os = getset(repo, baseset(repo), l[0])[-lim:]
974 return baseset([r for r in os if r in ss])
974 return baseset([r for r in os if r in ss])
975
975
976 def maxrev(repo, subset, x):
976 def maxrev(repo, subset, x):
977 """``max(set)``
977 """``max(set)``
978 Changeset with highest revision number in set.
978 Changeset with highest revision number in set.
979 """
979 """
980 os = getset(repo, baseset(repo), x)
980 os = getset(repo, baseset(repo), x)
981 if os:
981 if os:
982 m = max(os)
982 m = max(os)
983 if m in subset:
983 if m in subset:
984 return baseset([m])
984 return baseset([m])
985 return baseset([])
985 return baseset([])
986
986
987 def merge(repo, subset, x):
987 def merge(repo, subset, x):
988 """``merge()``
988 """``merge()``
989 Changeset is a merge changeset.
989 Changeset is a merge changeset.
990 """
990 """
991 # i18n: "merge" is a keyword
991 # i18n: "merge" is a keyword
992 getargs(x, 0, 0, _("merge takes no arguments"))
992 getargs(x, 0, 0, _("merge takes no arguments"))
993 cl = repo.changelog
993 cl = repo.changelog
994 return lazyset(subset, lambda r: cl.parentrevs(r)[1] != -1)
994 return lazyset(subset, lambda r: cl.parentrevs(r)[1] != -1)
995
995
996 def branchpoint(repo, subset, x):
996 def branchpoint(repo, subset, x):
997 """``branchpoint()``
997 """``branchpoint()``
998 Changesets with more than one child.
998 Changesets with more than one child.
999 """
999 """
1000 # i18n: "branchpoint" is a keyword
1000 # i18n: "branchpoint" is a keyword
1001 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1001 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1002 cl = repo.changelog
1002 cl = repo.changelog
1003 if not subset:
1003 if not subset:
1004 return baseset([])
1004 return baseset([])
1005 baserev = min(subset)
1005 baserev = min(subset)
1006 parentscount = [0]*(len(repo) - baserev)
1006 parentscount = [0]*(len(repo) - baserev)
1007 for r in cl.revs(start=baserev + 1):
1007 for r in cl.revs(start=baserev + 1):
1008 for p in cl.parentrevs(r):
1008 for p in cl.parentrevs(r):
1009 if p >= baserev:
1009 if p >= baserev:
1010 parentscount[p - baserev] += 1
1010 parentscount[p - baserev] += 1
1011 return baseset([r for r in subset if (parentscount[r - baserev] > 1)])
1011 return baseset([r for r in subset if (parentscount[r - baserev] > 1)])
1012
1012
1013 def minrev(repo, subset, x):
1013 def minrev(repo, subset, x):
1014 """``min(set)``
1014 """``min(set)``
1015 Changeset with lowest revision number in set.
1015 Changeset with lowest revision number in set.
1016 """
1016 """
1017 os = getset(repo, baseset(repo), x)
1017 os = getset(repo, baseset(repo), x)
1018 if os:
1018 if os:
1019 m = min(os)
1019 m = min(os)
1020 if m in subset:
1020 if m in subset:
1021 return baseset([m])
1021 return baseset([m])
1022 return baseset([])
1022 return baseset([])
1023
1023
1024 def _missingancestors(repo, subset, x):
1024 def _missingancestors(repo, subset, x):
1025 # i18n: "_missingancestors" is a keyword
1025 # i18n: "_missingancestors" is a keyword
1026 revs, bases = getargs(x, 2, 2,
1026 revs, bases = getargs(x, 2, 2,
1027 _("_missingancestors requires two arguments"))
1027 _("_missingancestors requires two arguments"))
1028 rs = baseset(repo)
1028 rs = baseset(repo)
1029 revs = getset(repo, rs, revs)
1029 revs = getset(repo, rs, revs)
1030 bases = getset(repo, rs, bases)
1030 bases = getset(repo, rs, bases)
1031 missing = set(repo.changelog.findmissingrevs(bases, revs))
1031 missing = set(repo.changelog.findmissingrevs(bases, revs))
1032 return baseset([r for r in subset if r in missing])
1032 return baseset([r for r in subset if r in missing])
1033
1033
1034 def modifies(repo, subset, x):
1034 def modifies(repo, subset, x):
1035 """``modifies(pattern)``
1035 """``modifies(pattern)``
1036 Changesets modifying files matched by pattern.
1036 Changesets modifying files matched by pattern.
1037
1037
1038 The pattern without explicit kind like ``glob:`` is expected to be
1038 The pattern without explicit kind like ``glob:`` is expected to be
1039 relative to the current directory and match against a file or a
1039 relative to the current directory and match against a file or a
1040 directory.
1040 directory.
1041 """
1041 """
1042 # i18n: "modifies" is a keyword
1042 # i18n: "modifies" is a keyword
1043 pat = getstring(x, _("modifies requires a pattern"))
1043 pat = getstring(x, _("modifies requires a pattern"))
1044 return checkstatus(repo, subset, pat, 0)
1044 return checkstatus(repo, subset, pat, 0)
1045
1045
1046 def node_(repo, subset, x):
1046 def node_(repo, subset, x):
1047 """``id(string)``
1047 """``id(string)``
1048 Revision non-ambiguously specified by the given hex string prefix.
1048 Revision non-ambiguously specified by the given hex string prefix.
1049 """
1049 """
1050 # i18n: "id" is a keyword
1050 # i18n: "id" is a keyword
1051 l = getargs(x, 1, 1, _("id requires one argument"))
1051 l = getargs(x, 1, 1, _("id requires one argument"))
1052 # i18n: "id" is a keyword
1052 # i18n: "id" is a keyword
1053 n = getstring(l[0], _("id requires a string"))
1053 n = getstring(l[0], _("id requires a string"))
1054 if len(n) == 40:
1054 if len(n) == 40:
1055 rn = repo[n].rev()
1055 rn = repo[n].rev()
1056 else:
1056 else:
1057 rn = None
1057 rn = None
1058 pm = repo.changelog._partialmatch(n)
1058 pm = repo.changelog._partialmatch(n)
1059 if pm is not None:
1059 if pm is not None:
1060 rn = repo.changelog.rev(pm)
1060 rn = repo.changelog.rev(pm)
1061
1061
1062 return baseset([r for r in subset if r == rn])
1062 return baseset([r for r in subset if r == rn])
1063
1063
1064 def obsolete(repo, subset, x):
1064 def obsolete(repo, subset, x):
1065 """``obsolete()``
1065 """``obsolete()``
1066 Mutable changeset with a newer version."""
1066 Mutable changeset with a newer version."""
1067 # i18n: "obsolete" is a keyword
1067 # i18n: "obsolete" is a keyword
1068 getargs(x, 0, 0, _("obsolete takes no arguments"))
1068 getargs(x, 0, 0, _("obsolete takes no arguments"))
1069 obsoletes = obsmod.getrevs(repo, 'obsolete')
1069 obsoletes = obsmod.getrevs(repo, 'obsolete')
1070 return subset & obsoletes
1070 return subset & obsoletes
1071
1071
1072 def origin(repo, subset, x):
1072 def origin(repo, subset, x):
1073 """``origin([set])``
1073 """``origin([set])``
1074 Changesets that were specified as a source for the grafts, transplants or
1074 Changesets that were specified as a source for the grafts, transplants or
1075 rebases that created the given revisions. Omitting the optional set is the
1075 rebases that created the given revisions. Omitting the optional set is the
1076 same as passing all(). If a changeset created by these operations is itself
1076 same as passing all(). If a changeset created by these operations is itself
1077 specified as a source for one of these operations, only the source changeset
1077 specified as a source for one of these operations, only the source changeset
1078 for the first operation is selected.
1078 for the first operation is selected.
1079 """
1079 """
1080 if x is not None:
1080 if x is not None:
1081 args = getset(repo, baseset(repo), x).set()
1081 args = getset(repo, baseset(repo), x).set()
1082 else:
1082 else:
1083 args = getall(repo, baseset(repo), x).set()
1083 args = getall(repo, baseset(repo), x).set()
1084
1084
1085 def _firstsrc(rev):
1085 def _firstsrc(rev):
1086 src = _getrevsource(repo, rev)
1086 src = _getrevsource(repo, rev)
1087 if src is None:
1087 if src is None:
1088 return None
1088 return None
1089
1089
1090 while True:
1090 while True:
1091 prev = _getrevsource(repo, src)
1091 prev = _getrevsource(repo, src)
1092
1092
1093 if prev is None:
1093 if prev is None:
1094 return src
1094 return src
1095 src = prev
1095 src = prev
1096
1096
1097 o = set([_firstsrc(r) for r in args])
1097 o = set([_firstsrc(r) for r in args])
1098 return baseset([r for r in subset if r in o])
1098 return baseset([r for r in subset if r in o])
1099
1099
1100 def outgoing(repo, subset, x):
1100 def outgoing(repo, subset, x):
1101 """``outgoing([path])``
1101 """``outgoing([path])``
1102 Changesets not found in the specified destination repository, or the
1102 Changesets not found in the specified destination repository, or the
1103 default push location.
1103 default push location.
1104 """
1104 """
1105 import hg # avoid start-up nasties
1105 import hg # avoid start-up nasties
1106 # i18n: "outgoing" is a keyword
1106 # i18n: "outgoing" is a keyword
1107 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1107 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1108 # i18n: "outgoing" is a keyword
1108 # i18n: "outgoing" is a keyword
1109 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1109 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1110 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1110 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1111 dest, branches = hg.parseurl(dest)
1111 dest, branches = hg.parseurl(dest)
1112 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1112 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1113 if revs:
1113 if revs:
1114 revs = [repo.lookup(rev) for rev in revs]
1114 revs = [repo.lookup(rev) for rev in revs]
1115 other = hg.peer(repo, {}, dest)
1115 other = hg.peer(repo, {}, dest)
1116 repo.ui.pushbuffer()
1116 repo.ui.pushbuffer()
1117 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1117 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1118 repo.ui.popbuffer()
1118 repo.ui.popbuffer()
1119 cl = repo.changelog
1119 cl = repo.changelog
1120 o = set([cl.rev(r) for r in outgoing.missing])
1120 o = set([cl.rev(r) for r in outgoing.missing])
1121 return baseset([r for r in subset if r in o])
1121 return baseset([r for r in subset if r in o])
1122
1122
1123 def p1(repo, subset, x):
1123 def p1(repo, subset, x):
1124 """``p1([set])``
1124 """``p1([set])``
1125 First parent of changesets in set, or the working directory.
1125 First parent of changesets in set, or the working directory.
1126 """
1126 """
1127 if x is None:
1127 if x is None:
1128 p = repo[x].p1().rev()
1128 p = repo[x].p1().rev()
1129 return baseset([r for r in subset if r == p])
1129 return baseset([r for r in subset if r == p])
1130
1130
1131 ps = set()
1131 ps = set()
1132 cl = repo.changelog
1132 cl = repo.changelog
1133 for r in getset(repo, baseset(repo), x):
1133 for r in getset(repo, baseset(repo), x):
1134 ps.add(cl.parentrevs(r)[0])
1134 ps.add(cl.parentrevs(r)[0])
1135 return subset & ps
1135 return subset & ps
1136
1136
1137 def p2(repo, subset, x):
1137 def p2(repo, subset, x):
1138 """``p2([set])``
1138 """``p2([set])``
1139 Second parent of changesets in set, or the working directory.
1139 Second parent of changesets in set, or the working directory.
1140 """
1140 """
1141 if x is None:
1141 if x is None:
1142 ps = repo[x].parents()
1142 ps = repo[x].parents()
1143 try:
1143 try:
1144 p = ps[1].rev()
1144 p = ps[1].rev()
1145 return baseset([r for r in subset if r == p])
1145 return baseset([r for r in subset if r == p])
1146 except IndexError:
1146 except IndexError:
1147 return baseset([])
1147 return baseset([])
1148
1148
1149 ps = set()
1149 ps = set()
1150 cl = repo.changelog
1150 cl = repo.changelog
1151 for r in getset(repo, baseset(repo), x):
1151 for r in getset(repo, baseset(repo), x):
1152 ps.add(cl.parentrevs(r)[1])
1152 ps.add(cl.parentrevs(r)[1])
1153 return subset & ps
1153 return subset & ps
1154
1154
1155 def parents(repo, subset, x):
1155 def parents(repo, subset, x):
1156 """``parents([set])``
1156 """``parents([set])``
1157 The set of all parents for all changesets in set, or the working directory.
1157 The set of all parents for all changesets in set, or the working directory.
1158 """
1158 """
1159 if x is None:
1159 if x is None:
1160 ps = tuple(p.rev() for p in repo[x].parents())
1160 ps = tuple(p.rev() for p in repo[x].parents())
1161 return subset & ps
1161 return subset & ps
1162
1162
1163 ps = set()
1163 ps = set()
1164 cl = repo.changelog
1164 cl = repo.changelog
1165 for r in getset(repo, baseset(repo), x):
1165 for r in getset(repo, baseset(repo), x):
1166 ps.update(cl.parentrevs(r))
1166 ps.update(cl.parentrevs(r))
1167 return subset & ps
1167 return subset & ps
1168
1168
1169 def parentspec(repo, subset, x, n):
1169 def parentspec(repo, subset, x, n):
1170 """``set^0``
1170 """``set^0``
1171 The set.
1171 The set.
1172 ``set^1`` (or ``set^``), ``set^2``
1172 ``set^1`` (or ``set^``), ``set^2``
1173 First or second parent, respectively, of all changesets in set.
1173 First or second parent, respectively, of all changesets in set.
1174 """
1174 """
1175 try:
1175 try:
1176 n = int(n[1])
1176 n = int(n[1])
1177 if n not in (0, 1, 2):
1177 if n not in (0, 1, 2):
1178 raise ValueError
1178 raise ValueError
1179 except (TypeError, ValueError):
1179 except (TypeError, ValueError):
1180 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1180 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1181 ps = set()
1181 ps = set()
1182 cl = repo.changelog
1182 cl = repo.changelog
1183 for r in getset(repo, baseset(cl), x):
1183 for r in getset(repo, baseset(cl), x):
1184 if n == 0:
1184 if n == 0:
1185 ps.add(r)
1185 ps.add(r)
1186 elif n == 1:
1186 elif n == 1:
1187 ps.add(cl.parentrevs(r)[0])
1187 ps.add(cl.parentrevs(r)[0])
1188 elif n == 2:
1188 elif n == 2:
1189 parents = cl.parentrevs(r)
1189 parents = cl.parentrevs(r)
1190 if len(parents) > 1:
1190 if len(parents) > 1:
1191 ps.add(parents[1])
1191 ps.add(parents[1])
1192 return subset & ps
1192 return subset & ps
1193
1193
1194 def present(repo, subset, x):
1194 def present(repo, subset, x):
1195 """``present(set)``
1195 """``present(set)``
1196 An empty set, if any revision in set isn't found; otherwise,
1196 An empty set, if any revision in set isn't found; otherwise,
1197 all revisions in set.
1197 all revisions in set.
1198
1198
1199 If any of specified revisions is not present in the local repository,
1199 If any of specified revisions is not present in the local repository,
1200 the query is normally aborted. But this predicate allows the query
1200 the query is normally aborted. But this predicate allows the query
1201 to continue even in such cases.
1201 to continue even in such cases.
1202 """
1202 """
1203 try:
1203 try:
1204 return getset(repo, subset, x)
1204 return getset(repo, subset, x)
1205 except error.RepoLookupError:
1205 except error.RepoLookupError:
1206 return baseset([])
1206 return baseset([])
1207
1207
1208 def public(repo, subset, x):
1208 def public(repo, subset, x):
1209 """``public()``
1209 """``public()``
1210 Changeset in public phase."""
1210 Changeset in public phase."""
1211 # i18n: "public" is a keyword
1211 # i18n: "public" is a keyword
1212 getargs(x, 0, 0, _("public takes no arguments"))
1212 getargs(x, 0, 0, _("public takes no arguments"))
1213 pc = repo._phasecache
1213 pc = repo._phasecache
1214 return lazyset(subset, lambda r: pc.phase(repo, r) == phases.public)
1214 return lazyset(subset, lambda r: pc.phase(repo, r) == phases.public)
1215
1215
1216 def remote(repo, subset, x):
1216 def remote(repo, subset, x):
1217 """``remote([id [,path]])``
1217 """``remote([id [,path]])``
1218 Local revision that corresponds to the given identifier in a
1218 Local revision that corresponds to the given identifier in a
1219 remote repository, if present. Here, the '.' identifier is a
1219 remote repository, if present. Here, the '.' identifier is a
1220 synonym for the current local branch.
1220 synonym for the current local branch.
1221 """
1221 """
1222
1222
1223 import hg # avoid start-up nasties
1223 import hg # avoid start-up nasties
1224 # i18n: "remote" is a keyword
1224 # i18n: "remote" is a keyword
1225 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1225 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1226
1226
1227 q = '.'
1227 q = '.'
1228 if len(l) > 0:
1228 if len(l) > 0:
1229 # i18n: "remote" is a keyword
1229 # i18n: "remote" is a keyword
1230 q = getstring(l[0], _("remote requires a string id"))
1230 q = getstring(l[0], _("remote requires a string id"))
1231 if q == '.':
1231 if q == '.':
1232 q = repo['.'].branch()
1232 q = repo['.'].branch()
1233
1233
1234 dest = ''
1234 dest = ''
1235 if len(l) > 1:
1235 if len(l) > 1:
1236 # i18n: "remote" is a keyword
1236 # i18n: "remote" is a keyword
1237 dest = getstring(l[1], _("remote requires a repository path"))
1237 dest = getstring(l[1], _("remote requires a repository path"))
1238 dest = repo.ui.expandpath(dest or 'default')
1238 dest = repo.ui.expandpath(dest or 'default')
1239 dest, branches = hg.parseurl(dest)
1239 dest, branches = hg.parseurl(dest)
1240 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1240 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1241 if revs:
1241 if revs:
1242 revs = [repo.lookup(rev) for rev in revs]
1242 revs = [repo.lookup(rev) for rev in revs]
1243 other = hg.peer(repo, {}, dest)
1243 other = hg.peer(repo, {}, dest)
1244 n = other.lookup(q)
1244 n = other.lookup(q)
1245 if n in repo:
1245 if n in repo:
1246 r = repo[n].rev()
1246 r = repo[n].rev()
1247 if r in subset:
1247 if r in subset:
1248 return baseset([r])
1248 return baseset([r])
1249 return baseset([])
1249 return baseset([])
1250
1250
1251 def removes(repo, subset, x):
1251 def removes(repo, subset, x):
1252 """``removes(pattern)``
1252 """``removes(pattern)``
1253 Changesets which remove files matching pattern.
1253 Changesets which remove files matching pattern.
1254
1254
1255 The pattern without explicit kind like ``glob:`` is expected to be
1255 The pattern without explicit kind like ``glob:`` is expected to be
1256 relative to the current directory and match against a file or a
1256 relative to the current directory and match against a file or a
1257 directory.
1257 directory.
1258 """
1258 """
1259 # i18n: "removes" is a keyword
1259 # i18n: "removes" is a keyword
1260 pat = getstring(x, _("removes requires a pattern"))
1260 pat = getstring(x, _("removes requires a pattern"))
1261 return checkstatus(repo, subset, pat, 2)
1261 return checkstatus(repo, subset, pat, 2)
1262
1262
1263 def rev(repo, subset, x):
1263 def rev(repo, subset, x):
1264 """``rev(number)``
1264 """``rev(number)``
1265 Revision with the given numeric identifier.
1265 Revision with the given numeric identifier.
1266 """
1266 """
1267 # i18n: "rev" is a keyword
1267 # i18n: "rev" is a keyword
1268 l = getargs(x, 1, 1, _("rev requires one argument"))
1268 l = getargs(x, 1, 1, _("rev requires one argument"))
1269 try:
1269 try:
1270 # i18n: "rev" is a keyword
1270 # i18n: "rev" is a keyword
1271 l = int(getstring(l[0], _("rev requires a number")))
1271 l = int(getstring(l[0], _("rev requires a number")))
1272 except (TypeError, ValueError):
1272 except (TypeError, ValueError):
1273 # i18n: "rev" is a keyword
1273 # i18n: "rev" is a keyword
1274 raise error.ParseError(_("rev expects a number"))
1274 raise error.ParseError(_("rev expects a number"))
1275 return baseset([r for r in subset if r == l])
1275 return baseset([r for r in subset if r == l])
1276
1276
1277 def matching(repo, subset, x):
1277 def matching(repo, subset, x):
1278 """``matching(revision [, field])``
1278 """``matching(revision [, field])``
1279 Changesets in which a given set of fields match the set of fields in the
1279 Changesets in which a given set of fields match the set of fields in the
1280 selected revision or set.
1280 selected revision or set.
1281
1281
1282 To match more than one field pass the list of fields to match separated
1282 To match more than one field pass the list of fields to match separated
1283 by spaces (e.g. ``author description``).
1283 by spaces (e.g. ``author description``).
1284
1284
1285 Valid fields are most regular revision fields and some special fields.
1285 Valid fields are most regular revision fields and some special fields.
1286
1286
1287 Regular revision fields are ``description``, ``author``, ``branch``,
1287 Regular revision fields are ``description``, ``author``, ``branch``,
1288 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1288 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1289 and ``diff``.
1289 and ``diff``.
1290 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1290 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1291 contents of the revision. Two revisions matching their ``diff`` will
1291 contents of the revision. Two revisions matching their ``diff`` will
1292 also match their ``files``.
1292 also match their ``files``.
1293
1293
1294 Special fields are ``summary`` and ``metadata``:
1294 Special fields are ``summary`` and ``metadata``:
1295 ``summary`` matches the first line of the description.
1295 ``summary`` matches the first line of the description.
1296 ``metadata`` is equivalent to matching ``description user date``
1296 ``metadata`` is equivalent to matching ``description user date``
1297 (i.e. it matches the main metadata fields).
1297 (i.e. it matches the main metadata fields).
1298
1298
1299 ``metadata`` is the default field which is used when no fields are
1299 ``metadata`` is the default field which is used when no fields are
1300 specified. You can match more than one field at a time.
1300 specified. You can match more than one field at a time.
1301 """
1301 """
1302 # i18n: "matching" is a keyword
1302 # i18n: "matching" is a keyword
1303 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1303 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1304
1304
1305 revs = getset(repo, baseset(repo.changelog), l[0])
1305 revs = getset(repo, baseset(repo.changelog), l[0])
1306
1306
1307 fieldlist = ['metadata']
1307 fieldlist = ['metadata']
1308 if len(l) > 1:
1308 if len(l) > 1:
1309 fieldlist = getstring(l[1],
1309 fieldlist = getstring(l[1],
1310 # i18n: "matching" is a keyword
1310 # i18n: "matching" is a keyword
1311 _("matching requires a string "
1311 _("matching requires a string "
1312 "as its second argument")).split()
1312 "as its second argument")).split()
1313
1313
1314 # Make sure that there are no repeated fields,
1314 # Make sure that there are no repeated fields,
1315 # expand the 'special' 'metadata' field type
1315 # expand the 'special' 'metadata' field type
1316 # and check the 'files' whenever we check the 'diff'
1316 # and check the 'files' whenever we check the 'diff'
1317 fields = []
1317 fields = []
1318 for field in fieldlist:
1318 for field in fieldlist:
1319 if field == 'metadata':
1319 if field == 'metadata':
1320 fields += ['user', 'description', 'date']
1320 fields += ['user', 'description', 'date']
1321 elif field == 'diff':
1321 elif field == 'diff':
1322 # a revision matching the diff must also match the files
1322 # a revision matching the diff must also match the files
1323 # since matching the diff is very costly, make sure to
1323 # since matching the diff is very costly, make sure to
1324 # also match the files first
1324 # also match the files first
1325 fields += ['files', 'diff']
1325 fields += ['files', 'diff']
1326 else:
1326 else:
1327 if field == 'author':
1327 if field == 'author':
1328 field = 'user'
1328 field = 'user'
1329 fields.append(field)
1329 fields.append(field)
1330 fields = set(fields)
1330 fields = set(fields)
1331 if 'summary' in fields and 'description' in fields:
1331 if 'summary' in fields and 'description' in fields:
1332 # If a revision matches its description it also matches its summary
1332 # If a revision matches its description it also matches its summary
1333 fields.discard('summary')
1333 fields.discard('summary')
1334
1334
1335 # We may want to match more than one field
1335 # We may want to match more than one field
1336 # Not all fields take the same amount of time to be matched
1336 # Not all fields take the same amount of time to be matched
1337 # Sort the selected fields in order of increasing matching cost
1337 # Sort the selected fields in order of increasing matching cost
1338 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1338 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1339 'files', 'description', 'substate', 'diff']
1339 'files', 'description', 'substate', 'diff']
1340 def fieldkeyfunc(f):
1340 def fieldkeyfunc(f):
1341 try:
1341 try:
1342 return fieldorder.index(f)
1342 return fieldorder.index(f)
1343 except ValueError:
1343 except ValueError:
1344 # assume an unknown field is very costly
1344 # assume an unknown field is very costly
1345 return len(fieldorder)
1345 return len(fieldorder)
1346 fields = list(fields)
1346 fields = list(fields)
1347 fields.sort(key=fieldkeyfunc)
1347 fields.sort(key=fieldkeyfunc)
1348
1348
1349 # Each field will be matched with its own "getfield" function
1349 # Each field will be matched with its own "getfield" function
1350 # which will be added to the getfieldfuncs array of functions
1350 # which will be added to the getfieldfuncs array of functions
1351 getfieldfuncs = []
1351 getfieldfuncs = []
1352 _funcs = {
1352 _funcs = {
1353 'user': lambda r: repo[r].user(),
1353 'user': lambda r: repo[r].user(),
1354 'branch': lambda r: repo[r].branch(),
1354 'branch': lambda r: repo[r].branch(),
1355 'date': lambda r: repo[r].date(),
1355 'date': lambda r: repo[r].date(),
1356 'description': lambda r: repo[r].description(),
1356 'description': lambda r: repo[r].description(),
1357 'files': lambda r: repo[r].files(),
1357 'files': lambda r: repo[r].files(),
1358 'parents': lambda r: repo[r].parents(),
1358 'parents': lambda r: repo[r].parents(),
1359 'phase': lambda r: repo[r].phase(),
1359 'phase': lambda r: repo[r].phase(),
1360 'substate': lambda r: repo[r].substate,
1360 'substate': lambda r: repo[r].substate,
1361 'summary': lambda r: repo[r].description().splitlines()[0],
1361 'summary': lambda r: repo[r].description().splitlines()[0],
1362 'diff': lambda r: list(repo[r].diff(git=True),)
1362 'diff': lambda r: list(repo[r].diff(git=True),)
1363 }
1363 }
1364 for info in fields:
1364 for info in fields:
1365 getfield = _funcs.get(info, None)
1365 getfield = _funcs.get(info, None)
1366 if getfield is None:
1366 if getfield is None:
1367 raise error.ParseError(
1367 raise error.ParseError(
1368 # i18n: "matching" is a keyword
1368 # i18n: "matching" is a keyword
1369 _("unexpected field name passed to matching: %s") % info)
1369 _("unexpected field name passed to matching: %s") % info)
1370 getfieldfuncs.append(getfield)
1370 getfieldfuncs.append(getfield)
1371 # convert the getfield array of functions into a "getinfo" function
1371 # convert the getfield array of functions into a "getinfo" function
1372 # which returns an array of field values (or a single value if there
1372 # which returns an array of field values (or a single value if there
1373 # is only one field to match)
1373 # is only one field to match)
1374 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1374 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1375
1375
1376 def matches(x):
1376 def matches(x):
1377 for rev in revs:
1377 for rev in revs:
1378 target = getinfo(rev)
1378 target = getinfo(rev)
1379 match = True
1379 match = True
1380 for n, f in enumerate(getfieldfuncs):
1380 for n, f in enumerate(getfieldfuncs):
1381 if target[n] != f(x):
1381 if target[n] != f(x):
1382 match = False
1382 match = False
1383 if match:
1383 if match:
1384 return True
1384 return True
1385 return False
1385 return False
1386
1386
1387 return lazyset(subset, matches)
1387 return lazyset(subset, matches)
1388
1388
1389 def reverse(repo, subset, x):
1389 def reverse(repo, subset, x):
1390 """``reverse(set)``
1390 """``reverse(set)``
1391 Reverse order of set.
1391 Reverse order of set.
1392 """
1392 """
1393 l = getset(repo, subset, x)
1393 l = getset(repo, subset, x)
1394 l.reverse()
1394 l.reverse()
1395 return l
1395 return l
1396
1396
1397 def roots(repo, subset, x):
1397 def roots(repo, subset, x):
1398 """``roots(set)``
1398 """``roots(set)``
1399 Changesets in set with no parent changeset in set.
1399 Changesets in set with no parent changeset in set.
1400 """
1400 """
1401 s = getset(repo, baseset(repo.changelog), x).set()
1401 s = getset(repo, baseset(repo.changelog), x).set()
1402 subset = baseset([r for r in subset if r in s])
1402 subset = baseset([r for r in subset if r in s])
1403 cs = _children(repo, subset, s)
1403 cs = _children(repo, subset, s)
1404 return subset - cs
1404 return subset - cs
1405
1405
1406 def secret(repo, subset, x):
1406 def secret(repo, subset, x):
1407 """``secret()``
1407 """``secret()``
1408 Changeset in secret phase."""
1408 Changeset in secret phase."""
1409 # i18n: "secret" is a keyword
1409 # i18n: "secret" is a keyword
1410 getargs(x, 0, 0, _("secret takes no arguments"))
1410 getargs(x, 0, 0, _("secret takes no arguments"))
1411 pc = repo._phasecache
1411 pc = repo._phasecache
1412 return lazyset(subset, lambda x: pc.phase(repo, x) == phases.secret)
1412 return lazyset(subset, lambda x: pc.phase(repo, x) == phases.secret)
1413
1413
1414 def sort(repo, subset, x):
1414 def sort(repo, subset, x):
1415 """``sort(set[, [-]key...])``
1415 """``sort(set[, [-]key...])``
1416 Sort set by keys. The default sort order is ascending, specify a key
1416 Sort set by keys. The default sort order is ascending, specify a key
1417 as ``-key`` to sort in descending order.
1417 as ``-key`` to sort in descending order.
1418
1418
1419 The keys can be:
1419 The keys can be:
1420
1420
1421 - ``rev`` for the revision number,
1421 - ``rev`` for the revision number,
1422 - ``branch`` for the branch name,
1422 - ``branch`` for the branch name,
1423 - ``desc`` for the commit message (description),
1423 - ``desc`` for the commit message (description),
1424 - ``user`` for user name (``author`` can be used as an alias),
1424 - ``user`` for user name (``author`` can be used as an alias),
1425 - ``date`` for the commit date
1425 - ``date`` for the commit date
1426 """
1426 """
1427 # i18n: "sort" is a keyword
1427 # i18n: "sort" is a keyword
1428 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1428 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1429 keys = "rev"
1429 keys = "rev"
1430 if len(l) == 2:
1430 if len(l) == 2:
1431 # i18n: "sort" is a keyword
1431 # i18n: "sort" is a keyword
1432 keys = getstring(l[1], _("sort spec must be a string"))
1432 keys = getstring(l[1], _("sort spec must be a string"))
1433
1433
1434 s = l[0]
1434 s = l[0]
1435 keys = keys.split()
1435 keys = keys.split()
1436 l = []
1436 l = []
1437 def invert(s):
1437 def invert(s):
1438 return "".join(chr(255 - ord(c)) for c in s)
1438 return "".join(chr(255 - ord(c)) for c in s)
1439 for r in getset(repo, subset, s):
1439 for r in getset(repo, subset, s):
1440 c = repo[r]
1440 c = repo[r]
1441 e = []
1441 e = []
1442 for k in keys:
1442 for k in keys:
1443 if k == 'rev':
1443 if k == 'rev':
1444 e.append(r)
1444 e.append(r)
1445 elif k == '-rev':
1445 elif k == '-rev':
1446 e.append(-r)
1446 e.append(-r)
1447 elif k == 'branch':
1447 elif k == 'branch':
1448 e.append(c.branch())
1448 e.append(c.branch())
1449 elif k == '-branch':
1449 elif k == '-branch':
1450 e.append(invert(c.branch()))
1450 e.append(invert(c.branch()))
1451 elif k == 'desc':
1451 elif k == 'desc':
1452 e.append(c.description())
1452 e.append(c.description())
1453 elif k == '-desc':
1453 elif k == '-desc':
1454 e.append(invert(c.description()))
1454 e.append(invert(c.description()))
1455 elif k in 'user author':
1455 elif k in 'user author':
1456 e.append(c.user())
1456 e.append(c.user())
1457 elif k in '-user -author':
1457 elif k in '-user -author':
1458 e.append(invert(c.user()))
1458 e.append(invert(c.user()))
1459 elif k == 'date':
1459 elif k == 'date':
1460 e.append(c.date()[0])
1460 e.append(c.date()[0])
1461 elif k == '-date':
1461 elif k == '-date':
1462 e.append(-c.date()[0])
1462 e.append(-c.date()[0])
1463 else:
1463 else:
1464 raise error.ParseError(_("unknown sort key %r") % k)
1464 raise error.ParseError(_("unknown sort key %r") % k)
1465 e.append(r)
1465 e.append(r)
1466 l.append(e)
1466 l.append(e)
1467 l.sort()
1467 l.sort()
1468 return baseset([e[-1] for e in l])
1468 return baseset([e[-1] for e in l])
1469
1469
1470 def _stringmatcher(pattern):
1470 def _stringmatcher(pattern):
1471 """
1471 """
1472 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1472 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1473 returns the matcher name, pattern, and matcher function.
1473 returns the matcher name, pattern, and matcher function.
1474 missing or unknown prefixes are treated as literal matches.
1474 missing or unknown prefixes are treated as literal matches.
1475
1475
1476 helper for tests:
1476 helper for tests:
1477 >>> def test(pattern, *tests):
1477 >>> def test(pattern, *tests):
1478 ... kind, pattern, matcher = _stringmatcher(pattern)
1478 ... kind, pattern, matcher = _stringmatcher(pattern)
1479 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1479 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1480
1480
1481 exact matching (no prefix):
1481 exact matching (no prefix):
1482 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1482 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1483 ('literal', 'abcdefg', [False, False, True])
1483 ('literal', 'abcdefg', [False, False, True])
1484
1484
1485 regex matching ('re:' prefix)
1485 regex matching ('re:' prefix)
1486 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1486 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1487 ('re', 'a.+b', [False, False, True])
1487 ('re', 'a.+b', [False, False, True])
1488
1488
1489 force exact matches ('literal:' prefix)
1489 force exact matches ('literal:' prefix)
1490 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1490 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1491 ('literal', 're:foobar', [False, True])
1491 ('literal', 're:foobar', [False, True])
1492
1492
1493 unknown prefixes are ignored and treated as literals
1493 unknown prefixes are ignored and treated as literals
1494 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1494 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1495 ('literal', 'foo:bar', [False, False, True])
1495 ('literal', 'foo:bar', [False, False, True])
1496 """
1496 """
1497 if pattern.startswith('re:'):
1497 if pattern.startswith('re:'):
1498 pattern = pattern[3:]
1498 pattern = pattern[3:]
1499 try:
1499 try:
1500 regex = re.compile(pattern)
1500 regex = re.compile(pattern)
1501 except re.error, e:
1501 except re.error, e:
1502 raise error.ParseError(_('invalid regular expression: %s')
1502 raise error.ParseError(_('invalid regular expression: %s')
1503 % e)
1503 % e)
1504 return 're', pattern, regex.search
1504 return 're', pattern, regex.search
1505 elif pattern.startswith('literal:'):
1505 elif pattern.startswith('literal:'):
1506 pattern = pattern[8:]
1506 pattern = pattern[8:]
1507 return 'literal', pattern, pattern.__eq__
1507 return 'literal', pattern, pattern.__eq__
1508
1508
1509 def _substringmatcher(pattern):
1509 def _substringmatcher(pattern):
1510 kind, pattern, matcher = _stringmatcher(pattern)
1510 kind, pattern, matcher = _stringmatcher(pattern)
1511 if kind == 'literal':
1511 if kind == 'literal':
1512 matcher = lambda s: pattern in s
1512 matcher = lambda s: pattern in s
1513 return kind, pattern, matcher
1513 return kind, pattern, matcher
1514
1514
1515 def tag(repo, subset, x):
1515 def tag(repo, subset, x):
1516 """``tag([name])``
1516 """``tag([name])``
1517 The specified tag by name, or all tagged revisions if no name is given.
1517 The specified tag by name, or all tagged revisions if no name is given.
1518 """
1518 """
1519 # i18n: "tag" is a keyword
1519 # i18n: "tag" is a keyword
1520 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1520 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1521 cl = repo.changelog
1521 cl = repo.changelog
1522 if args:
1522 if args:
1523 pattern = getstring(args[0],
1523 pattern = getstring(args[0],
1524 # i18n: "tag" is a keyword
1524 # i18n: "tag" is a keyword
1525 _('the argument to tag must be a string'))
1525 _('the argument to tag must be a string'))
1526 kind, pattern, matcher = _stringmatcher(pattern)
1526 kind, pattern, matcher = _stringmatcher(pattern)
1527 if kind == 'literal':
1527 if kind == 'literal':
1528 # avoid resolving all tags
1528 # avoid resolving all tags
1529 tn = repo._tagscache.tags.get(pattern, None)
1529 tn = repo._tagscache.tags.get(pattern, None)
1530 if tn is None:
1530 if tn is None:
1531 raise util.Abort(_("tag '%s' does not exist") % pattern)
1531 raise util.Abort(_("tag '%s' does not exist") % pattern)
1532 s = set([repo[tn].rev()])
1532 s = set([repo[tn].rev()])
1533 else:
1533 else:
1534 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1534 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1535 else:
1535 else:
1536 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1536 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1537 return subset & s
1537 return subset & s
1538
1538
1539 def tagged(repo, subset, x):
1539 def tagged(repo, subset, x):
1540 return tag(repo, subset, x)
1540 return tag(repo, subset, x)
1541
1541
1542 def unstable(repo, subset, x):
1542 def unstable(repo, subset, x):
1543 """``unstable()``
1543 """``unstable()``
1544 Non-obsolete changesets with obsolete ancestors.
1544 Non-obsolete changesets with obsolete ancestors.
1545 """
1545 """
1546 # i18n: "unstable" is a keyword
1546 # i18n: "unstable" is a keyword
1547 getargs(x, 0, 0, _("unstable takes no arguments"))
1547 getargs(x, 0, 0, _("unstable takes no arguments"))
1548 unstables = obsmod.getrevs(repo, 'unstable')
1548 unstables = obsmod.getrevs(repo, 'unstable')
1549 return subset & unstables
1549 return subset & unstables
1550
1550
1551
1551
1552 def user(repo, subset, x):
1552 def user(repo, subset, x):
1553 """``user(string)``
1553 """``user(string)``
1554 User name contains string. The match is case-insensitive.
1554 User name contains string. The match is case-insensitive.
1555
1555
1556 If `string` starts with `re:`, the remainder of the string is treated as
1556 If `string` starts with `re:`, the remainder of the string is treated as
1557 a regular expression. To match a user that actually contains `re:`, use
1557 a regular expression. To match a user that actually contains `re:`, use
1558 the prefix `literal:`.
1558 the prefix `literal:`.
1559 """
1559 """
1560 return author(repo, subset, x)
1560 return author(repo, subset, x)
1561
1561
1562 # for internal use
1562 # for internal use
1563 def _list(repo, subset, x):
1563 def _list(repo, subset, x):
1564 s = getstring(x, "internal error")
1564 s = getstring(x, "internal error")
1565 if not s:
1565 if not s:
1566 return baseset([])
1566 return baseset([])
1567 ls = [repo[r].rev() for r in s.split('\0')]
1567 ls = [repo[r].rev() for r in s.split('\0')]
1568 s = subset.set()
1568 s = subset.set()
1569 return baseset([r for r in ls if r in s])
1569 return baseset([r for r in ls if r in s])
1570
1570
1571 symbols = {
1571 symbols = {
1572 "adds": adds,
1572 "adds": adds,
1573 "all": getall,
1573 "all": getall,
1574 "ancestor": ancestor,
1574 "ancestor": ancestor,
1575 "ancestors": ancestors,
1575 "ancestors": ancestors,
1576 "_firstancestors": _firstancestors,
1576 "_firstancestors": _firstancestors,
1577 "author": author,
1577 "author": author,
1578 "bisect": bisect,
1578 "bisect": bisect,
1579 "bisected": bisected,
1579 "bisected": bisected,
1580 "bookmark": bookmark,
1580 "bookmark": bookmark,
1581 "branch": branch,
1581 "branch": branch,
1582 "branchpoint": branchpoint,
1582 "branchpoint": branchpoint,
1583 "bumped": bumped,
1583 "bumped": bumped,
1584 "bundle": bundle,
1584 "bundle": bundle,
1585 "children": children,
1585 "children": children,
1586 "closed": closed,
1586 "closed": closed,
1587 "contains": contains,
1587 "contains": contains,
1588 "converted": converted,
1588 "converted": converted,
1589 "date": date,
1589 "date": date,
1590 "desc": desc,
1590 "desc": desc,
1591 "descendants": descendants,
1591 "descendants": descendants,
1592 "_firstdescendants": _firstdescendants,
1592 "_firstdescendants": _firstdescendants,
1593 "destination": destination,
1593 "destination": destination,
1594 "divergent": divergent,
1594 "divergent": divergent,
1595 "draft": draft,
1595 "draft": draft,
1596 "extinct": extinct,
1596 "extinct": extinct,
1597 "extra": extra,
1597 "extra": extra,
1598 "file": hasfile,
1598 "file": hasfile,
1599 "filelog": filelog,
1599 "filelog": filelog,
1600 "first": first,
1600 "first": first,
1601 "follow": follow,
1601 "follow": follow,
1602 "_followfirst": _followfirst,
1602 "_followfirst": _followfirst,
1603 "grep": grep,
1603 "grep": grep,
1604 "head": head,
1604 "head": head,
1605 "heads": heads,
1605 "heads": heads,
1606 "hidden": hidden,
1606 "hidden": hidden,
1607 "id": node_,
1607 "id": node_,
1608 "keyword": keyword,
1608 "keyword": keyword,
1609 "last": last,
1609 "last": last,
1610 "limit": limit,
1610 "limit": limit,
1611 "_matchfiles": _matchfiles,
1611 "_matchfiles": _matchfiles,
1612 "max": maxrev,
1612 "max": maxrev,
1613 "merge": merge,
1613 "merge": merge,
1614 "min": minrev,
1614 "min": minrev,
1615 "_missingancestors": _missingancestors,
1615 "_missingancestors": _missingancestors,
1616 "modifies": modifies,
1616 "modifies": modifies,
1617 "obsolete": obsolete,
1617 "obsolete": obsolete,
1618 "origin": origin,
1618 "origin": origin,
1619 "outgoing": outgoing,
1619 "outgoing": outgoing,
1620 "p1": p1,
1620 "p1": p1,
1621 "p2": p2,
1621 "p2": p2,
1622 "parents": parents,
1622 "parents": parents,
1623 "present": present,
1623 "present": present,
1624 "public": public,
1624 "public": public,
1625 "remote": remote,
1625 "remote": remote,
1626 "removes": removes,
1626 "removes": removes,
1627 "rev": rev,
1627 "rev": rev,
1628 "reverse": reverse,
1628 "reverse": reverse,
1629 "roots": roots,
1629 "roots": roots,
1630 "sort": sort,
1630 "sort": sort,
1631 "secret": secret,
1631 "secret": secret,
1632 "matching": matching,
1632 "matching": matching,
1633 "tag": tag,
1633 "tag": tag,
1634 "tagged": tagged,
1634 "tagged": tagged,
1635 "user": user,
1635 "user": user,
1636 "unstable": unstable,
1636 "unstable": unstable,
1637 "_list": _list,
1637 "_list": _list,
1638 }
1638 }
1639
1639
1640 # symbols which can't be used for a DoS attack for any given input
1640 # symbols which can't be used for a DoS attack for any given input
1641 # (e.g. those which accept regexes as plain strings shouldn't be included)
1641 # (e.g. those which accept regexes as plain strings shouldn't be included)
1642 # functions that just return a lot of changesets (like all) don't count here
1642 # functions that just return a lot of changesets (like all) don't count here
1643 safesymbols = set([
1643 safesymbols = set([
1644 "adds",
1644 "adds",
1645 "all",
1645 "all",
1646 "ancestor",
1646 "ancestor",
1647 "ancestors",
1647 "ancestors",
1648 "_firstancestors",
1648 "_firstancestors",
1649 "author",
1649 "author",
1650 "bisect",
1650 "bisect",
1651 "bisected",
1651 "bisected",
1652 "bookmark",
1652 "bookmark",
1653 "branch",
1653 "branch",
1654 "branchpoint",
1654 "branchpoint",
1655 "bumped",
1655 "bumped",
1656 "bundle",
1656 "bundle",
1657 "children",
1657 "children",
1658 "closed",
1658 "closed",
1659 "converted",
1659 "converted",
1660 "date",
1660 "date",
1661 "desc",
1661 "desc",
1662 "descendants",
1662 "descendants",
1663 "_firstdescendants",
1663 "_firstdescendants",
1664 "destination",
1664 "destination",
1665 "divergent",
1665 "divergent",
1666 "draft",
1666 "draft",
1667 "extinct",
1667 "extinct",
1668 "extra",
1668 "extra",
1669 "file",
1669 "file",
1670 "filelog",
1670 "filelog",
1671 "first",
1671 "first",
1672 "follow",
1672 "follow",
1673 "_followfirst",
1673 "_followfirst",
1674 "head",
1674 "head",
1675 "heads",
1675 "heads",
1676 "hidden",
1676 "hidden",
1677 "id",
1677 "id",
1678 "keyword",
1678 "keyword",
1679 "last",
1679 "last",
1680 "limit",
1680 "limit",
1681 "_matchfiles",
1681 "_matchfiles",
1682 "max",
1682 "max",
1683 "merge",
1683 "merge",
1684 "min",
1684 "min",
1685 "_missingancestors",
1685 "_missingancestors",
1686 "modifies",
1686 "modifies",
1687 "obsolete",
1687 "obsolete",
1688 "origin",
1688 "origin",
1689 "outgoing",
1689 "outgoing",
1690 "p1",
1690 "p1",
1691 "p2",
1691 "p2",
1692 "parents",
1692 "parents",
1693 "present",
1693 "present",
1694 "public",
1694 "public",
1695 "remote",
1695 "remote",
1696 "removes",
1696 "removes",
1697 "rev",
1697 "rev",
1698 "reverse",
1698 "reverse",
1699 "roots",
1699 "roots",
1700 "sort",
1700 "sort",
1701 "secret",
1701 "secret",
1702 "matching",
1702 "matching",
1703 "tag",
1703 "tag",
1704 "tagged",
1704 "tagged",
1705 "user",
1705 "user",
1706 "unstable",
1706 "unstable",
1707 "_list",
1707 "_list",
1708 ])
1708 ])
1709
1709
1710 methods = {
1710 methods = {
1711 "range": rangeset,
1711 "range": rangeset,
1712 "dagrange": dagrange,
1712 "dagrange": dagrange,
1713 "string": stringset,
1713 "string": stringset,
1714 "symbol": symbolset,
1714 "symbol": symbolset,
1715 "and": andset,
1715 "and": andset,
1716 "or": orset,
1716 "or": orset,
1717 "not": notset,
1717 "not": notset,
1718 "list": listset,
1718 "list": listset,
1719 "func": func,
1719 "func": func,
1720 "ancestor": ancestorspec,
1720 "ancestor": ancestorspec,
1721 "parent": parentspec,
1721 "parent": parentspec,
1722 "parentpost": p1,
1722 "parentpost": p1,
1723 }
1723 }
1724
1724
1725 def optimize(x, small):
1725 def optimize(x, small):
1726 if x is None:
1726 if x is None:
1727 return 0, x
1727 return 0, x
1728
1728
1729 smallbonus = 1
1729 smallbonus = 1
1730 if small:
1730 if small:
1731 smallbonus = .5
1731 smallbonus = .5
1732
1732
1733 op = x[0]
1733 op = x[0]
1734 if op == 'minus':
1734 if op == 'minus':
1735 return optimize(('and', x[1], ('not', x[2])), small)
1735 return optimize(('and', x[1], ('not', x[2])), small)
1736 elif op == 'dagrangepre':
1736 elif op == 'dagrangepre':
1737 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1737 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1738 elif op == 'dagrangepost':
1738 elif op == 'dagrangepost':
1739 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1739 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1740 elif op == 'rangepre':
1740 elif op == 'rangepre':
1741 return optimize(('range', ('string', '0'), x[1]), small)
1741 return optimize(('range', ('string', '0'), x[1]), small)
1742 elif op == 'rangepost':
1742 elif op == 'rangepost':
1743 return optimize(('range', x[1], ('string', 'tip')), small)
1743 return optimize(('range', x[1], ('string', 'tip')), small)
1744 elif op == 'negate':
1744 elif op == 'negate':
1745 return optimize(('string',
1745 return optimize(('string',
1746 '-' + getstring(x[1], _("can't negate that"))), small)
1746 '-' + getstring(x[1], _("can't negate that"))), small)
1747 elif op in 'string symbol negate':
1747 elif op in 'string symbol negate':
1748 return smallbonus, x # single revisions are small
1748 return smallbonus, x # single revisions are small
1749 elif op == 'and':
1749 elif op == 'and':
1750 wa, ta = optimize(x[1], True)
1750 wa, ta = optimize(x[1], True)
1751 wb, tb = optimize(x[2], True)
1751 wb, tb = optimize(x[2], True)
1752
1753 # (::x and not ::y)/(not ::y and ::x) have a fast path
1754 def ismissingancestors(revs, bases):
1755 return (
1756 revs[0] == 'func'
1757 and getstring(revs[1], _('not a symbol')) == 'ancestors'
1758 and bases[0] == 'not'
1759 and bases[1][0] == 'func'
1760 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
1761
1752 w = min(wa, wb)
1762 w = min(wa, wb)
1763 if ismissingancestors(ta, tb):
1764 return w, ('func', ('symbol', '_missingancestors'),
1765 ('list', ta[2], tb[1][2]))
1766 if ismissingancestors(tb, ta):
1767 return w, ('func', ('symbol', '_missingancestors'),
1768 ('list', tb[2], ta[1][2]))
1769
1753 if wa > wb:
1770 if wa > wb:
1754 return w, (op, tb, ta)
1771 return w, (op, tb, ta)
1755 return w, (op, ta, tb)
1772 return w, (op, ta, tb)
1756 elif op == 'or':
1773 elif op == 'or':
1757 wa, ta = optimize(x[1], False)
1774 wa, ta = optimize(x[1], False)
1758 wb, tb = optimize(x[2], False)
1775 wb, tb = optimize(x[2], False)
1759 if wb < wa:
1776 if wb < wa:
1760 wb, wa = wa, wb
1777 wb, wa = wa, wb
1761 return max(wa, wb), (op, ta, tb)
1778 return max(wa, wb), (op, ta, tb)
1762 elif op == 'not':
1779 elif op == 'not':
1763 o = optimize(x[1], not small)
1780 o = optimize(x[1], not small)
1764 return o[0], (op, o[1])
1781 return o[0], (op, o[1])
1765 elif op == 'parentpost':
1782 elif op == 'parentpost':
1766 o = optimize(x[1], small)
1783 o = optimize(x[1], small)
1767 return o[0], (op, o[1])
1784 return o[0], (op, o[1])
1768 elif op == 'group':
1785 elif op == 'group':
1769 return optimize(x[1], small)
1786 return optimize(x[1], small)
1770 elif op in 'dagrange range list parent ancestorspec':
1787 elif op in 'dagrange range list parent ancestorspec':
1771 if op == 'parent':
1788 if op == 'parent':
1772 # x^:y means (x^) : y, not x ^ (:y)
1789 # x^:y means (x^) : y, not x ^ (:y)
1773 post = ('parentpost', x[1])
1790 post = ('parentpost', x[1])
1774 if x[2][0] == 'dagrangepre':
1791 if x[2][0] == 'dagrangepre':
1775 return optimize(('dagrange', post, x[2][1]), small)
1792 return optimize(('dagrange', post, x[2][1]), small)
1776 elif x[2][0] == 'rangepre':
1793 elif x[2][0] == 'rangepre':
1777 return optimize(('range', post, x[2][1]), small)
1794 return optimize(('range', post, x[2][1]), small)
1778
1795
1779 wa, ta = optimize(x[1], small)
1796 wa, ta = optimize(x[1], small)
1780 wb, tb = optimize(x[2], small)
1797 wb, tb = optimize(x[2], small)
1781 return wa + wb, (op, ta, tb)
1798 return wa + wb, (op, ta, tb)
1782 elif op == 'func':
1799 elif op == 'func':
1783 f = getstring(x[1], _("not a symbol"))
1800 f = getstring(x[1], _("not a symbol"))
1784 wa, ta = optimize(x[2], small)
1801 wa, ta = optimize(x[2], small)
1785 if f in ("author branch closed date desc file grep keyword "
1802 if f in ("author branch closed date desc file grep keyword "
1786 "outgoing user"):
1803 "outgoing user"):
1787 w = 10 # slow
1804 w = 10 # slow
1788 elif f in "modifies adds removes":
1805 elif f in "modifies adds removes":
1789 w = 30 # slower
1806 w = 30 # slower
1790 elif f == "contains":
1807 elif f == "contains":
1791 w = 100 # very slow
1808 w = 100 # very slow
1792 elif f == "ancestor":
1809 elif f == "ancestor":
1793 w = 1 * smallbonus
1810 w = 1 * smallbonus
1794 elif f in "reverse limit first":
1811 elif f in "reverse limit first":
1795 w = 0
1812 w = 0
1796 elif f in "sort":
1813 elif f in "sort":
1797 w = 10 # assume most sorts look at changelog
1814 w = 10 # assume most sorts look at changelog
1798 else:
1815 else:
1799 w = 1
1816 w = 1
1800 return w + wa, (op, x[1], ta)
1817 return w + wa, (op, x[1], ta)
1801 return 1, x
1818 return 1, x
1802
1819
1803 _aliasarg = ('func', ('symbol', '_aliasarg'))
1820 _aliasarg = ('func', ('symbol', '_aliasarg'))
1804 def _getaliasarg(tree):
1821 def _getaliasarg(tree):
1805 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1822 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1806 return X, None otherwise.
1823 return X, None otherwise.
1807 """
1824 """
1808 if (len(tree) == 3 and tree[:2] == _aliasarg
1825 if (len(tree) == 3 and tree[:2] == _aliasarg
1809 and tree[2][0] == 'string'):
1826 and tree[2][0] == 'string'):
1810 return tree[2][1]
1827 return tree[2][1]
1811 return None
1828 return None
1812
1829
1813 def _checkaliasarg(tree, known=None):
1830 def _checkaliasarg(tree, known=None):
1814 """Check tree contains no _aliasarg construct or only ones which
1831 """Check tree contains no _aliasarg construct or only ones which
1815 value is in known. Used to avoid alias placeholders injection.
1832 value is in known. Used to avoid alias placeholders injection.
1816 """
1833 """
1817 if isinstance(tree, tuple):
1834 if isinstance(tree, tuple):
1818 arg = _getaliasarg(tree)
1835 arg = _getaliasarg(tree)
1819 if arg is not None and (not known or arg not in known):
1836 if arg is not None and (not known or arg not in known):
1820 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1837 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1821 for t in tree:
1838 for t in tree:
1822 _checkaliasarg(t, known)
1839 _checkaliasarg(t, known)
1823
1840
1824 class revsetalias(object):
1841 class revsetalias(object):
1825 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1842 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1826 args = None
1843 args = None
1827
1844
1828 def __init__(self, name, value):
1845 def __init__(self, name, value):
1829 '''Aliases like:
1846 '''Aliases like:
1830
1847
1831 h = heads(default)
1848 h = heads(default)
1832 b($1) = ancestors($1) - ancestors(default)
1849 b($1) = ancestors($1) - ancestors(default)
1833 '''
1850 '''
1834 m = self.funcre.search(name)
1851 m = self.funcre.search(name)
1835 if m:
1852 if m:
1836 self.name = m.group(1)
1853 self.name = m.group(1)
1837 self.tree = ('func', ('symbol', m.group(1)))
1854 self.tree = ('func', ('symbol', m.group(1)))
1838 self.args = [x.strip() for x in m.group(2).split(',')]
1855 self.args = [x.strip() for x in m.group(2).split(',')]
1839 for arg in self.args:
1856 for arg in self.args:
1840 # _aliasarg() is an unknown symbol only used separate
1857 # _aliasarg() is an unknown symbol only used separate
1841 # alias argument placeholders from regular strings.
1858 # alias argument placeholders from regular strings.
1842 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1859 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1843 else:
1860 else:
1844 self.name = name
1861 self.name = name
1845 self.tree = ('symbol', name)
1862 self.tree = ('symbol', name)
1846
1863
1847 self.replacement, pos = parse(value)
1864 self.replacement, pos = parse(value)
1848 if pos != len(value):
1865 if pos != len(value):
1849 raise error.ParseError(_('invalid token'), pos)
1866 raise error.ParseError(_('invalid token'), pos)
1850 # Check for placeholder injection
1867 # Check for placeholder injection
1851 _checkaliasarg(self.replacement, self.args)
1868 _checkaliasarg(self.replacement, self.args)
1852
1869
1853 def _getalias(aliases, tree):
1870 def _getalias(aliases, tree):
1854 """If tree looks like an unexpanded alias, return it. Return None
1871 """If tree looks like an unexpanded alias, return it. Return None
1855 otherwise.
1872 otherwise.
1856 """
1873 """
1857 if isinstance(tree, tuple) and tree:
1874 if isinstance(tree, tuple) and tree:
1858 if tree[0] == 'symbol' and len(tree) == 2:
1875 if tree[0] == 'symbol' and len(tree) == 2:
1859 name = tree[1]
1876 name = tree[1]
1860 alias = aliases.get(name)
1877 alias = aliases.get(name)
1861 if alias and alias.args is None and alias.tree == tree:
1878 if alias and alias.args is None and alias.tree == tree:
1862 return alias
1879 return alias
1863 if tree[0] == 'func' and len(tree) > 1:
1880 if tree[0] == 'func' and len(tree) > 1:
1864 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1881 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1865 name = tree[1][1]
1882 name = tree[1][1]
1866 alias = aliases.get(name)
1883 alias = aliases.get(name)
1867 if alias and alias.args is not None and alias.tree == tree[:2]:
1884 if alias and alias.args is not None and alias.tree == tree[:2]:
1868 return alias
1885 return alias
1869 return None
1886 return None
1870
1887
1871 def _expandargs(tree, args):
1888 def _expandargs(tree, args):
1872 """Replace _aliasarg instances with the substitution value of the
1889 """Replace _aliasarg instances with the substitution value of the
1873 same name in args, recursively.
1890 same name in args, recursively.
1874 """
1891 """
1875 if not tree or not isinstance(tree, tuple):
1892 if not tree or not isinstance(tree, tuple):
1876 return tree
1893 return tree
1877 arg = _getaliasarg(tree)
1894 arg = _getaliasarg(tree)
1878 if arg is not None:
1895 if arg is not None:
1879 return args[arg]
1896 return args[arg]
1880 return tuple(_expandargs(t, args) for t in tree)
1897 return tuple(_expandargs(t, args) for t in tree)
1881
1898
1882 def _expandaliases(aliases, tree, expanding, cache):
1899 def _expandaliases(aliases, tree, expanding, cache):
1883 """Expand aliases in tree, recursively.
1900 """Expand aliases in tree, recursively.
1884
1901
1885 'aliases' is a dictionary mapping user defined aliases to
1902 'aliases' is a dictionary mapping user defined aliases to
1886 revsetalias objects.
1903 revsetalias objects.
1887 """
1904 """
1888 if not isinstance(tree, tuple):
1905 if not isinstance(tree, tuple):
1889 # Do not expand raw strings
1906 # Do not expand raw strings
1890 return tree
1907 return tree
1891 alias = _getalias(aliases, tree)
1908 alias = _getalias(aliases, tree)
1892 if alias is not None:
1909 if alias is not None:
1893 if alias in expanding:
1910 if alias in expanding:
1894 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1911 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1895 'detected') % alias.name)
1912 'detected') % alias.name)
1896 expanding.append(alias)
1913 expanding.append(alias)
1897 if alias.name not in cache:
1914 if alias.name not in cache:
1898 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1915 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1899 expanding, cache)
1916 expanding, cache)
1900 result = cache[alias.name]
1917 result = cache[alias.name]
1901 expanding.pop()
1918 expanding.pop()
1902 if alias.args is not None:
1919 if alias.args is not None:
1903 l = getlist(tree[2])
1920 l = getlist(tree[2])
1904 if len(l) != len(alias.args):
1921 if len(l) != len(alias.args):
1905 raise error.ParseError(
1922 raise error.ParseError(
1906 _('invalid number of arguments: %s') % len(l))
1923 _('invalid number of arguments: %s') % len(l))
1907 l = [_expandaliases(aliases, a, [], cache) for a in l]
1924 l = [_expandaliases(aliases, a, [], cache) for a in l]
1908 result = _expandargs(result, dict(zip(alias.args, l)))
1925 result = _expandargs(result, dict(zip(alias.args, l)))
1909 else:
1926 else:
1910 result = tuple(_expandaliases(aliases, t, expanding, cache)
1927 result = tuple(_expandaliases(aliases, t, expanding, cache)
1911 for t in tree)
1928 for t in tree)
1912 return result
1929 return result
1913
1930
1914 def findaliases(ui, tree):
1931 def findaliases(ui, tree):
1915 _checkaliasarg(tree)
1932 _checkaliasarg(tree)
1916 aliases = {}
1933 aliases = {}
1917 for k, v in ui.configitems('revsetalias'):
1934 for k, v in ui.configitems('revsetalias'):
1918 alias = revsetalias(k, v)
1935 alias = revsetalias(k, v)
1919 aliases[alias.name] = alias
1936 aliases[alias.name] = alias
1920 return _expandaliases(aliases, tree, [], {})
1937 return _expandaliases(aliases, tree, [], {})
1921
1938
1922 def parse(spec):
1939 def parse(spec):
1923 p = parser.parser(tokenize, elements)
1940 p = parser.parser(tokenize, elements)
1924 return p.parse(spec)
1941 return p.parse(spec)
1925
1942
1926 def match(ui, spec):
1943 def match(ui, spec):
1927 if not spec:
1944 if not spec:
1928 raise error.ParseError(_("empty query"))
1945 raise error.ParseError(_("empty query"))
1929 tree, pos = parse(spec)
1946 tree, pos = parse(spec)
1930 if (pos != len(spec)):
1947 if (pos != len(spec)):
1931 raise error.ParseError(_("invalid token"), pos)
1948 raise error.ParseError(_("invalid token"), pos)
1932 if ui:
1949 if ui:
1933 tree = findaliases(ui, tree)
1950 tree = findaliases(ui, tree)
1934 weight, tree = optimize(tree, True)
1951 weight, tree = optimize(tree, True)
1935 def mfunc(repo, subset):
1952 def mfunc(repo, subset):
1936 return getset(repo, subset, tree)
1953 return getset(repo, subset, tree)
1937 return mfunc
1954 return mfunc
1938
1955
1939 def formatspec(expr, *args):
1956 def formatspec(expr, *args):
1940 '''
1957 '''
1941 This is a convenience function for using revsets internally, and
1958 This is a convenience function for using revsets internally, and
1942 escapes arguments appropriately. Aliases are intentionally ignored
1959 escapes arguments appropriately. Aliases are intentionally ignored
1943 so that intended expression behavior isn't accidentally subverted.
1960 so that intended expression behavior isn't accidentally subverted.
1944
1961
1945 Supported arguments:
1962 Supported arguments:
1946
1963
1947 %r = revset expression, parenthesized
1964 %r = revset expression, parenthesized
1948 %d = int(arg), no quoting
1965 %d = int(arg), no quoting
1949 %s = string(arg), escaped and single-quoted
1966 %s = string(arg), escaped and single-quoted
1950 %b = arg.branch(), escaped and single-quoted
1967 %b = arg.branch(), escaped and single-quoted
1951 %n = hex(arg), single-quoted
1968 %n = hex(arg), single-quoted
1952 %% = a literal '%'
1969 %% = a literal '%'
1953
1970
1954 Prefixing the type with 'l' specifies a parenthesized list of that type.
1971 Prefixing the type with 'l' specifies a parenthesized list of that type.
1955
1972
1956 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1973 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1957 '(10 or 11):: and ((this()) or (that()))'
1974 '(10 or 11):: and ((this()) or (that()))'
1958 >>> formatspec('%d:: and not %d::', 10, 20)
1975 >>> formatspec('%d:: and not %d::', 10, 20)
1959 '10:: and not 20::'
1976 '10:: and not 20::'
1960 >>> formatspec('%ld or %ld', [], [1])
1977 >>> formatspec('%ld or %ld', [], [1])
1961 "_list('') or 1"
1978 "_list('') or 1"
1962 >>> formatspec('keyword(%s)', 'foo\\xe9')
1979 >>> formatspec('keyword(%s)', 'foo\\xe9')
1963 "keyword('foo\\\\xe9')"
1980 "keyword('foo\\\\xe9')"
1964 >>> b = lambda: 'default'
1981 >>> b = lambda: 'default'
1965 >>> b.branch = b
1982 >>> b.branch = b
1966 >>> formatspec('branch(%b)', b)
1983 >>> formatspec('branch(%b)', b)
1967 "branch('default')"
1984 "branch('default')"
1968 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1985 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1969 "root(_list('a\\x00b\\x00c\\x00d'))"
1986 "root(_list('a\\x00b\\x00c\\x00d'))"
1970 '''
1987 '''
1971
1988
1972 def quote(s):
1989 def quote(s):
1973 return repr(str(s))
1990 return repr(str(s))
1974
1991
1975 def argtype(c, arg):
1992 def argtype(c, arg):
1976 if c == 'd':
1993 if c == 'd':
1977 return str(int(arg))
1994 return str(int(arg))
1978 elif c == 's':
1995 elif c == 's':
1979 return quote(arg)
1996 return quote(arg)
1980 elif c == 'r':
1997 elif c == 'r':
1981 parse(arg) # make sure syntax errors are confined
1998 parse(arg) # make sure syntax errors are confined
1982 return '(%s)' % arg
1999 return '(%s)' % arg
1983 elif c == 'n':
2000 elif c == 'n':
1984 return quote(node.hex(arg))
2001 return quote(node.hex(arg))
1985 elif c == 'b':
2002 elif c == 'b':
1986 return quote(arg.branch())
2003 return quote(arg.branch())
1987
2004
1988 def listexp(s, t):
2005 def listexp(s, t):
1989 l = len(s)
2006 l = len(s)
1990 if l == 0:
2007 if l == 0:
1991 return "_list('')"
2008 return "_list('')"
1992 elif l == 1:
2009 elif l == 1:
1993 return argtype(t, s[0])
2010 return argtype(t, s[0])
1994 elif t == 'd':
2011 elif t == 'd':
1995 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
2012 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1996 elif t == 's':
2013 elif t == 's':
1997 return "_list('%s')" % "\0".join(s)
2014 return "_list('%s')" % "\0".join(s)
1998 elif t == 'n':
2015 elif t == 'n':
1999 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
2016 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
2000 elif t == 'b':
2017 elif t == 'b':
2001 return "_list('%s')" % "\0".join(a.branch() for a in s)
2018 return "_list('%s')" % "\0".join(a.branch() for a in s)
2002
2019
2003 m = l // 2
2020 m = l // 2
2004 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2021 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2005
2022
2006 ret = ''
2023 ret = ''
2007 pos = 0
2024 pos = 0
2008 arg = 0
2025 arg = 0
2009 while pos < len(expr):
2026 while pos < len(expr):
2010 c = expr[pos]
2027 c = expr[pos]
2011 if c == '%':
2028 if c == '%':
2012 pos += 1
2029 pos += 1
2013 d = expr[pos]
2030 d = expr[pos]
2014 if d == '%':
2031 if d == '%':
2015 ret += d
2032 ret += d
2016 elif d in 'dsnbr':
2033 elif d in 'dsnbr':
2017 ret += argtype(d, args[arg])
2034 ret += argtype(d, args[arg])
2018 arg += 1
2035 arg += 1
2019 elif d == 'l':
2036 elif d == 'l':
2020 # a list of some type
2037 # a list of some type
2021 pos += 1
2038 pos += 1
2022 d = expr[pos]
2039 d = expr[pos]
2023 ret += listexp(list(args[arg]), d)
2040 ret += listexp(list(args[arg]), d)
2024 arg += 1
2041 arg += 1
2025 else:
2042 else:
2026 raise util.Abort('unexpected revspec format character %s' % d)
2043 raise util.Abort('unexpected revspec format character %s' % d)
2027 else:
2044 else:
2028 ret += c
2045 ret += c
2029 pos += 1
2046 pos += 1
2030
2047
2031 return ret
2048 return ret
2032
2049
2033 def prettyformat(tree):
2050 def prettyformat(tree):
2034 def _prettyformat(tree, level, lines):
2051 def _prettyformat(tree, level, lines):
2035 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2052 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2036 lines.append((level, str(tree)))
2053 lines.append((level, str(tree)))
2037 else:
2054 else:
2038 lines.append((level, '(%s' % tree[0]))
2055 lines.append((level, '(%s' % tree[0]))
2039 for s in tree[1:]:
2056 for s in tree[1:]:
2040 _prettyformat(s, level + 1, lines)
2057 _prettyformat(s, level + 1, lines)
2041 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2058 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2042
2059
2043 lines = []
2060 lines = []
2044 _prettyformat(tree, 0, lines)
2061 _prettyformat(tree, 0, lines)
2045 output = '\n'.join((' '*l + s) for l, s in lines)
2062 output = '\n'.join((' '*l + s) for l, s in lines)
2046 return output
2063 return output
2047
2064
2048 def depth(tree):
2065 def depth(tree):
2049 if isinstance(tree, tuple):
2066 if isinstance(tree, tuple):
2050 return max(map(depth, tree)) + 1
2067 return max(map(depth, tree)) + 1
2051 else:
2068 else:
2052 return 0
2069 return 0
2053
2070
2054 def funcsused(tree):
2071 def funcsused(tree):
2055 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2072 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2056 return set()
2073 return set()
2057 else:
2074 else:
2058 funcs = set()
2075 funcs = set()
2059 for s in tree[1:]:
2076 for s in tree[1:]:
2060 funcs |= funcsused(s)
2077 funcs |= funcsused(s)
2061 if tree[0] == 'func':
2078 if tree[0] == 'func':
2062 funcs.add(tree[1][1])
2079 funcs.add(tree[1][1])
2063 return funcs
2080 return funcs
2064
2081
2065 class baseset(list):
2082 class baseset(list):
2066 """Basic data structure that represents a revset and contains the basic
2083 """Basic data structure that represents a revset and contains the basic
2067 operation that it should be able to perform.
2084 operation that it should be able to perform.
2068 """
2085 """
2069 def __init__(self, data):
2086 def __init__(self, data):
2070 super(baseset, self).__init__(data)
2087 super(baseset, self).__init__(data)
2071 self._set = None
2088 self._set = None
2072
2089
2073 def set(self):
2090 def set(self):
2074 if not self._set:
2091 if not self._set:
2075 self._set = set(self)
2092 self._set = set(self)
2076 return self._set
2093 return self._set
2077
2094
2078 def __sub__(self, x):
2095 def __sub__(self, x):
2079 if isinstance(x, baseset):
2096 if isinstance(x, baseset):
2080 s = x.set()
2097 s = x.set()
2081 else:
2098 else:
2082 s = set(x)
2099 s = set(x)
2083 return baseset(self.set() - s)
2100 return baseset(self.set() - s)
2084
2101
2085 def __and__(self, x):
2102 def __and__(self, x):
2086 if isinstance(x, baseset):
2103 if isinstance(x, baseset):
2087 x = x.set()
2104 x = x.set()
2088 return baseset([y for y in self if y in x])
2105 return baseset([y for y in self if y in x])
2089
2106
2090 def __add__(self, x):
2107 def __add__(self, x):
2091 s = self.set()
2108 s = self.set()
2092 l = [r for r in x if r not in s]
2109 l = [r for r in x if r not in s]
2093 return baseset(list(self) + l)
2110 return baseset(list(self) + l)
2094
2111
2095 class lazyset(object):
2112 class lazyset(object):
2096 """Duck type for baseset class which iterates lazily over the revisions in
2113 """Duck type for baseset class which iterates lazily over the revisions in
2097 the subset and contains a function which tests for membership in the
2114 the subset and contains a function which tests for membership in the
2098 revset
2115 revset
2099 """
2116 """
2100 def __init__(self, subset, condition):
2117 def __init__(self, subset, condition):
2101 self._subset = subset
2118 self._subset = subset
2102 self._condition = condition
2119 self._condition = condition
2103
2120
2104 def __contains__(self, x):
2121 def __contains__(self, x):
2105 return x in self._subset and self._condition(x)
2122 return x in self._subset and self._condition(x)
2106
2123
2107 def __iter__(self):
2124 def __iter__(self):
2108 cond = self._condition
2125 cond = self._condition
2109 for x in self._subset:
2126 for x in self._subset:
2110 if cond(x):
2127 if cond(x):
2111 yield x
2128 yield x
2112
2129
2113 def __and__(self, x):
2130 def __and__(self, x):
2114 return lazyset(self, lambda r: r in x)
2131 return lazyset(self, lambda r: r in x)
2115
2132
2116 def __sub__(self, x):
2133 def __sub__(self, x):
2117 return lazyset(self, lambda r: r not in x)
2134 return lazyset(self, lambda r: r not in x)
2118
2135
2119 def __add__(self, x):
2136 def __add__(self, x):
2120 l = baseset([r for r in self])
2137 l = baseset([r for r in self])
2121 return l + baseset(x)
2138 return l + baseset(x)
2122
2139
2123 def __len__(self):
2140 def __len__(self):
2124 # Basic implementation to be changed in future patches.
2141 # Basic implementation to be changed in future patches.
2125 l = baseset([r for r in self])
2142 l = baseset([r for r in self])
2126 return len(l)
2143 return len(l)
2127
2144
2128 def __getitem__(self, x):
2145 def __getitem__(self, x):
2129 # Basic implementation to be changed in future patches.
2146 # Basic implementation to be changed in future patches.
2130 l = baseset([r for r in self])
2147 l = baseset([r for r in self])
2131 return l[x]
2148 return l[x]
2132
2149
2133 def sort(self, reverse=False):
2150 def sort(self, reverse=False):
2134 # Basic implementation to be changed in future patches.
2151 # Basic implementation to be changed in future patches.
2135 self._subset = baseset(self._subset)
2152 self._subset = baseset(self._subset)
2136 self._subset.sort(reverse=reverse)
2153 self._subset.sort(reverse=reverse)
2137
2154
2138 def reverse(self):
2155 def reverse(self):
2139 self._subset.reverse()
2156 self._subset.reverse()
2140
2157
2141 def set(self):
2158 def set(self):
2142 return set([r for r in self])
2159 return set([r for r in self])
2143
2160
2144 class spanset(object):
2161 class spanset(object):
2145 """Duck type for baseset class which represents a range of revisions and
2162 """Duck type for baseset class which represents a range of revisions and
2146 can work lazily and without having all the range in memory
2163 can work lazily and without having all the range in memory
2147 """
2164 """
2148 def __init__(self, start, end):
2165 def __init__(self, start, end):
2149 self._start = start
2166 self._start = start
2150 self._end = end
2167 self._end = end
2151
2168
2152 def __iter__(self):
2169 def __iter__(self):
2153 if self._start <= self._end:
2170 if self._start <= self._end:
2154 for r in xrange(self._start, self._end):
2171 for r in xrange(self._start, self._end):
2155 yield r
2172 yield r
2156 else:
2173 else:
2157 for r in xrange(self._start, self._end, -1):
2174 for r in xrange(self._start, self._end, -1):
2158 yield r
2175 yield r
2159
2176
2160 def __contains__(self, x):
2177 def __contains__(self, x):
2161 return (x <= self._start and x > self._end) or (x >= self._start and x<
2178 return (x <= self._start and x > self._end) or (x >= self._start and x<
2162 self._end)
2179 self._end)
2163
2180
2164 def __and__(self, x):
2181 def __and__(self, x):
2165 return lazyset(self, lambda r: r in x)
2182 return lazyset(self, lambda r: r in x)
2166
2183
2167 def __sub__(self, x):
2184 def __sub__(self, x):
2168 return lazyset(self, lambda r: r not in x)
2185 return lazyset(self, lambda r: r not in x)
2169
2186
2170 def __add__(self, x):
2187 def __add__(self, x):
2171 l = baseset(self)
2188 l = baseset(self)
2172 return l + baseset(x)
2189 return l + baseset(x)
2173
2190
2174 def __len__(self):
2191 def __len__(self):
2175 return abs(self._end - self._start)
2192 return abs(self._end - self._start)
2176
2193
2177 def __getitem__(self, x):
2194 def __getitem__(self, x):
2178 # Basic implementation to be changed in future patches.
2195 # Basic implementation to be changed in future patches.
2179 l = baseset([r for r in self])
2196 l = baseset([r for r in self])
2180 return l[x]
2197 return l[x]
2181
2198
2182 def sort(self, reverse=False):
2199 def sort(self, reverse=False):
2183 # Basic implementation to be changed in future patches.
2200 # Basic implementation to be changed in future patches.
2184 if reverse:
2201 if reverse:
2185 self.reverse()
2202 self.reverse()
2186
2203
2187 def reverse(self):
2204 def reverse(self):
2188 if self._start <= self._end:
2205 if self._start <= self._end:
2189 self._start, self._end = self._end - 1, self._start - 1
2206 self._start, self._end = self._end - 1, self._start - 1
2190 else:
2207 else:
2191 self._start, self._end = self._end + 1, self._start + 1
2208 self._start, self._end = self._end + 1, self._start + 1
2192
2209
2193 def set(self):
2210 def set(self):
2194 return self
2211 return self
2195
2212
2196 # tell hggettext to extract docstrings from these functions:
2213 # tell hggettext to extract docstrings from these functions:
2197 i18nfunctions = symbols.values()
2214 i18nfunctions = symbols.values()
@@ -1,895 +1,959 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 $ try -- -a-b-c--a # complains
127 $ try -- -a-b-c--a # complains
128 (minus
128 (minus
129 (minus
129 (minus
130 (minus
130 (minus
131 (negate
131 (negate
132 ('symbol', 'a'))
132 ('symbol', 'a'))
133 ('symbol', 'b'))
133 ('symbol', 'b'))
134 ('symbol', 'c'))
134 ('symbol', 'c'))
135 (negate
135 (negate
136 ('symbol', 'a')))
136 ('symbol', 'a')))
137 abort: unknown revision '-a'!
137 abort: unknown revision '-a'!
138 [255]
138 [255]
139 $ try Γ©
139 $ try Γ©
140 ('symbol', '\xc3\xa9')
140 ('symbol', '\xc3\xa9')
141 9
141 9
142
142
143 quoting needed
143 quoting needed
144
144
145 $ try '"-a-b-c-"-a'
145 $ try '"-a-b-c-"-a'
146 (minus
146 (minus
147 ('string', '-a-b-c-')
147 ('string', '-a-b-c-')
148 ('symbol', 'a'))
148 ('symbol', 'a'))
149 4
149 4
150
150
151 $ log '1 or 2'
151 $ log '1 or 2'
152 1
152 1
153 2
153 2
154 $ log '1|2'
154 $ log '1|2'
155 1
155 1
156 2
156 2
157 $ log '1 and 2'
157 $ log '1 and 2'
158 $ log '1&2'
158 $ log '1&2'
159 $ try '1&2|3' # precedence - and is higher
159 $ try '1&2|3' # precedence - and is higher
160 (or
160 (or
161 (and
161 (and
162 ('symbol', '1')
162 ('symbol', '1')
163 ('symbol', '2'))
163 ('symbol', '2'))
164 ('symbol', '3'))
164 ('symbol', '3'))
165 3
165 3
166 $ try '1|2&3'
166 $ try '1|2&3'
167 (or
167 (or
168 ('symbol', '1')
168 ('symbol', '1')
169 (and
169 (and
170 ('symbol', '2')
170 ('symbol', '2')
171 ('symbol', '3')))
171 ('symbol', '3')))
172 1
172 1
173 $ try '1&2&3' # associativity
173 $ try '1&2&3' # associativity
174 (and
174 (and
175 (and
175 (and
176 ('symbol', '1')
176 ('symbol', '1')
177 ('symbol', '2'))
177 ('symbol', '2'))
178 ('symbol', '3'))
178 ('symbol', '3'))
179 $ try '1|(2|3)'
179 $ try '1|(2|3)'
180 (or
180 (or
181 ('symbol', '1')
181 ('symbol', '1')
182 (group
182 (group
183 (or
183 (or
184 ('symbol', '2')
184 ('symbol', '2')
185 ('symbol', '3'))))
185 ('symbol', '3'))))
186 1
186 1
187 2
187 2
188 3
188 3
189 $ log '1.0' # tag
189 $ log '1.0' # tag
190 6
190 6
191 $ log 'a' # branch
191 $ log 'a' # branch
192 0
192 0
193 $ log '2785f51ee'
193 $ log '2785f51ee'
194 0
194 0
195 $ log 'date(2005)'
195 $ log 'date(2005)'
196 4
196 4
197 $ log 'date(this is a test)'
197 $ log 'date(this is a test)'
198 hg: parse error at 10: unexpected token: symbol
198 hg: parse error at 10: unexpected token: symbol
199 [255]
199 [255]
200 $ log 'date()'
200 $ log 'date()'
201 hg: parse error: date requires a string
201 hg: parse error: date requires a string
202 [255]
202 [255]
203 $ log 'date'
203 $ log 'date'
204 hg: parse error: can't use date here
204 hg: parse error: can't use date here
205 [255]
205 [255]
206 $ log 'date('
206 $ log 'date('
207 hg: parse error at 5: not a prefix: end
207 hg: parse error at 5: not a prefix: end
208 [255]
208 [255]
209 $ log 'date(tip)'
209 $ log 'date(tip)'
210 abort: invalid date: 'tip'
210 abort: invalid date: 'tip'
211 [255]
211 [255]
212 $ log '"date"'
212 $ log '"date"'
213 abort: unknown revision 'date'!
213 abort: unknown revision 'date'!
214 [255]
214 [255]
215 $ log 'date(2005) and 1::'
215 $ log 'date(2005) and 1::'
216 4
216 4
217
217
218 ancestor can accept 0 or more arguments
218 ancestor can accept 0 or more arguments
219
219
220 $ log 'ancestor()'
220 $ log 'ancestor()'
221 $ log 'ancestor(1)'
221 $ log 'ancestor(1)'
222 1
222 1
223 $ log 'ancestor(4,5)'
223 $ log 'ancestor(4,5)'
224 1
224 1
225 $ log 'ancestor(4,5) and 4'
225 $ log 'ancestor(4,5) and 4'
226 $ log 'ancestor(0,0,1,3)'
226 $ log 'ancestor(0,0,1,3)'
227 0
227 0
228 $ log 'ancestor(3,1,5,3,5,1)'
228 $ log 'ancestor(3,1,5,3,5,1)'
229 1
229 1
230 $ log 'ancestor(0,1,3,5)'
230 $ log 'ancestor(0,1,3,5)'
231 0
231 0
232 $ log 'ancestor(1,2,3,4,5)'
232 $ log 'ancestor(1,2,3,4,5)'
233 1
233 1
234 $ log 'ancestors(5)'
234 $ log 'ancestors(5)'
235 0
235 0
236 1
236 1
237 3
237 3
238 5
238 5
239 $ log 'ancestor(ancestors(5))'
239 $ log 'ancestor(ancestors(5))'
240 0
240 0
241 $ log 'author(bob)'
241 $ log 'author(bob)'
242 2
242 2
243 $ log 'author("re:bob|test")'
243 $ log 'author("re:bob|test")'
244 0
244 0
245 1
245 1
246 2
246 2
247 3
247 3
248 4
248 4
249 5
249 5
250 6
250 6
251 7
251 7
252 8
252 8
253 9
253 9
254 $ log 'branch(Γ©)'
254 $ log 'branch(Γ©)'
255 8
255 8
256 9
256 9
257 $ log 'branch(a)'
257 $ log 'branch(a)'
258 0
258 0
259 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
259 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
260 0 a
260 0 a
261 2 a-b-c-
261 2 a-b-c-
262 3 +a+b+c+
262 3 +a+b+c+
263 4 -a-b-c-
263 4 -a-b-c-
264 5 !a/b/c/
264 5 !a/b/c/
265 6 _a_b_c_
265 6 _a_b_c_
266 7 .a.b.c.
266 7 .a.b.c.
267 $ log 'children(ancestor(4,5))'
267 $ log 'children(ancestor(4,5))'
268 2
268 2
269 3
269 3
270 $ log 'closed()'
270 $ log 'closed()'
271 $ log 'contains(a)'
271 $ log 'contains(a)'
272 0
272 0
273 1
273 1
274 3
274 3
275 5
275 5
276 $ log 'contains("../repo/a")'
276 $ log 'contains("../repo/a")'
277 0
277 0
278 1
278 1
279 3
279 3
280 5
280 5
281 $ log 'desc(B)'
281 $ log 'desc(B)'
282 5
282 5
283 $ log 'descendants(2 or 3)'
283 $ log 'descendants(2 or 3)'
284 2
284 2
285 3
285 3
286 4
286 4
287 5
287 5
288 6
288 6
289 7
289 7
290 8
290 8
291 9
291 9
292 $ log 'file("b*")'
292 $ log 'file("b*")'
293 1
293 1
294 4
294 4
295 $ log 'filelog("b")'
295 $ log 'filelog("b")'
296 1
296 1
297 4
297 4
298 $ log 'filelog("../repo/b")'
298 $ log 'filelog("../repo/b")'
299 1
299 1
300 4
300 4
301 $ log 'follow()'
301 $ log 'follow()'
302 0
302 0
303 1
303 1
304 2
304 2
305 4
305 4
306 8
306 8
307 9
307 9
308 $ log 'grep("issue\d+")'
308 $ log 'grep("issue\d+")'
309 6
309 6
310 $ try 'grep("(")' # invalid regular expression
310 $ try 'grep("(")' # invalid regular expression
311 (func
311 (func
312 ('symbol', 'grep')
312 ('symbol', 'grep')
313 ('string', '('))
313 ('string', '('))
314 hg: parse error: invalid match pattern: unbalanced parenthesis
314 hg: parse error: invalid match pattern: unbalanced parenthesis
315 [255]
315 [255]
316 $ try 'grep("\bissue\d+")'
316 $ try 'grep("\bissue\d+")'
317 (func
317 (func
318 ('symbol', 'grep')
318 ('symbol', 'grep')
319 ('string', '\x08issue\\d+'))
319 ('string', '\x08issue\\d+'))
320 $ try 'grep(r"\bissue\d+")'
320 $ try 'grep(r"\bissue\d+")'
321 (func
321 (func
322 ('symbol', 'grep')
322 ('symbol', 'grep')
323 ('string', '\\bissue\\d+'))
323 ('string', '\\bissue\\d+'))
324 6
324 6
325 $ try 'grep(r"\")'
325 $ try 'grep(r"\")'
326 hg: parse error at 7: unterminated string
326 hg: parse error at 7: unterminated string
327 [255]
327 [255]
328 $ log 'head()'
328 $ log 'head()'
329 0
329 0
330 1
330 1
331 2
331 2
332 3
332 3
333 4
333 4
334 5
334 5
335 6
335 6
336 7
336 7
337 9
337 9
338 $ log 'heads(6::)'
338 $ log 'heads(6::)'
339 7
339 7
340 $ log 'keyword(issue)'
340 $ log 'keyword(issue)'
341 6
341 6
342 $ log 'keyword("test a")'
342 $ log 'keyword("test a")'
343 $ log 'limit(head(), 1)'
343 $ log 'limit(head(), 1)'
344 0
344 0
345 $ log 'matching(6)'
345 $ log 'matching(6)'
346 6
346 6
347 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
347 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
348 6
348 6
349 7
349 7
350 $ log 'max(contains(a))'
350 $ log 'max(contains(a))'
351 5
351 5
352 $ log 'min(contains(a))'
352 $ log 'min(contains(a))'
353 0
353 0
354 $ log 'merge()'
354 $ log 'merge()'
355 6
355 6
356 $ log 'branchpoint()'
356 $ log 'branchpoint()'
357 1
357 1
358 4
358 4
359 $ log 'modifies(b)'
359 $ log 'modifies(b)'
360 4
360 4
361 $ log 'modifies("path:b")'
361 $ log 'modifies("path:b")'
362 4
362 4
363 $ log 'modifies("*")'
363 $ log 'modifies("*")'
364 4
364 4
365 6
365 6
366 $ log 'modifies("set:modified()")'
366 $ log 'modifies("set:modified()")'
367 4
367 4
368 $ log 'id(5)'
368 $ log 'id(5)'
369 2
369 2
370 $ log 'outgoing()'
370 $ log 'outgoing()'
371 8
371 8
372 9
372 9
373 $ log 'outgoing("../remote1")'
373 $ log 'outgoing("../remote1")'
374 8
374 8
375 9
375 9
376 $ log 'outgoing("../remote2")'
376 $ log 'outgoing("../remote2")'
377 3
377 3
378 5
378 5
379 6
379 6
380 7
380 7
381 9
381 9
382 $ log 'p1(merge())'
382 $ log 'p1(merge())'
383 5
383 5
384 $ log 'p2(merge())'
384 $ log 'p2(merge())'
385 4
385 4
386 $ log 'parents(merge())'
386 $ log 'parents(merge())'
387 4
387 4
388 5
388 5
389 $ log 'p1(branchpoint())'
389 $ log 'p1(branchpoint())'
390 0
390 0
391 2
391 2
392 $ log 'p2(branchpoint())'
392 $ log 'p2(branchpoint())'
393 $ log 'parents(branchpoint())'
393 $ log 'parents(branchpoint())'
394 0
394 0
395 2
395 2
396 $ log 'removes(a)'
396 $ log 'removes(a)'
397 2
397 2
398 6
398 6
399 $ log 'roots(all())'
399 $ log 'roots(all())'
400 0
400 0
401 $ log 'reverse(2 or 3 or 4 or 5)'
401 $ log 'reverse(2 or 3 or 4 or 5)'
402 5
402 5
403 4
403 4
404 3
404 3
405 2
405 2
406 $ log 'reverse(all())'
406 $ log 'reverse(all())'
407 9
407 9
408 8
408 8
409 7
409 7
410 6
410 6
411 5
411 5
412 4
412 4
413 3
413 3
414 2
414 2
415 1
415 1
416 0
416 0
417 $ log '1:: and reverse(all())'
417 $ log '1:: and reverse(all())'
418 9
418 9
419 8
419 8
420 7
420 7
421 6
421 6
422 5
422 5
423 4
423 4
424 3
424 3
425 2
425 2
426 1
426 1
427 $ log 'rev(5)'
427 $ log 'rev(5)'
428 5
428 5
429 $ log 'sort(limit(reverse(all()), 3))'
429 $ log 'sort(limit(reverse(all()), 3))'
430 7
430 7
431 8
431 8
432 9
432 9
433 $ log 'sort(2 or 3 or 4 or 5, date)'
433 $ log 'sort(2 or 3 or 4 or 5, date)'
434 2
434 2
435 3
435 3
436 5
436 5
437 4
437 4
438 $ log 'tagged()'
438 $ log 'tagged()'
439 6
439 6
440 $ log 'tag()'
440 $ log 'tag()'
441 6
441 6
442 $ log 'tag(1.0)'
442 $ log 'tag(1.0)'
443 6
443 6
444 $ log 'tag(tip)'
444 $ log 'tag(tip)'
445 9
445 9
446
446
447 check that conversion to _missingancestors works
448 $ try --optimize '::3 - ::1'
449 (minus
450 (dagrangepre
451 ('symbol', '3'))
452 (dagrangepre
453 ('symbol', '1')))
454 * optimized:
455 (func
456 ('symbol', '_missingancestors')
457 (list
458 ('symbol', '3')
459 ('symbol', '1')))
460 3
461 $ try --optimize 'ancestors(1) - ancestors(3)'
462 (minus
463 (func
464 ('symbol', 'ancestors')
465 ('symbol', '1'))
466 (func
467 ('symbol', 'ancestors')
468 ('symbol', '3')))
469 * optimized:
470 (func
471 ('symbol', '_missingancestors')
472 (list
473 ('symbol', '1')
474 ('symbol', '3')))
475 $ try --optimize 'not ::2 and ::6'
476 (and
477 (not
478 (dagrangepre
479 ('symbol', '2')))
480 (dagrangepre
481 ('symbol', '6')))
482 * optimized:
483 (func
484 ('symbol', '_missingancestors')
485 (list
486 ('symbol', '6')
487 ('symbol', '2')))
488 3
489 4
490 5
491 6
492 $ try --optimize 'ancestors(6) and not ancestors(4)'
493 (and
494 (func
495 ('symbol', 'ancestors')
496 ('symbol', '6'))
497 (not
498 (func
499 ('symbol', 'ancestors')
500 ('symbol', '4'))))
501 * optimized:
502 (func
503 ('symbol', '_missingancestors')
504 (list
505 ('symbol', '6')
506 ('symbol', '4')))
507 3
508 5
509 6
510
447 we can use patterns when searching for tags
511 we can use patterns when searching for tags
448
512
449 $ log 'tag("1..*")'
513 $ log 'tag("1..*")'
450 abort: tag '1..*' does not exist
514 abort: tag '1..*' does not exist
451 [255]
515 [255]
452 $ log 'tag("re:1..*")'
516 $ log 'tag("re:1..*")'
453 6
517 6
454 $ log 'tag("re:[0-9].[0-9]")'
518 $ log 'tag("re:[0-9].[0-9]")'
455 6
519 6
456 $ log 'tag("literal:1.0")'
520 $ log 'tag("literal:1.0")'
457 6
521 6
458 $ log 'tag("re:0..*")'
522 $ log 'tag("re:0..*")'
459
523
460 $ log 'tag(unknown)'
524 $ log 'tag(unknown)'
461 abort: tag 'unknown' does not exist
525 abort: tag 'unknown' does not exist
462 [255]
526 [255]
463 $ log 'branch(unknown)'
527 $ log 'branch(unknown)'
464 abort: unknown revision 'unknown'!
528 abort: unknown revision 'unknown'!
465 [255]
529 [255]
466 $ log 'user(bob)'
530 $ log 'user(bob)'
467 2
531 2
468
532
469 $ log '4::8'
533 $ log '4::8'
470 4
534 4
471 8
535 8
472 $ log '4:8'
536 $ log '4:8'
473 4
537 4
474 5
538 5
475 6
539 6
476 7
540 7
477 8
541 8
478
542
479 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
543 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
480 4
544 4
481 2
545 2
482 5
546 5
483
547
484 $ log 'not 0 and 0:2'
548 $ log 'not 0 and 0:2'
485 1
549 1
486 2
550 2
487 $ log 'not 1 and 0:2'
551 $ log 'not 1 and 0:2'
488 0
552 0
489 2
553 2
490 $ log 'not 2 and 0:2'
554 $ log 'not 2 and 0:2'
491 0
555 0
492 1
556 1
493 $ log '(1 and 2)::'
557 $ log '(1 and 2)::'
494 $ log '(1 and 2):'
558 $ log '(1 and 2):'
495 $ log '(1 and 2):3'
559 $ log '(1 and 2):3'
496 $ log 'sort(head(), -rev)'
560 $ log 'sort(head(), -rev)'
497 9
561 9
498 7
562 7
499 6
563 6
500 5
564 5
501 4
565 4
502 3
566 3
503 2
567 2
504 1
568 1
505 0
569 0
506 $ log '4::8 - 8'
570 $ log '4::8 - 8'
507 4
571 4
508 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
572 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
509 2
573 2
510 3
574 3
511 1
575 1
512
576
513 issue2437
577 issue2437
514
578
515 $ log '3 and p1(5)'
579 $ log '3 and p1(5)'
516 3
580 3
517 $ log '4 and p2(6)'
581 $ log '4 and p2(6)'
518 4
582 4
519 $ log '1 and parents(:2)'
583 $ log '1 and parents(:2)'
520 1
584 1
521 $ log '2 and children(1:)'
585 $ log '2 and children(1:)'
522 2
586 2
523 $ log 'roots(all()) or roots(all())'
587 $ log 'roots(all()) or roots(all())'
524 0
588 0
525 $ hg debugrevspec 'roots(all()) or roots(all())'
589 $ hg debugrevspec 'roots(all()) or roots(all())'
526 0
590 0
527 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
591 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
528 9
592 9
529 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
593 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
530 4
594 4
531
595
532 issue2654: report a parse error if the revset was not completely parsed
596 issue2654: report a parse error if the revset was not completely parsed
533
597
534 $ log '1 OR 2'
598 $ log '1 OR 2'
535 hg: parse error at 2: invalid token
599 hg: parse error at 2: invalid token
536 [255]
600 [255]
537
601
538 or operator should preserve ordering:
602 or operator should preserve ordering:
539 $ log 'reverse(2::4) or tip'
603 $ log 'reverse(2::4) or tip'
540 4
604 4
541 2
605 2
542 9
606 9
543
607
544 parentrevspec
608 parentrevspec
545
609
546 $ log 'merge()^0'
610 $ log 'merge()^0'
547 6
611 6
548 $ log 'merge()^'
612 $ log 'merge()^'
549 5
613 5
550 $ log 'merge()^1'
614 $ log 'merge()^1'
551 5
615 5
552 $ log 'merge()^2'
616 $ log 'merge()^2'
553 4
617 4
554 $ log 'merge()^^'
618 $ log 'merge()^^'
555 3
619 3
556 $ log 'merge()^1^'
620 $ log 'merge()^1^'
557 3
621 3
558 $ log 'merge()^^^'
622 $ log 'merge()^^^'
559 1
623 1
560
624
561 $ log 'merge()~0'
625 $ log 'merge()~0'
562 6
626 6
563 $ log 'merge()~1'
627 $ log 'merge()~1'
564 5
628 5
565 $ log 'merge()~2'
629 $ log 'merge()~2'
566 3
630 3
567 $ log 'merge()~2^1'
631 $ log 'merge()~2^1'
568 1
632 1
569 $ log 'merge()~3'
633 $ log 'merge()~3'
570 1
634 1
571
635
572 $ log '(-3:tip)^'
636 $ log '(-3:tip)^'
573 4
637 4
574 6
638 6
575 8
639 8
576
640
577 $ log 'tip^foo'
641 $ log 'tip^foo'
578 hg: parse error: ^ expects a number 0, 1, or 2
642 hg: parse error: ^ expects a number 0, 1, or 2
579 [255]
643 [255]
580
644
581 aliases:
645 aliases:
582
646
583 $ echo '[revsetalias]' >> .hg/hgrc
647 $ echo '[revsetalias]' >> .hg/hgrc
584 $ echo 'm = merge()' >> .hg/hgrc
648 $ echo 'm = merge()' >> .hg/hgrc
585 $ echo 'sincem = descendants(m)' >> .hg/hgrc
649 $ echo 'sincem = descendants(m)' >> .hg/hgrc
586 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
650 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
587 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
651 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
588 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
652 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
589
653
590 $ try m
654 $ try m
591 ('symbol', 'm')
655 ('symbol', 'm')
592 (func
656 (func
593 ('symbol', 'merge')
657 ('symbol', 'merge')
594 None)
658 None)
595 6
659 6
596
660
597 test alias recursion
661 test alias recursion
598
662
599 $ try sincem
663 $ try sincem
600 ('symbol', 'sincem')
664 ('symbol', 'sincem')
601 (func
665 (func
602 ('symbol', 'descendants')
666 ('symbol', 'descendants')
603 (func
667 (func
604 ('symbol', 'merge')
668 ('symbol', 'merge')
605 None))
669 None))
606 6
670 6
607 7
671 7
608
672
609 test infinite recursion
673 test infinite recursion
610
674
611 $ echo 'recurse1 = recurse2' >> .hg/hgrc
675 $ echo 'recurse1 = recurse2' >> .hg/hgrc
612 $ echo 'recurse2 = recurse1' >> .hg/hgrc
676 $ echo 'recurse2 = recurse1' >> .hg/hgrc
613 $ try recurse1
677 $ try recurse1
614 ('symbol', 'recurse1')
678 ('symbol', 'recurse1')
615 hg: parse error: infinite expansion of revset alias "recurse1" detected
679 hg: parse error: infinite expansion of revset alias "recurse1" detected
616 [255]
680 [255]
617
681
618 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
682 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
619 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
683 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
620 $ try "level2(level1(1, 2), 3)"
684 $ try "level2(level1(1, 2), 3)"
621 (func
685 (func
622 ('symbol', 'level2')
686 ('symbol', 'level2')
623 (list
687 (list
624 (func
688 (func
625 ('symbol', 'level1')
689 ('symbol', 'level1')
626 (list
690 (list
627 ('symbol', '1')
691 ('symbol', '1')
628 ('symbol', '2')))
692 ('symbol', '2')))
629 ('symbol', '3')))
693 ('symbol', '3')))
630 (or
694 (or
631 ('symbol', '3')
695 ('symbol', '3')
632 (or
696 (or
633 ('symbol', '1')
697 ('symbol', '1')
634 ('symbol', '2')))
698 ('symbol', '2')))
635 3
699 3
636 1
700 1
637 2
701 2
638
702
639 test nesting and variable passing
703 test nesting and variable passing
640
704
641 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
705 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
642 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
706 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
643 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
707 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
644 $ try 'nested(2:5)'
708 $ try 'nested(2:5)'
645 (func
709 (func
646 ('symbol', 'nested')
710 ('symbol', 'nested')
647 (range
711 (range
648 ('symbol', '2')
712 ('symbol', '2')
649 ('symbol', '5')))
713 ('symbol', '5')))
650 (func
714 (func
651 ('symbol', 'max')
715 ('symbol', 'max')
652 (range
716 (range
653 ('symbol', '2')
717 ('symbol', '2')
654 ('symbol', '5')))
718 ('symbol', '5')))
655 5
719 5
656
720
657 test variable isolation, variable placeholders are rewritten as string
721 test variable isolation, variable placeholders are rewritten as string
658 then parsed and matched again as string. Check they do not leak too
722 then parsed and matched again as string. Check they do not leak too
659 far away.
723 far away.
660
724
661 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
725 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
662 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
726 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
663 $ try 'callinjection(2:5)'
727 $ try 'callinjection(2:5)'
664 (func
728 (func
665 ('symbol', 'callinjection')
729 ('symbol', 'callinjection')
666 (range
730 (range
667 ('symbol', '2')
731 ('symbol', '2')
668 ('symbol', '5')))
732 ('symbol', '5')))
669 (func
733 (func
670 ('symbol', 'descendants')
734 ('symbol', 'descendants')
671 (func
735 (func
672 ('symbol', 'max')
736 ('symbol', 'max')
673 ('string', '$1')))
737 ('string', '$1')))
674 abort: unknown revision '$1'!
738 abort: unknown revision '$1'!
675 [255]
739 [255]
676
740
677 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
741 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
678 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
742 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
679 $ try 'callinjection2(2:5)'
743 $ try 'callinjection2(2:5)'
680 (func
744 (func
681 ('symbol', 'callinjection2')
745 ('symbol', 'callinjection2')
682 (range
746 (range
683 ('symbol', '2')
747 ('symbol', '2')
684 ('symbol', '5')))
748 ('symbol', '5')))
685 hg: parse error: not a function: _aliasarg
749 hg: parse error: not a function: _aliasarg
686 [255]
750 [255]
687 >>> data = file('.hg/hgrc', 'rb').read()
751 >>> data = file('.hg/hgrc', 'rb').read()
688 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
752 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
689
753
690 $ try 'd(2:5)'
754 $ try 'd(2:5)'
691 (func
755 (func
692 ('symbol', 'd')
756 ('symbol', 'd')
693 (range
757 (range
694 ('symbol', '2')
758 ('symbol', '2')
695 ('symbol', '5')))
759 ('symbol', '5')))
696 (func
760 (func
697 ('symbol', 'reverse')
761 ('symbol', 'reverse')
698 (func
762 (func
699 ('symbol', 'sort')
763 ('symbol', 'sort')
700 (list
764 (list
701 (range
765 (range
702 ('symbol', '2')
766 ('symbol', '2')
703 ('symbol', '5'))
767 ('symbol', '5'))
704 ('symbol', 'date'))))
768 ('symbol', 'date'))))
705 4
769 4
706 5
770 5
707 3
771 3
708 2
772 2
709 $ try 'rs(2 or 3, date)'
773 $ try 'rs(2 or 3, date)'
710 (func
774 (func
711 ('symbol', 'rs')
775 ('symbol', 'rs')
712 (list
776 (list
713 (or
777 (or
714 ('symbol', '2')
778 ('symbol', '2')
715 ('symbol', '3'))
779 ('symbol', '3'))
716 ('symbol', 'date')))
780 ('symbol', 'date')))
717 (func
781 (func
718 ('symbol', 'reverse')
782 ('symbol', 'reverse')
719 (func
783 (func
720 ('symbol', 'sort')
784 ('symbol', 'sort')
721 (list
785 (list
722 (or
786 (or
723 ('symbol', '2')
787 ('symbol', '2')
724 ('symbol', '3'))
788 ('symbol', '3'))
725 ('symbol', 'date'))))
789 ('symbol', 'date'))))
726 3
790 3
727 2
791 2
728 $ try 'rs()'
792 $ try 'rs()'
729 (func
793 (func
730 ('symbol', 'rs')
794 ('symbol', 'rs')
731 None)
795 None)
732 hg: parse error: invalid number of arguments: 0
796 hg: parse error: invalid number of arguments: 0
733 [255]
797 [255]
734 $ try 'rs(2)'
798 $ try 'rs(2)'
735 (func
799 (func
736 ('symbol', 'rs')
800 ('symbol', 'rs')
737 ('symbol', '2'))
801 ('symbol', '2'))
738 hg: parse error: invalid number of arguments: 1
802 hg: parse error: invalid number of arguments: 1
739 [255]
803 [255]
740 $ try 'rs(2, data, 7)'
804 $ try 'rs(2, data, 7)'
741 (func
805 (func
742 ('symbol', 'rs')
806 ('symbol', 'rs')
743 (list
807 (list
744 (list
808 (list
745 ('symbol', '2')
809 ('symbol', '2')
746 ('symbol', 'data'))
810 ('symbol', 'data'))
747 ('symbol', '7')))
811 ('symbol', '7')))
748 hg: parse error: invalid number of arguments: 3
812 hg: parse error: invalid number of arguments: 3
749 [255]
813 [255]
750 $ try 'rs4(2 or 3, x, x, date)'
814 $ try 'rs4(2 or 3, x, x, date)'
751 (func
815 (func
752 ('symbol', 'rs4')
816 ('symbol', 'rs4')
753 (list
817 (list
754 (list
818 (list
755 (list
819 (list
756 (or
820 (or
757 ('symbol', '2')
821 ('symbol', '2')
758 ('symbol', '3'))
822 ('symbol', '3'))
759 ('symbol', 'x'))
823 ('symbol', 'x'))
760 ('symbol', 'x'))
824 ('symbol', 'x'))
761 ('symbol', 'date')))
825 ('symbol', 'date')))
762 (func
826 (func
763 ('symbol', 'reverse')
827 ('symbol', 'reverse')
764 (func
828 (func
765 ('symbol', 'sort')
829 ('symbol', 'sort')
766 (list
830 (list
767 (or
831 (or
768 ('symbol', '2')
832 ('symbol', '2')
769 ('symbol', '3'))
833 ('symbol', '3'))
770 ('symbol', 'date'))))
834 ('symbol', 'date'))))
771 3
835 3
772 2
836 2
773
837
774 issue2549 - correct optimizations
838 issue2549 - correct optimizations
775
839
776 $ log 'limit(1 or 2 or 3, 2) and not 2'
840 $ log 'limit(1 or 2 or 3, 2) and not 2'
777 1
841 1
778 $ log 'max(1 or 2) and not 2'
842 $ log 'max(1 or 2) and not 2'
779 $ log 'min(1 or 2) and not 1'
843 $ log 'min(1 or 2) and not 1'
780 $ log 'last(1 or 2, 1) and not 2'
844 $ log 'last(1 or 2, 1) and not 2'
781
845
782 test revsets started with 40-chars hash (issue3669)
846 test revsets started with 40-chars hash (issue3669)
783
847
784 $ ISSUE3669_TIP=`hg tip --template '{node}'`
848 $ ISSUE3669_TIP=`hg tip --template '{node}'`
785 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
849 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
786 9
850 9
787 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
851 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
788 8
852 8
789
853
790 test or-ed indirect predicates (issue3775)
854 test or-ed indirect predicates (issue3775)
791
855
792 $ log '6 or 6^1' | sort
856 $ log '6 or 6^1' | sort
793 5
857 5
794 6
858 6
795 $ log '6^1 or 6' | sort
859 $ log '6^1 or 6' | sort
796 5
860 5
797 6
861 6
798 $ log '4 or 4~1' | sort
862 $ log '4 or 4~1' | sort
799 2
863 2
800 4
864 4
801 $ log '4~1 or 4' | sort
865 $ log '4~1 or 4' | sort
802 2
866 2
803 4
867 4
804 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
868 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
805 0
869 0
806 1
870 1
807 2
871 2
808 3
872 3
809 4
873 4
810 5
874 5
811 6
875 6
812 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
876 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
813 0
877 0
814 1
878 1
815 2
879 2
816 3
880 3
817 4
881 4
818 5
882 5
819 6
883 6
820
884
821 tests for 'remote()' predicate:
885 tests for 'remote()' predicate:
822 #. (csets in remote) (id) (remote)
886 #. (csets in remote) (id) (remote)
823 1. less than local current branch "default"
887 1. less than local current branch "default"
824 2. same with local specified "default"
888 2. same with local specified "default"
825 3. more than local specified specified
889 3. more than local specified specified
826
890
827 $ hg clone --quiet -U . ../remote3
891 $ hg clone --quiet -U . ../remote3
828 $ cd ../remote3
892 $ cd ../remote3
829 $ hg update -q 7
893 $ hg update -q 7
830 $ echo r > r
894 $ echo r > r
831 $ hg ci -Aqm 10
895 $ hg ci -Aqm 10
832 $ log 'remote()'
896 $ log 'remote()'
833 7
897 7
834 $ log 'remote("a-b-c-")'
898 $ log 'remote("a-b-c-")'
835 2
899 2
836 $ cd ../repo
900 $ cd ../repo
837 $ log 'remote(".a.b.c.", "../remote3")'
901 $ log 'remote(".a.b.c.", "../remote3")'
838
902
839 $ cd ..
903 $ cd ..
840
904
841 test author/desc/keyword in problematic encoding
905 test author/desc/keyword in problematic encoding
842 # unicode: cp932:
906 # unicode: cp932:
843 # u30A2 0x83 0x41(= 'A')
907 # u30A2 0x83 0x41(= 'A')
844 # u30C2 0x83 0x61(= 'a')
908 # u30C2 0x83 0x61(= 'a')
845
909
846 $ hg init problematicencoding
910 $ hg init problematicencoding
847 $ cd problematicencoding
911 $ cd problematicencoding
848
912
849 $ python > setup.sh <<EOF
913 $ python > setup.sh <<EOF
850 > print u'''
914 > print u'''
851 > echo a > text
915 > echo a > text
852 > hg add text
916 > hg add text
853 > hg --encoding utf-8 commit -u '\u30A2' -m none
917 > hg --encoding utf-8 commit -u '\u30A2' -m none
854 > echo b > text
918 > echo b > text
855 > hg --encoding utf-8 commit -u '\u30C2' -m none
919 > hg --encoding utf-8 commit -u '\u30C2' -m none
856 > echo c > text
920 > echo c > text
857 > hg --encoding utf-8 commit -u none -m '\u30A2'
921 > hg --encoding utf-8 commit -u none -m '\u30A2'
858 > echo d > text
922 > echo d > text
859 > hg --encoding utf-8 commit -u none -m '\u30C2'
923 > hg --encoding utf-8 commit -u none -m '\u30C2'
860 > '''.encode('utf-8')
924 > '''.encode('utf-8')
861 > EOF
925 > EOF
862 $ sh < setup.sh
926 $ sh < setup.sh
863
927
864 test in problematic encoding
928 test in problematic encoding
865 $ python > test.sh <<EOF
929 $ python > test.sh <<EOF
866 > print u'''
930 > print u'''
867 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
931 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
868 > echo ====
932 > echo ====
869 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
933 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
870 > echo ====
934 > echo ====
871 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
935 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
872 > echo ====
936 > echo ====
873 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
937 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
874 > echo ====
938 > echo ====
875 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
939 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
876 > echo ====
940 > echo ====
877 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
941 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
878 > '''.encode('cp932')
942 > '''.encode('cp932')
879 > EOF
943 > EOF
880 $ sh < test.sh
944 $ sh < test.sh
881 0
945 0
882 ====
946 ====
883 1
947 1
884 ====
948 ====
885 2
949 2
886 ====
950 ====
887 3
951 3
888 ====
952 ====
889 0
953 0
890 2
954 2
891 ====
955 ====
892 1
956 1
893 3
957 3
894
958
895 $ cd ..
959 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now