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