##// END OF EJS Templates
addset: fix `first` and `last` on sorted addset (issue4426)...
Pierre-Yves David -
r23127:7361d824 stable
parent child Browse files
Show More
@@ -1,2971 +1,2972
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 heapq
11 import heapq
12 import match as matchmod
12 import match as matchmod
13 import ancestor as ancestormod
13 import ancestor as ancestormod
14 from i18n import _
14 from i18n import _
15 import encoding
15 import encoding
16 import obsolete as obsmod
16 import obsolete as obsmod
17 import pathutil
17 import pathutil
18 import repoview
18 import repoview
19
19
20 def _revancestors(repo, revs, followfirst):
20 def _revancestors(repo, revs, followfirst):
21 """Like revlog.ancestors(), but supports followfirst."""
21 """Like revlog.ancestors(), but supports followfirst."""
22 cut = followfirst and 1 or None
22 cut = followfirst and 1 or None
23 cl = repo.changelog
23 cl = repo.changelog
24
24
25 def iterate():
25 def iterate():
26 revqueue, revsnode = None, None
26 revqueue, revsnode = None, None
27 h = []
27 h = []
28
28
29 revs.sort(reverse=True)
29 revs.sort(reverse=True)
30 revqueue = util.deque(revs)
30 revqueue = util.deque(revs)
31 if revqueue:
31 if revqueue:
32 revsnode = revqueue.popleft()
32 revsnode = revqueue.popleft()
33 heapq.heappush(h, -revsnode)
33 heapq.heappush(h, -revsnode)
34
34
35 seen = set([node.nullrev])
35 seen = set([node.nullrev])
36 while h:
36 while h:
37 current = -heapq.heappop(h)
37 current = -heapq.heappop(h)
38 if current not in seen:
38 if current not in seen:
39 if revsnode and current == revsnode:
39 if revsnode and current == revsnode:
40 if revqueue:
40 if revqueue:
41 revsnode = revqueue.popleft()
41 revsnode = revqueue.popleft()
42 heapq.heappush(h, -revsnode)
42 heapq.heappush(h, -revsnode)
43 seen.add(current)
43 seen.add(current)
44 yield current
44 yield current
45 for parent in cl.parentrevs(current)[:cut]:
45 for parent in cl.parentrevs(current)[:cut]:
46 if parent != node.nullrev:
46 if parent != node.nullrev:
47 heapq.heappush(h, -parent)
47 heapq.heappush(h, -parent)
48
48
49 return generatorset(iterate(), iterasc=False)
49 return generatorset(iterate(), iterasc=False)
50
50
51 def _revdescendants(repo, revs, followfirst):
51 def _revdescendants(repo, revs, followfirst):
52 """Like revlog.descendants() but supports followfirst."""
52 """Like revlog.descendants() but supports followfirst."""
53 cut = followfirst and 1 or None
53 cut = followfirst and 1 or None
54
54
55 def iterate():
55 def iterate():
56 cl = repo.changelog
56 cl = repo.changelog
57 first = min(revs)
57 first = min(revs)
58 nullrev = node.nullrev
58 nullrev = node.nullrev
59 if first == nullrev:
59 if first == nullrev:
60 # Are there nodes with a null first parent and a non-null
60 # Are there nodes with a null first parent and a non-null
61 # second one? Maybe. Do we care? Probably not.
61 # second one? Maybe. Do we care? Probably not.
62 for i in cl:
62 for i in cl:
63 yield i
63 yield i
64 else:
64 else:
65 seen = set(revs)
65 seen = set(revs)
66 for i in cl.revs(first + 1):
66 for i in cl.revs(first + 1):
67 for x in cl.parentrevs(i)[:cut]:
67 for x in cl.parentrevs(i)[:cut]:
68 if x != nullrev and x in seen:
68 if x != nullrev and x in seen:
69 seen.add(i)
69 seen.add(i)
70 yield i
70 yield i
71 break
71 break
72
72
73 return generatorset(iterate(), iterasc=True)
73 return generatorset(iterate(), iterasc=True)
74
74
75 def _revsbetween(repo, roots, heads):
75 def _revsbetween(repo, roots, heads):
76 """Return all paths between roots and heads, inclusive of both endpoint
76 """Return all paths between roots and heads, inclusive of both endpoint
77 sets."""
77 sets."""
78 if not roots:
78 if not roots:
79 return baseset()
79 return baseset()
80 parentrevs = repo.changelog.parentrevs
80 parentrevs = repo.changelog.parentrevs
81 visit = list(heads)
81 visit = list(heads)
82 reachable = set()
82 reachable = set()
83 seen = {}
83 seen = {}
84 minroot = min(roots)
84 minroot = min(roots)
85 roots = set(roots)
85 roots = set(roots)
86 # open-code the post-order traversal due to the tiny size of
86 # open-code the post-order traversal due to the tiny size of
87 # sys.getrecursionlimit()
87 # sys.getrecursionlimit()
88 while visit:
88 while visit:
89 rev = visit.pop()
89 rev = visit.pop()
90 if rev in roots:
90 if rev in roots:
91 reachable.add(rev)
91 reachable.add(rev)
92 parents = parentrevs(rev)
92 parents = parentrevs(rev)
93 seen[rev] = parents
93 seen[rev] = parents
94 for parent in parents:
94 for parent in parents:
95 if parent >= minroot and parent not in seen:
95 if parent >= minroot and parent not in seen:
96 visit.append(parent)
96 visit.append(parent)
97 if not reachable:
97 if not reachable:
98 return baseset()
98 return baseset()
99 for rev in sorted(seen):
99 for rev in sorted(seen):
100 for parent in seen[rev]:
100 for parent in seen[rev]:
101 if parent in reachable:
101 if parent in reachable:
102 reachable.add(rev)
102 reachable.add(rev)
103 return baseset(sorted(reachable))
103 return baseset(sorted(reachable))
104
104
105 elements = {
105 elements = {
106 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
106 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
107 "~": (18, None, ("ancestor", 18)),
107 "~": (18, None, ("ancestor", 18)),
108 "^": (18, None, ("parent", 18), ("parentpost", 18)),
108 "^": (18, None, ("parent", 18), ("parentpost", 18)),
109 "-": (5, ("negate", 19), ("minus", 5)),
109 "-": (5, ("negate", 19), ("minus", 5)),
110 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
110 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
111 ("dagrangepost", 17)),
111 ("dagrangepost", 17)),
112 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
112 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
113 ("dagrangepost", 17)),
113 ("dagrangepost", 17)),
114 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
114 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
115 "not": (10, ("not", 10)),
115 "not": (10, ("not", 10)),
116 "!": (10, ("not", 10)),
116 "!": (10, ("not", 10)),
117 "and": (5, None, ("and", 5)),
117 "and": (5, None, ("and", 5)),
118 "&": (5, None, ("and", 5)),
118 "&": (5, None, ("and", 5)),
119 "or": (4, None, ("or", 4)),
119 "or": (4, None, ("or", 4)),
120 "|": (4, None, ("or", 4)),
120 "|": (4, None, ("or", 4)),
121 "+": (4, None, ("or", 4)),
121 "+": (4, None, ("or", 4)),
122 ",": (2, None, ("list", 2)),
122 ",": (2, None, ("list", 2)),
123 ")": (0, None, None),
123 ")": (0, None, None),
124 "symbol": (0, ("symbol",), None),
124 "symbol": (0, ("symbol",), None),
125 "string": (0, ("string",), None),
125 "string": (0, ("string",), None),
126 "end": (0, None, None),
126 "end": (0, None, None),
127 }
127 }
128
128
129 keywords = set(['and', 'or', 'not'])
129 keywords = set(['and', 'or', 'not'])
130
130
131 def tokenize(program, lookup=None):
131 def tokenize(program, lookup=None):
132 '''
132 '''
133 Parse a revset statement into a stream of tokens
133 Parse a revset statement into a stream of tokens
134
134
135 Check that @ is a valid unquoted token character (issue3686):
135 Check that @ is a valid unquoted token character (issue3686):
136 >>> list(tokenize("@::"))
136 >>> list(tokenize("@::"))
137 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
137 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
138
138
139 '''
139 '''
140
140
141 pos, l = 0, len(program)
141 pos, l = 0, len(program)
142 while pos < l:
142 while pos < l:
143 c = program[pos]
143 c = program[pos]
144 if c.isspace(): # skip inter-token whitespace
144 if c.isspace(): # skip inter-token whitespace
145 pass
145 pass
146 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
146 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
147 yield ('::', None, pos)
147 yield ('::', None, pos)
148 pos += 1 # skip ahead
148 pos += 1 # skip ahead
149 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
149 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
150 yield ('..', None, pos)
150 yield ('..', None, pos)
151 pos += 1 # skip ahead
151 pos += 1 # skip ahead
152 elif c in "():,-|&+!~^": # handle simple operators
152 elif c in "():,-|&+!~^": # handle simple operators
153 yield (c, None, pos)
153 yield (c, None, pos)
154 elif (c in '"\'' or c == 'r' and
154 elif (c in '"\'' or c == 'r' and
155 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
155 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
156 if c == 'r':
156 if c == 'r':
157 pos += 1
157 pos += 1
158 c = program[pos]
158 c = program[pos]
159 decode = lambda x: x
159 decode = lambda x: x
160 else:
160 else:
161 decode = lambda x: x.decode('string-escape')
161 decode = lambda x: x.decode('string-escape')
162 pos += 1
162 pos += 1
163 s = pos
163 s = pos
164 while pos < l: # find closing quote
164 while pos < l: # find closing quote
165 d = program[pos]
165 d = program[pos]
166 if d == '\\': # skip over escaped characters
166 if d == '\\': # skip over escaped characters
167 pos += 2
167 pos += 2
168 continue
168 continue
169 if d == c:
169 if d == c:
170 yield ('string', decode(program[s:pos]), s)
170 yield ('string', decode(program[s:pos]), s)
171 break
171 break
172 pos += 1
172 pos += 1
173 else:
173 else:
174 raise error.ParseError(_("unterminated string"), s)
174 raise error.ParseError(_("unterminated string"), s)
175 # gather up a symbol/keyword
175 # gather up a symbol/keyword
176 elif c.isalnum() or c in '._@' or ord(c) > 127:
176 elif c.isalnum() or c in '._@' or ord(c) > 127:
177 s = pos
177 s = pos
178 pos += 1
178 pos += 1
179 while pos < l: # find end of symbol
179 while pos < l: # find end of symbol
180 d = program[pos]
180 d = program[pos]
181 if not (d.isalnum() or d in "-._/@" or ord(d) > 127):
181 if not (d.isalnum() or d in "-._/@" or ord(d) > 127):
182 break
182 break
183 if d == '.' and program[pos - 1] == '.': # special case for ..
183 if d == '.' and program[pos - 1] == '.': # special case for ..
184 pos -= 1
184 pos -= 1
185 break
185 break
186 pos += 1
186 pos += 1
187 sym = program[s:pos]
187 sym = program[s:pos]
188 if sym in keywords: # operator keywords
188 if sym in keywords: # operator keywords
189 yield (sym, None, s)
189 yield (sym, None, s)
190 elif '-' in sym:
190 elif '-' in sym:
191 # some jerk gave us foo-bar-baz, try to check if it's a symbol
191 # some jerk gave us foo-bar-baz, try to check if it's a symbol
192 if lookup and lookup(sym):
192 if lookup and lookup(sym):
193 # looks like a real symbol
193 # looks like a real symbol
194 yield ('symbol', sym, s)
194 yield ('symbol', sym, s)
195 else:
195 else:
196 # looks like an expression
196 # looks like an expression
197 parts = sym.split('-')
197 parts = sym.split('-')
198 for p in parts[:-1]:
198 for p in parts[:-1]:
199 if p: # possible consecutive -
199 if p: # possible consecutive -
200 yield ('symbol', p, s)
200 yield ('symbol', p, s)
201 s += len(p)
201 s += len(p)
202 yield ('-', None, pos)
202 yield ('-', None, pos)
203 s += 1
203 s += 1
204 if parts[-1]: # possible trailing -
204 if parts[-1]: # possible trailing -
205 yield ('symbol', parts[-1], s)
205 yield ('symbol', parts[-1], s)
206 else:
206 else:
207 yield ('symbol', sym, s)
207 yield ('symbol', sym, s)
208 pos -= 1
208 pos -= 1
209 else:
209 else:
210 raise error.ParseError(_("syntax error"), pos)
210 raise error.ParseError(_("syntax error"), pos)
211 pos += 1
211 pos += 1
212 yield ('end', None, pos)
212 yield ('end', None, pos)
213
213
214 # helpers
214 # helpers
215
215
216 def getstring(x, err):
216 def getstring(x, err):
217 if x and (x[0] == 'string' or x[0] == 'symbol'):
217 if x and (x[0] == 'string' or x[0] == 'symbol'):
218 return x[1]
218 return x[1]
219 raise error.ParseError(err)
219 raise error.ParseError(err)
220
220
221 def getlist(x):
221 def getlist(x):
222 if not x:
222 if not x:
223 return []
223 return []
224 if x[0] == 'list':
224 if x[0] == 'list':
225 return getlist(x[1]) + [x[2]]
225 return getlist(x[1]) + [x[2]]
226 return [x]
226 return [x]
227
227
228 def getargs(x, min, max, err):
228 def getargs(x, min, max, err):
229 l = getlist(x)
229 l = getlist(x)
230 if len(l) < min or (max >= 0 and len(l) > max):
230 if len(l) < min or (max >= 0 and len(l) > max):
231 raise error.ParseError(err)
231 raise error.ParseError(err)
232 return l
232 return l
233
233
234 def getset(repo, subset, x):
234 def getset(repo, subset, x):
235 if not x:
235 if not x:
236 raise error.ParseError(_("missing argument"))
236 raise error.ParseError(_("missing argument"))
237 s = methods[x[0]](repo, subset, *x[1:])
237 s = methods[x[0]](repo, subset, *x[1:])
238 if util.safehasattr(s, 'isascending'):
238 if util.safehasattr(s, 'isascending'):
239 return s
239 return s
240 return baseset(s)
240 return baseset(s)
241
241
242 def _getrevsource(repo, r):
242 def _getrevsource(repo, r):
243 extra = repo[r].extra()
243 extra = repo[r].extra()
244 for label in ('source', 'transplant_source', 'rebase_source'):
244 for label in ('source', 'transplant_source', 'rebase_source'):
245 if label in extra:
245 if label in extra:
246 try:
246 try:
247 return repo[extra[label]].rev()
247 return repo[extra[label]].rev()
248 except error.RepoLookupError:
248 except error.RepoLookupError:
249 pass
249 pass
250 return None
250 return None
251
251
252 # operator methods
252 # operator methods
253
253
254 def stringset(repo, subset, x):
254 def stringset(repo, subset, x):
255 x = repo[x].rev()
255 x = repo[x].rev()
256 if x == -1 and len(subset) == len(repo):
256 if x == -1 and len(subset) == len(repo):
257 return baseset([-1])
257 return baseset([-1])
258 if len(subset) == len(repo) or x in subset:
258 if len(subset) == len(repo) or x in subset:
259 return baseset([x])
259 return baseset([x])
260 return baseset()
260 return baseset()
261
261
262 def symbolset(repo, subset, x):
262 def symbolset(repo, subset, x):
263 if x in symbols:
263 if x in symbols:
264 raise error.ParseError(_("can't use %s here") % x)
264 raise error.ParseError(_("can't use %s here") % x)
265 return stringset(repo, subset, x)
265 return stringset(repo, subset, x)
266
266
267 def rangeset(repo, subset, x, y):
267 def rangeset(repo, subset, x, y):
268 cl = baseset(repo.changelog)
268 cl = baseset(repo.changelog)
269 m = getset(repo, cl, x)
269 m = getset(repo, cl, x)
270 n = getset(repo, cl, y)
270 n = getset(repo, cl, y)
271
271
272 if not m or not n:
272 if not m or not n:
273 return baseset()
273 return baseset()
274 m, n = m.first(), n.last()
274 m, n = m.first(), n.last()
275
275
276 if m < n:
276 if m < n:
277 r = spanset(repo, m, n + 1)
277 r = spanset(repo, m, n + 1)
278 else:
278 else:
279 r = spanset(repo, m, n - 1)
279 r = spanset(repo, m, n - 1)
280 return r & subset
280 return r & subset
281
281
282 def dagrange(repo, subset, x, y):
282 def dagrange(repo, subset, x, y):
283 r = spanset(repo)
283 r = spanset(repo)
284 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
284 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
285 return xs & subset
285 return xs & subset
286
286
287 def andset(repo, subset, x, y):
287 def andset(repo, subset, x, y):
288 return getset(repo, getset(repo, subset, x), y)
288 return getset(repo, getset(repo, subset, x), y)
289
289
290 def orset(repo, subset, x, y):
290 def orset(repo, subset, x, y):
291 xl = getset(repo, subset, x)
291 xl = getset(repo, subset, x)
292 yl = getset(repo, subset - xl, y)
292 yl = getset(repo, subset - xl, y)
293 return xl + yl
293 return xl + yl
294
294
295 def notset(repo, subset, x):
295 def notset(repo, subset, x):
296 return subset - getset(repo, subset, x)
296 return subset - getset(repo, subset, x)
297
297
298 def listset(repo, subset, a, b):
298 def listset(repo, subset, a, b):
299 raise error.ParseError(_("can't use a list in this context"))
299 raise error.ParseError(_("can't use a list in this context"))
300
300
301 def func(repo, subset, a, b):
301 def func(repo, subset, a, b):
302 if a[0] == 'symbol' and a[1] in symbols:
302 if a[0] == 'symbol' and a[1] in symbols:
303 return symbols[a[1]](repo, subset, b)
303 return symbols[a[1]](repo, subset, b)
304 raise error.ParseError(_("not a function: %s") % a[1])
304 raise error.ParseError(_("not a function: %s") % a[1])
305
305
306 # functions
306 # functions
307
307
308 def adds(repo, subset, x):
308 def adds(repo, subset, x):
309 """``adds(pattern)``
309 """``adds(pattern)``
310 Changesets that add a file matching pattern.
310 Changesets that add a file matching pattern.
311
311
312 The pattern without explicit kind like ``glob:`` is expected to be
312 The pattern without explicit kind like ``glob:`` is expected to be
313 relative to the current directory and match against a file or a
313 relative to the current directory and match against a file or a
314 directory.
314 directory.
315 """
315 """
316 # i18n: "adds" is a keyword
316 # i18n: "adds" is a keyword
317 pat = getstring(x, _("adds requires a pattern"))
317 pat = getstring(x, _("adds requires a pattern"))
318 return checkstatus(repo, subset, pat, 1)
318 return checkstatus(repo, subset, pat, 1)
319
319
320 def ancestor(repo, subset, x):
320 def ancestor(repo, subset, x):
321 """``ancestor(*changeset)``
321 """``ancestor(*changeset)``
322 A greatest common ancestor of the changesets.
322 A greatest common ancestor of the changesets.
323
323
324 Accepts 0 or more changesets.
324 Accepts 0 or more changesets.
325 Will return empty list when passed no args.
325 Will return empty list when passed no args.
326 Greatest common ancestor of a single changeset is that changeset.
326 Greatest common ancestor of a single changeset is that changeset.
327 """
327 """
328 # i18n: "ancestor" is a keyword
328 # i18n: "ancestor" is a keyword
329 l = getlist(x)
329 l = getlist(x)
330 rl = spanset(repo)
330 rl = spanset(repo)
331 anc = None
331 anc = None
332
332
333 # (getset(repo, rl, i) for i in l) generates a list of lists
333 # (getset(repo, rl, i) for i in l) generates a list of lists
334 for revs in (getset(repo, rl, i) for i in l):
334 for revs in (getset(repo, rl, i) for i in l):
335 for r in revs:
335 for r in revs:
336 if anc is None:
336 if anc is None:
337 anc = repo[r]
337 anc = repo[r]
338 else:
338 else:
339 anc = anc.ancestor(repo[r])
339 anc = anc.ancestor(repo[r])
340
340
341 if anc is not None and anc.rev() in subset:
341 if anc is not None and anc.rev() in subset:
342 return baseset([anc.rev()])
342 return baseset([anc.rev()])
343 return baseset()
343 return baseset()
344
344
345 def _ancestors(repo, subset, x, followfirst=False):
345 def _ancestors(repo, subset, x, followfirst=False):
346 heads = getset(repo, spanset(repo), x)
346 heads = getset(repo, spanset(repo), x)
347 if not heads:
347 if not heads:
348 return baseset()
348 return baseset()
349 s = _revancestors(repo, heads, followfirst)
349 s = _revancestors(repo, heads, followfirst)
350 return subset & s
350 return subset & s
351
351
352 def ancestors(repo, subset, x):
352 def ancestors(repo, subset, x):
353 """``ancestors(set)``
353 """``ancestors(set)``
354 Changesets that are ancestors of a changeset in set.
354 Changesets that are ancestors of a changeset in set.
355 """
355 """
356 return _ancestors(repo, subset, x)
356 return _ancestors(repo, subset, x)
357
357
358 def _firstancestors(repo, subset, x):
358 def _firstancestors(repo, subset, x):
359 # ``_firstancestors(set)``
359 # ``_firstancestors(set)``
360 # Like ``ancestors(set)`` but follows only the first parents.
360 # Like ``ancestors(set)`` but follows only the first parents.
361 return _ancestors(repo, subset, x, followfirst=True)
361 return _ancestors(repo, subset, x, followfirst=True)
362
362
363 def ancestorspec(repo, subset, x, n):
363 def ancestorspec(repo, subset, x, n):
364 """``set~n``
364 """``set~n``
365 Changesets that are the Nth ancestor (first parents only) of a changeset
365 Changesets that are the Nth ancestor (first parents only) of a changeset
366 in set.
366 in set.
367 """
367 """
368 try:
368 try:
369 n = int(n[1])
369 n = int(n[1])
370 except (TypeError, ValueError):
370 except (TypeError, ValueError):
371 raise error.ParseError(_("~ expects a number"))
371 raise error.ParseError(_("~ expects a number"))
372 ps = set()
372 ps = set()
373 cl = repo.changelog
373 cl = repo.changelog
374 for r in getset(repo, baseset(cl), x):
374 for r in getset(repo, baseset(cl), x):
375 for i in range(n):
375 for i in range(n):
376 r = cl.parentrevs(r)[0]
376 r = cl.parentrevs(r)[0]
377 ps.add(r)
377 ps.add(r)
378 return subset & ps
378 return subset & ps
379
379
380 def author(repo, subset, x):
380 def author(repo, subset, x):
381 """``author(string)``
381 """``author(string)``
382 Alias for ``user(string)``.
382 Alias for ``user(string)``.
383 """
383 """
384 # i18n: "author" is a keyword
384 # i18n: "author" is a keyword
385 n = encoding.lower(getstring(x, _("author requires a string")))
385 n = encoding.lower(getstring(x, _("author requires a string")))
386 kind, pattern, matcher = _substringmatcher(n)
386 kind, pattern, matcher = _substringmatcher(n)
387 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())))
387 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())))
388
388
389 def only(repo, subset, x):
389 def only(repo, subset, x):
390 """``only(set, [set])``
390 """``only(set, [set])``
391 Changesets that are ancestors of the first set that are not ancestors
391 Changesets that are ancestors of the first set that are not ancestors
392 of any other head in the repo. If a second set is specified, the result
392 of any other head in the repo. If a second set is specified, the result
393 is ancestors of the first set that are not ancestors of the second set
393 is ancestors of the first set that are not ancestors of the second set
394 (i.e. ::<set1> - ::<set2>).
394 (i.e. ::<set1> - ::<set2>).
395 """
395 """
396 cl = repo.changelog
396 cl = repo.changelog
397 # i18n: "only" is a keyword
397 # i18n: "only" is a keyword
398 args = getargs(x, 1, 2, _('only takes one or two arguments'))
398 args = getargs(x, 1, 2, _('only takes one or two arguments'))
399 include = getset(repo, spanset(repo), args[0])
399 include = getset(repo, spanset(repo), args[0])
400 if len(args) == 1:
400 if len(args) == 1:
401 if not include:
401 if not include:
402 return baseset()
402 return baseset()
403
403
404 descendants = set(_revdescendants(repo, include, False))
404 descendants = set(_revdescendants(repo, include, False))
405 exclude = [rev for rev in cl.headrevs()
405 exclude = [rev for rev in cl.headrevs()
406 if not rev in descendants and not rev in include]
406 if not rev in descendants and not rev in include]
407 else:
407 else:
408 exclude = getset(repo, spanset(repo), args[1])
408 exclude = getset(repo, spanset(repo), args[1])
409
409
410 results = set(ancestormod.missingancestors(include, exclude, cl.parentrevs))
410 results = set(ancestormod.missingancestors(include, exclude, cl.parentrevs))
411 return subset & results
411 return subset & results
412
412
413 def bisect(repo, subset, x):
413 def bisect(repo, subset, x):
414 """``bisect(string)``
414 """``bisect(string)``
415 Changesets marked in the specified bisect status:
415 Changesets marked in the specified bisect status:
416
416
417 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
417 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
418 - ``goods``, ``bads`` : csets topologically good/bad
418 - ``goods``, ``bads`` : csets topologically good/bad
419 - ``range`` : csets taking part in the bisection
419 - ``range`` : csets taking part in the bisection
420 - ``pruned`` : csets that are goods, bads or skipped
420 - ``pruned`` : csets that are goods, bads or skipped
421 - ``untested`` : csets whose fate is yet unknown
421 - ``untested`` : csets whose fate is yet unknown
422 - ``ignored`` : csets ignored due to DAG topology
422 - ``ignored`` : csets ignored due to DAG topology
423 - ``current`` : the cset currently being bisected
423 - ``current`` : the cset currently being bisected
424 """
424 """
425 # i18n: "bisect" is a keyword
425 # i18n: "bisect" is a keyword
426 status = getstring(x, _("bisect requires a string")).lower()
426 status = getstring(x, _("bisect requires a string")).lower()
427 state = set(hbisect.get(repo, status))
427 state = set(hbisect.get(repo, status))
428 return subset & state
428 return subset & state
429
429
430 # Backward-compatibility
430 # Backward-compatibility
431 # - no help entry so that we do not advertise it any more
431 # - no help entry so that we do not advertise it any more
432 def bisected(repo, subset, x):
432 def bisected(repo, subset, x):
433 return bisect(repo, subset, x)
433 return bisect(repo, subset, x)
434
434
435 def bookmark(repo, subset, x):
435 def bookmark(repo, subset, x):
436 """``bookmark([name])``
436 """``bookmark([name])``
437 The named bookmark or all bookmarks.
437 The named bookmark or all bookmarks.
438
438
439 If `name` starts with `re:`, the remainder of the name is treated as
439 If `name` starts with `re:`, the remainder of the name is treated as
440 a regular expression. To match a bookmark that actually starts with `re:`,
440 a regular expression. To match a bookmark that actually starts with `re:`,
441 use the prefix `literal:`.
441 use the prefix `literal:`.
442 """
442 """
443 # i18n: "bookmark" is a keyword
443 # i18n: "bookmark" is a keyword
444 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
444 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
445 if args:
445 if args:
446 bm = getstring(args[0],
446 bm = getstring(args[0],
447 # i18n: "bookmark" is a keyword
447 # i18n: "bookmark" is a keyword
448 _('the argument to bookmark must be a string'))
448 _('the argument to bookmark must be a string'))
449 kind, pattern, matcher = _stringmatcher(bm)
449 kind, pattern, matcher = _stringmatcher(bm)
450 bms = set()
450 bms = set()
451 if kind == 'literal':
451 if kind == 'literal':
452 bmrev = repo._bookmarks.get(pattern, None)
452 bmrev = repo._bookmarks.get(pattern, None)
453 if not bmrev:
453 if not bmrev:
454 raise util.Abort(_("bookmark '%s' does not exist") % bm)
454 raise util.Abort(_("bookmark '%s' does not exist") % bm)
455 bms.add(repo[bmrev].rev())
455 bms.add(repo[bmrev].rev())
456 else:
456 else:
457 matchrevs = set()
457 matchrevs = set()
458 for name, bmrev in repo._bookmarks.iteritems():
458 for name, bmrev in repo._bookmarks.iteritems():
459 if matcher(name):
459 if matcher(name):
460 matchrevs.add(bmrev)
460 matchrevs.add(bmrev)
461 if not matchrevs:
461 if not matchrevs:
462 raise util.Abort(_("no bookmarks exist that match '%s'")
462 raise util.Abort(_("no bookmarks exist that match '%s'")
463 % pattern)
463 % pattern)
464 for bmrev in matchrevs:
464 for bmrev in matchrevs:
465 bms.add(repo[bmrev].rev())
465 bms.add(repo[bmrev].rev())
466 else:
466 else:
467 bms = set([repo[r].rev()
467 bms = set([repo[r].rev()
468 for r in repo._bookmarks.values()])
468 for r in repo._bookmarks.values()])
469 bms -= set([node.nullrev])
469 bms -= set([node.nullrev])
470 return subset & bms
470 return subset & bms
471
471
472 def branch(repo, subset, x):
472 def branch(repo, subset, x):
473 """``branch(string or set)``
473 """``branch(string or set)``
474 All changesets belonging to the given branch or the branches of the given
474 All changesets belonging to the given branch or the branches of the given
475 changesets.
475 changesets.
476
476
477 If `string` starts with `re:`, the remainder of the name is treated as
477 If `string` starts with `re:`, the remainder of the name is treated as
478 a regular expression. To match a branch that actually starts with `re:`,
478 a regular expression. To match a branch that actually starts with `re:`,
479 use the prefix `literal:`.
479 use the prefix `literal:`.
480 """
480 """
481 try:
481 try:
482 b = getstring(x, '')
482 b = getstring(x, '')
483 except error.ParseError:
483 except error.ParseError:
484 # not a string, but another revspec, e.g. tip()
484 # not a string, but another revspec, e.g. tip()
485 pass
485 pass
486 else:
486 else:
487 kind, pattern, matcher = _stringmatcher(b)
487 kind, pattern, matcher = _stringmatcher(b)
488 if kind == 'literal':
488 if kind == 'literal':
489 # note: falls through to the revspec case if no branch with
489 # note: falls through to the revspec case if no branch with
490 # this name exists
490 # this name exists
491 if pattern in repo.branchmap():
491 if pattern in repo.branchmap():
492 return subset.filter(lambda r: matcher(repo[r].branch()))
492 return subset.filter(lambda r: matcher(repo[r].branch()))
493 else:
493 else:
494 return subset.filter(lambda r: matcher(repo[r].branch()))
494 return subset.filter(lambda r: matcher(repo[r].branch()))
495
495
496 s = getset(repo, spanset(repo), x)
496 s = getset(repo, spanset(repo), x)
497 b = set()
497 b = set()
498 for r in s:
498 for r in s:
499 b.add(repo[r].branch())
499 b.add(repo[r].branch())
500 c = s.__contains__
500 c = s.__contains__
501 return subset.filter(lambda r: c(r) or repo[r].branch() in b)
501 return subset.filter(lambda r: c(r) or repo[r].branch() in b)
502
502
503 def bumped(repo, subset, x):
503 def bumped(repo, subset, x):
504 """``bumped()``
504 """``bumped()``
505 Mutable changesets marked as successors of public changesets.
505 Mutable changesets marked as successors of public changesets.
506
506
507 Only non-public and non-obsolete changesets can be `bumped`.
507 Only non-public and non-obsolete changesets can be `bumped`.
508 """
508 """
509 # i18n: "bumped" is a keyword
509 # i18n: "bumped" is a keyword
510 getargs(x, 0, 0, _("bumped takes no arguments"))
510 getargs(x, 0, 0, _("bumped takes no arguments"))
511 bumped = obsmod.getrevs(repo, 'bumped')
511 bumped = obsmod.getrevs(repo, 'bumped')
512 return subset & bumped
512 return subset & bumped
513
513
514 def bundle(repo, subset, x):
514 def bundle(repo, subset, x):
515 """``bundle()``
515 """``bundle()``
516 Changesets in the bundle.
516 Changesets in the bundle.
517
517
518 Bundle must be specified by the -R option."""
518 Bundle must be specified by the -R option."""
519
519
520 try:
520 try:
521 bundlerevs = repo.changelog.bundlerevs
521 bundlerevs = repo.changelog.bundlerevs
522 except AttributeError:
522 except AttributeError:
523 raise util.Abort(_("no bundle provided - specify with -R"))
523 raise util.Abort(_("no bundle provided - specify with -R"))
524 return subset & bundlerevs
524 return subset & bundlerevs
525
525
526 def checkstatus(repo, subset, pat, field):
526 def checkstatus(repo, subset, pat, field):
527 hasset = matchmod.patkind(pat) == 'set'
527 hasset = matchmod.patkind(pat) == 'set'
528
528
529 mcache = [None]
529 mcache = [None]
530 def matches(x):
530 def matches(x):
531 c = repo[x]
531 c = repo[x]
532 if not mcache[0] or hasset:
532 if not mcache[0] or hasset:
533 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
533 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
534 m = mcache[0]
534 m = mcache[0]
535 fname = None
535 fname = None
536 if not m.anypats() and len(m.files()) == 1:
536 if not m.anypats() and len(m.files()) == 1:
537 fname = m.files()[0]
537 fname = m.files()[0]
538 if fname is not None:
538 if fname is not None:
539 if fname not in c.files():
539 if fname not in c.files():
540 return False
540 return False
541 else:
541 else:
542 for f in c.files():
542 for f in c.files():
543 if m(f):
543 if m(f):
544 break
544 break
545 else:
545 else:
546 return False
546 return False
547 files = repo.status(c.p1().node(), c.node())[field]
547 files = repo.status(c.p1().node(), c.node())[field]
548 if fname is not None:
548 if fname is not None:
549 if fname in files:
549 if fname in files:
550 return True
550 return True
551 else:
551 else:
552 for f in files:
552 for f in files:
553 if m(f):
553 if m(f):
554 return True
554 return True
555
555
556 return subset.filter(matches)
556 return subset.filter(matches)
557
557
558 def _children(repo, narrow, parentset):
558 def _children(repo, narrow, parentset):
559 cs = set()
559 cs = set()
560 if not parentset:
560 if not parentset:
561 return baseset(cs)
561 return baseset(cs)
562 pr = repo.changelog.parentrevs
562 pr = repo.changelog.parentrevs
563 minrev = min(parentset)
563 minrev = min(parentset)
564 for r in narrow:
564 for r in narrow:
565 if r <= minrev:
565 if r <= minrev:
566 continue
566 continue
567 for p in pr(r):
567 for p in pr(r):
568 if p in parentset:
568 if p in parentset:
569 cs.add(r)
569 cs.add(r)
570 return baseset(cs)
570 return baseset(cs)
571
571
572 def children(repo, subset, x):
572 def children(repo, subset, x):
573 """``children(set)``
573 """``children(set)``
574 Child changesets of changesets in set.
574 Child changesets of changesets in set.
575 """
575 """
576 s = getset(repo, baseset(repo), x)
576 s = getset(repo, baseset(repo), x)
577 cs = _children(repo, subset, s)
577 cs = _children(repo, subset, s)
578 return subset & cs
578 return subset & cs
579
579
580 def closed(repo, subset, x):
580 def closed(repo, subset, x):
581 """``closed()``
581 """``closed()``
582 Changeset is closed.
582 Changeset is closed.
583 """
583 """
584 # i18n: "closed" is a keyword
584 # i18n: "closed" is a keyword
585 getargs(x, 0, 0, _("closed takes no arguments"))
585 getargs(x, 0, 0, _("closed takes no arguments"))
586 return subset.filter(lambda r: repo[r].closesbranch())
586 return subset.filter(lambda r: repo[r].closesbranch())
587
587
588 def contains(repo, subset, x):
588 def contains(repo, subset, x):
589 """``contains(pattern)``
589 """``contains(pattern)``
590 The revision's manifest contains a file matching pattern (but might not
590 The revision's manifest contains a file matching pattern (but might not
591 modify it). See :hg:`help patterns` for information about file patterns.
591 modify it). See :hg:`help patterns` for information about file patterns.
592
592
593 The pattern without explicit kind like ``glob:`` is expected to be
593 The pattern without explicit kind like ``glob:`` is expected to be
594 relative to the current directory and match against a file exactly
594 relative to the current directory and match against a file exactly
595 for efficiency.
595 for efficiency.
596 """
596 """
597 # i18n: "contains" is a keyword
597 # i18n: "contains" is a keyword
598 pat = getstring(x, _("contains requires a pattern"))
598 pat = getstring(x, _("contains requires a pattern"))
599
599
600 def matches(x):
600 def matches(x):
601 if not matchmod.patkind(pat):
601 if not matchmod.patkind(pat):
602 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
602 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
603 if pats in repo[x]:
603 if pats in repo[x]:
604 return True
604 return True
605 else:
605 else:
606 c = repo[x]
606 c = repo[x]
607 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
607 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
608 for f in c.manifest():
608 for f in c.manifest():
609 if m(f):
609 if m(f):
610 return True
610 return True
611 return False
611 return False
612
612
613 return subset.filter(matches)
613 return subset.filter(matches)
614
614
615 def converted(repo, subset, x):
615 def converted(repo, subset, x):
616 """``converted([id])``
616 """``converted([id])``
617 Changesets converted from the given identifier in the old repository if
617 Changesets converted from the given identifier in the old repository if
618 present, or all converted changesets if no identifier is specified.
618 present, or all converted changesets if no identifier is specified.
619 """
619 """
620
620
621 # There is exactly no chance of resolving the revision, so do a simple
621 # There is exactly no chance of resolving the revision, so do a simple
622 # string compare and hope for the best
622 # string compare and hope for the best
623
623
624 rev = None
624 rev = None
625 # i18n: "converted" is a keyword
625 # i18n: "converted" is a keyword
626 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
626 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
627 if l:
627 if l:
628 # i18n: "converted" is a keyword
628 # i18n: "converted" is a keyword
629 rev = getstring(l[0], _('converted requires a revision'))
629 rev = getstring(l[0], _('converted requires a revision'))
630
630
631 def _matchvalue(r):
631 def _matchvalue(r):
632 source = repo[r].extra().get('convert_revision', None)
632 source = repo[r].extra().get('convert_revision', None)
633 return source is not None and (rev is None or source.startswith(rev))
633 return source is not None and (rev is None or source.startswith(rev))
634
634
635 return subset.filter(lambda r: _matchvalue(r))
635 return subset.filter(lambda r: _matchvalue(r))
636
636
637 def date(repo, subset, x):
637 def date(repo, subset, x):
638 """``date(interval)``
638 """``date(interval)``
639 Changesets within the interval, see :hg:`help dates`.
639 Changesets within the interval, see :hg:`help dates`.
640 """
640 """
641 # i18n: "date" is a keyword
641 # i18n: "date" is a keyword
642 ds = getstring(x, _("date requires a string"))
642 ds = getstring(x, _("date requires a string"))
643 dm = util.matchdate(ds)
643 dm = util.matchdate(ds)
644 return subset.filter(lambda x: dm(repo[x].date()[0]))
644 return subset.filter(lambda x: dm(repo[x].date()[0]))
645
645
646 def desc(repo, subset, x):
646 def desc(repo, subset, x):
647 """``desc(string)``
647 """``desc(string)``
648 Search commit message for string. The match is case-insensitive.
648 Search commit message for string. The match is case-insensitive.
649 """
649 """
650 # i18n: "desc" is a keyword
650 # i18n: "desc" is a keyword
651 ds = encoding.lower(getstring(x, _("desc requires a string")))
651 ds = encoding.lower(getstring(x, _("desc requires a string")))
652
652
653 def matches(x):
653 def matches(x):
654 c = repo[x]
654 c = repo[x]
655 return ds in encoding.lower(c.description())
655 return ds in encoding.lower(c.description())
656
656
657 return subset.filter(matches)
657 return subset.filter(matches)
658
658
659 def _descendants(repo, subset, x, followfirst=False):
659 def _descendants(repo, subset, x, followfirst=False):
660 roots = getset(repo, spanset(repo), x)
660 roots = getset(repo, spanset(repo), x)
661 if not roots:
661 if not roots:
662 return baseset()
662 return baseset()
663 s = _revdescendants(repo, roots, followfirst)
663 s = _revdescendants(repo, roots, followfirst)
664
664
665 # Both sets need to be ascending in order to lazily return the union
665 # Both sets need to be ascending in order to lazily return the union
666 # in the correct order.
666 # in the correct order.
667 base = subset & roots
667 base = subset & roots
668 desc = subset & s
668 desc = subset & s
669 result = base + desc
669 result = base + desc
670 if subset.isascending():
670 if subset.isascending():
671 result.sort()
671 result.sort()
672 elif subset.isdescending():
672 elif subset.isdescending():
673 result.sort(reverse=True)
673 result.sort(reverse=True)
674 else:
674 else:
675 result = subset & result
675 result = subset & result
676 return result
676 return result
677
677
678 def descendants(repo, subset, x):
678 def descendants(repo, subset, x):
679 """``descendants(set)``
679 """``descendants(set)``
680 Changesets which are descendants of changesets in set.
680 Changesets which are descendants of changesets in set.
681 """
681 """
682 return _descendants(repo, subset, x)
682 return _descendants(repo, subset, x)
683
683
684 def _firstdescendants(repo, subset, x):
684 def _firstdescendants(repo, subset, x):
685 # ``_firstdescendants(set)``
685 # ``_firstdescendants(set)``
686 # Like ``descendants(set)`` but follows only the first parents.
686 # Like ``descendants(set)`` but follows only the first parents.
687 return _descendants(repo, subset, x, followfirst=True)
687 return _descendants(repo, subset, x, followfirst=True)
688
688
689 def destination(repo, subset, x):
689 def destination(repo, subset, x):
690 """``destination([set])``
690 """``destination([set])``
691 Changesets that were created by a graft, transplant or rebase operation,
691 Changesets that were created by a graft, transplant or rebase operation,
692 with the given revisions specified as the source. Omitting the optional set
692 with the given revisions specified as the source. Omitting the optional set
693 is the same as passing all().
693 is the same as passing all().
694 """
694 """
695 if x is not None:
695 if x is not None:
696 sources = getset(repo, spanset(repo), x)
696 sources = getset(repo, spanset(repo), x)
697 else:
697 else:
698 sources = getall(repo, spanset(repo), x)
698 sources = getall(repo, spanset(repo), x)
699
699
700 dests = set()
700 dests = set()
701
701
702 # subset contains all of the possible destinations that can be returned, so
702 # subset contains all of the possible destinations that can be returned, so
703 # iterate over them and see if their source(s) were provided in the arg set.
703 # iterate over them and see if their source(s) were provided in the arg set.
704 # Even if the immediate src of r is not in the arg set, src's source (or
704 # Even if the immediate src of r is not in the arg set, src's source (or
705 # further back) may be. Scanning back further than the immediate src allows
705 # further back) may be. Scanning back further than the immediate src allows
706 # transitive transplants and rebases to yield the same results as transitive
706 # transitive transplants and rebases to yield the same results as transitive
707 # grafts.
707 # grafts.
708 for r in subset:
708 for r in subset:
709 src = _getrevsource(repo, r)
709 src = _getrevsource(repo, r)
710 lineage = None
710 lineage = None
711
711
712 while src is not None:
712 while src is not None:
713 if lineage is None:
713 if lineage is None:
714 lineage = list()
714 lineage = list()
715
715
716 lineage.append(r)
716 lineage.append(r)
717
717
718 # The visited lineage is a match if the current source is in the arg
718 # The visited lineage is a match if the current source is in the arg
719 # set. Since every candidate dest is visited by way of iterating
719 # set. Since every candidate dest is visited by way of iterating
720 # subset, any dests further back in the lineage will be tested by a
720 # subset, any dests further back in the lineage will be tested by a
721 # different iteration over subset. Likewise, if the src was already
721 # different iteration over subset. Likewise, if the src was already
722 # selected, the current lineage can be selected without going back
722 # selected, the current lineage can be selected without going back
723 # further.
723 # further.
724 if src in sources or src in dests:
724 if src in sources or src in dests:
725 dests.update(lineage)
725 dests.update(lineage)
726 break
726 break
727
727
728 r = src
728 r = src
729 src = _getrevsource(repo, r)
729 src = _getrevsource(repo, r)
730
730
731 return subset.filter(dests.__contains__)
731 return subset.filter(dests.__contains__)
732
732
733 def divergent(repo, subset, x):
733 def divergent(repo, subset, x):
734 """``divergent()``
734 """``divergent()``
735 Final successors of changesets with an alternative set of final successors.
735 Final successors of changesets with an alternative set of final successors.
736 """
736 """
737 # i18n: "divergent" is a keyword
737 # i18n: "divergent" is a keyword
738 getargs(x, 0, 0, _("divergent takes no arguments"))
738 getargs(x, 0, 0, _("divergent takes no arguments"))
739 divergent = obsmod.getrevs(repo, 'divergent')
739 divergent = obsmod.getrevs(repo, 'divergent')
740 return subset & divergent
740 return subset & divergent
741
741
742 def draft(repo, subset, x):
742 def draft(repo, subset, x):
743 """``draft()``
743 """``draft()``
744 Changeset in draft phase."""
744 Changeset in draft phase."""
745 # i18n: "draft" is a keyword
745 # i18n: "draft" is a keyword
746 getargs(x, 0, 0, _("draft takes no arguments"))
746 getargs(x, 0, 0, _("draft takes no arguments"))
747 phase = repo._phasecache.phase
747 phase = repo._phasecache.phase
748 target = phases.draft
748 target = phases.draft
749 condition = lambda r: phase(repo, r) == target
749 condition = lambda r: phase(repo, r) == target
750 return subset.filter(condition, cache=False)
750 return subset.filter(condition, cache=False)
751
751
752 def extinct(repo, subset, x):
752 def extinct(repo, subset, x):
753 """``extinct()``
753 """``extinct()``
754 Obsolete changesets with obsolete descendants only.
754 Obsolete changesets with obsolete descendants only.
755 """
755 """
756 # i18n: "extinct" is a keyword
756 # i18n: "extinct" is a keyword
757 getargs(x, 0, 0, _("extinct takes no arguments"))
757 getargs(x, 0, 0, _("extinct takes no arguments"))
758 extincts = obsmod.getrevs(repo, 'extinct')
758 extincts = obsmod.getrevs(repo, 'extinct')
759 return subset & extincts
759 return subset & extincts
760
760
761 def extra(repo, subset, x):
761 def extra(repo, subset, x):
762 """``extra(label, [value])``
762 """``extra(label, [value])``
763 Changesets with the given label in the extra metadata, with the given
763 Changesets with the given label in the extra metadata, with the given
764 optional value.
764 optional value.
765
765
766 If `value` starts with `re:`, the remainder of the value is treated as
766 If `value` starts with `re:`, the remainder of the value is treated as
767 a regular expression. To match a value that actually starts with `re:`,
767 a regular expression. To match a value that actually starts with `re:`,
768 use the prefix `literal:`.
768 use the prefix `literal:`.
769 """
769 """
770
770
771 # i18n: "extra" is a keyword
771 # i18n: "extra" is a keyword
772 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
772 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
773 # i18n: "extra" is a keyword
773 # i18n: "extra" is a keyword
774 label = getstring(l[0], _('first argument to extra must be a string'))
774 label = getstring(l[0], _('first argument to extra must be a string'))
775 value = None
775 value = None
776
776
777 if len(l) > 1:
777 if len(l) > 1:
778 # i18n: "extra" is a keyword
778 # i18n: "extra" is a keyword
779 value = getstring(l[1], _('second argument to extra must be a string'))
779 value = getstring(l[1], _('second argument to extra must be a string'))
780 kind, value, matcher = _stringmatcher(value)
780 kind, value, matcher = _stringmatcher(value)
781
781
782 def _matchvalue(r):
782 def _matchvalue(r):
783 extra = repo[r].extra()
783 extra = repo[r].extra()
784 return label in extra and (value is None or matcher(extra[label]))
784 return label in extra and (value is None or matcher(extra[label]))
785
785
786 return subset.filter(lambda r: _matchvalue(r))
786 return subset.filter(lambda r: _matchvalue(r))
787
787
788 def filelog(repo, subset, x):
788 def filelog(repo, subset, x):
789 """``filelog(pattern)``
789 """``filelog(pattern)``
790 Changesets connected to the specified filelog.
790 Changesets connected to the specified filelog.
791
791
792 For performance reasons, visits only revisions mentioned in the file-level
792 For performance reasons, visits only revisions mentioned in the file-level
793 filelog, rather than filtering through all changesets (much faster, but
793 filelog, rather than filtering through all changesets (much faster, but
794 doesn't include deletes or duplicate changes). For a slower, more accurate
794 doesn't include deletes or duplicate changes). For a slower, more accurate
795 result, use ``file()``.
795 result, use ``file()``.
796
796
797 The pattern without explicit kind like ``glob:`` is expected to be
797 The pattern without explicit kind like ``glob:`` is expected to be
798 relative to the current directory and match against a file exactly
798 relative to the current directory and match against a file exactly
799 for efficiency.
799 for efficiency.
800 """
800 """
801
801
802 # i18n: "filelog" is a keyword
802 # i18n: "filelog" is a keyword
803 pat = getstring(x, _("filelog requires a pattern"))
803 pat = getstring(x, _("filelog requires a pattern"))
804 s = set()
804 s = set()
805
805
806 if not matchmod.patkind(pat):
806 if not matchmod.patkind(pat):
807 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
807 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
808 fl = repo.file(f)
808 fl = repo.file(f)
809 for fr in fl:
809 for fr in fl:
810 s.add(fl.linkrev(fr))
810 s.add(fl.linkrev(fr))
811 else:
811 else:
812 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
812 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
813 for f in repo[None]:
813 for f in repo[None]:
814 if m(f):
814 if m(f):
815 fl = repo.file(f)
815 fl = repo.file(f)
816 for fr in fl:
816 for fr in fl:
817 s.add(fl.linkrev(fr))
817 s.add(fl.linkrev(fr))
818
818
819 return subset & s
819 return subset & s
820
820
821 def first(repo, subset, x):
821 def first(repo, subset, x):
822 """``first(set, [n])``
822 """``first(set, [n])``
823 An alias for limit().
823 An alias for limit().
824 """
824 """
825 return limit(repo, subset, x)
825 return limit(repo, subset, x)
826
826
827 def _follow(repo, subset, x, name, followfirst=False):
827 def _follow(repo, subset, x, name, followfirst=False):
828 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
828 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
829 c = repo['.']
829 c = repo['.']
830 if l:
830 if l:
831 x = getstring(l[0], _("%s expected a filename") % name)
831 x = getstring(l[0], _("%s expected a filename") % name)
832 if x in c:
832 if x in c:
833 cx = c[x]
833 cx = c[x]
834 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
834 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
835 # include the revision responsible for the most recent version
835 # include the revision responsible for the most recent version
836 s.add(cx.linkrev())
836 s.add(cx.linkrev())
837 else:
837 else:
838 return baseset()
838 return baseset()
839 else:
839 else:
840 s = _revancestors(repo, baseset([c.rev()]), followfirst)
840 s = _revancestors(repo, baseset([c.rev()]), followfirst)
841
841
842 return subset & s
842 return subset & s
843
843
844 def follow(repo, subset, x):
844 def follow(repo, subset, x):
845 """``follow([file])``
845 """``follow([file])``
846 An alias for ``::.`` (ancestors of the working copy's first parent).
846 An alias for ``::.`` (ancestors of the working copy's first parent).
847 If a filename is specified, the history of the given file is followed,
847 If a filename is specified, the history of the given file is followed,
848 including copies.
848 including copies.
849 """
849 """
850 return _follow(repo, subset, x, 'follow')
850 return _follow(repo, subset, x, 'follow')
851
851
852 def _followfirst(repo, subset, x):
852 def _followfirst(repo, subset, x):
853 # ``followfirst([file])``
853 # ``followfirst([file])``
854 # Like ``follow([file])`` but follows only the first parent of
854 # Like ``follow([file])`` but follows only the first parent of
855 # every revision or file revision.
855 # every revision or file revision.
856 return _follow(repo, subset, x, '_followfirst', followfirst=True)
856 return _follow(repo, subset, x, '_followfirst', followfirst=True)
857
857
858 def getall(repo, subset, x):
858 def getall(repo, subset, x):
859 """``all()``
859 """``all()``
860 All changesets, the same as ``0:tip``.
860 All changesets, the same as ``0:tip``.
861 """
861 """
862 # i18n: "all" is a keyword
862 # i18n: "all" is a keyword
863 getargs(x, 0, 0, _("all takes no arguments"))
863 getargs(x, 0, 0, _("all takes no arguments"))
864 return subset
864 return subset
865
865
866 def grep(repo, subset, x):
866 def grep(repo, subset, x):
867 """``grep(regex)``
867 """``grep(regex)``
868 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
868 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
869 to ensure special escape characters are handled correctly. Unlike
869 to ensure special escape characters are handled correctly. Unlike
870 ``keyword(string)``, the match is case-sensitive.
870 ``keyword(string)``, the match is case-sensitive.
871 """
871 """
872 try:
872 try:
873 # i18n: "grep" is a keyword
873 # i18n: "grep" is a keyword
874 gr = re.compile(getstring(x, _("grep requires a string")))
874 gr = re.compile(getstring(x, _("grep requires a string")))
875 except re.error, e:
875 except re.error, e:
876 raise error.ParseError(_('invalid match pattern: %s') % e)
876 raise error.ParseError(_('invalid match pattern: %s') % e)
877
877
878 def matches(x):
878 def matches(x):
879 c = repo[x]
879 c = repo[x]
880 for e in c.files() + [c.user(), c.description()]:
880 for e in c.files() + [c.user(), c.description()]:
881 if gr.search(e):
881 if gr.search(e):
882 return True
882 return True
883 return False
883 return False
884
884
885 return subset.filter(matches)
885 return subset.filter(matches)
886
886
887 def _matchfiles(repo, subset, x):
887 def _matchfiles(repo, subset, x):
888 # _matchfiles takes a revset list of prefixed arguments:
888 # _matchfiles takes a revset list of prefixed arguments:
889 #
889 #
890 # [p:foo, i:bar, x:baz]
890 # [p:foo, i:bar, x:baz]
891 #
891 #
892 # builds a match object from them and filters subset. Allowed
892 # builds a match object from them and filters subset. Allowed
893 # prefixes are 'p:' for regular patterns, 'i:' for include
893 # prefixes are 'p:' for regular patterns, 'i:' for include
894 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
894 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
895 # a revision identifier, or the empty string to reference the
895 # a revision identifier, or the empty string to reference the
896 # working directory, from which the match object is
896 # working directory, from which the match object is
897 # initialized. Use 'd:' to set the default matching mode, default
897 # initialized. Use 'd:' to set the default matching mode, default
898 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
898 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
899
899
900 # i18n: "_matchfiles" is a keyword
900 # i18n: "_matchfiles" is a keyword
901 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
901 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
902 pats, inc, exc = [], [], []
902 pats, inc, exc = [], [], []
903 rev, default = None, None
903 rev, default = None, None
904 for arg in l:
904 for arg in l:
905 # i18n: "_matchfiles" is a keyword
905 # i18n: "_matchfiles" is a keyword
906 s = getstring(arg, _("_matchfiles requires string arguments"))
906 s = getstring(arg, _("_matchfiles requires string arguments"))
907 prefix, value = s[:2], s[2:]
907 prefix, value = s[:2], s[2:]
908 if prefix == 'p:':
908 if prefix == 'p:':
909 pats.append(value)
909 pats.append(value)
910 elif prefix == 'i:':
910 elif prefix == 'i:':
911 inc.append(value)
911 inc.append(value)
912 elif prefix == 'x:':
912 elif prefix == 'x:':
913 exc.append(value)
913 exc.append(value)
914 elif prefix == 'r:':
914 elif prefix == 'r:':
915 if rev is not None:
915 if rev is not None:
916 # i18n: "_matchfiles" is a keyword
916 # i18n: "_matchfiles" is a keyword
917 raise error.ParseError(_('_matchfiles expected at most one '
917 raise error.ParseError(_('_matchfiles expected at most one '
918 'revision'))
918 'revision'))
919 rev = value
919 rev = value
920 elif prefix == 'd:':
920 elif prefix == 'd:':
921 if default is not None:
921 if default is not None:
922 # i18n: "_matchfiles" is a keyword
922 # i18n: "_matchfiles" is a keyword
923 raise error.ParseError(_('_matchfiles expected at most one '
923 raise error.ParseError(_('_matchfiles expected at most one '
924 'default mode'))
924 'default mode'))
925 default = value
925 default = value
926 else:
926 else:
927 # i18n: "_matchfiles" is a keyword
927 # i18n: "_matchfiles" is a keyword
928 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
928 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
929 if not default:
929 if not default:
930 default = 'glob'
930 default = 'glob'
931
931
932 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
932 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
933 exclude=exc, ctx=repo[rev], default=default)
933 exclude=exc, ctx=repo[rev], default=default)
934
934
935 def matches(x):
935 def matches(x):
936 for f in repo[x].files():
936 for f in repo[x].files():
937 if m(f):
937 if m(f):
938 return True
938 return True
939 return False
939 return False
940
940
941 return subset.filter(matches)
941 return subset.filter(matches)
942
942
943 def hasfile(repo, subset, x):
943 def hasfile(repo, subset, x):
944 """``file(pattern)``
944 """``file(pattern)``
945 Changesets affecting files matched by pattern.
945 Changesets affecting files matched by pattern.
946
946
947 For a faster but less accurate result, consider using ``filelog()``
947 For a faster but less accurate result, consider using ``filelog()``
948 instead.
948 instead.
949
949
950 This predicate uses ``glob:`` as the default kind of pattern.
950 This predicate uses ``glob:`` as the default kind of pattern.
951 """
951 """
952 # i18n: "file" is a keyword
952 # i18n: "file" is a keyword
953 pat = getstring(x, _("file requires a pattern"))
953 pat = getstring(x, _("file requires a pattern"))
954 return _matchfiles(repo, subset, ('string', 'p:' + pat))
954 return _matchfiles(repo, subset, ('string', 'p:' + pat))
955
955
956 def head(repo, subset, x):
956 def head(repo, subset, x):
957 """``head()``
957 """``head()``
958 Changeset is a named branch head.
958 Changeset is a named branch head.
959 """
959 """
960 # i18n: "head" is a keyword
960 # i18n: "head" is a keyword
961 getargs(x, 0, 0, _("head takes no arguments"))
961 getargs(x, 0, 0, _("head takes no arguments"))
962 hs = set()
962 hs = set()
963 for b, ls in repo.branchmap().iteritems():
963 for b, ls in repo.branchmap().iteritems():
964 hs.update(repo[h].rev() for h in ls)
964 hs.update(repo[h].rev() for h in ls)
965 return baseset(hs).filter(subset.__contains__)
965 return baseset(hs).filter(subset.__contains__)
966
966
967 def heads(repo, subset, x):
967 def heads(repo, subset, x):
968 """``heads(set)``
968 """``heads(set)``
969 Members of set with no children in set.
969 Members of set with no children in set.
970 """
970 """
971 s = getset(repo, subset, x)
971 s = getset(repo, subset, x)
972 ps = parents(repo, subset, x)
972 ps = parents(repo, subset, x)
973 return s - ps
973 return s - ps
974
974
975 def hidden(repo, subset, x):
975 def hidden(repo, subset, x):
976 """``hidden()``
976 """``hidden()``
977 Hidden changesets.
977 Hidden changesets.
978 """
978 """
979 # i18n: "hidden" is a keyword
979 # i18n: "hidden" is a keyword
980 getargs(x, 0, 0, _("hidden takes no arguments"))
980 getargs(x, 0, 0, _("hidden takes no arguments"))
981 hiddenrevs = repoview.filterrevs(repo, 'visible')
981 hiddenrevs = repoview.filterrevs(repo, 'visible')
982 return subset & hiddenrevs
982 return subset & hiddenrevs
983
983
984 def keyword(repo, subset, x):
984 def keyword(repo, subset, x):
985 """``keyword(string)``
985 """``keyword(string)``
986 Search commit message, user name, and names of changed files for
986 Search commit message, user name, and names of changed files for
987 string. The match is case-insensitive.
987 string. The match is case-insensitive.
988 """
988 """
989 # i18n: "keyword" is a keyword
989 # i18n: "keyword" is a keyword
990 kw = encoding.lower(getstring(x, _("keyword requires a string")))
990 kw = encoding.lower(getstring(x, _("keyword requires a string")))
991
991
992 def matches(r):
992 def matches(r):
993 c = repo[r]
993 c = repo[r]
994 return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
994 return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
995 c.description()])
995 c.description()])
996
996
997 return subset.filter(matches)
997 return subset.filter(matches)
998
998
999 def limit(repo, subset, x):
999 def limit(repo, subset, x):
1000 """``limit(set, [n])``
1000 """``limit(set, [n])``
1001 First n members of set, defaulting to 1.
1001 First n members of set, defaulting to 1.
1002 """
1002 """
1003 # i18n: "limit" is a keyword
1003 # i18n: "limit" is a keyword
1004 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
1004 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
1005 try:
1005 try:
1006 lim = 1
1006 lim = 1
1007 if len(l) == 2:
1007 if len(l) == 2:
1008 # i18n: "limit" is a keyword
1008 # i18n: "limit" is a keyword
1009 lim = int(getstring(l[1], _("limit requires a number")))
1009 lim = int(getstring(l[1], _("limit requires a number")))
1010 except (TypeError, ValueError):
1010 except (TypeError, ValueError):
1011 # i18n: "limit" is a keyword
1011 # i18n: "limit" is a keyword
1012 raise error.ParseError(_("limit expects a number"))
1012 raise error.ParseError(_("limit expects a number"))
1013 ss = subset
1013 ss = subset
1014 os = getset(repo, spanset(repo), l[0])
1014 os = getset(repo, spanset(repo), l[0])
1015 result = []
1015 result = []
1016 it = iter(os)
1016 it = iter(os)
1017 for x in xrange(lim):
1017 for x in xrange(lim):
1018 try:
1018 try:
1019 y = it.next()
1019 y = it.next()
1020 if y in ss:
1020 if y in ss:
1021 result.append(y)
1021 result.append(y)
1022 except (StopIteration):
1022 except (StopIteration):
1023 break
1023 break
1024 return baseset(result)
1024 return baseset(result)
1025
1025
1026 def last(repo, subset, x):
1026 def last(repo, subset, x):
1027 """``last(set, [n])``
1027 """``last(set, [n])``
1028 Last n members of set, defaulting to 1.
1028 Last n members of set, defaulting to 1.
1029 """
1029 """
1030 # i18n: "last" is a keyword
1030 # i18n: "last" is a keyword
1031 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1031 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1032 try:
1032 try:
1033 lim = 1
1033 lim = 1
1034 if len(l) == 2:
1034 if len(l) == 2:
1035 # i18n: "last" is a keyword
1035 # i18n: "last" is a keyword
1036 lim = int(getstring(l[1], _("last requires a number")))
1036 lim = int(getstring(l[1], _("last requires a number")))
1037 except (TypeError, ValueError):
1037 except (TypeError, ValueError):
1038 # i18n: "last" is a keyword
1038 # i18n: "last" is a keyword
1039 raise error.ParseError(_("last expects a number"))
1039 raise error.ParseError(_("last expects a number"))
1040 ss = subset
1040 ss = subset
1041 os = getset(repo, spanset(repo), l[0])
1041 os = getset(repo, spanset(repo), l[0])
1042 os.reverse()
1042 os.reverse()
1043 result = []
1043 result = []
1044 it = iter(os)
1044 it = iter(os)
1045 for x in xrange(lim):
1045 for x in xrange(lim):
1046 try:
1046 try:
1047 y = it.next()
1047 y = it.next()
1048 if y in ss:
1048 if y in ss:
1049 result.append(y)
1049 result.append(y)
1050 except (StopIteration):
1050 except (StopIteration):
1051 break
1051 break
1052 return baseset(result)
1052 return baseset(result)
1053
1053
1054 def maxrev(repo, subset, x):
1054 def maxrev(repo, subset, x):
1055 """``max(set)``
1055 """``max(set)``
1056 Changeset with highest revision number in set.
1056 Changeset with highest revision number in set.
1057 """
1057 """
1058 os = getset(repo, spanset(repo), x)
1058 os = getset(repo, spanset(repo), x)
1059 if os:
1059 if os:
1060 m = os.max()
1060 m = os.max()
1061 if m in subset:
1061 if m in subset:
1062 return baseset([m])
1062 return baseset([m])
1063 return baseset()
1063 return baseset()
1064
1064
1065 def merge(repo, subset, x):
1065 def merge(repo, subset, x):
1066 """``merge()``
1066 """``merge()``
1067 Changeset is a merge changeset.
1067 Changeset is a merge changeset.
1068 """
1068 """
1069 # i18n: "merge" is a keyword
1069 # i18n: "merge" is a keyword
1070 getargs(x, 0, 0, _("merge takes no arguments"))
1070 getargs(x, 0, 0, _("merge takes no arguments"))
1071 cl = repo.changelog
1071 cl = repo.changelog
1072 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1)
1072 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1)
1073
1073
1074 def branchpoint(repo, subset, x):
1074 def branchpoint(repo, subset, x):
1075 """``branchpoint()``
1075 """``branchpoint()``
1076 Changesets with more than one child.
1076 Changesets with more than one child.
1077 """
1077 """
1078 # i18n: "branchpoint" is a keyword
1078 # i18n: "branchpoint" is a keyword
1079 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1079 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1080 cl = repo.changelog
1080 cl = repo.changelog
1081 if not subset:
1081 if not subset:
1082 return baseset()
1082 return baseset()
1083 baserev = min(subset)
1083 baserev = min(subset)
1084 parentscount = [0]*(len(repo) - baserev)
1084 parentscount = [0]*(len(repo) - baserev)
1085 for r in cl.revs(start=baserev + 1):
1085 for r in cl.revs(start=baserev + 1):
1086 for p in cl.parentrevs(r):
1086 for p in cl.parentrevs(r):
1087 if p >= baserev:
1087 if p >= baserev:
1088 parentscount[p - baserev] += 1
1088 parentscount[p - baserev] += 1
1089 return subset.filter(lambda r: parentscount[r - baserev] > 1)
1089 return subset.filter(lambda r: parentscount[r - baserev] > 1)
1090
1090
1091 def minrev(repo, subset, x):
1091 def minrev(repo, subset, x):
1092 """``min(set)``
1092 """``min(set)``
1093 Changeset with lowest revision number in set.
1093 Changeset with lowest revision number in set.
1094 """
1094 """
1095 os = getset(repo, spanset(repo), x)
1095 os = getset(repo, spanset(repo), x)
1096 if os:
1096 if os:
1097 m = os.min()
1097 m = os.min()
1098 if m in subset:
1098 if m in subset:
1099 return baseset([m])
1099 return baseset([m])
1100 return baseset()
1100 return baseset()
1101
1101
1102 def modifies(repo, subset, x):
1102 def modifies(repo, subset, x):
1103 """``modifies(pattern)``
1103 """``modifies(pattern)``
1104 Changesets modifying files matched by pattern.
1104 Changesets modifying files matched by pattern.
1105
1105
1106 The pattern without explicit kind like ``glob:`` is expected to be
1106 The pattern without explicit kind like ``glob:`` is expected to be
1107 relative to the current directory and match against a file or a
1107 relative to the current directory and match against a file or a
1108 directory.
1108 directory.
1109 """
1109 """
1110 # i18n: "modifies" is a keyword
1110 # i18n: "modifies" is a keyword
1111 pat = getstring(x, _("modifies requires a pattern"))
1111 pat = getstring(x, _("modifies requires a pattern"))
1112 return checkstatus(repo, subset, pat, 0)
1112 return checkstatus(repo, subset, pat, 0)
1113
1113
1114 def node_(repo, subset, x):
1114 def node_(repo, subset, x):
1115 """``id(string)``
1115 """``id(string)``
1116 Revision non-ambiguously specified by the given hex string prefix.
1116 Revision non-ambiguously specified by the given hex string prefix.
1117 """
1117 """
1118 # i18n: "id" is a keyword
1118 # i18n: "id" is a keyword
1119 l = getargs(x, 1, 1, _("id requires one argument"))
1119 l = getargs(x, 1, 1, _("id requires one argument"))
1120 # i18n: "id" is a keyword
1120 # i18n: "id" is a keyword
1121 n = getstring(l[0], _("id requires a string"))
1121 n = getstring(l[0], _("id requires a string"))
1122 if len(n) == 40:
1122 if len(n) == 40:
1123 rn = repo[n].rev()
1123 rn = repo[n].rev()
1124 else:
1124 else:
1125 rn = None
1125 rn = None
1126 pm = repo.changelog._partialmatch(n)
1126 pm = repo.changelog._partialmatch(n)
1127 if pm is not None:
1127 if pm is not None:
1128 rn = repo.changelog.rev(pm)
1128 rn = repo.changelog.rev(pm)
1129
1129
1130 if rn is None:
1130 if rn is None:
1131 return baseset()
1131 return baseset()
1132 result = baseset([rn])
1132 result = baseset([rn])
1133 return result & subset
1133 return result & subset
1134
1134
1135 def obsolete(repo, subset, x):
1135 def obsolete(repo, subset, x):
1136 """``obsolete()``
1136 """``obsolete()``
1137 Mutable changeset with a newer version."""
1137 Mutable changeset with a newer version."""
1138 # i18n: "obsolete" is a keyword
1138 # i18n: "obsolete" is a keyword
1139 getargs(x, 0, 0, _("obsolete takes no arguments"))
1139 getargs(x, 0, 0, _("obsolete takes no arguments"))
1140 obsoletes = obsmod.getrevs(repo, 'obsolete')
1140 obsoletes = obsmod.getrevs(repo, 'obsolete')
1141 return subset & obsoletes
1141 return subset & obsoletes
1142
1142
1143 def origin(repo, subset, x):
1143 def origin(repo, subset, x):
1144 """``origin([set])``
1144 """``origin([set])``
1145 Changesets that were specified as a source for the grafts, transplants or
1145 Changesets that were specified as a source for the grafts, transplants or
1146 rebases that created the given revisions. Omitting the optional set is the
1146 rebases that created the given revisions. Omitting the optional set is the
1147 same as passing all(). If a changeset created by these operations is itself
1147 same as passing all(). If a changeset created by these operations is itself
1148 specified as a source for one of these operations, only the source changeset
1148 specified as a source for one of these operations, only the source changeset
1149 for the first operation is selected.
1149 for the first operation is selected.
1150 """
1150 """
1151 if x is not None:
1151 if x is not None:
1152 dests = getset(repo, spanset(repo), x)
1152 dests = getset(repo, spanset(repo), x)
1153 else:
1153 else:
1154 dests = getall(repo, spanset(repo), x)
1154 dests = getall(repo, spanset(repo), x)
1155
1155
1156 def _firstsrc(rev):
1156 def _firstsrc(rev):
1157 src = _getrevsource(repo, rev)
1157 src = _getrevsource(repo, rev)
1158 if src is None:
1158 if src is None:
1159 return None
1159 return None
1160
1160
1161 while True:
1161 while True:
1162 prev = _getrevsource(repo, src)
1162 prev = _getrevsource(repo, src)
1163
1163
1164 if prev is None:
1164 if prev is None:
1165 return src
1165 return src
1166 src = prev
1166 src = prev
1167
1167
1168 o = set([_firstsrc(r) for r in dests])
1168 o = set([_firstsrc(r) for r in dests])
1169 o -= set([None])
1169 o -= set([None])
1170 return subset & o
1170 return subset & o
1171
1171
1172 def outgoing(repo, subset, x):
1172 def outgoing(repo, subset, x):
1173 """``outgoing([path])``
1173 """``outgoing([path])``
1174 Changesets not found in the specified destination repository, or the
1174 Changesets not found in the specified destination repository, or the
1175 default push location.
1175 default push location.
1176 """
1176 """
1177 import hg # avoid start-up nasties
1177 import hg # avoid start-up nasties
1178 # i18n: "outgoing" is a keyword
1178 # i18n: "outgoing" is a keyword
1179 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1179 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1180 # i18n: "outgoing" is a keyword
1180 # i18n: "outgoing" is a keyword
1181 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1181 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1182 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1182 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1183 dest, branches = hg.parseurl(dest)
1183 dest, branches = hg.parseurl(dest)
1184 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1184 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1185 if revs:
1185 if revs:
1186 revs = [repo.lookup(rev) for rev in revs]
1186 revs = [repo.lookup(rev) for rev in revs]
1187 other = hg.peer(repo, {}, dest)
1187 other = hg.peer(repo, {}, dest)
1188 repo.ui.pushbuffer()
1188 repo.ui.pushbuffer()
1189 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1189 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1190 repo.ui.popbuffer()
1190 repo.ui.popbuffer()
1191 cl = repo.changelog
1191 cl = repo.changelog
1192 o = set([cl.rev(r) for r in outgoing.missing])
1192 o = set([cl.rev(r) for r in outgoing.missing])
1193 return subset & o
1193 return subset & o
1194
1194
1195 def p1(repo, subset, x):
1195 def p1(repo, subset, x):
1196 """``p1([set])``
1196 """``p1([set])``
1197 First parent of changesets in set, or the working directory.
1197 First parent of changesets in set, or the working directory.
1198 """
1198 """
1199 if x is None:
1199 if x is None:
1200 p = repo[x].p1().rev()
1200 p = repo[x].p1().rev()
1201 if p >= 0:
1201 if p >= 0:
1202 return subset & baseset([p])
1202 return subset & baseset([p])
1203 return baseset()
1203 return baseset()
1204
1204
1205 ps = set()
1205 ps = set()
1206 cl = repo.changelog
1206 cl = repo.changelog
1207 for r in getset(repo, spanset(repo), x):
1207 for r in getset(repo, spanset(repo), x):
1208 ps.add(cl.parentrevs(r)[0])
1208 ps.add(cl.parentrevs(r)[0])
1209 ps -= set([node.nullrev])
1209 ps -= set([node.nullrev])
1210 return subset & ps
1210 return subset & ps
1211
1211
1212 def p2(repo, subset, x):
1212 def p2(repo, subset, x):
1213 """``p2([set])``
1213 """``p2([set])``
1214 Second parent of changesets in set, or the working directory.
1214 Second parent of changesets in set, or the working directory.
1215 """
1215 """
1216 if x is None:
1216 if x is None:
1217 ps = repo[x].parents()
1217 ps = repo[x].parents()
1218 try:
1218 try:
1219 p = ps[1].rev()
1219 p = ps[1].rev()
1220 if p >= 0:
1220 if p >= 0:
1221 return subset & baseset([p])
1221 return subset & baseset([p])
1222 return baseset()
1222 return baseset()
1223 except IndexError:
1223 except IndexError:
1224 return baseset()
1224 return baseset()
1225
1225
1226 ps = set()
1226 ps = set()
1227 cl = repo.changelog
1227 cl = repo.changelog
1228 for r in getset(repo, spanset(repo), x):
1228 for r in getset(repo, spanset(repo), x):
1229 ps.add(cl.parentrevs(r)[1])
1229 ps.add(cl.parentrevs(r)[1])
1230 ps -= set([node.nullrev])
1230 ps -= set([node.nullrev])
1231 return subset & ps
1231 return subset & ps
1232
1232
1233 def parents(repo, subset, x):
1233 def parents(repo, subset, x):
1234 """``parents([set])``
1234 """``parents([set])``
1235 The set of all parents for all changesets in set, or the working directory.
1235 The set of all parents for all changesets in set, or the working directory.
1236 """
1236 """
1237 if x is None:
1237 if x is None:
1238 ps = set(p.rev() for p in repo[x].parents())
1238 ps = set(p.rev() for p in repo[x].parents())
1239 else:
1239 else:
1240 ps = set()
1240 ps = set()
1241 cl = repo.changelog
1241 cl = repo.changelog
1242 for r in getset(repo, spanset(repo), x):
1242 for r in getset(repo, spanset(repo), x):
1243 ps.update(cl.parentrevs(r))
1243 ps.update(cl.parentrevs(r))
1244 ps -= set([node.nullrev])
1244 ps -= set([node.nullrev])
1245 return subset & ps
1245 return subset & ps
1246
1246
1247 def parentspec(repo, subset, x, n):
1247 def parentspec(repo, subset, x, n):
1248 """``set^0``
1248 """``set^0``
1249 The set.
1249 The set.
1250 ``set^1`` (or ``set^``), ``set^2``
1250 ``set^1`` (or ``set^``), ``set^2``
1251 First or second parent, respectively, of all changesets in set.
1251 First or second parent, respectively, of all changesets in set.
1252 """
1252 """
1253 try:
1253 try:
1254 n = int(n[1])
1254 n = int(n[1])
1255 if n not in (0, 1, 2):
1255 if n not in (0, 1, 2):
1256 raise ValueError
1256 raise ValueError
1257 except (TypeError, ValueError):
1257 except (TypeError, ValueError):
1258 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1258 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1259 ps = set()
1259 ps = set()
1260 cl = repo.changelog
1260 cl = repo.changelog
1261 for r in getset(repo, baseset(cl), x):
1261 for r in getset(repo, baseset(cl), x):
1262 if n == 0:
1262 if n == 0:
1263 ps.add(r)
1263 ps.add(r)
1264 elif n == 1:
1264 elif n == 1:
1265 ps.add(cl.parentrevs(r)[0])
1265 ps.add(cl.parentrevs(r)[0])
1266 elif n == 2:
1266 elif n == 2:
1267 parents = cl.parentrevs(r)
1267 parents = cl.parentrevs(r)
1268 if len(parents) > 1:
1268 if len(parents) > 1:
1269 ps.add(parents[1])
1269 ps.add(parents[1])
1270 return subset & ps
1270 return subset & ps
1271
1271
1272 def present(repo, subset, x):
1272 def present(repo, subset, x):
1273 """``present(set)``
1273 """``present(set)``
1274 An empty set, if any revision in set isn't found; otherwise,
1274 An empty set, if any revision in set isn't found; otherwise,
1275 all revisions in set.
1275 all revisions in set.
1276
1276
1277 If any of specified revisions is not present in the local repository,
1277 If any of specified revisions is not present in the local repository,
1278 the query is normally aborted. But this predicate allows the query
1278 the query is normally aborted. But this predicate allows the query
1279 to continue even in such cases.
1279 to continue even in such cases.
1280 """
1280 """
1281 try:
1281 try:
1282 return getset(repo, subset, x)
1282 return getset(repo, subset, x)
1283 except error.RepoLookupError:
1283 except error.RepoLookupError:
1284 return baseset()
1284 return baseset()
1285
1285
1286 def public(repo, subset, x):
1286 def public(repo, subset, x):
1287 """``public()``
1287 """``public()``
1288 Changeset in public phase."""
1288 Changeset in public phase."""
1289 # i18n: "public" is a keyword
1289 # i18n: "public" is a keyword
1290 getargs(x, 0, 0, _("public takes no arguments"))
1290 getargs(x, 0, 0, _("public takes no arguments"))
1291 phase = repo._phasecache.phase
1291 phase = repo._phasecache.phase
1292 target = phases.public
1292 target = phases.public
1293 condition = lambda r: phase(repo, r) == target
1293 condition = lambda r: phase(repo, r) == target
1294 return subset.filter(condition, cache=False)
1294 return subset.filter(condition, cache=False)
1295
1295
1296 def remote(repo, subset, x):
1296 def remote(repo, subset, x):
1297 """``remote([id [,path]])``
1297 """``remote([id [,path]])``
1298 Local revision that corresponds to the given identifier in a
1298 Local revision that corresponds to the given identifier in a
1299 remote repository, if present. Here, the '.' identifier is a
1299 remote repository, if present. Here, the '.' identifier is a
1300 synonym for the current local branch.
1300 synonym for the current local branch.
1301 """
1301 """
1302
1302
1303 import hg # avoid start-up nasties
1303 import hg # avoid start-up nasties
1304 # i18n: "remote" is a keyword
1304 # i18n: "remote" is a keyword
1305 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1305 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1306
1306
1307 q = '.'
1307 q = '.'
1308 if len(l) > 0:
1308 if len(l) > 0:
1309 # i18n: "remote" is a keyword
1309 # i18n: "remote" is a keyword
1310 q = getstring(l[0], _("remote requires a string id"))
1310 q = getstring(l[0], _("remote requires a string id"))
1311 if q == '.':
1311 if q == '.':
1312 q = repo['.'].branch()
1312 q = repo['.'].branch()
1313
1313
1314 dest = ''
1314 dest = ''
1315 if len(l) > 1:
1315 if len(l) > 1:
1316 # i18n: "remote" is a keyword
1316 # i18n: "remote" is a keyword
1317 dest = getstring(l[1], _("remote requires a repository path"))
1317 dest = getstring(l[1], _("remote requires a repository path"))
1318 dest = repo.ui.expandpath(dest or 'default')
1318 dest = repo.ui.expandpath(dest or 'default')
1319 dest, branches = hg.parseurl(dest)
1319 dest, branches = hg.parseurl(dest)
1320 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1320 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1321 if revs:
1321 if revs:
1322 revs = [repo.lookup(rev) for rev in revs]
1322 revs = [repo.lookup(rev) for rev in revs]
1323 other = hg.peer(repo, {}, dest)
1323 other = hg.peer(repo, {}, dest)
1324 n = other.lookup(q)
1324 n = other.lookup(q)
1325 if n in repo:
1325 if n in repo:
1326 r = repo[n].rev()
1326 r = repo[n].rev()
1327 if r in subset:
1327 if r in subset:
1328 return baseset([r])
1328 return baseset([r])
1329 return baseset()
1329 return baseset()
1330
1330
1331 def removes(repo, subset, x):
1331 def removes(repo, subset, x):
1332 """``removes(pattern)``
1332 """``removes(pattern)``
1333 Changesets which remove files matching pattern.
1333 Changesets which remove files matching pattern.
1334
1334
1335 The pattern without explicit kind like ``glob:`` is expected to be
1335 The pattern without explicit kind like ``glob:`` is expected to be
1336 relative to the current directory and match against a file or a
1336 relative to the current directory and match against a file or a
1337 directory.
1337 directory.
1338 """
1338 """
1339 # i18n: "removes" is a keyword
1339 # i18n: "removes" is a keyword
1340 pat = getstring(x, _("removes requires a pattern"))
1340 pat = getstring(x, _("removes requires a pattern"))
1341 return checkstatus(repo, subset, pat, 2)
1341 return checkstatus(repo, subset, pat, 2)
1342
1342
1343 def rev(repo, subset, x):
1343 def rev(repo, subset, x):
1344 """``rev(number)``
1344 """``rev(number)``
1345 Revision with the given numeric identifier.
1345 Revision with the given numeric identifier.
1346 """
1346 """
1347 # i18n: "rev" is a keyword
1347 # i18n: "rev" is a keyword
1348 l = getargs(x, 1, 1, _("rev requires one argument"))
1348 l = getargs(x, 1, 1, _("rev requires one argument"))
1349 try:
1349 try:
1350 # i18n: "rev" is a keyword
1350 # i18n: "rev" is a keyword
1351 l = int(getstring(l[0], _("rev requires a number")))
1351 l = int(getstring(l[0], _("rev requires a number")))
1352 except (TypeError, ValueError):
1352 except (TypeError, ValueError):
1353 # i18n: "rev" is a keyword
1353 # i18n: "rev" is a keyword
1354 raise error.ParseError(_("rev expects a number"))
1354 raise error.ParseError(_("rev expects a number"))
1355 if l not in fullreposet(repo):
1355 if l not in fullreposet(repo):
1356 return baseset()
1356 return baseset()
1357 return subset & baseset([l])
1357 return subset & baseset([l])
1358
1358
1359 def matching(repo, subset, x):
1359 def matching(repo, subset, x):
1360 """``matching(revision [, field])``
1360 """``matching(revision [, field])``
1361 Changesets in which a given set of fields match the set of fields in the
1361 Changesets in which a given set of fields match the set of fields in the
1362 selected revision or set.
1362 selected revision or set.
1363
1363
1364 To match more than one field pass the list of fields to match separated
1364 To match more than one field pass the list of fields to match separated
1365 by spaces (e.g. ``author description``).
1365 by spaces (e.g. ``author description``).
1366
1366
1367 Valid fields are most regular revision fields and some special fields.
1367 Valid fields are most regular revision fields and some special fields.
1368
1368
1369 Regular revision fields are ``description``, ``author``, ``branch``,
1369 Regular revision fields are ``description``, ``author``, ``branch``,
1370 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1370 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1371 and ``diff``.
1371 and ``diff``.
1372 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1372 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1373 contents of the revision. Two revisions matching their ``diff`` will
1373 contents of the revision. Two revisions matching their ``diff`` will
1374 also match their ``files``.
1374 also match their ``files``.
1375
1375
1376 Special fields are ``summary`` and ``metadata``:
1376 Special fields are ``summary`` and ``metadata``:
1377 ``summary`` matches the first line of the description.
1377 ``summary`` matches the first line of the description.
1378 ``metadata`` is equivalent to matching ``description user date``
1378 ``metadata`` is equivalent to matching ``description user date``
1379 (i.e. it matches the main metadata fields).
1379 (i.e. it matches the main metadata fields).
1380
1380
1381 ``metadata`` is the default field which is used when no fields are
1381 ``metadata`` is the default field which is used when no fields are
1382 specified. You can match more than one field at a time.
1382 specified. You can match more than one field at a time.
1383 """
1383 """
1384 # i18n: "matching" is a keyword
1384 # i18n: "matching" is a keyword
1385 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1385 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1386
1386
1387 revs = getset(repo, baseset(repo.changelog), l[0])
1387 revs = getset(repo, baseset(repo.changelog), l[0])
1388
1388
1389 fieldlist = ['metadata']
1389 fieldlist = ['metadata']
1390 if len(l) > 1:
1390 if len(l) > 1:
1391 fieldlist = getstring(l[1],
1391 fieldlist = getstring(l[1],
1392 # i18n: "matching" is a keyword
1392 # i18n: "matching" is a keyword
1393 _("matching requires a string "
1393 _("matching requires a string "
1394 "as its second argument")).split()
1394 "as its second argument")).split()
1395
1395
1396 # Make sure that there are no repeated fields,
1396 # Make sure that there are no repeated fields,
1397 # expand the 'special' 'metadata' field type
1397 # expand the 'special' 'metadata' field type
1398 # and check the 'files' whenever we check the 'diff'
1398 # and check the 'files' whenever we check the 'diff'
1399 fields = []
1399 fields = []
1400 for field in fieldlist:
1400 for field in fieldlist:
1401 if field == 'metadata':
1401 if field == 'metadata':
1402 fields += ['user', 'description', 'date']
1402 fields += ['user', 'description', 'date']
1403 elif field == 'diff':
1403 elif field == 'diff':
1404 # a revision matching the diff must also match the files
1404 # a revision matching the diff must also match the files
1405 # since matching the diff is very costly, make sure to
1405 # since matching the diff is very costly, make sure to
1406 # also match the files first
1406 # also match the files first
1407 fields += ['files', 'diff']
1407 fields += ['files', 'diff']
1408 else:
1408 else:
1409 if field == 'author':
1409 if field == 'author':
1410 field = 'user'
1410 field = 'user'
1411 fields.append(field)
1411 fields.append(field)
1412 fields = set(fields)
1412 fields = set(fields)
1413 if 'summary' in fields and 'description' in fields:
1413 if 'summary' in fields and 'description' in fields:
1414 # If a revision matches its description it also matches its summary
1414 # If a revision matches its description it also matches its summary
1415 fields.discard('summary')
1415 fields.discard('summary')
1416
1416
1417 # We may want to match more than one field
1417 # We may want to match more than one field
1418 # Not all fields take the same amount of time to be matched
1418 # Not all fields take the same amount of time to be matched
1419 # Sort the selected fields in order of increasing matching cost
1419 # Sort the selected fields in order of increasing matching cost
1420 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1420 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1421 'files', 'description', 'substate', 'diff']
1421 'files', 'description', 'substate', 'diff']
1422 def fieldkeyfunc(f):
1422 def fieldkeyfunc(f):
1423 try:
1423 try:
1424 return fieldorder.index(f)
1424 return fieldorder.index(f)
1425 except ValueError:
1425 except ValueError:
1426 # assume an unknown field is very costly
1426 # assume an unknown field is very costly
1427 return len(fieldorder)
1427 return len(fieldorder)
1428 fields = list(fields)
1428 fields = list(fields)
1429 fields.sort(key=fieldkeyfunc)
1429 fields.sort(key=fieldkeyfunc)
1430
1430
1431 # Each field will be matched with its own "getfield" function
1431 # Each field will be matched with its own "getfield" function
1432 # which will be added to the getfieldfuncs array of functions
1432 # which will be added to the getfieldfuncs array of functions
1433 getfieldfuncs = []
1433 getfieldfuncs = []
1434 _funcs = {
1434 _funcs = {
1435 'user': lambda r: repo[r].user(),
1435 'user': lambda r: repo[r].user(),
1436 'branch': lambda r: repo[r].branch(),
1436 'branch': lambda r: repo[r].branch(),
1437 'date': lambda r: repo[r].date(),
1437 'date': lambda r: repo[r].date(),
1438 'description': lambda r: repo[r].description(),
1438 'description': lambda r: repo[r].description(),
1439 'files': lambda r: repo[r].files(),
1439 'files': lambda r: repo[r].files(),
1440 'parents': lambda r: repo[r].parents(),
1440 'parents': lambda r: repo[r].parents(),
1441 'phase': lambda r: repo[r].phase(),
1441 'phase': lambda r: repo[r].phase(),
1442 'substate': lambda r: repo[r].substate,
1442 'substate': lambda r: repo[r].substate,
1443 'summary': lambda r: repo[r].description().splitlines()[0],
1443 'summary': lambda r: repo[r].description().splitlines()[0],
1444 'diff': lambda r: list(repo[r].diff(git=True),)
1444 'diff': lambda r: list(repo[r].diff(git=True),)
1445 }
1445 }
1446 for info in fields:
1446 for info in fields:
1447 getfield = _funcs.get(info, None)
1447 getfield = _funcs.get(info, None)
1448 if getfield is None:
1448 if getfield is None:
1449 raise error.ParseError(
1449 raise error.ParseError(
1450 # i18n: "matching" is a keyword
1450 # i18n: "matching" is a keyword
1451 _("unexpected field name passed to matching: %s") % info)
1451 _("unexpected field name passed to matching: %s") % info)
1452 getfieldfuncs.append(getfield)
1452 getfieldfuncs.append(getfield)
1453 # convert the getfield array of functions into a "getinfo" function
1453 # convert the getfield array of functions into a "getinfo" function
1454 # which returns an array of field values (or a single value if there
1454 # which returns an array of field values (or a single value if there
1455 # is only one field to match)
1455 # is only one field to match)
1456 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1456 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1457
1457
1458 def matches(x):
1458 def matches(x):
1459 for rev in revs:
1459 for rev in revs:
1460 target = getinfo(rev)
1460 target = getinfo(rev)
1461 match = True
1461 match = True
1462 for n, f in enumerate(getfieldfuncs):
1462 for n, f in enumerate(getfieldfuncs):
1463 if target[n] != f(x):
1463 if target[n] != f(x):
1464 match = False
1464 match = False
1465 if match:
1465 if match:
1466 return True
1466 return True
1467 return False
1467 return False
1468
1468
1469 return subset.filter(matches)
1469 return subset.filter(matches)
1470
1470
1471 def reverse(repo, subset, x):
1471 def reverse(repo, subset, x):
1472 """``reverse(set)``
1472 """``reverse(set)``
1473 Reverse order of set.
1473 Reverse order of set.
1474 """
1474 """
1475 l = getset(repo, subset, x)
1475 l = getset(repo, subset, x)
1476 l.reverse()
1476 l.reverse()
1477 return l
1477 return l
1478
1478
1479 def roots(repo, subset, x):
1479 def roots(repo, subset, x):
1480 """``roots(set)``
1480 """``roots(set)``
1481 Changesets in set with no parent changeset in set.
1481 Changesets in set with no parent changeset in set.
1482 """
1482 """
1483 s = getset(repo, spanset(repo), x)
1483 s = getset(repo, spanset(repo), x)
1484 subset = baseset([r for r in s if r in subset])
1484 subset = baseset([r for r in s if r in subset])
1485 cs = _children(repo, subset, s)
1485 cs = _children(repo, subset, s)
1486 return subset - cs
1486 return subset - cs
1487
1487
1488 def secret(repo, subset, x):
1488 def secret(repo, subset, x):
1489 """``secret()``
1489 """``secret()``
1490 Changeset in secret phase."""
1490 Changeset in secret phase."""
1491 # i18n: "secret" is a keyword
1491 # i18n: "secret" is a keyword
1492 getargs(x, 0, 0, _("secret takes no arguments"))
1492 getargs(x, 0, 0, _("secret takes no arguments"))
1493 phase = repo._phasecache.phase
1493 phase = repo._phasecache.phase
1494 target = phases.secret
1494 target = phases.secret
1495 condition = lambda r: phase(repo, r) == target
1495 condition = lambda r: phase(repo, r) == target
1496 return subset.filter(condition, cache=False)
1496 return subset.filter(condition, cache=False)
1497
1497
1498 def sort(repo, subset, x):
1498 def sort(repo, subset, x):
1499 """``sort(set[, [-]key...])``
1499 """``sort(set[, [-]key...])``
1500 Sort set by keys. The default sort order is ascending, specify a key
1500 Sort set by keys. The default sort order is ascending, specify a key
1501 as ``-key`` to sort in descending order.
1501 as ``-key`` to sort in descending order.
1502
1502
1503 The keys can be:
1503 The keys can be:
1504
1504
1505 - ``rev`` for the revision number,
1505 - ``rev`` for the revision number,
1506 - ``branch`` for the branch name,
1506 - ``branch`` for the branch name,
1507 - ``desc`` for the commit message (description),
1507 - ``desc`` for the commit message (description),
1508 - ``user`` for user name (``author`` can be used as an alias),
1508 - ``user`` for user name (``author`` can be used as an alias),
1509 - ``date`` for the commit date
1509 - ``date`` for the commit date
1510 """
1510 """
1511 # i18n: "sort" is a keyword
1511 # i18n: "sort" is a keyword
1512 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1512 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1513 keys = "rev"
1513 keys = "rev"
1514 if len(l) == 2:
1514 if len(l) == 2:
1515 # i18n: "sort" is a keyword
1515 # i18n: "sort" is a keyword
1516 keys = getstring(l[1], _("sort spec must be a string"))
1516 keys = getstring(l[1], _("sort spec must be a string"))
1517
1517
1518 s = l[0]
1518 s = l[0]
1519 keys = keys.split()
1519 keys = keys.split()
1520 l = []
1520 l = []
1521 def invert(s):
1521 def invert(s):
1522 return "".join(chr(255 - ord(c)) for c in s)
1522 return "".join(chr(255 - ord(c)) for c in s)
1523 revs = getset(repo, subset, s)
1523 revs = getset(repo, subset, s)
1524 if keys == ["rev"]:
1524 if keys == ["rev"]:
1525 revs.sort()
1525 revs.sort()
1526 return revs
1526 return revs
1527 elif keys == ["-rev"]:
1527 elif keys == ["-rev"]:
1528 revs.sort(reverse=True)
1528 revs.sort(reverse=True)
1529 return revs
1529 return revs
1530 for r in revs:
1530 for r in revs:
1531 c = repo[r]
1531 c = repo[r]
1532 e = []
1532 e = []
1533 for k in keys:
1533 for k in keys:
1534 if k == 'rev':
1534 if k == 'rev':
1535 e.append(r)
1535 e.append(r)
1536 elif k == '-rev':
1536 elif k == '-rev':
1537 e.append(-r)
1537 e.append(-r)
1538 elif k == 'branch':
1538 elif k == 'branch':
1539 e.append(c.branch())
1539 e.append(c.branch())
1540 elif k == '-branch':
1540 elif k == '-branch':
1541 e.append(invert(c.branch()))
1541 e.append(invert(c.branch()))
1542 elif k == 'desc':
1542 elif k == 'desc':
1543 e.append(c.description())
1543 e.append(c.description())
1544 elif k == '-desc':
1544 elif k == '-desc':
1545 e.append(invert(c.description()))
1545 e.append(invert(c.description()))
1546 elif k in 'user author':
1546 elif k in 'user author':
1547 e.append(c.user())
1547 e.append(c.user())
1548 elif k in '-user -author':
1548 elif k in '-user -author':
1549 e.append(invert(c.user()))
1549 e.append(invert(c.user()))
1550 elif k == 'date':
1550 elif k == 'date':
1551 e.append(c.date()[0])
1551 e.append(c.date()[0])
1552 elif k == '-date':
1552 elif k == '-date':
1553 e.append(-c.date()[0])
1553 e.append(-c.date()[0])
1554 else:
1554 else:
1555 raise error.ParseError(_("unknown sort key %r") % k)
1555 raise error.ParseError(_("unknown sort key %r") % k)
1556 e.append(r)
1556 e.append(r)
1557 l.append(e)
1557 l.append(e)
1558 l.sort()
1558 l.sort()
1559 return baseset([e[-1] for e in l])
1559 return baseset([e[-1] for e in l])
1560
1560
1561 def _stringmatcher(pattern):
1561 def _stringmatcher(pattern):
1562 """
1562 """
1563 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1563 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1564 returns the matcher name, pattern, and matcher function.
1564 returns the matcher name, pattern, and matcher function.
1565 missing or unknown prefixes are treated as literal matches.
1565 missing or unknown prefixes are treated as literal matches.
1566
1566
1567 helper for tests:
1567 helper for tests:
1568 >>> def test(pattern, *tests):
1568 >>> def test(pattern, *tests):
1569 ... kind, pattern, matcher = _stringmatcher(pattern)
1569 ... kind, pattern, matcher = _stringmatcher(pattern)
1570 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1570 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1571
1571
1572 exact matching (no prefix):
1572 exact matching (no prefix):
1573 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1573 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1574 ('literal', 'abcdefg', [False, False, True])
1574 ('literal', 'abcdefg', [False, False, True])
1575
1575
1576 regex matching ('re:' prefix)
1576 regex matching ('re:' prefix)
1577 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1577 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1578 ('re', 'a.+b', [False, False, True])
1578 ('re', 'a.+b', [False, False, True])
1579
1579
1580 force exact matches ('literal:' prefix)
1580 force exact matches ('literal:' prefix)
1581 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1581 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1582 ('literal', 're:foobar', [False, True])
1582 ('literal', 're:foobar', [False, True])
1583
1583
1584 unknown prefixes are ignored and treated as literals
1584 unknown prefixes are ignored and treated as literals
1585 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1585 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1586 ('literal', 'foo:bar', [False, False, True])
1586 ('literal', 'foo:bar', [False, False, True])
1587 """
1587 """
1588 if pattern.startswith('re:'):
1588 if pattern.startswith('re:'):
1589 pattern = pattern[3:]
1589 pattern = pattern[3:]
1590 try:
1590 try:
1591 regex = re.compile(pattern)
1591 regex = re.compile(pattern)
1592 except re.error, e:
1592 except re.error, e:
1593 raise error.ParseError(_('invalid regular expression: %s')
1593 raise error.ParseError(_('invalid regular expression: %s')
1594 % e)
1594 % e)
1595 return 're', pattern, regex.search
1595 return 're', pattern, regex.search
1596 elif pattern.startswith('literal:'):
1596 elif pattern.startswith('literal:'):
1597 pattern = pattern[8:]
1597 pattern = pattern[8:]
1598 return 'literal', pattern, pattern.__eq__
1598 return 'literal', pattern, pattern.__eq__
1599
1599
1600 def _substringmatcher(pattern):
1600 def _substringmatcher(pattern):
1601 kind, pattern, matcher = _stringmatcher(pattern)
1601 kind, pattern, matcher = _stringmatcher(pattern)
1602 if kind == 'literal':
1602 if kind == 'literal':
1603 matcher = lambda s: pattern in s
1603 matcher = lambda s: pattern in s
1604 return kind, pattern, matcher
1604 return kind, pattern, matcher
1605
1605
1606 def tag(repo, subset, x):
1606 def tag(repo, subset, x):
1607 """``tag([name])``
1607 """``tag([name])``
1608 The specified tag by name, or all tagged revisions if no name is given.
1608 The specified tag by name, or all tagged revisions if no name is given.
1609
1609
1610 If `name` starts with `re:`, the remainder of the name is treated as
1610 If `name` starts with `re:`, the remainder of the name is treated as
1611 a regular expression. To match a tag that actually starts with `re:`,
1611 a regular expression. To match a tag that actually starts with `re:`,
1612 use the prefix `literal:`.
1612 use the prefix `literal:`.
1613 """
1613 """
1614 # i18n: "tag" is a keyword
1614 # i18n: "tag" is a keyword
1615 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1615 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1616 cl = repo.changelog
1616 cl = repo.changelog
1617 if args:
1617 if args:
1618 pattern = getstring(args[0],
1618 pattern = getstring(args[0],
1619 # i18n: "tag" is a keyword
1619 # i18n: "tag" is a keyword
1620 _('the argument to tag must be a string'))
1620 _('the argument to tag must be a string'))
1621 kind, pattern, matcher = _stringmatcher(pattern)
1621 kind, pattern, matcher = _stringmatcher(pattern)
1622 if kind == 'literal':
1622 if kind == 'literal':
1623 # avoid resolving all tags
1623 # avoid resolving all tags
1624 tn = repo._tagscache.tags.get(pattern, None)
1624 tn = repo._tagscache.tags.get(pattern, None)
1625 if tn is None:
1625 if tn is None:
1626 raise util.Abort(_("tag '%s' does not exist") % pattern)
1626 raise util.Abort(_("tag '%s' does not exist") % pattern)
1627 s = set([repo[tn].rev()])
1627 s = set([repo[tn].rev()])
1628 else:
1628 else:
1629 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1629 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1630 else:
1630 else:
1631 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1631 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1632 return subset & s
1632 return subset & s
1633
1633
1634 def tagged(repo, subset, x):
1634 def tagged(repo, subset, x):
1635 return tag(repo, subset, x)
1635 return tag(repo, subset, x)
1636
1636
1637 def unstable(repo, subset, x):
1637 def unstable(repo, subset, x):
1638 """``unstable()``
1638 """``unstable()``
1639 Non-obsolete changesets with obsolete ancestors.
1639 Non-obsolete changesets with obsolete ancestors.
1640 """
1640 """
1641 # i18n: "unstable" is a keyword
1641 # i18n: "unstable" is a keyword
1642 getargs(x, 0, 0, _("unstable takes no arguments"))
1642 getargs(x, 0, 0, _("unstable takes no arguments"))
1643 unstables = obsmod.getrevs(repo, 'unstable')
1643 unstables = obsmod.getrevs(repo, 'unstable')
1644 return subset & unstables
1644 return subset & unstables
1645
1645
1646
1646
1647 def user(repo, subset, x):
1647 def user(repo, subset, x):
1648 """``user(string)``
1648 """``user(string)``
1649 User name contains string. The match is case-insensitive.
1649 User name contains string. The match is case-insensitive.
1650
1650
1651 If `string` starts with `re:`, the remainder of the string is treated as
1651 If `string` starts with `re:`, the remainder of the string is treated as
1652 a regular expression. To match a user that actually contains `re:`, use
1652 a regular expression. To match a user that actually contains `re:`, use
1653 the prefix `literal:`.
1653 the prefix `literal:`.
1654 """
1654 """
1655 return author(repo, subset, x)
1655 return author(repo, subset, x)
1656
1656
1657 # for internal use
1657 # for internal use
1658 def _list(repo, subset, x):
1658 def _list(repo, subset, x):
1659 s = getstring(x, "internal error")
1659 s = getstring(x, "internal error")
1660 if not s:
1660 if not s:
1661 return baseset()
1661 return baseset()
1662 ls = [repo[r].rev() for r in s.split('\0')]
1662 ls = [repo[r].rev() for r in s.split('\0')]
1663 s = subset
1663 s = subset
1664 return baseset([r for r in ls if r in s])
1664 return baseset([r for r in ls if r in s])
1665
1665
1666 # for internal use
1666 # for internal use
1667 def _intlist(repo, subset, x):
1667 def _intlist(repo, subset, x):
1668 s = getstring(x, "internal error")
1668 s = getstring(x, "internal error")
1669 if not s:
1669 if not s:
1670 return baseset()
1670 return baseset()
1671 ls = [int(r) for r in s.split('\0')]
1671 ls = [int(r) for r in s.split('\0')]
1672 s = subset
1672 s = subset
1673 return baseset([r for r in ls if r in s])
1673 return baseset([r for r in ls if r in s])
1674
1674
1675 # for internal use
1675 # for internal use
1676 def _hexlist(repo, subset, x):
1676 def _hexlist(repo, subset, x):
1677 s = getstring(x, "internal error")
1677 s = getstring(x, "internal error")
1678 if not s:
1678 if not s:
1679 return baseset()
1679 return baseset()
1680 cl = repo.changelog
1680 cl = repo.changelog
1681 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
1681 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
1682 s = subset
1682 s = subset
1683 return baseset([r for r in ls if r in s])
1683 return baseset([r for r in ls if r in s])
1684
1684
1685 symbols = {
1685 symbols = {
1686 "adds": adds,
1686 "adds": adds,
1687 "all": getall,
1687 "all": getall,
1688 "ancestor": ancestor,
1688 "ancestor": ancestor,
1689 "ancestors": ancestors,
1689 "ancestors": ancestors,
1690 "_firstancestors": _firstancestors,
1690 "_firstancestors": _firstancestors,
1691 "author": author,
1691 "author": author,
1692 "only": only,
1692 "only": only,
1693 "bisect": bisect,
1693 "bisect": bisect,
1694 "bisected": bisected,
1694 "bisected": bisected,
1695 "bookmark": bookmark,
1695 "bookmark": bookmark,
1696 "branch": branch,
1696 "branch": branch,
1697 "branchpoint": branchpoint,
1697 "branchpoint": branchpoint,
1698 "bumped": bumped,
1698 "bumped": bumped,
1699 "bundle": bundle,
1699 "bundle": bundle,
1700 "children": children,
1700 "children": children,
1701 "closed": closed,
1701 "closed": closed,
1702 "contains": contains,
1702 "contains": contains,
1703 "converted": converted,
1703 "converted": converted,
1704 "date": date,
1704 "date": date,
1705 "desc": desc,
1705 "desc": desc,
1706 "descendants": descendants,
1706 "descendants": descendants,
1707 "_firstdescendants": _firstdescendants,
1707 "_firstdescendants": _firstdescendants,
1708 "destination": destination,
1708 "destination": destination,
1709 "divergent": divergent,
1709 "divergent": divergent,
1710 "draft": draft,
1710 "draft": draft,
1711 "extinct": extinct,
1711 "extinct": extinct,
1712 "extra": extra,
1712 "extra": extra,
1713 "file": hasfile,
1713 "file": hasfile,
1714 "filelog": filelog,
1714 "filelog": filelog,
1715 "first": first,
1715 "first": first,
1716 "follow": follow,
1716 "follow": follow,
1717 "_followfirst": _followfirst,
1717 "_followfirst": _followfirst,
1718 "grep": grep,
1718 "grep": grep,
1719 "head": head,
1719 "head": head,
1720 "heads": heads,
1720 "heads": heads,
1721 "hidden": hidden,
1721 "hidden": hidden,
1722 "id": node_,
1722 "id": node_,
1723 "keyword": keyword,
1723 "keyword": keyword,
1724 "last": last,
1724 "last": last,
1725 "limit": limit,
1725 "limit": limit,
1726 "_matchfiles": _matchfiles,
1726 "_matchfiles": _matchfiles,
1727 "max": maxrev,
1727 "max": maxrev,
1728 "merge": merge,
1728 "merge": merge,
1729 "min": minrev,
1729 "min": minrev,
1730 "modifies": modifies,
1730 "modifies": modifies,
1731 "obsolete": obsolete,
1731 "obsolete": obsolete,
1732 "origin": origin,
1732 "origin": origin,
1733 "outgoing": outgoing,
1733 "outgoing": outgoing,
1734 "p1": p1,
1734 "p1": p1,
1735 "p2": p2,
1735 "p2": p2,
1736 "parents": parents,
1736 "parents": parents,
1737 "present": present,
1737 "present": present,
1738 "public": public,
1738 "public": public,
1739 "remote": remote,
1739 "remote": remote,
1740 "removes": removes,
1740 "removes": removes,
1741 "rev": rev,
1741 "rev": rev,
1742 "reverse": reverse,
1742 "reverse": reverse,
1743 "roots": roots,
1743 "roots": roots,
1744 "sort": sort,
1744 "sort": sort,
1745 "secret": secret,
1745 "secret": secret,
1746 "matching": matching,
1746 "matching": matching,
1747 "tag": tag,
1747 "tag": tag,
1748 "tagged": tagged,
1748 "tagged": tagged,
1749 "user": user,
1749 "user": user,
1750 "unstable": unstable,
1750 "unstable": unstable,
1751 "_list": _list,
1751 "_list": _list,
1752 "_intlist": _intlist,
1752 "_intlist": _intlist,
1753 "_hexlist": _hexlist,
1753 "_hexlist": _hexlist,
1754 }
1754 }
1755
1755
1756 # symbols which can't be used for a DoS attack for any given input
1756 # symbols which can't be used for a DoS attack for any given input
1757 # (e.g. those which accept regexes as plain strings shouldn't be included)
1757 # (e.g. those which accept regexes as plain strings shouldn't be included)
1758 # functions that just return a lot of changesets (like all) don't count here
1758 # functions that just return a lot of changesets (like all) don't count here
1759 safesymbols = set([
1759 safesymbols = set([
1760 "adds",
1760 "adds",
1761 "all",
1761 "all",
1762 "ancestor",
1762 "ancestor",
1763 "ancestors",
1763 "ancestors",
1764 "_firstancestors",
1764 "_firstancestors",
1765 "author",
1765 "author",
1766 "bisect",
1766 "bisect",
1767 "bisected",
1767 "bisected",
1768 "bookmark",
1768 "bookmark",
1769 "branch",
1769 "branch",
1770 "branchpoint",
1770 "branchpoint",
1771 "bumped",
1771 "bumped",
1772 "bundle",
1772 "bundle",
1773 "children",
1773 "children",
1774 "closed",
1774 "closed",
1775 "converted",
1775 "converted",
1776 "date",
1776 "date",
1777 "desc",
1777 "desc",
1778 "descendants",
1778 "descendants",
1779 "_firstdescendants",
1779 "_firstdescendants",
1780 "destination",
1780 "destination",
1781 "divergent",
1781 "divergent",
1782 "draft",
1782 "draft",
1783 "extinct",
1783 "extinct",
1784 "extra",
1784 "extra",
1785 "file",
1785 "file",
1786 "filelog",
1786 "filelog",
1787 "first",
1787 "first",
1788 "follow",
1788 "follow",
1789 "_followfirst",
1789 "_followfirst",
1790 "head",
1790 "head",
1791 "heads",
1791 "heads",
1792 "hidden",
1792 "hidden",
1793 "id",
1793 "id",
1794 "keyword",
1794 "keyword",
1795 "last",
1795 "last",
1796 "limit",
1796 "limit",
1797 "_matchfiles",
1797 "_matchfiles",
1798 "max",
1798 "max",
1799 "merge",
1799 "merge",
1800 "min",
1800 "min",
1801 "modifies",
1801 "modifies",
1802 "obsolete",
1802 "obsolete",
1803 "origin",
1803 "origin",
1804 "outgoing",
1804 "outgoing",
1805 "p1",
1805 "p1",
1806 "p2",
1806 "p2",
1807 "parents",
1807 "parents",
1808 "present",
1808 "present",
1809 "public",
1809 "public",
1810 "remote",
1810 "remote",
1811 "removes",
1811 "removes",
1812 "rev",
1812 "rev",
1813 "reverse",
1813 "reverse",
1814 "roots",
1814 "roots",
1815 "sort",
1815 "sort",
1816 "secret",
1816 "secret",
1817 "matching",
1817 "matching",
1818 "tag",
1818 "tag",
1819 "tagged",
1819 "tagged",
1820 "user",
1820 "user",
1821 "unstable",
1821 "unstable",
1822 "_list",
1822 "_list",
1823 "_intlist",
1823 "_intlist",
1824 "_hexlist",
1824 "_hexlist",
1825 ])
1825 ])
1826
1826
1827 methods = {
1827 methods = {
1828 "range": rangeset,
1828 "range": rangeset,
1829 "dagrange": dagrange,
1829 "dagrange": dagrange,
1830 "string": stringset,
1830 "string": stringset,
1831 "symbol": symbolset,
1831 "symbol": symbolset,
1832 "and": andset,
1832 "and": andset,
1833 "or": orset,
1833 "or": orset,
1834 "not": notset,
1834 "not": notset,
1835 "list": listset,
1835 "list": listset,
1836 "func": func,
1836 "func": func,
1837 "ancestor": ancestorspec,
1837 "ancestor": ancestorspec,
1838 "parent": parentspec,
1838 "parent": parentspec,
1839 "parentpost": p1,
1839 "parentpost": p1,
1840 }
1840 }
1841
1841
1842 def optimize(x, small):
1842 def optimize(x, small):
1843 if x is None:
1843 if x is None:
1844 return 0, x
1844 return 0, x
1845
1845
1846 smallbonus = 1
1846 smallbonus = 1
1847 if small:
1847 if small:
1848 smallbonus = .5
1848 smallbonus = .5
1849
1849
1850 op = x[0]
1850 op = x[0]
1851 if op == 'minus':
1851 if op == 'minus':
1852 return optimize(('and', x[1], ('not', x[2])), small)
1852 return optimize(('and', x[1], ('not', x[2])), small)
1853 elif op == 'dagrangepre':
1853 elif op == 'dagrangepre':
1854 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1854 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1855 elif op == 'dagrangepost':
1855 elif op == 'dagrangepost':
1856 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1856 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1857 elif op == 'rangepre':
1857 elif op == 'rangepre':
1858 return optimize(('range', ('string', '0'), x[1]), small)
1858 return optimize(('range', ('string', '0'), x[1]), small)
1859 elif op == 'rangepost':
1859 elif op == 'rangepost':
1860 return optimize(('range', x[1], ('string', 'tip')), small)
1860 return optimize(('range', x[1], ('string', 'tip')), small)
1861 elif op == 'negate':
1861 elif op == 'negate':
1862 return optimize(('string',
1862 return optimize(('string',
1863 '-' + getstring(x[1], _("can't negate that"))), small)
1863 '-' + getstring(x[1], _("can't negate that"))), small)
1864 elif op in 'string symbol negate':
1864 elif op in 'string symbol negate':
1865 return smallbonus, x # single revisions are small
1865 return smallbonus, x # single revisions are small
1866 elif op == 'and':
1866 elif op == 'and':
1867 wa, ta = optimize(x[1], True)
1867 wa, ta = optimize(x[1], True)
1868 wb, tb = optimize(x[2], True)
1868 wb, tb = optimize(x[2], True)
1869
1869
1870 # (::x and not ::y)/(not ::y and ::x) have a fast path
1870 # (::x and not ::y)/(not ::y and ::x) have a fast path
1871 def isonly(revs, bases):
1871 def isonly(revs, bases):
1872 return (
1872 return (
1873 revs[0] == 'func'
1873 revs[0] == 'func'
1874 and getstring(revs[1], _('not a symbol')) == 'ancestors'
1874 and getstring(revs[1], _('not a symbol')) == 'ancestors'
1875 and bases[0] == 'not'
1875 and bases[0] == 'not'
1876 and bases[1][0] == 'func'
1876 and bases[1][0] == 'func'
1877 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
1877 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
1878
1878
1879 w = min(wa, wb)
1879 w = min(wa, wb)
1880 if isonly(ta, tb):
1880 if isonly(ta, tb):
1881 return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2]))
1881 return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2]))
1882 if isonly(tb, ta):
1882 if isonly(tb, ta):
1883 return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2]))
1883 return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2]))
1884
1884
1885 if wa > wb:
1885 if wa > wb:
1886 return w, (op, tb, ta)
1886 return w, (op, tb, ta)
1887 return w, (op, ta, tb)
1887 return w, (op, ta, tb)
1888 elif op == 'or':
1888 elif op == 'or':
1889 wa, ta = optimize(x[1], False)
1889 wa, ta = optimize(x[1], False)
1890 wb, tb = optimize(x[2], False)
1890 wb, tb = optimize(x[2], False)
1891 if wb < wa:
1891 if wb < wa:
1892 wb, wa = wa, wb
1892 wb, wa = wa, wb
1893 return max(wa, wb), (op, ta, tb)
1893 return max(wa, wb), (op, ta, tb)
1894 elif op == 'not':
1894 elif op == 'not':
1895 o = optimize(x[1], not small)
1895 o = optimize(x[1], not small)
1896 return o[0], (op, o[1])
1896 return o[0], (op, o[1])
1897 elif op == 'parentpost':
1897 elif op == 'parentpost':
1898 o = optimize(x[1], small)
1898 o = optimize(x[1], small)
1899 return o[0], (op, o[1])
1899 return o[0], (op, o[1])
1900 elif op == 'group':
1900 elif op == 'group':
1901 return optimize(x[1], small)
1901 return optimize(x[1], small)
1902 elif op in 'dagrange range list parent ancestorspec':
1902 elif op in 'dagrange range list parent ancestorspec':
1903 if op == 'parent':
1903 if op == 'parent':
1904 # x^:y means (x^) : y, not x ^ (:y)
1904 # x^:y means (x^) : y, not x ^ (:y)
1905 post = ('parentpost', x[1])
1905 post = ('parentpost', x[1])
1906 if x[2][0] == 'dagrangepre':
1906 if x[2][0] == 'dagrangepre':
1907 return optimize(('dagrange', post, x[2][1]), small)
1907 return optimize(('dagrange', post, x[2][1]), small)
1908 elif x[2][0] == 'rangepre':
1908 elif x[2][0] == 'rangepre':
1909 return optimize(('range', post, x[2][1]), small)
1909 return optimize(('range', post, x[2][1]), small)
1910
1910
1911 wa, ta = optimize(x[1], small)
1911 wa, ta = optimize(x[1], small)
1912 wb, tb = optimize(x[2], small)
1912 wb, tb = optimize(x[2], small)
1913 return wa + wb, (op, ta, tb)
1913 return wa + wb, (op, ta, tb)
1914 elif op == 'func':
1914 elif op == 'func':
1915 f = getstring(x[1], _("not a symbol"))
1915 f = getstring(x[1], _("not a symbol"))
1916 wa, ta = optimize(x[2], small)
1916 wa, ta = optimize(x[2], small)
1917 if f in ("author branch closed date desc file grep keyword "
1917 if f in ("author branch closed date desc file grep keyword "
1918 "outgoing user"):
1918 "outgoing user"):
1919 w = 10 # slow
1919 w = 10 # slow
1920 elif f in "modifies adds removes":
1920 elif f in "modifies adds removes":
1921 w = 30 # slower
1921 w = 30 # slower
1922 elif f == "contains":
1922 elif f == "contains":
1923 w = 100 # very slow
1923 w = 100 # very slow
1924 elif f == "ancestor":
1924 elif f == "ancestor":
1925 w = 1 * smallbonus
1925 w = 1 * smallbonus
1926 elif f in "reverse limit first _intlist":
1926 elif f in "reverse limit first _intlist":
1927 w = 0
1927 w = 0
1928 elif f in "sort":
1928 elif f in "sort":
1929 w = 10 # assume most sorts look at changelog
1929 w = 10 # assume most sorts look at changelog
1930 else:
1930 else:
1931 w = 1
1931 w = 1
1932 return w + wa, (op, x[1], ta)
1932 return w + wa, (op, x[1], ta)
1933 return 1, x
1933 return 1, x
1934
1934
1935 _aliasarg = ('func', ('symbol', '_aliasarg'))
1935 _aliasarg = ('func', ('symbol', '_aliasarg'))
1936 def _getaliasarg(tree):
1936 def _getaliasarg(tree):
1937 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1937 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1938 return X, None otherwise.
1938 return X, None otherwise.
1939 """
1939 """
1940 if (len(tree) == 3 and tree[:2] == _aliasarg
1940 if (len(tree) == 3 and tree[:2] == _aliasarg
1941 and tree[2][0] == 'string'):
1941 and tree[2][0] == 'string'):
1942 return tree[2][1]
1942 return tree[2][1]
1943 return None
1943 return None
1944
1944
1945 def _checkaliasarg(tree, known=None):
1945 def _checkaliasarg(tree, known=None):
1946 """Check tree contains no _aliasarg construct or only ones which
1946 """Check tree contains no _aliasarg construct or only ones which
1947 value is in known. Used to avoid alias placeholders injection.
1947 value is in known. Used to avoid alias placeholders injection.
1948 """
1948 """
1949 if isinstance(tree, tuple):
1949 if isinstance(tree, tuple):
1950 arg = _getaliasarg(tree)
1950 arg = _getaliasarg(tree)
1951 if arg is not None and (not known or arg not in known):
1951 if arg is not None and (not known or arg not in known):
1952 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1952 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1953 for t in tree:
1953 for t in tree:
1954 _checkaliasarg(t, known)
1954 _checkaliasarg(t, known)
1955
1955
1956 class revsetalias(object):
1956 class revsetalias(object):
1957 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1957 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1958 args = None
1958 args = None
1959
1959
1960 def __init__(self, name, value):
1960 def __init__(self, name, value):
1961 '''Aliases like:
1961 '''Aliases like:
1962
1962
1963 h = heads(default)
1963 h = heads(default)
1964 b($1) = ancestors($1) - ancestors(default)
1964 b($1) = ancestors($1) - ancestors(default)
1965 '''
1965 '''
1966 m = self.funcre.search(name)
1966 m = self.funcre.search(name)
1967 if m:
1967 if m:
1968 self.name = m.group(1)
1968 self.name = m.group(1)
1969 self.tree = ('func', ('symbol', m.group(1)))
1969 self.tree = ('func', ('symbol', m.group(1)))
1970 self.args = [x.strip() for x in m.group(2).split(',')]
1970 self.args = [x.strip() for x in m.group(2).split(',')]
1971 for arg in self.args:
1971 for arg in self.args:
1972 # _aliasarg() is an unknown symbol only used separate
1972 # _aliasarg() is an unknown symbol only used separate
1973 # alias argument placeholders from regular strings.
1973 # alias argument placeholders from regular strings.
1974 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1974 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1975 else:
1975 else:
1976 self.name = name
1976 self.name = name
1977 self.tree = ('symbol', name)
1977 self.tree = ('symbol', name)
1978
1978
1979 self.replacement, pos = parse(value)
1979 self.replacement, pos = parse(value)
1980 if pos != len(value):
1980 if pos != len(value):
1981 raise error.ParseError(_('invalid token'), pos)
1981 raise error.ParseError(_('invalid token'), pos)
1982 # Check for placeholder injection
1982 # Check for placeholder injection
1983 _checkaliasarg(self.replacement, self.args)
1983 _checkaliasarg(self.replacement, self.args)
1984
1984
1985 def _getalias(aliases, tree):
1985 def _getalias(aliases, tree):
1986 """If tree looks like an unexpanded alias, return it. Return None
1986 """If tree looks like an unexpanded alias, return it. Return None
1987 otherwise.
1987 otherwise.
1988 """
1988 """
1989 if isinstance(tree, tuple) and tree:
1989 if isinstance(tree, tuple) and tree:
1990 if tree[0] == 'symbol' and len(tree) == 2:
1990 if tree[0] == 'symbol' and len(tree) == 2:
1991 name = tree[1]
1991 name = tree[1]
1992 alias = aliases.get(name)
1992 alias = aliases.get(name)
1993 if alias and alias.args is None and alias.tree == tree:
1993 if alias and alias.args is None and alias.tree == tree:
1994 return alias
1994 return alias
1995 if tree[0] == 'func' and len(tree) > 1:
1995 if tree[0] == 'func' and len(tree) > 1:
1996 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1996 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1997 name = tree[1][1]
1997 name = tree[1][1]
1998 alias = aliases.get(name)
1998 alias = aliases.get(name)
1999 if alias and alias.args is not None and alias.tree == tree[:2]:
1999 if alias and alias.args is not None and alias.tree == tree[:2]:
2000 return alias
2000 return alias
2001 return None
2001 return None
2002
2002
2003 def _expandargs(tree, args):
2003 def _expandargs(tree, args):
2004 """Replace _aliasarg instances with the substitution value of the
2004 """Replace _aliasarg instances with the substitution value of the
2005 same name in args, recursively.
2005 same name in args, recursively.
2006 """
2006 """
2007 if not tree or not isinstance(tree, tuple):
2007 if not tree or not isinstance(tree, tuple):
2008 return tree
2008 return tree
2009 arg = _getaliasarg(tree)
2009 arg = _getaliasarg(tree)
2010 if arg is not None:
2010 if arg is not None:
2011 return args[arg]
2011 return args[arg]
2012 return tuple(_expandargs(t, args) for t in tree)
2012 return tuple(_expandargs(t, args) for t in tree)
2013
2013
2014 def _expandaliases(aliases, tree, expanding, cache):
2014 def _expandaliases(aliases, tree, expanding, cache):
2015 """Expand aliases in tree, recursively.
2015 """Expand aliases in tree, recursively.
2016
2016
2017 'aliases' is a dictionary mapping user defined aliases to
2017 'aliases' is a dictionary mapping user defined aliases to
2018 revsetalias objects.
2018 revsetalias objects.
2019 """
2019 """
2020 if not isinstance(tree, tuple):
2020 if not isinstance(tree, tuple):
2021 # Do not expand raw strings
2021 # Do not expand raw strings
2022 return tree
2022 return tree
2023 alias = _getalias(aliases, tree)
2023 alias = _getalias(aliases, tree)
2024 if alias is not None:
2024 if alias is not None:
2025 if alias in expanding:
2025 if alias in expanding:
2026 raise error.ParseError(_('infinite expansion of revset alias "%s" '
2026 raise error.ParseError(_('infinite expansion of revset alias "%s" '
2027 'detected') % alias.name)
2027 'detected') % alias.name)
2028 expanding.append(alias)
2028 expanding.append(alias)
2029 if alias.name not in cache:
2029 if alias.name not in cache:
2030 cache[alias.name] = _expandaliases(aliases, alias.replacement,
2030 cache[alias.name] = _expandaliases(aliases, alias.replacement,
2031 expanding, cache)
2031 expanding, cache)
2032 result = cache[alias.name]
2032 result = cache[alias.name]
2033 expanding.pop()
2033 expanding.pop()
2034 if alias.args is not None:
2034 if alias.args is not None:
2035 l = getlist(tree[2])
2035 l = getlist(tree[2])
2036 if len(l) != len(alias.args):
2036 if len(l) != len(alias.args):
2037 raise error.ParseError(
2037 raise error.ParseError(
2038 _('invalid number of arguments: %s') % len(l))
2038 _('invalid number of arguments: %s') % len(l))
2039 l = [_expandaliases(aliases, a, [], cache) for a in l]
2039 l = [_expandaliases(aliases, a, [], cache) for a in l]
2040 result = _expandargs(result, dict(zip(alias.args, l)))
2040 result = _expandargs(result, dict(zip(alias.args, l)))
2041 else:
2041 else:
2042 result = tuple(_expandaliases(aliases, t, expanding, cache)
2042 result = tuple(_expandaliases(aliases, t, expanding, cache)
2043 for t in tree)
2043 for t in tree)
2044 return result
2044 return result
2045
2045
2046 def findaliases(ui, tree):
2046 def findaliases(ui, tree):
2047 _checkaliasarg(tree)
2047 _checkaliasarg(tree)
2048 aliases = {}
2048 aliases = {}
2049 for k, v in ui.configitems('revsetalias'):
2049 for k, v in ui.configitems('revsetalias'):
2050 alias = revsetalias(k, v)
2050 alias = revsetalias(k, v)
2051 aliases[alias.name] = alias
2051 aliases[alias.name] = alias
2052 return _expandaliases(aliases, tree, [], {})
2052 return _expandaliases(aliases, tree, [], {})
2053
2053
2054 def parse(spec, lookup=None):
2054 def parse(spec, lookup=None):
2055 p = parser.parser(tokenize, elements)
2055 p = parser.parser(tokenize, elements)
2056 return p.parse(spec, lookup=lookup)
2056 return p.parse(spec, lookup=lookup)
2057
2057
2058 def match(ui, spec, repo=None):
2058 def match(ui, spec, repo=None):
2059 if not spec:
2059 if not spec:
2060 raise error.ParseError(_("empty query"))
2060 raise error.ParseError(_("empty query"))
2061 lookup = None
2061 lookup = None
2062 if repo:
2062 if repo:
2063 lookup = repo.__contains__
2063 lookup = repo.__contains__
2064 tree, pos = parse(spec, lookup)
2064 tree, pos = parse(spec, lookup)
2065 if (pos != len(spec)):
2065 if (pos != len(spec)):
2066 raise error.ParseError(_("invalid token"), pos)
2066 raise error.ParseError(_("invalid token"), pos)
2067 if ui:
2067 if ui:
2068 tree = findaliases(ui, tree)
2068 tree = findaliases(ui, tree)
2069 weight, tree = optimize(tree, True)
2069 weight, tree = optimize(tree, True)
2070 def mfunc(repo, subset):
2070 def mfunc(repo, subset):
2071 if util.safehasattr(subset, 'isascending'):
2071 if util.safehasattr(subset, 'isascending'):
2072 result = getset(repo, subset, tree)
2072 result = getset(repo, subset, tree)
2073 else:
2073 else:
2074 result = getset(repo, baseset(subset), tree)
2074 result = getset(repo, baseset(subset), tree)
2075 return result
2075 return result
2076 return mfunc
2076 return mfunc
2077
2077
2078 def formatspec(expr, *args):
2078 def formatspec(expr, *args):
2079 '''
2079 '''
2080 This is a convenience function for using revsets internally, and
2080 This is a convenience function for using revsets internally, and
2081 escapes arguments appropriately. Aliases are intentionally ignored
2081 escapes arguments appropriately. Aliases are intentionally ignored
2082 so that intended expression behavior isn't accidentally subverted.
2082 so that intended expression behavior isn't accidentally subverted.
2083
2083
2084 Supported arguments:
2084 Supported arguments:
2085
2085
2086 %r = revset expression, parenthesized
2086 %r = revset expression, parenthesized
2087 %d = int(arg), no quoting
2087 %d = int(arg), no quoting
2088 %s = string(arg), escaped and single-quoted
2088 %s = string(arg), escaped and single-quoted
2089 %b = arg.branch(), escaped and single-quoted
2089 %b = arg.branch(), escaped and single-quoted
2090 %n = hex(arg), single-quoted
2090 %n = hex(arg), single-quoted
2091 %% = a literal '%'
2091 %% = a literal '%'
2092
2092
2093 Prefixing the type with 'l' specifies a parenthesized list of that type.
2093 Prefixing the type with 'l' specifies a parenthesized list of that type.
2094
2094
2095 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2095 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2096 '(10 or 11):: and ((this()) or (that()))'
2096 '(10 or 11):: and ((this()) or (that()))'
2097 >>> formatspec('%d:: and not %d::', 10, 20)
2097 >>> formatspec('%d:: and not %d::', 10, 20)
2098 '10:: and not 20::'
2098 '10:: and not 20::'
2099 >>> formatspec('%ld or %ld', [], [1])
2099 >>> formatspec('%ld or %ld', [], [1])
2100 "_list('') or 1"
2100 "_list('') or 1"
2101 >>> formatspec('keyword(%s)', 'foo\\xe9')
2101 >>> formatspec('keyword(%s)', 'foo\\xe9')
2102 "keyword('foo\\\\xe9')"
2102 "keyword('foo\\\\xe9')"
2103 >>> b = lambda: 'default'
2103 >>> b = lambda: 'default'
2104 >>> b.branch = b
2104 >>> b.branch = b
2105 >>> formatspec('branch(%b)', b)
2105 >>> formatspec('branch(%b)', b)
2106 "branch('default')"
2106 "branch('default')"
2107 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2107 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2108 "root(_list('a\\x00b\\x00c\\x00d'))"
2108 "root(_list('a\\x00b\\x00c\\x00d'))"
2109 '''
2109 '''
2110
2110
2111 def quote(s):
2111 def quote(s):
2112 return repr(str(s))
2112 return repr(str(s))
2113
2113
2114 def argtype(c, arg):
2114 def argtype(c, arg):
2115 if c == 'd':
2115 if c == 'd':
2116 return str(int(arg))
2116 return str(int(arg))
2117 elif c == 's':
2117 elif c == 's':
2118 return quote(arg)
2118 return quote(arg)
2119 elif c == 'r':
2119 elif c == 'r':
2120 parse(arg) # make sure syntax errors are confined
2120 parse(arg) # make sure syntax errors are confined
2121 return '(%s)' % arg
2121 return '(%s)' % arg
2122 elif c == 'n':
2122 elif c == 'n':
2123 return quote(node.hex(arg))
2123 return quote(node.hex(arg))
2124 elif c == 'b':
2124 elif c == 'b':
2125 return quote(arg.branch())
2125 return quote(arg.branch())
2126
2126
2127 def listexp(s, t):
2127 def listexp(s, t):
2128 l = len(s)
2128 l = len(s)
2129 if l == 0:
2129 if l == 0:
2130 return "_list('')"
2130 return "_list('')"
2131 elif l == 1:
2131 elif l == 1:
2132 return argtype(t, s[0])
2132 return argtype(t, s[0])
2133 elif t == 'd':
2133 elif t == 'd':
2134 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2134 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2135 elif t == 's':
2135 elif t == 's':
2136 return "_list('%s')" % "\0".join(s)
2136 return "_list('%s')" % "\0".join(s)
2137 elif t == 'n':
2137 elif t == 'n':
2138 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2138 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2139 elif t == 'b':
2139 elif t == 'b':
2140 return "_list('%s')" % "\0".join(a.branch() for a in s)
2140 return "_list('%s')" % "\0".join(a.branch() for a in s)
2141
2141
2142 m = l // 2
2142 m = l // 2
2143 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2143 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2144
2144
2145 ret = ''
2145 ret = ''
2146 pos = 0
2146 pos = 0
2147 arg = 0
2147 arg = 0
2148 while pos < len(expr):
2148 while pos < len(expr):
2149 c = expr[pos]
2149 c = expr[pos]
2150 if c == '%':
2150 if c == '%':
2151 pos += 1
2151 pos += 1
2152 d = expr[pos]
2152 d = expr[pos]
2153 if d == '%':
2153 if d == '%':
2154 ret += d
2154 ret += d
2155 elif d in 'dsnbr':
2155 elif d in 'dsnbr':
2156 ret += argtype(d, args[arg])
2156 ret += argtype(d, args[arg])
2157 arg += 1
2157 arg += 1
2158 elif d == 'l':
2158 elif d == 'l':
2159 # a list of some type
2159 # a list of some type
2160 pos += 1
2160 pos += 1
2161 d = expr[pos]
2161 d = expr[pos]
2162 ret += listexp(list(args[arg]), d)
2162 ret += listexp(list(args[arg]), d)
2163 arg += 1
2163 arg += 1
2164 else:
2164 else:
2165 raise util.Abort('unexpected revspec format character %s' % d)
2165 raise util.Abort('unexpected revspec format character %s' % d)
2166 else:
2166 else:
2167 ret += c
2167 ret += c
2168 pos += 1
2168 pos += 1
2169
2169
2170 return ret
2170 return ret
2171
2171
2172 def prettyformat(tree):
2172 def prettyformat(tree):
2173 def _prettyformat(tree, level, lines):
2173 def _prettyformat(tree, level, lines):
2174 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2174 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2175 lines.append((level, str(tree)))
2175 lines.append((level, str(tree)))
2176 else:
2176 else:
2177 lines.append((level, '(%s' % tree[0]))
2177 lines.append((level, '(%s' % tree[0]))
2178 for s in tree[1:]:
2178 for s in tree[1:]:
2179 _prettyformat(s, level + 1, lines)
2179 _prettyformat(s, level + 1, lines)
2180 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2180 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2181
2181
2182 lines = []
2182 lines = []
2183 _prettyformat(tree, 0, lines)
2183 _prettyformat(tree, 0, lines)
2184 output = '\n'.join((' '*l + s) for l, s in lines)
2184 output = '\n'.join((' '*l + s) for l, s in lines)
2185 return output
2185 return output
2186
2186
2187 def depth(tree):
2187 def depth(tree):
2188 if isinstance(tree, tuple):
2188 if isinstance(tree, tuple):
2189 return max(map(depth, tree)) + 1
2189 return max(map(depth, tree)) + 1
2190 else:
2190 else:
2191 return 0
2191 return 0
2192
2192
2193 def funcsused(tree):
2193 def funcsused(tree):
2194 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2194 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2195 return set()
2195 return set()
2196 else:
2196 else:
2197 funcs = set()
2197 funcs = set()
2198 for s in tree[1:]:
2198 for s in tree[1:]:
2199 funcs |= funcsused(s)
2199 funcs |= funcsused(s)
2200 if tree[0] == 'func':
2200 if tree[0] == 'func':
2201 funcs.add(tree[1][1])
2201 funcs.add(tree[1][1])
2202 return funcs
2202 return funcs
2203
2203
2204 class abstractsmartset(object):
2204 class abstractsmartset(object):
2205
2205
2206 def __nonzero__(self):
2206 def __nonzero__(self):
2207 """True if the smartset is not empty"""
2207 """True if the smartset is not empty"""
2208 raise NotImplementedError()
2208 raise NotImplementedError()
2209
2209
2210 def __contains__(self, rev):
2210 def __contains__(self, rev):
2211 """provide fast membership testing"""
2211 """provide fast membership testing"""
2212 raise NotImplementedError()
2212 raise NotImplementedError()
2213
2213
2214 def __iter__(self):
2214 def __iter__(self):
2215 """iterate the set in the order it is supposed to be iterated"""
2215 """iterate the set in the order it is supposed to be iterated"""
2216 raise NotImplementedError()
2216 raise NotImplementedError()
2217
2217
2218 # Attributes containing a function to perform a fast iteration in a given
2218 # Attributes containing a function to perform a fast iteration in a given
2219 # direction. A smartset can have none, one, or both defined.
2219 # direction. A smartset can have none, one, or both defined.
2220 #
2220 #
2221 # Default value is None instead of a function returning None to avoid
2221 # Default value is None instead of a function returning None to avoid
2222 # initializing an iterator just for testing if a fast method exists.
2222 # initializing an iterator just for testing if a fast method exists.
2223 fastasc = None
2223 fastasc = None
2224 fastdesc = None
2224 fastdesc = None
2225
2225
2226 def isascending(self):
2226 def isascending(self):
2227 """True if the set will iterate in ascending order"""
2227 """True if the set will iterate in ascending order"""
2228 raise NotImplementedError()
2228 raise NotImplementedError()
2229
2229
2230 def isdescending(self):
2230 def isdescending(self):
2231 """True if the set will iterate in descending order"""
2231 """True if the set will iterate in descending order"""
2232 raise NotImplementedError()
2232 raise NotImplementedError()
2233
2233
2234 def min(self):
2234 def min(self):
2235 """return the minimum element in the set"""
2235 """return the minimum element in the set"""
2236 if self.fastasc is not None:
2236 if self.fastasc is not None:
2237 for r in self.fastasc():
2237 for r in self.fastasc():
2238 return r
2238 return r
2239 raise ValueError('arg is an empty sequence')
2239 raise ValueError('arg is an empty sequence')
2240 return min(self)
2240 return min(self)
2241
2241
2242 def max(self):
2242 def max(self):
2243 """return the maximum element in the set"""
2243 """return the maximum element in the set"""
2244 if self.fastdesc is not None:
2244 if self.fastdesc is not None:
2245 for r in self.fastdesc():
2245 for r in self.fastdesc():
2246 return r
2246 return r
2247 raise ValueError('arg is an empty sequence')
2247 raise ValueError('arg is an empty sequence')
2248 return max(self)
2248 return max(self)
2249
2249
2250 def first(self):
2250 def first(self):
2251 """return the first element in the set (user iteration perspective)
2251 """return the first element in the set (user iteration perspective)
2252
2252
2253 Return None if the set is empty"""
2253 Return None if the set is empty"""
2254 raise NotImplementedError()
2254 raise NotImplementedError()
2255
2255
2256 def last(self):
2256 def last(self):
2257 """return the last element in the set (user iteration perspective)
2257 """return the last element in the set (user iteration perspective)
2258
2258
2259 Return None if the set is empty"""
2259 Return None if the set is empty"""
2260 raise NotImplementedError()
2260 raise NotImplementedError()
2261
2261
2262 def __len__(self):
2262 def __len__(self):
2263 """return the length of the smartsets
2263 """return the length of the smartsets
2264
2264
2265 This can be expensive on smartset that could be lazy otherwise."""
2265 This can be expensive on smartset that could be lazy otherwise."""
2266 raise NotImplementedError()
2266 raise NotImplementedError()
2267
2267
2268 def reverse(self):
2268 def reverse(self):
2269 """reverse the expected iteration order"""
2269 """reverse the expected iteration order"""
2270 raise NotImplementedError()
2270 raise NotImplementedError()
2271
2271
2272 def sort(self, reverse=True):
2272 def sort(self, reverse=True):
2273 """get the set to iterate in an ascending or descending order"""
2273 """get the set to iterate in an ascending or descending order"""
2274 raise NotImplementedError()
2274 raise NotImplementedError()
2275
2275
2276 def __and__(self, other):
2276 def __and__(self, other):
2277 """Returns a new object with the intersection of the two collections.
2277 """Returns a new object with the intersection of the two collections.
2278
2278
2279 This is part of the mandatory API for smartset."""
2279 This is part of the mandatory API for smartset."""
2280 return self.filter(other.__contains__, cache=False)
2280 return self.filter(other.__contains__, cache=False)
2281
2281
2282 def __add__(self, other):
2282 def __add__(self, other):
2283 """Returns a new object with the union of the two collections.
2283 """Returns a new object with the union of the two collections.
2284
2284
2285 This is part of the mandatory API for smartset."""
2285 This is part of the mandatory API for smartset."""
2286 return addset(self, other)
2286 return addset(self, other)
2287
2287
2288 def __sub__(self, other):
2288 def __sub__(self, other):
2289 """Returns a new object with the substraction of the two collections.
2289 """Returns a new object with the substraction of the two collections.
2290
2290
2291 This is part of the mandatory API for smartset."""
2291 This is part of the mandatory API for smartset."""
2292 c = other.__contains__
2292 c = other.__contains__
2293 return self.filter(lambda r: not c(r), cache=False)
2293 return self.filter(lambda r: not c(r), cache=False)
2294
2294
2295 def filter(self, condition, cache=True):
2295 def filter(self, condition, cache=True):
2296 """Returns this smartset filtered by condition as a new smartset.
2296 """Returns this smartset filtered by condition as a new smartset.
2297
2297
2298 `condition` is a callable which takes a revision number and returns a
2298 `condition` is a callable which takes a revision number and returns a
2299 boolean.
2299 boolean.
2300
2300
2301 This is part of the mandatory API for smartset."""
2301 This is part of the mandatory API for smartset."""
2302 # builtin cannot be cached. but do not needs to
2302 # builtin cannot be cached. but do not needs to
2303 if cache and util.safehasattr(condition, 'func_code'):
2303 if cache and util.safehasattr(condition, 'func_code'):
2304 condition = util.cachefunc(condition)
2304 condition = util.cachefunc(condition)
2305 return filteredset(self, condition)
2305 return filteredset(self, condition)
2306
2306
2307 class baseset(abstractsmartset):
2307 class baseset(abstractsmartset):
2308 """Basic data structure that represents a revset and contains the basic
2308 """Basic data structure that represents a revset and contains the basic
2309 operation that it should be able to perform.
2309 operation that it should be able to perform.
2310
2310
2311 Every method in this class should be implemented by any smartset class.
2311 Every method in this class should be implemented by any smartset class.
2312 """
2312 """
2313 def __init__(self, data=()):
2313 def __init__(self, data=()):
2314 if not isinstance(data, list):
2314 if not isinstance(data, list):
2315 data = list(data)
2315 data = list(data)
2316 self._list = data
2316 self._list = data
2317 self._ascending = None
2317 self._ascending = None
2318
2318
2319 @util.propertycache
2319 @util.propertycache
2320 def _set(self):
2320 def _set(self):
2321 return set(self._list)
2321 return set(self._list)
2322
2322
2323 @util.propertycache
2323 @util.propertycache
2324 def _asclist(self):
2324 def _asclist(self):
2325 asclist = self._list[:]
2325 asclist = self._list[:]
2326 asclist.sort()
2326 asclist.sort()
2327 return asclist
2327 return asclist
2328
2328
2329 def __iter__(self):
2329 def __iter__(self):
2330 if self._ascending is None:
2330 if self._ascending is None:
2331 return iter(self._list)
2331 return iter(self._list)
2332 elif self._ascending:
2332 elif self._ascending:
2333 return iter(self._asclist)
2333 return iter(self._asclist)
2334 else:
2334 else:
2335 return reversed(self._asclist)
2335 return reversed(self._asclist)
2336
2336
2337 def fastasc(self):
2337 def fastasc(self):
2338 return iter(self._asclist)
2338 return iter(self._asclist)
2339
2339
2340 def fastdesc(self):
2340 def fastdesc(self):
2341 return reversed(self._asclist)
2341 return reversed(self._asclist)
2342
2342
2343 @util.propertycache
2343 @util.propertycache
2344 def __contains__(self):
2344 def __contains__(self):
2345 return self._set.__contains__
2345 return self._set.__contains__
2346
2346
2347 def __nonzero__(self):
2347 def __nonzero__(self):
2348 return bool(self._list)
2348 return bool(self._list)
2349
2349
2350 def sort(self, reverse=False):
2350 def sort(self, reverse=False):
2351 self._ascending = not bool(reverse)
2351 self._ascending = not bool(reverse)
2352
2352
2353 def reverse(self):
2353 def reverse(self):
2354 if self._ascending is None:
2354 if self._ascending is None:
2355 self._list.reverse()
2355 self._list.reverse()
2356 else:
2356 else:
2357 self._ascending = not self._ascending
2357 self._ascending = not self._ascending
2358
2358
2359 def __len__(self):
2359 def __len__(self):
2360 return len(self._list)
2360 return len(self._list)
2361
2361
2362 def isascending(self):
2362 def isascending(self):
2363 """Returns True if the collection is ascending order, False if not.
2363 """Returns True if the collection is ascending order, False if not.
2364
2364
2365 This is part of the mandatory API for smartset."""
2365 This is part of the mandatory API for smartset."""
2366 if len(self) <= 1:
2366 if len(self) <= 1:
2367 return True
2367 return True
2368 return self._ascending is not None and self._ascending
2368 return self._ascending is not None and self._ascending
2369
2369
2370 def isdescending(self):
2370 def isdescending(self):
2371 """Returns True if the collection is descending order, False if not.
2371 """Returns True if the collection is descending order, False if not.
2372
2372
2373 This is part of the mandatory API for smartset."""
2373 This is part of the mandatory API for smartset."""
2374 if len(self) <= 1:
2374 if len(self) <= 1:
2375 return True
2375 return True
2376 return self._ascending is not None and not self._ascending
2376 return self._ascending is not None and not self._ascending
2377
2377
2378 def first(self):
2378 def first(self):
2379 if self:
2379 if self:
2380 if self._ascending is None:
2380 if self._ascending is None:
2381 return self._list[0]
2381 return self._list[0]
2382 elif self._ascending:
2382 elif self._ascending:
2383 return self._asclist[0]
2383 return self._asclist[0]
2384 else:
2384 else:
2385 return self._asclist[-1]
2385 return self._asclist[-1]
2386 return None
2386 return None
2387
2387
2388 def last(self):
2388 def last(self):
2389 if self:
2389 if self:
2390 if self._ascending is None:
2390 if self._ascending is None:
2391 return self._list[-1]
2391 return self._list[-1]
2392 elif self._ascending:
2392 elif self._ascending:
2393 return self._asclist[-1]
2393 return self._asclist[-1]
2394 else:
2394 else:
2395 return self._asclist[0]
2395 return self._asclist[0]
2396 return None
2396 return None
2397
2397
2398 class filteredset(abstractsmartset):
2398 class filteredset(abstractsmartset):
2399 """Duck type for baseset class which iterates lazily over the revisions in
2399 """Duck type for baseset class which iterates lazily over the revisions in
2400 the subset and contains a function which tests for membership in the
2400 the subset and contains a function which tests for membership in the
2401 revset
2401 revset
2402 """
2402 """
2403 def __init__(self, subset, condition=lambda x: True):
2403 def __init__(self, subset, condition=lambda x: True):
2404 """
2404 """
2405 condition: a function that decide whether a revision in the subset
2405 condition: a function that decide whether a revision in the subset
2406 belongs to the revset or not.
2406 belongs to the revset or not.
2407 """
2407 """
2408 self._subset = subset
2408 self._subset = subset
2409 self._condition = condition
2409 self._condition = condition
2410 self._cache = {}
2410 self._cache = {}
2411
2411
2412 def __contains__(self, x):
2412 def __contains__(self, x):
2413 c = self._cache
2413 c = self._cache
2414 if x not in c:
2414 if x not in c:
2415 v = c[x] = x in self._subset and self._condition(x)
2415 v = c[x] = x in self._subset and self._condition(x)
2416 return v
2416 return v
2417 return c[x]
2417 return c[x]
2418
2418
2419 def __iter__(self):
2419 def __iter__(self):
2420 return self._iterfilter(self._subset)
2420 return self._iterfilter(self._subset)
2421
2421
2422 def _iterfilter(self, it):
2422 def _iterfilter(self, it):
2423 cond = self._condition
2423 cond = self._condition
2424 for x in it:
2424 for x in it:
2425 if cond(x):
2425 if cond(x):
2426 yield x
2426 yield x
2427
2427
2428 @property
2428 @property
2429 def fastasc(self):
2429 def fastasc(self):
2430 it = self._subset.fastasc
2430 it = self._subset.fastasc
2431 if it is None:
2431 if it is None:
2432 return None
2432 return None
2433 return lambda: self._iterfilter(it())
2433 return lambda: self._iterfilter(it())
2434
2434
2435 @property
2435 @property
2436 def fastdesc(self):
2436 def fastdesc(self):
2437 it = self._subset.fastdesc
2437 it = self._subset.fastdesc
2438 if it is None:
2438 if it is None:
2439 return None
2439 return None
2440 return lambda: self._iterfilter(it())
2440 return lambda: self._iterfilter(it())
2441
2441
2442 def __nonzero__(self):
2442 def __nonzero__(self):
2443 for r in self:
2443 for r in self:
2444 return True
2444 return True
2445 return False
2445 return False
2446
2446
2447 def __len__(self):
2447 def __len__(self):
2448 # Basic implementation to be changed in future patches.
2448 # Basic implementation to be changed in future patches.
2449 l = baseset([r for r in self])
2449 l = baseset([r for r in self])
2450 return len(l)
2450 return len(l)
2451
2451
2452 def sort(self, reverse=False):
2452 def sort(self, reverse=False):
2453 self._subset.sort(reverse=reverse)
2453 self._subset.sort(reverse=reverse)
2454
2454
2455 def reverse(self):
2455 def reverse(self):
2456 self._subset.reverse()
2456 self._subset.reverse()
2457
2457
2458 def isascending(self):
2458 def isascending(self):
2459 return self._subset.isascending()
2459 return self._subset.isascending()
2460
2460
2461 def isdescending(self):
2461 def isdescending(self):
2462 return self._subset.isdescending()
2462 return self._subset.isdescending()
2463
2463
2464 def first(self):
2464 def first(self):
2465 for x in self:
2465 for x in self:
2466 return x
2466 return x
2467 return None
2467 return None
2468
2468
2469 def last(self):
2469 def last(self):
2470 it = None
2470 it = None
2471 if self._subset.isascending:
2471 if self._subset.isascending:
2472 it = self.fastdesc
2472 it = self.fastdesc
2473 elif self._subset.isdescending:
2473 elif self._subset.isdescending:
2474 it = self.fastdesc
2474 it = self.fastdesc
2475 if it is None:
2475 if it is None:
2476 # slowly consume everything. This needs improvement
2476 # slowly consume everything. This needs improvement
2477 it = lambda: reversed(list(self))
2477 it = lambda: reversed(list(self))
2478 for x in it():
2478 for x in it():
2479 return x
2479 return x
2480 return None
2480 return None
2481
2481
2482 class addset(abstractsmartset):
2482 class addset(abstractsmartset):
2483 """Represent the addition of two sets
2483 """Represent the addition of two sets
2484
2484
2485 Wrapper structure for lazily adding two structures without losing much
2485 Wrapper structure for lazily adding two structures without losing much
2486 performance on the __contains__ method
2486 performance on the __contains__ method
2487
2487
2488 If the ascending attribute is set, that means the two structures are
2488 If the ascending attribute is set, that means the two structures are
2489 ordered in either an ascending or descending way. Therefore, we can add
2489 ordered in either an ascending or descending way. Therefore, we can add
2490 them maintaining the order by iterating over both at the same time
2490 them maintaining the order by iterating over both at the same time
2491 """
2491 """
2492 def __init__(self, revs1, revs2, ascending=None):
2492 def __init__(self, revs1, revs2, ascending=None):
2493 self._r1 = revs1
2493 self._r1 = revs1
2494 self._r2 = revs2
2494 self._r2 = revs2
2495 self._iter = None
2495 self._iter = None
2496 self._ascending = ascending
2496 self._ascending = ascending
2497 self._genlist = None
2497 self._genlist = None
2498 self._asclist = None
2498 self._asclist = None
2499
2499
2500 def __len__(self):
2500 def __len__(self):
2501 return len(self._list)
2501 return len(self._list)
2502
2502
2503 def __nonzero__(self):
2503 def __nonzero__(self):
2504 return bool(self._r1) or bool(self._r2)
2504 return bool(self._r1) or bool(self._r2)
2505
2505
2506 @util.propertycache
2506 @util.propertycache
2507 def _list(self):
2507 def _list(self):
2508 if not self._genlist:
2508 if not self._genlist:
2509 self._genlist = baseset(self._iterator())
2509 self._genlist = baseset(self._iterator())
2510 return self._genlist
2510 return self._genlist
2511
2511
2512 def _iterator(self):
2512 def _iterator(self):
2513 """Iterate over both collections without repeating elements
2513 """Iterate over both collections without repeating elements
2514
2514
2515 If the ascending attribute is not set, iterate over the first one and
2515 If the ascending attribute is not set, iterate over the first one and
2516 then over the second one checking for membership on the first one so we
2516 then over the second one checking for membership on the first one so we
2517 dont yield any duplicates.
2517 dont yield any duplicates.
2518
2518
2519 If the ascending attribute is set, iterate over both collections at the
2519 If the ascending attribute is set, iterate over both collections at the
2520 same time, yielding only one value at a time in the given order.
2520 same time, yielding only one value at a time in the given order.
2521 """
2521 """
2522 if self._ascending is None:
2522 if self._ascending is None:
2523 def gen():
2523 def gen():
2524 for r in self._r1:
2524 for r in self._r1:
2525 yield r
2525 yield r
2526 inr1 = self._r1.__contains__
2526 inr1 = self._r1.__contains__
2527 for r in self._r2:
2527 for r in self._r2:
2528 if not inr1(r):
2528 if not inr1(r):
2529 yield r
2529 yield r
2530 gen = gen()
2530 gen = gen()
2531 else:
2531 else:
2532 iter1 = iter(self._r1)
2532 iter1 = iter(self._r1)
2533 iter2 = iter(self._r2)
2533 iter2 = iter(self._r2)
2534 gen = self._iterordered(self._ascending, iter1, iter2)
2534 gen = self._iterordered(self._ascending, iter1, iter2)
2535 return gen
2535 return gen
2536
2536
2537 def __iter__(self):
2537 def __iter__(self):
2538 if self._ascending is None:
2538 if self._ascending is None:
2539 if self._genlist:
2539 if self._genlist:
2540 return iter(self._genlist)
2540 return iter(self._genlist)
2541 return iter(self._iterator())
2541 return iter(self._iterator())
2542 self._trysetasclist()
2542 self._trysetasclist()
2543 if self._ascending:
2543 if self._ascending:
2544 it = self.fastasc
2544 it = self.fastasc
2545 else:
2545 else:
2546 it = self.fastdesc
2546 it = self.fastdesc
2547 if it is None:
2547 if it is None:
2548 # consume the gen and try again
2548 # consume the gen and try again
2549 self._list
2549 self._list
2550 return iter(self)
2550 return iter(self)
2551 return it()
2551 return it()
2552
2552
2553 def _trysetasclist(self):
2553 def _trysetasclist(self):
2554 """populate the _asclist attribut if possible and necessary"""
2554 """populate the _asclist attribut if possible and necessary"""
2555 if self._genlist is not None and self._asclist is None:
2555 if self._genlist is not None and self._asclist is None:
2556 self._asclist = sorted(self._genlist)
2556 self._asclist = sorted(self._genlist)
2557
2557
2558 @property
2558 @property
2559 def fastasc(self):
2559 def fastasc(self):
2560 self._trysetasclist()
2560 self._trysetasclist()
2561 if self._asclist is not None:
2561 if self._asclist is not None:
2562 return self._asclist.__iter__
2562 return self._asclist.__iter__
2563 iter1 = self._r1.fastasc
2563 iter1 = self._r1.fastasc
2564 iter2 = self._r2.fastasc
2564 iter2 = self._r2.fastasc
2565 if None in (iter1, iter2):
2565 if None in (iter1, iter2):
2566 return None
2566 return None
2567 return lambda: self._iterordered(True, iter1(), iter2())
2567 return lambda: self._iterordered(True, iter1(), iter2())
2568
2568
2569 @property
2569 @property
2570 def fastdesc(self):
2570 def fastdesc(self):
2571 self._trysetasclist()
2571 self._trysetasclist()
2572 if self._asclist is not None:
2572 if self._asclist is not None:
2573 return self._asclist.__reversed__
2573 return self._asclist.__reversed__
2574 iter1 = self._r1.fastdesc
2574 iter1 = self._r1.fastdesc
2575 iter2 = self._r2.fastdesc
2575 iter2 = self._r2.fastdesc
2576 if None in (iter1, iter2):
2576 if None in (iter1, iter2):
2577 return None
2577 return None
2578 return lambda: self._iterordered(False, iter1(), iter2())
2578 return lambda: self._iterordered(False, iter1(), iter2())
2579
2579
2580 def _iterordered(self, ascending, iter1, iter2):
2580 def _iterordered(self, ascending, iter1, iter2):
2581 """produce an ordered iteration from two iterators with the same order
2581 """produce an ordered iteration from two iterators with the same order
2582
2582
2583 The ascending is used to indicated the iteration direction.
2583 The ascending is used to indicated the iteration direction.
2584 """
2584 """
2585 choice = max
2585 choice = max
2586 if ascending:
2586 if ascending:
2587 choice = min
2587 choice = min
2588
2588
2589 val1 = None
2589 val1 = None
2590 val2 = None
2590 val2 = None
2591
2591
2592 choice = max
2592 choice = max
2593 if ascending:
2593 if ascending:
2594 choice = min
2594 choice = min
2595 try:
2595 try:
2596 # Consume both iterators in an ordered way until one is
2596 # Consume both iterators in an ordered way until one is
2597 # empty
2597 # empty
2598 while True:
2598 while True:
2599 if val1 is None:
2599 if val1 is None:
2600 val1 = iter1.next()
2600 val1 = iter1.next()
2601 if val2 is None:
2601 if val2 is None:
2602 val2 = iter2.next()
2602 val2 = iter2.next()
2603 next = choice(val1, val2)
2603 next = choice(val1, val2)
2604 yield next
2604 yield next
2605 if val1 == next:
2605 if val1 == next:
2606 val1 = None
2606 val1 = None
2607 if val2 == next:
2607 if val2 == next:
2608 val2 = None
2608 val2 = None
2609 except StopIteration:
2609 except StopIteration:
2610 # Flush any remaining values and consume the other one
2610 # Flush any remaining values and consume the other one
2611 it = iter2
2611 it = iter2
2612 if val1 is not None:
2612 if val1 is not None:
2613 yield val1
2613 yield val1
2614 it = iter1
2614 it = iter1
2615 elif val2 is not None:
2615 elif val2 is not None:
2616 # might have been equality and both are empty
2616 # might have been equality and both are empty
2617 yield val2
2617 yield val2
2618 for val in it:
2618 for val in it:
2619 yield val
2619 yield val
2620
2620
2621 def __contains__(self, x):
2621 def __contains__(self, x):
2622 return x in self._r1 or x in self._r2
2622 return x in self._r1 or x in self._r2
2623
2623
2624 def sort(self, reverse=False):
2624 def sort(self, reverse=False):
2625 """Sort the added set
2625 """Sort the added set
2626
2626
2627 For this we use the cached list with all the generated values and if we
2627 For this we use the cached list with all the generated values and if we
2628 know they are ascending or descending we can sort them in a smart way.
2628 know they are ascending or descending we can sort them in a smart way.
2629 """
2629 """
2630 self._ascending = not reverse
2630 self._ascending = not reverse
2631
2631
2632 def isascending(self):
2632 def isascending(self):
2633 return self._ascending is not None and self._ascending
2633 return self._ascending is not None and self._ascending
2634
2634
2635 def isdescending(self):
2635 def isdescending(self):
2636 return self._ascending is not None and not self._ascending
2636 return self._ascending is not None and not self._ascending
2637
2637
2638 def reverse(self):
2638 def reverse(self):
2639 if self._ascending is None:
2639 if self._ascending is None:
2640 self._list.reverse()
2640 self._list.reverse()
2641 else:
2641 else:
2642 self._ascending = not self._ascending
2642 self._ascending = not self._ascending
2643
2643
2644 def first(self):
2644 def first(self):
2645 if self:
2645 for x in self:
2646 return self._list.first()
2646 return x
2647 return None
2647 return None
2648
2648
2649 def last(self):
2649 def last(self):
2650 if self:
2650 self.reverse()
2651 return self._list.last()
2651 val = self.first()
2652 return None
2652 self.reverse()
2653 return val
2653
2654
2654 class generatorset(abstractsmartset):
2655 class generatorset(abstractsmartset):
2655 """Wrap a generator for lazy iteration
2656 """Wrap a generator for lazy iteration
2656
2657
2657 Wrapper structure for generators that provides lazy membership and can
2658 Wrapper structure for generators that provides lazy membership and can
2658 be iterated more than once.
2659 be iterated more than once.
2659 When asked for membership it generates values until either it finds the
2660 When asked for membership it generates values until either it finds the
2660 requested one or has gone through all the elements in the generator
2661 requested one or has gone through all the elements in the generator
2661 """
2662 """
2662 def __init__(self, gen, iterasc=None):
2663 def __init__(self, gen, iterasc=None):
2663 """
2664 """
2664 gen: a generator producing the values for the generatorset.
2665 gen: a generator producing the values for the generatorset.
2665 """
2666 """
2666 self._gen = gen
2667 self._gen = gen
2667 self._asclist = None
2668 self._asclist = None
2668 self._cache = {}
2669 self._cache = {}
2669 self._genlist = []
2670 self._genlist = []
2670 self._finished = False
2671 self._finished = False
2671 self._ascending = True
2672 self._ascending = True
2672 if iterasc is not None:
2673 if iterasc is not None:
2673 if iterasc:
2674 if iterasc:
2674 self.fastasc = self._iterator
2675 self.fastasc = self._iterator
2675 self.__contains__ = self._asccontains
2676 self.__contains__ = self._asccontains
2676 else:
2677 else:
2677 self.fastdesc = self._iterator
2678 self.fastdesc = self._iterator
2678 self.__contains__ = self._desccontains
2679 self.__contains__ = self._desccontains
2679
2680
2680 def __nonzero__(self):
2681 def __nonzero__(self):
2681 for r in self:
2682 for r in self:
2682 return True
2683 return True
2683 return False
2684 return False
2684
2685
2685 def __contains__(self, x):
2686 def __contains__(self, x):
2686 if x in self._cache:
2687 if x in self._cache:
2687 return self._cache[x]
2688 return self._cache[x]
2688
2689
2689 # Use new values only, as existing values would be cached.
2690 # Use new values only, as existing values would be cached.
2690 for l in self._consumegen():
2691 for l in self._consumegen():
2691 if l == x:
2692 if l == x:
2692 return True
2693 return True
2693
2694
2694 self._cache[x] = False
2695 self._cache[x] = False
2695 return False
2696 return False
2696
2697
2697 def _asccontains(self, x):
2698 def _asccontains(self, x):
2698 """version of contains optimised for ascending generator"""
2699 """version of contains optimised for ascending generator"""
2699 if x in self._cache:
2700 if x in self._cache:
2700 return self._cache[x]
2701 return self._cache[x]
2701
2702
2702 # Use new values only, as existing values would be cached.
2703 # Use new values only, as existing values would be cached.
2703 for l in self._consumegen():
2704 for l in self._consumegen():
2704 if l == x:
2705 if l == x:
2705 return True
2706 return True
2706 if l > x:
2707 if l > x:
2707 break
2708 break
2708
2709
2709 self._cache[x] = False
2710 self._cache[x] = False
2710 return False
2711 return False
2711
2712
2712 def _desccontains(self, x):
2713 def _desccontains(self, x):
2713 """version of contains optimised for descending generator"""
2714 """version of contains optimised for descending generator"""
2714 if x in self._cache:
2715 if x in self._cache:
2715 return self._cache[x]
2716 return self._cache[x]
2716
2717
2717 # Use new values only, as existing values would be cached.
2718 # Use new values only, as existing values would be cached.
2718 for l in self._consumegen():
2719 for l in self._consumegen():
2719 if l == x:
2720 if l == x:
2720 return True
2721 return True
2721 if l < x:
2722 if l < x:
2722 break
2723 break
2723
2724
2724 self._cache[x] = False
2725 self._cache[x] = False
2725 return False
2726 return False
2726
2727
2727 def __iter__(self):
2728 def __iter__(self):
2728 if self._ascending:
2729 if self._ascending:
2729 it = self.fastasc
2730 it = self.fastasc
2730 else:
2731 else:
2731 it = self.fastdesc
2732 it = self.fastdesc
2732 if it is not None:
2733 if it is not None:
2733 return it()
2734 return it()
2734 # we need to consume the iterator
2735 # we need to consume the iterator
2735 for x in self._consumegen():
2736 for x in self._consumegen():
2736 pass
2737 pass
2737 # recall the same code
2738 # recall the same code
2738 return iter(self)
2739 return iter(self)
2739
2740
2740 def _iterator(self):
2741 def _iterator(self):
2741 if self._finished:
2742 if self._finished:
2742 return iter(self._genlist)
2743 return iter(self._genlist)
2743
2744
2744 # We have to use this complex iteration strategy to allow multiple
2745 # We have to use this complex iteration strategy to allow multiple
2745 # iterations at the same time. We need to be able to catch revision
2746 # iterations at the same time. We need to be able to catch revision
2746 # removed from `consumegen` and added to genlist in another instance.
2747 # removed from `consumegen` and added to genlist in another instance.
2747 #
2748 #
2748 # Getting rid of it would provide an about 15% speed up on this
2749 # Getting rid of it would provide an about 15% speed up on this
2749 # iteration.
2750 # iteration.
2750 genlist = self._genlist
2751 genlist = self._genlist
2751 nextrev = self._consumegen().next
2752 nextrev = self._consumegen().next
2752 _len = len # cache global lookup
2753 _len = len # cache global lookup
2753 def gen():
2754 def gen():
2754 i = 0
2755 i = 0
2755 while True:
2756 while True:
2756 if i < _len(genlist):
2757 if i < _len(genlist):
2757 yield genlist[i]
2758 yield genlist[i]
2758 else:
2759 else:
2759 yield nextrev()
2760 yield nextrev()
2760 i += 1
2761 i += 1
2761 return gen()
2762 return gen()
2762
2763
2763 def _consumegen(self):
2764 def _consumegen(self):
2764 cache = self._cache
2765 cache = self._cache
2765 genlist = self._genlist.append
2766 genlist = self._genlist.append
2766 for item in self._gen:
2767 for item in self._gen:
2767 cache[item] = True
2768 cache[item] = True
2768 genlist(item)
2769 genlist(item)
2769 yield item
2770 yield item
2770 if not self._finished:
2771 if not self._finished:
2771 self._finished = True
2772 self._finished = True
2772 asc = self._genlist[:]
2773 asc = self._genlist[:]
2773 asc.sort()
2774 asc.sort()
2774 self._asclist = asc
2775 self._asclist = asc
2775 self.fastasc = asc.__iter__
2776 self.fastasc = asc.__iter__
2776 self.fastdesc = asc.__reversed__
2777 self.fastdesc = asc.__reversed__
2777
2778
2778 def __len__(self):
2779 def __len__(self):
2779 for x in self._consumegen():
2780 for x in self._consumegen():
2780 pass
2781 pass
2781 return len(self._genlist)
2782 return len(self._genlist)
2782
2783
2783 def sort(self, reverse=False):
2784 def sort(self, reverse=False):
2784 self._ascending = not reverse
2785 self._ascending = not reverse
2785
2786
2786 def reverse(self):
2787 def reverse(self):
2787 self._ascending = not self._ascending
2788 self._ascending = not self._ascending
2788
2789
2789 def isascending(self):
2790 def isascending(self):
2790 return self._ascending
2791 return self._ascending
2791
2792
2792 def isdescending(self):
2793 def isdescending(self):
2793 return not self._ascending
2794 return not self._ascending
2794
2795
2795 def first(self):
2796 def first(self):
2796 if self._ascending:
2797 if self._ascending:
2797 it = self.fastasc
2798 it = self.fastasc
2798 else:
2799 else:
2799 it = self.fastdesc
2800 it = self.fastdesc
2800 if it is None:
2801 if it is None:
2801 # we need to consume all and try again
2802 # we need to consume all and try again
2802 for x in self._consumegen():
2803 for x in self._consumegen():
2803 pass
2804 pass
2804 return self.first()
2805 return self.first()
2805 if self:
2806 if self:
2806 return it.next()
2807 return it.next()
2807 return None
2808 return None
2808
2809
2809 def last(self):
2810 def last(self):
2810 if self._ascending:
2811 if self._ascending:
2811 it = self.fastdesc
2812 it = self.fastdesc
2812 else:
2813 else:
2813 it = self.fastasc
2814 it = self.fastasc
2814 if it is None:
2815 if it is None:
2815 # we need to consume all and try again
2816 # we need to consume all and try again
2816 for x in self._consumegen():
2817 for x in self._consumegen():
2817 pass
2818 pass
2818 return self.first()
2819 return self.first()
2819 if self:
2820 if self:
2820 return it.next()
2821 return it.next()
2821 return None
2822 return None
2822
2823
2823 def spanset(repo, start=None, end=None):
2824 def spanset(repo, start=None, end=None):
2824 """factory function to dispatch between fullreposet and actual spanset
2825 """factory function to dispatch between fullreposet and actual spanset
2825
2826
2826 Feel free to update all spanset call sites and kill this function at some
2827 Feel free to update all spanset call sites and kill this function at some
2827 point.
2828 point.
2828 """
2829 """
2829 if start is None and end is None:
2830 if start is None and end is None:
2830 return fullreposet(repo)
2831 return fullreposet(repo)
2831 return _spanset(repo, start, end)
2832 return _spanset(repo, start, end)
2832
2833
2833
2834
2834 class _spanset(abstractsmartset):
2835 class _spanset(abstractsmartset):
2835 """Duck type for baseset class which represents a range of revisions and
2836 """Duck type for baseset class which represents a range of revisions and
2836 can work lazily and without having all the range in memory
2837 can work lazily and without having all the range in memory
2837
2838
2838 Note that spanset(x, y) behave almost like xrange(x, y) except for two
2839 Note that spanset(x, y) behave almost like xrange(x, y) except for two
2839 notable points:
2840 notable points:
2840 - when x < y it will be automatically descending,
2841 - when x < y it will be automatically descending,
2841 - revision filtered with this repoview will be skipped.
2842 - revision filtered with this repoview will be skipped.
2842
2843
2843 """
2844 """
2844 def __init__(self, repo, start=0, end=None):
2845 def __init__(self, repo, start=0, end=None):
2845 """
2846 """
2846 start: first revision included the set
2847 start: first revision included the set
2847 (default to 0)
2848 (default to 0)
2848 end: first revision excluded (last+1)
2849 end: first revision excluded (last+1)
2849 (default to len(repo)
2850 (default to len(repo)
2850
2851
2851 Spanset will be descending if `end` < `start`.
2852 Spanset will be descending if `end` < `start`.
2852 """
2853 """
2853 if end is None:
2854 if end is None:
2854 end = len(repo)
2855 end = len(repo)
2855 self._ascending = start <= end
2856 self._ascending = start <= end
2856 if not self._ascending:
2857 if not self._ascending:
2857 start, end = end + 1, start +1
2858 start, end = end + 1, start +1
2858 self._start = start
2859 self._start = start
2859 self._end = end
2860 self._end = end
2860 self._hiddenrevs = repo.changelog.filteredrevs
2861 self._hiddenrevs = repo.changelog.filteredrevs
2861
2862
2862 def sort(self, reverse=False):
2863 def sort(self, reverse=False):
2863 self._ascending = not reverse
2864 self._ascending = not reverse
2864
2865
2865 def reverse(self):
2866 def reverse(self):
2866 self._ascending = not self._ascending
2867 self._ascending = not self._ascending
2867
2868
2868 def _iterfilter(self, iterrange):
2869 def _iterfilter(self, iterrange):
2869 s = self._hiddenrevs
2870 s = self._hiddenrevs
2870 for r in iterrange:
2871 for r in iterrange:
2871 if r not in s:
2872 if r not in s:
2872 yield r
2873 yield r
2873
2874
2874 def __iter__(self):
2875 def __iter__(self):
2875 if self._ascending:
2876 if self._ascending:
2876 return self.fastasc()
2877 return self.fastasc()
2877 else:
2878 else:
2878 return self.fastdesc()
2879 return self.fastdesc()
2879
2880
2880 def fastasc(self):
2881 def fastasc(self):
2881 iterrange = xrange(self._start, self._end)
2882 iterrange = xrange(self._start, self._end)
2882 if self._hiddenrevs:
2883 if self._hiddenrevs:
2883 return self._iterfilter(iterrange)
2884 return self._iterfilter(iterrange)
2884 return iter(iterrange)
2885 return iter(iterrange)
2885
2886
2886 def fastdesc(self):
2887 def fastdesc(self):
2887 iterrange = xrange(self._end - 1, self._start - 1, -1)
2888 iterrange = xrange(self._end - 1, self._start - 1, -1)
2888 if self._hiddenrevs:
2889 if self._hiddenrevs:
2889 return self._iterfilter(iterrange)
2890 return self._iterfilter(iterrange)
2890 return iter(iterrange)
2891 return iter(iterrange)
2891
2892
2892 def __contains__(self, rev):
2893 def __contains__(self, rev):
2893 hidden = self._hiddenrevs
2894 hidden = self._hiddenrevs
2894 return ((self._start <= rev < self._end)
2895 return ((self._start <= rev < self._end)
2895 and not (hidden and rev in hidden))
2896 and not (hidden and rev in hidden))
2896
2897
2897 def __nonzero__(self):
2898 def __nonzero__(self):
2898 for r in self:
2899 for r in self:
2899 return True
2900 return True
2900 return False
2901 return False
2901
2902
2902 def __len__(self):
2903 def __len__(self):
2903 if not self._hiddenrevs:
2904 if not self._hiddenrevs:
2904 return abs(self._end - self._start)
2905 return abs(self._end - self._start)
2905 else:
2906 else:
2906 count = 0
2907 count = 0
2907 start = self._start
2908 start = self._start
2908 end = self._end
2909 end = self._end
2909 for rev in self._hiddenrevs:
2910 for rev in self._hiddenrevs:
2910 if (end < rev <= start) or (start <= rev < end):
2911 if (end < rev <= start) or (start <= rev < end):
2911 count += 1
2912 count += 1
2912 return abs(self._end - self._start) - count
2913 return abs(self._end - self._start) - count
2913
2914
2914 def isascending(self):
2915 def isascending(self):
2915 return self._start <= self._end
2916 return self._start <= self._end
2916
2917
2917 def isdescending(self):
2918 def isdescending(self):
2918 return self._start >= self._end
2919 return self._start >= self._end
2919
2920
2920 def first(self):
2921 def first(self):
2921 if self._ascending:
2922 if self._ascending:
2922 it = self.fastasc
2923 it = self.fastasc
2923 else:
2924 else:
2924 it = self.fastdesc
2925 it = self.fastdesc
2925 for x in it():
2926 for x in it():
2926 return x
2927 return x
2927 return None
2928 return None
2928
2929
2929 def last(self):
2930 def last(self):
2930 if self._ascending:
2931 if self._ascending:
2931 it = self.fastdesc
2932 it = self.fastdesc
2932 else:
2933 else:
2933 it = self.fastasc
2934 it = self.fastasc
2934 for x in it():
2935 for x in it():
2935 return x
2936 return x
2936 return None
2937 return None
2937
2938
2938 class fullreposet(_spanset):
2939 class fullreposet(_spanset):
2939 """a set containing all revisions in the repo
2940 """a set containing all revisions in the repo
2940
2941
2941 This class exists to host special optimisation.
2942 This class exists to host special optimisation.
2942 """
2943 """
2943
2944
2944 def __init__(self, repo):
2945 def __init__(self, repo):
2945 super(fullreposet, self).__init__(repo)
2946 super(fullreposet, self).__init__(repo)
2946
2947
2947 def __and__(self, other):
2948 def __and__(self, other):
2948 """fullrepo & other -> other
2949 """fullrepo & other -> other
2949
2950
2950 As self contains the whole repo, all of the other set should also be in
2951 As self contains the whole repo, all of the other set should also be in
2951 self. Therefor `self & other = other`.
2952 self. Therefor `self & other = other`.
2952
2953
2953 This boldly assumes the other contains valid revs only.
2954 This boldly assumes the other contains valid revs only.
2954 """
2955 """
2955 # other not a smartset, make is so
2956 # other not a smartset, make is so
2956 if not util.safehasattr(other, 'isascending'):
2957 if not util.safehasattr(other, 'isascending'):
2957 # filter out hidden revision
2958 # filter out hidden revision
2958 # (this boldly assumes all smartset are pure)
2959 # (this boldly assumes all smartset are pure)
2959 #
2960 #
2960 # `other` was used with "&", let's assume this is a set like
2961 # `other` was used with "&", let's assume this is a set like
2961 # object.
2962 # object.
2962 other = baseset(other - self._hiddenrevs)
2963 other = baseset(other - self._hiddenrevs)
2963
2964
2964 if self.isascending():
2965 if self.isascending():
2965 other.sort()
2966 other.sort()
2966 else:
2967 else:
2967 other.sort(reverse)
2968 other.sort(reverse)
2968 return other
2969 return other
2969
2970
2970 # tell hggettext to extract docstrings from these functions:
2971 # tell hggettext to extract docstrings from these functions:
2971 i18nfunctions = symbols.values()
2972 i18nfunctions = symbols.values()
@@ -1,1583 +1,1599
1 $ checkundo()
1 $ checkundo()
2 > {
2 > {
3 > if [ -f .hg/store/undo ]; then
3 > if [ -f .hg/store/undo ]; then
4 > echo ".hg/store/undo still exists after $1"
4 > echo ".hg/store/undo still exists after $1"
5 > fi
5 > fi
6 > }
6 > }
7
7
8 $ echo "[extensions]" >> $HGRCPATH
8 $ echo "[extensions]" >> $HGRCPATH
9 $ echo "mq=" >> $HGRCPATH
9 $ echo "mq=" >> $HGRCPATH
10
10
11 $ echo "[mq]" >> $HGRCPATH
11 $ echo "[mq]" >> $HGRCPATH
12 $ echo "plain=true" >> $HGRCPATH
12 $ echo "plain=true" >> $HGRCPATH
13
13
14
14
15 help
15 help
16
16
17 $ hg help mq
17 $ hg help mq
18 mq extension - manage a stack of patches
18 mq extension - manage a stack of patches
19
19
20 This extension lets you work with a stack of patches in a Mercurial
20 This extension lets you work with a stack of patches in a Mercurial
21 repository. It manages two stacks of patches - all known patches, and applied
21 repository. It manages two stacks of patches - all known patches, and applied
22 patches (subset of known patches).
22 patches (subset of known patches).
23
23
24 Known patches are represented as patch files in the .hg/patches directory.
24 Known patches are represented as patch files in the .hg/patches directory.
25 Applied patches are both patch files and changesets.
25 Applied patches are both patch files and changesets.
26
26
27 Common tasks (use "hg help command" for more details):
27 Common tasks (use "hg help command" for more details):
28
28
29 create new patch qnew
29 create new patch qnew
30 import existing patch qimport
30 import existing patch qimport
31
31
32 print patch series qseries
32 print patch series qseries
33 print applied patches qapplied
33 print applied patches qapplied
34
34
35 add known patch to applied stack qpush
35 add known patch to applied stack qpush
36 remove patch from applied stack qpop
36 remove patch from applied stack qpop
37 refresh contents of top applied patch qrefresh
37 refresh contents of top applied patch qrefresh
38
38
39 By default, mq will automatically use git patches when required to avoid
39 By default, mq will automatically use git patches when required to avoid
40 losing file mode changes, copy records, binary files or empty files creations
40 losing file mode changes, copy records, binary files or empty files creations
41 or deletions. This behaviour can be configured with:
41 or deletions. This behaviour can be configured with:
42
42
43 [mq]
43 [mq]
44 git = auto/keep/yes/no
44 git = auto/keep/yes/no
45
45
46 If set to 'keep', mq will obey the [diff] section configuration while
46 If set to 'keep', mq will obey the [diff] section configuration while
47 preserving existing git patches upon qrefresh. If set to 'yes' or 'no', mq
47 preserving existing git patches upon qrefresh. If set to 'yes' or 'no', mq
48 will override the [diff] section and always generate git or regular patches,
48 will override the [diff] section and always generate git or regular patches,
49 possibly losing data in the second case.
49 possibly losing data in the second case.
50
50
51 It may be desirable for mq changesets to be kept in the secret phase (see "hg
51 It may be desirable for mq changesets to be kept in the secret phase (see "hg
52 help phases"), which can be enabled with the following setting:
52 help phases"), which can be enabled with the following setting:
53
53
54 [mq]
54 [mq]
55 secret = True
55 secret = True
56
56
57 You will by default be managing a patch queue named "patches". You can create
57 You will by default be managing a patch queue named "patches". You can create
58 other, independent patch queues with the "hg qqueue" command.
58 other, independent patch queues with the "hg qqueue" command.
59
59
60 If the working directory contains uncommitted files, qpush, qpop and qgoto
60 If the working directory contains uncommitted files, qpush, qpop and qgoto
61 abort immediately. If -f/--force is used, the changes are discarded. Setting:
61 abort immediately. If -f/--force is used, the changes are discarded. Setting:
62
62
63 [mq]
63 [mq]
64 keepchanges = True
64 keepchanges = True
65
65
66 make them behave as if --keep-changes were passed, and non-conflicting local
66 make them behave as if --keep-changes were passed, and non-conflicting local
67 changes will be tolerated and preserved. If incompatible options such as
67 changes will be tolerated and preserved. If incompatible options such as
68 -f/--force or --exact are passed, this setting is ignored.
68 -f/--force or --exact are passed, this setting is ignored.
69
69
70 This extension used to provide a strip command. This command now lives in the
70 This extension used to provide a strip command. This command now lives in the
71 strip extension.
71 strip extension.
72
72
73 list of commands:
73 list of commands:
74
74
75 qapplied print the patches already applied
75 qapplied print the patches already applied
76 qclone clone main and patch repository at same time
76 qclone clone main and patch repository at same time
77 qdelete remove patches from queue
77 qdelete remove patches from queue
78 qdiff diff of the current patch and subsequent modifications
78 qdiff diff of the current patch and subsequent modifications
79 qfinish move applied patches into repository history
79 qfinish move applied patches into repository history
80 qfold fold the named patches into the current patch
80 qfold fold the named patches into the current patch
81 qgoto push or pop patches until named patch is at top of stack
81 qgoto push or pop patches until named patch is at top of stack
82 qguard set or print guards for a patch
82 qguard set or print guards for a patch
83 qheader print the header of the topmost or specified patch
83 qheader print the header of the topmost or specified patch
84 qimport import a patch or existing changeset
84 qimport import a patch or existing changeset
85 qnew create a new patch
85 qnew create a new patch
86 qnext print the name of the next pushable patch
86 qnext print the name of the next pushable patch
87 qpop pop the current patch off the stack
87 qpop pop the current patch off the stack
88 qprev print the name of the preceding applied patch
88 qprev print the name of the preceding applied patch
89 qpush push the next patch onto the stack
89 qpush push the next patch onto the stack
90 qqueue manage multiple patch queues
90 qqueue manage multiple patch queues
91 qrefresh update the current patch
91 qrefresh update the current patch
92 qrename rename a patch
92 qrename rename a patch
93 qselect set or print guarded patches to push
93 qselect set or print guarded patches to push
94 qseries print the entire series file
94 qseries print the entire series file
95 qtop print the name of the current patch
95 qtop print the name of the current patch
96 qunapplied print the patches not yet applied
96 qunapplied print the patches not yet applied
97
97
98 (use "hg help -v mq" to show built-in aliases and global options)
98 (use "hg help -v mq" to show built-in aliases and global options)
99
99
100 $ hg init a
100 $ hg init a
101 $ cd a
101 $ cd a
102 $ echo a > a
102 $ echo a > a
103 $ hg ci -Ama
103 $ hg ci -Ama
104 adding a
104 adding a
105
105
106 $ hg clone . ../k
106 $ hg clone . ../k
107 updating to branch default
107 updating to branch default
108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109
109
110 $ mkdir b
110 $ mkdir b
111 $ echo z > b/z
111 $ echo z > b/z
112 $ hg ci -Ama
112 $ hg ci -Ama
113 adding b/z
113 adding b/z
114
114
115
115
116 qinit
116 qinit
117
117
118 $ hg qinit
118 $ hg qinit
119
119
120 $ cd ..
120 $ cd ..
121 $ hg init b
121 $ hg init b
122
122
123
123
124 -R qinit
124 -R qinit
125
125
126 $ hg -R b qinit
126 $ hg -R b qinit
127
127
128 $ hg init c
128 $ hg init c
129
129
130
130
131 qinit -c
131 qinit -c
132
132
133 $ hg --cwd c qinit -c
133 $ hg --cwd c qinit -c
134 $ hg -R c/.hg/patches st
134 $ hg -R c/.hg/patches st
135 A .hgignore
135 A .hgignore
136 A series
136 A series
137
137
138
138
139 qinit; qinit -c
139 qinit; qinit -c
140
140
141 $ hg init d
141 $ hg init d
142 $ cd d
142 $ cd d
143 $ hg qinit
143 $ hg qinit
144 $ hg qinit -c
144 $ hg qinit -c
145
145
146 qinit -c should create both files if they don't exist
146 qinit -c should create both files if they don't exist
147
147
148 $ cat .hg/patches/.hgignore
148 $ cat .hg/patches/.hgignore
149 ^\.hg
149 ^\.hg
150 ^\.mq
150 ^\.mq
151 syntax: glob
151 syntax: glob
152 status
152 status
153 guards
153 guards
154 $ cat .hg/patches/series
154 $ cat .hg/patches/series
155 $ hg qinit -c
155 $ hg qinit -c
156 abort: repository $TESTTMP/d/.hg/patches already exists! (glob)
156 abort: repository $TESTTMP/d/.hg/patches already exists! (glob)
157 [255]
157 [255]
158 $ cd ..
158 $ cd ..
159
159
160 $ echo '% qinit; <stuff>; qinit -c'
160 $ echo '% qinit; <stuff>; qinit -c'
161 % qinit; <stuff>; qinit -c
161 % qinit; <stuff>; qinit -c
162 $ hg init e
162 $ hg init e
163 $ cd e
163 $ cd e
164 $ hg qnew A
164 $ hg qnew A
165 $ checkundo qnew
165 $ checkundo qnew
166 $ echo foo > foo
166 $ echo foo > foo
167 $ hg phase -r qbase
167 $ hg phase -r qbase
168 0: draft
168 0: draft
169 $ hg add foo
169 $ hg add foo
170 $ hg qrefresh
170 $ hg qrefresh
171 $ hg phase -r qbase
171 $ hg phase -r qbase
172 0: draft
172 0: draft
173 $ hg qnew B
173 $ hg qnew B
174 $ echo >> foo
174 $ echo >> foo
175 $ hg qrefresh
175 $ hg qrefresh
176 $ echo status >> .hg/patches/.hgignore
176 $ echo status >> .hg/patches/.hgignore
177 $ echo bleh >> .hg/patches/.hgignore
177 $ echo bleh >> .hg/patches/.hgignore
178 $ hg qinit -c
178 $ hg qinit -c
179 adding .hg/patches/A (glob)
179 adding .hg/patches/A (glob)
180 adding .hg/patches/B (glob)
180 adding .hg/patches/B (glob)
181 $ hg -R .hg/patches status
181 $ hg -R .hg/patches status
182 A .hgignore
182 A .hgignore
183 A A
183 A A
184 A B
184 A B
185 A series
185 A series
186
186
187 qinit -c shouldn't touch these files if they already exist
187 qinit -c shouldn't touch these files if they already exist
188
188
189 $ cat .hg/patches/.hgignore
189 $ cat .hg/patches/.hgignore
190 status
190 status
191 bleh
191 bleh
192 $ cat .hg/patches/series
192 $ cat .hg/patches/series
193 A
193 A
194 B
194 B
195
195
196 add an untracked file
196 add an untracked file
197
197
198 $ echo >> .hg/patches/flaf
198 $ echo >> .hg/patches/flaf
199
199
200 status --mq with color (issue2096)
200 status --mq with color (issue2096)
201
201
202 $ hg status --mq --config extensions.color= --config color.mode=ansi --color=always
202 $ hg status --mq --config extensions.color= --config color.mode=ansi --color=always
203 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1m.hgignore\x1b[0m (esc)
203 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1m.hgignore\x1b[0m (esc)
204 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mA\x1b[0m (esc)
204 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mA\x1b[0m (esc)
205 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mB\x1b[0m (esc)
205 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mB\x1b[0m (esc)
206 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mseries\x1b[0m (esc)
206 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mseries\x1b[0m (esc)
207 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mflaf\x1b[0m (esc)
207 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mflaf\x1b[0m (esc)
208
208
209 try the --mq option on a command provided by an extension
209 try the --mq option on a command provided by an extension
210
210
211 $ hg purge --mq --verbose --config extensions.purge=
211 $ hg purge --mq --verbose --config extensions.purge=
212 removing file flaf
212 removing file flaf
213
213
214 $ cd ..
214 $ cd ..
215
215
216 #if no-outer-repo
216 #if no-outer-repo
217
217
218 init --mq without repo
218 init --mq without repo
219
219
220 $ mkdir f
220 $ mkdir f
221 $ cd f
221 $ cd f
222 $ hg init --mq
222 $ hg init --mq
223 abort: there is no Mercurial repository here (.hg not found)
223 abort: there is no Mercurial repository here (.hg not found)
224 [255]
224 [255]
225 $ cd ..
225 $ cd ..
226
226
227 #endif
227 #endif
228
228
229 init --mq with repo path
229 init --mq with repo path
230
230
231 $ hg init g
231 $ hg init g
232 $ hg init --mq g
232 $ hg init --mq g
233 $ test -d g/.hg/patches/.hg
233 $ test -d g/.hg/patches/.hg
234
234
235 init --mq with nonexistent directory
235 init --mq with nonexistent directory
236
236
237 $ hg init --mq nonexistentdir
237 $ hg init --mq nonexistentdir
238 abort: repository nonexistentdir not found!
238 abort: repository nonexistentdir not found!
239 [255]
239 [255]
240
240
241
241
242 init --mq with bundle (non "local")
242 init --mq with bundle (non "local")
243
243
244 $ hg -R a bundle --all a.bundle >/dev/null
244 $ hg -R a bundle --all a.bundle >/dev/null
245 $ hg init --mq a.bundle
245 $ hg init --mq a.bundle
246 abort: only a local queue repository may be initialized
246 abort: only a local queue repository may be initialized
247 [255]
247 [255]
248
248
249 $ cd a
249 $ cd a
250
250
251 $ hg qnew -m 'foo bar' test.patch
251 $ hg qnew -m 'foo bar' test.patch
252
252
253 $ echo '# comment' > .hg/patches/series.tmp
253 $ echo '# comment' > .hg/patches/series.tmp
254 $ echo >> .hg/patches/series.tmp # empty line
254 $ echo >> .hg/patches/series.tmp # empty line
255 $ cat .hg/patches/series >> .hg/patches/series.tmp
255 $ cat .hg/patches/series >> .hg/patches/series.tmp
256 $ mv .hg/patches/series.tmp .hg/patches/series
256 $ mv .hg/patches/series.tmp .hg/patches/series
257
257
258
258
259 qrefresh
259 qrefresh
260
260
261 $ echo a >> a
261 $ echo a >> a
262 $ hg qrefresh
262 $ hg qrefresh
263 $ cat .hg/patches/test.patch
263 $ cat .hg/patches/test.patch
264 foo bar
264 foo bar
265
265
266 diff -r [a-f0-9]* a (re)
266 diff -r [a-f0-9]* a (re)
267 --- a/a\t(?P<date>.*) (re)
267 --- a/a\t(?P<date>.*) (re)
268 \+\+\+ b/a\t(?P<date2>.*) (re)
268 \+\+\+ b/a\t(?P<date2>.*) (re)
269 @@ -1,1 +1,2 @@
269 @@ -1,1 +1,2 @@
270 a
270 a
271 +a
271 +a
272
272
273 empty qrefresh
273 empty qrefresh
274
274
275 $ hg qrefresh -X a
275 $ hg qrefresh -X a
276
276
277 revision:
277 revision:
278
278
279 $ hg diff -r -2 -r -1
279 $ hg diff -r -2 -r -1
280
280
281 patch:
281 patch:
282
282
283 $ cat .hg/patches/test.patch
283 $ cat .hg/patches/test.patch
284 foo bar
284 foo bar
285
285
286
286
287 working dir diff:
287 working dir diff:
288
288
289 $ hg diff --nodates -q
289 $ hg diff --nodates -q
290 --- a/a
290 --- a/a
291 +++ b/a
291 +++ b/a
292 @@ -1,1 +1,2 @@
292 @@ -1,1 +1,2 @@
293 a
293 a
294 +a
294 +a
295
295
296 restore things
296 restore things
297
297
298 $ hg qrefresh
298 $ hg qrefresh
299 $ checkundo qrefresh
299 $ checkundo qrefresh
300
300
301
301
302 qpop
302 qpop
303
303
304 $ hg qpop
304 $ hg qpop
305 popping test.patch
305 popping test.patch
306 patch queue now empty
306 patch queue now empty
307 $ checkundo qpop
307 $ checkundo qpop
308
308
309
309
310 qpush with dump of tag cache
310 qpush with dump of tag cache
311 Dump the tag cache to ensure that it has exactly one head after qpush.
311 Dump the tag cache to ensure that it has exactly one head after qpush.
312
312
313 $ rm -f .hg/cache/tags
313 $ rm -f .hg/cache/tags
314 $ hg tags > /dev/null
314 $ hg tags > /dev/null
315
315
316 .hg/cache/tags (pre qpush):
316 .hg/cache/tags (pre qpush):
317
317
318 $ cat .hg/cache/tags
318 $ cat .hg/cache/tags
319 1 [\da-f]{40} (re)
319 1 [\da-f]{40} (re)
320
320
321 $ hg qpush
321 $ hg qpush
322 applying test.patch
322 applying test.patch
323 now at: test.patch
323 now at: test.patch
324 $ hg phase -r qbase
324 $ hg phase -r qbase
325 2: draft
325 2: draft
326 $ hg tags > /dev/null
326 $ hg tags > /dev/null
327
327
328 .hg/cache/tags (post qpush):
328 .hg/cache/tags (post qpush):
329
329
330 $ cat .hg/cache/tags
330 $ cat .hg/cache/tags
331 2 [\da-f]{40} (re)
331 2 [\da-f]{40} (re)
332
332
333 $ checkundo qpush
333 $ checkundo qpush
334 $ cd ..
334 $ cd ..
335
335
336
336
337 pop/push outside repo
337 pop/push outside repo
338 $ hg -R a qpop
338 $ hg -R a qpop
339 popping test.patch
339 popping test.patch
340 patch queue now empty
340 patch queue now empty
341 $ hg -R a qpush
341 $ hg -R a qpush
342 applying test.patch
342 applying test.patch
343 now at: test.patch
343 now at: test.patch
344
344
345 $ cd a
345 $ cd a
346 $ hg qnew test2.patch
346 $ hg qnew test2.patch
347
347
348 qrefresh in subdir
348 qrefresh in subdir
349
349
350 $ cd b
350 $ cd b
351 $ echo a > a
351 $ echo a > a
352 $ hg add a
352 $ hg add a
353 $ hg qrefresh
353 $ hg qrefresh
354
354
355 pop/push -a in subdir
355 pop/push -a in subdir
356
356
357 $ hg qpop -a
357 $ hg qpop -a
358 popping test2.patch
358 popping test2.patch
359 popping test.patch
359 popping test.patch
360 patch queue now empty
360 patch queue now empty
361 $ hg --traceback qpush -a
361 $ hg --traceback qpush -a
362 applying test.patch
362 applying test.patch
363 applying test2.patch
363 applying test2.patch
364 now at: test2.patch
364 now at: test2.patch
365
365
366
366
367 setting columns & formatted tests truncating (issue1912)
367 setting columns & formatted tests truncating (issue1912)
368
368
369 $ COLUMNS=4 hg qseries --config ui.formatted=true
369 $ COLUMNS=4 hg qseries --config ui.formatted=true
370 test.patch
370 test.patch
371 test2.patch
371 test2.patch
372 $ COLUMNS=20 hg qseries --config ui.formatted=true -vs
372 $ COLUMNS=20 hg qseries --config ui.formatted=true -vs
373 0 A test.patch: f...
373 0 A test.patch: f...
374 1 A test2.patch:
374 1 A test2.patch:
375 $ hg qpop
375 $ hg qpop
376 popping test2.patch
376 popping test2.patch
377 now at: test.patch
377 now at: test.patch
378 $ hg qseries -vs
378 $ hg qseries -vs
379 0 A test.patch: foo bar
379 0 A test.patch: foo bar
380 1 U test2.patch:
380 1 U test2.patch:
381 $ hg sum | grep mq
381 $ hg sum | grep mq
382 mq: 1 applied, 1 unapplied
382 mq: 1 applied, 1 unapplied
383 $ hg qpush
383 $ hg qpush
384 applying test2.patch
384 applying test2.patch
385 now at: test2.patch
385 now at: test2.patch
386 $ hg sum | grep mq
386 $ hg sum | grep mq
387 mq: 2 applied
387 mq: 2 applied
388 $ hg qapplied
388 $ hg qapplied
389 test.patch
389 test.patch
390 test2.patch
390 test2.patch
391 $ hg qtop
391 $ hg qtop
392 test2.patch
392 test2.patch
393
393
394
394
395 prev
395 prev
396
396
397 $ hg qapp -1
397 $ hg qapp -1
398 test.patch
398 test.patch
399
399
400 next
400 next
401
401
402 $ hg qunapp -1
402 $ hg qunapp -1
403 all patches applied
403 all patches applied
404 [1]
404 [1]
405
405
406 $ hg qpop
406 $ hg qpop
407 popping test2.patch
407 popping test2.patch
408 now at: test.patch
408 now at: test.patch
409
409
410 commit should fail
410 commit should fail
411
411
412 $ hg commit
412 $ hg commit
413 abort: cannot commit over an applied mq patch
413 abort: cannot commit over an applied mq patch
414 [255]
414 [255]
415
415
416 push should fail if draft
416 push should fail if draft
417
417
418 $ hg push ../../k
418 $ hg push ../../k
419 pushing to ../../k
419 pushing to ../../k
420 abort: source has mq patches applied
420 abort: source has mq patches applied
421 [255]
421 [255]
422
422
423
423
424 import should fail
424 import should fail
425
425
426 $ hg st .
426 $ hg st .
427 $ echo foo >> ../a
427 $ echo foo >> ../a
428 $ hg diff > ../../import.diff
428 $ hg diff > ../../import.diff
429 $ hg revert --no-backup ../a
429 $ hg revert --no-backup ../a
430 $ hg import ../../import.diff
430 $ hg import ../../import.diff
431 abort: cannot import over an applied patch
431 abort: cannot import over an applied patch
432 [255]
432 [255]
433 $ hg st
433 $ hg st
434
434
435 import --no-commit should succeed
435 import --no-commit should succeed
436
436
437 $ hg import --no-commit ../../import.diff
437 $ hg import --no-commit ../../import.diff
438 applying ../../import.diff
438 applying ../../import.diff
439 $ hg st
439 $ hg st
440 M a
440 M a
441 $ hg revert --no-backup ../a
441 $ hg revert --no-backup ../a
442
442
443
443
444 qunapplied
444 qunapplied
445
445
446 $ hg qunapplied
446 $ hg qunapplied
447 test2.patch
447 test2.patch
448
448
449
449
450 qpush/qpop with index
450 qpush/qpop with index
451
451
452 $ hg qnew test1b.patch
452 $ hg qnew test1b.patch
453 $ echo 1b > 1b
453 $ echo 1b > 1b
454 $ hg add 1b
454 $ hg add 1b
455 $ hg qrefresh
455 $ hg qrefresh
456 $ hg qpush 2
456 $ hg qpush 2
457 applying test2.patch
457 applying test2.patch
458 now at: test2.patch
458 now at: test2.patch
459 $ hg qpop 0
459 $ hg qpop 0
460 popping test2.patch
460 popping test2.patch
461 popping test1b.patch
461 popping test1b.patch
462 now at: test.patch
462 now at: test.patch
463 $ hg qpush test.patch+1
463 $ hg qpush test.patch+1
464 applying test1b.patch
464 applying test1b.patch
465 now at: test1b.patch
465 now at: test1b.patch
466 $ hg qpush test.patch+2
466 $ hg qpush test.patch+2
467 applying test2.patch
467 applying test2.patch
468 now at: test2.patch
468 now at: test2.patch
469 $ hg qpop test2.patch-1
469 $ hg qpop test2.patch-1
470 popping test2.patch
470 popping test2.patch
471 now at: test1b.patch
471 now at: test1b.patch
472 $ hg qpop test2.patch-2
472 $ hg qpop test2.patch-2
473 popping test1b.patch
473 popping test1b.patch
474 now at: test.patch
474 now at: test.patch
475 $ hg qpush test1b.patch+1
475 $ hg qpush test1b.patch+1
476 applying test1b.patch
476 applying test1b.patch
477 applying test2.patch
477 applying test2.patch
478 now at: test2.patch
478 now at: test2.patch
479
479
480
480
481 qpush --move
481 qpush --move
482
482
483 $ hg qpop -a
483 $ hg qpop -a
484 popping test2.patch
484 popping test2.patch
485 popping test1b.patch
485 popping test1b.patch
486 popping test.patch
486 popping test.patch
487 patch queue now empty
487 patch queue now empty
488 $ hg qguard test1b.patch -- -negguard
488 $ hg qguard test1b.patch -- -negguard
489 $ hg qguard test2.patch -- +posguard
489 $ hg qguard test2.patch -- +posguard
490 $ hg qpush --move test2.patch # can't move guarded patch
490 $ hg qpush --move test2.patch # can't move guarded patch
491 cannot push 'test2.patch' - guarded by '+posguard'
491 cannot push 'test2.patch' - guarded by '+posguard'
492 [1]
492 [1]
493 $ hg qselect posguard
493 $ hg qselect posguard
494 number of unguarded, unapplied patches has changed from 2 to 3
494 number of unguarded, unapplied patches has changed from 2 to 3
495 $ hg qpush --move test2.patch # move to front
495 $ hg qpush --move test2.patch # move to front
496 applying test2.patch
496 applying test2.patch
497 now at: test2.patch
497 now at: test2.patch
498 $ hg qpush --move test1b.patch # negative guard unselected
498 $ hg qpush --move test1b.patch # negative guard unselected
499 applying test1b.patch
499 applying test1b.patch
500 now at: test1b.patch
500 now at: test1b.patch
501 $ hg qpush --move test.patch # noop move
501 $ hg qpush --move test.patch # noop move
502 applying test.patch
502 applying test.patch
503 now at: test.patch
503 now at: test.patch
504 $ hg qseries -v
504 $ hg qseries -v
505 0 A test2.patch
505 0 A test2.patch
506 1 A test1b.patch
506 1 A test1b.patch
507 2 A test.patch
507 2 A test.patch
508 $ hg qpop -a
508 $ hg qpop -a
509 popping test.patch
509 popping test.patch
510 popping test1b.patch
510 popping test1b.patch
511 popping test2.patch
511 popping test2.patch
512 patch queue now empty
512 patch queue now empty
513
513
514 cleaning up
514 cleaning up
515
515
516 $ hg qselect --none
516 $ hg qselect --none
517 guards deactivated
517 guards deactivated
518 number of unguarded, unapplied patches has changed from 3 to 2
518 number of unguarded, unapplied patches has changed from 3 to 2
519 $ hg qguard --none test1b.patch
519 $ hg qguard --none test1b.patch
520 $ hg qguard --none test2.patch
520 $ hg qguard --none test2.patch
521 $ hg qpush --move test.patch
521 $ hg qpush --move test.patch
522 applying test.patch
522 applying test.patch
523 now at: test.patch
523 now at: test.patch
524 $ hg qpush --move test1b.patch
524 $ hg qpush --move test1b.patch
525 applying test1b.patch
525 applying test1b.patch
526 now at: test1b.patch
526 now at: test1b.patch
527 $ hg qpush --move bogus # nonexistent patch
527 $ hg qpush --move bogus # nonexistent patch
528 abort: patch bogus not in series
528 abort: patch bogus not in series
529 [255]
529 [255]
530 $ hg qpush --move # no patch
530 $ hg qpush --move # no patch
531 abort: please specify the patch to move
531 abort: please specify the patch to move
532 [255]
532 [255]
533 $ hg qpush --move test.patch # already applied
533 $ hg qpush --move test.patch # already applied
534 abort: cannot push to a previous patch: test.patch
534 abort: cannot push to a previous patch: test.patch
535 [255]
535 [255]
536 $ sed '2i\
536 $ sed '2i\
537 > # make qtip index different in series and fullseries
537 > # make qtip index different in series and fullseries
538 > ' `hg root`/.hg/patches/series > $TESTTMP/sedtmp
538 > ' `hg root`/.hg/patches/series > $TESTTMP/sedtmp
539 $ cp $TESTTMP/sedtmp `hg root`/.hg/patches/series
539 $ cp $TESTTMP/sedtmp `hg root`/.hg/patches/series
540 $ cat `hg root`/.hg/patches/series
540 $ cat `hg root`/.hg/patches/series
541 # comment
541 # comment
542 # make qtip index different in series and fullseries
542 # make qtip index different in series and fullseries
543
543
544 test.patch
544 test.patch
545 test1b.patch
545 test1b.patch
546 test2.patch
546 test2.patch
547 $ hg qpush --move test2.patch
547 $ hg qpush --move test2.patch
548 applying test2.patch
548 applying test2.patch
549 now at: test2.patch
549 now at: test2.patch
550
550
551
551
552 series after move
552 series after move
553
553
554 $ cat `hg root`/.hg/patches/series
554 $ cat `hg root`/.hg/patches/series
555 # comment
555 # comment
556 # make qtip index different in series and fullseries
556 # make qtip index different in series and fullseries
557
557
558 test.patch
558 test.patch
559 test1b.patch
559 test1b.patch
560 test2.patch
560 test2.patch
561
561
562
562
563 pop, qapplied, qunapplied
563 pop, qapplied, qunapplied
564
564
565 $ hg qseries -v
565 $ hg qseries -v
566 0 A test.patch
566 0 A test.patch
567 1 A test1b.patch
567 1 A test1b.patch
568 2 A test2.patch
568 2 A test2.patch
569
569
570 qapplied -1 test.patch
570 qapplied -1 test.patch
571
571
572 $ hg qapplied -1 test.patch
572 $ hg qapplied -1 test.patch
573 only one patch applied
573 only one patch applied
574 [1]
574 [1]
575
575
576 qapplied -1 test1b.patch
576 qapplied -1 test1b.patch
577
577
578 $ hg qapplied -1 test1b.patch
578 $ hg qapplied -1 test1b.patch
579 test.patch
579 test.patch
580
580
581 qapplied -1 test2.patch
581 qapplied -1 test2.patch
582
582
583 $ hg qapplied -1 test2.patch
583 $ hg qapplied -1 test2.patch
584 test1b.patch
584 test1b.patch
585
585
586 qapplied -1
586 qapplied -1
587
587
588 $ hg qapplied -1
588 $ hg qapplied -1
589 test1b.patch
589 test1b.patch
590
590
591 qapplied
591 qapplied
592
592
593 $ hg qapplied
593 $ hg qapplied
594 test.patch
594 test.patch
595 test1b.patch
595 test1b.patch
596 test2.patch
596 test2.patch
597
597
598 qapplied test1b.patch
598 qapplied test1b.patch
599
599
600 $ hg qapplied test1b.patch
600 $ hg qapplied test1b.patch
601 test.patch
601 test.patch
602 test1b.patch
602 test1b.patch
603
603
604 qunapplied -1
604 qunapplied -1
605
605
606 $ hg qunapplied -1
606 $ hg qunapplied -1
607 all patches applied
607 all patches applied
608 [1]
608 [1]
609
609
610 qunapplied
610 qunapplied
611
611
612 $ hg qunapplied
612 $ hg qunapplied
613
613
614 popping
614 popping
615
615
616 $ hg qpop
616 $ hg qpop
617 popping test2.patch
617 popping test2.patch
618 now at: test1b.patch
618 now at: test1b.patch
619
619
620 qunapplied -1
620 qunapplied -1
621
621
622 $ hg qunapplied -1
622 $ hg qunapplied -1
623 test2.patch
623 test2.patch
624
624
625 qunapplied
625 qunapplied
626
626
627 $ hg qunapplied
627 $ hg qunapplied
628 test2.patch
628 test2.patch
629
629
630 qunapplied test2.patch
630 qunapplied test2.patch
631
631
632 $ hg qunapplied test2.patch
632 $ hg qunapplied test2.patch
633
633
634 qunapplied -1 test2.patch
634 qunapplied -1 test2.patch
635
635
636 $ hg qunapplied -1 test2.patch
636 $ hg qunapplied -1 test2.patch
637 all patches applied
637 all patches applied
638 [1]
638 [1]
639
639
640 popping -a
640 popping -a
641
641
642 $ hg qpop -a
642 $ hg qpop -a
643 popping test1b.patch
643 popping test1b.patch
644 popping test.patch
644 popping test.patch
645 patch queue now empty
645 patch queue now empty
646
646
647 qapplied
647 qapplied
648
648
649 $ hg qapplied
649 $ hg qapplied
650
650
651 qapplied -1
651 qapplied -1
652
652
653 $ hg qapplied -1
653 $ hg qapplied -1
654 no patches applied
654 no patches applied
655 [1]
655 [1]
656 $ hg qpush
656 $ hg qpush
657 applying test.patch
657 applying test.patch
658 now at: test.patch
658 now at: test.patch
659
659
660
660
661 push should succeed
661 push should succeed
662
662
663 $ hg qpop -a
663 $ hg qpop -a
664 popping test.patch
664 popping test.patch
665 patch queue now empty
665 patch queue now empty
666 $ hg push ../../k
666 $ hg push ../../k
667 pushing to ../../k
667 pushing to ../../k
668 searching for changes
668 searching for changes
669 adding changesets
669 adding changesets
670 adding manifests
670 adding manifests
671 adding file changes
671 adding file changes
672 added 1 changesets with 1 changes to 1 files
672 added 1 changesets with 1 changes to 1 files
673
673
674
674
675 we want to start with some patches applied
675 we want to start with some patches applied
676
676
677 $ hg qpush -a
677 $ hg qpush -a
678 applying test.patch
678 applying test.patch
679 applying test1b.patch
679 applying test1b.patch
680 applying test2.patch
680 applying test2.patch
681 now at: test2.patch
681 now at: test2.patch
682
682
683 % pops all patches and succeeds
683 % pops all patches and succeeds
684
684
685 $ hg qpop -a
685 $ hg qpop -a
686 popping test2.patch
686 popping test2.patch
687 popping test1b.patch
687 popping test1b.patch
688 popping test.patch
688 popping test.patch
689 patch queue now empty
689 patch queue now empty
690
690
691 % does nothing and succeeds
691 % does nothing and succeeds
692
692
693 $ hg qpop -a
693 $ hg qpop -a
694 no patches applied
694 no patches applied
695
695
696 % fails - nothing else to pop
696 % fails - nothing else to pop
697
697
698 $ hg qpop
698 $ hg qpop
699 no patches applied
699 no patches applied
700 [1]
700 [1]
701
701
702 % pushes a patch and succeeds
702 % pushes a patch and succeeds
703
703
704 $ hg qpush
704 $ hg qpush
705 applying test.patch
705 applying test.patch
706 now at: test.patch
706 now at: test.patch
707
707
708 % pops a patch and succeeds
708 % pops a patch and succeeds
709
709
710 $ hg qpop
710 $ hg qpop
711 popping test.patch
711 popping test.patch
712 patch queue now empty
712 patch queue now empty
713
713
714 % pushes up to test1b.patch and succeeds
714 % pushes up to test1b.patch and succeeds
715
715
716 $ hg qpush test1b.patch
716 $ hg qpush test1b.patch
717 applying test.patch
717 applying test.patch
718 applying test1b.patch
718 applying test1b.patch
719 now at: test1b.patch
719 now at: test1b.patch
720
720
721 % does nothing and succeeds
721 % does nothing and succeeds
722
722
723 $ hg qpush test1b.patch
723 $ hg qpush test1b.patch
724 qpush: test1b.patch is already at the top
724 qpush: test1b.patch is already at the top
725
725
726 % does nothing and succeeds
726 % does nothing and succeeds
727
727
728 $ hg qpop test1b.patch
728 $ hg qpop test1b.patch
729 qpop: test1b.patch is already at the top
729 qpop: test1b.patch is already at the top
730
730
731 % fails - can't push to this patch
731 % fails - can't push to this patch
732
732
733 $ hg qpush test.patch
733 $ hg qpush test.patch
734 abort: cannot push to a previous patch: test.patch
734 abort: cannot push to a previous patch: test.patch
735 [255]
735 [255]
736
736
737 % fails - can't pop to this patch
737 % fails - can't pop to this patch
738
738
739 $ hg qpop test2.patch
739 $ hg qpop test2.patch
740 abort: patch test2.patch is not applied
740 abort: patch test2.patch is not applied
741 [255]
741 [255]
742
742
743 % pops up to test.patch and succeeds
743 % pops up to test.patch and succeeds
744
744
745 $ hg qpop test.patch
745 $ hg qpop test.patch
746 popping test1b.patch
746 popping test1b.patch
747 now at: test.patch
747 now at: test.patch
748
748
749 % pushes all patches and succeeds
749 % pushes all patches and succeeds
750
750
751 $ hg qpush -a
751 $ hg qpush -a
752 applying test1b.patch
752 applying test1b.patch
753 applying test2.patch
753 applying test2.patch
754 now at: test2.patch
754 now at: test2.patch
755
755
756 % does nothing and succeeds
756 % does nothing and succeeds
757
757
758 $ hg qpush -a
758 $ hg qpush -a
759 all patches are currently applied
759 all patches are currently applied
760
760
761 % fails - nothing else to push
761 % fails - nothing else to push
762
762
763 $ hg qpush
763 $ hg qpush
764 patch series already fully applied
764 patch series already fully applied
765 [1]
765 [1]
766
766
767 % does nothing and succeeds
767 % does nothing and succeeds
768
768
769 $ hg qpush test2.patch
769 $ hg qpush test2.patch
770 qpush: test2.patch is already at the top
770 qpush: test2.patch is already at the top
771
771
772 strip
772 strip
773
773
774 $ cd ../../b
774 $ cd ../../b
775 $ echo x>x
775 $ echo x>x
776 $ hg ci -Ama
776 $ hg ci -Ama
777 adding x
777 adding x
778 $ hg strip tip
778 $ hg strip tip
779 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
779 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
780 saved backup bundle to $TESTTMP/b/.hg/strip-backup/*-backup.hg (glob)
780 saved backup bundle to $TESTTMP/b/.hg/strip-backup/*-backup.hg (glob)
781 $ hg unbundle .hg/strip-backup/*
781 $ hg unbundle .hg/strip-backup/*
782 adding changesets
782 adding changesets
783 adding manifests
783 adding manifests
784 adding file changes
784 adding file changes
785 added 1 changesets with 1 changes to 1 files
785 added 1 changesets with 1 changes to 1 files
786 (run 'hg update' to get a working copy)
786 (run 'hg update' to get a working copy)
787
787
788
788
789 strip with local changes, should complain
789 strip with local changes, should complain
790
790
791 $ hg up
791 $ hg up
792 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
792 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
793 $ echo y>y
793 $ echo y>y
794 $ hg add y
794 $ hg add y
795 $ hg strip tip
795 $ hg strip tip
796 abort: local changes found
796 abort: local changes found
797 [255]
797 [255]
798
798
799 --force strip with local changes
799 --force strip with local changes
800
800
801 $ hg strip -f tip
801 $ hg strip -f tip
802 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
802 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
803 saved backup bundle to $TESTTMP/b/.hg/strip-backup/*-backup.hg (glob)
803 saved backup bundle to $TESTTMP/b/.hg/strip-backup/*-backup.hg (glob)
804 $ cd ..
804 $ cd ..
805
805
806
806
807 cd b; hg qrefresh
807 cd b; hg qrefresh
808
808
809 $ hg init refresh
809 $ hg init refresh
810 $ cd refresh
810 $ cd refresh
811 $ echo a > a
811 $ echo a > a
812 $ hg ci -Ama
812 $ hg ci -Ama
813 adding a
813 adding a
814 $ hg qnew -mfoo foo
814 $ hg qnew -mfoo foo
815 $ echo a >> a
815 $ echo a >> a
816 $ hg qrefresh
816 $ hg qrefresh
817 $ mkdir b
817 $ mkdir b
818 $ cd b
818 $ cd b
819 $ echo f > f
819 $ echo f > f
820 $ hg add f
820 $ hg add f
821 $ hg qrefresh
821 $ hg qrefresh
822 $ cat ../.hg/patches/foo
822 $ cat ../.hg/patches/foo
823 foo
823 foo
824
824
825 diff -r cb9a9f314b8b a
825 diff -r cb9a9f314b8b a
826 --- a/a\t(?P<date>.*) (re)
826 --- a/a\t(?P<date>.*) (re)
827 \+\+\+ b/a\t(?P<date>.*) (re)
827 \+\+\+ b/a\t(?P<date>.*) (re)
828 @@ -1,1 +1,2 @@
828 @@ -1,1 +1,2 @@
829 a
829 a
830 +a
830 +a
831 diff -r cb9a9f314b8b b/f
831 diff -r cb9a9f314b8b b/f
832 --- /dev/null\t(?P<date>.*) (re)
832 --- /dev/null\t(?P<date>.*) (re)
833 \+\+\+ b/b/f\t(?P<date>.*) (re)
833 \+\+\+ b/b/f\t(?P<date>.*) (re)
834 @@ -0,0 +1,1 @@
834 @@ -0,0 +1,1 @@
835 +f
835 +f
836
836
837 hg qrefresh .
837 hg qrefresh .
838
838
839 $ hg qrefresh .
839 $ hg qrefresh .
840 $ cat ../.hg/patches/foo
840 $ cat ../.hg/patches/foo
841 foo
841 foo
842
842
843 diff -r cb9a9f314b8b b/f
843 diff -r cb9a9f314b8b b/f
844 --- /dev/null\t(?P<date>.*) (re)
844 --- /dev/null\t(?P<date>.*) (re)
845 \+\+\+ b/b/f\t(?P<date>.*) (re)
845 \+\+\+ b/b/f\t(?P<date>.*) (re)
846 @@ -0,0 +1,1 @@
846 @@ -0,0 +1,1 @@
847 +f
847 +f
848 $ hg status
848 $ hg status
849 M a
849 M a
850
850
851
851
852 qpush failure
852 qpush failure
853
853
854 $ cd ..
854 $ cd ..
855 $ hg qrefresh
855 $ hg qrefresh
856 $ hg qnew -mbar bar
856 $ hg qnew -mbar bar
857 $ echo foo > foo
857 $ echo foo > foo
858 $ echo bar > bar
858 $ echo bar > bar
859 $ hg add foo bar
859 $ hg add foo bar
860 $ hg qrefresh
860 $ hg qrefresh
861 $ hg qpop -a
861 $ hg qpop -a
862 popping bar
862 popping bar
863 popping foo
863 popping foo
864 patch queue now empty
864 patch queue now empty
865 $ echo bar > foo
865 $ echo bar > foo
866 $ hg qpush -a
866 $ hg qpush -a
867 applying foo
867 applying foo
868 applying bar
868 applying bar
869 file foo already exists
869 file foo already exists
870 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
870 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
871 patch failed, unable to continue (try -v)
871 patch failed, unable to continue (try -v)
872 patch failed, rejects left in working dir
872 patch failed, rejects left in working dir
873 errors during apply, please fix and refresh bar
873 errors during apply, please fix and refresh bar
874 [2]
874 [2]
875 $ hg st
875 $ hg st
876 ? foo
876 ? foo
877 ? foo.rej
877 ? foo.rej
878
878
879
879
880 mq tags
880 mq tags
881
881
882 $ hg log --template '{rev} {tags}\n' -r qparent:qtip
882 $ hg log --template '{rev} {tags}\n' -r qparent:qtip
883 0 qparent
883 0 qparent
884 1 foo qbase
884 1 foo qbase
885 2 bar qtip tip
885 2 bar qtip tip
886
886
887 mq revset
887 mq revset
888
888
889 $ hg log -r 'mq()' --template '{rev}\n'
889 $ hg log -r 'mq()' --template '{rev}\n'
890 1
890 1
891 2
891 2
892 $ hg help revsets | grep -i mq
892 $ hg help revsets | grep -i mq
893 "mq()"
893 "mq()"
894 Changesets managed by MQ.
894 Changesets managed by MQ.
895
895
896 bad node in status
896 bad node in status
897
897
898 $ hg qpop
898 $ hg qpop
899 popping bar
899 popping bar
900 now at: foo
900 now at: foo
901 $ hg strip -qn tip
901 $ hg strip -qn tip
902 $ hg tip
902 $ hg tip
903 changeset: 0:cb9a9f314b8b
903 changeset: 0:cb9a9f314b8b
904 tag: tip
904 tag: tip
905 user: test
905 user: test
906 date: Thu Jan 01 00:00:00 1970 +0000
906 date: Thu Jan 01 00:00:00 1970 +0000
907 summary: a
907 summary: a
908
908
909 $ hg branches
909 $ hg branches
910 default 0:cb9a9f314b8b
910 default 0:cb9a9f314b8b
911 $ hg qpop
911 $ hg qpop
912 no patches applied
912 no patches applied
913 [1]
913 [1]
914
914
915 $ cd ..
915 $ cd ..
916
916
917
917
918 git patches
918 git patches
919
919
920 $ cat >>$HGRCPATH <<EOF
920 $ cat >>$HGRCPATH <<EOF
921 > [diff]
921 > [diff]
922 > git = True
922 > git = True
923 > EOF
923 > EOF
924 $ hg init git
924 $ hg init git
925 $ cd git
925 $ cd git
926 $ hg qinit
926 $ hg qinit
927
927
928 $ hg qnew -m'new file' new
928 $ hg qnew -m'new file' new
929 $ echo foo > new
929 $ echo foo > new
930 #if execbit
930 #if execbit
931 $ chmod +x new
931 $ chmod +x new
932 #endif
932 #endif
933 $ hg add new
933 $ hg add new
934 $ hg qrefresh
934 $ hg qrefresh
935 #if execbit
935 #if execbit
936 $ cat .hg/patches/new
936 $ cat .hg/patches/new
937 new file
937 new file
938
938
939 diff --git a/new b/new
939 diff --git a/new b/new
940 new file mode 100755
940 new file mode 100755
941 --- /dev/null
941 --- /dev/null
942 +++ b/new
942 +++ b/new
943 @@ -0,0 +1,1 @@
943 @@ -0,0 +1,1 @@
944 +foo
944 +foo
945 #else
945 #else
946 $ cat .hg/patches/new
946 $ cat .hg/patches/new
947 new file
947 new file
948
948
949 diff --git a/new b/new
949 diff --git a/new b/new
950 new file mode 100644
950 new file mode 100644
951 --- /dev/null
951 --- /dev/null
952 +++ b/new
952 +++ b/new
953 @@ -0,0 +1,1 @@
953 @@ -0,0 +1,1 @@
954 +foo
954 +foo
955 #endif
955 #endif
956
956
957 $ hg qnew -m'copy file' copy
957 $ hg qnew -m'copy file' copy
958 $ hg cp new copy
958 $ hg cp new copy
959 $ hg qrefresh
959 $ hg qrefresh
960 $ cat .hg/patches/copy
960 $ cat .hg/patches/copy
961 copy file
961 copy file
962
962
963 diff --git a/new b/copy
963 diff --git a/new b/copy
964 copy from new
964 copy from new
965 copy to copy
965 copy to copy
966
966
967 $ hg qpop
967 $ hg qpop
968 popping copy
968 popping copy
969 now at: new
969 now at: new
970 $ hg qpush
970 $ hg qpush
971 applying copy
971 applying copy
972 now at: copy
972 now at: copy
973 $ hg qdiff
973 $ hg qdiff
974 diff --git a/new b/copy
974 diff --git a/new b/copy
975 copy from new
975 copy from new
976 copy to copy
976 copy to copy
977 $ cat >>$HGRCPATH <<EOF
977 $ cat >>$HGRCPATH <<EOF
978 > [diff]
978 > [diff]
979 > git = False
979 > git = False
980 > EOF
980 > EOF
981 $ hg qdiff --git
981 $ hg qdiff --git
982 diff --git a/new b/copy
982 diff --git a/new b/copy
983 copy from new
983 copy from new
984 copy to copy
984 copy to copy
985 $ cd ..
985 $ cd ..
986
986
987 empty lines in status
987 empty lines in status
988
988
989 $ hg init emptystatus
989 $ hg init emptystatus
990 $ cd emptystatus
990 $ cd emptystatus
991 $ hg qinit
991 $ hg qinit
992 $ printf '\n\n' > .hg/patches/status
992 $ printf '\n\n' > .hg/patches/status
993 $ hg qser
993 $ hg qser
994 $ cd ..
994 $ cd ..
995
995
996 bad line in status (without ":")
996 bad line in status (without ":")
997
997
998 $ hg init badstatus
998 $ hg init badstatus
999 $ cd badstatus
999 $ cd badstatus
1000 $ hg qinit
1000 $ hg qinit
1001 $ printf 'babar has no colon in this line\n' > .hg/patches/status
1001 $ printf 'babar has no colon in this line\n' > .hg/patches/status
1002 $ hg qser
1002 $ hg qser
1003 malformated mq status line: ['babar has no colon in this line']
1003 malformated mq status line: ['babar has no colon in this line']
1004 $ cd ..
1004 $ cd ..
1005
1005
1006
1006
1007 test file addition in slow path
1007 test file addition in slow path
1008
1008
1009 $ hg init slow
1009 $ hg init slow
1010 $ cd slow
1010 $ cd slow
1011 $ hg qinit
1011 $ hg qinit
1012 $ echo foo > foo
1012 $ echo foo > foo
1013 $ hg add foo
1013 $ hg add foo
1014 $ hg ci -m 'add foo'
1014 $ hg ci -m 'add foo'
1015 $ hg qnew bar
1015 $ hg qnew bar
1016 $ echo bar > bar
1016 $ echo bar > bar
1017 $ hg add bar
1017 $ hg add bar
1018 $ hg mv foo baz
1018 $ hg mv foo baz
1019 $ hg qrefresh --git
1019 $ hg qrefresh --git
1020 $ hg up -C 0
1020 $ hg up -C 0
1021 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1021 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1022 $ echo >> foo
1022 $ echo >> foo
1023 $ hg ci -m 'change foo'
1023 $ hg ci -m 'change foo'
1024 created new head
1024 created new head
1025 $ hg up -C 1
1025 $ hg up -C 1
1026 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1026 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1027 $ hg qrefresh --git
1027 $ hg qrefresh --git
1028 $ cat .hg/patches/bar
1028 $ cat .hg/patches/bar
1029 diff --git a/bar b/bar
1029 diff --git a/bar b/bar
1030 new file mode 100644
1030 new file mode 100644
1031 --- /dev/null
1031 --- /dev/null
1032 +++ b/bar
1032 +++ b/bar
1033 @@ -0,0 +1,1 @@
1033 @@ -0,0 +1,1 @@
1034 +bar
1034 +bar
1035 diff --git a/foo b/baz
1035 diff --git a/foo b/baz
1036 rename from foo
1036 rename from foo
1037 rename to baz
1037 rename to baz
1038 $ hg log -v --template '{rev} {file_copies}\n' -r .
1038 $ hg log -v --template '{rev} {file_copies}\n' -r .
1039 2 baz (foo)
1039 2 baz (foo)
1040 $ hg qrefresh --git
1040 $ hg qrefresh --git
1041 $ cat .hg/patches/bar
1041 $ cat .hg/patches/bar
1042 diff --git a/bar b/bar
1042 diff --git a/bar b/bar
1043 new file mode 100644
1043 new file mode 100644
1044 --- /dev/null
1044 --- /dev/null
1045 +++ b/bar
1045 +++ b/bar
1046 @@ -0,0 +1,1 @@
1046 @@ -0,0 +1,1 @@
1047 +bar
1047 +bar
1048 diff --git a/foo b/baz
1048 diff --git a/foo b/baz
1049 rename from foo
1049 rename from foo
1050 rename to baz
1050 rename to baz
1051 $ hg log -v --template '{rev} {file_copies}\n' -r .
1051 $ hg log -v --template '{rev} {file_copies}\n' -r .
1052 2 baz (foo)
1052 2 baz (foo)
1053 $ hg qrefresh
1053 $ hg qrefresh
1054 $ grep 'diff --git' .hg/patches/bar
1054 $ grep 'diff --git' .hg/patches/bar
1055 diff --git a/bar b/bar
1055 diff --git a/bar b/bar
1056 diff --git a/foo b/baz
1056 diff --git a/foo b/baz
1057
1057
1058
1058
1059 test file move chains in the slow path
1059 test file move chains in the slow path
1060
1060
1061 $ hg up -C 1
1061 $ hg up -C 1
1062 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1062 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1063 $ echo >> foo
1063 $ echo >> foo
1064 $ hg ci -m 'change foo again'
1064 $ hg ci -m 'change foo again'
1065 $ hg up -C 2
1065 $ hg up -C 2
1066 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1066 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1067 $ hg mv bar quux
1067 $ hg mv bar quux
1068 $ hg mv baz bleh
1068 $ hg mv baz bleh
1069 $ hg qrefresh --git
1069 $ hg qrefresh --git
1070 $ cat .hg/patches/bar
1070 $ cat .hg/patches/bar
1071 diff --git a/foo b/bleh
1071 diff --git a/foo b/bleh
1072 rename from foo
1072 rename from foo
1073 rename to bleh
1073 rename to bleh
1074 diff --git a/quux b/quux
1074 diff --git a/quux b/quux
1075 new file mode 100644
1075 new file mode 100644
1076 --- /dev/null
1076 --- /dev/null
1077 +++ b/quux
1077 +++ b/quux
1078 @@ -0,0 +1,1 @@
1078 @@ -0,0 +1,1 @@
1079 +bar
1079 +bar
1080 $ hg log -v --template '{rev} {file_copies}\n' -r .
1080 $ hg log -v --template '{rev} {file_copies}\n' -r .
1081 3 bleh (foo)
1081 3 bleh (foo)
1082 $ hg mv quux fred
1082 $ hg mv quux fred
1083 $ hg mv bleh barney
1083 $ hg mv bleh barney
1084 $ hg qrefresh --git
1084 $ hg qrefresh --git
1085 $ cat .hg/patches/bar
1085 $ cat .hg/patches/bar
1086 diff --git a/foo b/barney
1086 diff --git a/foo b/barney
1087 rename from foo
1087 rename from foo
1088 rename to barney
1088 rename to barney
1089 diff --git a/fred b/fred
1089 diff --git a/fred b/fred
1090 new file mode 100644
1090 new file mode 100644
1091 --- /dev/null
1091 --- /dev/null
1092 +++ b/fred
1092 +++ b/fred
1093 @@ -0,0 +1,1 @@
1093 @@ -0,0 +1,1 @@
1094 +bar
1094 +bar
1095 $ hg log -v --template '{rev} {file_copies}\n' -r .
1095 $ hg log -v --template '{rev} {file_copies}\n' -r .
1096 3 barney (foo)
1096 3 barney (foo)
1097
1097
1098
1098
1099 refresh omitting an added file
1099 refresh omitting an added file
1100
1100
1101 $ hg qnew baz
1101 $ hg qnew baz
1102 $ echo newfile > newfile
1102 $ echo newfile > newfile
1103 $ hg add newfile
1103 $ hg add newfile
1104 $ hg qrefresh
1104 $ hg qrefresh
1105 $ hg st -A newfile
1105 $ hg st -A newfile
1106 C newfile
1106 C newfile
1107 $ hg qrefresh -X newfile
1107 $ hg qrefresh -X newfile
1108 $ hg st -A newfile
1108 $ hg st -A newfile
1109 A newfile
1109 A newfile
1110 $ hg revert newfile
1110 $ hg revert newfile
1111 $ rm newfile
1111 $ rm newfile
1112 $ hg qpop
1112 $ hg qpop
1113 popping baz
1113 popping baz
1114 now at: bar
1114 now at: bar
1115
1115
1116 test qdel/qrm
1116 test qdel/qrm
1117
1117
1118 $ hg qdel baz
1118 $ hg qdel baz
1119 $ echo p >> .hg/patches/series
1119 $ echo p >> .hg/patches/series
1120 $ hg qrm p
1120 $ hg qrm p
1121 $ hg qser
1121 $ hg qser
1122 bar
1122 bar
1123
1123
1124 create a git patch
1124 create a git patch
1125
1125
1126 $ echo a > alexander
1126 $ echo a > alexander
1127 $ hg add alexander
1127 $ hg add alexander
1128 $ hg qnew -f --git addalexander
1128 $ hg qnew -f --git addalexander
1129 $ grep diff .hg/patches/addalexander
1129 $ grep diff .hg/patches/addalexander
1130 diff --git a/alexander b/alexander
1130 diff --git a/alexander b/alexander
1131
1131
1132
1132
1133 create a git binary patch
1133 create a git binary patch
1134
1134
1135 $ cat > writebin.py <<EOF
1135 $ cat > writebin.py <<EOF
1136 > import sys
1136 > import sys
1137 > path = sys.argv[1]
1137 > path = sys.argv[1]
1138 > open(path, 'wb').write('BIN\x00ARY')
1138 > open(path, 'wb').write('BIN\x00ARY')
1139 > EOF
1139 > EOF
1140 $ python writebin.py bucephalus
1140 $ python writebin.py bucephalus
1141
1141
1142 $ python "$TESTDIR/md5sum.py" bucephalus
1142 $ python "$TESTDIR/md5sum.py" bucephalus
1143 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
1143 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
1144 $ hg add bucephalus
1144 $ hg add bucephalus
1145 $ hg qnew -f --git addbucephalus
1145 $ hg qnew -f --git addbucephalus
1146 $ grep diff .hg/patches/addbucephalus
1146 $ grep diff .hg/patches/addbucephalus
1147 diff --git a/bucephalus b/bucephalus
1147 diff --git a/bucephalus b/bucephalus
1148
1148
1149
1149
1150 check binary patches can be popped and pushed
1150 check binary patches can be popped and pushed
1151
1151
1152 $ hg qpop
1152 $ hg qpop
1153 popping addbucephalus
1153 popping addbucephalus
1154 now at: addalexander
1154 now at: addalexander
1155 $ test -f bucephalus && echo % bucephalus should not be there
1155 $ test -f bucephalus && echo % bucephalus should not be there
1156 [1]
1156 [1]
1157 $ hg qpush
1157 $ hg qpush
1158 applying addbucephalus
1158 applying addbucephalus
1159 now at: addbucephalus
1159 now at: addbucephalus
1160 $ test -f bucephalus
1160 $ test -f bucephalus
1161 $ python "$TESTDIR/md5sum.py" bucephalus
1161 $ python "$TESTDIR/md5sum.py" bucephalus
1162 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
1162 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
1163
1163
1164
1164
1165
1165
1166 strip again
1166 strip again
1167
1167
1168 $ cd ..
1168 $ cd ..
1169 $ hg init strip
1169 $ hg init strip
1170 $ cd strip
1170 $ cd strip
1171 $ touch foo
1171 $ touch foo
1172 $ hg add foo
1172 $ hg add foo
1173 $ hg ci -m 'add foo'
1173 $ hg ci -m 'add foo'
1174 $ echo >> foo
1174 $ echo >> foo
1175 $ hg ci -m 'change foo 1'
1175 $ hg ci -m 'change foo 1'
1176 $ hg up -C 0
1176 $ hg up -C 0
1177 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1177 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1178 $ echo 1 >> foo
1178 $ echo 1 >> foo
1179 $ hg ci -m 'change foo 2'
1179 $ hg ci -m 'change foo 2'
1180 created new head
1180 created new head
1181 $ HGMERGE=true hg merge
1181 $ HGMERGE=true hg merge
1182 merging foo
1182 merging foo
1183 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1183 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1184 (branch merge, don't forget to commit)
1184 (branch merge, don't forget to commit)
1185 $ hg ci -m merge
1185 $ hg ci -m merge
1186 $ hg log
1186 $ hg log
1187 changeset: 3:99615015637b
1187 changeset: 3:99615015637b
1188 tag: tip
1188 tag: tip
1189 parent: 2:20cbbe65cff7
1189 parent: 2:20cbbe65cff7
1190 parent: 1:d2871fc282d4
1190 parent: 1:d2871fc282d4
1191 user: test
1191 user: test
1192 date: Thu Jan 01 00:00:00 1970 +0000
1192 date: Thu Jan 01 00:00:00 1970 +0000
1193 summary: merge
1193 summary: merge
1194
1194
1195 changeset: 2:20cbbe65cff7
1195 changeset: 2:20cbbe65cff7
1196 parent: 0:53245c60e682
1196 parent: 0:53245c60e682
1197 user: test
1197 user: test
1198 date: Thu Jan 01 00:00:00 1970 +0000
1198 date: Thu Jan 01 00:00:00 1970 +0000
1199 summary: change foo 2
1199 summary: change foo 2
1200
1200
1201 changeset: 1:d2871fc282d4
1201 changeset: 1:d2871fc282d4
1202 user: test
1202 user: test
1203 date: Thu Jan 01 00:00:00 1970 +0000
1203 date: Thu Jan 01 00:00:00 1970 +0000
1204 summary: change foo 1
1204 summary: change foo 1
1205
1205
1206 changeset: 0:53245c60e682
1206 changeset: 0:53245c60e682
1207 user: test
1207 user: test
1208 date: Thu Jan 01 00:00:00 1970 +0000
1208 date: Thu Jan 01 00:00:00 1970 +0000
1209 summary: add foo
1209 summary: add foo
1210
1210
1211 $ hg strip 1
1211 $ hg strip 1
1212 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1212 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1213 saved backup bundle to $TESTTMP/strip/.hg/strip-backup/*-backup.hg (glob)
1213 saved backup bundle to $TESTTMP/strip/.hg/strip-backup/*-backup.hg (glob)
1214 $ checkundo strip
1214 $ checkundo strip
1215 $ hg log
1215 $ hg log
1216 changeset: 1:20cbbe65cff7
1216 changeset: 1:20cbbe65cff7
1217 tag: tip
1217 tag: tip
1218 user: test
1218 user: test
1219 date: Thu Jan 01 00:00:00 1970 +0000
1219 date: Thu Jan 01 00:00:00 1970 +0000
1220 summary: change foo 2
1220 summary: change foo 2
1221
1221
1222 changeset: 0:53245c60e682
1222 changeset: 0:53245c60e682
1223 user: test
1223 user: test
1224 date: Thu Jan 01 00:00:00 1970 +0000
1224 date: Thu Jan 01 00:00:00 1970 +0000
1225 summary: add foo
1225 summary: add foo
1226
1226
1227 $ cd ..
1227 $ cd ..
1228
1228
1229
1229
1230 qclone
1230 qclone
1231
1231
1232 $ qlog()
1232 $ qlog()
1233 > {
1233 > {
1234 > echo 'main repo:'
1234 > echo 'main repo:'
1235 > hg log --template ' rev {rev}: {desc}\n'
1235 > hg log --template ' rev {rev}: {desc}\n'
1236 > echo 'patch repo:'
1236 > echo 'patch repo:'
1237 > hg -R .hg/patches log --template ' rev {rev}: {desc}\n'
1237 > hg -R .hg/patches log --template ' rev {rev}: {desc}\n'
1238 > }
1238 > }
1239 $ hg init qclonesource
1239 $ hg init qclonesource
1240 $ cd qclonesource
1240 $ cd qclonesource
1241 $ echo foo > foo
1241 $ echo foo > foo
1242 $ hg add foo
1242 $ hg add foo
1243 $ hg ci -m 'add foo'
1243 $ hg ci -m 'add foo'
1244 $ hg qinit
1244 $ hg qinit
1245 $ hg qnew patch1
1245 $ hg qnew patch1
1246 $ echo bar >> foo
1246 $ echo bar >> foo
1247 $ hg qrefresh -m 'change foo'
1247 $ hg qrefresh -m 'change foo'
1248 $ cd ..
1248 $ cd ..
1249
1249
1250
1250
1251 repo with unversioned patch dir
1251 repo with unversioned patch dir
1252
1252
1253 $ hg qclone qclonesource failure
1253 $ hg qclone qclonesource failure
1254 abort: versioned patch repository not found (see init --mq)
1254 abort: versioned patch repository not found (see init --mq)
1255 [255]
1255 [255]
1256
1256
1257 $ cd qclonesource
1257 $ cd qclonesource
1258 $ hg qinit -c
1258 $ hg qinit -c
1259 adding .hg/patches/patch1 (glob)
1259 adding .hg/patches/patch1 (glob)
1260 $ hg qci -m checkpoint
1260 $ hg qci -m checkpoint
1261 $ qlog
1261 $ qlog
1262 main repo:
1262 main repo:
1263 rev 1: change foo
1263 rev 1: change foo
1264 rev 0: add foo
1264 rev 0: add foo
1265 patch repo:
1265 patch repo:
1266 rev 0: checkpoint
1266 rev 0: checkpoint
1267 $ cd ..
1267 $ cd ..
1268
1268
1269
1269
1270 repo with patches applied
1270 repo with patches applied
1271
1271
1272 $ hg qclone qclonesource qclonedest
1272 $ hg qclone qclonesource qclonedest
1273 updating to branch default
1273 updating to branch default
1274 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1274 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1275 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1275 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1276 $ cd qclonedest
1276 $ cd qclonedest
1277 $ qlog
1277 $ qlog
1278 main repo:
1278 main repo:
1279 rev 0: add foo
1279 rev 0: add foo
1280 patch repo:
1280 patch repo:
1281 rev 0: checkpoint
1281 rev 0: checkpoint
1282 $ cd ..
1282 $ cd ..
1283
1283
1284
1284
1285 repo with patches unapplied
1285 repo with patches unapplied
1286
1286
1287 $ cd qclonesource
1287 $ cd qclonesource
1288 $ hg qpop -a
1288 $ hg qpop -a
1289 popping patch1
1289 popping patch1
1290 patch queue now empty
1290 patch queue now empty
1291 $ qlog
1291 $ qlog
1292 main repo:
1292 main repo:
1293 rev 0: add foo
1293 rev 0: add foo
1294 patch repo:
1294 patch repo:
1295 rev 0: checkpoint
1295 rev 0: checkpoint
1296 $ cd ..
1296 $ cd ..
1297 $ hg qclone qclonesource qclonedest2
1297 $ hg qclone qclonesource qclonedest2
1298 updating to branch default
1298 updating to branch default
1299 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1299 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1300 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1300 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1301 $ cd qclonedest2
1301 $ cd qclonedest2
1302 $ qlog
1302 $ qlog
1303 main repo:
1303 main repo:
1304 rev 0: add foo
1304 rev 0: add foo
1305 patch repo:
1305 patch repo:
1306 rev 0: checkpoint
1306 rev 0: checkpoint
1307 $ cd ..
1307 $ cd ..
1308
1308
1309
1309
1310 Issue1033: test applying on an empty file
1310 Issue1033: test applying on an empty file
1311
1311
1312 $ hg init empty
1312 $ hg init empty
1313 $ cd empty
1313 $ cd empty
1314 $ touch a
1314 $ touch a
1315 $ hg ci -Am addempty
1315 $ hg ci -Am addempty
1316 adding a
1316 adding a
1317 $ echo a > a
1317 $ echo a > a
1318 $ hg qnew -f -e changea
1318 $ hg qnew -f -e changea
1319 $ hg qpop
1319 $ hg qpop
1320 popping changea
1320 popping changea
1321 patch queue now empty
1321 patch queue now empty
1322 $ hg qpush
1322 $ hg qpush
1323 applying changea
1323 applying changea
1324 now at: changea
1324 now at: changea
1325 $ cd ..
1325 $ cd ..
1326
1326
1327 test qpush with --force, issue1087
1327 test qpush with --force, issue1087
1328
1328
1329 $ hg init forcepush
1329 $ hg init forcepush
1330 $ cd forcepush
1330 $ cd forcepush
1331 $ echo hello > hello.txt
1331 $ echo hello > hello.txt
1332 $ echo bye > bye.txt
1332 $ echo bye > bye.txt
1333 $ hg ci -Ama
1333 $ hg ci -Ama
1334 adding bye.txt
1334 adding bye.txt
1335 adding hello.txt
1335 adding hello.txt
1336 $ hg qnew -d '0 0' empty
1336 $ hg qnew -d '0 0' empty
1337 $ hg qpop
1337 $ hg qpop
1338 popping empty
1338 popping empty
1339 patch queue now empty
1339 patch queue now empty
1340 $ echo world >> hello.txt
1340 $ echo world >> hello.txt
1341
1341
1342
1342
1343 qpush should fail, local changes
1343 qpush should fail, local changes
1344
1344
1345 $ hg qpush
1345 $ hg qpush
1346 abort: local changes found
1346 abort: local changes found
1347 [255]
1347 [255]
1348
1348
1349
1349
1350 apply force, should not discard changes with empty patch
1350 apply force, should not discard changes with empty patch
1351
1351
1352 $ hg qpush -f
1352 $ hg qpush -f
1353 applying empty
1353 applying empty
1354 patch empty is empty
1354 patch empty is empty
1355 now at: empty
1355 now at: empty
1356 $ hg diff --config diff.nodates=True
1356 $ hg diff --config diff.nodates=True
1357 diff -r d58265112590 hello.txt
1357 diff -r d58265112590 hello.txt
1358 --- a/hello.txt
1358 --- a/hello.txt
1359 +++ b/hello.txt
1359 +++ b/hello.txt
1360 @@ -1,1 +1,2 @@
1360 @@ -1,1 +1,2 @@
1361 hello
1361 hello
1362 +world
1362 +world
1363 $ hg qdiff --config diff.nodates=True
1363 $ hg qdiff --config diff.nodates=True
1364 diff -r 9ecee4f634e3 hello.txt
1364 diff -r 9ecee4f634e3 hello.txt
1365 --- a/hello.txt
1365 --- a/hello.txt
1366 +++ b/hello.txt
1366 +++ b/hello.txt
1367 @@ -1,1 +1,2 @@
1367 @@ -1,1 +1,2 @@
1368 hello
1368 hello
1369 +world
1369 +world
1370 $ hg log -l1 -p
1370 $ hg log -l1 -p
1371 changeset: 1:d58265112590
1371 changeset: 1:d58265112590
1372 tag: empty
1372 tag: empty
1373 tag: qbase
1373 tag: qbase
1374 tag: qtip
1374 tag: qtip
1375 tag: tip
1375 tag: tip
1376 user: test
1376 user: test
1377 date: Thu Jan 01 00:00:00 1970 +0000
1377 date: Thu Jan 01 00:00:00 1970 +0000
1378 summary: imported patch empty
1378 summary: imported patch empty
1379
1379
1380
1380
1381 $ hg qref -d '0 0'
1381 $ hg qref -d '0 0'
1382 $ hg qpop
1382 $ hg qpop
1383 popping empty
1383 popping empty
1384 patch queue now empty
1384 patch queue now empty
1385 $ echo universe >> hello.txt
1385 $ echo universe >> hello.txt
1386 $ echo universe >> bye.txt
1386 $ echo universe >> bye.txt
1387
1387
1388
1388
1389 qpush should fail, local changes
1389 qpush should fail, local changes
1390
1390
1391 $ hg qpush
1391 $ hg qpush
1392 abort: local changes found
1392 abort: local changes found
1393 [255]
1393 [255]
1394
1394
1395
1395
1396 apply force, should discard changes in hello, but not bye
1396 apply force, should discard changes in hello, but not bye
1397
1397
1398 $ hg qpush -f --verbose
1398 $ hg qpush -f --verbose
1399 applying empty
1399 applying empty
1400 saving current version of hello.txt as hello.txt.orig
1400 saving current version of hello.txt as hello.txt.orig
1401 patching file hello.txt
1401 patching file hello.txt
1402 hello.txt
1402 hello.txt
1403 now at: empty
1403 now at: empty
1404 $ hg st
1404 $ hg st
1405 M bye.txt
1405 M bye.txt
1406 ? hello.txt.orig
1406 ? hello.txt.orig
1407 $ hg diff --config diff.nodates=True
1407 $ hg diff --config diff.nodates=True
1408 diff -r ba252371dbc1 bye.txt
1408 diff -r ba252371dbc1 bye.txt
1409 --- a/bye.txt
1409 --- a/bye.txt
1410 +++ b/bye.txt
1410 +++ b/bye.txt
1411 @@ -1,1 +1,2 @@
1411 @@ -1,1 +1,2 @@
1412 bye
1412 bye
1413 +universe
1413 +universe
1414 $ hg qdiff --config diff.nodates=True
1414 $ hg qdiff --config diff.nodates=True
1415 diff -r 9ecee4f634e3 bye.txt
1415 diff -r 9ecee4f634e3 bye.txt
1416 --- a/bye.txt
1416 --- a/bye.txt
1417 +++ b/bye.txt
1417 +++ b/bye.txt
1418 @@ -1,1 +1,2 @@
1418 @@ -1,1 +1,2 @@
1419 bye
1419 bye
1420 +universe
1420 +universe
1421 diff -r 9ecee4f634e3 hello.txt
1421 diff -r 9ecee4f634e3 hello.txt
1422 --- a/hello.txt
1422 --- a/hello.txt
1423 +++ b/hello.txt
1423 +++ b/hello.txt
1424 @@ -1,1 +1,3 @@
1424 @@ -1,1 +1,3 @@
1425 hello
1425 hello
1426 +world
1426 +world
1427 +universe
1427 +universe
1428
1428
1429
1429
1430 test popping revisions not in working dir ancestry
1430 test popping revisions not in working dir ancestry
1431
1431
1432 $ hg qseries -v
1432 $ hg qseries -v
1433 0 A empty
1433 0 A empty
1434 $ hg up qparent
1434 $ hg up qparent
1435 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1435 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1436 $ hg qpop
1436 $ hg qpop
1437 popping empty
1437 popping empty
1438 patch queue now empty
1438 patch queue now empty
1439
1439
1440 $ cd ..
1440 $ cd ..
1441 $ hg init deletion-order
1441 $ hg init deletion-order
1442 $ cd deletion-order
1442 $ cd deletion-order
1443
1443
1444 $ touch a
1444 $ touch a
1445 $ hg ci -Aqm0
1445 $ hg ci -Aqm0
1446
1446
1447 $ hg qnew rename-dir
1447 $ hg qnew rename-dir
1448 $ hg rm a
1448 $ hg rm a
1449 $ hg qrefresh
1449 $ hg qrefresh
1450
1450
1451 $ mkdir a b
1451 $ mkdir a b
1452 $ touch a/a b/b
1452 $ touch a/a b/b
1453 $ hg add -q a b
1453 $ hg add -q a b
1454 $ hg qrefresh
1454 $ hg qrefresh
1455
1455
1456
1456
1457 test popping must remove files added in subdirectories first
1457 test popping must remove files added in subdirectories first
1458
1458
1459 $ hg qpop
1459 $ hg qpop
1460 popping rename-dir
1460 popping rename-dir
1461 patch queue now empty
1461 patch queue now empty
1462 $ cd ..
1462 $ cd ..
1463
1463
1464
1464
1465 test case preservation through patch pushing especially on case
1465 test case preservation through patch pushing especially on case
1466 insensitive filesystem
1466 insensitive filesystem
1467
1467
1468 $ hg init casepreserve
1468 $ hg init casepreserve
1469 $ cd casepreserve
1469 $ cd casepreserve
1470
1470
1471 $ hg qnew add-file1
1471 $ hg qnew add-file1
1472 $ echo a > TeXtFiLe.TxT
1472 $ echo a > TeXtFiLe.TxT
1473 $ hg add TeXtFiLe.TxT
1473 $ hg add TeXtFiLe.TxT
1474 $ hg qrefresh
1474 $ hg qrefresh
1475
1475
1476 $ hg qnew add-file2
1476 $ hg qnew add-file2
1477 $ echo b > AnOtHeRFiLe.TxT
1477 $ echo b > AnOtHeRFiLe.TxT
1478 $ hg add AnOtHeRFiLe.TxT
1478 $ hg add AnOtHeRFiLe.TxT
1479 $ hg qrefresh
1479 $ hg qrefresh
1480
1480
1481 $ hg qnew modify-file
1481 $ hg qnew modify-file
1482 $ echo c >> AnOtHeRFiLe.TxT
1482 $ echo c >> AnOtHeRFiLe.TxT
1483 $ hg qrefresh
1483 $ hg qrefresh
1484
1484
1485 $ hg qapplied
1485 $ hg qapplied
1486 add-file1
1486 add-file1
1487 add-file2
1487 add-file2
1488 modify-file
1488 modify-file
1489 $ hg qpop -a
1489 $ hg qpop -a
1490 popping modify-file
1490 popping modify-file
1491 popping add-file2
1491 popping add-file2
1492 popping add-file1
1492 popping add-file1
1493 patch queue now empty
1493 patch queue now empty
1494
1494
1495 this qpush causes problems below, if case preservation on case
1495 this qpush causes problems below, if case preservation on case
1496 insensitive filesystem is not enough:
1496 insensitive filesystem is not enough:
1497 (1) unexpected "adding ..." messages are shown
1497 (1) unexpected "adding ..." messages are shown
1498 (2) patching fails in modification of (1) files
1498 (2) patching fails in modification of (1) files
1499
1499
1500 $ hg qpush -a
1500 $ hg qpush -a
1501 applying add-file1
1501 applying add-file1
1502 applying add-file2
1502 applying add-file2
1503 applying modify-file
1503 applying modify-file
1504 now at: modify-file
1504 now at: modify-file
1505
1505
1506 Proper phase default with mq:
1506 Proper phase default with mq:
1507
1507
1508 1. mq.secret=false
1508 1. mq.secret=false
1509
1509
1510 $ rm .hg/store/phaseroots
1510 $ rm .hg/store/phaseroots
1511 $ hg phase 'qparent::'
1511 $ hg phase 'qparent::'
1512 -1: public
1512 -1: public
1513 0: draft
1513 0: draft
1514 1: draft
1514 1: draft
1515 2: draft
1515 2: draft
1516 $ echo '[mq]' >> $HGRCPATH
1516 $ echo '[mq]' >> $HGRCPATH
1517 $ echo 'secret=true' >> $HGRCPATH
1517 $ echo 'secret=true' >> $HGRCPATH
1518 $ rm -f .hg/store/phaseroots
1518 $ rm -f .hg/store/phaseroots
1519 $ hg phase 'qparent::'
1519 $ hg phase 'qparent::'
1520 -1: public
1520 -1: public
1521 0: secret
1521 0: secret
1522 1: secret
1522 1: secret
1523 2: secret
1523 2: secret
1524
1524
1525 Test that qfinish change phase when mq.secret=true
1525 Test that qfinish change phase when mq.secret=true
1526
1526
1527 $ hg qfinish qbase
1527 $ hg qfinish qbase
1528 patch add-file1 finalized without changeset message
1528 patch add-file1 finalized without changeset message
1529 $ hg phase 'all()'
1529 $ hg phase 'all()'
1530 0: draft
1530 0: draft
1531 1: secret
1531 1: secret
1532 2: secret
1532 2: secret
1533
1533
1534 Test that qfinish respect phases.new-commit setting
1534 Test that qfinish respect phases.new-commit setting
1535
1535
1536 $ echo '[phases]' >> $HGRCPATH
1536 $ echo '[phases]' >> $HGRCPATH
1537 $ echo 'new-commit=secret' >> $HGRCPATH
1537 $ echo 'new-commit=secret' >> $HGRCPATH
1538 $ hg qfinish qbase
1538 $ hg qfinish qbase
1539 patch add-file2 finalized without changeset message
1539 patch add-file2 finalized without changeset message
1540 $ hg phase 'all()'
1540 $ hg phase 'all()'
1541 0: draft
1541 0: draft
1542 1: secret
1542 1: secret
1543 2: secret
1543 2: secret
1544
1544
1545 (restore env for next test)
1545 (restore env for next test)
1546
1546
1547 $ sed -e 's/new-commit=secret//' $HGRCPATH > $TESTTMP/sedtmp
1547 $ sed -e 's/new-commit=secret//' $HGRCPATH > $TESTTMP/sedtmp
1548 $ cp $TESTTMP/sedtmp $HGRCPATH
1548 $ cp $TESTTMP/sedtmp $HGRCPATH
1549 $ hg qimport -r 1 --name add-file2
1549 $ hg qimport -r 1 --name add-file2
1550
1550
1551 Test that qfinish preserve phase when mq.secret=false
1551 Test that qfinish preserve phase when mq.secret=false
1552
1552
1553 $ sed -e 's/secret=true/secret=false/' $HGRCPATH > $TESTTMP/sedtmp
1553 $ sed -e 's/secret=true/secret=false/' $HGRCPATH > $TESTTMP/sedtmp
1554 $ cp $TESTTMP/sedtmp $HGRCPATH
1554 $ cp $TESTTMP/sedtmp $HGRCPATH
1555 $ hg qfinish qbase
1555 $ hg qfinish qbase
1556 patch add-file2 finalized without changeset message
1556 patch add-file2 finalized without changeset message
1557 $ hg phase 'all()'
1557 $ hg phase 'all()'
1558 0: draft
1558 0: draft
1559 1: secret
1559 1: secret
1560 2: secret
1560 2: secret
1561
1561
1562 Test that secret mq patch does not break hgweb
1562 Test that secret mq patch does not break hgweb
1563
1563
1564 $ cat > hgweb.cgi <<HGWEB
1564 $ cat > hgweb.cgi <<HGWEB
1565 > from mercurial import demandimport; demandimport.enable()
1565 > from mercurial import demandimport; demandimport.enable()
1566 > from mercurial.hgweb import hgweb
1566 > from mercurial.hgweb import hgweb
1567 > from mercurial.hgweb import wsgicgi
1567 > from mercurial.hgweb import wsgicgi
1568 > import cgitb
1568 > import cgitb
1569 > cgitb.enable()
1569 > cgitb.enable()
1570 > app = hgweb('.', 'test')
1570 > app = hgweb('.', 'test')
1571 > wsgicgi.launch(app)
1571 > wsgicgi.launch(app)
1572 > HGWEB
1572 > HGWEB
1573 $ . "$TESTDIR/cgienv"
1573 $ . "$TESTDIR/cgienv"
1574 #if msys
1574 #if msys
1575 $ PATH_INFO=//tags; export PATH_INFO
1575 $ PATH_INFO=//tags; export PATH_INFO
1576 #else
1576 #else
1577 $ PATH_INFO=/tags; export PATH_INFO
1577 $ PATH_INFO=/tags; export PATH_INFO
1578 #endif
1578 #endif
1579 $ QUERY_STRING='style=raw'
1579 $ QUERY_STRING='style=raw'
1580 $ python hgweb.cgi | grep '^tip'
1580 $ python hgweb.cgi | grep '^tip'
1581 tip [0-9a-f]{40} (re)
1581 tip [0-9a-f]{40} (re)
1582
1582
1583 $ cd ..
1583 $ cd ..
1584
1585 Test interraction with revset (issue4426)
1586
1587 $ hg init issue4426
1588 $ cd issue4426
1589
1590 $ echo a > a
1591 $ hg ci -Am a
1592 adding a
1593 $ echo a >> a
1594 $ hg ci -m a
1595 $ echo a >> a
1596 $ hg ci -m a
1597 $ hg qimport -r 0::
1598
1599 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now