##// END OF EJS Templates
linkrev-filelog: handle filtered linkrev with no visible children (issue4307)...
Pierre-Yves David -
r23720:8ec03e0e default
parent child Browse files
Show More
@@ -1,3034 +1,3050 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import re
8 import re
9 import parser, util, error, discovery, hbisect, phases
9 import parser, util, error, discovery, hbisect, phases
10 import node
10 import node
11 import heapq
11 import heapq
12 import match as matchmod
12 import match as matchmod
13 from i18n import _
13 from i18n import _
14 import encoding
14 import encoding
15 import obsolete as obsmod
15 import obsolete as obsmod
16 import pathutil
16 import pathutil
17 import repoview
17 import repoview
18
18
19 def _revancestors(repo, revs, followfirst):
19 def _revancestors(repo, revs, followfirst):
20 """Like revlog.ancestors(), but supports followfirst."""
20 """Like revlog.ancestors(), but supports followfirst."""
21 cut = followfirst and 1 or None
21 cut = followfirst and 1 or None
22 cl = repo.changelog
22 cl = repo.changelog
23
23
24 def iterate():
24 def iterate():
25 revqueue, revsnode = None, None
25 revqueue, revsnode = None, None
26 h = []
26 h = []
27
27
28 revs.sort(reverse=True)
28 revs.sort(reverse=True)
29 revqueue = util.deque(revs)
29 revqueue = util.deque(revs)
30 if revqueue:
30 if revqueue:
31 revsnode = revqueue.popleft()
31 revsnode = revqueue.popleft()
32 heapq.heappush(h, -revsnode)
32 heapq.heappush(h, -revsnode)
33
33
34 seen = set([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 If some linkrev points to revisions filtered by the current repoview, we'll
775 If some linkrev points to revisions filtered by the current repoview, we'll
776 work around it to return a non-filtered value.
776 work around it to return a non-filtered value.
777 """
777 """
778
778
779 # i18n: "filelog" is a keyword
779 # i18n: "filelog" is a keyword
780 pat = getstring(x, _("filelog requires a pattern"))
780 pat = getstring(x, _("filelog requires a pattern"))
781 s = set()
781 s = set()
782 cl = repo.changelog
782 cl = repo.changelog
783
783
784 if not matchmod.patkind(pat):
784 if not matchmod.patkind(pat):
785 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
785 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
786 files = [f]
786 files = [f]
787 else:
787 else:
788 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
788 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
789 files = (f for f in repo[None] if m(f))
789 files = (f for f in repo[None] if m(f))
790
790
791 for f in files:
791 for f in files:
792 backrevref = {} # final value for: changerev -> filerev
792 backrevref = {} # final value for: changerev -> filerev
793 lowestchild = {} # lowest known filerev child of a filerev
793 lowestchild = {} # lowest known filerev child of a filerev
794 delayed = [] # filerev with filtered linkrev, for post-processing
794 delayed = [] # filerev with filtered linkrev, for post-processing
795 lowesthead = None # cache for manifest content of all head revisions
795 fl = repo.file(f)
796 fl = repo.file(f)
796 for fr in list(fl):
797 for fr in list(fl):
797 lkr = rev = fl.linkrev(fr)
798 lkr = rev = fl.linkrev(fr)
798 if rev not in cl:
799 if rev not in cl:
799 # changerev pointed in linkrev is filtered
800 # changerev pointed in linkrev is filtered
800 # record it for post processing.
801 # record it for post processing.
801 delayed.append((fr, rev))
802 delayed.append((fr, rev))
802 continue
803 continue
803 for p in fl.parentrevs(fr):
804 for p in fl.parentrevs(fr):
804 if 0 <= p and p not in lowestchild:
805 if 0 <= p and p not in lowestchild:
805 lowestchild[p] = fr
806 lowestchild[p] = fr
806 backrevref[fr] = rev
807 backrevref[fr] = rev
807 s.add(rev)
808 s.add(rev)
808
809
809 # Post-processing of all filerevs we skipped because they were
810 # Post-processing of all filerevs we skipped because they were
810 # filtered. If such filerevs have known and unfiltered children, this
811 # filtered. If such filerevs have known and unfiltered children, this
811 # means they have an unfiltered appearance out there. We'll use linkrev
812 # means they have an unfiltered appearance out there. We'll use linkrev
812 # adjustment to find one of these appearances. The lowest known child
813 # adjustment to find one of these appearances. The lowest known child
813 # will be used as a starting point because it is the best upper-bound we
814 # will be used as a starting point because it is the best upper-bound we
814 # have.
815 # have.
815 #
816 #
816 # This approach will fail when an unfiltered but linkrev-shadowed
817 # This approach will fail when an unfiltered but linkrev-shadowed
817 # appearance exists in a head changeset without unfiltered filerev
818 # appearance exists in a head changeset without unfiltered filerev
818 # children anywhere.
819 # children anywhere.
819 while delayed:
820 while delayed:
820 # must be a descending iteration. To slowly fill lowest child
821 # must be a descending iteration. To slowly fill lowest child
821 # information that is of potential use by the next item.
822 # information that is of potential use by the next item.
822 fr, rev = delayed.pop()
823 fr, rev = delayed.pop()
823 lkr = rev
824 lkr = rev
824
825
825 child = lowestchild.get(fr)
826 child = lowestchild.get(fr)
826
827
827 if child is None:
828 if child is None:
828 # XXX content could be linkrev-shadowed in a head, but lets
829 # search for existence of this file revision in a head revision.
829 # ignore this case for now.
830 # There are three possibilities:
830 continue
831 # - the revision exists in a head and we can find an
832 # introduction from there,
833 # - the revision does not exist in a head because it has been
834 # changed since its introduction: we would have found a child
835 # and be in the other 'else' clause,
836 # - all versions of the revision are hidden.
837 if lowesthead is None:
838 lowesthead = {}
839 for h in repo.heads():
840 fnode = repo[h].manifest()[f]
841 lowesthead[fl.rev(fnode)] = h
842 headrev = lowesthead.get(fr)
843 if headrev is None:
844 # content is nowhere unfiltered
845 continue
846 rev = repo[headrev][f].introrev()
831 else:
847 else:
832 # the lowest known child is a good upper bound
848 # the lowest known child is a good upper bound
833 childcrev = backrevref[child]
849 childcrev = backrevref[child]
834 # XXX this does not guarantee returning the lowest
850 # XXX this does not guarantee returning the lowest
835 # introduction of this revision, but this gives a
851 # introduction of this revision, but this gives a
836 # result which is a good start and will fit in most
852 # result which is a good start and will fit in most
837 # cases. We probably need to fix the multiple
853 # cases. We probably need to fix the multiple
838 # introductions case properly (report each
854 # introductions case properly (report each
839 # introduction, even for identical file revisions)
855 # introduction, even for identical file revisions)
840 # once and for all at some point anyway.
856 # once and for all at some point anyway.
841 for p in repo[childcrev][f].parents():
857 for p in repo[childcrev][f].parents():
842 if p.filerev() == fr:
858 if p.filerev() == fr:
843 rev = p.rev()
859 rev = p.rev()
844 break
860 break
845 if rev == lkr: # no shadowed entry found
861 if rev == lkr: # no shadowed entry found
846 # XXX This should never happen unless some manifest points
862 # XXX This should never happen unless some manifest points
847 # to biggish file revisions (like a revision that uses a
863 # to biggish file revisions (like a revision that uses a
848 # parent that never appears in the manifest ancestors)
864 # parent that never appears in the manifest ancestors)
849 continue
865 continue
850
866
851 # Fill the data for the next iteration.
867 # Fill the data for the next iteration.
852 for p in fl.parentrevs(fr):
868 for p in fl.parentrevs(fr):
853 if 0 <= p and p not in lowestchild:
869 if 0 <= p and p not in lowestchild:
854 lowestchild[p] = fr
870 lowestchild[p] = fr
855 backrevref[fr] = rev
871 backrevref[fr] = rev
856 s.add(rev)
872 s.add(rev)
857
873
858 return subset & s
874 return subset & s
859
875
860 def first(repo, subset, x):
876 def first(repo, subset, x):
861 """``first(set, [n])``
877 """``first(set, [n])``
862 An alias for limit().
878 An alias for limit().
863 """
879 """
864 return limit(repo, subset, x)
880 return limit(repo, subset, x)
865
881
866 def _follow(repo, subset, x, name, followfirst=False):
882 def _follow(repo, subset, x, name, followfirst=False):
867 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
883 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
868 c = repo['.']
884 c = repo['.']
869 if l:
885 if l:
870 x = getstring(l[0], _("%s expected a filename") % name)
886 x = getstring(l[0], _("%s expected a filename") % name)
871 if x in c:
887 if x in c:
872 cx = c[x]
888 cx = c[x]
873 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
889 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
874 # include the revision responsible for the most recent version
890 # include the revision responsible for the most recent version
875 s.add(cx.introrev())
891 s.add(cx.introrev())
876 else:
892 else:
877 return baseset()
893 return baseset()
878 else:
894 else:
879 s = _revancestors(repo, baseset([c.rev()]), followfirst)
895 s = _revancestors(repo, baseset([c.rev()]), followfirst)
880
896
881 return subset & s
897 return subset & s
882
898
883 def follow(repo, subset, x):
899 def follow(repo, subset, x):
884 """``follow([file])``
900 """``follow([file])``
885 An alias for ``::.`` (ancestors of the working copy's first parent).
901 An alias for ``::.`` (ancestors of the working copy's first parent).
886 If a filename is specified, the history of the given file is followed,
902 If a filename is specified, the history of the given file is followed,
887 including copies.
903 including copies.
888 """
904 """
889 return _follow(repo, subset, x, 'follow')
905 return _follow(repo, subset, x, 'follow')
890
906
891 def _followfirst(repo, subset, x):
907 def _followfirst(repo, subset, x):
892 # ``followfirst([file])``
908 # ``followfirst([file])``
893 # Like ``follow([file])`` but follows only the first parent of
909 # Like ``follow([file])`` but follows only the first parent of
894 # every revision or file revision.
910 # every revision or file revision.
895 return _follow(repo, subset, x, '_followfirst', followfirst=True)
911 return _follow(repo, subset, x, '_followfirst', followfirst=True)
896
912
897 def getall(repo, subset, x):
913 def getall(repo, subset, x):
898 """``all()``
914 """``all()``
899 All changesets, the same as ``0:tip``.
915 All changesets, the same as ``0:tip``.
900 """
916 """
901 # i18n: "all" is a keyword
917 # i18n: "all" is a keyword
902 getargs(x, 0, 0, _("all takes no arguments"))
918 getargs(x, 0, 0, _("all takes no arguments"))
903 return subset
919 return subset
904
920
905 def grep(repo, subset, x):
921 def grep(repo, subset, x):
906 """``grep(regex)``
922 """``grep(regex)``
907 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
923 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
908 to ensure special escape characters are handled correctly. Unlike
924 to ensure special escape characters are handled correctly. Unlike
909 ``keyword(string)``, the match is case-sensitive.
925 ``keyword(string)``, the match is case-sensitive.
910 """
926 """
911 try:
927 try:
912 # i18n: "grep" is a keyword
928 # i18n: "grep" is a keyword
913 gr = re.compile(getstring(x, _("grep requires a string")))
929 gr = re.compile(getstring(x, _("grep requires a string")))
914 except re.error, e:
930 except re.error, e:
915 raise error.ParseError(_('invalid match pattern: %s') % e)
931 raise error.ParseError(_('invalid match pattern: %s') % e)
916
932
917 def matches(x):
933 def matches(x):
918 c = repo[x]
934 c = repo[x]
919 for e in c.files() + [c.user(), c.description()]:
935 for e in c.files() + [c.user(), c.description()]:
920 if gr.search(e):
936 if gr.search(e):
921 return True
937 return True
922 return False
938 return False
923
939
924 return subset.filter(matches)
940 return subset.filter(matches)
925
941
926 def _matchfiles(repo, subset, x):
942 def _matchfiles(repo, subset, x):
927 # _matchfiles takes a revset list of prefixed arguments:
943 # _matchfiles takes a revset list of prefixed arguments:
928 #
944 #
929 # [p:foo, i:bar, x:baz]
945 # [p:foo, i:bar, x:baz]
930 #
946 #
931 # builds a match object from them and filters subset. Allowed
947 # builds a match object from them and filters subset. Allowed
932 # prefixes are 'p:' for regular patterns, 'i:' for include
948 # prefixes are 'p:' for regular patterns, 'i:' for include
933 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
949 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
934 # a revision identifier, or the empty string to reference the
950 # a revision identifier, or the empty string to reference the
935 # working directory, from which the match object is
951 # working directory, from which the match object is
936 # initialized. Use 'd:' to set the default matching mode, default
952 # initialized. Use 'd:' to set the default matching mode, default
937 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
953 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
938
954
939 # i18n: "_matchfiles" is a keyword
955 # i18n: "_matchfiles" is a keyword
940 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
956 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
941 pats, inc, exc = [], [], []
957 pats, inc, exc = [], [], []
942 rev, default = None, None
958 rev, default = None, None
943 for arg in l:
959 for arg in l:
944 # i18n: "_matchfiles" is a keyword
960 # i18n: "_matchfiles" is a keyword
945 s = getstring(arg, _("_matchfiles requires string arguments"))
961 s = getstring(arg, _("_matchfiles requires string arguments"))
946 prefix, value = s[:2], s[2:]
962 prefix, value = s[:2], s[2:]
947 if prefix == 'p:':
963 if prefix == 'p:':
948 pats.append(value)
964 pats.append(value)
949 elif prefix == 'i:':
965 elif prefix == 'i:':
950 inc.append(value)
966 inc.append(value)
951 elif prefix == 'x:':
967 elif prefix == 'x:':
952 exc.append(value)
968 exc.append(value)
953 elif prefix == 'r:':
969 elif prefix == 'r:':
954 if rev is not None:
970 if rev is not None:
955 # i18n: "_matchfiles" is a keyword
971 # i18n: "_matchfiles" is a keyword
956 raise error.ParseError(_('_matchfiles expected at most one '
972 raise error.ParseError(_('_matchfiles expected at most one '
957 'revision'))
973 'revision'))
958 rev = value
974 rev = value
959 elif prefix == 'd:':
975 elif prefix == 'd:':
960 if default is not None:
976 if default is not None:
961 # i18n: "_matchfiles" is a keyword
977 # i18n: "_matchfiles" is a keyword
962 raise error.ParseError(_('_matchfiles expected at most one '
978 raise error.ParseError(_('_matchfiles expected at most one '
963 'default mode'))
979 'default mode'))
964 default = value
980 default = value
965 else:
981 else:
966 # i18n: "_matchfiles" is a keyword
982 # i18n: "_matchfiles" is a keyword
967 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
983 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
968 if not default:
984 if not default:
969 default = 'glob'
985 default = 'glob'
970
986
971 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
987 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
972 exclude=exc, ctx=repo[rev], default=default)
988 exclude=exc, ctx=repo[rev], default=default)
973
989
974 def matches(x):
990 def matches(x):
975 for f in repo[x].files():
991 for f in repo[x].files():
976 if m(f):
992 if m(f):
977 return True
993 return True
978 return False
994 return False
979
995
980 return subset.filter(matches)
996 return subset.filter(matches)
981
997
982 def hasfile(repo, subset, x):
998 def hasfile(repo, subset, x):
983 """``file(pattern)``
999 """``file(pattern)``
984 Changesets affecting files matched by pattern.
1000 Changesets affecting files matched by pattern.
985
1001
986 For a faster but less accurate result, consider using ``filelog()``
1002 For a faster but less accurate result, consider using ``filelog()``
987 instead.
1003 instead.
988
1004
989 This predicate uses ``glob:`` as the default kind of pattern.
1005 This predicate uses ``glob:`` as the default kind of pattern.
990 """
1006 """
991 # i18n: "file" is a keyword
1007 # i18n: "file" is a keyword
992 pat = getstring(x, _("file requires a pattern"))
1008 pat = getstring(x, _("file requires a pattern"))
993 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1009 return _matchfiles(repo, subset, ('string', 'p:' + pat))
994
1010
995 def head(repo, subset, x):
1011 def head(repo, subset, x):
996 """``head()``
1012 """``head()``
997 Changeset is a named branch head.
1013 Changeset is a named branch head.
998 """
1014 """
999 # i18n: "head" is a keyword
1015 # i18n: "head" is a keyword
1000 getargs(x, 0, 0, _("head takes no arguments"))
1016 getargs(x, 0, 0, _("head takes no arguments"))
1001 hs = set()
1017 hs = set()
1002 for b, ls in repo.branchmap().iteritems():
1018 for b, ls in repo.branchmap().iteritems():
1003 hs.update(repo[h].rev() for h in ls)
1019 hs.update(repo[h].rev() for h in ls)
1004 return baseset(hs).filter(subset.__contains__)
1020 return baseset(hs).filter(subset.__contains__)
1005
1021
1006 def heads(repo, subset, x):
1022 def heads(repo, subset, x):
1007 """``heads(set)``
1023 """``heads(set)``
1008 Members of set with no children in set.
1024 Members of set with no children in set.
1009 """
1025 """
1010 s = getset(repo, subset, x)
1026 s = getset(repo, subset, x)
1011 ps = parents(repo, subset, x)
1027 ps = parents(repo, subset, x)
1012 return s - ps
1028 return s - ps
1013
1029
1014 def hidden(repo, subset, x):
1030 def hidden(repo, subset, x):
1015 """``hidden()``
1031 """``hidden()``
1016 Hidden changesets.
1032 Hidden changesets.
1017 """
1033 """
1018 # i18n: "hidden" is a keyword
1034 # i18n: "hidden" is a keyword
1019 getargs(x, 0, 0, _("hidden takes no arguments"))
1035 getargs(x, 0, 0, _("hidden takes no arguments"))
1020 hiddenrevs = repoview.filterrevs(repo, 'visible')
1036 hiddenrevs = repoview.filterrevs(repo, 'visible')
1021 return subset & hiddenrevs
1037 return subset & hiddenrevs
1022
1038
1023 def keyword(repo, subset, x):
1039 def keyword(repo, subset, x):
1024 """``keyword(string)``
1040 """``keyword(string)``
1025 Search commit message, user name, and names of changed files for
1041 Search commit message, user name, and names of changed files for
1026 string. The match is case-insensitive.
1042 string. The match is case-insensitive.
1027 """
1043 """
1028 # i18n: "keyword" is a keyword
1044 # i18n: "keyword" is a keyword
1029 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1045 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1030
1046
1031 def matches(r):
1047 def matches(r):
1032 c = repo[r]
1048 c = repo[r]
1033 return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
1049 return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
1034 c.description()])
1050 c.description()])
1035
1051
1036 return subset.filter(matches)
1052 return subset.filter(matches)
1037
1053
1038 def limit(repo, subset, x):
1054 def limit(repo, subset, x):
1039 """``limit(set, [n])``
1055 """``limit(set, [n])``
1040 First n members of set, defaulting to 1.
1056 First n members of set, defaulting to 1.
1041 """
1057 """
1042 # i18n: "limit" is a keyword
1058 # i18n: "limit" is a keyword
1043 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
1059 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
1044 try:
1060 try:
1045 lim = 1
1061 lim = 1
1046 if len(l) == 2:
1062 if len(l) == 2:
1047 # i18n: "limit" is a keyword
1063 # i18n: "limit" is a keyword
1048 lim = int(getstring(l[1], _("limit requires a number")))
1064 lim = int(getstring(l[1], _("limit requires a number")))
1049 except (TypeError, ValueError):
1065 except (TypeError, ValueError):
1050 # i18n: "limit" is a keyword
1066 # i18n: "limit" is a keyword
1051 raise error.ParseError(_("limit expects a number"))
1067 raise error.ParseError(_("limit expects a number"))
1052 ss = subset
1068 ss = subset
1053 os = getset(repo, spanset(repo), l[0])
1069 os = getset(repo, spanset(repo), l[0])
1054 result = []
1070 result = []
1055 it = iter(os)
1071 it = iter(os)
1056 for x in xrange(lim):
1072 for x in xrange(lim):
1057 try:
1073 try:
1058 y = it.next()
1074 y = it.next()
1059 if y in ss:
1075 if y in ss:
1060 result.append(y)
1076 result.append(y)
1061 except (StopIteration):
1077 except (StopIteration):
1062 break
1078 break
1063 return baseset(result)
1079 return baseset(result)
1064
1080
1065 def last(repo, subset, x):
1081 def last(repo, subset, x):
1066 """``last(set, [n])``
1082 """``last(set, [n])``
1067 Last n members of set, defaulting to 1.
1083 Last n members of set, defaulting to 1.
1068 """
1084 """
1069 # i18n: "last" is a keyword
1085 # i18n: "last" is a keyword
1070 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1086 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1071 try:
1087 try:
1072 lim = 1
1088 lim = 1
1073 if len(l) == 2:
1089 if len(l) == 2:
1074 # i18n: "last" is a keyword
1090 # i18n: "last" is a keyword
1075 lim = int(getstring(l[1], _("last requires a number")))
1091 lim = int(getstring(l[1], _("last requires a number")))
1076 except (TypeError, ValueError):
1092 except (TypeError, ValueError):
1077 # i18n: "last" is a keyword
1093 # i18n: "last" is a keyword
1078 raise error.ParseError(_("last expects a number"))
1094 raise error.ParseError(_("last expects a number"))
1079 ss = subset
1095 ss = subset
1080 os = getset(repo, spanset(repo), l[0])
1096 os = getset(repo, spanset(repo), l[0])
1081 os.reverse()
1097 os.reverse()
1082 result = []
1098 result = []
1083 it = iter(os)
1099 it = iter(os)
1084 for x in xrange(lim):
1100 for x in xrange(lim):
1085 try:
1101 try:
1086 y = it.next()
1102 y = it.next()
1087 if y in ss:
1103 if y in ss:
1088 result.append(y)
1104 result.append(y)
1089 except (StopIteration):
1105 except (StopIteration):
1090 break
1106 break
1091 return baseset(result)
1107 return baseset(result)
1092
1108
1093 def maxrev(repo, subset, x):
1109 def maxrev(repo, subset, x):
1094 """``max(set)``
1110 """``max(set)``
1095 Changeset with highest revision number in set.
1111 Changeset with highest revision number in set.
1096 """
1112 """
1097 os = getset(repo, spanset(repo), x)
1113 os = getset(repo, spanset(repo), x)
1098 if os:
1114 if os:
1099 m = os.max()
1115 m = os.max()
1100 if m in subset:
1116 if m in subset:
1101 return baseset([m])
1117 return baseset([m])
1102 return baseset()
1118 return baseset()
1103
1119
1104 def merge(repo, subset, x):
1120 def merge(repo, subset, x):
1105 """``merge()``
1121 """``merge()``
1106 Changeset is a merge changeset.
1122 Changeset is a merge changeset.
1107 """
1123 """
1108 # i18n: "merge" is a keyword
1124 # i18n: "merge" is a keyword
1109 getargs(x, 0, 0, _("merge takes no arguments"))
1125 getargs(x, 0, 0, _("merge takes no arguments"))
1110 cl = repo.changelog
1126 cl = repo.changelog
1111 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1)
1127 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1)
1112
1128
1113 def branchpoint(repo, subset, x):
1129 def branchpoint(repo, subset, x):
1114 """``branchpoint()``
1130 """``branchpoint()``
1115 Changesets with more than one child.
1131 Changesets with more than one child.
1116 """
1132 """
1117 # i18n: "branchpoint" is a keyword
1133 # i18n: "branchpoint" is a keyword
1118 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1134 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1119 cl = repo.changelog
1135 cl = repo.changelog
1120 if not subset:
1136 if not subset:
1121 return baseset()
1137 return baseset()
1122 baserev = min(subset)
1138 baserev = min(subset)
1123 parentscount = [0]*(len(repo) - baserev)
1139 parentscount = [0]*(len(repo) - baserev)
1124 for r in cl.revs(start=baserev + 1):
1140 for r in cl.revs(start=baserev + 1):
1125 for p in cl.parentrevs(r):
1141 for p in cl.parentrevs(r):
1126 if p >= baserev:
1142 if p >= baserev:
1127 parentscount[p - baserev] += 1
1143 parentscount[p - baserev] += 1
1128 return subset.filter(lambda r: parentscount[r - baserev] > 1)
1144 return subset.filter(lambda r: parentscount[r - baserev] > 1)
1129
1145
1130 def minrev(repo, subset, x):
1146 def minrev(repo, subset, x):
1131 """``min(set)``
1147 """``min(set)``
1132 Changeset with lowest revision number in set.
1148 Changeset with lowest revision number in set.
1133 """
1149 """
1134 os = getset(repo, spanset(repo), x)
1150 os = getset(repo, spanset(repo), x)
1135 if os:
1151 if os:
1136 m = os.min()
1152 m = os.min()
1137 if m in subset:
1153 if m in subset:
1138 return baseset([m])
1154 return baseset([m])
1139 return baseset()
1155 return baseset()
1140
1156
1141 def modifies(repo, subset, x):
1157 def modifies(repo, subset, x):
1142 """``modifies(pattern)``
1158 """``modifies(pattern)``
1143 Changesets modifying files matched by pattern.
1159 Changesets modifying files matched by pattern.
1144
1160
1145 The pattern without explicit kind like ``glob:`` is expected to be
1161 The pattern without explicit kind like ``glob:`` is expected to be
1146 relative to the current directory and match against a file or a
1162 relative to the current directory and match against a file or a
1147 directory.
1163 directory.
1148 """
1164 """
1149 # i18n: "modifies" is a keyword
1165 # i18n: "modifies" is a keyword
1150 pat = getstring(x, _("modifies requires a pattern"))
1166 pat = getstring(x, _("modifies requires a pattern"))
1151 return checkstatus(repo, subset, pat, 0)
1167 return checkstatus(repo, subset, pat, 0)
1152
1168
1153 def node_(repo, subset, x):
1169 def node_(repo, subset, x):
1154 """``id(string)``
1170 """``id(string)``
1155 Revision non-ambiguously specified by the given hex string prefix.
1171 Revision non-ambiguously specified by the given hex string prefix.
1156 """
1172 """
1157 # i18n: "id" is a keyword
1173 # i18n: "id" is a keyword
1158 l = getargs(x, 1, 1, _("id requires one argument"))
1174 l = getargs(x, 1, 1, _("id requires one argument"))
1159 # i18n: "id" is a keyword
1175 # i18n: "id" is a keyword
1160 n = getstring(l[0], _("id requires a string"))
1176 n = getstring(l[0], _("id requires a string"))
1161 if len(n) == 40:
1177 if len(n) == 40:
1162 rn = repo[n].rev()
1178 rn = repo[n].rev()
1163 else:
1179 else:
1164 rn = None
1180 rn = None
1165 pm = repo.changelog._partialmatch(n)
1181 pm = repo.changelog._partialmatch(n)
1166 if pm is not None:
1182 if pm is not None:
1167 rn = repo.changelog.rev(pm)
1183 rn = repo.changelog.rev(pm)
1168
1184
1169 if rn is None:
1185 if rn is None:
1170 return baseset()
1186 return baseset()
1171 result = baseset([rn])
1187 result = baseset([rn])
1172 return result & subset
1188 return result & subset
1173
1189
1174 def obsolete(repo, subset, x):
1190 def obsolete(repo, subset, x):
1175 """``obsolete()``
1191 """``obsolete()``
1176 Mutable changeset with a newer version."""
1192 Mutable changeset with a newer version."""
1177 # i18n: "obsolete" is a keyword
1193 # i18n: "obsolete" is a keyword
1178 getargs(x, 0, 0, _("obsolete takes no arguments"))
1194 getargs(x, 0, 0, _("obsolete takes no arguments"))
1179 obsoletes = obsmod.getrevs(repo, 'obsolete')
1195 obsoletes = obsmod.getrevs(repo, 'obsolete')
1180 return subset & obsoletes
1196 return subset & obsoletes
1181
1197
1182 def only(repo, subset, x):
1198 def only(repo, subset, x):
1183 """``only(set, [set])``
1199 """``only(set, [set])``
1184 Changesets that are ancestors of the first set that are not ancestors
1200 Changesets that are ancestors of the first set that are not ancestors
1185 of any other head in the repo. If a second set is specified, the result
1201 of any other head in the repo. If a second set is specified, the result
1186 is ancestors of the first set that are not ancestors of the second set
1202 is ancestors of the first set that are not ancestors of the second set
1187 (i.e. ::<set1> - ::<set2>).
1203 (i.e. ::<set1> - ::<set2>).
1188 """
1204 """
1189 cl = repo.changelog
1205 cl = repo.changelog
1190 # i18n: "only" is a keyword
1206 # i18n: "only" is a keyword
1191 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1207 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1192 include = getset(repo, spanset(repo), args[0])
1208 include = getset(repo, spanset(repo), args[0])
1193 if len(args) == 1:
1209 if len(args) == 1:
1194 if not include:
1210 if not include:
1195 return baseset()
1211 return baseset()
1196
1212
1197 descendants = set(_revdescendants(repo, include, False))
1213 descendants = set(_revdescendants(repo, include, False))
1198 exclude = [rev for rev in cl.headrevs()
1214 exclude = [rev for rev in cl.headrevs()
1199 if not rev in descendants and not rev in include]
1215 if not rev in descendants and not rev in include]
1200 else:
1216 else:
1201 exclude = getset(repo, spanset(repo), args[1])
1217 exclude = getset(repo, spanset(repo), args[1])
1202
1218
1203 results = set(cl.findmissingrevs(common=exclude, heads=include))
1219 results = set(cl.findmissingrevs(common=exclude, heads=include))
1204 return subset & results
1220 return subset & results
1205
1221
1206 def origin(repo, subset, x):
1222 def origin(repo, subset, x):
1207 """``origin([set])``
1223 """``origin([set])``
1208 Changesets that were specified as a source for the grafts, transplants or
1224 Changesets that were specified as a source for the grafts, transplants or
1209 rebases that created the given revisions. Omitting the optional set is the
1225 rebases that created the given revisions. Omitting the optional set is the
1210 same as passing all(). If a changeset created by these operations is itself
1226 same as passing all(). If a changeset created by these operations is itself
1211 specified as a source for one of these operations, only the source changeset
1227 specified as a source for one of these operations, only the source changeset
1212 for the first operation is selected.
1228 for the first operation is selected.
1213 """
1229 """
1214 if x is not None:
1230 if x is not None:
1215 dests = getset(repo, spanset(repo), x)
1231 dests = getset(repo, spanset(repo), x)
1216 else:
1232 else:
1217 dests = getall(repo, spanset(repo), x)
1233 dests = getall(repo, spanset(repo), x)
1218
1234
1219 def _firstsrc(rev):
1235 def _firstsrc(rev):
1220 src = _getrevsource(repo, rev)
1236 src = _getrevsource(repo, rev)
1221 if src is None:
1237 if src is None:
1222 return None
1238 return None
1223
1239
1224 while True:
1240 while True:
1225 prev = _getrevsource(repo, src)
1241 prev = _getrevsource(repo, src)
1226
1242
1227 if prev is None:
1243 if prev is None:
1228 return src
1244 return src
1229 src = prev
1245 src = prev
1230
1246
1231 o = set([_firstsrc(r) for r in dests])
1247 o = set([_firstsrc(r) for r in dests])
1232 o -= set([None])
1248 o -= set([None])
1233 return subset & o
1249 return subset & o
1234
1250
1235 def outgoing(repo, subset, x):
1251 def outgoing(repo, subset, x):
1236 """``outgoing([path])``
1252 """``outgoing([path])``
1237 Changesets not found in the specified destination repository, or the
1253 Changesets not found in the specified destination repository, or the
1238 default push location.
1254 default push location.
1239 """
1255 """
1240 import hg # avoid start-up nasties
1256 import hg # avoid start-up nasties
1241 # i18n: "outgoing" is a keyword
1257 # i18n: "outgoing" is a keyword
1242 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1258 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1243 # i18n: "outgoing" is a keyword
1259 # i18n: "outgoing" is a keyword
1244 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1260 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1245 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1261 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1246 dest, branches = hg.parseurl(dest)
1262 dest, branches = hg.parseurl(dest)
1247 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1263 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1248 if revs:
1264 if revs:
1249 revs = [repo.lookup(rev) for rev in revs]
1265 revs = [repo.lookup(rev) for rev in revs]
1250 other = hg.peer(repo, {}, dest)
1266 other = hg.peer(repo, {}, dest)
1251 repo.ui.pushbuffer()
1267 repo.ui.pushbuffer()
1252 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1268 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1253 repo.ui.popbuffer()
1269 repo.ui.popbuffer()
1254 cl = repo.changelog
1270 cl = repo.changelog
1255 o = set([cl.rev(r) for r in outgoing.missing])
1271 o = set([cl.rev(r) for r in outgoing.missing])
1256 return subset & o
1272 return subset & o
1257
1273
1258 def p1(repo, subset, x):
1274 def p1(repo, subset, x):
1259 """``p1([set])``
1275 """``p1([set])``
1260 First parent of changesets in set, or the working directory.
1276 First parent of changesets in set, or the working directory.
1261 """
1277 """
1262 if x is None:
1278 if x is None:
1263 p = repo[x].p1().rev()
1279 p = repo[x].p1().rev()
1264 if p >= 0:
1280 if p >= 0:
1265 return subset & baseset([p])
1281 return subset & baseset([p])
1266 return baseset()
1282 return baseset()
1267
1283
1268 ps = set()
1284 ps = set()
1269 cl = repo.changelog
1285 cl = repo.changelog
1270 for r in getset(repo, spanset(repo), x):
1286 for r in getset(repo, spanset(repo), x):
1271 ps.add(cl.parentrevs(r)[0])
1287 ps.add(cl.parentrevs(r)[0])
1272 ps -= set([node.nullrev])
1288 ps -= set([node.nullrev])
1273 return subset & ps
1289 return subset & ps
1274
1290
1275 def p2(repo, subset, x):
1291 def p2(repo, subset, x):
1276 """``p2([set])``
1292 """``p2([set])``
1277 Second parent of changesets in set, or the working directory.
1293 Second parent of changesets in set, or the working directory.
1278 """
1294 """
1279 if x is None:
1295 if x is None:
1280 ps = repo[x].parents()
1296 ps = repo[x].parents()
1281 try:
1297 try:
1282 p = ps[1].rev()
1298 p = ps[1].rev()
1283 if p >= 0:
1299 if p >= 0:
1284 return subset & baseset([p])
1300 return subset & baseset([p])
1285 return baseset()
1301 return baseset()
1286 except IndexError:
1302 except IndexError:
1287 return baseset()
1303 return baseset()
1288
1304
1289 ps = set()
1305 ps = set()
1290 cl = repo.changelog
1306 cl = repo.changelog
1291 for r in getset(repo, spanset(repo), x):
1307 for r in getset(repo, spanset(repo), x):
1292 ps.add(cl.parentrevs(r)[1])
1308 ps.add(cl.parentrevs(r)[1])
1293 ps -= set([node.nullrev])
1309 ps -= set([node.nullrev])
1294 return subset & ps
1310 return subset & ps
1295
1311
1296 def parents(repo, subset, x):
1312 def parents(repo, subset, x):
1297 """``parents([set])``
1313 """``parents([set])``
1298 The set of all parents for all changesets in set, or the working directory.
1314 The set of all parents for all changesets in set, or the working directory.
1299 """
1315 """
1300 if x is None:
1316 if x is None:
1301 ps = set(p.rev() for p in repo[x].parents())
1317 ps = set(p.rev() for p in repo[x].parents())
1302 else:
1318 else:
1303 ps = set()
1319 ps = set()
1304 cl = repo.changelog
1320 cl = repo.changelog
1305 for r in getset(repo, spanset(repo), x):
1321 for r in getset(repo, spanset(repo), x):
1306 ps.update(cl.parentrevs(r))
1322 ps.update(cl.parentrevs(r))
1307 ps -= set([node.nullrev])
1323 ps -= set([node.nullrev])
1308 return subset & ps
1324 return subset & ps
1309
1325
1310 def parentspec(repo, subset, x, n):
1326 def parentspec(repo, subset, x, n):
1311 """``set^0``
1327 """``set^0``
1312 The set.
1328 The set.
1313 ``set^1`` (or ``set^``), ``set^2``
1329 ``set^1`` (or ``set^``), ``set^2``
1314 First or second parent, respectively, of all changesets in set.
1330 First or second parent, respectively, of all changesets in set.
1315 """
1331 """
1316 try:
1332 try:
1317 n = int(n[1])
1333 n = int(n[1])
1318 if n not in (0, 1, 2):
1334 if n not in (0, 1, 2):
1319 raise ValueError
1335 raise ValueError
1320 except (TypeError, ValueError):
1336 except (TypeError, ValueError):
1321 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1337 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1322 ps = set()
1338 ps = set()
1323 cl = repo.changelog
1339 cl = repo.changelog
1324 for r in getset(repo, fullreposet(repo), x):
1340 for r in getset(repo, fullreposet(repo), x):
1325 if n == 0:
1341 if n == 0:
1326 ps.add(r)
1342 ps.add(r)
1327 elif n == 1:
1343 elif n == 1:
1328 ps.add(cl.parentrevs(r)[0])
1344 ps.add(cl.parentrevs(r)[0])
1329 elif n == 2:
1345 elif n == 2:
1330 parents = cl.parentrevs(r)
1346 parents = cl.parentrevs(r)
1331 if len(parents) > 1:
1347 if len(parents) > 1:
1332 ps.add(parents[1])
1348 ps.add(parents[1])
1333 return subset & ps
1349 return subset & ps
1334
1350
1335 def present(repo, subset, x):
1351 def present(repo, subset, x):
1336 """``present(set)``
1352 """``present(set)``
1337 An empty set, if any revision in set isn't found; otherwise,
1353 An empty set, if any revision in set isn't found; otherwise,
1338 all revisions in set.
1354 all revisions in set.
1339
1355
1340 If any of specified revisions is not present in the local repository,
1356 If any of specified revisions is not present in the local repository,
1341 the query is normally aborted. But this predicate allows the query
1357 the query is normally aborted. But this predicate allows the query
1342 to continue even in such cases.
1358 to continue even in such cases.
1343 """
1359 """
1344 try:
1360 try:
1345 return getset(repo, subset, x)
1361 return getset(repo, subset, x)
1346 except error.RepoLookupError:
1362 except error.RepoLookupError:
1347 return baseset()
1363 return baseset()
1348
1364
1349 def public(repo, subset, x):
1365 def public(repo, subset, x):
1350 """``public()``
1366 """``public()``
1351 Changeset in public phase."""
1367 Changeset in public phase."""
1352 # i18n: "public" is a keyword
1368 # i18n: "public" is a keyword
1353 getargs(x, 0, 0, _("public takes no arguments"))
1369 getargs(x, 0, 0, _("public takes no arguments"))
1354 phase = repo._phasecache.phase
1370 phase = repo._phasecache.phase
1355 target = phases.public
1371 target = phases.public
1356 condition = lambda r: phase(repo, r) == target
1372 condition = lambda r: phase(repo, r) == target
1357 return subset.filter(condition, cache=False)
1373 return subset.filter(condition, cache=False)
1358
1374
1359 def remote(repo, subset, x):
1375 def remote(repo, subset, x):
1360 """``remote([id [,path]])``
1376 """``remote([id [,path]])``
1361 Local revision that corresponds to the given identifier in a
1377 Local revision that corresponds to the given identifier in a
1362 remote repository, if present. Here, the '.' identifier is a
1378 remote repository, if present. Here, the '.' identifier is a
1363 synonym for the current local branch.
1379 synonym for the current local branch.
1364 """
1380 """
1365
1381
1366 import hg # avoid start-up nasties
1382 import hg # avoid start-up nasties
1367 # i18n: "remote" is a keyword
1383 # i18n: "remote" is a keyword
1368 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1384 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1369
1385
1370 q = '.'
1386 q = '.'
1371 if len(l) > 0:
1387 if len(l) > 0:
1372 # i18n: "remote" is a keyword
1388 # i18n: "remote" is a keyword
1373 q = getstring(l[0], _("remote requires a string id"))
1389 q = getstring(l[0], _("remote requires a string id"))
1374 if q == '.':
1390 if q == '.':
1375 q = repo['.'].branch()
1391 q = repo['.'].branch()
1376
1392
1377 dest = ''
1393 dest = ''
1378 if len(l) > 1:
1394 if len(l) > 1:
1379 # i18n: "remote" is a keyword
1395 # i18n: "remote" is a keyword
1380 dest = getstring(l[1], _("remote requires a repository path"))
1396 dest = getstring(l[1], _("remote requires a repository path"))
1381 dest = repo.ui.expandpath(dest or 'default')
1397 dest = repo.ui.expandpath(dest or 'default')
1382 dest, branches = hg.parseurl(dest)
1398 dest, branches = hg.parseurl(dest)
1383 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1399 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1384 if revs:
1400 if revs:
1385 revs = [repo.lookup(rev) for rev in revs]
1401 revs = [repo.lookup(rev) for rev in revs]
1386 other = hg.peer(repo, {}, dest)
1402 other = hg.peer(repo, {}, dest)
1387 n = other.lookup(q)
1403 n = other.lookup(q)
1388 if n in repo:
1404 if n in repo:
1389 r = repo[n].rev()
1405 r = repo[n].rev()
1390 if r in subset:
1406 if r in subset:
1391 return baseset([r])
1407 return baseset([r])
1392 return baseset()
1408 return baseset()
1393
1409
1394 def removes(repo, subset, x):
1410 def removes(repo, subset, x):
1395 """``removes(pattern)``
1411 """``removes(pattern)``
1396 Changesets which remove files matching pattern.
1412 Changesets which remove files matching pattern.
1397
1413
1398 The pattern without explicit kind like ``glob:`` is expected to be
1414 The pattern without explicit kind like ``glob:`` is expected to be
1399 relative to the current directory and match against a file or a
1415 relative to the current directory and match against a file or a
1400 directory.
1416 directory.
1401 """
1417 """
1402 # i18n: "removes" is a keyword
1418 # i18n: "removes" is a keyword
1403 pat = getstring(x, _("removes requires a pattern"))
1419 pat = getstring(x, _("removes requires a pattern"))
1404 return checkstatus(repo, subset, pat, 2)
1420 return checkstatus(repo, subset, pat, 2)
1405
1421
1406 def rev(repo, subset, x):
1422 def rev(repo, subset, x):
1407 """``rev(number)``
1423 """``rev(number)``
1408 Revision with the given numeric identifier.
1424 Revision with the given numeric identifier.
1409 """
1425 """
1410 # i18n: "rev" is a keyword
1426 # i18n: "rev" is a keyword
1411 l = getargs(x, 1, 1, _("rev requires one argument"))
1427 l = getargs(x, 1, 1, _("rev requires one argument"))
1412 try:
1428 try:
1413 # i18n: "rev" is a keyword
1429 # i18n: "rev" is a keyword
1414 l = int(getstring(l[0], _("rev requires a number")))
1430 l = int(getstring(l[0], _("rev requires a number")))
1415 except (TypeError, ValueError):
1431 except (TypeError, ValueError):
1416 # i18n: "rev" is a keyword
1432 # i18n: "rev" is a keyword
1417 raise error.ParseError(_("rev expects a number"))
1433 raise error.ParseError(_("rev expects a number"))
1418 if l not in fullreposet(repo):
1434 if l not in fullreposet(repo):
1419 return baseset()
1435 return baseset()
1420 return subset & baseset([l])
1436 return subset & baseset([l])
1421
1437
1422 def matching(repo, subset, x):
1438 def matching(repo, subset, x):
1423 """``matching(revision [, field])``
1439 """``matching(revision [, field])``
1424 Changesets in which a given set of fields match the set of fields in the
1440 Changesets in which a given set of fields match the set of fields in the
1425 selected revision or set.
1441 selected revision or set.
1426
1442
1427 To match more than one field pass the list of fields to match separated
1443 To match more than one field pass the list of fields to match separated
1428 by spaces (e.g. ``author description``).
1444 by spaces (e.g. ``author description``).
1429
1445
1430 Valid fields are most regular revision fields and some special fields.
1446 Valid fields are most regular revision fields and some special fields.
1431
1447
1432 Regular revision fields are ``description``, ``author``, ``branch``,
1448 Regular revision fields are ``description``, ``author``, ``branch``,
1433 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1449 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1434 and ``diff``.
1450 and ``diff``.
1435 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1451 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1436 contents of the revision. Two revisions matching their ``diff`` will
1452 contents of the revision. Two revisions matching their ``diff`` will
1437 also match their ``files``.
1453 also match their ``files``.
1438
1454
1439 Special fields are ``summary`` and ``metadata``:
1455 Special fields are ``summary`` and ``metadata``:
1440 ``summary`` matches the first line of the description.
1456 ``summary`` matches the first line of the description.
1441 ``metadata`` is equivalent to matching ``description user date``
1457 ``metadata`` is equivalent to matching ``description user date``
1442 (i.e. it matches the main metadata fields).
1458 (i.e. it matches the main metadata fields).
1443
1459
1444 ``metadata`` is the default field which is used when no fields are
1460 ``metadata`` is the default field which is used when no fields are
1445 specified. You can match more than one field at a time.
1461 specified. You can match more than one field at a time.
1446 """
1462 """
1447 # i18n: "matching" is a keyword
1463 # i18n: "matching" is a keyword
1448 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1464 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1449
1465
1450 revs = getset(repo, fullreposet(repo), l[0])
1466 revs = getset(repo, fullreposet(repo), l[0])
1451
1467
1452 fieldlist = ['metadata']
1468 fieldlist = ['metadata']
1453 if len(l) > 1:
1469 if len(l) > 1:
1454 fieldlist = getstring(l[1],
1470 fieldlist = getstring(l[1],
1455 # i18n: "matching" is a keyword
1471 # i18n: "matching" is a keyword
1456 _("matching requires a string "
1472 _("matching requires a string "
1457 "as its second argument")).split()
1473 "as its second argument")).split()
1458
1474
1459 # Make sure that there are no repeated fields,
1475 # Make sure that there are no repeated fields,
1460 # expand the 'special' 'metadata' field type
1476 # expand the 'special' 'metadata' field type
1461 # and check the 'files' whenever we check the 'diff'
1477 # and check the 'files' whenever we check the 'diff'
1462 fields = []
1478 fields = []
1463 for field in fieldlist:
1479 for field in fieldlist:
1464 if field == 'metadata':
1480 if field == 'metadata':
1465 fields += ['user', 'description', 'date']
1481 fields += ['user', 'description', 'date']
1466 elif field == 'diff':
1482 elif field == 'diff':
1467 # a revision matching the diff must also match the files
1483 # a revision matching the diff must also match the files
1468 # since matching the diff is very costly, make sure to
1484 # since matching the diff is very costly, make sure to
1469 # also match the files first
1485 # also match the files first
1470 fields += ['files', 'diff']
1486 fields += ['files', 'diff']
1471 else:
1487 else:
1472 if field == 'author':
1488 if field == 'author':
1473 field = 'user'
1489 field = 'user'
1474 fields.append(field)
1490 fields.append(field)
1475 fields = set(fields)
1491 fields = set(fields)
1476 if 'summary' in fields and 'description' in fields:
1492 if 'summary' in fields and 'description' in fields:
1477 # If a revision matches its description it also matches its summary
1493 # If a revision matches its description it also matches its summary
1478 fields.discard('summary')
1494 fields.discard('summary')
1479
1495
1480 # We may want to match more than one field
1496 # We may want to match more than one field
1481 # Not all fields take the same amount of time to be matched
1497 # Not all fields take the same amount of time to be matched
1482 # Sort the selected fields in order of increasing matching cost
1498 # Sort the selected fields in order of increasing matching cost
1483 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1499 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1484 'files', 'description', 'substate', 'diff']
1500 'files', 'description', 'substate', 'diff']
1485 def fieldkeyfunc(f):
1501 def fieldkeyfunc(f):
1486 try:
1502 try:
1487 return fieldorder.index(f)
1503 return fieldorder.index(f)
1488 except ValueError:
1504 except ValueError:
1489 # assume an unknown field is very costly
1505 # assume an unknown field is very costly
1490 return len(fieldorder)
1506 return len(fieldorder)
1491 fields = list(fields)
1507 fields = list(fields)
1492 fields.sort(key=fieldkeyfunc)
1508 fields.sort(key=fieldkeyfunc)
1493
1509
1494 # Each field will be matched with its own "getfield" function
1510 # Each field will be matched with its own "getfield" function
1495 # which will be added to the getfieldfuncs array of functions
1511 # which will be added to the getfieldfuncs array of functions
1496 getfieldfuncs = []
1512 getfieldfuncs = []
1497 _funcs = {
1513 _funcs = {
1498 'user': lambda r: repo[r].user(),
1514 'user': lambda r: repo[r].user(),
1499 'branch': lambda r: repo[r].branch(),
1515 'branch': lambda r: repo[r].branch(),
1500 'date': lambda r: repo[r].date(),
1516 'date': lambda r: repo[r].date(),
1501 'description': lambda r: repo[r].description(),
1517 'description': lambda r: repo[r].description(),
1502 'files': lambda r: repo[r].files(),
1518 'files': lambda r: repo[r].files(),
1503 'parents': lambda r: repo[r].parents(),
1519 'parents': lambda r: repo[r].parents(),
1504 'phase': lambda r: repo[r].phase(),
1520 'phase': lambda r: repo[r].phase(),
1505 'substate': lambda r: repo[r].substate,
1521 'substate': lambda r: repo[r].substate,
1506 'summary': lambda r: repo[r].description().splitlines()[0],
1522 'summary': lambda r: repo[r].description().splitlines()[0],
1507 'diff': lambda r: list(repo[r].diff(git=True),)
1523 'diff': lambda r: list(repo[r].diff(git=True),)
1508 }
1524 }
1509 for info in fields:
1525 for info in fields:
1510 getfield = _funcs.get(info, None)
1526 getfield = _funcs.get(info, None)
1511 if getfield is None:
1527 if getfield is None:
1512 raise error.ParseError(
1528 raise error.ParseError(
1513 # i18n: "matching" is a keyword
1529 # i18n: "matching" is a keyword
1514 _("unexpected field name passed to matching: %s") % info)
1530 _("unexpected field name passed to matching: %s") % info)
1515 getfieldfuncs.append(getfield)
1531 getfieldfuncs.append(getfield)
1516 # convert the getfield array of functions into a "getinfo" function
1532 # convert the getfield array of functions into a "getinfo" function
1517 # which returns an array of field values (or a single value if there
1533 # which returns an array of field values (or a single value if there
1518 # is only one field to match)
1534 # is only one field to match)
1519 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1535 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1520
1536
1521 def matches(x):
1537 def matches(x):
1522 for rev in revs:
1538 for rev in revs:
1523 target = getinfo(rev)
1539 target = getinfo(rev)
1524 match = True
1540 match = True
1525 for n, f in enumerate(getfieldfuncs):
1541 for n, f in enumerate(getfieldfuncs):
1526 if target[n] != f(x):
1542 if target[n] != f(x):
1527 match = False
1543 match = False
1528 if match:
1544 if match:
1529 return True
1545 return True
1530 return False
1546 return False
1531
1547
1532 return subset.filter(matches)
1548 return subset.filter(matches)
1533
1549
1534 def reverse(repo, subset, x):
1550 def reverse(repo, subset, x):
1535 """``reverse(set)``
1551 """``reverse(set)``
1536 Reverse order of set.
1552 Reverse order of set.
1537 """
1553 """
1538 l = getset(repo, subset, x)
1554 l = getset(repo, subset, x)
1539 l.reverse()
1555 l.reverse()
1540 return l
1556 return l
1541
1557
1542 def roots(repo, subset, x):
1558 def roots(repo, subset, x):
1543 """``roots(set)``
1559 """``roots(set)``
1544 Changesets in set with no parent changeset in set.
1560 Changesets in set with no parent changeset in set.
1545 """
1561 """
1546 s = getset(repo, spanset(repo), x)
1562 s = getset(repo, spanset(repo), x)
1547 subset = baseset([r for r in s if r in subset])
1563 subset = baseset([r for r in s if r in subset])
1548 cs = _children(repo, subset, s)
1564 cs = _children(repo, subset, s)
1549 return subset - cs
1565 return subset - cs
1550
1566
1551 def secret(repo, subset, x):
1567 def secret(repo, subset, x):
1552 """``secret()``
1568 """``secret()``
1553 Changeset in secret phase."""
1569 Changeset in secret phase."""
1554 # i18n: "secret" is a keyword
1570 # i18n: "secret" is a keyword
1555 getargs(x, 0, 0, _("secret takes no arguments"))
1571 getargs(x, 0, 0, _("secret takes no arguments"))
1556 phase = repo._phasecache.phase
1572 phase = repo._phasecache.phase
1557 target = phases.secret
1573 target = phases.secret
1558 condition = lambda r: phase(repo, r) == target
1574 condition = lambda r: phase(repo, r) == target
1559 return subset.filter(condition, cache=False)
1575 return subset.filter(condition, cache=False)
1560
1576
1561 def sort(repo, subset, x):
1577 def sort(repo, subset, x):
1562 """``sort(set[, [-]key...])``
1578 """``sort(set[, [-]key...])``
1563 Sort set by keys. The default sort order is ascending, specify a key
1579 Sort set by keys. The default sort order is ascending, specify a key
1564 as ``-key`` to sort in descending order.
1580 as ``-key`` to sort in descending order.
1565
1581
1566 The keys can be:
1582 The keys can be:
1567
1583
1568 - ``rev`` for the revision number,
1584 - ``rev`` for the revision number,
1569 - ``branch`` for the branch name,
1585 - ``branch`` for the branch name,
1570 - ``desc`` for the commit message (description),
1586 - ``desc`` for the commit message (description),
1571 - ``user`` for user name (``author`` can be used as an alias),
1587 - ``user`` for user name (``author`` can be used as an alias),
1572 - ``date`` for the commit date
1588 - ``date`` for the commit date
1573 """
1589 """
1574 # i18n: "sort" is a keyword
1590 # i18n: "sort" is a keyword
1575 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1591 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1576 keys = "rev"
1592 keys = "rev"
1577 if len(l) == 2:
1593 if len(l) == 2:
1578 # i18n: "sort" is a keyword
1594 # i18n: "sort" is a keyword
1579 keys = getstring(l[1], _("sort spec must be a string"))
1595 keys = getstring(l[1], _("sort spec must be a string"))
1580
1596
1581 s = l[0]
1597 s = l[0]
1582 keys = keys.split()
1598 keys = keys.split()
1583 l = []
1599 l = []
1584 def invert(s):
1600 def invert(s):
1585 return "".join(chr(255 - ord(c)) for c in s)
1601 return "".join(chr(255 - ord(c)) for c in s)
1586 revs = getset(repo, subset, s)
1602 revs = getset(repo, subset, s)
1587 if keys == ["rev"]:
1603 if keys == ["rev"]:
1588 revs.sort()
1604 revs.sort()
1589 return revs
1605 return revs
1590 elif keys == ["-rev"]:
1606 elif keys == ["-rev"]:
1591 revs.sort(reverse=True)
1607 revs.sort(reverse=True)
1592 return revs
1608 return revs
1593 for r in revs:
1609 for r in revs:
1594 c = repo[r]
1610 c = repo[r]
1595 e = []
1611 e = []
1596 for k in keys:
1612 for k in keys:
1597 if k == 'rev':
1613 if k == 'rev':
1598 e.append(r)
1614 e.append(r)
1599 elif k == '-rev':
1615 elif k == '-rev':
1600 e.append(-r)
1616 e.append(-r)
1601 elif k == 'branch':
1617 elif k == 'branch':
1602 e.append(c.branch())
1618 e.append(c.branch())
1603 elif k == '-branch':
1619 elif k == '-branch':
1604 e.append(invert(c.branch()))
1620 e.append(invert(c.branch()))
1605 elif k == 'desc':
1621 elif k == 'desc':
1606 e.append(c.description())
1622 e.append(c.description())
1607 elif k == '-desc':
1623 elif k == '-desc':
1608 e.append(invert(c.description()))
1624 e.append(invert(c.description()))
1609 elif k in 'user author':
1625 elif k in 'user author':
1610 e.append(c.user())
1626 e.append(c.user())
1611 elif k in '-user -author':
1627 elif k in '-user -author':
1612 e.append(invert(c.user()))
1628 e.append(invert(c.user()))
1613 elif k == 'date':
1629 elif k == 'date':
1614 e.append(c.date()[0])
1630 e.append(c.date()[0])
1615 elif k == '-date':
1631 elif k == '-date':
1616 e.append(-c.date()[0])
1632 e.append(-c.date()[0])
1617 else:
1633 else:
1618 raise error.ParseError(_("unknown sort key %r") % k)
1634 raise error.ParseError(_("unknown sort key %r") % k)
1619 e.append(r)
1635 e.append(r)
1620 l.append(e)
1636 l.append(e)
1621 l.sort()
1637 l.sort()
1622 return baseset([e[-1] for e in l])
1638 return baseset([e[-1] for e in l])
1623
1639
1624 def _stringmatcher(pattern):
1640 def _stringmatcher(pattern):
1625 """
1641 """
1626 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1642 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1627 returns the matcher name, pattern, and matcher function.
1643 returns the matcher name, pattern, and matcher function.
1628 missing or unknown prefixes are treated as literal matches.
1644 missing or unknown prefixes are treated as literal matches.
1629
1645
1630 helper for tests:
1646 helper for tests:
1631 >>> def test(pattern, *tests):
1647 >>> def test(pattern, *tests):
1632 ... kind, pattern, matcher = _stringmatcher(pattern)
1648 ... kind, pattern, matcher = _stringmatcher(pattern)
1633 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1649 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1634
1650
1635 exact matching (no prefix):
1651 exact matching (no prefix):
1636 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1652 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1637 ('literal', 'abcdefg', [False, False, True])
1653 ('literal', 'abcdefg', [False, False, True])
1638
1654
1639 regex matching ('re:' prefix)
1655 regex matching ('re:' prefix)
1640 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1656 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1641 ('re', 'a.+b', [False, False, True])
1657 ('re', 'a.+b', [False, False, True])
1642
1658
1643 force exact matches ('literal:' prefix)
1659 force exact matches ('literal:' prefix)
1644 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1660 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1645 ('literal', 're:foobar', [False, True])
1661 ('literal', 're:foobar', [False, True])
1646
1662
1647 unknown prefixes are ignored and treated as literals
1663 unknown prefixes are ignored and treated as literals
1648 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1664 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1649 ('literal', 'foo:bar', [False, False, True])
1665 ('literal', 'foo:bar', [False, False, True])
1650 """
1666 """
1651 if pattern.startswith('re:'):
1667 if pattern.startswith('re:'):
1652 pattern = pattern[3:]
1668 pattern = pattern[3:]
1653 try:
1669 try:
1654 regex = re.compile(pattern)
1670 regex = re.compile(pattern)
1655 except re.error, e:
1671 except re.error, e:
1656 raise error.ParseError(_('invalid regular expression: %s')
1672 raise error.ParseError(_('invalid regular expression: %s')
1657 % e)
1673 % e)
1658 return 're', pattern, regex.search
1674 return 're', pattern, regex.search
1659 elif pattern.startswith('literal:'):
1675 elif pattern.startswith('literal:'):
1660 pattern = pattern[8:]
1676 pattern = pattern[8:]
1661 return 'literal', pattern, pattern.__eq__
1677 return 'literal', pattern, pattern.__eq__
1662
1678
1663 def _substringmatcher(pattern):
1679 def _substringmatcher(pattern):
1664 kind, pattern, matcher = _stringmatcher(pattern)
1680 kind, pattern, matcher = _stringmatcher(pattern)
1665 if kind == 'literal':
1681 if kind == 'literal':
1666 matcher = lambda s: pattern in s
1682 matcher = lambda s: pattern in s
1667 return kind, pattern, matcher
1683 return kind, pattern, matcher
1668
1684
1669 def tag(repo, subset, x):
1685 def tag(repo, subset, x):
1670 """``tag([name])``
1686 """``tag([name])``
1671 The specified tag by name, or all tagged revisions if no name is given.
1687 The specified tag by name, or all tagged revisions if no name is given.
1672
1688
1673 If `name` starts with `re:`, the remainder of the name is treated as
1689 If `name` starts with `re:`, the remainder of the name is treated as
1674 a regular expression. To match a tag that actually starts with `re:`,
1690 a regular expression. To match a tag that actually starts with `re:`,
1675 use the prefix `literal:`.
1691 use the prefix `literal:`.
1676 """
1692 """
1677 # i18n: "tag" is a keyword
1693 # i18n: "tag" is a keyword
1678 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1694 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1679 cl = repo.changelog
1695 cl = repo.changelog
1680 if args:
1696 if args:
1681 pattern = getstring(args[0],
1697 pattern = getstring(args[0],
1682 # i18n: "tag" is a keyword
1698 # i18n: "tag" is a keyword
1683 _('the argument to tag must be a string'))
1699 _('the argument to tag must be a string'))
1684 kind, pattern, matcher = _stringmatcher(pattern)
1700 kind, pattern, matcher = _stringmatcher(pattern)
1685 if kind == 'literal':
1701 if kind == 'literal':
1686 # avoid resolving all tags
1702 # avoid resolving all tags
1687 tn = repo._tagscache.tags.get(pattern, None)
1703 tn = repo._tagscache.tags.get(pattern, None)
1688 if tn is None:
1704 if tn is None:
1689 raise util.Abort(_("tag '%s' does not exist") % pattern)
1705 raise util.Abort(_("tag '%s' does not exist") % pattern)
1690 s = set([repo[tn].rev()])
1706 s = set([repo[tn].rev()])
1691 else:
1707 else:
1692 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1708 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1693 else:
1709 else:
1694 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1710 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1695 return subset & s
1711 return subset & s
1696
1712
1697 def tagged(repo, subset, x):
1713 def tagged(repo, subset, x):
1698 return tag(repo, subset, x)
1714 return tag(repo, subset, x)
1699
1715
1700 def unstable(repo, subset, x):
1716 def unstable(repo, subset, x):
1701 """``unstable()``
1717 """``unstable()``
1702 Non-obsolete changesets with obsolete ancestors.
1718 Non-obsolete changesets with obsolete ancestors.
1703 """
1719 """
1704 # i18n: "unstable" is a keyword
1720 # i18n: "unstable" is a keyword
1705 getargs(x, 0, 0, _("unstable takes no arguments"))
1721 getargs(x, 0, 0, _("unstable takes no arguments"))
1706 unstables = obsmod.getrevs(repo, 'unstable')
1722 unstables = obsmod.getrevs(repo, 'unstable')
1707 return subset & unstables
1723 return subset & unstables
1708
1724
1709
1725
1710 def user(repo, subset, x):
1726 def user(repo, subset, x):
1711 """``user(string)``
1727 """``user(string)``
1712 User name contains string. The match is case-insensitive.
1728 User name contains string. The match is case-insensitive.
1713
1729
1714 If `string` starts with `re:`, the remainder of the string is treated as
1730 If `string` starts with `re:`, the remainder of the string is treated as
1715 a regular expression. To match a user that actually contains `re:`, use
1731 a regular expression. To match a user that actually contains `re:`, use
1716 the prefix `literal:`.
1732 the prefix `literal:`.
1717 """
1733 """
1718 return author(repo, subset, x)
1734 return author(repo, subset, x)
1719
1735
1720 # for internal use
1736 # for internal use
1721 def _list(repo, subset, x):
1737 def _list(repo, subset, x):
1722 s = getstring(x, "internal error")
1738 s = getstring(x, "internal error")
1723 if not s:
1739 if not s:
1724 return baseset()
1740 return baseset()
1725 ls = [repo[r].rev() for r in s.split('\0')]
1741 ls = [repo[r].rev() for r in s.split('\0')]
1726 s = subset
1742 s = subset
1727 return baseset([r for r in ls if r in s])
1743 return baseset([r for r in ls if r in s])
1728
1744
1729 # for internal use
1745 # for internal use
1730 def _intlist(repo, subset, x):
1746 def _intlist(repo, subset, x):
1731 s = getstring(x, "internal error")
1747 s = getstring(x, "internal error")
1732 if not s:
1748 if not s:
1733 return baseset()
1749 return baseset()
1734 ls = [int(r) for r in s.split('\0')]
1750 ls = [int(r) for r in s.split('\0')]
1735 s = subset
1751 s = subset
1736 return baseset([r for r in ls if r in s])
1752 return baseset([r for r in ls if r in s])
1737
1753
1738 # for internal use
1754 # for internal use
1739 def _hexlist(repo, subset, x):
1755 def _hexlist(repo, subset, x):
1740 s = getstring(x, "internal error")
1756 s = getstring(x, "internal error")
1741 if not s:
1757 if not s:
1742 return baseset()
1758 return baseset()
1743 cl = repo.changelog
1759 cl = repo.changelog
1744 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
1760 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
1745 s = subset
1761 s = subset
1746 return baseset([r for r in ls if r in s])
1762 return baseset([r for r in ls if r in s])
1747
1763
1748 symbols = {
1764 symbols = {
1749 "adds": adds,
1765 "adds": adds,
1750 "all": getall,
1766 "all": getall,
1751 "ancestor": ancestor,
1767 "ancestor": ancestor,
1752 "ancestors": ancestors,
1768 "ancestors": ancestors,
1753 "_firstancestors": _firstancestors,
1769 "_firstancestors": _firstancestors,
1754 "author": author,
1770 "author": author,
1755 "bisect": bisect,
1771 "bisect": bisect,
1756 "bisected": bisected,
1772 "bisected": bisected,
1757 "bookmark": bookmark,
1773 "bookmark": bookmark,
1758 "branch": branch,
1774 "branch": branch,
1759 "branchpoint": branchpoint,
1775 "branchpoint": branchpoint,
1760 "bumped": bumped,
1776 "bumped": bumped,
1761 "bundle": bundle,
1777 "bundle": bundle,
1762 "children": children,
1778 "children": children,
1763 "closed": closed,
1779 "closed": closed,
1764 "contains": contains,
1780 "contains": contains,
1765 "converted": converted,
1781 "converted": converted,
1766 "date": date,
1782 "date": date,
1767 "desc": desc,
1783 "desc": desc,
1768 "descendants": descendants,
1784 "descendants": descendants,
1769 "_firstdescendants": _firstdescendants,
1785 "_firstdescendants": _firstdescendants,
1770 "destination": destination,
1786 "destination": destination,
1771 "divergent": divergent,
1787 "divergent": divergent,
1772 "draft": draft,
1788 "draft": draft,
1773 "extinct": extinct,
1789 "extinct": extinct,
1774 "extra": extra,
1790 "extra": extra,
1775 "file": hasfile,
1791 "file": hasfile,
1776 "filelog": filelog,
1792 "filelog": filelog,
1777 "first": first,
1793 "first": first,
1778 "follow": follow,
1794 "follow": follow,
1779 "_followfirst": _followfirst,
1795 "_followfirst": _followfirst,
1780 "grep": grep,
1796 "grep": grep,
1781 "head": head,
1797 "head": head,
1782 "heads": heads,
1798 "heads": heads,
1783 "hidden": hidden,
1799 "hidden": hidden,
1784 "id": node_,
1800 "id": node_,
1785 "keyword": keyword,
1801 "keyword": keyword,
1786 "last": last,
1802 "last": last,
1787 "limit": limit,
1803 "limit": limit,
1788 "_matchfiles": _matchfiles,
1804 "_matchfiles": _matchfiles,
1789 "max": maxrev,
1805 "max": maxrev,
1790 "merge": merge,
1806 "merge": merge,
1791 "min": minrev,
1807 "min": minrev,
1792 "modifies": modifies,
1808 "modifies": modifies,
1793 "obsolete": obsolete,
1809 "obsolete": obsolete,
1794 "only": only,
1810 "only": only,
1795 "origin": origin,
1811 "origin": origin,
1796 "outgoing": outgoing,
1812 "outgoing": outgoing,
1797 "p1": p1,
1813 "p1": p1,
1798 "p2": p2,
1814 "p2": p2,
1799 "parents": parents,
1815 "parents": parents,
1800 "present": present,
1816 "present": present,
1801 "public": public,
1817 "public": public,
1802 "remote": remote,
1818 "remote": remote,
1803 "removes": removes,
1819 "removes": removes,
1804 "rev": rev,
1820 "rev": rev,
1805 "reverse": reverse,
1821 "reverse": reverse,
1806 "roots": roots,
1822 "roots": roots,
1807 "sort": sort,
1823 "sort": sort,
1808 "secret": secret,
1824 "secret": secret,
1809 "matching": matching,
1825 "matching": matching,
1810 "tag": tag,
1826 "tag": tag,
1811 "tagged": tagged,
1827 "tagged": tagged,
1812 "user": user,
1828 "user": user,
1813 "unstable": unstable,
1829 "unstable": unstable,
1814 "_list": _list,
1830 "_list": _list,
1815 "_intlist": _intlist,
1831 "_intlist": _intlist,
1816 "_hexlist": _hexlist,
1832 "_hexlist": _hexlist,
1817 }
1833 }
1818
1834
1819 # symbols which can't be used for a DoS attack for any given input
1835 # symbols which can't be used for a DoS attack for any given input
1820 # (e.g. those which accept regexes as plain strings shouldn't be included)
1836 # (e.g. those which accept regexes as plain strings shouldn't be included)
1821 # functions that just return a lot of changesets (like all) don't count here
1837 # functions that just return a lot of changesets (like all) don't count here
1822 safesymbols = set([
1838 safesymbols = set([
1823 "adds",
1839 "adds",
1824 "all",
1840 "all",
1825 "ancestor",
1841 "ancestor",
1826 "ancestors",
1842 "ancestors",
1827 "_firstancestors",
1843 "_firstancestors",
1828 "author",
1844 "author",
1829 "bisect",
1845 "bisect",
1830 "bisected",
1846 "bisected",
1831 "bookmark",
1847 "bookmark",
1832 "branch",
1848 "branch",
1833 "branchpoint",
1849 "branchpoint",
1834 "bumped",
1850 "bumped",
1835 "bundle",
1851 "bundle",
1836 "children",
1852 "children",
1837 "closed",
1853 "closed",
1838 "converted",
1854 "converted",
1839 "date",
1855 "date",
1840 "desc",
1856 "desc",
1841 "descendants",
1857 "descendants",
1842 "_firstdescendants",
1858 "_firstdescendants",
1843 "destination",
1859 "destination",
1844 "divergent",
1860 "divergent",
1845 "draft",
1861 "draft",
1846 "extinct",
1862 "extinct",
1847 "extra",
1863 "extra",
1848 "file",
1864 "file",
1849 "filelog",
1865 "filelog",
1850 "first",
1866 "first",
1851 "follow",
1867 "follow",
1852 "_followfirst",
1868 "_followfirst",
1853 "head",
1869 "head",
1854 "heads",
1870 "heads",
1855 "hidden",
1871 "hidden",
1856 "id",
1872 "id",
1857 "keyword",
1873 "keyword",
1858 "last",
1874 "last",
1859 "limit",
1875 "limit",
1860 "_matchfiles",
1876 "_matchfiles",
1861 "max",
1877 "max",
1862 "merge",
1878 "merge",
1863 "min",
1879 "min",
1864 "modifies",
1880 "modifies",
1865 "obsolete",
1881 "obsolete",
1866 "only",
1882 "only",
1867 "origin",
1883 "origin",
1868 "outgoing",
1884 "outgoing",
1869 "p1",
1885 "p1",
1870 "p2",
1886 "p2",
1871 "parents",
1887 "parents",
1872 "present",
1888 "present",
1873 "public",
1889 "public",
1874 "remote",
1890 "remote",
1875 "removes",
1891 "removes",
1876 "rev",
1892 "rev",
1877 "reverse",
1893 "reverse",
1878 "roots",
1894 "roots",
1879 "sort",
1895 "sort",
1880 "secret",
1896 "secret",
1881 "matching",
1897 "matching",
1882 "tag",
1898 "tag",
1883 "tagged",
1899 "tagged",
1884 "user",
1900 "user",
1885 "unstable",
1901 "unstable",
1886 "_list",
1902 "_list",
1887 "_intlist",
1903 "_intlist",
1888 "_hexlist",
1904 "_hexlist",
1889 ])
1905 ])
1890
1906
1891 methods = {
1907 methods = {
1892 "range": rangeset,
1908 "range": rangeset,
1893 "dagrange": dagrange,
1909 "dagrange": dagrange,
1894 "string": stringset,
1910 "string": stringset,
1895 "symbol": symbolset,
1911 "symbol": symbolset,
1896 "and": andset,
1912 "and": andset,
1897 "or": orset,
1913 "or": orset,
1898 "not": notset,
1914 "not": notset,
1899 "list": listset,
1915 "list": listset,
1900 "func": func,
1916 "func": func,
1901 "ancestor": ancestorspec,
1917 "ancestor": ancestorspec,
1902 "parent": parentspec,
1918 "parent": parentspec,
1903 "parentpost": p1,
1919 "parentpost": p1,
1904 }
1920 }
1905
1921
1906 def optimize(x, small):
1922 def optimize(x, small):
1907 if x is None:
1923 if x is None:
1908 return 0, x
1924 return 0, x
1909
1925
1910 smallbonus = 1
1926 smallbonus = 1
1911 if small:
1927 if small:
1912 smallbonus = .5
1928 smallbonus = .5
1913
1929
1914 op = x[0]
1930 op = x[0]
1915 if op == 'minus':
1931 if op == 'minus':
1916 return optimize(('and', x[1], ('not', x[2])), small)
1932 return optimize(('and', x[1], ('not', x[2])), small)
1917 elif op == 'dagrangepre':
1933 elif op == 'dagrangepre':
1918 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1934 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1919 elif op == 'dagrangepost':
1935 elif op == 'dagrangepost':
1920 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1936 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1921 elif op == 'rangepre':
1937 elif op == 'rangepre':
1922 return optimize(('range', ('string', '0'), x[1]), small)
1938 return optimize(('range', ('string', '0'), x[1]), small)
1923 elif op == 'rangepost':
1939 elif op == 'rangepost':
1924 return optimize(('range', x[1], ('string', 'tip')), small)
1940 return optimize(('range', x[1], ('string', 'tip')), small)
1925 elif op == 'negate':
1941 elif op == 'negate':
1926 return optimize(('string',
1942 return optimize(('string',
1927 '-' + getstring(x[1], _("can't negate that"))), small)
1943 '-' + getstring(x[1], _("can't negate that"))), small)
1928 elif op in 'string symbol negate':
1944 elif op in 'string symbol negate':
1929 return smallbonus, x # single revisions are small
1945 return smallbonus, x # single revisions are small
1930 elif op == 'and':
1946 elif op == 'and':
1931 wa, ta = optimize(x[1], True)
1947 wa, ta = optimize(x[1], True)
1932 wb, tb = optimize(x[2], True)
1948 wb, tb = optimize(x[2], True)
1933
1949
1934 # (::x and not ::y)/(not ::y and ::x) have a fast path
1950 # (::x and not ::y)/(not ::y and ::x) have a fast path
1935 def isonly(revs, bases):
1951 def isonly(revs, bases):
1936 return (
1952 return (
1937 revs[0] == 'func'
1953 revs[0] == 'func'
1938 and getstring(revs[1], _('not a symbol')) == 'ancestors'
1954 and getstring(revs[1], _('not a symbol')) == 'ancestors'
1939 and bases[0] == 'not'
1955 and bases[0] == 'not'
1940 and bases[1][0] == 'func'
1956 and bases[1][0] == 'func'
1941 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
1957 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
1942
1958
1943 w = min(wa, wb)
1959 w = min(wa, wb)
1944 if isonly(ta, tb):
1960 if isonly(ta, tb):
1945 return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2]))
1961 return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2]))
1946 if isonly(tb, ta):
1962 if isonly(tb, ta):
1947 return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2]))
1963 return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2]))
1948
1964
1949 if wa > wb:
1965 if wa > wb:
1950 return w, (op, tb, ta)
1966 return w, (op, tb, ta)
1951 return w, (op, ta, tb)
1967 return w, (op, ta, tb)
1952 elif op == 'or':
1968 elif op == 'or':
1953 wa, ta = optimize(x[1], False)
1969 wa, ta = optimize(x[1], False)
1954 wb, tb = optimize(x[2], False)
1970 wb, tb = optimize(x[2], False)
1955 if wb < wa:
1971 if wb < wa:
1956 wb, wa = wa, wb
1972 wb, wa = wa, wb
1957 return max(wa, wb), (op, ta, tb)
1973 return max(wa, wb), (op, ta, tb)
1958 elif op == 'not':
1974 elif op == 'not':
1959 o = optimize(x[1], not small)
1975 o = optimize(x[1], not small)
1960 return o[0], (op, o[1])
1976 return o[0], (op, o[1])
1961 elif op == 'parentpost':
1977 elif op == 'parentpost':
1962 o = optimize(x[1], small)
1978 o = optimize(x[1], small)
1963 return o[0], (op, o[1])
1979 return o[0], (op, o[1])
1964 elif op == 'group':
1980 elif op == 'group':
1965 return optimize(x[1], small)
1981 return optimize(x[1], small)
1966 elif op in 'dagrange range list parent ancestorspec':
1982 elif op in 'dagrange range list parent ancestorspec':
1967 if op == 'parent':
1983 if op == 'parent':
1968 # x^:y means (x^) : y, not x ^ (:y)
1984 # x^:y means (x^) : y, not x ^ (:y)
1969 post = ('parentpost', x[1])
1985 post = ('parentpost', x[1])
1970 if x[2][0] == 'dagrangepre':
1986 if x[2][0] == 'dagrangepre':
1971 return optimize(('dagrange', post, x[2][1]), small)
1987 return optimize(('dagrange', post, x[2][1]), small)
1972 elif x[2][0] == 'rangepre':
1988 elif x[2][0] == 'rangepre':
1973 return optimize(('range', post, x[2][1]), small)
1989 return optimize(('range', post, x[2][1]), small)
1974
1990
1975 wa, ta = optimize(x[1], small)
1991 wa, ta = optimize(x[1], small)
1976 wb, tb = optimize(x[2], small)
1992 wb, tb = optimize(x[2], small)
1977 return wa + wb, (op, ta, tb)
1993 return wa + wb, (op, ta, tb)
1978 elif op == 'func':
1994 elif op == 'func':
1979 f = getstring(x[1], _("not a symbol"))
1995 f = getstring(x[1], _("not a symbol"))
1980 wa, ta = optimize(x[2], small)
1996 wa, ta = optimize(x[2], small)
1981 if f in ("author branch closed date desc file grep keyword "
1997 if f in ("author branch closed date desc file grep keyword "
1982 "outgoing user"):
1998 "outgoing user"):
1983 w = 10 # slow
1999 w = 10 # slow
1984 elif f in "modifies adds removes":
2000 elif f in "modifies adds removes":
1985 w = 30 # slower
2001 w = 30 # slower
1986 elif f == "contains":
2002 elif f == "contains":
1987 w = 100 # very slow
2003 w = 100 # very slow
1988 elif f == "ancestor":
2004 elif f == "ancestor":
1989 w = 1 * smallbonus
2005 w = 1 * smallbonus
1990 elif f in "reverse limit first _intlist":
2006 elif f in "reverse limit first _intlist":
1991 w = 0
2007 w = 0
1992 elif f in "sort":
2008 elif f in "sort":
1993 w = 10 # assume most sorts look at changelog
2009 w = 10 # assume most sorts look at changelog
1994 else:
2010 else:
1995 w = 1
2011 w = 1
1996 return w + wa, (op, x[1], ta)
2012 return w + wa, (op, x[1], ta)
1997 return 1, x
2013 return 1, x
1998
2014
1999 _aliasarg = ('func', ('symbol', '_aliasarg'))
2015 _aliasarg = ('func', ('symbol', '_aliasarg'))
2000 def _getaliasarg(tree):
2016 def _getaliasarg(tree):
2001 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
2017 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
2002 return X, None otherwise.
2018 return X, None otherwise.
2003 """
2019 """
2004 if (len(tree) == 3 and tree[:2] == _aliasarg
2020 if (len(tree) == 3 and tree[:2] == _aliasarg
2005 and tree[2][0] == 'string'):
2021 and tree[2][0] == 'string'):
2006 return tree[2][1]
2022 return tree[2][1]
2007 return None
2023 return None
2008
2024
2009 def _checkaliasarg(tree, known=None):
2025 def _checkaliasarg(tree, known=None):
2010 """Check tree contains no _aliasarg construct or only ones which
2026 """Check tree contains no _aliasarg construct or only ones which
2011 value is in known. Used to avoid alias placeholders injection.
2027 value is in known. Used to avoid alias placeholders injection.
2012 """
2028 """
2013 if isinstance(tree, tuple):
2029 if isinstance(tree, tuple):
2014 arg = _getaliasarg(tree)
2030 arg = _getaliasarg(tree)
2015 if arg is not None and (not known or arg not in known):
2031 if arg is not None and (not known or arg not in known):
2016 raise error.ParseError(_("not a function: %s") % '_aliasarg')
2032 raise error.ParseError(_("not a function: %s") % '_aliasarg')
2017 for t in tree:
2033 for t in tree:
2018 _checkaliasarg(t, known)
2034 _checkaliasarg(t, known)
2019
2035
2020 class revsetalias(object):
2036 class revsetalias(object):
2021 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
2037 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
2022 args = None
2038 args = None
2023
2039
2024 def __init__(self, name, value):
2040 def __init__(self, name, value):
2025 '''Aliases like:
2041 '''Aliases like:
2026
2042
2027 h = heads(default)
2043 h = heads(default)
2028 b($1) = ancestors($1) - ancestors(default)
2044 b($1) = ancestors($1) - ancestors(default)
2029 '''
2045 '''
2030 m = self.funcre.search(name)
2046 m = self.funcre.search(name)
2031 if m:
2047 if m:
2032 self.name = m.group(1)
2048 self.name = m.group(1)
2033 self.tree = ('func', ('symbol', m.group(1)))
2049 self.tree = ('func', ('symbol', m.group(1)))
2034 self.args = [x.strip() for x in m.group(2).split(',')]
2050 self.args = [x.strip() for x in m.group(2).split(',')]
2035 for arg in self.args:
2051 for arg in self.args:
2036 # _aliasarg() is an unknown symbol only used separate
2052 # _aliasarg() is an unknown symbol only used separate
2037 # alias argument placeholders from regular strings.
2053 # alias argument placeholders from regular strings.
2038 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
2054 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
2039 else:
2055 else:
2040 self.name = name
2056 self.name = name
2041 self.tree = ('symbol', name)
2057 self.tree = ('symbol', name)
2042
2058
2043 self.replacement, pos = parse(value)
2059 self.replacement, pos = parse(value)
2044 if pos != len(value):
2060 if pos != len(value):
2045 raise error.ParseError(_('invalid token'), pos)
2061 raise error.ParseError(_('invalid token'), pos)
2046 # Check for placeholder injection
2062 # Check for placeholder injection
2047 _checkaliasarg(self.replacement, self.args)
2063 _checkaliasarg(self.replacement, self.args)
2048
2064
2049 def _getalias(aliases, tree):
2065 def _getalias(aliases, tree):
2050 """If tree looks like an unexpanded alias, return it. Return None
2066 """If tree looks like an unexpanded alias, return it. Return None
2051 otherwise.
2067 otherwise.
2052 """
2068 """
2053 if isinstance(tree, tuple) and tree:
2069 if isinstance(tree, tuple) and tree:
2054 if tree[0] == 'symbol' and len(tree) == 2:
2070 if tree[0] == 'symbol' and len(tree) == 2:
2055 name = tree[1]
2071 name = tree[1]
2056 alias = aliases.get(name)
2072 alias = aliases.get(name)
2057 if alias and alias.args is None and alias.tree == tree:
2073 if alias and alias.args is None and alias.tree == tree:
2058 return alias
2074 return alias
2059 if tree[0] == 'func' and len(tree) > 1:
2075 if tree[0] == 'func' and len(tree) > 1:
2060 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
2076 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
2061 name = tree[1][1]
2077 name = tree[1][1]
2062 alias = aliases.get(name)
2078 alias = aliases.get(name)
2063 if alias and alias.args is not None and alias.tree == tree[:2]:
2079 if alias and alias.args is not None and alias.tree == tree[:2]:
2064 return alias
2080 return alias
2065 return None
2081 return None
2066
2082
2067 def _expandargs(tree, args):
2083 def _expandargs(tree, args):
2068 """Replace _aliasarg instances with the substitution value of the
2084 """Replace _aliasarg instances with the substitution value of the
2069 same name in args, recursively.
2085 same name in args, recursively.
2070 """
2086 """
2071 if not tree or not isinstance(tree, tuple):
2087 if not tree or not isinstance(tree, tuple):
2072 return tree
2088 return tree
2073 arg = _getaliasarg(tree)
2089 arg = _getaliasarg(tree)
2074 if arg is not None:
2090 if arg is not None:
2075 return args[arg]
2091 return args[arg]
2076 return tuple(_expandargs(t, args) for t in tree)
2092 return tuple(_expandargs(t, args) for t in tree)
2077
2093
2078 def _expandaliases(aliases, tree, expanding, cache):
2094 def _expandaliases(aliases, tree, expanding, cache):
2079 """Expand aliases in tree, recursively.
2095 """Expand aliases in tree, recursively.
2080
2096
2081 'aliases' is a dictionary mapping user defined aliases to
2097 'aliases' is a dictionary mapping user defined aliases to
2082 revsetalias objects.
2098 revsetalias objects.
2083 """
2099 """
2084 if not isinstance(tree, tuple):
2100 if not isinstance(tree, tuple):
2085 # Do not expand raw strings
2101 # Do not expand raw strings
2086 return tree
2102 return tree
2087 alias = _getalias(aliases, tree)
2103 alias = _getalias(aliases, tree)
2088 if alias is not None:
2104 if alias is not None:
2089 if alias in expanding:
2105 if alias in expanding:
2090 raise error.ParseError(_('infinite expansion of revset alias "%s" '
2106 raise error.ParseError(_('infinite expansion of revset alias "%s" '
2091 'detected') % alias.name)
2107 'detected') % alias.name)
2092 expanding.append(alias)
2108 expanding.append(alias)
2093 if alias.name not in cache:
2109 if alias.name not in cache:
2094 cache[alias.name] = _expandaliases(aliases, alias.replacement,
2110 cache[alias.name] = _expandaliases(aliases, alias.replacement,
2095 expanding, cache)
2111 expanding, cache)
2096 result = cache[alias.name]
2112 result = cache[alias.name]
2097 expanding.pop()
2113 expanding.pop()
2098 if alias.args is not None:
2114 if alias.args is not None:
2099 l = getlist(tree[2])
2115 l = getlist(tree[2])
2100 if len(l) != len(alias.args):
2116 if len(l) != len(alias.args):
2101 raise error.ParseError(
2117 raise error.ParseError(
2102 _('invalid number of arguments: %s') % len(l))
2118 _('invalid number of arguments: %s') % len(l))
2103 l = [_expandaliases(aliases, a, [], cache) for a in l]
2119 l = [_expandaliases(aliases, a, [], cache) for a in l]
2104 result = _expandargs(result, dict(zip(alias.args, l)))
2120 result = _expandargs(result, dict(zip(alias.args, l)))
2105 else:
2121 else:
2106 result = tuple(_expandaliases(aliases, t, expanding, cache)
2122 result = tuple(_expandaliases(aliases, t, expanding, cache)
2107 for t in tree)
2123 for t in tree)
2108 return result
2124 return result
2109
2125
2110 def findaliases(ui, tree):
2126 def findaliases(ui, tree):
2111 _checkaliasarg(tree)
2127 _checkaliasarg(tree)
2112 aliases = {}
2128 aliases = {}
2113 for k, v in ui.configitems('revsetalias'):
2129 for k, v in ui.configitems('revsetalias'):
2114 alias = revsetalias(k, v)
2130 alias = revsetalias(k, v)
2115 aliases[alias.name] = alias
2131 aliases[alias.name] = alias
2116 return _expandaliases(aliases, tree, [], {})
2132 return _expandaliases(aliases, tree, [], {})
2117
2133
2118 def parse(spec, lookup=None):
2134 def parse(spec, lookup=None):
2119 p = parser.parser(tokenize, elements)
2135 p = parser.parser(tokenize, elements)
2120 return p.parse(spec, lookup=lookup)
2136 return p.parse(spec, lookup=lookup)
2121
2137
2122 def match(ui, spec, repo=None):
2138 def match(ui, spec, repo=None):
2123 if not spec:
2139 if not spec:
2124 raise error.ParseError(_("empty query"))
2140 raise error.ParseError(_("empty query"))
2125 lookup = None
2141 lookup = None
2126 if repo:
2142 if repo:
2127 lookup = repo.__contains__
2143 lookup = repo.__contains__
2128 tree, pos = parse(spec, lookup)
2144 tree, pos = parse(spec, lookup)
2129 if (pos != len(spec)):
2145 if (pos != len(spec)):
2130 raise error.ParseError(_("invalid token"), pos)
2146 raise error.ParseError(_("invalid token"), pos)
2131 if ui:
2147 if ui:
2132 tree = findaliases(ui, tree)
2148 tree = findaliases(ui, tree)
2133 weight, tree = optimize(tree, True)
2149 weight, tree = optimize(tree, True)
2134 def mfunc(repo, subset):
2150 def mfunc(repo, subset):
2135 if util.safehasattr(subset, 'isascending'):
2151 if util.safehasattr(subset, 'isascending'):
2136 result = getset(repo, subset, tree)
2152 result = getset(repo, subset, tree)
2137 else:
2153 else:
2138 result = getset(repo, baseset(subset), tree)
2154 result = getset(repo, baseset(subset), tree)
2139 return result
2155 return result
2140 return mfunc
2156 return mfunc
2141
2157
2142 def formatspec(expr, *args):
2158 def formatspec(expr, *args):
2143 '''
2159 '''
2144 This is a convenience function for using revsets internally, and
2160 This is a convenience function for using revsets internally, and
2145 escapes arguments appropriately. Aliases are intentionally ignored
2161 escapes arguments appropriately. Aliases are intentionally ignored
2146 so that intended expression behavior isn't accidentally subverted.
2162 so that intended expression behavior isn't accidentally subverted.
2147
2163
2148 Supported arguments:
2164 Supported arguments:
2149
2165
2150 %r = revset expression, parenthesized
2166 %r = revset expression, parenthesized
2151 %d = int(arg), no quoting
2167 %d = int(arg), no quoting
2152 %s = string(arg), escaped and single-quoted
2168 %s = string(arg), escaped and single-quoted
2153 %b = arg.branch(), escaped and single-quoted
2169 %b = arg.branch(), escaped and single-quoted
2154 %n = hex(arg), single-quoted
2170 %n = hex(arg), single-quoted
2155 %% = a literal '%'
2171 %% = a literal '%'
2156
2172
2157 Prefixing the type with 'l' specifies a parenthesized list of that type.
2173 Prefixing the type with 'l' specifies a parenthesized list of that type.
2158
2174
2159 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2175 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2160 '(10 or 11):: and ((this()) or (that()))'
2176 '(10 or 11):: and ((this()) or (that()))'
2161 >>> formatspec('%d:: and not %d::', 10, 20)
2177 >>> formatspec('%d:: and not %d::', 10, 20)
2162 '10:: and not 20::'
2178 '10:: and not 20::'
2163 >>> formatspec('%ld or %ld', [], [1])
2179 >>> formatspec('%ld or %ld', [], [1])
2164 "_list('') or 1"
2180 "_list('') or 1"
2165 >>> formatspec('keyword(%s)', 'foo\\xe9')
2181 >>> formatspec('keyword(%s)', 'foo\\xe9')
2166 "keyword('foo\\\\xe9')"
2182 "keyword('foo\\\\xe9')"
2167 >>> b = lambda: 'default'
2183 >>> b = lambda: 'default'
2168 >>> b.branch = b
2184 >>> b.branch = b
2169 >>> formatspec('branch(%b)', b)
2185 >>> formatspec('branch(%b)', b)
2170 "branch('default')"
2186 "branch('default')"
2171 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2187 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2172 "root(_list('a\\x00b\\x00c\\x00d'))"
2188 "root(_list('a\\x00b\\x00c\\x00d'))"
2173 '''
2189 '''
2174
2190
2175 def quote(s):
2191 def quote(s):
2176 return repr(str(s))
2192 return repr(str(s))
2177
2193
2178 def argtype(c, arg):
2194 def argtype(c, arg):
2179 if c == 'd':
2195 if c == 'd':
2180 return str(int(arg))
2196 return str(int(arg))
2181 elif c == 's':
2197 elif c == 's':
2182 return quote(arg)
2198 return quote(arg)
2183 elif c == 'r':
2199 elif c == 'r':
2184 parse(arg) # make sure syntax errors are confined
2200 parse(arg) # make sure syntax errors are confined
2185 return '(%s)' % arg
2201 return '(%s)' % arg
2186 elif c == 'n':
2202 elif c == 'n':
2187 return quote(node.hex(arg))
2203 return quote(node.hex(arg))
2188 elif c == 'b':
2204 elif c == 'b':
2189 return quote(arg.branch())
2205 return quote(arg.branch())
2190
2206
2191 def listexp(s, t):
2207 def listexp(s, t):
2192 l = len(s)
2208 l = len(s)
2193 if l == 0:
2209 if l == 0:
2194 return "_list('')"
2210 return "_list('')"
2195 elif l == 1:
2211 elif l == 1:
2196 return argtype(t, s[0])
2212 return argtype(t, s[0])
2197 elif t == 'd':
2213 elif t == 'd':
2198 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2214 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2199 elif t == 's':
2215 elif t == 's':
2200 return "_list('%s')" % "\0".join(s)
2216 return "_list('%s')" % "\0".join(s)
2201 elif t == 'n':
2217 elif t == 'n':
2202 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2218 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2203 elif t == 'b':
2219 elif t == 'b':
2204 return "_list('%s')" % "\0".join(a.branch() for a in s)
2220 return "_list('%s')" % "\0".join(a.branch() for a in s)
2205
2221
2206 m = l // 2
2222 m = l // 2
2207 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2223 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2208
2224
2209 ret = ''
2225 ret = ''
2210 pos = 0
2226 pos = 0
2211 arg = 0
2227 arg = 0
2212 while pos < len(expr):
2228 while pos < len(expr):
2213 c = expr[pos]
2229 c = expr[pos]
2214 if c == '%':
2230 if c == '%':
2215 pos += 1
2231 pos += 1
2216 d = expr[pos]
2232 d = expr[pos]
2217 if d == '%':
2233 if d == '%':
2218 ret += d
2234 ret += d
2219 elif d in 'dsnbr':
2235 elif d in 'dsnbr':
2220 ret += argtype(d, args[arg])
2236 ret += argtype(d, args[arg])
2221 arg += 1
2237 arg += 1
2222 elif d == 'l':
2238 elif d == 'l':
2223 # a list of some type
2239 # a list of some type
2224 pos += 1
2240 pos += 1
2225 d = expr[pos]
2241 d = expr[pos]
2226 ret += listexp(list(args[arg]), d)
2242 ret += listexp(list(args[arg]), d)
2227 arg += 1
2243 arg += 1
2228 else:
2244 else:
2229 raise util.Abort('unexpected revspec format character %s' % d)
2245 raise util.Abort('unexpected revspec format character %s' % d)
2230 else:
2246 else:
2231 ret += c
2247 ret += c
2232 pos += 1
2248 pos += 1
2233
2249
2234 return ret
2250 return ret
2235
2251
2236 def prettyformat(tree):
2252 def prettyformat(tree):
2237 def _prettyformat(tree, level, lines):
2253 def _prettyformat(tree, level, lines):
2238 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2254 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2239 lines.append((level, str(tree)))
2255 lines.append((level, str(tree)))
2240 else:
2256 else:
2241 lines.append((level, '(%s' % tree[0]))
2257 lines.append((level, '(%s' % tree[0]))
2242 for s in tree[1:]:
2258 for s in tree[1:]:
2243 _prettyformat(s, level + 1, lines)
2259 _prettyformat(s, level + 1, lines)
2244 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2260 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
2245
2261
2246 lines = []
2262 lines = []
2247 _prettyformat(tree, 0, lines)
2263 _prettyformat(tree, 0, lines)
2248 output = '\n'.join((' '*l + s) for l, s in lines)
2264 output = '\n'.join((' '*l + s) for l, s in lines)
2249 return output
2265 return output
2250
2266
2251 def depth(tree):
2267 def depth(tree):
2252 if isinstance(tree, tuple):
2268 if isinstance(tree, tuple):
2253 return max(map(depth, tree)) + 1
2269 return max(map(depth, tree)) + 1
2254 else:
2270 else:
2255 return 0
2271 return 0
2256
2272
2257 def funcsused(tree):
2273 def funcsused(tree):
2258 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2274 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2259 return set()
2275 return set()
2260 else:
2276 else:
2261 funcs = set()
2277 funcs = set()
2262 for s in tree[1:]:
2278 for s in tree[1:]:
2263 funcs |= funcsused(s)
2279 funcs |= funcsused(s)
2264 if tree[0] == 'func':
2280 if tree[0] == 'func':
2265 funcs.add(tree[1][1])
2281 funcs.add(tree[1][1])
2266 return funcs
2282 return funcs
2267
2283
2268 class abstractsmartset(object):
2284 class abstractsmartset(object):
2269
2285
2270 def __nonzero__(self):
2286 def __nonzero__(self):
2271 """True if the smartset is not empty"""
2287 """True if the smartset is not empty"""
2272 raise NotImplementedError()
2288 raise NotImplementedError()
2273
2289
2274 def __contains__(self, rev):
2290 def __contains__(self, rev):
2275 """provide fast membership testing"""
2291 """provide fast membership testing"""
2276 raise NotImplementedError()
2292 raise NotImplementedError()
2277
2293
2278 def __iter__(self):
2294 def __iter__(self):
2279 """iterate the set in the order it is supposed to be iterated"""
2295 """iterate the set in the order it is supposed to be iterated"""
2280 raise NotImplementedError()
2296 raise NotImplementedError()
2281
2297
2282 # Attributes containing a function to perform a fast iteration in a given
2298 # Attributes containing a function to perform a fast iteration in a given
2283 # direction. A smartset can have none, one, or both defined.
2299 # direction. A smartset can have none, one, or both defined.
2284 #
2300 #
2285 # Default value is None instead of a function returning None to avoid
2301 # Default value is None instead of a function returning None to avoid
2286 # initializing an iterator just for testing if a fast method exists.
2302 # initializing an iterator just for testing if a fast method exists.
2287 fastasc = None
2303 fastasc = None
2288 fastdesc = None
2304 fastdesc = None
2289
2305
2290 def isascending(self):
2306 def isascending(self):
2291 """True if the set will iterate in ascending order"""
2307 """True if the set will iterate in ascending order"""
2292 raise NotImplementedError()
2308 raise NotImplementedError()
2293
2309
2294 def isdescending(self):
2310 def isdescending(self):
2295 """True if the set will iterate in descending order"""
2311 """True if the set will iterate in descending order"""
2296 raise NotImplementedError()
2312 raise NotImplementedError()
2297
2313
2298 def min(self):
2314 def min(self):
2299 """return the minimum element in the set"""
2315 """return the minimum element in the set"""
2300 if self.fastasc is not None:
2316 if self.fastasc is not None:
2301 for r in self.fastasc():
2317 for r in self.fastasc():
2302 return r
2318 return r
2303 raise ValueError('arg is an empty sequence')
2319 raise ValueError('arg is an empty sequence')
2304 return min(self)
2320 return min(self)
2305
2321
2306 def max(self):
2322 def max(self):
2307 """return the maximum element in the set"""
2323 """return the maximum element in the set"""
2308 if self.fastdesc is not None:
2324 if self.fastdesc is not None:
2309 for r in self.fastdesc():
2325 for r in self.fastdesc():
2310 return r
2326 return r
2311 raise ValueError('arg is an empty sequence')
2327 raise ValueError('arg is an empty sequence')
2312 return max(self)
2328 return max(self)
2313
2329
2314 def first(self):
2330 def first(self):
2315 """return the first element in the set (user iteration perspective)
2331 """return the first element in the set (user iteration perspective)
2316
2332
2317 Return None if the set is empty"""
2333 Return None if the set is empty"""
2318 raise NotImplementedError()
2334 raise NotImplementedError()
2319
2335
2320 def last(self):
2336 def last(self):
2321 """return the last element in the set (user iteration perspective)
2337 """return the last element in the set (user iteration perspective)
2322
2338
2323 Return None if the set is empty"""
2339 Return None if the set is empty"""
2324 raise NotImplementedError()
2340 raise NotImplementedError()
2325
2341
2326 def __len__(self):
2342 def __len__(self):
2327 """return the length of the smartsets
2343 """return the length of the smartsets
2328
2344
2329 This can be expensive on smartset that could be lazy otherwise."""
2345 This can be expensive on smartset that could be lazy otherwise."""
2330 raise NotImplementedError()
2346 raise NotImplementedError()
2331
2347
2332 def reverse(self):
2348 def reverse(self):
2333 """reverse the expected iteration order"""
2349 """reverse the expected iteration order"""
2334 raise NotImplementedError()
2350 raise NotImplementedError()
2335
2351
2336 def sort(self, reverse=True):
2352 def sort(self, reverse=True):
2337 """get the set to iterate in an ascending or descending order"""
2353 """get the set to iterate in an ascending or descending order"""
2338 raise NotImplementedError()
2354 raise NotImplementedError()
2339
2355
2340 def __and__(self, other):
2356 def __and__(self, other):
2341 """Returns a new object with the intersection of the two collections.
2357 """Returns a new object with the intersection of the two collections.
2342
2358
2343 This is part of the mandatory API for smartset."""
2359 This is part of the mandatory API for smartset."""
2344 return self.filter(other.__contains__, cache=False)
2360 return self.filter(other.__contains__, cache=False)
2345
2361
2346 def __add__(self, other):
2362 def __add__(self, other):
2347 """Returns a new object with the union of the two collections.
2363 """Returns a new object with the union of the two collections.
2348
2364
2349 This is part of the mandatory API for smartset."""
2365 This is part of the mandatory API for smartset."""
2350 return addset(self, other)
2366 return addset(self, other)
2351
2367
2352 def __sub__(self, other):
2368 def __sub__(self, other):
2353 """Returns a new object with the substraction of the two collections.
2369 """Returns a new object with the substraction of the two collections.
2354
2370
2355 This is part of the mandatory API for smartset."""
2371 This is part of the mandatory API for smartset."""
2356 c = other.__contains__
2372 c = other.__contains__
2357 return self.filter(lambda r: not c(r), cache=False)
2373 return self.filter(lambda r: not c(r), cache=False)
2358
2374
2359 def filter(self, condition, cache=True):
2375 def filter(self, condition, cache=True):
2360 """Returns this smartset filtered by condition as a new smartset.
2376 """Returns this smartset filtered by condition as a new smartset.
2361
2377
2362 `condition` is a callable which takes a revision number and returns a
2378 `condition` is a callable which takes a revision number and returns a
2363 boolean.
2379 boolean.
2364
2380
2365 This is part of the mandatory API for smartset."""
2381 This is part of the mandatory API for smartset."""
2366 # builtin cannot be cached. but do not needs to
2382 # builtin cannot be cached. but do not needs to
2367 if cache and util.safehasattr(condition, 'func_code'):
2383 if cache and util.safehasattr(condition, 'func_code'):
2368 condition = util.cachefunc(condition)
2384 condition = util.cachefunc(condition)
2369 return filteredset(self, condition)
2385 return filteredset(self, condition)
2370
2386
2371 class baseset(abstractsmartset):
2387 class baseset(abstractsmartset):
2372 """Basic data structure that represents a revset and contains the basic
2388 """Basic data structure that represents a revset and contains the basic
2373 operation that it should be able to perform.
2389 operation that it should be able to perform.
2374
2390
2375 Every method in this class should be implemented by any smartset class.
2391 Every method in this class should be implemented by any smartset class.
2376 """
2392 """
2377 def __init__(self, data=()):
2393 def __init__(self, data=()):
2378 if not isinstance(data, list):
2394 if not isinstance(data, list):
2379 data = list(data)
2395 data = list(data)
2380 self._list = data
2396 self._list = data
2381 self._ascending = None
2397 self._ascending = None
2382
2398
2383 @util.propertycache
2399 @util.propertycache
2384 def _set(self):
2400 def _set(self):
2385 return set(self._list)
2401 return set(self._list)
2386
2402
2387 @util.propertycache
2403 @util.propertycache
2388 def _asclist(self):
2404 def _asclist(self):
2389 asclist = self._list[:]
2405 asclist = self._list[:]
2390 asclist.sort()
2406 asclist.sort()
2391 return asclist
2407 return asclist
2392
2408
2393 def __iter__(self):
2409 def __iter__(self):
2394 if self._ascending is None:
2410 if self._ascending is None:
2395 return iter(self._list)
2411 return iter(self._list)
2396 elif self._ascending:
2412 elif self._ascending:
2397 return iter(self._asclist)
2413 return iter(self._asclist)
2398 else:
2414 else:
2399 return reversed(self._asclist)
2415 return reversed(self._asclist)
2400
2416
2401 def fastasc(self):
2417 def fastasc(self):
2402 return iter(self._asclist)
2418 return iter(self._asclist)
2403
2419
2404 def fastdesc(self):
2420 def fastdesc(self):
2405 return reversed(self._asclist)
2421 return reversed(self._asclist)
2406
2422
2407 @util.propertycache
2423 @util.propertycache
2408 def __contains__(self):
2424 def __contains__(self):
2409 return self._set.__contains__
2425 return self._set.__contains__
2410
2426
2411 def __nonzero__(self):
2427 def __nonzero__(self):
2412 return bool(self._list)
2428 return bool(self._list)
2413
2429
2414 def sort(self, reverse=False):
2430 def sort(self, reverse=False):
2415 self._ascending = not bool(reverse)
2431 self._ascending = not bool(reverse)
2416
2432
2417 def reverse(self):
2433 def reverse(self):
2418 if self._ascending is None:
2434 if self._ascending is None:
2419 self._list.reverse()
2435 self._list.reverse()
2420 else:
2436 else:
2421 self._ascending = not self._ascending
2437 self._ascending = not self._ascending
2422
2438
2423 def __len__(self):
2439 def __len__(self):
2424 return len(self._list)
2440 return len(self._list)
2425
2441
2426 def isascending(self):
2442 def isascending(self):
2427 """Returns True if the collection is ascending order, False if not.
2443 """Returns True if the collection is ascending order, False if not.
2428
2444
2429 This is part of the mandatory API for smartset."""
2445 This is part of the mandatory API for smartset."""
2430 if len(self) <= 1:
2446 if len(self) <= 1:
2431 return True
2447 return True
2432 return self._ascending is not None and self._ascending
2448 return self._ascending is not None and self._ascending
2433
2449
2434 def isdescending(self):
2450 def isdescending(self):
2435 """Returns True if the collection is descending order, False if not.
2451 """Returns True if the collection is descending order, False if not.
2436
2452
2437 This is part of the mandatory API for smartset."""
2453 This is part of the mandatory API for smartset."""
2438 if len(self) <= 1:
2454 if len(self) <= 1:
2439 return True
2455 return True
2440 return self._ascending is not None and not self._ascending
2456 return self._ascending is not None and not self._ascending
2441
2457
2442 def first(self):
2458 def first(self):
2443 if self:
2459 if self:
2444 if self._ascending is None:
2460 if self._ascending is None:
2445 return self._list[0]
2461 return self._list[0]
2446 elif self._ascending:
2462 elif self._ascending:
2447 return self._asclist[0]
2463 return self._asclist[0]
2448 else:
2464 else:
2449 return self._asclist[-1]
2465 return self._asclist[-1]
2450 return None
2466 return None
2451
2467
2452 def last(self):
2468 def last(self):
2453 if self:
2469 if self:
2454 if self._ascending is None:
2470 if self._ascending is None:
2455 return self._list[-1]
2471 return self._list[-1]
2456 elif self._ascending:
2472 elif self._ascending:
2457 return self._asclist[-1]
2473 return self._asclist[-1]
2458 else:
2474 else:
2459 return self._asclist[0]
2475 return self._asclist[0]
2460 return None
2476 return None
2461
2477
2462 class filteredset(abstractsmartset):
2478 class filteredset(abstractsmartset):
2463 """Duck type for baseset class which iterates lazily over the revisions in
2479 """Duck type for baseset class which iterates lazily over the revisions in
2464 the subset and contains a function which tests for membership in the
2480 the subset and contains a function which tests for membership in the
2465 revset
2481 revset
2466 """
2482 """
2467 def __init__(self, subset, condition=lambda x: True):
2483 def __init__(self, subset, condition=lambda x: True):
2468 """
2484 """
2469 condition: a function that decide whether a revision in the subset
2485 condition: a function that decide whether a revision in the subset
2470 belongs to the revset or not.
2486 belongs to the revset or not.
2471 """
2487 """
2472 self._subset = subset
2488 self._subset = subset
2473 self._condition = condition
2489 self._condition = condition
2474 self._cache = {}
2490 self._cache = {}
2475
2491
2476 def __contains__(self, x):
2492 def __contains__(self, x):
2477 c = self._cache
2493 c = self._cache
2478 if x not in c:
2494 if x not in c:
2479 v = c[x] = x in self._subset and self._condition(x)
2495 v = c[x] = x in self._subset and self._condition(x)
2480 return v
2496 return v
2481 return c[x]
2497 return c[x]
2482
2498
2483 def __iter__(self):
2499 def __iter__(self):
2484 return self._iterfilter(self._subset)
2500 return self._iterfilter(self._subset)
2485
2501
2486 def _iterfilter(self, it):
2502 def _iterfilter(self, it):
2487 cond = self._condition
2503 cond = self._condition
2488 for x in it:
2504 for x in it:
2489 if cond(x):
2505 if cond(x):
2490 yield x
2506 yield x
2491
2507
2492 @property
2508 @property
2493 def fastasc(self):
2509 def fastasc(self):
2494 it = self._subset.fastasc
2510 it = self._subset.fastasc
2495 if it is None:
2511 if it is None:
2496 return None
2512 return None
2497 return lambda: self._iterfilter(it())
2513 return lambda: self._iterfilter(it())
2498
2514
2499 @property
2515 @property
2500 def fastdesc(self):
2516 def fastdesc(self):
2501 it = self._subset.fastdesc
2517 it = self._subset.fastdesc
2502 if it is None:
2518 if it is None:
2503 return None
2519 return None
2504 return lambda: self._iterfilter(it())
2520 return lambda: self._iterfilter(it())
2505
2521
2506 def __nonzero__(self):
2522 def __nonzero__(self):
2507 for r in self:
2523 for r in self:
2508 return True
2524 return True
2509 return False
2525 return False
2510
2526
2511 def __len__(self):
2527 def __len__(self):
2512 # Basic implementation to be changed in future patches.
2528 # Basic implementation to be changed in future patches.
2513 l = baseset([r for r in self])
2529 l = baseset([r for r in self])
2514 return len(l)
2530 return len(l)
2515
2531
2516 def sort(self, reverse=False):
2532 def sort(self, reverse=False):
2517 self._subset.sort(reverse=reverse)
2533 self._subset.sort(reverse=reverse)
2518
2534
2519 def reverse(self):
2535 def reverse(self):
2520 self._subset.reverse()
2536 self._subset.reverse()
2521
2537
2522 def isascending(self):
2538 def isascending(self):
2523 return self._subset.isascending()
2539 return self._subset.isascending()
2524
2540
2525 def isdescending(self):
2541 def isdescending(self):
2526 return self._subset.isdescending()
2542 return self._subset.isdescending()
2527
2543
2528 def first(self):
2544 def first(self):
2529 for x in self:
2545 for x in self:
2530 return x
2546 return x
2531 return None
2547 return None
2532
2548
2533 def last(self):
2549 def last(self):
2534 it = None
2550 it = None
2535 if self._subset.isascending:
2551 if self._subset.isascending:
2536 it = self.fastdesc
2552 it = self.fastdesc
2537 elif self._subset.isdescending:
2553 elif self._subset.isdescending:
2538 it = self.fastdesc
2554 it = self.fastdesc
2539 if it is None:
2555 if it is None:
2540 # slowly consume everything. This needs improvement
2556 # slowly consume everything. This needs improvement
2541 it = lambda: reversed(list(self))
2557 it = lambda: reversed(list(self))
2542 for x in it():
2558 for x in it():
2543 return x
2559 return x
2544 return None
2560 return None
2545
2561
2546 class addset(abstractsmartset):
2562 class addset(abstractsmartset):
2547 """Represent the addition of two sets
2563 """Represent the addition of two sets
2548
2564
2549 Wrapper structure for lazily adding two structures without losing much
2565 Wrapper structure for lazily adding two structures without losing much
2550 performance on the __contains__ method
2566 performance on the __contains__ method
2551
2567
2552 If the ascending attribute is set, that means the two structures are
2568 If the ascending attribute is set, that means the two structures are
2553 ordered in either an ascending or descending way. Therefore, we can add
2569 ordered in either an ascending or descending way. Therefore, we can add
2554 them maintaining the order by iterating over both at the same time
2570 them maintaining the order by iterating over both at the same time
2555 """
2571 """
2556 def __init__(self, revs1, revs2, ascending=None):
2572 def __init__(self, revs1, revs2, ascending=None):
2557 self._r1 = revs1
2573 self._r1 = revs1
2558 self._r2 = revs2
2574 self._r2 = revs2
2559 self._iter = None
2575 self._iter = None
2560 self._ascending = ascending
2576 self._ascending = ascending
2561 self._genlist = None
2577 self._genlist = None
2562 self._asclist = None
2578 self._asclist = None
2563
2579
2564 def __len__(self):
2580 def __len__(self):
2565 return len(self._list)
2581 return len(self._list)
2566
2582
2567 def __nonzero__(self):
2583 def __nonzero__(self):
2568 return bool(self._r1) or bool(self._r2)
2584 return bool(self._r1) or bool(self._r2)
2569
2585
2570 @util.propertycache
2586 @util.propertycache
2571 def _list(self):
2587 def _list(self):
2572 if not self._genlist:
2588 if not self._genlist:
2573 self._genlist = baseset(self._iterator())
2589 self._genlist = baseset(self._iterator())
2574 return self._genlist
2590 return self._genlist
2575
2591
2576 def _iterator(self):
2592 def _iterator(self):
2577 """Iterate over both collections without repeating elements
2593 """Iterate over both collections without repeating elements
2578
2594
2579 If the ascending attribute is not set, iterate over the first one and
2595 If the ascending attribute is not set, iterate over the first one and
2580 then over the second one checking for membership on the first one so we
2596 then over the second one checking for membership on the first one so we
2581 dont yield any duplicates.
2597 dont yield any duplicates.
2582
2598
2583 If the ascending attribute is set, iterate over both collections at the
2599 If the ascending attribute is set, iterate over both collections at the
2584 same time, yielding only one value at a time in the given order.
2600 same time, yielding only one value at a time in the given order.
2585 """
2601 """
2586 if self._ascending is None:
2602 if self._ascending is None:
2587 def gen():
2603 def gen():
2588 for r in self._r1:
2604 for r in self._r1:
2589 yield r
2605 yield r
2590 inr1 = self._r1.__contains__
2606 inr1 = self._r1.__contains__
2591 for r in self._r2:
2607 for r in self._r2:
2592 if not inr1(r):
2608 if not inr1(r):
2593 yield r
2609 yield r
2594 gen = gen()
2610 gen = gen()
2595 else:
2611 else:
2596 iter1 = iter(self._r1)
2612 iter1 = iter(self._r1)
2597 iter2 = iter(self._r2)
2613 iter2 = iter(self._r2)
2598 gen = self._iterordered(self._ascending, iter1, iter2)
2614 gen = self._iterordered(self._ascending, iter1, iter2)
2599 return gen
2615 return gen
2600
2616
2601 def __iter__(self):
2617 def __iter__(self):
2602 if self._ascending is None:
2618 if self._ascending is None:
2603 if self._genlist:
2619 if self._genlist:
2604 return iter(self._genlist)
2620 return iter(self._genlist)
2605 return iter(self._iterator())
2621 return iter(self._iterator())
2606 self._trysetasclist()
2622 self._trysetasclist()
2607 if self._ascending:
2623 if self._ascending:
2608 it = self.fastasc
2624 it = self.fastasc
2609 else:
2625 else:
2610 it = self.fastdesc
2626 it = self.fastdesc
2611 if it is None:
2627 if it is None:
2612 # consume the gen and try again
2628 # consume the gen and try again
2613 self._list
2629 self._list
2614 return iter(self)
2630 return iter(self)
2615 return it()
2631 return it()
2616
2632
2617 def _trysetasclist(self):
2633 def _trysetasclist(self):
2618 """populate the _asclist attribute if possible and necessary"""
2634 """populate the _asclist attribute if possible and necessary"""
2619 if self._genlist is not None and self._asclist is None:
2635 if self._genlist is not None and self._asclist is None:
2620 self._asclist = sorted(self._genlist)
2636 self._asclist = sorted(self._genlist)
2621
2637
2622 @property
2638 @property
2623 def fastasc(self):
2639 def fastasc(self):
2624 self._trysetasclist()
2640 self._trysetasclist()
2625 if self._asclist is not None:
2641 if self._asclist is not None:
2626 return self._asclist.__iter__
2642 return self._asclist.__iter__
2627 iter1 = self._r1.fastasc
2643 iter1 = self._r1.fastasc
2628 iter2 = self._r2.fastasc
2644 iter2 = self._r2.fastasc
2629 if None in (iter1, iter2):
2645 if None in (iter1, iter2):
2630 return None
2646 return None
2631 return lambda: self._iterordered(True, iter1(), iter2())
2647 return lambda: self._iterordered(True, iter1(), iter2())
2632
2648
2633 @property
2649 @property
2634 def fastdesc(self):
2650 def fastdesc(self):
2635 self._trysetasclist()
2651 self._trysetasclist()
2636 if self._asclist is not None:
2652 if self._asclist is not None:
2637 return self._asclist.__reversed__
2653 return self._asclist.__reversed__
2638 iter1 = self._r1.fastdesc
2654 iter1 = self._r1.fastdesc
2639 iter2 = self._r2.fastdesc
2655 iter2 = self._r2.fastdesc
2640 if None in (iter1, iter2):
2656 if None in (iter1, iter2):
2641 return None
2657 return None
2642 return lambda: self._iterordered(False, iter1(), iter2())
2658 return lambda: self._iterordered(False, iter1(), iter2())
2643
2659
2644 def _iterordered(self, ascending, iter1, iter2):
2660 def _iterordered(self, ascending, iter1, iter2):
2645 """produce an ordered iteration from two iterators with the same order
2661 """produce an ordered iteration from two iterators with the same order
2646
2662
2647 The ascending is used to indicated the iteration direction.
2663 The ascending is used to indicated the iteration direction.
2648 """
2664 """
2649 choice = max
2665 choice = max
2650 if ascending:
2666 if ascending:
2651 choice = min
2667 choice = min
2652
2668
2653 val1 = None
2669 val1 = None
2654 val2 = None
2670 val2 = None
2655
2671
2656 choice = max
2672 choice = max
2657 if ascending:
2673 if ascending:
2658 choice = min
2674 choice = min
2659 try:
2675 try:
2660 # Consume both iterators in an ordered way until one is
2676 # Consume both iterators in an ordered way until one is
2661 # empty
2677 # empty
2662 while True:
2678 while True:
2663 if val1 is None:
2679 if val1 is None:
2664 val1 = iter1.next()
2680 val1 = iter1.next()
2665 if val2 is None:
2681 if val2 is None:
2666 val2 = iter2.next()
2682 val2 = iter2.next()
2667 next = choice(val1, val2)
2683 next = choice(val1, val2)
2668 yield next
2684 yield next
2669 if val1 == next:
2685 if val1 == next:
2670 val1 = None
2686 val1 = None
2671 if val2 == next:
2687 if val2 == next:
2672 val2 = None
2688 val2 = None
2673 except StopIteration:
2689 except StopIteration:
2674 # Flush any remaining values and consume the other one
2690 # Flush any remaining values and consume the other one
2675 it = iter2
2691 it = iter2
2676 if val1 is not None:
2692 if val1 is not None:
2677 yield val1
2693 yield val1
2678 it = iter1
2694 it = iter1
2679 elif val2 is not None:
2695 elif val2 is not None:
2680 # might have been equality and both are empty
2696 # might have been equality and both are empty
2681 yield val2
2697 yield val2
2682 for val in it:
2698 for val in it:
2683 yield val
2699 yield val
2684
2700
2685 def __contains__(self, x):
2701 def __contains__(self, x):
2686 return x in self._r1 or x in self._r2
2702 return x in self._r1 or x in self._r2
2687
2703
2688 def sort(self, reverse=False):
2704 def sort(self, reverse=False):
2689 """Sort the added set
2705 """Sort the added set
2690
2706
2691 For this we use the cached list with all the generated values and if we
2707 For this we use the cached list with all the generated values and if we
2692 know they are ascending or descending we can sort them in a smart way.
2708 know they are ascending or descending we can sort them in a smart way.
2693 """
2709 """
2694 self._ascending = not reverse
2710 self._ascending = not reverse
2695
2711
2696 def isascending(self):
2712 def isascending(self):
2697 return self._ascending is not None and self._ascending
2713 return self._ascending is not None and self._ascending
2698
2714
2699 def isdescending(self):
2715 def isdescending(self):
2700 return self._ascending is not None and not self._ascending
2716 return self._ascending is not None and not self._ascending
2701
2717
2702 def reverse(self):
2718 def reverse(self):
2703 if self._ascending is None:
2719 if self._ascending is None:
2704 self._list.reverse()
2720 self._list.reverse()
2705 else:
2721 else:
2706 self._ascending = not self._ascending
2722 self._ascending = not self._ascending
2707
2723
2708 def first(self):
2724 def first(self):
2709 for x in self:
2725 for x in self:
2710 return x
2726 return x
2711 return None
2727 return None
2712
2728
2713 def last(self):
2729 def last(self):
2714 self.reverse()
2730 self.reverse()
2715 val = self.first()
2731 val = self.first()
2716 self.reverse()
2732 self.reverse()
2717 return val
2733 return val
2718
2734
2719 class generatorset(abstractsmartset):
2735 class generatorset(abstractsmartset):
2720 """Wrap a generator for lazy iteration
2736 """Wrap a generator for lazy iteration
2721
2737
2722 Wrapper structure for generators that provides lazy membership and can
2738 Wrapper structure for generators that provides lazy membership and can
2723 be iterated more than once.
2739 be iterated more than once.
2724 When asked for membership it generates values until either it finds the
2740 When asked for membership it generates values until either it finds the
2725 requested one or has gone through all the elements in the generator
2741 requested one or has gone through all the elements in the generator
2726 """
2742 """
2727 def __init__(self, gen, iterasc=None):
2743 def __init__(self, gen, iterasc=None):
2728 """
2744 """
2729 gen: a generator producing the values for the generatorset.
2745 gen: a generator producing the values for the generatorset.
2730 """
2746 """
2731 self._gen = gen
2747 self._gen = gen
2732 self._asclist = None
2748 self._asclist = None
2733 self._cache = {}
2749 self._cache = {}
2734 self._genlist = []
2750 self._genlist = []
2735 self._finished = False
2751 self._finished = False
2736 self._ascending = True
2752 self._ascending = True
2737 if iterasc is not None:
2753 if iterasc is not None:
2738 if iterasc:
2754 if iterasc:
2739 self.fastasc = self._iterator
2755 self.fastasc = self._iterator
2740 self.__contains__ = self._asccontains
2756 self.__contains__ = self._asccontains
2741 else:
2757 else:
2742 self.fastdesc = self._iterator
2758 self.fastdesc = self._iterator
2743 self.__contains__ = self._desccontains
2759 self.__contains__ = self._desccontains
2744
2760
2745 def __nonzero__(self):
2761 def __nonzero__(self):
2746 for r in self:
2762 for r in self:
2747 return True
2763 return True
2748 return False
2764 return False
2749
2765
2750 def __contains__(self, x):
2766 def __contains__(self, x):
2751 if x in self._cache:
2767 if x in self._cache:
2752 return self._cache[x]
2768 return self._cache[x]
2753
2769
2754 # Use new values only, as existing values would be cached.
2770 # Use new values only, as existing values would be cached.
2755 for l in self._consumegen():
2771 for l in self._consumegen():
2756 if l == x:
2772 if l == x:
2757 return True
2773 return True
2758
2774
2759 self._cache[x] = False
2775 self._cache[x] = False
2760 return False
2776 return False
2761
2777
2762 def _asccontains(self, x):
2778 def _asccontains(self, x):
2763 """version of contains optimised for ascending generator"""
2779 """version of contains optimised for ascending generator"""
2764 if x in self._cache:
2780 if x in self._cache:
2765 return self._cache[x]
2781 return self._cache[x]
2766
2782
2767 # Use new values only, as existing values would be cached.
2783 # Use new values only, as existing values would be cached.
2768 for l in self._consumegen():
2784 for l in self._consumegen():
2769 if l == x:
2785 if l == x:
2770 return True
2786 return True
2771 if l > x:
2787 if l > x:
2772 break
2788 break
2773
2789
2774 self._cache[x] = False
2790 self._cache[x] = False
2775 return False
2791 return False
2776
2792
2777 def _desccontains(self, x):
2793 def _desccontains(self, x):
2778 """version of contains optimised for descending generator"""
2794 """version of contains optimised for descending generator"""
2779 if x in self._cache:
2795 if x in self._cache:
2780 return self._cache[x]
2796 return self._cache[x]
2781
2797
2782 # Use new values only, as existing values would be cached.
2798 # Use new values only, as existing values would be cached.
2783 for l in self._consumegen():
2799 for l in self._consumegen():
2784 if l == x:
2800 if l == x:
2785 return True
2801 return True
2786 if l < x:
2802 if l < x:
2787 break
2803 break
2788
2804
2789 self._cache[x] = False
2805 self._cache[x] = False
2790 return False
2806 return False
2791
2807
2792 def __iter__(self):
2808 def __iter__(self):
2793 if self._ascending:
2809 if self._ascending:
2794 it = self.fastasc
2810 it = self.fastasc
2795 else:
2811 else:
2796 it = self.fastdesc
2812 it = self.fastdesc
2797 if it is not None:
2813 if it is not None:
2798 return it()
2814 return it()
2799 # we need to consume the iterator
2815 # we need to consume the iterator
2800 for x in self._consumegen():
2816 for x in self._consumegen():
2801 pass
2817 pass
2802 # recall the same code
2818 # recall the same code
2803 return iter(self)
2819 return iter(self)
2804
2820
2805 def _iterator(self):
2821 def _iterator(self):
2806 if self._finished:
2822 if self._finished:
2807 return iter(self._genlist)
2823 return iter(self._genlist)
2808
2824
2809 # We have to use this complex iteration strategy to allow multiple
2825 # We have to use this complex iteration strategy to allow multiple
2810 # iterations at the same time. We need to be able to catch revision
2826 # iterations at the same time. We need to be able to catch revision
2811 # removed from _consumegen and added to genlist in another instance.
2827 # removed from _consumegen and added to genlist in another instance.
2812 #
2828 #
2813 # Getting rid of it would provide an about 15% speed up on this
2829 # Getting rid of it would provide an about 15% speed up on this
2814 # iteration.
2830 # iteration.
2815 genlist = self._genlist
2831 genlist = self._genlist
2816 nextrev = self._consumegen().next
2832 nextrev = self._consumegen().next
2817 _len = len # cache global lookup
2833 _len = len # cache global lookup
2818 def gen():
2834 def gen():
2819 i = 0
2835 i = 0
2820 while True:
2836 while True:
2821 if i < _len(genlist):
2837 if i < _len(genlist):
2822 yield genlist[i]
2838 yield genlist[i]
2823 else:
2839 else:
2824 yield nextrev()
2840 yield nextrev()
2825 i += 1
2841 i += 1
2826 return gen()
2842 return gen()
2827
2843
2828 def _consumegen(self):
2844 def _consumegen(self):
2829 cache = self._cache
2845 cache = self._cache
2830 genlist = self._genlist.append
2846 genlist = self._genlist.append
2831 for item in self._gen:
2847 for item in self._gen:
2832 cache[item] = True
2848 cache[item] = True
2833 genlist(item)
2849 genlist(item)
2834 yield item
2850 yield item
2835 if not self._finished:
2851 if not self._finished:
2836 self._finished = True
2852 self._finished = True
2837 asc = self._genlist[:]
2853 asc = self._genlist[:]
2838 asc.sort()
2854 asc.sort()
2839 self._asclist = asc
2855 self._asclist = asc
2840 self.fastasc = asc.__iter__
2856 self.fastasc = asc.__iter__
2841 self.fastdesc = asc.__reversed__
2857 self.fastdesc = asc.__reversed__
2842
2858
2843 def __len__(self):
2859 def __len__(self):
2844 for x in self._consumegen():
2860 for x in self._consumegen():
2845 pass
2861 pass
2846 return len(self._genlist)
2862 return len(self._genlist)
2847
2863
2848 def sort(self, reverse=False):
2864 def sort(self, reverse=False):
2849 self._ascending = not reverse
2865 self._ascending = not reverse
2850
2866
2851 def reverse(self):
2867 def reverse(self):
2852 self._ascending = not self._ascending
2868 self._ascending = not self._ascending
2853
2869
2854 def isascending(self):
2870 def isascending(self):
2855 return self._ascending
2871 return self._ascending
2856
2872
2857 def isdescending(self):
2873 def isdescending(self):
2858 return not self._ascending
2874 return not self._ascending
2859
2875
2860 def first(self):
2876 def first(self):
2861 if self._ascending:
2877 if self._ascending:
2862 it = self.fastasc
2878 it = self.fastasc
2863 else:
2879 else:
2864 it = self.fastdesc
2880 it = self.fastdesc
2865 if it is None:
2881 if it is None:
2866 # we need to consume all and try again
2882 # we need to consume all and try again
2867 for x in self._consumegen():
2883 for x in self._consumegen():
2868 pass
2884 pass
2869 return self.first()
2885 return self.first()
2870 if self:
2886 if self:
2871 return it().next()
2887 return it().next()
2872 return None
2888 return None
2873
2889
2874 def last(self):
2890 def last(self):
2875 if self._ascending:
2891 if self._ascending:
2876 it = self.fastdesc
2892 it = self.fastdesc
2877 else:
2893 else:
2878 it = self.fastasc
2894 it = self.fastasc
2879 if it is None:
2895 if it is None:
2880 # we need to consume all and try again
2896 # we need to consume all and try again
2881 for x in self._consumegen():
2897 for x in self._consumegen():
2882 pass
2898 pass
2883 return self.first()
2899 return self.first()
2884 if self:
2900 if self:
2885 return it().next()
2901 return it().next()
2886 return None
2902 return None
2887
2903
2888 def spanset(repo, start=None, end=None):
2904 def spanset(repo, start=None, end=None):
2889 """factory function to dispatch between fullreposet and actual spanset
2905 """factory function to dispatch between fullreposet and actual spanset
2890
2906
2891 Feel free to update all spanset call sites and kill this function at some
2907 Feel free to update all spanset call sites and kill this function at some
2892 point.
2908 point.
2893 """
2909 """
2894 if start is None and end is None:
2910 if start is None and end is None:
2895 return fullreposet(repo)
2911 return fullreposet(repo)
2896 return _spanset(repo, start, end)
2912 return _spanset(repo, start, end)
2897
2913
2898
2914
2899 class _spanset(abstractsmartset):
2915 class _spanset(abstractsmartset):
2900 """Duck type for baseset class which represents a range of revisions and
2916 """Duck type for baseset class which represents a range of revisions and
2901 can work lazily and without having all the range in memory
2917 can work lazily and without having all the range in memory
2902
2918
2903 Note that spanset(x, y) behave almost like xrange(x, y) except for two
2919 Note that spanset(x, y) behave almost like xrange(x, y) except for two
2904 notable points:
2920 notable points:
2905 - when x < y it will be automatically descending,
2921 - when x < y it will be automatically descending,
2906 - revision filtered with this repoview will be skipped.
2922 - revision filtered with this repoview will be skipped.
2907
2923
2908 """
2924 """
2909 def __init__(self, repo, start=0, end=None):
2925 def __init__(self, repo, start=0, end=None):
2910 """
2926 """
2911 start: first revision included the set
2927 start: first revision included the set
2912 (default to 0)
2928 (default to 0)
2913 end: first revision excluded (last+1)
2929 end: first revision excluded (last+1)
2914 (default to len(repo)
2930 (default to len(repo)
2915
2931
2916 Spanset will be descending if `end` < `start`.
2932 Spanset will be descending if `end` < `start`.
2917 """
2933 """
2918 if end is None:
2934 if end is None:
2919 end = len(repo)
2935 end = len(repo)
2920 self._ascending = start <= end
2936 self._ascending = start <= end
2921 if not self._ascending:
2937 if not self._ascending:
2922 start, end = end + 1, start +1
2938 start, end = end + 1, start +1
2923 self._start = start
2939 self._start = start
2924 self._end = end
2940 self._end = end
2925 self._hiddenrevs = repo.changelog.filteredrevs
2941 self._hiddenrevs = repo.changelog.filteredrevs
2926
2942
2927 def sort(self, reverse=False):
2943 def sort(self, reverse=False):
2928 self._ascending = not reverse
2944 self._ascending = not reverse
2929
2945
2930 def reverse(self):
2946 def reverse(self):
2931 self._ascending = not self._ascending
2947 self._ascending = not self._ascending
2932
2948
2933 def _iterfilter(self, iterrange):
2949 def _iterfilter(self, iterrange):
2934 s = self._hiddenrevs
2950 s = self._hiddenrevs
2935 for r in iterrange:
2951 for r in iterrange:
2936 if r not in s:
2952 if r not in s:
2937 yield r
2953 yield r
2938
2954
2939 def __iter__(self):
2955 def __iter__(self):
2940 if self._ascending:
2956 if self._ascending:
2941 return self.fastasc()
2957 return self.fastasc()
2942 else:
2958 else:
2943 return self.fastdesc()
2959 return self.fastdesc()
2944
2960
2945 def fastasc(self):
2961 def fastasc(self):
2946 iterrange = xrange(self._start, self._end)
2962 iterrange = xrange(self._start, self._end)
2947 if self._hiddenrevs:
2963 if self._hiddenrevs:
2948 return self._iterfilter(iterrange)
2964 return self._iterfilter(iterrange)
2949 return iter(iterrange)
2965 return iter(iterrange)
2950
2966
2951 def fastdesc(self):
2967 def fastdesc(self):
2952 iterrange = xrange(self._end - 1, self._start - 1, -1)
2968 iterrange = xrange(self._end - 1, self._start - 1, -1)
2953 if self._hiddenrevs:
2969 if self._hiddenrevs:
2954 return self._iterfilter(iterrange)
2970 return self._iterfilter(iterrange)
2955 return iter(iterrange)
2971 return iter(iterrange)
2956
2972
2957 def __contains__(self, rev):
2973 def __contains__(self, rev):
2958 hidden = self._hiddenrevs
2974 hidden = self._hiddenrevs
2959 return ((self._start <= rev < self._end)
2975 return ((self._start <= rev < self._end)
2960 and not (hidden and rev in hidden))
2976 and not (hidden and rev in hidden))
2961
2977
2962 def __nonzero__(self):
2978 def __nonzero__(self):
2963 for r in self:
2979 for r in self:
2964 return True
2980 return True
2965 return False
2981 return False
2966
2982
2967 def __len__(self):
2983 def __len__(self):
2968 if not self._hiddenrevs:
2984 if not self._hiddenrevs:
2969 return abs(self._end - self._start)
2985 return abs(self._end - self._start)
2970 else:
2986 else:
2971 count = 0
2987 count = 0
2972 start = self._start
2988 start = self._start
2973 end = self._end
2989 end = self._end
2974 for rev in self._hiddenrevs:
2990 for rev in self._hiddenrevs:
2975 if (end < rev <= start) or (start <= rev < end):
2991 if (end < rev <= start) or (start <= rev < end):
2976 count += 1
2992 count += 1
2977 return abs(self._end - self._start) - count
2993 return abs(self._end - self._start) - count
2978
2994
2979 def isascending(self):
2995 def isascending(self):
2980 return self._start <= self._end
2996 return self._start <= self._end
2981
2997
2982 def isdescending(self):
2998 def isdescending(self):
2983 return self._start >= self._end
2999 return self._start >= self._end
2984
3000
2985 def first(self):
3001 def first(self):
2986 if self._ascending:
3002 if self._ascending:
2987 it = self.fastasc
3003 it = self.fastasc
2988 else:
3004 else:
2989 it = self.fastdesc
3005 it = self.fastdesc
2990 for x in it():
3006 for x in it():
2991 return x
3007 return x
2992 return None
3008 return None
2993
3009
2994 def last(self):
3010 def last(self):
2995 if self._ascending:
3011 if self._ascending:
2996 it = self.fastdesc
3012 it = self.fastdesc
2997 else:
3013 else:
2998 it = self.fastasc
3014 it = self.fastasc
2999 for x in it():
3015 for x in it():
3000 return x
3016 return x
3001 return None
3017 return None
3002
3018
3003 class fullreposet(_spanset):
3019 class fullreposet(_spanset):
3004 """a set containing all revisions in the repo
3020 """a set containing all revisions in the repo
3005
3021
3006 This class exists to host special optimization.
3022 This class exists to host special optimization.
3007 """
3023 """
3008
3024
3009 def __init__(self, repo):
3025 def __init__(self, repo):
3010 super(fullreposet, self).__init__(repo)
3026 super(fullreposet, self).__init__(repo)
3011
3027
3012 def __and__(self, other):
3028 def __and__(self, other):
3013 """As self contains the whole repo, all of the other set should also be
3029 """As self contains the whole repo, all of the other set should also be
3014 in self. Therefore `self & other = other`.
3030 in self. Therefore `self & other = other`.
3015
3031
3016 This boldly assumes the other contains valid revs only.
3032 This boldly assumes the other contains valid revs only.
3017 """
3033 """
3018 # other not a smartset, make is so
3034 # other not a smartset, make is so
3019 if not util.safehasattr(other, 'isascending'):
3035 if not util.safehasattr(other, 'isascending'):
3020 # filter out hidden revision
3036 # filter out hidden revision
3021 # (this boldly assumes all smartset are pure)
3037 # (this boldly assumes all smartset are pure)
3022 #
3038 #
3023 # `other` was used with "&", let's assume this is a set like
3039 # `other` was used with "&", let's assume this is a set like
3024 # object.
3040 # object.
3025 other = baseset(other - self._hiddenrevs)
3041 other = baseset(other - self._hiddenrevs)
3026
3042
3027 if self.isascending():
3043 if self.isascending():
3028 other.sort()
3044 other.sort()
3029 else:
3045 else:
3030 other.sort(reverse)
3046 other.sort(reverse)
3031 return other
3047 return other
3032
3048
3033 # tell hggettext to extract docstrings from these functions:
3049 # tell hggettext to extract docstrings from these functions:
3034 i18nfunctions = symbols.values()
3050 i18nfunctions = symbols.values()
@@ -1,1728 +1,1746 b''
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 @ c
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
1659
1660 hg log -f from the grafted changeset
1660 hg log -f from the grafted changeset
1661 (The bootstrap should properly take the topology in account)
1661 (The bootstrap should properly take the topology in account)
1662
1662
1663 $ hg up 'desc(content3)^'
1663 $ hg up 'desc(content3)^'
1664 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1664 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1665 $ hg log -Gf a
1665 $ hg log -Gf a
1666 @ changeset: 3:15b2327059e5
1666 @ changeset: 3:15b2327059e5
1667 | user: test
1667 | user: test
1668 | date: Thu Jan 01 00:00:00 1970 +0000
1668 | date: Thu Jan 01 00:00:00 1970 +0000
1669 | summary: content2
1669 | summary: content2
1670 |
1670 |
1671 o changeset: 0:ae0a3c9f9e95
1671 o changeset: 0:ae0a3c9f9e95
1672 user: test
1672 user: test
1673 date: Thu Jan 01 00:00:00 1970 +0000
1673 date: Thu Jan 01 00:00:00 1970 +0000
1674 summary: content1
1674 summary: content1
1675
1675
1676
1676
1677 Test that we use the first non-hidden changeset in that case.
1677 Test that we use the first non-hidden changeset in that case.
1678
1678
1679 (hide the changeset)
1679 (hide the changeset)
1680
1680
1681 $ hg log -T '{node}\n' -r 1
1681 $ hg log -T '{node}\n' -r 1
1682 2294ae80ad8447bc78383182eeac50cb049df623
1682 2294ae80ad8447bc78383182eeac50cb049df623
1683 $ hg debugobsolete 2294ae80ad8447bc78383182eeac50cb049df623
1683 $ hg debugobsolete 2294ae80ad8447bc78383182eeac50cb049df623
1684 $ hg log -G
1684 $ hg log -G
1685 o changeset: 4:50b9b36e9c5d
1685 o changeset: 4:50b9b36e9c5d
1686 | tag: tip
1686 | tag: tip
1687 | user: test
1687 | user: test
1688 | date: Thu Jan 01 00:00:00 1970 +0000
1688 | date: Thu Jan 01 00:00:00 1970 +0000
1689 | summary: content3
1689 | summary: content3
1690 |
1690 |
1691 @ changeset: 3:15b2327059e5
1691 @ changeset: 3:15b2327059e5
1692 | user: test
1692 | user: test
1693 | date: Thu Jan 01 00:00:00 1970 +0000
1693 | date: Thu Jan 01 00:00:00 1970 +0000
1694 | summary: content2
1694 | summary: content2
1695 |
1695 |
1696 o changeset: 2:2029acd1168c
1696 o changeset: 2:2029acd1168c
1697 | parent: 0:ae0a3c9f9e95
1697 | parent: 0:ae0a3c9f9e95
1698 | user: test
1698 | user: test
1699 | date: Thu Jan 01 00:00:00 1970 +0000
1699 | date: Thu Jan 01 00:00:00 1970 +0000
1700 | summary: unrelated
1700 | summary: unrelated
1701 |
1701 |
1702 o changeset: 0:ae0a3c9f9e95
1702 o changeset: 0:ae0a3c9f9e95
1703 user: test
1703 user: test
1704 date: Thu Jan 01 00:00:00 1970 +0000
1704 date: Thu Jan 01 00:00:00 1970 +0000
1705 summary: content1
1705 summary: content1
1706
1706
1707
1707
1708 Check that log on the file does not drop the file revision.
1708 Check that log on the file does not drop the file revision.
1709
1709
1710 $ hg log -G a
1710 $ hg log -G a
1711 o changeset: 4:50b9b36e9c5d
1711 o changeset: 4:50b9b36e9c5d
1712 | tag: tip
1712 | tag: tip
1713 | user: test
1713 | user: test
1714 | date: Thu Jan 01 00:00:00 1970 +0000
1714 | date: Thu Jan 01 00:00:00 1970 +0000
1715 | summary: content3
1715 | summary: content3
1716 |
1716 |
1717 @ changeset: 3:15b2327059e5
1717 @ changeset: 3:15b2327059e5
1718 | user: test
1718 | user: test
1719 | date: Thu Jan 01 00:00:00 1970 +0000
1719 | date: Thu Jan 01 00:00:00 1970 +0000
1720 | summary: content2
1720 | summary: content2
1721 |
1721 |
1722 o changeset: 0:ae0a3c9f9e95
1722 o changeset: 0:ae0a3c9f9e95
1723 user: test
1723 user: test
1724 date: Thu Jan 01 00:00:00 1970 +0000
1724 date: Thu Jan 01 00:00:00 1970 +0000
1725 summary: content1
1725 summary: content1
1726
1726
1727
1727
1728 Even when a head revision is linkrev-shadowed.
1729
1730 $ hg log -T '{node}\n' -r 4
1731 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
1732 $ hg debugobsolete 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
1733 $ hg log -G a
1734 @ changeset: 3:15b2327059e5
1735 | tag: tip
1736 | user: test
1737 | date: Thu Jan 01 00:00:00 1970 +0000
1738 | summary: content2
1739 |
1740 o changeset: 0:ae0a3c9f9e95
1741 user: test
1742 date: Thu Jan 01 00:00:00 1970 +0000
1743 summary: content1
1744
1745
1728 $ cd ..
1746 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now