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