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