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