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