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