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