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