##// END OF EJS Templates
revset: extend fullreposet to make "null" revision magically appears in set...
Yuya Nishihara -
r24204:d2de20e1 default
parent child Browse files
Show More
@@ -1,3345 +1,3344 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import re
8 import re
9 import parser, util, error, discovery, hbisect, phases
9 import parser, util, error, discovery, hbisect, phases
10 import node
10 import node
11 import heapq
11 import heapq
12 import match as matchmod
12 import match as matchmod
13 from i18n import _
13 from i18n import _
14 import encoding
14 import encoding
15 import obsolete as obsmod
15 import obsolete as obsmod
16 import pathutil
16 import pathutil
17 import repoview
17 import repoview
18
18
19 def _revancestors(repo, revs, followfirst):
19 def _revancestors(repo, revs, followfirst):
20 """Like revlog.ancestors(), but supports followfirst."""
20 """Like revlog.ancestors(), but supports followfirst."""
21 cut = followfirst and 1 or None
21 cut = followfirst and 1 or None
22 cl = repo.changelog
22 cl = repo.changelog
23
23
24 def iterate():
24 def iterate():
25 revqueue, revsnode = None, None
25 revqueue, revsnode = None, None
26 h = []
26 h = []
27
27
28 revs.sort(reverse=True)
28 revs.sort(reverse=True)
29 revqueue = util.deque(revs)
29 revqueue = util.deque(revs)
30 if revqueue:
30 if revqueue:
31 revsnode = revqueue.popleft()
31 revsnode = revqueue.popleft()
32 heapq.heappush(h, -revsnode)
32 heapq.heappush(h, -revsnode)
33
33
34 seen = set()
34 seen = set()
35 while h:
35 while h:
36 current = -heapq.heappop(h)
36 current = -heapq.heappop(h)
37 if current not in seen:
37 if current not in seen:
38 if revsnode and current == revsnode:
38 if revsnode and current == revsnode:
39 if revqueue:
39 if revqueue:
40 revsnode = revqueue.popleft()
40 revsnode = revqueue.popleft()
41 heapq.heappush(h, -revsnode)
41 heapq.heappush(h, -revsnode)
42 seen.add(current)
42 seen.add(current)
43 yield current
43 yield current
44 for parent in cl.parentrevs(current)[:cut]:
44 for parent in cl.parentrevs(current)[:cut]:
45 if parent != node.nullrev:
45 if parent != node.nullrev:
46 heapq.heappush(h, -parent)
46 heapq.heappush(h, -parent)
47
47
48 return generatorset(iterate(), iterasc=False)
48 return generatorset(iterate(), iterasc=False)
49
49
50 def _revdescendants(repo, revs, followfirst):
50 def _revdescendants(repo, revs, followfirst):
51 """Like revlog.descendants() but supports followfirst."""
51 """Like revlog.descendants() but supports followfirst."""
52 cut = followfirst and 1 or None
52 cut = followfirst and 1 or None
53
53
54 def iterate():
54 def iterate():
55 cl = repo.changelog
55 cl = repo.changelog
56 first = min(revs)
56 first = min(revs)
57 nullrev = node.nullrev
57 nullrev = node.nullrev
58 if first == nullrev:
58 if first == nullrev:
59 # Are there nodes with a null first parent and a non-null
59 # Are there nodes with a null first parent and a non-null
60 # second one? Maybe. Do we care? Probably not.
60 # second one? Maybe. Do we care? Probably not.
61 for i in cl:
61 for i in cl:
62 yield i
62 yield i
63 else:
63 else:
64 seen = set(revs)
64 seen = set(revs)
65 for i in cl.revs(first + 1):
65 for i in cl.revs(first + 1):
66 for x in cl.parentrevs(i)[:cut]:
66 for x in cl.parentrevs(i)[:cut]:
67 if x != nullrev and x in seen:
67 if x != nullrev and x in seen:
68 seen.add(i)
68 seen.add(i)
69 yield i
69 yield i
70 break
70 break
71
71
72 return generatorset(iterate(), iterasc=True)
72 return generatorset(iterate(), iterasc=True)
73
73
74 def _revsbetween(repo, roots, heads):
74 def _revsbetween(repo, roots, heads):
75 """Return all paths between roots and heads, inclusive of both endpoint
75 """Return all paths between roots and heads, inclusive of both endpoint
76 sets."""
76 sets."""
77 if not roots:
77 if not roots:
78 return baseset()
78 return baseset()
79 parentrevs = repo.changelog.parentrevs
79 parentrevs = repo.changelog.parentrevs
80 visit = list(heads)
80 visit = list(heads)
81 reachable = set()
81 reachable = set()
82 seen = {}
82 seen = {}
83 minroot = min(roots)
83 minroot = min(roots)
84 roots = set(roots)
84 roots = set(roots)
85 # open-code the post-order traversal due to the tiny size of
85 # open-code the post-order traversal due to the tiny size of
86 # sys.getrecursionlimit()
86 # sys.getrecursionlimit()
87 while visit:
87 while visit:
88 rev = visit.pop()
88 rev = visit.pop()
89 if rev in roots:
89 if rev in roots:
90 reachable.add(rev)
90 reachable.add(rev)
91 parents = parentrevs(rev)
91 parents = parentrevs(rev)
92 seen[rev] = parents
92 seen[rev] = parents
93 for parent in parents:
93 for parent in parents:
94 if parent >= minroot and parent not in seen:
94 if parent >= minroot and parent not in seen:
95 visit.append(parent)
95 visit.append(parent)
96 if not reachable:
96 if not reachable:
97 return baseset()
97 return baseset()
98 for rev in sorted(seen):
98 for rev in sorted(seen):
99 for parent in seen[rev]:
99 for parent in seen[rev]:
100 if parent in reachable:
100 if parent in reachable:
101 reachable.add(rev)
101 reachable.add(rev)
102 return baseset(sorted(reachable))
102 return baseset(sorted(reachable))
103
103
104 elements = {
104 elements = {
105 "(": (21, ("group", 1, ")"), ("func", 1, ")")),
105 "(": (21, ("group", 1, ")"), ("func", 1, ")")),
106 "##": (20, None, ("_concat", 20)),
106 "##": (20, None, ("_concat", 20)),
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 "%": (5, None, ("only", 5), ("onlypost", 5)),
119 "%": (5, None, ("only", 5), ("onlypost", 5)),
120 "or": (4, None, ("or", 4)),
120 "or": (4, None, ("or", 4)),
121 "|": (4, None, ("or", 4)),
121 "|": (4, None, ("or", 4)),
122 "+": (4, None, ("or", 4)),
122 "+": (4, None, ("or", 4)),
123 ",": (2, None, ("list", 2)),
123 ",": (2, None, ("list", 2)),
124 ")": (0, None, None),
124 ")": (0, None, None),
125 "symbol": (0, ("symbol",), None),
125 "symbol": (0, ("symbol",), None),
126 "string": (0, ("string",), None),
126 "string": (0, ("string",), None),
127 "end": (0, None, None),
127 "end": (0, None, None),
128 }
128 }
129
129
130 keywords = set(['and', 'or', 'not'])
130 keywords = set(['and', 'or', 'not'])
131
131
132 # default set of valid characters for the initial letter of symbols
132 # default set of valid characters for the initial letter of symbols
133 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
133 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
134 if c.isalnum() or c in '._@' or ord(c) > 127)
134 if c.isalnum() or c in '._@' or ord(c) > 127)
135
135
136 # default set of valid characters for non-initial letters of symbols
136 # default set of valid characters for non-initial letters of symbols
137 _symletters = set(c for c in [chr(i) for i in xrange(256)]
137 _symletters = set(c for c in [chr(i) for i in xrange(256)]
138 if c.isalnum() or c in '-._/@' or ord(c) > 127)
138 if c.isalnum() or c in '-._/@' or ord(c) > 127)
139
139
140 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
140 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
141 '''
141 '''
142 Parse a revset statement into a stream of tokens
142 Parse a revset statement into a stream of tokens
143
143
144 ``syminitletters`` is the set of valid characters for the initial
144 ``syminitletters`` is the set of valid characters for the initial
145 letter of symbols.
145 letter of symbols.
146
146
147 By default, character ``c`` is recognized as valid for initial
147 By default, character ``c`` is recognized as valid for initial
148 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
148 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
149
149
150 ``symletters`` is the set of valid characters for non-initial
150 ``symletters`` is the set of valid characters for non-initial
151 letters of symbols.
151 letters of symbols.
152
152
153 By default, character ``c`` is recognized as valid for non-initial
153 By default, character ``c`` is recognized as valid for non-initial
154 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
154 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
155
155
156 Check that @ is a valid unquoted token character (issue3686):
156 Check that @ is a valid unquoted token character (issue3686):
157 >>> list(tokenize("@::"))
157 >>> list(tokenize("@::"))
158 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
158 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
159
159
160 '''
160 '''
161 if syminitletters is None:
161 if syminitletters is None:
162 syminitletters = _syminitletters
162 syminitletters = _syminitletters
163 if symletters is None:
163 if symletters is None:
164 symletters = _symletters
164 symletters = _symletters
165
165
166 pos, l = 0, len(program)
166 pos, l = 0, len(program)
167 while pos < l:
167 while pos < l:
168 c = program[pos]
168 c = program[pos]
169 if c.isspace(): # skip inter-token whitespace
169 if c.isspace(): # skip inter-token whitespace
170 pass
170 pass
171 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
171 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
172 yield ('::', None, pos)
172 yield ('::', None, pos)
173 pos += 1 # skip ahead
173 pos += 1 # skip ahead
174 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
174 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
175 yield ('..', None, pos)
175 yield ('..', None, pos)
176 pos += 1 # skip ahead
176 pos += 1 # skip ahead
177 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
177 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
178 yield ('##', None, pos)
178 yield ('##', None, pos)
179 pos += 1 # skip ahead
179 pos += 1 # skip ahead
180 elif c in "():,-|&+!~^%": # handle simple operators
180 elif c in "():,-|&+!~^%": # handle simple operators
181 yield (c, None, pos)
181 yield (c, None, pos)
182 elif (c in '"\'' or c == 'r' and
182 elif (c in '"\'' or c == 'r' and
183 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
183 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
184 if c == 'r':
184 if c == 'r':
185 pos += 1
185 pos += 1
186 c = program[pos]
186 c = program[pos]
187 decode = lambda x: x
187 decode = lambda x: x
188 else:
188 else:
189 decode = lambda x: x.decode('string-escape')
189 decode = lambda x: x.decode('string-escape')
190 pos += 1
190 pos += 1
191 s = pos
191 s = pos
192 while pos < l: # find closing quote
192 while pos < l: # find closing quote
193 d = program[pos]
193 d = program[pos]
194 if d == '\\': # skip over escaped characters
194 if d == '\\': # skip over escaped characters
195 pos += 2
195 pos += 2
196 continue
196 continue
197 if d == c:
197 if d == c:
198 yield ('string', decode(program[s:pos]), s)
198 yield ('string', decode(program[s:pos]), s)
199 break
199 break
200 pos += 1
200 pos += 1
201 else:
201 else:
202 raise error.ParseError(_("unterminated string"), s)
202 raise error.ParseError(_("unterminated string"), s)
203 # gather up a symbol/keyword
203 # gather up a symbol/keyword
204 elif c in syminitletters:
204 elif c in syminitletters:
205 s = pos
205 s = pos
206 pos += 1
206 pos += 1
207 while pos < l: # find end of symbol
207 while pos < l: # find end of symbol
208 d = program[pos]
208 d = program[pos]
209 if d not in symletters:
209 if d not in symletters:
210 break
210 break
211 if d == '.' and program[pos - 1] == '.': # special case for ..
211 if d == '.' and program[pos - 1] == '.': # special case for ..
212 pos -= 1
212 pos -= 1
213 break
213 break
214 pos += 1
214 pos += 1
215 sym = program[s:pos]
215 sym = program[s:pos]
216 if sym in keywords: # operator keywords
216 if sym in keywords: # operator keywords
217 yield (sym, None, s)
217 yield (sym, None, s)
218 elif '-' in sym:
218 elif '-' in sym:
219 # some jerk gave us foo-bar-baz, try to check if it's a symbol
219 # some jerk gave us foo-bar-baz, try to check if it's a symbol
220 if lookup and lookup(sym):
220 if lookup and lookup(sym):
221 # looks like a real symbol
221 # looks like a real symbol
222 yield ('symbol', sym, s)
222 yield ('symbol', sym, s)
223 else:
223 else:
224 # looks like an expression
224 # looks like an expression
225 parts = sym.split('-')
225 parts = sym.split('-')
226 for p in parts[:-1]:
226 for p in parts[:-1]:
227 if p: # possible consecutive -
227 if p: # possible consecutive -
228 yield ('symbol', p, s)
228 yield ('symbol', p, s)
229 s += len(p)
229 s += len(p)
230 yield ('-', None, pos)
230 yield ('-', None, pos)
231 s += 1
231 s += 1
232 if parts[-1]: # possible trailing -
232 if parts[-1]: # possible trailing -
233 yield ('symbol', parts[-1], s)
233 yield ('symbol', parts[-1], s)
234 else:
234 else:
235 yield ('symbol', sym, s)
235 yield ('symbol', sym, s)
236 pos -= 1
236 pos -= 1
237 else:
237 else:
238 raise error.ParseError(_("syntax error"), pos)
238 raise error.ParseError(_("syntax error"), pos)
239 pos += 1
239 pos += 1
240 yield ('end', None, pos)
240 yield ('end', None, pos)
241
241
242 def parseerrordetail(inst):
242 def parseerrordetail(inst):
243 """Compose error message from specified ParseError object
243 """Compose error message from specified ParseError object
244 """
244 """
245 if len(inst.args) > 1:
245 if len(inst.args) > 1:
246 return _('at %s: %s') % (inst.args[1], inst.args[0])
246 return _('at %s: %s') % (inst.args[1], inst.args[0])
247 else:
247 else:
248 return inst.args[0]
248 return inst.args[0]
249
249
250 # helpers
250 # helpers
251
251
252 def getstring(x, err):
252 def getstring(x, err):
253 if x and (x[0] == 'string' or x[0] == 'symbol'):
253 if x and (x[0] == 'string' or x[0] == 'symbol'):
254 return x[1]
254 return x[1]
255 raise error.ParseError(err)
255 raise error.ParseError(err)
256
256
257 def getlist(x):
257 def getlist(x):
258 if not x:
258 if not x:
259 return []
259 return []
260 if x[0] == 'list':
260 if x[0] == 'list':
261 return getlist(x[1]) + [x[2]]
261 return getlist(x[1]) + [x[2]]
262 return [x]
262 return [x]
263
263
264 def getargs(x, min, max, err):
264 def getargs(x, min, max, err):
265 l = getlist(x)
265 l = getlist(x)
266 if len(l) < min or (max >= 0 and len(l) > max):
266 if len(l) < min or (max >= 0 and len(l) > max):
267 raise error.ParseError(err)
267 raise error.ParseError(err)
268 return l
268 return l
269
269
270 def isvalidsymbol(tree):
270 def isvalidsymbol(tree):
271 """Examine whether specified ``tree`` is valid ``symbol`` or not
271 """Examine whether specified ``tree`` is valid ``symbol`` or not
272 """
272 """
273 return tree[0] == 'symbol' and len(tree) > 1
273 return tree[0] == 'symbol' and len(tree) > 1
274
274
275 def getsymbol(tree):
275 def getsymbol(tree):
276 """Get symbol name from valid ``symbol`` in ``tree``
276 """Get symbol name from valid ``symbol`` in ``tree``
277
277
278 This assumes that ``tree`` is already examined by ``isvalidsymbol``.
278 This assumes that ``tree`` is already examined by ``isvalidsymbol``.
279 """
279 """
280 return tree[1]
280 return tree[1]
281
281
282 def isvalidfunc(tree):
282 def isvalidfunc(tree):
283 """Examine whether specified ``tree`` is valid ``func`` or not
283 """Examine whether specified ``tree`` is valid ``func`` or not
284 """
284 """
285 return tree[0] == 'func' and len(tree) > 1 and isvalidsymbol(tree[1])
285 return tree[0] == 'func' and len(tree) > 1 and isvalidsymbol(tree[1])
286
286
287 def getfuncname(tree):
287 def getfuncname(tree):
288 """Get function name from valid ``func`` in ``tree``
288 """Get function name from valid ``func`` in ``tree``
289
289
290 This assumes that ``tree`` is already examined by ``isvalidfunc``.
290 This assumes that ``tree`` is already examined by ``isvalidfunc``.
291 """
291 """
292 return getsymbol(tree[1])
292 return getsymbol(tree[1])
293
293
294 def getfuncargs(tree):
294 def getfuncargs(tree):
295 """Get list of function arguments from valid ``func`` in ``tree``
295 """Get list of function arguments from valid ``func`` in ``tree``
296
296
297 This assumes that ``tree`` is already examined by ``isvalidfunc``.
297 This assumes that ``tree`` is already examined by ``isvalidfunc``.
298 """
298 """
299 if len(tree) > 2:
299 if len(tree) > 2:
300 return getlist(tree[2])
300 return getlist(tree[2])
301 else:
301 else:
302 return []
302 return []
303
303
304 def getset(repo, subset, x):
304 def getset(repo, subset, x):
305 if not x:
305 if not x:
306 raise error.ParseError(_("missing argument"))
306 raise error.ParseError(_("missing argument"))
307 s = methods[x[0]](repo, subset, *x[1:])
307 s = methods[x[0]](repo, subset, *x[1:])
308 if util.safehasattr(s, 'isascending'):
308 if util.safehasattr(s, 'isascending'):
309 return s
309 return s
310 return baseset(s)
310 return baseset(s)
311
311
312 def _getrevsource(repo, r):
312 def _getrevsource(repo, r):
313 extra = repo[r].extra()
313 extra = repo[r].extra()
314 for label in ('source', 'transplant_source', 'rebase_source'):
314 for label in ('source', 'transplant_source', 'rebase_source'):
315 if label in extra:
315 if label in extra:
316 try:
316 try:
317 return repo[extra[label]].rev()
317 return repo[extra[label]].rev()
318 except error.RepoLookupError:
318 except error.RepoLookupError:
319 pass
319 pass
320 return None
320 return None
321
321
322 # operator methods
322 # operator methods
323
323
324 def stringset(repo, subset, x):
324 def stringset(repo, subset, x):
325 x = repo[x].rev()
325 x = repo[x].rev()
326 if x == -1 and len(subset) == len(repo):
327 return baseset([-1])
328 if x in subset:
326 if x in subset:
329 return baseset([x])
327 return baseset([x])
330 return baseset()
328 return baseset()
331
329
332 def symbolset(repo, subset, x):
330 def symbolset(repo, subset, x):
333 if x in symbols:
331 if x in symbols:
334 raise error.ParseError(_("can't use %s here") % x)
332 raise error.ParseError(_("can't use %s here") % x)
335 return stringset(repo, subset, x)
333 return stringset(repo, subset, x)
336
334
337 def rangeset(repo, subset, x, y):
335 def rangeset(repo, subset, x, y):
338 m = getset(repo, fullreposet(repo), x)
336 m = getset(repo, fullreposet(repo), x)
339 n = getset(repo, fullreposet(repo), y)
337 n = getset(repo, fullreposet(repo), y)
340
338
341 if not m or not n:
339 if not m or not n:
342 return baseset()
340 return baseset()
343 m, n = m.first(), n.last()
341 m, n = m.first(), n.last()
344
342
345 if m < n:
343 if m < n:
346 r = spanset(repo, m, n + 1)
344 r = spanset(repo, m, n + 1)
347 else:
345 else:
348 r = spanset(repo, m, n - 1)
346 r = spanset(repo, m, n - 1)
349 return r & subset
347 return r & subset
350
348
351 def dagrange(repo, subset, x, y):
349 def dagrange(repo, subset, x, y):
352 r = fullreposet(repo)
350 r = fullreposet(repo)
353 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
351 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
354 return xs & subset
352 return xs & subset
355
353
356 def andset(repo, subset, x, y):
354 def andset(repo, subset, x, y):
357 return getset(repo, getset(repo, subset, x), y)
355 return getset(repo, getset(repo, subset, x), y)
358
356
359 def orset(repo, subset, x, y):
357 def orset(repo, subset, x, y):
360 xl = getset(repo, subset, x)
358 xl = getset(repo, subset, x)
361 yl = getset(repo, subset - xl, y)
359 yl = getset(repo, subset - xl, y)
362 return xl + yl
360 return xl + yl
363
361
364 def notset(repo, subset, x):
362 def notset(repo, subset, x):
365 return subset - getset(repo, subset, x)
363 return subset - getset(repo, subset, x)
366
364
367 def listset(repo, subset, a, b):
365 def listset(repo, subset, a, b):
368 raise error.ParseError(_("can't use a list in this context"))
366 raise error.ParseError(_("can't use a list in this context"))
369
367
370 def func(repo, subset, a, b):
368 def func(repo, subset, a, b):
371 if a[0] == 'symbol' and a[1] in symbols:
369 if a[0] == 'symbol' and a[1] in symbols:
372 return symbols[a[1]](repo, subset, b)
370 return symbols[a[1]](repo, subset, b)
373 raise error.ParseError(_("not a function: %s") % a[1])
371 raise error.ParseError(_("not a function: %s") % a[1])
374
372
375 # functions
373 # functions
376
374
377 def adds(repo, subset, x):
375 def adds(repo, subset, x):
378 """``adds(pattern)``
376 """``adds(pattern)``
379 Changesets that add a file matching pattern.
377 Changesets that add a file matching pattern.
380
378
381 The pattern without explicit kind like ``glob:`` is expected to be
379 The pattern without explicit kind like ``glob:`` is expected to be
382 relative to the current directory and match against a file or a
380 relative to the current directory and match against a file or a
383 directory.
381 directory.
384 """
382 """
385 # i18n: "adds" is a keyword
383 # i18n: "adds" is a keyword
386 pat = getstring(x, _("adds requires a pattern"))
384 pat = getstring(x, _("adds requires a pattern"))
387 return checkstatus(repo, subset, pat, 1)
385 return checkstatus(repo, subset, pat, 1)
388
386
389 def ancestor(repo, subset, x):
387 def ancestor(repo, subset, x):
390 """``ancestor(*changeset)``
388 """``ancestor(*changeset)``
391 A greatest common ancestor of the changesets.
389 A greatest common ancestor of the changesets.
392
390
393 Accepts 0 or more changesets.
391 Accepts 0 or more changesets.
394 Will return empty list when passed no args.
392 Will return empty list when passed no args.
395 Greatest common ancestor of a single changeset is that changeset.
393 Greatest common ancestor of a single changeset is that changeset.
396 """
394 """
397 # i18n: "ancestor" is a keyword
395 # i18n: "ancestor" is a keyword
398 l = getlist(x)
396 l = getlist(x)
399 rl = fullreposet(repo)
397 rl = fullreposet(repo)
400 anc = None
398 anc = None
401
399
402 # (getset(repo, rl, i) for i in l) generates a list of lists
400 # (getset(repo, rl, i) for i in l) generates a list of lists
403 for revs in (getset(repo, rl, i) for i in l):
401 for revs in (getset(repo, rl, i) for i in l):
404 for r in revs:
402 for r in revs:
405 if anc is None:
403 if anc is None:
406 anc = repo[r]
404 anc = repo[r]
407 else:
405 else:
408 anc = anc.ancestor(repo[r])
406 anc = anc.ancestor(repo[r])
409
407
410 if anc is not None and anc.rev() in subset:
408 if anc is not None and anc.rev() in subset:
411 return baseset([anc.rev()])
409 return baseset([anc.rev()])
412 return baseset()
410 return baseset()
413
411
414 def _ancestors(repo, subset, x, followfirst=False):
412 def _ancestors(repo, subset, x, followfirst=False):
415 heads = getset(repo, fullreposet(repo), x)
413 heads = getset(repo, fullreposet(repo), x)
416 if not heads:
414 if not heads:
417 return baseset()
415 return baseset()
418 s = _revancestors(repo, heads, followfirst)
416 s = _revancestors(repo, heads, followfirst)
419 return subset & s
417 return subset & s
420
418
421 def ancestors(repo, subset, x):
419 def ancestors(repo, subset, x):
422 """``ancestors(set)``
420 """``ancestors(set)``
423 Changesets that are ancestors of a changeset in set.
421 Changesets that are ancestors of a changeset in set.
424 """
422 """
425 return _ancestors(repo, subset, x)
423 return _ancestors(repo, subset, x)
426
424
427 def _firstancestors(repo, subset, x):
425 def _firstancestors(repo, subset, x):
428 # ``_firstancestors(set)``
426 # ``_firstancestors(set)``
429 # Like ``ancestors(set)`` but follows only the first parents.
427 # Like ``ancestors(set)`` but follows only the first parents.
430 return _ancestors(repo, subset, x, followfirst=True)
428 return _ancestors(repo, subset, x, followfirst=True)
431
429
432 def ancestorspec(repo, subset, x, n):
430 def ancestorspec(repo, subset, x, n):
433 """``set~n``
431 """``set~n``
434 Changesets that are the Nth ancestor (first parents only) of a changeset
432 Changesets that are the Nth ancestor (first parents only) of a changeset
435 in set.
433 in set.
436 """
434 """
437 try:
435 try:
438 n = int(n[1])
436 n = int(n[1])
439 except (TypeError, ValueError):
437 except (TypeError, ValueError):
440 raise error.ParseError(_("~ expects a number"))
438 raise error.ParseError(_("~ expects a number"))
441 ps = set()
439 ps = set()
442 cl = repo.changelog
440 cl = repo.changelog
443 for r in getset(repo, fullreposet(repo), x):
441 for r in getset(repo, fullreposet(repo), x):
444 for i in range(n):
442 for i in range(n):
445 r = cl.parentrevs(r)[0]
443 r = cl.parentrevs(r)[0]
446 ps.add(r)
444 ps.add(r)
447 return subset & ps
445 return subset & ps
448
446
449 def author(repo, subset, x):
447 def author(repo, subset, x):
450 """``author(string)``
448 """``author(string)``
451 Alias for ``user(string)``.
449 Alias for ``user(string)``.
452 """
450 """
453 # i18n: "author" is a keyword
451 # i18n: "author" is a keyword
454 n = encoding.lower(getstring(x, _("author requires a string")))
452 n = encoding.lower(getstring(x, _("author requires a string")))
455 kind, pattern, matcher = _substringmatcher(n)
453 kind, pattern, matcher = _substringmatcher(n)
456 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())))
454 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())))
457
455
458 def bisect(repo, subset, x):
456 def bisect(repo, subset, x):
459 """``bisect(string)``
457 """``bisect(string)``
460 Changesets marked in the specified bisect status:
458 Changesets marked in the specified bisect status:
461
459
462 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
460 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
463 - ``goods``, ``bads`` : csets topologically good/bad
461 - ``goods``, ``bads`` : csets topologically good/bad
464 - ``range`` : csets taking part in the bisection
462 - ``range`` : csets taking part in the bisection
465 - ``pruned`` : csets that are goods, bads or skipped
463 - ``pruned`` : csets that are goods, bads or skipped
466 - ``untested`` : csets whose fate is yet unknown
464 - ``untested`` : csets whose fate is yet unknown
467 - ``ignored`` : csets ignored due to DAG topology
465 - ``ignored`` : csets ignored due to DAG topology
468 - ``current`` : the cset currently being bisected
466 - ``current`` : the cset currently being bisected
469 """
467 """
470 # i18n: "bisect" is a keyword
468 # i18n: "bisect" is a keyword
471 status = getstring(x, _("bisect requires a string")).lower()
469 status = getstring(x, _("bisect requires a string")).lower()
472 state = set(hbisect.get(repo, status))
470 state = set(hbisect.get(repo, status))
473 return subset & state
471 return subset & state
474
472
475 # Backward-compatibility
473 # Backward-compatibility
476 # - no help entry so that we do not advertise it any more
474 # - no help entry so that we do not advertise it any more
477 def bisected(repo, subset, x):
475 def bisected(repo, subset, x):
478 return bisect(repo, subset, x)
476 return bisect(repo, subset, x)
479
477
480 def bookmark(repo, subset, x):
478 def bookmark(repo, subset, x):
481 """``bookmark([name])``
479 """``bookmark([name])``
482 The named bookmark or all bookmarks.
480 The named bookmark or all bookmarks.
483
481
484 If `name` starts with `re:`, the remainder of the name is treated as
482 If `name` starts with `re:`, the remainder of the name is treated as
485 a regular expression. To match a bookmark that actually starts with `re:`,
483 a regular expression. To match a bookmark that actually starts with `re:`,
486 use the prefix `literal:`.
484 use the prefix `literal:`.
487 """
485 """
488 # i18n: "bookmark" is a keyword
486 # i18n: "bookmark" is a keyword
489 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
487 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
490 if args:
488 if args:
491 bm = getstring(args[0],
489 bm = getstring(args[0],
492 # i18n: "bookmark" is a keyword
490 # i18n: "bookmark" is a keyword
493 _('the argument to bookmark must be a string'))
491 _('the argument to bookmark must be a string'))
494 kind, pattern, matcher = _stringmatcher(bm)
492 kind, pattern, matcher = _stringmatcher(bm)
495 bms = set()
493 bms = set()
496 if kind == 'literal':
494 if kind == 'literal':
497 bmrev = repo._bookmarks.get(pattern, None)
495 bmrev = repo._bookmarks.get(pattern, None)
498 if not bmrev:
496 if not bmrev:
499 raise error.RepoLookupError(_("bookmark '%s' does not exist")
497 raise error.RepoLookupError(_("bookmark '%s' does not exist")
500 % bm)
498 % bm)
501 bms.add(repo[bmrev].rev())
499 bms.add(repo[bmrev].rev())
502 else:
500 else:
503 matchrevs = set()
501 matchrevs = set()
504 for name, bmrev in repo._bookmarks.iteritems():
502 for name, bmrev in repo._bookmarks.iteritems():
505 if matcher(name):
503 if matcher(name):
506 matchrevs.add(bmrev)
504 matchrevs.add(bmrev)
507 if not matchrevs:
505 if not matchrevs:
508 raise error.RepoLookupError(_("no bookmarks exist"
506 raise error.RepoLookupError(_("no bookmarks exist"
509 " that match '%s'") % pattern)
507 " that match '%s'") % pattern)
510 for bmrev in matchrevs:
508 for bmrev in matchrevs:
511 bms.add(repo[bmrev].rev())
509 bms.add(repo[bmrev].rev())
512 else:
510 else:
513 bms = set([repo[r].rev()
511 bms = set([repo[r].rev()
514 for r in repo._bookmarks.values()])
512 for r in repo._bookmarks.values()])
515 bms -= set([node.nullrev])
513 bms -= set([node.nullrev])
516 return subset & bms
514 return subset & bms
517
515
518 def branch(repo, subset, x):
516 def branch(repo, subset, x):
519 """``branch(string or set)``
517 """``branch(string or set)``
520 All changesets belonging to the given branch or the branches of the given
518 All changesets belonging to the given branch or the branches of the given
521 changesets.
519 changesets.
522
520
523 If `string` starts with `re:`, the remainder of the name is treated as
521 If `string` starts with `re:`, the remainder of the name is treated as
524 a regular expression. To match a branch that actually starts with `re:`,
522 a regular expression. To match a branch that actually starts with `re:`,
525 use the prefix `literal:`.
523 use the prefix `literal:`.
526 """
524 """
527 import branchmap
525 import branchmap
528 urepo = repo.unfiltered()
526 urepo = repo.unfiltered()
529 ucl = urepo.changelog
527 ucl = urepo.changelog
530 getbi = branchmap.revbranchcache(urepo, readonly=True).branchinfo
528 getbi = branchmap.revbranchcache(urepo, readonly=True).branchinfo
531
529
532 try:
530 try:
533 b = getstring(x, '')
531 b = getstring(x, '')
534 except error.ParseError:
532 except error.ParseError:
535 # not a string, but another revspec, e.g. tip()
533 # not a string, but another revspec, e.g. tip()
536 pass
534 pass
537 else:
535 else:
538 kind, pattern, matcher = _stringmatcher(b)
536 kind, pattern, matcher = _stringmatcher(b)
539 if kind == 'literal':
537 if kind == 'literal':
540 # note: falls through to the revspec case if no branch with
538 # note: falls through to the revspec case if no branch with
541 # this name exists
539 # this name exists
542 if pattern in repo.branchmap():
540 if pattern in repo.branchmap():
543 return subset.filter(lambda r: matcher(getbi(ucl, r)[0]))
541 return subset.filter(lambda r: matcher(getbi(ucl, r)[0]))
544 else:
542 else:
545 return subset.filter(lambda r: matcher(getbi(ucl, r)[0]))
543 return subset.filter(lambda r: matcher(getbi(ucl, r)[0]))
546
544
547 s = getset(repo, fullreposet(repo), x)
545 s = getset(repo, fullreposet(repo), x)
548 b = set()
546 b = set()
549 for r in s:
547 for r in s:
550 b.add(getbi(ucl, r)[0])
548 b.add(getbi(ucl, r)[0])
551 c = s.__contains__
549 c = s.__contains__
552 return subset.filter(lambda r: c(r) or getbi(ucl, r)[0] in b)
550 return subset.filter(lambda r: c(r) or getbi(ucl, r)[0] in b)
553
551
554 def bumped(repo, subset, x):
552 def bumped(repo, subset, x):
555 """``bumped()``
553 """``bumped()``
556 Mutable changesets marked as successors of public changesets.
554 Mutable changesets marked as successors of public changesets.
557
555
558 Only non-public and non-obsolete changesets can be `bumped`.
556 Only non-public and non-obsolete changesets can be `bumped`.
559 """
557 """
560 # i18n: "bumped" is a keyword
558 # i18n: "bumped" is a keyword
561 getargs(x, 0, 0, _("bumped takes no arguments"))
559 getargs(x, 0, 0, _("bumped takes no arguments"))
562 bumped = obsmod.getrevs(repo, 'bumped')
560 bumped = obsmod.getrevs(repo, 'bumped')
563 return subset & bumped
561 return subset & bumped
564
562
565 def bundle(repo, subset, x):
563 def bundle(repo, subset, x):
566 """``bundle()``
564 """``bundle()``
567 Changesets in the bundle.
565 Changesets in the bundle.
568
566
569 Bundle must be specified by the -R option."""
567 Bundle must be specified by the -R option."""
570
568
571 try:
569 try:
572 bundlerevs = repo.changelog.bundlerevs
570 bundlerevs = repo.changelog.bundlerevs
573 except AttributeError:
571 except AttributeError:
574 raise util.Abort(_("no bundle provided - specify with -R"))
572 raise util.Abort(_("no bundle provided - specify with -R"))
575 return subset & bundlerevs
573 return subset & bundlerevs
576
574
577 def checkstatus(repo, subset, pat, field):
575 def checkstatus(repo, subset, pat, field):
578 hasset = matchmod.patkind(pat) == 'set'
576 hasset = matchmod.patkind(pat) == 'set'
579
577
580 mcache = [None]
578 mcache = [None]
581 def matches(x):
579 def matches(x):
582 c = repo[x]
580 c = repo[x]
583 if not mcache[0] or hasset:
581 if not mcache[0] or hasset:
584 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
582 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
585 m = mcache[0]
583 m = mcache[0]
586 fname = None
584 fname = None
587 if not m.anypats() and len(m.files()) == 1:
585 if not m.anypats() and len(m.files()) == 1:
588 fname = m.files()[0]
586 fname = m.files()[0]
589 if fname is not None:
587 if fname is not None:
590 if fname not in c.files():
588 if fname not in c.files():
591 return False
589 return False
592 else:
590 else:
593 for f in c.files():
591 for f in c.files():
594 if m(f):
592 if m(f):
595 break
593 break
596 else:
594 else:
597 return False
595 return False
598 files = repo.status(c.p1().node(), c.node())[field]
596 files = repo.status(c.p1().node(), c.node())[field]
599 if fname is not None:
597 if fname is not None:
600 if fname in files:
598 if fname in files:
601 return True
599 return True
602 else:
600 else:
603 for f in files:
601 for f in files:
604 if m(f):
602 if m(f):
605 return True
603 return True
606
604
607 return subset.filter(matches)
605 return subset.filter(matches)
608
606
609 def _children(repo, narrow, parentset):
607 def _children(repo, narrow, parentset):
610 cs = set()
608 cs = set()
611 if not parentset:
609 if not parentset:
612 return baseset(cs)
610 return baseset(cs)
613 pr = repo.changelog.parentrevs
611 pr = repo.changelog.parentrevs
614 minrev = min(parentset)
612 minrev = min(parentset)
615 for r in narrow:
613 for r in narrow:
616 if r <= minrev:
614 if r <= minrev:
617 continue
615 continue
618 for p in pr(r):
616 for p in pr(r):
619 if p in parentset:
617 if p in parentset:
620 cs.add(r)
618 cs.add(r)
621 return baseset(cs)
619 return baseset(cs)
622
620
623 def children(repo, subset, x):
621 def children(repo, subset, x):
624 """``children(set)``
622 """``children(set)``
625 Child changesets of changesets in set.
623 Child changesets of changesets in set.
626 """
624 """
627 s = getset(repo, fullreposet(repo), x)
625 s = getset(repo, fullreposet(repo), x)
628 cs = _children(repo, subset, s)
626 cs = _children(repo, subset, s)
629 return subset & cs
627 return subset & cs
630
628
631 def closed(repo, subset, x):
629 def closed(repo, subset, x):
632 """``closed()``
630 """``closed()``
633 Changeset is closed.
631 Changeset is closed.
634 """
632 """
635 # i18n: "closed" is a keyword
633 # i18n: "closed" is a keyword
636 getargs(x, 0, 0, _("closed takes no arguments"))
634 getargs(x, 0, 0, _("closed takes no arguments"))
637 return subset.filter(lambda r: repo[r].closesbranch())
635 return subset.filter(lambda r: repo[r].closesbranch())
638
636
639 def contains(repo, subset, x):
637 def contains(repo, subset, x):
640 """``contains(pattern)``
638 """``contains(pattern)``
641 The revision's manifest contains a file matching pattern (but might not
639 The revision's manifest contains a file matching pattern (but might not
642 modify it). See :hg:`help patterns` for information about file patterns.
640 modify it). See :hg:`help patterns` for information about file patterns.
643
641
644 The pattern without explicit kind like ``glob:`` is expected to be
642 The pattern without explicit kind like ``glob:`` is expected to be
645 relative to the current directory and match against a file exactly
643 relative to the current directory and match against a file exactly
646 for efficiency.
644 for efficiency.
647 """
645 """
648 # i18n: "contains" is a keyword
646 # i18n: "contains" is a keyword
649 pat = getstring(x, _("contains requires a pattern"))
647 pat = getstring(x, _("contains requires a pattern"))
650
648
651 def matches(x):
649 def matches(x):
652 if not matchmod.patkind(pat):
650 if not matchmod.patkind(pat):
653 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
651 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
654 if pats in repo[x]:
652 if pats in repo[x]:
655 return True
653 return True
656 else:
654 else:
657 c = repo[x]
655 c = repo[x]
658 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
656 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
659 for f in c.manifest():
657 for f in c.manifest():
660 if m(f):
658 if m(f):
661 return True
659 return True
662 return False
660 return False
663
661
664 return subset.filter(matches)
662 return subset.filter(matches)
665
663
666 def converted(repo, subset, x):
664 def converted(repo, subset, x):
667 """``converted([id])``
665 """``converted([id])``
668 Changesets converted from the given identifier in the old repository if
666 Changesets converted from the given identifier in the old repository if
669 present, or all converted changesets if no identifier is specified.
667 present, or all converted changesets if no identifier is specified.
670 """
668 """
671
669
672 # There is exactly no chance of resolving the revision, so do a simple
670 # There is exactly no chance of resolving the revision, so do a simple
673 # string compare and hope for the best
671 # string compare and hope for the best
674
672
675 rev = None
673 rev = None
676 # i18n: "converted" is a keyword
674 # i18n: "converted" is a keyword
677 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
675 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
678 if l:
676 if l:
679 # i18n: "converted" is a keyword
677 # i18n: "converted" is a keyword
680 rev = getstring(l[0], _('converted requires a revision'))
678 rev = getstring(l[0], _('converted requires a revision'))
681
679
682 def _matchvalue(r):
680 def _matchvalue(r):
683 source = repo[r].extra().get('convert_revision', None)
681 source = repo[r].extra().get('convert_revision', None)
684 return source is not None and (rev is None or source.startswith(rev))
682 return source is not None and (rev is None or source.startswith(rev))
685
683
686 return subset.filter(lambda r: _matchvalue(r))
684 return subset.filter(lambda r: _matchvalue(r))
687
685
688 def date(repo, subset, x):
686 def date(repo, subset, x):
689 """``date(interval)``
687 """``date(interval)``
690 Changesets within the interval, see :hg:`help dates`.
688 Changesets within the interval, see :hg:`help dates`.
691 """
689 """
692 # i18n: "date" is a keyword
690 # i18n: "date" is a keyword
693 ds = getstring(x, _("date requires a string"))
691 ds = getstring(x, _("date requires a string"))
694 dm = util.matchdate(ds)
692 dm = util.matchdate(ds)
695 return subset.filter(lambda x: dm(repo[x].date()[0]))
693 return subset.filter(lambda x: dm(repo[x].date()[0]))
696
694
697 def desc(repo, subset, x):
695 def desc(repo, subset, x):
698 """``desc(string)``
696 """``desc(string)``
699 Search commit message for string. The match is case-insensitive.
697 Search commit message for string. The match is case-insensitive.
700 """
698 """
701 # i18n: "desc" is a keyword
699 # i18n: "desc" is a keyword
702 ds = encoding.lower(getstring(x, _("desc requires a string")))
700 ds = encoding.lower(getstring(x, _("desc requires a string")))
703
701
704 def matches(x):
702 def matches(x):
705 c = repo[x]
703 c = repo[x]
706 return ds in encoding.lower(c.description())
704 return ds in encoding.lower(c.description())
707
705
708 return subset.filter(matches)
706 return subset.filter(matches)
709
707
710 def _descendants(repo, subset, x, followfirst=False):
708 def _descendants(repo, subset, x, followfirst=False):
711 roots = getset(repo, fullreposet(repo), x)
709 roots = getset(repo, fullreposet(repo), x)
712 if not roots:
710 if not roots:
713 return baseset()
711 return baseset()
714 s = _revdescendants(repo, roots, followfirst)
712 s = _revdescendants(repo, roots, followfirst)
715
713
716 # Both sets need to be ascending in order to lazily return the union
714 # Both sets need to be ascending in order to lazily return the union
717 # in the correct order.
715 # in the correct order.
718 base = subset & roots
716 base = subset & roots
719 desc = subset & s
717 desc = subset & s
720 result = base + desc
718 result = base + desc
721 if subset.isascending():
719 if subset.isascending():
722 result.sort()
720 result.sort()
723 elif subset.isdescending():
721 elif subset.isdescending():
724 result.sort(reverse=True)
722 result.sort(reverse=True)
725 else:
723 else:
726 result = subset & result
724 result = subset & result
727 return result
725 return result
728
726
729 def descendants(repo, subset, x):
727 def descendants(repo, subset, x):
730 """``descendants(set)``
728 """``descendants(set)``
731 Changesets which are descendants of changesets in set.
729 Changesets which are descendants of changesets in set.
732 """
730 """
733 return _descendants(repo, subset, x)
731 return _descendants(repo, subset, x)
734
732
735 def _firstdescendants(repo, subset, x):
733 def _firstdescendants(repo, subset, x):
736 # ``_firstdescendants(set)``
734 # ``_firstdescendants(set)``
737 # Like ``descendants(set)`` but follows only the first parents.
735 # Like ``descendants(set)`` but follows only the first parents.
738 return _descendants(repo, subset, x, followfirst=True)
736 return _descendants(repo, subset, x, followfirst=True)
739
737
740 def destination(repo, subset, x):
738 def destination(repo, subset, x):
741 """``destination([set])``
739 """``destination([set])``
742 Changesets that were created by a graft, transplant or rebase operation,
740 Changesets that were created by a graft, transplant or rebase operation,
743 with the given revisions specified as the source. Omitting the optional set
741 with the given revisions specified as the source. Omitting the optional set
744 is the same as passing all().
742 is the same as passing all().
745 """
743 """
746 if x is not None:
744 if x is not None:
747 sources = getset(repo, fullreposet(repo), x)
745 sources = getset(repo, fullreposet(repo), x)
748 else:
746 else:
749 sources = fullreposet(repo)
747 sources = fullreposet(repo)
750
748
751 dests = set()
749 dests = set()
752
750
753 # subset contains all of the possible destinations that can be returned, so
751 # subset contains all of the possible destinations that can be returned, so
754 # iterate over them and see if their source(s) were provided in the arg set.
752 # iterate over them and see if their source(s) were provided in the arg set.
755 # Even if the immediate src of r is not in the arg set, src's source (or
753 # Even if the immediate src of r is not in the arg set, src's source (or
756 # further back) may be. Scanning back further than the immediate src allows
754 # further back) may be. Scanning back further than the immediate src allows
757 # transitive transplants and rebases to yield the same results as transitive
755 # transitive transplants and rebases to yield the same results as transitive
758 # grafts.
756 # grafts.
759 for r in subset:
757 for r in subset:
760 src = _getrevsource(repo, r)
758 src = _getrevsource(repo, r)
761 lineage = None
759 lineage = None
762
760
763 while src is not None:
761 while src is not None:
764 if lineage is None:
762 if lineage is None:
765 lineage = list()
763 lineage = list()
766
764
767 lineage.append(r)
765 lineage.append(r)
768
766
769 # The visited lineage is a match if the current source is in the arg
767 # The visited lineage is a match if the current source is in the arg
770 # set. Since every candidate dest is visited by way of iterating
768 # set. Since every candidate dest is visited by way of iterating
771 # subset, any dests further back in the lineage will be tested by a
769 # subset, any dests further back in the lineage will be tested by a
772 # different iteration over subset. Likewise, if the src was already
770 # different iteration over subset. Likewise, if the src was already
773 # selected, the current lineage can be selected without going back
771 # selected, the current lineage can be selected without going back
774 # further.
772 # further.
775 if src in sources or src in dests:
773 if src in sources or src in dests:
776 dests.update(lineage)
774 dests.update(lineage)
777 break
775 break
778
776
779 r = src
777 r = src
780 src = _getrevsource(repo, r)
778 src = _getrevsource(repo, r)
781
779
782 return subset.filter(dests.__contains__)
780 return subset.filter(dests.__contains__)
783
781
784 def divergent(repo, subset, x):
782 def divergent(repo, subset, x):
785 """``divergent()``
783 """``divergent()``
786 Final successors of changesets with an alternative set of final successors.
784 Final successors of changesets with an alternative set of final successors.
787 """
785 """
788 # i18n: "divergent" is a keyword
786 # i18n: "divergent" is a keyword
789 getargs(x, 0, 0, _("divergent takes no arguments"))
787 getargs(x, 0, 0, _("divergent takes no arguments"))
790 divergent = obsmod.getrevs(repo, 'divergent')
788 divergent = obsmod.getrevs(repo, 'divergent')
791 return subset & divergent
789 return subset & divergent
792
790
793 def draft(repo, subset, x):
791 def draft(repo, subset, x):
794 """``draft()``
792 """``draft()``
795 Changeset in draft phase."""
793 Changeset in draft phase."""
796 # i18n: "draft" is a keyword
794 # i18n: "draft" is a keyword
797 getargs(x, 0, 0, _("draft takes no arguments"))
795 getargs(x, 0, 0, _("draft takes no arguments"))
798 phase = repo._phasecache.phase
796 phase = repo._phasecache.phase
799 target = phases.draft
797 target = phases.draft
800 condition = lambda r: phase(repo, r) == target
798 condition = lambda r: phase(repo, r) == target
801 return subset.filter(condition, cache=False)
799 return subset.filter(condition, cache=False)
802
800
803 def extinct(repo, subset, x):
801 def extinct(repo, subset, x):
804 """``extinct()``
802 """``extinct()``
805 Obsolete changesets with obsolete descendants only.
803 Obsolete changesets with obsolete descendants only.
806 """
804 """
807 # i18n: "extinct" is a keyword
805 # i18n: "extinct" is a keyword
808 getargs(x, 0, 0, _("extinct takes no arguments"))
806 getargs(x, 0, 0, _("extinct takes no arguments"))
809 extincts = obsmod.getrevs(repo, 'extinct')
807 extincts = obsmod.getrevs(repo, 'extinct')
810 return subset & extincts
808 return subset & extincts
811
809
812 def extra(repo, subset, x):
810 def extra(repo, subset, x):
813 """``extra(label, [value])``
811 """``extra(label, [value])``
814 Changesets with the given label in the extra metadata, with the given
812 Changesets with the given label in the extra metadata, with the given
815 optional value.
813 optional value.
816
814
817 If `value` starts with `re:`, the remainder of the value is treated as
815 If `value` starts with `re:`, the remainder of the value is treated as
818 a regular expression. To match a value that actually starts with `re:`,
816 a regular expression. To match a value that actually starts with `re:`,
819 use the prefix `literal:`.
817 use the prefix `literal:`.
820 """
818 """
821
819
822 # i18n: "extra" is a keyword
820 # i18n: "extra" is a keyword
823 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
821 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
824 # i18n: "extra" is a keyword
822 # i18n: "extra" is a keyword
825 label = getstring(l[0], _('first argument to extra must be a string'))
823 label = getstring(l[0], _('first argument to extra must be a string'))
826 value = None
824 value = None
827
825
828 if len(l) > 1:
826 if len(l) > 1:
829 # i18n: "extra" is a keyword
827 # i18n: "extra" is a keyword
830 value = getstring(l[1], _('second argument to extra must be a string'))
828 value = getstring(l[1], _('second argument to extra must be a string'))
831 kind, value, matcher = _stringmatcher(value)
829 kind, value, matcher = _stringmatcher(value)
832
830
833 def _matchvalue(r):
831 def _matchvalue(r):
834 extra = repo[r].extra()
832 extra = repo[r].extra()
835 return label in extra and (value is None or matcher(extra[label]))
833 return label in extra and (value is None or matcher(extra[label]))
836
834
837 return subset.filter(lambda r: _matchvalue(r))
835 return subset.filter(lambda r: _matchvalue(r))
838
836
839 def filelog(repo, subset, x):
837 def filelog(repo, subset, x):
840 """``filelog(pattern)``
838 """``filelog(pattern)``
841 Changesets connected to the specified filelog.
839 Changesets connected to the specified filelog.
842
840
843 For performance reasons, visits only revisions mentioned in the file-level
841 For performance reasons, visits only revisions mentioned in the file-level
844 filelog, rather than filtering through all changesets (much faster, but
842 filelog, rather than filtering through all changesets (much faster, but
845 doesn't include deletes or duplicate changes). For a slower, more accurate
843 doesn't include deletes or duplicate changes). For a slower, more accurate
846 result, use ``file()``.
844 result, use ``file()``.
847
845
848 The pattern without explicit kind like ``glob:`` is expected to be
846 The pattern without explicit kind like ``glob:`` is expected to be
849 relative to the current directory and match against a file exactly
847 relative to the current directory and match against a file exactly
850 for efficiency.
848 for efficiency.
851
849
852 If some linkrev points to revisions filtered by the current repoview, we'll
850 If some linkrev points to revisions filtered by the current repoview, we'll
853 work around it to return a non-filtered value.
851 work around it to return a non-filtered value.
854 """
852 """
855
853
856 # i18n: "filelog" is a keyword
854 # i18n: "filelog" is a keyword
857 pat = getstring(x, _("filelog requires a pattern"))
855 pat = getstring(x, _("filelog requires a pattern"))
858 s = set()
856 s = set()
859 cl = repo.changelog
857 cl = repo.changelog
860
858
861 if not matchmod.patkind(pat):
859 if not matchmod.patkind(pat):
862 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
860 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
863 files = [f]
861 files = [f]
864 else:
862 else:
865 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
863 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
866 files = (f for f in repo[None] if m(f))
864 files = (f for f in repo[None] if m(f))
867
865
868 for f in files:
866 for f in files:
869 backrevref = {} # final value for: filerev -> changerev
867 backrevref = {} # final value for: filerev -> changerev
870 lowestchild = {} # lowest known filerev child of a filerev
868 lowestchild = {} # lowest known filerev child of a filerev
871 delayed = [] # filerev with filtered linkrev, for post-processing
869 delayed = [] # filerev with filtered linkrev, for post-processing
872 lowesthead = None # cache for manifest content of all head revisions
870 lowesthead = None # cache for manifest content of all head revisions
873 fl = repo.file(f)
871 fl = repo.file(f)
874 for fr in list(fl):
872 for fr in list(fl):
875 rev = fl.linkrev(fr)
873 rev = fl.linkrev(fr)
876 if rev not in cl:
874 if rev not in cl:
877 # changerev pointed in linkrev is filtered
875 # changerev pointed in linkrev is filtered
878 # record it for post processing.
876 # record it for post processing.
879 delayed.append((fr, rev))
877 delayed.append((fr, rev))
880 continue
878 continue
881 for p in fl.parentrevs(fr):
879 for p in fl.parentrevs(fr):
882 if 0 <= p and p not in lowestchild:
880 if 0 <= p and p not in lowestchild:
883 lowestchild[p] = fr
881 lowestchild[p] = fr
884 backrevref[fr] = rev
882 backrevref[fr] = rev
885 s.add(rev)
883 s.add(rev)
886
884
887 # Post-processing of all filerevs we skipped because they were
885 # Post-processing of all filerevs we skipped because they were
888 # filtered. If such filerevs have known and unfiltered children, this
886 # filtered. If such filerevs have known and unfiltered children, this
889 # means they have an unfiltered appearance out there. We'll use linkrev
887 # means they have an unfiltered appearance out there. We'll use linkrev
890 # adjustment to find one of these appearances. The lowest known child
888 # adjustment to find one of these appearances. The lowest known child
891 # will be used as a starting point because it is the best upper-bound we
889 # will be used as a starting point because it is the best upper-bound we
892 # have.
890 # have.
893 #
891 #
894 # This approach will fail when an unfiltered but linkrev-shadowed
892 # This approach will fail when an unfiltered but linkrev-shadowed
895 # appearance exists in a head changeset without unfiltered filerev
893 # appearance exists in a head changeset without unfiltered filerev
896 # children anywhere.
894 # children anywhere.
897 while delayed:
895 while delayed:
898 # must be a descending iteration. To slowly fill lowest child
896 # must be a descending iteration. To slowly fill lowest child
899 # information that is of potential use by the next item.
897 # information that is of potential use by the next item.
900 fr, rev = delayed.pop()
898 fr, rev = delayed.pop()
901 lkr = rev
899 lkr = rev
902
900
903 child = lowestchild.get(fr)
901 child = lowestchild.get(fr)
904
902
905 if child is None:
903 if child is None:
906 # search for existence of this file revision in a head revision.
904 # search for existence of this file revision in a head revision.
907 # There are three possibilities:
905 # There are three possibilities:
908 # - the revision exists in a head and we can find an
906 # - the revision exists in a head and we can find an
909 # introduction from there,
907 # introduction from there,
910 # - the revision does not exist in a head because it has been
908 # - the revision does not exist in a head because it has been
911 # changed since its introduction: we would have found a child
909 # changed since its introduction: we would have found a child
912 # and be in the other 'else' clause,
910 # and be in the other 'else' clause,
913 # - all versions of the revision are hidden.
911 # - all versions of the revision are hidden.
914 if lowesthead is None:
912 if lowesthead is None:
915 lowesthead = {}
913 lowesthead = {}
916 for h in repo.heads():
914 for h in repo.heads():
917 fnode = repo[h].manifest().get(f)
915 fnode = repo[h].manifest().get(f)
918 if fnode is not None:
916 if fnode is not None:
919 lowesthead[fl.rev(fnode)] = h
917 lowesthead[fl.rev(fnode)] = h
920 headrev = lowesthead.get(fr)
918 headrev = lowesthead.get(fr)
921 if headrev is None:
919 if headrev is None:
922 # content is nowhere unfiltered
920 # content is nowhere unfiltered
923 continue
921 continue
924 rev = repo[headrev][f].introrev()
922 rev = repo[headrev][f].introrev()
925 else:
923 else:
926 # the lowest known child is a good upper bound
924 # the lowest known child is a good upper bound
927 childcrev = backrevref[child]
925 childcrev = backrevref[child]
928 # XXX this does not guarantee returning the lowest
926 # XXX this does not guarantee returning the lowest
929 # introduction of this revision, but this gives a
927 # introduction of this revision, but this gives a
930 # result which is a good start and will fit in most
928 # result which is a good start and will fit in most
931 # cases. We probably need to fix the multiple
929 # cases. We probably need to fix the multiple
932 # introductions case properly (report each
930 # introductions case properly (report each
933 # introduction, even for identical file revisions)
931 # introduction, even for identical file revisions)
934 # once and for all at some point anyway.
932 # once and for all at some point anyway.
935 for p in repo[childcrev][f].parents():
933 for p in repo[childcrev][f].parents():
936 if p.filerev() == fr:
934 if p.filerev() == fr:
937 rev = p.rev()
935 rev = p.rev()
938 break
936 break
939 if rev == lkr: # no shadowed entry found
937 if rev == lkr: # no shadowed entry found
940 # XXX This should never happen unless some manifest points
938 # XXX This should never happen unless some manifest points
941 # to biggish file revisions (like a revision that uses a
939 # to biggish file revisions (like a revision that uses a
942 # parent that never appears in the manifest ancestors)
940 # parent that never appears in the manifest ancestors)
943 continue
941 continue
944
942
945 # Fill the data for the next iteration.
943 # Fill the data for the next iteration.
946 for p in fl.parentrevs(fr):
944 for p in fl.parentrevs(fr):
947 if 0 <= p and p not in lowestchild:
945 if 0 <= p and p not in lowestchild:
948 lowestchild[p] = fr
946 lowestchild[p] = fr
949 backrevref[fr] = rev
947 backrevref[fr] = rev
950 s.add(rev)
948 s.add(rev)
951
949
952 return subset & s
950 return subset & s
953
951
954 def first(repo, subset, x):
952 def first(repo, subset, x):
955 """``first(set, [n])``
953 """``first(set, [n])``
956 An alias for limit().
954 An alias for limit().
957 """
955 """
958 return limit(repo, subset, x)
956 return limit(repo, subset, x)
959
957
960 def _follow(repo, subset, x, name, followfirst=False):
958 def _follow(repo, subset, x, name, followfirst=False):
961 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
959 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
962 c = repo['.']
960 c = repo['.']
963 if l:
961 if l:
964 x = getstring(l[0], _("%s expected a filename") % name)
962 x = getstring(l[0], _("%s expected a filename") % name)
965 if x in c:
963 if x in c:
966 cx = c[x]
964 cx = c[x]
967 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
965 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
968 # include the revision responsible for the most recent version
966 # include the revision responsible for the most recent version
969 s.add(cx.introrev())
967 s.add(cx.introrev())
970 else:
968 else:
971 return baseset()
969 return baseset()
972 else:
970 else:
973 s = _revancestors(repo, baseset([c.rev()]), followfirst)
971 s = _revancestors(repo, baseset([c.rev()]), followfirst)
974
972
975 return subset & s
973 return subset & s
976
974
977 def follow(repo, subset, x):
975 def follow(repo, subset, x):
978 """``follow([file])``
976 """``follow([file])``
979 An alias for ``::.`` (ancestors of the working copy's first parent).
977 An alias for ``::.`` (ancestors of the working copy's first parent).
980 If a filename is specified, the history of the given file is followed,
978 If a filename is specified, the history of the given file is followed,
981 including copies.
979 including copies.
982 """
980 """
983 return _follow(repo, subset, x, 'follow')
981 return _follow(repo, subset, x, 'follow')
984
982
985 def _followfirst(repo, subset, x):
983 def _followfirst(repo, subset, x):
986 # ``followfirst([file])``
984 # ``followfirst([file])``
987 # Like ``follow([file])`` but follows only the first parent of
985 # Like ``follow([file])`` but follows only the first parent of
988 # every revision or file revision.
986 # every revision or file revision.
989 return _follow(repo, subset, x, '_followfirst', followfirst=True)
987 return _follow(repo, subset, x, '_followfirst', followfirst=True)
990
988
991 def getall(repo, subset, x):
989 def getall(repo, subset, x):
992 """``all()``
990 """``all()``
993 All changesets, the same as ``0:tip``.
991 All changesets, the same as ``0:tip``.
994 """
992 """
995 # i18n: "all" is a keyword
993 # i18n: "all" is a keyword
996 getargs(x, 0, 0, _("all takes no arguments"))
994 getargs(x, 0, 0, _("all takes no arguments"))
997 return subset & spanset(repo) # drop "null" if any
995 return subset & spanset(repo) # drop "null" if any
998
996
999 def grep(repo, subset, x):
997 def grep(repo, subset, x):
1000 """``grep(regex)``
998 """``grep(regex)``
1001 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
999 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1002 to ensure special escape characters are handled correctly. Unlike
1000 to ensure special escape characters are handled correctly. Unlike
1003 ``keyword(string)``, the match is case-sensitive.
1001 ``keyword(string)``, the match is case-sensitive.
1004 """
1002 """
1005 try:
1003 try:
1006 # i18n: "grep" is a keyword
1004 # i18n: "grep" is a keyword
1007 gr = re.compile(getstring(x, _("grep requires a string")))
1005 gr = re.compile(getstring(x, _("grep requires a string")))
1008 except re.error, e:
1006 except re.error, e:
1009 raise error.ParseError(_('invalid match pattern: %s') % e)
1007 raise error.ParseError(_('invalid match pattern: %s') % e)
1010
1008
1011 def matches(x):
1009 def matches(x):
1012 c = repo[x]
1010 c = repo[x]
1013 for e in c.files() + [c.user(), c.description()]:
1011 for e in c.files() + [c.user(), c.description()]:
1014 if gr.search(e):
1012 if gr.search(e):
1015 return True
1013 return True
1016 return False
1014 return False
1017
1015
1018 return subset.filter(matches)
1016 return subset.filter(matches)
1019
1017
1020 def _matchfiles(repo, subset, x):
1018 def _matchfiles(repo, subset, x):
1021 # _matchfiles takes a revset list of prefixed arguments:
1019 # _matchfiles takes a revset list of prefixed arguments:
1022 #
1020 #
1023 # [p:foo, i:bar, x:baz]
1021 # [p:foo, i:bar, x:baz]
1024 #
1022 #
1025 # builds a match object from them and filters subset. Allowed
1023 # builds a match object from them and filters subset. Allowed
1026 # prefixes are 'p:' for regular patterns, 'i:' for include
1024 # prefixes are 'p:' for regular patterns, 'i:' for include
1027 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1025 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1028 # a revision identifier, or the empty string to reference the
1026 # a revision identifier, or the empty string to reference the
1029 # working directory, from which the match object is
1027 # working directory, from which the match object is
1030 # initialized. Use 'd:' to set the default matching mode, default
1028 # initialized. Use 'd:' to set the default matching mode, default
1031 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1029 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1032
1030
1033 # i18n: "_matchfiles" is a keyword
1031 # i18n: "_matchfiles" is a keyword
1034 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
1032 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
1035 pats, inc, exc = [], [], []
1033 pats, inc, exc = [], [], []
1036 rev, default = None, None
1034 rev, default = None, None
1037 for arg in l:
1035 for arg in l:
1038 # i18n: "_matchfiles" is a keyword
1036 # i18n: "_matchfiles" is a keyword
1039 s = getstring(arg, _("_matchfiles requires string arguments"))
1037 s = getstring(arg, _("_matchfiles requires string arguments"))
1040 prefix, value = s[:2], s[2:]
1038 prefix, value = s[:2], s[2:]
1041 if prefix == 'p:':
1039 if prefix == 'p:':
1042 pats.append(value)
1040 pats.append(value)
1043 elif prefix == 'i:':
1041 elif prefix == 'i:':
1044 inc.append(value)
1042 inc.append(value)
1045 elif prefix == 'x:':
1043 elif prefix == 'x:':
1046 exc.append(value)
1044 exc.append(value)
1047 elif prefix == 'r:':
1045 elif prefix == 'r:':
1048 if rev is not None:
1046 if rev is not None:
1049 # i18n: "_matchfiles" is a keyword
1047 # i18n: "_matchfiles" is a keyword
1050 raise error.ParseError(_('_matchfiles expected at most one '
1048 raise error.ParseError(_('_matchfiles expected at most one '
1051 'revision'))
1049 'revision'))
1052 if value != '': # empty means working directory; leave rev as None
1050 if value != '': # empty means working directory; leave rev as None
1053 rev = value
1051 rev = value
1054 elif prefix == 'd:':
1052 elif prefix == 'd:':
1055 if default is not None:
1053 if default is not None:
1056 # i18n: "_matchfiles" is a keyword
1054 # i18n: "_matchfiles" is a keyword
1057 raise error.ParseError(_('_matchfiles expected at most one '
1055 raise error.ParseError(_('_matchfiles expected at most one '
1058 'default mode'))
1056 'default mode'))
1059 default = value
1057 default = value
1060 else:
1058 else:
1061 # i18n: "_matchfiles" is a keyword
1059 # i18n: "_matchfiles" is a keyword
1062 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
1060 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
1063 if not default:
1061 if not default:
1064 default = 'glob'
1062 default = 'glob'
1065
1063
1066 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1064 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1067 exclude=exc, ctx=repo[rev], default=default)
1065 exclude=exc, ctx=repo[rev], default=default)
1068
1066
1069 def matches(x):
1067 def matches(x):
1070 for f in repo[x].files():
1068 for f in repo[x].files():
1071 if m(f):
1069 if m(f):
1072 return True
1070 return True
1073 return False
1071 return False
1074
1072
1075 return subset.filter(matches)
1073 return subset.filter(matches)
1076
1074
1077 def hasfile(repo, subset, x):
1075 def hasfile(repo, subset, x):
1078 """``file(pattern)``
1076 """``file(pattern)``
1079 Changesets affecting files matched by pattern.
1077 Changesets affecting files matched by pattern.
1080
1078
1081 For a faster but less accurate result, consider using ``filelog()``
1079 For a faster but less accurate result, consider using ``filelog()``
1082 instead.
1080 instead.
1083
1081
1084 This predicate uses ``glob:`` as the default kind of pattern.
1082 This predicate uses ``glob:`` as the default kind of pattern.
1085 """
1083 """
1086 # i18n: "file" is a keyword
1084 # i18n: "file" is a keyword
1087 pat = getstring(x, _("file requires a pattern"))
1085 pat = getstring(x, _("file requires a pattern"))
1088 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1086 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1089
1087
1090 def head(repo, subset, x):
1088 def head(repo, subset, x):
1091 """``head()``
1089 """``head()``
1092 Changeset is a named branch head.
1090 Changeset is a named branch head.
1093 """
1091 """
1094 # i18n: "head" is a keyword
1092 # i18n: "head" is a keyword
1095 getargs(x, 0, 0, _("head takes no arguments"))
1093 getargs(x, 0, 0, _("head takes no arguments"))
1096 hs = set()
1094 hs = set()
1097 for b, ls in repo.branchmap().iteritems():
1095 for b, ls in repo.branchmap().iteritems():
1098 hs.update(repo[h].rev() for h in ls)
1096 hs.update(repo[h].rev() for h in ls)
1099 return baseset(hs).filter(subset.__contains__)
1097 return baseset(hs).filter(subset.__contains__)
1100
1098
1101 def heads(repo, subset, x):
1099 def heads(repo, subset, x):
1102 """``heads(set)``
1100 """``heads(set)``
1103 Members of set with no children in set.
1101 Members of set with no children in set.
1104 """
1102 """
1105 s = getset(repo, subset, x)
1103 s = getset(repo, subset, x)
1106 ps = parents(repo, subset, x)
1104 ps = parents(repo, subset, x)
1107 return s - ps
1105 return s - ps
1108
1106
1109 def hidden(repo, subset, x):
1107 def hidden(repo, subset, x):
1110 """``hidden()``
1108 """``hidden()``
1111 Hidden changesets.
1109 Hidden changesets.
1112 """
1110 """
1113 # i18n: "hidden" is a keyword
1111 # i18n: "hidden" is a keyword
1114 getargs(x, 0, 0, _("hidden takes no arguments"))
1112 getargs(x, 0, 0, _("hidden takes no arguments"))
1115 hiddenrevs = repoview.filterrevs(repo, 'visible')
1113 hiddenrevs = repoview.filterrevs(repo, 'visible')
1116 return subset & hiddenrevs
1114 return subset & hiddenrevs
1117
1115
1118 def keyword(repo, subset, x):
1116 def keyword(repo, subset, x):
1119 """``keyword(string)``
1117 """``keyword(string)``
1120 Search commit message, user name, and names of changed files for
1118 Search commit message, user name, and names of changed files for
1121 string. The match is case-insensitive.
1119 string. The match is case-insensitive.
1122 """
1120 """
1123 # i18n: "keyword" is a keyword
1121 # i18n: "keyword" is a keyword
1124 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1122 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1125
1123
1126 def matches(r):
1124 def matches(r):
1127 c = repo[r]
1125 c = repo[r]
1128 return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
1126 return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
1129 c.description()])
1127 c.description()])
1130
1128
1131 return subset.filter(matches)
1129 return subset.filter(matches)
1132
1130
1133 def limit(repo, subset, x):
1131 def limit(repo, subset, x):
1134 """``limit(set, [n])``
1132 """``limit(set, [n])``
1135 First n members of set, defaulting to 1.
1133 First n members of set, defaulting to 1.
1136 """
1134 """
1137 # i18n: "limit" is a keyword
1135 # i18n: "limit" is a keyword
1138 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
1136 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
1139 try:
1137 try:
1140 lim = 1
1138 lim = 1
1141 if len(l) == 2:
1139 if len(l) == 2:
1142 # i18n: "limit" is a keyword
1140 # i18n: "limit" is a keyword
1143 lim = int(getstring(l[1], _("limit requires a number")))
1141 lim = int(getstring(l[1], _("limit requires a number")))
1144 except (TypeError, ValueError):
1142 except (TypeError, ValueError):
1145 # i18n: "limit" is a keyword
1143 # i18n: "limit" is a keyword
1146 raise error.ParseError(_("limit expects a number"))
1144 raise error.ParseError(_("limit expects a number"))
1147 ss = subset
1145 ss = subset
1148 os = getset(repo, fullreposet(repo), l[0])
1146 os = getset(repo, fullreposet(repo), l[0])
1149 result = []
1147 result = []
1150 it = iter(os)
1148 it = iter(os)
1151 for x in xrange(lim):
1149 for x in xrange(lim):
1152 try:
1150 try:
1153 y = it.next()
1151 y = it.next()
1154 if y in ss:
1152 if y in ss:
1155 result.append(y)
1153 result.append(y)
1156 except (StopIteration):
1154 except (StopIteration):
1157 break
1155 break
1158 return baseset(result)
1156 return baseset(result)
1159
1157
1160 def last(repo, subset, x):
1158 def last(repo, subset, x):
1161 """``last(set, [n])``
1159 """``last(set, [n])``
1162 Last n members of set, defaulting to 1.
1160 Last n members of set, defaulting to 1.
1163 """
1161 """
1164 # i18n: "last" is a keyword
1162 # i18n: "last" is a keyword
1165 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1163 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1166 try:
1164 try:
1167 lim = 1
1165 lim = 1
1168 if len(l) == 2:
1166 if len(l) == 2:
1169 # i18n: "last" is a keyword
1167 # i18n: "last" is a keyword
1170 lim = int(getstring(l[1], _("last requires a number")))
1168 lim = int(getstring(l[1], _("last requires a number")))
1171 except (TypeError, ValueError):
1169 except (TypeError, ValueError):
1172 # i18n: "last" is a keyword
1170 # i18n: "last" is a keyword
1173 raise error.ParseError(_("last expects a number"))
1171 raise error.ParseError(_("last expects a number"))
1174 ss = subset
1172 ss = subset
1175 os = getset(repo, fullreposet(repo), l[0])
1173 os = getset(repo, fullreposet(repo), l[0])
1176 os.reverse()
1174 os.reverse()
1177 result = []
1175 result = []
1178 it = iter(os)
1176 it = iter(os)
1179 for x in xrange(lim):
1177 for x in xrange(lim):
1180 try:
1178 try:
1181 y = it.next()
1179 y = it.next()
1182 if y in ss:
1180 if y in ss:
1183 result.append(y)
1181 result.append(y)
1184 except (StopIteration):
1182 except (StopIteration):
1185 break
1183 break
1186 return baseset(result)
1184 return baseset(result)
1187
1185
1188 def maxrev(repo, subset, x):
1186 def maxrev(repo, subset, x):
1189 """``max(set)``
1187 """``max(set)``
1190 Changeset with highest revision number in set.
1188 Changeset with highest revision number in set.
1191 """
1189 """
1192 os = getset(repo, fullreposet(repo), x)
1190 os = getset(repo, fullreposet(repo), x)
1193 if os:
1191 if os:
1194 m = os.max()
1192 m = os.max()
1195 if m in subset:
1193 if m in subset:
1196 return baseset([m])
1194 return baseset([m])
1197 return baseset()
1195 return baseset()
1198
1196
1199 def merge(repo, subset, x):
1197 def merge(repo, subset, x):
1200 """``merge()``
1198 """``merge()``
1201 Changeset is a merge changeset.
1199 Changeset is a merge changeset.
1202 """
1200 """
1203 # i18n: "merge" is a keyword
1201 # i18n: "merge" is a keyword
1204 getargs(x, 0, 0, _("merge takes no arguments"))
1202 getargs(x, 0, 0, _("merge takes no arguments"))
1205 cl = repo.changelog
1203 cl = repo.changelog
1206 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1)
1204 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1)
1207
1205
1208 def branchpoint(repo, subset, x):
1206 def branchpoint(repo, subset, x):
1209 """``branchpoint()``
1207 """``branchpoint()``
1210 Changesets with more than one child.
1208 Changesets with more than one child.
1211 """
1209 """
1212 # i18n: "branchpoint" is a keyword
1210 # i18n: "branchpoint" is a keyword
1213 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1211 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1214 cl = repo.changelog
1212 cl = repo.changelog
1215 if not subset:
1213 if not subset:
1216 return baseset()
1214 return baseset()
1217 baserev = min(subset)
1215 baserev = min(subset)
1218 parentscount = [0]*(len(repo) - baserev)
1216 parentscount = [0]*(len(repo) - baserev)
1219 for r in cl.revs(start=baserev + 1):
1217 for r in cl.revs(start=baserev + 1):
1220 for p in cl.parentrevs(r):
1218 for p in cl.parentrevs(r):
1221 if p >= baserev:
1219 if p >= baserev:
1222 parentscount[p - baserev] += 1
1220 parentscount[p - baserev] += 1
1223 return subset.filter(lambda r: parentscount[r - baserev] > 1)
1221 return subset.filter(lambda r: parentscount[r - baserev] > 1)
1224
1222
1225 def minrev(repo, subset, x):
1223 def minrev(repo, subset, x):
1226 """``min(set)``
1224 """``min(set)``
1227 Changeset with lowest revision number in set.
1225 Changeset with lowest revision number in set.
1228 """
1226 """
1229 os = getset(repo, fullreposet(repo), x)
1227 os = getset(repo, fullreposet(repo), x)
1230 if os:
1228 if os:
1231 m = os.min()
1229 m = os.min()
1232 if m in subset:
1230 if m in subset:
1233 return baseset([m])
1231 return baseset([m])
1234 return baseset()
1232 return baseset()
1235
1233
1236 def modifies(repo, subset, x):
1234 def modifies(repo, subset, x):
1237 """``modifies(pattern)``
1235 """``modifies(pattern)``
1238 Changesets modifying files matched by pattern.
1236 Changesets modifying files matched by pattern.
1239
1237
1240 The pattern without explicit kind like ``glob:`` is expected to be
1238 The pattern without explicit kind like ``glob:`` is expected to be
1241 relative to the current directory and match against a file or a
1239 relative to the current directory and match against a file or a
1242 directory.
1240 directory.
1243 """
1241 """
1244 # i18n: "modifies" is a keyword
1242 # i18n: "modifies" is a keyword
1245 pat = getstring(x, _("modifies requires a pattern"))
1243 pat = getstring(x, _("modifies requires a pattern"))
1246 return checkstatus(repo, subset, pat, 0)
1244 return checkstatus(repo, subset, pat, 0)
1247
1245
1248 def named(repo, subset, x):
1246 def named(repo, subset, x):
1249 """``named(namespace)``
1247 """``named(namespace)``
1250 The changesets in a given namespace.
1248 The changesets in a given namespace.
1251
1249
1252 If `namespace` starts with `re:`, the remainder of the string is treated as
1250 If `namespace` starts with `re:`, the remainder of the string is treated as
1253 a regular expression. To match a namespace that actually starts with `re:`,
1251 a regular expression. To match a namespace that actually starts with `re:`,
1254 use the prefix `literal:`.
1252 use the prefix `literal:`.
1255 """
1253 """
1256 # i18n: "named" is a keyword
1254 # i18n: "named" is a keyword
1257 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1255 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1258
1256
1259 ns = getstring(args[0],
1257 ns = getstring(args[0],
1260 # i18n: "named" is a keyword
1258 # i18n: "named" is a keyword
1261 _('the argument to named must be a string'))
1259 _('the argument to named must be a string'))
1262 kind, pattern, matcher = _stringmatcher(ns)
1260 kind, pattern, matcher = _stringmatcher(ns)
1263 namespaces = set()
1261 namespaces = set()
1264 if kind == 'literal':
1262 if kind == 'literal':
1265 if pattern not in repo.names:
1263 if pattern not in repo.names:
1266 raise error.RepoLookupError(_("namespace '%s' does not exist")
1264 raise error.RepoLookupError(_("namespace '%s' does not exist")
1267 % ns)
1265 % ns)
1268 namespaces.add(repo.names[pattern])
1266 namespaces.add(repo.names[pattern])
1269 else:
1267 else:
1270 for name, ns in repo.names.iteritems():
1268 for name, ns in repo.names.iteritems():
1271 if matcher(name):
1269 if matcher(name):
1272 namespaces.add(ns)
1270 namespaces.add(ns)
1273 if not namespaces:
1271 if not namespaces:
1274 raise error.RepoLookupError(_("no namespace exists"
1272 raise error.RepoLookupError(_("no namespace exists"
1275 " that match '%s'") % pattern)
1273 " that match '%s'") % pattern)
1276
1274
1277 names = set()
1275 names = set()
1278 for ns in namespaces:
1276 for ns in namespaces:
1279 for name in ns.listnames(repo):
1277 for name in ns.listnames(repo):
1280 if name not in ns.deprecated:
1278 if name not in ns.deprecated:
1281 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1279 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1282
1280
1283 names -= set([node.nullrev])
1281 names -= set([node.nullrev])
1284 return subset & names
1282 return subset & names
1285
1283
1286 def node_(repo, subset, x):
1284 def node_(repo, subset, x):
1287 """``id(string)``
1285 """``id(string)``
1288 Revision non-ambiguously specified by the given hex string prefix.
1286 Revision non-ambiguously specified by the given hex string prefix.
1289 """
1287 """
1290 # i18n: "id" is a keyword
1288 # i18n: "id" is a keyword
1291 l = getargs(x, 1, 1, _("id requires one argument"))
1289 l = getargs(x, 1, 1, _("id requires one argument"))
1292 # i18n: "id" is a keyword
1290 # i18n: "id" is a keyword
1293 n = getstring(l[0], _("id requires a string"))
1291 n = getstring(l[0], _("id requires a string"))
1294 if len(n) == 40:
1292 if len(n) == 40:
1295 rn = repo[n].rev()
1293 rn = repo[n].rev()
1296 else:
1294 else:
1297 rn = None
1295 rn = None
1298 pm = repo.changelog._partialmatch(n)
1296 pm = repo.changelog._partialmatch(n)
1299 if pm is not None:
1297 if pm is not None:
1300 rn = repo.changelog.rev(pm)
1298 rn = repo.changelog.rev(pm)
1301
1299
1302 if rn is None:
1300 if rn is None:
1303 return baseset()
1301 return baseset()
1304 result = baseset([rn])
1302 result = baseset([rn])
1305 return result & subset
1303 return result & subset
1306
1304
1307 def obsolete(repo, subset, x):
1305 def obsolete(repo, subset, x):
1308 """``obsolete()``
1306 """``obsolete()``
1309 Mutable changeset with a newer version."""
1307 Mutable changeset with a newer version."""
1310 # i18n: "obsolete" is a keyword
1308 # i18n: "obsolete" is a keyword
1311 getargs(x, 0, 0, _("obsolete takes no arguments"))
1309 getargs(x, 0, 0, _("obsolete takes no arguments"))
1312 obsoletes = obsmod.getrevs(repo, 'obsolete')
1310 obsoletes = obsmod.getrevs(repo, 'obsolete')
1313 return subset & obsoletes
1311 return subset & obsoletes
1314
1312
1315 def only(repo, subset, x):
1313 def only(repo, subset, x):
1316 """``only(set, [set])``
1314 """``only(set, [set])``
1317 Changesets that are ancestors of the first set that are not ancestors
1315 Changesets that are ancestors of the first set that are not ancestors
1318 of any other head in the repo. If a second set is specified, the result
1316 of any other head in the repo. If a second set is specified, the result
1319 is ancestors of the first set that are not ancestors of the second set
1317 is ancestors of the first set that are not ancestors of the second set
1320 (i.e. ::<set1> - ::<set2>).
1318 (i.e. ::<set1> - ::<set2>).
1321 """
1319 """
1322 cl = repo.changelog
1320 cl = repo.changelog
1323 # i18n: "only" is a keyword
1321 # i18n: "only" is a keyword
1324 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1322 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1325 include = getset(repo, fullreposet(repo), args[0])
1323 include = getset(repo, fullreposet(repo), args[0])
1326 if len(args) == 1:
1324 if len(args) == 1:
1327 if not include:
1325 if not include:
1328 return baseset()
1326 return baseset()
1329
1327
1330 descendants = set(_revdescendants(repo, include, False))
1328 descendants = set(_revdescendants(repo, include, False))
1331 exclude = [rev for rev in cl.headrevs()
1329 exclude = [rev for rev in cl.headrevs()
1332 if not rev in descendants and not rev in include]
1330 if not rev in descendants and not rev in include]
1333 else:
1331 else:
1334 exclude = getset(repo, fullreposet(repo), args[1])
1332 exclude = getset(repo, fullreposet(repo), args[1])
1335
1333
1336 results = set(cl.findmissingrevs(common=exclude, heads=include))
1334 results = set(cl.findmissingrevs(common=exclude, heads=include))
1337 return subset & results
1335 return subset & results
1338
1336
1339 def origin(repo, subset, x):
1337 def origin(repo, subset, x):
1340 """``origin([set])``
1338 """``origin([set])``
1341 Changesets that were specified as a source for the grafts, transplants or
1339 Changesets that were specified as a source for the grafts, transplants or
1342 rebases that created the given revisions. Omitting the optional set is the
1340 rebases that created the given revisions. Omitting the optional set is the
1343 same as passing all(). If a changeset created by these operations is itself
1341 same as passing all(). If a changeset created by these operations is itself
1344 specified as a source for one of these operations, only the source changeset
1342 specified as a source for one of these operations, only the source changeset
1345 for the first operation is selected.
1343 for the first operation is selected.
1346 """
1344 """
1347 if x is not None:
1345 if x is not None:
1348 dests = getset(repo, fullreposet(repo), x)
1346 dests = getset(repo, fullreposet(repo), x)
1349 else:
1347 else:
1350 dests = fullreposet(repo)
1348 dests = fullreposet(repo)
1351
1349
1352 def _firstsrc(rev):
1350 def _firstsrc(rev):
1353 src = _getrevsource(repo, rev)
1351 src = _getrevsource(repo, rev)
1354 if src is None:
1352 if src is None:
1355 return None
1353 return None
1356
1354
1357 while True:
1355 while True:
1358 prev = _getrevsource(repo, src)
1356 prev = _getrevsource(repo, src)
1359
1357
1360 if prev is None:
1358 if prev is None:
1361 return src
1359 return src
1362 src = prev
1360 src = prev
1363
1361
1364 o = set([_firstsrc(r) for r in dests])
1362 o = set([_firstsrc(r) for r in dests])
1365 o -= set([None])
1363 o -= set([None])
1366 return subset & o
1364 return subset & o
1367
1365
1368 def outgoing(repo, subset, x):
1366 def outgoing(repo, subset, x):
1369 """``outgoing([path])``
1367 """``outgoing([path])``
1370 Changesets not found in the specified destination repository, or the
1368 Changesets not found in the specified destination repository, or the
1371 default push location.
1369 default push location.
1372 """
1370 """
1373 import hg # avoid start-up nasties
1371 import hg # avoid start-up nasties
1374 # i18n: "outgoing" is a keyword
1372 # i18n: "outgoing" is a keyword
1375 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1373 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1376 # i18n: "outgoing" is a keyword
1374 # i18n: "outgoing" is a keyword
1377 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1375 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1378 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1376 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1379 dest, branches = hg.parseurl(dest)
1377 dest, branches = hg.parseurl(dest)
1380 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1378 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1381 if revs:
1379 if revs:
1382 revs = [repo.lookup(rev) for rev in revs]
1380 revs = [repo.lookup(rev) for rev in revs]
1383 other = hg.peer(repo, {}, dest)
1381 other = hg.peer(repo, {}, dest)
1384 repo.ui.pushbuffer()
1382 repo.ui.pushbuffer()
1385 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1383 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1386 repo.ui.popbuffer()
1384 repo.ui.popbuffer()
1387 cl = repo.changelog
1385 cl = repo.changelog
1388 o = set([cl.rev(r) for r in outgoing.missing])
1386 o = set([cl.rev(r) for r in outgoing.missing])
1389 return subset & o
1387 return subset & o
1390
1388
1391 def p1(repo, subset, x):
1389 def p1(repo, subset, x):
1392 """``p1([set])``
1390 """``p1([set])``
1393 First parent of changesets in set, or the working directory.
1391 First parent of changesets in set, or the working directory.
1394 """
1392 """
1395 if x is None:
1393 if x is None:
1396 p = repo[x].p1().rev()
1394 p = repo[x].p1().rev()
1397 if p >= 0:
1395 if p >= 0:
1398 return subset & baseset([p])
1396 return subset & baseset([p])
1399 return baseset()
1397 return baseset()
1400
1398
1401 ps = set()
1399 ps = set()
1402 cl = repo.changelog
1400 cl = repo.changelog
1403 for r in getset(repo, fullreposet(repo), x):
1401 for r in getset(repo, fullreposet(repo), x):
1404 ps.add(cl.parentrevs(r)[0])
1402 ps.add(cl.parentrevs(r)[0])
1405 ps -= set([node.nullrev])
1403 ps -= set([node.nullrev])
1406 return subset & ps
1404 return subset & ps
1407
1405
1408 def p2(repo, subset, x):
1406 def p2(repo, subset, x):
1409 """``p2([set])``
1407 """``p2([set])``
1410 Second parent of changesets in set, or the working directory.
1408 Second parent of changesets in set, or the working directory.
1411 """
1409 """
1412 if x is None:
1410 if x is None:
1413 ps = repo[x].parents()
1411 ps = repo[x].parents()
1414 try:
1412 try:
1415 p = ps[1].rev()
1413 p = ps[1].rev()
1416 if p >= 0:
1414 if p >= 0:
1417 return subset & baseset([p])
1415 return subset & baseset([p])
1418 return baseset()
1416 return baseset()
1419 except IndexError:
1417 except IndexError:
1420 return baseset()
1418 return baseset()
1421
1419
1422 ps = set()
1420 ps = set()
1423 cl = repo.changelog
1421 cl = repo.changelog
1424 for r in getset(repo, fullreposet(repo), x):
1422 for r in getset(repo, fullreposet(repo), x):
1425 ps.add(cl.parentrevs(r)[1])
1423 ps.add(cl.parentrevs(r)[1])
1426 ps -= set([node.nullrev])
1424 ps -= set([node.nullrev])
1427 return subset & ps
1425 return subset & ps
1428
1426
1429 def parents(repo, subset, x):
1427 def parents(repo, subset, x):
1430 """``parents([set])``
1428 """``parents([set])``
1431 The set of all parents for all changesets in set, or the working directory.
1429 The set of all parents for all changesets in set, or the working directory.
1432 """
1430 """
1433 if x is None:
1431 if x is None:
1434 ps = set(p.rev() for p in repo[x].parents())
1432 ps = set(p.rev() for p in repo[x].parents())
1435 else:
1433 else:
1436 ps = set()
1434 ps = set()
1437 cl = repo.changelog
1435 cl = repo.changelog
1438 for r in getset(repo, fullreposet(repo), x):
1436 for r in getset(repo, fullreposet(repo), x):
1439 ps.update(cl.parentrevs(r))
1437 ps.update(cl.parentrevs(r))
1440 ps -= set([node.nullrev])
1438 ps -= set([node.nullrev])
1441 return subset & ps
1439 return subset & ps
1442
1440
1443 def parentspec(repo, subset, x, n):
1441 def parentspec(repo, subset, x, n):
1444 """``set^0``
1442 """``set^0``
1445 The set.
1443 The set.
1446 ``set^1`` (or ``set^``), ``set^2``
1444 ``set^1`` (or ``set^``), ``set^2``
1447 First or second parent, respectively, of all changesets in set.
1445 First or second parent, respectively, of all changesets in set.
1448 """
1446 """
1449 try:
1447 try:
1450 n = int(n[1])
1448 n = int(n[1])
1451 if n not in (0, 1, 2):
1449 if n not in (0, 1, 2):
1452 raise ValueError
1450 raise ValueError
1453 except (TypeError, ValueError):
1451 except (TypeError, ValueError):
1454 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1452 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1455 ps = set()
1453 ps = set()
1456 cl = repo.changelog
1454 cl = repo.changelog
1457 for r in getset(repo, fullreposet(repo), x):
1455 for r in getset(repo, fullreposet(repo), x):
1458 if n == 0:
1456 if n == 0:
1459 ps.add(r)
1457 ps.add(r)
1460 elif n == 1:
1458 elif n == 1:
1461 ps.add(cl.parentrevs(r)[0])
1459 ps.add(cl.parentrevs(r)[0])
1462 elif n == 2:
1460 elif n == 2:
1463 parents = cl.parentrevs(r)
1461 parents = cl.parentrevs(r)
1464 if len(parents) > 1:
1462 if len(parents) > 1:
1465 ps.add(parents[1])
1463 ps.add(parents[1])
1466 return subset & ps
1464 return subset & ps
1467
1465
1468 def present(repo, subset, x):
1466 def present(repo, subset, x):
1469 """``present(set)``
1467 """``present(set)``
1470 An empty set, if any revision in set isn't found; otherwise,
1468 An empty set, if any revision in set isn't found; otherwise,
1471 all revisions in set.
1469 all revisions in set.
1472
1470
1473 If any of specified revisions is not present in the local repository,
1471 If any of specified revisions is not present in the local repository,
1474 the query is normally aborted. But this predicate allows the query
1472 the query is normally aborted. But this predicate allows the query
1475 to continue even in such cases.
1473 to continue even in such cases.
1476 """
1474 """
1477 try:
1475 try:
1478 return getset(repo, subset, x)
1476 return getset(repo, subset, x)
1479 except error.RepoLookupError:
1477 except error.RepoLookupError:
1480 return baseset()
1478 return baseset()
1481
1479
1482 def public(repo, subset, x):
1480 def public(repo, subset, x):
1483 """``public()``
1481 """``public()``
1484 Changeset in public phase."""
1482 Changeset in public phase."""
1485 # i18n: "public" is a keyword
1483 # i18n: "public" is a keyword
1486 getargs(x, 0, 0, _("public takes no arguments"))
1484 getargs(x, 0, 0, _("public takes no arguments"))
1487 phase = repo._phasecache.phase
1485 phase = repo._phasecache.phase
1488 target = phases.public
1486 target = phases.public
1489 condition = lambda r: phase(repo, r) == target
1487 condition = lambda r: phase(repo, r) == target
1490 return subset.filter(condition, cache=False)
1488 return subset.filter(condition, cache=False)
1491
1489
1492 def remote(repo, subset, x):
1490 def remote(repo, subset, x):
1493 """``remote([id [,path]])``
1491 """``remote([id [,path]])``
1494 Local revision that corresponds to the given identifier in a
1492 Local revision that corresponds to the given identifier in a
1495 remote repository, if present. Here, the '.' identifier is a
1493 remote repository, if present. Here, the '.' identifier is a
1496 synonym for the current local branch.
1494 synonym for the current local branch.
1497 """
1495 """
1498
1496
1499 import hg # avoid start-up nasties
1497 import hg # avoid start-up nasties
1500 # i18n: "remote" is a keyword
1498 # i18n: "remote" is a keyword
1501 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1499 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1502
1500
1503 q = '.'
1501 q = '.'
1504 if len(l) > 0:
1502 if len(l) > 0:
1505 # i18n: "remote" is a keyword
1503 # i18n: "remote" is a keyword
1506 q = getstring(l[0], _("remote requires a string id"))
1504 q = getstring(l[0], _("remote requires a string id"))
1507 if q == '.':
1505 if q == '.':
1508 q = repo['.'].branch()
1506 q = repo['.'].branch()
1509
1507
1510 dest = ''
1508 dest = ''
1511 if len(l) > 1:
1509 if len(l) > 1:
1512 # i18n: "remote" is a keyword
1510 # i18n: "remote" is a keyword
1513 dest = getstring(l[1], _("remote requires a repository path"))
1511 dest = getstring(l[1], _("remote requires a repository path"))
1514 dest = repo.ui.expandpath(dest or 'default')
1512 dest = repo.ui.expandpath(dest or 'default')
1515 dest, branches = hg.parseurl(dest)
1513 dest, branches = hg.parseurl(dest)
1516 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1514 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1517 if revs:
1515 if revs:
1518 revs = [repo.lookup(rev) for rev in revs]
1516 revs = [repo.lookup(rev) for rev in revs]
1519 other = hg.peer(repo, {}, dest)
1517 other = hg.peer(repo, {}, dest)
1520 n = other.lookup(q)
1518 n = other.lookup(q)
1521 if n in repo:
1519 if n in repo:
1522 r = repo[n].rev()
1520 r = repo[n].rev()
1523 if r in subset:
1521 if r in subset:
1524 return baseset([r])
1522 return baseset([r])
1525 return baseset()
1523 return baseset()
1526
1524
1527 def removes(repo, subset, x):
1525 def removes(repo, subset, x):
1528 """``removes(pattern)``
1526 """``removes(pattern)``
1529 Changesets which remove files matching pattern.
1527 Changesets which remove files matching pattern.
1530
1528
1531 The pattern without explicit kind like ``glob:`` is expected to be
1529 The pattern without explicit kind like ``glob:`` is expected to be
1532 relative to the current directory and match against a file or a
1530 relative to the current directory and match against a file or a
1533 directory.
1531 directory.
1534 """
1532 """
1535 # i18n: "removes" is a keyword
1533 # i18n: "removes" is a keyword
1536 pat = getstring(x, _("removes requires a pattern"))
1534 pat = getstring(x, _("removes requires a pattern"))
1537 return checkstatus(repo, subset, pat, 2)
1535 return checkstatus(repo, subset, pat, 2)
1538
1536
1539 def rev(repo, subset, x):
1537 def rev(repo, subset, x):
1540 """``rev(number)``
1538 """``rev(number)``
1541 Revision with the given numeric identifier.
1539 Revision with the given numeric identifier.
1542 """
1540 """
1543 # i18n: "rev" is a keyword
1541 # i18n: "rev" is a keyword
1544 l = getargs(x, 1, 1, _("rev requires one argument"))
1542 l = getargs(x, 1, 1, _("rev requires one argument"))
1545 try:
1543 try:
1546 # i18n: "rev" is a keyword
1544 # i18n: "rev" is a keyword
1547 l = int(getstring(l[0], _("rev requires a number")))
1545 l = int(getstring(l[0], _("rev requires a number")))
1548 except (TypeError, ValueError):
1546 except (TypeError, ValueError):
1549 # i18n: "rev" is a keyword
1547 # i18n: "rev" is a keyword
1550 raise error.ParseError(_("rev expects a number"))
1548 raise error.ParseError(_("rev expects a number"))
1551 if l not in repo.changelog and l != node.nullrev:
1549 if l not in repo.changelog and l != node.nullrev:
1552 return baseset()
1550 return baseset()
1553 return subset & baseset([l])
1551 return subset & baseset([l])
1554
1552
1555 def matching(repo, subset, x):
1553 def matching(repo, subset, x):
1556 """``matching(revision [, field])``
1554 """``matching(revision [, field])``
1557 Changesets in which a given set of fields match the set of fields in the
1555 Changesets in which a given set of fields match the set of fields in the
1558 selected revision or set.
1556 selected revision or set.
1559
1557
1560 To match more than one field pass the list of fields to match separated
1558 To match more than one field pass the list of fields to match separated
1561 by spaces (e.g. ``author description``).
1559 by spaces (e.g. ``author description``).
1562
1560
1563 Valid fields are most regular revision fields and some special fields.
1561 Valid fields are most regular revision fields and some special fields.
1564
1562
1565 Regular revision fields are ``description``, ``author``, ``branch``,
1563 Regular revision fields are ``description``, ``author``, ``branch``,
1566 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1564 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1567 and ``diff``.
1565 and ``diff``.
1568 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1566 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1569 contents of the revision. Two revisions matching their ``diff`` will
1567 contents of the revision. Two revisions matching their ``diff`` will
1570 also match their ``files``.
1568 also match their ``files``.
1571
1569
1572 Special fields are ``summary`` and ``metadata``:
1570 Special fields are ``summary`` and ``metadata``:
1573 ``summary`` matches the first line of the description.
1571 ``summary`` matches the first line of the description.
1574 ``metadata`` is equivalent to matching ``description user date``
1572 ``metadata`` is equivalent to matching ``description user date``
1575 (i.e. it matches the main metadata fields).
1573 (i.e. it matches the main metadata fields).
1576
1574
1577 ``metadata`` is the default field which is used when no fields are
1575 ``metadata`` is the default field which is used when no fields are
1578 specified. You can match more than one field at a time.
1576 specified. You can match more than one field at a time.
1579 """
1577 """
1580 # i18n: "matching" is a keyword
1578 # i18n: "matching" is a keyword
1581 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1579 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1582
1580
1583 revs = getset(repo, fullreposet(repo), l[0])
1581 revs = getset(repo, fullreposet(repo), l[0])
1584
1582
1585 fieldlist = ['metadata']
1583 fieldlist = ['metadata']
1586 if len(l) > 1:
1584 if len(l) > 1:
1587 fieldlist = getstring(l[1],
1585 fieldlist = getstring(l[1],
1588 # i18n: "matching" is a keyword
1586 # i18n: "matching" is a keyword
1589 _("matching requires a string "
1587 _("matching requires a string "
1590 "as its second argument")).split()
1588 "as its second argument")).split()
1591
1589
1592 # Make sure that there are no repeated fields,
1590 # Make sure that there are no repeated fields,
1593 # expand the 'special' 'metadata' field type
1591 # expand the 'special' 'metadata' field type
1594 # and check the 'files' whenever we check the 'diff'
1592 # and check the 'files' whenever we check the 'diff'
1595 fields = []
1593 fields = []
1596 for field in fieldlist:
1594 for field in fieldlist:
1597 if field == 'metadata':
1595 if field == 'metadata':
1598 fields += ['user', 'description', 'date']
1596 fields += ['user', 'description', 'date']
1599 elif field == 'diff':
1597 elif field == 'diff':
1600 # a revision matching the diff must also match the files
1598 # a revision matching the diff must also match the files
1601 # since matching the diff is very costly, make sure to
1599 # since matching the diff is very costly, make sure to
1602 # also match the files first
1600 # also match the files first
1603 fields += ['files', 'diff']
1601 fields += ['files', 'diff']
1604 else:
1602 else:
1605 if field == 'author':
1603 if field == 'author':
1606 field = 'user'
1604 field = 'user'
1607 fields.append(field)
1605 fields.append(field)
1608 fields = set(fields)
1606 fields = set(fields)
1609 if 'summary' in fields and 'description' in fields:
1607 if 'summary' in fields and 'description' in fields:
1610 # If a revision matches its description it also matches its summary
1608 # If a revision matches its description it also matches its summary
1611 fields.discard('summary')
1609 fields.discard('summary')
1612
1610
1613 # We may want to match more than one field
1611 # We may want to match more than one field
1614 # Not all fields take the same amount of time to be matched
1612 # Not all fields take the same amount of time to be matched
1615 # Sort the selected fields in order of increasing matching cost
1613 # Sort the selected fields in order of increasing matching cost
1616 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1614 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1617 'files', 'description', 'substate', 'diff']
1615 'files', 'description', 'substate', 'diff']
1618 def fieldkeyfunc(f):
1616 def fieldkeyfunc(f):
1619 try:
1617 try:
1620 return fieldorder.index(f)
1618 return fieldorder.index(f)
1621 except ValueError:
1619 except ValueError:
1622 # assume an unknown field is very costly
1620 # assume an unknown field is very costly
1623 return len(fieldorder)
1621 return len(fieldorder)
1624 fields = list(fields)
1622 fields = list(fields)
1625 fields.sort(key=fieldkeyfunc)
1623 fields.sort(key=fieldkeyfunc)
1626
1624
1627 # Each field will be matched with its own "getfield" function
1625 # Each field will be matched with its own "getfield" function
1628 # which will be added to the getfieldfuncs array of functions
1626 # which will be added to the getfieldfuncs array of functions
1629 getfieldfuncs = []
1627 getfieldfuncs = []
1630 _funcs = {
1628 _funcs = {
1631 'user': lambda r: repo[r].user(),
1629 'user': lambda r: repo[r].user(),
1632 'branch': lambda r: repo[r].branch(),
1630 'branch': lambda r: repo[r].branch(),
1633 'date': lambda r: repo[r].date(),
1631 'date': lambda r: repo[r].date(),
1634 'description': lambda r: repo[r].description(),
1632 'description': lambda r: repo[r].description(),
1635 'files': lambda r: repo[r].files(),
1633 'files': lambda r: repo[r].files(),
1636 'parents': lambda r: repo[r].parents(),
1634 'parents': lambda r: repo[r].parents(),
1637 'phase': lambda r: repo[r].phase(),
1635 'phase': lambda r: repo[r].phase(),
1638 'substate': lambda r: repo[r].substate,
1636 'substate': lambda r: repo[r].substate,
1639 'summary': lambda r: repo[r].description().splitlines()[0],
1637 'summary': lambda r: repo[r].description().splitlines()[0],
1640 'diff': lambda r: list(repo[r].diff(git=True),)
1638 'diff': lambda r: list(repo[r].diff(git=True),)
1641 }
1639 }
1642 for info in fields:
1640 for info in fields:
1643 getfield = _funcs.get(info, None)
1641 getfield = _funcs.get(info, None)
1644 if getfield is None:
1642 if getfield is None:
1645 raise error.ParseError(
1643 raise error.ParseError(
1646 # i18n: "matching" is a keyword
1644 # i18n: "matching" is a keyword
1647 _("unexpected field name passed to matching: %s") % info)
1645 _("unexpected field name passed to matching: %s") % info)
1648 getfieldfuncs.append(getfield)
1646 getfieldfuncs.append(getfield)
1649 # convert the getfield array of functions into a "getinfo" function
1647 # convert the getfield array of functions into a "getinfo" function
1650 # which returns an array of field values (or a single value if there
1648 # which returns an array of field values (or a single value if there
1651 # is only one field to match)
1649 # is only one field to match)
1652 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1650 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1653
1651
1654 def matches(x):
1652 def matches(x):
1655 for rev in revs:
1653 for rev in revs:
1656 target = getinfo(rev)
1654 target = getinfo(rev)
1657 match = True
1655 match = True
1658 for n, f in enumerate(getfieldfuncs):
1656 for n, f in enumerate(getfieldfuncs):
1659 if target[n] != f(x):
1657 if target[n] != f(x):
1660 match = False
1658 match = False
1661 if match:
1659 if match:
1662 return True
1660 return True
1663 return False
1661 return False
1664
1662
1665 return subset.filter(matches)
1663 return subset.filter(matches)
1666
1664
1667 def reverse(repo, subset, x):
1665 def reverse(repo, subset, x):
1668 """``reverse(set)``
1666 """``reverse(set)``
1669 Reverse order of set.
1667 Reverse order of set.
1670 """
1668 """
1671 l = getset(repo, subset, x)
1669 l = getset(repo, subset, x)
1672 l.reverse()
1670 l.reverse()
1673 return l
1671 return l
1674
1672
1675 def roots(repo, subset, x):
1673 def roots(repo, subset, x):
1676 """``roots(set)``
1674 """``roots(set)``
1677 Changesets in set with no parent changeset in set.
1675 Changesets in set with no parent changeset in set.
1678 """
1676 """
1679 s = getset(repo, fullreposet(repo), x)
1677 s = getset(repo, fullreposet(repo), x)
1680 subset = baseset([r for r in s if r in subset])
1678 subset = baseset([r for r in s if r in subset])
1681 cs = _children(repo, subset, s)
1679 cs = _children(repo, subset, s)
1682 return subset - cs
1680 return subset - cs
1683
1681
1684 def secret(repo, subset, x):
1682 def secret(repo, subset, x):
1685 """``secret()``
1683 """``secret()``
1686 Changeset in secret phase."""
1684 Changeset in secret phase."""
1687 # i18n: "secret" is a keyword
1685 # i18n: "secret" is a keyword
1688 getargs(x, 0, 0, _("secret takes no arguments"))
1686 getargs(x, 0, 0, _("secret takes no arguments"))
1689 phase = repo._phasecache.phase
1687 phase = repo._phasecache.phase
1690 target = phases.secret
1688 target = phases.secret
1691 condition = lambda r: phase(repo, r) == target
1689 condition = lambda r: phase(repo, r) == target
1692 return subset.filter(condition, cache=False)
1690 return subset.filter(condition, cache=False)
1693
1691
1694 def sort(repo, subset, x):
1692 def sort(repo, subset, x):
1695 """``sort(set[, [-]key...])``
1693 """``sort(set[, [-]key...])``
1696 Sort set by keys. The default sort order is ascending, specify a key
1694 Sort set by keys. The default sort order is ascending, specify a key
1697 as ``-key`` to sort in descending order.
1695 as ``-key`` to sort in descending order.
1698
1696
1699 The keys can be:
1697 The keys can be:
1700
1698
1701 - ``rev`` for the revision number,
1699 - ``rev`` for the revision number,
1702 - ``branch`` for the branch name,
1700 - ``branch`` for the branch name,
1703 - ``desc`` for the commit message (description),
1701 - ``desc`` for the commit message (description),
1704 - ``user`` for user name (``author`` can be used as an alias),
1702 - ``user`` for user name (``author`` can be used as an alias),
1705 - ``date`` for the commit date
1703 - ``date`` for the commit date
1706 """
1704 """
1707 # i18n: "sort" is a keyword
1705 # i18n: "sort" is a keyword
1708 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1706 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1709 keys = "rev"
1707 keys = "rev"
1710 if len(l) == 2:
1708 if len(l) == 2:
1711 # i18n: "sort" is a keyword
1709 # i18n: "sort" is a keyword
1712 keys = getstring(l[1], _("sort spec must be a string"))
1710 keys = getstring(l[1], _("sort spec must be a string"))
1713
1711
1714 s = l[0]
1712 s = l[0]
1715 keys = keys.split()
1713 keys = keys.split()
1716 l = []
1714 l = []
1717 def invert(s):
1715 def invert(s):
1718 return "".join(chr(255 - ord(c)) for c in s)
1716 return "".join(chr(255 - ord(c)) for c in s)
1719 revs = getset(repo, subset, s)
1717 revs = getset(repo, subset, s)
1720 if keys == ["rev"]:
1718 if keys == ["rev"]:
1721 revs.sort()
1719 revs.sort()
1722 return revs
1720 return revs
1723 elif keys == ["-rev"]:
1721 elif keys == ["-rev"]:
1724 revs.sort(reverse=True)
1722 revs.sort(reverse=True)
1725 return revs
1723 return revs
1726 for r in revs:
1724 for r in revs:
1727 c = repo[r]
1725 c = repo[r]
1728 e = []
1726 e = []
1729 for k in keys:
1727 for k in keys:
1730 if k == 'rev':
1728 if k == 'rev':
1731 e.append(r)
1729 e.append(r)
1732 elif k == '-rev':
1730 elif k == '-rev':
1733 e.append(-r)
1731 e.append(-r)
1734 elif k == 'branch':
1732 elif k == 'branch':
1735 e.append(c.branch())
1733 e.append(c.branch())
1736 elif k == '-branch':
1734 elif k == '-branch':
1737 e.append(invert(c.branch()))
1735 e.append(invert(c.branch()))
1738 elif k == 'desc':
1736 elif k == 'desc':
1739 e.append(c.description())
1737 e.append(c.description())
1740 elif k == '-desc':
1738 elif k == '-desc':
1741 e.append(invert(c.description()))
1739 e.append(invert(c.description()))
1742 elif k in 'user author':
1740 elif k in 'user author':
1743 e.append(c.user())
1741 e.append(c.user())
1744 elif k in '-user -author':
1742 elif k in '-user -author':
1745 e.append(invert(c.user()))
1743 e.append(invert(c.user()))
1746 elif k == 'date':
1744 elif k == 'date':
1747 e.append(c.date()[0])
1745 e.append(c.date()[0])
1748 elif k == '-date':
1746 elif k == '-date':
1749 e.append(-c.date()[0])
1747 e.append(-c.date()[0])
1750 else:
1748 else:
1751 raise error.ParseError(_("unknown sort key %r") % k)
1749 raise error.ParseError(_("unknown sort key %r") % k)
1752 e.append(r)
1750 e.append(r)
1753 l.append(e)
1751 l.append(e)
1754 l.sort()
1752 l.sort()
1755 return baseset([e[-1] for e in l])
1753 return baseset([e[-1] for e in l])
1756
1754
1757 def _stringmatcher(pattern):
1755 def _stringmatcher(pattern):
1758 """
1756 """
1759 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1757 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1760 returns the matcher name, pattern, and matcher function.
1758 returns the matcher name, pattern, and matcher function.
1761 missing or unknown prefixes are treated as literal matches.
1759 missing or unknown prefixes are treated as literal matches.
1762
1760
1763 helper for tests:
1761 helper for tests:
1764 >>> def test(pattern, *tests):
1762 >>> def test(pattern, *tests):
1765 ... kind, pattern, matcher = _stringmatcher(pattern)
1763 ... kind, pattern, matcher = _stringmatcher(pattern)
1766 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1764 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1767
1765
1768 exact matching (no prefix):
1766 exact matching (no prefix):
1769 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1767 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1770 ('literal', 'abcdefg', [False, False, True])
1768 ('literal', 'abcdefg', [False, False, True])
1771
1769
1772 regex matching ('re:' prefix)
1770 regex matching ('re:' prefix)
1773 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1771 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1774 ('re', 'a.+b', [False, False, True])
1772 ('re', 'a.+b', [False, False, True])
1775
1773
1776 force exact matches ('literal:' prefix)
1774 force exact matches ('literal:' prefix)
1777 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1775 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1778 ('literal', 're:foobar', [False, True])
1776 ('literal', 're:foobar', [False, True])
1779
1777
1780 unknown prefixes are ignored and treated as literals
1778 unknown prefixes are ignored and treated as literals
1781 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1779 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1782 ('literal', 'foo:bar', [False, False, True])
1780 ('literal', 'foo:bar', [False, False, True])
1783 """
1781 """
1784 if pattern.startswith('re:'):
1782 if pattern.startswith('re:'):
1785 pattern = pattern[3:]
1783 pattern = pattern[3:]
1786 try:
1784 try:
1787 regex = re.compile(pattern)
1785 regex = re.compile(pattern)
1788 except re.error, e:
1786 except re.error, e:
1789 raise error.ParseError(_('invalid regular expression: %s')
1787 raise error.ParseError(_('invalid regular expression: %s')
1790 % e)
1788 % e)
1791 return 're', pattern, regex.search
1789 return 're', pattern, regex.search
1792 elif pattern.startswith('literal:'):
1790 elif pattern.startswith('literal:'):
1793 pattern = pattern[8:]
1791 pattern = pattern[8:]
1794 return 'literal', pattern, pattern.__eq__
1792 return 'literal', pattern, pattern.__eq__
1795
1793
1796 def _substringmatcher(pattern):
1794 def _substringmatcher(pattern):
1797 kind, pattern, matcher = _stringmatcher(pattern)
1795 kind, pattern, matcher = _stringmatcher(pattern)
1798 if kind == 'literal':
1796 if kind == 'literal':
1799 matcher = lambda s: pattern in s
1797 matcher = lambda s: pattern in s
1800 return kind, pattern, matcher
1798 return kind, pattern, matcher
1801
1799
1802 def tag(repo, subset, x):
1800 def tag(repo, subset, x):
1803 """``tag([name])``
1801 """``tag([name])``
1804 The specified tag by name, or all tagged revisions if no name is given.
1802 The specified tag by name, or all tagged revisions if no name is given.
1805
1803
1806 If `name` starts with `re:`, the remainder of the name is treated as
1804 If `name` starts with `re:`, the remainder of the name is treated as
1807 a regular expression. To match a tag that actually starts with `re:`,
1805 a regular expression. To match a tag that actually starts with `re:`,
1808 use the prefix `literal:`.
1806 use the prefix `literal:`.
1809 """
1807 """
1810 # i18n: "tag" is a keyword
1808 # i18n: "tag" is a keyword
1811 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1809 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1812 cl = repo.changelog
1810 cl = repo.changelog
1813 if args:
1811 if args:
1814 pattern = getstring(args[0],
1812 pattern = getstring(args[0],
1815 # i18n: "tag" is a keyword
1813 # i18n: "tag" is a keyword
1816 _('the argument to tag must be a string'))
1814 _('the argument to tag must be a string'))
1817 kind, pattern, matcher = _stringmatcher(pattern)
1815 kind, pattern, matcher = _stringmatcher(pattern)
1818 if kind == 'literal':
1816 if kind == 'literal':
1819 # avoid resolving all tags
1817 # avoid resolving all tags
1820 tn = repo._tagscache.tags.get(pattern, None)
1818 tn = repo._tagscache.tags.get(pattern, None)
1821 if tn is None:
1819 if tn is None:
1822 raise error.RepoLookupError(_("tag '%s' does not exist")
1820 raise error.RepoLookupError(_("tag '%s' does not exist")
1823 % pattern)
1821 % pattern)
1824 s = set([repo[tn].rev()])
1822 s = set([repo[tn].rev()])
1825 else:
1823 else:
1826 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1824 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1827 else:
1825 else:
1828 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1826 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1829 return subset & s
1827 return subset & s
1830
1828
1831 def tagged(repo, subset, x):
1829 def tagged(repo, subset, x):
1832 return tag(repo, subset, x)
1830 return tag(repo, subset, x)
1833
1831
1834 def unstable(repo, subset, x):
1832 def unstable(repo, subset, x):
1835 """``unstable()``
1833 """``unstable()``
1836 Non-obsolete changesets with obsolete ancestors.
1834 Non-obsolete changesets with obsolete ancestors.
1837 """
1835 """
1838 # i18n: "unstable" is a keyword
1836 # i18n: "unstable" is a keyword
1839 getargs(x, 0, 0, _("unstable takes no arguments"))
1837 getargs(x, 0, 0, _("unstable takes no arguments"))
1840 unstables = obsmod.getrevs(repo, 'unstable')
1838 unstables = obsmod.getrevs(repo, 'unstable')
1841 return subset & unstables
1839 return subset & unstables
1842
1840
1843
1841
1844 def user(repo, subset, x):
1842 def user(repo, subset, x):
1845 """``user(string)``
1843 """``user(string)``
1846 User name contains string. The match is case-insensitive.
1844 User name contains string. The match is case-insensitive.
1847
1845
1848 If `string` starts with `re:`, the remainder of the string is treated as
1846 If `string` starts with `re:`, the remainder of the string is treated as
1849 a regular expression. To match a user that actually contains `re:`, use
1847 a regular expression. To match a user that actually contains `re:`, use
1850 the prefix `literal:`.
1848 the prefix `literal:`.
1851 """
1849 """
1852 return author(repo, subset, x)
1850 return author(repo, subset, x)
1853
1851
1854 # for internal use
1852 # for internal use
1855 def _list(repo, subset, x):
1853 def _list(repo, subset, x):
1856 s = getstring(x, "internal error")
1854 s = getstring(x, "internal error")
1857 if not s:
1855 if not s:
1858 return baseset()
1856 return baseset()
1859 ls = [repo[r].rev() for r in s.split('\0')]
1857 ls = [repo[r].rev() for r in s.split('\0')]
1860 s = subset
1858 s = subset
1861 return baseset([r for r in ls if r in s])
1859 return baseset([r for r in ls if r in s])
1862
1860
1863 # for internal use
1861 # for internal use
1864 def _intlist(repo, subset, x):
1862 def _intlist(repo, subset, x):
1865 s = getstring(x, "internal error")
1863 s = getstring(x, "internal error")
1866 if not s:
1864 if not s:
1867 return baseset()
1865 return baseset()
1868 ls = [int(r) for r in s.split('\0')]
1866 ls = [int(r) for r in s.split('\0')]
1869 s = subset
1867 s = subset
1870 return baseset([r for r in ls if r in s])
1868 return baseset([r for r in ls if r in s])
1871
1869
1872 # for internal use
1870 # for internal use
1873 def _hexlist(repo, subset, x):
1871 def _hexlist(repo, subset, x):
1874 s = getstring(x, "internal error")
1872 s = getstring(x, "internal error")
1875 if not s:
1873 if not s:
1876 return baseset()
1874 return baseset()
1877 cl = repo.changelog
1875 cl = repo.changelog
1878 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
1876 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
1879 s = subset
1877 s = subset
1880 return baseset([r for r in ls if r in s])
1878 return baseset([r for r in ls if r in s])
1881
1879
1882 symbols = {
1880 symbols = {
1883 "adds": adds,
1881 "adds": adds,
1884 "all": getall,
1882 "all": getall,
1885 "ancestor": ancestor,
1883 "ancestor": ancestor,
1886 "ancestors": ancestors,
1884 "ancestors": ancestors,
1887 "_firstancestors": _firstancestors,
1885 "_firstancestors": _firstancestors,
1888 "author": author,
1886 "author": author,
1889 "bisect": bisect,
1887 "bisect": bisect,
1890 "bisected": bisected,
1888 "bisected": bisected,
1891 "bookmark": bookmark,
1889 "bookmark": bookmark,
1892 "branch": branch,
1890 "branch": branch,
1893 "branchpoint": branchpoint,
1891 "branchpoint": branchpoint,
1894 "bumped": bumped,
1892 "bumped": bumped,
1895 "bundle": bundle,
1893 "bundle": bundle,
1896 "children": children,
1894 "children": children,
1897 "closed": closed,
1895 "closed": closed,
1898 "contains": contains,
1896 "contains": contains,
1899 "converted": converted,
1897 "converted": converted,
1900 "date": date,
1898 "date": date,
1901 "desc": desc,
1899 "desc": desc,
1902 "descendants": descendants,
1900 "descendants": descendants,
1903 "_firstdescendants": _firstdescendants,
1901 "_firstdescendants": _firstdescendants,
1904 "destination": destination,
1902 "destination": destination,
1905 "divergent": divergent,
1903 "divergent": divergent,
1906 "draft": draft,
1904 "draft": draft,
1907 "extinct": extinct,
1905 "extinct": extinct,
1908 "extra": extra,
1906 "extra": extra,
1909 "file": hasfile,
1907 "file": hasfile,
1910 "filelog": filelog,
1908 "filelog": filelog,
1911 "first": first,
1909 "first": first,
1912 "follow": follow,
1910 "follow": follow,
1913 "_followfirst": _followfirst,
1911 "_followfirst": _followfirst,
1914 "grep": grep,
1912 "grep": grep,
1915 "head": head,
1913 "head": head,
1916 "heads": heads,
1914 "heads": heads,
1917 "hidden": hidden,
1915 "hidden": hidden,
1918 "id": node_,
1916 "id": node_,
1919 "keyword": keyword,
1917 "keyword": keyword,
1920 "last": last,
1918 "last": last,
1921 "limit": limit,
1919 "limit": limit,
1922 "_matchfiles": _matchfiles,
1920 "_matchfiles": _matchfiles,
1923 "max": maxrev,
1921 "max": maxrev,
1924 "merge": merge,
1922 "merge": merge,
1925 "min": minrev,
1923 "min": minrev,
1926 "modifies": modifies,
1924 "modifies": modifies,
1927 "named": named,
1925 "named": named,
1928 "obsolete": obsolete,
1926 "obsolete": obsolete,
1929 "only": only,
1927 "only": only,
1930 "origin": origin,
1928 "origin": origin,
1931 "outgoing": outgoing,
1929 "outgoing": outgoing,
1932 "p1": p1,
1930 "p1": p1,
1933 "p2": p2,
1931 "p2": p2,
1934 "parents": parents,
1932 "parents": parents,
1935 "present": present,
1933 "present": present,
1936 "public": public,
1934 "public": public,
1937 "remote": remote,
1935 "remote": remote,
1938 "removes": removes,
1936 "removes": removes,
1939 "rev": rev,
1937 "rev": rev,
1940 "reverse": reverse,
1938 "reverse": reverse,
1941 "roots": roots,
1939 "roots": roots,
1942 "sort": sort,
1940 "sort": sort,
1943 "secret": secret,
1941 "secret": secret,
1944 "matching": matching,
1942 "matching": matching,
1945 "tag": tag,
1943 "tag": tag,
1946 "tagged": tagged,
1944 "tagged": tagged,
1947 "user": user,
1945 "user": user,
1948 "unstable": unstable,
1946 "unstable": unstable,
1949 "_list": _list,
1947 "_list": _list,
1950 "_intlist": _intlist,
1948 "_intlist": _intlist,
1951 "_hexlist": _hexlist,
1949 "_hexlist": _hexlist,
1952 }
1950 }
1953
1951
1954 # symbols which can't be used for a DoS attack for any given input
1952 # symbols which can't be used for a DoS attack for any given input
1955 # (e.g. those which accept regexes as plain strings shouldn't be included)
1953 # (e.g. those which accept regexes as plain strings shouldn't be included)
1956 # functions that just return a lot of changesets (like all) don't count here
1954 # functions that just return a lot of changesets (like all) don't count here
1957 safesymbols = set([
1955 safesymbols = set([
1958 "adds",
1956 "adds",
1959 "all",
1957 "all",
1960 "ancestor",
1958 "ancestor",
1961 "ancestors",
1959 "ancestors",
1962 "_firstancestors",
1960 "_firstancestors",
1963 "author",
1961 "author",
1964 "bisect",
1962 "bisect",
1965 "bisected",
1963 "bisected",
1966 "bookmark",
1964 "bookmark",
1967 "branch",
1965 "branch",
1968 "branchpoint",
1966 "branchpoint",
1969 "bumped",
1967 "bumped",
1970 "bundle",
1968 "bundle",
1971 "children",
1969 "children",
1972 "closed",
1970 "closed",
1973 "converted",
1971 "converted",
1974 "date",
1972 "date",
1975 "desc",
1973 "desc",
1976 "descendants",
1974 "descendants",
1977 "_firstdescendants",
1975 "_firstdescendants",
1978 "destination",
1976 "destination",
1979 "divergent",
1977 "divergent",
1980 "draft",
1978 "draft",
1981 "extinct",
1979 "extinct",
1982 "extra",
1980 "extra",
1983 "file",
1981 "file",
1984 "filelog",
1982 "filelog",
1985 "first",
1983 "first",
1986 "follow",
1984 "follow",
1987 "_followfirst",
1985 "_followfirst",
1988 "head",
1986 "head",
1989 "heads",
1987 "heads",
1990 "hidden",
1988 "hidden",
1991 "id",
1989 "id",
1992 "keyword",
1990 "keyword",
1993 "last",
1991 "last",
1994 "limit",
1992 "limit",
1995 "_matchfiles",
1993 "_matchfiles",
1996 "max",
1994 "max",
1997 "merge",
1995 "merge",
1998 "min",
1996 "min",
1999 "modifies",
1997 "modifies",
2000 "obsolete",
1998 "obsolete",
2001 "only",
1999 "only",
2002 "origin",
2000 "origin",
2003 "outgoing",
2001 "outgoing",
2004 "p1",
2002 "p1",
2005 "p2",
2003 "p2",
2006 "parents",
2004 "parents",
2007 "present",
2005 "present",
2008 "public",
2006 "public",
2009 "remote",
2007 "remote",
2010 "removes",
2008 "removes",
2011 "rev",
2009 "rev",
2012 "reverse",
2010 "reverse",
2013 "roots",
2011 "roots",
2014 "sort",
2012 "sort",
2015 "secret",
2013 "secret",
2016 "matching",
2014 "matching",
2017 "tag",
2015 "tag",
2018 "tagged",
2016 "tagged",
2019 "user",
2017 "user",
2020 "unstable",
2018 "unstable",
2021 "_list",
2019 "_list",
2022 "_intlist",
2020 "_intlist",
2023 "_hexlist",
2021 "_hexlist",
2024 ])
2022 ])
2025
2023
2026 methods = {
2024 methods = {
2027 "range": rangeset,
2025 "range": rangeset,
2028 "dagrange": dagrange,
2026 "dagrange": dagrange,
2029 "string": stringset,
2027 "string": stringset,
2030 "symbol": symbolset,
2028 "symbol": symbolset,
2031 "and": andset,
2029 "and": andset,
2032 "or": orset,
2030 "or": orset,
2033 "not": notset,
2031 "not": notset,
2034 "list": listset,
2032 "list": listset,
2035 "func": func,
2033 "func": func,
2036 "ancestor": ancestorspec,
2034 "ancestor": ancestorspec,
2037 "parent": parentspec,
2035 "parent": parentspec,
2038 "parentpost": p1,
2036 "parentpost": p1,
2039 "only": only,
2037 "only": only,
2040 "onlypost": only,
2038 "onlypost": only,
2041 }
2039 }
2042
2040
2043 def optimize(x, small):
2041 def optimize(x, small):
2044 if x is None:
2042 if x is None:
2045 return 0, x
2043 return 0, x
2046
2044
2047 smallbonus = 1
2045 smallbonus = 1
2048 if small:
2046 if small:
2049 smallbonus = .5
2047 smallbonus = .5
2050
2048
2051 op = x[0]
2049 op = x[0]
2052 if op == 'minus':
2050 if op == 'minus':
2053 return optimize(('and', x[1], ('not', x[2])), small)
2051 return optimize(('and', x[1], ('not', x[2])), small)
2054 elif op == 'only':
2052 elif op == 'only':
2055 return optimize(('func', ('symbol', 'only'),
2053 return optimize(('func', ('symbol', 'only'),
2056 ('list', x[1], x[2])), small)
2054 ('list', x[1], x[2])), small)
2057 elif op == 'dagrangepre':
2055 elif op == 'dagrangepre':
2058 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
2056 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
2059 elif op == 'dagrangepost':
2057 elif op == 'dagrangepost':
2060 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
2058 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
2061 elif op == 'rangepre':
2059 elif op == 'rangepre':
2062 return optimize(('range', ('string', '0'), x[1]), small)
2060 return optimize(('range', ('string', '0'), x[1]), small)
2063 elif op == 'rangepost':
2061 elif op == 'rangepost':
2064 return optimize(('range', x[1], ('string', 'tip')), small)
2062 return optimize(('range', x[1], ('string', 'tip')), small)
2065 elif op == 'negate':
2063 elif op == 'negate':
2066 return optimize(('string',
2064 return optimize(('string',
2067 '-' + getstring(x[1], _("can't negate that"))), small)
2065 '-' + getstring(x[1], _("can't negate that"))), small)
2068 elif op in 'string symbol negate':
2066 elif op in 'string symbol negate':
2069 return smallbonus, x # single revisions are small
2067 return smallbonus, x # single revisions are small
2070 elif op == 'and':
2068 elif op == 'and':
2071 wa, ta = optimize(x[1], True)
2069 wa, ta = optimize(x[1], True)
2072 wb, tb = optimize(x[2], True)
2070 wb, tb = optimize(x[2], True)
2073
2071
2074 # (::x and not ::y)/(not ::y and ::x) have a fast path
2072 # (::x and not ::y)/(not ::y and ::x) have a fast path
2075 def isonly(revs, bases):
2073 def isonly(revs, bases):
2076 return (
2074 return (
2077 revs[0] == 'func'
2075 revs[0] == 'func'
2078 and getstring(revs[1], _('not a symbol')) == 'ancestors'
2076 and getstring(revs[1], _('not a symbol')) == 'ancestors'
2079 and bases[0] == 'not'
2077 and bases[0] == 'not'
2080 and bases[1][0] == 'func'
2078 and bases[1][0] == 'func'
2081 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
2079 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
2082
2080
2083 w = min(wa, wb)
2081 w = min(wa, wb)
2084 if isonly(ta, tb):
2082 if isonly(ta, tb):
2085 return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2]))
2083 return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2]))
2086 if isonly(tb, ta):
2084 if isonly(tb, ta):
2087 return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2]))
2085 return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2]))
2088
2086
2089 if wa > wb:
2087 if wa > wb:
2090 return w, (op, tb, ta)
2088 return w, (op, tb, ta)
2091 return w, (op, ta, tb)
2089 return w, (op, ta, tb)
2092 elif op == 'or':
2090 elif op == 'or':
2093 wa, ta = optimize(x[1], False)
2091 wa, ta = optimize(x[1], False)
2094 wb, tb = optimize(x[2], False)
2092 wb, tb = optimize(x[2], False)
2095 if wb < wa:
2093 if wb < wa:
2096 wb, wa = wa, wb
2094 wb, wa = wa, wb
2097 return max(wa, wb), (op, ta, tb)
2095 return max(wa, wb), (op, ta, tb)
2098 elif op == 'not':
2096 elif op == 'not':
2099 o = optimize(x[1], not small)
2097 o = optimize(x[1], not small)
2100 return o[0], (op, o[1])
2098 return o[0], (op, o[1])
2101 elif op == 'parentpost':
2099 elif op == 'parentpost':
2102 o = optimize(x[1], small)
2100 o = optimize(x[1], small)
2103 return o[0], (op, o[1])
2101 return o[0], (op, o[1])
2104 elif op == 'group':
2102 elif op == 'group':
2105 return optimize(x[1], small)
2103 return optimize(x[1], small)
2106 elif op in 'dagrange range list parent ancestorspec':
2104 elif op in 'dagrange range list parent ancestorspec':
2107 if op == 'parent':
2105 if op == 'parent':
2108 # x^:y means (x^) : y, not x ^ (:y)
2106 # x^:y means (x^) : y, not x ^ (:y)
2109 post = ('parentpost', x[1])
2107 post = ('parentpost', x[1])
2110 if x[2][0] == 'dagrangepre':
2108 if x[2][0] == 'dagrangepre':
2111 return optimize(('dagrange', post, x[2][1]), small)
2109 return optimize(('dagrange', post, x[2][1]), small)
2112 elif x[2][0] == 'rangepre':
2110 elif x[2][0] == 'rangepre':
2113 return optimize(('range', post, x[2][1]), small)
2111 return optimize(('range', post, x[2][1]), small)
2114
2112
2115 wa, ta = optimize(x[1], small)
2113 wa, ta = optimize(x[1], small)
2116 wb, tb = optimize(x[2], small)
2114 wb, tb = optimize(x[2], small)
2117 return wa + wb, (op, ta, tb)
2115 return wa + wb, (op, ta, tb)
2118 elif op == 'func':
2116 elif op == 'func':
2119 f = getstring(x[1], _("not a symbol"))
2117 f = getstring(x[1], _("not a symbol"))
2120 wa, ta = optimize(x[2], small)
2118 wa, ta = optimize(x[2], small)
2121 if f in ("author branch closed date desc file grep keyword "
2119 if f in ("author branch closed date desc file grep keyword "
2122 "outgoing user"):
2120 "outgoing user"):
2123 w = 10 # slow
2121 w = 10 # slow
2124 elif f in "modifies adds removes":
2122 elif f in "modifies adds removes":
2125 w = 30 # slower
2123 w = 30 # slower
2126 elif f == "contains":
2124 elif f == "contains":
2127 w = 100 # very slow
2125 w = 100 # very slow
2128 elif f == "ancestor":
2126 elif f == "ancestor":
2129 w = 1 * smallbonus
2127 w = 1 * smallbonus
2130 elif f in "reverse limit first _intlist":
2128 elif f in "reverse limit first _intlist":
2131 w = 0
2129 w = 0
2132 elif f in "sort":
2130 elif f in "sort":
2133 w = 10 # assume most sorts look at changelog
2131 w = 10 # assume most sorts look at changelog
2134 else:
2132 else:
2135 w = 1
2133 w = 1
2136 return w + wa, (op, x[1], ta)
2134 return w + wa, (op, x[1], ta)
2137 return 1, x
2135 return 1, x
2138
2136
2139 _aliasarg = ('func', ('symbol', '_aliasarg'))
2137 _aliasarg = ('func', ('symbol', '_aliasarg'))
2140 def _getaliasarg(tree):
2138 def _getaliasarg(tree):
2141 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
2139 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
2142 return X, None otherwise.
2140 return X, None otherwise.
2143 """
2141 """
2144 if (len(tree) == 3 and tree[:2] == _aliasarg
2142 if (len(tree) == 3 and tree[:2] == _aliasarg
2145 and tree[2][0] == 'string'):
2143 and tree[2][0] == 'string'):
2146 return tree[2][1]
2144 return tree[2][1]
2147 return None
2145 return None
2148
2146
2149 def _checkaliasarg(tree, known=None):
2147 def _checkaliasarg(tree, known=None):
2150 """Check tree contains no _aliasarg construct or only ones which
2148 """Check tree contains no _aliasarg construct or only ones which
2151 value is in known. Used to avoid alias placeholders injection.
2149 value is in known. Used to avoid alias placeholders injection.
2152 """
2150 """
2153 if isinstance(tree, tuple):
2151 if isinstance(tree, tuple):
2154 arg = _getaliasarg(tree)
2152 arg = _getaliasarg(tree)
2155 if arg is not None and (not known or arg not in known):
2153 if arg is not None and (not known or arg not in known):
2156 raise error.ParseError(_("not a function: %s") % '_aliasarg')
2154 raise error.ParseError(_("not a function: %s") % '_aliasarg')
2157 for t in tree:
2155 for t in tree:
2158 _checkaliasarg(t, known)
2156 _checkaliasarg(t, known)
2159
2157
2160 # the set of valid characters for the initial letter of symbols in
2158 # the set of valid characters for the initial letter of symbols in
2161 # alias declarations and definitions
2159 # alias declarations and definitions
2162 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2160 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2163 if c.isalnum() or c in '._@$' or ord(c) > 127)
2161 if c.isalnum() or c in '._@$' or ord(c) > 127)
2164
2162
2165 def _tokenizealias(program, lookup=None):
2163 def _tokenizealias(program, lookup=None):
2166 """Parse alias declaration/definition into a stream of tokens
2164 """Parse alias declaration/definition into a stream of tokens
2167
2165
2168 This allows symbol names to use also ``$`` as an initial letter
2166 This allows symbol names to use also ``$`` as an initial letter
2169 (for backward compatibility), and callers of this function should
2167 (for backward compatibility), and callers of this function should
2170 examine whether ``$`` is used also for unexpected symbols or not.
2168 examine whether ``$`` is used also for unexpected symbols or not.
2171 """
2169 """
2172 return tokenize(program, lookup=lookup,
2170 return tokenize(program, lookup=lookup,
2173 syminitletters=_aliassyminitletters)
2171 syminitletters=_aliassyminitletters)
2174
2172
2175 def _parsealiasdecl(decl):
2173 def _parsealiasdecl(decl):
2176 """Parse alias declaration ``decl``
2174 """Parse alias declaration ``decl``
2177
2175
2178 This returns ``(name, tree, args, errorstr)`` tuple:
2176 This returns ``(name, tree, args, errorstr)`` tuple:
2179
2177
2180 - ``name``: of declared alias (may be ``decl`` itself at error)
2178 - ``name``: of declared alias (may be ``decl`` itself at error)
2181 - ``tree``: parse result (or ``None`` at error)
2179 - ``tree``: parse result (or ``None`` at error)
2182 - ``args``: list of alias argument names (or None for symbol declaration)
2180 - ``args``: list of alias argument names (or None for symbol declaration)
2183 - ``errorstr``: detail about detected error (or None)
2181 - ``errorstr``: detail about detected error (or None)
2184
2182
2185 >>> _parsealiasdecl('foo')
2183 >>> _parsealiasdecl('foo')
2186 ('foo', ('symbol', 'foo'), None, None)
2184 ('foo', ('symbol', 'foo'), None, None)
2187 >>> _parsealiasdecl('$foo')
2185 >>> _parsealiasdecl('$foo')
2188 ('$foo', None, None, "'$' not for alias arguments")
2186 ('$foo', None, None, "'$' not for alias arguments")
2189 >>> _parsealiasdecl('foo::bar')
2187 >>> _parsealiasdecl('foo::bar')
2190 ('foo::bar', None, None, 'invalid format')
2188 ('foo::bar', None, None, 'invalid format')
2191 >>> _parsealiasdecl('foo bar')
2189 >>> _parsealiasdecl('foo bar')
2192 ('foo bar', None, None, 'at 4: invalid token')
2190 ('foo bar', None, None, 'at 4: invalid token')
2193 >>> _parsealiasdecl('foo()')
2191 >>> _parsealiasdecl('foo()')
2194 ('foo', ('func', ('symbol', 'foo')), [], None)
2192 ('foo', ('func', ('symbol', 'foo')), [], None)
2195 >>> _parsealiasdecl('$foo()')
2193 >>> _parsealiasdecl('$foo()')
2196 ('$foo()', None, None, "'$' not for alias arguments")
2194 ('$foo()', None, None, "'$' not for alias arguments")
2197 >>> _parsealiasdecl('foo($1, $2)')
2195 >>> _parsealiasdecl('foo($1, $2)')
2198 ('foo', ('func', ('symbol', 'foo')), ['$1', '$2'], None)
2196 ('foo', ('func', ('symbol', 'foo')), ['$1', '$2'], None)
2199 >>> _parsealiasdecl('foo(bar_bar, baz.baz)')
2197 >>> _parsealiasdecl('foo(bar_bar, baz.baz)')
2200 ('foo', ('func', ('symbol', 'foo')), ['bar_bar', 'baz.baz'], None)
2198 ('foo', ('func', ('symbol', 'foo')), ['bar_bar', 'baz.baz'], None)
2201 >>> _parsealiasdecl('foo($1, $2, nested($1, $2))')
2199 >>> _parsealiasdecl('foo($1, $2, nested($1, $2))')
2202 ('foo($1, $2, nested($1, $2))', None, None, 'invalid argument list')
2200 ('foo($1, $2, nested($1, $2))', None, None, 'invalid argument list')
2203 >>> _parsealiasdecl('foo(bar($1, $2))')
2201 >>> _parsealiasdecl('foo(bar($1, $2))')
2204 ('foo(bar($1, $2))', None, None, 'invalid argument list')
2202 ('foo(bar($1, $2))', None, None, 'invalid argument list')
2205 >>> _parsealiasdecl('foo("string")')
2203 >>> _parsealiasdecl('foo("string")')
2206 ('foo("string")', None, None, 'invalid argument list')
2204 ('foo("string")', None, None, 'invalid argument list')
2207 >>> _parsealiasdecl('foo($1, $2')
2205 >>> _parsealiasdecl('foo($1, $2')
2208 ('foo($1, $2', None, None, 'at 10: unexpected token: end')
2206 ('foo($1, $2', None, None, 'at 10: unexpected token: end')
2209 >>> _parsealiasdecl('foo("string')
2207 >>> _parsealiasdecl('foo("string')
2210 ('foo("string', None, None, 'at 5: unterminated string')
2208 ('foo("string', None, None, 'at 5: unterminated string')
2211 >>> _parsealiasdecl('foo($1, $2, $1)')
2209 >>> _parsealiasdecl('foo($1, $2, $1)')
2212 ('foo', None, None, 'argument names collide with each other')
2210 ('foo', None, None, 'argument names collide with each other')
2213 """
2211 """
2214 p = parser.parser(_tokenizealias, elements)
2212 p = parser.parser(_tokenizealias, elements)
2215 try:
2213 try:
2216 tree, pos = p.parse(decl)
2214 tree, pos = p.parse(decl)
2217 if (pos != len(decl)):
2215 if (pos != len(decl)):
2218 raise error.ParseError(_('invalid token'), pos)
2216 raise error.ParseError(_('invalid token'), pos)
2219
2217
2220 if isvalidsymbol(tree):
2218 if isvalidsymbol(tree):
2221 # "name = ...." style
2219 # "name = ...." style
2222 name = getsymbol(tree)
2220 name = getsymbol(tree)
2223 if name.startswith('$'):
2221 if name.startswith('$'):
2224 return (decl, None, None, _("'$' not for alias arguments"))
2222 return (decl, None, None, _("'$' not for alias arguments"))
2225 return (name, ('symbol', name), None, None)
2223 return (name, ('symbol', name), None, None)
2226
2224
2227 if isvalidfunc(tree):
2225 if isvalidfunc(tree):
2228 # "name(arg, ....) = ...." style
2226 # "name(arg, ....) = ...." style
2229 name = getfuncname(tree)
2227 name = getfuncname(tree)
2230 if name.startswith('$'):
2228 if name.startswith('$'):
2231 return (decl, None, None, _("'$' not for alias arguments"))
2229 return (decl, None, None, _("'$' not for alias arguments"))
2232 args = []
2230 args = []
2233 for arg in getfuncargs(tree):
2231 for arg in getfuncargs(tree):
2234 if not isvalidsymbol(arg):
2232 if not isvalidsymbol(arg):
2235 return (decl, None, None, _("invalid argument list"))
2233 return (decl, None, None, _("invalid argument list"))
2236 args.append(getsymbol(arg))
2234 args.append(getsymbol(arg))
2237 if len(args) != len(set(args)):
2235 if len(args) != len(set(args)):
2238 return (name, None, None,
2236 return (name, None, None,
2239 _("argument names collide with each other"))
2237 _("argument names collide with each other"))
2240 return (name, ('func', ('symbol', name)), args, None)
2238 return (name, ('func', ('symbol', name)), args, None)
2241
2239
2242 return (decl, None, None, _("invalid format"))
2240 return (decl, None, None, _("invalid format"))
2243 except error.ParseError, inst:
2241 except error.ParseError, inst:
2244 return (decl, None, None, parseerrordetail(inst))
2242 return (decl, None, None, parseerrordetail(inst))
2245
2243
2246 def _parsealiasdefn(defn, args):
2244 def _parsealiasdefn(defn, args):
2247 """Parse alias definition ``defn``
2245 """Parse alias definition ``defn``
2248
2246
2249 This function also replaces alias argument references in the
2247 This function also replaces alias argument references in the
2250 specified definition by ``_aliasarg(ARGNAME)``.
2248 specified definition by ``_aliasarg(ARGNAME)``.
2251
2249
2252 ``args`` is a list of alias argument names, or None if the alias
2250 ``args`` is a list of alias argument names, or None if the alias
2253 is declared as a symbol.
2251 is declared as a symbol.
2254
2252
2255 This returns "tree" as parsing result.
2253 This returns "tree" as parsing result.
2256
2254
2257 >>> args = ['$1', '$2', 'foo']
2255 >>> args = ['$1', '$2', 'foo']
2258 >>> print prettyformat(_parsealiasdefn('$1 or foo', args))
2256 >>> print prettyformat(_parsealiasdefn('$1 or foo', args))
2259 (or
2257 (or
2260 (func
2258 (func
2261 ('symbol', '_aliasarg')
2259 ('symbol', '_aliasarg')
2262 ('string', '$1'))
2260 ('string', '$1'))
2263 (func
2261 (func
2264 ('symbol', '_aliasarg')
2262 ('symbol', '_aliasarg')
2265 ('string', 'foo')))
2263 ('string', 'foo')))
2266 >>> try:
2264 >>> try:
2267 ... _parsealiasdefn('$1 or $bar', args)
2265 ... _parsealiasdefn('$1 or $bar', args)
2268 ... except error.ParseError, inst:
2266 ... except error.ParseError, inst:
2269 ... print parseerrordetail(inst)
2267 ... print parseerrordetail(inst)
2270 at 6: '$' not for alias arguments
2268 at 6: '$' not for alias arguments
2271 >>> args = ['$1', '$10', 'foo']
2269 >>> args = ['$1', '$10', 'foo']
2272 >>> print prettyformat(_parsealiasdefn('$10 or foobar', args))
2270 >>> print prettyformat(_parsealiasdefn('$10 or foobar', args))
2273 (or
2271 (or
2274 (func
2272 (func
2275 ('symbol', '_aliasarg')
2273 ('symbol', '_aliasarg')
2276 ('string', '$10'))
2274 ('string', '$10'))
2277 ('symbol', 'foobar'))
2275 ('symbol', 'foobar'))
2278 >>> print prettyformat(_parsealiasdefn('"$1" or "foo"', args))
2276 >>> print prettyformat(_parsealiasdefn('"$1" or "foo"', args))
2279 (or
2277 (or
2280 ('string', '$1')
2278 ('string', '$1')
2281 ('string', 'foo'))
2279 ('string', 'foo'))
2282 """
2280 """
2283 def tokenizedefn(program, lookup=None):
2281 def tokenizedefn(program, lookup=None):
2284 if args:
2282 if args:
2285 argset = set(args)
2283 argset = set(args)
2286 else:
2284 else:
2287 argset = set()
2285 argset = set()
2288
2286
2289 for t, value, pos in _tokenizealias(program, lookup=lookup):
2287 for t, value, pos in _tokenizealias(program, lookup=lookup):
2290 if t == 'symbol':
2288 if t == 'symbol':
2291 if value in argset:
2289 if value in argset:
2292 # emulate tokenization of "_aliasarg('ARGNAME')":
2290 # emulate tokenization of "_aliasarg('ARGNAME')":
2293 # "_aliasarg()" is an unknown symbol only used separate
2291 # "_aliasarg()" is an unknown symbol only used separate
2294 # alias argument placeholders from regular strings.
2292 # alias argument placeholders from regular strings.
2295 yield ('symbol', '_aliasarg', pos)
2293 yield ('symbol', '_aliasarg', pos)
2296 yield ('(', None, pos)
2294 yield ('(', None, pos)
2297 yield ('string', value, pos)
2295 yield ('string', value, pos)
2298 yield (')', None, pos)
2296 yield (')', None, pos)
2299 continue
2297 continue
2300 elif value.startswith('$'):
2298 elif value.startswith('$'):
2301 raise error.ParseError(_("'$' not for alias arguments"),
2299 raise error.ParseError(_("'$' not for alias arguments"),
2302 pos)
2300 pos)
2303 yield (t, value, pos)
2301 yield (t, value, pos)
2304
2302
2305 p = parser.parser(tokenizedefn, elements)
2303 p = parser.parser(tokenizedefn, elements)
2306 tree, pos = p.parse(defn)
2304 tree, pos = p.parse(defn)
2307 if pos != len(defn):
2305 if pos != len(defn):
2308 raise error.ParseError(_('invalid token'), pos)
2306 raise error.ParseError(_('invalid token'), pos)
2309 return tree
2307 return tree
2310
2308
2311 class revsetalias(object):
2309 class revsetalias(object):
2312 # whether own `error` information is already shown or not.
2310 # whether own `error` information is already shown or not.
2313 # this avoids showing same warning multiple times at each `findaliases`.
2311 # this avoids showing same warning multiple times at each `findaliases`.
2314 warned = False
2312 warned = False
2315
2313
2316 def __init__(self, name, value):
2314 def __init__(self, name, value):
2317 '''Aliases like:
2315 '''Aliases like:
2318
2316
2319 h = heads(default)
2317 h = heads(default)
2320 b($1) = ancestors($1) - ancestors(default)
2318 b($1) = ancestors($1) - ancestors(default)
2321 '''
2319 '''
2322 self.name, self.tree, self.args, self.error = _parsealiasdecl(name)
2320 self.name, self.tree, self.args, self.error = _parsealiasdecl(name)
2323 if self.error:
2321 if self.error:
2324 self.error = _('failed to parse the declaration of revset alias'
2322 self.error = _('failed to parse the declaration of revset alias'
2325 ' "%s": %s') % (self.name, self.error)
2323 ' "%s": %s') % (self.name, self.error)
2326 return
2324 return
2327
2325
2328 try:
2326 try:
2329 self.replacement = _parsealiasdefn(value, self.args)
2327 self.replacement = _parsealiasdefn(value, self.args)
2330 # Check for placeholder injection
2328 # Check for placeholder injection
2331 _checkaliasarg(self.replacement, self.args)
2329 _checkaliasarg(self.replacement, self.args)
2332 except error.ParseError, inst:
2330 except error.ParseError, inst:
2333 self.error = _('failed to parse the definition of revset alias'
2331 self.error = _('failed to parse the definition of revset alias'
2334 ' "%s": %s') % (self.name, parseerrordetail(inst))
2332 ' "%s": %s') % (self.name, parseerrordetail(inst))
2335
2333
2336 def _getalias(aliases, tree):
2334 def _getalias(aliases, tree):
2337 """If tree looks like an unexpanded alias, return it. Return None
2335 """If tree looks like an unexpanded alias, return it. Return None
2338 otherwise.
2336 otherwise.
2339 """
2337 """
2340 if isinstance(tree, tuple) and tree:
2338 if isinstance(tree, tuple) and tree:
2341 if tree[0] == 'symbol' and len(tree) == 2:
2339 if tree[0] == 'symbol' and len(tree) == 2:
2342 name = tree[1]
2340 name = tree[1]
2343 alias = aliases.get(name)
2341 alias = aliases.get(name)
2344 if alias and alias.args is None and alias.tree == tree:
2342 if alias and alias.args is None and alias.tree == tree:
2345 return alias
2343 return alias
2346 if tree[0] == 'func' and len(tree) > 1:
2344 if tree[0] == 'func' and len(tree) > 1:
2347 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
2345 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
2348 name = tree[1][1]
2346 name = tree[1][1]
2349 alias = aliases.get(name)
2347 alias = aliases.get(name)
2350 if alias and alias.args is not None and alias.tree == tree[:2]:
2348 if alias and alias.args is not None and alias.tree == tree[:2]:
2351 return alias
2349 return alias
2352 return None
2350 return None
2353
2351
2354 def _expandargs(tree, args):
2352 def _expandargs(tree, args):
2355 """Replace _aliasarg instances with the substitution value of the
2353 """Replace _aliasarg instances with the substitution value of the
2356 same name in args, recursively.
2354 same name in args, recursively.
2357 """
2355 """
2358 if not tree or not isinstance(tree, tuple):
2356 if not tree or not isinstance(tree, tuple):
2359 return tree
2357 return tree
2360 arg = _getaliasarg(tree)
2358 arg = _getaliasarg(tree)
2361 if arg is not None:
2359 if arg is not None:
2362 return args[arg]
2360 return args[arg]
2363 return tuple(_expandargs(t, args) for t in tree)
2361 return tuple(_expandargs(t, args) for t in tree)
2364
2362
2365 def _expandaliases(aliases, tree, expanding, cache):
2363 def _expandaliases(aliases, tree, expanding, cache):
2366 """Expand aliases in tree, recursively.
2364 """Expand aliases in tree, recursively.
2367
2365
2368 'aliases' is a dictionary mapping user defined aliases to
2366 'aliases' is a dictionary mapping user defined aliases to
2369 revsetalias objects.
2367 revsetalias objects.
2370 """
2368 """
2371 if not isinstance(tree, tuple):
2369 if not isinstance(tree, tuple):
2372 # Do not expand raw strings
2370 # Do not expand raw strings
2373 return tree
2371 return tree
2374 alias = _getalias(aliases, tree)
2372 alias = _getalias(aliases, tree)
2375 if alias is not None:
2373 if alias is not None:
2376 if alias.error:
2374 if alias.error:
2377 raise util.Abort(alias.error)
2375 raise util.Abort(alias.error)
2378 if alias in expanding:
2376 if alias in expanding:
2379 raise error.ParseError(_('infinite expansion of revset alias "%s" '
2377 raise error.ParseError(_('infinite expansion of revset alias "%s" '
2380 'detected') % alias.name)
2378 'detected') % alias.name)
2381 expanding.append(alias)
2379 expanding.append(alias)
2382 if alias.name not in cache:
2380 if alias.name not in cache:
2383 cache[alias.name] = _expandaliases(aliases, alias.replacement,
2381 cache[alias.name] = _expandaliases(aliases, alias.replacement,
2384 expanding, cache)
2382 expanding, cache)
2385 result = cache[alias.name]
2383 result = cache[alias.name]
2386 expanding.pop()
2384 expanding.pop()
2387 if alias.args is not None:
2385 if alias.args is not None:
2388 l = getlist(tree[2])
2386 l = getlist(tree[2])
2389 if len(l) != len(alias.args):
2387 if len(l) != len(alias.args):
2390 raise error.ParseError(
2388 raise error.ParseError(
2391 _('invalid number of arguments: %s') % len(l))
2389 _('invalid number of arguments: %s') % len(l))
2392 l = [_expandaliases(aliases, a, [], cache) for a in l]
2390 l = [_expandaliases(aliases, a, [], cache) for a in l]
2393 result = _expandargs(result, dict(zip(alias.args, l)))
2391 result = _expandargs(result, dict(zip(alias.args, l)))
2394 else:
2392 else:
2395 result = tuple(_expandaliases(aliases, t, expanding, cache)
2393 result = tuple(_expandaliases(aliases, t, expanding, cache)
2396 for t in tree)
2394 for t in tree)
2397 return result
2395 return result
2398
2396
2399 def findaliases(ui, tree, showwarning=None):
2397 def findaliases(ui, tree, showwarning=None):
2400 _checkaliasarg(tree)
2398 _checkaliasarg(tree)
2401 aliases = {}
2399 aliases = {}
2402 for k, v in ui.configitems('revsetalias'):
2400 for k, v in ui.configitems('revsetalias'):
2403 alias = revsetalias(k, v)
2401 alias = revsetalias(k, v)
2404 aliases[alias.name] = alias
2402 aliases[alias.name] = alias
2405 tree = _expandaliases(aliases, tree, [], {})
2403 tree = _expandaliases(aliases, tree, [], {})
2406 if showwarning:
2404 if showwarning:
2407 # warn about problematic (but not referred) aliases
2405 # warn about problematic (but not referred) aliases
2408 for name, alias in sorted(aliases.iteritems()):
2406 for name, alias in sorted(aliases.iteritems()):
2409 if alias.error and not alias.warned:
2407 if alias.error and not alias.warned:
2410 showwarning(_('warning: %s\n') % (alias.error))
2408 showwarning(_('warning: %s\n') % (alias.error))
2411 alias.warned = True
2409 alias.warned = True
2412 return tree
2410 return tree
2413
2411
2414 def foldconcat(tree):
2412 def foldconcat(tree):
2415 """Fold elements to be concatenated by `##`
2413 """Fold elements to be concatenated by `##`
2416 """
2414 """
2417 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2415 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2418 return tree
2416 return tree
2419 if tree[0] == '_concat':
2417 if tree[0] == '_concat':
2420 pending = [tree]
2418 pending = [tree]
2421 l = []
2419 l = []
2422 while pending:
2420 while pending:
2423 e = pending.pop()
2421 e = pending.pop()
2424 if e[0] == '_concat':
2422 if e[0] == '_concat':
2425 pending.extend(reversed(e[1:]))
2423 pending.extend(reversed(e[1:]))
2426 elif e[0] in ('string', 'symbol'):
2424 elif e[0] in ('string', 'symbol'):
2427 l.append(e[1])
2425 l.append(e[1])
2428 else:
2426 else:
2429 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2427 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2430 raise error.ParseError(msg)
2428 raise error.ParseError(msg)
2431 return ('string', ''.join(l))
2429 return ('string', ''.join(l))
2432 else:
2430 else:
2433 return tuple(foldconcat(t) for t in tree)
2431 return tuple(foldconcat(t) for t in tree)
2434
2432
2435 def parse(spec, lookup=None):
2433 def parse(spec, lookup=None):
2436 p = parser.parser(tokenize, elements)
2434 p = parser.parser(tokenize, elements)
2437 return p.parse(spec, lookup=lookup)
2435 return p.parse(spec, lookup=lookup)
2438
2436
2439 def match(ui, spec, repo=None):
2437 def match(ui, spec, repo=None):
2440 if not spec:
2438 if not spec:
2441 raise error.ParseError(_("empty query"))
2439 raise error.ParseError(_("empty query"))
2442 lookup = None
2440 lookup = None
2443 if repo:
2441 if repo:
2444 lookup = repo.__contains__
2442 lookup = repo.__contains__
2445 tree, pos = parse(spec, lookup)
2443 tree, pos = parse(spec, lookup)
2446 if (pos != len(spec)):
2444 if (pos != len(spec)):
2447 raise error.ParseError(_("invalid token"), pos)
2445 raise error.ParseError(_("invalid token"), pos)
2448 if ui:
2446 if ui:
2449 tree = findaliases(ui, tree, showwarning=ui.warn)
2447 tree = findaliases(ui, tree, showwarning=ui.warn)
2450 tree = foldconcat(tree)
2448 tree = foldconcat(tree)
2451 weight, tree = optimize(tree, True)
2449 weight, tree = optimize(tree, True)
2452 def mfunc(repo, subset=None):
2450 def mfunc(repo, subset=None):
2453 if subset is None:
2451 if subset is None:
2454 subset = fullreposet(repo)
2452 subset = fullreposet(repo)
2455 if util.safehasattr(subset, 'isascending'):
2453 if util.safehasattr(subset, 'isascending'):
2456 result = getset(repo, subset, tree)
2454 result = getset(repo, subset, tree)
2457 else:
2455 else:
2458 result = getset(repo, baseset(subset), tree)
2456 result = getset(repo, baseset(subset), tree)
2459 return result
2457 return result
2460 return mfunc
2458 return mfunc
2461
2459
2462 def formatspec(expr, *args):
2460 def formatspec(expr, *args):
2463 '''
2461 '''
2464 This is a convenience function for using revsets internally, and
2462 This is a convenience function for using revsets internally, and
2465 escapes arguments appropriately. Aliases are intentionally ignored
2463 escapes arguments appropriately. Aliases are intentionally ignored
2466 so that intended expression behavior isn't accidentally subverted.
2464 so that intended expression behavior isn't accidentally subverted.
2467
2465
2468 Supported arguments:
2466 Supported arguments:
2469
2467
2470 %r = revset expression, parenthesized
2468 %r = revset expression, parenthesized
2471 %d = int(arg), no quoting
2469 %d = int(arg), no quoting
2472 %s = string(arg), escaped and single-quoted
2470 %s = string(arg), escaped and single-quoted
2473 %b = arg.branch(), escaped and single-quoted
2471 %b = arg.branch(), escaped and single-quoted
2474 %n = hex(arg), single-quoted
2472 %n = hex(arg), single-quoted
2475 %% = a literal '%'
2473 %% = a literal '%'
2476
2474
2477 Prefixing the type with 'l' specifies a parenthesized list of that type.
2475 Prefixing the type with 'l' specifies a parenthesized list of that type.
2478
2476
2479 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2477 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2480 '(10 or 11):: and ((this()) or (that()))'
2478 '(10 or 11):: and ((this()) or (that()))'
2481 >>> formatspec('%d:: and not %d::', 10, 20)
2479 >>> formatspec('%d:: and not %d::', 10, 20)
2482 '10:: and not 20::'
2480 '10:: and not 20::'
2483 >>> formatspec('%ld or %ld', [], [1])
2481 >>> formatspec('%ld or %ld', [], [1])
2484 "_list('') or 1"
2482 "_list('') or 1"
2485 >>> formatspec('keyword(%s)', 'foo\\xe9')
2483 >>> formatspec('keyword(%s)', 'foo\\xe9')
2486 "keyword('foo\\\\xe9')"
2484 "keyword('foo\\\\xe9')"
2487 >>> b = lambda: 'default'
2485 >>> b = lambda: 'default'
2488 >>> b.branch = b
2486 >>> b.branch = b
2489 >>> formatspec('branch(%b)', b)
2487 >>> formatspec('branch(%b)', b)
2490 "branch('default')"
2488 "branch('default')"
2491 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2489 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2492 "root(_list('a\\x00b\\x00c\\x00d'))"
2490 "root(_list('a\\x00b\\x00c\\x00d'))"
2493 '''
2491 '''
2494
2492
2495 def quote(s):
2493 def quote(s):
2496 return repr(str(s))
2494 return repr(str(s))
2497
2495
2498 def argtype(c, arg):
2496 def argtype(c, arg):
2499 if c == 'd':
2497 if c == 'd':
2500 return str(int(arg))
2498 return str(int(arg))
2501 elif c == 's':
2499 elif c == 's':
2502 return quote(arg)
2500 return quote(arg)
2503 elif c == 'r':
2501 elif c == 'r':
2504 parse(arg) # make sure syntax errors are confined
2502 parse(arg) # make sure syntax errors are confined
2505 return '(%s)' % arg
2503 return '(%s)' % arg
2506 elif c == 'n':
2504 elif c == 'n':
2507 return quote(node.hex(arg))
2505 return quote(node.hex(arg))
2508 elif c == 'b':
2506 elif c == 'b':
2509 return quote(arg.branch())
2507 return quote(arg.branch())
2510
2508
2511 def listexp(s, t):
2509 def listexp(s, t):
2512 l = len(s)
2510 l = len(s)
2513 if l == 0:
2511 if l == 0:
2514 return "_list('')"
2512 return "_list('')"
2515 elif l == 1:
2513 elif l == 1:
2516 return argtype(t, s[0])
2514 return argtype(t, s[0])
2517 elif t == 'd':
2515 elif t == 'd':
2518 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2516 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2519 elif t == 's':
2517 elif t == 's':
2520 return "_list('%s')" % "\0".join(s)
2518 return "_list('%s')" % "\0".join(s)
2521 elif t == 'n':
2519 elif t == 'n':
2522 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2520 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2523 elif t == 'b':
2521 elif t == 'b':
2524 return "_list('%s')" % "\0".join(a.branch() for a in s)
2522 return "_list('%s')" % "\0".join(a.branch() for a in s)
2525
2523
2526 m = l // 2
2524 m = l // 2
2527 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2525 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2528
2526
2529 ret = ''
2527 ret = ''
2530 pos = 0
2528 pos = 0
2531 arg = 0
2529 arg = 0
2532 while pos < len(expr):
2530 while pos < len(expr):
2533 c = expr[pos]
2531 c = expr[pos]
2534 if c == '%':
2532 if c == '%':
2535 pos += 1
2533 pos += 1
2536 d = expr[pos]
2534 d = expr[pos]
2537 if d == '%':
2535 if d == '%':
2538 ret += d
2536 ret += d
2539 elif d in 'dsnbr':
2537 elif d in 'dsnbr':
2540 ret += argtype(d, args[arg])
2538 ret += argtype(d, args[arg])
2541 arg += 1
2539 arg += 1
2542 elif d == 'l':
2540 elif d == 'l':
2543 # a list of some type
2541 # a list of some type
2544 pos += 1
2542 pos += 1
2545 d = expr[pos]
2543 d = expr[pos]
2546 ret += listexp(list(args[arg]), d)
2544 ret += listexp(list(args[arg]), d)
2547 arg += 1
2545 arg += 1
2548 else:
2546 else:
2549 raise util.Abort('unexpected revspec format character %s' % d)
2547 raise util.Abort('unexpected revspec format character %s' % d)
2550 else:
2548 else:
2551 ret += c
2549 ret += c
2552 pos += 1
2550 pos += 1
2553
2551
2554 return ret
2552 return ret
2555
2553
2556 def prettyformat(tree):
2554 def prettyformat(tree):
2557 def _prettyformat(tree, level, lines):
2555 def _prettyformat(tree, level, lines):
2558 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2556 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2559 lines.append((level, str(tree)))
2557 lines.append((level, str(tree)))
2560 else:
2558 else:
2561 lines.append((level, '(%s' % tree[0]))
2559 lines.append((level, '(%s' % tree[0]))
2562 for s in tree[1:]:
2560 for s in tree[1:]:
2563 _prettyformat(s, level + 1, lines)
2561 _prettyformat(s, level + 1, lines)
2564 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2562 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2565
2563
2566 lines = []
2564 lines = []
2567 _prettyformat(tree, 0, lines)
2565 _prettyformat(tree, 0, lines)
2568 output = '\n'.join((' '*l + s) for l, s in lines)
2566 output = '\n'.join((' '*l + s) for l, s in lines)
2569 return output
2567 return output
2570
2568
2571 def depth(tree):
2569 def depth(tree):
2572 if isinstance(tree, tuple):
2570 if isinstance(tree, tuple):
2573 return max(map(depth, tree)) + 1
2571 return max(map(depth, tree)) + 1
2574 else:
2572 else:
2575 return 0
2573 return 0
2576
2574
2577 def funcsused(tree):
2575 def funcsused(tree):
2578 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2576 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2579 return set()
2577 return set()
2580 else:
2578 else:
2581 funcs = set()
2579 funcs = set()
2582 for s in tree[1:]:
2580 for s in tree[1:]:
2583 funcs |= funcsused(s)
2581 funcs |= funcsused(s)
2584 if tree[0] == 'func':
2582 if tree[0] == 'func':
2585 funcs.add(tree[1][1])
2583 funcs.add(tree[1][1])
2586 return funcs
2584 return funcs
2587
2585
2588 class abstractsmartset(object):
2586 class abstractsmartset(object):
2589
2587
2590 def __nonzero__(self):
2588 def __nonzero__(self):
2591 """True if the smartset is not empty"""
2589 """True if the smartset is not empty"""
2592 raise NotImplementedError()
2590 raise NotImplementedError()
2593
2591
2594 def __contains__(self, rev):
2592 def __contains__(self, rev):
2595 """provide fast membership testing"""
2593 """provide fast membership testing"""
2596 raise NotImplementedError()
2594 raise NotImplementedError()
2597
2595
2598 def __iter__(self):
2596 def __iter__(self):
2599 """iterate the set in the order it is supposed to be iterated"""
2597 """iterate the set in the order it is supposed to be iterated"""
2600 raise NotImplementedError()
2598 raise NotImplementedError()
2601
2599
2602 # Attributes containing a function to perform a fast iteration in a given
2600 # Attributes containing a function to perform a fast iteration in a given
2603 # direction. A smartset can have none, one, or both defined.
2601 # direction. A smartset can have none, one, or both defined.
2604 #
2602 #
2605 # Default value is None instead of a function returning None to avoid
2603 # Default value is None instead of a function returning None to avoid
2606 # initializing an iterator just for testing if a fast method exists.
2604 # initializing an iterator just for testing if a fast method exists.
2607 fastasc = None
2605 fastasc = None
2608 fastdesc = None
2606 fastdesc = None
2609
2607
2610 def isascending(self):
2608 def isascending(self):
2611 """True if the set will iterate in ascending order"""
2609 """True if the set will iterate in ascending order"""
2612 raise NotImplementedError()
2610 raise NotImplementedError()
2613
2611
2614 def isdescending(self):
2612 def isdescending(self):
2615 """True if the set will iterate in descending order"""
2613 """True if the set will iterate in descending order"""
2616 raise NotImplementedError()
2614 raise NotImplementedError()
2617
2615
2618 def min(self):
2616 def min(self):
2619 """return the minimum element in the set"""
2617 """return the minimum element in the set"""
2620 if self.fastasc is not None:
2618 if self.fastasc is not None:
2621 for r in self.fastasc():
2619 for r in self.fastasc():
2622 return r
2620 return r
2623 raise ValueError('arg is an empty sequence')
2621 raise ValueError('arg is an empty sequence')
2624 return min(self)
2622 return min(self)
2625
2623
2626 def max(self):
2624 def max(self):
2627 """return the maximum element in the set"""
2625 """return the maximum element in the set"""
2628 if self.fastdesc is not None:
2626 if self.fastdesc is not None:
2629 for r in self.fastdesc():
2627 for r in self.fastdesc():
2630 return r
2628 return r
2631 raise ValueError('arg is an empty sequence')
2629 raise ValueError('arg is an empty sequence')
2632 return max(self)
2630 return max(self)
2633
2631
2634 def first(self):
2632 def first(self):
2635 """return the first element in the set (user iteration perspective)
2633 """return the first element in the set (user iteration perspective)
2636
2634
2637 Return None if the set is empty"""
2635 Return None if the set is empty"""
2638 raise NotImplementedError()
2636 raise NotImplementedError()
2639
2637
2640 def last(self):
2638 def last(self):
2641 """return the last element in the set (user iteration perspective)
2639 """return the last element in the set (user iteration perspective)
2642
2640
2643 Return None if the set is empty"""
2641 Return None if the set is empty"""
2644 raise NotImplementedError()
2642 raise NotImplementedError()
2645
2643
2646 def __len__(self):
2644 def __len__(self):
2647 """return the length of the smartsets
2645 """return the length of the smartsets
2648
2646
2649 This can be expensive on smartset that could be lazy otherwise."""
2647 This can be expensive on smartset that could be lazy otherwise."""
2650 raise NotImplementedError()
2648 raise NotImplementedError()
2651
2649
2652 def reverse(self):
2650 def reverse(self):
2653 """reverse the expected iteration order"""
2651 """reverse the expected iteration order"""
2654 raise NotImplementedError()
2652 raise NotImplementedError()
2655
2653
2656 def sort(self, reverse=True):
2654 def sort(self, reverse=True):
2657 """get the set to iterate in an ascending or descending order"""
2655 """get the set to iterate in an ascending or descending order"""
2658 raise NotImplementedError()
2656 raise NotImplementedError()
2659
2657
2660 def __and__(self, other):
2658 def __and__(self, other):
2661 """Returns a new object with the intersection of the two collections.
2659 """Returns a new object with the intersection of the two collections.
2662
2660
2663 This is part of the mandatory API for smartset."""
2661 This is part of the mandatory API for smartset."""
2664 return self.filter(other.__contains__, cache=False)
2662 return self.filter(other.__contains__, cache=False)
2665
2663
2666 def __add__(self, other):
2664 def __add__(self, other):
2667 """Returns a new object with the union of the two collections.
2665 """Returns a new object with the union of the two collections.
2668
2666
2669 This is part of the mandatory API for smartset."""
2667 This is part of the mandatory API for smartset."""
2670 return addset(self, other)
2668 return addset(self, other)
2671
2669
2672 def __sub__(self, other):
2670 def __sub__(self, other):
2673 """Returns a new object with the substraction of the two collections.
2671 """Returns a new object with the substraction of the two collections.
2674
2672
2675 This is part of the mandatory API for smartset."""
2673 This is part of the mandatory API for smartset."""
2676 c = other.__contains__
2674 c = other.__contains__
2677 return self.filter(lambda r: not c(r), cache=False)
2675 return self.filter(lambda r: not c(r), cache=False)
2678
2676
2679 def filter(self, condition, cache=True):
2677 def filter(self, condition, cache=True):
2680 """Returns this smartset filtered by condition as a new smartset.
2678 """Returns this smartset filtered by condition as a new smartset.
2681
2679
2682 `condition` is a callable which takes a revision number and returns a
2680 `condition` is a callable which takes a revision number and returns a
2683 boolean.
2681 boolean.
2684
2682
2685 This is part of the mandatory API for smartset."""
2683 This is part of the mandatory API for smartset."""
2686 # builtin cannot be cached. but do not needs to
2684 # builtin cannot be cached. but do not needs to
2687 if cache and util.safehasattr(condition, 'func_code'):
2685 if cache and util.safehasattr(condition, 'func_code'):
2688 condition = util.cachefunc(condition)
2686 condition = util.cachefunc(condition)
2689 return filteredset(self, condition)
2687 return filteredset(self, condition)
2690
2688
2691 class baseset(abstractsmartset):
2689 class baseset(abstractsmartset):
2692 """Basic data structure that represents a revset and contains the basic
2690 """Basic data structure that represents a revset and contains the basic
2693 operation that it should be able to perform.
2691 operation that it should be able to perform.
2694
2692
2695 Every method in this class should be implemented by any smartset class.
2693 Every method in this class should be implemented by any smartset class.
2696 """
2694 """
2697 def __init__(self, data=()):
2695 def __init__(self, data=()):
2698 if not isinstance(data, list):
2696 if not isinstance(data, list):
2699 data = list(data)
2697 data = list(data)
2700 self._list = data
2698 self._list = data
2701 self._ascending = None
2699 self._ascending = None
2702
2700
2703 @util.propertycache
2701 @util.propertycache
2704 def _set(self):
2702 def _set(self):
2705 return set(self._list)
2703 return set(self._list)
2706
2704
2707 @util.propertycache
2705 @util.propertycache
2708 def _asclist(self):
2706 def _asclist(self):
2709 asclist = self._list[:]
2707 asclist = self._list[:]
2710 asclist.sort()
2708 asclist.sort()
2711 return asclist
2709 return asclist
2712
2710
2713 def __iter__(self):
2711 def __iter__(self):
2714 if self._ascending is None:
2712 if self._ascending is None:
2715 return iter(self._list)
2713 return iter(self._list)
2716 elif self._ascending:
2714 elif self._ascending:
2717 return iter(self._asclist)
2715 return iter(self._asclist)
2718 else:
2716 else:
2719 return reversed(self._asclist)
2717 return reversed(self._asclist)
2720
2718
2721 def fastasc(self):
2719 def fastasc(self):
2722 return iter(self._asclist)
2720 return iter(self._asclist)
2723
2721
2724 def fastdesc(self):
2722 def fastdesc(self):
2725 return reversed(self._asclist)
2723 return reversed(self._asclist)
2726
2724
2727 @util.propertycache
2725 @util.propertycache
2728 def __contains__(self):
2726 def __contains__(self):
2729 return self._set.__contains__
2727 return self._set.__contains__
2730
2728
2731 def __nonzero__(self):
2729 def __nonzero__(self):
2732 return bool(self._list)
2730 return bool(self._list)
2733
2731
2734 def sort(self, reverse=False):
2732 def sort(self, reverse=False):
2735 self._ascending = not bool(reverse)
2733 self._ascending = not bool(reverse)
2736
2734
2737 def reverse(self):
2735 def reverse(self):
2738 if self._ascending is None:
2736 if self._ascending is None:
2739 self._list.reverse()
2737 self._list.reverse()
2740 else:
2738 else:
2741 self._ascending = not self._ascending
2739 self._ascending = not self._ascending
2742
2740
2743 def __len__(self):
2741 def __len__(self):
2744 return len(self._list)
2742 return len(self._list)
2745
2743
2746 def isascending(self):
2744 def isascending(self):
2747 """Returns True if the collection is ascending order, False if not.
2745 """Returns True if the collection is ascending order, False if not.
2748
2746
2749 This is part of the mandatory API for smartset."""
2747 This is part of the mandatory API for smartset."""
2750 if len(self) <= 1:
2748 if len(self) <= 1:
2751 return True
2749 return True
2752 return self._ascending is not None and self._ascending
2750 return self._ascending is not None and self._ascending
2753
2751
2754 def isdescending(self):
2752 def isdescending(self):
2755 """Returns True if the collection is descending order, False if not.
2753 """Returns True if the collection is descending order, False if not.
2756
2754
2757 This is part of the mandatory API for smartset."""
2755 This is part of the mandatory API for smartset."""
2758 if len(self) <= 1:
2756 if len(self) <= 1:
2759 return True
2757 return True
2760 return self._ascending is not None and not self._ascending
2758 return self._ascending is not None and not self._ascending
2761
2759
2762 def first(self):
2760 def first(self):
2763 if self:
2761 if self:
2764 if self._ascending is None:
2762 if self._ascending is None:
2765 return self._list[0]
2763 return self._list[0]
2766 elif self._ascending:
2764 elif self._ascending:
2767 return self._asclist[0]
2765 return self._asclist[0]
2768 else:
2766 else:
2769 return self._asclist[-1]
2767 return self._asclist[-1]
2770 return None
2768 return None
2771
2769
2772 def last(self):
2770 def last(self):
2773 if self:
2771 if self:
2774 if self._ascending is None:
2772 if self._ascending is None:
2775 return self._list[-1]
2773 return self._list[-1]
2776 elif self._ascending:
2774 elif self._ascending:
2777 return self._asclist[-1]
2775 return self._asclist[-1]
2778 else:
2776 else:
2779 return self._asclist[0]
2777 return self._asclist[0]
2780 return None
2778 return None
2781
2779
2782 class filteredset(abstractsmartset):
2780 class filteredset(abstractsmartset):
2783 """Duck type for baseset class which iterates lazily over the revisions in
2781 """Duck type for baseset class which iterates lazily over the revisions in
2784 the subset and contains a function which tests for membership in the
2782 the subset and contains a function which tests for membership in the
2785 revset
2783 revset
2786 """
2784 """
2787 def __init__(self, subset, condition=lambda x: True):
2785 def __init__(self, subset, condition=lambda x: True):
2788 """
2786 """
2789 condition: a function that decide whether a revision in the subset
2787 condition: a function that decide whether a revision in the subset
2790 belongs to the revset or not.
2788 belongs to the revset or not.
2791 """
2789 """
2792 self._subset = subset
2790 self._subset = subset
2793 self._condition = condition
2791 self._condition = condition
2794 self._cache = {}
2792 self._cache = {}
2795
2793
2796 def __contains__(self, x):
2794 def __contains__(self, x):
2797 c = self._cache
2795 c = self._cache
2798 if x not in c:
2796 if x not in c:
2799 v = c[x] = x in self._subset and self._condition(x)
2797 v = c[x] = x in self._subset and self._condition(x)
2800 return v
2798 return v
2801 return c[x]
2799 return c[x]
2802
2800
2803 def __iter__(self):
2801 def __iter__(self):
2804 return self._iterfilter(self._subset)
2802 return self._iterfilter(self._subset)
2805
2803
2806 def _iterfilter(self, it):
2804 def _iterfilter(self, it):
2807 cond = self._condition
2805 cond = self._condition
2808 for x in it:
2806 for x in it:
2809 if cond(x):
2807 if cond(x):
2810 yield x
2808 yield x
2811
2809
2812 @property
2810 @property
2813 def fastasc(self):
2811 def fastasc(self):
2814 it = self._subset.fastasc
2812 it = self._subset.fastasc
2815 if it is None:
2813 if it is None:
2816 return None
2814 return None
2817 return lambda: self._iterfilter(it())
2815 return lambda: self._iterfilter(it())
2818
2816
2819 @property
2817 @property
2820 def fastdesc(self):
2818 def fastdesc(self):
2821 it = self._subset.fastdesc
2819 it = self._subset.fastdesc
2822 if it is None:
2820 if it is None:
2823 return None
2821 return None
2824 return lambda: self._iterfilter(it())
2822 return lambda: self._iterfilter(it())
2825
2823
2826 def __nonzero__(self):
2824 def __nonzero__(self):
2827 for r in self:
2825 for r in self:
2828 return True
2826 return True
2829 return False
2827 return False
2830
2828
2831 def __len__(self):
2829 def __len__(self):
2832 # Basic implementation to be changed in future patches.
2830 # Basic implementation to be changed in future patches.
2833 l = baseset([r for r in self])
2831 l = baseset([r for r in self])
2834 return len(l)
2832 return len(l)
2835
2833
2836 def sort(self, reverse=False):
2834 def sort(self, reverse=False):
2837 self._subset.sort(reverse=reverse)
2835 self._subset.sort(reverse=reverse)
2838
2836
2839 def reverse(self):
2837 def reverse(self):
2840 self._subset.reverse()
2838 self._subset.reverse()
2841
2839
2842 def isascending(self):
2840 def isascending(self):
2843 return self._subset.isascending()
2841 return self._subset.isascending()
2844
2842
2845 def isdescending(self):
2843 def isdescending(self):
2846 return self._subset.isdescending()
2844 return self._subset.isdescending()
2847
2845
2848 def first(self):
2846 def first(self):
2849 for x in self:
2847 for x in self:
2850 return x
2848 return x
2851 return None
2849 return None
2852
2850
2853 def last(self):
2851 def last(self):
2854 it = None
2852 it = None
2855 if self._subset.isascending:
2853 if self._subset.isascending:
2856 it = self.fastdesc
2854 it = self.fastdesc
2857 elif self._subset.isdescending:
2855 elif self._subset.isdescending:
2858 it = self.fastdesc
2856 it = self.fastdesc
2859 if it is None:
2857 if it is None:
2860 # slowly consume everything. This needs improvement
2858 # slowly consume everything. This needs improvement
2861 it = lambda: reversed(list(self))
2859 it = lambda: reversed(list(self))
2862 for x in it():
2860 for x in it():
2863 return x
2861 return x
2864 return None
2862 return None
2865
2863
2866 class addset(abstractsmartset):
2864 class addset(abstractsmartset):
2867 """Represent the addition of two sets
2865 """Represent the addition of two sets
2868
2866
2869 Wrapper structure for lazily adding two structures without losing much
2867 Wrapper structure for lazily adding two structures without losing much
2870 performance on the __contains__ method
2868 performance on the __contains__ method
2871
2869
2872 If the ascending attribute is set, that means the two structures are
2870 If the ascending attribute is set, that means the two structures are
2873 ordered in either an ascending or descending way. Therefore, we can add
2871 ordered in either an ascending or descending way. Therefore, we can add
2874 them maintaining the order by iterating over both at the same time
2872 them maintaining the order by iterating over both at the same time
2875 """
2873 """
2876 def __init__(self, revs1, revs2, ascending=None):
2874 def __init__(self, revs1, revs2, ascending=None):
2877 self._r1 = revs1
2875 self._r1 = revs1
2878 self._r2 = revs2
2876 self._r2 = revs2
2879 self._iter = None
2877 self._iter = None
2880 self._ascending = ascending
2878 self._ascending = ascending
2881 self._genlist = None
2879 self._genlist = None
2882 self._asclist = None
2880 self._asclist = None
2883
2881
2884 def __len__(self):
2882 def __len__(self):
2885 return len(self._list)
2883 return len(self._list)
2886
2884
2887 def __nonzero__(self):
2885 def __nonzero__(self):
2888 return bool(self._r1) or bool(self._r2)
2886 return bool(self._r1) or bool(self._r2)
2889
2887
2890 @util.propertycache
2888 @util.propertycache
2891 def _list(self):
2889 def _list(self):
2892 if not self._genlist:
2890 if not self._genlist:
2893 self._genlist = baseset(self._iterator())
2891 self._genlist = baseset(self._iterator())
2894 return self._genlist
2892 return self._genlist
2895
2893
2896 def _iterator(self):
2894 def _iterator(self):
2897 """Iterate over both collections without repeating elements
2895 """Iterate over both collections without repeating elements
2898
2896
2899 If the ascending attribute is not set, iterate over the first one and
2897 If the ascending attribute is not set, iterate over the first one and
2900 then over the second one checking for membership on the first one so we
2898 then over the second one checking for membership on the first one so we
2901 dont yield any duplicates.
2899 dont yield any duplicates.
2902
2900
2903 If the ascending attribute is set, iterate over both collections at the
2901 If the ascending attribute is set, iterate over both collections at the
2904 same time, yielding only one value at a time in the given order.
2902 same time, yielding only one value at a time in the given order.
2905 """
2903 """
2906 if self._ascending is None:
2904 if self._ascending is None:
2907 def gen():
2905 def gen():
2908 for r in self._r1:
2906 for r in self._r1:
2909 yield r
2907 yield r
2910 inr1 = self._r1.__contains__
2908 inr1 = self._r1.__contains__
2911 for r in self._r2:
2909 for r in self._r2:
2912 if not inr1(r):
2910 if not inr1(r):
2913 yield r
2911 yield r
2914 gen = gen()
2912 gen = gen()
2915 else:
2913 else:
2916 iter1 = iter(self._r1)
2914 iter1 = iter(self._r1)
2917 iter2 = iter(self._r2)
2915 iter2 = iter(self._r2)
2918 gen = self._iterordered(self._ascending, iter1, iter2)
2916 gen = self._iterordered(self._ascending, iter1, iter2)
2919 return gen
2917 return gen
2920
2918
2921 def __iter__(self):
2919 def __iter__(self):
2922 if self._ascending is None:
2920 if self._ascending is None:
2923 if self._genlist:
2921 if self._genlist:
2924 return iter(self._genlist)
2922 return iter(self._genlist)
2925 return iter(self._iterator())
2923 return iter(self._iterator())
2926 self._trysetasclist()
2924 self._trysetasclist()
2927 if self._ascending:
2925 if self._ascending:
2928 it = self.fastasc
2926 it = self.fastasc
2929 else:
2927 else:
2930 it = self.fastdesc
2928 it = self.fastdesc
2931 if it is None:
2929 if it is None:
2932 # consume the gen and try again
2930 # consume the gen and try again
2933 self._list
2931 self._list
2934 return iter(self)
2932 return iter(self)
2935 return it()
2933 return it()
2936
2934
2937 def _trysetasclist(self):
2935 def _trysetasclist(self):
2938 """populate the _asclist attribute if possible and necessary"""
2936 """populate the _asclist attribute if possible and necessary"""
2939 if self._genlist is not None and self._asclist is None:
2937 if self._genlist is not None and self._asclist is None:
2940 self._asclist = sorted(self._genlist)
2938 self._asclist = sorted(self._genlist)
2941
2939
2942 @property
2940 @property
2943 def fastasc(self):
2941 def fastasc(self):
2944 self._trysetasclist()
2942 self._trysetasclist()
2945 if self._asclist is not None:
2943 if self._asclist is not None:
2946 return self._asclist.__iter__
2944 return self._asclist.__iter__
2947 iter1 = self._r1.fastasc
2945 iter1 = self._r1.fastasc
2948 iter2 = self._r2.fastasc
2946 iter2 = self._r2.fastasc
2949 if None in (iter1, iter2):
2947 if None in (iter1, iter2):
2950 return None
2948 return None
2951 return lambda: self._iterordered(True, iter1(), iter2())
2949 return lambda: self._iterordered(True, iter1(), iter2())
2952
2950
2953 @property
2951 @property
2954 def fastdesc(self):
2952 def fastdesc(self):
2955 self._trysetasclist()
2953 self._trysetasclist()
2956 if self._asclist is not None:
2954 if self._asclist is not None:
2957 return self._asclist.__reversed__
2955 return self._asclist.__reversed__
2958 iter1 = self._r1.fastdesc
2956 iter1 = self._r1.fastdesc
2959 iter2 = self._r2.fastdesc
2957 iter2 = self._r2.fastdesc
2960 if None in (iter1, iter2):
2958 if None in (iter1, iter2):
2961 return None
2959 return None
2962 return lambda: self._iterordered(False, iter1(), iter2())
2960 return lambda: self._iterordered(False, iter1(), iter2())
2963
2961
2964 def _iterordered(self, ascending, iter1, iter2):
2962 def _iterordered(self, ascending, iter1, iter2):
2965 """produce an ordered iteration from two iterators with the same order
2963 """produce an ordered iteration from two iterators with the same order
2966
2964
2967 The ascending is used to indicated the iteration direction.
2965 The ascending is used to indicated the iteration direction.
2968 """
2966 """
2969 choice = max
2967 choice = max
2970 if ascending:
2968 if ascending:
2971 choice = min
2969 choice = min
2972
2970
2973 val1 = None
2971 val1 = None
2974 val2 = None
2972 val2 = None
2975
2973
2976 choice = max
2974 choice = max
2977 if ascending:
2975 if ascending:
2978 choice = min
2976 choice = min
2979 try:
2977 try:
2980 # Consume both iterators in an ordered way until one is
2978 # Consume both iterators in an ordered way until one is
2981 # empty
2979 # empty
2982 while True:
2980 while True:
2983 if val1 is None:
2981 if val1 is None:
2984 val1 = iter1.next()
2982 val1 = iter1.next()
2985 if val2 is None:
2983 if val2 is None:
2986 val2 = iter2.next()
2984 val2 = iter2.next()
2987 next = choice(val1, val2)
2985 next = choice(val1, val2)
2988 yield next
2986 yield next
2989 if val1 == next:
2987 if val1 == next:
2990 val1 = None
2988 val1 = None
2991 if val2 == next:
2989 if val2 == next:
2992 val2 = None
2990 val2 = None
2993 except StopIteration:
2991 except StopIteration:
2994 # Flush any remaining values and consume the other one
2992 # Flush any remaining values and consume the other one
2995 it = iter2
2993 it = iter2
2996 if val1 is not None:
2994 if val1 is not None:
2997 yield val1
2995 yield val1
2998 it = iter1
2996 it = iter1
2999 elif val2 is not None:
2997 elif val2 is not None:
3000 # might have been equality and both are empty
2998 # might have been equality and both are empty
3001 yield val2
2999 yield val2
3002 for val in it:
3000 for val in it:
3003 yield val
3001 yield val
3004
3002
3005 def __contains__(self, x):
3003 def __contains__(self, x):
3006 return x in self._r1 or x in self._r2
3004 return x in self._r1 or x in self._r2
3007
3005
3008 def sort(self, reverse=False):
3006 def sort(self, reverse=False):
3009 """Sort the added set
3007 """Sort the added set
3010
3008
3011 For this we use the cached list with all the generated values and if we
3009 For this we use the cached list with all the generated values and if we
3012 know they are ascending or descending we can sort them in a smart way.
3010 know they are ascending or descending we can sort them in a smart way.
3013 """
3011 """
3014 self._ascending = not reverse
3012 self._ascending = not reverse
3015
3013
3016 def isascending(self):
3014 def isascending(self):
3017 return self._ascending is not None and self._ascending
3015 return self._ascending is not None and self._ascending
3018
3016
3019 def isdescending(self):
3017 def isdescending(self):
3020 return self._ascending is not None and not self._ascending
3018 return self._ascending is not None and not self._ascending
3021
3019
3022 def reverse(self):
3020 def reverse(self):
3023 if self._ascending is None:
3021 if self._ascending is None:
3024 self._list.reverse()
3022 self._list.reverse()
3025 else:
3023 else:
3026 self._ascending = not self._ascending
3024 self._ascending = not self._ascending
3027
3025
3028 def first(self):
3026 def first(self):
3029 for x in self:
3027 for x in self:
3030 return x
3028 return x
3031 return None
3029 return None
3032
3030
3033 def last(self):
3031 def last(self):
3034 self.reverse()
3032 self.reverse()
3035 val = self.first()
3033 val = self.first()
3036 self.reverse()
3034 self.reverse()
3037 return val
3035 return val
3038
3036
3039 class generatorset(abstractsmartset):
3037 class generatorset(abstractsmartset):
3040 """Wrap a generator for lazy iteration
3038 """Wrap a generator for lazy iteration
3041
3039
3042 Wrapper structure for generators that provides lazy membership and can
3040 Wrapper structure for generators that provides lazy membership and can
3043 be iterated more than once.
3041 be iterated more than once.
3044 When asked for membership it generates values until either it finds the
3042 When asked for membership it generates values until either it finds the
3045 requested one or has gone through all the elements in the generator
3043 requested one or has gone through all the elements in the generator
3046 """
3044 """
3047 def __init__(self, gen, iterasc=None):
3045 def __init__(self, gen, iterasc=None):
3048 """
3046 """
3049 gen: a generator producing the values for the generatorset.
3047 gen: a generator producing the values for the generatorset.
3050 """
3048 """
3051 self._gen = gen
3049 self._gen = gen
3052 self._asclist = None
3050 self._asclist = None
3053 self._cache = {}
3051 self._cache = {}
3054 self._genlist = []
3052 self._genlist = []
3055 self._finished = False
3053 self._finished = False
3056 self._ascending = True
3054 self._ascending = True
3057 if iterasc is not None:
3055 if iterasc is not None:
3058 if iterasc:
3056 if iterasc:
3059 self.fastasc = self._iterator
3057 self.fastasc = self._iterator
3060 self.__contains__ = self._asccontains
3058 self.__contains__ = self._asccontains
3061 else:
3059 else:
3062 self.fastdesc = self._iterator
3060 self.fastdesc = self._iterator
3063 self.__contains__ = self._desccontains
3061 self.__contains__ = self._desccontains
3064
3062
3065 def __nonzero__(self):
3063 def __nonzero__(self):
3066 for r in self:
3064 for r in self:
3067 return True
3065 return True
3068 return False
3066 return False
3069
3067
3070 def __contains__(self, x):
3068 def __contains__(self, x):
3071 if x in self._cache:
3069 if x in self._cache:
3072 return self._cache[x]
3070 return self._cache[x]
3073
3071
3074 # Use new values only, as existing values would be cached.
3072 # Use new values only, as existing values would be cached.
3075 for l in self._consumegen():
3073 for l in self._consumegen():
3076 if l == x:
3074 if l == x:
3077 return True
3075 return True
3078
3076
3079 self._cache[x] = False
3077 self._cache[x] = False
3080 return False
3078 return False
3081
3079
3082 def _asccontains(self, x):
3080 def _asccontains(self, x):
3083 """version of contains optimised for ascending generator"""
3081 """version of contains optimised for ascending generator"""
3084 if x in self._cache:
3082 if x in self._cache:
3085 return self._cache[x]
3083 return self._cache[x]
3086
3084
3087 # Use new values only, as existing values would be cached.
3085 # Use new values only, as existing values would be cached.
3088 for l in self._consumegen():
3086 for l in self._consumegen():
3089 if l == x:
3087 if l == x:
3090 return True
3088 return True
3091 if l > x:
3089 if l > x:
3092 break
3090 break
3093
3091
3094 self._cache[x] = False
3092 self._cache[x] = False
3095 return False
3093 return False
3096
3094
3097 def _desccontains(self, x):
3095 def _desccontains(self, x):
3098 """version of contains optimised for descending generator"""
3096 """version of contains optimised for descending generator"""
3099 if x in self._cache:
3097 if x in self._cache:
3100 return self._cache[x]
3098 return self._cache[x]
3101
3099
3102 # Use new values only, as existing values would be cached.
3100 # Use new values only, as existing values would be cached.
3103 for l in self._consumegen():
3101 for l in self._consumegen():
3104 if l == x:
3102 if l == x:
3105 return True
3103 return True
3106 if l < x:
3104 if l < x:
3107 break
3105 break
3108
3106
3109 self._cache[x] = False
3107 self._cache[x] = False
3110 return False
3108 return False
3111
3109
3112 def __iter__(self):
3110 def __iter__(self):
3113 if self._ascending:
3111 if self._ascending:
3114 it = self.fastasc
3112 it = self.fastasc
3115 else:
3113 else:
3116 it = self.fastdesc
3114 it = self.fastdesc
3117 if it is not None:
3115 if it is not None:
3118 return it()
3116 return it()
3119 # we need to consume the iterator
3117 # we need to consume the iterator
3120 for x in self._consumegen():
3118 for x in self._consumegen():
3121 pass
3119 pass
3122 # recall the same code
3120 # recall the same code
3123 return iter(self)
3121 return iter(self)
3124
3122
3125 def _iterator(self):
3123 def _iterator(self):
3126 if self._finished:
3124 if self._finished:
3127 return iter(self._genlist)
3125 return iter(self._genlist)
3128
3126
3129 # We have to use this complex iteration strategy to allow multiple
3127 # We have to use this complex iteration strategy to allow multiple
3130 # iterations at the same time. We need to be able to catch revision
3128 # iterations at the same time. We need to be able to catch revision
3131 # removed from _consumegen and added to genlist in another instance.
3129 # removed from _consumegen and added to genlist in another instance.
3132 #
3130 #
3133 # Getting rid of it would provide an about 15% speed up on this
3131 # Getting rid of it would provide an about 15% speed up on this
3134 # iteration.
3132 # iteration.
3135 genlist = self._genlist
3133 genlist = self._genlist
3136 nextrev = self._consumegen().next
3134 nextrev = self._consumegen().next
3137 _len = len # cache global lookup
3135 _len = len # cache global lookup
3138 def gen():
3136 def gen():
3139 i = 0
3137 i = 0
3140 while True:
3138 while True:
3141 if i < _len(genlist):
3139 if i < _len(genlist):
3142 yield genlist[i]
3140 yield genlist[i]
3143 else:
3141 else:
3144 yield nextrev()
3142 yield nextrev()
3145 i += 1
3143 i += 1
3146 return gen()
3144 return gen()
3147
3145
3148 def _consumegen(self):
3146 def _consumegen(self):
3149 cache = self._cache
3147 cache = self._cache
3150 genlist = self._genlist.append
3148 genlist = self._genlist.append
3151 for item in self._gen:
3149 for item in self._gen:
3152 cache[item] = True
3150 cache[item] = True
3153 genlist(item)
3151 genlist(item)
3154 yield item
3152 yield item
3155 if not self._finished:
3153 if not self._finished:
3156 self._finished = True
3154 self._finished = True
3157 asc = self._genlist[:]
3155 asc = self._genlist[:]
3158 asc.sort()
3156 asc.sort()
3159 self._asclist = asc
3157 self._asclist = asc
3160 self.fastasc = asc.__iter__
3158 self.fastasc = asc.__iter__
3161 self.fastdesc = asc.__reversed__
3159 self.fastdesc = asc.__reversed__
3162
3160
3163 def __len__(self):
3161 def __len__(self):
3164 for x in self._consumegen():
3162 for x in self._consumegen():
3165 pass
3163 pass
3166 return len(self._genlist)
3164 return len(self._genlist)
3167
3165
3168 def sort(self, reverse=False):
3166 def sort(self, reverse=False):
3169 self._ascending = not reverse
3167 self._ascending = not reverse
3170
3168
3171 def reverse(self):
3169 def reverse(self):
3172 self._ascending = not self._ascending
3170 self._ascending = not self._ascending
3173
3171
3174 def isascending(self):
3172 def isascending(self):
3175 return self._ascending
3173 return self._ascending
3176
3174
3177 def isdescending(self):
3175 def isdescending(self):
3178 return not self._ascending
3176 return not self._ascending
3179
3177
3180 def first(self):
3178 def first(self):
3181 if self._ascending:
3179 if self._ascending:
3182 it = self.fastasc
3180 it = self.fastasc
3183 else:
3181 else:
3184 it = self.fastdesc
3182 it = self.fastdesc
3185 if it is None:
3183 if it is None:
3186 # we need to consume all and try again
3184 # we need to consume all and try again
3187 for x in self._consumegen():
3185 for x in self._consumegen():
3188 pass
3186 pass
3189 return self.first()
3187 return self.first()
3190 if self:
3188 if self:
3191 return it().next()
3189 return it().next()
3192 return None
3190 return None
3193
3191
3194 def last(self):
3192 def last(self):
3195 if self._ascending:
3193 if self._ascending:
3196 it = self.fastdesc
3194 it = self.fastdesc
3197 else:
3195 else:
3198 it = self.fastasc
3196 it = self.fastasc
3199 if it is None:
3197 if it is None:
3200 # we need to consume all and try again
3198 # we need to consume all and try again
3201 for x in self._consumegen():
3199 for x in self._consumegen():
3202 pass
3200 pass
3203 return self.first()
3201 return self.first()
3204 if self:
3202 if self:
3205 return it().next()
3203 return it().next()
3206 return None
3204 return None
3207
3205
3208 class spanset(abstractsmartset):
3206 class spanset(abstractsmartset):
3209 """Duck type for baseset class which represents a range of revisions and
3207 """Duck type for baseset class which represents a range of revisions and
3210 can work lazily and without having all the range in memory
3208 can work lazily and without having all the range in memory
3211
3209
3212 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3210 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3213 notable points:
3211 notable points:
3214 - when x < y it will be automatically descending,
3212 - when x < y it will be automatically descending,
3215 - revision filtered with this repoview will be skipped.
3213 - revision filtered with this repoview will be skipped.
3216
3214
3217 """
3215 """
3218 def __init__(self, repo, start=0, end=None):
3216 def __init__(self, repo, start=0, end=None):
3219 """
3217 """
3220 start: first revision included the set
3218 start: first revision included the set
3221 (default to 0)
3219 (default to 0)
3222 end: first revision excluded (last+1)
3220 end: first revision excluded (last+1)
3223 (default to len(repo)
3221 (default to len(repo)
3224
3222
3225 Spanset will be descending if `end` < `start`.
3223 Spanset will be descending if `end` < `start`.
3226 """
3224 """
3227 if end is None:
3225 if end is None:
3228 end = len(repo)
3226 end = len(repo)
3229 self._ascending = start <= end
3227 self._ascending = start <= end
3230 if not self._ascending:
3228 if not self._ascending:
3231 start, end = end + 1, start +1
3229 start, end = end + 1, start +1
3232 self._start = start
3230 self._start = start
3233 self._end = end
3231 self._end = end
3234 self._hiddenrevs = repo.changelog.filteredrevs
3232 self._hiddenrevs = repo.changelog.filteredrevs
3235
3233
3236 def sort(self, reverse=False):
3234 def sort(self, reverse=False):
3237 self._ascending = not reverse
3235 self._ascending = not reverse
3238
3236
3239 def reverse(self):
3237 def reverse(self):
3240 self._ascending = not self._ascending
3238 self._ascending = not self._ascending
3241
3239
3242 def _iterfilter(self, iterrange):
3240 def _iterfilter(self, iterrange):
3243 s = self._hiddenrevs
3241 s = self._hiddenrevs
3244 for r in iterrange:
3242 for r in iterrange:
3245 if r not in s:
3243 if r not in s:
3246 yield r
3244 yield r
3247
3245
3248 def __iter__(self):
3246 def __iter__(self):
3249 if self._ascending:
3247 if self._ascending:
3250 return self.fastasc()
3248 return self.fastasc()
3251 else:
3249 else:
3252 return self.fastdesc()
3250 return self.fastdesc()
3253
3251
3254 def fastasc(self):
3252 def fastasc(self):
3255 iterrange = xrange(self._start, self._end)
3253 iterrange = xrange(self._start, self._end)
3256 if self._hiddenrevs:
3254 if self._hiddenrevs:
3257 return self._iterfilter(iterrange)
3255 return self._iterfilter(iterrange)
3258 return iter(iterrange)
3256 return iter(iterrange)
3259
3257
3260 def fastdesc(self):
3258 def fastdesc(self):
3261 iterrange = xrange(self._end - 1, self._start - 1, -1)
3259 iterrange = xrange(self._end - 1, self._start - 1, -1)
3262 if self._hiddenrevs:
3260 if self._hiddenrevs:
3263 return self._iterfilter(iterrange)
3261 return self._iterfilter(iterrange)
3264 return iter(iterrange)
3262 return iter(iterrange)
3265
3263
3266 def __contains__(self, rev):
3264 def __contains__(self, rev):
3267 hidden = self._hiddenrevs
3265 hidden = self._hiddenrevs
3268 return ((self._start <= rev < self._end)
3266 return ((self._start <= rev < self._end)
3269 and not (hidden and rev in hidden))
3267 and not (hidden and rev in hidden))
3270
3268
3271 def __nonzero__(self):
3269 def __nonzero__(self):
3272 for r in self:
3270 for r in self:
3273 return True
3271 return True
3274 return False
3272 return False
3275
3273
3276 def __len__(self):
3274 def __len__(self):
3277 if not self._hiddenrevs:
3275 if not self._hiddenrevs:
3278 return abs(self._end - self._start)
3276 return abs(self._end - self._start)
3279 else:
3277 else:
3280 count = 0
3278 count = 0
3281 start = self._start
3279 start = self._start
3282 end = self._end
3280 end = self._end
3283 for rev in self._hiddenrevs:
3281 for rev in self._hiddenrevs:
3284 if (end < rev <= start) or (start <= rev < end):
3282 if (end < rev <= start) or (start <= rev < end):
3285 count += 1
3283 count += 1
3286 return abs(self._end - self._start) - count
3284 return abs(self._end - self._start) - count
3287
3285
3288 def isascending(self):
3286 def isascending(self):
3289 return self._ascending
3287 return self._ascending
3290
3288
3291 def isdescending(self):
3289 def isdescending(self):
3292 return not self._ascending
3290 return not self._ascending
3293
3291
3294 def first(self):
3292 def first(self):
3295 if self._ascending:
3293 if self._ascending:
3296 it = self.fastasc
3294 it = self.fastasc
3297 else:
3295 else:
3298 it = self.fastdesc
3296 it = self.fastdesc
3299 for x in it():
3297 for x in it():
3300 return x
3298 return x
3301 return None
3299 return None
3302
3300
3303 def last(self):
3301 def last(self):
3304 if self._ascending:
3302 if self._ascending:
3305 it = self.fastdesc
3303 it = self.fastdesc
3306 else:
3304 else:
3307 it = self.fastasc
3305 it = self.fastasc
3308 for x in it():
3306 for x in it():
3309 return x
3307 return x
3310 return None
3308 return None
3311
3309
3312 class fullreposet(spanset):
3310 class fullreposet(spanset):
3313 """a set containing all revisions in the repo
3311 """a set containing all revisions in the repo
3314
3312
3315 This class exists to host special optimization.
3313 This class exists to host special optimization and magic to handle virtual
3314 revisions such as "null".
3316 """
3315 """
3317
3316
3318 def __init__(self, repo):
3317 def __init__(self, repo):
3319 super(fullreposet, self).__init__(repo)
3318 super(fullreposet, self).__init__(repo)
3320
3319
3321 def __contains__(self, rev):
3320 def __contains__(self, rev):
3321 # assumes the given rev is valid
3322 hidden = self._hiddenrevs
3322 hidden = self._hiddenrevs
3323 return ((self._start <= rev < self._end)
3323 return not (hidden and rev in hidden)
3324 and not (hidden and rev in hidden))
3325
3324
3326 def __and__(self, other):
3325 def __and__(self, other):
3327 """As self contains the whole repo, all of the other set should also be
3326 """As self contains the whole repo, all of the other set should also be
3328 in self. Therefore `self & other = other`.
3327 in self. Therefore `self & other = other`.
3329
3328
3330 This boldly assumes the other contains valid revs only.
3329 This boldly assumes the other contains valid revs only.
3331 """
3330 """
3332 # other not a smartset, make is so
3331 # other not a smartset, make is so
3333 if not util.safehasattr(other, 'isascending'):
3332 if not util.safehasattr(other, 'isascending'):
3334 # filter out hidden revision
3333 # filter out hidden revision
3335 # (this boldly assumes all smartset are pure)
3334 # (this boldly assumes all smartset are pure)
3336 #
3335 #
3337 # `other` was used with "&", let's assume this is a set like
3336 # `other` was used with "&", let's assume this is a set like
3338 # object.
3337 # object.
3339 other = baseset(other - self._hiddenrevs)
3338 other = baseset(other - self._hiddenrevs)
3340
3339
3341 other.sort(reverse=self.isdescending())
3340 other.sort(reverse=self.isdescending())
3342 return other
3341 return other
3343
3342
3344 # tell hggettext to extract docstrings from these functions:
3343 # tell hggettext to extract docstrings from these functions:
3345 i18nfunctions = symbols.values()
3344 i18nfunctions = symbols.values()
@@ -1,836 +1,838 b''
1 $ cat >> $HGRCPATH << EOF
1 $ cat >> $HGRCPATH << EOF
2 > [phases]
2 > [phases]
3 > # public changeset are not obsolete
3 > # public changeset are not obsolete
4 > publish=false
4 > publish=false
5 > [ui]
5 > [ui]
6 > logtemplate="{rev}:{node|short} ({phase}) [{tags} {bookmarks}] {desc|firstline}\n"
6 > logtemplate="{rev}:{node|short} ({phase}) [{tags} {bookmarks}] {desc|firstline}\n"
7 > EOF
7 > EOF
8 $ mkcommit() {
8 $ mkcommit() {
9 > echo "$1" > "$1"
9 > echo "$1" > "$1"
10 > hg add "$1"
10 > hg add "$1"
11 > hg ci -m "add $1"
11 > hg ci -m "add $1"
12 > }
12 > }
13 $ getid() {
13 $ getid() {
14 > hg log -T "{node}\n" --hidden -r "desc('$1')"
14 > hg log -T "{node}\n" --hidden -r "desc('$1')"
15 > }
15 > }
16
16
17 $ cat > debugkeys.py <<EOF
17 $ cat > debugkeys.py <<EOF
18 > def reposetup(ui, repo):
18 > def reposetup(ui, repo):
19 > class debugkeysrepo(repo.__class__):
19 > class debugkeysrepo(repo.__class__):
20 > def listkeys(self, namespace):
20 > def listkeys(self, namespace):
21 > ui.write('listkeys %s\n' % (namespace,))
21 > ui.write('listkeys %s\n' % (namespace,))
22 > return super(debugkeysrepo, self).listkeys(namespace)
22 > return super(debugkeysrepo, self).listkeys(namespace)
23 >
23 >
24 > if repo.local():
24 > if repo.local():
25 > repo.__class__ = debugkeysrepo
25 > repo.__class__ = debugkeysrepo
26 > EOF
26 > EOF
27
27
28 $ hg init tmpa
28 $ hg init tmpa
29 $ cd tmpa
29 $ cd tmpa
30 $ mkcommit kill_me
30 $ mkcommit kill_me
31
31
32 Checking that the feature is properly disabled
32 Checking that the feature is properly disabled
33
33
34 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
34 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
35 abort: creating obsolete markers is not enabled on this repo
35 abort: creating obsolete markers is not enabled on this repo
36 [255]
36 [255]
37
37
38 Enabling it
38 Enabling it
39
39
40 $ cat >> $HGRCPATH << EOF
40 $ cat >> $HGRCPATH << EOF
41 > [experimental]
41 > [experimental]
42 > evolution=createmarkers,exchange
42 > evolution=createmarkers,exchange
43 > EOF
43 > EOF
44
44
45 Killing a single changeset without replacement
45 Killing a single changeset without replacement
46
46
47 $ hg debugobsolete 0
47 $ hg debugobsolete 0
48 abort: changeset references must be full hexadecimal node identifiers
48 abort: changeset references must be full hexadecimal node identifiers
49 [255]
49 [255]
50 $ hg debugobsolete '00'
50 $ hg debugobsolete '00'
51 abort: changeset references must be full hexadecimal node identifiers
51 abort: changeset references must be full hexadecimal node identifiers
52 [255]
52 [255]
53 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
53 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
54 $ hg debugobsolete
54 $ hg debugobsolete
55 97b7c2d76b1845ed3eb988cd612611e72406cef0 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'babar'}
55 97b7c2d76b1845ed3eb988cd612611e72406cef0 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'babar'}
56
56
57 (test that mercurial is not confused)
57 (test that mercurial is not confused)
58
58
59 $ hg up null --quiet # having 0 as parent prevents it to be hidden
59 $ hg up null --quiet # having 0 as parent prevents it to be hidden
60 $ hg tip
60 $ hg tip
61 -1:000000000000 (public) [tip ]
61 -1:000000000000 (public) [tip ]
62 $ hg up --hidden tip --quiet
62 $ hg up --hidden tip --quiet
63
63
64 Killing a single changeset with itself should fail
64 Killing a single changeset with itself should fail
65 (simple local safeguard)
65 (simple local safeguard)
66
66
67 $ hg debugobsolete `getid kill_me` `getid kill_me`
67 $ hg debugobsolete `getid kill_me` `getid kill_me`
68 abort: bad obsmarker input: in-marker cycle with 97b7c2d76b1845ed3eb988cd612611e72406cef0
68 abort: bad obsmarker input: in-marker cycle with 97b7c2d76b1845ed3eb988cd612611e72406cef0
69 [255]
69 [255]
70
70
71 $ cd ..
71 $ cd ..
72
72
73 Killing a single changeset with replacement
73 Killing a single changeset with replacement
74 (and testing the format option)
74 (and testing the format option)
75
75
76 $ hg init tmpb
76 $ hg init tmpb
77 $ cd tmpb
77 $ cd tmpb
78 $ mkcommit a
78 $ mkcommit a
79 $ mkcommit b
79 $ mkcommit b
80 $ mkcommit original_c
80 $ mkcommit original_c
81 $ hg up "desc('b')"
81 $ hg up "desc('b')"
82 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
82 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
83 $ mkcommit new_c
83 $ mkcommit new_c
84 created new head
84 created new head
85 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
85 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
86 $ hg debugobsolete --config format.obsstore-version=0 --flag 12 `getid original_c` `getid new_c` -d '121 120'
86 $ hg debugobsolete --config format.obsstore-version=0 --flag 12 `getid original_c` `getid new_c` -d '121 120'
87 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
87 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
88 2:245bde4270cd add original_c
88 2:245bde4270cd add original_c
89 $ hg debugrevlog -cd
89 $ hg debugrevlog -cd
90 # rev p1rev p2rev start end deltastart base p1 p2 rawsize totalsize compression heads chainlen
90 # rev p1rev p2rev start end deltastart base p1 p2 rawsize totalsize compression heads chainlen
91 0 -1 -1 0 59 0 0 0 0 58 58 0 1 0
91 0 -1 -1 0 59 0 0 0 0 58 58 0 1 0
92 1 0 -1 59 118 59 59 0 0 58 116 0 1 0
92 1 0 -1 59 118 59 59 0 0 58 116 0 1 0
93 2 1 -1 118 193 118 118 59 0 76 192 0 1 0
93 2 1 -1 118 193 118 118 59 0 76 192 0 1 0
94 3 1 -1 193 260 193 193 59 0 66 258 0 2 0
94 3 1 -1 193 260 193 193 59 0 66 258 0 2 0
95 $ hg debugobsolete
95 $ hg debugobsolete
96 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
96 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
97
97
98 (check for version number of the obsstore)
98 (check for version number of the obsstore)
99
99
100 $ dd bs=1 count=1 if=.hg/store/obsstore 2>/dev/null
100 $ dd bs=1 count=1 if=.hg/store/obsstore 2>/dev/null
101 \x00 (no-eol) (esc)
101 \x00 (no-eol) (esc)
102
102
103 do it again (it read the obsstore before adding new changeset)
103 do it again (it read the obsstore before adding new changeset)
104
104
105 $ hg up '.^'
105 $ hg up '.^'
106 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
106 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
107 $ mkcommit new_2_c
107 $ mkcommit new_2_c
108 created new head
108 created new head
109 $ hg debugobsolete -d '1337 0' `getid new_c` `getid new_2_c`
109 $ hg debugobsolete -d '1337 0' `getid new_c` `getid new_2_c`
110 $ hg debugobsolete
110 $ hg debugobsolete
111 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
111 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
112 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
112 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
113
113
114 Register two markers with a missing node
114 Register two markers with a missing node
115
115
116 $ hg up '.^'
116 $ hg up '.^'
117 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
117 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
118 $ mkcommit new_3_c
118 $ mkcommit new_3_c
119 created new head
119 created new head
120 $ hg debugobsolete -d '1338 0' `getid new_2_c` 1337133713371337133713371337133713371337
120 $ hg debugobsolete -d '1338 0' `getid new_2_c` 1337133713371337133713371337133713371337
121 $ hg debugobsolete -d '1339 0' 1337133713371337133713371337133713371337 `getid new_3_c`
121 $ hg debugobsolete -d '1339 0' 1337133713371337133713371337133713371337 `getid new_3_c`
122 $ hg debugobsolete
122 $ hg debugobsolete
123 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
123 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
124 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
124 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
125 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
125 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
126 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
126 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
127
127
128 Refuse pathological nullid successors
128 Refuse pathological nullid successors
129 $ hg debugobsolete -d '9001 0' 1337133713371337133713371337133713371337 0000000000000000000000000000000000000000
129 $ hg debugobsolete -d '9001 0' 1337133713371337133713371337133713371337 0000000000000000000000000000000000000000
130 transaction abort!
130 transaction abort!
131 rollback completed
131 rollback completed
132 abort: bad obsolescence marker detected: invalid successors nullid
132 abort: bad obsolescence marker detected: invalid successors nullid
133 [255]
133 [255]
134
134
135 Check that graphlog detect that a changeset is obsolete:
135 Check that graphlog detect that a changeset is obsolete:
136
136
137 $ hg log -G
137 $ hg log -G
138 @ 5:5601fb93a350 (draft) [tip ] add new_3_c
138 @ 5:5601fb93a350 (draft) [tip ] add new_3_c
139 |
139 |
140 o 1:7c3bad9141dc (draft) [ ] add b
140 o 1:7c3bad9141dc (draft) [ ] add b
141 |
141 |
142 o 0:1f0dee641bb7 (draft) [ ] add a
142 o 0:1f0dee641bb7 (draft) [ ] add a
143
143
144
144
145 check that heads does not report them
145 check that heads does not report them
146
146
147 $ hg heads
147 $ hg heads
148 5:5601fb93a350 (draft) [tip ] add new_3_c
148 5:5601fb93a350 (draft) [tip ] add new_3_c
149 $ hg heads --hidden
149 $ hg heads --hidden
150 5:5601fb93a350 (draft) [tip ] add new_3_c
150 5:5601fb93a350 (draft) [tip ] add new_3_c
151 4:ca819180edb9 (draft) [ ] add new_2_c
151 4:ca819180edb9 (draft) [ ] add new_2_c
152 3:cdbce2fbb163 (draft) [ ] add new_c
152 3:cdbce2fbb163 (draft) [ ] add new_c
153 2:245bde4270cd (draft) [ ] add original_c
153 2:245bde4270cd (draft) [ ] add original_c
154
154
155
155
156 check that summary does not report them
156 check that summary does not report them
157
157
158 $ hg init ../sink
158 $ hg init ../sink
159 $ echo '[paths]' >> .hg/hgrc
159 $ echo '[paths]' >> .hg/hgrc
160 $ echo 'default=../sink' >> .hg/hgrc
160 $ echo 'default=../sink' >> .hg/hgrc
161 $ hg summary --remote
161 $ hg summary --remote
162 parent: 5:5601fb93a350 tip
162 parent: 5:5601fb93a350 tip
163 add new_3_c
163 add new_3_c
164 branch: default
164 branch: default
165 commit: (clean)
165 commit: (clean)
166 update: (current)
166 update: (current)
167 remote: 3 outgoing
167 remote: 3 outgoing
168
168
169 $ hg summary --remote --hidden
169 $ hg summary --remote --hidden
170 parent: 5:5601fb93a350 tip
170 parent: 5:5601fb93a350 tip
171 add new_3_c
171 add new_3_c
172 branch: default
172 branch: default
173 commit: (clean)
173 commit: (clean)
174 update: 3 new changesets, 4 branch heads (merge)
174 update: 3 new changesets, 4 branch heads (merge)
175 remote: 3 outgoing
175 remote: 3 outgoing
176
176
177 check that various commands work well with filtering
177 check that various commands work well with filtering
178
178
179 $ hg tip
179 $ hg tip
180 5:5601fb93a350 (draft) [tip ] add new_3_c
180 5:5601fb93a350 (draft) [tip ] add new_3_c
181 $ hg log -r 6
181 $ hg log -r 6
182 abort: unknown revision '6'!
182 abort: unknown revision '6'!
183 [255]
183 [255]
184 $ hg log -r 4
184 $ hg log -r 4
185 abort: hidden revision '4'!
185 abort: hidden revision '4'!
186 (use --hidden to access hidden revisions)
186 (use --hidden to access hidden revisions)
187 [255]
187 [255]
188 $ hg debugrevspec 'rev(6)'
188 $ hg debugrevspec 'rev(6)'
189 $ hg debugrevspec 'rev(4)'
189 $ hg debugrevspec 'rev(4)'
190 $ hg debugrevspec 'null'
191 -1
190
192
191 Check that public changeset are not accounted as obsolete:
193 Check that public changeset are not accounted as obsolete:
192
194
193 $ hg --hidden phase --public 2
195 $ hg --hidden phase --public 2
194 $ hg log -G
196 $ hg log -G
195 @ 5:5601fb93a350 (draft) [tip ] add new_3_c
197 @ 5:5601fb93a350 (draft) [tip ] add new_3_c
196 |
198 |
197 | o 2:245bde4270cd (public) [ ] add original_c
199 | o 2:245bde4270cd (public) [ ] add original_c
198 |/
200 |/
199 o 1:7c3bad9141dc (public) [ ] add b
201 o 1:7c3bad9141dc (public) [ ] add b
200 |
202 |
201 o 0:1f0dee641bb7 (public) [ ] add a
203 o 0:1f0dee641bb7 (public) [ ] add a
202
204
203
205
204 And that bumped changeset are detected
206 And that bumped changeset are detected
205 --------------------------------------
207 --------------------------------------
206
208
207 If we didn't filtered obsolete changesets out, 3 and 4 would show up too. Also
209 If we didn't filtered obsolete changesets out, 3 and 4 would show up too. Also
208 note that the bumped changeset (5:5601fb93a350) is not a direct successor of
210 note that the bumped changeset (5:5601fb93a350) is not a direct successor of
209 the public changeset
211 the public changeset
210
212
211 $ hg log --hidden -r 'bumped()'
213 $ hg log --hidden -r 'bumped()'
212 5:5601fb93a350 (draft) [tip ] add new_3_c
214 5:5601fb93a350 (draft) [tip ] add new_3_c
213
215
214 And that we can't push bumped changeset
216 And that we can't push bumped changeset
215
217
216 $ hg push ../tmpa -r 0 --force #(make repo related)
218 $ hg push ../tmpa -r 0 --force #(make repo related)
217 pushing to ../tmpa
219 pushing to ../tmpa
218 searching for changes
220 searching for changes
219 warning: repository is unrelated
221 warning: repository is unrelated
220 adding changesets
222 adding changesets
221 adding manifests
223 adding manifests
222 adding file changes
224 adding file changes
223 added 1 changesets with 1 changes to 1 files (+1 heads)
225 added 1 changesets with 1 changes to 1 files (+1 heads)
224 $ hg push ../tmpa
226 $ hg push ../tmpa
225 pushing to ../tmpa
227 pushing to ../tmpa
226 searching for changes
228 searching for changes
227 abort: push includes bumped changeset: 5601fb93a350!
229 abort: push includes bumped changeset: 5601fb93a350!
228 [255]
230 [255]
229
231
230 Fixing "bumped" situation
232 Fixing "bumped" situation
231 We need to create a clone of 5 and add a special marker with a flag
233 We need to create a clone of 5 and add a special marker with a flag
232
234
233 $ hg up '5^'
235 $ hg up '5^'
234 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
236 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
235 $ hg revert -ar 5
237 $ hg revert -ar 5
236 adding new_3_c
238 adding new_3_c
237 $ hg ci -m 'add n3w_3_c'
239 $ hg ci -m 'add n3w_3_c'
238 created new head
240 created new head
239 $ hg debugobsolete -d '1338 0' --flags 1 `getid new_3_c` `getid n3w_3_c`
241 $ hg debugobsolete -d '1338 0' --flags 1 `getid new_3_c` `getid n3w_3_c`
240 $ hg log -r 'bumped()'
242 $ hg log -r 'bumped()'
241 $ hg log -G
243 $ hg log -G
242 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
244 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
243 |
245 |
244 | o 2:245bde4270cd (public) [ ] add original_c
246 | o 2:245bde4270cd (public) [ ] add original_c
245 |/
247 |/
246 o 1:7c3bad9141dc (public) [ ] add b
248 o 1:7c3bad9141dc (public) [ ] add b
247 |
249 |
248 o 0:1f0dee641bb7 (public) [ ] add a
250 o 0:1f0dee641bb7 (public) [ ] add a
249
251
250
252
251
253
252
254
253 $ cd ..
255 $ cd ..
254
256
255 Exchange Test
257 Exchange Test
256 ============================
258 ============================
257
259
258 Destination repo does not have any data
260 Destination repo does not have any data
259 ---------------------------------------
261 ---------------------------------------
260
262
261 Simple incoming test
263 Simple incoming test
262
264
263 $ hg init tmpc
265 $ hg init tmpc
264 $ cd tmpc
266 $ cd tmpc
265 $ hg incoming ../tmpb
267 $ hg incoming ../tmpb
266 comparing with ../tmpb
268 comparing with ../tmpb
267 0:1f0dee641bb7 (public) [ ] add a
269 0:1f0dee641bb7 (public) [ ] add a
268 1:7c3bad9141dc (public) [ ] add b
270 1:7c3bad9141dc (public) [ ] add b
269 2:245bde4270cd (public) [ ] add original_c
271 2:245bde4270cd (public) [ ] add original_c
270 6:6f9641995072 (draft) [tip ] add n3w_3_c
272 6:6f9641995072 (draft) [tip ] add n3w_3_c
271
273
272 Try to pull markers
274 Try to pull markers
273 (extinct changeset are excluded but marker are pushed)
275 (extinct changeset are excluded but marker are pushed)
274
276
275 $ hg pull ../tmpb
277 $ hg pull ../tmpb
276 pulling from ../tmpb
278 pulling from ../tmpb
277 requesting all changes
279 requesting all changes
278 adding changesets
280 adding changesets
279 adding manifests
281 adding manifests
280 adding file changes
282 adding file changes
281 added 4 changesets with 4 changes to 4 files (+1 heads)
283 added 4 changesets with 4 changes to 4 files (+1 heads)
282 (run 'hg heads' to see heads, 'hg merge' to merge)
284 (run 'hg heads' to see heads, 'hg merge' to merge)
283 $ hg debugobsolete
285 $ hg debugobsolete
284 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
286 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
285 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
287 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
286 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
288 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
287 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
289 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
288 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
290 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
289
291
290 Rollback//Transaction support
292 Rollback//Transaction support
291
293
292 $ hg debugobsolete -d '1340 0' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
294 $ hg debugobsolete -d '1340 0' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
293 $ hg debugobsolete
295 $ hg debugobsolete
294 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
296 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
295 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
297 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
296 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
298 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
297 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
299 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
298 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
300 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
299 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 (Thu Jan 01 00:22:20 1970 +0000) {'user': 'test'}
301 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 (Thu Jan 01 00:22:20 1970 +0000) {'user': 'test'}
300 $ hg rollback -n
302 $ hg rollback -n
301 repository tip rolled back to revision 3 (undo debugobsolete)
303 repository tip rolled back to revision 3 (undo debugobsolete)
302 $ hg rollback
304 $ hg rollback
303 repository tip rolled back to revision 3 (undo debugobsolete)
305 repository tip rolled back to revision 3 (undo debugobsolete)
304 $ hg debugobsolete
306 $ hg debugobsolete
305 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
307 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
306 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
308 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
307 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
309 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
308 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
310 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
309 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
311 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
310
312
311 $ cd ..
313 $ cd ..
312
314
313 Try to push markers
315 Try to push markers
314
316
315 $ hg init tmpd
317 $ hg init tmpd
316 $ hg -R tmpb push tmpd
318 $ hg -R tmpb push tmpd
317 pushing to tmpd
319 pushing to tmpd
318 searching for changes
320 searching for changes
319 adding changesets
321 adding changesets
320 adding manifests
322 adding manifests
321 adding file changes
323 adding file changes
322 added 4 changesets with 4 changes to 4 files (+1 heads)
324 added 4 changesets with 4 changes to 4 files (+1 heads)
323 $ hg -R tmpd debugobsolete | sort
325 $ hg -R tmpd debugobsolete | sort
324 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
326 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
325 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
327 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
326 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
328 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
327 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
329 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
328 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
330 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
329
331
330 Check obsolete keys are exchanged only if source has an obsolete store
332 Check obsolete keys are exchanged only if source has an obsolete store
331
333
332 $ hg init empty
334 $ hg init empty
333 $ hg --config extensions.debugkeys=debugkeys.py -R empty push tmpd
335 $ hg --config extensions.debugkeys=debugkeys.py -R empty push tmpd
334 pushing to tmpd
336 pushing to tmpd
335 listkeys phases
337 listkeys phases
336 listkeys bookmarks
338 listkeys bookmarks
337 no changes found
339 no changes found
338 listkeys phases
340 listkeys phases
339 [1]
341 [1]
340
342
341 clone support
343 clone support
342 (markers are copied and extinct changesets are included to allow hardlinks)
344 (markers are copied and extinct changesets are included to allow hardlinks)
343
345
344 $ hg clone tmpb clone-dest
346 $ hg clone tmpb clone-dest
345 updating to branch default
347 updating to branch default
346 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
347 $ hg -R clone-dest log -G --hidden
349 $ hg -R clone-dest log -G --hidden
348 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
350 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
349 |
351 |
350 | x 5:5601fb93a350 (draft) [ ] add new_3_c
352 | x 5:5601fb93a350 (draft) [ ] add new_3_c
351 |/
353 |/
352 | x 4:ca819180edb9 (draft) [ ] add new_2_c
354 | x 4:ca819180edb9 (draft) [ ] add new_2_c
353 |/
355 |/
354 | x 3:cdbce2fbb163 (draft) [ ] add new_c
356 | x 3:cdbce2fbb163 (draft) [ ] add new_c
355 |/
357 |/
356 | o 2:245bde4270cd (public) [ ] add original_c
358 | o 2:245bde4270cd (public) [ ] add original_c
357 |/
359 |/
358 o 1:7c3bad9141dc (public) [ ] add b
360 o 1:7c3bad9141dc (public) [ ] add b
359 |
361 |
360 o 0:1f0dee641bb7 (public) [ ] add a
362 o 0:1f0dee641bb7 (public) [ ] add a
361
363
362 $ hg -R clone-dest debugobsolete
364 $ hg -R clone-dest debugobsolete
363 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
365 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
364 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
366 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
365 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
367 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
366 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
368 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
367 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
369 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
368
370
369
371
370 Destination repo have existing data
372 Destination repo have existing data
371 ---------------------------------------
373 ---------------------------------------
372
374
373 On pull
375 On pull
374
376
375 $ hg init tmpe
377 $ hg init tmpe
376 $ cd tmpe
378 $ cd tmpe
377 $ hg debugobsolete -d '1339 0' 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00
379 $ hg debugobsolete -d '1339 0' 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00
378 $ hg pull ../tmpb
380 $ hg pull ../tmpb
379 pulling from ../tmpb
381 pulling from ../tmpb
380 requesting all changes
382 requesting all changes
381 adding changesets
383 adding changesets
382 adding manifests
384 adding manifests
383 adding file changes
385 adding file changes
384 added 4 changesets with 4 changes to 4 files (+1 heads)
386 added 4 changesets with 4 changes to 4 files (+1 heads)
385 (run 'hg heads' to see heads, 'hg merge' to merge)
387 (run 'hg heads' to see heads, 'hg merge' to merge)
386 $ hg debugobsolete
388 $ hg debugobsolete
387 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
389 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
388 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
390 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
389 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
391 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
390 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
392 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
391 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
393 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
392 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
394 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
393
395
394
396
395 On push
397 On push
396
398
397 $ hg push ../tmpc
399 $ hg push ../tmpc
398 pushing to ../tmpc
400 pushing to ../tmpc
399 searching for changes
401 searching for changes
400 no changes found
402 no changes found
401 [1]
403 [1]
402 $ hg -R ../tmpc debugobsolete
404 $ hg -R ../tmpc debugobsolete
403 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
405 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
404 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
406 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
405 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
407 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
406 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
408 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
407 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
409 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
408 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
410 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
409
411
410 detect outgoing obsolete and unstable
412 detect outgoing obsolete and unstable
411 ---------------------------------------
413 ---------------------------------------
412
414
413
415
414 $ hg log -G
416 $ hg log -G
415 o 3:6f9641995072 (draft) [tip ] add n3w_3_c
417 o 3:6f9641995072 (draft) [tip ] add n3w_3_c
416 |
418 |
417 | o 2:245bde4270cd (public) [ ] add original_c
419 | o 2:245bde4270cd (public) [ ] add original_c
418 |/
420 |/
419 o 1:7c3bad9141dc (public) [ ] add b
421 o 1:7c3bad9141dc (public) [ ] add b
420 |
422 |
421 o 0:1f0dee641bb7 (public) [ ] add a
423 o 0:1f0dee641bb7 (public) [ ] add a
422
424
423 $ hg up 'desc("n3w_3_c")'
425 $ hg up 'desc("n3w_3_c")'
424 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
426 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
425 $ mkcommit original_d
427 $ mkcommit original_d
426 $ mkcommit original_e
428 $ mkcommit original_e
427 $ hg debugobsolete --record-parents `getid original_d` -d '0 0'
429 $ hg debugobsolete --record-parents `getid original_d` -d '0 0'
428 $ hg debugobsolete | grep `getid original_d`
430 $ hg debugobsolete | grep `getid original_d`
429 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
431 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
430 $ hg log -r 'obsolete()'
432 $ hg log -r 'obsolete()'
431 4:94b33453f93b (draft) [ ] add original_d
433 4:94b33453f93b (draft) [ ] add original_d
432 $ hg log -G -r '::unstable()'
434 $ hg log -G -r '::unstable()'
433 @ 5:cda648ca50f5 (draft) [tip ] add original_e
435 @ 5:cda648ca50f5 (draft) [tip ] add original_e
434 |
436 |
435 x 4:94b33453f93b (draft) [ ] add original_d
437 x 4:94b33453f93b (draft) [ ] add original_d
436 |
438 |
437 o 3:6f9641995072 (draft) [ ] add n3w_3_c
439 o 3:6f9641995072 (draft) [ ] add n3w_3_c
438 |
440 |
439 o 1:7c3bad9141dc (public) [ ] add b
441 o 1:7c3bad9141dc (public) [ ] add b
440 |
442 |
441 o 0:1f0dee641bb7 (public) [ ] add a
443 o 0:1f0dee641bb7 (public) [ ] add a
442
444
443
445
444 refuse to push obsolete changeset
446 refuse to push obsolete changeset
445
447
446 $ hg push ../tmpc/ -r 'desc("original_d")'
448 $ hg push ../tmpc/ -r 'desc("original_d")'
447 pushing to ../tmpc/
449 pushing to ../tmpc/
448 searching for changes
450 searching for changes
449 abort: push includes obsolete changeset: 94b33453f93b!
451 abort: push includes obsolete changeset: 94b33453f93b!
450 [255]
452 [255]
451
453
452 refuse to push unstable changeset
454 refuse to push unstable changeset
453
455
454 $ hg push ../tmpc/
456 $ hg push ../tmpc/
455 pushing to ../tmpc/
457 pushing to ../tmpc/
456 searching for changes
458 searching for changes
457 abort: push includes unstable changeset: cda648ca50f5!
459 abort: push includes unstable changeset: cda648ca50f5!
458 [255]
460 [255]
459
461
460 Test that extinct changeset are properly detected
462 Test that extinct changeset are properly detected
461
463
462 $ hg log -r 'extinct()'
464 $ hg log -r 'extinct()'
463
465
464 Don't try to push extinct changeset
466 Don't try to push extinct changeset
465
467
466 $ hg init ../tmpf
468 $ hg init ../tmpf
467 $ hg out ../tmpf
469 $ hg out ../tmpf
468 comparing with ../tmpf
470 comparing with ../tmpf
469 searching for changes
471 searching for changes
470 0:1f0dee641bb7 (public) [ ] add a
472 0:1f0dee641bb7 (public) [ ] add a
471 1:7c3bad9141dc (public) [ ] add b
473 1:7c3bad9141dc (public) [ ] add b
472 2:245bde4270cd (public) [ ] add original_c
474 2:245bde4270cd (public) [ ] add original_c
473 3:6f9641995072 (draft) [ ] add n3w_3_c
475 3:6f9641995072 (draft) [ ] add n3w_3_c
474 4:94b33453f93b (draft) [ ] add original_d
476 4:94b33453f93b (draft) [ ] add original_d
475 5:cda648ca50f5 (draft) [tip ] add original_e
477 5:cda648ca50f5 (draft) [tip ] add original_e
476 $ hg push ../tmpf -f # -f because be push unstable too
478 $ hg push ../tmpf -f # -f because be push unstable too
477 pushing to ../tmpf
479 pushing to ../tmpf
478 searching for changes
480 searching for changes
479 adding changesets
481 adding changesets
480 adding manifests
482 adding manifests
481 adding file changes
483 adding file changes
482 added 6 changesets with 6 changes to 6 files (+1 heads)
484 added 6 changesets with 6 changes to 6 files (+1 heads)
483
485
484 no warning displayed
486 no warning displayed
485
487
486 $ hg push ../tmpf
488 $ hg push ../tmpf
487 pushing to ../tmpf
489 pushing to ../tmpf
488 searching for changes
490 searching for changes
489 no changes found
491 no changes found
490 [1]
492 [1]
491
493
492 Do not warn about new head when the new head is a successors of a remote one
494 Do not warn about new head when the new head is a successors of a remote one
493
495
494 $ hg log -G
496 $ hg log -G
495 @ 5:cda648ca50f5 (draft) [tip ] add original_e
497 @ 5:cda648ca50f5 (draft) [tip ] add original_e
496 |
498 |
497 x 4:94b33453f93b (draft) [ ] add original_d
499 x 4:94b33453f93b (draft) [ ] add original_d
498 |
500 |
499 o 3:6f9641995072 (draft) [ ] add n3w_3_c
501 o 3:6f9641995072 (draft) [ ] add n3w_3_c
500 |
502 |
501 | o 2:245bde4270cd (public) [ ] add original_c
503 | o 2:245bde4270cd (public) [ ] add original_c
502 |/
504 |/
503 o 1:7c3bad9141dc (public) [ ] add b
505 o 1:7c3bad9141dc (public) [ ] add b
504 |
506 |
505 o 0:1f0dee641bb7 (public) [ ] add a
507 o 0:1f0dee641bb7 (public) [ ] add a
506
508
507 $ hg up -q 'desc(n3w_3_c)'
509 $ hg up -q 'desc(n3w_3_c)'
508 $ mkcommit obsolete_e
510 $ mkcommit obsolete_e
509 created new head
511 created new head
510 $ hg debugobsolete `getid 'original_e'` `getid 'obsolete_e'`
512 $ hg debugobsolete `getid 'original_e'` `getid 'obsolete_e'`
511 $ hg outgoing ../tmpf # parasite hg outgoing testin
513 $ hg outgoing ../tmpf # parasite hg outgoing testin
512 comparing with ../tmpf
514 comparing with ../tmpf
513 searching for changes
515 searching for changes
514 6:3de5eca88c00 (draft) [tip ] add obsolete_e
516 6:3de5eca88c00 (draft) [tip ] add obsolete_e
515 $ hg push ../tmpf
517 $ hg push ../tmpf
516 pushing to ../tmpf
518 pushing to ../tmpf
517 searching for changes
519 searching for changes
518 adding changesets
520 adding changesets
519 adding manifests
521 adding manifests
520 adding file changes
522 adding file changes
521 added 1 changesets with 1 changes to 1 files (+1 heads)
523 added 1 changesets with 1 changes to 1 files (+1 heads)
522
524
523 test relevance computation
525 test relevance computation
524 ---------------------------------------
526 ---------------------------------------
525
527
526 Checking simple case of "marker relevance".
528 Checking simple case of "marker relevance".
527
529
528
530
529 Reminder of the repo situation
531 Reminder of the repo situation
530
532
531 $ hg log --hidden --graph
533 $ hg log --hidden --graph
532 @ 6:3de5eca88c00 (draft) [tip ] add obsolete_e
534 @ 6:3de5eca88c00 (draft) [tip ] add obsolete_e
533 |
535 |
534 | x 5:cda648ca50f5 (draft) [ ] add original_e
536 | x 5:cda648ca50f5 (draft) [ ] add original_e
535 | |
537 | |
536 | x 4:94b33453f93b (draft) [ ] add original_d
538 | x 4:94b33453f93b (draft) [ ] add original_d
537 |/
539 |/
538 o 3:6f9641995072 (draft) [ ] add n3w_3_c
540 o 3:6f9641995072 (draft) [ ] add n3w_3_c
539 |
541 |
540 | o 2:245bde4270cd (public) [ ] add original_c
542 | o 2:245bde4270cd (public) [ ] add original_c
541 |/
543 |/
542 o 1:7c3bad9141dc (public) [ ] add b
544 o 1:7c3bad9141dc (public) [ ] add b
543 |
545 |
544 o 0:1f0dee641bb7 (public) [ ] add a
546 o 0:1f0dee641bb7 (public) [ ] add a
545
547
546
548
547 List of all markers
549 List of all markers
548
550
549 $ hg debugobsolete
551 $ hg debugobsolete
550 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
552 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
551 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
553 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
552 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
554 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
553 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
555 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
554 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
556 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
555 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
557 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
556 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
558 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
557 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob)
559 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob)
558
560
559 List of changesets with no chain
561 List of changesets with no chain
560
562
561 $ hg debugobsolete --hidden --rev ::2
563 $ hg debugobsolete --hidden --rev ::2
562
564
563 List of changesets that are included on marker chain
565 List of changesets that are included on marker chain
564
566
565 $ hg debugobsolete --hidden --rev 6
567 $ hg debugobsolete --hidden --rev 6
566 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob)
568 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob)
567
569
568 List of changesets with a longer chain, (including a pruned children)
570 List of changesets with a longer chain, (including a pruned children)
569
571
570 $ hg debugobsolete --hidden --rev 3
572 $ hg debugobsolete --hidden --rev 3
571 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
573 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
572 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
574 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
573 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
575 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
574 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
576 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
575 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
577 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
576 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
578 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
577 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
579 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
578
580
579 List of both
581 List of both
580
582
581 $ hg debugobsolete --hidden --rev 3::6
583 $ hg debugobsolete --hidden --rev 3::6
582 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
584 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
583 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
585 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
584 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
586 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
585 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
587 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
586 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
588 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
587 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
589 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
588 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob)
590 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob)
589 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
591 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
590
592
591 #if serve
593 #if serve
592
594
593 check hgweb does not explode
595 check hgweb does not explode
594 ====================================
596 ====================================
595
597
596 $ hg unbundle $TESTDIR/bundles/hgweb+obs.hg
598 $ hg unbundle $TESTDIR/bundles/hgweb+obs.hg
597 adding changesets
599 adding changesets
598 adding manifests
600 adding manifests
599 adding file changes
601 adding file changes
600 added 62 changesets with 63 changes to 9 files (+60 heads)
602 added 62 changesets with 63 changes to 9 files (+60 heads)
601 (run 'hg heads .' to see heads, 'hg merge' to merge)
603 (run 'hg heads .' to see heads, 'hg merge' to merge)
602 $ for node in `hg log -r 'desc(babar_)' --template '{node}\n'`;
604 $ for node in `hg log -r 'desc(babar_)' --template '{node}\n'`;
603 > do
605 > do
604 > hg debugobsolete $node
606 > hg debugobsolete $node
605 > done
607 > done
606 $ hg up tip
608 $ hg up tip
607 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
609 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
608
610
609 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
611 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
610 $ cat hg.pid >> $DAEMON_PIDS
612 $ cat hg.pid >> $DAEMON_PIDS
611
613
612 check changelog view
614 check changelog view
613
615
614 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'shortlog/'
616 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'shortlog/'
615 200 Script output follows
617 200 Script output follows
616
618
617 check graph view
619 check graph view
618
620
619 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'graph'
621 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'graph'
620 200 Script output follows
622 200 Script output follows
621
623
622 check filelog view
624 check filelog view
623
625
624 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'log/'`hg log -r . -T "{node}"`/'babar'
626 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'log/'`hg log -r . -T "{node}"`/'babar'
625 200 Script output follows
627 200 Script output follows
626
628
627 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/68'
629 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/68'
628 200 Script output follows
630 200 Script output follows
629 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/67'
631 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/67'
630 404 Not Found
632 404 Not Found
631 [1]
633 [1]
632
634
633 check that web.view config option:
635 check that web.view config option:
634
636
635 $ "$TESTDIR/killdaemons.py" hg.pid
637 $ "$TESTDIR/killdaemons.py" hg.pid
636 $ cat >> .hg/hgrc << EOF
638 $ cat >> .hg/hgrc << EOF
637 > [web]
639 > [web]
638 > view=all
640 > view=all
639 > EOF
641 > EOF
640 $ wait
642 $ wait
641 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
643 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
642 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/67'
644 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/67'
643 200 Script output follows
645 200 Script output follows
644 $ "$TESTDIR/killdaemons.py" hg.pid
646 $ "$TESTDIR/killdaemons.py" hg.pid
645
647
646 Checking _enable=False warning if obsolete marker exists
648 Checking _enable=False warning if obsolete marker exists
647
649
648 $ echo '[experimental]' >> $HGRCPATH
650 $ echo '[experimental]' >> $HGRCPATH
649 $ echo "evolution=" >> $HGRCPATH
651 $ echo "evolution=" >> $HGRCPATH
650 $ hg log -r tip
652 $ hg log -r tip
651 obsolete feature not enabled but 68 markers found!
653 obsolete feature not enabled but 68 markers found!
652 68:c15e9edfca13 (draft) [tip ] add celestine
654 68:c15e9edfca13 (draft) [tip ] add celestine
653
655
654 reenable for later test
656 reenable for later test
655
657
656 $ echo '[experimental]' >> $HGRCPATH
658 $ echo '[experimental]' >> $HGRCPATH
657 $ echo "evolution=createmarkers,exchange" >> $HGRCPATH
659 $ echo "evolution=createmarkers,exchange" >> $HGRCPATH
658
660
659 #endif
661 #endif
660
662
661 Test incoming/outcoming with changesets obsoleted remotely, known locally
663 Test incoming/outcoming with changesets obsoleted remotely, known locally
662 ===============================================================================
664 ===============================================================================
663
665
664 This test issue 3805
666 This test issue 3805
665
667
666 $ hg init repo-issue3805
668 $ hg init repo-issue3805
667 $ cd repo-issue3805
669 $ cd repo-issue3805
668 $ echo "foo" > foo
670 $ echo "foo" > foo
669 $ hg ci -Am "A"
671 $ hg ci -Am "A"
670 adding foo
672 adding foo
671 $ hg clone . ../other-issue3805
673 $ hg clone . ../other-issue3805
672 updating to branch default
674 updating to branch default
673 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
675 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
674 $ echo "bar" >> foo
676 $ echo "bar" >> foo
675 $ hg ci --amend
677 $ hg ci --amend
676 $ cd ../other-issue3805
678 $ cd ../other-issue3805
677 $ hg log -G
679 $ hg log -G
678 @ 0:193e9254ce7e (draft) [tip ] A
680 @ 0:193e9254ce7e (draft) [tip ] A
679
681
680 $ hg log -G -R ../repo-issue3805
682 $ hg log -G -R ../repo-issue3805
681 @ 2:3816541e5485 (draft) [tip ] A
683 @ 2:3816541e5485 (draft) [tip ] A
682
684
683 $ hg incoming
685 $ hg incoming
684 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
686 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
685 searching for changes
687 searching for changes
686 2:3816541e5485 (draft) [tip ] A
688 2:3816541e5485 (draft) [tip ] A
687 $ hg incoming --bundle ../issue3805.hg
689 $ hg incoming --bundle ../issue3805.hg
688 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
690 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
689 searching for changes
691 searching for changes
690 2:3816541e5485 (draft) [tip ] A
692 2:3816541e5485 (draft) [tip ] A
691 $ hg outgoing
693 $ hg outgoing
692 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
694 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
693 searching for changes
695 searching for changes
694 no changes found
696 no changes found
695 [1]
697 [1]
696
698
697 #if serve
699 #if serve
698
700
699 $ hg serve -R ../repo-issue3805 -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
701 $ hg serve -R ../repo-issue3805 -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
700 $ cat hg.pid >> $DAEMON_PIDS
702 $ cat hg.pid >> $DAEMON_PIDS
701
703
702 $ hg incoming http://localhost:$HGPORT
704 $ hg incoming http://localhost:$HGPORT
703 comparing with http://localhost:$HGPORT/
705 comparing with http://localhost:$HGPORT/
704 searching for changes
706 searching for changes
705 1:3816541e5485 (draft) [tip ] A
707 1:3816541e5485 (draft) [tip ] A
706 $ hg outgoing http://localhost:$HGPORT
708 $ hg outgoing http://localhost:$HGPORT
707 comparing with http://localhost:$HGPORT/
709 comparing with http://localhost:$HGPORT/
708 searching for changes
710 searching for changes
709 no changes found
711 no changes found
710 [1]
712 [1]
711
713
712 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
714 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
713
715
714 #endif
716 #endif
715
717
716 This test issue 3814
718 This test issue 3814
717
719
718 (nothing to push but locally hidden changeset)
720 (nothing to push but locally hidden changeset)
719
721
720 $ cd ..
722 $ cd ..
721 $ hg init repo-issue3814
723 $ hg init repo-issue3814
722 $ cd repo-issue3805
724 $ cd repo-issue3805
723 $ hg push -r 3816541e5485 ../repo-issue3814
725 $ hg push -r 3816541e5485 ../repo-issue3814
724 pushing to ../repo-issue3814
726 pushing to ../repo-issue3814
725 searching for changes
727 searching for changes
726 adding changesets
728 adding changesets
727 adding manifests
729 adding manifests
728 adding file changes
730 adding file changes
729 added 1 changesets with 1 changes to 1 files
731 added 1 changesets with 1 changes to 1 files
730 $ hg out ../repo-issue3814
732 $ hg out ../repo-issue3814
731 comparing with ../repo-issue3814
733 comparing with ../repo-issue3814
732 searching for changes
734 searching for changes
733 no changes found
735 no changes found
734 [1]
736 [1]
735
737
736 Test that a local tag blocks a changeset from being hidden
738 Test that a local tag blocks a changeset from being hidden
737
739
738 $ hg tag -l visible -r 0 --hidden
740 $ hg tag -l visible -r 0 --hidden
739 $ hg log -G
741 $ hg log -G
740 @ 2:3816541e5485 (draft) [tip ] A
742 @ 2:3816541e5485 (draft) [tip ] A
741
743
742 x 0:193e9254ce7e (draft) [visible ] A
744 x 0:193e9254ce7e (draft) [visible ] A
743
745
744 Test that removing a local tag does not cause some commands to fail
746 Test that removing a local tag does not cause some commands to fail
745
747
746 $ hg tag -l -r tip tiptag
748 $ hg tag -l -r tip tiptag
747 $ hg tags
749 $ hg tags
748 tiptag 2:3816541e5485
750 tiptag 2:3816541e5485
749 tip 2:3816541e5485
751 tip 2:3816541e5485
750 visible 0:193e9254ce7e
752 visible 0:193e9254ce7e
751 $ hg --config extensions.strip= strip -r tip --no-backup
753 $ hg --config extensions.strip= strip -r tip --no-backup
752 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
754 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
753 $ hg tags
755 $ hg tags
754 visible 0:193e9254ce7e
756 visible 0:193e9254ce7e
755 tip 0:193e9254ce7e
757 tip 0:193e9254ce7e
756
758
757 #if serve
759 #if serve
758
760
759 Test issue 4506
761 Test issue 4506
760
762
761 $ cd ..
763 $ cd ..
762 $ hg init repo-issue4506
764 $ hg init repo-issue4506
763 $ cd repo-issue4506
765 $ cd repo-issue4506
764 $ echo "0" > foo
766 $ echo "0" > foo
765 $ hg add foo
767 $ hg add foo
766 $ hg ci -m "content-0"
768 $ hg ci -m "content-0"
767
769
768 $ hg up null
770 $ hg up null
769 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
771 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
770 $ echo "1" > bar
772 $ echo "1" > bar
771 $ hg add bar
773 $ hg add bar
772 $ hg ci -m "content-1"
774 $ hg ci -m "content-1"
773 created new head
775 created new head
774 $ hg up 0
776 $ hg up 0
775 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
777 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
776 $ hg graft 1
778 $ hg graft 1
777 grafting 1:1c9eddb02162 "content-1" (tip)
779 grafting 1:1c9eddb02162 "content-1" (tip)
778
780
779 $ hg debugobsolete `hg log -r1 -T'{node}'` `hg log -r2 -T'{node}'`
781 $ hg debugobsolete `hg log -r1 -T'{node}'` `hg log -r2 -T'{node}'`
780
782
781 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
783 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
782 $ cat hg.pid >> $DAEMON_PIDS
784 $ cat hg.pid >> $DAEMON_PIDS
783
785
784 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/1'
786 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/1'
785 404 Not Found
787 404 Not Found
786 [1]
788 [1]
787 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'file/tip/bar'
789 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'file/tip/bar'
788 200 Script output follows
790 200 Script output follows
789 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'annotate/tip/bar'
791 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'annotate/tip/bar'
790 200 Script output follows
792 200 Script output follows
791
793
792 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
794 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
793
795
794 #endif
796 #endif
795
797
796 $ hg init a
798 $ hg init a
797 $ cd a
799 $ cd a
798 $ touch foo
800 $ touch foo
799 $ hg add foo
801 $ hg add foo
800 $ hg ci -mfoo
802 $ hg ci -mfoo
801 $ touch bar
803 $ touch bar
802 $ hg add bar
804 $ hg add bar
803 $ hg ci -mbar
805 $ hg ci -mbar
804 $ hg up 0
806 $ hg up 0
805 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
807 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
806 $ touch quux
808 $ touch quux
807 $ hg add quux
809 $ hg add quux
808 $ hg ci -m quux
810 $ hg ci -m quux
809 created new head
811 created new head
810 $ hg up 1
812 $ hg up 1
811 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
813 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
812 $ hg tag 1.0
814 $ hg tag 1.0
813
815
814 $ hg up 2
816 $ hg up 2
815 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
817 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
816 $ hg log -G
818 $ hg log -G
817 o 3:bc47fc7e1c1d (draft) [tip ] Added tag 1.0 for changeset 50c889141114
819 o 3:bc47fc7e1c1d (draft) [tip ] Added tag 1.0 for changeset 50c889141114
818 |
820 |
819 | @ 2:3d7f255a0081 (draft) [ ] quux
821 | @ 2:3d7f255a0081 (draft) [ ] quux
820 | |
822 | |
821 o | 1:50c889141114 (draft) [1.0 ] bar
823 o | 1:50c889141114 (draft) [1.0 ] bar
822 |/
824 |/
823 o 0:1f7b0de80e11 (draft) [ ] foo
825 o 0:1f7b0de80e11 (draft) [ ] foo
824
826
825 $ hg debugobsolete `getid bar`
827 $ hg debugobsolete `getid bar`
826 $ hg debugobsolete `getid 1.0`
828 $ hg debugobsolete `getid 1.0`
827 $ hg tag 1.0
829 $ hg tag 1.0
828 $ hg log -G
830 $ hg log -G
829 @ 4:f9f2ab71ffd5 (draft) [tip ] Added tag 1.0 for changeset 3d7f255a0081
831 @ 4:f9f2ab71ffd5 (draft) [tip ] Added tag 1.0 for changeset 3d7f255a0081
830 |
832 |
831 o 2:3d7f255a0081 (draft) [1.0 ] quux
833 o 2:3d7f255a0081 (draft) [1.0 ] quux
832 |
834 |
833 o 0:1f7b0de80e11 (draft) [ ] foo
835 o 0:1f7b0de80e11 (draft) [ ] foo
834
836
835 $ cat .hgtags
837 $ cat .hgtags
836 3d7f255a008103380aeb2a7d581fe257f40969e7 1.0
838 3d7f255a008103380aeb2a7d581fe257f40969e7 1.0
@@ -1,1363 +1,1388 b''
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3
3
4 $ try() {
4 $ try() {
5 > hg debugrevspec --debug "$@"
5 > hg debugrevspec --debug "$@"
6 > }
6 > }
7
7
8 $ log() {
8 $ log() {
9 > hg log --template '{rev}\n' -r "$1"
9 > hg log --template '{rev}\n' -r "$1"
10 > }
10 > }
11
11
12 $ hg init repo
12 $ hg init repo
13 $ cd repo
13 $ cd repo
14
14
15 $ echo a > a
15 $ echo a > a
16 $ hg branch a
16 $ hg branch a
17 marked working directory as branch a
17 marked working directory as branch a
18 (branches are permanent and global, did you want a bookmark?)
18 (branches are permanent and global, did you want a bookmark?)
19 $ hg ci -Aqm0
19 $ hg ci -Aqm0
20
20
21 $ echo b > b
21 $ echo b > b
22 $ hg branch b
22 $ hg branch b
23 marked working directory as branch b
23 marked working directory as branch b
24 (branches are permanent and global, did you want a bookmark?)
24 (branches are permanent and global, did you want a bookmark?)
25 $ hg ci -Aqm1
25 $ hg ci -Aqm1
26
26
27 $ rm a
27 $ rm a
28 $ hg branch a-b-c-
28 $ hg branch a-b-c-
29 marked working directory as branch a-b-c-
29 marked working directory as branch a-b-c-
30 (branches are permanent and global, did you want a bookmark?)
30 (branches are permanent and global, did you want a bookmark?)
31 $ hg ci -Aqm2 -u Bob
31 $ hg ci -Aqm2 -u Bob
32
32
33 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
33 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
34 2
34 2
35 $ hg log -r "extra('branch')" --template '{rev}\n'
35 $ hg log -r "extra('branch')" --template '{rev}\n'
36 0
36 0
37 1
37 1
38 2
38 2
39 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
39 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
40 0 a
40 0 a
41 2 a-b-c-
41 2 a-b-c-
42
42
43 $ hg co 1
43 $ hg co 1
44 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
44 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 $ hg branch +a+b+c+
45 $ hg branch +a+b+c+
46 marked working directory as branch +a+b+c+
46 marked working directory as branch +a+b+c+
47 (branches are permanent and global, did you want a bookmark?)
47 (branches are permanent and global, did you want a bookmark?)
48 $ hg ci -Aqm3
48 $ hg ci -Aqm3
49
49
50 $ hg co 2 # interleave
50 $ hg co 2 # interleave
51 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
51 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
52 $ echo bb > b
52 $ echo bb > b
53 $ hg branch -- -a-b-c-
53 $ hg branch -- -a-b-c-
54 marked working directory as branch -a-b-c-
54 marked working directory as branch -a-b-c-
55 (branches are permanent and global, did you want a bookmark?)
55 (branches are permanent and global, did you want a bookmark?)
56 $ hg ci -Aqm4 -d "May 12 2005"
56 $ hg ci -Aqm4 -d "May 12 2005"
57
57
58 $ hg co 3
58 $ hg co 3
59 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 $ hg branch !a/b/c/
60 $ hg branch !a/b/c/
61 marked working directory as branch !a/b/c/
61 marked working directory as branch !a/b/c/
62 (branches are permanent and global, did you want a bookmark?)
62 (branches are permanent and global, did you want a bookmark?)
63 $ hg ci -Aqm"5 bug"
63 $ hg ci -Aqm"5 bug"
64
64
65 $ hg merge 4
65 $ hg merge 4
66 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
66 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
67 (branch merge, don't forget to commit)
67 (branch merge, don't forget to commit)
68 $ hg branch _a_b_c_
68 $ hg branch _a_b_c_
69 marked working directory as branch _a_b_c_
69 marked working directory as branch _a_b_c_
70 (branches are permanent and global, did you want a bookmark?)
70 (branches are permanent and global, did you want a bookmark?)
71 $ hg ci -Aqm"6 issue619"
71 $ hg ci -Aqm"6 issue619"
72
72
73 $ hg branch .a.b.c.
73 $ hg branch .a.b.c.
74 marked working directory as branch .a.b.c.
74 marked working directory as branch .a.b.c.
75 (branches are permanent and global, did you want a bookmark?)
75 (branches are permanent and global, did you want a bookmark?)
76 $ hg ci -Aqm7
76 $ hg ci -Aqm7
77
77
78 $ hg branch all
78 $ hg branch all
79 marked working directory as branch all
79 marked working directory as branch all
80 (branches are permanent and global, did you want a bookmark?)
80 (branches are permanent and global, did you want a bookmark?)
81
81
82 $ hg co 4
82 $ hg co 4
83 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 $ hg branch Γ©
84 $ hg branch Γ©
85 marked working directory as branch \xc3\xa9 (esc)
85 marked working directory as branch \xc3\xa9 (esc)
86 (branches are permanent and global, did you want a bookmark?)
86 (branches are permanent and global, did you want a bookmark?)
87 $ hg ci -Aqm9
87 $ hg ci -Aqm9
88
88
89 $ hg tag -r6 1.0
89 $ hg tag -r6 1.0
90
90
91 $ hg clone --quiet -U -r 7 . ../remote1
91 $ hg clone --quiet -U -r 7 . ../remote1
92 $ hg clone --quiet -U -r 8 . ../remote2
92 $ hg clone --quiet -U -r 8 . ../remote2
93 $ echo "[paths]" >> .hg/hgrc
93 $ echo "[paths]" >> .hg/hgrc
94 $ echo "default = ../remote1" >> .hg/hgrc
94 $ echo "default = ../remote1" >> .hg/hgrc
95
95
96 names that should work without quoting
96 names that should work without quoting
97
97
98 $ try a
98 $ try a
99 ('symbol', 'a')
99 ('symbol', 'a')
100 0
100 0
101 $ try b-a
101 $ try b-a
102 (minus
102 (minus
103 ('symbol', 'b')
103 ('symbol', 'b')
104 ('symbol', 'a'))
104 ('symbol', 'a'))
105 1
105 1
106 $ try _a_b_c_
106 $ try _a_b_c_
107 ('symbol', '_a_b_c_')
107 ('symbol', '_a_b_c_')
108 6
108 6
109 $ try _a_b_c_-a
109 $ try _a_b_c_-a
110 (minus
110 (minus
111 ('symbol', '_a_b_c_')
111 ('symbol', '_a_b_c_')
112 ('symbol', 'a'))
112 ('symbol', 'a'))
113 6
113 6
114 $ try .a.b.c.
114 $ try .a.b.c.
115 ('symbol', '.a.b.c.')
115 ('symbol', '.a.b.c.')
116 7
116 7
117 $ try .a.b.c.-a
117 $ try .a.b.c.-a
118 (minus
118 (minus
119 ('symbol', '.a.b.c.')
119 ('symbol', '.a.b.c.')
120 ('symbol', 'a'))
120 ('symbol', 'a'))
121 7
121 7
122 $ try -- '-a-b-c-' # complains
122 $ try -- '-a-b-c-' # complains
123 hg: parse error at 7: not a prefix: end
123 hg: parse error at 7: not a prefix: end
124 [255]
124 [255]
125 $ log -a-b-c- # succeeds with fallback
125 $ log -a-b-c- # succeeds with fallback
126 4
126 4
127
127
128 $ try -- -a-b-c--a # complains
128 $ try -- -a-b-c--a # complains
129 (minus
129 (minus
130 (minus
130 (minus
131 (minus
131 (minus
132 (negate
132 (negate
133 ('symbol', 'a'))
133 ('symbol', 'a'))
134 ('symbol', 'b'))
134 ('symbol', 'b'))
135 ('symbol', 'c'))
135 ('symbol', 'c'))
136 (negate
136 (negate
137 ('symbol', 'a')))
137 ('symbol', 'a')))
138 abort: unknown revision '-a'!
138 abort: unknown revision '-a'!
139 [255]
139 [255]
140 $ try Γ©
140 $ try Γ©
141 ('symbol', '\xc3\xa9')
141 ('symbol', '\xc3\xa9')
142 9
142 9
143
143
144 no quoting needed
144 no quoting needed
145
145
146 $ log ::a-b-c-
146 $ log ::a-b-c-
147 0
147 0
148 1
148 1
149 2
149 2
150
150
151 quoting needed
151 quoting needed
152
152
153 $ try '"-a-b-c-"-a'
153 $ try '"-a-b-c-"-a'
154 (minus
154 (minus
155 ('string', '-a-b-c-')
155 ('string', '-a-b-c-')
156 ('symbol', 'a'))
156 ('symbol', 'a'))
157 4
157 4
158
158
159 $ log '1 or 2'
159 $ log '1 or 2'
160 1
160 1
161 2
161 2
162 $ log '1|2'
162 $ log '1|2'
163 1
163 1
164 2
164 2
165 $ log '1 and 2'
165 $ log '1 and 2'
166 $ log '1&2'
166 $ log '1&2'
167 $ try '1&2|3' # precedence - and is higher
167 $ try '1&2|3' # precedence - and is higher
168 (or
168 (or
169 (and
169 (and
170 ('symbol', '1')
170 ('symbol', '1')
171 ('symbol', '2'))
171 ('symbol', '2'))
172 ('symbol', '3'))
172 ('symbol', '3'))
173 3
173 3
174 $ try '1|2&3'
174 $ try '1|2&3'
175 (or
175 (or
176 ('symbol', '1')
176 ('symbol', '1')
177 (and
177 (and
178 ('symbol', '2')
178 ('symbol', '2')
179 ('symbol', '3')))
179 ('symbol', '3')))
180 1
180 1
181 $ try '1&2&3' # associativity
181 $ try '1&2&3' # associativity
182 (and
182 (and
183 (and
183 (and
184 ('symbol', '1')
184 ('symbol', '1')
185 ('symbol', '2'))
185 ('symbol', '2'))
186 ('symbol', '3'))
186 ('symbol', '3'))
187 $ try '1|(2|3)'
187 $ try '1|(2|3)'
188 (or
188 (or
189 ('symbol', '1')
189 ('symbol', '1')
190 (group
190 (group
191 (or
191 (or
192 ('symbol', '2')
192 ('symbol', '2')
193 ('symbol', '3'))))
193 ('symbol', '3'))))
194 1
194 1
195 2
195 2
196 3
196 3
197 $ log '1.0' # tag
197 $ log '1.0' # tag
198 6
198 6
199 $ log 'a' # branch
199 $ log 'a' # branch
200 0
200 0
201 $ log '2785f51ee'
201 $ log '2785f51ee'
202 0
202 0
203 $ log 'date(2005)'
203 $ log 'date(2005)'
204 4
204 4
205 $ log 'date(this is a test)'
205 $ log 'date(this is a test)'
206 hg: parse error at 10: unexpected token: symbol
206 hg: parse error at 10: unexpected token: symbol
207 [255]
207 [255]
208 $ log 'date()'
208 $ log 'date()'
209 hg: parse error: date requires a string
209 hg: parse error: date requires a string
210 [255]
210 [255]
211 $ log 'date'
211 $ log 'date'
212 hg: parse error: can't use date here
212 hg: parse error: can't use date here
213 [255]
213 [255]
214 $ log 'date('
214 $ log 'date('
215 hg: parse error at 5: not a prefix: end
215 hg: parse error at 5: not a prefix: end
216 [255]
216 [255]
217 $ log 'date(tip)'
217 $ log 'date(tip)'
218 abort: invalid date: 'tip'
218 abort: invalid date: 'tip'
219 [255]
219 [255]
220 $ log '"date"'
220 $ log '"date"'
221 abort: unknown revision 'date'!
221 abort: unknown revision 'date'!
222 [255]
222 [255]
223 $ log 'date(2005) and 1::'
223 $ log 'date(2005) and 1::'
224 4
224 4
225
225
226 ancestor can accept 0 or more arguments
226 ancestor can accept 0 or more arguments
227
227
228 $ log 'ancestor()'
228 $ log 'ancestor()'
229 $ log 'ancestor(1)'
229 $ log 'ancestor(1)'
230 1
230 1
231 $ log 'ancestor(4,5)'
231 $ log 'ancestor(4,5)'
232 1
232 1
233 $ log 'ancestor(4,5) and 4'
233 $ log 'ancestor(4,5) and 4'
234 $ log 'ancestor(0,0,1,3)'
234 $ log 'ancestor(0,0,1,3)'
235 0
235 0
236 $ log 'ancestor(3,1,5,3,5,1)'
236 $ log 'ancestor(3,1,5,3,5,1)'
237 1
237 1
238 $ log 'ancestor(0,1,3,5)'
238 $ log 'ancestor(0,1,3,5)'
239 0
239 0
240 $ log 'ancestor(1,2,3,4,5)'
240 $ log 'ancestor(1,2,3,4,5)'
241 1
241 1
242 $ log 'ancestors(5)'
242 $ log 'ancestors(5)'
243 0
243 0
244 1
244 1
245 3
245 3
246 5
246 5
247 $ log 'ancestor(ancestors(5))'
247 $ log 'ancestor(ancestors(5))'
248 0
248 0
249 $ log 'author(bob)'
249 $ log 'author(bob)'
250 2
250 2
251 $ log 'author("re:bob|test")'
251 $ log 'author("re:bob|test")'
252 0
252 0
253 1
253 1
254 2
254 2
255 3
255 3
256 4
256 4
257 5
257 5
258 6
258 6
259 7
259 7
260 8
260 8
261 9
261 9
262 $ log 'branch(Γ©)'
262 $ log 'branch(Γ©)'
263 8
263 8
264 9
264 9
265 $ log 'branch(a)'
265 $ log 'branch(a)'
266 0
266 0
267 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
267 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
268 0 a
268 0 a
269 2 a-b-c-
269 2 a-b-c-
270 3 +a+b+c+
270 3 +a+b+c+
271 4 -a-b-c-
271 4 -a-b-c-
272 5 !a/b/c/
272 5 !a/b/c/
273 6 _a_b_c_
273 6 _a_b_c_
274 7 .a.b.c.
274 7 .a.b.c.
275 $ log 'children(ancestor(4,5))'
275 $ log 'children(ancestor(4,5))'
276 2
276 2
277 3
277 3
278 $ log 'closed()'
278 $ log 'closed()'
279 $ log 'contains(a)'
279 $ log 'contains(a)'
280 0
280 0
281 1
281 1
282 3
282 3
283 5
283 5
284 $ log 'contains("../repo/a")'
284 $ log 'contains("../repo/a")'
285 0
285 0
286 1
286 1
287 3
287 3
288 5
288 5
289 $ log 'desc(B)'
289 $ log 'desc(B)'
290 5
290 5
291 $ log 'descendants(2 or 3)'
291 $ log 'descendants(2 or 3)'
292 2
292 2
293 3
293 3
294 4
294 4
295 5
295 5
296 6
296 6
297 7
297 7
298 8
298 8
299 9
299 9
300 $ log 'file("b*")'
300 $ log 'file("b*")'
301 1
301 1
302 4
302 4
303 $ log 'filelog("b")'
303 $ log 'filelog("b")'
304 1
304 1
305 4
305 4
306 $ log 'filelog("../repo/b")'
306 $ log 'filelog("../repo/b")'
307 1
307 1
308 4
308 4
309 $ log 'follow()'
309 $ log 'follow()'
310 0
310 0
311 1
311 1
312 2
312 2
313 4
313 4
314 8
314 8
315 9
315 9
316 $ log 'grep("issue\d+")'
316 $ log 'grep("issue\d+")'
317 6
317 6
318 $ try 'grep("(")' # invalid regular expression
318 $ try 'grep("(")' # invalid regular expression
319 (func
319 (func
320 ('symbol', 'grep')
320 ('symbol', 'grep')
321 ('string', '('))
321 ('string', '('))
322 hg: parse error: invalid match pattern: unbalanced parenthesis
322 hg: parse error: invalid match pattern: unbalanced parenthesis
323 [255]
323 [255]
324 $ try 'grep("\bissue\d+")'
324 $ try 'grep("\bissue\d+")'
325 (func
325 (func
326 ('symbol', 'grep')
326 ('symbol', 'grep')
327 ('string', '\x08issue\\d+'))
327 ('string', '\x08issue\\d+'))
328 $ try 'grep(r"\bissue\d+")'
328 $ try 'grep(r"\bissue\d+")'
329 (func
329 (func
330 ('symbol', 'grep')
330 ('symbol', 'grep')
331 ('string', '\\bissue\\d+'))
331 ('string', '\\bissue\\d+'))
332 6
332 6
333 $ try 'grep(r"\")'
333 $ try 'grep(r"\")'
334 hg: parse error at 7: unterminated string
334 hg: parse error at 7: unterminated string
335 [255]
335 [255]
336 $ log 'head()'
336 $ log 'head()'
337 0
337 0
338 1
338 1
339 2
339 2
340 3
340 3
341 4
341 4
342 5
342 5
343 6
343 6
344 7
344 7
345 9
345 9
346 $ log 'heads(6::)'
346 $ log 'heads(6::)'
347 7
347 7
348 $ log 'keyword(issue)'
348 $ log 'keyword(issue)'
349 6
349 6
350 $ log 'keyword("test a")'
350 $ log 'keyword("test a")'
351 $ log 'limit(head(), 1)'
351 $ log 'limit(head(), 1)'
352 0
352 0
353 $ log 'matching(6)'
353 $ log 'matching(6)'
354 6
354 6
355 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
355 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
356 6
356 6
357 7
357 7
358
358
359 Testing min and max
359 Testing min and max
360
360
361 max: simple
361 max: simple
362
362
363 $ log 'max(contains(a))'
363 $ log 'max(contains(a))'
364 5
364 5
365
365
366 max: simple on unordered set)
366 max: simple on unordered set)
367
367
368 $ log 'max((4+0+2+5+7) and contains(a))'
368 $ log 'max((4+0+2+5+7) and contains(a))'
369 5
369 5
370
370
371 max: no result
371 max: no result
372
372
373 $ log 'max(contains(stringthatdoesnotappearanywhere))'
373 $ log 'max(contains(stringthatdoesnotappearanywhere))'
374
374
375 max: no result on unordered set
375 max: no result on unordered set
376
376
377 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
377 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
378
378
379 min: simple
379 min: simple
380
380
381 $ log 'min(contains(a))'
381 $ log 'min(contains(a))'
382 0
382 0
383
383
384 min: simple on unordered set
384 min: simple on unordered set
385
385
386 $ log 'min((4+0+2+5+7) and contains(a))'
386 $ log 'min((4+0+2+5+7) and contains(a))'
387 0
387 0
388
388
389 min: empty
389 min: empty
390
390
391 $ log 'min(contains(stringthatdoesnotappearanywhere))'
391 $ log 'min(contains(stringthatdoesnotappearanywhere))'
392
392
393 min: empty on unordered set
393 min: empty on unordered set
394
394
395 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
395 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
396
396
397
397
398 $ log 'merge()'
398 $ log 'merge()'
399 6
399 6
400 $ log 'branchpoint()'
400 $ log 'branchpoint()'
401 1
401 1
402 4
402 4
403 $ log 'modifies(b)'
403 $ log 'modifies(b)'
404 4
404 4
405 $ log 'modifies("path:b")'
405 $ log 'modifies("path:b")'
406 4
406 4
407 $ log 'modifies("*")'
407 $ log 'modifies("*")'
408 4
408 4
409 6
409 6
410 $ log 'modifies("set:modified()")'
410 $ log 'modifies("set:modified()")'
411 4
411 4
412 $ log 'id(5)'
412 $ log 'id(5)'
413 2
413 2
414 $ log 'only(9)'
414 $ log 'only(9)'
415 8
415 8
416 9
416 9
417 $ log 'only(8)'
417 $ log 'only(8)'
418 8
418 8
419 $ log 'only(9, 5)'
419 $ log 'only(9, 5)'
420 2
420 2
421 4
421 4
422 8
422 8
423 9
423 9
424 $ log 'only(7 + 9, 5 + 2)'
424 $ log 'only(7 + 9, 5 + 2)'
425 4
425 4
426 6
426 6
427 7
427 7
428 8
428 8
429 9
429 9
430
430
431 Test empty set input
431 Test empty set input
432 $ log 'only(p2())'
432 $ log 'only(p2())'
433 $ log 'only(p1(), p2())'
433 $ log 'only(p1(), p2())'
434 0
434 0
435 1
435 1
436 2
436 2
437 4
437 4
438 8
438 8
439 9
439 9
440
440
441 Test '%' operator
441 Test '%' operator
442
442
443 $ log '9%'
443 $ log '9%'
444 8
444 8
445 9
445 9
446 $ log '9%5'
446 $ log '9%5'
447 2
447 2
448 4
448 4
449 8
449 8
450 9
450 9
451 $ log '(7 + 9)%(5 + 2)'
451 $ log '(7 + 9)%(5 + 2)'
452 4
452 4
453 6
453 6
454 7
454 7
455 8
455 8
456 9
456 9
457
457
458 Test the order of operations
458 Test the order of operations
459
459
460 $ log '7 + 9%5 + 2'
460 $ log '7 + 9%5 + 2'
461 7
461 7
462 2
462 2
463 4
463 4
464 8
464 8
465 9
465 9
466
466
467 Test explicit numeric revision
467 Test explicit numeric revision
468 $ log 'rev(-2)'
468 $ log 'rev(-2)'
469 $ log 'rev(-1)'
469 $ log 'rev(-1)'
470 -1
470 -1
471 $ log 'rev(0)'
471 $ log 'rev(0)'
472 0
472 0
473 $ log 'rev(9)'
473 $ log 'rev(9)'
474 9
474 9
475 $ log 'rev(10)'
475 $ log 'rev(10)'
476 $ log 'rev(tip)'
476 $ log 'rev(tip)'
477 hg: parse error: rev expects a number
477 hg: parse error: rev expects a number
478 [255]
478 [255]
479
479
480 Test null revision
480 Test null revision
481 $ log '(null)'
482 -1
483 $ log '(null:0)'
484 -1
485 0
486 $ log '(0:null)'
487 0
488 -1
489 $ log 'null::0'
490 -1
491 0
492 $ log 'null:tip - 0:'
493 -1
494 $ log 'null: and null::' | head -1
495 -1
496 $ log 'null: or 0:' | head -2
497 -1
498 0
481 $ log 'ancestors(null)'
499 $ log 'ancestors(null)'
482 -1
500 -1
501 $ log 'reverse(null:)' | tail -2
502 0
503 -1
504 $ log 'first(null:)'
505 -1
506 $ log 'min(null:)'
507 -1
483 $ log 'tip:null and all()' | tail -2
508 $ log 'tip:null and all()' | tail -2
484 1
509 1
485 0
510 0
486
511
487 $ log 'outgoing()'
512 $ log 'outgoing()'
488 8
513 8
489 9
514 9
490 $ log 'outgoing("../remote1")'
515 $ log 'outgoing("../remote1")'
491 8
516 8
492 9
517 9
493 $ log 'outgoing("../remote2")'
518 $ log 'outgoing("../remote2")'
494 3
519 3
495 5
520 5
496 6
521 6
497 7
522 7
498 9
523 9
499 $ log 'p1(merge())'
524 $ log 'p1(merge())'
500 5
525 5
501 $ log 'p2(merge())'
526 $ log 'p2(merge())'
502 4
527 4
503 $ log 'parents(merge())'
528 $ log 'parents(merge())'
504 4
529 4
505 5
530 5
506 $ log 'p1(branchpoint())'
531 $ log 'p1(branchpoint())'
507 0
532 0
508 2
533 2
509 $ log 'p2(branchpoint())'
534 $ log 'p2(branchpoint())'
510 $ log 'parents(branchpoint())'
535 $ log 'parents(branchpoint())'
511 0
536 0
512 2
537 2
513 $ log 'removes(a)'
538 $ log 'removes(a)'
514 2
539 2
515 6
540 6
516 $ log 'roots(all())'
541 $ log 'roots(all())'
517 0
542 0
518 $ log 'reverse(2 or 3 or 4 or 5)'
543 $ log 'reverse(2 or 3 or 4 or 5)'
519 5
544 5
520 4
545 4
521 3
546 3
522 2
547 2
523 $ log 'reverse(all())'
548 $ log 'reverse(all())'
524 9
549 9
525 8
550 8
526 7
551 7
527 6
552 6
528 5
553 5
529 4
554 4
530 3
555 3
531 2
556 2
532 1
557 1
533 0
558 0
534 $ log 'reverse(all()) & filelog(b)'
559 $ log 'reverse(all()) & filelog(b)'
535 4
560 4
536 1
561 1
537 $ log 'rev(5)'
562 $ log 'rev(5)'
538 5
563 5
539 $ log 'sort(limit(reverse(all()), 3))'
564 $ log 'sort(limit(reverse(all()), 3))'
540 7
565 7
541 8
566 8
542 9
567 9
543 $ log 'sort(2 or 3 or 4 or 5, date)'
568 $ log 'sort(2 or 3 or 4 or 5, date)'
544 2
569 2
545 3
570 3
546 5
571 5
547 4
572 4
548 $ log 'tagged()'
573 $ log 'tagged()'
549 6
574 6
550 $ log 'tag()'
575 $ log 'tag()'
551 6
576 6
552 $ log 'tag(1.0)'
577 $ log 'tag(1.0)'
553 6
578 6
554 $ log 'tag(tip)'
579 $ log 'tag(tip)'
555 9
580 9
556
581
557 test sort revset
582 test sort revset
558 --------------------------------------------
583 --------------------------------------------
559
584
560 test when adding two unordered revsets
585 test when adding two unordered revsets
561
586
562 $ log 'sort(keyword(issue) or modifies(b))'
587 $ log 'sort(keyword(issue) or modifies(b))'
563 4
588 4
564 6
589 6
565
590
566 test when sorting a reversed collection in the same way it is
591 test when sorting a reversed collection in the same way it is
567
592
568 $ log 'sort(reverse(all()), -rev)'
593 $ log 'sort(reverse(all()), -rev)'
569 9
594 9
570 8
595 8
571 7
596 7
572 6
597 6
573 5
598 5
574 4
599 4
575 3
600 3
576 2
601 2
577 1
602 1
578 0
603 0
579
604
580 test when sorting a reversed collection
605 test when sorting a reversed collection
581
606
582 $ log 'sort(reverse(all()), rev)'
607 $ log 'sort(reverse(all()), rev)'
583 0
608 0
584 1
609 1
585 2
610 2
586 3
611 3
587 4
612 4
588 5
613 5
589 6
614 6
590 7
615 7
591 8
616 8
592 9
617 9
593
618
594
619
595 test sorting two sorted collections in different orders
620 test sorting two sorted collections in different orders
596
621
597 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
622 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
598 2
623 2
599 6
624 6
600 8
625 8
601 9
626 9
602
627
603 test sorting two sorted collections in different orders backwards
628 test sorting two sorted collections in different orders backwards
604
629
605 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
630 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
606 9
631 9
607 8
632 8
608 6
633 6
609 2
634 2
610
635
611 test subtracting something from an addset
636 test subtracting something from an addset
612
637
613 $ log '(outgoing() or removes(a)) - removes(a)'
638 $ log '(outgoing() or removes(a)) - removes(a)'
614 8
639 8
615 9
640 9
616
641
617 test intersecting something with an addset
642 test intersecting something with an addset
618
643
619 $ log 'parents(outgoing() or removes(a))'
644 $ log 'parents(outgoing() or removes(a))'
620 1
645 1
621 4
646 4
622 5
647 5
623 8
648 8
624
649
625 test that `or` operation combines elements in the right order:
650 test that `or` operation combines elements in the right order:
626
651
627 $ log '3:4 or 2:5'
652 $ log '3:4 or 2:5'
628 3
653 3
629 4
654 4
630 2
655 2
631 5
656 5
632 $ log '3:4 or 5:2'
657 $ log '3:4 or 5:2'
633 3
658 3
634 4
659 4
635 5
660 5
636 2
661 2
637 $ log 'sort(3:4 or 2:5)'
662 $ log 'sort(3:4 or 2:5)'
638 2
663 2
639 3
664 3
640 4
665 4
641 5
666 5
642 $ log 'sort(3:4 or 5:2)'
667 $ log 'sort(3:4 or 5:2)'
643 2
668 2
644 3
669 3
645 4
670 4
646 5
671 5
647
672
648 check that conversion to only works
673 check that conversion to only works
649 $ try --optimize '::3 - ::1'
674 $ try --optimize '::3 - ::1'
650 (minus
675 (minus
651 (dagrangepre
676 (dagrangepre
652 ('symbol', '3'))
677 ('symbol', '3'))
653 (dagrangepre
678 (dagrangepre
654 ('symbol', '1')))
679 ('symbol', '1')))
655 * optimized:
680 * optimized:
656 (func
681 (func
657 ('symbol', 'only')
682 ('symbol', 'only')
658 (list
683 (list
659 ('symbol', '3')
684 ('symbol', '3')
660 ('symbol', '1')))
685 ('symbol', '1')))
661 3
686 3
662 $ try --optimize 'ancestors(1) - ancestors(3)'
687 $ try --optimize 'ancestors(1) - ancestors(3)'
663 (minus
688 (minus
664 (func
689 (func
665 ('symbol', 'ancestors')
690 ('symbol', 'ancestors')
666 ('symbol', '1'))
691 ('symbol', '1'))
667 (func
692 (func
668 ('symbol', 'ancestors')
693 ('symbol', 'ancestors')
669 ('symbol', '3')))
694 ('symbol', '3')))
670 * optimized:
695 * optimized:
671 (func
696 (func
672 ('symbol', 'only')
697 ('symbol', 'only')
673 (list
698 (list
674 ('symbol', '1')
699 ('symbol', '1')
675 ('symbol', '3')))
700 ('symbol', '3')))
676 $ try --optimize 'not ::2 and ::6'
701 $ try --optimize 'not ::2 and ::6'
677 (and
702 (and
678 (not
703 (not
679 (dagrangepre
704 (dagrangepre
680 ('symbol', '2')))
705 ('symbol', '2')))
681 (dagrangepre
706 (dagrangepre
682 ('symbol', '6')))
707 ('symbol', '6')))
683 * optimized:
708 * optimized:
684 (func
709 (func
685 ('symbol', 'only')
710 ('symbol', 'only')
686 (list
711 (list
687 ('symbol', '6')
712 ('symbol', '6')
688 ('symbol', '2')))
713 ('symbol', '2')))
689 3
714 3
690 4
715 4
691 5
716 5
692 6
717 6
693 $ try --optimize 'ancestors(6) and not ancestors(4)'
718 $ try --optimize 'ancestors(6) and not ancestors(4)'
694 (and
719 (and
695 (func
720 (func
696 ('symbol', 'ancestors')
721 ('symbol', 'ancestors')
697 ('symbol', '6'))
722 ('symbol', '6'))
698 (not
723 (not
699 (func
724 (func
700 ('symbol', 'ancestors')
725 ('symbol', 'ancestors')
701 ('symbol', '4'))))
726 ('symbol', '4'))))
702 * optimized:
727 * optimized:
703 (func
728 (func
704 ('symbol', 'only')
729 ('symbol', 'only')
705 (list
730 (list
706 ('symbol', '6')
731 ('symbol', '6')
707 ('symbol', '4')))
732 ('symbol', '4')))
708 3
733 3
709 5
734 5
710 6
735 6
711
736
712 we can use patterns when searching for tags
737 we can use patterns when searching for tags
713
738
714 $ log 'tag("1..*")'
739 $ log 'tag("1..*")'
715 abort: tag '1..*' does not exist!
740 abort: tag '1..*' does not exist!
716 [255]
741 [255]
717 $ log 'tag("re:1..*")'
742 $ log 'tag("re:1..*")'
718 6
743 6
719 $ log 'tag("re:[0-9].[0-9]")'
744 $ log 'tag("re:[0-9].[0-9]")'
720 6
745 6
721 $ log 'tag("literal:1.0")'
746 $ log 'tag("literal:1.0")'
722 6
747 6
723 $ log 'tag("re:0..*")'
748 $ log 'tag("re:0..*")'
724
749
725 $ log 'tag(unknown)'
750 $ log 'tag(unknown)'
726 abort: tag 'unknown' does not exist!
751 abort: tag 'unknown' does not exist!
727 [255]
752 [255]
728 $ log 'tag("re:unknown")'
753 $ log 'tag("re:unknown")'
729 $ log 'present(tag("unknown"))'
754 $ log 'present(tag("unknown"))'
730 $ log 'present(tag("re:unknown"))'
755 $ log 'present(tag("re:unknown"))'
731 $ log 'branch(unknown)'
756 $ log 'branch(unknown)'
732 abort: unknown revision 'unknown'!
757 abort: unknown revision 'unknown'!
733 [255]
758 [255]
734 $ log 'branch("re:unknown")'
759 $ log 'branch("re:unknown")'
735 $ log 'present(branch("unknown"))'
760 $ log 'present(branch("unknown"))'
736 $ log 'present(branch("re:unknown"))'
761 $ log 'present(branch("re:unknown"))'
737 $ log 'user(bob)'
762 $ log 'user(bob)'
738 2
763 2
739
764
740 $ log '4::8'
765 $ log '4::8'
741 4
766 4
742 8
767 8
743 $ log '4:8'
768 $ log '4:8'
744 4
769 4
745 5
770 5
746 6
771 6
747 7
772 7
748 8
773 8
749
774
750 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
775 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
751 4
776 4
752 2
777 2
753 5
778 5
754
779
755 $ log 'not 0 and 0:2'
780 $ log 'not 0 and 0:2'
756 1
781 1
757 2
782 2
758 $ log 'not 1 and 0:2'
783 $ log 'not 1 and 0:2'
759 0
784 0
760 2
785 2
761 $ log 'not 2 and 0:2'
786 $ log 'not 2 and 0:2'
762 0
787 0
763 1
788 1
764 $ log '(1 and 2)::'
789 $ log '(1 and 2)::'
765 $ log '(1 and 2):'
790 $ log '(1 and 2):'
766 $ log '(1 and 2):3'
791 $ log '(1 and 2):3'
767 $ log 'sort(head(), -rev)'
792 $ log 'sort(head(), -rev)'
768 9
793 9
769 7
794 7
770 6
795 6
771 5
796 5
772 4
797 4
773 3
798 3
774 2
799 2
775 1
800 1
776 0
801 0
777 $ log '4::8 - 8'
802 $ log '4::8 - 8'
778 4
803 4
779 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
804 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
780 2
805 2
781 3
806 3
782 1
807 1
783
808
784 $ log 'named("unknown")'
809 $ log 'named("unknown")'
785 abort: namespace 'unknown' does not exist!
810 abort: namespace 'unknown' does not exist!
786 [255]
811 [255]
787 $ log 'named("re:unknown")'
812 $ log 'named("re:unknown")'
788 abort: no namespace exists that match 'unknown'!
813 abort: no namespace exists that match 'unknown'!
789 [255]
814 [255]
790 $ log 'present(named("unknown"))'
815 $ log 'present(named("unknown"))'
791 $ log 'present(named("re:unknown"))'
816 $ log 'present(named("re:unknown"))'
792
817
793 $ log 'tag()'
818 $ log 'tag()'
794 6
819 6
795 $ log 'named("tags")'
820 $ log 'named("tags")'
796 6
821 6
797
822
798 issue2437
823 issue2437
799
824
800 $ log '3 and p1(5)'
825 $ log '3 and p1(5)'
801 3
826 3
802 $ log '4 and p2(6)'
827 $ log '4 and p2(6)'
803 4
828 4
804 $ log '1 and parents(:2)'
829 $ log '1 and parents(:2)'
805 1
830 1
806 $ log '2 and children(1:)'
831 $ log '2 and children(1:)'
807 2
832 2
808 $ log 'roots(all()) or roots(all())'
833 $ log 'roots(all()) or roots(all())'
809 0
834 0
810 $ hg debugrevspec 'roots(all()) or roots(all())'
835 $ hg debugrevspec 'roots(all()) or roots(all())'
811 0
836 0
812 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
837 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
813 9
838 9
814 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
839 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
815 4
840 4
816
841
817 issue2654: report a parse error if the revset was not completely parsed
842 issue2654: report a parse error if the revset was not completely parsed
818
843
819 $ log '1 OR 2'
844 $ log '1 OR 2'
820 hg: parse error at 2: invalid token
845 hg: parse error at 2: invalid token
821 [255]
846 [255]
822
847
823 or operator should preserve ordering:
848 or operator should preserve ordering:
824 $ log 'reverse(2::4) or tip'
849 $ log 'reverse(2::4) or tip'
825 4
850 4
826 2
851 2
827 9
852 9
828
853
829 parentrevspec
854 parentrevspec
830
855
831 $ log 'merge()^0'
856 $ log 'merge()^0'
832 6
857 6
833 $ log 'merge()^'
858 $ log 'merge()^'
834 5
859 5
835 $ log 'merge()^1'
860 $ log 'merge()^1'
836 5
861 5
837 $ log 'merge()^2'
862 $ log 'merge()^2'
838 4
863 4
839 $ log 'merge()^^'
864 $ log 'merge()^^'
840 3
865 3
841 $ log 'merge()^1^'
866 $ log 'merge()^1^'
842 3
867 3
843 $ log 'merge()^^^'
868 $ log 'merge()^^^'
844 1
869 1
845
870
846 $ log 'merge()~0'
871 $ log 'merge()~0'
847 6
872 6
848 $ log 'merge()~1'
873 $ log 'merge()~1'
849 5
874 5
850 $ log 'merge()~2'
875 $ log 'merge()~2'
851 3
876 3
852 $ log 'merge()~2^1'
877 $ log 'merge()~2^1'
853 1
878 1
854 $ log 'merge()~3'
879 $ log 'merge()~3'
855 1
880 1
856
881
857 $ log '(-3:tip)^'
882 $ log '(-3:tip)^'
858 4
883 4
859 6
884 6
860 8
885 8
861
886
862 $ log 'tip^foo'
887 $ log 'tip^foo'
863 hg: parse error: ^ expects a number 0, 1, or 2
888 hg: parse error: ^ expects a number 0, 1, or 2
864 [255]
889 [255]
865
890
866 multiple revspecs
891 multiple revspecs
867
892
868 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
893 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
869 8
894 8
870 9
895 9
871 4
896 4
872 5
897 5
873 6
898 6
874 7
899 7
875
900
876 test usage in revpair (with "+")
901 test usage in revpair (with "+")
877
902
878 (real pair)
903 (real pair)
879
904
880 $ hg diff -r 'tip^^' -r 'tip'
905 $ hg diff -r 'tip^^' -r 'tip'
881 diff -r 2326846efdab -r 24286f4ae135 .hgtags
906 diff -r 2326846efdab -r 24286f4ae135 .hgtags
882 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
907 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
883 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
908 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
884 @@ -0,0 +1,1 @@
909 @@ -0,0 +1,1 @@
885 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
910 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
886 $ hg diff -r 'tip^^::tip'
911 $ hg diff -r 'tip^^::tip'
887 diff -r 2326846efdab -r 24286f4ae135 .hgtags
912 diff -r 2326846efdab -r 24286f4ae135 .hgtags
888 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
913 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
889 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
914 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
890 @@ -0,0 +1,1 @@
915 @@ -0,0 +1,1 @@
891 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
916 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
892
917
893 (single rev)
918 (single rev)
894
919
895 $ hg diff -r 'tip^' -r 'tip^'
920 $ hg diff -r 'tip^' -r 'tip^'
896 $ hg diff -r 'tip^::tip^ or tip^'
921 $ hg diff -r 'tip^::tip^ or tip^'
897
922
898 (single rev that does not looks like a range)
923 (single rev that does not looks like a range)
899
924
900 $ hg diff -r 'tip^ or tip^'
925 $ hg diff -r 'tip^ or tip^'
901 diff -r d5d0dcbdc4d9 .hgtags
926 diff -r d5d0dcbdc4d9 .hgtags
902 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
927 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
903 +++ b/.hgtags * (glob)
928 +++ b/.hgtags * (glob)
904 @@ -0,0 +1,1 @@
929 @@ -0,0 +1,1 @@
905 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
930 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
906
931
907 (no rev)
932 (no rev)
908
933
909 $ hg diff -r 'author("babar") or author("celeste")'
934 $ hg diff -r 'author("babar") or author("celeste")'
910 abort: empty revision range
935 abort: empty revision range
911 [255]
936 [255]
912
937
913 aliases:
938 aliases:
914
939
915 $ echo '[revsetalias]' >> .hg/hgrc
940 $ echo '[revsetalias]' >> .hg/hgrc
916 $ echo 'm = merge()' >> .hg/hgrc
941 $ echo 'm = merge()' >> .hg/hgrc
917 $ echo 'sincem = descendants(m)' >> .hg/hgrc
942 $ echo 'sincem = descendants(m)' >> .hg/hgrc
918 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
943 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
919 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
944 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
920 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
945 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
921
946
922 $ try m
947 $ try m
923 ('symbol', 'm')
948 ('symbol', 'm')
924 (func
949 (func
925 ('symbol', 'merge')
950 ('symbol', 'merge')
926 None)
951 None)
927 6
952 6
928
953
929 test alias recursion
954 test alias recursion
930
955
931 $ try sincem
956 $ try sincem
932 ('symbol', 'sincem')
957 ('symbol', 'sincem')
933 (func
958 (func
934 ('symbol', 'descendants')
959 ('symbol', 'descendants')
935 (func
960 (func
936 ('symbol', 'merge')
961 ('symbol', 'merge')
937 None))
962 None))
938 6
963 6
939 7
964 7
940
965
941 test infinite recursion
966 test infinite recursion
942
967
943 $ echo 'recurse1 = recurse2' >> .hg/hgrc
968 $ echo 'recurse1 = recurse2' >> .hg/hgrc
944 $ echo 'recurse2 = recurse1' >> .hg/hgrc
969 $ echo 'recurse2 = recurse1' >> .hg/hgrc
945 $ try recurse1
970 $ try recurse1
946 ('symbol', 'recurse1')
971 ('symbol', 'recurse1')
947 hg: parse error: infinite expansion of revset alias "recurse1" detected
972 hg: parse error: infinite expansion of revset alias "recurse1" detected
948 [255]
973 [255]
949
974
950 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
975 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
951 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
976 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
952 $ try "level2(level1(1, 2), 3)"
977 $ try "level2(level1(1, 2), 3)"
953 (func
978 (func
954 ('symbol', 'level2')
979 ('symbol', 'level2')
955 (list
980 (list
956 (func
981 (func
957 ('symbol', 'level1')
982 ('symbol', 'level1')
958 (list
983 (list
959 ('symbol', '1')
984 ('symbol', '1')
960 ('symbol', '2')))
985 ('symbol', '2')))
961 ('symbol', '3')))
986 ('symbol', '3')))
962 (or
987 (or
963 ('symbol', '3')
988 ('symbol', '3')
964 (or
989 (or
965 ('symbol', '1')
990 ('symbol', '1')
966 ('symbol', '2')))
991 ('symbol', '2')))
967 3
992 3
968 1
993 1
969 2
994 2
970
995
971 test nesting and variable passing
996 test nesting and variable passing
972
997
973 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
998 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
974 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
999 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
975 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
1000 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
976 $ try 'nested(2:5)'
1001 $ try 'nested(2:5)'
977 (func
1002 (func
978 ('symbol', 'nested')
1003 ('symbol', 'nested')
979 (range
1004 (range
980 ('symbol', '2')
1005 ('symbol', '2')
981 ('symbol', '5')))
1006 ('symbol', '5')))
982 (func
1007 (func
983 ('symbol', 'max')
1008 ('symbol', 'max')
984 (range
1009 (range
985 ('symbol', '2')
1010 ('symbol', '2')
986 ('symbol', '5')))
1011 ('symbol', '5')))
987 5
1012 5
988
1013
989 test variable isolation, variable placeholders are rewritten as string
1014 test variable isolation, variable placeholders are rewritten as string
990 then parsed and matched again as string. Check they do not leak too
1015 then parsed and matched again as string. Check they do not leak too
991 far away.
1016 far away.
992
1017
993 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1018 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
994 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1019 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
995 $ try 'callinjection(2:5)'
1020 $ try 'callinjection(2:5)'
996 (func
1021 (func
997 ('symbol', 'callinjection')
1022 ('symbol', 'callinjection')
998 (range
1023 (range
999 ('symbol', '2')
1024 ('symbol', '2')
1000 ('symbol', '5')))
1025 ('symbol', '5')))
1001 (func
1026 (func
1002 ('symbol', 'descendants')
1027 ('symbol', 'descendants')
1003 (func
1028 (func
1004 ('symbol', 'max')
1029 ('symbol', 'max')
1005 ('string', '$1')))
1030 ('string', '$1')))
1006 abort: unknown revision '$1'!
1031 abort: unknown revision '$1'!
1007 [255]
1032 [255]
1008
1033
1009 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
1034 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
1010 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
1035 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
1011 $ try 'callinjection2(2:5)'
1036 $ try 'callinjection2(2:5)'
1012 (func
1037 (func
1013 ('symbol', 'callinjection2')
1038 ('symbol', 'callinjection2')
1014 (range
1039 (range
1015 ('symbol', '2')
1040 ('symbol', '2')
1016 ('symbol', '5')))
1041 ('symbol', '5')))
1017 abort: failed to parse the definition of revset alias "injectparamasstring2": not a function: _aliasarg
1042 abort: failed to parse the definition of revset alias "injectparamasstring2": not a function: _aliasarg
1018 [255]
1043 [255]
1019 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1044 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1020 ('symbol', 'tip')
1045 ('symbol', 'tip')
1021 warning: failed to parse the definition of revset alias "anotherbadone": at 7: not a prefix: end
1046 warning: failed to parse the definition of revset alias "anotherbadone": at 7: not a prefix: end
1022 warning: failed to parse the definition of revset alias "injectparamasstring2": not a function: _aliasarg
1047 warning: failed to parse the definition of revset alias "injectparamasstring2": not a function: _aliasarg
1023 9
1048 9
1024 >>> data = file('.hg/hgrc', 'rb').read()
1049 >>> data = file('.hg/hgrc', 'rb').read()
1025 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
1050 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
1026
1051
1027 $ try 'tip'
1052 $ try 'tip'
1028 ('symbol', 'tip')
1053 ('symbol', 'tip')
1029 9
1054 9
1030
1055
1031 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1056 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1032 ('symbol', 'tip')
1057 ('symbol', 'tip')
1033 warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token
1058 warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token
1034 9
1059 9
1035 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1060 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1036 $ try 'strictreplacing("foo", tip)'
1061 $ try 'strictreplacing("foo", tip)'
1037 (func
1062 (func
1038 ('symbol', 'strictreplacing')
1063 ('symbol', 'strictreplacing')
1039 (list
1064 (list
1040 ('string', 'foo')
1065 ('string', 'foo')
1041 ('symbol', 'tip')))
1066 ('symbol', 'tip')))
1042 (or
1067 (or
1043 ('symbol', 'tip')
1068 ('symbol', 'tip')
1044 (func
1069 (func
1045 ('symbol', 'desc')
1070 ('symbol', 'desc')
1046 ('string', '$1')))
1071 ('string', '$1')))
1047 9
1072 9
1048
1073
1049 $ try 'd(2:5)'
1074 $ try 'd(2:5)'
1050 (func
1075 (func
1051 ('symbol', 'd')
1076 ('symbol', 'd')
1052 (range
1077 (range
1053 ('symbol', '2')
1078 ('symbol', '2')
1054 ('symbol', '5')))
1079 ('symbol', '5')))
1055 (func
1080 (func
1056 ('symbol', 'reverse')
1081 ('symbol', 'reverse')
1057 (func
1082 (func
1058 ('symbol', 'sort')
1083 ('symbol', 'sort')
1059 (list
1084 (list
1060 (range
1085 (range
1061 ('symbol', '2')
1086 ('symbol', '2')
1062 ('symbol', '5'))
1087 ('symbol', '5'))
1063 ('symbol', 'date'))))
1088 ('symbol', 'date'))))
1064 4
1089 4
1065 5
1090 5
1066 3
1091 3
1067 2
1092 2
1068 $ try 'rs(2 or 3, date)'
1093 $ try 'rs(2 or 3, date)'
1069 (func
1094 (func
1070 ('symbol', 'rs')
1095 ('symbol', 'rs')
1071 (list
1096 (list
1072 (or
1097 (or
1073 ('symbol', '2')
1098 ('symbol', '2')
1074 ('symbol', '3'))
1099 ('symbol', '3'))
1075 ('symbol', 'date')))
1100 ('symbol', 'date')))
1076 (func
1101 (func
1077 ('symbol', 'reverse')
1102 ('symbol', 'reverse')
1078 (func
1103 (func
1079 ('symbol', 'sort')
1104 ('symbol', 'sort')
1080 (list
1105 (list
1081 (or
1106 (or
1082 ('symbol', '2')
1107 ('symbol', '2')
1083 ('symbol', '3'))
1108 ('symbol', '3'))
1084 ('symbol', 'date'))))
1109 ('symbol', 'date'))))
1085 3
1110 3
1086 2
1111 2
1087 $ try 'rs()'
1112 $ try 'rs()'
1088 (func
1113 (func
1089 ('symbol', 'rs')
1114 ('symbol', 'rs')
1090 None)
1115 None)
1091 hg: parse error: invalid number of arguments: 0
1116 hg: parse error: invalid number of arguments: 0
1092 [255]
1117 [255]
1093 $ try 'rs(2)'
1118 $ try 'rs(2)'
1094 (func
1119 (func
1095 ('symbol', 'rs')
1120 ('symbol', 'rs')
1096 ('symbol', '2'))
1121 ('symbol', '2'))
1097 hg: parse error: invalid number of arguments: 1
1122 hg: parse error: invalid number of arguments: 1
1098 [255]
1123 [255]
1099 $ try 'rs(2, data, 7)'
1124 $ try 'rs(2, data, 7)'
1100 (func
1125 (func
1101 ('symbol', 'rs')
1126 ('symbol', 'rs')
1102 (list
1127 (list
1103 (list
1128 (list
1104 ('symbol', '2')
1129 ('symbol', '2')
1105 ('symbol', 'data'))
1130 ('symbol', 'data'))
1106 ('symbol', '7')))
1131 ('symbol', '7')))
1107 hg: parse error: invalid number of arguments: 3
1132 hg: parse error: invalid number of arguments: 3
1108 [255]
1133 [255]
1109 $ try 'rs4(2 or 3, x, x, date)'
1134 $ try 'rs4(2 or 3, x, x, date)'
1110 (func
1135 (func
1111 ('symbol', 'rs4')
1136 ('symbol', 'rs4')
1112 (list
1137 (list
1113 (list
1138 (list
1114 (list
1139 (list
1115 (or
1140 (or
1116 ('symbol', '2')
1141 ('symbol', '2')
1117 ('symbol', '3'))
1142 ('symbol', '3'))
1118 ('symbol', 'x'))
1143 ('symbol', 'x'))
1119 ('symbol', 'x'))
1144 ('symbol', 'x'))
1120 ('symbol', 'date')))
1145 ('symbol', 'date')))
1121 (func
1146 (func
1122 ('symbol', 'reverse')
1147 ('symbol', 'reverse')
1123 (func
1148 (func
1124 ('symbol', 'sort')
1149 ('symbol', 'sort')
1125 (list
1150 (list
1126 (or
1151 (or
1127 ('symbol', '2')
1152 ('symbol', '2')
1128 ('symbol', '3'))
1153 ('symbol', '3'))
1129 ('symbol', 'date'))))
1154 ('symbol', 'date'))))
1130 3
1155 3
1131 2
1156 2
1132
1157
1133 issue4553: check that revset aliases override existing hash prefix
1158 issue4553: check that revset aliases override existing hash prefix
1134
1159
1135 $ hg log -qr e
1160 $ hg log -qr e
1136 6:e0cc66ef77e8
1161 6:e0cc66ef77e8
1137
1162
1138 $ hg log -qr e --config revsetalias.e="all()"
1163 $ hg log -qr e --config revsetalias.e="all()"
1139 0:2785f51eece5
1164 0:2785f51eece5
1140 1:d75937da8da0
1165 1:d75937da8da0
1141 2:5ed5505e9f1c
1166 2:5ed5505e9f1c
1142 3:8528aa5637f2
1167 3:8528aa5637f2
1143 4:2326846efdab
1168 4:2326846efdab
1144 5:904fa392b941
1169 5:904fa392b941
1145 6:e0cc66ef77e8
1170 6:e0cc66ef77e8
1146 7:013af1973af4
1171 7:013af1973af4
1147 8:d5d0dcbdc4d9
1172 8:d5d0dcbdc4d9
1148 9:24286f4ae135
1173 9:24286f4ae135
1149
1174
1150 $ hg log -qr e: --config revsetalias.e="0"
1175 $ hg log -qr e: --config revsetalias.e="0"
1151 0:2785f51eece5
1176 0:2785f51eece5
1152 1:d75937da8da0
1177 1:d75937da8da0
1153 2:5ed5505e9f1c
1178 2:5ed5505e9f1c
1154 3:8528aa5637f2
1179 3:8528aa5637f2
1155 4:2326846efdab
1180 4:2326846efdab
1156 5:904fa392b941
1181 5:904fa392b941
1157 6:e0cc66ef77e8
1182 6:e0cc66ef77e8
1158 7:013af1973af4
1183 7:013af1973af4
1159 8:d5d0dcbdc4d9
1184 8:d5d0dcbdc4d9
1160 9:24286f4ae135
1185 9:24286f4ae135
1161
1186
1162 $ hg log -qr :e --config revsetalias.e="9"
1187 $ hg log -qr :e --config revsetalias.e="9"
1163 0:2785f51eece5
1188 0:2785f51eece5
1164 1:d75937da8da0
1189 1:d75937da8da0
1165 2:5ed5505e9f1c
1190 2:5ed5505e9f1c
1166 3:8528aa5637f2
1191 3:8528aa5637f2
1167 4:2326846efdab
1192 4:2326846efdab
1168 5:904fa392b941
1193 5:904fa392b941
1169 6:e0cc66ef77e8
1194 6:e0cc66ef77e8
1170 7:013af1973af4
1195 7:013af1973af4
1171 8:d5d0dcbdc4d9
1196 8:d5d0dcbdc4d9
1172 9:24286f4ae135
1197 9:24286f4ae135
1173
1198
1174 $ hg log -qr e:
1199 $ hg log -qr e:
1175 6:e0cc66ef77e8
1200 6:e0cc66ef77e8
1176 7:013af1973af4
1201 7:013af1973af4
1177 8:d5d0dcbdc4d9
1202 8:d5d0dcbdc4d9
1178 9:24286f4ae135
1203 9:24286f4ae135
1179
1204
1180 $ hg log -qr :e
1205 $ hg log -qr :e
1181 0:2785f51eece5
1206 0:2785f51eece5
1182 1:d75937da8da0
1207 1:d75937da8da0
1183 2:5ed5505e9f1c
1208 2:5ed5505e9f1c
1184 3:8528aa5637f2
1209 3:8528aa5637f2
1185 4:2326846efdab
1210 4:2326846efdab
1186 5:904fa392b941
1211 5:904fa392b941
1187 6:e0cc66ef77e8
1212 6:e0cc66ef77e8
1188
1213
1189 issue2549 - correct optimizations
1214 issue2549 - correct optimizations
1190
1215
1191 $ log 'limit(1 or 2 or 3, 2) and not 2'
1216 $ log 'limit(1 or 2 or 3, 2) and not 2'
1192 1
1217 1
1193 $ log 'max(1 or 2) and not 2'
1218 $ log 'max(1 or 2) and not 2'
1194 $ log 'min(1 or 2) and not 1'
1219 $ log 'min(1 or 2) and not 1'
1195 $ log 'last(1 or 2, 1) and not 2'
1220 $ log 'last(1 or 2, 1) and not 2'
1196
1221
1197 issue4289 - ordering of built-ins
1222 issue4289 - ordering of built-ins
1198 $ hg log -M -q -r 3:2
1223 $ hg log -M -q -r 3:2
1199 3:8528aa5637f2
1224 3:8528aa5637f2
1200 2:5ed5505e9f1c
1225 2:5ed5505e9f1c
1201
1226
1202 test revsets started with 40-chars hash (issue3669)
1227 test revsets started with 40-chars hash (issue3669)
1203
1228
1204 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1229 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1205 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1230 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1206 9
1231 9
1207 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1232 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1208 8
1233 8
1209
1234
1210 test or-ed indirect predicates (issue3775)
1235 test or-ed indirect predicates (issue3775)
1211
1236
1212 $ log '6 or 6^1' | sort
1237 $ log '6 or 6^1' | sort
1213 5
1238 5
1214 6
1239 6
1215 $ log '6^1 or 6' | sort
1240 $ log '6^1 or 6' | sort
1216 5
1241 5
1217 6
1242 6
1218 $ log '4 or 4~1' | sort
1243 $ log '4 or 4~1' | sort
1219 2
1244 2
1220 4
1245 4
1221 $ log '4~1 or 4' | sort
1246 $ log '4~1 or 4' | sort
1222 2
1247 2
1223 4
1248 4
1224 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1249 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1225 0
1250 0
1226 1
1251 1
1227 2
1252 2
1228 3
1253 3
1229 4
1254 4
1230 5
1255 5
1231 6
1256 6
1232 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1257 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1233 0
1258 0
1234 1
1259 1
1235 2
1260 2
1236 3
1261 3
1237 4
1262 4
1238 5
1263 5
1239 6
1264 6
1240
1265
1241 tests for 'remote()' predicate:
1266 tests for 'remote()' predicate:
1242 #. (csets in remote) (id) (remote)
1267 #. (csets in remote) (id) (remote)
1243 1. less than local current branch "default"
1268 1. less than local current branch "default"
1244 2. same with local specified "default"
1269 2. same with local specified "default"
1245 3. more than local specified specified
1270 3. more than local specified specified
1246
1271
1247 $ hg clone --quiet -U . ../remote3
1272 $ hg clone --quiet -U . ../remote3
1248 $ cd ../remote3
1273 $ cd ../remote3
1249 $ hg update -q 7
1274 $ hg update -q 7
1250 $ echo r > r
1275 $ echo r > r
1251 $ hg ci -Aqm 10
1276 $ hg ci -Aqm 10
1252 $ log 'remote()'
1277 $ log 'remote()'
1253 7
1278 7
1254 $ log 'remote("a-b-c-")'
1279 $ log 'remote("a-b-c-")'
1255 2
1280 2
1256 $ cd ../repo
1281 $ cd ../repo
1257 $ log 'remote(".a.b.c.", "../remote3")'
1282 $ log 'remote(".a.b.c.", "../remote3")'
1258
1283
1259 tests for concatenation of strings/symbols by "##"
1284 tests for concatenation of strings/symbols by "##"
1260
1285
1261 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
1286 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
1262 (_concat
1287 (_concat
1263 (_concat
1288 (_concat
1264 (_concat
1289 (_concat
1265 ('symbol', '278')
1290 ('symbol', '278')
1266 ('string', '5f5'))
1291 ('string', '5f5'))
1267 ('symbol', '1ee'))
1292 ('symbol', '1ee'))
1268 ('string', 'ce5'))
1293 ('string', 'ce5'))
1269 ('string', '2785f51eece5')
1294 ('string', '2785f51eece5')
1270 0
1295 0
1271
1296
1272 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
1297 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
1273 $ try "cat4(278, '5f5', 1ee, 'ce5')"
1298 $ try "cat4(278, '5f5', 1ee, 'ce5')"
1274 (func
1299 (func
1275 ('symbol', 'cat4')
1300 ('symbol', 'cat4')
1276 (list
1301 (list
1277 (list
1302 (list
1278 (list
1303 (list
1279 ('symbol', '278')
1304 ('symbol', '278')
1280 ('string', '5f5'))
1305 ('string', '5f5'))
1281 ('symbol', '1ee'))
1306 ('symbol', '1ee'))
1282 ('string', 'ce5')))
1307 ('string', 'ce5')))
1283 (_concat
1308 (_concat
1284 (_concat
1309 (_concat
1285 (_concat
1310 (_concat
1286 ('symbol', '278')
1311 ('symbol', '278')
1287 ('string', '5f5'))
1312 ('string', '5f5'))
1288 ('symbol', '1ee'))
1313 ('symbol', '1ee'))
1289 ('string', 'ce5'))
1314 ('string', 'ce5'))
1290 ('string', '2785f51eece5')
1315 ('string', '2785f51eece5')
1291 0
1316 0
1292
1317
1293 (check concatenation in alias nesting)
1318 (check concatenation in alias nesting)
1294
1319
1295 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
1320 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
1296 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
1321 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
1297 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
1322 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
1298 0
1323 0
1299
1324
1300 (check operator priority)
1325 (check operator priority)
1301
1326
1302 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
1327 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
1303 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
1328 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
1304 0
1329 0
1305 4
1330 4
1306
1331
1307 $ cd ..
1332 $ cd ..
1308
1333
1309 test author/desc/keyword in problematic encoding
1334 test author/desc/keyword in problematic encoding
1310 # unicode: cp932:
1335 # unicode: cp932:
1311 # u30A2 0x83 0x41(= 'A')
1336 # u30A2 0x83 0x41(= 'A')
1312 # u30C2 0x83 0x61(= 'a')
1337 # u30C2 0x83 0x61(= 'a')
1313
1338
1314 $ hg init problematicencoding
1339 $ hg init problematicencoding
1315 $ cd problematicencoding
1340 $ cd problematicencoding
1316
1341
1317 $ python > setup.sh <<EOF
1342 $ python > setup.sh <<EOF
1318 > print u'''
1343 > print u'''
1319 > echo a > text
1344 > echo a > text
1320 > hg add text
1345 > hg add text
1321 > hg --encoding utf-8 commit -u '\u30A2' -m none
1346 > hg --encoding utf-8 commit -u '\u30A2' -m none
1322 > echo b > text
1347 > echo b > text
1323 > hg --encoding utf-8 commit -u '\u30C2' -m none
1348 > hg --encoding utf-8 commit -u '\u30C2' -m none
1324 > echo c > text
1349 > echo c > text
1325 > hg --encoding utf-8 commit -u none -m '\u30A2'
1350 > hg --encoding utf-8 commit -u none -m '\u30A2'
1326 > echo d > text
1351 > echo d > text
1327 > hg --encoding utf-8 commit -u none -m '\u30C2'
1352 > hg --encoding utf-8 commit -u none -m '\u30C2'
1328 > '''.encode('utf-8')
1353 > '''.encode('utf-8')
1329 > EOF
1354 > EOF
1330 $ sh < setup.sh
1355 $ sh < setup.sh
1331
1356
1332 test in problematic encoding
1357 test in problematic encoding
1333 $ python > test.sh <<EOF
1358 $ python > test.sh <<EOF
1334 > print u'''
1359 > print u'''
1335 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
1360 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
1336 > echo ====
1361 > echo ====
1337 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
1362 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
1338 > echo ====
1363 > echo ====
1339 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
1364 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
1340 > echo ====
1365 > echo ====
1341 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
1366 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
1342 > echo ====
1367 > echo ====
1343 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
1368 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
1344 > echo ====
1369 > echo ====
1345 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
1370 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
1346 > '''.encode('cp932')
1371 > '''.encode('cp932')
1347 > EOF
1372 > EOF
1348 $ sh < test.sh
1373 $ sh < test.sh
1349 0
1374 0
1350 ====
1375 ====
1351 1
1376 1
1352 ====
1377 ====
1353 2
1378 2
1354 ====
1379 ====
1355 3
1380 3
1356 ====
1381 ====
1357 0
1382 0
1358 2
1383 2
1359 ====
1384 ====
1360 1
1385 1
1361 3
1386 3
1362
1387
1363 $ cd ..
1388 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now