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