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