##// END OF EJS Templates
revset: add a predicate for finding converted changesets...
Matt Harbison -
r17002:0eb52262 default
parent child Browse files
Show More
@@ -1,1685 +1,1707 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
15
16 def _revancestors(repo, revs, followfirst):
16 def _revancestors(repo, revs, followfirst):
17 """Like revlog.ancestors(), but supports followfirst."""
17 """Like revlog.ancestors(), but supports followfirst."""
18 cut = followfirst and 1 or None
18 cut = followfirst and 1 or None
19 cl = repo.changelog
19 cl = repo.changelog
20 visit = util.deque(revs)
20 visit = util.deque(revs)
21 seen = set([node.nullrev])
21 seen = set([node.nullrev])
22 while visit:
22 while visit:
23 for parent in cl.parentrevs(visit.popleft())[:cut]:
23 for parent in cl.parentrevs(visit.popleft())[:cut]:
24 if parent not in seen:
24 if parent not in seen:
25 visit.append(parent)
25 visit.append(parent)
26 seen.add(parent)
26 seen.add(parent)
27 yield parent
27 yield parent
28
28
29 def _revdescendants(repo, revs, followfirst):
29 def _revdescendants(repo, revs, followfirst):
30 """Like revlog.descendants() but supports followfirst."""
30 """Like revlog.descendants() but supports followfirst."""
31 cut = followfirst and 1 or None
31 cut = followfirst and 1 or None
32 cl = repo.changelog
32 cl = repo.changelog
33 first = min(revs)
33 first = min(revs)
34 nullrev = node.nullrev
34 nullrev = node.nullrev
35 if first == nullrev:
35 if first == nullrev:
36 # Are there nodes with a null first parent and a non-null
36 # Are there nodes with a null first parent and a non-null
37 # second one? Maybe. Do we care? Probably not.
37 # second one? Maybe. Do we care? Probably not.
38 for i in cl:
38 for i in cl:
39 yield i
39 yield i
40 return
40 return
41
41
42 seen = set(revs)
42 seen = set(revs)
43 for i in xrange(first + 1, len(cl)):
43 for i in xrange(first + 1, len(cl)):
44 for x in cl.parentrevs(i)[:cut]:
44 for x in cl.parentrevs(i)[:cut]:
45 if x != nullrev and x in seen:
45 if x != nullrev and x in seen:
46 seen.add(i)
46 seen.add(i)
47 yield i
47 yield i
48 break
48 break
49
49
50 def _revsbetween(repo, roots, heads):
50 def _revsbetween(repo, roots, heads):
51 """Return all paths between roots and heads, inclusive of both endpoint
51 """Return all paths between roots and heads, inclusive of both endpoint
52 sets."""
52 sets."""
53 if not roots:
53 if not roots:
54 return []
54 return []
55 parentrevs = repo.changelog.parentrevs
55 parentrevs = repo.changelog.parentrevs
56 visit = heads[:]
56 visit = heads[:]
57 reachable = set()
57 reachable = set()
58 seen = {}
58 seen = {}
59 minroot = min(roots)
59 minroot = min(roots)
60 roots = set(roots)
60 roots = set(roots)
61 # open-code the post-order traversal due to the tiny size of
61 # open-code the post-order traversal due to the tiny size of
62 # sys.getrecursionlimit()
62 # sys.getrecursionlimit()
63 while visit:
63 while visit:
64 rev = visit.pop()
64 rev = visit.pop()
65 if rev in roots:
65 if rev in roots:
66 reachable.add(rev)
66 reachable.add(rev)
67 parents = parentrevs(rev)
67 parents = parentrevs(rev)
68 seen[rev] = parents
68 seen[rev] = parents
69 for parent in parents:
69 for parent in parents:
70 if parent >= minroot and parent not in seen:
70 if parent >= minroot and parent not in seen:
71 visit.append(parent)
71 visit.append(parent)
72 if not reachable:
72 if not reachable:
73 return []
73 return []
74 for rev in sorted(seen):
74 for rev in sorted(seen):
75 for parent in seen[rev]:
75 for parent in seen[rev]:
76 if parent in reachable:
76 if parent in reachable:
77 reachable.add(rev)
77 reachable.add(rev)
78 return sorted(reachable)
78 return sorted(reachable)
79
79
80 elements = {
80 elements = {
81 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
81 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
82 "~": (18, None, ("ancestor", 18)),
82 "~": (18, None, ("ancestor", 18)),
83 "^": (18, None, ("parent", 18), ("parentpost", 18)),
83 "^": (18, None, ("parent", 18), ("parentpost", 18)),
84 "-": (5, ("negate", 19), ("minus", 5)),
84 "-": (5, ("negate", 19), ("minus", 5)),
85 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
85 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
86 ("dagrangepost", 17)),
86 ("dagrangepost", 17)),
87 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
87 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
88 ("dagrangepost", 17)),
88 ("dagrangepost", 17)),
89 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
89 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
90 "not": (10, ("not", 10)),
90 "not": (10, ("not", 10)),
91 "!": (10, ("not", 10)),
91 "!": (10, ("not", 10)),
92 "and": (5, None, ("and", 5)),
92 "and": (5, None, ("and", 5)),
93 "&": (5, None, ("and", 5)),
93 "&": (5, None, ("and", 5)),
94 "or": (4, None, ("or", 4)),
94 "or": (4, None, ("or", 4)),
95 "|": (4, None, ("or", 4)),
95 "|": (4, None, ("or", 4)),
96 "+": (4, None, ("or", 4)),
96 "+": (4, None, ("or", 4)),
97 ",": (2, None, ("list", 2)),
97 ",": (2, None, ("list", 2)),
98 ")": (0, None, None),
98 ")": (0, None, None),
99 "symbol": (0, ("symbol",), None),
99 "symbol": (0, ("symbol",), None),
100 "string": (0, ("string",), None),
100 "string": (0, ("string",), None),
101 "end": (0, None, None),
101 "end": (0, None, None),
102 }
102 }
103
103
104 keywords = set(['and', 'or', 'not'])
104 keywords = set(['and', 'or', 'not'])
105
105
106 def tokenize(program):
106 def tokenize(program):
107 pos, l = 0, len(program)
107 pos, l = 0, len(program)
108 while pos < l:
108 while pos < l:
109 c = program[pos]
109 c = program[pos]
110 if c.isspace(): # skip inter-token whitespace
110 if c.isspace(): # skip inter-token whitespace
111 pass
111 pass
112 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
112 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
113 yield ('::', None, pos)
113 yield ('::', None, pos)
114 pos += 1 # skip ahead
114 pos += 1 # skip ahead
115 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
115 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
116 yield ('..', None, pos)
116 yield ('..', None, pos)
117 pos += 1 # skip ahead
117 pos += 1 # skip ahead
118 elif c in "():,-|&+!~^": # handle simple operators
118 elif c in "():,-|&+!~^": # handle simple operators
119 yield (c, None, pos)
119 yield (c, None, pos)
120 elif (c in '"\'' or c == 'r' and
120 elif (c in '"\'' or c == 'r' and
121 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
121 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
122 if c == 'r':
122 if c == 'r':
123 pos += 1
123 pos += 1
124 c = program[pos]
124 c = program[pos]
125 decode = lambda x: x
125 decode = lambda x: x
126 else:
126 else:
127 decode = lambda x: x.decode('string-escape')
127 decode = lambda x: x.decode('string-escape')
128 pos += 1
128 pos += 1
129 s = pos
129 s = pos
130 while pos < l: # find closing quote
130 while pos < l: # find closing quote
131 d = program[pos]
131 d = program[pos]
132 if d == '\\': # skip over escaped characters
132 if d == '\\': # skip over escaped characters
133 pos += 2
133 pos += 2
134 continue
134 continue
135 if d == c:
135 if d == c:
136 yield ('string', decode(program[s:pos]), s)
136 yield ('string', decode(program[s:pos]), s)
137 break
137 break
138 pos += 1
138 pos += 1
139 else:
139 else:
140 raise error.ParseError(_("unterminated string"), s)
140 raise error.ParseError(_("unterminated string"), s)
141 # gather up a symbol/keyword
141 # gather up a symbol/keyword
142 elif c.isalnum() or c in '._' or ord(c) > 127:
142 elif c.isalnum() or c in '._' or ord(c) > 127:
143 s = pos
143 s = pos
144 pos += 1
144 pos += 1
145 while pos < l: # find end of symbol
145 while pos < l: # find end of symbol
146 d = program[pos]
146 d = program[pos]
147 if not (d.isalnum() or d in "._/" or ord(d) > 127):
147 if not (d.isalnum() or d in "._/" or ord(d) > 127):
148 break
148 break
149 if d == '.' and program[pos - 1] == '.': # special case for ..
149 if d == '.' and program[pos - 1] == '.': # special case for ..
150 pos -= 1
150 pos -= 1
151 break
151 break
152 pos += 1
152 pos += 1
153 sym = program[s:pos]
153 sym = program[s:pos]
154 if sym in keywords: # operator keywords
154 if sym in keywords: # operator keywords
155 yield (sym, None, s)
155 yield (sym, None, s)
156 else:
156 else:
157 yield ('symbol', sym, s)
157 yield ('symbol', sym, s)
158 pos -= 1
158 pos -= 1
159 else:
159 else:
160 raise error.ParseError(_("syntax error"), pos)
160 raise error.ParseError(_("syntax error"), pos)
161 pos += 1
161 pos += 1
162 yield ('end', None, pos)
162 yield ('end', None, pos)
163
163
164 # helpers
164 # helpers
165
165
166 def getstring(x, err):
166 def getstring(x, err):
167 if x and (x[0] == 'string' or x[0] == 'symbol'):
167 if x and (x[0] == 'string' or x[0] == 'symbol'):
168 return x[1]
168 return x[1]
169 raise error.ParseError(err)
169 raise error.ParseError(err)
170
170
171 def getlist(x):
171 def getlist(x):
172 if not x:
172 if not x:
173 return []
173 return []
174 if x[0] == 'list':
174 if x[0] == 'list':
175 return getlist(x[1]) + [x[2]]
175 return getlist(x[1]) + [x[2]]
176 return [x]
176 return [x]
177
177
178 def getargs(x, min, max, err):
178 def getargs(x, min, max, err):
179 l = getlist(x)
179 l = getlist(x)
180 if len(l) < min or (max >= 0 and len(l) > max):
180 if len(l) < min or (max >= 0 and len(l) > max):
181 raise error.ParseError(err)
181 raise error.ParseError(err)
182 return l
182 return l
183
183
184 def getset(repo, subset, x):
184 def getset(repo, subset, x):
185 if not x:
185 if not x:
186 raise error.ParseError(_("missing argument"))
186 raise error.ParseError(_("missing argument"))
187 return methods[x[0]](repo, subset, *x[1:])
187 return methods[x[0]](repo, subset, *x[1:])
188
188
189 # operator methods
189 # operator methods
190
190
191 def stringset(repo, subset, x):
191 def stringset(repo, subset, x):
192 x = repo[x].rev()
192 x = repo[x].rev()
193 if x == -1 and len(subset) == len(repo):
193 if x == -1 and len(subset) == len(repo):
194 return [-1]
194 return [-1]
195 if len(subset) == len(repo) or x in subset:
195 if len(subset) == len(repo) or x in subset:
196 return [x]
196 return [x]
197 return []
197 return []
198
198
199 def symbolset(repo, subset, x):
199 def symbolset(repo, subset, x):
200 if x in symbols:
200 if x in symbols:
201 raise error.ParseError(_("can't use %s here") % x)
201 raise error.ParseError(_("can't use %s here") % x)
202 return stringset(repo, subset, x)
202 return stringset(repo, subset, x)
203
203
204 def rangeset(repo, subset, x, y):
204 def rangeset(repo, subset, x, y):
205 m = getset(repo, subset, x)
205 m = getset(repo, subset, x)
206 if not m:
206 if not m:
207 m = getset(repo, range(len(repo)), x)
207 m = getset(repo, range(len(repo)), x)
208
208
209 n = getset(repo, subset, y)
209 n = getset(repo, subset, y)
210 if not n:
210 if not n:
211 n = getset(repo, range(len(repo)), y)
211 n = getset(repo, range(len(repo)), y)
212
212
213 if not m or not n:
213 if not m or not n:
214 return []
214 return []
215 m, n = m[0], n[-1]
215 m, n = m[0], n[-1]
216
216
217 if m < n:
217 if m < n:
218 r = range(m, n + 1)
218 r = range(m, n + 1)
219 else:
219 else:
220 r = range(m, n - 1, -1)
220 r = range(m, n - 1, -1)
221 s = set(subset)
221 s = set(subset)
222 return [x for x in r if x in s]
222 return [x for x in r if x in s]
223
223
224 def dagrange(repo, subset, x, y):
224 def dagrange(repo, subset, x, y):
225 if subset:
225 if subset:
226 r = range(len(repo))
226 r = range(len(repo))
227 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
227 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
228 s = set(subset)
228 s = set(subset)
229 return [r for r in xs if r in s]
229 return [r for r in xs if r in s]
230 return []
230 return []
231
231
232 def andset(repo, subset, x, y):
232 def andset(repo, subset, x, y):
233 return getset(repo, getset(repo, subset, x), y)
233 return getset(repo, getset(repo, subset, x), y)
234
234
235 def orset(repo, subset, x, y):
235 def orset(repo, subset, x, y):
236 xl = getset(repo, subset, x)
236 xl = getset(repo, subset, x)
237 s = set(xl)
237 s = set(xl)
238 yl = getset(repo, [r for r in subset if r not in s], y)
238 yl = getset(repo, [r for r in subset if r not in s], y)
239 return xl + yl
239 return xl + yl
240
240
241 def notset(repo, subset, x):
241 def notset(repo, subset, x):
242 s = set(getset(repo, subset, x))
242 s = set(getset(repo, subset, x))
243 return [r for r in subset if r not in s]
243 return [r for r in subset if r not in s]
244
244
245 def listset(repo, subset, a, b):
245 def listset(repo, subset, a, b):
246 raise error.ParseError(_("can't use a list in this context"))
246 raise error.ParseError(_("can't use a list in this context"))
247
247
248 def func(repo, subset, a, b):
248 def func(repo, subset, a, b):
249 if a[0] == 'symbol' and a[1] in symbols:
249 if a[0] == 'symbol' and a[1] in symbols:
250 return symbols[a[1]](repo, subset, b)
250 return symbols[a[1]](repo, subset, b)
251 raise error.ParseError(_("not a function: %s") % a[1])
251 raise error.ParseError(_("not a function: %s") % a[1])
252
252
253 # functions
253 # functions
254
254
255 def adds(repo, subset, x):
255 def adds(repo, subset, x):
256 """``adds(pattern)``
256 """``adds(pattern)``
257 Changesets that add a file matching pattern.
257 Changesets that add a file matching pattern.
258 """
258 """
259 # i18n: "adds" is a keyword
259 # i18n: "adds" is a keyword
260 pat = getstring(x, _("adds requires a pattern"))
260 pat = getstring(x, _("adds requires a pattern"))
261 return checkstatus(repo, subset, pat, 1)
261 return checkstatus(repo, subset, pat, 1)
262
262
263 def ancestor(repo, subset, x):
263 def ancestor(repo, subset, x):
264 """``ancestor(single, single)``
264 """``ancestor(single, single)``
265 Greatest common ancestor of the two changesets.
265 Greatest common ancestor of the two changesets.
266 """
266 """
267 # i18n: "ancestor" is a keyword
267 # i18n: "ancestor" is a keyword
268 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
268 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
269 r = range(len(repo))
269 r = range(len(repo))
270 a = getset(repo, r, l[0])
270 a = getset(repo, r, l[0])
271 b = getset(repo, r, l[1])
271 b = getset(repo, r, l[1])
272 if len(a) != 1 or len(b) != 1:
272 if len(a) != 1 or len(b) != 1:
273 # i18n: "ancestor" is a keyword
273 # i18n: "ancestor" is a keyword
274 raise error.ParseError(_("ancestor arguments must be single revisions"))
274 raise error.ParseError(_("ancestor arguments must be single revisions"))
275 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
275 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
276
276
277 return [r for r in an if r in subset]
277 return [r for r in an if r in subset]
278
278
279 def _ancestors(repo, subset, x, followfirst=False):
279 def _ancestors(repo, subset, x, followfirst=False):
280 args = getset(repo, range(len(repo)), x)
280 args = getset(repo, range(len(repo)), x)
281 if not args:
281 if not args:
282 return []
282 return []
283 s = set(_revancestors(repo, args, followfirst)) | set(args)
283 s = set(_revancestors(repo, args, followfirst)) | set(args)
284 return [r for r in subset if r in s]
284 return [r for r in subset if r in s]
285
285
286 def ancestors(repo, subset, x):
286 def ancestors(repo, subset, x):
287 """``ancestors(set)``
287 """``ancestors(set)``
288 Changesets that are ancestors of a changeset in set.
288 Changesets that are ancestors of a changeset in set.
289 """
289 """
290 return _ancestors(repo, subset, x)
290 return _ancestors(repo, subset, x)
291
291
292 def _firstancestors(repo, subset, x):
292 def _firstancestors(repo, subset, x):
293 # ``_firstancestors(set)``
293 # ``_firstancestors(set)``
294 # Like ``ancestors(set)`` but follows only the first parents.
294 # Like ``ancestors(set)`` but follows only the first parents.
295 return _ancestors(repo, subset, x, followfirst=True)
295 return _ancestors(repo, subset, x, followfirst=True)
296
296
297 def ancestorspec(repo, subset, x, n):
297 def ancestorspec(repo, subset, x, n):
298 """``set~n``
298 """``set~n``
299 Changesets that are the Nth ancestor (first parents only) of a changeset
299 Changesets that are the Nth ancestor (first parents only) of a changeset
300 in set.
300 in set.
301 """
301 """
302 try:
302 try:
303 n = int(n[1])
303 n = int(n[1])
304 except (TypeError, ValueError):
304 except (TypeError, ValueError):
305 raise error.ParseError(_("~ expects a number"))
305 raise error.ParseError(_("~ expects a number"))
306 ps = set()
306 ps = set()
307 cl = repo.changelog
307 cl = repo.changelog
308 for r in getset(repo, subset, x):
308 for r in getset(repo, subset, x):
309 for i in range(n):
309 for i in range(n):
310 r = cl.parentrevs(r)[0]
310 r = cl.parentrevs(r)[0]
311 ps.add(r)
311 ps.add(r)
312 return [r for r in subset if r in ps]
312 return [r for r in subset if r in ps]
313
313
314 def author(repo, subset, x):
314 def author(repo, subset, x):
315 """``author(string)``
315 """``author(string)``
316 Alias for ``user(string)``.
316 Alias for ``user(string)``.
317 """
317 """
318 # i18n: "author" is a keyword
318 # i18n: "author" is a keyword
319 n = encoding.lower(getstring(x, _("author requires a string")))
319 n = encoding.lower(getstring(x, _("author requires a string")))
320 kind, pattern, matcher = _substringmatcher(n)
320 kind, pattern, matcher = _substringmatcher(n)
321 return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
321 return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
322
322
323 def bisect(repo, subset, x):
323 def bisect(repo, subset, x):
324 """``bisect(string)``
324 """``bisect(string)``
325 Changesets marked in the specified bisect status:
325 Changesets marked in the specified bisect status:
326
326
327 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
327 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
328 - ``goods``, ``bads`` : csets topologicaly good/bad
328 - ``goods``, ``bads`` : csets topologicaly good/bad
329 - ``range`` : csets taking part in the bisection
329 - ``range`` : csets taking part in the bisection
330 - ``pruned`` : csets that are goods, bads or skipped
330 - ``pruned`` : csets that are goods, bads or skipped
331 - ``untested`` : csets whose fate is yet unknown
331 - ``untested`` : csets whose fate is yet unknown
332 - ``ignored`` : csets ignored due to DAG topology
332 - ``ignored`` : csets ignored due to DAG topology
333 - ``current`` : the cset currently being bisected
333 - ``current`` : the cset currently being bisected
334 """
334 """
335 status = getstring(x, _("bisect requires a string")).lower()
335 status = getstring(x, _("bisect requires a string")).lower()
336 state = set(hbisect.get(repo, status))
336 state = set(hbisect.get(repo, status))
337 return [r for r in subset if r in state]
337 return [r for r in subset if r in state]
338
338
339 # Backward-compatibility
339 # Backward-compatibility
340 # - no help entry so that we do not advertise it any more
340 # - no help entry so that we do not advertise it any more
341 def bisected(repo, subset, x):
341 def bisected(repo, subset, x):
342 return bisect(repo, subset, x)
342 return bisect(repo, subset, x)
343
343
344 def bookmark(repo, subset, x):
344 def bookmark(repo, subset, x):
345 """``bookmark([name])``
345 """``bookmark([name])``
346 The named bookmark or all bookmarks.
346 The named bookmark or all bookmarks.
347
347
348 If `name` starts with `re:`, the remainder of the name is treated as
348 If `name` starts with `re:`, the remainder of the name is treated as
349 a regular expression. To match a bookmark that actually starts with `re:`,
349 a regular expression. To match a bookmark that actually starts with `re:`,
350 use the prefix `literal:`.
350 use the prefix `literal:`.
351 """
351 """
352 # i18n: "bookmark" is a keyword
352 # i18n: "bookmark" is a keyword
353 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
353 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
354 if args:
354 if args:
355 bm = getstring(args[0],
355 bm = getstring(args[0],
356 # i18n: "bookmark" is a keyword
356 # i18n: "bookmark" is a keyword
357 _('the argument to bookmark must be a string'))
357 _('the argument to bookmark must be a string'))
358 kind, pattern, matcher = _stringmatcher(bm)
358 kind, pattern, matcher = _stringmatcher(bm)
359 if kind == 'literal':
359 if kind == 'literal':
360 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
360 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
361 if not bmrev:
361 if not bmrev:
362 raise util.Abort(_("bookmark '%s' does not exist") % bm)
362 raise util.Abort(_("bookmark '%s' does not exist") % bm)
363 bmrev = repo[bmrev].rev()
363 bmrev = repo[bmrev].rev()
364 return [r for r in subset if r == bmrev]
364 return [r for r in subset if r == bmrev]
365 else:
365 else:
366 matchrevs = set()
366 matchrevs = set()
367 for name, bmrev in bookmarksmod.listbookmarks(repo).iteritems():
367 for name, bmrev in bookmarksmod.listbookmarks(repo).iteritems():
368 if matcher(name):
368 if matcher(name):
369 matchrevs.add(bmrev)
369 matchrevs.add(bmrev)
370 if not matchrevs:
370 if not matchrevs:
371 raise util.Abort(_("no bookmarks exist that match '%s'")
371 raise util.Abort(_("no bookmarks exist that match '%s'")
372 % pattern)
372 % pattern)
373 bmrevs = set()
373 bmrevs = set()
374 for bmrev in matchrevs:
374 for bmrev in matchrevs:
375 bmrevs.add(repo[bmrev].rev())
375 bmrevs.add(repo[bmrev].rev())
376 return [r for r in subset if r in bmrevs]
376 return [r for r in subset if r in bmrevs]
377
377
378 bms = set([repo[r].rev()
378 bms = set([repo[r].rev()
379 for r in bookmarksmod.listbookmarks(repo).values()])
379 for r in bookmarksmod.listbookmarks(repo).values()])
380 return [r for r in subset if r in bms]
380 return [r for r in subset if r in bms]
381
381
382 def branch(repo, subset, x):
382 def branch(repo, subset, x):
383 """``branch(string or set)``
383 """``branch(string or set)``
384 All changesets belonging to the given branch or the branches of the given
384 All changesets belonging to the given branch or the branches of the given
385 changesets.
385 changesets.
386
386
387 If `string` starts with `re:`, the remainder of the name is treated as
387 If `string` starts with `re:`, the remainder of the name is treated as
388 a regular expression. To match a branch that actually starts with `re:`,
388 a regular expression. To match a branch that actually starts with `re:`,
389 use the prefix `literal:`.
389 use the prefix `literal:`.
390 """
390 """
391 try:
391 try:
392 b = getstring(x, '')
392 b = getstring(x, '')
393 except error.ParseError:
393 except error.ParseError:
394 # not a string, but another revspec, e.g. tip()
394 # not a string, but another revspec, e.g. tip()
395 pass
395 pass
396 else:
396 else:
397 kind, pattern, matcher = _stringmatcher(b)
397 kind, pattern, matcher = _stringmatcher(b)
398 if kind == 'literal':
398 if kind == 'literal':
399 # note: falls through to the revspec case if no branch with
399 # note: falls through to the revspec case if no branch with
400 # this name exists
400 # this name exists
401 if pattern in repo.branchmap():
401 if pattern in repo.branchmap():
402 return [r for r in subset if matcher(repo[r].branch())]
402 return [r for r in subset if matcher(repo[r].branch())]
403 else:
403 else:
404 return [r for r in subset if matcher(repo[r].branch())]
404 return [r for r in subset if matcher(repo[r].branch())]
405
405
406 s = getset(repo, range(len(repo)), x)
406 s = getset(repo, range(len(repo)), x)
407 b = set()
407 b = set()
408 for r in s:
408 for r in s:
409 b.add(repo[r].branch())
409 b.add(repo[r].branch())
410 s = set(s)
410 s = set(s)
411 return [r for r in subset if r in s or repo[r].branch() in b]
411 return [r for r in subset if r in s or repo[r].branch() in b]
412
412
413 def checkstatus(repo, subset, pat, field):
413 def checkstatus(repo, subset, pat, field):
414 m = None
414 m = None
415 s = []
415 s = []
416 hasset = matchmod.patkind(pat) == 'set'
416 hasset = matchmod.patkind(pat) == 'set'
417 fname = None
417 fname = None
418 for r in subset:
418 for r in subset:
419 c = repo[r]
419 c = repo[r]
420 if not m or hasset:
420 if not m or hasset:
421 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
421 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
422 if not m.anypats() and len(m.files()) == 1:
422 if not m.anypats() and len(m.files()) == 1:
423 fname = m.files()[0]
423 fname = m.files()[0]
424 if fname is not None:
424 if fname is not None:
425 if fname not in c.files():
425 if fname not in c.files():
426 continue
426 continue
427 else:
427 else:
428 for f in c.files():
428 for f in c.files():
429 if m(f):
429 if m(f):
430 break
430 break
431 else:
431 else:
432 continue
432 continue
433 files = repo.status(c.p1().node(), c.node())[field]
433 files = repo.status(c.p1().node(), c.node())[field]
434 if fname is not None:
434 if fname is not None:
435 if fname in files:
435 if fname in files:
436 s.append(r)
436 s.append(r)
437 else:
437 else:
438 for f in files:
438 for f in files:
439 if m(f):
439 if m(f):
440 s.append(r)
440 s.append(r)
441 break
441 break
442 return s
442 return s
443
443
444 def _children(repo, narrow, parentset):
444 def _children(repo, narrow, parentset):
445 cs = set()
445 cs = set()
446 pr = repo.changelog.parentrevs
446 pr = repo.changelog.parentrevs
447 for r in narrow:
447 for r in narrow:
448 for p in pr(r):
448 for p in pr(r):
449 if p in parentset:
449 if p in parentset:
450 cs.add(r)
450 cs.add(r)
451 return cs
451 return cs
452
452
453 def children(repo, subset, x):
453 def children(repo, subset, x):
454 """``children(set)``
454 """``children(set)``
455 Child changesets of changesets in set.
455 Child changesets of changesets in set.
456 """
456 """
457 s = set(getset(repo, range(len(repo)), x))
457 s = set(getset(repo, range(len(repo)), x))
458 cs = _children(repo, subset, s)
458 cs = _children(repo, subset, s)
459 return [r for r in subset if r in cs]
459 return [r for r in subset if r in cs]
460
460
461 def closed(repo, subset, x):
461 def closed(repo, subset, x):
462 """``closed()``
462 """``closed()``
463 Changeset is closed.
463 Changeset is closed.
464 """
464 """
465 # i18n: "closed" is a keyword
465 # i18n: "closed" is a keyword
466 getargs(x, 0, 0, _("closed takes no arguments"))
466 getargs(x, 0, 0, _("closed takes no arguments"))
467 return [r for r in subset if repo[r].closesbranch()]
467 return [r for r in subset if repo[r].closesbranch()]
468
468
469 def contains(repo, subset, x):
469 def contains(repo, subset, x):
470 """``contains(pattern)``
470 """``contains(pattern)``
471 Revision contains a file matching pattern. See :hg:`help patterns`
471 Revision contains a file matching pattern. See :hg:`help patterns`
472 for information about file patterns.
472 for information about file patterns.
473 """
473 """
474 # i18n: "contains" is a keyword
474 # i18n: "contains" is a keyword
475 pat = getstring(x, _("contains requires a pattern"))
475 pat = getstring(x, _("contains requires a pattern"))
476 m = None
476 m = None
477 s = []
477 s = []
478 if not matchmod.patkind(pat):
478 if not matchmod.patkind(pat):
479 for r in subset:
479 for r in subset:
480 if pat in repo[r]:
480 if pat in repo[r]:
481 s.append(r)
481 s.append(r)
482 else:
482 else:
483 for r in subset:
483 for r in subset:
484 c = repo[r]
484 c = repo[r]
485 if not m or matchmod.patkind(pat) == 'set':
485 if not m or matchmod.patkind(pat) == 'set':
486 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
486 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
487 for f in c.manifest():
487 for f in c.manifest():
488 if m(f):
488 if m(f):
489 s.append(r)
489 s.append(r)
490 break
490 break
491 return s
491 return s
492
492
493 def converted(repo, subset, x):
494 """``converted([id])``
495 Changesets converted from the given identifier in the old repository if
496 present, or all converted changesets if no identifier is specified.
497 """
498
499 # There is exactly no chance of resolving the revision, so do a simple
500 # string compare and hope for the best
501
502 # i18n: "converted" is a keyword
503 rev = None
504 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
505 if l:
506 rev = getstring(l[0], _('converted requires a revision'))
507
508 def _matchvalue(r):
509 source = repo[r].extra().get('convert_revision', None)
510 return source is not None and (rev is None or source.startswith(rev))
511
512 return [r for r in subset if _matchvalue(r)]
513
493 def date(repo, subset, x):
514 def date(repo, subset, x):
494 """``date(interval)``
515 """``date(interval)``
495 Changesets within the interval, see :hg:`help dates`.
516 Changesets within the interval, see :hg:`help dates`.
496 """
517 """
497 # i18n: "date" is a keyword
518 # i18n: "date" is a keyword
498 ds = getstring(x, _("date requires a string"))
519 ds = getstring(x, _("date requires a string"))
499 dm = util.matchdate(ds)
520 dm = util.matchdate(ds)
500 return [r for r in subset if dm(repo[r].date()[0])]
521 return [r for r in subset if dm(repo[r].date()[0])]
501
522
502 def desc(repo, subset, x):
523 def desc(repo, subset, x):
503 """``desc(string)``
524 """``desc(string)``
504 Search commit message for string. The match is case-insensitive.
525 Search commit message for string. The match is case-insensitive.
505 """
526 """
506 # i18n: "desc" is a keyword
527 # i18n: "desc" is a keyword
507 ds = encoding.lower(getstring(x, _("desc requires a string")))
528 ds = encoding.lower(getstring(x, _("desc requires a string")))
508 l = []
529 l = []
509 for r in subset:
530 for r in subset:
510 c = repo[r]
531 c = repo[r]
511 if ds in encoding.lower(c.description()):
532 if ds in encoding.lower(c.description()):
512 l.append(r)
533 l.append(r)
513 return l
534 return l
514
535
515 def _descendants(repo, subset, x, followfirst=False):
536 def _descendants(repo, subset, x, followfirst=False):
516 args = getset(repo, range(len(repo)), x)
537 args = getset(repo, range(len(repo)), x)
517 if not args:
538 if not args:
518 return []
539 return []
519 s = set(_revdescendants(repo, args, followfirst)) | set(args)
540 s = set(_revdescendants(repo, args, followfirst)) | set(args)
520 return [r for r in subset if r in s]
541 return [r for r in subset if r in s]
521
542
522 def descendants(repo, subset, x):
543 def descendants(repo, subset, x):
523 """``descendants(set)``
544 """``descendants(set)``
524 Changesets which are descendants of changesets in set.
545 Changesets which are descendants of changesets in set.
525 """
546 """
526 return _descendants(repo, subset, x)
547 return _descendants(repo, subset, x)
527
548
528 def _firstdescendants(repo, subset, x):
549 def _firstdescendants(repo, subset, x):
529 # ``_firstdescendants(set)``
550 # ``_firstdescendants(set)``
530 # Like ``descendants(set)`` but follows only the first parents.
551 # Like ``descendants(set)`` but follows only the first parents.
531 return _descendants(repo, subset, x, followfirst=True)
552 return _descendants(repo, subset, x, followfirst=True)
532
553
533 def draft(repo, subset, x):
554 def draft(repo, subset, x):
534 """``draft()``
555 """``draft()``
535 Changeset in draft phase."""
556 Changeset in draft phase."""
536 getargs(x, 0, 0, _("draft takes no arguments"))
557 getargs(x, 0, 0, _("draft takes no arguments"))
537 pc = repo._phasecache
558 pc = repo._phasecache
538 return [r for r in subset if pc.phase(repo, r) == phases.draft]
559 return [r for r in subset if pc.phase(repo, r) == phases.draft]
539
560
540 def extra(repo, subset, x):
561 def extra(repo, subset, x):
541 """``extra(label, [value])``
562 """``extra(label, [value])``
542 Changesets with the given label in the extra metadata, with the given
563 Changesets with the given label in the extra metadata, with the given
543 optional value.
564 optional value.
544
565
545 If `value` starts with `re:`, the remainder of the value is treated as
566 If `value` starts with `re:`, the remainder of the value is treated as
546 a regular expression. To match a value that actually starts with `re:`,
567 a regular expression. To match a value that actually starts with `re:`,
547 use the prefix `literal:`.
568 use the prefix `literal:`.
548 """
569 """
549
570
550 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
571 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
551 label = getstring(l[0], _('first argument to extra must be a string'))
572 label = getstring(l[0], _('first argument to extra must be a string'))
552 value = None
573 value = None
553
574
554 if len(l) > 1:
575 if len(l) > 1:
555 value = getstring(l[1], _('second argument to extra must be a string'))
576 value = getstring(l[1], _('second argument to extra must be a string'))
556 kind, value, matcher = _stringmatcher(value)
577 kind, value, matcher = _stringmatcher(value)
557
578
558 def _matchvalue(r):
579 def _matchvalue(r):
559 extra = repo[r].extra()
580 extra = repo[r].extra()
560 return label in extra and (value is None or matcher(extra[label]))
581 return label in extra and (value is None or matcher(extra[label]))
561
582
562 return [r for r in subset if _matchvalue(r)]
583 return [r for r in subset if _matchvalue(r)]
563
584
564 def filelog(repo, subset, x):
585 def filelog(repo, subset, x):
565 """``filelog(pattern)``
586 """``filelog(pattern)``
566 Changesets connected to the specified filelog.
587 Changesets connected to the specified filelog.
567 """
588 """
568
589
569 pat = getstring(x, _("filelog requires a pattern"))
590 pat = getstring(x, _("filelog requires a pattern"))
570 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
591 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
571 ctx=repo[None])
592 ctx=repo[None])
572 s = set()
593 s = set()
573
594
574 if not matchmod.patkind(pat):
595 if not matchmod.patkind(pat):
575 for f in m.files():
596 for f in m.files():
576 fl = repo.file(f)
597 fl = repo.file(f)
577 for fr in fl:
598 for fr in fl:
578 s.add(fl.linkrev(fr))
599 s.add(fl.linkrev(fr))
579 else:
600 else:
580 for f in repo[None]:
601 for f in repo[None]:
581 if m(f):
602 if m(f):
582 fl = repo.file(f)
603 fl = repo.file(f)
583 for fr in fl:
604 for fr in fl:
584 s.add(fl.linkrev(fr))
605 s.add(fl.linkrev(fr))
585
606
586 return [r for r in subset if r in s]
607 return [r for r in subset if r in s]
587
608
588 def first(repo, subset, x):
609 def first(repo, subset, x):
589 """``first(set, [n])``
610 """``first(set, [n])``
590 An alias for limit().
611 An alias for limit().
591 """
612 """
592 return limit(repo, subset, x)
613 return limit(repo, subset, x)
593
614
594 def _follow(repo, subset, x, name, followfirst=False):
615 def _follow(repo, subset, x, name, followfirst=False):
595 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
616 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
596 c = repo['.']
617 c = repo['.']
597 if l:
618 if l:
598 x = getstring(l[0], _("%s expected a filename") % name)
619 x = getstring(l[0], _("%s expected a filename") % name)
599 if x in c:
620 if x in c:
600 cx = c[x]
621 cx = c[x]
601 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
622 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
602 # include the revision responsible for the most recent version
623 # include the revision responsible for the most recent version
603 s.add(cx.linkrev())
624 s.add(cx.linkrev())
604 else:
625 else:
605 return []
626 return []
606 else:
627 else:
607 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
628 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
608
629
609 return [r for r in subset if r in s]
630 return [r for r in subset if r in s]
610
631
611 def follow(repo, subset, x):
632 def follow(repo, subset, x):
612 """``follow([file])``
633 """``follow([file])``
613 An alias for ``::.`` (ancestors of the working copy's first parent).
634 An alias for ``::.`` (ancestors of the working copy's first parent).
614 If a filename is specified, the history of the given file is followed,
635 If a filename is specified, the history of the given file is followed,
615 including copies.
636 including copies.
616 """
637 """
617 return _follow(repo, subset, x, 'follow')
638 return _follow(repo, subset, x, 'follow')
618
639
619 def _followfirst(repo, subset, x):
640 def _followfirst(repo, subset, x):
620 # ``followfirst([file])``
641 # ``followfirst([file])``
621 # Like ``follow([file])`` but follows only the first parent of
642 # Like ``follow([file])`` but follows only the first parent of
622 # every revision or file revision.
643 # every revision or file revision.
623 return _follow(repo, subset, x, '_followfirst', followfirst=True)
644 return _follow(repo, subset, x, '_followfirst', followfirst=True)
624
645
625 def getall(repo, subset, x):
646 def getall(repo, subset, x):
626 """``all()``
647 """``all()``
627 All changesets, the same as ``0:tip``.
648 All changesets, the same as ``0:tip``.
628 """
649 """
629 # i18n: "all" is a keyword
650 # i18n: "all" is a keyword
630 getargs(x, 0, 0, _("all takes no arguments"))
651 getargs(x, 0, 0, _("all takes no arguments"))
631 return subset
652 return subset
632
653
633 def grep(repo, subset, x):
654 def grep(repo, subset, x):
634 """``grep(regex)``
655 """``grep(regex)``
635 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
656 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
636 to ensure special escape characters are handled correctly. Unlike
657 to ensure special escape characters are handled correctly. Unlike
637 ``keyword(string)``, the match is case-sensitive.
658 ``keyword(string)``, the match is case-sensitive.
638 """
659 """
639 try:
660 try:
640 # i18n: "grep" is a keyword
661 # i18n: "grep" is a keyword
641 gr = re.compile(getstring(x, _("grep requires a string")))
662 gr = re.compile(getstring(x, _("grep requires a string")))
642 except re.error, e:
663 except re.error, e:
643 raise error.ParseError(_('invalid match pattern: %s') % e)
664 raise error.ParseError(_('invalid match pattern: %s') % e)
644 l = []
665 l = []
645 for r in subset:
666 for r in subset:
646 c = repo[r]
667 c = repo[r]
647 for e in c.files() + [c.user(), c.description()]:
668 for e in c.files() + [c.user(), c.description()]:
648 if gr.search(e):
669 if gr.search(e):
649 l.append(r)
670 l.append(r)
650 break
671 break
651 return l
672 return l
652
673
653 def _matchfiles(repo, subset, x):
674 def _matchfiles(repo, subset, x):
654 # _matchfiles takes a revset list of prefixed arguments:
675 # _matchfiles takes a revset list of prefixed arguments:
655 #
676 #
656 # [p:foo, i:bar, x:baz]
677 # [p:foo, i:bar, x:baz]
657 #
678 #
658 # builds a match object from them and filters subset. Allowed
679 # builds a match object from them and filters subset. Allowed
659 # prefixes are 'p:' for regular patterns, 'i:' for include
680 # prefixes are 'p:' for regular patterns, 'i:' for include
660 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
681 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
661 # a revision identifier, or the empty string to reference the
682 # a revision identifier, or the empty string to reference the
662 # working directory, from which the match object is
683 # working directory, from which the match object is
663 # initialized. Use 'd:' to set the default matching mode, default
684 # initialized. Use 'd:' to set the default matching mode, default
664 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
685 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
665
686
666 # i18n: "_matchfiles" is a keyword
687 # i18n: "_matchfiles" is a keyword
667 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
688 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
668 pats, inc, exc = [], [], []
689 pats, inc, exc = [], [], []
669 hasset = False
690 hasset = False
670 rev, default = None, None
691 rev, default = None, None
671 for arg in l:
692 for arg in l:
672 s = getstring(arg, _("_matchfiles requires string arguments"))
693 s = getstring(arg, _("_matchfiles requires string arguments"))
673 prefix, value = s[:2], s[2:]
694 prefix, value = s[:2], s[2:]
674 if prefix == 'p:':
695 if prefix == 'p:':
675 pats.append(value)
696 pats.append(value)
676 elif prefix == 'i:':
697 elif prefix == 'i:':
677 inc.append(value)
698 inc.append(value)
678 elif prefix == 'x:':
699 elif prefix == 'x:':
679 exc.append(value)
700 exc.append(value)
680 elif prefix == 'r:':
701 elif prefix == 'r:':
681 if rev is not None:
702 if rev is not None:
682 raise error.ParseError(_('_matchfiles expected at most one '
703 raise error.ParseError(_('_matchfiles expected at most one '
683 'revision'))
704 'revision'))
684 rev = value
705 rev = value
685 elif prefix == 'd:':
706 elif prefix == 'd:':
686 if default is not None:
707 if default is not None:
687 raise error.ParseError(_('_matchfiles expected at most one '
708 raise error.ParseError(_('_matchfiles expected at most one '
688 'default mode'))
709 'default mode'))
689 default = value
710 default = value
690 else:
711 else:
691 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
712 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
692 if not hasset and matchmod.patkind(value) == 'set':
713 if not hasset and matchmod.patkind(value) == 'set':
693 hasset = True
714 hasset = True
694 if not default:
715 if not default:
695 default = 'glob'
716 default = 'glob'
696 m = None
717 m = None
697 s = []
718 s = []
698 for r in subset:
719 for r in subset:
699 c = repo[r]
720 c = repo[r]
700 if not m or (hasset and rev is None):
721 if not m or (hasset and rev is None):
701 ctx = c
722 ctx = c
702 if rev is not None:
723 if rev is not None:
703 ctx = repo[rev or None]
724 ctx = repo[rev or None]
704 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
725 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
705 exclude=exc, ctx=ctx, default=default)
726 exclude=exc, ctx=ctx, default=default)
706 for f in c.files():
727 for f in c.files():
707 if m(f):
728 if m(f):
708 s.append(r)
729 s.append(r)
709 break
730 break
710 return s
731 return s
711
732
712 def hasfile(repo, subset, x):
733 def hasfile(repo, subset, x):
713 """``file(pattern)``
734 """``file(pattern)``
714 Changesets affecting files matched by pattern.
735 Changesets affecting files matched by pattern.
715 """
736 """
716 # i18n: "file" is a keyword
737 # i18n: "file" is a keyword
717 pat = getstring(x, _("file requires a pattern"))
738 pat = getstring(x, _("file requires a pattern"))
718 return _matchfiles(repo, subset, ('string', 'p:' + pat))
739 return _matchfiles(repo, subset, ('string', 'p:' + pat))
719
740
720 def head(repo, subset, x):
741 def head(repo, subset, x):
721 """``head()``
742 """``head()``
722 Changeset is a named branch head.
743 Changeset is a named branch head.
723 """
744 """
724 # i18n: "head" is a keyword
745 # i18n: "head" is a keyword
725 getargs(x, 0, 0, _("head takes no arguments"))
746 getargs(x, 0, 0, _("head takes no arguments"))
726 hs = set()
747 hs = set()
727 for b, ls in repo.branchmap().iteritems():
748 for b, ls in repo.branchmap().iteritems():
728 hs.update(repo[h].rev() for h in ls)
749 hs.update(repo[h].rev() for h in ls)
729 return [r for r in subset if r in hs]
750 return [r for r in subset if r in hs]
730
751
731 def heads(repo, subset, x):
752 def heads(repo, subset, x):
732 """``heads(set)``
753 """``heads(set)``
733 Members of set with no children in set.
754 Members of set with no children in set.
734 """
755 """
735 s = getset(repo, subset, x)
756 s = getset(repo, subset, x)
736 ps = set(parents(repo, subset, x))
757 ps = set(parents(repo, subset, x))
737 return [r for r in s if r not in ps]
758 return [r for r in s if r not in ps]
738
759
739 def keyword(repo, subset, x):
760 def keyword(repo, subset, x):
740 """``keyword(string)``
761 """``keyword(string)``
741 Search commit message, user name, and names of changed files for
762 Search commit message, user name, and names of changed files for
742 string. The match is case-insensitive.
763 string. The match is case-insensitive.
743 """
764 """
744 # i18n: "keyword" is a keyword
765 # i18n: "keyword" is a keyword
745 kw = encoding.lower(getstring(x, _("keyword requires a string")))
766 kw = encoding.lower(getstring(x, _("keyword requires a string")))
746 l = []
767 l = []
747 for r in subset:
768 for r in subset:
748 c = repo[r]
769 c = repo[r]
749 t = " ".join(c.files() + [c.user(), c.description()])
770 t = " ".join(c.files() + [c.user(), c.description()])
750 if kw in encoding.lower(t):
771 if kw in encoding.lower(t):
751 l.append(r)
772 l.append(r)
752 return l
773 return l
753
774
754 def limit(repo, subset, x):
775 def limit(repo, subset, x):
755 """``limit(set, [n])``
776 """``limit(set, [n])``
756 First n members of set, defaulting to 1.
777 First n members of set, defaulting to 1.
757 """
778 """
758 # i18n: "limit" is a keyword
779 # i18n: "limit" is a keyword
759 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
780 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
760 try:
781 try:
761 lim = 1
782 lim = 1
762 if len(l) == 2:
783 if len(l) == 2:
763 # i18n: "limit" is a keyword
784 # i18n: "limit" is a keyword
764 lim = int(getstring(l[1], _("limit requires a number")))
785 lim = int(getstring(l[1], _("limit requires a number")))
765 except (TypeError, ValueError):
786 except (TypeError, ValueError):
766 # i18n: "limit" is a keyword
787 # i18n: "limit" is a keyword
767 raise error.ParseError(_("limit expects a number"))
788 raise error.ParseError(_("limit expects a number"))
768 ss = set(subset)
789 ss = set(subset)
769 os = getset(repo, range(len(repo)), l[0])[:lim]
790 os = getset(repo, range(len(repo)), l[0])[:lim]
770 return [r for r in os if r in ss]
791 return [r for r in os if r in ss]
771
792
772 def last(repo, subset, x):
793 def last(repo, subset, x):
773 """``last(set, [n])``
794 """``last(set, [n])``
774 Last n members of set, defaulting to 1.
795 Last n members of set, defaulting to 1.
775 """
796 """
776 # i18n: "last" is a keyword
797 # i18n: "last" is a keyword
777 l = getargs(x, 1, 2, _("last requires one or two arguments"))
798 l = getargs(x, 1, 2, _("last requires one or two arguments"))
778 try:
799 try:
779 lim = 1
800 lim = 1
780 if len(l) == 2:
801 if len(l) == 2:
781 # i18n: "last" is a keyword
802 # i18n: "last" is a keyword
782 lim = int(getstring(l[1], _("last requires a number")))
803 lim = int(getstring(l[1], _("last requires a number")))
783 except (TypeError, ValueError):
804 except (TypeError, ValueError):
784 # i18n: "last" is a keyword
805 # i18n: "last" is a keyword
785 raise error.ParseError(_("last expects a number"))
806 raise error.ParseError(_("last expects a number"))
786 ss = set(subset)
807 ss = set(subset)
787 os = getset(repo, range(len(repo)), l[0])[-lim:]
808 os = getset(repo, range(len(repo)), l[0])[-lim:]
788 return [r for r in os if r in ss]
809 return [r for r in os if r in ss]
789
810
790 def maxrev(repo, subset, x):
811 def maxrev(repo, subset, x):
791 """``max(set)``
812 """``max(set)``
792 Changeset with highest revision number in set.
813 Changeset with highest revision number in set.
793 """
814 """
794 os = getset(repo, range(len(repo)), x)
815 os = getset(repo, range(len(repo)), x)
795 if os:
816 if os:
796 m = max(os)
817 m = max(os)
797 if m in subset:
818 if m in subset:
798 return [m]
819 return [m]
799 return []
820 return []
800
821
801 def merge(repo, subset, x):
822 def merge(repo, subset, x):
802 """``merge()``
823 """``merge()``
803 Changeset is a merge changeset.
824 Changeset is a merge changeset.
804 """
825 """
805 # i18n: "merge" is a keyword
826 # i18n: "merge" is a keyword
806 getargs(x, 0, 0, _("merge takes no arguments"))
827 getargs(x, 0, 0, _("merge takes no arguments"))
807 cl = repo.changelog
828 cl = repo.changelog
808 return [r for r in subset if cl.parentrevs(r)[1] != -1]
829 return [r for r in subset if cl.parentrevs(r)[1] != -1]
809
830
810 def minrev(repo, subset, x):
831 def minrev(repo, subset, x):
811 """``min(set)``
832 """``min(set)``
812 Changeset with lowest revision number in set.
833 Changeset with lowest revision number in set.
813 """
834 """
814 os = getset(repo, range(len(repo)), x)
835 os = getset(repo, range(len(repo)), x)
815 if os:
836 if os:
816 m = min(os)
837 m = min(os)
817 if m in subset:
838 if m in subset:
818 return [m]
839 return [m]
819 return []
840 return []
820
841
821 def modifies(repo, subset, x):
842 def modifies(repo, subset, x):
822 """``modifies(pattern)``
843 """``modifies(pattern)``
823 Changesets modifying files matched by pattern.
844 Changesets modifying files matched by pattern.
824 """
845 """
825 # i18n: "modifies" is a keyword
846 # i18n: "modifies" is a keyword
826 pat = getstring(x, _("modifies requires a pattern"))
847 pat = getstring(x, _("modifies requires a pattern"))
827 return checkstatus(repo, subset, pat, 0)
848 return checkstatus(repo, subset, pat, 0)
828
849
829 def node_(repo, subset, x):
850 def node_(repo, subset, x):
830 """``id(string)``
851 """``id(string)``
831 Revision non-ambiguously specified by the given hex string prefix.
852 Revision non-ambiguously specified by the given hex string prefix.
832 """
853 """
833 # i18n: "id" is a keyword
854 # i18n: "id" is a keyword
834 l = getargs(x, 1, 1, _("id requires one argument"))
855 l = getargs(x, 1, 1, _("id requires one argument"))
835 # i18n: "id" is a keyword
856 # i18n: "id" is a keyword
836 n = getstring(l[0], _("id requires a string"))
857 n = getstring(l[0], _("id requires a string"))
837 if len(n) == 40:
858 if len(n) == 40:
838 rn = repo[n].rev()
859 rn = repo[n].rev()
839 else:
860 else:
840 rn = None
861 rn = None
841 pm = repo.changelog._partialmatch(n)
862 pm = repo.changelog._partialmatch(n)
842 if pm is not None:
863 if pm is not None:
843 rn = repo.changelog.rev(pm)
864 rn = repo.changelog.rev(pm)
844
865
845 return [r for r in subset if r == rn]
866 return [r for r in subset if r == rn]
846
867
847 def outgoing(repo, subset, x):
868 def outgoing(repo, subset, x):
848 """``outgoing([path])``
869 """``outgoing([path])``
849 Changesets not found in the specified destination repository, or the
870 Changesets not found in the specified destination repository, or the
850 default push location.
871 default push location.
851 """
872 """
852 import hg # avoid start-up nasties
873 import hg # avoid start-up nasties
853 # i18n: "outgoing" is a keyword
874 # i18n: "outgoing" is a keyword
854 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
875 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
855 # i18n: "outgoing" is a keyword
876 # i18n: "outgoing" is a keyword
856 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
877 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
857 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
878 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
858 dest, branches = hg.parseurl(dest)
879 dest, branches = hg.parseurl(dest)
859 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
880 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
860 if revs:
881 if revs:
861 revs = [repo.lookup(rev) for rev in revs]
882 revs = [repo.lookup(rev) for rev in revs]
862 other = hg.peer(repo, {}, dest)
883 other = hg.peer(repo, {}, dest)
863 repo.ui.pushbuffer()
884 repo.ui.pushbuffer()
864 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
885 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
865 repo.ui.popbuffer()
886 repo.ui.popbuffer()
866 cl = repo.changelog
887 cl = repo.changelog
867 o = set([cl.rev(r) for r in outgoing.missing])
888 o = set([cl.rev(r) for r in outgoing.missing])
868 return [r for r in subset if r in o]
889 return [r for r in subset if r in o]
869
890
870 def p1(repo, subset, x):
891 def p1(repo, subset, x):
871 """``p1([set])``
892 """``p1([set])``
872 First parent of changesets in set, or the working directory.
893 First parent of changesets in set, or the working directory.
873 """
894 """
874 if x is None:
895 if x is None:
875 p = repo[x].p1().rev()
896 p = repo[x].p1().rev()
876 return [r for r in subset if r == p]
897 return [r for r in subset if r == p]
877
898
878 ps = set()
899 ps = set()
879 cl = repo.changelog
900 cl = repo.changelog
880 for r in getset(repo, range(len(repo)), x):
901 for r in getset(repo, range(len(repo)), x):
881 ps.add(cl.parentrevs(r)[0])
902 ps.add(cl.parentrevs(r)[0])
882 return [r for r in subset if r in ps]
903 return [r for r in subset if r in ps]
883
904
884 def p2(repo, subset, x):
905 def p2(repo, subset, x):
885 """``p2([set])``
906 """``p2([set])``
886 Second parent of changesets in set, or the working directory.
907 Second parent of changesets in set, or the working directory.
887 """
908 """
888 if x is None:
909 if x is None:
889 ps = repo[x].parents()
910 ps = repo[x].parents()
890 try:
911 try:
891 p = ps[1].rev()
912 p = ps[1].rev()
892 return [r for r in subset if r == p]
913 return [r for r in subset if r == p]
893 except IndexError:
914 except IndexError:
894 return []
915 return []
895
916
896 ps = set()
917 ps = set()
897 cl = repo.changelog
918 cl = repo.changelog
898 for r in getset(repo, range(len(repo)), x):
919 for r in getset(repo, range(len(repo)), x):
899 ps.add(cl.parentrevs(r)[1])
920 ps.add(cl.parentrevs(r)[1])
900 return [r for r in subset if r in ps]
921 return [r for r in subset if r in ps]
901
922
902 def parents(repo, subset, x):
923 def parents(repo, subset, x):
903 """``parents([set])``
924 """``parents([set])``
904 The set of all parents for all changesets in set, or the working directory.
925 The set of all parents for all changesets in set, or the working directory.
905 """
926 """
906 if x is None:
927 if x is None:
907 ps = tuple(p.rev() for p in repo[x].parents())
928 ps = tuple(p.rev() for p in repo[x].parents())
908 return [r for r in subset if r in ps]
929 return [r for r in subset if r in ps]
909
930
910 ps = set()
931 ps = set()
911 cl = repo.changelog
932 cl = repo.changelog
912 for r in getset(repo, range(len(repo)), x):
933 for r in getset(repo, range(len(repo)), x):
913 ps.update(cl.parentrevs(r))
934 ps.update(cl.parentrevs(r))
914 return [r for r in subset if r in ps]
935 return [r for r in subset if r in ps]
915
936
916 def parentspec(repo, subset, x, n):
937 def parentspec(repo, subset, x, n):
917 """``set^0``
938 """``set^0``
918 The set.
939 The set.
919 ``set^1`` (or ``set^``), ``set^2``
940 ``set^1`` (or ``set^``), ``set^2``
920 First or second parent, respectively, of all changesets in set.
941 First or second parent, respectively, of all changesets in set.
921 """
942 """
922 try:
943 try:
923 n = int(n[1])
944 n = int(n[1])
924 if n not in (0, 1, 2):
945 if n not in (0, 1, 2):
925 raise ValueError
946 raise ValueError
926 except (TypeError, ValueError):
947 except (TypeError, ValueError):
927 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
948 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
928 ps = set()
949 ps = set()
929 cl = repo.changelog
950 cl = repo.changelog
930 for r in getset(repo, subset, x):
951 for r in getset(repo, subset, x):
931 if n == 0:
952 if n == 0:
932 ps.add(r)
953 ps.add(r)
933 elif n == 1:
954 elif n == 1:
934 ps.add(cl.parentrevs(r)[0])
955 ps.add(cl.parentrevs(r)[0])
935 elif n == 2:
956 elif n == 2:
936 parents = cl.parentrevs(r)
957 parents = cl.parentrevs(r)
937 if len(parents) > 1:
958 if len(parents) > 1:
938 ps.add(parents[1])
959 ps.add(parents[1])
939 return [r for r in subset if r in ps]
960 return [r for r in subset if r in ps]
940
961
941 def present(repo, subset, x):
962 def present(repo, subset, x):
942 """``present(set)``
963 """``present(set)``
943 An empty set, if any revision in set isn't found; otherwise,
964 An empty set, if any revision in set isn't found; otherwise,
944 all revisions in set.
965 all revisions in set.
945
966
946 If any of specified revisions is not present in the local repository,
967 If any of specified revisions is not present in the local repository,
947 the query is normally aborted. But this predicate allows the query
968 the query is normally aborted. But this predicate allows the query
948 to continue even in such cases.
969 to continue even in such cases.
949 """
970 """
950 try:
971 try:
951 return getset(repo, subset, x)
972 return getset(repo, subset, x)
952 except error.RepoLookupError:
973 except error.RepoLookupError:
953 return []
974 return []
954
975
955 def public(repo, subset, x):
976 def public(repo, subset, x):
956 """``public()``
977 """``public()``
957 Changeset in public phase."""
978 Changeset in public phase."""
958 getargs(x, 0, 0, _("public takes no arguments"))
979 getargs(x, 0, 0, _("public takes no arguments"))
959 pc = repo._phasecache
980 pc = repo._phasecache
960 return [r for r in subset if pc.phase(repo, r) == phases.public]
981 return [r for r in subset if pc.phase(repo, r) == phases.public]
961
982
962 def remote(repo, subset, x):
983 def remote(repo, subset, x):
963 """``remote([id [,path]])``
984 """``remote([id [,path]])``
964 Local revision that corresponds to the given identifier in a
985 Local revision that corresponds to the given identifier in a
965 remote repository, if present. Here, the '.' identifier is a
986 remote repository, if present. Here, the '.' identifier is a
966 synonym for the current local branch.
987 synonym for the current local branch.
967 """
988 """
968
989
969 import hg # avoid start-up nasties
990 import hg # avoid start-up nasties
970 # i18n: "remote" is a keyword
991 # i18n: "remote" is a keyword
971 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
992 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
972
993
973 q = '.'
994 q = '.'
974 if len(l) > 0:
995 if len(l) > 0:
975 # i18n: "remote" is a keyword
996 # i18n: "remote" is a keyword
976 q = getstring(l[0], _("remote requires a string id"))
997 q = getstring(l[0], _("remote requires a string id"))
977 if q == '.':
998 if q == '.':
978 q = repo['.'].branch()
999 q = repo['.'].branch()
979
1000
980 dest = ''
1001 dest = ''
981 if len(l) > 1:
1002 if len(l) > 1:
982 # i18n: "remote" is a keyword
1003 # i18n: "remote" is a keyword
983 dest = getstring(l[1], _("remote requires a repository path"))
1004 dest = getstring(l[1], _("remote requires a repository path"))
984 dest = repo.ui.expandpath(dest or 'default')
1005 dest = repo.ui.expandpath(dest or 'default')
985 dest, branches = hg.parseurl(dest)
1006 dest, branches = hg.parseurl(dest)
986 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1007 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
987 if revs:
1008 if revs:
988 revs = [repo.lookup(rev) for rev in revs]
1009 revs = [repo.lookup(rev) for rev in revs]
989 other = hg.peer(repo, {}, dest)
1010 other = hg.peer(repo, {}, dest)
990 n = other.lookup(q)
1011 n = other.lookup(q)
991 if n in repo:
1012 if n in repo:
992 r = repo[n].rev()
1013 r = repo[n].rev()
993 if r in subset:
1014 if r in subset:
994 return [r]
1015 return [r]
995 return []
1016 return []
996
1017
997 def removes(repo, subset, x):
1018 def removes(repo, subset, x):
998 """``removes(pattern)``
1019 """``removes(pattern)``
999 Changesets which remove files matching pattern.
1020 Changesets which remove files matching pattern.
1000 """
1021 """
1001 # i18n: "removes" is a keyword
1022 # i18n: "removes" is a keyword
1002 pat = getstring(x, _("removes requires a pattern"))
1023 pat = getstring(x, _("removes requires a pattern"))
1003 return checkstatus(repo, subset, pat, 2)
1024 return checkstatus(repo, subset, pat, 2)
1004
1025
1005 def rev(repo, subset, x):
1026 def rev(repo, subset, x):
1006 """``rev(number)``
1027 """``rev(number)``
1007 Revision with the given numeric identifier.
1028 Revision with the given numeric identifier.
1008 """
1029 """
1009 # i18n: "rev" is a keyword
1030 # i18n: "rev" is a keyword
1010 l = getargs(x, 1, 1, _("rev requires one argument"))
1031 l = getargs(x, 1, 1, _("rev requires one argument"))
1011 try:
1032 try:
1012 # i18n: "rev" is a keyword
1033 # i18n: "rev" is a keyword
1013 l = int(getstring(l[0], _("rev requires a number")))
1034 l = int(getstring(l[0], _("rev requires a number")))
1014 except (TypeError, ValueError):
1035 except (TypeError, ValueError):
1015 # i18n: "rev" is a keyword
1036 # i18n: "rev" is a keyword
1016 raise error.ParseError(_("rev expects a number"))
1037 raise error.ParseError(_("rev expects a number"))
1017 return [r for r in subset if r == l]
1038 return [r for r in subset if r == l]
1018
1039
1019 def matching(repo, subset, x):
1040 def matching(repo, subset, x):
1020 """``matching(revision [, field])``
1041 """``matching(revision [, field])``
1021 Changesets in which a given set of fields match the set of fields in the
1042 Changesets in which a given set of fields match the set of fields in the
1022 selected revision or set.
1043 selected revision or set.
1023
1044
1024 To match more than one field pass the list of fields to match separated
1045 To match more than one field pass the list of fields to match separated
1025 by spaces (e.g. ``author description``).
1046 by spaces (e.g. ``author description``).
1026
1047
1027 Valid fields are most regular revision fields and some special fields.
1048 Valid fields are most regular revision fields and some special fields.
1028
1049
1029 Regular revision fields are ``description``, ``author``, ``branch``,
1050 Regular revision fields are ``description``, ``author``, ``branch``,
1030 ``date``, ``files``, ``phase``, ``parents``, ``substate`` and ``user``.
1051 ``date``, ``files``, ``phase``, ``parents``, ``substate`` and ``user``.
1031 Note that ``author`` and ``user`` are synonyms.
1052 Note that ``author`` and ``user`` are synonyms.
1032
1053
1033 Special fields are ``summary`` and ``metadata``:
1054 Special fields are ``summary`` and ``metadata``:
1034 ``summary`` matches the first line of the description.
1055 ``summary`` matches the first line of the description.
1035 ``metadata`` is equivalent to matching ``description user date``
1056 ``metadata`` is equivalent to matching ``description user date``
1036 (i.e. it matches the main metadata fields).
1057 (i.e. it matches the main metadata fields).
1037
1058
1038 ``metadata`` is the default field which is used when no fields are
1059 ``metadata`` is the default field which is used when no fields are
1039 specified. You can match more than one field at a time.
1060 specified. You can match more than one field at a time.
1040 """
1061 """
1041 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1062 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1042
1063
1043 revs = getset(repo, xrange(len(repo)), l[0])
1064 revs = getset(repo, xrange(len(repo)), l[0])
1044
1065
1045 fieldlist = ['metadata']
1066 fieldlist = ['metadata']
1046 if len(l) > 1:
1067 if len(l) > 1:
1047 fieldlist = getstring(l[1],
1068 fieldlist = getstring(l[1],
1048 _("matching requires a string "
1069 _("matching requires a string "
1049 "as its second argument")).split()
1070 "as its second argument")).split()
1050
1071
1051 # Make sure that there are no repeated fields, and expand the
1072 # Make sure that there are no repeated fields, and expand the
1052 # 'special' 'metadata' field type
1073 # 'special' 'metadata' field type
1053 fields = []
1074 fields = []
1054 for field in fieldlist:
1075 for field in fieldlist:
1055 if field == 'metadata':
1076 if field == 'metadata':
1056 fields += ['user', 'description', 'date']
1077 fields += ['user', 'description', 'date']
1057 else:
1078 else:
1058 if field == 'author':
1079 if field == 'author':
1059 field = 'user'
1080 field = 'user'
1060 fields.append(field)
1081 fields.append(field)
1061 fields = set(fields)
1082 fields = set(fields)
1062 if 'summary' in fields and 'description' in fields:
1083 if 'summary' in fields and 'description' in fields:
1063 # If a revision matches its description it also matches its summary
1084 # If a revision matches its description it also matches its summary
1064 fields.discard('summary')
1085 fields.discard('summary')
1065
1086
1066 # We may want to match more than one field
1087 # We may want to match more than one field
1067 # Not all fields take the same amount of time to be matched
1088 # Not all fields take the same amount of time to be matched
1068 # Sort the selected fields in order of increasing matching cost
1089 # Sort the selected fields in order of increasing matching cost
1069 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1090 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1070 'files', 'description', 'substate']
1091 'files', 'description', 'substate']
1071 def fieldkeyfunc(f):
1092 def fieldkeyfunc(f):
1072 try:
1093 try:
1073 return fieldorder.index(f)
1094 return fieldorder.index(f)
1074 except ValueError:
1095 except ValueError:
1075 # assume an unknown field is very costly
1096 # assume an unknown field is very costly
1076 return len(fieldorder)
1097 return len(fieldorder)
1077 fields = list(fields)
1098 fields = list(fields)
1078 fields.sort(key=fieldkeyfunc)
1099 fields.sort(key=fieldkeyfunc)
1079
1100
1080 # Each field will be matched with its own "getfield" function
1101 # Each field will be matched with its own "getfield" function
1081 # which will be added to the getfieldfuncs array of functions
1102 # which will be added to the getfieldfuncs array of functions
1082 getfieldfuncs = []
1103 getfieldfuncs = []
1083 _funcs = {
1104 _funcs = {
1084 'user': lambda r: repo[r].user(),
1105 'user': lambda r: repo[r].user(),
1085 'branch': lambda r: repo[r].branch(),
1106 'branch': lambda r: repo[r].branch(),
1086 'date': lambda r: repo[r].date(),
1107 'date': lambda r: repo[r].date(),
1087 'description': lambda r: repo[r].description(),
1108 'description': lambda r: repo[r].description(),
1088 'files': lambda r: repo[r].files(),
1109 'files': lambda r: repo[r].files(),
1089 'parents': lambda r: repo[r].parents(),
1110 'parents': lambda r: repo[r].parents(),
1090 'phase': lambda r: repo[r].phase(),
1111 'phase': lambda r: repo[r].phase(),
1091 'substate': lambda r: repo[r].substate,
1112 'substate': lambda r: repo[r].substate,
1092 'summary': lambda r: repo[r].description().splitlines()[0],
1113 'summary': lambda r: repo[r].description().splitlines()[0],
1093 }
1114 }
1094 for info in fields:
1115 for info in fields:
1095 getfield = _funcs.get(info, None)
1116 getfield = _funcs.get(info, None)
1096 if getfield is None:
1117 if getfield is None:
1097 raise error.ParseError(
1118 raise error.ParseError(
1098 _("unexpected field name passed to matching: %s") % info)
1119 _("unexpected field name passed to matching: %s") % info)
1099 getfieldfuncs.append(getfield)
1120 getfieldfuncs.append(getfield)
1100 # convert the getfield array of functions into a "getinfo" function
1121 # convert the getfield array of functions into a "getinfo" function
1101 # which returns an array of field values (or a single value if there
1122 # which returns an array of field values (or a single value if there
1102 # is only one field to match)
1123 # is only one field to match)
1103 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1124 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1104
1125
1105 matches = set()
1126 matches = set()
1106 for rev in revs:
1127 for rev in revs:
1107 target = getinfo(rev)
1128 target = getinfo(rev)
1108 for r in subset:
1129 for r in subset:
1109 match = True
1130 match = True
1110 for n, f in enumerate(getfieldfuncs):
1131 for n, f in enumerate(getfieldfuncs):
1111 if target[n] != f(r):
1132 if target[n] != f(r):
1112 match = False
1133 match = False
1113 break
1134 break
1114 if match:
1135 if match:
1115 matches.add(r)
1136 matches.add(r)
1116 return [r for r in subset if r in matches]
1137 return [r for r in subset if r in matches]
1117
1138
1118 def reverse(repo, subset, x):
1139 def reverse(repo, subset, x):
1119 """``reverse(set)``
1140 """``reverse(set)``
1120 Reverse order of set.
1141 Reverse order of set.
1121 """
1142 """
1122 l = getset(repo, subset, x)
1143 l = getset(repo, subset, x)
1123 l.reverse()
1144 l.reverse()
1124 return l
1145 return l
1125
1146
1126 def roots(repo, subset, x):
1147 def roots(repo, subset, x):
1127 """``roots(set)``
1148 """``roots(set)``
1128 Changesets in set with no parent changeset in set.
1149 Changesets in set with no parent changeset in set.
1129 """
1150 """
1130 s = set(getset(repo, xrange(len(repo)), x))
1151 s = set(getset(repo, xrange(len(repo)), x))
1131 subset = [r for r in subset if r in s]
1152 subset = [r for r in subset if r in s]
1132 cs = _children(repo, subset, s)
1153 cs = _children(repo, subset, s)
1133 return [r for r in subset if r not in cs]
1154 return [r for r in subset if r not in cs]
1134
1155
1135 def secret(repo, subset, x):
1156 def secret(repo, subset, x):
1136 """``secret()``
1157 """``secret()``
1137 Changeset in secret phase."""
1158 Changeset in secret phase."""
1138 getargs(x, 0, 0, _("secret takes no arguments"))
1159 getargs(x, 0, 0, _("secret takes no arguments"))
1139 pc = repo._phasecache
1160 pc = repo._phasecache
1140 return [r for r in subset if pc.phase(repo, r) == phases.secret]
1161 return [r for r in subset if pc.phase(repo, r) == phases.secret]
1141
1162
1142 def sort(repo, subset, x):
1163 def sort(repo, subset, x):
1143 """``sort(set[, [-]key...])``
1164 """``sort(set[, [-]key...])``
1144 Sort set by keys. The default sort order is ascending, specify a key
1165 Sort set by keys. The default sort order is ascending, specify a key
1145 as ``-key`` to sort in descending order.
1166 as ``-key`` to sort in descending order.
1146
1167
1147 The keys can be:
1168 The keys can be:
1148
1169
1149 - ``rev`` for the revision number,
1170 - ``rev`` for the revision number,
1150 - ``branch`` for the branch name,
1171 - ``branch`` for the branch name,
1151 - ``desc`` for the commit message (description),
1172 - ``desc`` for the commit message (description),
1152 - ``user`` for user name (``author`` can be used as an alias),
1173 - ``user`` for user name (``author`` can be used as an alias),
1153 - ``date`` for the commit date
1174 - ``date`` for the commit date
1154 """
1175 """
1155 # i18n: "sort" is a keyword
1176 # i18n: "sort" is a keyword
1156 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1177 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1157 keys = "rev"
1178 keys = "rev"
1158 if len(l) == 2:
1179 if len(l) == 2:
1159 keys = getstring(l[1], _("sort spec must be a string"))
1180 keys = getstring(l[1], _("sort spec must be a string"))
1160
1181
1161 s = l[0]
1182 s = l[0]
1162 keys = keys.split()
1183 keys = keys.split()
1163 l = []
1184 l = []
1164 def invert(s):
1185 def invert(s):
1165 return "".join(chr(255 - ord(c)) for c in s)
1186 return "".join(chr(255 - ord(c)) for c in s)
1166 for r in getset(repo, subset, s):
1187 for r in getset(repo, subset, s):
1167 c = repo[r]
1188 c = repo[r]
1168 e = []
1189 e = []
1169 for k in keys:
1190 for k in keys:
1170 if k == 'rev':
1191 if k == 'rev':
1171 e.append(r)
1192 e.append(r)
1172 elif k == '-rev':
1193 elif k == '-rev':
1173 e.append(-r)
1194 e.append(-r)
1174 elif k == 'branch':
1195 elif k == 'branch':
1175 e.append(c.branch())
1196 e.append(c.branch())
1176 elif k == '-branch':
1197 elif k == '-branch':
1177 e.append(invert(c.branch()))
1198 e.append(invert(c.branch()))
1178 elif k == 'desc':
1199 elif k == 'desc':
1179 e.append(c.description())
1200 e.append(c.description())
1180 elif k == '-desc':
1201 elif k == '-desc':
1181 e.append(invert(c.description()))
1202 e.append(invert(c.description()))
1182 elif k in 'user author':
1203 elif k in 'user author':
1183 e.append(c.user())
1204 e.append(c.user())
1184 elif k in '-user -author':
1205 elif k in '-user -author':
1185 e.append(invert(c.user()))
1206 e.append(invert(c.user()))
1186 elif k == 'date':
1207 elif k == 'date':
1187 e.append(c.date()[0])
1208 e.append(c.date()[0])
1188 elif k == '-date':
1209 elif k == '-date':
1189 e.append(-c.date()[0])
1210 e.append(-c.date()[0])
1190 else:
1211 else:
1191 raise error.ParseError(_("unknown sort key %r") % k)
1212 raise error.ParseError(_("unknown sort key %r") % k)
1192 e.append(r)
1213 e.append(r)
1193 l.append(e)
1214 l.append(e)
1194 l.sort()
1215 l.sort()
1195 return [e[-1] for e in l]
1216 return [e[-1] for e in l]
1196
1217
1197 def _stringmatcher(pattern):
1218 def _stringmatcher(pattern):
1198 """
1219 """
1199 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1220 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1200 returns the matcher name, pattern, and matcher function.
1221 returns the matcher name, pattern, and matcher function.
1201 missing or unknown prefixes are treated as literal matches.
1222 missing or unknown prefixes are treated as literal matches.
1202
1223
1203 helper for tests:
1224 helper for tests:
1204 >>> def test(pattern, *tests):
1225 >>> def test(pattern, *tests):
1205 ... kind, pattern, matcher = _stringmatcher(pattern)
1226 ... kind, pattern, matcher = _stringmatcher(pattern)
1206 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1227 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1207
1228
1208 exact matching (no prefix):
1229 exact matching (no prefix):
1209 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1230 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1210 ('literal', 'abcdefg', [False, False, True])
1231 ('literal', 'abcdefg', [False, False, True])
1211
1232
1212 regex matching ('re:' prefix)
1233 regex matching ('re:' prefix)
1213 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1234 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1214 ('re', 'a.+b', [False, False, True])
1235 ('re', 'a.+b', [False, False, True])
1215
1236
1216 force exact matches ('literal:' prefix)
1237 force exact matches ('literal:' prefix)
1217 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1238 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1218 ('literal', 're:foobar', [False, True])
1239 ('literal', 're:foobar', [False, True])
1219
1240
1220 unknown prefixes are ignored and treated as literals
1241 unknown prefixes are ignored and treated as literals
1221 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1242 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1222 ('literal', 'foo:bar', [False, False, True])
1243 ('literal', 'foo:bar', [False, False, True])
1223 """
1244 """
1224 if pattern.startswith('re:'):
1245 if pattern.startswith('re:'):
1225 pattern = pattern[3:]
1246 pattern = pattern[3:]
1226 try:
1247 try:
1227 regex = re.compile(pattern)
1248 regex = re.compile(pattern)
1228 except re.error, e:
1249 except re.error, e:
1229 raise error.ParseError(_('invalid regular expression: %s')
1250 raise error.ParseError(_('invalid regular expression: %s')
1230 % e)
1251 % e)
1231 return 're', pattern, regex.search
1252 return 're', pattern, regex.search
1232 elif pattern.startswith('literal:'):
1253 elif pattern.startswith('literal:'):
1233 pattern = pattern[8:]
1254 pattern = pattern[8:]
1234 return 'literal', pattern, pattern.__eq__
1255 return 'literal', pattern, pattern.__eq__
1235
1256
1236 def _substringmatcher(pattern):
1257 def _substringmatcher(pattern):
1237 kind, pattern, matcher = _stringmatcher(pattern)
1258 kind, pattern, matcher = _stringmatcher(pattern)
1238 if kind == 'literal':
1259 if kind == 'literal':
1239 matcher = lambda s: pattern in s
1260 matcher = lambda s: pattern in s
1240 return kind, pattern, matcher
1261 return kind, pattern, matcher
1241
1262
1242 def tag(repo, subset, x):
1263 def tag(repo, subset, x):
1243 """``tag([name])``
1264 """``tag([name])``
1244 The specified tag by name, or all tagged revisions if no name is given.
1265 The specified tag by name, or all tagged revisions if no name is given.
1245 """
1266 """
1246 # i18n: "tag" is a keyword
1267 # i18n: "tag" is a keyword
1247 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1268 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1248 cl = repo.changelog
1269 cl = repo.changelog
1249 if args:
1270 if args:
1250 pattern = getstring(args[0],
1271 pattern = getstring(args[0],
1251 # i18n: "tag" is a keyword
1272 # i18n: "tag" is a keyword
1252 _('the argument to tag must be a string'))
1273 _('the argument to tag must be a string'))
1253 kind, pattern, matcher = _stringmatcher(pattern)
1274 kind, pattern, matcher = _stringmatcher(pattern)
1254 if kind == 'literal':
1275 if kind == 'literal':
1255 # avoid resolving all tags
1276 # avoid resolving all tags
1256 tn = repo._tagscache.tags.get(pattern, None)
1277 tn = repo._tagscache.tags.get(pattern, None)
1257 if tn is None:
1278 if tn is None:
1258 raise util.Abort(_("tag '%s' does not exist") % pattern)
1279 raise util.Abort(_("tag '%s' does not exist") % pattern)
1259 s = set([repo[tn].rev()])
1280 s = set([repo[tn].rev()])
1260 else:
1281 else:
1261 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1282 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1262 if not s:
1283 if not s:
1263 raise util.Abort(_("no tags exist that match '%s'") % pattern)
1284 raise util.Abort(_("no tags exist that match '%s'") % pattern)
1264 else:
1285 else:
1265 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1286 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1266 return [r for r in subset if r in s]
1287 return [r for r in subset if r in s]
1267
1288
1268 def tagged(repo, subset, x):
1289 def tagged(repo, subset, x):
1269 return tag(repo, subset, x)
1290 return tag(repo, subset, x)
1270
1291
1271 def user(repo, subset, x):
1292 def user(repo, subset, x):
1272 """``user(string)``
1293 """``user(string)``
1273 User name contains string. The match is case-insensitive.
1294 User name contains string. The match is case-insensitive.
1274
1295
1275 If `string` starts with `re:`, the remainder of the string is treated as
1296 If `string` starts with `re:`, the remainder of the string is treated as
1276 a regular expression. To match a user that actually contains `re:`, use
1297 a regular expression. To match a user that actually contains `re:`, use
1277 the prefix `literal:`.
1298 the prefix `literal:`.
1278 """
1299 """
1279 return author(repo, subset, x)
1300 return author(repo, subset, x)
1280
1301
1281 # for internal use
1302 # for internal use
1282 def _list(repo, subset, x):
1303 def _list(repo, subset, x):
1283 s = getstring(x, "internal error")
1304 s = getstring(x, "internal error")
1284 if not s:
1305 if not s:
1285 return []
1306 return []
1286 if not isinstance(subset, set):
1307 if not isinstance(subset, set):
1287 subset = set(subset)
1308 subset = set(subset)
1288 ls = [repo[r].rev() for r in s.split('\0')]
1309 ls = [repo[r].rev() for r in s.split('\0')]
1289 return [r for r in ls if r in subset]
1310 return [r for r in ls if r in subset]
1290
1311
1291 symbols = {
1312 symbols = {
1292 "adds": adds,
1313 "adds": adds,
1293 "all": getall,
1314 "all": getall,
1294 "ancestor": ancestor,
1315 "ancestor": ancestor,
1295 "ancestors": ancestors,
1316 "ancestors": ancestors,
1296 "_firstancestors": _firstancestors,
1317 "_firstancestors": _firstancestors,
1297 "author": author,
1318 "author": author,
1298 "bisect": bisect,
1319 "bisect": bisect,
1299 "bisected": bisected,
1320 "bisected": bisected,
1300 "bookmark": bookmark,
1321 "bookmark": bookmark,
1301 "branch": branch,
1322 "branch": branch,
1302 "children": children,
1323 "children": children,
1303 "closed": closed,
1324 "closed": closed,
1304 "contains": contains,
1325 "contains": contains,
1326 "converted": converted,
1305 "date": date,
1327 "date": date,
1306 "desc": desc,
1328 "desc": desc,
1307 "descendants": descendants,
1329 "descendants": descendants,
1308 "_firstdescendants": _firstdescendants,
1330 "_firstdescendants": _firstdescendants,
1309 "draft": draft,
1331 "draft": draft,
1310 "extra": extra,
1332 "extra": extra,
1311 "file": hasfile,
1333 "file": hasfile,
1312 "filelog": filelog,
1334 "filelog": filelog,
1313 "first": first,
1335 "first": first,
1314 "follow": follow,
1336 "follow": follow,
1315 "_followfirst": _followfirst,
1337 "_followfirst": _followfirst,
1316 "grep": grep,
1338 "grep": grep,
1317 "head": head,
1339 "head": head,
1318 "heads": heads,
1340 "heads": heads,
1319 "id": node_,
1341 "id": node_,
1320 "keyword": keyword,
1342 "keyword": keyword,
1321 "last": last,
1343 "last": last,
1322 "limit": limit,
1344 "limit": limit,
1323 "_matchfiles": _matchfiles,
1345 "_matchfiles": _matchfiles,
1324 "max": maxrev,
1346 "max": maxrev,
1325 "merge": merge,
1347 "merge": merge,
1326 "min": minrev,
1348 "min": minrev,
1327 "modifies": modifies,
1349 "modifies": modifies,
1328 "outgoing": outgoing,
1350 "outgoing": outgoing,
1329 "p1": p1,
1351 "p1": p1,
1330 "p2": p2,
1352 "p2": p2,
1331 "parents": parents,
1353 "parents": parents,
1332 "present": present,
1354 "present": present,
1333 "public": public,
1355 "public": public,
1334 "remote": remote,
1356 "remote": remote,
1335 "removes": removes,
1357 "removes": removes,
1336 "rev": rev,
1358 "rev": rev,
1337 "reverse": reverse,
1359 "reverse": reverse,
1338 "roots": roots,
1360 "roots": roots,
1339 "sort": sort,
1361 "sort": sort,
1340 "secret": secret,
1362 "secret": secret,
1341 "matching": matching,
1363 "matching": matching,
1342 "tag": tag,
1364 "tag": tag,
1343 "tagged": tagged,
1365 "tagged": tagged,
1344 "user": user,
1366 "user": user,
1345 "_list": _list,
1367 "_list": _list,
1346 }
1368 }
1347
1369
1348 methods = {
1370 methods = {
1349 "range": rangeset,
1371 "range": rangeset,
1350 "dagrange": dagrange,
1372 "dagrange": dagrange,
1351 "string": stringset,
1373 "string": stringset,
1352 "symbol": symbolset,
1374 "symbol": symbolset,
1353 "and": andset,
1375 "and": andset,
1354 "or": orset,
1376 "or": orset,
1355 "not": notset,
1377 "not": notset,
1356 "list": listset,
1378 "list": listset,
1357 "func": func,
1379 "func": func,
1358 "ancestor": ancestorspec,
1380 "ancestor": ancestorspec,
1359 "parent": parentspec,
1381 "parent": parentspec,
1360 "parentpost": p1,
1382 "parentpost": p1,
1361 }
1383 }
1362
1384
1363 def optimize(x, small):
1385 def optimize(x, small):
1364 if x is None:
1386 if x is None:
1365 return 0, x
1387 return 0, x
1366
1388
1367 smallbonus = 1
1389 smallbonus = 1
1368 if small:
1390 if small:
1369 smallbonus = .5
1391 smallbonus = .5
1370
1392
1371 op = x[0]
1393 op = x[0]
1372 if op == 'minus':
1394 if op == 'minus':
1373 return optimize(('and', x[1], ('not', x[2])), small)
1395 return optimize(('and', x[1], ('not', x[2])), small)
1374 elif op == 'dagrangepre':
1396 elif op == 'dagrangepre':
1375 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1397 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1376 elif op == 'dagrangepost':
1398 elif op == 'dagrangepost':
1377 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1399 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1378 elif op == 'rangepre':
1400 elif op == 'rangepre':
1379 return optimize(('range', ('string', '0'), x[1]), small)
1401 return optimize(('range', ('string', '0'), x[1]), small)
1380 elif op == 'rangepost':
1402 elif op == 'rangepost':
1381 return optimize(('range', x[1], ('string', 'tip')), small)
1403 return optimize(('range', x[1], ('string', 'tip')), small)
1382 elif op == 'negate':
1404 elif op == 'negate':
1383 return optimize(('string',
1405 return optimize(('string',
1384 '-' + getstring(x[1], _("can't negate that"))), small)
1406 '-' + getstring(x[1], _("can't negate that"))), small)
1385 elif op in 'string symbol negate':
1407 elif op in 'string symbol negate':
1386 return smallbonus, x # single revisions are small
1408 return smallbonus, x # single revisions are small
1387 elif op == 'and':
1409 elif op == 'and':
1388 wa, ta = optimize(x[1], True)
1410 wa, ta = optimize(x[1], True)
1389 wb, tb = optimize(x[2], True)
1411 wb, tb = optimize(x[2], True)
1390 w = min(wa, wb)
1412 w = min(wa, wb)
1391 if wa > wb:
1413 if wa > wb:
1392 return w, (op, tb, ta)
1414 return w, (op, tb, ta)
1393 return w, (op, ta, tb)
1415 return w, (op, ta, tb)
1394 elif op == 'or':
1416 elif op == 'or':
1395 wa, ta = optimize(x[1], False)
1417 wa, ta = optimize(x[1], False)
1396 wb, tb = optimize(x[2], False)
1418 wb, tb = optimize(x[2], False)
1397 if wb < wa:
1419 if wb < wa:
1398 wb, wa = wa, wb
1420 wb, wa = wa, wb
1399 return max(wa, wb), (op, ta, tb)
1421 return max(wa, wb), (op, ta, tb)
1400 elif op == 'not':
1422 elif op == 'not':
1401 o = optimize(x[1], not small)
1423 o = optimize(x[1], not small)
1402 return o[0], (op, o[1])
1424 return o[0], (op, o[1])
1403 elif op == 'parentpost':
1425 elif op == 'parentpost':
1404 o = optimize(x[1], small)
1426 o = optimize(x[1], small)
1405 return o[0], (op, o[1])
1427 return o[0], (op, o[1])
1406 elif op == 'group':
1428 elif op == 'group':
1407 return optimize(x[1], small)
1429 return optimize(x[1], small)
1408 elif op in 'dagrange range list parent ancestorspec':
1430 elif op in 'dagrange range list parent ancestorspec':
1409 if op == 'parent':
1431 if op == 'parent':
1410 # x^:y means (x^) : y, not x ^ (:y)
1432 # x^:y means (x^) : y, not x ^ (:y)
1411 post = ('parentpost', x[1])
1433 post = ('parentpost', x[1])
1412 if x[2][0] == 'dagrangepre':
1434 if x[2][0] == 'dagrangepre':
1413 return optimize(('dagrange', post, x[2][1]), small)
1435 return optimize(('dagrange', post, x[2][1]), small)
1414 elif x[2][0] == 'rangepre':
1436 elif x[2][0] == 'rangepre':
1415 return optimize(('range', post, x[2][1]), small)
1437 return optimize(('range', post, x[2][1]), small)
1416
1438
1417 wa, ta = optimize(x[1], small)
1439 wa, ta = optimize(x[1], small)
1418 wb, tb = optimize(x[2], small)
1440 wb, tb = optimize(x[2], small)
1419 return wa + wb, (op, ta, tb)
1441 return wa + wb, (op, ta, tb)
1420 elif op == 'func':
1442 elif op == 'func':
1421 f = getstring(x[1], _("not a symbol"))
1443 f = getstring(x[1], _("not a symbol"))
1422 wa, ta = optimize(x[2], small)
1444 wa, ta = optimize(x[2], small)
1423 if f in ("author branch closed date desc file grep keyword "
1445 if f in ("author branch closed date desc file grep keyword "
1424 "outgoing user"):
1446 "outgoing user"):
1425 w = 10 # slow
1447 w = 10 # slow
1426 elif f in "modifies adds removes":
1448 elif f in "modifies adds removes":
1427 w = 30 # slower
1449 w = 30 # slower
1428 elif f == "contains":
1450 elif f == "contains":
1429 w = 100 # very slow
1451 w = 100 # very slow
1430 elif f == "ancestor":
1452 elif f == "ancestor":
1431 w = 1 * smallbonus
1453 w = 1 * smallbonus
1432 elif f in "reverse limit first":
1454 elif f in "reverse limit first":
1433 w = 0
1455 w = 0
1434 elif f in "sort":
1456 elif f in "sort":
1435 w = 10 # assume most sorts look at changelog
1457 w = 10 # assume most sorts look at changelog
1436 else:
1458 else:
1437 w = 1
1459 w = 1
1438 return w + wa, (op, x[1], ta)
1460 return w + wa, (op, x[1], ta)
1439 return 1, x
1461 return 1, x
1440
1462
1441 _aliasarg = ('func', ('symbol', '_aliasarg'))
1463 _aliasarg = ('func', ('symbol', '_aliasarg'))
1442 def _getaliasarg(tree):
1464 def _getaliasarg(tree):
1443 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1465 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1444 return X, None otherwise.
1466 return X, None otherwise.
1445 """
1467 """
1446 if (len(tree) == 3 and tree[:2] == _aliasarg
1468 if (len(tree) == 3 and tree[:2] == _aliasarg
1447 and tree[2][0] == 'string'):
1469 and tree[2][0] == 'string'):
1448 return tree[2][1]
1470 return tree[2][1]
1449 return None
1471 return None
1450
1472
1451 def _checkaliasarg(tree, known=None):
1473 def _checkaliasarg(tree, known=None):
1452 """Check tree contains no _aliasarg construct or only ones which
1474 """Check tree contains no _aliasarg construct or only ones which
1453 value is in known. Used to avoid alias placeholders injection.
1475 value is in known. Used to avoid alias placeholders injection.
1454 """
1476 """
1455 if isinstance(tree, tuple):
1477 if isinstance(tree, tuple):
1456 arg = _getaliasarg(tree)
1478 arg = _getaliasarg(tree)
1457 if arg is not None and (not known or arg not in known):
1479 if arg is not None and (not known or arg not in known):
1458 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1480 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1459 for t in tree:
1481 for t in tree:
1460 _checkaliasarg(t, known)
1482 _checkaliasarg(t, known)
1461
1483
1462 class revsetalias(object):
1484 class revsetalias(object):
1463 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1485 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1464 args = None
1486 args = None
1465
1487
1466 def __init__(self, name, value):
1488 def __init__(self, name, value):
1467 '''Aliases like:
1489 '''Aliases like:
1468
1490
1469 h = heads(default)
1491 h = heads(default)
1470 b($1) = ancestors($1) - ancestors(default)
1492 b($1) = ancestors($1) - ancestors(default)
1471 '''
1493 '''
1472 m = self.funcre.search(name)
1494 m = self.funcre.search(name)
1473 if m:
1495 if m:
1474 self.name = m.group(1)
1496 self.name = m.group(1)
1475 self.tree = ('func', ('symbol', m.group(1)))
1497 self.tree = ('func', ('symbol', m.group(1)))
1476 self.args = [x.strip() for x in m.group(2).split(',')]
1498 self.args = [x.strip() for x in m.group(2).split(',')]
1477 for arg in self.args:
1499 for arg in self.args:
1478 # _aliasarg() is an unknown symbol only used separate
1500 # _aliasarg() is an unknown symbol only used separate
1479 # alias argument placeholders from regular strings.
1501 # alias argument placeholders from regular strings.
1480 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1502 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1481 else:
1503 else:
1482 self.name = name
1504 self.name = name
1483 self.tree = ('symbol', name)
1505 self.tree = ('symbol', name)
1484
1506
1485 self.replacement, pos = parse(value)
1507 self.replacement, pos = parse(value)
1486 if pos != len(value):
1508 if pos != len(value):
1487 raise error.ParseError(_('invalid token'), pos)
1509 raise error.ParseError(_('invalid token'), pos)
1488 # Check for placeholder injection
1510 # Check for placeholder injection
1489 _checkaliasarg(self.replacement, self.args)
1511 _checkaliasarg(self.replacement, self.args)
1490
1512
1491 def _getalias(aliases, tree):
1513 def _getalias(aliases, tree):
1492 """If tree looks like an unexpanded alias, return it. Return None
1514 """If tree looks like an unexpanded alias, return it. Return None
1493 otherwise.
1515 otherwise.
1494 """
1516 """
1495 if isinstance(tree, tuple) and tree:
1517 if isinstance(tree, tuple) and tree:
1496 if tree[0] == 'symbol' and len(tree) == 2:
1518 if tree[0] == 'symbol' and len(tree) == 2:
1497 name = tree[1]
1519 name = tree[1]
1498 alias = aliases.get(name)
1520 alias = aliases.get(name)
1499 if alias and alias.args is None and alias.tree == tree:
1521 if alias and alias.args is None and alias.tree == tree:
1500 return alias
1522 return alias
1501 if tree[0] == 'func' and len(tree) > 1:
1523 if tree[0] == 'func' and len(tree) > 1:
1502 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1524 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1503 name = tree[1][1]
1525 name = tree[1][1]
1504 alias = aliases.get(name)
1526 alias = aliases.get(name)
1505 if alias and alias.args is not None and alias.tree == tree[:2]:
1527 if alias and alias.args is not None and alias.tree == tree[:2]:
1506 return alias
1528 return alias
1507 return None
1529 return None
1508
1530
1509 def _expandargs(tree, args):
1531 def _expandargs(tree, args):
1510 """Replace _aliasarg instances with the substitution value of the
1532 """Replace _aliasarg instances with the substitution value of the
1511 same name in args, recursively.
1533 same name in args, recursively.
1512 """
1534 """
1513 if not tree or not isinstance(tree, tuple):
1535 if not tree or not isinstance(tree, tuple):
1514 return tree
1536 return tree
1515 arg = _getaliasarg(tree)
1537 arg = _getaliasarg(tree)
1516 if arg is not None:
1538 if arg is not None:
1517 return args[arg]
1539 return args[arg]
1518 return tuple(_expandargs(t, args) for t in tree)
1540 return tuple(_expandargs(t, args) for t in tree)
1519
1541
1520 def _expandaliases(aliases, tree, expanding, cache):
1542 def _expandaliases(aliases, tree, expanding, cache):
1521 """Expand aliases in tree, recursively.
1543 """Expand aliases in tree, recursively.
1522
1544
1523 'aliases' is a dictionary mapping user defined aliases to
1545 'aliases' is a dictionary mapping user defined aliases to
1524 revsetalias objects.
1546 revsetalias objects.
1525 """
1547 """
1526 if not isinstance(tree, tuple):
1548 if not isinstance(tree, tuple):
1527 # Do not expand raw strings
1549 # Do not expand raw strings
1528 return tree
1550 return tree
1529 alias = _getalias(aliases, tree)
1551 alias = _getalias(aliases, tree)
1530 if alias is not None:
1552 if alias is not None:
1531 if alias in expanding:
1553 if alias in expanding:
1532 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1554 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1533 'detected') % alias.name)
1555 'detected') % alias.name)
1534 expanding.append(alias)
1556 expanding.append(alias)
1535 if alias.name not in cache:
1557 if alias.name not in cache:
1536 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1558 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1537 expanding, cache)
1559 expanding, cache)
1538 result = cache[alias.name]
1560 result = cache[alias.name]
1539 expanding.pop()
1561 expanding.pop()
1540 if alias.args is not None:
1562 if alias.args is not None:
1541 l = getlist(tree[2])
1563 l = getlist(tree[2])
1542 if len(l) != len(alias.args):
1564 if len(l) != len(alias.args):
1543 raise error.ParseError(
1565 raise error.ParseError(
1544 _('invalid number of arguments: %s') % len(l))
1566 _('invalid number of arguments: %s') % len(l))
1545 l = [_expandaliases(aliases, a, [], cache) for a in l]
1567 l = [_expandaliases(aliases, a, [], cache) for a in l]
1546 result = _expandargs(result, dict(zip(alias.args, l)))
1568 result = _expandargs(result, dict(zip(alias.args, l)))
1547 else:
1569 else:
1548 result = tuple(_expandaliases(aliases, t, expanding, cache)
1570 result = tuple(_expandaliases(aliases, t, expanding, cache)
1549 for t in tree)
1571 for t in tree)
1550 return result
1572 return result
1551
1573
1552 def findaliases(ui, tree):
1574 def findaliases(ui, tree):
1553 _checkaliasarg(tree)
1575 _checkaliasarg(tree)
1554 aliases = {}
1576 aliases = {}
1555 for k, v in ui.configitems('revsetalias'):
1577 for k, v in ui.configitems('revsetalias'):
1556 alias = revsetalias(k, v)
1578 alias = revsetalias(k, v)
1557 aliases[alias.name] = alias
1579 aliases[alias.name] = alias
1558 return _expandaliases(aliases, tree, [], {})
1580 return _expandaliases(aliases, tree, [], {})
1559
1581
1560 parse = parser.parser(tokenize, elements).parse
1582 parse = parser.parser(tokenize, elements).parse
1561
1583
1562 def match(ui, spec):
1584 def match(ui, spec):
1563 if not spec:
1585 if not spec:
1564 raise error.ParseError(_("empty query"))
1586 raise error.ParseError(_("empty query"))
1565 tree, pos = parse(spec)
1587 tree, pos = parse(spec)
1566 if (pos != len(spec)):
1588 if (pos != len(spec)):
1567 raise error.ParseError(_("invalid token"), pos)
1589 raise error.ParseError(_("invalid token"), pos)
1568 if ui:
1590 if ui:
1569 tree = findaliases(ui, tree)
1591 tree = findaliases(ui, tree)
1570 weight, tree = optimize(tree, True)
1592 weight, tree = optimize(tree, True)
1571 def mfunc(repo, subset):
1593 def mfunc(repo, subset):
1572 return getset(repo, subset, tree)
1594 return getset(repo, subset, tree)
1573 return mfunc
1595 return mfunc
1574
1596
1575 def formatspec(expr, *args):
1597 def formatspec(expr, *args):
1576 '''
1598 '''
1577 This is a convenience function for using revsets internally, and
1599 This is a convenience function for using revsets internally, and
1578 escapes arguments appropriately. Aliases are intentionally ignored
1600 escapes arguments appropriately. Aliases are intentionally ignored
1579 so that intended expression behavior isn't accidentally subverted.
1601 so that intended expression behavior isn't accidentally subverted.
1580
1602
1581 Supported arguments:
1603 Supported arguments:
1582
1604
1583 %r = revset expression, parenthesized
1605 %r = revset expression, parenthesized
1584 %d = int(arg), no quoting
1606 %d = int(arg), no quoting
1585 %s = string(arg), escaped and single-quoted
1607 %s = string(arg), escaped and single-quoted
1586 %b = arg.branch(), escaped and single-quoted
1608 %b = arg.branch(), escaped and single-quoted
1587 %n = hex(arg), single-quoted
1609 %n = hex(arg), single-quoted
1588 %% = a literal '%'
1610 %% = a literal '%'
1589
1611
1590 Prefixing the type with 'l' specifies a parenthesized list of that type.
1612 Prefixing the type with 'l' specifies a parenthesized list of that type.
1591
1613
1592 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1614 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1593 '(10 or 11):: and ((this()) or (that()))'
1615 '(10 or 11):: and ((this()) or (that()))'
1594 >>> formatspec('%d:: and not %d::', 10, 20)
1616 >>> formatspec('%d:: and not %d::', 10, 20)
1595 '10:: and not 20::'
1617 '10:: and not 20::'
1596 >>> formatspec('%ld or %ld', [], [1])
1618 >>> formatspec('%ld or %ld', [], [1])
1597 "_list('') or 1"
1619 "_list('') or 1"
1598 >>> formatspec('keyword(%s)', 'foo\\xe9')
1620 >>> formatspec('keyword(%s)', 'foo\\xe9')
1599 "keyword('foo\\\\xe9')"
1621 "keyword('foo\\\\xe9')"
1600 >>> b = lambda: 'default'
1622 >>> b = lambda: 'default'
1601 >>> b.branch = b
1623 >>> b.branch = b
1602 >>> formatspec('branch(%b)', b)
1624 >>> formatspec('branch(%b)', b)
1603 "branch('default')"
1625 "branch('default')"
1604 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1626 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1605 "root(_list('a\\x00b\\x00c\\x00d'))"
1627 "root(_list('a\\x00b\\x00c\\x00d'))"
1606 '''
1628 '''
1607
1629
1608 def quote(s):
1630 def quote(s):
1609 return repr(str(s))
1631 return repr(str(s))
1610
1632
1611 def argtype(c, arg):
1633 def argtype(c, arg):
1612 if c == 'd':
1634 if c == 'd':
1613 return str(int(arg))
1635 return str(int(arg))
1614 elif c == 's':
1636 elif c == 's':
1615 return quote(arg)
1637 return quote(arg)
1616 elif c == 'r':
1638 elif c == 'r':
1617 parse(arg) # make sure syntax errors are confined
1639 parse(arg) # make sure syntax errors are confined
1618 return '(%s)' % arg
1640 return '(%s)' % arg
1619 elif c == 'n':
1641 elif c == 'n':
1620 return quote(node.hex(arg))
1642 return quote(node.hex(arg))
1621 elif c == 'b':
1643 elif c == 'b':
1622 return quote(arg.branch())
1644 return quote(arg.branch())
1623
1645
1624 def listexp(s, t):
1646 def listexp(s, t):
1625 l = len(s)
1647 l = len(s)
1626 if l == 0:
1648 if l == 0:
1627 return "_list('')"
1649 return "_list('')"
1628 elif l == 1:
1650 elif l == 1:
1629 return argtype(t, s[0])
1651 return argtype(t, s[0])
1630 elif t == 'd':
1652 elif t == 'd':
1631 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1653 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1632 elif t == 's':
1654 elif t == 's':
1633 return "_list('%s')" % "\0".join(s)
1655 return "_list('%s')" % "\0".join(s)
1634 elif t == 'n':
1656 elif t == 'n':
1635 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1657 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1636 elif t == 'b':
1658 elif t == 'b':
1637 return "_list('%s')" % "\0".join(a.branch() for a in s)
1659 return "_list('%s')" % "\0".join(a.branch() for a in s)
1638
1660
1639 m = l // 2
1661 m = l // 2
1640 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1662 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1641
1663
1642 ret = ''
1664 ret = ''
1643 pos = 0
1665 pos = 0
1644 arg = 0
1666 arg = 0
1645 while pos < len(expr):
1667 while pos < len(expr):
1646 c = expr[pos]
1668 c = expr[pos]
1647 if c == '%':
1669 if c == '%':
1648 pos += 1
1670 pos += 1
1649 d = expr[pos]
1671 d = expr[pos]
1650 if d == '%':
1672 if d == '%':
1651 ret += d
1673 ret += d
1652 elif d in 'dsnbr':
1674 elif d in 'dsnbr':
1653 ret += argtype(d, args[arg])
1675 ret += argtype(d, args[arg])
1654 arg += 1
1676 arg += 1
1655 elif d == 'l':
1677 elif d == 'l':
1656 # a list of some type
1678 # a list of some type
1657 pos += 1
1679 pos += 1
1658 d = expr[pos]
1680 d = expr[pos]
1659 ret += listexp(list(args[arg]), d)
1681 ret += listexp(list(args[arg]), d)
1660 arg += 1
1682 arg += 1
1661 else:
1683 else:
1662 raise util.Abort('unexpected revspec format character %s' % d)
1684 raise util.Abort('unexpected revspec format character %s' % d)
1663 else:
1685 else:
1664 ret += c
1686 ret += c
1665 pos += 1
1687 pos += 1
1666
1688
1667 return ret
1689 return ret
1668
1690
1669 def prettyformat(tree):
1691 def prettyformat(tree):
1670 def _prettyformat(tree, level, lines):
1692 def _prettyformat(tree, level, lines):
1671 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1693 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1672 lines.append((level, str(tree)))
1694 lines.append((level, str(tree)))
1673 else:
1695 else:
1674 lines.append((level, '(%s' % tree[0]))
1696 lines.append((level, '(%s' % tree[0]))
1675 for s in tree[1:]:
1697 for s in tree[1:]:
1676 _prettyformat(s, level + 1, lines)
1698 _prettyformat(s, level + 1, lines)
1677 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1699 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1678
1700
1679 lines = []
1701 lines = []
1680 _prettyformat(tree, 0, lines)
1702 _prettyformat(tree, 0, lines)
1681 output = '\n'.join((' '*l + s) for l, s in lines)
1703 output = '\n'.join((' '*l + s) for l, s in lines)
1682 return output
1704 return output
1683
1705
1684 # tell hggettext to extract docstrings from these functions:
1706 # tell hggettext to extract docstrings from these functions:
1685 i18nfunctions = symbols.values()
1707 i18nfunctions = symbols.values()
@@ -1,398 +1,447 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > convert=
3 > convert=
4 > [convert]
4 > [convert]
5 > hg.saverev=False
5 > hg.saverev=False
6 > EOF
6 > EOF
7 $ hg help convert
7 $ hg help convert
8 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
8 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
9
9
10 convert a foreign SCM repository to a Mercurial one.
10 convert a foreign SCM repository to a Mercurial one.
11
11
12 Accepted source formats [identifiers]:
12 Accepted source formats [identifiers]:
13
13
14 - Mercurial [hg]
14 - Mercurial [hg]
15 - CVS [cvs]
15 - CVS [cvs]
16 - Darcs [darcs]
16 - Darcs [darcs]
17 - git [git]
17 - git [git]
18 - Subversion [svn]
18 - Subversion [svn]
19 - Monotone [mtn]
19 - Monotone [mtn]
20 - GNU Arch [gnuarch]
20 - GNU Arch [gnuarch]
21 - Bazaar [bzr]
21 - Bazaar [bzr]
22 - Perforce [p4]
22 - Perforce [p4]
23
23
24 Accepted destination formats [identifiers]:
24 Accepted destination formats [identifiers]:
25
25
26 - Mercurial [hg]
26 - Mercurial [hg]
27 - Subversion [svn] (history on branches is not preserved)
27 - Subversion [svn] (history on branches is not preserved)
28
28
29 If no revision is given, all revisions will be converted. Otherwise,
29 If no revision is given, all revisions will be converted. Otherwise,
30 convert will only import up to the named revision (given in a format
30 convert will only import up to the named revision (given in a format
31 understood by the source).
31 understood by the source).
32
32
33 If no destination directory name is specified, it defaults to the basename
33 If no destination directory name is specified, it defaults to the basename
34 of the source with "-hg" appended. If the destination repository doesn't
34 of the source with "-hg" appended. If the destination repository doesn't
35 exist, it will be created.
35 exist, it will be created.
36
36
37 By default, all sources except Mercurial will use --branchsort. Mercurial
37 By default, all sources except Mercurial will use --branchsort. Mercurial
38 uses --sourcesort to preserve original revision numbers order. Sort modes
38 uses --sourcesort to preserve original revision numbers order. Sort modes
39 have the following effects:
39 have the following effects:
40
40
41 --branchsort convert from parent to child revision when possible, which
41 --branchsort convert from parent to child revision when possible, which
42 means branches are usually converted one after the other.
42 means branches are usually converted one after the other.
43 It generates more compact repositories.
43 It generates more compact repositories.
44 --datesort sort revisions by date. Converted repositories have good-
44 --datesort sort revisions by date. Converted repositories have good-
45 looking changelogs but are often an order of magnitude
45 looking changelogs but are often an order of magnitude
46 larger than the same ones generated by --branchsort.
46 larger than the same ones generated by --branchsort.
47 --sourcesort try to preserve source revisions order, only supported by
47 --sourcesort try to preserve source revisions order, only supported by
48 Mercurial sources.
48 Mercurial sources.
49
49
50 If "REVMAP" isn't given, it will be put in a default location
50 If "REVMAP" isn't given, it will be put in a default location
51 ("<dest>/.hg/shamap" by default). The "REVMAP" is a simple text file that
51 ("<dest>/.hg/shamap" by default). The "REVMAP" is a simple text file that
52 maps each source commit ID to the destination ID for that revision, like
52 maps each source commit ID to the destination ID for that revision, like
53 so:
53 so:
54
54
55 <source ID> <destination ID>
55 <source ID> <destination ID>
56
56
57 If the file doesn't exist, it's automatically created. It's updated on
57 If the file doesn't exist, it's automatically created. It's updated on
58 each commit copied, so "hg convert" can be interrupted and can be run
58 each commit copied, so "hg convert" can be interrupted and can be run
59 repeatedly to copy new commits.
59 repeatedly to copy new commits.
60
60
61 The authormap is a simple text file that maps each source commit author to
61 The authormap is a simple text file that maps each source commit author to
62 a destination commit author. It is handy for source SCMs that use unix
62 a destination commit author. It is handy for source SCMs that use unix
63 logins to identify authors (eg: CVS). One line per author mapping and the
63 logins to identify authors (eg: CVS). One line per author mapping and the
64 line format is:
64 line format is:
65
65
66 source author = destination author
66 source author = destination author
67
67
68 Empty lines and lines starting with a "#" are ignored.
68 Empty lines and lines starting with a "#" are ignored.
69
69
70 The filemap is a file that allows filtering and remapping of files and
70 The filemap is a file that allows filtering and remapping of files and
71 directories. Each line can contain one of the following directives:
71 directories. Each line can contain one of the following directives:
72
72
73 include path/to/file-or-dir
73 include path/to/file-or-dir
74
74
75 exclude path/to/file-or-dir
75 exclude path/to/file-or-dir
76
76
77 rename path/to/source path/to/destination
77 rename path/to/source path/to/destination
78
78
79 Comment lines start with "#". A specified path matches if it equals the
79 Comment lines start with "#". A specified path matches if it equals the
80 full relative name of a file or one of its parent directories. The
80 full relative name of a file or one of its parent directories. The
81 "include" or "exclude" directive with the longest matching path applies,
81 "include" or "exclude" directive with the longest matching path applies,
82 so line order does not matter.
82 so line order does not matter.
83
83
84 The "include" directive causes a file, or all files under a directory, to
84 The "include" directive causes a file, or all files under a directory, to
85 be included in the destination repository, and the exclusion of all other
85 be included in the destination repository, and the exclusion of all other
86 files and directories not explicitly included. The "exclude" directive
86 files and directories not explicitly included. The "exclude" directive
87 causes files or directories to be omitted. The "rename" directive renames
87 causes files or directories to be omitted. The "rename" directive renames
88 a file or directory if it is converted. To rename from a subdirectory into
88 a file or directory if it is converted. To rename from a subdirectory into
89 the root of the repository, use "." as the path to rename to.
89 the root of the repository, use "." as the path to rename to.
90
90
91 The splicemap is a file that allows insertion of synthetic history,
91 The splicemap is a file that allows insertion of synthetic history,
92 letting you specify the parents of a revision. This is useful if you want
92 letting you specify the parents of a revision. This is useful if you want
93 to e.g. give a Subversion merge two parents, or graft two disconnected
93 to e.g. give a Subversion merge two parents, or graft two disconnected
94 series of history together. Each entry contains a key, followed by a
94 series of history together. Each entry contains a key, followed by a
95 space, followed by one or two comma-separated values:
95 space, followed by one or two comma-separated values:
96
96
97 key parent1, parent2
97 key parent1, parent2
98
98
99 The key is the revision ID in the source revision control system whose
99 The key is the revision ID in the source revision control system whose
100 parents should be modified (same format as a key in .hg/shamap). The
100 parents should be modified (same format as a key in .hg/shamap). The
101 values are the revision IDs (in either the source or destination revision
101 values are the revision IDs (in either the source or destination revision
102 control system) that should be used as the new parents for that node. For
102 control system) that should be used as the new parents for that node. For
103 example, if you have merged "release-1.0" into "trunk", then you should
103 example, if you have merged "release-1.0" into "trunk", then you should
104 specify the revision on "trunk" as the first parent and the one on the
104 specify the revision on "trunk" as the first parent and the one on the
105 "release-1.0" branch as the second.
105 "release-1.0" branch as the second.
106
106
107 The branchmap is a file that allows you to rename a branch when it is
107 The branchmap is a file that allows you to rename a branch when it is
108 being brought in from whatever external repository. When used in
108 being brought in from whatever external repository. When used in
109 conjunction with a splicemap, it allows for a powerful combination to help
109 conjunction with a splicemap, it allows for a powerful combination to help
110 fix even the most badly mismanaged repositories and turn them into nicely
110 fix even the most badly mismanaged repositories and turn them into nicely
111 structured Mercurial repositories. The branchmap contains lines of the
111 structured Mercurial repositories. The branchmap contains lines of the
112 form:
112 form:
113
113
114 original_branch_name new_branch_name
114 original_branch_name new_branch_name
115
115
116 where "original_branch_name" is the name of the branch in the source
116 where "original_branch_name" is the name of the branch in the source
117 repository, and "new_branch_name" is the name of the branch is the
117 repository, and "new_branch_name" is the name of the branch is the
118 destination repository. No whitespace is allowed in the branch names. This
118 destination repository. No whitespace is allowed in the branch names. This
119 can be used to (for instance) move code in one repository from "default"
119 can be used to (for instance) move code in one repository from "default"
120 to a named branch.
120 to a named branch.
121
121
122 Mercurial Source
122 Mercurial Source
123 ''''''''''''''''
123 ''''''''''''''''
124
124
125 The Mercurial source recognizes the following configuration options, which
125 The Mercurial source recognizes the following configuration options, which
126 you can set on the command line with "--config":
126 you can set on the command line with "--config":
127
127
128 convert.hg.ignoreerrors
128 convert.hg.ignoreerrors
129 ignore integrity errors when reading. Use it to fix
129 ignore integrity errors when reading. Use it to fix
130 Mercurial repositories with missing revlogs, by converting
130 Mercurial repositories with missing revlogs, by converting
131 from and to Mercurial. Default is False.
131 from and to Mercurial. Default is False.
132 convert.hg.saverev
132 convert.hg.saverev
133 store original revision ID in changeset (forces target IDs
133 store original revision ID in changeset (forces target IDs
134 to change). It takes a boolean argument and defaults to
134 to change). It takes a boolean argument and defaults to
135 False.
135 False.
136 convert.hg.startrev
136 convert.hg.startrev
137 convert start revision and its descendants. It takes a hg
137 convert start revision and its descendants. It takes a hg
138 revision identifier and defaults to 0.
138 revision identifier and defaults to 0.
139
139
140 CVS Source
140 CVS Source
141 ''''''''''
141 ''''''''''
142
142
143 CVS source will use a sandbox (i.e. a checked-out copy) from CVS to
143 CVS source will use a sandbox (i.e. a checked-out copy) from CVS to
144 indicate the starting point of what will be converted. Direct access to
144 indicate the starting point of what will be converted. Direct access to
145 the repository files is not needed, unless of course the repository is
145 the repository files is not needed, unless of course the repository is
146 ":local:". The conversion uses the top level directory in the sandbox to
146 ":local:". The conversion uses the top level directory in the sandbox to
147 find the CVS repository, and then uses CVS rlog commands to find files to
147 find the CVS repository, and then uses CVS rlog commands to find files to
148 convert. This means that unless a filemap is given, all files under the
148 convert. This means that unless a filemap is given, all files under the
149 starting directory will be converted, and that any directory
149 starting directory will be converted, and that any directory
150 reorganization in the CVS sandbox is ignored.
150 reorganization in the CVS sandbox is ignored.
151
151
152 The following options can be used with "--config":
152 The following options can be used with "--config":
153
153
154 convert.cvsps.cache
154 convert.cvsps.cache
155 Set to False to disable remote log caching, for testing and
155 Set to False to disable remote log caching, for testing and
156 debugging purposes. Default is True.
156 debugging purposes. Default is True.
157 convert.cvsps.fuzz
157 convert.cvsps.fuzz
158 Specify the maximum time (in seconds) that is allowed
158 Specify the maximum time (in seconds) that is allowed
159 between commits with identical user and log message in a
159 between commits with identical user and log message in a
160 single changeset. When very large files were checked in as
160 single changeset. When very large files were checked in as
161 part of a changeset then the default may not be long enough.
161 part of a changeset then the default may not be long enough.
162 The default is 60.
162 The default is 60.
163 convert.cvsps.mergeto
163 convert.cvsps.mergeto
164 Specify a regular expression to which commit log messages
164 Specify a regular expression to which commit log messages
165 are matched. If a match occurs, then the conversion process
165 are matched. If a match occurs, then the conversion process
166 will insert a dummy revision merging the branch on which
166 will insert a dummy revision merging the branch on which
167 this log message occurs to the branch indicated in the
167 this log message occurs to the branch indicated in the
168 regex. Default is "{{mergetobranch ([-\w]+)}}"
168 regex. Default is "{{mergetobranch ([-\w]+)}}"
169 convert.cvsps.mergefrom
169 convert.cvsps.mergefrom
170 Specify a regular expression to which commit log messages
170 Specify a regular expression to which commit log messages
171 are matched. If a match occurs, then the conversion process
171 are matched. If a match occurs, then the conversion process
172 will add the most recent revision on the branch indicated in
172 will add the most recent revision on the branch indicated in
173 the regex as the second parent of the changeset. Default is
173 the regex as the second parent of the changeset. Default is
174 "{{mergefrombranch ([-\w]+)}}"
174 "{{mergefrombranch ([-\w]+)}}"
175 hook.cvslog Specify a Python function to be called at the end of
175 hook.cvslog Specify a Python function to be called at the end of
176 gathering the CVS log. The function is passed a list with
176 gathering the CVS log. The function is passed a list with
177 the log entries, and can modify the entries in-place, or add
177 the log entries, and can modify the entries in-place, or add
178 or delete them.
178 or delete them.
179 hook.cvschangesets
179 hook.cvschangesets
180 Specify a Python function to be called after the changesets
180 Specify a Python function to be called after the changesets
181 are calculated from the the CVS log. The function is passed
181 are calculated from the the CVS log. The function is passed
182 a list with the changeset entries, and can modify the
182 a list with the changeset entries, and can modify the
183 changesets in-place, or add or delete them.
183 changesets in-place, or add or delete them.
184
184
185 An additional "debugcvsps" Mercurial command allows the builtin changeset
185 An additional "debugcvsps" Mercurial command allows the builtin changeset
186 merging code to be run without doing a conversion. Its parameters and
186 merging code to be run without doing a conversion. Its parameters and
187 output are similar to that of cvsps 2.1. Please see the command help for
187 output are similar to that of cvsps 2.1. Please see the command help for
188 more details.
188 more details.
189
189
190 Subversion Source
190 Subversion Source
191 '''''''''''''''''
191 '''''''''''''''''
192
192
193 Subversion source detects classical trunk/branches/tags layouts. By
193 Subversion source detects classical trunk/branches/tags layouts. By
194 default, the supplied "svn://repo/path/" source URL is converted as a
194 default, the supplied "svn://repo/path/" source URL is converted as a
195 single branch. If "svn://repo/path/trunk" exists it replaces the default
195 single branch. If "svn://repo/path/trunk" exists it replaces the default
196 branch. If "svn://repo/path/branches" exists, its subdirectories are
196 branch. If "svn://repo/path/branches" exists, its subdirectories are
197 listed as possible branches. If "svn://repo/path/tags" exists, it is
197 listed as possible branches. If "svn://repo/path/tags" exists, it is
198 looked for tags referencing converted branches. Default "trunk",
198 looked for tags referencing converted branches. Default "trunk",
199 "branches" and "tags" values can be overridden with following options. Set
199 "branches" and "tags" values can be overridden with following options. Set
200 them to paths relative to the source URL, or leave them blank to disable
200 them to paths relative to the source URL, or leave them blank to disable
201 auto detection.
201 auto detection.
202
202
203 The following options can be set with "--config":
203 The following options can be set with "--config":
204
204
205 convert.svn.branches
205 convert.svn.branches
206 specify the directory containing branches. The default is
206 specify the directory containing branches. The default is
207 "branches".
207 "branches".
208 convert.svn.tags
208 convert.svn.tags
209 specify the directory containing tags. The default is
209 specify the directory containing tags. The default is
210 "tags".
210 "tags".
211 convert.svn.trunk
211 convert.svn.trunk
212 specify the name of the trunk branch. The default is
212 specify the name of the trunk branch. The default is
213 "trunk".
213 "trunk".
214
214
215 Source history can be retrieved starting at a specific revision, instead
215 Source history can be retrieved starting at a specific revision, instead
216 of being integrally converted. Only single branch conversions are
216 of being integrally converted. Only single branch conversions are
217 supported.
217 supported.
218
218
219 convert.svn.startrev
219 convert.svn.startrev
220 specify start Subversion revision number. The default is 0.
220 specify start Subversion revision number. The default is 0.
221
221
222 Perforce Source
222 Perforce Source
223 '''''''''''''''
223 '''''''''''''''
224
224
225 The Perforce (P4) importer can be given a p4 depot path or a client
225 The Perforce (P4) importer can be given a p4 depot path or a client
226 specification as source. It will convert all files in the source to a flat
226 specification as source. It will convert all files in the source to a flat
227 Mercurial repository, ignoring labels, branches and integrations. Note
227 Mercurial repository, ignoring labels, branches and integrations. Note
228 that when a depot path is given you then usually should specify a target
228 that when a depot path is given you then usually should specify a target
229 directory, because otherwise the target may be named "...-hg".
229 directory, because otherwise the target may be named "...-hg".
230
230
231 It is possible to limit the amount of source history to be converted by
231 It is possible to limit the amount of source history to be converted by
232 specifying an initial Perforce revision:
232 specifying an initial Perforce revision:
233
233
234 convert.p4.startrev
234 convert.p4.startrev
235 specify initial Perforce revision (a Perforce changelist
235 specify initial Perforce revision (a Perforce changelist
236 number).
236 number).
237
237
238 Mercurial Destination
238 Mercurial Destination
239 '''''''''''''''''''''
239 '''''''''''''''''''''
240
240
241 The following options are supported:
241 The following options are supported:
242
242
243 convert.hg.clonebranches
243 convert.hg.clonebranches
244 dispatch source branches in separate clones. The default is
244 dispatch source branches in separate clones. The default is
245 False.
245 False.
246 convert.hg.tagsbranch
246 convert.hg.tagsbranch
247 branch name for tag revisions, defaults to "default".
247 branch name for tag revisions, defaults to "default".
248 convert.hg.usebranchnames
248 convert.hg.usebranchnames
249 preserve branch names. The default is True.
249 preserve branch names. The default is True.
250
250
251 options:
251 options:
252
252
253 -s --source-type TYPE source repository type
253 -s --source-type TYPE source repository type
254 -d --dest-type TYPE destination repository type
254 -d --dest-type TYPE destination repository type
255 -r --rev REV import up to target revision REV
255 -r --rev REV import up to target revision REV
256 -A --authormap FILE remap usernames using this file
256 -A --authormap FILE remap usernames using this file
257 --filemap FILE remap file names using contents of file
257 --filemap FILE remap file names using contents of file
258 --splicemap FILE splice synthesized history into place
258 --splicemap FILE splice synthesized history into place
259 --branchmap FILE change branch names while converting
259 --branchmap FILE change branch names while converting
260 --branchsort try to sort changesets by branches
260 --branchsort try to sort changesets by branches
261 --datesort try to sort changesets by date
261 --datesort try to sort changesets by date
262 --sourcesort preserve source changesets order
262 --sourcesort preserve source changesets order
263
263
264 use "hg -v help convert" to show more info
264 use "hg -v help convert" to show more info
265 $ hg init a
265 $ hg init a
266 $ cd a
266 $ cd a
267 $ echo a > a
267 $ echo a > a
268 $ hg ci -d'0 0' -Ama
268 $ hg ci -d'0 0' -Ama
269 adding a
269 adding a
270 $ hg cp a b
270 $ hg cp a b
271 $ hg ci -d'1 0' -mb
271 $ hg ci -d'1 0' -mb
272 $ hg rm a
272 $ hg rm a
273 $ hg ci -d'2 0' -mc
273 $ hg ci -d'2 0' -mc
274 $ hg mv b a
274 $ hg mv b a
275 $ hg ci -d'3 0' -md
275 $ hg ci -d'3 0' -md
276 $ echo a >> a
276 $ echo a >> a
277 $ hg ci -d'4 0' -me
277 $ hg ci -d'4 0' -me
278 $ cd ..
278 $ cd ..
279 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
279 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
280 assuming destination a-hg
280 assuming destination a-hg
281 initializing destination a-hg repository
281 initializing destination a-hg repository
282 scanning source...
282 scanning source...
283 sorting...
283 sorting...
284 converting...
284 converting...
285 4 a
285 4 a
286 3 b
286 3 b
287 2 c
287 2 c
288 1 d
288 1 d
289 0 e
289 0 e
290 $ hg --cwd a-hg pull ../a
290 $ hg --cwd a-hg pull ../a
291 pulling from ../a
291 pulling from ../a
292 searching for changes
292 searching for changes
293 no changes found
293 no changes found
294
294
295 conversion to existing file should fail
295 conversion to existing file should fail
296
296
297 $ touch bogusfile
297 $ touch bogusfile
298 $ hg convert a bogusfile
298 $ hg convert a bogusfile
299 initializing destination bogusfile repository
299 initializing destination bogusfile repository
300 abort: cannot create new bundle repository
300 abort: cannot create new bundle repository
301 [255]
301 [255]
302
302
303 #if unix-permissions
303 #if unix-permissions
304
304
305 conversion to dir without permissions should fail
305 conversion to dir without permissions should fail
306
306
307 $ mkdir bogusdir
307 $ mkdir bogusdir
308 $ chmod 000 bogusdir
308 $ chmod 000 bogusdir
309
309
310 $ hg convert a bogusdir
310 $ hg convert a bogusdir
311 abort: Permission denied: bogusdir
311 abort: Permission denied: bogusdir
312 [255]
312 [255]
313
313
314 user permissions should succeed
314 user permissions should succeed
315
315
316 $ chmod 700 bogusdir
316 $ chmod 700 bogusdir
317 $ hg convert a bogusdir
317 $ hg convert a bogusdir
318 initializing destination bogusdir repository
318 initializing destination bogusdir repository
319 scanning source...
319 scanning source...
320 sorting...
320 sorting...
321 converting...
321 converting...
322 4 a
322 4 a
323 3 b
323 3 b
324 2 c
324 2 c
325 1 d
325 1 d
326 0 e
326 0 e
327
327
328 #endif
328 #endif
329
329
330 test pre and post conversion actions
330 test pre and post conversion actions
331
331
332 $ echo 'include b' > filemap
332 $ echo 'include b' > filemap
333 $ hg convert --debug --filemap filemap a partialb | \
333 $ hg convert --debug --filemap filemap a partialb | \
334 > grep 'run hg'
334 > grep 'run hg'
335 run hg source pre-conversion action
335 run hg source pre-conversion action
336 run hg sink pre-conversion action
336 run hg sink pre-conversion action
337 run hg sink post-conversion action
337 run hg sink post-conversion action
338 run hg source post-conversion action
338 run hg source post-conversion action
339
339
340 converting empty dir should fail "nicely
340 converting empty dir should fail "nicely
341
341
342 $ mkdir emptydir
342 $ mkdir emptydir
343
343
344 override $PATH to ensure p4 not visible; use $PYTHON in case we're
344 override $PATH to ensure p4 not visible; use $PYTHON in case we're
345 running from a devel copy, not a temp installation
345 running from a devel copy, not a temp installation
346
346
347 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg convert emptydir
347 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg convert emptydir
348 assuming destination emptydir-hg
348 assuming destination emptydir-hg
349 initializing destination emptydir-hg repository
349 initializing destination emptydir-hg repository
350 emptydir does not look like a CVS checkout
350 emptydir does not look like a CVS checkout
351 emptydir does not look like a Git repository
351 emptydir does not look like a Git repository
352 emptydir does not look like a Subversion repository
352 emptydir does not look like a Subversion repository
353 emptydir is not a local Mercurial repository
353 emptydir is not a local Mercurial repository
354 emptydir does not look like a darcs repository
354 emptydir does not look like a darcs repository
355 emptydir does not look like a monotone repository
355 emptydir does not look like a monotone repository
356 emptydir does not look like a GNU Arch repository
356 emptydir does not look like a GNU Arch repository
357 emptydir does not look like a Bazaar repository
357 emptydir does not look like a Bazaar repository
358 cannot find required "p4" tool
358 cannot find required "p4" tool
359 abort: emptydir: missing or unsupported repository
359 abort: emptydir: missing or unsupported repository
360 [255]
360 [255]
361
361
362 convert with imaginary source type
362 convert with imaginary source type
363
363
364 $ hg convert --source-type foo a a-foo
364 $ hg convert --source-type foo a a-foo
365 initializing destination a-foo repository
365 initializing destination a-foo repository
366 abort: foo: invalid source repository type
366 abort: foo: invalid source repository type
367 [255]
367 [255]
368
368
369 convert with imaginary sink type
369 convert with imaginary sink type
370
370
371 $ hg convert --dest-type foo a a-foo
371 $ hg convert --dest-type foo a a-foo
372 abort: foo: invalid destination repository type
372 abort: foo: invalid destination repository type
373 [255]
373 [255]
374
374
375 testing: convert must not produce duplicate entries in fncache
375 testing: convert must not produce duplicate entries in fncache
376
376
377 $ hg convert a b
377 $ hg convert a b
378 initializing destination b repository
378 initializing destination b repository
379 scanning source...
379 scanning source...
380 sorting...
380 sorting...
381 converting...
381 converting...
382 4 a
382 4 a
383 3 b
383 3 b
384 2 c
384 2 c
385 1 d
385 1 d
386 0 e
386 0 e
387
387
388 contents of fncache file:
388 contents of fncache file:
389
389
390 $ cat b/.hg/store/fncache | sort
390 $ cat b/.hg/store/fncache | sort
391 data/a.i
391 data/a.i
392 data/b.i
392 data/b.i
393
393
394 test bogus URL
394 test bogus URL
395
395
396 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
396 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
397 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
397 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
398 [255]
398 [255]
399
400 test revset converted() lookup
401
402 $ hg --config convert.hg.saverev=True convert a c
403 initializing destination c repository
404 scanning source...
405 sorting...
406 converting...
407 4 a
408 3 b
409 2 c
410 1 d
411 0 e
412 $ echo f > c/f
413 $ hg -R c ci -d'0 0' -Amf
414 adding f
415 created new head
416 $ hg -R c log -r "converted(09d945a62ce6)"
417 changeset: 1:98c3dd46a874
418 user: test
419 date: Thu Jan 01 00:00:01 1970 +0000
420 summary: b
421
422 $ hg -R c log -r "converted()"
423 changeset: 0:31ed57b2037c
424 user: test
425 date: Thu Jan 01 00:00:00 1970 +0000
426 summary: a
427
428 changeset: 1:98c3dd46a874
429 user: test
430 date: Thu Jan 01 00:00:01 1970 +0000
431 summary: b
432
433 changeset: 2:3b9ca06ef716
434 user: test
435 date: Thu Jan 01 00:00:02 1970 +0000
436 summary: c
437
438 changeset: 3:4e0debd37cf2
439 user: test
440 date: Thu Jan 01 00:00:03 1970 +0000
441 summary: d
442
443 changeset: 4:9de3bc9349c5
444 user: test
445 date: Thu Jan 01 00:00:04 1970 +0000
446 summary: e
447
General Comments 0
You need to be logged in to leave comments. Login now