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