##// END OF EJS Templates
revset: fix adds/modifies/removes and patterns (issue3403)...
Patrick Mezard -
r16521:592701c8 stable
parent child Browse files
Show More
@@ -1,1488 +1,1491 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import re
8 import re
9 import parser, util, error, discovery, hbisect, phases
9 import parser, util, error, discovery, hbisect, phases
10 import node
10 import node
11 import bookmarks as bookmarksmod
11 import bookmarks as bookmarksmod
12 import match as matchmod
12 import match as matchmod
13 from i18n import _
13 from i18n import _
14 import encoding
14 import encoding
15
15
16 def _revancestors(repo, revs, followfirst):
16 def _revancestors(repo, revs, followfirst):
17 """Like revlog.ancestors(), but supports followfirst."""
17 """Like revlog.ancestors(), but supports followfirst."""
18 cut = followfirst and 1 or None
18 cut = followfirst and 1 or None
19 cl = repo.changelog
19 cl = repo.changelog
20 visit = list(revs)
20 visit = list(revs)
21 seen = set([node.nullrev])
21 seen = set([node.nullrev])
22 while visit:
22 while visit:
23 for parent in cl.parentrevs(visit.pop(0))[:cut]:
23 for parent in cl.parentrevs(visit.pop(0))[:cut]:
24 if parent not in seen:
24 if parent not in seen:
25 visit.append(parent)
25 visit.append(parent)
26 seen.add(parent)
26 seen.add(parent)
27 yield parent
27 yield parent
28
28
29 def _revdescendants(repo, revs, followfirst):
29 def _revdescendants(repo, revs, followfirst):
30 """Like revlog.descendants() but supports followfirst."""
30 """Like revlog.descendants() but supports followfirst."""
31 cut = followfirst and 1 or None
31 cut = followfirst and 1 or None
32 cl = repo.changelog
32 cl = repo.changelog
33 first = min(revs)
33 first = min(revs)
34 nullrev = node.nullrev
34 nullrev = node.nullrev
35 if first == nullrev:
35 if first == nullrev:
36 # Are there nodes with a null first parent and a non-null
36 # Are there nodes with a null first parent and a non-null
37 # second one? Maybe. Do we care? Probably not.
37 # second one? Maybe. Do we care? Probably not.
38 for i in cl:
38 for i in cl:
39 yield i
39 yield i
40 return
40 return
41
41
42 seen = set(revs)
42 seen = set(revs)
43 for i in xrange(first + 1, len(cl)):
43 for i in xrange(first + 1, len(cl)):
44 for x in cl.parentrevs(i)[:cut]:
44 for x in cl.parentrevs(i)[:cut]:
45 if x != nullrev and x in seen:
45 if x != nullrev and x in seen:
46 seen.add(i)
46 seen.add(i)
47 yield i
47 yield i
48 break
48 break
49
49
50 elements = {
50 elements = {
51 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
51 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
52 "~": (18, None, ("ancestor", 18)),
52 "~": (18, None, ("ancestor", 18)),
53 "^": (18, None, ("parent", 18), ("parentpost", 18)),
53 "^": (18, None, ("parent", 18), ("parentpost", 18)),
54 "-": (5, ("negate", 19), ("minus", 5)),
54 "-": (5, ("negate", 19), ("minus", 5)),
55 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
55 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
56 ("dagrangepost", 17)),
56 ("dagrangepost", 17)),
57 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
57 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
58 ("dagrangepost", 17)),
58 ("dagrangepost", 17)),
59 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
59 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
60 "not": (10, ("not", 10)),
60 "not": (10, ("not", 10)),
61 "!": (10, ("not", 10)),
61 "!": (10, ("not", 10)),
62 "and": (5, None, ("and", 5)),
62 "and": (5, None, ("and", 5)),
63 "&": (5, None, ("and", 5)),
63 "&": (5, None, ("and", 5)),
64 "or": (4, None, ("or", 4)),
64 "or": (4, None, ("or", 4)),
65 "|": (4, None, ("or", 4)),
65 "|": (4, None, ("or", 4)),
66 "+": (4, None, ("or", 4)),
66 "+": (4, None, ("or", 4)),
67 ",": (2, None, ("list", 2)),
67 ",": (2, None, ("list", 2)),
68 ")": (0, None, None),
68 ")": (0, None, None),
69 "symbol": (0, ("symbol",), None),
69 "symbol": (0, ("symbol",), None),
70 "string": (0, ("string",), None),
70 "string": (0, ("string",), None),
71 "end": (0, None, None),
71 "end": (0, None, None),
72 }
72 }
73
73
74 keywords = set(['and', 'or', 'not'])
74 keywords = set(['and', 'or', 'not'])
75
75
76 def tokenize(program):
76 def tokenize(program):
77 pos, l = 0, len(program)
77 pos, l = 0, len(program)
78 while pos < l:
78 while pos < l:
79 c = program[pos]
79 c = program[pos]
80 if c.isspace(): # skip inter-token whitespace
80 if c.isspace(): # skip inter-token whitespace
81 pass
81 pass
82 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
82 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
83 yield ('::', None, pos)
83 yield ('::', None, pos)
84 pos += 1 # skip ahead
84 pos += 1 # skip ahead
85 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
85 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
86 yield ('..', None, pos)
86 yield ('..', None, pos)
87 pos += 1 # skip ahead
87 pos += 1 # skip ahead
88 elif c in "():,-|&+!~^": # handle simple operators
88 elif c in "():,-|&+!~^": # handle simple operators
89 yield (c, None, pos)
89 yield (c, None, pos)
90 elif (c in '"\'' or c == 'r' and
90 elif (c in '"\'' or c == 'r' and
91 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
91 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
92 if c == 'r':
92 if c == 'r':
93 pos += 1
93 pos += 1
94 c = program[pos]
94 c = program[pos]
95 decode = lambda x: x
95 decode = lambda x: x
96 else:
96 else:
97 decode = lambda x: x.decode('string-escape')
97 decode = lambda x: x.decode('string-escape')
98 pos += 1
98 pos += 1
99 s = pos
99 s = pos
100 while pos < l: # find closing quote
100 while pos < l: # find closing quote
101 d = program[pos]
101 d = program[pos]
102 if d == '\\': # skip over escaped characters
102 if d == '\\': # skip over escaped characters
103 pos += 2
103 pos += 2
104 continue
104 continue
105 if d == c:
105 if d == c:
106 yield ('string', decode(program[s:pos]), s)
106 yield ('string', decode(program[s:pos]), s)
107 break
107 break
108 pos += 1
108 pos += 1
109 else:
109 else:
110 raise error.ParseError(_("unterminated string"), s)
110 raise error.ParseError(_("unterminated string"), s)
111 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
111 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
112 s = pos
112 s = pos
113 pos += 1
113 pos += 1
114 while pos < l: # find end of symbol
114 while pos < l: # find end of symbol
115 d = program[pos]
115 d = program[pos]
116 if not (d.isalnum() or d in "._/" or ord(d) > 127):
116 if not (d.isalnum() or d in "._/" or ord(d) > 127):
117 break
117 break
118 if d == '.' and program[pos - 1] == '.': # special case for ..
118 if d == '.' and program[pos - 1] == '.': # special case for ..
119 pos -= 1
119 pos -= 1
120 break
120 break
121 pos += 1
121 pos += 1
122 sym = program[s:pos]
122 sym = program[s:pos]
123 if sym in keywords: # operator keywords
123 if sym in keywords: # operator keywords
124 yield (sym, None, s)
124 yield (sym, None, s)
125 else:
125 else:
126 yield ('symbol', sym, s)
126 yield ('symbol', sym, s)
127 pos -= 1
127 pos -= 1
128 else:
128 else:
129 raise error.ParseError(_("syntax error"), pos)
129 raise error.ParseError(_("syntax error"), pos)
130 pos += 1
130 pos += 1
131 yield ('end', None, pos)
131 yield ('end', None, pos)
132
132
133 # helpers
133 # helpers
134
134
135 def getstring(x, err):
135 def getstring(x, err):
136 if x and (x[0] == 'string' or x[0] == 'symbol'):
136 if x and (x[0] == 'string' or x[0] == 'symbol'):
137 return x[1]
137 return x[1]
138 raise error.ParseError(err)
138 raise error.ParseError(err)
139
139
140 def getlist(x):
140 def getlist(x):
141 if not x:
141 if not x:
142 return []
142 return []
143 if x[0] == 'list':
143 if x[0] == 'list':
144 return getlist(x[1]) + [x[2]]
144 return getlist(x[1]) + [x[2]]
145 return [x]
145 return [x]
146
146
147 def getargs(x, min, max, err):
147 def getargs(x, min, max, err):
148 l = getlist(x)
148 l = getlist(x)
149 if len(l) < min or (max >= 0 and len(l) > max):
149 if len(l) < min or (max >= 0 and len(l) > max):
150 raise error.ParseError(err)
150 raise error.ParseError(err)
151 return l
151 return l
152
152
153 def getset(repo, subset, x):
153 def getset(repo, subset, x):
154 if not x:
154 if not x:
155 raise error.ParseError(_("missing argument"))
155 raise error.ParseError(_("missing argument"))
156 return methods[x[0]](repo, subset, *x[1:])
156 return methods[x[0]](repo, subset, *x[1:])
157
157
158 # operator methods
158 # operator methods
159
159
160 def stringset(repo, subset, x):
160 def stringset(repo, subset, x):
161 x = repo[x].rev()
161 x = repo[x].rev()
162 if x == -1 and len(subset) == len(repo):
162 if x == -1 and len(subset) == len(repo):
163 return [-1]
163 return [-1]
164 if len(subset) == len(repo) or x in subset:
164 if len(subset) == len(repo) or x in subset:
165 return [x]
165 return [x]
166 return []
166 return []
167
167
168 def symbolset(repo, subset, x):
168 def symbolset(repo, subset, x):
169 if x in symbols:
169 if x in symbols:
170 raise error.ParseError(_("can't use %s here") % x)
170 raise error.ParseError(_("can't use %s here") % x)
171 return stringset(repo, subset, x)
171 return stringset(repo, subset, x)
172
172
173 def rangeset(repo, subset, x, y):
173 def rangeset(repo, subset, x, y):
174 m = getset(repo, subset, x)
174 m = getset(repo, subset, x)
175 if not m:
175 if not m:
176 m = getset(repo, range(len(repo)), x)
176 m = getset(repo, range(len(repo)), x)
177
177
178 n = getset(repo, subset, y)
178 n = getset(repo, subset, y)
179 if not n:
179 if not n:
180 n = getset(repo, range(len(repo)), y)
180 n = getset(repo, range(len(repo)), y)
181
181
182 if not m or not n:
182 if not m or not n:
183 return []
183 return []
184 m, n = m[0], n[-1]
184 m, n = m[0], n[-1]
185
185
186 if m < n:
186 if m < n:
187 r = range(m, n + 1)
187 r = range(m, n + 1)
188 else:
188 else:
189 r = range(m, n - 1, -1)
189 r = range(m, n - 1, -1)
190 s = set(subset)
190 s = set(subset)
191 return [x for x in r if x in s]
191 return [x for x in r if x in s]
192
192
193 def andset(repo, subset, x, y):
193 def andset(repo, subset, x, y):
194 return getset(repo, getset(repo, subset, x), y)
194 return getset(repo, getset(repo, subset, x), y)
195
195
196 def orset(repo, subset, x, y):
196 def orset(repo, subset, x, y):
197 xl = getset(repo, subset, x)
197 xl = getset(repo, subset, x)
198 s = set(xl)
198 s = set(xl)
199 yl = getset(repo, [r for r in subset if r not in s], y)
199 yl = getset(repo, [r for r in subset if r not in s], y)
200 return xl + yl
200 return xl + yl
201
201
202 def notset(repo, subset, x):
202 def notset(repo, subset, x):
203 s = set(getset(repo, subset, x))
203 s = set(getset(repo, subset, x))
204 return [r for r in subset if r not in s]
204 return [r for r in subset if r not in s]
205
205
206 def listset(repo, subset, a, b):
206 def listset(repo, subset, a, b):
207 raise error.ParseError(_("can't use a list in this context"))
207 raise error.ParseError(_("can't use a list in this context"))
208
208
209 def func(repo, subset, a, b):
209 def func(repo, subset, a, b):
210 if a[0] == 'symbol' and a[1] in symbols:
210 if a[0] == 'symbol' and a[1] in symbols:
211 return symbols[a[1]](repo, subset, b)
211 return symbols[a[1]](repo, subset, b)
212 raise error.ParseError(_("not a function: %s") % a[1])
212 raise error.ParseError(_("not a function: %s") % a[1])
213
213
214 # functions
214 # functions
215
215
216 def adds(repo, subset, x):
216 def adds(repo, subset, x):
217 """``adds(pattern)``
217 """``adds(pattern)``
218 Changesets that add a file matching pattern.
218 Changesets that add a file matching pattern.
219 """
219 """
220 # i18n: "adds" is a keyword
220 # i18n: "adds" is a keyword
221 pat = getstring(x, _("adds requires a pattern"))
221 pat = getstring(x, _("adds requires a pattern"))
222 return checkstatus(repo, subset, pat, 1)
222 return checkstatus(repo, subset, pat, 1)
223
223
224 def ancestor(repo, subset, x):
224 def ancestor(repo, subset, x):
225 """``ancestor(single, single)``
225 """``ancestor(single, single)``
226 Greatest common ancestor of the two changesets.
226 Greatest common ancestor of the two changesets.
227 """
227 """
228 # i18n: "ancestor" is a keyword
228 # i18n: "ancestor" is a keyword
229 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
229 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
230 r = range(len(repo))
230 r = range(len(repo))
231 a = getset(repo, r, l[0])
231 a = getset(repo, r, l[0])
232 b = getset(repo, r, l[1])
232 b = getset(repo, r, l[1])
233 if len(a) != 1 or len(b) != 1:
233 if len(a) != 1 or len(b) != 1:
234 # i18n: "ancestor" is a keyword
234 # i18n: "ancestor" is a keyword
235 raise error.ParseError(_("ancestor arguments must be single revisions"))
235 raise error.ParseError(_("ancestor arguments must be single revisions"))
236 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
236 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
237
237
238 return [r for r in an if r in subset]
238 return [r for r in an if r in subset]
239
239
240 def _ancestors(repo, subset, x, followfirst=False):
240 def _ancestors(repo, subset, x, followfirst=False):
241 args = getset(repo, range(len(repo)), x)
241 args = getset(repo, range(len(repo)), x)
242 if not args:
242 if not args:
243 return []
243 return []
244 s = set(_revancestors(repo, args, followfirst)) | set(args)
244 s = set(_revancestors(repo, args, followfirst)) | set(args)
245 return [r for r in subset if r in s]
245 return [r for r in subset if r in s]
246
246
247 def ancestors(repo, subset, x):
247 def ancestors(repo, subset, x):
248 """``ancestors(set)``
248 """``ancestors(set)``
249 Changesets that are ancestors of a changeset in set.
249 Changesets that are ancestors of a changeset in set.
250 """
250 """
251 return _ancestors(repo, subset, x)
251 return _ancestors(repo, subset, x)
252
252
253 def _firstancestors(repo, subset, x):
253 def _firstancestors(repo, subset, x):
254 # ``_firstancestors(set)``
254 # ``_firstancestors(set)``
255 # Like ``ancestors(set)`` but follows only the first parents.
255 # Like ``ancestors(set)`` but follows only the first parents.
256 return _ancestors(repo, subset, x, followfirst=True)
256 return _ancestors(repo, subset, x, followfirst=True)
257
257
258 def ancestorspec(repo, subset, x, n):
258 def ancestorspec(repo, subset, x, n):
259 """``set~n``
259 """``set~n``
260 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
260 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
261 """
261 """
262 try:
262 try:
263 n = int(n[1])
263 n = int(n[1])
264 except (TypeError, ValueError):
264 except (TypeError, ValueError):
265 raise error.ParseError(_("~ expects a number"))
265 raise error.ParseError(_("~ expects a number"))
266 ps = set()
266 ps = set()
267 cl = repo.changelog
267 cl = repo.changelog
268 for r in getset(repo, subset, x):
268 for r in getset(repo, subset, x):
269 for i in range(n):
269 for i in range(n):
270 r = cl.parentrevs(r)[0]
270 r = cl.parentrevs(r)[0]
271 ps.add(r)
271 ps.add(r)
272 return [r for r in subset if r in ps]
272 return [r for r in subset if r in ps]
273
273
274 def author(repo, subset, x):
274 def author(repo, subset, x):
275 """``author(string)``
275 """``author(string)``
276 Alias for ``user(string)``.
276 Alias for ``user(string)``.
277 """
277 """
278 # i18n: "author" is a keyword
278 # i18n: "author" is a keyword
279 n = encoding.lower(getstring(x, _("author requires a string")))
279 n = encoding.lower(getstring(x, _("author requires a string")))
280 return [r for r in subset if n in encoding.lower(repo[r].user())]
280 return [r for r in subset if n in encoding.lower(repo[r].user())]
281
281
282 def bisect(repo, subset, x):
282 def bisect(repo, subset, x):
283 """``bisect(string)``
283 """``bisect(string)``
284 Changesets marked in the specified bisect status:
284 Changesets marked in the specified bisect status:
285
285
286 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
286 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
287 - ``goods``, ``bads`` : csets topologicaly good/bad
287 - ``goods``, ``bads`` : csets topologicaly good/bad
288 - ``range`` : csets taking part in the bisection
288 - ``range`` : csets taking part in the bisection
289 - ``pruned`` : csets that are goods, bads or skipped
289 - ``pruned`` : csets that are goods, bads or skipped
290 - ``untested`` : csets whose fate is yet unknown
290 - ``untested`` : csets whose fate is yet unknown
291 - ``ignored`` : csets ignored due to DAG topology
291 - ``ignored`` : csets ignored due to DAG topology
292 """
292 """
293 status = getstring(x, _("bisect requires a string")).lower()
293 status = getstring(x, _("bisect requires a string")).lower()
294 state = set(hbisect.get(repo, status))
294 state = set(hbisect.get(repo, status))
295 return [r for r in subset if r in state]
295 return [r for r in subset if r in state]
296
296
297 # Backward-compatibility
297 # Backward-compatibility
298 # - no help entry so that we do not advertise it any more
298 # - no help entry so that we do not advertise it any more
299 def bisected(repo, subset, x):
299 def bisected(repo, subset, x):
300 return bisect(repo, subset, x)
300 return bisect(repo, subset, x)
301
301
302 def bookmark(repo, subset, x):
302 def bookmark(repo, subset, x):
303 """``bookmark([name])``
303 """``bookmark([name])``
304 The named bookmark or all bookmarks.
304 The named bookmark or all bookmarks.
305 """
305 """
306 # i18n: "bookmark" is a keyword
306 # i18n: "bookmark" is a keyword
307 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
307 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
308 if args:
308 if args:
309 bm = getstring(args[0],
309 bm = getstring(args[0],
310 # i18n: "bookmark" is a keyword
310 # i18n: "bookmark" is a keyword
311 _('the argument to bookmark must be a string'))
311 _('the argument to bookmark must be a string'))
312 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
312 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
313 if not bmrev:
313 if not bmrev:
314 raise util.Abort(_("bookmark '%s' does not exist") % bm)
314 raise util.Abort(_("bookmark '%s' does not exist") % bm)
315 bmrev = repo[bmrev].rev()
315 bmrev = repo[bmrev].rev()
316 return [r for r in subset if r == bmrev]
316 return [r for r in subset if r == bmrev]
317 bms = set([repo[r].rev()
317 bms = set([repo[r].rev()
318 for r in bookmarksmod.listbookmarks(repo).values()])
318 for r in bookmarksmod.listbookmarks(repo).values()])
319 return [r for r in subset if r in bms]
319 return [r for r in subset if r in bms]
320
320
321 def branch(repo, subset, x):
321 def branch(repo, subset, x):
322 """``branch(string or set)``
322 """``branch(string or set)``
323 All changesets belonging to the given branch or the branches of the given
323 All changesets belonging to the given branch or the branches of the given
324 changesets.
324 changesets.
325 """
325 """
326 try:
326 try:
327 b = getstring(x, '')
327 b = getstring(x, '')
328 if b in repo.branchmap():
328 if b in repo.branchmap():
329 return [r for r in subset if repo[r].branch() == b]
329 return [r for r in subset if repo[r].branch() == b]
330 except error.ParseError:
330 except error.ParseError:
331 # not a string, but another revspec, e.g. tip()
331 # not a string, but another revspec, e.g. tip()
332 pass
332 pass
333
333
334 s = getset(repo, range(len(repo)), x)
334 s = getset(repo, range(len(repo)), x)
335 b = set()
335 b = set()
336 for r in s:
336 for r in s:
337 b.add(repo[r].branch())
337 b.add(repo[r].branch())
338 s = set(s)
338 s = set(s)
339 return [r for r in subset if r in s or repo[r].branch() in b]
339 return [r for r in subset if r in s or repo[r].branch() in b]
340
340
341 def checkstatus(repo, subset, pat, field):
341 def checkstatus(repo, subset, pat, field):
342 m = None
342 m = None
343 s = []
343 s = []
344 fast = not matchmod.patkind(pat)
344 hasset = matchmod.patkind(pat) == 'set'
345 fname = None
345 for r in subset:
346 for r in subset:
346 c = repo[r]
347 c = repo[r]
347 if fast:
348 if not m or hasset:
348 if pat not in c.files():
349 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
350 if not m.anypats() and len(m.files()) == 1:
351 fname = m.files()[0]
352 if fname is not None:
353 if fname not in c.files():
349 continue
354 continue
350 else:
355 else:
351 if not m or matchmod.patkind(pat) == 'set':
352 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
353 for f in c.files():
356 for f in c.files():
354 if m(f):
357 if m(f):
355 break
358 break
356 else:
359 else:
357 continue
360 continue
358 files = repo.status(c.p1().node(), c.node())[field]
361 files = repo.status(c.p1().node(), c.node())[field]
359 if fast:
362 if fname is not None:
360 if pat in files:
363 if fname in files:
361 s.append(r)
364 s.append(r)
362 else:
365 else:
363 for f in files:
366 for f in files:
364 if m(f):
367 if m(f):
365 s.append(r)
368 s.append(r)
366 break
369 break
367 return s
370 return s
368
371
369 def _children(repo, narrow, parentset):
372 def _children(repo, narrow, parentset):
370 cs = set()
373 cs = set()
371 pr = repo.changelog.parentrevs
374 pr = repo.changelog.parentrevs
372 for r in narrow:
375 for r in narrow:
373 for p in pr(r):
376 for p in pr(r):
374 if p in parentset:
377 if p in parentset:
375 cs.add(r)
378 cs.add(r)
376 return cs
379 return cs
377
380
378 def children(repo, subset, x):
381 def children(repo, subset, x):
379 """``children(set)``
382 """``children(set)``
380 Child changesets of changesets in set.
383 Child changesets of changesets in set.
381 """
384 """
382 s = set(getset(repo, range(len(repo)), x))
385 s = set(getset(repo, range(len(repo)), x))
383 cs = _children(repo, subset, s)
386 cs = _children(repo, subset, s)
384 return [r for r in subset if r in cs]
387 return [r for r in subset if r in cs]
385
388
386 def closed(repo, subset, x):
389 def closed(repo, subset, x):
387 """``closed()``
390 """``closed()``
388 Changeset is closed.
391 Changeset is closed.
389 """
392 """
390 # i18n: "closed" is a keyword
393 # i18n: "closed" is a keyword
391 getargs(x, 0, 0, _("closed takes no arguments"))
394 getargs(x, 0, 0, _("closed takes no arguments"))
392 return [r for r in subset if repo[r].extra().get('close')]
395 return [r for r in subset if repo[r].extra().get('close')]
393
396
394 def contains(repo, subset, x):
397 def contains(repo, subset, x):
395 """``contains(pattern)``
398 """``contains(pattern)``
396 Revision contains a file matching pattern. See :hg:`help patterns`
399 Revision contains a file matching pattern. See :hg:`help patterns`
397 for information about file patterns.
400 for information about file patterns.
398 """
401 """
399 # i18n: "contains" is a keyword
402 # i18n: "contains" is a keyword
400 pat = getstring(x, _("contains requires a pattern"))
403 pat = getstring(x, _("contains requires a pattern"))
401 m = None
404 m = None
402 s = []
405 s = []
403 if not matchmod.patkind(pat):
406 if not matchmod.patkind(pat):
404 for r in subset:
407 for r in subset:
405 if pat in repo[r]:
408 if pat in repo[r]:
406 s.append(r)
409 s.append(r)
407 else:
410 else:
408 for r in subset:
411 for r in subset:
409 c = repo[r]
412 c = repo[r]
410 if not m or matchmod.patkind(pat) == 'set':
413 if not m or matchmod.patkind(pat) == 'set':
411 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
414 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
412 for f in c.manifest():
415 for f in c.manifest():
413 if m(f):
416 if m(f):
414 s.append(r)
417 s.append(r)
415 break
418 break
416 return s
419 return s
417
420
418 def date(repo, subset, x):
421 def date(repo, subset, x):
419 """``date(interval)``
422 """``date(interval)``
420 Changesets within the interval, see :hg:`help dates`.
423 Changesets within the interval, see :hg:`help dates`.
421 """
424 """
422 # i18n: "date" is a keyword
425 # i18n: "date" is a keyword
423 ds = getstring(x, _("date requires a string"))
426 ds = getstring(x, _("date requires a string"))
424 dm = util.matchdate(ds)
427 dm = util.matchdate(ds)
425 return [r for r in subset if dm(repo[r].date()[0])]
428 return [r for r in subset if dm(repo[r].date()[0])]
426
429
427 def desc(repo, subset, x):
430 def desc(repo, subset, x):
428 """``desc(string)``
431 """``desc(string)``
429 Search commit message for string. The match is case-insensitive.
432 Search commit message for string. The match is case-insensitive.
430 """
433 """
431 # i18n: "desc" is a keyword
434 # i18n: "desc" is a keyword
432 ds = encoding.lower(getstring(x, _("desc requires a string")))
435 ds = encoding.lower(getstring(x, _("desc requires a string")))
433 l = []
436 l = []
434 for r in subset:
437 for r in subset:
435 c = repo[r]
438 c = repo[r]
436 if ds in encoding.lower(c.description()):
439 if ds in encoding.lower(c.description()):
437 l.append(r)
440 l.append(r)
438 return l
441 return l
439
442
440 def _descendants(repo, subset, x, followfirst=False):
443 def _descendants(repo, subset, x, followfirst=False):
441 args = getset(repo, range(len(repo)), x)
444 args = getset(repo, range(len(repo)), x)
442 if not args:
445 if not args:
443 return []
446 return []
444 s = set(_revdescendants(repo, args, followfirst)) | set(args)
447 s = set(_revdescendants(repo, args, followfirst)) | set(args)
445 return [r for r in subset if r in s]
448 return [r for r in subset if r in s]
446
449
447 def descendants(repo, subset, x):
450 def descendants(repo, subset, x):
448 """``descendants(set)``
451 """``descendants(set)``
449 Changesets which are descendants of changesets in set.
452 Changesets which are descendants of changesets in set.
450 """
453 """
451 return _descendants(repo, subset, x)
454 return _descendants(repo, subset, x)
452
455
453 def _firstdescendants(repo, subset, x):
456 def _firstdescendants(repo, subset, x):
454 # ``_firstdescendants(set)``
457 # ``_firstdescendants(set)``
455 # Like ``descendants(set)`` but follows only the first parents.
458 # Like ``descendants(set)`` but follows only the first parents.
456 return _descendants(repo, subset, x, followfirst=True)
459 return _descendants(repo, subset, x, followfirst=True)
457
460
458 def draft(repo, subset, x):
461 def draft(repo, subset, x):
459 """``draft()``
462 """``draft()``
460 Changeset in draft phase."""
463 Changeset in draft phase."""
461 getargs(x, 0, 0, _("draft takes no arguments"))
464 getargs(x, 0, 0, _("draft takes no arguments"))
462 return [r for r in subset if repo._phaserev[r] == phases.draft]
465 return [r for r in subset if repo._phaserev[r] == phases.draft]
463
466
464 def filelog(repo, subset, x):
467 def filelog(repo, subset, x):
465 """``filelog(pattern)``
468 """``filelog(pattern)``
466 Changesets connected to the specified filelog.
469 Changesets connected to the specified filelog.
467 """
470 """
468
471
469 pat = getstring(x, _("filelog requires a pattern"))
472 pat = getstring(x, _("filelog requires a pattern"))
470 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
473 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
471 ctx=repo[None])
474 ctx=repo[None])
472 s = set()
475 s = set()
473
476
474 if not matchmod.patkind(pat):
477 if not matchmod.patkind(pat):
475 for f in m.files():
478 for f in m.files():
476 fl = repo.file(f)
479 fl = repo.file(f)
477 for fr in fl:
480 for fr in fl:
478 s.add(fl.linkrev(fr))
481 s.add(fl.linkrev(fr))
479 else:
482 else:
480 for f in repo[None]:
483 for f in repo[None]:
481 if m(f):
484 if m(f):
482 fl = repo.file(f)
485 fl = repo.file(f)
483 for fr in fl:
486 for fr in fl:
484 s.add(fl.linkrev(fr))
487 s.add(fl.linkrev(fr))
485
488
486 return [r for r in subset if r in s]
489 return [r for r in subset if r in s]
487
490
488 def first(repo, subset, x):
491 def first(repo, subset, x):
489 """``first(set, [n])``
492 """``first(set, [n])``
490 An alias for limit().
493 An alias for limit().
491 """
494 """
492 return limit(repo, subset, x)
495 return limit(repo, subset, x)
493
496
494 def _follow(repo, subset, x, name, followfirst=False):
497 def _follow(repo, subset, x, name, followfirst=False):
495 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
498 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
496 c = repo['.']
499 c = repo['.']
497 if l:
500 if l:
498 x = getstring(l[0], _("%s expected a filename") % name)
501 x = getstring(l[0], _("%s expected a filename") % name)
499 if x in c:
502 if x in c:
500 cx = c[x]
503 cx = c[x]
501 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
504 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
502 # include the revision responsible for the most recent version
505 # include the revision responsible for the most recent version
503 s.add(cx.linkrev())
506 s.add(cx.linkrev())
504 else:
507 else:
505 return []
508 return []
506 else:
509 else:
507 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
510 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
508
511
509 return [r for r in subset if r in s]
512 return [r for r in subset if r in s]
510
513
511 def follow(repo, subset, x):
514 def follow(repo, subset, x):
512 """``follow([file])``
515 """``follow([file])``
513 An alias for ``::.`` (ancestors of the working copy's first parent).
516 An alias for ``::.`` (ancestors of the working copy's first parent).
514 If a filename is specified, the history of the given file is followed,
517 If a filename is specified, the history of the given file is followed,
515 including copies.
518 including copies.
516 """
519 """
517 return _follow(repo, subset, x, 'follow')
520 return _follow(repo, subset, x, 'follow')
518
521
519 def _followfirst(repo, subset, x):
522 def _followfirst(repo, subset, x):
520 # ``followfirst([file])``
523 # ``followfirst([file])``
521 # Like ``follow([file])`` but follows only the first parent of
524 # Like ``follow([file])`` but follows only the first parent of
522 # every revision or file revision.
525 # every revision or file revision.
523 return _follow(repo, subset, x, '_followfirst', followfirst=True)
526 return _follow(repo, subset, x, '_followfirst', followfirst=True)
524
527
525 def getall(repo, subset, x):
528 def getall(repo, subset, x):
526 """``all()``
529 """``all()``
527 All changesets, the same as ``0:tip``.
530 All changesets, the same as ``0:tip``.
528 """
531 """
529 # i18n: "all" is a keyword
532 # i18n: "all" is a keyword
530 getargs(x, 0, 0, _("all takes no arguments"))
533 getargs(x, 0, 0, _("all takes no arguments"))
531 return subset
534 return subset
532
535
533 def grep(repo, subset, x):
536 def grep(repo, subset, x):
534 """``grep(regex)``
537 """``grep(regex)``
535 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
538 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
536 to ensure special escape characters are handled correctly. Unlike
539 to ensure special escape characters are handled correctly. Unlike
537 ``keyword(string)``, the match is case-sensitive.
540 ``keyword(string)``, the match is case-sensitive.
538 """
541 """
539 try:
542 try:
540 # i18n: "grep" is a keyword
543 # i18n: "grep" is a keyword
541 gr = re.compile(getstring(x, _("grep requires a string")))
544 gr = re.compile(getstring(x, _("grep requires a string")))
542 except re.error, e:
545 except re.error, e:
543 raise error.ParseError(_('invalid match pattern: %s') % e)
546 raise error.ParseError(_('invalid match pattern: %s') % e)
544 l = []
547 l = []
545 for r in subset:
548 for r in subset:
546 c = repo[r]
549 c = repo[r]
547 for e in c.files() + [c.user(), c.description()]:
550 for e in c.files() + [c.user(), c.description()]:
548 if gr.search(e):
551 if gr.search(e):
549 l.append(r)
552 l.append(r)
550 break
553 break
551 return l
554 return l
552
555
553 def _matchfiles(repo, subset, x):
556 def _matchfiles(repo, subset, x):
554 # _matchfiles takes a revset list of prefixed arguments:
557 # _matchfiles takes a revset list of prefixed arguments:
555 #
558 #
556 # [p:foo, i:bar, x:baz]
559 # [p:foo, i:bar, x:baz]
557 #
560 #
558 # builds a match object from them and filters subset. Allowed
561 # builds a match object from them and filters subset. Allowed
559 # prefixes are 'p:' for regular patterns, 'i:' for include
562 # prefixes are 'p:' for regular patterns, 'i:' for include
560 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
563 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
561 # a revision identifier, or the empty string to reference the
564 # a revision identifier, or the empty string to reference the
562 # working directory, from which the match object is
565 # working directory, from which the match object is
563 # initialized. Use 'd:' to set the default matching mode, default
566 # initialized. Use 'd:' to set the default matching mode, default
564 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
567 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
565
568
566 # i18n: "_matchfiles" is a keyword
569 # i18n: "_matchfiles" is a keyword
567 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
570 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
568 pats, inc, exc = [], [], []
571 pats, inc, exc = [], [], []
569 hasset = False
572 hasset = False
570 rev, default = None, None
573 rev, default = None, None
571 for arg in l:
574 for arg in l:
572 s = getstring(arg, _("_matchfiles requires string arguments"))
575 s = getstring(arg, _("_matchfiles requires string arguments"))
573 prefix, value = s[:2], s[2:]
576 prefix, value = s[:2], s[2:]
574 if prefix == 'p:':
577 if prefix == 'p:':
575 pats.append(value)
578 pats.append(value)
576 elif prefix == 'i:':
579 elif prefix == 'i:':
577 inc.append(value)
580 inc.append(value)
578 elif prefix == 'x:':
581 elif prefix == 'x:':
579 exc.append(value)
582 exc.append(value)
580 elif prefix == 'r:':
583 elif prefix == 'r:':
581 if rev is not None:
584 if rev is not None:
582 raise error.ParseError(_('_matchfiles expected at most one '
585 raise error.ParseError(_('_matchfiles expected at most one '
583 'revision'))
586 'revision'))
584 rev = value
587 rev = value
585 elif prefix == 'd:':
588 elif prefix == 'd:':
586 if default is not None:
589 if default is not None:
587 raise error.ParseError(_('_matchfiles expected at most one '
590 raise error.ParseError(_('_matchfiles expected at most one '
588 'default mode'))
591 'default mode'))
589 default = value
592 default = value
590 else:
593 else:
591 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
594 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
592 if not hasset and matchmod.patkind(value) == 'set':
595 if not hasset and matchmod.patkind(value) == 'set':
593 hasset = True
596 hasset = True
594 if not default:
597 if not default:
595 default = 'glob'
598 default = 'glob'
596 m = None
599 m = None
597 s = []
600 s = []
598 for r in subset:
601 for r in subset:
599 c = repo[r]
602 c = repo[r]
600 if not m or (hasset and rev is None):
603 if not m or (hasset and rev is None):
601 ctx = c
604 ctx = c
602 if rev is not None:
605 if rev is not None:
603 ctx = repo[rev or None]
606 ctx = repo[rev or None]
604 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
607 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
605 exclude=exc, ctx=ctx, default=default)
608 exclude=exc, ctx=ctx, default=default)
606 for f in c.files():
609 for f in c.files():
607 if m(f):
610 if m(f):
608 s.append(r)
611 s.append(r)
609 break
612 break
610 return s
613 return s
611
614
612 def hasfile(repo, subset, x):
615 def hasfile(repo, subset, x):
613 """``file(pattern)``
616 """``file(pattern)``
614 Changesets affecting files matched by pattern.
617 Changesets affecting files matched by pattern.
615 """
618 """
616 # i18n: "file" is a keyword
619 # i18n: "file" is a keyword
617 pat = getstring(x, _("file requires a pattern"))
620 pat = getstring(x, _("file requires a pattern"))
618 return _matchfiles(repo, subset, ('string', 'p:' + pat))
621 return _matchfiles(repo, subset, ('string', 'p:' + pat))
619
622
620 def head(repo, subset, x):
623 def head(repo, subset, x):
621 """``head()``
624 """``head()``
622 Changeset is a named branch head.
625 Changeset is a named branch head.
623 """
626 """
624 # i18n: "head" is a keyword
627 # i18n: "head" is a keyword
625 getargs(x, 0, 0, _("head takes no arguments"))
628 getargs(x, 0, 0, _("head takes no arguments"))
626 hs = set()
629 hs = set()
627 for b, ls in repo.branchmap().iteritems():
630 for b, ls in repo.branchmap().iteritems():
628 hs.update(repo[h].rev() for h in ls)
631 hs.update(repo[h].rev() for h in ls)
629 return [r for r in subset if r in hs]
632 return [r for r in subset if r in hs]
630
633
631 def heads(repo, subset, x):
634 def heads(repo, subset, x):
632 """``heads(set)``
635 """``heads(set)``
633 Members of set with no children in set.
636 Members of set with no children in set.
634 """
637 """
635 s = getset(repo, subset, x)
638 s = getset(repo, subset, x)
636 ps = set(parents(repo, subset, x))
639 ps = set(parents(repo, subset, x))
637 return [r for r in s if r not in ps]
640 return [r for r in s if r not in ps]
638
641
639 def keyword(repo, subset, x):
642 def keyword(repo, subset, x):
640 """``keyword(string)``
643 """``keyword(string)``
641 Search commit message, user name, and names of changed files for
644 Search commit message, user name, and names of changed files for
642 string. The match is case-insensitive.
645 string. The match is case-insensitive.
643 """
646 """
644 # i18n: "keyword" is a keyword
647 # i18n: "keyword" is a keyword
645 kw = encoding.lower(getstring(x, _("keyword requires a string")))
648 kw = encoding.lower(getstring(x, _("keyword requires a string")))
646 l = []
649 l = []
647 for r in subset:
650 for r in subset:
648 c = repo[r]
651 c = repo[r]
649 t = " ".join(c.files() + [c.user(), c.description()])
652 t = " ".join(c.files() + [c.user(), c.description()])
650 if kw in encoding.lower(t):
653 if kw in encoding.lower(t):
651 l.append(r)
654 l.append(r)
652 return l
655 return l
653
656
654 def limit(repo, subset, x):
657 def limit(repo, subset, x):
655 """``limit(set, [n])``
658 """``limit(set, [n])``
656 First n members of set, defaulting to 1.
659 First n members of set, defaulting to 1.
657 """
660 """
658 # i18n: "limit" is a keyword
661 # i18n: "limit" is a keyword
659 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
662 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
660 try:
663 try:
661 lim = 1
664 lim = 1
662 if len(l) == 2:
665 if len(l) == 2:
663 # i18n: "limit" is a keyword
666 # i18n: "limit" is a keyword
664 lim = int(getstring(l[1], _("limit requires a number")))
667 lim = int(getstring(l[1], _("limit requires a number")))
665 except (TypeError, ValueError):
668 except (TypeError, ValueError):
666 # i18n: "limit" is a keyword
669 # i18n: "limit" is a keyword
667 raise error.ParseError(_("limit expects a number"))
670 raise error.ParseError(_("limit expects a number"))
668 ss = set(subset)
671 ss = set(subset)
669 os = getset(repo, range(len(repo)), l[0])[:lim]
672 os = getset(repo, range(len(repo)), l[0])[:lim]
670 return [r for r in os if r in ss]
673 return [r for r in os if r in ss]
671
674
672 def last(repo, subset, x):
675 def last(repo, subset, x):
673 """``last(set, [n])``
676 """``last(set, [n])``
674 Last n members of set, defaulting to 1.
677 Last n members of set, defaulting to 1.
675 """
678 """
676 # i18n: "last" is a keyword
679 # i18n: "last" is a keyword
677 l = getargs(x, 1, 2, _("last requires one or two arguments"))
680 l = getargs(x, 1, 2, _("last requires one or two arguments"))
678 try:
681 try:
679 lim = 1
682 lim = 1
680 if len(l) == 2:
683 if len(l) == 2:
681 # i18n: "last" is a keyword
684 # i18n: "last" is a keyword
682 lim = int(getstring(l[1], _("last requires a number")))
685 lim = int(getstring(l[1], _("last requires a number")))
683 except (TypeError, ValueError):
686 except (TypeError, ValueError):
684 # i18n: "last" is a keyword
687 # i18n: "last" is a keyword
685 raise error.ParseError(_("last expects a number"))
688 raise error.ParseError(_("last expects a number"))
686 ss = set(subset)
689 ss = set(subset)
687 os = getset(repo, range(len(repo)), l[0])[-lim:]
690 os = getset(repo, range(len(repo)), l[0])[-lim:]
688 return [r for r in os if r in ss]
691 return [r for r in os if r in ss]
689
692
690 def maxrev(repo, subset, x):
693 def maxrev(repo, subset, x):
691 """``max(set)``
694 """``max(set)``
692 Changeset with highest revision number in set.
695 Changeset with highest revision number in set.
693 """
696 """
694 os = getset(repo, range(len(repo)), x)
697 os = getset(repo, range(len(repo)), x)
695 if os:
698 if os:
696 m = max(os)
699 m = max(os)
697 if m in subset:
700 if m in subset:
698 return [m]
701 return [m]
699 return []
702 return []
700
703
701 def merge(repo, subset, x):
704 def merge(repo, subset, x):
702 """``merge()``
705 """``merge()``
703 Changeset is a merge changeset.
706 Changeset is a merge changeset.
704 """
707 """
705 # i18n: "merge" is a keyword
708 # i18n: "merge" is a keyword
706 getargs(x, 0, 0, _("merge takes no arguments"))
709 getargs(x, 0, 0, _("merge takes no arguments"))
707 cl = repo.changelog
710 cl = repo.changelog
708 return [r for r in subset if cl.parentrevs(r)[1] != -1]
711 return [r for r in subset if cl.parentrevs(r)[1] != -1]
709
712
710 def minrev(repo, subset, x):
713 def minrev(repo, subset, x):
711 """``min(set)``
714 """``min(set)``
712 Changeset with lowest revision number in set.
715 Changeset with lowest revision number in set.
713 """
716 """
714 os = getset(repo, range(len(repo)), x)
717 os = getset(repo, range(len(repo)), x)
715 if os:
718 if os:
716 m = min(os)
719 m = min(os)
717 if m in subset:
720 if m in subset:
718 return [m]
721 return [m]
719 return []
722 return []
720
723
721 def modifies(repo, subset, x):
724 def modifies(repo, subset, x):
722 """``modifies(pattern)``
725 """``modifies(pattern)``
723 Changesets modifying files matched by pattern.
726 Changesets modifying files matched by pattern.
724 """
727 """
725 # i18n: "modifies" is a keyword
728 # i18n: "modifies" is a keyword
726 pat = getstring(x, _("modifies requires a pattern"))
729 pat = getstring(x, _("modifies requires a pattern"))
727 return checkstatus(repo, subset, pat, 0)
730 return checkstatus(repo, subset, pat, 0)
728
731
729 def node_(repo, subset, x):
732 def node_(repo, subset, x):
730 """``id(string)``
733 """``id(string)``
731 Revision non-ambiguously specified by the given hex string prefix.
734 Revision non-ambiguously specified by the given hex string prefix.
732 """
735 """
733 # i18n: "id" is a keyword
736 # i18n: "id" is a keyword
734 l = getargs(x, 1, 1, _("id requires one argument"))
737 l = getargs(x, 1, 1, _("id requires one argument"))
735 # i18n: "id" is a keyword
738 # i18n: "id" is a keyword
736 n = getstring(l[0], _("id requires a string"))
739 n = getstring(l[0], _("id requires a string"))
737 if len(n) == 40:
740 if len(n) == 40:
738 rn = repo[n].rev()
741 rn = repo[n].rev()
739 else:
742 else:
740 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
743 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
741 return [r for r in subset if r == rn]
744 return [r for r in subset if r == rn]
742
745
743 def outgoing(repo, subset, x):
746 def outgoing(repo, subset, x):
744 """``outgoing([path])``
747 """``outgoing([path])``
745 Changesets not found in the specified destination repository, or the
748 Changesets not found in the specified destination repository, or the
746 default push location.
749 default push location.
747 """
750 """
748 import hg # avoid start-up nasties
751 import hg # avoid start-up nasties
749 # i18n: "outgoing" is a keyword
752 # i18n: "outgoing" is a keyword
750 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
753 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
751 # i18n: "outgoing" is a keyword
754 # i18n: "outgoing" is a keyword
752 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
755 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
753 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
756 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
754 dest, branches = hg.parseurl(dest)
757 dest, branches = hg.parseurl(dest)
755 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
758 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
756 if revs:
759 if revs:
757 revs = [repo.lookup(rev) for rev in revs]
760 revs = [repo.lookup(rev) for rev in revs]
758 other = hg.peer(repo, {}, dest)
761 other = hg.peer(repo, {}, dest)
759 repo.ui.pushbuffer()
762 repo.ui.pushbuffer()
760 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
763 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
761 repo.ui.popbuffer()
764 repo.ui.popbuffer()
762 cl = repo.changelog
765 cl = repo.changelog
763 o = set([cl.rev(r) for r in outgoing.missing])
766 o = set([cl.rev(r) for r in outgoing.missing])
764 return [r for r in subset if r in o]
767 return [r for r in subset if r in o]
765
768
766 def p1(repo, subset, x):
769 def p1(repo, subset, x):
767 """``p1([set])``
770 """``p1([set])``
768 First parent of changesets in set, or the working directory.
771 First parent of changesets in set, or the working directory.
769 """
772 """
770 if x is None:
773 if x is None:
771 p = repo[x].p1().rev()
774 p = repo[x].p1().rev()
772 return [r for r in subset if r == p]
775 return [r for r in subset if r == p]
773
776
774 ps = set()
777 ps = set()
775 cl = repo.changelog
778 cl = repo.changelog
776 for r in getset(repo, range(len(repo)), x):
779 for r in getset(repo, range(len(repo)), x):
777 ps.add(cl.parentrevs(r)[0])
780 ps.add(cl.parentrevs(r)[0])
778 return [r for r in subset if r in ps]
781 return [r for r in subset if r in ps]
779
782
780 def p2(repo, subset, x):
783 def p2(repo, subset, x):
781 """``p2([set])``
784 """``p2([set])``
782 Second parent of changesets in set, or the working directory.
785 Second parent of changesets in set, or the working directory.
783 """
786 """
784 if x is None:
787 if x is None:
785 ps = repo[x].parents()
788 ps = repo[x].parents()
786 try:
789 try:
787 p = ps[1].rev()
790 p = ps[1].rev()
788 return [r for r in subset if r == p]
791 return [r for r in subset if r == p]
789 except IndexError:
792 except IndexError:
790 return []
793 return []
791
794
792 ps = set()
795 ps = set()
793 cl = repo.changelog
796 cl = repo.changelog
794 for r in getset(repo, range(len(repo)), x):
797 for r in getset(repo, range(len(repo)), x):
795 ps.add(cl.parentrevs(r)[1])
798 ps.add(cl.parentrevs(r)[1])
796 return [r for r in subset if r in ps]
799 return [r for r in subset if r in ps]
797
800
798 def parents(repo, subset, x):
801 def parents(repo, subset, x):
799 """``parents([set])``
802 """``parents([set])``
800 The set of all parents for all changesets in set, or the working directory.
803 The set of all parents for all changesets in set, or the working directory.
801 """
804 """
802 if x is None:
805 if x is None:
803 ps = tuple(p.rev() for p in repo[x].parents())
806 ps = tuple(p.rev() for p in repo[x].parents())
804 return [r for r in subset if r in ps]
807 return [r for r in subset if r in ps]
805
808
806 ps = set()
809 ps = set()
807 cl = repo.changelog
810 cl = repo.changelog
808 for r in getset(repo, range(len(repo)), x):
811 for r in getset(repo, range(len(repo)), x):
809 ps.update(cl.parentrevs(r))
812 ps.update(cl.parentrevs(r))
810 return [r for r in subset if r in ps]
813 return [r for r in subset if r in ps]
811
814
812 def parentspec(repo, subset, x, n):
815 def parentspec(repo, subset, x, n):
813 """``set^0``
816 """``set^0``
814 The set.
817 The set.
815 ``set^1`` (or ``set^``), ``set^2``
818 ``set^1`` (or ``set^``), ``set^2``
816 First or second parent, respectively, of all changesets in set.
819 First or second parent, respectively, of all changesets in set.
817 """
820 """
818 try:
821 try:
819 n = int(n[1])
822 n = int(n[1])
820 if n not in (0, 1, 2):
823 if n not in (0, 1, 2):
821 raise ValueError
824 raise ValueError
822 except (TypeError, ValueError):
825 except (TypeError, ValueError):
823 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
826 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
824 ps = set()
827 ps = set()
825 cl = repo.changelog
828 cl = repo.changelog
826 for r in getset(repo, subset, x):
829 for r in getset(repo, subset, x):
827 if n == 0:
830 if n == 0:
828 ps.add(r)
831 ps.add(r)
829 elif n == 1:
832 elif n == 1:
830 ps.add(cl.parentrevs(r)[0])
833 ps.add(cl.parentrevs(r)[0])
831 elif n == 2:
834 elif n == 2:
832 parents = cl.parentrevs(r)
835 parents = cl.parentrevs(r)
833 if len(parents) > 1:
836 if len(parents) > 1:
834 ps.add(parents[1])
837 ps.add(parents[1])
835 return [r for r in subset if r in ps]
838 return [r for r in subset if r in ps]
836
839
837 def present(repo, subset, x):
840 def present(repo, subset, x):
838 """``present(set)``
841 """``present(set)``
839 An empty set, if any revision in set isn't found; otherwise,
842 An empty set, if any revision in set isn't found; otherwise,
840 all revisions in set.
843 all revisions in set.
841 """
844 """
842 try:
845 try:
843 return getset(repo, subset, x)
846 return getset(repo, subset, x)
844 except error.RepoLookupError:
847 except error.RepoLookupError:
845 return []
848 return []
846
849
847 def public(repo, subset, x):
850 def public(repo, subset, x):
848 """``public()``
851 """``public()``
849 Changeset in public phase."""
852 Changeset in public phase."""
850 getargs(x, 0, 0, _("public takes no arguments"))
853 getargs(x, 0, 0, _("public takes no arguments"))
851 return [r for r in subset if repo._phaserev[r] == phases.public]
854 return [r for r in subset if repo._phaserev[r] == phases.public]
852
855
853 def remote(repo, subset, x):
856 def remote(repo, subset, x):
854 """``remote([id [,path]])``
857 """``remote([id [,path]])``
855 Local revision that corresponds to the given identifier in a
858 Local revision that corresponds to the given identifier in a
856 remote repository, if present. Here, the '.' identifier is a
859 remote repository, if present. Here, the '.' identifier is a
857 synonym for the current local branch.
860 synonym for the current local branch.
858 """
861 """
859
862
860 import hg # avoid start-up nasties
863 import hg # avoid start-up nasties
861 # i18n: "remote" is a keyword
864 # i18n: "remote" is a keyword
862 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
865 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
863
866
864 q = '.'
867 q = '.'
865 if len(l) > 0:
868 if len(l) > 0:
866 # i18n: "remote" is a keyword
869 # i18n: "remote" is a keyword
867 q = getstring(l[0], _("remote requires a string id"))
870 q = getstring(l[0], _("remote requires a string id"))
868 if q == '.':
871 if q == '.':
869 q = repo['.'].branch()
872 q = repo['.'].branch()
870
873
871 dest = ''
874 dest = ''
872 if len(l) > 1:
875 if len(l) > 1:
873 # i18n: "remote" is a keyword
876 # i18n: "remote" is a keyword
874 dest = getstring(l[1], _("remote requires a repository path"))
877 dest = getstring(l[1], _("remote requires a repository path"))
875 dest = repo.ui.expandpath(dest or 'default')
878 dest = repo.ui.expandpath(dest or 'default')
876 dest, branches = hg.parseurl(dest)
879 dest, branches = hg.parseurl(dest)
877 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
880 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
878 if revs:
881 if revs:
879 revs = [repo.lookup(rev) for rev in revs]
882 revs = [repo.lookup(rev) for rev in revs]
880 other = hg.peer(repo, {}, dest)
883 other = hg.peer(repo, {}, dest)
881 n = other.lookup(q)
884 n = other.lookup(q)
882 if n in repo:
885 if n in repo:
883 r = repo[n].rev()
886 r = repo[n].rev()
884 if r in subset:
887 if r in subset:
885 return [r]
888 return [r]
886 return []
889 return []
887
890
888 def removes(repo, subset, x):
891 def removes(repo, subset, x):
889 """``removes(pattern)``
892 """``removes(pattern)``
890 Changesets which remove files matching pattern.
893 Changesets which remove files matching pattern.
891 """
894 """
892 # i18n: "removes" is a keyword
895 # i18n: "removes" is a keyword
893 pat = getstring(x, _("removes requires a pattern"))
896 pat = getstring(x, _("removes requires a pattern"))
894 return checkstatus(repo, subset, pat, 2)
897 return checkstatus(repo, subset, pat, 2)
895
898
896 def rev(repo, subset, x):
899 def rev(repo, subset, x):
897 """``rev(number)``
900 """``rev(number)``
898 Revision with the given numeric identifier.
901 Revision with the given numeric identifier.
899 """
902 """
900 # i18n: "rev" is a keyword
903 # i18n: "rev" is a keyword
901 l = getargs(x, 1, 1, _("rev requires one argument"))
904 l = getargs(x, 1, 1, _("rev requires one argument"))
902 try:
905 try:
903 # i18n: "rev" is a keyword
906 # i18n: "rev" is a keyword
904 l = int(getstring(l[0], _("rev requires a number")))
907 l = int(getstring(l[0], _("rev requires a number")))
905 except (TypeError, ValueError):
908 except (TypeError, ValueError):
906 # i18n: "rev" is a keyword
909 # i18n: "rev" is a keyword
907 raise error.ParseError(_("rev expects a number"))
910 raise error.ParseError(_("rev expects a number"))
908 return [r for r in subset if r == l]
911 return [r for r in subset if r == l]
909
912
910 def matching(repo, subset, x):
913 def matching(repo, subset, x):
911 """``matching(revision [, field])``
914 """``matching(revision [, field])``
912 Changesets in which a given set of fields match the set of fields in the
915 Changesets in which a given set of fields match the set of fields in the
913 selected revision or set.
916 selected revision or set.
914 To match more than one field pass the list of fields to match separated
917 To match more than one field pass the list of fields to match separated
915 by spaces (e.g. 'author description').
918 by spaces (e.g. 'author description').
916 Valid fields are most regular revision fields and some special fields:
919 Valid fields are most regular revision fields and some special fields:
917 * regular fields:
920 * regular fields:
918 - description, author, branch, date, files, phase, parents,
921 - description, author, branch, date, files, phase, parents,
919 substate, user.
922 substate, user.
920 Note that author and user are synonyms.
923 Note that author and user are synonyms.
921 * special fields: summary, metadata.
924 * special fields: summary, metadata.
922 - summary: matches the first line of the description.
925 - summary: matches the first line of the description.
923 - metatadata: It is equivalent to matching 'description user date'
926 - metatadata: It is equivalent to matching 'description user date'
924 (i.e. it matches the main metadata fields).
927 (i.e. it matches the main metadata fields).
925 metadata is the default field which is used when no fields are specified.
928 metadata is the default field which is used when no fields are specified.
926 You can match more than one field at a time.
929 You can match more than one field at a time.
927 """
930 """
928 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
931 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
929
932
930 revs = getset(repo, xrange(len(repo)), l[0])
933 revs = getset(repo, xrange(len(repo)), l[0])
931
934
932 fieldlist = ['metadata']
935 fieldlist = ['metadata']
933 if len(l) > 1:
936 if len(l) > 1:
934 fieldlist = getstring(l[1],
937 fieldlist = getstring(l[1],
935 _("matching requires a string "
938 _("matching requires a string "
936 "as its second argument")).split()
939 "as its second argument")).split()
937
940
938 # Make sure that there are no repeated fields, and expand the
941 # Make sure that there are no repeated fields, and expand the
939 # 'special' 'metadata' field type
942 # 'special' 'metadata' field type
940 fields = []
943 fields = []
941 for field in fieldlist:
944 for field in fieldlist:
942 if field == 'metadata':
945 if field == 'metadata':
943 fields += ['user', 'description', 'date']
946 fields += ['user', 'description', 'date']
944 else:
947 else:
945 if field == 'author':
948 if field == 'author':
946 field = 'user'
949 field = 'user'
947 fields.append(field)
950 fields.append(field)
948 fields = set(fields)
951 fields = set(fields)
949 if 'summary' in fields and 'description' in fields:
952 if 'summary' in fields and 'description' in fields:
950 # If a revision matches its description it also matches its summary
953 # If a revision matches its description it also matches its summary
951 fields.discard('summary')
954 fields.discard('summary')
952
955
953 # We may want to match more than one field
956 # We may want to match more than one field
954 # Not all fields take the same amount of time to be matched
957 # Not all fields take the same amount of time to be matched
955 # Sort the selected fields in order of increasing matching cost
958 # Sort the selected fields in order of increasing matching cost
956 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
959 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
957 'files', 'description', 'substate']
960 'files', 'description', 'substate']
958 def fieldkeyfunc(f):
961 def fieldkeyfunc(f):
959 try:
962 try:
960 return fieldorder.index(f)
963 return fieldorder.index(f)
961 except ValueError:
964 except ValueError:
962 # assume an unknown field is very costly
965 # assume an unknown field is very costly
963 return len(fieldorder)
966 return len(fieldorder)
964 fields = list(fields)
967 fields = list(fields)
965 fields.sort(key=fieldkeyfunc)
968 fields.sort(key=fieldkeyfunc)
966
969
967 # Each field will be matched with its own "getfield" function
970 # Each field will be matched with its own "getfield" function
968 # which will be added to the getfieldfuncs array of functions
971 # which will be added to the getfieldfuncs array of functions
969 getfieldfuncs = []
972 getfieldfuncs = []
970 _funcs = {
973 _funcs = {
971 'user': lambda r: repo[r].user(),
974 'user': lambda r: repo[r].user(),
972 'branch': lambda r: repo[r].branch(),
975 'branch': lambda r: repo[r].branch(),
973 'date': lambda r: repo[r].date(),
976 'date': lambda r: repo[r].date(),
974 'description': lambda r: repo[r].description(),
977 'description': lambda r: repo[r].description(),
975 'files': lambda r: repo[r].files(),
978 'files': lambda r: repo[r].files(),
976 'parents': lambda r: repo[r].parents(),
979 'parents': lambda r: repo[r].parents(),
977 'phase': lambda r: repo[r].phase(),
980 'phase': lambda r: repo[r].phase(),
978 'substate': lambda r: repo[r].substate,
981 'substate': lambda r: repo[r].substate,
979 'summary': lambda r: repo[r].description().splitlines()[0],
982 'summary': lambda r: repo[r].description().splitlines()[0],
980 }
983 }
981 for info in fields:
984 for info in fields:
982 getfield = _funcs.get(info, None)
985 getfield = _funcs.get(info, None)
983 if getfield is None:
986 if getfield is None:
984 raise error.ParseError(
987 raise error.ParseError(
985 _("unexpected field name passed to matching: %s") % info)
988 _("unexpected field name passed to matching: %s") % info)
986 getfieldfuncs.append(getfield)
989 getfieldfuncs.append(getfield)
987 # convert the getfield array of functions into a "getinfo" function
990 # convert the getfield array of functions into a "getinfo" function
988 # which returns an array of field values (or a single value if there
991 # which returns an array of field values (or a single value if there
989 # is only one field to match)
992 # is only one field to match)
990 getinfo = lambda r: [f(r) for f in getfieldfuncs]
993 getinfo = lambda r: [f(r) for f in getfieldfuncs]
991
994
992 matches = []
995 matches = []
993 for rev in revs:
996 for rev in revs:
994 target = getinfo(rev)
997 target = getinfo(rev)
995 for r in subset:
998 for r in subset:
996 match = True
999 match = True
997 for n, f in enumerate(getfieldfuncs):
1000 for n, f in enumerate(getfieldfuncs):
998 if target[n] != f(r):
1001 if target[n] != f(r):
999 match = False
1002 match = False
1000 break
1003 break
1001 if match:
1004 if match:
1002 matches.append(r)
1005 matches.append(r)
1003 if len(revs) > 1:
1006 if len(revs) > 1:
1004 matches = sorted(set(matches))
1007 matches = sorted(set(matches))
1005 return matches
1008 return matches
1006
1009
1007 def reverse(repo, subset, x):
1010 def reverse(repo, subset, x):
1008 """``reverse(set)``
1011 """``reverse(set)``
1009 Reverse order of set.
1012 Reverse order of set.
1010 """
1013 """
1011 l = getset(repo, subset, x)
1014 l = getset(repo, subset, x)
1012 l.reverse()
1015 l.reverse()
1013 return l
1016 return l
1014
1017
1015 def roots(repo, subset, x):
1018 def roots(repo, subset, x):
1016 """``roots(set)``
1019 """``roots(set)``
1017 Changesets in set with no parent changeset in set.
1020 Changesets in set with no parent changeset in set.
1018 """
1021 """
1019 s = set(getset(repo, xrange(len(repo)), x))
1022 s = set(getset(repo, xrange(len(repo)), x))
1020 subset = [r for r in subset if r in s]
1023 subset = [r for r in subset if r in s]
1021 cs = _children(repo, subset, s)
1024 cs = _children(repo, subset, s)
1022 return [r for r in subset if r not in cs]
1025 return [r for r in subset if r not in cs]
1023
1026
1024 def secret(repo, subset, x):
1027 def secret(repo, subset, x):
1025 """``secret()``
1028 """``secret()``
1026 Changeset in secret phase."""
1029 Changeset in secret phase."""
1027 getargs(x, 0, 0, _("secret takes no arguments"))
1030 getargs(x, 0, 0, _("secret takes no arguments"))
1028 return [r for r in subset if repo._phaserev[r] == phases.secret]
1031 return [r for r in subset if repo._phaserev[r] == phases.secret]
1029
1032
1030 def sort(repo, subset, x):
1033 def sort(repo, subset, x):
1031 """``sort(set[, [-]key...])``
1034 """``sort(set[, [-]key...])``
1032 Sort set by keys. The default sort order is ascending, specify a key
1035 Sort set by keys. The default sort order is ascending, specify a key
1033 as ``-key`` to sort in descending order.
1036 as ``-key`` to sort in descending order.
1034
1037
1035 The keys can be:
1038 The keys can be:
1036
1039
1037 - ``rev`` for the revision number,
1040 - ``rev`` for the revision number,
1038 - ``branch`` for the branch name,
1041 - ``branch`` for the branch name,
1039 - ``desc`` for the commit message (description),
1042 - ``desc`` for the commit message (description),
1040 - ``user`` for user name (``author`` can be used as an alias),
1043 - ``user`` for user name (``author`` can be used as an alias),
1041 - ``date`` for the commit date
1044 - ``date`` for the commit date
1042 """
1045 """
1043 # i18n: "sort" is a keyword
1046 # i18n: "sort" is a keyword
1044 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1047 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1045 keys = "rev"
1048 keys = "rev"
1046 if len(l) == 2:
1049 if len(l) == 2:
1047 keys = getstring(l[1], _("sort spec must be a string"))
1050 keys = getstring(l[1], _("sort spec must be a string"))
1048
1051
1049 s = l[0]
1052 s = l[0]
1050 keys = keys.split()
1053 keys = keys.split()
1051 l = []
1054 l = []
1052 def invert(s):
1055 def invert(s):
1053 return "".join(chr(255 - ord(c)) for c in s)
1056 return "".join(chr(255 - ord(c)) for c in s)
1054 for r in getset(repo, subset, s):
1057 for r in getset(repo, subset, s):
1055 c = repo[r]
1058 c = repo[r]
1056 e = []
1059 e = []
1057 for k in keys:
1060 for k in keys:
1058 if k == 'rev':
1061 if k == 'rev':
1059 e.append(r)
1062 e.append(r)
1060 elif k == '-rev':
1063 elif k == '-rev':
1061 e.append(-r)
1064 e.append(-r)
1062 elif k == 'branch':
1065 elif k == 'branch':
1063 e.append(c.branch())
1066 e.append(c.branch())
1064 elif k == '-branch':
1067 elif k == '-branch':
1065 e.append(invert(c.branch()))
1068 e.append(invert(c.branch()))
1066 elif k == 'desc':
1069 elif k == 'desc':
1067 e.append(c.description())
1070 e.append(c.description())
1068 elif k == '-desc':
1071 elif k == '-desc':
1069 e.append(invert(c.description()))
1072 e.append(invert(c.description()))
1070 elif k in 'user author':
1073 elif k in 'user author':
1071 e.append(c.user())
1074 e.append(c.user())
1072 elif k in '-user -author':
1075 elif k in '-user -author':
1073 e.append(invert(c.user()))
1076 e.append(invert(c.user()))
1074 elif k == 'date':
1077 elif k == 'date':
1075 e.append(c.date()[0])
1078 e.append(c.date()[0])
1076 elif k == '-date':
1079 elif k == '-date':
1077 e.append(-c.date()[0])
1080 e.append(-c.date()[0])
1078 else:
1081 else:
1079 raise error.ParseError(_("unknown sort key %r") % k)
1082 raise error.ParseError(_("unknown sort key %r") % k)
1080 e.append(r)
1083 e.append(r)
1081 l.append(e)
1084 l.append(e)
1082 l.sort()
1085 l.sort()
1083 return [e[-1] for e in l]
1086 return [e[-1] for e in l]
1084
1087
1085 def tag(repo, subset, x):
1088 def tag(repo, subset, x):
1086 """``tag([name])``
1089 """``tag([name])``
1087 The specified tag by name, or all tagged revisions if no name is given.
1090 The specified tag by name, or all tagged revisions if no name is given.
1088 """
1091 """
1089 # i18n: "tag" is a keyword
1092 # i18n: "tag" is a keyword
1090 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1093 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1091 cl = repo.changelog
1094 cl = repo.changelog
1092 if args:
1095 if args:
1093 tn = getstring(args[0],
1096 tn = getstring(args[0],
1094 # i18n: "tag" is a keyword
1097 # i18n: "tag" is a keyword
1095 _('the argument to tag must be a string'))
1098 _('the argument to tag must be a string'))
1096 if not repo.tags().get(tn, None):
1099 if not repo.tags().get(tn, None):
1097 raise util.Abort(_("tag '%s' does not exist") % tn)
1100 raise util.Abort(_("tag '%s' does not exist") % tn)
1098 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
1101 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
1099 else:
1102 else:
1100 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1103 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1101 return [r for r in subset if r in s]
1104 return [r for r in subset if r in s]
1102
1105
1103 def tagged(repo, subset, x):
1106 def tagged(repo, subset, x):
1104 return tag(repo, subset, x)
1107 return tag(repo, subset, x)
1105
1108
1106 def user(repo, subset, x):
1109 def user(repo, subset, x):
1107 """``user(string)``
1110 """``user(string)``
1108 User name contains string. The match is case-insensitive.
1111 User name contains string. The match is case-insensitive.
1109 """
1112 """
1110 return author(repo, subset, x)
1113 return author(repo, subset, x)
1111
1114
1112 # for internal use
1115 # for internal use
1113 def _list(repo, subset, x):
1116 def _list(repo, subset, x):
1114 s = getstring(x, "internal error")
1117 s = getstring(x, "internal error")
1115 if not s:
1118 if not s:
1116 return []
1119 return []
1117 if not isinstance(subset, set):
1120 if not isinstance(subset, set):
1118 subset = set(subset)
1121 subset = set(subset)
1119 ls = [repo[r].rev() for r in s.split('\0')]
1122 ls = [repo[r].rev() for r in s.split('\0')]
1120 return [r for r in ls if r in subset]
1123 return [r for r in ls if r in subset]
1121
1124
1122 symbols = {
1125 symbols = {
1123 "adds": adds,
1126 "adds": adds,
1124 "all": getall,
1127 "all": getall,
1125 "ancestor": ancestor,
1128 "ancestor": ancestor,
1126 "ancestors": ancestors,
1129 "ancestors": ancestors,
1127 "_firstancestors": _firstancestors,
1130 "_firstancestors": _firstancestors,
1128 "author": author,
1131 "author": author,
1129 "bisect": bisect,
1132 "bisect": bisect,
1130 "bisected": bisected,
1133 "bisected": bisected,
1131 "bookmark": bookmark,
1134 "bookmark": bookmark,
1132 "branch": branch,
1135 "branch": branch,
1133 "children": children,
1136 "children": children,
1134 "closed": closed,
1137 "closed": closed,
1135 "contains": contains,
1138 "contains": contains,
1136 "date": date,
1139 "date": date,
1137 "desc": desc,
1140 "desc": desc,
1138 "descendants": descendants,
1141 "descendants": descendants,
1139 "_firstdescendants": _firstdescendants,
1142 "_firstdescendants": _firstdescendants,
1140 "draft": draft,
1143 "draft": draft,
1141 "file": hasfile,
1144 "file": hasfile,
1142 "filelog": filelog,
1145 "filelog": filelog,
1143 "first": first,
1146 "first": first,
1144 "follow": follow,
1147 "follow": follow,
1145 "_followfirst": _followfirst,
1148 "_followfirst": _followfirst,
1146 "grep": grep,
1149 "grep": grep,
1147 "head": head,
1150 "head": head,
1148 "heads": heads,
1151 "heads": heads,
1149 "id": node_,
1152 "id": node_,
1150 "keyword": keyword,
1153 "keyword": keyword,
1151 "last": last,
1154 "last": last,
1152 "limit": limit,
1155 "limit": limit,
1153 "_matchfiles": _matchfiles,
1156 "_matchfiles": _matchfiles,
1154 "max": maxrev,
1157 "max": maxrev,
1155 "merge": merge,
1158 "merge": merge,
1156 "min": minrev,
1159 "min": minrev,
1157 "modifies": modifies,
1160 "modifies": modifies,
1158 "outgoing": outgoing,
1161 "outgoing": outgoing,
1159 "p1": p1,
1162 "p1": p1,
1160 "p2": p2,
1163 "p2": p2,
1161 "parents": parents,
1164 "parents": parents,
1162 "present": present,
1165 "present": present,
1163 "public": public,
1166 "public": public,
1164 "remote": remote,
1167 "remote": remote,
1165 "removes": removes,
1168 "removes": removes,
1166 "rev": rev,
1169 "rev": rev,
1167 "reverse": reverse,
1170 "reverse": reverse,
1168 "roots": roots,
1171 "roots": roots,
1169 "sort": sort,
1172 "sort": sort,
1170 "secret": secret,
1173 "secret": secret,
1171 "matching": matching,
1174 "matching": matching,
1172 "tag": tag,
1175 "tag": tag,
1173 "tagged": tagged,
1176 "tagged": tagged,
1174 "user": user,
1177 "user": user,
1175 "_list": _list,
1178 "_list": _list,
1176 }
1179 }
1177
1180
1178 methods = {
1181 methods = {
1179 "range": rangeset,
1182 "range": rangeset,
1180 "string": stringset,
1183 "string": stringset,
1181 "symbol": symbolset,
1184 "symbol": symbolset,
1182 "and": andset,
1185 "and": andset,
1183 "or": orset,
1186 "or": orset,
1184 "not": notset,
1187 "not": notset,
1185 "list": listset,
1188 "list": listset,
1186 "func": func,
1189 "func": func,
1187 "ancestor": ancestorspec,
1190 "ancestor": ancestorspec,
1188 "parent": parentspec,
1191 "parent": parentspec,
1189 "parentpost": p1,
1192 "parentpost": p1,
1190 }
1193 }
1191
1194
1192 def optimize(x, small):
1195 def optimize(x, small):
1193 if x is None:
1196 if x is None:
1194 return 0, x
1197 return 0, x
1195
1198
1196 smallbonus = 1
1199 smallbonus = 1
1197 if small:
1200 if small:
1198 smallbonus = .5
1201 smallbonus = .5
1199
1202
1200 op = x[0]
1203 op = x[0]
1201 if op == 'minus':
1204 if op == 'minus':
1202 return optimize(('and', x[1], ('not', x[2])), small)
1205 return optimize(('and', x[1], ('not', x[2])), small)
1203 elif op == 'dagrange':
1206 elif op == 'dagrange':
1204 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
1207 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
1205 ('func', ('symbol', 'ancestors'), x[2])), small)
1208 ('func', ('symbol', 'ancestors'), x[2])), small)
1206 elif op == 'dagrangepre':
1209 elif op == 'dagrangepre':
1207 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1210 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1208 elif op == 'dagrangepost':
1211 elif op == 'dagrangepost':
1209 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1212 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1210 elif op == 'rangepre':
1213 elif op == 'rangepre':
1211 return optimize(('range', ('string', '0'), x[1]), small)
1214 return optimize(('range', ('string', '0'), x[1]), small)
1212 elif op == 'rangepost':
1215 elif op == 'rangepost':
1213 return optimize(('range', x[1], ('string', 'tip')), small)
1216 return optimize(('range', x[1], ('string', 'tip')), small)
1214 elif op == 'negate':
1217 elif op == 'negate':
1215 return optimize(('string',
1218 return optimize(('string',
1216 '-' + getstring(x[1], _("can't negate that"))), small)
1219 '-' + getstring(x[1], _("can't negate that"))), small)
1217 elif op in 'string symbol negate':
1220 elif op in 'string symbol negate':
1218 return smallbonus, x # single revisions are small
1221 return smallbonus, x # single revisions are small
1219 elif op == 'and' or op == 'dagrange':
1222 elif op == 'and' or op == 'dagrange':
1220 wa, ta = optimize(x[1], True)
1223 wa, ta = optimize(x[1], True)
1221 wb, tb = optimize(x[2], True)
1224 wb, tb = optimize(x[2], True)
1222 w = min(wa, wb)
1225 w = min(wa, wb)
1223 if wa > wb:
1226 if wa > wb:
1224 return w, (op, tb, ta)
1227 return w, (op, tb, ta)
1225 return w, (op, ta, tb)
1228 return w, (op, ta, tb)
1226 elif op == 'or':
1229 elif op == 'or':
1227 wa, ta = optimize(x[1], False)
1230 wa, ta = optimize(x[1], False)
1228 wb, tb = optimize(x[2], False)
1231 wb, tb = optimize(x[2], False)
1229 if wb < wa:
1232 if wb < wa:
1230 wb, wa = wa, wb
1233 wb, wa = wa, wb
1231 return max(wa, wb), (op, ta, tb)
1234 return max(wa, wb), (op, ta, tb)
1232 elif op == 'not':
1235 elif op == 'not':
1233 o = optimize(x[1], not small)
1236 o = optimize(x[1], not small)
1234 return o[0], (op, o[1])
1237 return o[0], (op, o[1])
1235 elif op == 'parentpost':
1238 elif op == 'parentpost':
1236 o = optimize(x[1], small)
1239 o = optimize(x[1], small)
1237 return o[0], (op, o[1])
1240 return o[0], (op, o[1])
1238 elif op == 'group':
1241 elif op == 'group':
1239 return optimize(x[1], small)
1242 return optimize(x[1], small)
1240 elif op in 'range list parent ancestorspec':
1243 elif op in 'range list parent ancestorspec':
1241 if op == 'parent':
1244 if op == 'parent':
1242 # x^:y means (x^) : y, not x ^ (:y)
1245 # x^:y means (x^) : y, not x ^ (:y)
1243 post = ('parentpost', x[1])
1246 post = ('parentpost', x[1])
1244 if x[2][0] == 'dagrangepre':
1247 if x[2][0] == 'dagrangepre':
1245 return optimize(('dagrange', post, x[2][1]), small)
1248 return optimize(('dagrange', post, x[2][1]), small)
1246 elif x[2][0] == 'rangepre':
1249 elif x[2][0] == 'rangepre':
1247 return optimize(('range', post, x[2][1]), small)
1250 return optimize(('range', post, x[2][1]), small)
1248
1251
1249 wa, ta = optimize(x[1], small)
1252 wa, ta = optimize(x[1], small)
1250 wb, tb = optimize(x[2], small)
1253 wb, tb = optimize(x[2], small)
1251 return wa + wb, (op, ta, tb)
1254 return wa + wb, (op, ta, tb)
1252 elif op == 'func':
1255 elif op == 'func':
1253 f = getstring(x[1], _("not a symbol"))
1256 f = getstring(x[1], _("not a symbol"))
1254 wa, ta = optimize(x[2], small)
1257 wa, ta = optimize(x[2], small)
1255 if f in ("author branch closed date desc file grep keyword "
1258 if f in ("author branch closed date desc file grep keyword "
1256 "outgoing user"):
1259 "outgoing user"):
1257 w = 10 # slow
1260 w = 10 # slow
1258 elif f in "modifies adds removes":
1261 elif f in "modifies adds removes":
1259 w = 30 # slower
1262 w = 30 # slower
1260 elif f == "contains":
1263 elif f == "contains":
1261 w = 100 # very slow
1264 w = 100 # very slow
1262 elif f == "ancestor":
1265 elif f == "ancestor":
1263 w = 1 * smallbonus
1266 w = 1 * smallbonus
1264 elif f in "reverse limit first":
1267 elif f in "reverse limit first":
1265 w = 0
1268 w = 0
1266 elif f in "sort":
1269 elif f in "sort":
1267 w = 10 # assume most sorts look at changelog
1270 w = 10 # assume most sorts look at changelog
1268 else:
1271 else:
1269 w = 1
1272 w = 1
1270 return w + wa, (op, x[1], ta)
1273 return w + wa, (op, x[1], ta)
1271 return 1, x
1274 return 1, x
1272
1275
1273 class revsetalias(object):
1276 class revsetalias(object):
1274 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1277 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1275 args = None
1278 args = None
1276
1279
1277 def __init__(self, name, value):
1280 def __init__(self, name, value):
1278 '''Aliases like:
1281 '''Aliases like:
1279
1282
1280 h = heads(default)
1283 h = heads(default)
1281 b($1) = ancestors($1) - ancestors(default)
1284 b($1) = ancestors($1) - ancestors(default)
1282 '''
1285 '''
1283 m = self.funcre.search(name)
1286 m = self.funcre.search(name)
1284 if m:
1287 if m:
1285 self.name = m.group(1)
1288 self.name = m.group(1)
1286 self.tree = ('func', ('symbol', m.group(1)))
1289 self.tree = ('func', ('symbol', m.group(1)))
1287 self.args = [x.strip() for x in m.group(2).split(',')]
1290 self.args = [x.strip() for x in m.group(2).split(',')]
1288 for arg in self.args:
1291 for arg in self.args:
1289 value = value.replace(arg, repr(arg))
1292 value = value.replace(arg, repr(arg))
1290 else:
1293 else:
1291 self.name = name
1294 self.name = name
1292 self.tree = ('symbol', name)
1295 self.tree = ('symbol', name)
1293
1296
1294 self.replacement, pos = parse(value)
1297 self.replacement, pos = parse(value)
1295 if pos != len(value):
1298 if pos != len(value):
1296 raise error.ParseError(_('invalid token'), pos)
1299 raise error.ParseError(_('invalid token'), pos)
1297
1300
1298 def _getalias(aliases, tree):
1301 def _getalias(aliases, tree):
1299 """If tree looks like an unexpanded alias, return it. Return None
1302 """If tree looks like an unexpanded alias, return it. Return None
1300 otherwise.
1303 otherwise.
1301 """
1304 """
1302 if isinstance(tree, tuple) and tree:
1305 if isinstance(tree, tuple) and tree:
1303 if tree[0] == 'symbol' and len(tree) == 2:
1306 if tree[0] == 'symbol' and len(tree) == 2:
1304 name = tree[1]
1307 name = tree[1]
1305 alias = aliases.get(name)
1308 alias = aliases.get(name)
1306 if alias and alias.args is None and alias.tree == tree:
1309 if alias and alias.args is None and alias.tree == tree:
1307 return alias
1310 return alias
1308 if tree[0] == 'func' and len(tree) > 1:
1311 if tree[0] == 'func' and len(tree) > 1:
1309 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1312 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1310 name = tree[1][1]
1313 name = tree[1][1]
1311 alias = aliases.get(name)
1314 alias = aliases.get(name)
1312 if alias and alias.args is not None and alias.tree == tree[:2]:
1315 if alias and alias.args is not None and alias.tree == tree[:2]:
1313 return alias
1316 return alias
1314 return None
1317 return None
1315
1318
1316 def _expandargs(tree, args):
1319 def _expandargs(tree, args):
1317 """Replace all occurences of ('string', name) with the
1320 """Replace all occurences of ('string', name) with the
1318 substitution value of the same name in args, recursively.
1321 substitution value of the same name in args, recursively.
1319 """
1322 """
1320 if not isinstance(tree, tuple):
1323 if not isinstance(tree, tuple):
1321 return tree
1324 return tree
1322 if len(tree) == 2 and tree[0] == 'string':
1325 if len(tree) == 2 and tree[0] == 'string':
1323 return args.get(tree[1], tree)
1326 return args.get(tree[1], tree)
1324 return tuple(_expandargs(t, args) for t in tree)
1327 return tuple(_expandargs(t, args) for t in tree)
1325
1328
1326 def _expandaliases(aliases, tree, expanding):
1329 def _expandaliases(aliases, tree, expanding):
1327 """Expand aliases in tree, recursively.
1330 """Expand aliases in tree, recursively.
1328
1331
1329 'aliases' is a dictionary mapping user defined aliases to
1332 'aliases' is a dictionary mapping user defined aliases to
1330 revsetalias objects.
1333 revsetalias objects.
1331 """
1334 """
1332 if not isinstance(tree, tuple):
1335 if not isinstance(tree, tuple):
1333 # Do not expand raw strings
1336 # Do not expand raw strings
1334 return tree
1337 return tree
1335 alias = _getalias(aliases, tree)
1338 alias = _getalias(aliases, tree)
1336 if alias is not None:
1339 if alias is not None:
1337 if alias in expanding:
1340 if alias in expanding:
1338 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1341 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1339 'detected') % alias.name)
1342 'detected') % alias.name)
1340 expanding.append(alias)
1343 expanding.append(alias)
1341 result = alias.replacement
1344 result = alias.replacement
1342 if alias.args is not None:
1345 if alias.args is not None:
1343 l = getlist(tree[2])
1346 l = getlist(tree[2])
1344 if len(l) != len(alias.args):
1347 if len(l) != len(alias.args):
1345 raise error.ParseError(
1348 raise error.ParseError(
1346 _('invalid number of arguments: %s') % len(l))
1349 _('invalid number of arguments: %s') % len(l))
1347 result = _expandargs(result, dict(zip(alias.args, l)))
1350 result = _expandargs(result, dict(zip(alias.args, l)))
1348 # Recurse in place, the base expression may have been rewritten
1351 # Recurse in place, the base expression may have been rewritten
1349 result = _expandaliases(aliases, result, expanding)
1352 result = _expandaliases(aliases, result, expanding)
1350 expanding.pop()
1353 expanding.pop()
1351 else:
1354 else:
1352 result = tuple(_expandaliases(aliases, t, expanding)
1355 result = tuple(_expandaliases(aliases, t, expanding)
1353 for t in tree)
1356 for t in tree)
1354 return result
1357 return result
1355
1358
1356 def findaliases(ui, tree):
1359 def findaliases(ui, tree):
1357 aliases = {}
1360 aliases = {}
1358 for k, v in ui.configitems('revsetalias'):
1361 for k, v in ui.configitems('revsetalias'):
1359 alias = revsetalias(k, v)
1362 alias = revsetalias(k, v)
1360 aliases[alias.name] = alias
1363 aliases[alias.name] = alias
1361 return _expandaliases(aliases, tree, [])
1364 return _expandaliases(aliases, tree, [])
1362
1365
1363 parse = parser.parser(tokenize, elements).parse
1366 parse = parser.parser(tokenize, elements).parse
1364
1367
1365 def match(ui, spec):
1368 def match(ui, spec):
1366 if not spec:
1369 if not spec:
1367 raise error.ParseError(_("empty query"))
1370 raise error.ParseError(_("empty query"))
1368 tree, pos = parse(spec)
1371 tree, pos = parse(spec)
1369 if (pos != len(spec)):
1372 if (pos != len(spec)):
1370 raise error.ParseError(_("invalid token"), pos)
1373 raise error.ParseError(_("invalid token"), pos)
1371 if ui:
1374 if ui:
1372 tree = findaliases(ui, tree)
1375 tree = findaliases(ui, tree)
1373 weight, tree = optimize(tree, True)
1376 weight, tree = optimize(tree, True)
1374 def mfunc(repo, subset):
1377 def mfunc(repo, subset):
1375 return getset(repo, subset, tree)
1378 return getset(repo, subset, tree)
1376 return mfunc
1379 return mfunc
1377
1380
1378 def formatspec(expr, *args):
1381 def formatspec(expr, *args):
1379 '''
1382 '''
1380 This is a convenience function for using revsets internally, and
1383 This is a convenience function for using revsets internally, and
1381 escapes arguments appropriately. Aliases are intentionally ignored
1384 escapes arguments appropriately. Aliases are intentionally ignored
1382 so that intended expression behavior isn't accidentally subverted.
1385 so that intended expression behavior isn't accidentally subverted.
1383
1386
1384 Supported arguments:
1387 Supported arguments:
1385
1388
1386 %r = revset expression, parenthesized
1389 %r = revset expression, parenthesized
1387 %d = int(arg), no quoting
1390 %d = int(arg), no quoting
1388 %s = string(arg), escaped and single-quoted
1391 %s = string(arg), escaped and single-quoted
1389 %b = arg.branch(), escaped and single-quoted
1392 %b = arg.branch(), escaped and single-quoted
1390 %n = hex(arg), single-quoted
1393 %n = hex(arg), single-quoted
1391 %% = a literal '%'
1394 %% = a literal '%'
1392
1395
1393 Prefixing the type with 'l' specifies a parenthesized list of that type.
1396 Prefixing the type with 'l' specifies a parenthesized list of that type.
1394
1397
1395 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1398 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1396 '(10 or 11):: and ((this()) or (that()))'
1399 '(10 or 11):: and ((this()) or (that()))'
1397 >>> formatspec('%d:: and not %d::', 10, 20)
1400 >>> formatspec('%d:: and not %d::', 10, 20)
1398 '10:: and not 20::'
1401 '10:: and not 20::'
1399 >>> formatspec('%ld or %ld', [], [1])
1402 >>> formatspec('%ld or %ld', [], [1])
1400 "_list('') or 1"
1403 "_list('') or 1"
1401 >>> formatspec('keyword(%s)', 'foo\\xe9')
1404 >>> formatspec('keyword(%s)', 'foo\\xe9')
1402 "keyword('foo\\\\xe9')"
1405 "keyword('foo\\\\xe9')"
1403 >>> b = lambda: 'default'
1406 >>> b = lambda: 'default'
1404 >>> b.branch = b
1407 >>> b.branch = b
1405 >>> formatspec('branch(%b)', b)
1408 >>> formatspec('branch(%b)', b)
1406 "branch('default')"
1409 "branch('default')"
1407 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1410 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1408 "root(_list('a\\x00b\\x00c\\x00d'))"
1411 "root(_list('a\\x00b\\x00c\\x00d'))"
1409 '''
1412 '''
1410
1413
1411 def quote(s):
1414 def quote(s):
1412 return repr(str(s))
1415 return repr(str(s))
1413
1416
1414 def argtype(c, arg):
1417 def argtype(c, arg):
1415 if c == 'd':
1418 if c == 'd':
1416 return str(int(arg))
1419 return str(int(arg))
1417 elif c == 's':
1420 elif c == 's':
1418 return quote(arg)
1421 return quote(arg)
1419 elif c == 'r':
1422 elif c == 'r':
1420 parse(arg) # make sure syntax errors are confined
1423 parse(arg) # make sure syntax errors are confined
1421 return '(%s)' % arg
1424 return '(%s)' % arg
1422 elif c == 'n':
1425 elif c == 'n':
1423 return quote(node.hex(arg))
1426 return quote(node.hex(arg))
1424 elif c == 'b':
1427 elif c == 'b':
1425 return quote(arg.branch())
1428 return quote(arg.branch())
1426
1429
1427 def listexp(s, t):
1430 def listexp(s, t):
1428 l = len(s)
1431 l = len(s)
1429 if l == 0:
1432 if l == 0:
1430 return "_list('')"
1433 return "_list('')"
1431 elif l == 1:
1434 elif l == 1:
1432 return argtype(t, s[0])
1435 return argtype(t, s[0])
1433 elif t == 'd':
1436 elif t == 'd':
1434 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1437 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1435 elif t == 's':
1438 elif t == 's':
1436 return "_list('%s')" % "\0".join(s)
1439 return "_list('%s')" % "\0".join(s)
1437 elif t == 'n':
1440 elif t == 'n':
1438 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1441 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1439 elif t == 'b':
1442 elif t == 'b':
1440 return "_list('%s')" % "\0".join(a.branch() for a in s)
1443 return "_list('%s')" % "\0".join(a.branch() for a in s)
1441
1444
1442 m = l // 2
1445 m = l // 2
1443 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1446 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1444
1447
1445 ret = ''
1448 ret = ''
1446 pos = 0
1449 pos = 0
1447 arg = 0
1450 arg = 0
1448 while pos < len(expr):
1451 while pos < len(expr):
1449 c = expr[pos]
1452 c = expr[pos]
1450 if c == '%':
1453 if c == '%':
1451 pos += 1
1454 pos += 1
1452 d = expr[pos]
1455 d = expr[pos]
1453 if d == '%':
1456 if d == '%':
1454 ret += d
1457 ret += d
1455 elif d in 'dsnbr':
1458 elif d in 'dsnbr':
1456 ret += argtype(d, args[arg])
1459 ret += argtype(d, args[arg])
1457 arg += 1
1460 arg += 1
1458 elif d == 'l':
1461 elif d == 'l':
1459 # a list of some type
1462 # a list of some type
1460 pos += 1
1463 pos += 1
1461 d = expr[pos]
1464 d = expr[pos]
1462 ret += listexp(list(args[arg]), d)
1465 ret += listexp(list(args[arg]), d)
1463 arg += 1
1466 arg += 1
1464 else:
1467 else:
1465 raise util.Abort('unexpected revspec format character %s' % d)
1468 raise util.Abort('unexpected revspec format character %s' % d)
1466 else:
1469 else:
1467 ret += c
1470 ret += c
1468 pos += 1
1471 pos += 1
1469
1472
1470 return ret
1473 return ret
1471
1474
1472 def prettyformat(tree):
1475 def prettyformat(tree):
1473 def _prettyformat(tree, level, lines):
1476 def _prettyformat(tree, level, lines):
1474 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1477 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1475 lines.append((level, str(tree)))
1478 lines.append((level, str(tree)))
1476 else:
1479 else:
1477 lines.append((level, '(%s' % tree[0]))
1480 lines.append((level, '(%s' % tree[0]))
1478 for s in tree[1:]:
1481 for s in tree[1:]:
1479 _prettyformat(s, level + 1, lines)
1482 _prettyformat(s, level + 1, lines)
1480 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1483 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1481
1484
1482 lines = []
1485 lines = []
1483 _prettyformat(tree, 0, lines)
1486 _prettyformat(tree, 0, lines)
1484 output = '\n'.join((' '*l + s) for l, s in lines)
1487 output = '\n'.join((' '*l + s) for l, s in lines)
1485 return output
1488 return output
1486
1489
1487 # tell hggettext to extract docstrings from these functions:
1490 # tell hggettext to extract docstrings from these functions:
1488 i18nfunctions = symbols.values()
1491 i18nfunctions = symbols.values()
@@ -1,716 +1,723 b''
1 $ "$TESTDIR/hghave" no-msys || exit 80 # MSYS will translate /a/b/c/ as if it was a real file path
1 $ "$TESTDIR/hghave" no-msys || exit 80 # MSYS will translate /a/b/c/ as if it was a real file path
2
2
3 $ HGENCODING=utf-8
3 $ HGENCODING=utf-8
4 $ export HGENCODING
4 $ export HGENCODING
5
5
6 $ try() {
6 $ try() {
7 > hg debugrevspec --debug "$@"
7 > hg debugrevspec --debug "$@"
8 > }
8 > }
9
9
10 $ log() {
10 $ log() {
11 > hg log --template '{rev}\n' -r "$1"
11 > hg log --template '{rev}\n' -r "$1"
12 > }
12 > }
13
13
14 $ hg init repo
14 $ hg init repo
15 $ cd repo
15 $ cd repo
16
16
17 $ echo a > a
17 $ echo a > a
18 $ hg branch a
18 $ hg branch a
19 marked working directory as branch a
19 marked working directory as branch a
20 (branches are permanent and global, did you want a bookmark?)
20 (branches are permanent and global, did you want a bookmark?)
21 $ hg ci -Aqm0
21 $ hg ci -Aqm0
22
22
23 $ echo b > b
23 $ echo b > b
24 $ hg branch b
24 $ hg branch b
25 marked working directory as branch b
25 marked working directory as branch b
26 (branches are permanent and global, did you want a bookmark?)
26 (branches are permanent and global, did you want a bookmark?)
27 $ hg ci -Aqm1
27 $ hg ci -Aqm1
28
28
29 $ rm a
29 $ rm a
30 $ hg branch a-b-c-
30 $ hg branch a-b-c-
31 marked working directory as branch a-b-c-
31 marked working directory as branch a-b-c-
32 (branches are permanent and global, did you want a bookmark?)
32 (branches are permanent and global, did you want a bookmark?)
33 $ hg ci -Aqm2 -u Bob
33 $ hg ci -Aqm2 -u Bob
34
34
35 $ hg co 1
35 $ hg co 1
36 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
37 $ hg branch +a+b+c+
37 $ hg branch +a+b+c+
38 marked working directory as branch +a+b+c+
38 marked working directory as branch +a+b+c+
39 (branches are permanent and global, did you want a bookmark?)
39 (branches are permanent and global, did you want a bookmark?)
40 $ hg ci -Aqm3
40 $ hg ci -Aqm3
41
41
42 $ hg co 2 # interleave
42 $ hg co 2 # interleave
43 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
43 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
44 $ echo bb > b
44 $ echo bb > b
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 -Aqm4 -d "May 12 2005"
48 $ hg ci -Aqm4 -d "May 12 2005"
49
49
50 $ hg co 3
50 $ hg co 3
51 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 $ hg branch /a/b/c/
52 $ hg branch /a/b/c/
53 marked working directory as branch /a/b/c/
53 marked working directory as branch /a/b/c/
54 (branches are permanent and global, did you want a bookmark?)
54 (branches are permanent and global, did you want a bookmark?)
55 $ hg ci -Aqm"5 bug"
55 $ hg ci -Aqm"5 bug"
56
56
57 $ hg merge 4
57 $ hg merge 4
58 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
58 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
59 (branch merge, don't forget to commit)
59 (branch merge, don't forget to commit)
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"6 issue619"
63 $ hg ci -Aqm"6 issue619"
64
64
65 $ hg branch .a.b.c.
65 $ hg branch .a.b.c.
66 marked working directory as branch .a.b.c.
66 marked working directory as branch .a.b.c.
67 (branches are permanent and global, did you want a bookmark?)
67 (branches are permanent and global, did you want a bookmark?)
68 $ hg ci -Aqm7
68 $ hg ci -Aqm7
69
69
70 $ hg branch all
70 $ hg branch all
71 marked working directory as branch all
71 marked working directory as branch all
72 (branches are permanent and global, did you want a bookmark?)
72 (branches are permanent and global, did you want a bookmark?)
73 $ hg ci --close-branch -Aqm8
73 $ hg ci --close-branch -Aqm8
74 abort: can only close branch heads
74 abort: can only close branch heads
75 [255]
75 [255]
76
76
77 $ hg co 4
77 $ hg co 4
78 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 $ hg branch Γ©
79 $ hg branch Γ©
80 marked working directory as branch \xc3\xa9 (esc)
80 marked working directory as branch \xc3\xa9 (esc)
81 (branches are permanent and global, did you want a bookmark?)
81 (branches are permanent and global, did you want a bookmark?)
82 $ hg ci -Aqm9
82 $ hg ci -Aqm9
83
83
84 $ hg tag -r6 1.0
84 $ hg tag -r6 1.0
85
85
86 $ hg clone --quiet -U -r 7 . ../remote1
86 $ hg clone --quiet -U -r 7 . ../remote1
87 $ hg clone --quiet -U -r 8 . ../remote2
87 $ hg clone --quiet -U -r 8 . ../remote2
88 $ echo "[paths]" >> .hg/hgrc
88 $ echo "[paths]" >> .hg/hgrc
89 $ echo "default = ../remote1" >> .hg/hgrc
89 $ echo "default = ../remote1" >> .hg/hgrc
90
90
91 names that should work without quoting
91 names that should work without quoting
92
92
93 $ try a
93 $ try a
94 ('symbol', 'a')
94 ('symbol', 'a')
95 0
95 0
96 $ try b-a
96 $ try b-a
97 (minus
97 (minus
98 ('symbol', 'b')
98 ('symbol', 'b')
99 ('symbol', 'a'))
99 ('symbol', 'a'))
100 1
100 1
101 $ try _a_b_c_
101 $ try _a_b_c_
102 ('symbol', '_a_b_c_')
102 ('symbol', '_a_b_c_')
103 6
103 6
104 $ try _a_b_c_-a
104 $ try _a_b_c_-a
105 (minus
105 (minus
106 ('symbol', '_a_b_c_')
106 ('symbol', '_a_b_c_')
107 ('symbol', 'a'))
107 ('symbol', 'a'))
108 6
108 6
109 $ try .a.b.c.
109 $ try .a.b.c.
110 ('symbol', '.a.b.c.')
110 ('symbol', '.a.b.c.')
111 7
111 7
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 7
116 7
117 $ try -- '-a-b-c-' # complains
117 $ try -- '-a-b-c-' # complains
118 hg: parse error at 7: not a prefix: end
118 hg: parse error at 7: not a prefix: end
119 [255]
119 [255]
120 $ log -a-b-c- # succeeds with fallback
120 $ log -a-b-c- # succeeds with fallback
121 4
121 4
122 $ try -- -a-b-c--a # complains
122 $ try -- -a-b-c--a # complains
123 (minus
123 (minus
124 (minus
124 (minus
125 (minus
125 (minus
126 (negate
126 (negate
127 ('symbol', 'a'))
127 ('symbol', 'a'))
128 ('symbol', 'b'))
128 ('symbol', 'b'))
129 ('symbol', 'c'))
129 ('symbol', 'c'))
130 (negate
130 (negate
131 ('symbol', 'a')))
131 ('symbol', 'a')))
132 abort: unknown revision '-a'!
132 abort: unknown revision '-a'!
133 [255]
133 [255]
134 $ try Γ©
134 $ try Γ©
135 ('symbol', '\xc3\xa9')
135 ('symbol', '\xc3\xa9')
136 9
136 9
137
137
138 quoting needed
138 quoting needed
139
139
140 $ try '"-a-b-c-"-a'
140 $ try '"-a-b-c-"-a'
141 (minus
141 (minus
142 ('string', '-a-b-c-')
142 ('string', '-a-b-c-')
143 ('symbol', 'a'))
143 ('symbol', 'a'))
144 4
144 4
145
145
146 $ log '1 or 2'
146 $ log '1 or 2'
147 1
147 1
148 2
148 2
149 $ log '1|2'
149 $ log '1|2'
150 1
150 1
151 2
151 2
152 $ log '1 and 2'
152 $ log '1 and 2'
153 $ log '1&2'
153 $ log '1&2'
154 $ try '1&2|3' # precedence - and is higher
154 $ try '1&2|3' # precedence - and is higher
155 (or
155 (or
156 (and
156 (and
157 ('symbol', '1')
157 ('symbol', '1')
158 ('symbol', '2'))
158 ('symbol', '2'))
159 ('symbol', '3'))
159 ('symbol', '3'))
160 3
160 3
161 $ try '1|2&3'
161 $ try '1|2&3'
162 (or
162 (or
163 ('symbol', '1')
163 ('symbol', '1')
164 (and
164 (and
165 ('symbol', '2')
165 ('symbol', '2')
166 ('symbol', '3')))
166 ('symbol', '3')))
167 1
167 1
168 $ try '1&2&3' # associativity
168 $ try '1&2&3' # associativity
169 (and
169 (and
170 (and
170 (and
171 ('symbol', '1')
171 ('symbol', '1')
172 ('symbol', '2'))
172 ('symbol', '2'))
173 ('symbol', '3'))
173 ('symbol', '3'))
174 $ try '1|(2|3)'
174 $ try '1|(2|3)'
175 (or
175 (or
176 ('symbol', '1')
176 ('symbol', '1')
177 (group
177 (group
178 (or
178 (or
179 ('symbol', '2')
179 ('symbol', '2')
180 ('symbol', '3'))))
180 ('symbol', '3'))))
181 1
181 1
182 2
182 2
183 3
183 3
184 $ log '1.0' # tag
184 $ log '1.0' # tag
185 6
185 6
186 $ log 'a' # branch
186 $ log 'a' # branch
187 0
187 0
188 $ log '2785f51ee'
188 $ log '2785f51ee'
189 0
189 0
190 $ log 'date(2005)'
190 $ log 'date(2005)'
191 4
191 4
192 $ log 'date(this is a test)'
192 $ log 'date(this is a test)'
193 hg: parse error at 10: unexpected token: symbol
193 hg: parse error at 10: unexpected token: symbol
194 [255]
194 [255]
195 $ log 'date()'
195 $ log 'date()'
196 hg: parse error: date requires a string
196 hg: parse error: date requires a string
197 [255]
197 [255]
198 $ log 'date'
198 $ log 'date'
199 hg: parse error: can't use date here
199 hg: parse error: can't use date here
200 [255]
200 [255]
201 $ log 'date('
201 $ log 'date('
202 hg: parse error at 5: not a prefix: end
202 hg: parse error at 5: not a prefix: end
203 [255]
203 [255]
204 $ log 'date(tip)'
204 $ log 'date(tip)'
205 abort: invalid date: 'tip'
205 abort: invalid date: 'tip'
206 [255]
206 [255]
207 $ log '"date"'
207 $ log '"date"'
208 abort: unknown revision 'date'!
208 abort: unknown revision 'date'!
209 [255]
209 [255]
210 $ log 'date(2005) and 1::'
210 $ log 'date(2005) and 1::'
211 4
211 4
212
212
213 $ log 'ancestor(1)'
213 $ log 'ancestor(1)'
214 hg: parse error: ancestor requires two arguments
214 hg: parse error: ancestor requires two arguments
215 [255]
215 [255]
216 $ log 'ancestor(4,5)'
216 $ log 'ancestor(4,5)'
217 1
217 1
218 $ log 'ancestor(4,5) and 4'
218 $ log 'ancestor(4,5) and 4'
219 $ log 'ancestors(5)'
219 $ log 'ancestors(5)'
220 0
220 0
221 1
221 1
222 3
222 3
223 5
223 5
224 $ log 'author(bob)'
224 $ log 'author(bob)'
225 2
225 2
226 $ log 'branch(Γ©)'
226 $ log 'branch(Γ©)'
227 8
227 8
228 9
228 9
229 $ log 'children(ancestor(4,5))'
229 $ log 'children(ancestor(4,5))'
230 2
230 2
231 3
231 3
232 $ log 'closed()'
232 $ log 'closed()'
233 $ log 'contains(a)'
233 $ log 'contains(a)'
234 0
234 0
235 1
235 1
236 3
236 3
237 5
237 5
238 $ log 'desc(B)'
238 $ log 'desc(B)'
239 5
239 5
240 $ log 'descendants(2 or 3)'
240 $ log 'descendants(2 or 3)'
241 2
241 2
242 3
242 3
243 4
243 4
244 5
244 5
245 6
245 6
246 7
246 7
247 8
247 8
248 9
248 9
249 $ log 'file("b*")'
249 $ log 'file("b*")'
250 1
250 1
251 4
251 4
252 $ log 'follow()'
252 $ log 'follow()'
253 0
253 0
254 1
254 1
255 2
255 2
256 4
256 4
257 8
257 8
258 9
258 9
259 $ log 'grep("issue\d+")'
259 $ log 'grep("issue\d+")'
260 6
260 6
261 $ try 'grep("(")' # invalid regular expression
261 $ try 'grep("(")' # invalid regular expression
262 (func
262 (func
263 ('symbol', 'grep')
263 ('symbol', 'grep')
264 ('string', '('))
264 ('string', '('))
265 hg: parse error: invalid match pattern: unbalanced parenthesis
265 hg: parse error: invalid match pattern: unbalanced parenthesis
266 [255]
266 [255]
267 $ try 'grep("\bissue\d+")'
267 $ try 'grep("\bissue\d+")'
268 (func
268 (func
269 ('symbol', 'grep')
269 ('symbol', 'grep')
270 ('string', '\x08issue\\d+'))
270 ('string', '\x08issue\\d+'))
271 $ try 'grep(r"\bissue\d+")'
271 $ try 'grep(r"\bissue\d+")'
272 (func
272 (func
273 ('symbol', 'grep')
273 ('symbol', 'grep')
274 ('string', '\\bissue\\d+'))
274 ('string', '\\bissue\\d+'))
275 6
275 6
276 $ try 'grep(r"\")'
276 $ try 'grep(r"\")'
277 hg: parse error at 7: unterminated string
277 hg: parse error at 7: unterminated string
278 [255]
278 [255]
279 $ log 'head()'
279 $ log 'head()'
280 0
280 0
281 1
281 1
282 2
282 2
283 3
283 3
284 4
284 4
285 5
285 5
286 6
286 6
287 7
287 7
288 9
288 9
289 $ log 'heads(6::)'
289 $ log 'heads(6::)'
290 7
290 7
291 $ log 'keyword(issue)'
291 $ log 'keyword(issue)'
292 6
292 6
293 $ log 'limit(head(), 1)'
293 $ log 'limit(head(), 1)'
294 0
294 0
295 $ log 'matching(6)'
295 $ log 'matching(6)'
296 6
296 6
297 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
297 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
298 6
298 6
299 7
299 7
300 $ log 'max(contains(a))'
300 $ log 'max(contains(a))'
301 5
301 5
302 $ log 'min(contains(a))'
302 $ log 'min(contains(a))'
303 0
303 0
304 $ log 'merge()'
304 $ log 'merge()'
305 6
305 6
306 $ log 'modifies(b)'
306 $ log 'modifies(b)'
307 4
307 4
308 $ log 'modifies("path:b")'
309 4
310 $ log 'modifies("*")'
311 4
312 6
313 $ log 'modifies("set:modified()")'
314 4
308 $ log 'id(5)'
315 $ log 'id(5)'
309 2
316 2
310 $ log 'outgoing()'
317 $ log 'outgoing()'
311 8
318 8
312 9
319 9
313 $ log 'outgoing("../remote1")'
320 $ log 'outgoing("../remote1")'
314 8
321 8
315 9
322 9
316 $ log 'outgoing("../remote2")'
323 $ log 'outgoing("../remote2")'
317 3
324 3
318 5
325 5
319 6
326 6
320 7
327 7
321 9
328 9
322 $ log 'p1(merge())'
329 $ log 'p1(merge())'
323 5
330 5
324 $ log 'p2(merge())'
331 $ log 'p2(merge())'
325 4
332 4
326 $ log 'parents(merge())'
333 $ log 'parents(merge())'
327 4
334 4
328 5
335 5
329 $ log 'removes(a)'
336 $ log 'removes(a)'
330 2
337 2
331 6
338 6
332 $ log 'roots(all())'
339 $ log 'roots(all())'
333 0
340 0
334 $ log 'reverse(2 or 3 or 4 or 5)'
341 $ log 'reverse(2 or 3 or 4 or 5)'
335 5
342 5
336 4
343 4
337 3
344 3
338 2
345 2
339 $ log 'rev(5)'
346 $ log 'rev(5)'
340 5
347 5
341 $ log 'sort(limit(reverse(all()), 3))'
348 $ log 'sort(limit(reverse(all()), 3))'
342 7
349 7
343 8
350 8
344 9
351 9
345 $ log 'sort(2 or 3 or 4 or 5, date)'
352 $ log 'sort(2 or 3 or 4 or 5, date)'
346 2
353 2
347 3
354 3
348 5
355 5
349 4
356 4
350 $ log 'tagged()'
357 $ log 'tagged()'
351 6
358 6
352 $ log 'tag()'
359 $ log 'tag()'
353 6
360 6
354 $ log 'tag(1.0)'
361 $ log 'tag(1.0)'
355 6
362 6
356 $ log 'tag(tip)'
363 $ log 'tag(tip)'
357 9
364 9
358 $ log 'tag(unknown)'
365 $ log 'tag(unknown)'
359 abort: tag 'unknown' does not exist
366 abort: tag 'unknown' does not exist
360 [255]
367 [255]
361 $ log 'branch(unknown)'
368 $ log 'branch(unknown)'
362 abort: unknown revision 'unknown'!
369 abort: unknown revision 'unknown'!
363 [255]
370 [255]
364 $ log 'user(bob)'
371 $ log 'user(bob)'
365 2
372 2
366
373
367 $ log '4::8'
374 $ log '4::8'
368 4
375 4
369 8
376 8
370 $ log '4:8'
377 $ log '4:8'
371 4
378 4
372 5
379 5
373 6
380 6
374 7
381 7
375 8
382 8
376
383
377 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
384 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
378 4
385 4
379 2
386 2
380 5
387 5
381
388
382 $ log 'not 0 and 0:2'
389 $ log 'not 0 and 0:2'
383 1
390 1
384 2
391 2
385 $ log 'not 1 and 0:2'
392 $ log 'not 1 and 0:2'
386 0
393 0
387 2
394 2
388 $ log 'not 2 and 0:2'
395 $ log 'not 2 and 0:2'
389 0
396 0
390 1
397 1
391 $ log '(1 and 2)::'
398 $ log '(1 and 2)::'
392 $ log '(1 and 2):'
399 $ log '(1 and 2):'
393 $ log '(1 and 2):3'
400 $ log '(1 and 2):3'
394 $ log 'sort(head(), -rev)'
401 $ log 'sort(head(), -rev)'
395 9
402 9
396 7
403 7
397 6
404 6
398 5
405 5
399 4
406 4
400 3
407 3
401 2
408 2
402 1
409 1
403 0
410 0
404 $ log '4::8 - 8'
411 $ log '4::8 - 8'
405 4
412 4
406
413
407 issue2437
414 issue2437
408
415
409 $ log '3 and p1(5)'
416 $ log '3 and p1(5)'
410 3
417 3
411 $ log '4 and p2(6)'
418 $ log '4 and p2(6)'
412 4
419 4
413 $ log '1 and parents(:2)'
420 $ log '1 and parents(:2)'
414 1
421 1
415 $ log '2 and children(1:)'
422 $ log '2 and children(1:)'
416 2
423 2
417 $ log 'roots(all()) or roots(all())'
424 $ log 'roots(all()) or roots(all())'
418 0
425 0
419 $ hg debugrevspec 'roots(all()) or roots(all())'
426 $ hg debugrevspec 'roots(all()) or roots(all())'
420 0
427 0
421 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
428 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
422 9
429 9
423 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
430 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
424 4
431 4
425
432
426 issue2654: report a parse error if the revset was not completely parsed
433 issue2654: report a parse error if the revset was not completely parsed
427
434
428 $ log '1 OR 2'
435 $ log '1 OR 2'
429 hg: parse error at 2: invalid token
436 hg: parse error at 2: invalid token
430 [255]
437 [255]
431
438
432 or operator should preserve ordering:
439 or operator should preserve ordering:
433 $ log 'reverse(2::4) or tip'
440 $ log 'reverse(2::4) or tip'
434 4
441 4
435 2
442 2
436 9
443 9
437
444
438 parentrevspec
445 parentrevspec
439
446
440 $ log 'merge()^0'
447 $ log 'merge()^0'
441 6
448 6
442 $ log 'merge()^'
449 $ log 'merge()^'
443 5
450 5
444 $ log 'merge()^1'
451 $ log 'merge()^1'
445 5
452 5
446 $ log 'merge()^2'
453 $ log 'merge()^2'
447 4
454 4
448 $ log 'merge()^^'
455 $ log 'merge()^^'
449 3
456 3
450 $ log 'merge()^1^'
457 $ log 'merge()^1^'
451 3
458 3
452 $ log 'merge()^^^'
459 $ log 'merge()^^^'
453 1
460 1
454
461
455 $ log 'merge()~0'
462 $ log 'merge()~0'
456 6
463 6
457 $ log 'merge()~1'
464 $ log 'merge()~1'
458 5
465 5
459 $ log 'merge()~2'
466 $ log 'merge()~2'
460 3
467 3
461 $ log 'merge()~2^1'
468 $ log 'merge()~2^1'
462 1
469 1
463 $ log 'merge()~3'
470 $ log 'merge()~3'
464 1
471 1
465
472
466 $ log '(-3:tip)^'
473 $ log '(-3:tip)^'
467 4
474 4
468 6
475 6
469 8
476 8
470
477
471 $ log 'tip^foo'
478 $ log 'tip^foo'
472 hg: parse error: ^ expects a number 0, 1, or 2
479 hg: parse error: ^ expects a number 0, 1, or 2
473 [255]
480 [255]
474
481
475 aliases:
482 aliases:
476
483
477 $ echo '[revsetalias]' >> .hg/hgrc
484 $ echo '[revsetalias]' >> .hg/hgrc
478 $ echo 'm = merge()' >> .hg/hgrc
485 $ echo 'm = merge()' >> .hg/hgrc
479 $ echo 'sincem = descendants(m)' >> .hg/hgrc
486 $ echo 'sincem = descendants(m)' >> .hg/hgrc
480 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
487 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
481 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
488 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
482 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
489 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
483
490
484 $ try m
491 $ try m
485 ('symbol', 'm')
492 ('symbol', 'm')
486 (func
493 (func
487 ('symbol', 'merge')
494 ('symbol', 'merge')
488 None)
495 None)
489 6
496 6
490
497
491 test alias recursion
498 test alias recursion
492
499
493 $ try sincem
500 $ try sincem
494 ('symbol', 'sincem')
501 ('symbol', 'sincem')
495 (func
502 (func
496 ('symbol', 'descendants')
503 ('symbol', 'descendants')
497 (func
504 (func
498 ('symbol', 'merge')
505 ('symbol', 'merge')
499 None))
506 None))
500 6
507 6
501 7
508 7
502
509
503 test infinite recursion
510 test infinite recursion
504
511
505 $ echo 'recurse1 = recurse2' >> .hg/hgrc
512 $ echo 'recurse1 = recurse2' >> .hg/hgrc
506 $ echo 'recurse2 = recurse1' >> .hg/hgrc
513 $ echo 'recurse2 = recurse1' >> .hg/hgrc
507 $ try recurse1
514 $ try recurse1
508 ('symbol', 'recurse1')
515 ('symbol', 'recurse1')
509 hg: parse error: infinite expansion of revset alias "recurse1" detected
516 hg: parse error: infinite expansion of revset alias "recurse1" detected
510 [255]
517 [255]
511
518
512 test nesting and variable passing
519 test nesting and variable passing
513
520
514 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
521 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
515 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
522 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
516 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
523 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
517 $ try 'nested(2:5)'
524 $ try 'nested(2:5)'
518 (func
525 (func
519 ('symbol', 'nested')
526 ('symbol', 'nested')
520 (range
527 (range
521 ('symbol', '2')
528 ('symbol', '2')
522 ('symbol', '5')))
529 ('symbol', '5')))
523 (func
530 (func
524 ('symbol', 'max')
531 ('symbol', 'max')
525 (range
532 (range
526 ('symbol', '2')
533 ('symbol', '2')
527 ('symbol', '5')))
534 ('symbol', '5')))
528 5
535 5
529
536
530 test variable isolation, variable placeholders are rewritten as string
537 test variable isolation, variable placeholders are rewritten as string
531 then parsed and matched again as string. Check they do not leak too
538 then parsed and matched again as string. Check they do not leak too
532 far away.
539 far away.
533
540
534 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
541 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
535 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
542 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
536 $ try 'callinjection(2:5)'
543 $ try 'callinjection(2:5)'
537 (func
544 (func
538 ('symbol', 'callinjection')
545 ('symbol', 'callinjection')
539 (range
546 (range
540 ('symbol', '2')
547 ('symbol', '2')
541 ('symbol', '5')))
548 ('symbol', '5')))
542 (func
549 (func
543 ('symbol', 'descendants')
550 ('symbol', 'descendants')
544 (func
551 (func
545 ('symbol', 'max')
552 ('symbol', 'max')
546 ('string', '$1')))
553 ('string', '$1')))
547 abort: unknown revision '$1'!
554 abort: unknown revision '$1'!
548 [255]
555 [255]
549
556
550 $ try 'd(2:5)'
557 $ try 'd(2:5)'
551 (func
558 (func
552 ('symbol', 'd')
559 ('symbol', 'd')
553 (range
560 (range
554 ('symbol', '2')
561 ('symbol', '2')
555 ('symbol', '5')))
562 ('symbol', '5')))
556 (func
563 (func
557 ('symbol', 'reverse')
564 ('symbol', 'reverse')
558 (func
565 (func
559 ('symbol', 'sort')
566 ('symbol', 'sort')
560 (list
567 (list
561 (range
568 (range
562 ('symbol', '2')
569 ('symbol', '2')
563 ('symbol', '5'))
570 ('symbol', '5'))
564 ('symbol', 'date'))))
571 ('symbol', 'date'))))
565 4
572 4
566 5
573 5
567 3
574 3
568 2
575 2
569 $ try 'rs(2 or 3, date)'
576 $ try 'rs(2 or 3, date)'
570 (func
577 (func
571 ('symbol', 'rs')
578 ('symbol', 'rs')
572 (list
579 (list
573 (or
580 (or
574 ('symbol', '2')
581 ('symbol', '2')
575 ('symbol', '3'))
582 ('symbol', '3'))
576 ('symbol', 'date')))
583 ('symbol', 'date')))
577 (func
584 (func
578 ('symbol', 'reverse')
585 ('symbol', 'reverse')
579 (func
586 (func
580 ('symbol', 'sort')
587 ('symbol', 'sort')
581 (list
588 (list
582 (or
589 (or
583 ('symbol', '2')
590 ('symbol', '2')
584 ('symbol', '3'))
591 ('symbol', '3'))
585 ('symbol', 'date'))))
592 ('symbol', 'date'))))
586 3
593 3
587 2
594 2
588 $ try 'rs()'
595 $ try 'rs()'
589 (func
596 (func
590 ('symbol', 'rs')
597 ('symbol', 'rs')
591 None)
598 None)
592 hg: parse error: invalid number of arguments: 0
599 hg: parse error: invalid number of arguments: 0
593 [255]
600 [255]
594 $ try 'rs(2)'
601 $ try 'rs(2)'
595 (func
602 (func
596 ('symbol', 'rs')
603 ('symbol', 'rs')
597 ('symbol', '2'))
604 ('symbol', '2'))
598 hg: parse error: invalid number of arguments: 1
605 hg: parse error: invalid number of arguments: 1
599 [255]
606 [255]
600 $ try 'rs(2, data, 7)'
607 $ try 'rs(2, data, 7)'
601 (func
608 (func
602 ('symbol', 'rs')
609 ('symbol', 'rs')
603 (list
610 (list
604 (list
611 (list
605 ('symbol', '2')
612 ('symbol', '2')
606 ('symbol', 'data'))
613 ('symbol', 'data'))
607 ('symbol', '7')))
614 ('symbol', '7')))
608 hg: parse error: invalid number of arguments: 3
615 hg: parse error: invalid number of arguments: 3
609 [255]
616 [255]
610 $ try 'rs4(2 or 3, x, x, date)'
617 $ try 'rs4(2 or 3, x, x, date)'
611 (func
618 (func
612 ('symbol', 'rs4')
619 ('symbol', 'rs4')
613 (list
620 (list
614 (list
621 (list
615 (list
622 (list
616 (or
623 (or
617 ('symbol', '2')
624 ('symbol', '2')
618 ('symbol', '3'))
625 ('symbol', '3'))
619 ('symbol', 'x'))
626 ('symbol', 'x'))
620 ('symbol', 'x'))
627 ('symbol', 'x'))
621 ('symbol', 'date')))
628 ('symbol', 'date')))
622 (func
629 (func
623 ('symbol', 'reverse')
630 ('symbol', 'reverse')
624 (func
631 (func
625 ('symbol', 'sort')
632 ('symbol', 'sort')
626 (list
633 (list
627 (or
634 (or
628 ('symbol', '2')
635 ('symbol', '2')
629 ('symbol', '3'))
636 ('symbol', '3'))
630 ('symbol', 'date'))))
637 ('symbol', 'date'))))
631 3
638 3
632 2
639 2
633
640
634 issue2549 - correct optimizations
641 issue2549 - correct optimizations
635
642
636 $ log 'limit(1 or 2 or 3, 2) and not 2'
643 $ log 'limit(1 or 2 or 3, 2) and not 2'
637 1
644 1
638 $ log 'max(1 or 2) and not 2'
645 $ log 'max(1 or 2) and not 2'
639 $ log 'min(1 or 2) and not 1'
646 $ log 'min(1 or 2) and not 1'
640 $ log 'last(1 or 2, 1) and not 2'
647 $ log 'last(1 or 2, 1) and not 2'
641
648
642 tests for 'remote()' predicate:
649 tests for 'remote()' predicate:
643 #. (csets in remote) (id) (remote)
650 #. (csets in remote) (id) (remote)
644 1. less than local current branch "default"
651 1. less than local current branch "default"
645 2. same with local specified "default"
652 2. same with local specified "default"
646 3. more than local specified specified
653 3. more than local specified specified
647
654
648 $ hg clone --quiet -U . ../remote3
655 $ hg clone --quiet -U . ../remote3
649 $ cd ../remote3
656 $ cd ../remote3
650 $ hg update -q 7
657 $ hg update -q 7
651 $ echo r > r
658 $ echo r > r
652 $ hg ci -Aqm 10
659 $ hg ci -Aqm 10
653 $ log 'remote()'
660 $ log 'remote()'
654 7
661 7
655 $ log 'remote("a-b-c-")'
662 $ log 'remote("a-b-c-")'
656 2
663 2
657 $ cd ../repo
664 $ cd ../repo
658 $ log 'remote(".a.b.c.", "../remote3")'
665 $ log 'remote(".a.b.c.", "../remote3")'
659
666
660 $ cd ..
667 $ cd ..
661
668
662 test author/desc/keyword in problematic encoding
669 test author/desc/keyword in problematic encoding
663 # unicode: cp932:
670 # unicode: cp932:
664 # u30A2 0x83 0x41(= 'A')
671 # u30A2 0x83 0x41(= 'A')
665 # u30C2 0x83 0x61(= 'a')
672 # u30C2 0x83 0x61(= 'a')
666
673
667 $ hg init problematicencoding
674 $ hg init problematicencoding
668 $ cd problematicencoding
675 $ cd problematicencoding
669
676
670 $ python > setup.sh <<EOF
677 $ python > setup.sh <<EOF
671 > print u'''
678 > print u'''
672 > echo a > text
679 > echo a > text
673 > hg add text
680 > hg add text
674 > hg --encoding utf-8 commit -u '\u30A2' -m none
681 > hg --encoding utf-8 commit -u '\u30A2' -m none
675 > echo b > text
682 > echo b > text
676 > hg --encoding utf-8 commit -u '\u30C2' -m none
683 > hg --encoding utf-8 commit -u '\u30C2' -m none
677 > echo c > text
684 > echo c > text
678 > hg --encoding utf-8 commit -u none -m '\u30A2'
685 > hg --encoding utf-8 commit -u none -m '\u30A2'
679 > echo d > text
686 > echo d > text
680 > hg --encoding utf-8 commit -u none -m '\u30C2'
687 > hg --encoding utf-8 commit -u none -m '\u30C2'
681 > '''.encode('utf-8')
688 > '''.encode('utf-8')
682 > EOF
689 > EOF
683 $ sh < setup.sh
690 $ sh < setup.sh
684
691
685 test in problematic encoding
692 test in problematic encoding
686 $ python > test.sh <<EOF
693 $ python > test.sh <<EOF
687 > print u'''
694 > print u'''
688 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
695 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
689 > echo ====
696 > echo ====
690 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
697 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
691 > echo ====
698 > echo ====
692 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
699 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
693 > echo ====
700 > echo ====
694 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
701 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
695 > echo ====
702 > echo ====
696 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
703 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
697 > echo ====
704 > echo ====
698 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
705 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
699 > '''.encode('cp932')
706 > '''.encode('cp932')
700 > EOF
707 > EOF
701 $ sh < test.sh
708 $ sh < test.sh
702 0
709 0
703 ====
710 ====
704 1
711 1
705 ====
712 ====
706 2
713 2
707 ====
714 ====
708 3
715 3
709 ====
716 ====
710 0
717 0
711 2
718 2
712 ====
719 ====
713 1
720 1
714 3
721 3
715
722
716 $ cd ..
723 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now