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