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