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