##// END OF EJS Templates
revset: support "follow(renamed.py, e22f4f3f06c3)" (issue5334)...
Gábor Stefanik -
r29814:cbf9984a default
parent child Browse files
Show More
@@ -1,3679 +1,3687
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 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import heapq
10 import heapq
11 import re
11 import re
12
12
13 from .i18n import _
13 from .i18n import _
14 from . import (
14 from . import (
15 destutil,
15 destutil,
16 encoding,
16 encoding,
17 error,
17 error,
18 hbisect,
18 hbisect,
19 match as matchmod,
19 match as matchmod,
20 node,
20 node,
21 obsolete as obsmod,
21 obsolete as obsmod,
22 parser,
22 parser,
23 pathutil,
23 pathutil,
24 phases,
24 phases,
25 registrar,
25 registrar,
26 repoview,
26 repoview,
27 util,
27 util,
28 )
28 )
29
29
30 def _revancestors(repo, revs, followfirst):
30 def _revancestors(repo, revs, followfirst):
31 """Like revlog.ancestors(), but supports followfirst."""
31 """Like revlog.ancestors(), but supports followfirst."""
32 if followfirst:
32 if followfirst:
33 cut = 1
33 cut = 1
34 else:
34 else:
35 cut = None
35 cut = None
36 cl = repo.changelog
36 cl = repo.changelog
37
37
38 def iterate():
38 def iterate():
39 revs.sort(reverse=True)
39 revs.sort(reverse=True)
40 irevs = iter(revs)
40 irevs = iter(revs)
41 h = []
41 h = []
42
42
43 inputrev = next(irevs, None)
43 inputrev = next(irevs, None)
44 if inputrev is not None:
44 if inputrev is not None:
45 heapq.heappush(h, -inputrev)
45 heapq.heappush(h, -inputrev)
46
46
47 seen = set()
47 seen = set()
48 while h:
48 while h:
49 current = -heapq.heappop(h)
49 current = -heapq.heappop(h)
50 if current == inputrev:
50 if current == inputrev:
51 inputrev = next(irevs, None)
51 inputrev = next(irevs, None)
52 if inputrev is not None:
52 if inputrev is not None:
53 heapq.heappush(h, -inputrev)
53 heapq.heappush(h, -inputrev)
54 if current not in seen:
54 if current not in seen:
55 seen.add(current)
55 seen.add(current)
56 yield current
56 yield current
57 for parent in cl.parentrevs(current)[:cut]:
57 for parent in cl.parentrevs(current)[:cut]:
58 if parent != node.nullrev:
58 if parent != node.nullrev:
59 heapq.heappush(h, -parent)
59 heapq.heappush(h, -parent)
60
60
61 return generatorset(iterate(), iterasc=False)
61 return generatorset(iterate(), iterasc=False)
62
62
63 def _revdescendants(repo, revs, followfirst):
63 def _revdescendants(repo, revs, followfirst):
64 """Like revlog.descendants() but supports followfirst."""
64 """Like revlog.descendants() but supports followfirst."""
65 if followfirst:
65 if followfirst:
66 cut = 1
66 cut = 1
67 else:
67 else:
68 cut = None
68 cut = None
69
69
70 def iterate():
70 def iterate():
71 cl = repo.changelog
71 cl = repo.changelog
72 # XXX this should be 'parentset.min()' assuming 'parentset' is a
72 # XXX this should be 'parentset.min()' assuming 'parentset' is a
73 # smartset (and if it is not, it should.)
73 # smartset (and if it is not, it should.)
74 first = min(revs)
74 first = min(revs)
75 nullrev = node.nullrev
75 nullrev = node.nullrev
76 if first == nullrev:
76 if first == nullrev:
77 # Are there nodes with a null first parent and a non-null
77 # Are there nodes with a null first parent and a non-null
78 # second one? Maybe. Do we care? Probably not.
78 # second one? Maybe. Do we care? Probably not.
79 for i in cl:
79 for i in cl:
80 yield i
80 yield i
81 else:
81 else:
82 seen = set(revs)
82 seen = set(revs)
83 for i in cl.revs(first + 1):
83 for i in cl.revs(first + 1):
84 for x in cl.parentrevs(i)[:cut]:
84 for x in cl.parentrevs(i)[:cut]:
85 if x != nullrev and x in seen:
85 if x != nullrev and x in seen:
86 seen.add(i)
86 seen.add(i)
87 yield i
87 yield i
88 break
88 break
89
89
90 return generatorset(iterate(), iterasc=True)
90 return generatorset(iterate(), iterasc=True)
91
91
92 def _reachablerootspure(repo, minroot, roots, heads, includepath):
92 def _reachablerootspure(repo, minroot, roots, heads, includepath):
93 """return (heads(::<roots> and ::<heads>))
93 """return (heads(::<roots> and ::<heads>))
94
94
95 If includepath is True, return (<roots>::<heads>)."""
95 If includepath is True, return (<roots>::<heads>)."""
96 if not roots:
96 if not roots:
97 return []
97 return []
98 parentrevs = repo.changelog.parentrevs
98 parentrevs = repo.changelog.parentrevs
99 roots = set(roots)
99 roots = set(roots)
100 visit = list(heads)
100 visit = list(heads)
101 reachable = set()
101 reachable = set()
102 seen = {}
102 seen = {}
103 # prefetch all the things! (because python is slow)
103 # prefetch all the things! (because python is slow)
104 reached = reachable.add
104 reached = reachable.add
105 dovisit = visit.append
105 dovisit = visit.append
106 nextvisit = visit.pop
106 nextvisit = visit.pop
107 # open-code the post-order traversal due to the tiny size of
107 # open-code the post-order traversal due to the tiny size of
108 # sys.getrecursionlimit()
108 # sys.getrecursionlimit()
109 while visit:
109 while visit:
110 rev = nextvisit()
110 rev = nextvisit()
111 if rev in roots:
111 if rev in roots:
112 reached(rev)
112 reached(rev)
113 if not includepath:
113 if not includepath:
114 continue
114 continue
115 parents = parentrevs(rev)
115 parents = parentrevs(rev)
116 seen[rev] = parents
116 seen[rev] = parents
117 for parent in parents:
117 for parent in parents:
118 if parent >= minroot and parent not in seen:
118 if parent >= minroot and parent not in seen:
119 dovisit(parent)
119 dovisit(parent)
120 if not reachable:
120 if not reachable:
121 return baseset()
121 return baseset()
122 if not includepath:
122 if not includepath:
123 return reachable
123 return reachable
124 for rev in sorted(seen):
124 for rev in sorted(seen):
125 for parent in seen[rev]:
125 for parent in seen[rev]:
126 if parent in reachable:
126 if parent in reachable:
127 reached(rev)
127 reached(rev)
128 return reachable
128 return reachable
129
129
130 def reachableroots(repo, roots, heads, includepath=False):
130 def reachableroots(repo, roots, heads, includepath=False):
131 """return (heads(::<roots> and ::<heads>))
131 """return (heads(::<roots> and ::<heads>))
132
132
133 If includepath is True, return (<roots>::<heads>)."""
133 If includepath is True, return (<roots>::<heads>)."""
134 if not roots:
134 if not roots:
135 return baseset()
135 return baseset()
136 minroot = roots.min()
136 minroot = roots.min()
137 roots = list(roots)
137 roots = list(roots)
138 heads = list(heads)
138 heads = list(heads)
139 try:
139 try:
140 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
140 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
141 except AttributeError:
141 except AttributeError:
142 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
142 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
143 revs = baseset(revs)
143 revs = baseset(revs)
144 revs.sort()
144 revs.sort()
145 return revs
145 return revs
146
146
147 elements = {
147 elements = {
148 # token-type: binding-strength, primary, prefix, infix, suffix
148 # token-type: binding-strength, primary, prefix, infix, suffix
149 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
149 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
150 "##": (20, None, None, ("_concat", 20), None),
150 "##": (20, None, None, ("_concat", 20), None),
151 "~": (18, None, None, ("ancestor", 18), None),
151 "~": (18, None, None, ("ancestor", 18), None),
152 "^": (18, None, None, ("parent", 18), "parentpost"),
152 "^": (18, None, None, ("parent", 18), "parentpost"),
153 "-": (5, None, ("negate", 19), ("minus", 5), None),
153 "-": (5, None, ("negate", 19), ("minus", 5), None),
154 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
154 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
155 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
155 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
156 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"),
156 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"),
157 "not": (10, None, ("not", 10), None, None),
157 "not": (10, None, ("not", 10), None, None),
158 "!": (10, None, ("not", 10), None, None),
158 "!": (10, None, ("not", 10), None, None),
159 "and": (5, None, None, ("and", 5), None),
159 "and": (5, None, None, ("and", 5), None),
160 "&": (5, None, None, ("and", 5), None),
160 "&": (5, None, None, ("and", 5), None),
161 "%": (5, None, None, ("only", 5), "onlypost"),
161 "%": (5, None, None, ("only", 5), "onlypost"),
162 "or": (4, None, None, ("or", 4), None),
162 "or": (4, None, None, ("or", 4), None),
163 "|": (4, None, None, ("or", 4), None),
163 "|": (4, None, None, ("or", 4), None),
164 "+": (4, None, None, ("or", 4), None),
164 "+": (4, None, None, ("or", 4), None),
165 "=": (3, None, None, ("keyvalue", 3), None),
165 "=": (3, None, None, ("keyvalue", 3), None),
166 ",": (2, None, None, ("list", 2), None),
166 ",": (2, None, None, ("list", 2), None),
167 ")": (0, None, None, None, None),
167 ")": (0, None, None, None, None),
168 "symbol": (0, "symbol", None, None, None),
168 "symbol": (0, "symbol", None, None, None),
169 "string": (0, "string", None, None, None),
169 "string": (0, "string", None, None, None),
170 "end": (0, None, None, None, None),
170 "end": (0, None, None, None, None),
171 }
171 }
172
172
173 keywords = set(['and', 'or', 'not'])
173 keywords = set(['and', 'or', 'not'])
174
174
175 # default set of valid characters for the initial letter of symbols
175 # default set of valid characters for the initial letter of symbols
176 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
176 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
177 if c.isalnum() or c in '._@' or ord(c) > 127)
177 if c.isalnum() or c in '._@' or ord(c) > 127)
178
178
179 # default set of valid characters for non-initial letters of symbols
179 # default set of valid characters for non-initial letters of symbols
180 _symletters = set(c for c in [chr(i) for i in xrange(256)]
180 _symletters = set(c for c in [chr(i) for i in xrange(256)]
181 if c.isalnum() or c in '-._/@' or ord(c) > 127)
181 if c.isalnum() or c in '-._/@' or ord(c) > 127)
182
182
183 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
183 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
184 '''
184 '''
185 Parse a revset statement into a stream of tokens
185 Parse a revset statement into a stream of tokens
186
186
187 ``syminitletters`` is the set of valid characters for the initial
187 ``syminitletters`` is the set of valid characters for the initial
188 letter of symbols.
188 letter of symbols.
189
189
190 By default, character ``c`` is recognized as valid for initial
190 By default, character ``c`` is recognized as valid for initial
191 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
191 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
192
192
193 ``symletters`` is the set of valid characters for non-initial
193 ``symletters`` is the set of valid characters for non-initial
194 letters of symbols.
194 letters of symbols.
195
195
196 By default, character ``c`` is recognized as valid for non-initial
196 By default, character ``c`` is recognized as valid for non-initial
197 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
197 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
198
198
199 Check that @ is a valid unquoted token character (issue3686):
199 Check that @ is a valid unquoted token character (issue3686):
200 >>> list(tokenize("@::"))
200 >>> list(tokenize("@::"))
201 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
201 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
202
202
203 '''
203 '''
204 if syminitletters is None:
204 if syminitletters is None:
205 syminitletters = _syminitletters
205 syminitletters = _syminitletters
206 if symletters is None:
206 if symletters is None:
207 symletters = _symletters
207 symletters = _symletters
208
208
209 if program and lookup:
209 if program and lookup:
210 # attempt to parse old-style ranges first to deal with
210 # attempt to parse old-style ranges first to deal with
211 # things like old-tag which contain query metacharacters
211 # things like old-tag which contain query metacharacters
212 parts = program.split(':', 1)
212 parts = program.split(':', 1)
213 if all(lookup(sym) for sym in parts if sym):
213 if all(lookup(sym) for sym in parts if sym):
214 if parts[0]:
214 if parts[0]:
215 yield ('symbol', parts[0], 0)
215 yield ('symbol', parts[0], 0)
216 if len(parts) > 1:
216 if len(parts) > 1:
217 s = len(parts[0])
217 s = len(parts[0])
218 yield (':', None, s)
218 yield (':', None, s)
219 if parts[1]:
219 if parts[1]:
220 yield ('symbol', parts[1], s + 1)
220 yield ('symbol', parts[1], s + 1)
221 yield ('end', None, len(program))
221 yield ('end', None, len(program))
222 return
222 return
223
223
224 pos, l = 0, len(program)
224 pos, l = 0, len(program)
225 while pos < l:
225 while pos < l:
226 c = program[pos]
226 c = program[pos]
227 if c.isspace(): # skip inter-token whitespace
227 if c.isspace(): # skip inter-token whitespace
228 pass
228 pass
229 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
229 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
230 yield ('::', None, pos)
230 yield ('::', None, pos)
231 pos += 1 # skip ahead
231 pos += 1 # skip ahead
232 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
232 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
233 yield ('..', None, pos)
233 yield ('..', None, pos)
234 pos += 1 # skip ahead
234 pos += 1 # skip ahead
235 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
235 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
236 yield ('##', None, pos)
236 yield ('##', None, pos)
237 pos += 1 # skip ahead
237 pos += 1 # skip ahead
238 elif c in "():=,-|&+!~^%": # handle simple operators
238 elif c in "():=,-|&+!~^%": # handle simple operators
239 yield (c, None, pos)
239 yield (c, None, pos)
240 elif (c in '"\'' or c == 'r' and
240 elif (c in '"\'' or c == 'r' and
241 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
241 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
242 if c == 'r':
242 if c == 'r':
243 pos += 1
243 pos += 1
244 c = program[pos]
244 c = program[pos]
245 decode = lambda x: x
245 decode = lambda x: x
246 else:
246 else:
247 decode = parser.unescapestr
247 decode = parser.unescapestr
248 pos += 1
248 pos += 1
249 s = pos
249 s = pos
250 while pos < l: # find closing quote
250 while pos < l: # find closing quote
251 d = program[pos]
251 d = program[pos]
252 if d == '\\': # skip over escaped characters
252 if d == '\\': # skip over escaped characters
253 pos += 2
253 pos += 2
254 continue
254 continue
255 if d == c:
255 if d == c:
256 yield ('string', decode(program[s:pos]), s)
256 yield ('string', decode(program[s:pos]), s)
257 break
257 break
258 pos += 1
258 pos += 1
259 else:
259 else:
260 raise error.ParseError(_("unterminated string"), s)
260 raise error.ParseError(_("unterminated string"), s)
261 # gather up a symbol/keyword
261 # gather up a symbol/keyword
262 elif c in syminitletters:
262 elif c in syminitletters:
263 s = pos
263 s = pos
264 pos += 1
264 pos += 1
265 while pos < l: # find end of symbol
265 while pos < l: # find end of symbol
266 d = program[pos]
266 d = program[pos]
267 if d not in symletters:
267 if d not in symletters:
268 break
268 break
269 if d == '.' and program[pos - 1] == '.': # special case for ..
269 if d == '.' and program[pos - 1] == '.': # special case for ..
270 pos -= 1
270 pos -= 1
271 break
271 break
272 pos += 1
272 pos += 1
273 sym = program[s:pos]
273 sym = program[s:pos]
274 if sym in keywords: # operator keywords
274 if sym in keywords: # operator keywords
275 yield (sym, None, s)
275 yield (sym, None, s)
276 elif '-' in sym:
276 elif '-' in sym:
277 # some jerk gave us foo-bar-baz, try to check if it's a symbol
277 # some jerk gave us foo-bar-baz, try to check if it's a symbol
278 if lookup and lookup(sym):
278 if lookup and lookup(sym):
279 # looks like a real symbol
279 # looks like a real symbol
280 yield ('symbol', sym, s)
280 yield ('symbol', sym, s)
281 else:
281 else:
282 # looks like an expression
282 # looks like an expression
283 parts = sym.split('-')
283 parts = sym.split('-')
284 for p in parts[:-1]:
284 for p in parts[:-1]:
285 if p: # possible consecutive -
285 if p: # possible consecutive -
286 yield ('symbol', p, s)
286 yield ('symbol', p, s)
287 s += len(p)
287 s += len(p)
288 yield ('-', None, pos)
288 yield ('-', None, pos)
289 s += 1
289 s += 1
290 if parts[-1]: # possible trailing -
290 if parts[-1]: # possible trailing -
291 yield ('symbol', parts[-1], s)
291 yield ('symbol', parts[-1], s)
292 else:
292 else:
293 yield ('symbol', sym, s)
293 yield ('symbol', sym, s)
294 pos -= 1
294 pos -= 1
295 else:
295 else:
296 raise error.ParseError(_("syntax error in revset '%s'") %
296 raise error.ParseError(_("syntax error in revset '%s'") %
297 program, pos)
297 program, pos)
298 pos += 1
298 pos += 1
299 yield ('end', None, pos)
299 yield ('end', None, pos)
300
300
301 # helpers
301 # helpers
302
302
303 def getsymbol(x):
303 def getsymbol(x):
304 if x and x[0] == 'symbol':
304 if x and x[0] == 'symbol':
305 return x[1]
305 return x[1]
306 raise error.ParseError(_('not a symbol'))
306 raise error.ParseError(_('not a symbol'))
307
307
308 def getstring(x, err):
308 def getstring(x, err):
309 if x and (x[0] == 'string' or x[0] == 'symbol'):
309 if x and (x[0] == 'string' or x[0] == 'symbol'):
310 return x[1]
310 return x[1]
311 raise error.ParseError(err)
311 raise error.ParseError(err)
312
312
313 def getlist(x):
313 def getlist(x):
314 if not x:
314 if not x:
315 return []
315 return []
316 if x[0] == 'list':
316 if x[0] == 'list':
317 return list(x[1:])
317 return list(x[1:])
318 return [x]
318 return [x]
319
319
320 def getargs(x, min, max, err):
320 def getargs(x, min, max, err):
321 l = getlist(x)
321 l = getlist(x)
322 if len(l) < min or (max >= 0 and len(l) > max):
322 if len(l) < min or (max >= 0 and len(l) > max):
323 raise error.ParseError(err)
323 raise error.ParseError(err)
324 return l
324 return l
325
325
326 def getargsdict(x, funcname, keys):
326 def getargsdict(x, funcname, keys):
327 return parser.buildargsdict(getlist(x), funcname, keys.split(),
327 return parser.buildargsdict(getlist(x), funcname, keys.split(),
328 keyvaluenode='keyvalue', keynode='symbol')
328 keyvaluenode='keyvalue', keynode='symbol')
329
329
330 def getset(repo, subset, x):
330 def getset(repo, subset, x):
331 if not x:
331 if not x:
332 raise error.ParseError(_("missing argument"))
332 raise error.ParseError(_("missing argument"))
333 s = methods[x[0]](repo, subset, *x[1:])
333 s = methods[x[0]](repo, subset, *x[1:])
334 if util.safehasattr(s, 'isascending'):
334 if util.safehasattr(s, 'isascending'):
335 return s
335 return s
336 # else case should not happen, because all non-func are internal,
336 # else case should not happen, because all non-func are internal,
337 # ignoring for now.
337 # ignoring for now.
338 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
338 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
339 repo.ui.deprecwarn('revset "%s" uses list instead of smartset'
339 repo.ui.deprecwarn('revset "%s" uses list instead of smartset'
340 % x[1][1],
340 % x[1][1],
341 '3.9')
341 '3.9')
342 return baseset(s)
342 return baseset(s)
343
343
344 def _getrevsource(repo, r):
344 def _getrevsource(repo, r):
345 extra = repo[r].extra()
345 extra = repo[r].extra()
346 for label in ('source', 'transplant_source', 'rebase_source'):
346 for label in ('source', 'transplant_source', 'rebase_source'):
347 if label in extra:
347 if label in extra:
348 try:
348 try:
349 return repo[extra[label]].rev()
349 return repo[extra[label]].rev()
350 except error.RepoLookupError:
350 except error.RepoLookupError:
351 pass
351 pass
352 return None
352 return None
353
353
354 # operator methods
354 # operator methods
355
355
356 def stringset(repo, subset, x):
356 def stringset(repo, subset, x):
357 x = repo[x].rev()
357 x = repo[x].rev()
358 if (x in subset
358 if (x in subset
359 or x == node.nullrev and isinstance(subset, fullreposet)):
359 or x == node.nullrev and isinstance(subset, fullreposet)):
360 return baseset([x])
360 return baseset([x])
361 return baseset()
361 return baseset()
362
362
363 def rangeset(repo, subset, x, y):
363 def rangeset(repo, subset, x, y):
364 m = getset(repo, fullreposet(repo), x)
364 m = getset(repo, fullreposet(repo), x)
365 n = getset(repo, fullreposet(repo), y)
365 n = getset(repo, fullreposet(repo), y)
366
366
367 if not m or not n:
367 if not m or not n:
368 return baseset()
368 return baseset()
369 m, n = m.first(), n.last()
369 m, n = m.first(), n.last()
370
370
371 if m == n:
371 if m == n:
372 r = baseset([m])
372 r = baseset([m])
373 elif n == node.wdirrev:
373 elif n == node.wdirrev:
374 r = spanset(repo, m, len(repo)) + baseset([n])
374 r = spanset(repo, m, len(repo)) + baseset([n])
375 elif m == node.wdirrev:
375 elif m == node.wdirrev:
376 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
376 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
377 elif m < n:
377 elif m < n:
378 r = spanset(repo, m, n + 1)
378 r = spanset(repo, m, n + 1)
379 else:
379 else:
380 r = spanset(repo, m, n - 1)
380 r = spanset(repo, m, n - 1)
381 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
381 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
382 # necessary to ensure we preserve the order in subset.
382 # necessary to ensure we preserve the order in subset.
383 #
383 #
384 # This has performance implication, carrying the sorting over when possible
384 # This has performance implication, carrying the sorting over when possible
385 # would be more efficient.
385 # would be more efficient.
386 return r & subset
386 return r & subset
387
387
388 def dagrange(repo, subset, x, y):
388 def dagrange(repo, subset, x, y):
389 r = fullreposet(repo)
389 r = fullreposet(repo)
390 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
390 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
391 includepath=True)
391 includepath=True)
392 return subset & xs
392 return subset & xs
393
393
394 def andset(repo, subset, x, y):
394 def andset(repo, subset, x, y):
395 return getset(repo, getset(repo, subset, x), y)
395 return getset(repo, getset(repo, subset, x), y)
396
396
397 def differenceset(repo, subset, x, y):
397 def differenceset(repo, subset, x, y):
398 return getset(repo, subset, x) - getset(repo, subset, y)
398 return getset(repo, subset, x) - getset(repo, subset, y)
399
399
400 def orset(repo, subset, *xs):
400 def orset(repo, subset, *xs):
401 assert xs
401 assert xs
402 if len(xs) == 1:
402 if len(xs) == 1:
403 return getset(repo, subset, xs[0])
403 return getset(repo, subset, xs[0])
404 p = len(xs) // 2
404 p = len(xs) // 2
405 a = orset(repo, subset, *xs[:p])
405 a = orset(repo, subset, *xs[:p])
406 b = orset(repo, subset, *xs[p:])
406 b = orset(repo, subset, *xs[p:])
407 return a + b
407 return a + b
408
408
409 def notset(repo, subset, x):
409 def notset(repo, subset, x):
410 return subset - getset(repo, subset, x)
410 return subset - getset(repo, subset, x)
411
411
412 def listset(repo, subset, *xs):
412 def listset(repo, subset, *xs):
413 raise error.ParseError(_("can't use a list in this context"),
413 raise error.ParseError(_("can't use a list in this context"),
414 hint=_('see hg help "revsets.x or y"'))
414 hint=_('see hg help "revsets.x or y"'))
415
415
416 def keyvaluepair(repo, subset, k, v):
416 def keyvaluepair(repo, subset, k, v):
417 raise error.ParseError(_("can't use a key-value pair in this context"))
417 raise error.ParseError(_("can't use a key-value pair in this context"))
418
418
419 def func(repo, subset, a, b):
419 def func(repo, subset, a, b):
420 f = getsymbol(a)
420 f = getsymbol(a)
421 if f in symbols:
421 if f in symbols:
422 return symbols[f](repo, subset, b)
422 return symbols[f](repo, subset, b)
423
423
424 keep = lambda fn: getattr(fn, '__doc__', None) is not None
424 keep = lambda fn: getattr(fn, '__doc__', None) is not None
425
425
426 syms = [s for (s, fn) in symbols.items() if keep(fn)]
426 syms = [s for (s, fn) in symbols.items() if keep(fn)]
427 raise error.UnknownIdentifier(f, syms)
427 raise error.UnknownIdentifier(f, syms)
428
428
429 # functions
429 # functions
430
430
431 # symbols are callables like:
431 # symbols are callables like:
432 # fn(repo, subset, x)
432 # fn(repo, subset, x)
433 # with:
433 # with:
434 # repo - current repository instance
434 # repo - current repository instance
435 # subset - of revisions to be examined
435 # subset - of revisions to be examined
436 # x - argument in tree form
436 # x - argument in tree form
437 symbols = {}
437 symbols = {}
438
438
439 # symbols which can't be used for a DoS attack for any given input
439 # symbols which can't be used for a DoS attack for any given input
440 # (e.g. those which accept regexes as plain strings shouldn't be included)
440 # (e.g. those which accept regexes as plain strings shouldn't be included)
441 # functions that just return a lot of changesets (like all) don't count here
441 # functions that just return a lot of changesets (like all) don't count here
442 safesymbols = set()
442 safesymbols = set()
443
443
444 predicate = registrar.revsetpredicate()
444 predicate = registrar.revsetpredicate()
445
445
446 @predicate('_destupdate')
446 @predicate('_destupdate')
447 def _destupdate(repo, subset, x):
447 def _destupdate(repo, subset, x):
448 # experimental revset for update destination
448 # experimental revset for update destination
449 args = getargsdict(x, 'limit', 'clean check')
449 args = getargsdict(x, 'limit', 'clean check')
450 return subset & baseset([destutil.destupdate(repo, **args)[0]])
450 return subset & baseset([destutil.destupdate(repo, **args)[0]])
451
451
452 @predicate('_destmerge')
452 @predicate('_destmerge')
453 def _destmerge(repo, subset, x):
453 def _destmerge(repo, subset, x):
454 # experimental revset for merge destination
454 # experimental revset for merge destination
455 sourceset = None
455 sourceset = None
456 if x is not None:
456 if x is not None:
457 sourceset = getset(repo, fullreposet(repo), x)
457 sourceset = getset(repo, fullreposet(repo), x)
458 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
458 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
459
459
460 @predicate('adds(pattern)', safe=True)
460 @predicate('adds(pattern)', safe=True)
461 def adds(repo, subset, x):
461 def adds(repo, subset, x):
462 """Changesets that add a file matching pattern.
462 """Changesets that add a file matching pattern.
463
463
464 The pattern without explicit kind like ``glob:`` is expected to be
464 The pattern without explicit kind like ``glob:`` is expected to be
465 relative to the current directory and match against a file or a
465 relative to the current directory and match against a file or a
466 directory.
466 directory.
467 """
467 """
468 # i18n: "adds" is a keyword
468 # i18n: "adds" is a keyword
469 pat = getstring(x, _("adds requires a pattern"))
469 pat = getstring(x, _("adds requires a pattern"))
470 return checkstatus(repo, subset, pat, 1)
470 return checkstatus(repo, subset, pat, 1)
471
471
472 @predicate('ancestor(*changeset)', safe=True)
472 @predicate('ancestor(*changeset)', safe=True)
473 def ancestor(repo, subset, x):
473 def ancestor(repo, subset, x):
474 """A greatest common ancestor of the changesets.
474 """A greatest common ancestor of the changesets.
475
475
476 Accepts 0 or more changesets.
476 Accepts 0 or more changesets.
477 Will return empty list when passed no args.
477 Will return empty list when passed no args.
478 Greatest common ancestor of a single changeset is that changeset.
478 Greatest common ancestor of a single changeset is that changeset.
479 """
479 """
480 # i18n: "ancestor" is a keyword
480 # i18n: "ancestor" is a keyword
481 l = getlist(x)
481 l = getlist(x)
482 rl = fullreposet(repo)
482 rl = fullreposet(repo)
483 anc = None
483 anc = None
484
484
485 # (getset(repo, rl, i) for i in l) generates a list of lists
485 # (getset(repo, rl, i) for i in l) generates a list of lists
486 for revs in (getset(repo, rl, i) for i in l):
486 for revs in (getset(repo, rl, i) for i in l):
487 for r in revs:
487 for r in revs:
488 if anc is None:
488 if anc is None:
489 anc = repo[r]
489 anc = repo[r]
490 else:
490 else:
491 anc = anc.ancestor(repo[r])
491 anc = anc.ancestor(repo[r])
492
492
493 if anc is not None and anc.rev() in subset:
493 if anc is not None and anc.rev() in subset:
494 return baseset([anc.rev()])
494 return baseset([anc.rev()])
495 return baseset()
495 return baseset()
496
496
497 def _ancestors(repo, subset, x, followfirst=False):
497 def _ancestors(repo, subset, x, followfirst=False):
498 heads = getset(repo, fullreposet(repo), x)
498 heads = getset(repo, fullreposet(repo), x)
499 if not heads:
499 if not heads:
500 return baseset()
500 return baseset()
501 s = _revancestors(repo, heads, followfirst)
501 s = _revancestors(repo, heads, followfirst)
502 return subset & s
502 return subset & s
503
503
504 @predicate('ancestors(set)', safe=True)
504 @predicate('ancestors(set)', safe=True)
505 def ancestors(repo, subset, x):
505 def ancestors(repo, subset, x):
506 """Changesets that are ancestors of a changeset in set.
506 """Changesets that are ancestors of a changeset in set.
507 """
507 """
508 return _ancestors(repo, subset, x)
508 return _ancestors(repo, subset, x)
509
509
510 @predicate('_firstancestors', safe=True)
510 @predicate('_firstancestors', safe=True)
511 def _firstancestors(repo, subset, x):
511 def _firstancestors(repo, subset, x):
512 # ``_firstancestors(set)``
512 # ``_firstancestors(set)``
513 # Like ``ancestors(set)`` but follows only the first parents.
513 # Like ``ancestors(set)`` but follows only the first parents.
514 return _ancestors(repo, subset, x, followfirst=True)
514 return _ancestors(repo, subset, x, followfirst=True)
515
515
516 def ancestorspec(repo, subset, x, n):
516 def ancestorspec(repo, subset, x, n):
517 """``set~n``
517 """``set~n``
518 Changesets that are the Nth ancestor (first parents only) of a changeset
518 Changesets that are the Nth ancestor (first parents only) of a changeset
519 in set.
519 in set.
520 """
520 """
521 try:
521 try:
522 n = int(n[1])
522 n = int(n[1])
523 except (TypeError, ValueError):
523 except (TypeError, ValueError):
524 raise error.ParseError(_("~ expects a number"))
524 raise error.ParseError(_("~ expects a number"))
525 ps = set()
525 ps = set()
526 cl = repo.changelog
526 cl = repo.changelog
527 for r in getset(repo, fullreposet(repo), x):
527 for r in getset(repo, fullreposet(repo), x):
528 for i in range(n):
528 for i in range(n):
529 r = cl.parentrevs(r)[0]
529 r = cl.parentrevs(r)[0]
530 ps.add(r)
530 ps.add(r)
531 return subset & ps
531 return subset & ps
532
532
533 @predicate('author(string)', safe=True)
533 @predicate('author(string)', safe=True)
534 def author(repo, subset, x):
534 def author(repo, subset, x):
535 """Alias for ``user(string)``.
535 """Alias for ``user(string)``.
536 """
536 """
537 # i18n: "author" is a keyword
537 # i18n: "author" is a keyword
538 n = encoding.lower(getstring(x, _("author requires a string")))
538 n = encoding.lower(getstring(x, _("author requires a string")))
539 kind, pattern, matcher = _substringmatcher(n)
539 kind, pattern, matcher = _substringmatcher(n)
540 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())),
540 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())),
541 condrepr=('<user %r>', n))
541 condrepr=('<user %r>', n))
542
542
543 @predicate('bisect(string)', safe=True)
543 @predicate('bisect(string)', safe=True)
544 def bisect(repo, subset, x):
544 def bisect(repo, subset, x):
545 """Changesets marked in the specified bisect status:
545 """Changesets marked in the specified bisect status:
546
546
547 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
547 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
548 - ``goods``, ``bads`` : csets topologically good/bad
548 - ``goods``, ``bads`` : csets topologically good/bad
549 - ``range`` : csets taking part in the bisection
549 - ``range`` : csets taking part in the bisection
550 - ``pruned`` : csets that are goods, bads or skipped
550 - ``pruned`` : csets that are goods, bads or skipped
551 - ``untested`` : csets whose fate is yet unknown
551 - ``untested`` : csets whose fate is yet unknown
552 - ``ignored`` : csets ignored due to DAG topology
552 - ``ignored`` : csets ignored due to DAG topology
553 - ``current`` : the cset currently being bisected
553 - ``current`` : the cset currently being bisected
554 """
554 """
555 # i18n: "bisect" is a keyword
555 # i18n: "bisect" is a keyword
556 status = getstring(x, _("bisect requires a string")).lower()
556 status = getstring(x, _("bisect requires a string")).lower()
557 state = set(hbisect.get(repo, status))
557 state = set(hbisect.get(repo, status))
558 return subset & state
558 return subset & state
559
559
560 # Backward-compatibility
560 # Backward-compatibility
561 # - no help entry so that we do not advertise it any more
561 # - no help entry so that we do not advertise it any more
562 @predicate('bisected', safe=True)
562 @predicate('bisected', safe=True)
563 def bisected(repo, subset, x):
563 def bisected(repo, subset, x):
564 return bisect(repo, subset, x)
564 return bisect(repo, subset, x)
565
565
566 @predicate('bookmark([name])', safe=True)
566 @predicate('bookmark([name])', safe=True)
567 def bookmark(repo, subset, x):
567 def bookmark(repo, subset, x):
568 """The named bookmark or all bookmarks.
568 """The named bookmark or all bookmarks.
569
569
570 If `name` starts with `re:`, the remainder of the name is treated as
570 If `name` starts with `re:`, the remainder of the name is treated as
571 a regular expression. To match a bookmark that actually starts with `re:`,
571 a regular expression. To match a bookmark that actually starts with `re:`,
572 use the prefix `literal:`.
572 use the prefix `literal:`.
573 """
573 """
574 # i18n: "bookmark" is a keyword
574 # i18n: "bookmark" is a keyword
575 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
575 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
576 if args:
576 if args:
577 bm = getstring(args[0],
577 bm = getstring(args[0],
578 # i18n: "bookmark" is a keyword
578 # i18n: "bookmark" is a keyword
579 _('the argument to bookmark must be a string'))
579 _('the argument to bookmark must be a string'))
580 kind, pattern, matcher = util.stringmatcher(bm)
580 kind, pattern, matcher = util.stringmatcher(bm)
581 bms = set()
581 bms = set()
582 if kind == 'literal':
582 if kind == 'literal':
583 bmrev = repo._bookmarks.get(pattern, None)
583 bmrev = repo._bookmarks.get(pattern, None)
584 if not bmrev:
584 if not bmrev:
585 raise error.RepoLookupError(_("bookmark '%s' does not exist")
585 raise error.RepoLookupError(_("bookmark '%s' does not exist")
586 % pattern)
586 % pattern)
587 bms.add(repo[bmrev].rev())
587 bms.add(repo[bmrev].rev())
588 else:
588 else:
589 matchrevs = set()
589 matchrevs = set()
590 for name, bmrev in repo._bookmarks.iteritems():
590 for name, bmrev in repo._bookmarks.iteritems():
591 if matcher(name):
591 if matcher(name):
592 matchrevs.add(bmrev)
592 matchrevs.add(bmrev)
593 if not matchrevs:
593 if not matchrevs:
594 raise error.RepoLookupError(_("no bookmarks exist"
594 raise error.RepoLookupError(_("no bookmarks exist"
595 " that match '%s'") % pattern)
595 " that match '%s'") % pattern)
596 for bmrev in matchrevs:
596 for bmrev in matchrevs:
597 bms.add(repo[bmrev].rev())
597 bms.add(repo[bmrev].rev())
598 else:
598 else:
599 bms = set([repo[r].rev()
599 bms = set([repo[r].rev()
600 for r in repo._bookmarks.values()])
600 for r in repo._bookmarks.values()])
601 bms -= set([node.nullrev])
601 bms -= set([node.nullrev])
602 return subset & bms
602 return subset & bms
603
603
604 @predicate('branch(string or set)', safe=True)
604 @predicate('branch(string or set)', safe=True)
605 def branch(repo, subset, x):
605 def branch(repo, subset, x):
606 """
606 """
607 All changesets belonging to the given branch or the branches of the given
607 All changesets belonging to the given branch or the branches of the given
608 changesets.
608 changesets.
609
609
610 If `string` starts with `re:`, the remainder of the name is treated as
610 If `string` starts with `re:`, the remainder of the name is treated as
611 a regular expression. To match a branch that actually starts with `re:`,
611 a regular expression. To match a branch that actually starts with `re:`,
612 use the prefix `literal:`.
612 use the prefix `literal:`.
613 """
613 """
614 getbi = repo.revbranchcache().branchinfo
614 getbi = repo.revbranchcache().branchinfo
615
615
616 try:
616 try:
617 b = getstring(x, '')
617 b = getstring(x, '')
618 except error.ParseError:
618 except error.ParseError:
619 # not a string, but another revspec, e.g. tip()
619 # not a string, but another revspec, e.g. tip()
620 pass
620 pass
621 else:
621 else:
622 kind, pattern, matcher = util.stringmatcher(b)
622 kind, pattern, matcher = util.stringmatcher(b)
623 if kind == 'literal':
623 if kind == 'literal':
624 # note: falls through to the revspec case if no branch with
624 # note: falls through to the revspec case if no branch with
625 # this name exists and pattern kind is not specified explicitly
625 # this name exists and pattern kind is not specified explicitly
626 if pattern in repo.branchmap():
626 if pattern in repo.branchmap():
627 return subset.filter(lambda r: matcher(getbi(r)[0]),
627 return subset.filter(lambda r: matcher(getbi(r)[0]),
628 condrepr=('<branch %r>', b))
628 condrepr=('<branch %r>', b))
629 if b.startswith('literal:'):
629 if b.startswith('literal:'):
630 raise error.RepoLookupError(_("branch '%s' does not exist")
630 raise error.RepoLookupError(_("branch '%s' does not exist")
631 % pattern)
631 % pattern)
632 else:
632 else:
633 return subset.filter(lambda r: matcher(getbi(r)[0]),
633 return subset.filter(lambda r: matcher(getbi(r)[0]),
634 condrepr=('<branch %r>', b))
634 condrepr=('<branch %r>', b))
635
635
636 s = getset(repo, fullreposet(repo), x)
636 s = getset(repo, fullreposet(repo), x)
637 b = set()
637 b = set()
638 for r in s:
638 for r in s:
639 b.add(getbi(r)[0])
639 b.add(getbi(r)[0])
640 c = s.__contains__
640 c = s.__contains__
641 return subset.filter(lambda r: c(r) or getbi(r)[0] in b,
641 return subset.filter(lambda r: c(r) or getbi(r)[0] in b,
642 condrepr=lambda: '<branch %r>' % sorted(b))
642 condrepr=lambda: '<branch %r>' % sorted(b))
643
643
644 @predicate('bumped()', safe=True)
644 @predicate('bumped()', safe=True)
645 def bumped(repo, subset, x):
645 def bumped(repo, subset, x):
646 """Mutable changesets marked as successors of public changesets.
646 """Mutable changesets marked as successors of public changesets.
647
647
648 Only non-public and non-obsolete changesets can be `bumped`.
648 Only non-public and non-obsolete changesets can be `bumped`.
649 """
649 """
650 # i18n: "bumped" is a keyword
650 # i18n: "bumped" is a keyword
651 getargs(x, 0, 0, _("bumped takes no arguments"))
651 getargs(x, 0, 0, _("bumped takes no arguments"))
652 bumped = obsmod.getrevs(repo, 'bumped')
652 bumped = obsmod.getrevs(repo, 'bumped')
653 return subset & bumped
653 return subset & bumped
654
654
655 @predicate('bundle()', safe=True)
655 @predicate('bundle()', safe=True)
656 def bundle(repo, subset, x):
656 def bundle(repo, subset, x):
657 """Changesets in the bundle.
657 """Changesets in the bundle.
658
658
659 Bundle must be specified by the -R option."""
659 Bundle must be specified by the -R option."""
660
660
661 try:
661 try:
662 bundlerevs = repo.changelog.bundlerevs
662 bundlerevs = repo.changelog.bundlerevs
663 except AttributeError:
663 except AttributeError:
664 raise error.Abort(_("no bundle provided - specify with -R"))
664 raise error.Abort(_("no bundle provided - specify with -R"))
665 return subset & bundlerevs
665 return subset & bundlerevs
666
666
667 def checkstatus(repo, subset, pat, field):
667 def checkstatus(repo, subset, pat, field):
668 hasset = matchmod.patkind(pat) == 'set'
668 hasset = matchmod.patkind(pat) == 'set'
669
669
670 mcache = [None]
670 mcache = [None]
671 def matches(x):
671 def matches(x):
672 c = repo[x]
672 c = repo[x]
673 if not mcache[0] or hasset:
673 if not mcache[0] or hasset:
674 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
674 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
675 m = mcache[0]
675 m = mcache[0]
676 fname = None
676 fname = None
677 if not m.anypats() and len(m.files()) == 1:
677 if not m.anypats() and len(m.files()) == 1:
678 fname = m.files()[0]
678 fname = m.files()[0]
679 if fname is not None:
679 if fname is not None:
680 if fname not in c.files():
680 if fname not in c.files():
681 return False
681 return False
682 else:
682 else:
683 for f in c.files():
683 for f in c.files():
684 if m(f):
684 if m(f):
685 break
685 break
686 else:
686 else:
687 return False
687 return False
688 files = repo.status(c.p1().node(), c.node())[field]
688 files = repo.status(c.p1().node(), c.node())[field]
689 if fname is not None:
689 if fname is not None:
690 if fname in files:
690 if fname in files:
691 return True
691 return True
692 else:
692 else:
693 for f in files:
693 for f in files:
694 if m(f):
694 if m(f):
695 return True
695 return True
696
696
697 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
697 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
698
698
699 def _children(repo, subset, parentset):
699 def _children(repo, subset, parentset):
700 if not parentset:
700 if not parentset:
701 return baseset()
701 return baseset()
702 cs = set()
702 cs = set()
703 pr = repo.changelog.parentrevs
703 pr = repo.changelog.parentrevs
704 minrev = parentset.min()
704 minrev = parentset.min()
705 for r in subset:
705 for r in subset:
706 if r <= minrev:
706 if r <= minrev:
707 continue
707 continue
708 for p in pr(r):
708 for p in pr(r):
709 if p in parentset:
709 if p in parentset:
710 cs.add(r)
710 cs.add(r)
711 return baseset(cs)
711 return baseset(cs)
712
712
713 @predicate('children(set)', safe=True)
713 @predicate('children(set)', safe=True)
714 def children(repo, subset, x):
714 def children(repo, subset, x):
715 """Child changesets of changesets in set.
715 """Child changesets of changesets in set.
716 """
716 """
717 s = getset(repo, fullreposet(repo), x)
717 s = getset(repo, fullreposet(repo), x)
718 cs = _children(repo, subset, s)
718 cs = _children(repo, subset, s)
719 return subset & cs
719 return subset & cs
720
720
721 @predicate('closed()', safe=True)
721 @predicate('closed()', safe=True)
722 def closed(repo, subset, x):
722 def closed(repo, subset, x):
723 """Changeset is closed.
723 """Changeset is closed.
724 """
724 """
725 # i18n: "closed" is a keyword
725 # i18n: "closed" is a keyword
726 getargs(x, 0, 0, _("closed takes no arguments"))
726 getargs(x, 0, 0, _("closed takes no arguments"))
727 return subset.filter(lambda r: repo[r].closesbranch(),
727 return subset.filter(lambda r: repo[r].closesbranch(),
728 condrepr='<branch closed>')
728 condrepr='<branch closed>')
729
729
730 @predicate('contains(pattern)')
730 @predicate('contains(pattern)')
731 def contains(repo, subset, x):
731 def contains(repo, subset, x):
732 """The revision's manifest contains a file matching pattern (but might not
732 """The revision's manifest contains a file matching pattern (but might not
733 modify it). See :hg:`help patterns` for information about file patterns.
733 modify it). See :hg:`help patterns` for information about file patterns.
734
734
735 The pattern without explicit kind like ``glob:`` is expected to be
735 The pattern without explicit kind like ``glob:`` is expected to be
736 relative to the current directory and match against a file exactly
736 relative to the current directory and match against a file exactly
737 for efficiency.
737 for efficiency.
738 """
738 """
739 # i18n: "contains" is a keyword
739 # i18n: "contains" is a keyword
740 pat = getstring(x, _("contains requires a pattern"))
740 pat = getstring(x, _("contains requires a pattern"))
741
741
742 def matches(x):
742 def matches(x):
743 if not matchmod.patkind(pat):
743 if not matchmod.patkind(pat):
744 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
744 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
745 if pats in repo[x]:
745 if pats in repo[x]:
746 return True
746 return True
747 else:
747 else:
748 c = repo[x]
748 c = repo[x]
749 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
749 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
750 for f in c.manifest():
750 for f in c.manifest():
751 if m(f):
751 if m(f):
752 return True
752 return True
753 return False
753 return False
754
754
755 return subset.filter(matches, condrepr=('<contains %r>', pat))
755 return subset.filter(matches, condrepr=('<contains %r>', pat))
756
756
757 @predicate('converted([id])', safe=True)
757 @predicate('converted([id])', safe=True)
758 def converted(repo, subset, x):
758 def converted(repo, subset, x):
759 """Changesets converted from the given identifier in the old repository if
759 """Changesets converted from the given identifier in the old repository if
760 present, or all converted changesets if no identifier is specified.
760 present, or all converted changesets if no identifier is specified.
761 """
761 """
762
762
763 # There is exactly no chance of resolving the revision, so do a simple
763 # There is exactly no chance of resolving the revision, so do a simple
764 # string compare and hope for the best
764 # string compare and hope for the best
765
765
766 rev = None
766 rev = None
767 # i18n: "converted" is a keyword
767 # i18n: "converted" is a keyword
768 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
768 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
769 if l:
769 if l:
770 # i18n: "converted" is a keyword
770 # i18n: "converted" is a keyword
771 rev = getstring(l[0], _('converted requires a revision'))
771 rev = getstring(l[0], _('converted requires a revision'))
772
772
773 def _matchvalue(r):
773 def _matchvalue(r):
774 source = repo[r].extra().get('convert_revision', None)
774 source = repo[r].extra().get('convert_revision', None)
775 return source is not None and (rev is None or source.startswith(rev))
775 return source is not None and (rev is None or source.startswith(rev))
776
776
777 return subset.filter(lambda r: _matchvalue(r),
777 return subset.filter(lambda r: _matchvalue(r),
778 condrepr=('<converted %r>', rev))
778 condrepr=('<converted %r>', rev))
779
779
780 @predicate('date(interval)', safe=True)
780 @predicate('date(interval)', safe=True)
781 def date(repo, subset, x):
781 def date(repo, subset, x):
782 """Changesets within the interval, see :hg:`help dates`.
782 """Changesets within the interval, see :hg:`help dates`.
783 """
783 """
784 # i18n: "date" is a keyword
784 # i18n: "date" is a keyword
785 ds = getstring(x, _("date requires a string"))
785 ds = getstring(x, _("date requires a string"))
786 dm = util.matchdate(ds)
786 dm = util.matchdate(ds)
787 return subset.filter(lambda x: dm(repo[x].date()[0]),
787 return subset.filter(lambda x: dm(repo[x].date()[0]),
788 condrepr=('<date %r>', ds))
788 condrepr=('<date %r>', ds))
789
789
790 @predicate('desc(string)', safe=True)
790 @predicate('desc(string)', safe=True)
791 def desc(repo, subset, x):
791 def desc(repo, subset, x):
792 """Search commit message for string. The match is case-insensitive.
792 """Search commit message for string. The match is case-insensitive.
793 """
793 """
794 # i18n: "desc" is a keyword
794 # i18n: "desc" is a keyword
795 ds = encoding.lower(getstring(x, _("desc requires a string")))
795 ds = encoding.lower(getstring(x, _("desc requires a string")))
796
796
797 def matches(x):
797 def matches(x):
798 c = repo[x]
798 c = repo[x]
799 return ds in encoding.lower(c.description())
799 return ds in encoding.lower(c.description())
800
800
801 return subset.filter(matches, condrepr=('<desc %r>', ds))
801 return subset.filter(matches, condrepr=('<desc %r>', ds))
802
802
803 def _descendants(repo, subset, x, followfirst=False):
803 def _descendants(repo, subset, x, followfirst=False):
804 roots = getset(repo, fullreposet(repo), x)
804 roots = getset(repo, fullreposet(repo), x)
805 if not roots:
805 if not roots:
806 return baseset()
806 return baseset()
807 s = _revdescendants(repo, roots, followfirst)
807 s = _revdescendants(repo, roots, followfirst)
808
808
809 # Both sets need to be ascending in order to lazily return the union
809 # Both sets need to be ascending in order to lazily return the union
810 # in the correct order.
810 # in the correct order.
811 base = subset & roots
811 base = subset & roots
812 desc = subset & s
812 desc = subset & s
813 result = base + desc
813 result = base + desc
814 if subset.isascending():
814 if subset.isascending():
815 result.sort()
815 result.sort()
816 elif subset.isdescending():
816 elif subset.isdescending():
817 result.sort(reverse=True)
817 result.sort(reverse=True)
818 else:
818 else:
819 result = subset & result
819 result = subset & result
820 return result
820 return result
821
821
822 @predicate('descendants(set)', safe=True)
822 @predicate('descendants(set)', safe=True)
823 def descendants(repo, subset, x):
823 def descendants(repo, subset, x):
824 """Changesets which are descendants of changesets in set.
824 """Changesets which are descendants of changesets in set.
825 """
825 """
826 return _descendants(repo, subset, x)
826 return _descendants(repo, subset, x)
827
827
828 @predicate('_firstdescendants', safe=True)
828 @predicate('_firstdescendants', safe=True)
829 def _firstdescendants(repo, subset, x):
829 def _firstdescendants(repo, subset, x):
830 # ``_firstdescendants(set)``
830 # ``_firstdescendants(set)``
831 # Like ``descendants(set)`` but follows only the first parents.
831 # Like ``descendants(set)`` but follows only the first parents.
832 return _descendants(repo, subset, x, followfirst=True)
832 return _descendants(repo, subset, x, followfirst=True)
833
833
834 @predicate('destination([set])', safe=True)
834 @predicate('destination([set])', safe=True)
835 def destination(repo, subset, x):
835 def destination(repo, subset, x):
836 """Changesets that were created by a graft, transplant or rebase operation,
836 """Changesets that were created by a graft, transplant or rebase operation,
837 with the given revisions specified as the source. Omitting the optional set
837 with the given revisions specified as the source. Omitting the optional set
838 is the same as passing all().
838 is the same as passing all().
839 """
839 """
840 if x is not None:
840 if x is not None:
841 sources = getset(repo, fullreposet(repo), x)
841 sources = getset(repo, fullreposet(repo), x)
842 else:
842 else:
843 sources = fullreposet(repo)
843 sources = fullreposet(repo)
844
844
845 dests = set()
845 dests = set()
846
846
847 # subset contains all of the possible destinations that can be returned, so
847 # subset contains all of the possible destinations that can be returned, so
848 # iterate over them and see if their source(s) were provided in the arg set.
848 # iterate over them and see if their source(s) were provided in the arg set.
849 # Even if the immediate src of r is not in the arg set, src's source (or
849 # Even if the immediate src of r is not in the arg set, src's source (or
850 # further back) may be. Scanning back further than the immediate src allows
850 # further back) may be. Scanning back further than the immediate src allows
851 # transitive transplants and rebases to yield the same results as transitive
851 # transitive transplants and rebases to yield the same results as transitive
852 # grafts.
852 # grafts.
853 for r in subset:
853 for r in subset:
854 src = _getrevsource(repo, r)
854 src = _getrevsource(repo, r)
855 lineage = None
855 lineage = None
856
856
857 while src is not None:
857 while src is not None:
858 if lineage is None:
858 if lineage is None:
859 lineage = list()
859 lineage = list()
860
860
861 lineage.append(r)
861 lineage.append(r)
862
862
863 # The visited lineage is a match if the current source is in the arg
863 # The visited lineage is a match if the current source is in the arg
864 # set. Since every candidate dest is visited by way of iterating
864 # set. Since every candidate dest is visited by way of iterating
865 # subset, any dests further back in the lineage will be tested by a
865 # subset, any dests further back in the lineage will be tested by a
866 # different iteration over subset. Likewise, if the src was already
866 # different iteration over subset. Likewise, if the src was already
867 # selected, the current lineage can be selected without going back
867 # selected, the current lineage can be selected without going back
868 # further.
868 # further.
869 if src in sources or src in dests:
869 if src in sources or src in dests:
870 dests.update(lineage)
870 dests.update(lineage)
871 break
871 break
872
872
873 r = src
873 r = src
874 src = _getrevsource(repo, r)
874 src = _getrevsource(repo, r)
875
875
876 return subset.filter(dests.__contains__,
876 return subset.filter(dests.__contains__,
877 condrepr=lambda: '<destination %r>' % sorted(dests))
877 condrepr=lambda: '<destination %r>' % sorted(dests))
878
878
879 @predicate('divergent()', safe=True)
879 @predicate('divergent()', safe=True)
880 def divergent(repo, subset, x):
880 def divergent(repo, subset, x):
881 """
881 """
882 Final successors of changesets with an alternative set of final successors.
882 Final successors of changesets with an alternative set of final successors.
883 """
883 """
884 # i18n: "divergent" is a keyword
884 # i18n: "divergent" is a keyword
885 getargs(x, 0, 0, _("divergent takes no arguments"))
885 getargs(x, 0, 0, _("divergent takes no arguments"))
886 divergent = obsmod.getrevs(repo, 'divergent')
886 divergent = obsmod.getrevs(repo, 'divergent')
887 return subset & divergent
887 return subset & divergent
888
888
889 @predicate('extinct()', safe=True)
889 @predicate('extinct()', safe=True)
890 def extinct(repo, subset, x):
890 def extinct(repo, subset, x):
891 """Obsolete changesets with obsolete descendants only.
891 """Obsolete changesets with obsolete descendants only.
892 """
892 """
893 # i18n: "extinct" is a keyword
893 # i18n: "extinct" is a keyword
894 getargs(x, 0, 0, _("extinct takes no arguments"))
894 getargs(x, 0, 0, _("extinct takes no arguments"))
895 extincts = obsmod.getrevs(repo, 'extinct')
895 extincts = obsmod.getrevs(repo, 'extinct')
896 return subset & extincts
896 return subset & extincts
897
897
898 @predicate('extra(label, [value])', safe=True)
898 @predicate('extra(label, [value])', safe=True)
899 def extra(repo, subset, x):
899 def extra(repo, subset, x):
900 """Changesets with the given label in the extra metadata, with the given
900 """Changesets with the given label in the extra metadata, with the given
901 optional value.
901 optional value.
902
902
903 If `value` starts with `re:`, the remainder of the value is treated as
903 If `value` starts with `re:`, the remainder of the value is treated as
904 a regular expression. To match a value that actually starts with `re:`,
904 a regular expression. To match a value that actually starts with `re:`,
905 use the prefix `literal:`.
905 use the prefix `literal:`.
906 """
906 """
907 args = getargsdict(x, 'extra', 'label value')
907 args = getargsdict(x, 'extra', 'label value')
908 if 'label' not in args:
908 if 'label' not in args:
909 # i18n: "extra" is a keyword
909 # i18n: "extra" is a keyword
910 raise error.ParseError(_('extra takes at least 1 argument'))
910 raise error.ParseError(_('extra takes at least 1 argument'))
911 # i18n: "extra" is a keyword
911 # i18n: "extra" is a keyword
912 label = getstring(args['label'], _('first argument to extra must be '
912 label = getstring(args['label'], _('first argument to extra must be '
913 'a string'))
913 'a string'))
914 value = None
914 value = None
915
915
916 if 'value' in args:
916 if 'value' in args:
917 # i18n: "extra" is a keyword
917 # i18n: "extra" is a keyword
918 value = getstring(args['value'], _('second argument to extra must be '
918 value = getstring(args['value'], _('second argument to extra must be '
919 'a string'))
919 'a string'))
920 kind, value, matcher = util.stringmatcher(value)
920 kind, value, matcher = util.stringmatcher(value)
921
921
922 def _matchvalue(r):
922 def _matchvalue(r):
923 extra = repo[r].extra()
923 extra = repo[r].extra()
924 return label in extra and (value is None or matcher(extra[label]))
924 return label in extra and (value is None or matcher(extra[label]))
925
925
926 return subset.filter(lambda r: _matchvalue(r),
926 return subset.filter(lambda r: _matchvalue(r),
927 condrepr=('<extra[%r] %r>', label, value))
927 condrepr=('<extra[%r] %r>', label, value))
928
928
929 @predicate('filelog(pattern)', safe=True)
929 @predicate('filelog(pattern)', safe=True)
930 def filelog(repo, subset, x):
930 def filelog(repo, subset, x):
931 """Changesets connected to the specified filelog.
931 """Changesets connected to the specified filelog.
932
932
933 For performance reasons, visits only revisions mentioned in the file-level
933 For performance reasons, visits only revisions mentioned in the file-level
934 filelog, rather than filtering through all changesets (much faster, but
934 filelog, rather than filtering through all changesets (much faster, but
935 doesn't include deletes or duplicate changes). For a slower, more accurate
935 doesn't include deletes or duplicate changes). For a slower, more accurate
936 result, use ``file()``.
936 result, use ``file()``.
937
937
938 The pattern without explicit kind like ``glob:`` is expected to be
938 The pattern without explicit kind like ``glob:`` is expected to be
939 relative to the current directory and match against a file exactly
939 relative to the current directory and match against a file exactly
940 for efficiency.
940 for efficiency.
941
941
942 If some linkrev points to revisions filtered by the current repoview, we'll
942 If some linkrev points to revisions filtered by the current repoview, we'll
943 work around it to return a non-filtered value.
943 work around it to return a non-filtered value.
944 """
944 """
945
945
946 # i18n: "filelog" is a keyword
946 # i18n: "filelog" is a keyword
947 pat = getstring(x, _("filelog requires a pattern"))
947 pat = getstring(x, _("filelog requires a pattern"))
948 s = set()
948 s = set()
949 cl = repo.changelog
949 cl = repo.changelog
950
950
951 if not matchmod.patkind(pat):
951 if not matchmod.patkind(pat):
952 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
952 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
953 files = [f]
953 files = [f]
954 else:
954 else:
955 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
955 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
956 files = (f for f in repo[None] if m(f))
956 files = (f for f in repo[None] if m(f))
957
957
958 for f in files:
958 for f in files:
959 fl = repo.file(f)
959 fl = repo.file(f)
960 known = {}
960 known = {}
961 scanpos = 0
961 scanpos = 0
962 for fr in list(fl):
962 for fr in list(fl):
963 fn = fl.node(fr)
963 fn = fl.node(fr)
964 if fn in known:
964 if fn in known:
965 s.add(known[fn])
965 s.add(known[fn])
966 continue
966 continue
967
967
968 lr = fl.linkrev(fr)
968 lr = fl.linkrev(fr)
969 if lr in cl:
969 if lr in cl:
970 s.add(lr)
970 s.add(lr)
971 elif scanpos is not None:
971 elif scanpos is not None:
972 # lowest matching changeset is filtered, scan further
972 # lowest matching changeset is filtered, scan further
973 # ahead in changelog
973 # ahead in changelog
974 start = max(lr, scanpos) + 1
974 start = max(lr, scanpos) + 1
975 scanpos = None
975 scanpos = None
976 for r in cl.revs(start):
976 for r in cl.revs(start):
977 # minimize parsing of non-matching entries
977 # minimize parsing of non-matching entries
978 if f in cl.revision(r) and f in cl.readfiles(r):
978 if f in cl.revision(r) and f in cl.readfiles(r):
979 try:
979 try:
980 # try to use manifest delta fastpath
980 # try to use manifest delta fastpath
981 n = repo[r].filenode(f)
981 n = repo[r].filenode(f)
982 if n not in known:
982 if n not in known:
983 if n == fn:
983 if n == fn:
984 s.add(r)
984 s.add(r)
985 scanpos = r
985 scanpos = r
986 break
986 break
987 else:
987 else:
988 known[n] = r
988 known[n] = r
989 except error.ManifestLookupError:
989 except error.ManifestLookupError:
990 # deletion in changelog
990 # deletion in changelog
991 continue
991 continue
992
992
993 return subset & s
993 return subset & s
994
994
995 @predicate('first(set, [n])', safe=True)
995 @predicate('first(set, [n])', safe=True)
996 def first(repo, subset, x):
996 def first(repo, subset, x):
997 """An alias for limit().
997 """An alias for limit().
998 """
998 """
999 return limit(repo, subset, x)
999 return limit(repo, subset, x)
1000
1000
1001 def _follow(repo, subset, x, name, followfirst=False):
1001 def _follow(repo, subset, x, name, followfirst=False):
1002 l = getargs(x, 0, 1, _("%s takes no arguments or a pattern") % name)
1002 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
1003 "and an optional revset") % name)
1003 c = repo['.']
1004 c = repo['.']
1004 if l:
1005 if l:
1005 x = getstring(l[0], _("%s expected a pattern") % name)
1006 x = getstring(l[0], _("%s expected a pattern") % name)
1007 rev = None
1008 if len(l) >= 2:
1009 rev = getset(repo, fullreposet(repo), l[1]).last()
1010 if rev is None:
1011 raise error.RepoLookupError(
1012 _("%s: starting revision set cannot be empty") % name)
1013 c = repo[rev]
1006 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
1014 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
1007 ctx=repo[None], default='path')
1015 ctx=repo[rev], default='path')
1008
1016
1009 files = c.manifest().walk(matcher)
1017 files = c.manifest().walk(matcher)
1010
1018
1011 s = set()
1019 s = set()
1012 for fname in files:
1020 for fname in files:
1013 fctx = c[fname]
1021 fctx = c[fname]
1014 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
1022 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
1015 # include the revision responsible for the most recent version
1023 # include the revision responsible for the most recent version
1016 s.add(fctx.introrev())
1024 s.add(fctx.introrev())
1017 else:
1025 else:
1018 s = _revancestors(repo, baseset([c.rev()]), followfirst)
1026 s = _revancestors(repo, baseset([c.rev()]), followfirst)
1019
1027
1020 return subset & s
1028 return subset & s
1021
1029
1022 @predicate('follow([pattern])', safe=True)
1030 @predicate('follow([pattern[, startrev]])', safe=True)
1023 def follow(repo, subset, x):
1031 def follow(repo, subset, x):
1024 """
1032 """
1025 An alias for ``::.`` (ancestors of the working directory's first parent).
1033 An alias for ``::.`` (ancestors of the working directory's first parent).
1026 If pattern is specified, the histories of files matching given
1034 If pattern is specified, the histories of files matching given
1027 pattern is followed, including copies.
1035 pattern in the revision given by startrev are followed, including copies.
1028 """
1036 """
1029 return _follow(repo, subset, x, 'follow')
1037 return _follow(repo, subset, x, 'follow')
1030
1038
1031 @predicate('_followfirst', safe=True)
1039 @predicate('_followfirst', safe=True)
1032 def _followfirst(repo, subset, x):
1040 def _followfirst(repo, subset, x):
1033 # ``followfirst([pattern])``
1041 # ``followfirst([pattern[, startrev]])``
1034 # Like ``follow([pattern])`` but follows only the first parent of
1042 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
1035 # every revisions or files revisions.
1043 # of every revisions or files revisions.
1036 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1044 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1037
1045
1038 @predicate('all()', safe=True)
1046 @predicate('all()', safe=True)
1039 def getall(repo, subset, x):
1047 def getall(repo, subset, x):
1040 """All changesets, the same as ``0:tip``.
1048 """All changesets, the same as ``0:tip``.
1041 """
1049 """
1042 # i18n: "all" is a keyword
1050 # i18n: "all" is a keyword
1043 getargs(x, 0, 0, _("all takes no arguments"))
1051 getargs(x, 0, 0, _("all takes no arguments"))
1044 return subset & spanset(repo) # drop "null" if any
1052 return subset & spanset(repo) # drop "null" if any
1045
1053
1046 @predicate('grep(regex)')
1054 @predicate('grep(regex)')
1047 def grep(repo, subset, x):
1055 def grep(repo, subset, x):
1048 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1056 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1049 to ensure special escape characters are handled correctly. Unlike
1057 to ensure special escape characters are handled correctly. Unlike
1050 ``keyword(string)``, the match is case-sensitive.
1058 ``keyword(string)``, the match is case-sensitive.
1051 """
1059 """
1052 try:
1060 try:
1053 # i18n: "grep" is a keyword
1061 # i18n: "grep" is a keyword
1054 gr = re.compile(getstring(x, _("grep requires a string")))
1062 gr = re.compile(getstring(x, _("grep requires a string")))
1055 except re.error as e:
1063 except re.error as e:
1056 raise error.ParseError(_('invalid match pattern: %s') % e)
1064 raise error.ParseError(_('invalid match pattern: %s') % e)
1057
1065
1058 def matches(x):
1066 def matches(x):
1059 c = repo[x]
1067 c = repo[x]
1060 for e in c.files() + [c.user(), c.description()]:
1068 for e in c.files() + [c.user(), c.description()]:
1061 if gr.search(e):
1069 if gr.search(e):
1062 return True
1070 return True
1063 return False
1071 return False
1064
1072
1065 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1073 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1066
1074
1067 @predicate('_matchfiles', safe=True)
1075 @predicate('_matchfiles', safe=True)
1068 def _matchfiles(repo, subset, x):
1076 def _matchfiles(repo, subset, x):
1069 # _matchfiles takes a revset list of prefixed arguments:
1077 # _matchfiles takes a revset list of prefixed arguments:
1070 #
1078 #
1071 # [p:foo, i:bar, x:baz]
1079 # [p:foo, i:bar, x:baz]
1072 #
1080 #
1073 # builds a match object from them and filters subset. Allowed
1081 # builds a match object from them and filters subset. Allowed
1074 # prefixes are 'p:' for regular patterns, 'i:' for include
1082 # prefixes are 'p:' for regular patterns, 'i:' for include
1075 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1083 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1076 # a revision identifier, or the empty string to reference the
1084 # a revision identifier, or the empty string to reference the
1077 # working directory, from which the match object is
1085 # working directory, from which the match object is
1078 # initialized. Use 'd:' to set the default matching mode, default
1086 # initialized. Use 'd:' to set the default matching mode, default
1079 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1087 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1080
1088
1081 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1089 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1082 pats, inc, exc = [], [], []
1090 pats, inc, exc = [], [], []
1083 rev, default = None, None
1091 rev, default = None, None
1084 for arg in l:
1092 for arg in l:
1085 s = getstring(arg, "_matchfiles requires string arguments")
1093 s = getstring(arg, "_matchfiles requires string arguments")
1086 prefix, value = s[:2], s[2:]
1094 prefix, value = s[:2], s[2:]
1087 if prefix == 'p:':
1095 if prefix == 'p:':
1088 pats.append(value)
1096 pats.append(value)
1089 elif prefix == 'i:':
1097 elif prefix == 'i:':
1090 inc.append(value)
1098 inc.append(value)
1091 elif prefix == 'x:':
1099 elif prefix == 'x:':
1092 exc.append(value)
1100 exc.append(value)
1093 elif prefix == 'r:':
1101 elif prefix == 'r:':
1094 if rev is not None:
1102 if rev is not None:
1095 raise error.ParseError('_matchfiles expected at most one '
1103 raise error.ParseError('_matchfiles expected at most one '
1096 'revision')
1104 'revision')
1097 if value != '': # empty means working directory; leave rev as None
1105 if value != '': # empty means working directory; leave rev as None
1098 rev = value
1106 rev = value
1099 elif prefix == 'd:':
1107 elif prefix == 'd:':
1100 if default is not None:
1108 if default is not None:
1101 raise error.ParseError('_matchfiles expected at most one '
1109 raise error.ParseError('_matchfiles expected at most one '
1102 'default mode')
1110 'default mode')
1103 default = value
1111 default = value
1104 else:
1112 else:
1105 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1113 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1106 if not default:
1114 if not default:
1107 default = 'glob'
1115 default = 'glob'
1108
1116
1109 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1117 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1110 exclude=exc, ctx=repo[rev], default=default)
1118 exclude=exc, ctx=repo[rev], default=default)
1111
1119
1112 # This directly read the changelog data as creating changectx for all
1120 # This directly read the changelog data as creating changectx for all
1113 # revisions is quite expensive.
1121 # revisions is quite expensive.
1114 getfiles = repo.changelog.readfiles
1122 getfiles = repo.changelog.readfiles
1115 wdirrev = node.wdirrev
1123 wdirrev = node.wdirrev
1116 def matches(x):
1124 def matches(x):
1117 if x == wdirrev:
1125 if x == wdirrev:
1118 files = repo[x].files()
1126 files = repo[x].files()
1119 else:
1127 else:
1120 files = getfiles(x)
1128 files = getfiles(x)
1121 for f in files:
1129 for f in files:
1122 if m(f):
1130 if m(f):
1123 return True
1131 return True
1124 return False
1132 return False
1125
1133
1126 return subset.filter(matches,
1134 return subset.filter(matches,
1127 condrepr=('<matchfiles patterns=%r, include=%r '
1135 condrepr=('<matchfiles patterns=%r, include=%r '
1128 'exclude=%r, default=%r, rev=%r>',
1136 'exclude=%r, default=%r, rev=%r>',
1129 pats, inc, exc, default, rev))
1137 pats, inc, exc, default, rev))
1130
1138
1131 @predicate('file(pattern)', safe=True)
1139 @predicate('file(pattern)', safe=True)
1132 def hasfile(repo, subset, x):
1140 def hasfile(repo, subset, x):
1133 """Changesets affecting files matched by pattern.
1141 """Changesets affecting files matched by pattern.
1134
1142
1135 For a faster but less accurate result, consider using ``filelog()``
1143 For a faster but less accurate result, consider using ``filelog()``
1136 instead.
1144 instead.
1137
1145
1138 This predicate uses ``glob:`` as the default kind of pattern.
1146 This predicate uses ``glob:`` as the default kind of pattern.
1139 """
1147 """
1140 # i18n: "file" is a keyword
1148 # i18n: "file" is a keyword
1141 pat = getstring(x, _("file requires a pattern"))
1149 pat = getstring(x, _("file requires a pattern"))
1142 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1150 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1143
1151
1144 @predicate('head()', safe=True)
1152 @predicate('head()', safe=True)
1145 def head(repo, subset, x):
1153 def head(repo, subset, x):
1146 """Changeset is a named branch head.
1154 """Changeset is a named branch head.
1147 """
1155 """
1148 # i18n: "head" is a keyword
1156 # i18n: "head" is a keyword
1149 getargs(x, 0, 0, _("head takes no arguments"))
1157 getargs(x, 0, 0, _("head takes no arguments"))
1150 hs = set()
1158 hs = set()
1151 cl = repo.changelog
1159 cl = repo.changelog
1152 for ls in repo.branchmap().itervalues():
1160 for ls in repo.branchmap().itervalues():
1153 hs.update(cl.rev(h) for h in ls)
1161 hs.update(cl.rev(h) for h in ls)
1154 return subset & baseset(hs)
1162 return subset & baseset(hs)
1155
1163
1156 @predicate('heads(set)', safe=True)
1164 @predicate('heads(set)', safe=True)
1157 def heads(repo, subset, x):
1165 def heads(repo, subset, x):
1158 """Members of set with no children in set.
1166 """Members of set with no children in set.
1159 """
1167 """
1160 s = getset(repo, subset, x)
1168 s = getset(repo, subset, x)
1161 ps = parents(repo, subset, x)
1169 ps = parents(repo, subset, x)
1162 return s - ps
1170 return s - ps
1163
1171
1164 @predicate('hidden()', safe=True)
1172 @predicate('hidden()', safe=True)
1165 def hidden(repo, subset, x):
1173 def hidden(repo, subset, x):
1166 """Hidden changesets.
1174 """Hidden changesets.
1167 """
1175 """
1168 # i18n: "hidden" is a keyword
1176 # i18n: "hidden" is a keyword
1169 getargs(x, 0, 0, _("hidden takes no arguments"))
1177 getargs(x, 0, 0, _("hidden takes no arguments"))
1170 hiddenrevs = repoview.filterrevs(repo, 'visible')
1178 hiddenrevs = repoview.filterrevs(repo, 'visible')
1171 return subset & hiddenrevs
1179 return subset & hiddenrevs
1172
1180
1173 @predicate('keyword(string)', safe=True)
1181 @predicate('keyword(string)', safe=True)
1174 def keyword(repo, subset, x):
1182 def keyword(repo, subset, x):
1175 """Search commit message, user name, and names of changed files for
1183 """Search commit message, user name, and names of changed files for
1176 string. The match is case-insensitive.
1184 string. The match is case-insensitive.
1177 """
1185 """
1178 # i18n: "keyword" is a keyword
1186 # i18n: "keyword" is a keyword
1179 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1187 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1180
1188
1181 def matches(r):
1189 def matches(r):
1182 c = repo[r]
1190 c = repo[r]
1183 return any(kw in encoding.lower(t)
1191 return any(kw in encoding.lower(t)
1184 for t in c.files() + [c.user(), c.description()])
1192 for t in c.files() + [c.user(), c.description()])
1185
1193
1186 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1194 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1187
1195
1188 @predicate('limit(set[, n[, offset]])', safe=True)
1196 @predicate('limit(set[, n[, offset]])', safe=True)
1189 def limit(repo, subset, x):
1197 def limit(repo, subset, x):
1190 """First n members of set, defaulting to 1, starting from offset.
1198 """First n members of set, defaulting to 1, starting from offset.
1191 """
1199 """
1192 args = getargsdict(x, 'limit', 'set n offset')
1200 args = getargsdict(x, 'limit', 'set n offset')
1193 if 'set' not in args:
1201 if 'set' not in args:
1194 # i18n: "limit" is a keyword
1202 # i18n: "limit" is a keyword
1195 raise error.ParseError(_("limit requires one to three arguments"))
1203 raise error.ParseError(_("limit requires one to three arguments"))
1196 try:
1204 try:
1197 lim, ofs = 1, 0
1205 lim, ofs = 1, 0
1198 if 'n' in args:
1206 if 'n' in args:
1199 # i18n: "limit" is a keyword
1207 # i18n: "limit" is a keyword
1200 lim = int(getstring(args['n'], _("limit requires a number")))
1208 lim = int(getstring(args['n'], _("limit requires a number")))
1201 if 'offset' in args:
1209 if 'offset' in args:
1202 # i18n: "limit" is a keyword
1210 # i18n: "limit" is a keyword
1203 ofs = int(getstring(args['offset'], _("limit requires a number")))
1211 ofs = int(getstring(args['offset'], _("limit requires a number")))
1204 if ofs < 0:
1212 if ofs < 0:
1205 raise error.ParseError(_("negative offset"))
1213 raise error.ParseError(_("negative offset"))
1206 except (TypeError, ValueError):
1214 except (TypeError, ValueError):
1207 # i18n: "limit" is a keyword
1215 # i18n: "limit" is a keyword
1208 raise error.ParseError(_("limit expects a number"))
1216 raise error.ParseError(_("limit expects a number"))
1209 os = getset(repo, fullreposet(repo), args['set'])
1217 os = getset(repo, fullreposet(repo), args['set'])
1210 result = []
1218 result = []
1211 it = iter(os)
1219 it = iter(os)
1212 for x in xrange(ofs):
1220 for x in xrange(ofs):
1213 y = next(it, None)
1221 y = next(it, None)
1214 if y is None:
1222 if y is None:
1215 break
1223 break
1216 for x in xrange(lim):
1224 for x in xrange(lim):
1217 y = next(it, None)
1225 y = next(it, None)
1218 if y is None:
1226 if y is None:
1219 break
1227 break
1220 elif y in subset:
1228 elif y in subset:
1221 result.append(y)
1229 result.append(y)
1222 return baseset(result, datarepr=('<limit n=%d, offset=%d, %r, %r>',
1230 return baseset(result, datarepr=('<limit n=%d, offset=%d, %r, %r>',
1223 lim, ofs, subset, os))
1231 lim, ofs, subset, os))
1224
1232
1225 @predicate('last(set, [n])', safe=True)
1233 @predicate('last(set, [n])', safe=True)
1226 def last(repo, subset, x):
1234 def last(repo, subset, x):
1227 """Last n members of set, defaulting to 1.
1235 """Last n members of set, defaulting to 1.
1228 """
1236 """
1229 # i18n: "last" is a keyword
1237 # i18n: "last" is a keyword
1230 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1238 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1231 try:
1239 try:
1232 lim = 1
1240 lim = 1
1233 if len(l) == 2:
1241 if len(l) == 2:
1234 # i18n: "last" is a keyword
1242 # i18n: "last" is a keyword
1235 lim = int(getstring(l[1], _("last requires a number")))
1243 lim = int(getstring(l[1], _("last requires a number")))
1236 except (TypeError, ValueError):
1244 except (TypeError, ValueError):
1237 # i18n: "last" is a keyword
1245 # i18n: "last" is a keyword
1238 raise error.ParseError(_("last expects a number"))
1246 raise error.ParseError(_("last expects a number"))
1239 os = getset(repo, fullreposet(repo), l[0])
1247 os = getset(repo, fullreposet(repo), l[0])
1240 os.reverse()
1248 os.reverse()
1241 result = []
1249 result = []
1242 it = iter(os)
1250 it = iter(os)
1243 for x in xrange(lim):
1251 for x in xrange(lim):
1244 y = next(it, None)
1252 y = next(it, None)
1245 if y is None:
1253 if y is None:
1246 break
1254 break
1247 elif y in subset:
1255 elif y in subset:
1248 result.append(y)
1256 result.append(y)
1249 return baseset(result, datarepr=('<last n=%d, %r, %r>', lim, subset, os))
1257 return baseset(result, datarepr=('<last n=%d, %r, %r>', lim, subset, os))
1250
1258
1251 @predicate('max(set)', safe=True)
1259 @predicate('max(set)', safe=True)
1252 def maxrev(repo, subset, x):
1260 def maxrev(repo, subset, x):
1253 """Changeset with highest revision number in set.
1261 """Changeset with highest revision number in set.
1254 """
1262 """
1255 os = getset(repo, fullreposet(repo), x)
1263 os = getset(repo, fullreposet(repo), x)
1256 try:
1264 try:
1257 m = os.max()
1265 m = os.max()
1258 if m in subset:
1266 if m in subset:
1259 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1267 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1260 except ValueError:
1268 except ValueError:
1261 # os.max() throws a ValueError when the collection is empty.
1269 # os.max() throws a ValueError when the collection is empty.
1262 # Same as python's max().
1270 # Same as python's max().
1263 pass
1271 pass
1264 return baseset(datarepr=('<max %r, %r>', subset, os))
1272 return baseset(datarepr=('<max %r, %r>', subset, os))
1265
1273
1266 @predicate('merge()', safe=True)
1274 @predicate('merge()', safe=True)
1267 def merge(repo, subset, x):
1275 def merge(repo, subset, x):
1268 """Changeset is a merge changeset.
1276 """Changeset is a merge changeset.
1269 """
1277 """
1270 # i18n: "merge" is a keyword
1278 # i18n: "merge" is a keyword
1271 getargs(x, 0, 0, _("merge takes no arguments"))
1279 getargs(x, 0, 0, _("merge takes no arguments"))
1272 cl = repo.changelog
1280 cl = repo.changelog
1273 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1281 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1274 condrepr='<merge>')
1282 condrepr='<merge>')
1275
1283
1276 @predicate('branchpoint()', safe=True)
1284 @predicate('branchpoint()', safe=True)
1277 def branchpoint(repo, subset, x):
1285 def branchpoint(repo, subset, x):
1278 """Changesets with more than one child.
1286 """Changesets with more than one child.
1279 """
1287 """
1280 # i18n: "branchpoint" is a keyword
1288 # i18n: "branchpoint" is a keyword
1281 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1289 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1282 cl = repo.changelog
1290 cl = repo.changelog
1283 if not subset:
1291 if not subset:
1284 return baseset()
1292 return baseset()
1285 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1293 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1286 # (and if it is not, it should.)
1294 # (and if it is not, it should.)
1287 baserev = min(subset)
1295 baserev = min(subset)
1288 parentscount = [0]*(len(repo) - baserev)
1296 parentscount = [0]*(len(repo) - baserev)
1289 for r in cl.revs(start=baserev + 1):
1297 for r in cl.revs(start=baserev + 1):
1290 for p in cl.parentrevs(r):
1298 for p in cl.parentrevs(r):
1291 if p >= baserev:
1299 if p >= baserev:
1292 parentscount[p - baserev] += 1
1300 parentscount[p - baserev] += 1
1293 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1301 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1294 condrepr='<branchpoint>')
1302 condrepr='<branchpoint>')
1295
1303
1296 @predicate('min(set)', safe=True)
1304 @predicate('min(set)', safe=True)
1297 def minrev(repo, subset, x):
1305 def minrev(repo, subset, x):
1298 """Changeset with lowest revision number in set.
1306 """Changeset with lowest revision number in set.
1299 """
1307 """
1300 os = getset(repo, fullreposet(repo), x)
1308 os = getset(repo, fullreposet(repo), x)
1301 try:
1309 try:
1302 m = os.min()
1310 m = os.min()
1303 if m in subset:
1311 if m in subset:
1304 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1312 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1305 except ValueError:
1313 except ValueError:
1306 # os.min() throws a ValueError when the collection is empty.
1314 # os.min() throws a ValueError when the collection is empty.
1307 # Same as python's min().
1315 # Same as python's min().
1308 pass
1316 pass
1309 return baseset(datarepr=('<min %r, %r>', subset, os))
1317 return baseset(datarepr=('<min %r, %r>', subset, os))
1310
1318
1311 @predicate('modifies(pattern)', safe=True)
1319 @predicate('modifies(pattern)', safe=True)
1312 def modifies(repo, subset, x):
1320 def modifies(repo, subset, x):
1313 """Changesets modifying files matched by pattern.
1321 """Changesets modifying files matched by pattern.
1314
1322
1315 The pattern without explicit kind like ``glob:`` is expected to be
1323 The pattern without explicit kind like ``glob:`` is expected to be
1316 relative to the current directory and match against a file or a
1324 relative to the current directory and match against a file or a
1317 directory.
1325 directory.
1318 """
1326 """
1319 # i18n: "modifies" is a keyword
1327 # i18n: "modifies" is a keyword
1320 pat = getstring(x, _("modifies requires a pattern"))
1328 pat = getstring(x, _("modifies requires a pattern"))
1321 return checkstatus(repo, subset, pat, 0)
1329 return checkstatus(repo, subset, pat, 0)
1322
1330
1323 @predicate('named(namespace)')
1331 @predicate('named(namespace)')
1324 def named(repo, subset, x):
1332 def named(repo, subset, x):
1325 """The changesets in a given namespace.
1333 """The changesets in a given namespace.
1326
1334
1327 If `namespace` starts with `re:`, the remainder of the string is treated as
1335 If `namespace` starts with `re:`, the remainder of the string is treated as
1328 a regular expression. To match a namespace that actually starts with `re:`,
1336 a regular expression. To match a namespace that actually starts with `re:`,
1329 use the prefix `literal:`.
1337 use the prefix `literal:`.
1330 """
1338 """
1331 # i18n: "named" is a keyword
1339 # i18n: "named" is a keyword
1332 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1340 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1333
1341
1334 ns = getstring(args[0],
1342 ns = getstring(args[0],
1335 # i18n: "named" is a keyword
1343 # i18n: "named" is a keyword
1336 _('the argument to named must be a string'))
1344 _('the argument to named must be a string'))
1337 kind, pattern, matcher = util.stringmatcher(ns)
1345 kind, pattern, matcher = util.stringmatcher(ns)
1338 namespaces = set()
1346 namespaces = set()
1339 if kind == 'literal':
1347 if kind == 'literal':
1340 if pattern not in repo.names:
1348 if pattern not in repo.names:
1341 raise error.RepoLookupError(_("namespace '%s' does not exist")
1349 raise error.RepoLookupError(_("namespace '%s' does not exist")
1342 % ns)
1350 % ns)
1343 namespaces.add(repo.names[pattern])
1351 namespaces.add(repo.names[pattern])
1344 else:
1352 else:
1345 for name, ns in repo.names.iteritems():
1353 for name, ns in repo.names.iteritems():
1346 if matcher(name):
1354 if matcher(name):
1347 namespaces.add(ns)
1355 namespaces.add(ns)
1348 if not namespaces:
1356 if not namespaces:
1349 raise error.RepoLookupError(_("no namespace exists"
1357 raise error.RepoLookupError(_("no namespace exists"
1350 " that match '%s'") % pattern)
1358 " that match '%s'") % pattern)
1351
1359
1352 names = set()
1360 names = set()
1353 for ns in namespaces:
1361 for ns in namespaces:
1354 for name in ns.listnames(repo):
1362 for name in ns.listnames(repo):
1355 if name not in ns.deprecated:
1363 if name not in ns.deprecated:
1356 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1364 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1357
1365
1358 names -= set([node.nullrev])
1366 names -= set([node.nullrev])
1359 return subset & names
1367 return subset & names
1360
1368
1361 @predicate('id(string)', safe=True)
1369 @predicate('id(string)', safe=True)
1362 def node_(repo, subset, x):
1370 def node_(repo, subset, x):
1363 """Revision non-ambiguously specified by the given hex string prefix.
1371 """Revision non-ambiguously specified by the given hex string prefix.
1364 """
1372 """
1365 # i18n: "id" is a keyword
1373 # i18n: "id" is a keyword
1366 l = getargs(x, 1, 1, _("id requires one argument"))
1374 l = getargs(x, 1, 1, _("id requires one argument"))
1367 # i18n: "id" is a keyword
1375 # i18n: "id" is a keyword
1368 n = getstring(l[0], _("id requires a string"))
1376 n = getstring(l[0], _("id requires a string"))
1369 if len(n) == 40:
1377 if len(n) == 40:
1370 try:
1378 try:
1371 rn = repo.changelog.rev(node.bin(n))
1379 rn = repo.changelog.rev(node.bin(n))
1372 except (LookupError, TypeError):
1380 except (LookupError, TypeError):
1373 rn = None
1381 rn = None
1374 else:
1382 else:
1375 rn = None
1383 rn = None
1376 pm = repo.changelog._partialmatch(n)
1384 pm = repo.changelog._partialmatch(n)
1377 if pm is not None:
1385 if pm is not None:
1378 rn = repo.changelog.rev(pm)
1386 rn = repo.changelog.rev(pm)
1379
1387
1380 if rn is None:
1388 if rn is None:
1381 return baseset()
1389 return baseset()
1382 result = baseset([rn])
1390 result = baseset([rn])
1383 return result & subset
1391 return result & subset
1384
1392
1385 @predicate('obsolete()', safe=True)
1393 @predicate('obsolete()', safe=True)
1386 def obsolete(repo, subset, x):
1394 def obsolete(repo, subset, x):
1387 """Mutable changeset with a newer version."""
1395 """Mutable changeset with a newer version."""
1388 # i18n: "obsolete" is a keyword
1396 # i18n: "obsolete" is a keyword
1389 getargs(x, 0, 0, _("obsolete takes no arguments"))
1397 getargs(x, 0, 0, _("obsolete takes no arguments"))
1390 obsoletes = obsmod.getrevs(repo, 'obsolete')
1398 obsoletes = obsmod.getrevs(repo, 'obsolete')
1391 return subset & obsoletes
1399 return subset & obsoletes
1392
1400
1393 @predicate('only(set, [set])', safe=True)
1401 @predicate('only(set, [set])', safe=True)
1394 def only(repo, subset, x):
1402 def only(repo, subset, x):
1395 """Changesets that are ancestors of the first set that are not ancestors
1403 """Changesets that are ancestors of the first set that are not ancestors
1396 of any other head in the repo. If a second set is specified, the result
1404 of any other head in the repo. If a second set is specified, the result
1397 is ancestors of the first set that are not ancestors of the second set
1405 is ancestors of the first set that are not ancestors of the second set
1398 (i.e. ::<set1> - ::<set2>).
1406 (i.e. ::<set1> - ::<set2>).
1399 """
1407 """
1400 cl = repo.changelog
1408 cl = repo.changelog
1401 # i18n: "only" is a keyword
1409 # i18n: "only" is a keyword
1402 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1410 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1403 include = getset(repo, fullreposet(repo), args[0])
1411 include = getset(repo, fullreposet(repo), args[0])
1404 if len(args) == 1:
1412 if len(args) == 1:
1405 if not include:
1413 if not include:
1406 return baseset()
1414 return baseset()
1407
1415
1408 descendants = set(_revdescendants(repo, include, False))
1416 descendants = set(_revdescendants(repo, include, False))
1409 exclude = [rev for rev in cl.headrevs()
1417 exclude = [rev for rev in cl.headrevs()
1410 if not rev in descendants and not rev in include]
1418 if not rev in descendants and not rev in include]
1411 else:
1419 else:
1412 exclude = getset(repo, fullreposet(repo), args[1])
1420 exclude = getset(repo, fullreposet(repo), args[1])
1413
1421
1414 results = set(cl.findmissingrevs(common=exclude, heads=include))
1422 results = set(cl.findmissingrevs(common=exclude, heads=include))
1415 # XXX we should turn this into a baseset instead of a set, smartset may do
1423 # XXX we should turn this into a baseset instead of a set, smartset may do
1416 # some optimisations from the fact this is a baseset.
1424 # some optimisations from the fact this is a baseset.
1417 return subset & results
1425 return subset & results
1418
1426
1419 @predicate('origin([set])', safe=True)
1427 @predicate('origin([set])', safe=True)
1420 def origin(repo, subset, x):
1428 def origin(repo, subset, x):
1421 """
1429 """
1422 Changesets that were specified as a source for the grafts, transplants or
1430 Changesets that were specified as a source for the grafts, transplants or
1423 rebases that created the given revisions. Omitting the optional set is the
1431 rebases that created the given revisions. Omitting the optional set is the
1424 same as passing all(). If a changeset created by these operations is itself
1432 same as passing all(). If a changeset created by these operations is itself
1425 specified as a source for one of these operations, only the source changeset
1433 specified as a source for one of these operations, only the source changeset
1426 for the first operation is selected.
1434 for the first operation is selected.
1427 """
1435 """
1428 if x is not None:
1436 if x is not None:
1429 dests = getset(repo, fullreposet(repo), x)
1437 dests = getset(repo, fullreposet(repo), x)
1430 else:
1438 else:
1431 dests = fullreposet(repo)
1439 dests = fullreposet(repo)
1432
1440
1433 def _firstsrc(rev):
1441 def _firstsrc(rev):
1434 src = _getrevsource(repo, rev)
1442 src = _getrevsource(repo, rev)
1435 if src is None:
1443 if src is None:
1436 return None
1444 return None
1437
1445
1438 while True:
1446 while True:
1439 prev = _getrevsource(repo, src)
1447 prev = _getrevsource(repo, src)
1440
1448
1441 if prev is None:
1449 if prev is None:
1442 return src
1450 return src
1443 src = prev
1451 src = prev
1444
1452
1445 o = set([_firstsrc(r) for r in dests])
1453 o = set([_firstsrc(r) for r in dests])
1446 o -= set([None])
1454 o -= set([None])
1447 # XXX we should turn this into a baseset instead of a set, smartset may do
1455 # XXX we should turn this into a baseset instead of a set, smartset may do
1448 # some optimisations from the fact this is a baseset.
1456 # some optimisations from the fact this is a baseset.
1449 return subset & o
1457 return subset & o
1450
1458
1451 @predicate('outgoing([path])', safe=True)
1459 @predicate('outgoing([path])', safe=True)
1452 def outgoing(repo, subset, x):
1460 def outgoing(repo, subset, x):
1453 """Changesets not found in the specified destination repository, or the
1461 """Changesets not found in the specified destination repository, or the
1454 default push location.
1462 default push location.
1455 """
1463 """
1456 # Avoid cycles.
1464 # Avoid cycles.
1457 from . import (
1465 from . import (
1458 discovery,
1466 discovery,
1459 hg,
1467 hg,
1460 )
1468 )
1461 # i18n: "outgoing" is a keyword
1469 # i18n: "outgoing" is a keyword
1462 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1470 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1463 # i18n: "outgoing" is a keyword
1471 # i18n: "outgoing" is a keyword
1464 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1472 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1465 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1473 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1466 dest, branches = hg.parseurl(dest)
1474 dest, branches = hg.parseurl(dest)
1467 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1475 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1468 if revs:
1476 if revs:
1469 revs = [repo.lookup(rev) for rev in revs]
1477 revs = [repo.lookup(rev) for rev in revs]
1470 other = hg.peer(repo, {}, dest)
1478 other = hg.peer(repo, {}, dest)
1471 repo.ui.pushbuffer()
1479 repo.ui.pushbuffer()
1472 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1480 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1473 repo.ui.popbuffer()
1481 repo.ui.popbuffer()
1474 cl = repo.changelog
1482 cl = repo.changelog
1475 o = set([cl.rev(r) for r in outgoing.missing])
1483 o = set([cl.rev(r) for r in outgoing.missing])
1476 return subset & o
1484 return subset & o
1477
1485
1478 @predicate('p1([set])', safe=True)
1486 @predicate('p1([set])', safe=True)
1479 def p1(repo, subset, x):
1487 def p1(repo, subset, x):
1480 """First parent of changesets in set, or the working directory.
1488 """First parent of changesets in set, or the working directory.
1481 """
1489 """
1482 if x is None:
1490 if x is None:
1483 p = repo[x].p1().rev()
1491 p = repo[x].p1().rev()
1484 if p >= 0:
1492 if p >= 0:
1485 return subset & baseset([p])
1493 return subset & baseset([p])
1486 return baseset()
1494 return baseset()
1487
1495
1488 ps = set()
1496 ps = set()
1489 cl = repo.changelog
1497 cl = repo.changelog
1490 for r in getset(repo, fullreposet(repo), x):
1498 for r in getset(repo, fullreposet(repo), x):
1491 ps.add(cl.parentrevs(r)[0])
1499 ps.add(cl.parentrevs(r)[0])
1492 ps -= set([node.nullrev])
1500 ps -= set([node.nullrev])
1493 # XXX we should turn this into a baseset instead of a set, smartset may do
1501 # XXX we should turn this into a baseset instead of a set, smartset may do
1494 # some optimisations from the fact this is a baseset.
1502 # some optimisations from the fact this is a baseset.
1495 return subset & ps
1503 return subset & ps
1496
1504
1497 @predicate('p2([set])', safe=True)
1505 @predicate('p2([set])', safe=True)
1498 def p2(repo, subset, x):
1506 def p2(repo, subset, x):
1499 """Second parent of changesets in set, or the working directory.
1507 """Second parent of changesets in set, or the working directory.
1500 """
1508 """
1501 if x is None:
1509 if x is None:
1502 ps = repo[x].parents()
1510 ps = repo[x].parents()
1503 try:
1511 try:
1504 p = ps[1].rev()
1512 p = ps[1].rev()
1505 if p >= 0:
1513 if p >= 0:
1506 return subset & baseset([p])
1514 return subset & baseset([p])
1507 return baseset()
1515 return baseset()
1508 except IndexError:
1516 except IndexError:
1509 return baseset()
1517 return baseset()
1510
1518
1511 ps = set()
1519 ps = set()
1512 cl = repo.changelog
1520 cl = repo.changelog
1513 for r in getset(repo, fullreposet(repo), x):
1521 for r in getset(repo, fullreposet(repo), x):
1514 ps.add(cl.parentrevs(r)[1])
1522 ps.add(cl.parentrevs(r)[1])
1515 ps -= set([node.nullrev])
1523 ps -= set([node.nullrev])
1516 # XXX we should turn this into a baseset instead of a set, smartset may do
1524 # XXX we should turn this into a baseset instead of a set, smartset may do
1517 # some optimisations from the fact this is a baseset.
1525 # some optimisations from the fact this is a baseset.
1518 return subset & ps
1526 return subset & ps
1519
1527
1520 @predicate('parents([set])', safe=True)
1528 @predicate('parents([set])', safe=True)
1521 def parents(repo, subset, x):
1529 def parents(repo, subset, x):
1522 """
1530 """
1523 The set of all parents for all changesets in set, or the working directory.
1531 The set of all parents for all changesets in set, or the working directory.
1524 """
1532 """
1525 if x is None:
1533 if x is None:
1526 ps = set(p.rev() for p in repo[x].parents())
1534 ps = set(p.rev() for p in repo[x].parents())
1527 else:
1535 else:
1528 ps = set()
1536 ps = set()
1529 cl = repo.changelog
1537 cl = repo.changelog
1530 up = ps.update
1538 up = ps.update
1531 parentrevs = cl.parentrevs
1539 parentrevs = cl.parentrevs
1532 for r in getset(repo, fullreposet(repo), x):
1540 for r in getset(repo, fullreposet(repo), x):
1533 if r == node.wdirrev:
1541 if r == node.wdirrev:
1534 up(p.rev() for p in repo[r].parents())
1542 up(p.rev() for p in repo[r].parents())
1535 else:
1543 else:
1536 up(parentrevs(r))
1544 up(parentrevs(r))
1537 ps -= set([node.nullrev])
1545 ps -= set([node.nullrev])
1538 return subset & ps
1546 return subset & ps
1539
1547
1540 def _phase(repo, subset, target):
1548 def _phase(repo, subset, target):
1541 """helper to select all rev in phase <target>"""
1549 """helper to select all rev in phase <target>"""
1542 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1550 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1543 if repo._phasecache._phasesets:
1551 if repo._phasecache._phasesets:
1544 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1552 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1545 s = baseset(s)
1553 s = baseset(s)
1546 s.sort() # set are non ordered, so we enforce ascending
1554 s.sort() # set are non ordered, so we enforce ascending
1547 return subset & s
1555 return subset & s
1548 else:
1556 else:
1549 phase = repo._phasecache.phase
1557 phase = repo._phasecache.phase
1550 condition = lambda r: phase(repo, r) == target
1558 condition = lambda r: phase(repo, r) == target
1551 return subset.filter(condition, condrepr=('<phase %r>', target),
1559 return subset.filter(condition, condrepr=('<phase %r>', target),
1552 cache=False)
1560 cache=False)
1553
1561
1554 @predicate('draft()', safe=True)
1562 @predicate('draft()', safe=True)
1555 def draft(repo, subset, x):
1563 def draft(repo, subset, x):
1556 """Changeset in draft phase."""
1564 """Changeset in draft phase."""
1557 # i18n: "draft" is a keyword
1565 # i18n: "draft" is a keyword
1558 getargs(x, 0, 0, _("draft takes no arguments"))
1566 getargs(x, 0, 0, _("draft takes no arguments"))
1559 target = phases.draft
1567 target = phases.draft
1560 return _phase(repo, subset, target)
1568 return _phase(repo, subset, target)
1561
1569
1562 @predicate('secret()', safe=True)
1570 @predicate('secret()', safe=True)
1563 def secret(repo, subset, x):
1571 def secret(repo, subset, x):
1564 """Changeset in secret phase."""
1572 """Changeset in secret phase."""
1565 # i18n: "secret" is a keyword
1573 # i18n: "secret" is a keyword
1566 getargs(x, 0, 0, _("secret takes no arguments"))
1574 getargs(x, 0, 0, _("secret takes no arguments"))
1567 target = phases.secret
1575 target = phases.secret
1568 return _phase(repo, subset, target)
1576 return _phase(repo, subset, target)
1569
1577
1570 def parentspec(repo, subset, x, n):
1578 def parentspec(repo, subset, x, n):
1571 """``set^0``
1579 """``set^0``
1572 The set.
1580 The set.
1573 ``set^1`` (or ``set^``), ``set^2``
1581 ``set^1`` (or ``set^``), ``set^2``
1574 First or second parent, respectively, of all changesets in set.
1582 First or second parent, respectively, of all changesets in set.
1575 """
1583 """
1576 try:
1584 try:
1577 n = int(n[1])
1585 n = int(n[1])
1578 if n not in (0, 1, 2):
1586 if n not in (0, 1, 2):
1579 raise ValueError
1587 raise ValueError
1580 except (TypeError, ValueError):
1588 except (TypeError, ValueError):
1581 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1589 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1582 ps = set()
1590 ps = set()
1583 cl = repo.changelog
1591 cl = repo.changelog
1584 for r in getset(repo, fullreposet(repo), x):
1592 for r in getset(repo, fullreposet(repo), x):
1585 if n == 0:
1593 if n == 0:
1586 ps.add(r)
1594 ps.add(r)
1587 elif n == 1:
1595 elif n == 1:
1588 ps.add(cl.parentrevs(r)[0])
1596 ps.add(cl.parentrevs(r)[0])
1589 elif n == 2:
1597 elif n == 2:
1590 parents = cl.parentrevs(r)
1598 parents = cl.parentrevs(r)
1591 if len(parents) > 1:
1599 if len(parents) > 1:
1592 ps.add(parents[1])
1600 ps.add(parents[1])
1593 return subset & ps
1601 return subset & ps
1594
1602
1595 @predicate('present(set)', safe=True)
1603 @predicate('present(set)', safe=True)
1596 def present(repo, subset, x):
1604 def present(repo, subset, x):
1597 """An empty set, if any revision in set isn't found; otherwise,
1605 """An empty set, if any revision in set isn't found; otherwise,
1598 all revisions in set.
1606 all revisions in set.
1599
1607
1600 If any of specified revisions is not present in the local repository,
1608 If any of specified revisions is not present in the local repository,
1601 the query is normally aborted. But this predicate allows the query
1609 the query is normally aborted. But this predicate allows the query
1602 to continue even in such cases.
1610 to continue even in such cases.
1603 """
1611 """
1604 try:
1612 try:
1605 return getset(repo, subset, x)
1613 return getset(repo, subset, x)
1606 except error.RepoLookupError:
1614 except error.RepoLookupError:
1607 return baseset()
1615 return baseset()
1608
1616
1609 # for internal use
1617 # for internal use
1610 @predicate('_notpublic', safe=True)
1618 @predicate('_notpublic', safe=True)
1611 def _notpublic(repo, subset, x):
1619 def _notpublic(repo, subset, x):
1612 getargs(x, 0, 0, "_notpublic takes no arguments")
1620 getargs(x, 0, 0, "_notpublic takes no arguments")
1613 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1621 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1614 if repo._phasecache._phasesets:
1622 if repo._phasecache._phasesets:
1615 s = set()
1623 s = set()
1616 for u in repo._phasecache._phasesets[1:]:
1624 for u in repo._phasecache._phasesets[1:]:
1617 s.update(u)
1625 s.update(u)
1618 s = baseset(s - repo.changelog.filteredrevs)
1626 s = baseset(s - repo.changelog.filteredrevs)
1619 s.sort()
1627 s.sort()
1620 return subset & s
1628 return subset & s
1621 else:
1629 else:
1622 phase = repo._phasecache.phase
1630 phase = repo._phasecache.phase
1623 target = phases.public
1631 target = phases.public
1624 condition = lambda r: phase(repo, r) != target
1632 condition = lambda r: phase(repo, r) != target
1625 return subset.filter(condition, condrepr=('<phase %r>', target),
1633 return subset.filter(condition, condrepr=('<phase %r>', target),
1626 cache=False)
1634 cache=False)
1627
1635
1628 @predicate('public()', safe=True)
1636 @predicate('public()', safe=True)
1629 def public(repo, subset, x):
1637 def public(repo, subset, x):
1630 """Changeset in public phase."""
1638 """Changeset in public phase."""
1631 # i18n: "public" is a keyword
1639 # i18n: "public" is a keyword
1632 getargs(x, 0, 0, _("public takes no arguments"))
1640 getargs(x, 0, 0, _("public takes no arguments"))
1633 phase = repo._phasecache.phase
1641 phase = repo._phasecache.phase
1634 target = phases.public
1642 target = phases.public
1635 condition = lambda r: phase(repo, r) == target
1643 condition = lambda r: phase(repo, r) == target
1636 return subset.filter(condition, condrepr=('<phase %r>', target),
1644 return subset.filter(condition, condrepr=('<phase %r>', target),
1637 cache=False)
1645 cache=False)
1638
1646
1639 @predicate('remote([id [,path]])', safe=True)
1647 @predicate('remote([id [,path]])', safe=True)
1640 def remote(repo, subset, x):
1648 def remote(repo, subset, x):
1641 """Local revision that corresponds to the given identifier in a
1649 """Local revision that corresponds to the given identifier in a
1642 remote repository, if present. Here, the '.' identifier is a
1650 remote repository, if present. Here, the '.' identifier is a
1643 synonym for the current local branch.
1651 synonym for the current local branch.
1644 """
1652 """
1645
1653
1646 from . import hg # avoid start-up nasties
1654 from . import hg # avoid start-up nasties
1647 # i18n: "remote" is a keyword
1655 # i18n: "remote" is a keyword
1648 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1656 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1649
1657
1650 q = '.'
1658 q = '.'
1651 if len(l) > 0:
1659 if len(l) > 0:
1652 # i18n: "remote" is a keyword
1660 # i18n: "remote" is a keyword
1653 q = getstring(l[0], _("remote requires a string id"))
1661 q = getstring(l[0], _("remote requires a string id"))
1654 if q == '.':
1662 if q == '.':
1655 q = repo['.'].branch()
1663 q = repo['.'].branch()
1656
1664
1657 dest = ''
1665 dest = ''
1658 if len(l) > 1:
1666 if len(l) > 1:
1659 # i18n: "remote" is a keyword
1667 # i18n: "remote" is a keyword
1660 dest = getstring(l[1], _("remote requires a repository path"))
1668 dest = getstring(l[1], _("remote requires a repository path"))
1661 dest = repo.ui.expandpath(dest or 'default')
1669 dest = repo.ui.expandpath(dest or 'default')
1662 dest, branches = hg.parseurl(dest)
1670 dest, branches = hg.parseurl(dest)
1663 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1671 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1664 if revs:
1672 if revs:
1665 revs = [repo.lookup(rev) for rev in revs]
1673 revs = [repo.lookup(rev) for rev in revs]
1666 other = hg.peer(repo, {}, dest)
1674 other = hg.peer(repo, {}, dest)
1667 n = other.lookup(q)
1675 n = other.lookup(q)
1668 if n in repo:
1676 if n in repo:
1669 r = repo[n].rev()
1677 r = repo[n].rev()
1670 if r in subset:
1678 if r in subset:
1671 return baseset([r])
1679 return baseset([r])
1672 return baseset()
1680 return baseset()
1673
1681
1674 @predicate('removes(pattern)', safe=True)
1682 @predicate('removes(pattern)', safe=True)
1675 def removes(repo, subset, x):
1683 def removes(repo, subset, x):
1676 """Changesets which remove files matching pattern.
1684 """Changesets which remove files matching pattern.
1677
1685
1678 The pattern without explicit kind like ``glob:`` is expected to be
1686 The pattern without explicit kind like ``glob:`` is expected to be
1679 relative to the current directory and match against a file or a
1687 relative to the current directory and match against a file or a
1680 directory.
1688 directory.
1681 """
1689 """
1682 # i18n: "removes" is a keyword
1690 # i18n: "removes" is a keyword
1683 pat = getstring(x, _("removes requires a pattern"))
1691 pat = getstring(x, _("removes requires a pattern"))
1684 return checkstatus(repo, subset, pat, 2)
1692 return checkstatus(repo, subset, pat, 2)
1685
1693
1686 @predicate('rev(number)', safe=True)
1694 @predicate('rev(number)', safe=True)
1687 def rev(repo, subset, x):
1695 def rev(repo, subset, x):
1688 """Revision with the given numeric identifier.
1696 """Revision with the given numeric identifier.
1689 """
1697 """
1690 # i18n: "rev" is a keyword
1698 # i18n: "rev" is a keyword
1691 l = getargs(x, 1, 1, _("rev requires one argument"))
1699 l = getargs(x, 1, 1, _("rev requires one argument"))
1692 try:
1700 try:
1693 # i18n: "rev" is a keyword
1701 # i18n: "rev" is a keyword
1694 l = int(getstring(l[0], _("rev requires a number")))
1702 l = int(getstring(l[0], _("rev requires a number")))
1695 except (TypeError, ValueError):
1703 except (TypeError, ValueError):
1696 # i18n: "rev" is a keyword
1704 # i18n: "rev" is a keyword
1697 raise error.ParseError(_("rev expects a number"))
1705 raise error.ParseError(_("rev expects a number"))
1698 if l not in repo.changelog and l != node.nullrev:
1706 if l not in repo.changelog and l != node.nullrev:
1699 return baseset()
1707 return baseset()
1700 return subset & baseset([l])
1708 return subset & baseset([l])
1701
1709
1702 @predicate('matching(revision [, field])', safe=True)
1710 @predicate('matching(revision [, field])', safe=True)
1703 def matching(repo, subset, x):
1711 def matching(repo, subset, x):
1704 """Changesets in which a given set of fields match the set of fields in the
1712 """Changesets in which a given set of fields match the set of fields in the
1705 selected revision or set.
1713 selected revision or set.
1706
1714
1707 To match more than one field pass the list of fields to match separated
1715 To match more than one field pass the list of fields to match separated
1708 by spaces (e.g. ``author description``).
1716 by spaces (e.g. ``author description``).
1709
1717
1710 Valid fields are most regular revision fields and some special fields.
1718 Valid fields are most regular revision fields and some special fields.
1711
1719
1712 Regular revision fields are ``description``, ``author``, ``branch``,
1720 Regular revision fields are ``description``, ``author``, ``branch``,
1713 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1721 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1714 and ``diff``.
1722 and ``diff``.
1715 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1723 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1716 contents of the revision. Two revisions matching their ``diff`` will
1724 contents of the revision. Two revisions matching their ``diff`` will
1717 also match their ``files``.
1725 also match their ``files``.
1718
1726
1719 Special fields are ``summary`` and ``metadata``:
1727 Special fields are ``summary`` and ``metadata``:
1720 ``summary`` matches the first line of the description.
1728 ``summary`` matches the first line of the description.
1721 ``metadata`` is equivalent to matching ``description user date``
1729 ``metadata`` is equivalent to matching ``description user date``
1722 (i.e. it matches the main metadata fields).
1730 (i.e. it matches the main metadata fields).
1723
1731
1724 ``metadata`` is the default field which is used when no fields are
1732 ``metadata`` is the default field which is used when no fields are
1725 specified. You can match more than one field at a time.
1733 specified. You can match more than one field at a time.
1726 """
1734 """
1727 # i18n: "matching" is a keyword
1735 # i18n: "matching" is a keyword
1728 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1736 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1729
1737
1730 revs = getset(repo, fullreposet(repo), l[0])
1738 revs = getset(repo, fullreposet(repo), l[0])
1731
1739
1732 fieldlist = ['metadata']
1740 fieldlist = ['metadata']
1733 if len(l) > 1:
1741 if len(l) > 1:
1734 fieldlist = getstring(l[1],
1742 fieldlist = getstring(l[1],
1735 # i18n: "matching" is a keyword
1743 # i18n: "matching" is a keyword
1736 _("matching requires a string "
1744 _("matching requires a string "
1737 "as its second argument")).split()
1745 "as its second argument")).split()
1738
1746
1739 # Make sure that there are no repeated fields,
1747 # Make sure that there are no repeated fields,
1740 # expand the 'special' 'metadata' field type
1748 # expand the 'special' 'metadata' field type
1741 # and check the 'files' whenever we check the 'diff'
1749 # and check the 'files' whenever we check the 'diff'
1742 fields = []
1750 fields = []
1743 for field in fieldlist:
1751 for field in fieldlist:
1744 if field == 'metadata':
1752 if field == 'metadata':
1745 fields += ['user', 'description', 'date']
1753 fields += ['user', 'description', 'date']
1746 elif field == 'diff':
1754 elif field == 'diff':
1747 # a revision matching the diff must also match the files
1755 # a revision matching the diff must also match the files
1748 # since matching the diff is very costly, make sure to
1756 # since matching the diff is very costly, make sure to
1749 # also match the files first
1757 # also match the files first
1750 fields += ['files', 'diff']
1758 fields += ['files', 'diff']
1751 else:
1759 else:
1752 if field == 'author':
1760 if field == 'author':
1753 field = 'user'
1761 field = 'user'
1754 fields.append(field)
1762 fields.append(field)
1755 fields = set(fields)
1763 fields = set(fields)
1756 if 'summary' in fields and 'description' in fields:
1764 if 'summary' in fields and 'description' in fields:
1757 # If a revision matches its description it also matches its summary
1765 # If a revision matches its description it also matches its summary
1758 fields.discard('summary')
1766 fields.discard('summary')
1759
1767
1760 # We may want to match more than one field
1768 # We may want to match more than one field
1761 # Not all fields take the same amount of time to be matched
1769 # Not all fields take the same amount of time to be matched
1762 # Sort the selected fields in order of increasing matching cost
1770 # Sort the selected fields in order of increasing matching cost
1763 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1771 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1764 'files', 'description', 'substate', 'diff']
1772 'files', 'description', 'substate', 'diff']
1765 def fieldkeyfunc(f):
1773 def fieldkeyfunc(f):
1766 try:
1774 try:
1767 return fieldorder.index(f)
1775 return fieldorder.index(f)
1768 except ValueError:
1776 except ValueError:
1769 # assume an unknown field is very costly
1777 # assume an unknown field is very costly
1770 return len(fieldorder)
1778 return len(fieldorder)
1771 fields = list(fields)
1779 fields = list(fields)
1772 fields.sort(key=fieldkeyfunc)
1780 fields.sort(key=fieldkeyfunc)
1773
1781
1774 # Each field will be matched with its own "getfield" function
1782 # Each field will be matched with its own "getfield" function
1775 # which will be added to the getfieldfuncs array of functions
1783 # which will be added to the getfieldfuncs array of functions
1776 getfieldfuncs = []
1784 getfieldfuncs = []
1777 _funcs = {
1785 _funcs = {
1778 'user': lambda r: repo[r].user(),
1786 'user': lambda r: repo[r].user(),
1779 'branch': lambda r: repo[r].branch(),
1787 'branch': lambda r: repo[r].branch(),
1780 'date': lambda r: repo[r].date(),
1788 'date': lambda r: repo[r].date(),
1781 'description': lambda r: repo[r].description(),
1789 'description': lambda r: repo[r].description(),
1782 'files': lambda r: repo[r].files(),
1790 'files': lambda r: repo[r].files(),
1783 'parents': lambda r: repo[r].parents(),
1791 'parents': lambda r: repo[r].parents(),
1784 'phase': lambda r: repo[r].phase(),
1792 'phase': lambda r: repo[r].phase(),
1785 'substate': lambda r: repo[r].substate,
1793 'substate': lambda r: repo[r].substate,
1786 'summary': lambda r: repo[r].description().splitlines()[0],
1794 'summary': lambda r: repo[r].description().splitlines()[0],
1787 'diff': lambda r: list(repo[r].diff(git=True),)
1795 'diff': lambda r: list(repo[r].diff(git=True),)
1788 }
1796 }
1789 for info in fields:
1797 for info in fields:
1790 getfield = _funcs.get(info, None)
1798 getfield = _funcs.get(info, None)
1791 if getfield is None:
1799 if getfield is None:
1792 raise error.ParseError(
1800 raise error.ParseError(
1793 # i18n: "matching" is a keyword
1801 # i18n: "matching" is a keyword
1794 _("unexpected field name passed to matching: %s") % info)
1802 _("unexpected field name passed to matching: %s") % info)
1795 getfieldfuncs.append(getfield)
1803 getfieldfuncs.append(getfield)
1796 # convert the getfield array of functions into a "getinfo" function
1804 # convert the getfield array of functions into a "getinfo" function
1797 # which returns an array of field values (or a single value if there
1805 # which returns an array of field values (or a single value if there
1798 # is only one field to match)
1806 # is only one field to match)
1799 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1807 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1800
1808
1801 def matches(x):
1809 def matches(x):
1802 for rev in revs:
1810 for rev in revs:
1803 target = getinfo(rev)
1811 target = getinfo(rev)
1804 match = True
1812 match = True
1805 for n, f in enumerate(getfieldfuncs):
1813 for n, f in enumerate(getfieldfuncs):
1806 if target[n] != f(x):
1814 if target[n] != f(x):
1807 match = False
1815 match = False
1808 if match:
1816 if match:
1809 return True
1817 return True
1810 return False
1818 return False
1811
1819
1812 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1820 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1813
1821
1814 @predicate('reverse(set)', safe=True)
1822 @predicate('reverse(set)', safe=True)
1815 def reverse(repo, subset, x):
1823 def reverse(repo, subset, x):
1816 """Reverse order of set.
1824 """Reverse order of set.
1817 """
1825 """
1818 l = getset(repo, subset, x)
1826 l = getset(repo, subset, x)
1819 l.reverse()
1827 l.reverse()
1820 return l
1828 return l
1821
1829
1822 @predicate('roots(set)', safe=True)
1830 @predicate('roots(set)', safe=True)
1823 def roots(repo, subset, x):
1831 def roots(repo, subset, x):
1824 """Changesets in set with no parent changeset in set.
1832 """Changesets in set with no parent changeset in set.
1825 """
1833 """
1826 s = getset(repo, fullreposet(repo), x)
1834 s = getset(repo, fullreposet(repo), x)
1827 parents = repo.changelog.parentrevs
1835 parents = repo.changelog.parentrevs
1828 def filter(r):
1836 def filter(r):
1829 for p in parents(r):
1837 for p in parents(r):
1830 if 0 <= p and p in s:
1838 if 0 <= p and p in s:
1831 return False
1839 return False
1832 return True
1840 return True
1833 return subset & s.filter(filter, condrepr='<roots>')
1841 return subset & s.filter(filter, condrepr='<roots>')
1834
1842
1835 _sortkeyfuncs = {
1843 _sortkeyfuncs = {
1836 'rev': lambda c: c.rev(),
1844 'rev': lambda c: c.rev(),
1837 'branch': lambda c: c.branch(),
1845 'branch': lambda c: c.branch(),
1838 'desc': lambda c: c.description(),
1846 'desc': lambda c: c.description(),
1839 'user': lambda c: c.user(),
1847 'user': lambda c: c.user(),
1840 'author': lambda c: c.user(),
1848 'author': lambda c: c.user(),
1841 'date': lambda c: c.date()[0],
1849 'date': lambda c: c.date()[0],
1842 }
1850 }
1843
1851
1844 def _getsortargs(x):
1852 def _getsortargs(x):
1845 """Parse sort options into (set, [(key, reverse)], opts)"""
1853 """Parse sort options into (set, [(key, reverse)], opts)"""
1846 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1854 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1847 if 'set' not in args:
1855 if 'set' not in args:
1848 # i18n: "sort" is a keyword
1856 # i18n: "sort" is a keyword
1849 raise error.ParseError(_('sort requires one or two arguments'))
1857 raise error.ParseError(_('sort requires one or two arguments'))
1850 keys = "rev"
1858 keys = "rev"
1851 if 'keys' in args:
1859 if 'keys' in args:
1852 # i18n: "sort" is a keyword
1860 # i18n: "sort" is a keyword
1853 keys = getstring(args['keys'], _("sort spec must be a string"))
1861 keys = getstring(args['keys'], _("sort spec must be a string"))
1854
1862
1855 keyflags = []
1863 keyflags = []
1856 for k in keys.split():
1864 for k in keys.split():
1857 fk = k
1865 fk = k
1858 reverse = (k[0] == '-')
1866 reverse = (k[0] == '-')
1859 if reverse:
1867 if reverse:
1860 k = k[1:]
1868 k = k[1:]
1861 if k not in _sortkeyfuncs and k != 'topo':
1869 if k not in _sortkeyfuncs and k != 'topo':
1862 raise error.ParseError(_("unknown sort key %r") % fk)
1870 raise error.ParseError(_("unknown sort key %r") % fk)
1863 keyflags.append((k, reverse))
1871 keyflags.append((k, reverse))
1864
1872
1865 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1873 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1866 # i18n: "topo" is a keyword
1874 # i18n: "topo" is a keyword
1867 raise error.ParseError(_('topo sort order cannot be combined '
1875 raise error.ParseError(_('topo sort order cannot be combined '
1868 'with other sort keys'))
1876 'with other sort keys'))
1869
1877
1870 opts = {}
1878 opts = {}
1871 if 'topo.firstbranch' in args:
1879 if 'topo.firstbranch' in args:
1872 if any(k == 'topo' for k, reverse in keyflags):
1880 if any(k == 'topo' for k, reverse in keyflags):
1873 opts['topo.firstbranch'] = args['topo.firstbranch']
1881 opts['topo.firstbranch'] = args['topo.firstbranch']
1874 else:
1882 else:
1875 # i18n: "topo" and "topo.firstbranch" are keywords
1883 # i18n: "topo" and "topo.firstbranch" are keywords
1876 raise error.ParseError(_('topo.firstbranch can only be used '
1884 raise error.ParseError(_('topo.firstbranch can only be used '
1877 'when using the topo sort key'))
1885 'when using the topo sort key'))
1878
1886
1879 return args['set'], keyflags, opts
1887 return args['set'], keyflags, opts
1880
1888
1881 @predicate('sort(set[, [-]key... [, ...]])', safe=True)
1889 @predicate('sort(set[, [-]key... [, ...]])', safe=True)
1882 def sort(repo, subset, x):
1890 def sort(repo, subset, x):
1883 """Sort set by keys. The default sort order is ascending, specify a key
1891 """Sort set by keys. The default sort order is ascending, specify a key
1884 as ``-key`` to sort in descending order.
1892 as ``-key`` to sort in descending order.
1885
1893
1886 The keys can be:
1894 The keys can be:
1887
1895
1888 - ``rev`` for the revision number,
1896 - ``rev`` for the revision number,
1889 - ``branch`` for the branch name,
1897 - ``branch`` for the branch name,
1890 - ``desc`` for the commit message (description),
1898 - ``desc`` for the commit message (description),
1891 - ``user`` for user name (``author`` can be used as an alias),
1899 - ``user`` for user name (``author`` can be used as an alias),
1892 - ``date`` for the commit date
1900 - ``date`` for the commit date
1893 - ``topo`` for a reverse topographical sort
1901 - ``topo`` for a reverse topographical sort
1894
1902
1895 The ``topo`` sort order cannot be combined with other sort keys. This sort
1903 The ``topo`` sort order cannot be combined with other sort keys. This sort
1896 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1904 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1897 specifies what topographical branches to prioritize in the sort.
1905 specifies what topographical branches to prioritize in the sort.
1898
1906
1899 """
1907 """
1900 s, keyflags, opts = _getsortargs(x)
1908 s, keyflags, opts = _getsortargs(x)
1901 revs = getset(repo, subset, s)
1909 revs = getset(repo, subset, s)
1902
1910
1903 if not keyflags:
1911 if not keyflags:
1904 return revs
1912 return revs
1905 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1913 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1906 revs.sort(reverse=keyflags[0][1])
1914 revs.sort(reverse=keyflags[0][1])
1907 return revs
1915 return revs
1908 elif keyflags[0][0] == "topo":
1916 elif keyflags[0][0] == "topo":
1909 firstbranch = ()
1917 firstbranch = ()
1910 if 'topo.firstbranch' in opts:
1918 if 'topo.firstbranch' in opts:
1911 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1919 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1912 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1920 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1913 istopo=True)
1921 istopo=True)
1914 if keyflags[0][1]:
1922 if keyflags[0][1]:
1915 revs.reverse()
1923 revs.reverse()
1916 return revs
1924 return revs
1917
1925
1918 # sort() is guaranteed to be stable
1926 # sort() is guaranteed to be stable
1919 ctxs = [repo[r] for r in revs]
1927 ctxs = [repo[r] for r in revs]
1920 for k, reverse in reversed(keyflags):
1928 for k, reverse in reversed(keyflags):
1921 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1929 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1922 return baseset([c.rev() for c in ctxs])
1930 return baseset([c.rev() for c in ctxs])
1923
1931
1924 def _toposort(revs, parentsfunc, firstbranch=()):
1932 def _toposort(revs, parentsfunc, firstbranch=()):
1925 """Yield revisions from heads to roots one (topo) branch at a time.
1933 """Yield revisions from heads to roots one (topo) branch at a time.
1926
1934
1927 This function aims to be used by a graph generator that wishes to minimize
1935 This function aims to be used by a graph generator that wishes to minimize
1928 the number of parallel branches and their interleaving.
1936 the number of parallel branches and their interleaving.
1929
1937
1930 Example iteration order (numbers show the "true" order in a changelog):
1938 Example iteration order (numbers show the "true" order in a changelog):
1931
1939
1932 o 4
1940 o 4
1933 |
1941 |
1934 o 1
1942 o 1
1935 |
1943 |
1936 | o 3
1944 | o 3
1937 | |
1945 | |
1938 | o 2
1946 | o 2
1939 |/
1947 |/
1940 o 0
1948 o 0
1941
1949
1942 Note that the ancestors of merges are understood by the current
1950 Note that the ancestors of merges are understood by the current
1943 algorithm to be on the same branch. This means no reordering will
1951 algorithm to be on the same branch. This means no reordering will
1944 occur behind a merge.
1952 occur behind a merge.
1945 """
1953 """
1946
1954
1947 ### Quick summary of the algorithm
1955 ### Quick summary of the algorithm
1948 #
1956 #
1949 # This function is based around a "retention" principle. We keep revisions
1957 # This function is based around a "retention" principle. We keep revisions
1950 # in memory until we are ready to emit a whole branch that immediately
1958 # in memory until we are ready to emit a whole branch that immediately
1951 # "merges" into an existing one. This reduces the number of parallel
1959 # "merges" into an existing one. This reduces the number of parallel
1952 # branches with interleaved revisions.
1960 # branches with interleaved revisions.
1953 #
1961 #
1954 # During iteration revs are split into two groups:
1962 # During iteration revs are split into two groups:
1955 # A) revision already emitted
1963 # A) revision already emitted
1956 # B) revision in "retention". They are stored as different subgroups.
1964 # B) revision in "retention". They are stored as different subgroups.
1957 #
1965 #
1958 # for each REV, we do the following logic:
1966 # for each REV, we do the following logic:
1959 #
1967 #
1960 # 1) if REV is a parent of (A), we will emit it. If there is a
1968 # 1) if REV is a parent of (A), we will emit it. If there is a
1961 # retention group ((B) above) that is blocked on REV being
1969 # retention group ((B) above) that is blocked on REV being
1962 # available, we emit all the revisions out of that retention
1970 # available, we emit all the revisions out of that retention
1963 # group first.
1971 # group first.
1964 #
1972 #
1965 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
1973 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
1966 # available, if such subgroup exist, we add REV to it and the subgroup is
1974 # available, if such subgroup exist, we add REV to it and the subgroup is
1967 # now awaiting for REV.parents() to be available.
1975 # now awaiting for REV.parents() to be available.
1968 #
1976 #
1969 # 3) finally if no such group existed in (B), we create a new subgroup.
1977 # 3) finally if no such group existed in (B), we create a new subgroup.
1970 #
1978 #
1971 #
1979 #
1972 # To bootstrap the algorithm, we emit the tipmost revision (which
1980 # To bootstrap the algorithm, we emit the tipmost revision (which
1973 # puts it in group (A) from above).
1981 # puts it in group (A) from above).
1974
1982
1975 revs.sort(reverse=True)
1983 revs.sort(reverse=True)
1976
1984
1977 # Set of parents of revision that have been emitted. They can be considered
1985 # Set of parents of revision that have been emitted. They can be considered
1978 # unblocked as the graph generator is already aware of them so there is no
1986 # unblocked as the graph generator is already aware of them so there is no
1979 # need to delay the revisions that reference them.
1987 # need to delay the revisions that reference them.
1980 #
1988 #
1981 # If someone wants to prioritize a branch over the others, pre-filling this
1989 # If someone wants to prioritize a branch over the others, pre-filling this
1982 # set will force all other branches to wait until this branch is ready to be
1990 # set will force all other branches to wait until this branch is ready to be
1983 # emitted.
1991 # emitted.
1984 unblocked = set(firstbranch)
1992 unblocked = set(firstbranch)
1985
1993
1986 # list of groups waiting to be displayed, each group is defined by:
1994 # list of groups waiting to be displayed, each group is defined by:
1987 #
1995 #
1988 # (revs: lists of revs waiting to be displayed,
1996 # (revs: lists of revs waiting to be displayed,
1989 # blocked: set of that cannot be displayed before those in 'revs')
1997 # blocked: set of that cannot be displayed before those in 'revs')
1990 #
1998 #
1991 # The second value ('blocked') correspond to parents of any revision in the
1999 # The second value ('blocked') correspond to parents of any revision in the
1992 # group ('revs') that is not itself contained in the group. The main idea
2000 # group ('revs') that is not itself contained in the group. The main idea
1993 # of this algorithm is to delay as much as possible the emission of any
2001 # of this algorithm is to delay as much as possible the emission of any
1994 # revision. This means waiting for the moment we are about to display
2002 # revision. This means waiting for the moment we are about to display
1995 # these parents to display the revs in a group.
2003 # these parents to display the revs in a group.
1996 #
2004 #
1997 # This first implementation is smart until it encounters a merge: it will
2005 # This first implementation is smart until it encounters a merge: it will
1998 # emit revs as soon as any parent is about to be emitted and can grow an
2006 # emit revs as soon as any parent is about to be emitted and can grow an
1999 # arbitrary number of revs in 'blocked'. In practice this mean we properly
2007 # arbitrary number of revs in 'blocked'. In practice this mean we properly
2000 # retains new branches but gives up on any special ordering for ancestors
2008 # retains new branches but gives up on any special ordering for ancestors
2001 # of merges. The implementation can be improved to handle this better.
2009 # of merges. The implementation can be improved to handle this better.
2002 #
2010 #
2003 # The first subgroup is special. It corresponds to all the revision that
2011 # The first subgroup is special. It corresponds to all the revision that
2004 # were already emitted. The 'revs' lists is expected to be empty and the
2012 # were already emitted. The 'revs' lists is expected to be empty and the
2005 # 'blocked' set contains the parents revisions of already emitted revision.
2013 # 'blocked' set contains the parents revisions of already emitted revision.
2006 #
2014 #
2007 # You could pre-seed the <parents> set of groups[0] to a specific
2015 # You could pre-seed the <parents> set of groups[0] to a specific
2008 # changesets to select what the first emitted branch should be.
2016 # changesets to select what the first emitted branch should be.
2009 groups = [([], unblocked)]
2017 groups = [([], unblocked)]
2010 pendingheap = []
2018 pendingheap = []
2011 pendingset = set()
2019 pendingset = set()
2012
2020
2013 heapq.heapify(pendingheap)
2021 heapq.heapify(pendingheap)
2014 heappop = heapq.heappop
2022 heappop = heapq.heappop
2015 heappush = heapq.heappush
2023 heappush = heapq.heappush
2016 for currentrev in revs:
2024 for currentrev in revs:
2017 # Heap works with smallest element, we want highest so we invert
2025 # Heap works with smallest element, we want highest so we invert
2018 if currentrev not in pendingset:
2026 if currentrev not in pendingset:
2019 heappush(pendingheap, -currentrev)
2027 heappush(pendingheap, -currentrev)
2020 pendingset.add(currentrev)
2028 pendingset.add(currentrev)
2021 # iterates on pending rev until after the current rev have been
2029 # iterates on pending rev until after the current rev have been
2022 # processed.
2030 # processed.
2023 rev = None
2031 rev = None
2024 while rev != currentrev:
2032 while rev != currentrev:
2025 rev = -heappop(pendingheap)
2033 rev = -heappop(pendingheap)
2026 pendingset.remove(rev)
2034 pendingset.remove(rev)
2027
2035
2028 # Seek for a subgroup blocked, waiting for the current revision.
2036 # Seek for a subgroup blocked, waiting for the current revision.
2029 matching = [i for i, g in enumerate(groups) if rev in g[1]]
2037 matching = [i for i, g in enumerate(groups) if rev in g[1]]
2030
2038
2031 if matching:
2039 if matching:
2032 # The main idea is to gather together all sets that are blocked
2040 # The main idea is to gather together all sets that are blocked
2033 # on the same revision.
2041 # on the same revision.
2034 #
2042 #
2035 # Groups are merged when a common blocking ancestor is
2043 # Groups are merged when a common blocking ancestor is
2036 # observed. For example, given two groups:
2044 # observed. For example, given two groups:
2037 #
2045 #
2038 # revs [5, 4] waiting for 1
2046 # revs [5, 4] waiting for 1
2039 # revs [3, 2] waiting for 1
2047 # revs [3, 2] waiting for 1
2040 #
2048 #
2041 # These two groups will be merged when we process
2049 # These two groups will be merged when we process
2042 # 1. In theory, we could have merged the groups when
2050 # 1. In theory, we could have merged the groups when
2043 # we added 2 to the group it is now in (we could have
2051 # we added 2 to the group it is now in (we could have
2044 # noticed the groups were both blocked on 1 then), but
2052 # noticed the groups were both blocked on 1 then), but
2045 # the way it works now makes the algorithm simpler.
2053 # the way it works now makes the algorithm simpler.
2046 #
2054 #
2047 # We also always keep the oldest subgroup first. We can
2055 # We also always keep the oldest subgroup first. We can
2048 # probably improve the behavior by having the longest set
2056 # probably improve the behavior by having the longest set
2049 # first. That way, graph algorithms could minimise the length
2057 # first. That way, graph algorithms could minimise the length
2050 # of parallel lines their drawing. This is currently not done.
2058 # of parallel lines their drawing. This is currently not done.
2051 targetidx = matching.pop(0)
2059 targetidx = matching.pop(0)
2052 trevs, tparents = groups[targetidx]
2060 trevs, tparents = groups[targetidx]
2053 for i in matching:
2061 for i in matching:
2054 gr = groups[i]
2062 gr = groups[i]
2055 trevs.extend(gr[0])
2063 trevs.extend(gr[0])
2056 tparents |= gr[1]
2064 tparents |= gr[1]
2057 # delete all merged subgroups (except the one we kept)
2065 # delete all merged subgroups (except the one we kept)
2058 # (starting from the last subgroup for performance and
2066 # (starting from the last subgroup for performance and
2059 # sanity reasons)
2067 # sanity reasons)
2060 for i in reversed(matching):
2068 for i in reversed(matching):
2061 del groups[i]
2069 del groups[i]
2062 else:
2070 else:
2063 # This is a new head. We create a new subgroup for it.
2071 # This is a new head. We create a new subgroup for it.
2064 targetidx = len(groups)
2072 targetidx = len(groups)
2065 groups.append(([], set([rev])))
2073 groups.append(([], set([rev])))
2066
2074
2067 gr = groups[targetidx]
2075 gr = groups[targetidx]
2068
2076
2069 # We now add the current nodes to this subgroups. This is done
2077 # We now add the current nodes to this subgroups. This is done
2070 # after the subgroup merging because all elements from a subgroup
2078 # after the subgroup merging because all elements from a subgroup
2071 # that relied on this rev must precede it.
2079 # that relied on this rev must precede it.
2072 #
2080 #
2073 # we also update the <parents> set to include the parents of the
2081 # we also update the <parents> set to include the parents of the
2074 # new nodes.
2082 # new nodes.
2075 if rev == currentrev: # only display stuff in rev
2083 if rev == currentrev: # only display stuff in rev
2076 gr[0].append(rev)
2084 gr[0].append(rev)
2077 gr[1].remove(rev)
2085 gr[1].remove(rev)
2078 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2086 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2079 gr[1].update(parents)
2087 gr[1].update(parents)
2080 for p in parents:
2088 for p in parents:
2081 if p not in pendingset:
2089 if p not in pendingset:
2082 pendingset.add(p)
2090 pendingset.add(p)
2083 heappush(pendingheap, -p)
2091 heappush(pendingheap, -p)
2084
2092
2085 # Look for a subgroup to display
2093 # Look for a subgroup to display
2086 #
2094 #
2087 # When unblocked is empty (if clause), we were not waiting for any
2095 # When unblocked is empty (if clause), we were not waiting for any
2088 # revisions during the first iteration (if no priority was given) or
2096 # revisions during the first iteration (if no priority was given) or
2089 # if we emitted a whole disconnected set of the graph (reached a
2097 # if we emitted a whole disconnected set of the graph (reached a
2090 # root). In that case we arbitrarily take the oldest known
2098 # root). In that case we arbitrarily take the oldest known
2091 # subgroup. The heuristic could probably be better.
2099 # subgroup. The heuristic could probably be better.
2092 #
2100 #
2093 # Otherwise (elif clause) if the subgroup is blocked on
2101 # Otherwise (elif clause) if the subgroup is blocked on
2094 # a revision we just emitted, we can safely emit it as
2102 # a revision we just emitted, we can safely emit it as
2095 # well.
2103 # well.
2096 if not unblocked:
2104 if not unblocked:
2097 if len(groups) > 1: # display other subset
2105 if len(groups) > 1: # display other subset
2098 targetidx = 1
2106 targetidx = 1
2099 gr = groups[1]
2107 gr = groups[1]
2100 elif not gr[1] & unblocked:
2108 elif not gr[1] & unblocked:
2101 gr = None
2109 gr = None
2102
2110
2103 if gr is not None:
2111 if gr is not None:
2104 # update the set of awaited revisions with the one from the
2112 # update the set of awaited revisions with the one from the
2105 # subgroup
2113 # subgroup
2106 unblocked |= gr[1]
2114 unblocked |= gr[1]
2107 # output all revisions in the subgroup
2115 # output all revisions in the subgroup
2108 for r in gr[0]:
2116 for r in gr[0]:
2109 yield r
2117 yield r
2110 # delete the subgroup that you just output
2118 # delete the subgroup that you just output
2111 # unless it is groups[0] in which case you just empty it.
2119 # unless it is groups[0] in which case you just empty it.
2112 if targetidx:
2120 if targetidx:
2113 del groups[targetidx]
2121 del groups[targetidx]
2114 else:
2122 else:
2115 gr[0][:] = []
2123 gr[0][:] = []
2116 # Check if we have some subgroup waiting for revisions we are not going to
2124 # Check if we have some subgroup waiting for revisions we are not going to
2117 # iterate over
2125 # iterate over
2118 for g in groups:
2126 for g in groups:
2119 for r in g[0]:
2127 for r in g[0]:
2120 yield r
2128 yield r
2121
2129
2122 @predicate('subrepo([pattern])')
2130 @predicate('subrepo([pattern])')
2123 def subrepo(repo, subset, x):
2131 def subrepo(repo, subset, x):
2124 """Changesets that add, modify or remove the given subrepo. If no subrepo
2132 """Changesets that add, modify or remove the given subrepo. If no subrepo
2125 pattern is named, any subrepo changes are returned.
2133 pattern is named, any subrepo changes are returned.
2126 """
2134 """
2127 # i18n: "subrepo" is a keyword
2135 # i18n: "subrepo" is a keyword
2128 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2136 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2129 pat = None
2137 pat = None
2130 if len(args) != 0:
2138 if len(args) != 0:
2131 pat = getstring(args[0], _("subrepo requires a pattern"))
2139 pat = getstring(args[0], _("subrepo requires a pattern"))
2132
2140
2133 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2141 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2134
2142
2135 def submatches(names):
2143 def submatches(names):
2136 k, p, m = util.stringmatcher(pat)
2144 k, p, m = util.stringmatcher(pat)
2137 for name in names:
2145 for name in names:
2138 if m(name):
2146 if m(name):
2139 yield name
2147 yield name
2140
2148
2141 def matches(x):
2149 def matches(x):
2142 c = repo[x]
2150 c = repo[x]
2143 s = repo.status(c.p1().node(), c.node(), match=m)
2151 s = repo.status(c.p1().node(), c.node(), match=m)
2144
2152
2145 if pat is None:
2153 if pat is None:
2146 return s.added or s.modified or s.removed
2154 return s.added or s.modified or s.removed
2147
2155
2148 if s.added:
2156 if s.added:
2149 return any(submatches(c.substate.keys()))
2157 return any(submatches(c.substate.keys()))
2150
2158
2151 if s.modified:
2159 if s.modified:
2152 subs = set(c.p1().substate.keys())
2160 subs = set(c.p1().substate.keys())
2153 subs.update(c.substate.keys())
2161 subs.update(c.substate.keys())
2154
2162
2155 for path in submatches(subs):
2163 for path in submatches(subs):
2156 if c.p1().substate.get(path) != c.substate.get(path):
2164 if c.p1().substate.get(path) != c.substate.get(path):
2157 return True
2165 return True
2158
2166
2159 if s.removed:
2167 if s.removed:
2160 return any(submatches(c.p1().substate.keys()))
2168 return any(submatches(c.p1().substate.keys()))
2161
2169
2162 return False
2170 return False
2163
2171
2164 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2172 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2165
2173
2166 def _substringmatcher(pattern):
2174 def _substringmatcher(pattern):
2167 kind, pattern, matcher = util.stringmatcher(pattern)
2175 kind, pattern, matcher = util.stringmatcher(pattern)
2168 if kind == 'literal':
2176 if kind == 'literal':
2169 matcher = lambda s: pattern in s
2177 matcher = lambda s: pattern in s
2170 return kind, pattern, matcher
2178 return kind, pattern, matcher
2171
2179
2172 @predicate('tag([name])', safe=True)
2180 @predicate('tag([name])', safe=True)
2173 def tag(repo, subset, x):
2181 def tag(repo, subset, x):
2174 """The specified tag by name, or all tagged revisions if no name is given.
2182 """The specified tag by name, or all tagged revisions if no name is given.
2175
2183
2176 If `name` starts with `re:`, the remainder of the name is treated as
2184 If `name` starts with `re:`, the remainder of the name is treated as
2177 a regular expression. To match a tag that actually starts with `re:`,
2185 a regular expression. To match a tag that actually starts with `re:`,
2178 use the prefix `literal:`.
2186 use the prefix `literal:`.
2179 """
2187 """
2180 # i18n: "tag" is a keyword
2188 # i18n: "tag" is a keyword
2181 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2189 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2182 cl = repo.changelog
2190 cl = repo.changelog
2183 if args:
2191 if args:
2184 pattern = getstring(args[0],
2192 pattern = getstring(args[0],
2185 # i18n: "tag" is a keyword
2193 # i18n: "tag" is a keyword
2186 _('the argument to tag must be a string'))
2194 _('the argument to tag must be a string'))
2187 kind, pattern, matcher = util.stringmatcher(pattern)
2195 kind, pattern, matcher = util.stringmatcher(pattern)
2188 if kind == 'literal':
2196 if kind == 'literal':
2189 # avoid resolving all tags
2197 # avoid resolving all tags
2190 tn = repo._tagscache.tags.get(pattern, None)
2198 tn = repo._tagscache.tags.get(pattern, None)
2191 if tn is None:
2199 if tn is None:
2192 raise error.RepoLookupError(_("tag '%s' does not exist")
2200 raise error.RepoLookupError(_("tag '%s' does not exist")
2193 % pattern)
2201 % pattern)
2194 s = set([repo[tn].rev()])
2202 s = set([repo[tn].rev()])
2195 else:
2203 else:
2196 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2204 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2197 else:
2205 else:
2198 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2206 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2199 return subset & s
2207 return subset & s
2200
2208
2201 @predicate('tagged', safe=True)
2209 @predicate('tagged', safe=True)
2202 def tagged(repo, subset, x):
2210 def tagged(repo, subset, x):
2203 return tag(repo, subset, x)
2211 return tag(repo, subset, x)
2204
2212
2205 @predicate('unstable()', safe=True)
2213 @predicate('unstable()', safe=True)
2206 def unstable(repo, subset, x):
2214 def unstable(repo, subset, x):
2207 """Non-obsolete changesets with obsolete ancestors.
2215 """Non-obsolete changesets with obsolete ancestors.
2208 """
2216 """
2209 # i18n: "unstable" is a keyword
2217 # i18n: "unstable" is a keyword
2210 getargs(x, 0, 0, _("unstable takes no arguments"))
2218 getargs(x, 0, 0, _("unstable takes no arguments"))
2211 unstables = obsmod.getrevs(repo, 'unstable')
2219 unstables = obsmod.getrevs(repo, 'unstable')
2212 return subset & unstables
2220 return subset & unstables
2213
2221
2214
2222
2215 @predicate('user(string)', safe=True)
2223 @predicate('user(string)', safe=True)
2216 def user(repo, subset, x):
2224 def user(repo, subset, x):
2217 """User name contains string. The match is case-insensitive.
2225 """User name contains string. The match is case-insensitive.
2218
2226
2219 If `string` starts with `re:`, the remainder of the string is treated as
2227 If `string` starts with `re:`, the remainder of the string is treated as
2220 a regular expression. To match a user that actually contains `re:`, use
2228 a regular expression. To match a user that actually contains `re:`, use
2221 the prefix `literal:`.
2229 the prefix `literal:`.
2222 """
2230 """
2223 return author(repo, subset, x)
2231 return author(repo, subset, x)
2224
2232
2225 # experimental
2233 # experimental
2226 @predicate('wdir', safe=True)
2234 @predicate('wdir', safe=True)
2227 def wdir(repo, subset, x):
2235 def wdir(repo, subset, x):
2228 # i18n: "wdir" is a keyword
2236 # i18n: "wdir" is a keyword
2229 getargs(x, 0, 0, _("wdir takes no arguments"))
2237 getargs(x, 0, 0, _("wdir takes no arguments"))
2230 if node.wdirrev in subset or isinstance(subset, fullreposet):
2238 if node.wdirrev in subset or isinstance(subset, fullreposet):
2231 return baseset([node.wdirrev])
2239 return baseset([node.wdirrev])
2232 return baseset()
2240 return baseset()
2233
2241
2234 # for internal use
2242 # for internal use
2235 @predicate('_list', safe=True)
2243 @predicate('_list', safe=True)
2236 def _list(repo, subset, x):
2244 def _list(repo, subset, x):
2237 s = getstring(x, "internal error")
2245 s = getstring(x, "internal error")
2238 if not s:
2246 if not s:
2239 return baseset()
2247 return baseset()
2240 # remove duplicates here. it's difficult for caller to deduplicate sets
2248 # remove duplicates here. it's difficult for caller to deduplicate sets
2241 # because different symbols can point to the same rev.
2249 # because different symbols can point to the same rev.
2242 cl = repo.changelog
2250 cl = repo.changelog
2243 ls = []
2251 ls = []
2244 seen = set()
2252 seen = set()
2245 for t in s.split('\0'):
2253 for t in s.split('\0'):
2246 try:
2254 try:
2247 # fast path for integer revision
2255 # fast path for integer revision
2248 r = int(t)
2256 r = int(t)
2249 if str(r) != t or r not in cl:
2257 if str(r) != t or r not in cl:
2250 raise ValueError
2258 raise ValueError
2251 revs = [r]
2259 revs = [r]
2252 except ValueError:
2260 except ValueError:
2253 revs = stringset(repo, subset, t)
2261 revs = stringset(repo, subset, t)
2254
2262
2255 for r in revs:
2263 for r in revs:
2256 if r in seen:
2264 if r in seen:
2257 continue
2265 continue
2258 if (r in subset
2266 if (r in subset
2259 or r == node.nullrev and isinstance(subset, fullreposet)):
2267 or r == node.nullrev and isinstance(subset, fullreposet)):
2260 ls.append(r)
2268 ls.append(r)
2261 seen.add(r)
2269 seen.add(r)
2262 return baseset(ls)
2270 return baseset(ls)
2263
2271
2264 # for internal use
2272 # for internal use
2265 @predicate('_intlist', safe=True)
2273 @predicate('_intlist', safe=True)
2266 def _intlist(repo, subset, x):
2274 def _intlist(repo, subset, x):
2267 s = getstring(x, "internal error")
2275 s = getstring(x, "internal error")
2268 if not s:
2276 if not s:
2269 return baseset()
2277 return baseset()
2270 ls = [int(r) for r in s.split('\0')]
2278 ls = [int(r) for r in s.split('\0')]
2271 s = subset
2279 s = subset
2272 return baseset([r for r in ls if r in s])
2280 return baseset([r for r in ls if r in s])
2273
2281
2274 # for internal use
2282 # for internal use
2275 @predicate('_hexlist', safe=True)
2283 @predicate('_hexlist', safe=True)
2276 def _hexlist(repo, subset, x):
2284 def _hexlist(repo, subset, x):
2277 s = getstring(x, "internal error")
2285 s = getstring(x, "internal error")
2278 if not s:
2286 if not s:
2279 return baseset()
2287 return baseset()
2280 cl = repo.changelog
2288 cl = repo.changelog
2281 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2289 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2282 s = subset
2290 s = subset
2283 return baseset([r for r in ls if r in s])
2291 return baseset([r for r in ls if r in s])
2284
2292
2285 methods = {
2293 methods = {
2286 "range": rangeset,
2294 "range": rangeset,
2287 "dagrange": dagrange,
2295 "dagrange": dagrange,
2288 "string": stringset,
2296 "string": stringset,
2289 "symbol": stringset,
2297 "symbol": stringset,
2290 "and": andset,
2298 "and": andset,
2291 "or": orset,
2299 "or": orset,
2292 "not": notset,
2300 "not": notset,
2293 "difference": differenceset,
2301 "difference": differenceset,
2294 "list": listset,
2302 "list": listset,
2295 "keyvalue": keyvaluepair,
2303 "keyvalue": keyvaluepair,
2296 "func": func,
2304 "func": func,
2297 "ancestor": ancestorspec,
2305 "ancestor": ancestorspec,
2298 "parent": parentspec,
2306 "parent": parentspec,
2299 "parentpost": p1,
2307 "parentpost": p1,
2300 }
2308 }
2301
2309
2302 def _matchonly(revs, bases):
2310 def _matchonly(revs, bases):
2303 """
2311 """
2304 >>> f = lambda *args: _matchonly(*map(parse, args))
2312 >>> f = lambda *args: _matchonly(*map(parse, args))
2305 >>> f('ancestors(A)', 'not ancestors(B)')
2313 >>> f('ancestors(A)', 'not ancestors(B)')
2306 ('list', ('symbol', 'A'), ('symbol', 'B'))
2314 ('list', ('symbol', 'A'), ('symbol', 'B'))
2307 """
2315 """
2308 if (revs is not None
2316 if (revs is not None
2309 and revs[0] == 'func'
2317 and revs[0] == 'func'
2310 and getsymbol(revs[1]) == 'ancestors'
2318 and getsymbol(revs[1]) == 'ancestors'
2311 and bases is not None
2319 and bases is not None
2312 and bases[0] == 'not'
2320 and bases[0] == 'not'
2313 and bases[1][0] == 'func'
2321 and bases[1][0] == 'func'
2314 and getsymbol(bases[1][1]) == 'ancestors'):
2322 and getsymbol(bases[1][1]) == 'ancestors'):
2315 return ('list', revs[2], bases[1][2])
2323 return ('list', revs[2], bases[1][2])
2316
2324
2317 def _fixops(x):
2325 def _fixops(x):
2318 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
2326 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
2319 handled well by our simple top-down parser"""
2327 handled well by our simple top-down parser"""
2320 if not isinstance(x, tuple):
2328 if not isinstance(x, tuple):
2321 return x
2329 return x
2322
2330
2323 op = x[0]
2331 op = x[0]
2324 if op == 'parent':
2332 if op == 'parent':
2325 # x^:y means (x^) : y, not x ^ (:y)
2333 # x^:y means (x^) : y, not x ^ (:y)
2326 # x^: means (x^) :, not x ^ (:)
2334 # x^: means (x^) :, not x ^ (:)
2327 post = ('parentpost', x[1])
2335 post = ('parentpost', x[1])
2328 if x[2][0] == 'dagrangepre':
2336 if x[2][0] == 'dagrangepre':
2329 return _fixops(('dagrange', post, x[2][1]))
2337 return _fixops(('dagrange', post, x[2][1]))
2330 elif x[2][0] == 'rangepre':
2338 elif x[2][0] == 'rangepre':
2331 return _fixops(('range', post, x[2][1]))
2339 return _fixops(('range', post, x[2][1]))
2332 elif x[2][0] == 'rangeall':
2340 elif x[2][0] == 'rangeall':
2333 return _fixops(('rangepost', post))
2341 return _fixops(('rangepost', post))
2334
2342
2335 return (op,) + tuple(_fixops(y) for y in x[1:])
2343 return (op,) + tuple(_fixops(y) for y in x[1:])
2336
2344
2337 def _optimize(x, small):
2345 def _optimize(x, small):
2338 if x is None:
2346 if x is None:
2339 return 0, x
2347 return 0, x
2340
2348
2341 smallbonus = 1
2349 smallbonus = 1
2342 if small:
2350 if small:
2343 smallbonus = .5
2351 smallbonus = .5
2344
2352
2345 op = x[0]
2353 op = x[0]
2346 if op == 'minus':
2354 if op == 'minus':
2347 return _optimize(('and', x[1], ('not', x[2])), small)
2355 return _optimize(('and', x[1], ('not', x[2])), small)
2348 elif op == 'only':
2356 elif op == 'only':
2349 t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
2357 t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
2350 return _optimize(t, small)
2358 return _optimize(t, small)
2351 elif op == 'onlypost':
2359 elif op == 'onlypost':
2352 return _optimize(('func', ('symbol', 'only'), x[1]), small)
2360 return _optimize(('func', ('symbol', 'only'), x[1]), small)
2353 elif op == 'dagrangepre':
2361 elif op == 'dagrangepre':
2354 return _optimize(('func', ('symbol', 'ancestors'), x[1]), small)
2362 return _optimize(('func', ('symbol', 'ancestors'), x[1]), small)
2355 elif op == 'dagrangepost':
2363 elif op == 'dagrangepost':
2356 return _optimize(('func', ('symbol', 'descendants'), x[1]), small)
2364 return _optimize(('func', ('symbol', 'descendants'), x[1]), small)
2357 elif op == 'rangeall':
2365 elif op == 'rangeall':
2358 return _optimize(('range', ('string', '0'), ('string', 'tip')), small)
2366 return _optimize(('range', ('string', '0'), ('string', 'tip')), small)
2359 elif op == 'rangepre':
2367 elif op == 'rangepre':
2360 return _optimize(('range', ('string', '0'), x[1]), small)
2368 return _optimize(('range', ('string', '0'), x[1]), small)
2361 elif op == 'rangepost':
2369 elif op == 'rangepost':
2362 return _optimize(('range', x[1], ('string', 'tip')), small)
2370 return _optimize(('range', x[1], ('string', 'tip')), small)
2363 elif op == 'negate':
2371 elif op == 'negate':
2364 s = getstring(x[1], _("can't negate that"))
2372 s = getstring(x[1], _("can't negate that"))
2365 return _optimize(('string', '-' + s), small)
2373 return _optimize(('string', '-' + s), small)
2366 elif op in 'string symbol negate':
2374 elif op in 'string symbol negate':
2367 return smallbonus, x # single revisions are small
2375 return smallbonus, x # single revisions are small
2368 elif op == 'and':
2376 elif op == 'and':
2369 wa, ta = _optimize(x[1], True)
2377 wa, ta = _optimize(x[1], True)
2370 wb, tb = _optimize(x[2], True)
2378 wb, tb = _optimize(x[2], True)
2371 w = min(wa, wb)
2379 w = min(wa, wb)
2372
2380
2373 # (::x and not ::y)/(not ::y and ::x) have a fast path
2381 # (::x and not ::y)/(not ::y and ::x) have a fast path
2374 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2382 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2375 if tm:
2383 if tm:
2376 return w, ('func', ('symbol', 'only'), tm)
2384 return w, ('func', ('symbol', 'only'), tm)
2377
2385
2378 if tb is not None and tb[0] == 'not':
2386 if tb is not None and tb[0] == 'not':
2379 return wa, ('difference', ta, tb[1])
2387 return wa, ('difference', ta, tb[1])
2380
2388
2381 if wa > wb:
2389 if wa > wb:
2382 return w, (op, tb, ta)
2390 return w, (op, tb, ta)
2383 return w, (op, ta, tb)
2391 return w, (op, ta, tb)
2384 elif op == 'or':
2392 elif op == 'or':
2385 # fast path for machine-generated expression, that is likely to have
2393 # fast path for machine-generated expression, that is likely to have
2386 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2394 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2387 ws, ts, ss = [], [], []
2395 ws, ts, ss = [], [], []
2388 def flushss():
2396 def flushss():
2389 if not ss:
2397 if not ss:
2390 return
2398 return
2391 if len(ss) == 1:
2399 if len(ss) == 1:
2392 w, t = ss[0]
2400 w, t = ss[0]
2393 else:
2401 else:
2394 s = '\0'.join(t[1] for w, t in ss)
2402 s = '\0'.join(t[1] for w, t in ss)
2395 y = ('func', ('symbol', '_list'), ('string', s))
2403 y = ('func', ('symbol', '_list'), ('string', s))
2396 w, t = _optimize(y, False)
2404 w, t = _optimize(y, False)
2397 ws.append(w)
2405 ws.append(w)
2398 ts.append(t)
2406 ts.append(t)
2399 del ss[:]
2407 del ss[:]
2400 for y in x[1:]:
2408 for y in x[1:]:
2401 w, t = _optimize(y, False)
2409 w, t = _optimize(y, False)
2402 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2410 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2403 ss.append((w, t))
2411 ss.append((w, t))
2404 continue
2412 continue
2405 flushss()
2413 flushss()
2406 ws.append(w)
2414 ws.append(w)
2407 ts.append(t)
2415 ts.append(t)
2408 flushss()
2416 flushss()
2409 if len(ts) == 1:
2417 if len(ts) == 1:
2410 return ws[0], ts[0] # 'or' operation is fully optimized out
2418 return ws[0], ts[0] # 'or' operation is fully optimized out
2411 # we can't reorder trees by weight because it would change the order.
2419 # we can't reorder trees by weight because it would change the order.
2412 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2420 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2413 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2421 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2414 return max(ws), (op,) + tuple(ts)
2422 return max(ws), (op,) + tuple(ts)
2415 elif op == 'not':
2423 elif op == 'not':
2416 # Optimize not public() to _notpublic() because we have a fast version
2424 # Optimize not public() to _notpublic() because we have a fast version
2417 if x[1] == ('func', ('symbol', 'public'), None):
2425 if x[1] == ('func', ('symbol', 'public'), None):
2418 newsym = ('func', ('symbol', '_notpublic'), None)
2426 newsym = ('func', ('symbol', '_notpublic'), None)
2419 o = _optimize(newsym, not small)
2427 o = _optimize(newsym, not small)
2420 return o[0], o[1]
2428 return o[0], o[1]
2421 else:
2429 else:
2422 o = _optimize(x[1], not small)
2430 o = _optimize(x[1], not small)
2423 return o[0], (op, o[1])
2431 return o[0], (op, o[1])
2424 elif op == 'parentpost':
2432 elif op == 'parentpost':
2425 o = _optimize(x[1], small)
2433 o = _optimize(x[1], small)
2426 return o[0], (op, o[1])
2434 return o[0], (op, o[1])
2427 elif op == 'group':
2435 elif op == 'group':
2428 return _optimize(x[1], small)
2436 return _optimize(x[1], small)
2429 elif op in 'dagrange range parent ancestorspec':
2437 elif op in 'dagrange range parent ancestorspec':
2430 wa, ta = _optimize(x[1], small)
2438 wa, ta = _optimize(x[1], small)
2431 wb, tb = _optimize(x[2], small)
2439 wb, tb = _optimize(x[2], small)
2432 return wa + wb, (op, ta, tb)
2440 return wa + wb, (op, ta, tb)
2433 elif op == 'list':
2441 elif op == 'list':
2434 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2442 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2435 return sum(ws), (op,) + ts
2443 return sum(ws), (op,) + ts
2436 elif op == 'keyvalue':
2444 elif op == 'keyvalue':
2437 w, t = _optimize(x[2], small)
2445 w, t = _optimize(x[2], small)
2438 return w, (op, x[1], t)
2446 return w, (op, x[1], t)
2439 elif op == 'func':
2447 elif op == 'func':
2440 f = getsymbol(x[1])
2448 f = getsymbol(x[1])
2441 wa, ta = _optimize(x[2], small)
2449 wa, ta = _optimize(x[2], small)
2442 if f in ("author branch closed date desc file grep keyword "
2450 if f in ("author branch closed date desc file grep keyword "
2443 "outgoing user"):
2451 "outgoing user"):
2444 w = 10 # slow
2452 w = 10 # slow
2445 elif f in "modifies adds removes":
2453 elif f in "modifies adds removes":
2446 w = 30 # slower
2454 w = 30 # slower
2447 elif f == "contains":
2455 elif f == "contains":
2448 w = 100 # very slow
2456 w = 100 # very slow
2449 elif f == "ancestor":
2457 elif f == "ancestor":
2450 w = 1 * smallbonus
2458 w = 1 * smallbonus
2451 elif f in "reverse limit first _intlist":
2459 elif f in "reverse limit first _intlist":
2452 w = 0
2460 w = 0
2453 elif f in "sort":
2461 elif f in "sort":
2454 w = 10 # assume most sorts look at changelog
2462 w = 10 # assume most sorts look at changelog
2455 else:
2463 else:
2456 w = 1
2464 w = 1
2457 return w + wa, (op, x[1], ta)
2465 return w + wa, (op, x[1], ta)
2458 return 1, x
2466 return 1, x
2459
2467
2460 def optimize(tree):
2468 def optimize(tree):
2461 _weight, newtree = _optimize(tree, small=True)
2469 _weight, newtree = _optimize(tree, small=True)
2462 return newtree
2470 return newtree
2463
2471
2464 # the set of valid characters for the initial letter of symbols in
2472 # the set of valid characters for the initial letter of symbols in
2465 # alias declarations and definitions
2473 # alias declarations and definitions
2466 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2474 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2467 if c.isalnum() or c in '._@$' or ord(c) > 127)
2475 if c.isalnum() or c in '._@$' or ord(c) > 127)
2468
2476
2469 def _parsewith(spec, lookup=None, syminitletters=None):
2477 def _parsewith(spec, lookup=None, syminitletters=None):
2470 """Generate a parse tree of given spec with given tokenizing options
2478 """Generate a parse tree of given spec with given tokenizing options
2471
2479
2472 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
2480 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
2473 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2481 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2474 >>> _parsewith('$1')
2482 >>> _parsewith('$1')
2475 Traceback (most recent call last):
2483 Traceback (most recent call last):
2476 ...
2484 ...
2477 ParseError: ("syntax error in revset '$1'", 0)
2485 ParseError: ("syntax error in revset '$1'", 0)
2478 >>> _parsewith('foo bar')
2486 >>> _parsewith('foo bar')
2479 Traceback (most recent call last):
2487 Traceback (most recent call last):
2480 ...
2488 ...
2481 ParseError: ('invalid token', 4)
2489 ParseError: ('invalid token', 4)
2482 """
2490 """
2483 p = parser.parser(elements)
2491 p = parser.parser(elements)
2484 tree, pos = p.parse(tokenize(spec, lookup=lookup,
2492 tree, pos = p.parse(tokenize(spec, lookup=lookup,
2485 syminitletters=syminitletters))
2493 syminitletters=syminitletters))
2486 if pos != len(spec):
2494 if pos != len(spec):
2487 raise error.ParseError(_('invalid token'), pos)
2495 raise error.ParseError(_('invalid token'), pos)
2488 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
2496 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
2489
2497
2490 class _aliasrules(parser.basealiasrules):
2498 class _aliasrules(parser.basealiasrules):
2491 """Parsing and expansion rule set of revset aliases"""
2499 """Parsing and expansion rule set of revset aliases"""
2492 _section = _('revset alias')
2500 _section = _('revset alias')
2493
2501
2494 @staticmethod
2502 @staticmethod
2495 def _parse(spec):
2503 def _parse(spec):
2496 """Parse alias declaration/definition ``spec``
2504 """Parse alias declaration/definition ``spec``
2497
2505
2498 This allows symbol names to use also ``$`` as an initial letter
2506 This allows symbol names to use also ``$`` as an initial letter
2499 (for backward compatibility), and callers of this function should
2507 (for backward compatibility), and callers of this function should
2500 examine whether ``$`` is used also for unexpected symbols or not.
2508 examine whether ``$`` is used also for unexpected symbols or not.
2501 """
2509 """
2502 return _parsewith(spec, syminitletters=_aliassyminitletters)
2510 return _parsewith(spec, syminitletters=_aliassyminitletters)
2503
2511
2504 @staticmethod
2512 @staticmethod
2505 def _trygetfunc(tree):
2513 def _trygetfunc(tree):
2506 if tree[0] == 'func' and tree[1][0] == 'symbol':
2514 if tree[0] == 'func' and tree[1][0] == 'symbol':
2507 return tree[1][1], getlist(tree[2])
2515 return tree[1][1], getlist(tree[2])
2508
2516
2509 def expandaliases(ui, tree, showwarning=None):
2517 def expandaliases(ui, tree, showwarning=None):
2510 aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))
2518 aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))
2511 tree = _aliasrules.expand(aliases, tree)
2519 tree = _aliasrules.expand(aliases, tree)
2512 if showwarning:
2520 if showwarning:
2513 # warn about problematic (but not referred) aliases
2521 # warn about problematic (but not referred) aliases
2514 for name, alias in sorted(aliases.iteritems()):
2522 for name, alias in sorted(aliases.iteritems()):
2515 if alias.error and not alias.warned:
2523 if alias.error and not alias.warned:
2516 showwarning(_('warning: %s\n') % (alias.error))
2524 showwarning(_('warning: %s\n') % (alias.error))
2517 alias.warned = True
2525 alias.warned = True
2518 return tree
2526 return tree
2519
2527
2520 def foldconcat(tree):
2528 def foldconcat(tree):
2521 """Fold elements to be concatenated by `##`
2529 """Fold elements to be concatenated by `##`
2522 """
2530 """
2523 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2531 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2524 return tree
2532 return tree
2525 if tree[0] == '_concat':
2533 if tree[0] == '_concat':
2526 pending = [tree]
2534 pending = [tree]
2527 l = []
2535 l = []
2528 while pending:
2536 while pending:
2529 e = pending.pop()
2537 e = pending.pop()
2530 if e[0] == '_concat':
2538 if e[0] == '_concat':
2531 pending.extend(reversed(e[1:]))
2539 pending.extend(reversed(e[1:]))
2532 elif e[0] in ('string', 'symbol'):
2540 elif e[0] in ('string', 'symbol'):
2533 l.append(e[1])
2541 l.append(e[1])
2534 else:
2542 else:
2535 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2543 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2536 raise error.ParseError(msg)
2544 raise error.ParseError(msg)
2537 return ('string', ''.join(l))
2545 return ('string', ''.join(l))
2538 else:
2546 else:
2539 return tuple(foldconcat(t) for t in tree)
2547 return tuple(foldconcat(t) for t in tree)
2540
2548
2541 def parse(spec, lookup=None):
2549 def parse(spec, lookup=None):
2542 return _parsewith(spec, lookup=lookup)
2550 return _parsewith(spec, lookup=lookup)
2543
2551
2544 def posttreebuilthook(tree, repo):
2552 def posttreebuilthook(tree, repo):
2545 # hook for extensions to execute code on the optimized tree
2553 # hook for extensions to execute code on the optimized tree
2546 pass
2554 pass
2547
2555
2548 def match(ui, spec, repo=None):
2556 def match(ui, spec, repo=None):
2549 """Create a matcher for a single revision spec."""
2557 """Create a matcher for a single revision spec."""
2550 return matchany(ui, [spec], repo=repo)
2558 return matchany(ui, [spec], repo=repo)
2551
2559
2552 def matchany(ui, specs, repo=None):
2560 def matchany(ui, specs, repo=None):
2553 """Create a matcher that will include any revisions matching one of the
2561 """Create a matcher that will include any revisions matching one of the
2554 given specs"""
2562 given specs"""
2555 if not specs:
2563 if not specs:
2556 def mfunc(repo, subset=None):
2564 def mfunc(repo, subset=None):
2557 return baseset()
2565 return baseset()
2558 return mfunc
2566 return mfunc
2559 if not all(specs):
2567 if not all(specs):
2560 raise error.ParseError(_("empty query"))
2568 raise error.ParseError(_("empty query"))
2561 lookup = None
2569 lookup = None
2562 if repo:
2570 if repo:
2563 lookup = repo.__contains__
2571 lookup = repo.__contains__
2564 if len(specs) == 1:
2572 if len(specs) == 1:
2565 tree = parse(specs[0], lookup)
2573 tree = parse(specs[0], lookup)
2566 else:
2574 else:
2567 tree = ('or',) + tuple(parse(s, lookup) for s in specs)
2575 tree = ('or',) + tuple(parse(s, lookup) for s in specs)
2568 return _makematcher(ui, tree, repo)
2576 return _makematcher(ui, tree, repo)
2569
2577
2570 def _makematcher(ui, tree, repo):
2578 def _makematcher(ui, tree, repo):
2571 if ui:
2579 if ui:
2572 tree = expandaliases(ui, tree, showwarning=ui.warn)
2580 tree = expandaliases(ui, tree, showwarning=ui.warn)
2573 tree = foldconcat(tree)
2581 tree = foldconcat(tree)
2574 tree = optimize(tree)
2582 tree = optimize(tree)
2575 posttreebuilthook(tree, repo)
2583 posttreebuilthook(tree, repo)
2576 def mfunc(repo, subset=None):
2584 def mfunc(repo, subset=None):
2577 if subset is None:
2585 if subset is None:
2578 subset = fullreposet(repo)
2586 subset = fullreposet(repo)
2579 if util.safehasattr(subset, 'isascending'):
2587 if util.safehasattr(subset, 'isascending'):
2580 result = getset(repo, subset, tree)
2588 result = getset(repo, subset, tree)
2581 else:
2589 else:
2582 result = getset(repo, baseset(subset), tree)
2590 result = getset(repo, baseset(subset), tree)
2583 return result
2591 return result
2584 return mfunc
2592 return mfunc
2585
2593
2586 def formatspec(expr, *args):
2594 def formatspec(expr, *args):
2587 '''
2595 '''
2588 This is a convenience function for using revsets internally, and
2596 This is a convenience function for using revsets internally, and
2589 escapes arguments appropriately. Aliases are intentionally ignored
2597 escapes arguments appropriately. Aliases are intentionally ignored
2590 so that intended expression behavior isn't accidentally subverted.
2598 so that intended expression behavior isn't accidentally subverted.
2591
2599
2592 Supported arguments:
2600 Supported arguments:
2593
2601
2594 %r = revset expression, parenthesized
2602 %r = revset expression, parenthesized
2595 %d = int(arg), no quoting
2603 %d = int(arg), no quoting
2596 %s = string(arg), escaped and single-quoted
2604 %s = string(arg), escaped and single-quoted
2597 %b = arg.branch(), escaped and single-quoted
2605 %b = arg.branch(), escaped and single-quoted
2598 %n = hex(arg), single-quoted
2606 %n = hex(arg), single-quoted
2599 %% = a literal '%'
2607 %% = a literal '%'
2600
2608
2601 Prefixing the type with 'l' specifies a parenthesized list of that type.
2609 Prefixing the type with 'l' specifies a parenthesized list of that type.
2602
2610
2603 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2611 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2604 '(10 or 11):: and ((this()) or (that()))'
2612 '(10 or 11):: and ((this()) or (that()))'
2605 >>> formatspec('%d:: and not %d::', 10, 20)
2613 >>> formatspec('%d:: and not %d::', 10, 20)
2606 '10:: and not 20::'
2614 '10:: and not 20::'
2607 >>> formatspec('%ld or %ld', [], [1])
2615 >>> formatspec('%ld or %ld', [], [1])
2608 "_list('') or 1"
2616 "_list('') or 1"
2609 >>> formatspec('keyword(%s)', 'foo\\xe9')
2617 >>> formatspec('keyword(%s)', 'foo\\xe9')
2610 "keyword('foo\\\\xe9')"
2618 "keyword('foo\\\\xe9')"
2611 >>> b = lambda: 'default'
2619 >>> b = lambda: 'default'
2612 >>> b.branch = b
2620 >>> b.branch = b
2613 >>> formatspec('branch(%b)', b)
2621 >>> formatspec('branch(%b)', b)
2614 "branch('default')"
2622 "branch('default')"
2615 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2623 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2616 "root(_list('a\\x00b\\x00c\\x00d'))"
2624 "root(_list('a\\x00b\\x00c\\x00d'))"
2617 '''
2625 '''
2618
2626
2619 def quote(s):
2627 def quote(s):
2620 return repr(str(s))
2628 return repr(str(s))
2621
2629
2622 def argtype(c, arg):
2630 def argtype(c, arg):
2623 if c == 'd':
2631 if c == 'd':
2624 return str(int(arg))
2632 return str(int(arg))
2625 elif c == 's':
2633 elif c == 's':
2626 return quote(arg)
2634 return quote(arg)
2627 elif c == 'r':
2635 elif c == 'r':
2628 parse(arg) # make sure syntax errors are confined
2636 parse(arg) # make sure syntax errors are confined
2629 return '(%s)' % arg
2637 return '(%s)' % arg
2630 elif c == 'n':
2638 elif c == 'n':
2631 return quote(node.hex(arg))
2639 return quote(node.hex(arg))
2632 elif c == 'b':
2640 elif c == 'b':
2633 return quote(arg.branch())
2641 return quote(arg.branch())
2634
2642
2635 def listexp(s, t):
2643 def listexp(s, t):
2636 l = len(s)
2644 l = len(s)
2637 if l == 0:
2645 if l == 0:
2638 return "_list('')"
2646 return "_list('')"
2639 elif l == 1:
2647 elif l == 1:
2640 return argtype(t, s[0])
2648 return argtype(t, s[0])
2641 elif t == 'd':
2649 elif t == 'd':
2642 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2650 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2643 elif t == 's':
2651 elif t == 's':
2644 return "_list('%s')" % "\0".join(s)
2652 return "_list('%s')" % "\0".join(s)
2645 elif t == 'n':
2653 elif t == 'n':
2646 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2654 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2647 elif t == 'b':
2655 elif t == 'b':
2648 return "_list('%s')" % "\0".join(a.branch() for a in s)
2656 return "_list('%s')" % "\0".join(a.branch() for a in s)
2649
2657
2650 m = l // 2
2658 m = l // 2
2651 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2659 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2652
2660
2653 ret = ''
2661 ret = ''
2654 pos = 0
2662 pos = 0
2655 arg = 0
2663 arg = 0
2656 while pos < len(expr):
2664 while pos < len(expr):
2657 c = expr[pos]
2665 c = expr[pos]
2658 if c == '%':
2666 if c == '%':
2659 pos += 1
2667 pos += 1
2660 d = expr[pos]
2668 d = expr[pos]
2661 if d == '%':
2669 if d == '%':
2662 ret += d
2670 ret += d
2663 elif d in 'dsnbr':
2671 elif d in 'dsnbr':
2664 ret += argtype(d, args[arg])
2672 ret += argtype(d, args[arg])
2665 arg += 1
2673 arg += 1
2666 elif d == 'l':
2674 elif d == 'l':
2667 # a list of some type
2675 # a list of some type
2668 pos += 1
2676 pos += 1
2669 d = expr[pos]
2677 d = expr[pos]
2670 ret += listexp(list(args[arg]), d)
2678 ret += listexp(list(args[arg]), d)
2671 arg += 1
2679 arg += 1
2672 else:
2680 else:
2673 raise error.Abort(_('unexpected revspec format character %s')
2681 raise error.Abort(_('unexpected revspec format character %s')
2674 % d)
2682 % d)
2675 else:
2683 else:
2676 ret += c
2684 ret += c
2677 pos += 1
2685 pos += 1
2678
2686
2679 return ret
2687 return ret
2680
2688
2681 def prettyformat(tree):
2689 def prettyformat(tree):
2682 return parser.prettyformat(tree, ('string', 'symbol'))
2690 return parser.prettyformat(tree, ('string', 'symbol'))
2683
2691
2684 def depth(tree):
2692 def depth(tree):
2685 if isinstance(tree, tuple):
2693 if isinstance(tree, tuple):
2686 return max(map(depth, tree)) + 1
2694 return max(map(depth, tree)) + 1
2687 else:
2695 else:
2688 return 0
2696 return 0
2689
2697
2690 def funcsused(tree):
2698 def funcsused(tree):
2691 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2699 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2692 return set()
2700 return set()
2693 else:
2701 else:
2694 funcs = set()
2702 funcs = set()
2695 for s in tree[1:]:
2703 for s in tree[1:]:
2696 funcs |= funcsused(s)
2704 funcs |= funcsused(s)
2697 if tree[0] == 'func':
2705 if tree[0] == 'func':
2698 funcs.add(tree[1][1])
2706 funcs.add(tree[1][1])
2699 return funcs
2707 return funcs
2700
2708
2701 def _formatsetrepr(r):
2709 def _formatsetrepr(r):
2702 """Format an optional printable representation of a set
2710 """Format an optional printable representation of a set
2703
2711
2704 ======== =================================
2712 ======== =================================
2705 type(r) example
2713 type(r) example
2706 ======== =================================
2714 ======== =================================
2707 tuple ('<not %r>', other)
2715 tuple ('<not %r>', other)
2708 str '<branch closed>'
2716 str '<branch closed>'
2709 callable lambda: '<branch %r>' % sorted(b)
2717 callable lambda: '<branch %r>' % sorted(b)
2710 object other
2718 object other
2711 ======== =================================
2719 ======== =================================
2712 """
2720 """
2713 if r is None:
2721 if r is None:
2714 return ''
2722 return ''
2715 elif isinstance(r, tuple):
2723 elif isinstance(r, tuple):
2716 return r[0] % r[1:]
2724 return r[0] % r[1:]
2717 elif isinstance(r, str):
2725 elif isinstance(r, str):
2718 return r
2726 return r
2719 elif callable(r):
2727 elif callable(r):
2720 return r()
2728 return r()
2721 else:
2729 else:
2722 return repr(r)
2730 return repr(r)
2723
2731
2724 class abstractsmartset(object):
2732 class abstractsmartset(object):
2725
2733
2726 def __nonzero__(self):
2734 def __nonzero__(self):
2727 """True if the smartset is not empty"""
2735 """True if the smartset is not empty"""
2728 raise NotImplementedError()
2736 raise NotImplementedError()
2729
2737
2730 def __contains__(self, rev):
2738 def __contains__(self, rev):
2731 """provide fast membership testing"""
2739 """provide fast membership testing"""
2732 raise NotImplementedError()
2740 raise NotImplementedError()
2733
2741
2734 def __iter__(self):
2742 def __iter__(self):
2735 """iterate the set in the order it is supposed to be iterated"""
2743 """iterate the set in the order it is supposed to be iterated"""
2736 raise NotImplementedError()
2744 raise NotImplementedError()
2737
2745
2738 # Attributes containing a function to perform a fast iteration in a given
2746 # Attributes containing a function to perform a fast iteration in a given
2739 # direction. A smartset can have none, one, or both defined.
2747 # direction. A smartset can have none, one, or both defined.
2740 #
2748 #
2741 # Default value is None instead of a function returning None to avoid
2749 # Default value is None instead of a function returning None to avoid
2742 # initializing an iterator just for testing if a fast method exists.
2750 # initializing an iterator just for testing if a fast method exists.
2743 fastasc = None
2751 fastasc = None
2744 fastdesc = None
2752 fastdesc = None
2745
2753
2746 def isascending(self):
2754 def isascending(self):
2747 """True if the set will iterate in ascending order"""
2755 """True if the set will iterate in ascending order"""
2748 raise NotImplementedError()
2756 raise NotImplementedError()
2749
2757
2750 def isdescending(self):
2758 def isdescending(self):
2751 """True if the set will iterate in descending order"""
2759 """True if the set will iterate in descending order"""
2752 raise NotImplementedError()
2760 raise NotImplementedError()
2753
2761
2754 def istopo(self):
2762 def istopo(self):
2755 """True if the set will iterate in topographical order"""
2763 """True if the set will iterate in topographical order"""
2756 raise NotImplementedError()
2764 raise NotImplementedError()
2757
2765
2758 @util.cachefunc
2766 @util.cachefunc
2759 def min(self):
2767 def min(self):
2760 """return the minimum element in the set"""
2768 """return the minimum element in the set"""
2761 if self.fastasc is not None:
2769 if self.fastasc is not None:
2762 for r in self.fastasc():
2770 for r in self.fastasc():
2763 return r
2771 return r
2764 raise ValueError('arg is an empty sequence')
2772 raise ValueError('arg is an empty sequence')
2765 return min(self)
2773 return min(self)
2766
2774
2767 @util.cachefunc
2775 @util.cachefunc
2768 def max(self):
2776 def max(self):
2769 """return the maximum element in the set"""
2777 """return the maximum element in the set"""
2770 if self.fastdesc is not None:
2778 if self.fastdesc is not None:
2771 for r in self.fastdesc():
2779 for r in self.fastdesc():
2772 return r
2780 return r
2773 raise ValueError('arg is an empty sequence')
2781 raise ValueError('arg is an empty sequence')
2774 return max(self)
2782 return max(self)
2775
2783
2776 def first(self):
2784 def first(self):
2777 """return the first element in the set (user iteration perspective)
2785 """return the first element in the set (user iteration perspective)
2778
2786
2779 Return None if the set is empty"""
2787 Return None if the set is empty"""
2780 raise NotImplementedError()
2788 raise NotImplementedError()
2781
2789
2782 def last(self):
2790 def last(self):
2783 """return the last element in the set (user iteration perspective)
2791 """return the last element in the set (user iteration perspective)
2784
2792
2785 Return None if the set is empty"""
2793 Return None if the set is empty"""
2786 raise NotImplementedError()
2794 raise NotImplementedError()
2787
2795
2788 def __len__(self):
2796 def __len__(self):
2789 """return the length of the smartsets
2797 """return the length of the smartsets
2790
2798
2791 This can be expensive on smartset that could be lazy otherwise."""
2799 This can be expensive on smartset that could be lazy otherwise."""
2792 raise NotImplementedError()
2800 raise NotImplementedError()
2793
2801
2794 def reverse(self):
2802 def reverse(self):
2795 """reverse the expected iteration order"""
2803 """reverse the expected iteration order"""
2796 raise NotImplementedError()
2804 raise NotImplementedError()
2797
2805
2798 def sort(self, reverse=True):
2806 def sort(self, reverse=True):
2799 """get the set to iterate in an ascending or descending order"""
2807 """get the set to iterate in an ascending or descending order"""
2800 raise NotImplementedError()
2808 raise NotImplementedError()
2801
2809
2802 def __and__(self, other):
2810 def __and__(self, other):
2803 """Returns a new object with the intersection of the two collections.
2811 """Returns a new object with the intersection of the two collections.
2804
2812
2805 This is part of the mandatory API for smartset."""
2813 This is part of the mandatory API for smartset."""
2806 if isinstance(other, fullreposet):
2814 if isinstance(other, fullreposet):
2807 return self
2815 return self
2808 return self.filter(other.__contains__, condrepr=other, cache=False)
2816 return self.filter(other.__contains__, condrepr=other, cache=False)
2809
2817
2810 def __add__(self, other):
2818 def __add__(self, other):
2811 """Returns a new object with the union of the two collections.
2819 """Returns a new object with the union of the two collections.
2812
2820
2813 This is part of the mandatory API for smartset."""
2821 This is part of the mandatory API for smartset."""
2814 return addset(self, other)
2822 return addset(self, other)
2815
2823
2816 def __sub__(self, other):
2824 def __sub__(self, other):
2817 """Returns a new object with the substraction of the two collections.
2825 """Returns a new object with the substraction of the two collections.
2818
2826
2819 This is part of the mandatory API for smartset."""
2827 This is part of the mandatory API for smartset."""
2820 c = other.__contains__
2828 c = other.__contains__
2821 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
2829 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
2822 cache=False)
2830 cache=False)
2823
2831
2824 def filter(self, condition, condrepr=None, cache=True):
2832 def filter(self, condition, condrepr=None, cache=True):
2825 """Returns this smartset filtered by condition as a new smartset.
2833 """Returns this smartset filtered by condition as a new smartset.
2826
2834
2827 `condition` is a callable which takes a revision number and returns a
2835 `condition` is a callable which takes a revision number and returns a
2828 boolean. Optional `condrepr` provides a printable representation of
2836 boolean. Optional `condrepr` provides a printable representation of
2829 the given `condition`.
2837 the given `condition`.
2830
2838
2831 This is part of the mandatory API for smartset."""
2839 This is part of the mandatory API for smartset."""
2832 # builtin cannot be cached. but do not needs to
2840 # builtin cannot be cached. but do not needs to
2833 if cache and util.safehasattr(condition, 'func_code'):
2841 if cache and util.safehasattr(condition, 'func_code'):
2834 condition = util.cachefunc(condition)
2842 condition = util.cachefunc(condition)
2835 return filteredset(self, condition, condrepr)
2843 return filteredset(self, condition, condrepr)
2836
2844
2837 class baseset(abstractsmartset):
2845 class baseset(abstractsmartset):
2838 """Basic data structure that represents a revset and contains the basic
2846 """Basic data structure that represents a revset and contains the basic
2839 operation that it should be able to perform.
2847 operation that it should be able to perform.
2840
2848
2841 Every method in this class should be implemented by any smartset class.
2849 Every method in this class should be implemented by any smartset class.
2842 """
2850 """
2843 def __init__(self, data=(), datarepr=None, istopo=False):
2851 def __init__(self, data=(), datarepr=None, istopo=False):
2844 """
2852 """
2845 datarepr: a tuple of (format, obj, ...), a function or an object that
2853 datarepr: a tuple of (format, obj, ...), a function or an object that
2846 provides a printable representation of the given data.
2854 provides a printable representation of the given data.
2847 """
2855 """
2848 self._ascending = None
2856 self._ascending = None
2849 self._istopo = istopo
2857 self._istopo = istopo
2850 if not isinstance(data, list):
2858 if not isinstance(data, list):
2851 if isinstance(data, set):
2859 if isinstance(data, set):
2852 self._set = data
2860 self._set = data
2853 # set has no order we pick one for stability purpose
2861 # set has no order we pick one for stability purpose
2854 self._ascending = True
2862 self._ascending = True
2855 data = list(data)
2863 data = list(data)
2856 self._list = data
2864 self._list = data
2857 self._datarepr = datarepr
2865 self._datarepr = datarepr
2858
2866
2859 @util.propertycache
2867 @util.propertycache
2860 def _set(self):
2868 def _set(self):
2861 return set(self._list)
2869 return set(self._list)
2862
2870
2863 @util.propertycache
2871 @util.propertycache
2864 def _asclist(self):
2872 def _asclist(self):
2865 asclist = self._list[:]
2873 asclist = self._list[:]
2866 asclist.sort()
2874 asclist.sort()
2867 return asclist
2875 return asclist
2868
2876
2869 def __iter__(self):
2877 def __iter__(self):
2870 if self._ascending is None:
2878 if self._ascending is None:
2871 return iter(self._list)
2879 return iter(self._list)
2872 elif self._ascending:
2880 elif self._ascending:
2873 return iter(self._asclist)
2881 return iter(self._asclist)
2874 else:
2882 else:
2875 return reversed(self._asclist)
2883 return reversed(self._asclist)
2876
2884
2877 def fastasc(self):
2885 def fastasc(self):
2878 return iter(self._asclist)
2886 return iter(self._asclist)
2879
2887
2880 def fastdesc(self):
2888 def fastdesc(self):
2881 return reversed(self._asclist)
2889 return reversed(self._asclist)
2882
2890
2883 @util.propertycache
2891 @util.propertycache
2884 def __contains__(self):
2892 def __contains__(self):
2885 return self._set.__contains__
2893 return self._set.__contains__
2886
2894
2887 def __nonzero__(self):
2895 def __nonzero__(self):
2888 return bool(self._list)
2896 return bool(self._list)
2889
2897
2890 def sort(self, reverse=False):
2898 def sort(self, reverse=False):
2891 self._ascending = not bool(reverse)
2899 self._ascending = not bool(reverse)
2892 self._istopo = False
2900 self._istopo = False
2893
2901
2894 def reverse(self):
2902 def reverse(self):
2895 if self._ascending is None:
2903 if self._ascending is None:
2896 self._list.reverse()
2904 self._list.reverse()
2897 else:
2905 else:
2898 self._ascending = not self._ascending
2906 self._ascending = not self._ascending
2899 self._istopo = False
2907 self._istopo = False
2900
2908
2901 def __len__(self):
2909 def __len__(self):
2902 return len(self._list)
2910 return len(self._list)
2903
2911
2904 def isascending(self):
2912 def isascending(self):
2905 """Returns True if the collection is ascending order, False if not.
2913 """Returns True if the collection is ascending order, False if not.
2906
2914
2907 This is part of the mandatory API for smartset."""
2915 This is part of the mandatory API for smartset."""
2908 if len(self) <= 1:
2916 if len(self) <= 1:
2909 return True
2917 return True
2910 return self._ascending is not None and self._ascending
2918 return self._ascending is not None and self._ascending
2911
2919
2912 def isdescending(self):
2920 def isdescending(self):
2913 """Returns True if the collection is descending order, False if not.
2921 """Returns True if the collection is descending order, False if not.
2914
2922
2915 This is part of the mandatory API for smartset."""
2923 This is part of the mandatory API for smartset."""
2916 if len(self) <= 1:
2924 if len(self) <= 1:
2917 return True
2925 return True
2918 return self._ascending is not None and not self._ascending
2926 return self._ascending is not None and not self._ascending
2919
2927
2920 def istopo(self):
2928 def istopo(self):
2921 """Is the collection is in topographical order or not.
2929 """Is the collection is in topographical order or not.
2922
2930
2923 This is part of the mandatory API for smartset."""
2931 This is part of the mandatory API for smartset."""
2924 if len(self) <= 1:
2932 if len(self) <= 1:
2925 return True
2933 return True
2926 return self._istopo
2934 return self._istopo
2927
2935
2928 def first(self):
2936 def first(self):
2929 if self:
2937 if self:
2930 if self._ascending is None:
2938 if self._ascending is None:
2931 return self._list[0]
2939 return self._list[0]
2932 elif self._ascending:
2940 elif self._ascending:
2933 return self._asclist[0]
2941 return self._asclist[0]
2934 else:
2942 else:
2935 return self._asclist[-1]
2943 return self._asclist[-1]
2936 return None
2944 return None
2937
2945
2938 def last(self):
2946 def last(self):
2939 if self:
2947 if self:
2940 if self._ascending is None:
2948 if self._ascending is None:
2941 return self._list[-1]
2949 return self._list[-1]
2942 elif self._ascending:
2950 elif self._ascending:
2943 return self._asclist[-1]
2951 return self._asclist[-1]
2944 else:
2952 else:
2945 return self._asclist[0]
2953 return self._asclist[0]
2946 return None
2954 return None
2947
2955
2948 def __repr__(self):
2956 def __repr__(self):
2949 d = {None: '', False: '-', True: '+'}[self._ascending]
2957 d = {None: '', False: '-', True: '+'}[self._ascending]
2950 s = _formatsetrepr(self._datarepr)
2958 s = _formatsetrepr(self._datarepr)
2951 if not s:
2959 if not s:
2952 l = self._list
2960 l = self._list
2953 # if _list has been built from a set, it might have a different
2961 # if _list has been built from a set, it might have a different
2954 # order from one python implementation to another.
2962 # order from one python implementation to another.
2955 # We fallback to the sorted version for a stable output.
2963 # We fallback to the sorted version for a stable output.
2956 if self._ascending is not None:
2964 if self._ascending is not None:
2957 l = self._asclist
2965 l = self._asclist
2958 s = repr(l)
2966 s = repr(l)
2959 return '<%s%s %s>' % (type(self).__name__, d, s)
2967 return '<%s%s %s>' % (type(self).__name__, d, s)
2960
2968
2961 class filteredset(abstractsmartset):
2969 class filteredset(abstractsmartset):
2962 """Duck type for baseset class which iterates lazily over the revisions in
2970 """Duck type for baseset class which iterates lazily over the revisions in
2963 the subset and contains a function which tests for membership in the
2971 the subset and contains a function which tests for membership in the
2964 revset
2972 revset
2965 """
2973 """
2966 def __init__(self, subset, condition=lambda x: True, condrepr=None):
2974 def __init__(self, subset, condition=lambda x: True, condrepr=None):
2967 """
2975 """
2968 condition: a function that decide whether a revision in the subset
2976 condition: a function that decide whether a revision in the subset
2969 belongs to the revset or not.
2977 belongs to the revset or not.
2970 condrepr: a tuple of (format, obj, ...), a function or an object that
2978 condrepr: a tuple of (format, obj, ...), a function or an object that
2971 provides a printable representation of the given condition.
2979 provides a printable representation of the given condition.
2972 """
2980 """
2973 self._subset = subset
2981 self._subset = subset
2974 self._condition = condition
2982 self._condition = condition
2975 self._condrepr = condrepr
2983 self._condrepr = condrepr
2976
2984
2977 def __contains__(self, x):
2985 def __contains__(self, x):
2978 return x in self._subset and self._condition(x)
2986 return x in self._subset and self._condition(x)
2979
2987
2980 def __iter__(self):
2988 def __iter__(self):
2981 return self._iterfilter(self._subset)
2989 return self._iterfilter(self._subset)
2982
2990
2983 def _iterfilter(self, it):
2991 def _iterfilter(self, it):
2984 cond = self._condition
2992 cond = self._condition
2985 for x in it:
2993 for x in it:
2986 if cond(x):
2994 if cond(x):
2987 yield x
2995 yield x
2988
2996
2989 @property
2997 @property
2990 def fastasc(self):
2998 def fastasc(self):
2991 it = self._subset.fastasc
2999 it = self._subset.fastasc
2992 if it is None:
3000 if it is None:
2993 return None
3001 return None
2994 return lambda: self._iterfilter(it())
3002 return lambda: self._iterfilter(it())
2995
3003
2996 @property
3004 @property
2997 def fastdesc(self):
3005 def fastdesc(self):
2998 it = self._subset.fastdesc
3006 it = self._subset.fastdesc
2999 if it is None:
3007 if it is None:
3000 return None
3008 return None
3001 return lambda: self._iterfilter(it())
3009 return lambda: self._iterfilter(it())
3002
3010
3003 def __nonzero__(self):
3011 def __nonzero__(self):
3004 fast = None
3012 fast = None
3005 candidates = [self.fastasc if self.isascending() else None,
3013 candidates = [self.fastasc if self.isascending() else None,
3006 self.fastdesc if self.isdescending() else None,
3014 self.fastdesc if self.isdescending() else None,
3007 self.fastasc,
3015 self.fastasc,
3008 self.fastdesc]
3016 self.fastdesc]
3009 for candidate in candidates:
3017 for candidate in candidates:
3010 if candidate is not None:
3018 if candidate is not None:
3011 fast = candidate
3019 fast = candidate
3012 break
3020 break
3013
3021
3014 if fast is not None:
3022 if fast is not None:
3015 it = fast()
3023 it = fast()
3016 else:
3024 else:
3017 it = self
3025 it = self
3018
3026
3019 for r in it:
3027 for r in it:
3020 return True
3028 return True
3021 return False
3029 return False
3022
3030
3023 def __len__(self):
3031 def __len__(self):
3024 # Basic implementation to be changed in future patches.
3032 # Basic implementation to be changed in future patches.
3025 # until this gets improved, we use generator expression
3033 # until this gets improved, we use generator expression
3026 # here, since list compr is free to call __len__ again
3034 # here, since list compr is free to call __len__ again
3027 # causing infinite recursion
3035 # causing infinite recursion
3028 l = baseset(r for r in self)
3036 l = baseset(r for r in self)
3029 return len(l)
3037 return len(l)
3030
3038
3031 def sort(self, reverse=False):
3039 def sort(self, reverse=False):
3032 self._subset.sort(reverse=reverse)
3040 self._subset.sort(reverse=reverse)
3033
3041
3034 def reverse(self):
3042 def reverse(self):
3035 self._subset.reverse()
3043 self._subset.reverse()
3036
3044
3037 def isascending(self):
3045 def isascending(self):
3038 return self._subset.isascending()
3046 return self._subset.isascending()
3039
3047
3040 def isdescending(self):
3048 def isdescending(self):
3041 return self._subset.isdescending()
3049 return self._subset.isdescending()
3042
3050
3043 def istopo(self):
3051 def istopo(self):
3044 return self._subset.istopo()
3052 return self._subset.istopo()
3045
3053
3046 def first(self):
3054 def first(self):
3047 for x in self:
3055 for x in self:
3048 return x
3056 return x
3049 return None
3057 return None
3050
3058
3051 def last(self):
3059 def last(self):
3052 it = None
3060 it = None
3053 if self.isascending():
3061 if self.isascending():
3054 it = self.fastdesc
3062 it = self.fastdesc
3055 elif self.isdescending():
3063 elif self.isdescending():
3056 it = self.fastasc
3064 it = self.fastasc
3057 if it is not None:
3065 if it is not None:
3058 for x in it():
3066 for x in it():
3059 return x
3067 return x
3060 return None #empty case
3068 return None #empty case
3061 else:
3069 else:
3062 x = None
3070 x = None
3063 for x in self:
3071 for x in self:
3064 pass
3072 pass
3065 return x
3073 return x
3066
3074
3067 def __repr__(self):
3075 def __repr__(self):
3068 xs = [repr(self._subset)]
3076 xs = [repr(self._subset)]
3069 s = _formatsetrepr(self._condrepr)
3077 s = _formatsetrepr(self._condrepr)
3070 if s:
3078 if s:
3071 xs.append(s)
3079 xs.append(s)
3072 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3080 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3073
3081
3074 def _iterordered(ascending, iter1, iter2):
3082 def _iterordered(ascending, iter1, iter2):
3075 """produce an ordered iteration from two iterators with the same order
3083 """produce an ordered iteration from two iterators with the same order
3076
3084
3077 The ascending is used to indicated the iteration direction.
3085 The ascending is used to indicated the iteration direction.
3078 """
3086 """
3079 choice = max
3087 choice = max
3080 if ascending:
3088 if ascending:
3081 choice = min
3089 choice = min
3082
3090
3083 val1 = None
3091 val1 = None
3084 val2 = None
3092 val2 = None
3085 try:
3093 try:
3086 # Consume both iterators in an ordered way until one is empty
3094 # Consume both iterators in an ordered way until one is empty
3087 while True:
3095 while True:
3088 if val1 is None:
3096 if val1 is None:
3089 val1 = next(iter1)
3097 val1 = next(iter1)
3090 if val2 is None:
3098 if val2 is None:
3091 val2 = next(iter2)
3099 val2 = next(iter2)
3092 n = choice(val1, val2)
3100 n = choice(val1, val2)
3093 yield n
3101 yield n
3094 if val1 == n:
3102 if val1 == n:
3095 val1 = None
3103 val1 = None
3096 if val2 == n:
3104 if val2 == n:
3097 val2 = None
3105 val2 = None
3098 except StopIteration:
3106 except StopIteration:
3099 # Flush any remaining values and consume the other one
3107 # Flush any remaining values and consume the other one
3100 it = iter2
3108 it = iter2
3101 if val1 is not None:
3109 if val1 is not None:
3102 yield val1
3110 yield val1
3103 it = iter1
3111 it = iter1
3104 elif val2 is not None:
3112 elif val2 is not None:
3105 # might have been equality and both are empty
3113 # might have been equality and both are empty
3106 yield val2
3114 yield val2
3107 for val in it:
3115 for val in it:
3108 yield val
3116 yield val
3109
3117
3110 class addset(abstractsmartset):
3118 class addset(abstractsmartset):
3111 """Represent the addition of two sets
3119 """Represent the addition of two sets
3112
3120
3113 Wrapper structure for lazily adding two structures without losing much
3121 Wrapper structure for lazily adding two structures without losing much
3114 performance on the __contains__ method
3122 performance on the __contains__ method
3115
3123
3116 If the ascending attribute is set, that means the two structures are
3124 If the ascending attribute is set, that means the two structures are
3117 ordered in either an ascending or descending way. Therefore, we can add
3125 ordered in either an ascending or descending way. Therefore, we can add
3118 them maintaining the order by iterating over both at the same time
3126 them maintaining the order by iterating over both at the same time
3119
3127
3120 >>> xs = baseset([0, 3, 2])
3128 >>> xs = baseset([0, 3, 2])
3121 >>> ys = baseset([5, 2, 4])
3129 >>> ys = baseset([5, 2, 4])
3122
3130
3123 >>> rs = addset(xs, ys)
3131 >>> rs = addset(xs, ys)
3124 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3132 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3125 (True, True, False, True, 0, 4)
3133 (True, True, False, True, 0, 4)
3126 >>> rs = addset(xs, baseset([]))
3134 >>> rs = addset(xs, baseset([]))
3127 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3135 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3128 (True, True, False, 0, 2)
3136 (True, True, False, 0, 2)
3129 >>> rs = addset(baseset([]), baseset([]))
3137 >>> rs = addset(baseset([]), baseset([]))
3130 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3138 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3131 (False, False, None, None)
3139 (False, False, None, None)
3132
3140
3133 iterate unsorted:
3141 iterate unsorted:
3134 >>> rs = addset(xs, ys)
3142 >>> rs = addset(xs, ys)
3135 >>> # (use generator because pypy could call len())
3143 >>> # (use generator because pypy could call len())
3136 >>> list(x for x in rs) # without _genlist
3144 >>> list(x for x in rs) # without _genlist
3137 [0, 3, 2, 5, 4]
3145 [0, 3, 2, 5, 4]
3138 >>> assert not rs._genlist
3146 >>> assert not rs._genlist
3139 >>> len(rs)
3147 >>> len(rs)
3140 5
3148 5
3141 >>> [x for x in rs] # with _genlist
3149 >>> [x for x in rs] # with _genlist
3142 [0, 3, 2, 5, 4]
3150 [0, 3, 2, 5, 4]
3143 >>> assert rs._genlist
3151 >>> assert rs._genlist
3144
3152
3145 iterate ascending:
3153 iterate ascending:
3146 >>> rs = addset(xs, ys, ascending=True)
3154 >>> rs = addset(xs, ys, ascending=True)
3147 >>> # (use generator because pypy could call len())
3155 >>> # (use generator because pypy could call len())
3148 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
3156 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
3149 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3157 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3150 >>> assert not rs._asclist
3158 >>> assert not rs._asclist
3151 >>> len(rs)
3159 >>> len(rs)
3152 5
3160 5
3153 >>> [x for x in rs], [x for x in rs.fastasc()]
3161 >>> [x for x in rs], [x for x in rs.fastasc()]
3154 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3162 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3155 >>> assert rs._asclist
3163 >>> assert rs._asclist
3156
3164
3157 iterate descending:
3165 iterate descending:
3158 >>> rs = addset(xs, ys, ascending=False)
3166 >>> rs = addset(xs, ys, ascending=False)
3159 >>> # (use generator because pypy could call len())
3167 >>> # (use generator because pypy could call len())
3160 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
3168 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
3161 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3169 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3162 >>> assert not rs._asclist
3170 >>> assert not rs._asclist
3163 >>> len(rs)
3171 >>> len(rs)
3164 5
3172 5
3165 >>> [x for x in rs], [x for x in rs.fastdesc()]
3173 >>> [x for x in rs], [x for x in rs.fastdesc()]
3166 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3174 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3167 >>> assert rs._asclist
3175 >>> assert rs._asclist
3168
3176
3169 iterate ascending without fastasc:
3177 iterate ascending without fastasc:
3170 >>> rs = addset(xs, generatorset(ys), ascending=True)
3178 >>> rs = addset(xs, generatorset(ys), ascending=True)
3171 >>> assert rs.fastasc is None
3179 >>> assert rs.fastasc is None
3172 >>> [x for x in rs]
3180 >>> [x for x in rs]
3173 [0, 2, 3, 4, 5]
3181 [0, 2, 3, 4, 5]
3174
3182
3175 iterate descending without fastdesc:
3183 iterate descending without fastdesc:
3176 >>> rs = addset(generatorset(xs), ys, ascending=False)
3184 >>> rs = addset(generatorset(xs), ys, ascending=False)
3177 >>> assert rs.fastdesc is None
3185 >>> assert rs.fastdesc is None
3178 >>> [x for x in rs]
3186 >>> [x for x in rs]
3179 [5, 4, 3, 2, 0]
3187 [5, 4, 3, 2, 0]
3180 """
3188 """
3181 def __init__(self, revs1, revs2, ascending=None):
3189 def __init__(self, revs1, revs2, ascending=None):
3182 self._r1 = revs1
3190 self._r1 = revs1
3183 self._r2 = revs2
3191 self._r2 = revs2
3184 self._iter = None
3192 self._iter = None
3185 self._ascending = ascending
3193 self._ascending = ascending
3186 self._genlist = None
3194 self._genlist = None
3187 self._asclist = None
3195 self._asclist = None
3188
3196
3189 def __len__(self):
3197 def __len__(self):
3190 return len(self._list)
3198 return len(self._list)
3191
3199
3192 def __nonzero__(self):
3200 def __nonzero__(self):
3193 return bool(self._r1) or bool(self._r2)
3201 return bool(self._r1) or bool(self._r2)
3194
3202
3195 @util.propertycache
3203 @util.propertycache
3196 def _list(self):
3204 def _list(self):
3197 if not self._genlist:
3205 if not self._genlist:
3198 self._genlist = baseset(iter(self))
3206 self._genlist = baseset(iter(self))
3199 return self._genlist
3207 return self._genlist
3200
3208
3201 def __iter__(self):
3209 def __iter__(self):
3202 """Iterate over both collections without repeating elements
3210 """Iterate over both collections without repeating elements
3203
3211
3204 If the ascending attribute is not set, iterate over the first one and
3212 If the ascending attribute is not set, iterate over the first one and
3205 then over the second one checking for membership on the first one so we
3213 then over the second one checking for membership on the first one so we
3206 dont yield any duplicates.
3214 dont yield any duplicates.
3207
3215
3208 If the ascending attribute is set, iterate over both collections at the
3216 If the ascending attribute is set, iterate over both collections at the
3209 same time, yielding only one value at a time in the given order.
3217 same time, yielding only one value at a time in the given order.
3210 """
3218 """
3211 if self._ascending is None:
3219 if self._ascending is None:
3212 if self._genlist:
3220 if self._genlist:
3213 return iter(self._genlist)
3221 return iter(self._genlist)
3214 def arbitraryordergen():
3222 def arbitraryordergen():
3215 for r in self._r1:
3223 for r in self._r1:
3216 yield r
3224 yield r
3217 inr1 = self._r1.__contains__
3225 inr1 = self._r1.__contains__
3218 for r in self._r2:
3226 for r in self._r2:
3219 if not inr1(r):
3227 if not inr1(r):
3220 yield r
3228 yield r
3221 return arbitraryordergen()
3229 return arbitraryordergen()
3222 # try to use our own fast iterator if it exists
3230 # try to use our own fast iterator if it exists
3223 self._trysetasclist()
3231 self._trysetasclist()
3224 if self._ascending:
3232 if self._ascending:
3225 attr = 'fastasc'
3233 attr = 'fastasc'
3226 else:
3234 else:
3227 attr = 'fastdesc'
3235 attr = 'fastdesc'
3228 it = getattr(self, attr)
3236 it = getattr(self, attr)
3229 if it is not None:
3237 if it is not None:
3230 return it()
3238 return it()
3231 # maybe half of the component supports fast
3239 # maybe half of the component supports fast
3232 # get iterator for _r1
3240 # get iterator for _r1
3233 iter1 = getattr(self._r1, attr)
3241 iter1 = getattr(self._r1, attr)
3234 if iter1 is None:
3242 if iter1 is None:
3235 # let's avoid side effect (not sure it matters)
3243 # let's avoid side effect (not sure it matters)
3236 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3244 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3237 else:
3245 else:
3238 iter1 = iter1()
3246 iter1 = iter1()
3239 # get iterator for _r2
3247 # get iterator for _r2
3240 iter2 = getattr(self._r2, attr)
3248 iter2 = getattr(self._r2, attr)
3241 if iter2 is None:
3249 if iter2 is None:
3242 # let's avoid side effect (not sure it matters)
3250 # let's avoid side effect (not sure it matters)
3243 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3251 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3244 else:
3252 else:
3245 iter2 = iter2()
3253 iter2 = iter2()
3246 return _iterordered(self._ascending, iter1, iter2)
3254 return _iterordered(self._ascending, iter1, iter2)
3247
3255
3248 def _trysetasclist(self):
3256 def _trysetasclist(self):
3249 """populate the _asclist attribute if possible and necessary"""
3257 """populate the _asclist attribute if possible and necessary"""
3250 if self._genlist is not None and self._asclist is None:
3258 if self._genlist is not None and self._asclist is None:
3251 self._asclist = sorted(self._genlist)
3259 self._asclist = sorted(self._genlist)
3252
3260
3253 @property
3261 @property
3254 def fastasc(self):
3262 def fastasc(self):
3255 self._trysetasclist()
3263 self._trysetasclist()
3256 if self._asclist is not None:
3264 if self._asclist is not None:
3257 return self._asclist.__iter__
3265 return self._asclist.__iter__
3258 iter1 = self._r1.fastasc
3266 iter1 = self._r1.fastasc
3259 iter2 = self._r2.fastasc
3267 iter2 = self._r2.fastasc
3260 if None in (iter1, iter2):
3268 if None in (iter1, iter2):
3261 return None
3269 return None
3262 return lambda: _iterordered(True, iter1(), iter2())
3270 return lambda: _iterordered(True, iter1(), iter2())
3263
3271
3264 @property
3272 @property
3265 def fastdesc(self):
3273 def fastdesc(self):
3266 self._trysetasclist()
3274 self._trysetasclist()
3267 if self._asclist is not None:
3275 if self._asclist is not None:
3268 return self._asclist.__reversed__
3276 return self._asclist.__reversed__
3269 iter1 = self._r1.fastdesc
3277 iter1 = self._r1.fastdesc
3270 iter2 = self._r2.fastdesc
3278 iter2 = self._r2.fastdesc
3271 if None in (iter1, iter2):
3279 if None in (iter1, iter2):
3272 return None
3280 return None
3273 return lambda: _iterordered(False, iter1(), iter2())
3281 return lambda: _iterordered(False, iter1(), iter2())
3274
3282
3275 def __contains__(self, x):
3283 def __contains__(self, x):
3276 return x in self._r1 or x in self._r2
3284 return x in self._r1 or x in self._r2
3277
3285
3278 def sort(self, reverse=False):
3286 def sort(self, reverse=False):
3279 """Sort the added set
3287 """Sort the added set
3280
3288
3281 For this we use the cached list with all the generated values and if we
3289 For this we use the cached list with all the generated values and if we
3282 know they are ascending or descending we can sort them in a smart way.
3290 know they are ascending or descending we can sort them in a smart way.
3283 """
3291 """
3284 self._ascending = not reverse
3292 self._ascending = not reverse
3285
3293
3286 def isascending(self):
3294 def isascending(self):
3287 return self._ascending is not None and self._ascending
3295 return self._ascending is not None and self._ascending
3288
3296
3289 def isdescending(self):
3297 def isdescending(self):
3290 return self._ascending is not None and not self._ascending
3298 return self._ascending is not None and not self._ascending
3291
3299
3292 def istopo(self):
3300 def istopo(self):
3293 # not worth the trouble asserting if the two sets combined are still
3301 # not worth the trouble asserting if the two sets combined are still
3294 # in topographical order. Use the sort() predicate to explicitly sort
3302 # in topographical order. Use the sort() predicate to explicitly sort
3295 # again instead.
3303 # again instead.
3296 return False
3304 return False
3297
3305
3298 def reverse(self):
3306 def reverse(self):
3299 if self._ascending is None:
3307 if self._ascending is None:
3300 self._list.reverse()
3308 self._list.reverse()
3301 else:
3309 else:
3302 self._ascending = not self._ascending
3310 self._ascending = not self._ascending
3303
3311
3304 def first(self):
3312 def first(self):
3305 for x in self:
3313 for x in self:
3306 return x
3314 return x
3307 return None
3315 return None
3308
3316
3309 def last(self):
3317 def last(self):
3310 self.reverse()
3318 self.reverse()
3311 val = self.first()
3319 val = self.first()
3312 self.reverse()
3320 self.reverse()
3313 return val
3321 return val
3314
3322
3315 def __repr__(self):
3323 def __repr__(self):
3316 d = {None: '', False: '-', True: '+'}[self._ascending]
3324 d = {None: '', False: '-', True: '+'}[self._ascending]
3317 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3325 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3318
3326
3319 class generatorset(abstractsmartset):
3327 class generatorset(abstractsmartset):
3320 """Wrap a generator for lazy iteration
3328 """Wrap a generator for lazy iteration
3321
3329
3322 Wrapper structure for generators that provides lazy membership and can
3330 Wrapper structure for generators that provides lazy membership and can
3323 be iterated more than once.
3331 be iterated more than once.
3324 When asked for membership it generates values until either it finds the
3332 When asked for membership it generates values until either it finds the
3325 requested one or has gone through all the elements in the generator
3333 requested one or has gone through all the elements in the generator
3326 """
3334 """
3327 def __init__(self, gen, iterasc=None):
3335 def __init__(self, gen, iterasc=None):
3328 """
3336 """
3329 gen: a generator producing the values for the generatorset.
3337 gen: a generator producing the values for the generatorset.
3330 """
3338 """
3331 self._gen = gen
3339 self._gen = gen
3332 self._asclist = None
3340 self._asclist = None
3333 self._cache = {}
3341 self._cache = {}
3334 self._genlist = []
3342 self._genlist = []
3335 self._finished = False
3343 self._finished = False
3336 self._ascending = True
3344 self._ascending = True
3337 if iterasc is not None:
3345 if iterasc is not None:
3338 if iterasc:
3346 if iterasc:
3339 self.fastasc = self._iterator
3347 self.fastasc = self._iterator
3340 self.__contains__ = self._asccontains
3348 self.__contains__ = self._asccontains
3341 else:
3349 else:
3342 self.fastdesc = self._iterator
3350 self.fastdesc = self._iterator
3343 self.__contains__ = self._desccontains
3351 self.__contains__ = self._desccontains
3344
3352
3345 def __nonzero__(self):
3353 def __nonzero__(self):
3346 # Do not use 'for r in self' because it will enforce the iteration
3354 # Do not use 'for r in self' because it will enforce the iteration
3347 # order (default ascending), possibly unrolling a whole descending
3355 # order (default ascending), possibly unrolling a whole descending
3348 # iterator.
3356 # iterator.
3349 if self._genlist:
3357 if self._genlist:
3350 return True
3358 return True
3351 for r in self._consumegen():
3359 for r in self._consumegen():
3352 return True
3360 return True
3353 return False
3361 return False
3354
3362
3355 def __contains__(self, x):
3363 def __contains__(self, x):
3356 if x in self._cache:
3364 if x in self._cache:
3357 return self._cache[x]
3365 return self._cache[x]
3358
3366
3359 # Use new values only, as existing values would be cached.
3367 # Use new values only, as existing values would be cached.
3360 for l in self._consumegen():
3368 for l in self._consumegen():
3361 if l == x:
3369 if l == x:
3362 return True
3370 return True
3363
3371
3364 self._cache[x] = False
3372 self._cache[x] = False
3365 return False
3373 return False
3366
3374
3367 def _asccontains(self, x):
3375 def _asccontains(self, x):
3368 """version of contains optimised for ascending generator"""
3376 """version of contains optimised for ascending generator"""
3369 if x in self._cache:
3377 if x in self._cache:
3370 return self._cache[x]
3378 return self._cache[x]
3371
3379
3372 # Use new values only, as existing values would be cached.
3380 # Use new values only, as existing values would be cached.
3373 for l in self._consumegen():
3381 for l in self._consumegen():
3374 if l == x:
3382 if l == x:
3375 return True
3383 return True
3376 if l > x:
3384 if l > x:
3377 break
3385 break
3378
3386
3379 self._cache[x] = False
3387 self._cache[x] = False
3380 return False
3388 return False
3381
3389
3382 def _desccontains(self, x):
3390 def _desccontains(self, x):
3383 """version of contains optimised for descending generator"""
3391 """version of contains optimised for descending generator"""
3384 if x in self._cache:
3392 if x in self._cache:
3385 return self._cache[x]
3393 return self._cache[x]
3386
3394
3387 # Use new values only, as existing values would be cached.
3395 # Use new values only, as existing values would be cached.
3388 for l in self._consumegen():
3396 for l in self._consumegen():
3389 if l == x:
3397 if l == x:
3390 return True
3398 return True
3391 if l < x:
3399 if l < x:
3392 break
3400 break
3393
3401
3394 self._cache[x] = False
3402 self._cache[x] = False
3395 return False
3403 return False
3396
3404
3397 def __iter__(self):
3405 def __iter__(self):
3398 if self._ascending:
3406 if self._ascending:
3399 it = self.fastasc
3407 it = self.fastasc
3400 else:
3408 else:
3401 it = self.fastdesc
3409 it = self.fastdesc
3402 if it is not None:
3410 if it is not None:
3403 return it()
3411 return it()
3404 # we need to consume the iterator
3412 # we need to consume the iterator
3405 for x in self._consumegen():
3413 for x in self._consumegen():
3406 pass
3414 pass
3407 # recall the same code
3415 # recall the same code
3408 return iter(self)
3416 return iter(self)
3409
3417
3410 def _iterator(self):
3418 def _iterator(self):
3411 if self._finished:
3419 if self._finished:
3412 return iter(self._genlist)
3420 return iter(self._genlist)
3413
3421
3414 # We have to use this complex iteration strategy to allow multiple
3422 # We have to use this complex iteration strategy to allow multiple
3415 # iterations at the same time. We need to be able to catch revision
3423 # iterations at the same time. We need to be able to catch revision
3416 # removed from _consumegen and added to genlist in another instance.
3424 # removed from _consumegen and added to genlist in another instance.
3417 #
3425 #
3418 # Getting rid of it would provide an about 15% speed up on this
3426 # Getting rid of it would provide an about 15% speed up on this
3419 # iteration.
3427 # iteration.
3420 genlist = self._genlist
3428 genlist = self._genlist
3421 nextrev = self._consumegen().next
3429 nextrev = self._consumegen().next
3422 _len = len # cache global lookup
3430 _len = len # cache global lookup
3423 def gen():
3431 def gen():
3424 i = 0
3432 i = 0
3425 while True:
3433 while True:
3426 if i < _len(genlist):
3434 if i < _len(genlist):
3427 yield genlist[i]
3435 yield genlist[i]
3428 else:
3436 else:
3429 yield nextrev()
3437 yield nextrev()
3430 i += 1
3438 i += 1
3431 return gen()
3439 return gen()
3432
3440
3433 def _consumegen(self):
3441 def _consumegen(self):
3434 cache = self._cache
3442 cache = self._cache
3435 genlist = self._genlist.append
3443 genlist = self._genlist.append
3436 for item in self._gen:
3444 for item in self._gen:
3437 cache[item] = True
3445 cache[item] = True
3438 genlist(item)
3446 genlist(item)
3439 yield item
3447 yield item
3440 if not self._finished:
3448 if not self._finished:
3441 self._finished = True
3449 self._finished = True
3442 asc = self._genlist[:]
3450 asc = self._genlist[:]
3443 asc.sort()
3451 asc.sort()
3444 self._asclist = asc
3452 self._asclist = asc
3445 self.fastasc = asc.__iter__
3453 self.fastasc = asc.__iter__
3446 self.fastdesc = asc.__reversed__
3454 self.fastdesc = asc.__reversed__
3447
3455
3448 def __len__(self):
3456 def __len__(self):
3449 for x in self._consumegen():
3457 for x in self._consumegen():
3450 pass
3458 pass
3451 return len(self._genlist)
3459 return len(self._genlist)
3452
3460
3453 def sort(self, reverse=False):
3461 def sort(self, reverse=False):
3454 self._ascending = not reverse
3462 self._ascending = not reverse
3455
3463
3456 def reverse(self):
3464 def reverse(self):
3457 self._ascending = not self._ascending
3465 self._ascending = not self._ascending
3458
3466
3459 def isascending(self):
3467 def isascending(self):
3460 return self._ascending
3468 return self._ascending
3461
3469
3462 def isdescending(self):
3470 def isdescending(self):
3463 return not self._ascending
3471 return not self._ascending
3464
3472
3465 def istopo(self):
3473 def istopo(self):
3466 # not worth the trouble asserting if the two sets combined are still
3474 # not worth the trouble asserting if the two sets combined are still
3467 # in topographical order. Use the sort() predicate to explicitly sort
3475 # in topographical order. Use the sort() predicate to explicitly sort
3468 # again instead.
3476 # again instead.
3469 return False
3477 return False
3470
3478
3471 def first(self):
3479 def first(self):
3472 if self._ascending:
3480 if self._ascending:
3473 it = self.fastasc
3481 it = self.fastasc
3474 else:
3482 else:
3475 it = self.fastdesc
3483 it = self.fastdesc
3476 if it is None:
3484 if it is None:
3477 # we need to consume all and try again
3485 # we need to consume all and try again
3478 for x in self._consumegen():
3486 for x in self._consumegen():
3479 pass
3487 pass
3480 return self.first()
3488 return self.first()
3481 return next(it(), None)
3489 return next(it(), None)
3482
3490
3483 def last(self):
3491 def last(self):
3484 if self._ascending:
3492 if self._ascending:
3485 it = self.fastdesc
3493 it = self.fastdesc
3486 else:
3494 else:
3487 it = self.fastasc
3495 it = self.fastasc
3488 if it is None:
3496 if it is None:
3489 # we need to consume all and try again
3497 # we need to consume all and try again
3490 for x in self._consumegen():
3498 for x in self._consumegen():
3491 pass
3499 pass
3492 return self.first()
3500 return self.first()
3493 return next(it(), None)
3501 return next(it(), None)
3494
3502
3495 def __repr__(self):
3503 def __repr__(self):
3496 d = {False: '-', True: '+'}[self._ascending]
3504 d = {False: '-', True: '+'}[self._ascending]
3497 return '<%s%s>' % (type(self).__name__, d)
3505 return '<%s%s>' % (type(self).__name__, d)
3498
3506
3499 class spanset(abstractsmartset):
3507 class spanset(abstractsmartset):
3500 """Duck type for baseset class which represents a range of revisions and
3508 """Duck type for baseset class which represents a range of revisions and
3501 can work lazily and without having all the range in memory
3509 can work lazily and without having all the range in memory
3502
3510
3503 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3511 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3504 notable points:
3512 notable points:
3505 - when x < y it will be automatically descending,
3513 - when x < y it will be automatically descending,
3506 - revision filtered with this repoview will be skipped.
3514 - revision filtered with this repoview will be skipped.
3507
3515
3508 """
3516 """
3509 def __init__(self, repo, start=0, end=None):
3517 def __init__(self, repo, start=0, end=None):
3510 """
3518 """
3511 start: first revision included the set
3519 start: first revision included the set
3512 (default to 0)
3520 (default to 0)
3513 end: first revision excluded (last+1)
3521 end: first revision excluded (last+1)
3514 (default to len(repo)
3522 (default to len(repo)
3515
3523
3516 Spanset will be descending if `end` < `start`.
3524 Spanset will be descending if `end` < `start`.
3517 """
3525 """
3518 if end is None:
3526 if end is None:
3519 end = len(repo)
3527 end = len(repo)
3520 self._ascending = start <= end
3528 self._ascending = start <= end
3521 if not self._ascending:
3529 if not self._ascending:
3522 start, end = end + 1, start +1
3530 start, end = end + 1, start +1
3523 self._start = start
3531 self._start = start
3524 self._end = end
3532 self._end = end
3525 self._hiddenrevs = repo.changelog.filteredrevs
3533 self._hiddenrevs = repo.changelog.filteredrevs
3526
3534
3527 def sort(self, reverse=False):
3535 def sort(self, reverse=False):
3528 self._ascending = not reverse
3536 self._ascending = not reverse
3529
3537
3530 def reverse(self):
3538 def reverse(self):
3531 self._ascending = not self._ascending
3539 self._ascending = not self._ascending
3532
3540
3533 def istopo(self):
3541 def istopo(self):
3534 # not worth the trouble asserting if the two sets combined are still
3542 # not worth the trouble asserting if the two sets combined are still
3535 # in topographical order. Use the sort() predicate to explicitly sort
3543 # in topographical order. Use the sort() predicate to explicitly sort
3536 # again instead.
3544 # again instead.
3537 return False
3545 return False
3538
3546
3539 def _iterfilter(self, iterrange):
3547 def _iterfilter(self, iterrange):
3540 s = self._hiddenrevs
3548 s = self._hiddenrevs
3541 for r in iterrange:
3549 for r in iterrange:
3542 if r not in s:
3550 if r not in s:
3543 yield r
3551 yield r
3544
3552
3545 def __iter__(self):
3553 def __iter__(self):
3546 if self._ascending:
3554 if self._ascending:
3547 return self.fastasc()
3555 return self.fastasc()
3548 else:
3556 else:
3549 return self.fastdesc()
3557 return self.fastdesc()
3550
3558
3551 def fastasc(self):
3559 def fastasc(self):
3552 iterrange = xrange(self._start, self._end)
3560 iterrange = xrange(self._start, self._end)
3553 if self._hiddenrevs:
3561 if self._hiddenrevs:
3554 return self._iterfilter(iterrange)
3562 return self._iterfilter(iterrange)
3555 return iter(iterrange)
3563 return iter(iterrange)
3556
3564
3557 def fastdesc(self):
3565 def fastdesc(self):
3558 iterrange = xrange(self._end - 1, self._start - 1, -1)
3566 iterrange = xrange(self._end - 1, self._start - 1, -1)
3559 if self._hiddenrevs:
3567 if self._hiddenrevs:
3560 return self._iterfilter(iterrange)
3568 return self._iterfilter(iterrange)
3561 return iter(iterrange)
3569 return iter(iterrange)
3562
3570
3563 def __contains__(self, rev):
3571 def __contains__(self, rev):
3564 hidden = self._hiddenrevs
3572 hidden = self._hiddenrevs
3565 return ((self._start <= rev < self._end)
3573 return ((self._start <= rev < self._end)
3566 and not (hidden and rev in hidden))
3574 and not (hidden and rev in hidden))
3567
3575
3568 def __nonzero__(self):
3576 def __nonzero__(self):
3569 for r in self:
3577 for r in self:
3570 return True
3578 return True
3571 return False
3579 return False
3572
3580
3573 def __len__(self):
3581 def __len__(self):
3574 if not self._hiddenrevs:
3582 if not self._hiddenrevs:
3575 return abs(self._end - self._start)
3583 return abs(self._end - self._start)
3576 else:
3584 else:
3577 count = 0
3585 count = 0
3578 start = self._start
3586 start = self._start
3579 end = self._end
3587 end = self._end
3580 for rev in self._hiddenrevs:
3588 for rev in self._hiddenrevs:
3581 if (end < rev <= start) or (start <= rev < end):
3589 if (end < rev <= start) or (start <= rev < end):
3582 count += 1
3590 count += 1
3583 return abs(self._end - self._start) - count
3591 return abs(self._end - self._start) - count
3584
3592
3585 def isascending(self):
3593 def isascending(self):
3586 return self._ascending
3594 return self._ascending
3587
3595
3588 def isdescending(self):
3596 def isdescending(self):
3589 return not self._ascending
3597 return not self._ascending
3590
3598
3591 def first(self):
3599 def first(self):
3592 if self._ascending:
3600 if self._ascending:
3593 it = self.fastasc
3601 it = self.fastasc
3594 else:
3602 else:
3595 it = self.fastdesc
3603 it = self.fastdesc
3596 for x in it():
3604 for x in it():
3597 return x
3605 return x
3598 return None
3606 return None
3599
3607
3600 def last(self):
3608 def last(self):
3601 if self._ascending:
3609 if self._ascending:
3602 it = self.fastdesc
3610 it = self.fastdesc
3603 else:
3611 else:
3604 it = self.fastasc
3612 it = self.fastasc
3605 for x in it():
3613 for x in it():
3606 return x
3614 return x
3607 return None
3615 return None
3608
3616
3609 def __repr__(self):
3617 def __repr__(self):
3610 d = {False: '-', True: '+'}[self._ascending]
3618 d = {False: '-', True: '+'}[self._ascending]
3611 return '<%s%s %d:%d>' % (type(self).__name__, d,
3619 return '<%s%s %d:%d>' % (type(self).__name__, d,
3612 self._start, self._end - 1)
3620 self._start, self._end - 1)
3613
3621
3614 class fullreposet(spanset):
3622 class fullreposet(spanset):
3615 """a set containing all revisions in the repo
3623 """a set containing all revisions in the repo
3616
3624
3617 This class exists to host special optimization and magic to handle virtual
3625 This class exists to host special optimization and magic to handle virtual
3618 revisions such as "null".
3626 revisions such as "null".
3619 """
3627 """
3620
3628
3621 def __init__(self, repo):
3629 def __init__(self, repo):
3622 super(fullreposet, self).__init__(repo)
3630 super(fullreposet, self).__init__(repo)
3623
3631
3624 def __and__(self, other):
3632 def __and__(self, other):
3625 """As self contains the whole repo, all of the other set should also be
3633 """As self contains the whole repo, all of the other set should also be
3626 in self. Therefore `self & other = other`.
3634 in self. Therefore `self & other = other`.
3627
3635
3628 This boldly assumes the other contains valid revs only.
3636 This boldly assumes the other contains valid revs only.
3629 """
3637 """
3630 # other not a smartset, make is so
3638 # other not a smartset, make is so
3631 if not util.safehasattr(other, 'isascending'):
3639 if not util.safehasattr(other, 'isascending'):
3632 # filter out hidden revision
3640 # filter out hidden revision
3633 # (this boldly assumes all smartset are pure)
3641 # (this boldly assumes all smartset are pure)
3634 #
3642 #
3635 # `other` was used with "&", let's assume this is a set like
3643 # `other` was used with "&", let's assume this is a set like
3636 # object.
3644 # object.
3637 other = baseset(other - self._hiddenrevs)
3645 other = baseset(other - self._hiddenrevs)
3638
3646
3639 # XXX As fullreposet is also used as bootstrap, this is wrong.
3647 # XXX As fullreposet is also used as bootstrap, this is wrong.
3640 #
3648 #
3641 # With a giveme312() revset returning [3,1,2], this makes
3649 # With a giveme312() revset returning [3,1,2], this makes
3642 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3650 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3643 # We cannot just drop it because other usage still need to sort it:
3651 # We cannot just drop it because other usage still need to sort it:
3644 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3652 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3645 #
3653 #
3646 # There is also some faulty revset implementations that rely on it
3654 # There is also some faulty revset implementations that rely on it
3647 # (eg: children as of its state in e8075329c5fb)
3655 # (eg: children as of its state in e8075329c5fb)
3648 #
3656 #
3649 # When we fix the two points above we can move this into the if clause
3657 # When we fix the two points above we can move this into the if clause
3650 other.sort(reverse=self.isdescending())
3658 other.sort(reverse=self.isdescending())
3651 return other
3659 return other
3652
3660
3653 def prettyformatset(revs):
3661 def prettyformatset(revs):
3654 lines = []
3662 lines = []
3655 rs = repr(revs)
3663 rs = repr(revs)
3656 p = 0
3664 p = 0
3657 while p < len(rs):
3665 while p < len(rs):
3658 q = rs.find('<', p + 1)
3666 q = rs.find('<', p + 1)
3659 if q < 0:
3667 if q < 0:
3660 q = len(rs)
3668 q = len(rs)
3661 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3669 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3662 assert l >= 0
3670 assert l >= 0
3663 lines.append((l, rs[p:q].rstrip()))
3671 lines.append((l, rs[p:q].rstrip()))
3664 p = q
3672 p = q
3665 return '\n'.join(' ' * l + s for l, s in lines)
3673 return '\n'.join(' ' * l + s for l, s in lines)
3666
3674
3667 def loadpredicate(ui, extname, registrarobj):
3675 def loadpredicate(ui, extname, registrarobj):
3668 """Load revset predicates from specified registrarobj
3676 """Load revset predicates from specified registrarobj
3669 """
3677 """
3670 for name, func in registrarobj._table.iteritems():
3678 for name, func in registrarobj._table.iteritems():
3671 symbols[name] = func
3679 symbols[name] = func
3672 if func._safe:
3680 if func._safe:
3673 safesymbols.add(name)
3681 safesymbols.add(name)
3674
3682
3675 # load built-in predicates explicitly to setup safesymbols
3683 # load built-in predicates explicitly to setup safesymbols
3676 loadpredicate(None, None, predicate)
3684 loadpredicate(None, None, predicate)
3677
3685
3678 # tell hggettext to extract docstrings from these functions:
3686 # tell hggettext to extract docstrings from these functions:
3679 i18nfunctions = symbols.values()
3687 i18nfunctions = symbols.values()
@@ -1,2271 +1,2284
1 Log on empty repository: checking consistency
1 Log on empty repository: checking consistency
2
2
3 $ hg init empty
3 $ hg init empty
4 $ cd empty
4 $ cd empty
5 $ hg log
5 $ hg log
6 $ hg log -r 1
6 $ hg log -r 1
7 abort: unknown revision '1'!
7 abort: unknown revision '1'!
8 [255]
8 [255]
9 $ hg log -r -1:0
9 $ hg log -r -1:0
10 abort: unknown revision '-1'!
10 abort: unknown revision '-1'!
11 [255]
11 [255]
12 $ hg log -r 'branch(name)'
12 $ hg log -r 'branch(name)'
13 abort: unknown revision 'name'!
13 abort: unknown revision 'name'!
14 [255]
14 [255]
15 $ hg log -r null -q
15 $ hg log -r null -q
16 -1:000000000000
16 -1:000000000000
17
17
18 The g is crafted to have 2 filelog topological heads in a linear
18 The g is crafted to have 2 filelog topological heads in a linear
19 changeset graph
19 changeset graph
20
20
21 $ hg init a
21 $ hg init a
22 $ cd a
22 $ cd a
23 $ echo a > a
23 $ echo a > a
24 $ echo f > f
24 $ echo f > f
25 $ hg ci -Ama -d '1 0'
25 $ hg ci -Ama -d '1 0'
26 adding a
26 adding a
27 adding f
27 adding f
28
28
29 $ hg cp a b
29 $ hg cp a b
30 $ hg cp f g
30 $ hg cp f g
31 $ hg ci -mb -d '2 0'
31 $ hg ci -mb -d '2 0'
32
32
33 $ mkdir dir
33 $ mkdir dir
34 $ hg mv b dir
34 $ hg mv b dir
35 $ echo g >> g
35 $ echo g >> g
36 $ echo f >> f
36 $ echo f >> f
37 $ hg ci -mc -d '3 0'
37 $ hg ci -mc -d '3 0'
38
38
39 $ hg mv a b
39 $ hg mv a b
40 $ hg cp -f f g
40 $ hg cp -f f g
41 $ echo a > d
41 $ echo a > d
42 $ hg add d
42 $ hg add d
43 $ hg ci -md -d '4 0'
43 $ hg ci -md -d '4 0'
44
44
45 $ hg mv dir/b e
45 $ hg mv dir/b e
46 $ hg ci -me -d '5 0'
46 $ hg ci -me -d '5 0'
47
47
48 Make sure largefiles doesn't interfere with logging a regular file
48 Make sure largefiles doesn't interfere with logging a regular file
49 $ hg --debug log a -T '{rev}: {desc}\n' --config extensions.largefiles=
49 $ hg --debug log a -T '{rev}: {desc}\n' --config extensions.largefiles=
50 updated patterns: ['.hglf/a', 'a']
50 updated patterns: ['.hglf/a', 'a']
51 0: a
51 0: a
52 $ hg log a
52 $ hg log a
53 changeset: 0:9161b9aeaf16
53 changeset: 0:9161b9aeaf16
54 user: test
54 user: test
55 date: Thu Jan 01 00:00:01 1970 +0000
55 date: Thu Jan 01 00:00:01 1970 +0000
56 summary: a
56 summary: a
57
57
58 $ hg log glob:a*
58 $ hg log glob:a*
59 changeset: 3:2ca5ba701980
59 changeset: 3:2ca5ba701980
60 user: test
60 user: test
61 date: Thu Jan 01 00:00:04 1970 +0000
61 date: Thu Jan 01 00:00:04 1970 +0000
62 summary: d
62 summary: d
63
63
64 changeset: 0:9161b9aeaf16
64 changeset: 0:9161b9aeaf16
65 user: test
65 user: test
66 date: Thu Jan 01 00:00:01 1970 +0000
66 date: Thu Jan 01 00:00:01 1970 +0000
67 summary: a
67 summary: a
68
68
69 $ hg --debug log glob:a* -T '{rev}: {desc}\n' --config extensions.largefiles=
69 $ hg --debug log glob:a* -T '{rev}: {desc}\n' --config extensions.largefiles=
70 updated patterns: ['glob:.hglf/a*', 'glob:a*']
70 updated patterns: ['glob:.hglf/a*', 'glob:a*']
71 3: d
71 3: d
72 0: a
72 0: a
73
73
74 log on directory
74 log on directory
75
75
76 $ hg log dir
76 $ hg log dir
77 changeset: 4:7e4639b4691b
77 changeset: 4:7e4639b4691b
78 tag: tip
78 tag: tip
79 user: test
79 user: test
80 date: Thu Jan 01 00:00:05 1970 +0000
80 date: Thu Jan 01 00:00:05 1970 +0000
81 summary: e
81 summary: e
82
82
83 changeset: 2:f8954cd4dc1f
83 changeset: 2:f8954cd4dc1f
84 user: test
84 user: test
85 date: Thu Jan 01 00:00:03 1970 +0000
85 date: Thu Jan 01 00:00:03 1970 +0000
86 summary: c
86 summary: c
87
87
88 $ hg log somethingthatdoesntexist dir
88 $ hg log somethingthatdoesntexist dir
89 changeset: 4:7e4639b4691b
89 changeset: 4:7e4639b4691b
90 tag: tip
90 tag: tip
91 user: test
91 user: test
92 date: Thu Jan 01 00:00:05 1970 +0000
92 date: Thu Jan 01 00:00:05 1970 +0000
93 summary: e
93 summary: e
94
94
95 changeset: 2:f8954cd4dc1f
95 changeset: 2:f8954cd4dc1f
96 user: test
96 user: test
97 date: Thu Jan 01 00:00:03 1970 +0000
97 date: Thu Jan 01 00:00:03 1970 +0000
98 summary: c
98 summary: c
99
99
100
100
101 -f, non-existent directory
101 -f, non-existent directory
102
102
103 $ hg log -f dir
103 $ hg log -f dir
104 abort: cannot follow file not in parent revision: "dir"
104 abort: cannot follow file not in parent revision: "dir"
105 [255]
105 [255]
106
106
107 -f, directory
107 -f, directory
108
108
109 $ hg up -q 3
109 $ hg up -q 3
110 $ hg log -f dir
110 $ hg log -f dir
111 changeset: 2:f8954cd4dc1f
111 changeset: 2:f8954cd4dc1f
112 user: test
112 user: test
113 date: Thu Jan 01 00:00:03 1970 +0000
113 date: Thu Jan 01 00:00:03 1970 +0000
114 summary: c
114 summary: c
115
115
116 -f, directory with --patch
116 -f, directory with --patch
117
117
118 $ hg log -f dir -p
118 $ hg log -f dir -p
119 changeset: 2:f8954cd4dc1f
119 changeset: 2:f8954cd4dc1f
120 user: test
120 user: test
121 date: Thu Jan 01 00:00:03 1970 +0000
121 date: Thu Jan 01 00:00:03 1970 +0000
122 summary: c
122 summary: c
123
123
124 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
124 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
125 --- /dev/null* (glob)
125 --- /dev/null* (glob)
126 +++ b/dir/b* (glob)
126 +++ b/dir/b* (glob)
127 @@ -0,0 +1,1 @@
127 @@ -0,0 +1,1 @@
128 +a
128 +a
129
129
130
130
131 -f, pattern
131 -f, pattern
132
132
133 $ hg log -f -I 'dir**' -p
133 $ hg log -f -I 'dir**' -p
134 changeset: 2:f8954cd4dc1f
134 changeset: 2:f8954cd4dc1f
135 user: test
135 user: test
136 date: Thu Jan 01 00:00:03 1970 +0000
136 date: Thu Jan 01 00:00:03 1970 +0000
137 summary: c
137 summary: c
138
138
139 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
139 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
140 --- /dev/null* (glob)
140 --- /dev/null* (glob)
141 +++ b/dir/b* (glob)
141 +++ b/dir/b* (glob)
142 @@ -0,0 +1,1 @@
142 @@ -0,0 +1,1 @@
143 +a
143 +a
144
144
145 $ hg up -q 4
145 $ hg up -q 4
146
146
147 -f, a wrong style
147 -f, a wrong style
148
148
149 $ hg log -f -l1 --style something
149 $ hg log -f -l1 --style something
150 abort: style 'something' not found
150 abort: style 'something' not found
151 (available styles: bisect, changelog, compact, default, phases, status, xml)
151 (available styles: bisect, changelog, compact, default, phases, status, xml)
152 [255]
152 [255]
153
153
154 -f, phases style
154 -f, phases style
155
155
156
156
157 $ hg log -f -l1 --style phases
157 $ hg log -f -l1 --style phases
158 changeset: 4:7e4639b4691b
158 changeset: 4:7e4639b4691b
159 tag: tip
159 tag: tip
160 phase: draft
160 phase: draft
161 user: test
161 user: test
162 date: Thu Jan 01 00:00:05 1970 +0000
162 date: Thu Jan 01 00:00:05 1970 +0000
163 summary: e
163 summary: e
164
164
165
165
166 $ hg log -f -l1 --style phases -q
166 $ hg log -f -l1 --style phases -q
167 4:7e4639b4691b
167 4:7e4639b4691b
168
168
169 -f, but no args
169 -f, but no args
170
170
171 $ hg log -f
171 $ hg log -f
172 changeset: 4:7e4639b4691b
172 changeset: 4:7e4639b4691b
173 tag: tip
173 tag: tip
174 user: test
174 user: test
175 date: Thu Jan 01 00:00:05 1970 +0000
175 date: Thu Jan 01 00:00:05 1970 +0000
176 summary: e
176 summary: e
177
177
178 changeset: 3:2ca5ba701980
178 changeset: 3:2ca5ba701980
179 user: test
179 user: test
180 date: Thu Jan 01 00:00:04 1970 +0000
180 date: Thu Jan 01 00:00:04 1970 +0000
181 summary: d
181 summary: d
182
182
183 changeset: 2:f8954cd4dc1f
183 changeset: 2:f8954cd4dc1f
184 user: test
184 user: test
185 date: Thu Jan 01 00:00:03 1970 +0000
185 date: Thu Jan 01 00:00:03 1970 +0000
186 summary: c
186 summary: c
187
187
188 changeset: 1:d89b0a12d229
188 changeset: 1:d89b0a12d229
189 user: test
189 user: test
190 date: Thu Jan 01 00:00:02 1970 +0000
190 date: Thu Jan 01 00:00:02 1970 +0000
191 summary: b
191 summary: b
192
192
193 changeset: 0:9161b9aeaf16
193 changeset: 0:9161b9aeaf16
194 user: test
194 user: test
195 date: Thu Jan 01 00:00:01 1970 +0000
195 date: Thu Jan 01 00:00:01 1970 +0000
196 summary: a
196 summary: a
197
197
198
198
199 one rename
199 one rename
200
200
201 $ hg up -q 2
201 $ hg up -q 2
202 $ hg log -vf a
202 $ hg log -vf a
203 changeset: 0:9161b9aeaf16
203 changeset: 0:9161b9aeaf16
204 user: test
204 user: test
205 date: Thu Jan 01 00:00:01 1970 +0000
205 date: Thu Jan 01 00:00:01 1970 +0000
206 files: a f
206 files: a f
207 description:
207 description:
208 a
208 a
209
209
210
210
211
211
212 many renames
212 many renames
213
213
214 $ hg up -q tip
214 $ hg up -q tip
215 $ hg log -vf e
215 $ hg log -vf e
216 changeset: 4:7e4639b4691b
216 changeset: 4:7e4639b4691b
217 tag: tip
217 tag: tip
218 user: test
218 user: test
219 date: Thu Jan 01 00:00:05 1970 +0000
219 date: Thu Jan 01 00:00:05 1970 +0000
220 files: dir/b e
220 files: dir/b e
221 description:
221 description:
222 e
222 e
223
223
224
224
225 changeset: 2:f8954cd4dc1f
225 changeset: 2:f8954cd4dc1f
226 user: test
226 user: test
227 date: Thu Jan 01 00:00:03 1970 +0000
227 date: Thu Jan 01 00:00:03 1970 +0000
228 files: b dir/b f g
228 files: b dir/b f g
229 description:
229 description:
230 c
230 c
231
231
232
232
233 changeset: 1:d89b0a12d229
233 changeset: 1:d89b0a12d229
234 user: test
234 user: test
235 date: Thu Jan 01 00:00:02 1970 +0000
235 date: Thu Jan 01 00:00:02 1970 +0000
236 files: b g
236 files: b g
237 description:
237 description:
238 b
238 b
239
239
240
240
241 changeset: 0:9161b9aeaf16
241 changeset: 0:9161b9aeaf16
242 user: test
242 user: test
243 date: Thu Jan 01 00:00:01 1970 +0000
243 date: Thu Jan 01 00:00:01 1970 +0000
244 files: a f
244 files: a f
245 description:
245 description:
246 a
246 a
247
247
248
248
249
249
250
250
251 log -pf dir/b
251 log -pf dir/b
252
252
253 $ hg up -q 3
253 $ hg up -q 3
254 $ hg log -pf dir/b
254 $ hg log -pf dir/b
255 changeset: 2:f8954cd4dc1f
255 changeset: 2:f8954cd4dc1f
256 user: test
256 user: test
257 date: Thu Jan 01 00:00:03 1970 +0000
257 date: Thu Jan 01 00:00:03 1970 +0000
258 summary: c
258 summary: c
259
259
260 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
260 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
261 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
261 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
262 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
262 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
263 @@ -0,0 +1,1 @@
263 @@ -0,0 +1,1 @@
264 +a
264 +a
265
265
266 changeset: 1:d89b0a12d229
266 changeset: 1:d89b0a12d229
267 user: test
267 user: test
268 date: Thu Jan 01 00:00:02 1970 +0000
268 date: Thu Jan 01 00:00:02 1970 +0000
269 summary: b
269 summary: b
270
270
271 diff -r 9161b9aeaf16 -r d89b0a12d229 b
271 diff -r 9161b9aeaf16 -r d89b0a12d229 b
272 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
272 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
273 +++ b/b Thu Jan 01 00:00:02 1970 +0000
273 +++ b/b Thu Jan 01 00:00:02 1970 +0000
274 @@ -0,0 +1,1 @@
274 @@ -0,0 +1,1 @@
275 +a
275 +a
276
276
277 changeset: 0:9161b9aeaf16
277 changeset: 0:9161b9aeaf16
278 user: test
278 user: test
279 date: Thu Jan 01 00:00:01 1970 +0000
279 date: Thu Jan 01 00:00:01 1970 +0000
280 summary: a
280 summary: a
281
281
282 diff -r 000000000000 -r 9161b9aeaf16 a
282 diff -r 000000000000 -r 9161b9aeaf16 a
283 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
283 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
284 +++ b/a Thu Jan 01 00:00:01 1970 +0000
284 +++ b/a Thu Jan 01 00:00:01 1970 +0000
285 @@ -0,0 +1,1 @@
285 @@ -0,0 +1,1 @@
286 +a
286 +a
287
287
288
288
289 log -pf b inside dir
289 log -pf b inside dir
290
290
291 $ hg --cwd=dir log -pf b
291 $ hg --cwd=dir log -pf b
292 changeset: 2:f8954cd4dc1f
292 changeset: 2:f8954cd4dc1f
293 user: test
293 user: test
294 date: Thu Jan 01 00:00:03 1970 +0000
294 date: Thu Jan 01 00:00:03 1970 +0000
295 summary: c
295 summary: c
296
296
297 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
297 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
298 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
298 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
299 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
299 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
300 @@ -0,0 +1,1 @@
300 @@ -0,0 +1,1 @@
301 +a
301 +a
302
302
303 changeset: 1:d89b0a12d229
303 changeset: 1:d89b0a12d229
304 user: test
304 user: test
305 date: Thu Jan 01 00:00:02 1970 +0000
305 date: Thu Jan 01 00:00:02 1970 +0000
306 summary: b
306 summary: b
307
307
308 diff -r 9161b9aeaf16 -r d89b0a12d229 b
308 diff -r 9161b9aeaf16 -r d89b0a12d229 b
309 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
309 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
310 +++ b/b Thu Jan 01 00:00:02 1970 +0000
310 +++ b/b Thu Jan 01 00:00:02 1970 +0000
311 @@ -0,0 +1,1 @@
311 @@ -0,0 +1,1 @@
312 +a
312 +a
313
313
314 changeset: 0:9161b9aeaf16
314 changeset: 0:9161b9aeaf16
315 user: test
315 user: test
316 date: Thu Jan 01 00:00:01 1970 +0000
316 date: Thu Jan 01 00:00:01 1970 +0000
317 summary: a
317 summary: a
318
318
319 diff -r 000000000000 -r 9161b9aeaf16 a
319 diff -r 000000000000 -r 9161b9aeaf16 a
320 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
320 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
321 +++ b/a Thu Jan 01 00:00:01 1970 +0000
321 +++ b/a Thu Jan 01 00:00:01 1970 +0000
322 @@ -0,0 +1,1 @@
322 @@ -0,0 +1,1 @@
323 +a
323 +a
324
324
325
325
326 log -pf, but no args
326 log -pf, but no args
327
327
328 $ hg log -pf
328 $ hg log -pf
329 changeset: 3:2ca5ba701980
329 changeset: 3:2ca5ba701980
330 user: test
330 user: test
331 date: Thu Jan 01 00:00:04 1970 +0000
331 date: Thu Jan 01 00:00:04 1970 +0000
332 summary: d
332 summary: d
333
333
334 diff -r f8954cd4dc1f -r 2ca5ba701980 a
334 diff -r f8954cd4dc1f -r 2ca5ba701980 a
335 --- a/a Thu Jan 01 00:00:03 1970 +0000
335 --- a/a Thu Jan 01 00:00:03 1970 +0000
336 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
336 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
337 @@ -1,1 +0,0 @@
337 @@ -1,1 +0,0 @@
338 -a
338 -a
339 diff -r f8954cd4dc1f -r 2ca5ba701980 b
339 diff -r f8954cd4dc1f -r 2ca5ba701980 b
340 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
340 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
341 +++ b/b Thu Jan 01 00:00:04 1970 +0000
341 +++ b/b Thu Jan 01 00:00:04 1970 +0000
342 @@ -0,0 +1,1 @@
342 @@ -0,0 +1,1 @@
343 +a
343 +a
344 diff -r f8954cd4dc1f -r 2ca5ba701980 d
344 diff -r f8954cd4dc1f -r 2ca5ba701980 d
345 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
345 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
346 +++ b/d Thu Jan 01 00:00:04 1970 +0000
346 +++ b/d Thu Jan 01 00:00:04 1970 +0000
347 @@ -0,0 +1,1 @@
347 @@ -0,0 +1,1 @@
348 +a
348 +a
349 diff -r f8954cd4dc1f -r 2ca5ba701980 g
349 diff -r f8954cd4dc1f -r 2ca5ba701980 g
350 --- a/g Thu Jan 01 00:00:03 1970 +0000
350 --- a/g Thu Jan 01 00:00:03 1970 +0000
351 +++ b/g Thu Jan 01 00:00:04 1970 +0000
351 +++ b/g Thu Jan 01 00:00:04 1970 +0000
352 @@ -1,2 +1,2 @@
352 @@ -1,2 +1,2 @@
353 f
353 f
354 -g
354 -g
355 +f
355 +f
356
356
357 changeset: 2:f8954cd4dc1f
357 changeset: 2:f8954cd4dc1f
358 user: test
358 user: test
359 date: Thu Jan 01 00:00:03 1970 +0000
359 date: Thu Jan 01 00:00:03 1970 +0000
360 summary: c
360 summary: c
361
361
362 diff -r d89b0a12d229 -r f8954cd4dc1f b
362 diff -r d89b0a12d229 -r f8954cd4dc1f b
363 --- a/b Thu Jan 01 00:00:02 1970 +0000
363 --- a/b Thu Jan 01 00:00:02 1970 +0000
364 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
364 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
365 @@ -1,1 +0,0 @@
365 @@ -1,1 +0,0 @@
366 -a
366 -a
367 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
367 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
368 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
368 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
369 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
369 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
370 @@ -0,0 +1,1 @@
370 @@ -0,0 +1,1 @@
371 +a
371 +a
372 diff -r d89b0a12d229 -r f8954cd4dc1f f
372 diff -r d89b0a12d229 -r f8954cd4dc1f f
373 --- a/f Thu Jan 01 00:00:02 1970 +0000
373 --- a/f Thu Jan 01 00:00:02 1970 +0000
374 +++ b/f Thu Jan 01 00:00:03 1970 +0000
374 +++ b/f Thu Jan 01 00:00:03 1970 +0000
375 @@ -1,1 +1,2 @@
375 @@ -1,1 +1,2 @@
376 f
376 f
377 +f
377 +f
378 diff -r d89b0a12d229 -r f8954cd4dc1f g
378 diff -r d89b0a12d229 -r f8954cd4dc1f g
379 --- a/g Thu Jan 01 00:00:02 1970 +0000
379 --- a/g Thu Jan 01 00:00:02 1970 +0000
380 +++ b/g Thu Jan 01 00:00:03 1970 +0000
380 +++ b/g Thu Jan 01 00:00:03 1970 +0000
381 @@ -1,1 +1,2 @@
381 @@ -1,1 +1,2 @@
382 f
382 f
383 +g
383 +g
384
384
385 changeset: 1:d89b0a12d229
385 changeset: 1:d89b0a12d229
386 user: test
386 user: test
387 date: Thu Jan 01 00:00:02 1970 +0000
387 date: Thu Jan 01 00:00:02 1970 +0000
388 summary: b
388 summary: b
389
389
390 diff -r 9161b9aeaf16 -r d89b0a12d229 b
390 diff -r 9161b9aeaf16 -r d89b0a12d229 b
391 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
391 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
392 +++ b/b Thu Jan 01 00:00:02 1970 +0000
392 +++ b/b Thu Jan 01 00:00:02 1970 +0000
393 @@ -0,0 +1,1 @@
393 @@ -0,0 +1,1 @@
394 +a
394 +a
395 diff -r 9161b9aeaf16 -r d89b0a12d229 g
395 diff -r 9161b9aeaf16 -r d89b0a12d229 g
396 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
396 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
397 +++ b/g Thu Jan 01 00:00:02 1970 +0000
397 +++ b/g Thu Jan 01 00:00:02 1970 +0000
398 @@ -0,0 +1,1 @@
398 @@ -0,0 +1,1 @@
399 +f
399 +f
400
400
401 changeset: 0:9161b9aeaf16
401 changeset: 0:9161b9aeaf16
402 user: test
402 user: test
403 date: Thu Jan 01 00:00:01 1970 +0000
403 date: Thu Jan 01 00:00:01 1970 +0000
404 summary: a
404 summary: a
405
405
406 diff -r 000000000000 -r 9161b9aeaf16 a
406 diff -r 000000000000 -r 9161b9aeaf16 a
407 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
407 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
408 +++ b/a Thu Jan 01 00:00:01 1970 +0000
408 +++ b/a Thu Jan 01 00:00:01 1970 +0000
409 @@ -0,0 +1,1 @@
409 @@ -0,0 +1,1 @@
410 +a
410 +a
411 diff -r 000000000000 -r 9161b9aeaf16 f
411 diff -r 000000000000 -r 9161b9aeaf16 f
412 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
412 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
413 +++ b/f Thu Jan 01 00:00:01 1970 +0000
413 +++ b/f Thu Jan 01 00:00:01 1970 +0000
414 @@ -0,0 +1,1 @@
414 @@ -0,0 +1,1 @@
415 +f
415 +f
416
416
417
417
418 log -vf dir/b
418 log -vf dir/b
419
419
420 $ hg log -vf dir/b
420 $ hg log -vf dir/b
421 changeset: 2:f8954cd4dc1f
421 changeset: 2:f8954cd4dc1f
422 user: test
422 user: test
423 date: Thu Jan 01 00:00:03 1970 +0000
423 date: Thu Jan 01 00:00:03 1970 +0000
424 files: b dir/b f g
424 files: b dir/b f g
425 description:
425 description:
426 c
426 c
427
427
428
428
429 changeset: 1:d89b0a12d229
429 changeset: 1:d89b0a12d229
430 user: test
430 user: test
431 date: Thu Jan 01 00:00:02 1970 +0000
431 date: Thu Jan 01 00:00:02 1970 +0000
432 files: b g
432 files: b g
433 description:
433 description:
434 b
434 b
435
435
436
436
437 changeset: 0:9161b9aeaf16
437 changeset: 0:9161b9aeaf16
438 user: test
438 user: test
439 date: Thu Jan 01 00:00:01 1970 +0000
439 date: Thu Jan 01 00:00:01 1970 +0000
440 files: a f
440 files: a f
441 description:
441 description:
442 a
442 a
443
443
444
444
445
445
446
446
447 -f and multiple filelog heads
447 -f and multiple filelog heads
448
448
449 $ hg up -q 2
449 $ hg up -q 2
450 $ hg log -f g --template '{rev}\n'
450 $ hg log -f g --template '{rev}\n'
451 2
451 2
452 1
452 1
453 0
453 0
454 $ hg up -q tip
454 $ hg up -q tip
455 $ hg log -f g --template '{rev}\n'
455 $ hg log -f g --template '{rev}\n'
456 3
456 3
457 2
457 2
458 0
458 0
459
459
460
460
461 log copies with --copies
461 log copies with --copies
462
462
463 $ hg log -vC --template '{rev} {file_copies}\n'
463 $ hg log -vC --template '{rev} {file_copies}\n'
464 4 e (dir/b)
464 4 e (dir/b)
465 3 b (a)g (f)
465 3 b (a)g (f)
466 2 dir/b (b)
466 2 dir/b (b)
467 1 b (a)g (f)
467 1 b (a)g (f)
468 0
468 0
469
469
470 log copies switch without --copies, with old filecopy template
470 log copies switch without --copies, with old filecopy template
471
471
472 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
472 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
473 4
473 4
474 3
474 3
475 2
475 2
476 1
476 1
477 0
477 0
478
478
479 log copies switch with --copies
479 log copies switch with --copies
480
480
481 $ hg log -vC --template '{rev} {file_copies_switch}\n'
481 $ hg log -vC --template '{rev} {file_copies_switch}\n'
482 4 e (dir/b)
482 4 e (dir/b)
483 3 b (a)g (f)
483 3 b (a)g (f)
484 2 dir/b (b)
484 2 dir/b (b)
485 1 b (a)g (f)
485 1 b (a)g (f)
486 0
486 0
487
487
488
488
489 log copies with hardcoded style and with --style=default
489 log copies with hardcoded style and with --style=default
490
490
491 $ hg log -vC -r4
491 $ hg log -vC -r4
492 changeset: 4:7e4639b4691b
492 changeset: 4:7e4639b4691b
493 tag: tip
493 tag: tip
494 user: test
494 user: test
495 date: Thu Jan 01 00:00:05 1970 +0000
495 date: Thu Jan 01 00:00:05 1970 +0000
496 files: dir/b e
496 files: dir/b e
497 copies: e (dir/b)
497 copies: e (dir/b)
498 description:
498 description:
499 e
499 e
500
500
501
501
502 $ hg log -vC -r4 --style=default
502 $ hg log -vC -r4 --style=default
503 changeset: 4:7e4639b4691b
503 changeset: 4:7e4639b4691b
504 tag: tip
504 tag: tip
505 user: test
505 user: test
506 date: Thu Jan 01 00:00:05 1970 +0000
506 date: Thu Jan 01 00:00:05 1970 +0000
507 files: dir/b e
507 files: dir/b e
508 copies: e (dir/b)
508 copies: e (dir/b)
509 description:
509 description:
510 e
510 e
511
511
512
512
513 $ hg log -vC -r4 -Tjson
513 $ hg log -vC -r4 -Tjson
514 [
514 [
515 {
515 {
516 "rev": 4,
516 "rev": 4,
517 "node": "7e4639b4691b9f84b81036a8d4fb218ce3c5e3a3",
517 "node": "7e4639b4691b9f84b81036a8d4fb218ce3c5e3a3",
518 "branch": "default",
518 "branch": "default",
519 "phase": "draft",
519 "phase": "draft",
520 "user": "test",
520 "user": "test",
521 "date": [5, 0],
521 "date": [5, 0],
522 "desc": "e",
522 "desc": "e",
523 "bookmarks": [],
523 "bookmarks": [],
524 "tags": ["tip"],
524 "tags": ["tip"],
525 "parents": ["2ca5ba7019804f1f597249caddf22a64d34df0ba"],
525 "parents": ["2ca5ba7019804f1f597249caddf22a64d34df0ba"],
526 "files": ["dir/b", "e"],
526 "files": ["dir/b", "e"],
527 "copies": {"e": "dir/b"}
527 "copies": {"e": "dir/b"}
528 }
528 }
529 ]
529 ]
530
530
531 log copies, non-linear manifest
531 log copies, non-linear manifest
532
532
533 $ hg up -C 3
533 $ hg up -C 3
534 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
534 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
535 $ hg mv dir/b e
535 $ hg mv dir/b e
536 $ echo foo > foo
536 $ echo foo > foo
537 $ hg ci -Ame2 -d '6 0'
537 $ hg ci -Ame2 -d '6 0'
538 adding foo
538 adding foo
539 created new head
539 created new head
540 $ hg log -v --template '{rev} {file_copies}\n' -r 5
540 $ hg log -v --template '{rev} {file_copies}\n' -r 5
541 5 e (dir/b)
541 5 e (dir/b)
542
542
543
543
544 log copies, execute bit set
544 log copies, execute bit set
545
545
546 #if execbit
546 #if execbit
547 $ chmod +x e
547 $ chmod +x e
548 $ hg ci -me3 -d '7 0'
548 $ hg ci -me3 -d '7 0'
549 $ hg log -v --template '{rev} {file_copies}\n' -r 6
549 $ hg log -v --template '{rev} {file_copies}\n' -r 6
550 6
550 6
551 #endif
551 #endif
552
552
553
553
554 log -p d
554 log -p d
555
555
556 $ hg log -pv d
556 $ hg log -pv d
557 changeset: 3:2ca5ba701980
557 changeset: 3:2ca5ba701980
558 user: test
558 user: test
559 date: Thu Jan 01 00:00:04 1970 +0000
559 date: Thu Jan 01 00:00:04 1970 +0000
560 files: a b d g
560 files: a b d g
561 description:
561 description:
562 d
562 d
563
563
564
564
565 diff -r f8954cd4dc1f -r 2ca5ba701980 d
565 diff -r f8954cd4dc1f -r 2ca5ba701980 d
566 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
566 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
567 +++ b/d Thu Jan 01 00:00:04 1970 +0000
567 +++ b/d Thu Jan 01 00:00:04 1970 +0000
568 @@ -0,0 +1,1 @@
568 @@ -0,0 +1,1 @@
569 +a
569 +a
570
570
571
571
572
572
573 log --removed file
573 log --removed file
574
574
575 $ hg log --removed -v a
575 $ hg log --removed -v a
576 changeset: 3:2ca5ba701980
576 changeset: 3:2ca5ba701980
577 user: test
577 user: test
578 date: Thu Jan 01 00:00:04 1970 +0000
578 date: Thu Jan 01 00:00:04 1970 +0000
579 files: a b d g
579 files: a b d g
580 description:
580 description:
581 d
581 d
582
582
583
583
584 changeset: 0:9161b9aeaf16
584 changeset: 0:9161b9aeaf16
585 user: test
585 user: test
586 date: Thu Jan 01 00:00:01 1970 +0000
586 date: Thu Jan 01 00:00:01 1970 +0000
587 files: a f
587 files: a f
588 description:
588 description:
589 a
589 a
590
590
591
591
592
592
593 log --removed revrange file
593 log --removed revrange file
594
594
595 $ hg log --removed -v -r0:2 a
595 $ hg log --removed -v -r0:2 a
596 changeset: 0:9161b9aeaf16
596 changeset: 0:9161b9aeaf16
597 user: test
597 user: test
598 date: Thu Jan 01 00:00:01 1970 +0000
598 date: Thu Jan 01 00:00:01 1970 +0000
599 files: a f
599 files: a f
600 description:
600 description:
601 a
601 a
602
602
603
603
604 $ cd ..
604 $ cd ..
605
605
606 log --follow tests
606 log --follow tests
607
607
608 $ hg init follow
608 $ hg init follow
609 $ cd follow
609 $ cd follow
610
610
611 $ echo base > base
611 $ echo base > base
612 $ hg ci -Ambase -d '1 0'
612 $ hg ci -Ambase -d '1 0'
613 adding base
613 adding base
614
614
615 $ echo r1 >> base
615 $ echo r1 >> base
616 $ hg ci -Amr1 -d '1 0'
616 $ hg ci -Amr1 -d '1 0'
617 $ echo r2 >> base
617 $ echo r2 >> base
618 $ hg ci -Amr2 -d '1 0'
618 $ hg ci -Amr2 -d '1 0'
619
619
620 $ hg up -C 1
620 $ hg up -C 1
621 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
621 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
622 $ echo b1 > b1
622 $ echo b1 > b1
623
623
624 log -r "follow('set:clean()')"
624 log -r "follow('set:clean()')"
625
625
626 $ hg log -r "follow('set:clean()')"
626 $ hg log -r "follow('set:clean()')"
627 changeset: 0:67e992f2c4f3
627 changeset: 0:67e992f2c4f3
628 user: test
628 user: test
629 date: Thu Jan 01 00:00:01 1970 +0000
629 date: Thu Jan 01 00:00:01 1970 +0000
630 summary: base
630 summary: base
631
631
632 changeset: 1:3d5bf5654eda
632 changeset: 1:3d5bf5654eda
633 user: test
633 user: test
634 date: Thu Jan 01 00:00:01 1970 +0000
634 date: Thu Jan 01 00:00:01 1970 +0000
635 summary: r1
635 summary: r1
636
636
637
637
638 $ hg ci -Amb1 -d '1 0'
638 $ hg ci -Amb1 -d '1 0'
639 adding b1
639 adding b1
640 created new head
640 created new head
641
641
642
642
643 log -f
643 log -f
644
644
645 $ hg log -f
645 $ hg log -f
646 changeset: 3:e62f78d544b4
646 changeset: 3:e62f78d544b4
647 tag: tip
647 tag: tip
648 parent: 1:3d5bf5654eda
648 parent: 1:3d5bf5654eda
649 user: test
649 user: test
650 date: Thu Jan 01 00:00:01 1970 +0000
650 date: Thu Jan 01 00:00:01 1970 +0000
651 summary: b1
651 summary: b1
652
652
653 changeset: 1:3d5bf5654eda
653 changeset: 1:3d5bf5654eda
654 user: test
654 user: test
655 date: Thu Jan 01 00:00:01 1970 +0000
655 date: Thu Jan 01 00:00:01 1970 +0000
656 summary: r1
656 summary: r1
657
657
658 changeset: 0:67e992f2c4f3
658 changeset: 0:67e992f2c4f3
659 user: test
659 user: test
660 date: Thu Jan 01 00:00:01 1970 +0000
660 date: Thu Jan 01 00:00:01 1970 +0000
661 summary: base
661 summary: base
662
662
663
663
664 log -r follow('glob:b*')
664 log -r follow('glob:b*')
665
665
666 $ hg log -r "follow('glob:b*')"
666 $ hg log -r "follow('glob:b*')"
667 changeset: 0:67e992f2c4f3
667 changeset: 0:67e992f2c4f3
668 user: test
668 user: test
669 date: Thu Jan 01 00:00:01 1970 +0000
669 date: Thu Jan 01 00:00:01 1970 +0000
670 summary: base
670 summary: base
671
671
672 changeset: 1:3d5bf5654eda
672 changeset: 1:3d5bf5654eda
673 user: test
673 user: test
674 date: Thu Jan 01 00:00:01 1970 +0000
674 date: Thu Jan 01 00:00:01 1970 +0000
675 summary: r1
675 summary: r1
676
676
677 changeset: 3:e62f78d544b4
677 changeset: 3:e62f78d544b4
678 tag: tip
678 tag: tip
679 parent: 1:3d5bf5654eda
679 parent: 1:3d5bf5654eda
680 user: test
680 user: test
681 date: Thu Jan 01 00:00:01 1970 +0000
681 date: Thu Jan 01 00:00:01 1970 +0000
682 summary: b1
682 summary: b1
683
683
684 log -f -r '1 + 4'
684 log -f -r '1 + 4'
685
685
686 $ hg up -C 0
686 $ hg up -C 0
687 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
687 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
688 $ echo b2 > b2
688 $ echo b2 > b2
689 $ hg ci -Amb2 -d '1 0'
689 $ hg ci -Amb2 -d '1 0'
690 adding b2
690 adding b2
691 created new head
691 created new head
692 $ hg log -f -r '1 + 4'
692 $ hg log -f -r '1 + 4'
693 changeset: 4:ddb82e70d1a1
693 changeset: 4:ddb82e70d1a1
694 tag: tip
694 tag: tip
695 parent: 0:67e992f2c4f3
695 parent: 0:67e992f2c4f3
696 user: test
696 user: test
697 date: Thu Jan 01 00:00:01 1970 +0000
697 date: Thu Jan 01 00:00:01 1970 +0000
698 summary: b2
698 summary: b2
699
699
700 changeset: 1:3d5bf5654eda
700 changeset: 1:3d5bf5654eda
701 user: test
701 user: test
702 date: Thu Jan 01 00:00:01 1970 +0000
702 date: Thu Jan 01 00:00:01 1970 +0000
703 summary: r1
703 summary: r1
704
704
705 changeset: 0:67e992f2c4f3
705 changeset: 0:67e992f2c4f3
706 user: test
706 user: test
707 date: Thu Jan 01 00:00:01 1970 +0000
707 date: Thu Jan 01 00:00:01 1970 +0000
708 summary: base
708 summary: base
709
709
710 log -r "follow('set:grep(b2)')"
710 log -r "follow('set:grep(b2)')"
711
711
712 $ hg log -r "follow('set:grep(b2)')"
712 $ hg log -r "follow('set:grep(b2)')"
713 changeset: 4:ddb82e70d1a1
713 changeset: 4:ddb82e70d1a1
714 tag: tip
714 tag: tip
715 parent: 0:67e992f2c4f3
715 parent: 0:67e992f2c4f3
716 user: test
716 user: test
717 date: Thu Jan 01 00:00:01 1970 +0000
717 date: Thu Jan 01 00:00:01 1970 +0000
718 summary: b2
718 summary: b2
719
719
720 log -r "follow('set:grep(b2)', 4)"
721
722 $ hg up -qC 0
723 $ hg log -r "follow('set:grep(b2)', 4)"
724 changeset: 4:ddb82e70d1a1
725 tag: tip
726 parent: 0:67e992f2c4f3
727 user: test
728 date: Thu Jan 01 00:00:01 1970 +0000
729 summary: b2
730
731 $ hg up -qC 4
732
720 log -f -r null
733 log -f -r null
721
734
722 $ hg log -f -r null
735 $ hg log -f -r null
723 changeset: -1:000000000000
736 changeset: -1:000000000000
724 user:
737 user:
725 date: Thu Jan 01 00:00:00 1970 +0000
738 date: Thu Jan 01 00:00:00 1970 +0000
726
739
727 $ hg log -f -r null -G
740 $ hg log -f -r null -G
728 o changeset: -1:000000000000
741 o changeset: -1:000000000000
729 user:
742 user:
730 date: Thu Jan 01 00:00:00 1970 +0000
743 date: Thu Jan 01 00:00:00 1970 +0000
731
744
732
745
733
746
734 log -f with null parent
747 log -f with null parent
735
748
736 $ hg up -C null
749 $ hg up -C null
737 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
750 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
738 $ hg log -f
751 $ hg log -f
739
752
740
753
741 log -r . with two parents
754 log -r . with two parents
742
755
743 $ hg up -C 3
756 $ hg up -C 3
744 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
757 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
745 $ hg merge tip
758 $ hg merge tip
746 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
759 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
747 (branch merge, don't forget to commit)
760 (branch merge, don't forget to commit)
748 $ hg log -r .
761 $ hg log -r .
749 changeset: 3:e62f78d544b4
762 changeset: 3:e62f78d544b4
750 parent: 1:3d5bf5654eda
763 parent: 1:3d5bf5654eda
751 user: test
764 user: test
752 date: Thu Jan 01 00:00:01 1970 +0000
765 date: Thu Jan 01 00:00:01 1970 +0000
753 summary: b1
766 summary: b1
754
767
755
768
756
769
757 log -r . with one parent
770 log -r . with one parent
758
771
759 $ hg ci -mm12 -d '1 0'
772 $ hg ci -mm12 -d '1 0'
760 $ hg log -r .
773 $ hg log -r .
761 changeset: 5:302e9dd6890d
774 changeset: 5:302e9dd6890d
762 tag: tip
775 tag: tip
763 parent: 3:e62f78d544b4
776 parent: 3:e62f78d544b4
764 parent: 4:ddb82e70d1a1
777 parent: 4:ddb82e70d1a1
765 user: test
778 user: test
766 date: Thu Jan 01 00:00:01 1970 +0000
779 date: Thu Jan 01 00:00:01 1970 +0000
767 summary: m12
780 summary: m12
768
781
769
782
770 $ echo postm >> b1
783 $ echo postm >> b1
771 $ hg ci -Amb1.1 -d'1 0'
784 $ hg ci -Amb1.1 -d'1 0'
772
785
773
786
774 log --follow-first
787 log --follow-first
775
788
776 $ hg log --follow-first
789 $ hg log --follow-first
777 changeset: 6:2404bbcab562
790 changeset: 6:2404bbcab562
778 tag: tip
791 tag: tip
779 user: test
792 user: test
780 date: Thu Jan 01 00:00:01 1970 +0000
793 date: Thu Jan 01 00:00:01 1970 +0000
781 summary: b1.1
794 summary: b1.1
782
795
783 changeset: 5:302e9dd6890d
796 changeset: 5:302e9dd6890d
784 parent: 3:e62f78d544b4
797 parent: 3:e62f78d544b4
785 parent: 4:ddb82e70d1a1
798 parent: 4:ddb82e70d1a1
786 user: test
799 user: test
787 date: Thu Jan 01 00:00:01 1970 +0000
800 date: Thu Jan 01 00:00:01 1970 +0000
788 summary: m12
801 summary: m12
789
802
790 changeset: 3:e62f78d544b4
803 changeset: 3:e62f78d544b4
791 parent: 1:3d5bf5654eda
804 parent: 1:3d5bf5654eda
792 user: test
805 user: test
793 date: Thu Jan 01 00:00:01 1970 +0000
806 date: Thu Jan 01 00:00:01 1970 +0000
794 summary: b1
807 summary: b1
795
808
796 changeset: 1:3d5bf5654eda
809 changeset: 1:3d5bf5654eda
797 user: test
810 user: test
798 date: Thu Jan 01 00:00:01 1970 +0000
811 date: Thu Jan 01 00:00:01 1970 +0000
799 summary: r1
812 summary: r1
800
813
801 changeset: 0:67e992f2c4f3
814 changeset: 0:67e992f2c4f3
802 user: test
815 user: test
803 date: Thu Jan 01 00:00:01 1970 +0000
816 date: Thu Jan 01 00:00:01 1970 +0000
804 summary: base
817 summary: base
805
818
806
819
807
820
808 log -P 2
821 log -P 2
809
822
810 $ hg log -P 2
823 $ hg log -P 2
811 changeset: 6:2404bbcab562
824 changeset: 6:2404bbcab562
812 tag: tip
825 tag: tip
813 user: test
826 user: test
814 date: Thu Jan 01 00:00:01 1970 +0000
827 date: Thu Jan 01 00:00:01 1970 +0000
815 summary: b1.1
828 summary: b1.1
816
829
817 changeset: 5:302e9dd6890d
830 changeset: 5:302e9dd6890d
818 parent: 3:e62f78d544b4
831 parent: 3:e62f78d544b4
819 parent: 4:ddb82e70d1a1
832 parent: 4:ddb82e70d1a1
820 user: test
833 user: test
821 date: Thu Jan 01 00:00:01 1970 +0000
834 date: Thu Jan 01 00:00:01 1970 +0000
822 summary: m12
835 summary: m12
823
836
824 changeset: 4:ddb82e70d1a1
837 changeset: 4:ddb82e70d1a1
825 parent: 0:67e992f2c4f3
838 parent: 0:67e992f2c4f3
826 user: test
839 user: test
827 date: Thu Jan 01 00:00:01 1970 +0000
840 date: Thu Jan 01 00:00:01 1970 +0000
828 summary: b2
841 summary: b2
829
842
830 changeset: 3:e62f78d544b4
843 changeset: 3:e62f78d544b4
831 parent: 1:3d5bf5654eda
844 parent: 1:3d5bf5654eda
832 user: test
845 user: test
833 date: Thu Jan 01 00:00:01 1970 +0000
846 date: Thu Jan 01 00:00:01 1970 +0000
834 summary: b1
847 summary: b1
835
848
836
849
837
850
838 log -r tip -p --git
851 log -r tip -p --git
839
852
840 $ hg log -r tip -p --git
853 $ hg log -r tip -p --git
841 changeset: 6:2404bbcab562
854 changeset: 6:2404bbcab562
842 tag: tip
855 tag: tip
843 user: test
856 user: test
844 date: Thu Jan 01 00:00:01 1970 +0000
857 date: Thu Jan 01 00:00:01 1970 +0000
845 summary: b1.1
858 summary: b1.1
846
859
847 diff --git a/b1 b/b1
860 diff --git a/b1 b/b1
848 --- a/b1
861 --- a/b1
849 +++ b/b1
862 +++ b/b1
850 @@ -1,1 +1,2 @@
863 @@ -1,1 +1,2 @@
851 b1
864 b1
852 +postm
865 +postm
853
866
854
867
855
868
856 log -r ""
869 log -r ""
857
870
858 $ hg log -r ''
871 $ hg log -r ''
859 hg: parse error: empty query
872 hg: parse error: empty query
860 [255]
873 [255]
861
874
862 log -r <some unknown node id>
875 log -r <some unknown node id>
863
876
864 $ hg log -r 1000000000000000000000000000000000000000
877 $ hg log -r 1000000000000000000000000000000000000000
865 abort: unknown revision '1000000000000000000000000000000000000000'!
878 abort: unknown revision '1000000000000000000000000000000000000000'!
866 [255]
879 [255]
867
880
868 log -k r1
881 log -k r1
869
882
870 $ hg log -k r1
883 $ hg log -k r1
871 changeset: 1:3d5bf5654eda
884 changeset: 1:3d5bf5654eda
872 user: test
885 user: test
873 date: Thu Jan 01 00:00:01 1970 +0000
886 date: Thu Jan 01 00:00:01 1970 +0000
874 summary: r1
887 summary: r1
875
888
876 log -p -l2 --color=always
889 log -p -l2 --color=always
877
890
878 $ hg --config extensions.color= --config color.mode=ansi \
891 $ hg --config extensions.color= --config color.mode=ansi \
879 > log -p -l2 --color=always
892 > log -p -l2 --color=always
880 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
893 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
881 tag: tip
894 tag: tip
882 user: test
895 user: test
883 date: Thu Jan 01 00:00:01 1970 +0000
896 date: Thu Jan 01 00:00:01 1970 +0000
884 summary: b1.1
897 summary: b1.1
885
898
886 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
899 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
887 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
900 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
888 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
901 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
889 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
902 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
890 b1
903 b1
891 \x1b[0;32m+postm\x1b[0m (esc)
904 \x1b[0;32m+postm\x1b[0m (esc)
892
905
893 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
906 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
894 parent: 3:e62f78d544b4
907 parent: 3:e62f78d544b4
895 parent: 4:ddb82e70d1a1
908 parent: 4:ddb82e70d1a1
896 user: test
909 user: test
897 date: Thu Jan 01 00:00:01 1970 +0000
910 date: Thu Jan 01 00:00:01 1970 +0000
898 summary: m12
911 summary: m12
899
912
900 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
913 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
901 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
914 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
902 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
915 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
903 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
916 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
904 \x1b[0;32m+b2\x1b[0m (esc)
917 \x1b[0;32m+b2\x1b[0m (esc)
905
918
906
919
907
920
908 log -r tip --stat
921 log -r tip --stat
909
922
910 $ hg log -r tip --stat
923 $ hg log -r tip --stat
911 changeset: 6:2404bbcab562
924 changeset: 6:2404bbcab562
912 tag: tip
925 tag: tip
913 user: test
926 user: test
914 date: Thu Jan 01 00:00:01 1970 +0000
927 date: Thu Jan 01 00:00:01 1970 +0000
915 summary: b1.1
928 summary: b1.1
916
929
917 b1 | 1 +
930 b1 | 1 +
918 1 files changed, 1 insertions(+), 0 deletions(-)
931 1 files changed, 1 insertions(+), 0 deletions(-)
919
932
920
933
921 $ cd ..
934 $ cd ..
922
935
923 Test that log should respect the order of -rREV even if multiple OR conditions
936 Test that log should respect the order of -rREV even if multiple OR conditions
924 are specified (issue5100):
937 are specified (issue5100):
925
938
926 $ hg init revorder
939 $ hg init revorder
927 $ cd revorder
940 $ cd revorder
928
941
929 $ hg branch -q b0
942 $ hg branch -q b0
930 $ echo 0 >> f0
943 $ echo 0 >> f0
931 $ hg ci -qAm k0 -u u0
944 $ hg ci -qAm k0 -u u0
932 $ hg branch -q b1
945 $ hg branch -q b1
933 $ echo 1 >> f1
946 $ echo 1 >> f1
934 $ hg ci -qAm k1 -u u1
947 $ hg ci -qAm k1 -u u1
935 $ hg branch -q b2
948 $ hg branch -q b2
936 $ echo 2 >> f2
949 $ echo 2 >> f2
937 $ hg ci -qAm k2 -u u2
950 $ hg ci -qAm k2 -u u2
938
951
939 $ hg update -q b2
952 $ hg update -q b2
940 $ echo 3 >> f2
953 $ echo 3 >> f2
941 $ hg ci -qAm k2 -u u2
954 $ hg ci -qAm k2 -u u2
942 $ hg update -q b1
955 $ hg update -q b1
943 $ echo 4 >> f1
956 $ echo 4 >> f1
944 $ hg ci -qAm k1 -u u1
957 $ hg ci -qAm k1 -u u1
945 $ hg update -q b0
958 $ hg update -q b0
946 $ echo 5 >> f0
959 $ echo 5 >> f0
947 $ hg ci -qAm k0 -u u0
960 $ hg ci -qAm k0 -u u0
948
961
949 summary of revisions:
962 summary of revisions:
950
963
951 $ hg log -G -T '{rev} {branch} {author} {desc} {files}\n'
964 $ hg log -G -T '{rev} {branch} {author} {desc} {files}\n'
952 @ 5 b0 u0 k0 f0
965 @ 5 b0 u0 k0 f0
953 |
966 |
954 | o 4 b1 u1 k1 f1
967 | o 4 b1 u1 k1 f1
955 | |
968 | |
956 | | o 3 b2 u2 k2 f2
969 | | o 3 b2 u2 k2 f2
957 | | |
970 | | |
958 | | o 2 b2 u2 k2 f2
971 | | o 2 b2 u2 k2 f2
959 | |/
972 | |/
960 | o 1 b1 u1 k1 f1
973 | o 1 b1 u1 k1 f1
961 |/
974 |/
962 o 0 b0 u0 k0 f0
975 o 0 b0 u0 k0 f0
963
976
964
977
965 log -b BRANCH in ascending order:
978 log -b BRANCH in ascending order:
966
979
967 $ hg log -r0:tip -T '{rev} {branch}\n' -b b0 -b b1
980 $ hg log -r0:tip -T '{rev} {branch}\n' -b b0 -b b1
968 0 b0
981 0 b0
969 1 b1
982 1 b1
970 4 b1
983 4 b1
971 5 b0
984 5 b0
972 $ hg log -r0:tip -T '{rev} {branch}\n' -b b1 -b b0
985 $ hg log -r0:tip -T '{rev} {branch}\n' -b b1 -b b0
973 0 b0
986 0 b0
974 1 b1
987 1 b1
975 4 b1
988 4 b1
976 5 b0
989 5 b0
977
990
978 log --only-branch BRANCH in descending order:
991 log --only-branch BRANCH in descending order:
979
992
980 $ hg log -rtip:0 -T '{rev} {branch}\n' --only-branch b1 --only-branch b2
993 $ hg log -rtip:0 -T '{rev} {branch}\n' --only-branch b1 --only-branch b2
981 4 b1
994 4 b1
982 3 b2
995 3 b2
983 2 b2
996 2 b2
984 1 b1
997 1 b1
985 $ hg log -rtip:0 -T '{rev} {branch}\n' --only-branch b2 --only-branch b1
998 $ hg log -rtip:0 -T '{rev} {branch}\n' --only-branch b2 --only-branch b1
986 4 b1
999 4 b1
987 3 b2
1000 3 b2
988 2 b2
1001 2 b2
989 1 b1
1002 1 b1
990
1003
991 log -u USER in ascending order, against compound set:
1004 log -u USER in ascending order, against compound set:
992
1005
993 $ hg log -r'::head()' -T '{rev} {author}\n' -u u0 -u u2
1006 $ hg log -r'::head()' -T '{rev} {author}\n' -u u0 -u u2
994 0 u0
1007 0 u0
995 2 u2
1008 2 u2
996 3 u2
1009 3 u2
997 5 u0
1010 5 u0
998 $ hg log -r'::head()' -T '{rev} {author}\n' -u u2 -u u0
1011 $ hg log -r'::head()' -T '{rev} {author}\n' -u u2 -u u0
999 0 u0
1012 0 u0
1000 2 u2
1013 2 u2
1001 3 u2
1014 3 u2
1002 5 u0
1015 5 u0
1003
1016
1004 log -k TEXT in descending order, against compound set:
1017 log -k TEXT in descending order, against compound set:
1005
1018
1006 $ hg log -r'5 + reverse(::3)' -T '{rev} {desc}\n' -k k0 -k k1 -k k2
1019 $ hg log -r'5 + reverse(::3)' -T '{rev} {desc}\n' -k k0 -k k1 -k k2
1007 5 k0
1020 5 k0
1008 3 k2
1021 3 k2
1009 2 k2
1022 2 k2
1010 1 k1
1023 1 k1
1011 0 k0
1024 0 k0
1012 $ hg log -r'5 + reverse(::3)' -T '{rev} {desc}\n' -k k2 -k k1 -k k0
1025 $ hg log -r'5 + reverse(::3)' -T '{rev} {desc}\n' -k k2 -k k1 -k k0
1013 5 k0
1026 5 k0
1014 3 k2
1027 3 k2
1015 2 k2
1028 2 k2
1016 1 k1
1029 1 k1
1017 0 k0
1030 0 k0
1018
1031
1019 log FILE in ascending order, against dagrange:
1032 log FILE in ascending order, against dagrange:
1020
1033
1021 $ hg log -r1:: -T '{rev} {files}\n' f1 f2
1034 $ hg log -r1:: -T '{rev} {files}\n' f1 f2
1022 1 f1
1035 1 f1
1023 2 f2
1036 2 f2
1024 3 f2
1037 3 f2
1025 4 f1
1038 4 f1
1026 $ hg log -r1:: -T '{rev} {files}\n' f2 f1
1039 $ hg log -r1:: -T '{rev} {files}\n' f2 f1
1027 1 f1
1040 1 f1
1028 2 f2
1041 2 f2
1029 3 f2
1042 3 f2
1030 4 f1
1043 4 f1
1031
1044
1032 $ cd ..
1045 $ cd ..
1033
1046
1034 User
1047 User
1035
1048
1036 $ hg init usertest
1049 $ hg init usertest
1037 $ cd usertest
1050 $ cd usertest
1038
1051
1039 $ echo a > a
1052 $ echo a > a
1040 $ hg ci -A -m "a" -u "User One <user1@example.org>"
1053 $ hg ci -A -m "a" -u "User One <user1@example.org>"
1041 adding a
1054 adding a
1042 $ echo b > b
1055 $ echo b > b
1043 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
1056 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
1044 adding b
1057 adding b
1045
1058
1046 $ hg log -u "User One <user1@example.org>"
1059 $ hg log -u "User One <user1@example.org>"
1047 changeset: 0:29a4c94f1924
1060 changeset: 0:29a4c94f1924
1048 user: User One <user1@example.org>
1061 user: User One <user1@example.org>
1049 date: Thu Jan 01 00:00:00 1970 +0000
1062 date: Thu Jan 01 00:00:00 1970 +0000
1050 summary: a
1063 summary: a
1051
1064
1052 $ hg log -u "user1" -u "user2"
1065 $ hg log -u "user1" -u "user2"
1053 changeset: 1:e834b5e69c0e
1066 changeset: 1:e834b5e69c0e
1054 tag: tip
1067 tag: tip
1055 user: User Two <user2@example.org>
1068 user: User Two <user2@example.org>
1056 date: Thu Jan 01 00:00:00 1970 +0000
1069 date: Thu Jan 01 00:00:00 1970 +0000
1057 summary: b
1070 summary: b
1058
1071
1059 changeset: 0:29a4c94f1924
1072 changeset: 0:29a4c94f1924
1060 user: User One <user1@example.org>
1073 user: User One <user1@example.org>
1061 date: Thu Jan 01 00:00:00 1970 +0000
1074 date: Thu Jan 01 00:00:00 1970 +0000
1062 summary: a
1075 summary: a
1063
1076
1064 $ hg log -u "user3"
1077 $ hg log -u "user3"
1065
1078
1066 $ cd ..
1079 $ cd ..
1067
1080
1068 $ hg init branches
1081 $ hg init branches
1069 $ cd branches
1082 $ cd branches
1070
1083
1071 $ echo a > a
1084 $ echo a > a
1072 $ hg ci -A -m "commit on default"
1085 $ hg ci -A -m "commit on default"
1073 adding a
1086 adding a
1074 $ hg branch test
1087 $ hg branch test
1075 marked working directory as branch test
1088 marked working directory as branch test
1076 (branches are permanent and global, did you want a bookmark?)
1089 (branches are permanent and global, did you want a bookmark?)
1077 $ echo b > b
1090 $ echo b > b
1078 $ hg ci -A -m "commit on test"
1091 $ hg ci -A -m "commit on test"
1079 adding b
1092 adding b
1080
1093
1081 $ hg up default
1094 $ hg up default
1082 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1095 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1083 $ echo c > c
1096 $ echo c > c
1084 $ hg ci -A -m "commit on default"
1097 $ hg ci -A -m "commit on default"
1085 adding c
1098 adding c
1086 $ hg up test
1099 $ hg up test
1087 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1100 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1088 $ echo c > c
1101 $ echo c > c
1089 $ hg ci -A -m "commit on test"
1102 $ hg ci -A -m "commit on test"
1090 adding c
1103 adding c
1091
1104
1092
1105
1093 log -b default
1106 log -b default
1094
1107
1095 $ hg log -b default
1108 $ hg log -b default
1096 changeset: 2:c3a4f03cc9a7
1109 changeset: 2:c3a4f03cc9a7
1097 parent: 0:24427303d56f
1110 parent: 0:24427303d56f
1098 user: test
1111 user: test
1099 date: Thu Jan 01 00:00:00 1970 +0000
1112 date: Thu Jan 01 00:00:00 1970 +0000
1100 summary: commit on default
1113 summary: commit on default
1101
1114
1102 changeset: 0:24427303d56f
1115 changeset: 0:24427303d56f
1103 user: test
1116 user: test
1104 date: Thu Jan 01 00:00:00 1970 +0000
1117 date: Thu Jan 01 00:00:00 1970 +0000
1105 summary: commit on default
1118 summary: commit on default
1106
1119
1107
1120
1108
1121
1109 log -b test
1122 log -b test
1110
1123
1111 $ hg log -b test
1124 $ hg log -b test
1112 changeset: 3:f5d8de11c2e2
1125 changeset: 3:f5d8de11c2e2
1113 branch: test
1126 branch: test
1114 tag: tip
1127 tag: tip
1115 parent: 1:d32277701ccb
1128 parent: 1:d32277701ccb
1116 user: test
1129 user: test
1117 date: Thu Jan 01 00:00:00 1970 +0000
1130 date: Thu Jan 01 00:00:00 1970 +0000
1118 summary: commit on test
1131 summary: commit on test
1119
1132
1120 changeset: 1:d32277701ccb
1133 changeset: 1:d32277701ccb
1121 branch: test
1134 branch: test
1122 user: test
1135 user: test
1123 date: Thu Jan 01 00:00:00 1970 +0000
1136 date: Thu Jan 01 00:00:00 1970 +0000
1124 summary: commit on test
1137 summary: commit on test
1125
1138
1126
1139
1127
1140
1128 log -b dummy
1141 log -b dummy
1129
1142
1130 $ hg log -b dummy
1143 $ hg log -b dummy
1131 abort: unknown revision 'dummy'!
1144 abort: unknown revision 'dummy'!
1132 [255]
1145 [255]
1133
1146
1134
1147
1135 log -b .
1148 log -b .
1136
1149
1137 $ hg log -b .
1150 $ hg log -b .
1138 changeset: 3:f5d8de11c2e2
1151 changeset: 3:f5d8de11c2e2
1139 branch: test
1152 branch: test
1140 tag: tip
1153 tag: tip
1141 parent: 1:d32277701ccb
1154 parent: 1:d32277701ccb
1142 user: test
1155 user: test
1143 date: Thu Jan 01 00:00:00 1970 +0000
1156 date: Thu Jan 01 00:00:00 1970 +0000
1144 summary: commit on test
1157 summary: commit on test
1145
1158
1146 changeset: 1:d32277701ccb
1159 changeset: 1:d32277701ccb
1147 branch: test
1160 branch: test
1148 user: test
1161 user: test
1149 date: Thu Jan 01 00:00:00 1970 +0000
1162 date: Thu Jan 01 00:00:00 1970 +0000
1150 summary: commit on test
1163 summary: commit on test
1151
1164
1152
1165
1153
1166
1154 log -b default -b test
1167 log -b default -b test
1155
1168
1156 $ hg log -b default -b test
1169 $ hg log -b default -b test
1157 changeset: 3:f5d8de11c2e2
1170 changeset: 3:f5d8de11c2e2
1158 branch: test
1171 branch: test
1159 tag: tip
1172 tag: tip
1160 parent: 1:d32277701ccb
1173 parent: 1:d32277701ccb
1161 user: test
1174 user: test
1162 date: Thu Jan 01 00:00:00 1970 +0000
1175 date: Thu Jan 01 00:00:00 1970 +0000
1163 summary: commit on test
1176 summary: commit on test
1164
1177
1165 changeset: 2:c3a4f03cc9a7
1178 changeset: 2:c3a4f03cc9a7
1166 parent: 0:24427303d56f
1179 parent: 0:24427303d56f
1167 user: test
1180 user: test
1168 date: Thu Jan 01 00:00:00 1970 +0000
1181 date: Thu Jan 01 00:00:00 1970 +0000
1169 summary: commit on default
1182 summary: commit on default
1170
1183
1171 changeset: 1:d32277701ccb
1184 changeset: 1:d32277701ccb
1172 branch: test
1185 branch: test
1173 user: test
1186 user: test
1174 date: Thu Jan 01 00:00:00 1970 +0000
1187 date: Thu Jan 01 00:00:00 1970 +0000
1175 summary: commit on test
1188 summary: commit on test
1176
1189
1177 changeset: 0:24427303d56f
1190 changeset: 0:24427303d56f
1178 user: test
1191 user: test
1179 date: Thu Jan 01 00:00:00 1970 +0000
1192 date: Thu Jan 01 00:00:00 1970 +0000
1180 summary: commit on default
1193 summary: commit on default
1181
1194
1182
1195
1183
1196
1184 log -b default -b .
1197 log -b default -b .
1185
1198
1186 $ hg log -b default -b .
1199 $ hg log -b default -b .
1187 changeset: 3:f5d8de11c2e2
1200 changeset: 3:f5d8de11c2e2
1188 branch: test
1201 branch: test
1189 tag: tip
1202 tag: tip
1190 parent: 1:d32277701ccb
1203 parent: 1:d32277701ccb
1191 user: test
1204 user: test
1192 date: Thu Jan 01 00:00:00 1970 +0000
1205 date: Thu Jan 01 00:00:00 1970 +0000
1193 summary: commit on test
1206 summary: commit on test
1194
1207
1195 changeset: 2:c3a4f03cc9a7
1208 changeset: 2:c3a4f03cc9a7
1196 parent: 0:24427303d56f
1209 parent: 0:24427303d56f
1197 user: test
1210 user: test
1198 date: Thu Jan 01 00:00:00 1970 +0000
1211 date: Thu Jan 01 00:00:00 1970 +0000
1199 summary: commit on default
1212 summary: commit on default
1200
1213
1201 changeset: 1:d32277701ccb
1214 changeset: 1:d32277701ccb
1202 branch: test
1215 branch: test
1203 user: test
1216 user: test
1204 date: Thu Jan 01 00:00:00 1970 +0000
1217 date: Thu Jan 01 00:00:00 1970 +0000
1205 summary: commit on test
1218 summary: commit on test
1206
1219
1207 changeset: 0:24427303d56f
1220 changeset: 0:24427303d56f
1208 user: test
1221 user: test
1209 date: Thu Jan 01 00:00:00 1970 +0000
1222 date: Thu Jan 01 00:00:00 1970 +0000
1210 summary: commit on default
1223 summary: commit on default
1211
1224
1212
1225
1213
1226
1214 log -b . -b test
1227 log -b . -b test
1215
1228
1216 $ hg log -b . -b test
1229 $ hg log -b . -b test
1217 changeset: 3:f5d8de11c2e2
1230 changeset: 3:f5d8de11c2e2
1218 branch: test
1231 branch: test
1219 tag: tip
1232 tag: tip
1220 parent: 1:d32277701ccb
1233 parent: 1:d32277701ccb
1221 user: test
1234 user: test
1222 date: Thu Jan 01 00:00:00 1970 +0000
1235 date: Thu Jan 01 00:00:00 1970 +0000
1223 summary: commit on test
1236 summary: commit on test
1224
1237
1225 changeset: 1:d32277701ccb
1238 changeset: 1:d32277701ccb
1226 branch: test
1239 branch: test
1227 user: test
1240 user: test
1228 date: Thu Jan 01 00:00:00 1970 +0000
1241 date: Thu Jan 01 00:00:00 1970 +0000
1229 summary: commit on test
1242 summary: commit on test
1230
1243
1231
1244
1232
1245
1233 log -b 2
1246 log -b 2
1234
1247
1235 $ hg log -b 2
1248 $ hg log -b 2
1236 changeset: 2:c3a4f03cc9a7
1249 changeset: 2:c3a4f03cc9a7
1237 parent: 0:24427303d56f
1250 parent: 0:24427303d56f
1238 user: test
1251 user: test
1239 date: Thu Jan 01 00:00:00 1970 +0000
1252 date: Thu Jan 01 00:00:00 1970 +0000
1240 summary: commit on default
1253 summary: commit on default
1241
1254
1242 changeset: 0:24427303d56f
1255 changeset: 0:24427303d56f
1243 user: test
1256 user: test
1244 date: Thu Jan 01 00:00:00 1970 +0000
1257 date: Thu Jan 01 00:00:00 1970 +0000
1245 summary: commit on default
1258 summary: commit on default
1246
1259
1247 #if gettext
1260 #if gettext
1248
1261
1249 Test that all log names are translated (e.g. branches, bookmarks, tags):
1262 Test that all log names are translated (e.g. branches, bookmarks, tags):
1250
1263
1251 $ hg bookmark babar -r tip
1264 $ hg bookmark babar -r tip
1252
1265
1253 $ HGENCODING=UTF-8 LANGUAGE=de hg log -r tip
1266 $ HGENCODING=UTF-8 LANGUAGE=de hg log -r tip
1254 \xc3\x84nderung: 3:f5d8de11c2e2 (esc)
1267 \xc3\x84nderung: 3:f5d8de11c2e2 (esc)
1255 Zweig: test
1268 Zweig: test
1256 Lesezeichen: babar
1269 Lesezeichen: babar
1257 Marke: tip
1270 Marke: tip
1258 Vorg\xc3\xa4nger: 1:d32277701ccb (esc)
1271 Vorg\xc3\xa4nger: 1:d32277701ccb (esc)
1259 Nutzer: test
1272 Nutzer: test
1260 Datum: Thu Jan 01 00:00:00 1970 +0000
1273 Datum: Thu Jan 01 00:00:00 1970 +0000
1261 Zusammenfassung: commit on test
1274 Zusammenfassung: commit on test
1262
1275
1263 $ hg bookmark -d babar
1276 $ hg bookmark -d babar
1264
1277
1265 #endif
1278 #endif
1266
1279
1267 log -p --cwd dir (in subdir)
1280 log -p --cwd dir (in subdir)
1268
1281
1269 $ mkdir dir
1282 $ mkdir dir
1270 $ hg log -p --cwd dir
1283 $ hg log -p --cwd dir
1271 changeset: 3:f5d8de11c2e2
1284 changeset: 3:f5d8de11c2e2
1272 branch: test
1285 branch: test
1273 tag: tip
1286 tag: tip
1274 parent: 1:d32277701ccb
1287 parent: 1:d32277701ccb
1275 user: test
1288 user: test
1276 date: Thu Jan 01 00:00:00 1970 +0000
1289 date: Thu Jan 01 00:00:00 1970 +0000
1277 summary: commit on test
1290 summary: commit on test
1278
1291
1279 diff -r d32277701ccb -r f5d8de11c2e2 c
1292 diff -r d32277701ccb -r f5d8de11c2e2 c
1280 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1293 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1281 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1294 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1282 @@ -0,0 +1,1 @@
1295 @@ -0,0 +1,1 @@
1283 +c
1296 +c
1284
1297
1285 changeset: 2:c3a4f03cc9a7
1298 changeset: 2:c3a4f03cc9a7
1286 parent: 0:24427303d56f
1299 parent: 0:24427303d56f
1287 user: test
1300 user: test
1288 date: Thu Jan 01 00:00:00 1970 +0000
1301 date: Thu Jan 01 00:00:00 1970 +0000
1289 summary: commit on default
1302 summary: commit on default
1290
1303
1291 diff -r 24427303d56f -r c3a4f03cc9a7 c
1304 diff -r 24427303d56f -r c3a4f03cc9a7 c
1292 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1305 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1293 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1306 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1294 @@ -0,0 +1,1 @@
1307 @@ -0,0 +1,1 @@
1295 +c
1308 +c
1296
1309
1297 changeset: 1:d32277701ccb
1310 changeset: 1:d32277701ccb
1298 branch: test
1311 branch: test
1299 user: test
1312 user: test
1300 date: Thu Jan 01 00:00:00 1970 +0000
1313 date: Thu Jan 01 00:00:00 1970 +0000
1301 summary: commit on test
1314 summary: commit on test
1302
1315
1303 diff -r 24427303d56f -r d32277701ccb b
1316 diff -r 24427303d56f -r d32277701ccb b
1304 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1317 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1305 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1318 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1306 @@ -0,0 +1,1 @@
1319 @@ -0,0 +1,1 @@
1307 +b
1320 +b
1308
1321
1309 changeset: 0:24427303d56f
1322 changeset: 0:24427303d56f
1310 user: test
1323 user: test
1311 date: Thu Jan 01 00:00:00 1970 +0000
1324 date: Thu Jan 01 00:00:00 1970 +0000
1312 summary: commit on default
1325 summary: commit on default
1313
1326
1314 diff -r 000000000000 -r 24427303d56f a
1327 diff -r 000000000000 -r 24427303d56f a
1315 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1328 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1316 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1329 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1317 @@ -0,0 +1,1 @@
1330 @@ -0,0 +1,1 @@
1318 +a
1331 +a
1319
1332
1320
1333
1321
1334
1322 log -p -R repo
1335 log -p -R repo
1323
1336
1324 $ cd dir
1337 $ cd dir
1325 $ hg log -p -R .. ../a
1338 $ hg log -p -R .. ../a
1326 changeset: 0:24427303d56f
1339 changeset: 0:24427303d56f
1327 user: test
1340 user: test
1328 date: Thu Jan 01 00:00:00 1970 +0000
1341 date: Thu Jan 01 00:00:00 1970 +0000
1329 summary: commit on default
1342 summary: commit on default
1330
1343
1331 diff -r 000000000000 -r 24427303d56f a
1344 diff -r 000000000000 -r 24427303d56f a
1332 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1345 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1333 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1346 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1334 @@ -0,0 +1,1 @@
1347 @@ -0,0 +1,1 @@
1335 +a
1348 +a
1336
1349
1337
1350
1338 $ cd ../..
1351 $ cd ../..
1339
1352
1340 $ hg init follow2
1353 $ hg init follow2
1341 $ cd follow2
1354 $ cd follow2
1342
1355
1343 # Build the following history:
1356 # Build the following history:
1344 # tip - o - x - o - x - x
1357 # tip - o - x - o - x - x
1345 # \ /
1358 # \ /
1346 # o - o - o - x
1359 # o - o - o - x
1347 # \ /
1360 # \ /
1348 # o
1361 # o
1349 #
1362 #
1350 # Where "o" is a revision containing "foo" and
1363 # Where "o" is a revision containing "foo" and
1351 # "x" is a revision without "foo"
1364 # "x" is a revision without "foo"
1352
1365
1353 $ touch init
1366 $ touch init
1354 $ hg ci -A -m "init, unrelated"
1367 $ hg ci -A -m "init, unrelated"
1355 adding init
1368 adding init
1356 $ echo 'foo' > init
1369 $ echo 'foo' > init
1357 $ hg ci -m "change, unrelated"
1370 $ hg ci -m "change, unrelated"
1358 $ echo 'foo' > foo
1371 $ echo 'foo' > foo
1359 $ hg ci -A -m "add unrelated old foo"
1372 $ hg ci -A -m "add unrelated old foo"
1360 adding foo
1373 adding foo
1361 $ hg rm foo
1374 $ hg rm foo
1362 $ hg ci -m "delete foo, unrelated"
1375 $ hg ci -m "delete foo, unrelated"
1363 $ echo 'related' > foo
1376 $ echo 'related' > foo
1364 $ hg ci -A -m "add foo, related"
1377 $ hg ci -A -m "add foo, related"
1365 adding foo
1378 adding foo
1366
1379
1367 $ hg up 0
1380 $ hg up 0
1368 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1381 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1369 $ touch branch
1382 $ touch branch
1370 $ hg ci -A -m "first branch, unrelated"
1383 $ hg ci -A -m "first branch, unrelated"
1371 adding branch
1384 adding branch
1372 created new head
1385 created new head
1373 $ touch foo
1386 $ touch foo
1374 $ hg ci -A -m "create foo, related"
1387 $ hg ci -A -m "create foo, related"
1375 adding foo
1388 adding foo
1376 $ echo 'change' > foo
1389 $ echo 'change' > foo
1377 $ hg ci -m "change foo, related"
1390 $ hg ci -m "change foo, related"
1378
1391
1379 $ hg up 6
1392 $ hg up 6
1380 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1393 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1381 $ echo 'change foo in branch' > foo
1394 $ echo 'change foo in branch' > foo
1382 $ hg ci -m "change foo in branch, related"
1395 $ hg ci -m "change foo in branch, related"
1383 created new head
1396 created new head
1384 $ hg merge 7
1397 $ hg merge 7
1385 merging foo
1398 merging foo
1386 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
1399 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
1387 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1400 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1388 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1401 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1389 [1]
1402 [1]
1390 $ echo 'merge 1' > foo
1403 $ echo 'merge 1' > foo
1391 $ hg resolve -m foo
1404 $ hg resolve -m foo
1392 (no more unresolved files)
1405 (no more unresolved files)
1393 $ hg ci -m "First merge, related"
1406 $ hg ci -m "First merge, related"
1394
1407
1395 $ hg merge 4
1408 $ hg merge 4
1396 merging foo
1409 merging foo
1397 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
1410 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
1398 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
1411 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
1399 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1412 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1400 [1]
1413 [1]
1401 $ echo 'merge 2' > foo
1414 $ echo 'merge 2' > foo
1402 $ hg resolve -m foo
1415 $ hg resolve -m foo
1403 (no more unresolved files)
1416 (no more unresolved files)
1404 $ hg ci -m "Last merge, related"
1417 $ hg ci -m "Last merge, related"
1405
1418
1406 $ hg log --graph
1419 $ hg log --graph
1407 @ changeset: 10:4dae8563d2c5
1420 @ changeset: 10:4dae8563d2c5
1408 |\ tag: tip
1421 |\ tag: tip
1409 | | parent: 9:7b35701b003e
1422 | | parent: 9:7b35701b003e
1410 | | parent: 4:88176d361b69
1423 | | parent: 4:88176d361b69
1411 | | user: test
1424 | | user: test
1412 | | date: Thu Jan 01 00:00:00 1970 +0000
1425 | | date: Thu Jan 01 00:00:00 1970 +0000
1413 | | summary: Last merge, related
1426 | | summary: Last merge, related
1414 | |
1427 | |
1415 | o changeset: 9:7b35701b003e
1428 | o changeset: 9:7b35701b003e
1416 | |\ parent: 8:e5416ad8a855
1429 | |\ parent: 8:e5416ad8a855
1417 | | | parent: 7:87fe3144dcfa
1430 | | | parent: 7:87fe3144dcfa
1418 | | | user: test
1431 | | | user: test
1419 | | | date: Thu Jan 01 00:00:00 1970 +0000
1432 | | | date: Thu Jan 01 00:00:00 1970 +0000
1420 | | | summary: First merge, related
1433 | | | summary: First merge, related
1421 | | |
1434 | | |
1422 | | o changeset: 8:e5416ad8a855
1435 | | o changeset: 8:e5416ad8a855
1423 | | | parent: 6:dc6c325fe5ee
1436 | | | parent: 6:dc6c325fe5ee
1424 | | | user: test
1437 | | | user: test
1425 | | | date: Thu Jan 01 00:00:00 1970 +0000
1438 | | | date: Thu Jan 01 00:00:00 1970 +0000
1426 | | | summary: change foo in branch, related
1439 | | | summary: change foo in branch, related
1427 | | |
1440 | | |
1428 | o | changeset: 7:87fe3144dcfa
1441 | o | changeset: 7:87fe3144dcfa
1429 | |/ user: test
1442 | |/ user: test
1430 | | date: Thu Jan 01 00:00:00 1970 +0000
1443 | | date: Thu Jan 01 00:00:00 1970 +0000
1431 | | summary: change foo, related
1444 | | summary: change foo, related
1432 | |
1445 | |
1433 | o changeset: 6:dc6c325fe5ee
1446 | o changeset: 6:dc6c325fe5ee
1434 | | user: test
1447 | | user: test
1435 | | date: Thu Jan 01 00:00:00 1970 +0000
1448 | | date: Thu Jan 01 00:00:00 1970 +0000
1436 | | summary: create foo, related
1449 | | summary: create foo, related
1437 | |
1450 | |
1438 | o changeset: 5:73db34516eb9
1451 | o changeset: 5:73db34516eb9
1439 | | parent: 0:e87515fd044a
1452 | | parent: 0:e87515fd044a
1440 | | user: test
1453 | | user: test
1441 | | date: Thu Jan 01 00:00:00 1970 +0000
1454 | | date: Thu Jan 01 00:00:00 1970 +0000
1442 | | summary: first branch, unrelated
1455 | | summary: first branch, unrelated
1443 | |
1456 | |
1444 o | changeset: 4:88176d361b69
1457 o | changeset: 4:88176d361b69
1445 | | user: test
1458 | | user: test
1446 | | date: Thu Jan 01 00:00:00 1970 +0000
1459 | | date: Thu Jan 01 00:00:00 1970 +0000
1447 | | summary: add foo, related
1460 | | summary: add foo, related
1448 | |
1461 | |
1449 o | changeset: 3:dd78ae4afb56
1462 o | changeset: 3:dd78ae4afb56
1450 | | user: test
1463 | | user: test
1451 | | date: Thu Jan 01 00:00:00 1970 +0000
1464 | | date: Thu Jan 01 00:00:00 1970 +0000
1452 | | summary: delete foo, unrelated
1465 | | summary: delete foo, unrelated
1453 | |
1466 | |
1454 o | changeset: 2:c4c64aedf0f7
1467 o | changeset: 2:c4c64aedf0f7
1455 | | user: test
1468 | | user: test
1456 | | date: Thu Jan 01 00:00:00 1970 +0000
1469 | | date: Thu Jan 01 00:00:00 1970 +0000
1457 | | summary: add unrelated old foo
1470 | | summary: add unrelated old foo
1458 | |
1471 | |
1459 o | changeset: 1:e5faa7440653
1472 o | changeset: 1:e5faa7440653
1460 |/ user: test
1473 |/ user: test
1461 | date: Thu Jan 01 00:00:00 1970 +0000
1474 | date: Thu Jan 01 00:00:00 1970 +0000
1462 | summary: change, unrelated
1475 | summary: change, unrelated
1463 |
1476 |
1464 o changeset: 0:e87515fd044a
1477 o changeset: 0:e87515fd044a
1465 user: test
1478 user: test
1466 date: Thu Jan 01 00:00:00 1970 +0000
1479 date: Thu Jan 01 00:00:00 1970 +0000
1467 summary: init, unrelated
1480 summary: init, unrelated
1468
1481
1469
1482
1470 $ hg --traceback log -f foo
1483 $ hg --traceback log -f foo
1471 changeset: 10:4dae8563d2c5
1484 changeset: 10:4dae8563d2c5
1472 tag: tip
1485 tag: tip
1473 parent: 9:7b35701b003e
1486 parent: 9:7b35701b003e
1474 parent: 4:88176d361b69
1487 parent: 4:88176d361b69
1475 user: test
1488 user: test
1476 date: Thu Jan 01 00:00:00 1970 +0000
1489 date: Thu Jan 01 00:00:00 1970 +0000
1477 summary: Last merge, related
1490 summary: Last merge, related
1478
1491
1479 changeset: 9:7b35701b003e
1492 changeset: 9:7b35701b003e
1480 parent: 8:e5416ad8a855
1493 parent: 8:e5416ad8a855
1481 parent: 7:87fe3144dcfa
1494 parent: 7:87fe3144dcfa
1482 user: test
1495 user: test
1483 date: Thu Jan 01 00:00:00 1970 +0000
1496 date: Thu Jan 01 00:00:00 1970 +0000
1484 summary: First merge, related
1497 summary: First merge, related
1485
1498
1486 changeset: 8:e5416ad8a855
1499 changeset: 8:e5416ad8a855
1487 parent: 6:dc6c325fe5ee
1500 parent: 6:dc6c325fe5ee
1488 user: test
1501 user: test
1489 date: Thu Jan 01 00:00:00 1970 +0000
1502 date: Thu Jan 01 00:00:00 1970 +0000
1490 summary: change foo in branch, related
1503 summary: change foo in branch, related
1491
1504
1492 changeset: 7:87fe3144dcfa
1505 changeset: 7:87fe3144dcfa
1493 user: test
1506 user: test
1494 date: Thu Jan 01 00:00:00 1970 +0000
1507 date: Thu Jan 01 00:00:00 1970 +0000
1495 summary: change foo, related
1508 summary: change foo, related
1496
1509
1497 changeset: 6:dc6c325fe5ee
1510 changeset: 6:dc6c325fe5ee
1498 user: test
1511 user: test
1499 date: Thu Jan 01 00:00:00 1970 +0000
1512 date: Thu Jan 01 00:00:00 1970 +0000
1500 summary: create foo, related
1513 summary: create foo, related
1501
1514
1502 changeset: 4:88176d361b69
1515 changeset: 4:88176d361b69
1503 user: test
1516 user: test
1504 date: Thu Jan 01 00:00:00 1970 +0000
1517 date: Thu Jan 01 00:00:00 1970 +0000
1505 summary: add foo, related
1518 summary: add foo, related
1506
1519
1507
1520
1508 Also check when maxrev < lastrevfilelog
1521 Also check when maxrev < lastrevfilelog
1509
1522
1510 $ hg --traceback log -f -r4 foo
1523 $ hg --traceback log -f -r4 foo
1511 changeset: 4:88176d361b69
1524 changeset: 4:88176d361b69
1512 user: test
1525 user: test
1513 date: Thu Jan 01 00:00:00 1970 +0000
1526 date: Thu Jan 01 00:00:00 1970 +0000
1514 summary: add foo, related
1527 summary: add foo, related
1515
1528
1516 changeset: 2:c4c64aedf0f7
1529 changeset: 2:c4c64aedf0f7
1517 user: test
1530 user: test
1518 date: Thu Jan 01 00:00:00 1970 +0000
1531 date: Thu Jan 01 00:00:00 1970 +0000
1519 summary: add unrelated old foo
1532 summary: add unrelated old foo
1520
1533
1521 $ cd ..
1534 $ cd ..
1522
1535
1523 Issue2383: hg log showing _less_ differences than hg diff
1536 Issue2383: hg log showing _less_ differences than hg diff
1524
1537
1525 $ hg init issue2383
1538 $ hg init issue2383
1526 $ cd issue2383
1539 $ cd issue2383
1527
1540
1528 Create a test repo:
1541 Create a test repo:
1529
1542
1530 $ echo a > a
1543 $ echo a > a
1531 $ hg ci -Am0
1544 $ hg ci -Am0
1532 adding a
1545 adding a
1533 $ echo b > b
1546 $ echo b > b
1534 $ hg ci -Am1
1547 $ hg ci -Am1
1535 adding b
1548 adding b
1536 $ hg co 0
1549 $ hg co 0
1537 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1550 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1538 $ echo b > a
1551 $ echo b > a
1539 $ hg ci -m2
1552 $ hg ci -m2
1540 created new head
1553 created new head
1541
1554
1542 Merge:
1555 Merge:
1543
1556
1544 $ hg merge
1557 $ hg merge
1545 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1558 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1546 (branch merge, don't forget to commit)
1559 (branch merge, don't forget to commit)
1547
1560
1548 Make sure there's a file listed in the merge to trigger the bug:
1561 Make sure there's a file listed in the merge to trigger the bug:
1549
1562
1550 $ echo c > a
1563 $ echo c > a
1551 $ hg ci -m3
1564 $ hg ci -m3
1552
1565
1553 Two files shown here in diff:
1566 Two files shown here in diff:
1554
1567
1555 $ hg diff --rev 2:3
1568 $ hg diff --rev 2:3
1556 diff -r b09be438c43a -r 8e07aafe1edc a
1569 diff -r b09be438c43a -r 8e07aafe1edc a
1557 --- a/a Thu Jan 01 00:00:00 1970 +0000
1570 --- a/a Thu Jan 01 00:00:00 1970 +0000
1558 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1571 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1559 @@ -1,1 +1,1 @@
1572 @@ -1,1 +1,1 @@
1560 -b
1573 -b
1561 +c
1574 +c
1562 diff -r b09be438c43a -r 8e07aafe1edc b
1575 diff -r b09be438c43a -r 8e07aafe1edc b
1563 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1576 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1564 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1577 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1565 @@ -0,0 +1,1 @@
1578 @@ -0,0 +1,1 @@
1566 +b
1579 +b
1567
1580
1568 Diff here should be the same:
1581 Diff here should be the same:
1569
1582
1570 $ hg log -vpr 3
1583 $ hg log -vpr 3
1571 changeset: 3:8e07aafe1edc
1584 changeset: 3:8e07aafe1edc
1572 tag: tip
1585 tag: tip
1573 parent: 2:b09be438c43a
1586 parent: 2:b09be438c43a
1574 parent: 1:925d80f479bb
1587 parent: 1:925d80f479bb
1575 user: test
1588 user: test
1576 date: Thu Jan 01 00:00:00 1970 +0000
1589 date: Thu Jan 01 00:00:00 1970 +0000
1577 files: a
1590 files: a
1578 description:
1591 description:
1579 3
1592 3
1580
1593
1581
1594
1582 diff -r b09be438c43a -r 8e07aafe1edc a
1595 diff -r b09be438c43a -r 8e07aafe1edc a
1583 --- a/a Thu Jan 01 00:00:00 1970 +0000
1596 --- a/a Thu Jan 01 00:00:00 1970 +0000
1584 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1597 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1585 @@ -1,1 +1,1 @@
1598 @@ -1,1 +1,1 @@
1586 -b
1599 -b
1587 +c
1600 +c
1588 diff -r b09be438c43a -r 8e07aafe1edc b
1601 diff -r b09be438c43a -r 8e07aafe1edc b
1589 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1602 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1590 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1603 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1591 @@ -0,0 +1,1 @@
1604 @@ -0,0 +1,1 @@
1592 +b
1605 +b
1593
1606
1594 $ cd ..
1607 $ cd ..
1595
1608
1596 'hg log -r rev fn' when last(filelog(fn)) != rev
1609 'hg log -r rev fn' when last(filelog(fn)) != rev
1597
1610
1598 $ hg init simplelog
1611 $ hg init simplelog
1599 $ cd simplelog
1612 $ cd simplelog
1600 $ echo f > a
1613 $ echo f > a
1601 $ hg ci -Am'a' -d '0 0'
1614 $ hg ci -Am'a' -d '0 0'
1602 adding a
1615 adding a
1603 $ echo f >> a
1616 $ echo f >> a
1604 $ hg ci -Am'a bis' -d '1 0'
1617 $ hg ci -Am'a bis' -d '1 0'
1605
1618
1606 $ hg log -r0 a
1619 $ hg log -r0 a
1607 changeset: 0:9f758d63dcde
1620 changeset: 0:9f758d63dcde
1608 user: test
1621 user: test
1609 date: Thu Jan 01 00:00:00 1970 +0000
1622 date: Thu Jan 01 00:00:00 1970 +0000
1610 summary: a
1623 summary: a
1611
1624
1612 enable obsolete to test hidden feature
1625 enable obsolete to test hidden feature
1613
1626
1614 $ cat >> $HGRCPATH << EOF
1627 $ cat >> $HGRCPATH << EOF
1615 > [experimental]
1628 > [experimental]
1616 > evolution=createmarkers
1629 > evolution=createmarkers
1617 > EOF
1630 > EOF
1618
1631
1619 $ hg log --template='{rev}:{node}\n'
1632 $ hg log --template='{rev}:{node}\n'
1620 1:a765632148dc55d38c35c4f247c618701886cb2f
1633 1:a765632148dc55d38c35c4f247c618701886cb2f
1621 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1634 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1622 $ hg debugobsolete a765632148dc55d38c35c4f247c618701886cb2f
1635 $ hg debugobsolete a765632148dc55d38c35c4f247c618701886cb2f
1623 $ hg up null -q
1636 $ hg up null -q
1624 $ hg log --template='{rev}:{node}\n'
1637 $ hg log --template='{rev}:{node}\n'
1625 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1638 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1626 $ hg log --template='{rev}:{node}\n' --hidden
1639 $ hg log --template='{rev}:{node}\n' --hidden
1627 1:a765632148dc55d38c35c4f247c618701886cb2f
1640 1:a765632148dc55d38c35c4f247c618701886cb2f
1628 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1641 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1629 $ hg log -r a
1642 $ hg log -r a
1630 abort: hidden revision 'a'!
1643 abort: hidden revision 'a'!
1631 (use --hidden to access hidden revisions)
1644 (use --hidden to access hidden revisions)
1632 [255]
1645 [255]
1633
1646
1634 test that parent prevent a changeset to be hidden
1647 test that parent prevent a changeset to be hidden
1635
1648
1636 $ hg up 1 -q --hidden
1649 $ hg up 1 -q --hidden
1637 $ hg log --template='{rev}:{node}\n'
1650 $ hg log --template='{rev}:{node}\n'
1638 1:a765632148dc55d38c35c4f247c618701886cb2f
1651 1:a765632148dc55d38c35c4f247c618701886cb2f
1639 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1652 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1640
1653
1641 test that second parent prevent a changeset to be hidden too
1654 test that second parent prevent a changeset to be hidden too
1642
1655
1643 $ hg debugsetparents 0 1 # nothing suitable to merge here
1656 $ hg debugsetparents 0 1 # nothing suitable to merge here
1644 $ hg log --template='{rev}:{node}\n'
1657 $ hg log --template='{rev}:{node}\n'
1645 1:a765632148dc55d38c35c4f247c618701886cb2f
1658 1:a765632148dc55d38c35c4f247c618701886cb2f
1646 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1659 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1647 $ hg debugsetparents 1
1660 $ hg debugsetparents 1
1648 $ hg up -q null
1661 $ hg up -q null
1649
1662
1650 bookmarks prevent a changeset being hidden
1663 bookmarks prevent a changeset being hidden
1651
1664
1652 $ hg bookmark --hidden -r 1 X
1665 $ hg bookmark --hidden -r 1 X
1653 $ hg log --template '{rev}:{node}\n'
1666 $ hg log --template '{rev}:{node}\n'
1654 1:a765632148dc55d38c35c4f247c618701886cb2f
1667 1:a765632148dc55d38c35c4f247c618701886cb2f
1655 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1668 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1656 $ hg bookmark -d X
1669 $ hg bookmark -d X
1657
1670
1658 divergent bookmarks are not hidden
1671 divergent bookmarks are not hidden
1659
1672
1660 $ hg bookmark --hidden -r 1 X@foo
1673 $ hg bookmark --hidden -r 1 X@foo
1661 $ hg log --template '{rev}:{node}\n'
1674 $ hg log --template '{rev}:{node}\n'
1662 1:a765632148dc55d38c35c4f247c618701886cb2f
1675 1:a765632148dc55d38c35c4f247c618701886cb2f
1663 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1676 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1664
1677
1665 clear extensions configuration
1678 clear extensions configuration
1666 $ echo '[extensions]' >> $HGRCPATH
1679 $ echo '[extensions]' >> $HGRCPATH
1667 $ echo "obs=!" >> $HGRCPATH
1680 $ echo "obs=!" >> $HGRCPATH
1668 $ cd ..
1681 $ cd ..
1669
1682
1670 test -u/-k for problematic encoding
1683 test -u/-k for problematic encoding
1671 # unicode: cp932:
1684 # unicode: cp932:
1672 # u30A2 0x83 0x41(= 'A')
1685 # u30A2 0x83 0x41(= 'A')
1673 # u30C2 0x83 0x61(= 'a')
1686 # u30C2 0x83 0x61(= 'a')
1674
1687
1675 $ hg init problematicencoding
1688 $ hg init problematicencoding
1676 $ cd problematicencoding
1689 $ cd problematicencoding
1677
1690
1678 $ python > setup.sh <<EOF
1691 $ python > setup.sh <<EOF
1679 > print u'''
1692 > print u'''
1680 > echo a > text
1693 > echo a > text
1681 > hg add text
1694 > hg add text
1682 > hg --encoding utf-8 commit -u '\u30A2' -m none
1695 > hg --encoding utf-8 commit -u '\u30A2' -m none
1683 > echo b > text
1696 > echo b > text
1684 > hg --encoding utf-8 commit -u '\u30C2' -m none
1697 > hg --encoding utf-8 commit -u '\u30C2' -m none
1685 > echo c > text
1698 > echo c > text
1686 > hg --encoding utf-8 commit -u none -m '\u30A2'
1699 > hg --encoding utf-8 commit -u none -m '\u30A2'
1687 > echo d > text
1700 > echo d > text
1688 > hg --encoding utf-8 commit -u none -m '\u30C2'
1701 > hg --encoding utf-8 commit -u none -m '\u30C2'
1689 > '''.encode('utf-8')
1702 > '''.encode('utf-8')
1690 > EOF
1703 > EOF
1691 $ sh < setup.sh
1704 $ sh < setup.sh
1692
1705
1693 test in problematic encoding
1706 test in problematic encoding
1694 $ python > test.sh <<EOF
1707 $ python > test.sh <<EOF
1695 > print u'''
1708 > print u'''
1696 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30A2'
1709 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30A2'
1697 > echo ====
1710 > echo ====
1698 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30C2'
1711 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30C2'
1699 > echo ====
1712 > echo ====
1700 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30A2'
1713 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30A2'
1701 > echo ====
1714 > echo ====
1702 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30C2'
1715 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30C2'
1703 > '''.encode('cp932')
1716 > '''.encode('cp932')
1704 > EOF
1717 > EOF
1705 $ sh < test.sh
1718 $ sh < test.sh
1706 0
1719 0
1707 ====
1720 ====
1708 1
1721 1
1709 ====
1722 ====
1710 2
1723 2
1711 0
1724 0
1712 ====
1725 ====
1713 3
1726 3
1714 1
1727 1
1715
1728
1716 $ cd ..
1729 $ cd ..
1717
1730
1718 test hg log on non-existent files and on directories
1731 test hg log on non-existent files and on directories
1719 $ hg init issue1340
1732 $ hg init issue1340
1720 $ cd issue1340
1733 $ cd issue1340
1721 $ mkdir d1; mkdir D2; mkdir D3.i; mkdir d4.hg; mkdir d5.d; mkdir .d6
1734 $ mkdir d1; mkdir D2; mkdir D3.i; mkdir d4.hg; mkdir d5.d; mkdir .d6
1722 $ echo 1 > d1/f1
1735 $ echo 1 > d1/f1
1723 $ echo 1 > D2/f1
1736 $ echo 1 > D2/f1
1724 $ echo 1 > D3.i/f1
1737 $ echo 1 > D3.i/f1
1725 $ echo 1 > d4.hg/f1
1738 $ echo 1 > d4.hg/f1
1726 $ echo 1 > d5.d/f1
1739 $ echo 1 > d5.d/f1
1727 $ echo 1 > .d6/f1
1740 $ echo 1 > .d6/f1
1728 $ hg -q add .
1741 $ hg -q add .
1729 $ hg commit -m "a bunch of weird directories"
1742 $ hg commit -m "a bunch of weird directories"
1730 $ hg log -l1 d1/f1 | grep changeset
1743 $ hg log -l1 d1/f1 | grep changeset
1731 changeset: 0:65624cd9070a
1744 changeset: 0:65624cd9070a
1732 $ hg log -l1 f1
1745 $ hg log -l1 f1
1733 $ hg log -l1 . | grep changeset
1746 $ hg log -l1 . | grep changeset
1734 changeset: 0:65624cd9070a
1747 changeset: 0:65624cd9070a
1735 $ hg log -l1 ./ | grep changeset
1748 $ hg log -l1 ./ | grep changeset
1736 changeset: 0:65624cd9070a
1749 changeset: 0:65624cd9070a
1737 $ hg log -l1 d1 | grep changeset
1750 $ hg log -l1 d1 | grep changeset
1738 changeset: 0:65624cd9070a
1751 changeset: 0:65624cd9070a
1739 $ hg log -l1 D2 | grep changeset
1752 $ hg log -l1 D2 | grep changeset
1740 changeset: 0:65624cd9070a
1753 changeset: 0:65624cd9070a
1741 $ hg log -l1 D2/f1 | grep changeset
1754 $ hg log -l1 D2/f1 | grep changeset
1742 changeset: 0:65624cd9070a
1755 changeset: 0:65624cd9070a
1743 $ hg log -l1 D3.i | grep changeset
1756 $ hg log -l1 D3.i | grep changeset
1744 changeset: 0:65624cd9070a
1757 changeset: 0:65624cd9070a
1745 $ hg log -l1 D3.i/f1 | grep changeset
1758 $ hg log -l1 D3.i/f1 | grep changeset
1746 changeset: 0:65624cd9070a
1759 changeset: 0:65624cd9070a
1747 $ hg log -l1 d4.hg | grep changeset
1760 $ hg log -l1 d4.hg | grep changeset
1748 changeset: 0:65624cd9070a
1761 changeset: 0:65624cd9070a
1749 $ hg log -l1 d4.hg/f1 | grep changeset
1762 $ hg log -l1 d4.hg/f1 | grep changeset
1750 changeset: 0:65624cd9070a
1763 changeset: 0:65624cd9070a
1751 $ hg log -l1 d5.d | grep changeset
1764 $ hg log -l1 d5.d | grep changeset
1752 changeset: 0:65624cd9070a
1765 changeset: 0:65624cd9070a
1753 $ hg log -l1 d5.d/f1 | grep changeset
1766 $ hg log -l1 d5.d/f1 | grep changeset
1754 changeset: 0:65624cd9070a
1767 changeset: 0:65624cd9070a
1755 $ hg log -l1 .d6 | grep changeset
1768 $ hg log -l1 .d6 | grep changeset
1756 changeset: 0:65624cd9070a
1769 changeset: 0:65624cd9070a
1757 $ hg log -l1 .d6/f1 | grep changeset
1770 $ hg log -l1 .d6/f1 | grep changeset
1758 changeset: 0:65624cd9070a
1771 changeset: 0:65624cd9070a
1759
1772
1760 issue3772: hg log -r :null showing revision 0 as well
1773 issue3772: hg log -r :null showing revision 0 as well
1761
1774
1762 $ hg log -r :null
1775 $ hg log -r :null
1763 changeset: 0:65624cd9070a
1776 changeset: 0:65624cd9070a
1764 tag: tip
1777 tag: tip
1765 user: test
1778 user: test
1766 date: Thu Jan 01 00:00:00 1970 +0000
1779 date: Thu Jan 01 00:00:00 1970 +0000
1767 summary: a bunch of weird directories
1780 summary: a bunch of weird directories
1768
1781
1769 changeset: -1:000000000000
1782 changeset: -1:000000000000
1770 user:
1783 user:
1771 date: Thu Jan 01 00:00:00 1970 +0000
1784 date: Thu Jan 01 00:00:00 1970 +0000
1772
1785
1773 $ hg log -r null:null
1786 $ hg log -r null:null
1774 changeset: -1:000000000000
1787 changeset: -1:000000000000
1775 user:
1788 user:
1776 date: Thu Jan 01 00:00:00 1970 +0000
1789 date: Thu Jan 01 00:00:00 1970 +0000
1777
1790
1778 working-directory revision requires special treatment
1791 working-directory revision requires special treatment
1779
1792
1780 clean:
1793 clean:
1781
1794
1782 $ hg log -r 'wdir()' --debug
1795 $ hg log -r 'wdir()' --debug
1783 changeset: 2147483647:ffffffffffffffffffffffffffffffffffffffff
1796 changeset: 2147483647:ffffffffffffffffffffffffffffffffffffffff
1784 phase: draft
1797 phase: draft
1785 parent: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08
1798 parent: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08
1786 parent: -1:0000000000000000000000000000000000000000
1799 parent: -1:0000000000000000000000000000000000000000
1787 user: test
1800 user: test
1788 date: [A-Za-z0-9:+ ]+ (re)
1801 date: [A-Za-z0-9:+ ]+ (re)
1789 extra: branch=default
1802 extra: branch=default
1790
1803
1791 $ hg log -r 'wdir()' -p --stat
1804 $ hg log -r 'wdir()' -p --stat
1792 changeset: 2147483647:ffffffffffff
1805 changeset: 2147483647:ffffffffffff
1793 parent: 0:65624cd9070a
1806 parent: 0:65624cd9070a
1794 user: test
1807 user: test
1795 date: [A-Za-z0-9:+ ]+ (re)
1808 date: [A-Za-z0-9:+ ]+ (re)
1796
1809
1797
1810
1798
1811
1799
1812
1800 dirty:
1813 dirty:
1801
1814
1802 $ echo 2 >> d1/f1
1815 $ echo 2 >> d1/f1
1803 $ echo 2 > d1/f2
1816 $ echo 2 > d1/f2
1804 $ hg add d1/f2
1817 $ hg add d1/f2
1805 $ hg remove .d6/f1
1818 $ hg remove .d6/f1
1806 $ hg status
1819 $ hg status
1807 M d1/f1
1820 M d1/f1
1808 A d1/f2
1821 A d1/f2
1809 R .d6/f1
1822 R .d6/f1
1810
1823
1811 $ hg log -r 'wdir()'
1824 $ hg log -r 'wdir()'
1812 changeset: 2147483647:ffffffffffff
1825 changeset: 2147483647:ffffffffffff
1813 parent: 0:65624cd9070a
1826 parent: 0:65624cd9070a
1814 user: test
1827 user: test
1815 date: [A-Za-z0-9:+ ]+ (re)
1828 date: [A-Za-z0-9:+ ]+ (re)
1816
1829
1817 $ hg log -r 'wdir()' -q
1830 $ hg log -r 'wdir()' -q
1818 2147483647:ffffffffffff
1831 2147483647:ffffffffffff
1819
1832
1820 $ hg log -r 'wdir()' --debug
1833 $ hg log -r 'wdir()' --debug
1821 changeset: 2147483647:ffffffffffffffffffffffffffffffffffffffff
1834 changeset: 2147483647:ffffffffffffffffffffffffffffffffffffffff
1822 phase: draft
1835 phase: draft
1823 parent: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08
1836 parent: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08
1824 parent: -1:0000000000000000000000000000000000000000
1837 parent: -1:0000000000000000000000000000000000000000
1825 user: test
1838 user: test
1826 date: [A-Za-z0-9:+ ]+ (re)
1839 date: [A-Za-z0-9:+ ]+ (re)
1827 files: d1/f1
1840 files: d1/f1
1828 files+: d1/f2
1841 files+: d1/f2
1829 files-: .d6/f1
1842 files-: .d6/f1
1830 extra: branch=default
1843 extra: branch=default
1831
1844
1832 $ hg log -r 'wdir()' -p --stat --git
1845 $ hg log -r 'wdir()' -p --stat --git
1833 changeset: 2147483647:ffffffffffff
1846 changeset: 2147483647:ffffffffffff
1834 parent: 0:65624cd9070a
1847 parent: 0:65624cd9070a
1835 user: test
1848 user: test
1836 date: [A-Za-z0-9:+ ]+ (re)
1849 date: [A-Za-z0-9:+ ]+ (re)
1837
1850
1838 .d6/f1 | 1 -
1851 .d6/f1 | 1 -
1839 d1/f1 | 1 +
1852 d1/f1 | 1 +
1840 d1/f2 | 1 +
1853 d1/f2 | 1 +
1841 3 files changed, 2 insertions(+), 1 deletions(-)
1854 3 files changed, 2 insertions(+), 1 deletions(-)
1842
1855
1843 diff --git a/.d6/f1 b/.d6/f1
1856 diff --git a/.d6/f1 b/.d6/f1
1844 deleted file mode 100644
1857 deleted file mode 100644
1845 --- a/.d6/f1
1858 --- a/.d6/f1
1846 +++ /dev/null
1859 +++ /dev/null
1847 @@ -1,1 +0,0 @@
1860 @@ -1,1 +0,0 @@
1848 -1
1861 -1
1849 diff --git a/d1/f1 b/d1/f1
1862 diff --git a/d1/f1 b/d1/f1
1850 --- a/d1/f1
1863 --- a/d1/f1
1851 +++ b/d1/f1
1864 +++ b/d1/f1
1852 @@ -1,1 +1,2 @@
1865 @@ -1,1 +1,2 @@
1853 1
1866 1
1854 +2
1867 +2
1855 diff --git a/d1/f2 b/d1/f2
1868 diff --git a/d1/f2 b/d1/f2
1856 new file mode 100644
1869 new file mode 100644
1857 --- /dev/null
1870 --- /dev/null
1858 +++ b/d1/f2
1871 +++ b/d1/f2
1859 @@ -0,0 +1,1 @@
1872 @@ -0,0 +1,1 @@
1860 +2
1873 +2
1861
1874
1862 $ hg log -r 'wdir()' -Tjson
1875 $ hg log -r 'wdir()' -Tjson
1863 [
1876 [
1864 {
1877 {
1865 "rev": null,
1878 "rev": null,
1866 "node": null,
1879 "node": null,
1867 "branch": "default",
1880 "branch": "default",
1868 "phase": "draft",
1881 "phase": "draft",
1869 "user": "test",
1882 "user": "test",
1870 "date": [*, 0], (glob)
1883 "date": [*, 0], (glob)
1871 "desc": "",
1884 "desc": "",
1872 "bookmarks": [],
1885 "bookmarks": [],
1873 "tags": [],
1886 "tags": [],
1874 "parents": ["65624cd9070a035fa7191a54f2b8af39f16b0c08"]
1887 "parents": ["65624cd9070a035fa7191a54f2b8af39f16b0c08"]
1875 }
1888 }
1876 ]
1889 ]
1877
1890
1878 $ hg log -r 'wdir()' -Tjson -q
1891 $ hg log -r 'wdir()' -Tjson -q
1879 [
1892 [
1880 {
1893 {
1881 "rev": null,
1894 "rev": null,
1882 "node": null
1895 "node": null
1883 }
1896 }
1884 ]
1897 ]
1885
1898
1886 $ hg log -r 'wdir()' -Tjson --debug
1899 $ hg log -r 'wdir()' -Tjson --debug
1887 [
1900 [
1888 {
1901 {
1889 "rev": null,
1902 "rev": null,
1890 "node": null,
1903 "node": null,
1891 "branch": "default",
1904 "branch": "default",
1892 "phase": "draft",
1905 "phase": "draft",
1893 "user": "test",
1906 "user": "test",
1894 "date": [*, 0], (glob)
1907 "date": [*, 0], (glob)
1895 "desc": "",
1908 "desc": "",
1896 "bookmarks": [],
1909 "bookmarks": [],
1897 "tags": [],
1910 "tags": [],
1898 "parents": ["65624cd9070a035fa7191a54f2b8af39f16b0c08"],
1911 "parents": ["65624cd9070a035fa7191a54f2b8af39f16b0c08"],
1899 "manifest": null,
1912 "manifest": null,
1900 "extra": {"branch": "default"},
1913 "extra": {"branch": "default"},
1901 "modified": ["d1/f1"],
1914 "modified": ["d1/f1"],
1902 "added": ["d1/f2"],
1915 "added": ["d1/f2"],
1903 "removed": [".d6/f1"]
1916 "removed": [".d6/f1"]
1904 }
1917 }
1905 ]
1918 ]
1906
1919
1907 $ hg revert -aqC
1920 $ hg revert -aqC
1908
1921
1909 Check that adding an arbitrary name shows up in log automatically
1922 Check that adding an arbitrary name shows up in log automatically
1910
1923
1911 $ cat > ../names.py <<EOF
1924 $ cat > ../names.py <<EOF
1912 > """A small extension to test adding arbitrary names to a repo"""
1925 > """A small extension to test adding arbitrary names to a repo"""
1913 > from mercurial.namespaces import namespace
1926 > from mercurial.namespaces import namespace
1914 >
1927 >
1915 > def reposetup(ui, repo):
1928 > def reposetup(ui, repo):
1916 > foo = {'foo': repo[0].node()}
1929 > foo = {'foo': repo[0].node()}
1917 > names = lambda r: foo.keys()
1930 > names = lambda r: foo.keys()
1918 > namemap = lambda r, name: foo.get(name)
1931 > namemap = lambda r, name: foo.get(name)
1919 > nodemap = lambda r, node: [name for name, n in foo.iteritems()
1932 > nodemap = lambda r, node: [name for name, n in foo.iteritems()
1920 > if n == node]
1933 > if n == node]
1921 > ns = namespace("bars", templatename="bar", logname="barlog",
1934 > ns = namespace("bars", templatename="bar", logname="barlog",
1922 > colorname="barcolor", listnames=names, namemap=namemap,
1935 > colorname="barcolor", listnames=names, namemap=namemap,
1923 > nodemap=nodemap)
1936 > nodemap=nodemap)
1924 >
1937 >
1925 > repo.names.addnamespace(ns)
1938 > repo.names.addnamespace(ns)
1926 > EOF
1939 > EOF
1927
1940
1928 $ hg --config extensions.names=../names.py log -r 0
1941 $ hg --config extensions.names=../names.py log -r 0
1929 changeset: 0:65624cd9070a
1942 changeset: 0:65624cd9070a
1930 tag: tip
1943 tag: tip
1931 barlog: foo
1944 barlog: foo
1932 user: test
1945 user: test
1933 date: Thu Jan 01 00:00:00 1970 +0000
1946 date: Thu Jan 01 00:00:00 1970 +0000
1934 summary: a bunch of weird directories
1947 summary: a bunch of weird directories
1935
1948
1936 $ hg --config extensions.names=../names.py \
1949 $ hg --config extensions.names=../names.py \
1937 > --config extensions.color= --config color.log.barcolor=red \
1950 > --config extensions.color= --config color.log.barcolor=red \
1938 > --color=always log -r 0
1951 > --color=always log -r 0
1939 \x1b[0;33mchangeset: 0:65624cd9070a\x1b[0m (esc)
1952 \x1b[0;33mchangeset: 0:65624cd9070a\x1b[0m (esc)
1940 tag: tip
1953 tag: tip
1941 \x1b[0;31mbarlog: foo\x1b[0m (esc)
1954 \x1b[0;31mbarlog: foo\x1b[0m (esc)
1942 user: test
1955 user: test
1943 date: Thu Jan 01 00:00:00 1970 +0000
1956 date: Thu Jan 01 00:00:00 1970 +0000
1944 summary: a bunch of weird directories
1957 summary: a bunch of weird directories
1945
1958
1946 $ hg --config extensions.names=../names.py log -r 0 --template '{bars}\n'
1959 $ hg --config extensions.names=../names.py log -r 0 --template '{bars}\n'
1947 foo
1960 foo
1948
1961
1949 $ cd ..
1962 $ cd ..
1950
1963
1951 hg log -f dir across branches
1964 hg log -f dir across branches
1952
1965
1953 $ hg init acrossbranches
1966 $ hg init acrossbranches
1954 $ cd acrossbranches
1967 $ cd acrossbranches
1955 $ mkdir d
1968 $ mkdir d
1956 $ echo a > d/a && hg ci -Aqm a
1969 $ echo a > d/a && hg ci -Aqm a
1957 $ echo b > d/a && hg ci -Aqm b
1970 $ echo b > d/a && hg ci -Aqm b
1958 $ hg up -q 0
1971 $ hg up -q 0
1959 $ echo b > d/a && hg ci -Aqm c
1972 $ echo b > d/a && hg ci -Aqm c
1960 $ hg log -f d -T '{desc}' -G
1973 $ hg log -f d -T '{desc}' -G
1961 @ c
1974 @ c
1962 |
1975 |
1963 o a
1976 o a
1964
1977
1965 Ensure that largefiles doesn't interfere with following a normal file
1978 Ensure that largefiles doesn't interfere with following a normal file
1966 $ hg --config extensions.largefiles= log -f d -T '{desc}' -G
1979 $ hg --config extensions.largefiles= log -f d -T '{desc}' -G
1967 @ c
1980 @ c
1968 |
1981 |
1969 o a
1982 o a
1970
1983
1971 $ hg log -f d/a -T '{desc}' -G
1984 $ hg log -f d/a -T '{desc}' -G
1972 @ c
1985 @ c
1973 |
1986 |
1974 o a
1987 o a
1975
1988
1976 $ cd ..
1989 $ cd ..
1977
1990
1978 hg log -f with linkrev pointing to another branch
1991 hg log -f with linkrev pointing to another branch
1979 -------------------------------------------------
1992 -------------------------------------------------
1980
1993
1981 create history with a filerev whose linkrev points to another branch
1994 create history with a filerev whose linkrev points to another branch
1982
1995
1983 $ hg init branchedlinkrev
1996 $ hg init branchedlinkrev
1984 $ cd branchedlinkrev
1997 $ cd branchedlinkrev
1985 $ echo 1 > a
1998 $ echo 1 > a
1986 $ hg commit -Am 'content1'
1999 $ hg commit -Am 'content1'
1987 adding a
2000 adding a
1988 $ echo 2 > a
2001 $ echo 2 > a
1989 $ hg commit -m 'content2'
2002 $ hg commit -m 'content2'
1990 $ hg up --rev 'desc(content1)'
2003 $ hg up --rev 'desc(content1)'
1991 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2004 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1992 $ echo unrelated > unrelated
2005 $ echo unrelated > unrelated
1993 $ hg commit -Am 'unrelated'
2006 $ hg commit -Am 'unrelated'
1994 adding unrelated
2007 adding unrelated
1995 created new head
2008 created new head
1996 $ hg graft -r 'desc(content2)'
2009 $ hg graft -r 'desc(content2)'
1997 grafting 1:2294ae80ad84 "content2"
2010 grafting 1:2294ae80ad84 "content2"
1998 $ echo 3 > a
2011 $ echo 3 > a
1999 $ hg commit -m 'content3'
2012 $ hg commit -m 'content3'
2000 $ hg log -G
2013 $ hg log -G
2001 @ changeset: 4:50b9b36e9c5d
2014 @ changeset: 4:50b9b36e9c5d
2002 | tag: tip
2015 | tag: tip
2003 | user: test
2016 | user: test
2004 | date: Thu Jan 01 00:00:00 1970 +0000
2017 | date: Thu Jan 01 00:00:00 1970 +0000
2005 | summary: content3
2018 | summary: content3
2006 |
2019 |
2007 o changeset: 3:15b2327059e5
2020 o changeset: 3:15b2327059e5
2008 | user: test
2021 | user: test
2009 | date: Thu Jan 01 00:00:00 1970 +0000
2022 | date: Thu Jan 01 00:00:00 1970 +0000
2010 | summary: content2
2023 | summary: content2
2011 |
2024 |
2012 o changeset: 2:2029acd1168c
2025 o changeset: 2:2029acd1168c
2013 | parent: 0:ae0a3c9f9e95
2026 | parent: 0:ae0a3c9f9e95
2014 | user: test
2027 | user: test
2015 | date: Thu Jan 01 00:00:00 1970 +0000
2028 | date: Thu Jan 01 00:00:00 1970 +0000
2016 | summary: unrelated
2029 | summary: unrelated
2017 |
2030 |
2018 | o changeset: 1:2294ae80ad84
2031 | o changeset: 1:2294ae80ad84
2019 |/ user: test
2032 |/ user: test
2020 | date: Thu Jan 01 00:00:00 1970 +0000
2033 | date: Thu Jan 01 00:00:00 1970 +0000
2021 | summary: content2
2034 | summary: content2
2022 |
2035 |
2023 o changeset: 0:ae0a3c9f9e95
2036 o changeset: 0:ae0a3c9f9e95
2024 user: test
2037 user: test
2025 date: Thu Jan 01 00:00:00 1970 +0000
2038 date: Thu Jan 01 00:00:00 1970 +0000
2026 summary: content1
2039 summary: content1
2027
2040
2028
2041
2029 log -f on the file should list the graft result.
2042 log -f on the file should list the graft result.
2030
2043
2031 $ hg log -Gf a
2044 $ hg log -Gf a
2032 @ changeset: 4:50b9b36e9c5d
2045 @ changeset: 4:50b9b36e9c5d
2033 | tag: tip
2046 | tag: tip
2034 | user: test
2047 | user: test
2035 | date: Thu Jan 01 00:00:00 1970 +0000
2048 | date: Thu Jan 01 00:00:00 1970 +0000
2036 | summary: content3
2049 | summary: content3
2037 |
2050 |
2038 o changeset: 3:15b2327059e5
2051 o changeset: 3:15b2327059e5
2039 : user: test
2052 : user: test
2040 : date: Thu Jan 01 00:00:00 1970 +0000
2053 : date: Thu Jan 01 00:00:00 1970 +0000
2041 : summary: content2
2054 : summary: content2
2042 :
2055 :
2043 o changeset: 0:ae0a3c9f9e95
2056 o changeset: 0:ae0a3c9f9e95
2044 user: test
2057 user: test
2045 date: Thu Jan 01 00:00:00 1970 +0000
2058 date: Thu Jan 01 00:00:00 1970 +0000
2046 summary: content1
2059 summary: content1
2047
2060
2048
2061
2049 plain log lists the original version
2062 plain log lists the original version
2050 (XXX we should probably list both)
2063 (XXX we should probably list both)
2051
2064
2052 $ hg log -G a
2065 $ hg log -G a
2053 @ changeset: 4:50b9b36e9c5d
2066 @ changeset: 4:50b9b36e9c5d
2054 : tag: tip
2067 : tag: tip
2055 : user: test
2068 : user: test
2056 : date: Thu Jan 01 00:00:00 1970 +0000
2069 : date: Thu Jan 01 00:00:00 1970 +0000
2057 : summary: content3
2070 : summary: content3
2058 :
2071 :
2059 : o changeset: 1:2294ae80ad84
2072 : o changeset: 1:2294ae80ad84
2060 :/ user: test
2073 :/ user: test
2061 : date: Thu Jan 01 00:00:00 1970 +0000
2074 : date: Thu Jan 01 00:00:00 1970 +0000
2062 : summary: content2
2075 : summary: content2
2063 :
2076 :
2064 o changeset: 0:ae0a3c9f9e95
2077 o changeset: 0:ae0a3c9f9e95
2065 user: test
2078 user: test
2066 date: Thu Jan 01 00:00:00 1970 +0000
2079 date: Thu Jan 01 00:00:00 1970 +0000
2067 summary: content1
2080 summary: content1
2068
2081
2069
2082
2070 hg log -f from the grafted changeset
2083 hg log -f from the grafted changeset
2071 (The bootstrap should properly take the topology in account)
2084 (The bootstrap should properly take the topology in account)
2072
2085
2073 $ hg up 'desc(content3)^'
2086 $ hg up 'desc(content3)^'
2074 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2087 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2075 $ hg log -Gf a
2088 $ hg log -Gf a
2076 @ changeset: 3:15b2327059e5
2089 @ changeset: 3:15b2327059e5
2077 : user: test
2090 : user: test
2078 : date: Thu Jan 01 00:00:00 1970 +0000
2091 : date: Thu Jan 01 00:00:00 1970 +0000
2079 : summary: content2
2092 : summary: content2
2080 :
2093 :
2081 o changeset: 0:ae0a3c9f9e95
2094 o changeset: 0:ae0a3c9f9e95
2082 user: test
2095 user: test
2083 date: Thu Jan 01 00:00:00 1970 +0000
2096 date: Thu Jan 01 00:00:00 1970 +0000
2084 summary: content1
2097 summary: content1
2085
2098
2086
2099
2087 Test that we use the first non-hidden changeset in that case.
2100 Test that we use the first non-hidden changeset in that case.
2088
2101
2089 (hide the changeset)
2102 (hide the changeset)
2090
2103
2091 $ hg log -T '{node}\n' -r 1
2104 $ hg log -T '{node}\n' -r 1
2092 2294ae80ad8447bc78383182eeac50cb049df623
2105 2294ae80ad8447bc78383182eeac50cb049df623
2093 $ hg debugobsolete 2294ae80ad8447bc78383182eeac50cb049df623
2106 $ hg debugobsolete 2294ae80ad8447bc78383182eeac50cb049df623
2094 $ hg log -G
2107 $ hg log -G
2095 o changeset: 4:50b9b36e9c5d
2108 o changeset: 4:50b9b36e9c5d
2096 | tag: tip
2109 | tag: tip
2097 | user: test
2110 | user: test
2098 | date: Thu Jan 01 00:00:00 1970 +0000
2111 | date: Thu Jan 01 00:00:00 1970 +0000
2099 | summary: content3
2112 | summary: content3
2100 |
2113 |
2101 @ changeset: 3:15b2327059e5
2114 @ changeset: 3:15b2327059e5
2102 | user: test
2115 | user: test
2103 | date: Thu Jan 01 00:00:00 1970 +0000
2116 | date: Thu Jan 01 00:00:00 1970 +0000
2104 | summary: content2
2117 | summary: content2
2105 |
2118 |
2106 o changeset: 2:2029acd1168c
2119 o changeset: 2:2029acd1168c
2107 | parent: 0:ae0a3c9f9e95
2120 | parent: 0:ae0a3c9f9e95
2108 | user: test
2121 | user: test
2109 | date: Thu Jan 01 00:00:00 1970 +0000
2122 | date: Thu Jan 01 00:00:00 1970 +0000
2110 | summary: unrelated
2123 | summary: unrelated
2111 |
2124 |
2112 o changeset: 0:ae0a3c9f9e95
2125 o changeset: 0:ae0a3c9f9e95
2113 user: test
2126 user: test
2114 date: Thu Jan 01 00:00:00 1970 +0000
2127 date: Thu Jan 01 00:00:00 1970 +0000
2115 summary: content1
2128 summary: content1
2116
2129
2117
2130
2118 Check that log on the file does not drop the file revision.
2131 Check that log on the file does not drop the file revision.
2119
2132
2120 $ hg log -G a
2133 $ hg log -G a
2121 o changeset: 4:50b9b36e9c5d
2134 o changeset: 4:50b9b36e9c5d
2122 | tag: tip
2135 | tag: tip
2123 | user: test
2136 | user: test
2124 | date: Thu Jan 01 00:00:00 1970 +0000
2137 | date: Thu Jan 01 00:00:00 1970 +0000
2125 | summary: content3
2138 | summary: content3
2126 |
2139 |
2127 @ changeset: 3:15b2327059e5
2140 @ changeset: 3:15b2327059e5
2128 : user: test
2141 : user: test
2129 : date: Thu Jan 01 00:00:00 1970 +0000
2142 : date: Thu Jan 01 00:00:00 1970 +0000
2130 : summary: content2
2143 : summary: content2
2131 :
2144 :
2132 o changeset: 0:ae0a3c9f9e95
2145 o changeset: 0:ae0a3c9f9e95
2133 user: test
2146 user: test
2134 date: Thu Jan 01 00:00:00 1970 +0000
2147 date: Thu Jan 01 00:00:00 1970 +0000
2135 summary: content1
2148 summary: content1
2136
2149
2137
2150
2138 Even when a head revision is linkrev-shadowed.
2151 Even when a head revision is linkrev-shadowed.
2139
2152
2140 $ hg log -T '{node}\n' -r 4
2153 $ hg log -T '{node}\n' -r 4
2141 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
2154 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
2142 $ hg debugobsolete 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
2155 $ hg debugobsolete 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
2143 $ hg log -G a
2156 $ hg log -G a
2144 @ changeset: 3:15b2327059e5
2157 @ changeset: 3:15b2327059e5
2145 : tag: tip
2158 : tag: tip
2146 : user: test
2159 : user: test
2147 : date: Thu Jan 01 00:00:00 1970 +0000
2160 : date: Thu Jan 01 00:00:00 1970 +0000
2148 : summary: content2
2161 : summary: content2
2149 :
2162 :
2150 o changeset: 0:ae0a3c9f9e95
2163 o changeset: 0:ae0a3c9f9e95
2151 user: test
2164 user: test
2152 date: Thu Jan 01 00:00:00 1970 +0000
2165 date: Thu Jan 01 00:00:00 1970 +0000
2153 summary: content1
2166 summary: content1
2154
2167
2155
2168
2156 $ cd ..
2169 $ cd ..
2157
2170
2158 Even when the file revision is missing from some head:
2171 Even when the file revision is missing from some head:
2159
2172
2160 $ hg init issue4490
2173 $ hg init issue4490
2161 $ cd issue4490
2174 $ cd issue4490
2162 $ echo '[experimental]' >> .hg/hgrc
2175 $ echo '[experimental]' >> .hg/hgrc
2163 $ echo 'evolution=createmarkers' >> .hg/hgrc
2176 $ echo 'evolution=createmarkers' >> .hg/hgrc
2164 $ echo a > a
2177 $ echo a > a
2165 $ hg ci -Am0
2178 $ hg ci -Am0
2166 adding a
2179 adding a
2167 $ echo b > b
2180 $ echo b > b
2168 $ hg ci -Am1
2181 $ hg ci -Am1
2169 adding b
2182 adding b
2170 $ echo B > b
2183 $ echo B > b
2171 $ hg ci --amend -m 1
2184 $ hg ci --amend -m 1
2172 $ hg up 0
2185 $ hg up 0
2173 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2186 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2174 $ echo c > c
2187 $ echo c > c
2175 $ hg ci -Am2
2188 $ hg ci -Am2
2176 adding c
2189 adding c
2177 created new head
2190 created new head
2178 $ hg up 'head() and not .'
2191 $ hg up 'head() and not .'
2179 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
2192 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
2180 $ hg log -G
2193 $ hg log -G
2181 o changeset: 4:db815d6d32e6
2194 o changeset: 4:db815d6d32e6
2182 | tag: tip
2195 | tag: tip
2183 | parent: 0:f7b1eb17ad24
2196 | parent: 0:f7b1eb17ad24
2184 | user: test
2197 | user: test
2185 | date: Thu Jan 01 00:00:00 1970 +0000
2198 | date: Thu Jan 01 00:00:00 1970 +0000
2186 | summary: 2
2199 | summary: 2
2187 |
2200 |
2188 | @ changeset: 3:9bc8ce7f9356
2201 | @ changeset: 3:9bc8ce7f9356
2189 |/ parent: 0:f7b1eb17ad24
2202 |/ parent: 0:f7b1eb17ad24
2190 | user: test
2203 | user: test
2191 | date: Thu Jan 01 00:00:00 1970 +0000
2204 | date: Thu Jan 01 00:00:00 1970 +0000
2192 | summary: 1
2205 | summary: 1
2193 |
2206 |
2194 o changeset: 0:f7b1eb17ad24
2207 o changeset: 0:f7b1eb17ad24
2195 user: test
2208 user: test
2196 date: Thu Jan 01 00:00:00 1970 +0000
2209 date: Thu Jan 01 00:00:00 1970 +0000
2197 summary: 0
2210 summary: 0
2198
2211
2199 $ hg log -f -G b
2212 $ hg log -f -G b
2200 @ changeset: 3:9bc8ce7f9356
2213 @ changeset: 3:9bc8ce7f9356
2201 | parent: 0:f7b1eb17ad24
2214 | parent: 0:f7b1eb17ad24
2202 ~ user: test
2215 ~ user: test
2203 date: Thu Jan 01 00:00:00 1970 +0000
2216 date: Thu Jan 01 00:00:00 1970 +0000
2204 summary: 1
2217 summary: 1
2205
2218
2206 $ hg log -G b
2219 $ hg log -G b
2207 @ changeset: 3:9bc8ce7f9356
2220 @ changeset: 3:9bc8ce7f9356
2208 | parent: 0:f7b1eb17ad24
2221 | parent: 0:f7b1eb17ad24
2209 ~ user: test
2222 ~ user: test
2210 date: Thu Jan 01 00:00:00 1970 +0000
2223 date: Thu Jan 01 00:00:00 1970 +0000
2211 summary: 1
2224 summary: 1
2212
2225
2213 $ cd ..
2226 $ cd ..
2214
2227
2215 Check proper report when the manifest changes but not the file issue4499
2228 Check proper report when the manifest changes but not the file issue4499
2216 ------------------------------------------------------------------------
2229 ------------------------------------------------------------------------
2217
2230
2218 $ hg init issue4499
2231 $ hg init issue4499
2219 $ cd issue4499
2232 $ cd issue4499
2220 $ for f in A B C D F E G H I J K L M N O P Q R S T U; do
2233 $ for f in A B C D F E G H I J K L M N O P Q R S T U; do
2221 > echo 1 > $f;
2234 > echo 1 > $f;
2222 > hg add $f;
2235 > hg add $f;
2223 > done
2236 > done
2224 $ hg commit -m 'A1B1C1'
2237 $ hg commit -m 'A1B1C1'
2225 $ echo 2 > A
2238 $ echo 2 > A
2226 $ echo 2 > B
2239 $ echo 2 > B
2227 $ echo 2 > C
2240 $ echo 2 > C
2228 $ hg commit -m 'A2B2C2'
2241 $ hg commit -m 'A2B2C2'
2229 $ hg up 0
2242 $ hg up 0
2230 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
2243 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
2231 $ echo 3 > A
2244 $ echo 3 > A
2232 $ echo 2 > B
2245 $ echo 2 > B
2233 $ echo 2 > C
2246 $ echo 2 > C
2234 $ hg commit -m 'A3B2C2'
2247 $ hg commit -m 'A3B2C2'
2235 created new head
2248 created new head
2236
2249
2237 $ hg log -G
2250 $ hg log -G
2238 @ changeset: 2:fe5fc3d0eb17
2251 @ changeset: 2:fe5fc3d0eb17
2239 | tag: tip
2252 | tag: tip
2240 | parent: 0:abf4f0e38563
2253 | parent: 0:abf4f0e38563
2241 | user: test
2254 | user: test
2242 | date: Thu Jan 01 00:00:00 1970 +0000
2255 | date: Thu Jan 01 00:00:00 1970 +0000
2243 | summary: A3B2C2
2256 | summary: A3B2C2
2244 |
2257 |
2245 | o changeset: 1:07dcc6b312c0
2258 | o changeset: 1:07dcc6b312c0
2246 |/ user: test
2259 |/ user: test
2247 | date: Thu Jan 01 00:00:00 1970 +0000
2260 | date: Thu Jan 01 00:00:00 1970 +0000
2248 | summary: A2B2C2
2261 | summary: A2B2C2
2249 |
2262 |
2250 o changeset: 0:abf4f0e38563
2263 o changeset: 0:abf4f0e38563
2251 user: test
2264 user: test
2252 date: Thu Jan 01 00:00:00 1970 +0000
2265 date: Thu Jan 01 00:00:00 1970 +0000
2253 summary: A1B1C1
2266 summary: A1B1C1
2254
2267
2255
2268
2256 Log -f on B should reports current changesets
2269 Log -f on B should reports current changesets
2257
2270
2258 $ hg log -fG B
2271 $ hg log -fG B
2259 @ changeset: 2:fe5fc3d0eb17
2272 @ changeset: 2:fe5fc3d0eb17
2260 | tag: tip
2273 | tag: tip
2261 | parent: 0:abf4f0e38563
2274 | parent: 0:abf4f0e38563
2262 | user: test
2275 | user: test
2263 | date: Thu Jan 01 00:00:00 1970 +0000
2276 | date: Thu Jan 01 00:00:00 1970 +0000
2264 | summary: A3B2C2
2277 | summary: A3B2C2
2265 |
2278 |
2266 o changeset: 0:abf4f0e38563
2279 o changeset: 0:abf4f0e38563
2267 user: test
2280 user: test
2268 date: Thu Jan 01 00:00:00 1970 +0000
2281 date: Thu Jan 01 00:00:00 1970 +0000
2269 summary: A1B1C1
2282 summary: A1B1C1
2270
2283
2271 $ cd ..
2284 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now