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