##// END OF EJS Templates
revset: rename bisected() to bisect()...
"Yann E. MORIN" -
r15134:81adf777 default
parent child Browse files
Show More
@@ -1,1098 +1,1104
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, node
9 import parser, util, error, discovery, hbisect, node
10 import bookmarks as bookmarksmod
10 import bookmarks as bookmarksmod
11 import match as matchmod
11 import match as matchmod
12 from i18n import _
12 from i18n import _
13
13
14 elements = {
14 elements = {
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
16 "~": (18, None, ("ancestor", 18)),
16 "~": (18, None, ("ancestor", 18)),
17 "^": (18, None, ("parent", 18), ("parentpost", 18)),
17 "^": (18, None, ("parent", 18), ("parentpost", 18)),
18 "-": (5, ("negate", 19), ("minus", 5)),
18 "-": (5, ("negate", 19), ("minus", 5)),
19 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
19 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
20 ("dagrangepost", 17)),
20 ("dagrangepost", 17)),
21 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
21 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
22 ("dagrangepost", 17)),
22 ("dagrangepost", 17)),
23 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
23 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
24 "not": (10, ("not", 10)),
24 "not": (10, ("not", 10)),
25 "!": (10, ("not", 10)),
25 "!": (10, ("not", 10)),
26 "and": (5, None, ("and", 5)),
26 "and": (5, None, ("and", 5)),
27 "&": (5, None, ("and", 5)),
27 "&": (5, None, ("and", 5)),
28 "or": (4, None, ("or", 4)),
28 "or": (4, None, ("or", 4)),
29 "|": (4, None, ("or", 4)),
29 "|": (4, None, ("or", 4)),
30 "+": (4, None, ("or", 4)),
30 "+": (4, None, ("or", 4)),
31 ",": (2, None, ("list", 2)),
31 ",": (2, None, ("list", 2)),
32 ")": (0, None, None),
32 ")": (0, None, None),
33 "symbol": (0, ("symbol",), None),
33 "symbol": (0, ("symbol",), None),
34 "string": (0, ("string",), None),
34 "string": (0, ("string",), None),
35 "end": (0, None, None),
35 "end": (0, None, None),
36 }
36 }
37
37
38 keywords = set(['and', 'or', 'not'])
38 keywords = set(['and', 'or', 'not'])
39
39
40 def tokenize(program):
40 def tokenize(program):
41 pos, l = 0, len(program)
41 pos, l = 0, len(program)
42 while pos < l:
42 while pos < l:
43 c = program[pos]
43 c = program[pos]
44 if c.isspace(): # skip inter-token whitespace
44 if c.isspace(): # skip inter-token whitespace
45 pass
45 pass
46 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
46 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
47 yield ('::', None, pos)
47 yield ('::', None, pos)
48 pos += 1 # skip ahead
48 pos += 1 # skip ahead
49 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
49 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
50 yield ('..', None, pos)
50 yield ('..', None, pos)
51 pos += 1 # skip ahead
51 pos += 1 # skip ahead
52 elif c in "():,-|&+!~^": # handle simple operators
52 elif c in "():,-|&+!~^": # handle simple operators
53 yield (c, None, pos)
53 yield (c, None, pos)
54 elif (c in '"\'' or c == 'r' and
54 elif (c in '"\'' or c == 'r' and
55 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
55 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
56 if c == 'r':
56 if c == 'r':
57 pos += 1
57 pos += 1
58 c = program[pos]
58 c = program[pos]
59 decode = lambda x: x
59 decode = lambda x: x
60 else:
60 else:
61 decode = lambda x: x.decode('string-escape')
61 decode = lambda x: x.decode('string-escape')
62 pos += 1
62 pos += 1
63 s = pos
63 s = pos
64 while pos < l: # find closing quote
64 while pos < l: # find closing quote
65 d = program[pos]
65 d = program[pos]
66 if d == '\\': # skip over escaped characters
66 if d == '\\': # skip over escaped characters
67 pos += 2
67 pos += 2
68 continue
68 continue
69 if d == c:
69 if d == c:
70 yield ('string', decode(program[s:pos]), s)
70 yield ('string', decode(program[s:pos]), s)
71 break
71 break
72 pos += 1
72 pos += 1
73 else:
73 else:
74 raise error.ParseError(_("unterminated string"), s)
74 raise error.ParseError(_("unterminated string"), s)
75 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
75 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
76 s = pos
76 s = pos
77 pos += 1
77 pos += 1
78 while pos < l: # find end of symbol
78 while pos < l: # find end of symbol
79 d = program[pos]
79 d = program[pos]
80 if not (d.isalnum() or d in "._" or ord(d) > 127):
80 if not (d.isalnum() or d in "._" or ord(d) > 127):
81 break
81 break
82 if d == '.' and program[pos - 1] == '.': # special case for ..
82 if d == '.' and program[pos - 1] == '.': # special case for ..
83 pos -= 1
83 pos -= 1
84 break
84 break
85 pos += 1
85 pos += 1
86 sym = program[s:pos]
86 sym = program[s:pos]
87 if sym in keywords: # operator keywords
87 if sym in keywords: # operator keywords
88 yield (sym, None, s)
88 yield (sym, None, s)
89 else:
89 else:
90 yield ('symbol', sym, s)
90 yield ('symbol', sym, s)
91 pos -= 1
91 pos -= 1
92 else:
92 else:
93 raise error.ParseError(_("syntax error"), pos)
93 raise error.ParseError(_("syntax error"), pos)
94 pos += 1
94 pos += 1
95 yield ('end', None, pos)
95 yield ('end', None, pos)
96
96
97 # helpers
97 # helpers
98
98
99 def getstring(x, err):
99 def getstring(x, err):
100 if x and (x[0] == 'string' or x[0] == 'symbol'):
100 if x and (x[0] == 'string' or x[0] == 'symbol'):
101 return x[1]
101 return x[1]
102 raise error.ParseError(err)
102 raise error.ParseError(err)
103
103
104 def getlist(x):
104 def getlist(x):
105 if not x:
105 if not x:
106 return []
106 return []
107 if x[0] == 'list':
107 if x[0] == 'list':
108 return getlist(x[1]) + [x[2]]
108 return getlist(x[1]) + [x[2]]
109 return [x]
109 return [x]
110
110
111 def getargs(x, min, max, err):
111 def getargs(x, min, max, err):
112 l = getlist(x)
112 l = getlist(x)
113 if len(l) < min or len(l) > max:
113 if len(l) < min or len(l) > max:
114 raise error.ParseError(err)
114 raise error.ParseError(err)
115 return l
115 return l
116
116
117 def getset(repo, subset, x):
117 def getset(repo, subset, x):
118 if not x:
118 if not x:
119 raise error.ParseError(_("missing argument"))
119 raise error.ParseError(_("missing argument"))
120 return methods[x[0]](repo, subset, *x[1:])
120 return methods[x[0]](repo, subset, *x[1:])
121
121
122 # operator methods
122 # operator methods
123
123
124 def stringset(repo, subset, x):
124 def stringset(repo, subset, x):
125 x = repo[x].rev()
125 x = repo[x].rev()
126 if x == -1 and len(subset) == len(repo):
126 if x == -1 and len(subset) == len(repo):
127 return [-1]
127 return [-1]
128 if len(subset) == len(repo) or x in subset:
128 if len(subset) == len(repo) or x in subset:
129 return [x]
129 return [x]
130 return []
130 return []
131
131
132 def symbolset(repo, subset, x):
132 def symbolset(repo, subset, x):
133 if x in symbols:
133 if x in symbols:
134 raise error.ParseError(_("can't use %s here") % x)
134 raise error.ParseError(_("can't use %s here") % x)
135 return stringset(repo, subset, x)
135 return stringset(repo, subset, x)
136
136
137 def rangeset(repo, subset, x, y):
137 def rangeset(repo, subset, x, y):
138 m = getset(repo, subset, x)
138 m = getset(repo, subset, x)
139 if not m:
139 if not m:
140 m = getset(repo, range(len(repo)), x)
140 m = getset(repo, range(len(repo)), x)
141
141
142 n = getset(repo, subset, y)
142 n = getset(repo, subset, y)
143 if not n:
143 if not n:
144 n = getset(repo, range(len(repo)), y)
144 n = getset(repo, range(len(repo)), y)
145
145
146 if not m or not n:
146 if not m or not n:
147 return []
147 return []
148 m, n = m[0], n[-1]
148 m, n = m[0], n[-1]
149
149
150 if m < n:
150 if m < n:
151 r = range(m, n + 1)
151 r = range(m, n + 1)
152 else:
152 else:
153 r = range(m, n - 1, -1)
153 r = range(m, n - 1, -1)
154 s = set(subset)
154 s = set(subset)
155 return [x for x in r if x in s]
155 return [x for x in r if x in s]
156
156
157 def andset(repo, subset, x, y):
157 def andset(repo, subset, x, y):
158 return getset(repo, getset(repo, subset, x), y)
158 return getset(repo, getset(repo, subset, x), y)
159
159
160 def orset(repo, subset, x, y):
160 def orset(repo, subset, x, y):
161 xl = getset(repo, subset, x)
161 xl = getset(repo, subset, x)
162 s = set(xl)
162 s = set(xl)
163 yl = getset(repo, [r for r in subset if r not in s], y)
163 yl = getset(repo, [r for r in subset if r not in s], y)
164 return xl + yl
164 return xl + yl
165
165
166 def notset(repo, subset, x):
166 def notset(repo, subset, x):
167 s = set(getset(repo, subset, x))
167 s = set(getset(repo, subset, x))
168 return [r for r in subset if r not in s]
168 return [r for r in subset if r not in s]
169
169
170 def listset(repo, subset, a, b):
170 def listset(repo, subset, a, b):
171 raise error.ParseError(_("can't use a list in this context"))
171 raise error.ParseError(_("can't use a list in this context"))
172
172
173 def func(repo, subset, a, b):
173 def func(repo, subset, a, b):
174 if a[0] == 'symbol' and a[1] in symbols:
174 if a[0] == 'symbol' and a[1] in symbols:
175 return symbols[a[1]](repo, subset, b)
175 return symbols[a[1]](repo, subset, b)
176 raise error.ParseError(_("not a function: %s") % a[1])
176 raise error.ParseError(_("not a function: %s") % a[1])
177
177
178 # functions
178 # functions
179
179
180 def adds(repo, subset, x):
180 def adds(repo, subset, x):
181 """``adds(pattern)``
181 """``adds(pattern)``
182 Changesets that add a file matching pattern.
182 Changesets that add a file matching pattern.
183 """
183 """
184 # i18n: "adds" is a keyword
184 # i18n: "adds" is a keyword
185 pat = getstring(x, _("adds requires a pattern"))
185 pat = getstring(x, _("adds requires a pattern"))
186 return checkstatus(repo, subset, pat, 1)
186 return checkstatus(repo, subset, pat, 1)
187
187
188 def ancestor(repo, subset, x):
188 def ancestor(repo, subset, x):
189 """``ancestor(single, single)``
189 """``ancestor(single, single)``
190 Greatest common ancestor of the two changesets.
190 Greatest common ancestor of the two changesets.
191 """
191 """
192 # i18n: "ancestor" is a keyword
192 # i18n: "ancestor" is a keyword
193 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
193 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
194 r = range(len(repo))
194 r = range(len(repo))
195 a = getset(repo, r, l[0])
195 a = getset(repo, r, l[0])
196 b = getset(repo, r, l[1])
196 b = getset(repo, r, l[1])
197 if len(a) != 1 or len(b) != 1:
197 if len(a) != 1 or len(b) != 1:
198 # i18n: "ancestor" is a keyword
198 # i18n: "ancestor" is a keyword
199 raise error.ParseError(_("ancestor arguments must be single revisions"))
199 raise error.ParseError(_("ancestor arguments must be single revisions"))
200 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
200 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
201
201
202 return [r for r in an if r in subset]
202 return [r for r in an if r in subset]
203
203
204 def ancestors(repo, subset, x):
204 def ancestors(repo, subset, x):
205 """``ancestors(set)``
205 """``ancestors(set)``
206 Changesets that are ancestors of a changeset in set.
206 Changesets that are ancestors of a changeset in set.
207 """
207 """
208 args = getset(repo, range(len(repo)), x)
208 args = getset(repo, range(len(repo)), x)
209 if not args:
209 if not args:
210 return []
210 return []
211 s = set(repo.changelog.ancestors(*args)) | set(args)
211 s = set(repo.changelog.ancestors(*args)) | set(args)
212 return [r for r in subset if r in s]
212 return [r for r in subset if r in s]
213
213
214 def ancestorspec(repo, subset, x, n):
214 def ancestorspec(repo, subset, x, n):
215 """``set~n``
215 """``set~n``
216 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
216 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
217 """
217 """
218 try:
218 try:
219 n = int(n[1])
219 n = int(n[1])
220 except (TypeError, ValueError):
220 except (TypeError, ValueError):
221 raise error.ParseError(_("~ expects a number"))
221 raise error.ParseError(_("~ expects a number"))
222 ps = set()
222 ps = set()
223 cl = repo.changelog
223 cl = repo.changelog
224 for r in getset(repo, subset, x):
224 for r in getset(repo, subset, x):
225 for i in range(n):
225 for i in range(n):
226 r = cl.parentrevs(r)[0]
226 r = cl.parentrevs(r)[0]
227 ps.add(r)
227 ps.add(r)
228 return [r for r in subset if r in ps]
228 return [r for r in subset if r in ps]
229
229
230 def author(repo, subset, x):
230 def author(repo, subset, x):
231 """``author(string)``
231 """``author(string)``
232 Alias for ``user(string)``.
232 Alias for ``user(string)``.
233 """
233 """
234 # i18n: "author" is a keyword
234 # i18n: "author" is a keyword
235 n = getstring(x, _("author requires a string")).lower()
235 n = getstring(x, _("author requires a string")).lower()
236 return [r for r in subset if n in repo[r].user().lower()]
236 return [r for r in subset if n in repo[r].user().lower()]
237
237
238 def bisected(repo, subset, x):
238 def bisect(repo, subset, x):
239 """``bisected(string)``
239 """``bisect(string)``
240 Changesets marked in the specified bisect state (good, bad, skip).
240 Changesets marked in the specified bisect state (good, bad, skip).
241 """
241 """
242 state = getstring(x, _("bisect requires a string")).lower()
242 state = getstring(x, _("bisect requires a string")).lower()
243 if state not in ('good', 'bad', 'skip'):
243 if state not in ('good', 'bad', 'skip'):
244 raise error.ParseError(_('invalid bisect state'))
244 raise error.ParseError(_('invalid bisect state'))
245 marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
245 marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
246 return [r for r in subset if r in marked]
246 return [r for r in subset if r in marked]
247
247
248 # Backward-compatibility
249 # - no help entry so that we do not advertise it any more
250 def bisected(repo, subset, x):
251 return bisect(repo, subset, x)
252
248 def bookmark(repo, subset, x):
253 def bookmark(repo, subset, x):
249 """``bookmark([name])``
254 """``bookmark([name])``
250 The named bookmark or all bookmarks.
255 The named bookmark or all bookmarks.
251 """
256 """
252 # i18n: "bookmark" is a keyword
257 # i18n: "bookmark" is a keyword
253 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
258 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
254 if args:
259 if args:
255 bm = getstring(args[0],
260 bm = getstring(args[0],
256 # i18n: "bookmark" is a keyword
261 # i18n: "bookmark" is a keyword
257 _('the argument to bookmark must be a string'))
262 _('the argument to bookmark must be a string'))
258 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
263 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
259 if not bmrev:
264 if not bmrev:
260 raise util.Abort(_("bookmark '%s' does not exist") % bm)
265 raise util.Abort(_("bookmark '%s' does not exist") % bm)
261 bmrev = repo[bmrev].rev()
266 bmrev = repo[bmrev].rev()
262 return [r for r in subset if r == bmrev]
267 return [r for r in subset if r == bmrev]
263 bms = set([repo[r].rev()
268 bms = set([repo[r].rev()
264 for r in bookmarksmod.listbookmarks(repo).values()])
269 for r in bookmarksmod.listbookmarks(repo).values()])
265 return [r for r in subset if r in bms]
270 return [r for r in subset if r in bms]
266
271
267 def branch(repo, subset, x):
272 def branch(repo, subset, x):
268 """``branch(string or set)``
273 """``branch(string or set)``
269 All changesets belonging to the given branch or the branches of the given
274 All changesets belonging to the given branch or the branches of the given
270 changesets.
275 changesets.
271 """
276 """
272 try:
277 try:
273 b = getstring(x, '')
278 b = getstring(x, '')
274 if b in repo.branchmap():
279 if b in repo.branchmap():
275 return [r for r in subset if repo[r].branch() == b]
280 return [r for r in subset if repo[r].branch() == b]
276 except error.ParseError:
281 except error.ParseError:
277 # not a string, but another revspec, e.g. tip()
282 # not a string, but another revspec, e.g. tip()
278 pass
283 pass
279
284
280 s = getset(repo, range(len(repo)), x)
285 s = getset(repo, range(len(repo)), x)
281 b = set()
286 b = set()
282 for r in s:
287 for r in s:
283 b.add(repo[r].branch())
288 b.add(repo[r].branch())
284 s = set(s)
289 s = set(s)
285 return [r for r in subset if r in s or repo[r].branch() in b]
290 return [r for r in subset if r in s or repo[r].branch() in b]
286
291
287 def checkstatus(repo, subset, pat, field):
292 def checkstatus(repo, subset, pat, field):
288 m = matchmod.match(repo.root, repo.getcwd(), [pat])
293 m = matchmod.match(repo.root, repo.getcwd(), [pat])
289 s = []
294 s = []
290 fast = (m.files() == [pat])
295 fast = (m.files() == [pat])
291 for r in subset:
296 for r in subset:
292 c = repo[r]
297 c = repo[r]
293 if fast:
298 if fast:
294 if pat not in c.files():
299 if pat not in c.files():
295 continue
300 continue
296 else:
301 else:
297 for f in c.files():
302 for f in c.files():
298 if m(f):
303 if m(f):
299 break
304 break
300 else:
305 else:
301 continue
306 continue
302 files = repo.status(c.p1().node(), c.node())[field]
307 files = repo.status(c.p1().node(), c.node())[field]
303 if fast:
308 if fast:
304 if pat in files:
309 if pat in files:
305 s.append(r)
310 s.append(r)
306 else:
311 else:
307 for f in files:
312 for f in files:
308 if m(f):
313 if m(f):
309 s.append(r)
314 s.append(r)
310 break
315 break
311 return s
316 return s
312
317
313 def children(repo, subset, x):
318 def children(repo, subset, x):
314 """``children(set)``
319 """``children(set)``
315 Child changesets of changesets in set.
320 Child changesets of changesets in set.
316 """
321 """
317 cs = set()
322 cs = set()
318 cl = repo.changelog
323 cl = repo.changelog
319 s = set(getset(repo, range(len(repo)), x))
324 s = set(getset(repo, range(len(repo)), x))
320 for r in xrange(0, len(repo)):
325 for r in xrange(0, len(repo)):
321 for p in cl.parentrevs(r):
326 for p in cl.parentrevs(r):
322 if p in s:
327 if p in s:
323 cs.add(r)
328 cs.add(r)
324 return [r for r in subset if r in cs]
329 return [r for r in subset if r in cs]
325
330
326 def closed(repo, subset, x):
331 def closed(repo, subset, x):
327 """``closed()``
332 """``closed()``
328 Changeset is closed.
333 Changeset is closed.
329 """
334 """
330 # i18n: "closed" is a keyword
335 # i18n: "closed" is a keyword
331 getargs(x, 0, 0, _("closed takes no arguments"))
336 getargs(x, 0, 0, _("closed takes no arguments"))
332 return [r for r in subset if repo[r].extra().get('close')]
337 return [r for r in subset if repo[r].extra().get('close')]
333
338
334 def contains(repo, subset, x):
339 def contains(repo, subset, x):
335 """``contains(pattern)``
340 """``contains(pattern)``
336 Revision contains a file matching pattern. See :hg:`help patterns`
341 Revision contains a file matching pattern. See :hg:`help patterns`
337 for information about file patterns.
342 for information about file patterns.
338 """
343 """
339 # i18n: "contains" is a keyword
344 # i18n: "contains" is a keyword
340 pat = getstring(x, _("contains requires a pattern"))
345 pat = getstring(x, _("contains requires a pattern"))
341 m = matchmod.match(repo.root, repo.getcwd(), [pat])
346 m = matchmod.match(repo.root, repo.getcwd(), [pat])
342 s = []
347 s = []
343 if m.files() == [pat]:
348 if m.files() == [pat]:
344 for r in subset:
349 for r in subset:
345 if pat in repo[r]:
350 if pat in repo[r]:
346 s.append(r)
351 s.append(r)
347 else:
352 else:
348 for r in subset:
353 for r in subset:
349 for f in repo[r].manifest():
354 for f in repo[r].manifest():
350 if m(f):
355 if m(f):
351 s.append(r)
356 s.append(r)
352 break
357 break
353 return s
358 return s
354
359
355 def date(repo, subset, x):
360 def date(repo, subset, x):
356 """``date(interval)``
361 """``date(interval)``
357 Changesets within the interval, see :hg:`help dates`.
362 Changesets within the interval, see :hg:`help dates`.
358 """
363 """
359 # i18n: "date" is a keyword
364 # i18n: "date" is a keyword
360 ds = getstring(x, _("date requires a string"))
365 ds = getstring(x, _("date requires a string"))
361 dm = util.matchdate(ds)
366 dm = util.matchdate(ds)
362 return [r for r in subset if dm(repo[r].date()[0])]
367 return [r for r in subset if dm(repo[r].date()[0])]
363
368
364 def desc(repo, subset, x):
369 def desc(repo, subset, x):
365 """``desc(string)``
370 """``desc(string)``
366 Search commit message for string. The match is case-insensitive.
371 Search commit message for string. The match is case-insensitive.
367 """
372 """
368 # i18n: "desc" is a keyword
373 # i18n: "desc" is a keyword
369 ds = getstring(x, _("desc requires a string")).lower()
374 ds = getstring(x, _("desc requires a string")).lower()
370 l = []
375 l = []
371 for r in subset:
376 for r in subset:
372 c = repo[r]
377 c = repo[r]
373 if ds in c.description().lower():
378 if ds in c.description().lower():
374 l.append(r)
379 l.append(r)
375 return l
380 return l
376
381
377 def descendants(repo, subset, x):
382 def descendants(repo, subset, x):
378 """``descendants(set)``
383 """``descendants(set)``
379 Changesets which are descendants of changesets in set.
384 Changesets which are descendants of changesets in set.
380 """
385 """
381 args = getset(repo, range(len(repo)), x)
386 args = getset(repo, range(len(repo)), x)
382 if not args:
387 if not args:
383 return []
388 return []
384 s = set(repo.changelog.descendants(*args)) | set(args)
389 s = set(repo.changelog.descendants(*args)) | set(args)
385 return [r for r in subset if r in s]
390 return [r for r in subset if r in s]
386
391
387 def filelog(repo, subset, x):
392 def filelog(repo, subset, x):
388 """``filelog(pattern)``
393 """``filelog(pattern)``
389 Changesets connected to the specified filelog.
394 Changesets connected to the specified filelog.
390 """
395 """
391
396
392 pat = getstring(x, _("filelog requires a pattern"))
397 pat = getstring(x, _("filelog requires a pattern"))
393 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath')
398 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath')
394 s = set()
399 s = set()
395
400
396 if not m.anypats():
401 if not m.anypats():
397 for f in m.files():
402 for f in m.files():
398 fl = repo.file(f)
403 fl = repo.file(f)
399 for fr in fl:
404 for fr in fl:
400 s.add(fl.linkrev(fr))
405 s.add(fl.linkrev(fr))
401 else:
406 else:
402 for f in repo[None]:
407 for f in repo[None]:
403 if m(f):
408 if m(f):
404 fl = repo.file(f)
409 fl = repo.file(f)
405 for fr in fl:
410 for fr in fl:
406 s.add(fl.linkrev(fr))
411 s.add(fl.linkrev(fr))
407
412
408 return [r for r in subset if r in s]
413 return [r for r in subset if r in s]
409
414
410 def first(repo, subset, x):
415 def first(repo, subset, x):
411 """``first(set, [n])``
416 """``first(set, [n])``
412 An alias for limit().
417 An alias for limit().
413 """
418 """
414 return limit(repo, subset, x)
419 return limit(repo, subset, x)
415
420
416 def follow(repo, subset, x):
421 def follow(repo, subset, x):
417 """``follow([file])``
422 """``follow([file])``
418 An alias for ``::.`` (ancestors of the working copy's first parent).
423 An alias for ``::.`` (ancestors of the working copy's first parent).
419 If a filename is specified, the history of the given file is followed,
424 If a filename is specified, the history of the given file is followed,
420 including copies.
425 including copies.
421 """
426 """
422 # i18n: "follow" is a keyword
427 # i18n: "follow" is a keyword
423 l = getargs(x, 0, 1, _("follow takes no arguments or a filename"))
428 l = getargs(x, 0, 1, _("follow takes no arguments or a filename"))
424 p = repo['.'].rev()
429 p = repo['.'].rev()
425 if l:
430 if l:
426 x = getstring(l[0], _("follow expected a filename"))
431 x = getstring(l[0], _("follow expected a filename"))
427 s = set(ctx.rev() for ctx in repo['.'][x].ancestors())
432 s = set(ctx.rev() for ctx in repo['.'][x].ancestors())
428 else:
433 else:
429 s = set(repo.changelog.ancestors(p))
434 s = set(repo.changelog.ancestors(p))
430
435
431 s |= set([p])
436 s |= set([p])
432 return [r for r in subset if r in s]
437 return [r for r in subset if r in s]
433
438
434 def followfile(repo, subset, x):
439 def followfile(repo, subset, x):
435 """``follow()``
440 """``follow()``
436 An alias for ``::.`` (ancestors of the working copy's first parent).
441 An alias for ``::.`` (ancestors of the working copy's first parent).
437 """
442 """
438 # i18n: "follow" is a keyword
443 # i18n: "follow" is a keyword
439 getargs(x, 0, 0, _("follow takes no arguments"))
444 getargs(x, 0, 0, _("follow takes no arguments"))
440 p = repo['.'].rev()
445 p = repo['.'].rev()
441 s = set(repo.changelog.ancestors(p)) | set([p])
446 s = set(repo.changelog.ancestors(p)) | set([p])
442 return [r for r in subset if r in s]
447 return [r for r in subset if r in s]
443
448
444 def getall(repo, subset, x):
449 def getall(repo, subset, x):
445 """``all()``
450 """``all()``
446 All changesets, the same as ``0:tip``.
451 All changesets, the same as ``0:tip``.
447 """
452 """
448 # i18n: "all" is a keyword
453 # i18n: "all" is a keyword
449 getargs(x, 0, 0, _("all takes no arguments"))
454 getargs(x, 0, 0, _("all takes no arguments"))
450 return subset
455 return subset
451
456
452 def grep(repo, subset, x):
457 def grep(repo, subset, x):
453 """``grep(regex)``
458 """``grep(regex)``
454 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
459 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
455 to ensure special escape characters are handled correctly. Unlike
460 to ensure special escape characters are handled correctly. Unlike
456 ``keyword(string)``, the match is case-sensitive.
461 ``keyword(string)``, the match is case-sensitive.
457 """
462 """
458 try:
463 try:
459 # i18n: "grep" is a keyword
464 # i18n: "grep" is a keyword
460 gr = re.compile(getstring(x, _("grep requires a string")))
465 gr = re.compile(getstring(x, _("grep requires a string")))
461 except re.error, e:
466 except re.error, e:
462 raise error.ParseError(_('invalid match pattern: %s') % e)
467 raise error.ParseError(_('invalid match pattern: %s') % e)
463 l = []
468 l = []
464 for r in subset:
469 for r in subset:
465 c = repo[r]
470 c = repo[r]
466 for e in c.files() + [c.user(), c.description()]:
471 for e in c.files() + [c.user(), c.description()]:
467 if gr.search(e):
472 if gr.search(e):
468 l.append(r)
473 l.append(r)
469 break
474 break
470 return l
475 return l
471
476
472 def hasfile(repo, subset, x):
477 def hasfile(repo, subset, x):
473 """``file(pattern)``
478 """``file(pattern)``
474 Changesets affecting files matched by pattern.
479 Changesets affecting files matched by pattern.
475 """
480 """
476 # i18n: "file" is a keyword
481 # i18n: "file" is a keyword
477 pat = getstring(x, _("file requires a pattern"))
482 pat = getstring(x, _("file requires a pattern"))
478 m = matchmod.match(repo.root, repo.getcwd(), [pat])
483 m = matchmod.match(repo.root, repo.getcwd(), [pat])
479 s = []
484 s = []
480 for r in subset:
485 for r in subset:
481 for f in repo[r].files():
486 for f in repo[r].files():
482 if m(f):
487 if m(f):
483 s.append(r)
488 s.append(r)
484 break
489 break
485 return s
490 return s
486
491
487 def head(repo, subset, x):
492 def head(repo, subset, x):
488 """``head()``
493 """``head()``
489 Changeset is a named branch head.
494 Changeset is a named branch head.
490 """
495 """
491 # i18n: "head" is a keyword
496 # i18n: "head" is a keyword
492 getargs(x, 0, 0, _("head takes no arguments"))
497 getargs(x, 0, 0, _("head takes no arguments"))
493 hs = set()
498 hs = set()
494 for b, ls in repo.branchmap().iteritems():
499 for b, ls in repo.branchmap().iteritems():
495 hs.update(repo[h].rev() for h in ls)
500 hs.update(repo[h].rev() for h in ls)
496 return [r for r in subset if r in hs]
501 return [r for r in subset if r in hs]
497
502
498 def heads(repo, subset, x):
503 def heads(repo, subset, x):
499 """``heads(set)``
504 """``heads(set)``
500 Members of set with no children in set.
505 Members of set with no children in set.
501 """
506 """
502 s = getset(repo, subset, x)
507 s = getset(repo, subset, x)
503 ps = set(parents(repo, subset, x))
508 ps = set(parents(repo, subset, x))
504 return [r for r in s if r not in ps]
509 return [r for r in s if r not in ps]
505
510
506 def keyword(repo, subset, x):
511 def keyword(repo, subset, x):
507 """``keyword(string)``
512 """``keyword(string)``
508 Search commit message, user name, and names of changed files for
513 Search commit message, user name, and names of changed files for
509 string. The match is case-insensitive.
514 string. The match is case-insensitive.
510 """
515 """
511 # i18n: "keyword" is a keyword
516 # i18n: "keyword" is a keyword
512 kw = getstring(x, _("keyword requires a string")).lower()
517 kw = getstring(x, _("keyword requires a string")).lower()
513 l = []
518 l = []
514 for r in subset:
519 for r in subset:
515 c = repo[r]
520 c = repo[r]
516 t = " ".join(c.files() + [c.user(), c.description()])
521 t = " ".join(c.files() + [c.user(), c.description()])
517 if kw in t.lower():
522 if kw in t.lower():
518 l.append(r)
523 l.append(r)
519 return l
524 return l
520
525
521 def limit(repo, subset, x):
526 def limit(repo, subset, x):
522 """``limit(set, [n])``
527 """``limit(set, [n])``
523 First n members of set, defaulting to 1.
528 First n members of set, defaulting to 1.
524 """
529 """
525 # i18n: "limit" is a keyword
530 # i18n: "limit" is a keyword
526 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
531 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
527 try:
532 try:
528 lim = 1
533 lim = 1
529 if len(l) == 2:
534 if len(l) == 2:
530 # i18n: "limit" is a keyword
535 # i18n: "limit" is a keyword
531 lim = int(getstring(l[1], _("limit requires a number")))
536 lim = int(getstring(l[1], _("limit requires a number")))
532 except (TypeError, ValueError):
537 except (TypeError, ValueError):
533 # i18n: "limit" is a keyword
538 # i18n: "limit" is a keyword
534 raise error.ParseError(_("limit expects a number"))
539 raise error.ParseError(_("limit expects a number"))
535 ss = set(subset)
540 ss = set(subset)
536 os = getset(repo, range(len(repo)), l[0])[:lim]
541 os = getset(repo, range(len(repo)), l[0])[:lim]
537 return [r for r in os if r in ss]
542 return [r for r in os if r in ss]
538
543
539 def last(repo, subset, x):
544 def last(repo, subset, x):
540 """``last(set, [n])``
545 """``last(set, [n])``
541 Last n members of set, defaulting to 1.
546 Last n members of set, defaulting to 1.
542 """
547 """
543 # i18n: "last" is a keyword
548 # i18n: "last" is a keyword
544 l = getargs(x, 1, 2, _("last requires one or two arguments"))
549 l = getargs(x, 1, 2, _("last requires one or two arguments"))
545 try:
550 try:
546 lim = 1
551 lim = 1
547 if len(l) == 2:
552 if len(l) == 2:
548 # i18n: "last" is a keyword
553 # i18n: "last" is a keyword
549 lim = int(getstring(l[1], _("last requires a number")))
554 lim = int(getstring(l[1], _("last requires a number")))
550 except (TypeError, ValueError):
555 except (TypeError, ValueError):
551 # i18n: "last" is a keyword
556 # i18n: "last" is a keyword
552 raise error.ParseError(_("last expects a number"))
557 raise error.ParseError(_("last expects a number"))
553 ss = set(subset)
558 ss = set(subset)
554 os = getset(repo, range(len(repo)), l[0])[-lim:]
559 os = getset(repo, range(len(repo)), l[0])[-lim:]
555 return [r for r in os if r in ss]
560 return [r for r in os if r in ss]
556
561
557 def maxrev(repo, subset, x):
562 def maxrev(repo, subset, x):
558 """``max(set)``
563 """``max(set)``
559 Changeset with highest revision number in set.
564 Changeset with highest revision number in set.
560 """
565 """
561 os = getset(repo, range(len(repo)), x)
566 os = getset(repo, range(len(repo)), x)
562 if os:
567 if os:
563 m = max(os)
568 m = max(os)
564 if m in subset:
569 if m in subset:
565 return [m]
570 return [m]
566 return []
571 return []
567
572
568 def merge(repo, subset, x):
573 def merge(repo, subset, x):
569 """``merge()``
574 """``merge()``
570 Changeset is a merge changeset.
575 Changeset is a merge changeset.
571 """
576 """
572 # i18n: "merge" is a keyword
577 # i18n: "merge" is a keyword
573 getargs(x, 0, 0, _("merge takes no arguments"))
578 getargs(x, 0, 0, _("merge takes no arguments"))
574 cl = repo.changelog
579 cl = repo.changelog
575 return [r for r in subset if cl.parentrevs(r)[1] != -1]
580 return [r for r in subset if cl.parentrevs(r)[1] != -1]
576
581
577 def minrev(repo, subset, x):
582 def minrev(repo, subset, x):
578 """``min(set)``
583 """``min(set)``
579 Changeset with lowest revision number in set.
584 Changeset with lowest revision number in set.
580 """
585 """
581 os = getset(repo, range(len(repo)), x)
586 os = getset(repo, range(len(repo)), x)
582 if os:
587 if os:
583 m = min(os)
588 m = min(os)
584 if m in subset:
589 if m in subset:
585 return [m]
590 return [m]
586 return []
591 return []
587
592
588 def modifies(repo, subset, x):
593 def modifies(repo, subset, x):
589 """``modifies(pattern)``
594 """``modifies(pattern)``
590 Changesets modifying files matched by pattern.
595 Changesets modifying files matched by pattern.
591 """
596 """
592 # i18n: "modifies" is a keyword
597 # i18n: "modifies" is a keyword
593 pat = getstring(x, _("modifies requires a pattern"))
598 pat = getstring(x, _("modifies requires a pattern"))
594 return checkstatus(repo, subset, pat, 0)
599 return checkstatus(repo, subset, pat, 0)
595
600
596 def node(repo, subset, x):
601 def node(repo, subset, x):
597 """``id(string)``
602 """``id(string)``
598 Revision non-ambiguously specified by the given hex string prefix.
603 Revision non-ambiguously specified by the given hex string prefix.
599 """
604 """
600 # i18n: "id" is a keyword
605 # i18n: "id" is a keyword
601 l = getargs(x, 1, 1, _("id requires one argument"))
606 l = getargs(x, 1, 1, _("id requires one argument"))
602 # i18n: "id" is a keyword
607 # i18n: "id" is a keyword
603 n = getstring(l[0], _("id requires a string"))
608 n = getstring(l[0], _("id requires a string"))
604 if len(n) == 40:
609 if len(n) == 40:
605 rn = repo[n].rev()
610 rn = repo[n].rev()
606 else:
611 else:
607 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
612 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
608 return [r for r in subset if r == rn]
613 return [r for r in subset if r == rn]
609
614
610 def outgoing(repo, subset, x):
615 def outgoing(repo, subset, x):
611 """``outgoing([path])``
616 """``outgoing([path])``
612 Changesets not found in the specified destination repository, or the
617 Changesets not found in the specified destination repository, or the
613 default push location.
618 default push location.
614 """
619 """
615 import hg # avoid start-up nasties
620 import hg # avoid start-up nasties
616 # i18n: "outgoing" is a keyword
621 # i18n: "outgoing" is a keyword
617 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
622 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
618 # i18n: "outgoing" is a keyword
623 # i18n: "outgoing" is a keyword
619 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
624 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
620 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
625 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
621 dest, branches = hg.parseurl(dest)
626 dest, branches = hg.parseurl(dest)
622 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
627 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
623 if revs:
628 if revs:
624 revs = [repo.lookup(rev) for rev in revs]
629 revs = [repo.lookup(rev) for rev in revs]
625 other = hg.peer(repo, {}, dest)
630 other = hg.peer(repo, {}, dest)
626 repo.ui.pushbuffer()
631 repo.ui.pushbuffer()
627 common, outheads = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
632 common, outheads = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
628 repo.ui.popbuffer()
633 repo.ui.popbuffer()
629 cl = repo.changelog
634 cl = repo.changelog
630 o = set([cl.rev(r) for r in repo.changelog.findmissing(common, outheads)])
635 o = set([cl.rev(r) for r in repo.changelog.findmissing(common, outheads)])
631 return [r for r in subset if r in o]
636 return [r for r in subset if r in o]
632
637
633 def p1(repo, subset, x):
638 def p1(repo, subset, x):
634 """``p1([set])``
639 """``p1([set])``
635 First parent of changesets in set, or the working directory.
640 First parent of changesets in set, or the working directory.
636 """
641 """
637 if x is None:
642 if x is None:
638 p = repo[x].p1().rev()
643 p = repo[x].p1().rev()
639 return [r for r in subset if r == p]
644 return [r for r in subset if r == p]
640
645
641 ps = set()
646 ps = set()
642 cl = repo.changelog
647 cl = repo.changelog
643 for r in getset(repo, range(len(repo)), x):
648 for r in getset(repo, range(len(repo)), x):
644 ps.add(cl.parentrevs(r)[0])
649 ps.add(cl.parentrevs(r)[0])
645 return [r for r in subset if r in ps]
650 return [r for r in subset if r in ps]
646
651
647 def p2(repo, subset, x):
652 def p2(repo, subset, x):
648 """``p2([set])``
653 """``p2([set])``
649 Second parent of changesets in set, or the working directory.
654 Second parent of changesets in set, or the working directory.
650 """
655 """
651 if x is None:
656 if x is None:
652 ps = repo[x].parents()
657 ps = repo[x].parents()
653 try:
658 try:
654 p = ps[1].rev()
659 p = ps[1].rev()
655 return [r for r in subset if r == p]
660 return [r for r in subset if r == p]
656 except IndexError:
661 except IndexError:
657 return []
662 return []
658
663
659 ps = set()
664 ps = set()
660 cl = repo.changelog
665 cl = repo.changelog
661 for r in getset(repo, range(len(repo)), x):
666 for r in getset(repo, range(len(repo)), x):
662 ps.add(cl.parentrevs(r)[1])
667 ps.add(cl.parentrevs(r)[1])
663 return [r for r in subset if r in ps]
668 return [r for r in subset if r in ps]
664
669
665 def parents(repo, subset, x):
670 def parents(repo, subset, x):
666 """``parents([set])``
671 """``parents([set])``
667 The set of all parents for all changesets in set, or the working directory.
672 The set of all parents for all changesets in set, or the working directory.
668 """
673 """
669 if x is None:
674 if x is None:
670 ps = tuple(p.rev() for p in repo[x].parents())
675 ps = tuple(p.rev() for p in repo[x].parents())
671 return [r for r in subset if r in ps]
676 return [r for r in subset if r in ps]
672
677
673 ps = set()
678 ps = set()
674 cl = repo.changelog
679 cl = repo.changelog
675 for r in getset(repo, range(len(repo)), x):
680 for r in getset(repo, range(len(repo)), x):
676 ps.update(cl.parentrevs(r))
681 ps.update(cl.parentrevs(r))
677 return [r for r in subset if r in ps]
682 return [r for r in subset if r in ps]
678
683
679 def parentspec(repo, subset, x, n):
684 def parentspec(repo, subset, x, n):
680 """``set^0``
685 """``set^0``
681 The set.
686 The set.
682 ``set^1`` (or ``set^``), ``set^2``
687 ``set^1`` (or ``set^``), ``set^2``
683 First or second parent, respectively, of all changesets in set.
688 First or second parent, respectively, of all changesets in set.
684 """
689 """
685 try:
690 try:
686 n = int(n[1])
691 n = int(n[1])
687 if n not in (0, 1, 2):
692 if n not in (0, 1, 2):
688 raise ValueError
693 raise ValueError
689 except (TypeError, ValueError):
694 except (TypeError, ValueError):
690 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
695 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
691 ps = set()
696 ps = set()
692 cl = repo.changelog
697 cl = repo.changelog
693 for r in getset(repo, subset, x):
698 for r in getset(repo, subset, x):
694 if n == 0:
699 if n == 0:
695 ps.add(r)
700 ps.add(r)
696 elif n == 1:
701 elif n == 1:
697 ps.add(cl.parentrevs(r)[0])
702 ps.add(cl.parentrevs(r)[0])
698 elif n == 2:
703 elif n == 2:
699 parents = cl.parentrevs(r)
704 parents = cl.parentrevs(r)
700 if len(parents) > 1:
705 if len(parents) > 1:
701 ps.add(parents[1])
706 ps.add(parents[1])
702 return [r for r in subset if r in ps]
707 return [r for r in subset if r in ps]
703
708
704 def present(repo, subset, x):
709 def present(repo, subset, x):
705 """``present(set)``
710 """``present(set)``
706 An empty set, if any revision in set isn't found; otherwise,
711 An empty set, if any revision in set isn't found; otherwise,
707 all revisions in set.
712 all revisions in set.
708 """
713 """
709 try:
714 try:
710 return getset(repo, subset, x)
715 return getset(repo, subset, x)
711 except error.RepoLookupError:
716 except error.RepoLookupError:
712 return []
717 return []
713
718
714 def removes(repo, subset, x):
719 def removes(repo, subset, x):
715 """``removes(pattern)``
720 """``removes(pattern)``
716 Changesets which remove files matching pattern.
721 Changesets which remove files matching pattern.
717 """
722 """
718 # i18n: "removes" is a keyword
723 # i18n: "removes" is a keyword
719 pat = getstring(x, _("removes requires a pattern"))
724 pat = getstring(x, _("removes requires a pattern"))
720 return checkstatus(repo, subset, pat, 2)
725 return checkstatus(repo, subset, pat, 2)
721
726
722 def rev(repo, subset, x):
727 def rev(repo, subset, x):
723 """``rev(number)``
728 """``rev(number)``
724 Revision with the given numeric identifier.
729 Revision with the given numeric identifier.
725 """
730 """
726 # i18n: "rev" is a keyword
731 # i18n: "rev" is a keyword
727 l = getargs(x, 1, 1, _("rev requires one argument"))
732 l = getargs(x, 1, 1, _("rev requires one argument"))
728 try:
733 try:
729 # i18n: "rev" is a keyword
734 # i18n: "rev" is a keyword
730 l = int(getstring(l[0], _("rev requires a number")))
735 l = int(getstring(l[0], _("rev requires a number")))
731 except (TypeError, ValueError):
736 except (TypeError, ValueError):
732 # i18n: "rev" is a keyword
737 # i18n: "rev" is a keyword
733 raise error.ParseError(_("rev expects a number"))
738 raise error.ParseError(_("rev expects a number"))
734 return [r for r in subset if r == l]
739 return [r for r in subset if r == l]
735
740
736 def reverse(repo, subset, x):
741 def reverse(repo, subset, x):
737 """``reverse(set)``
742 """``reverse(set)``
738 Reverse order of set.
743 Reverse order of set.
739 """
744 """
740 l = getset(repo, subset, x)
745 l = getset(repo, subset, x)
741 l.reverse()
746 l.reverse()
742 return l
747 return l
743
748
744 def roots(repo, subset, x):
749 def roots(repo, subset, x):
745 """``roots(set)``
750 """``roots(set)``
746 Changesets with no parent changeset in set.
751 Changesets with no parent changeset in set.
747 """
752 """
748 s = getset(repo, subset, x)
753 s = getset(repo, subset, x)
749 cs = set(children(repo, subset, x))
754 cs = set(children(repo, subset, x))
750 return [r for r in s if r not in cs]
755 return [r for r in s if r not in cs]
751
756
752 def sort(repo, subset, x):
757 def sort(repo, subset, x):
753 """``sort(set[, [-]key...])``
758 """``sort(set[, [-]key...])``
754 Sort set by keys. The default sort order is ascending, specify a key
759 Sort set by keys. The default sort order is ascending, specify a key
755 as ``-key`` to sort in descending order.
760 as ``-key`` to sort in descending order.
756
761
757 The keys can be:
762 The keys can be:
758
763
759 - ``rev`` for the revision number,
764 - ``rev`` for the revision number,
760 - ``branch`` for the branch name,
765 - ``branch`` for the branch name,
761 - ``desc`` for the commit message (description),
766 - ``desc`` for the commit message (description),
762 - ``user`` for user name (``author`` can be used as an alias),
767 - ``user`` for user name (``author`` can be used as an alias),
763 - ``date`` for the commit date
768 - ``date`` for the commit date
764 """
769 """
765 # i18n: "sort" is a keyword
770 # i18n: "sort" is a keyword
766 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
771 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
767 keys = "rev"
772 keys = "rev"
768 if len(l) == 2:
773 if len(l) == 2:
769 keys = getstring(l[1], _("sort spec must be a string"))
774 keys = getstring(l[1], _("sort spec must be a string"))
770
775
771 s = l[0]
776 s = l[0]
772 keys = keys.split()
777 keys = keys.split()
773 l = []
778 l = []
774 def invert(s):
779 def invert(s):
775 return "".join(chr(255 - ord(c)) for c in s)
780 return "".join(chr(255 - ord(c)) for c in s)
776 for r in getset(repo, subset, s):
781 for r in getset(repo, subset, s):
777 c = repo[r]
782 c = repo[r]
778 e = []
783 e = []
779 for k in keys:
784 for k in keys:
780 if k == 'rev':
785 if k == 'rev':
781 e.append(r)
786 e.append(r)
782 elif k == '-rev':
787 elif k == '-rev':
783 e.append(-r)
788 e.append(-r)
784 elif k == 'branch':
789 elif k == 'branch':
785 e.append(c.branch())
790 e.append(c.branch())
786 elif k == '-branch':
791 elif k == '-branch':
787 e.append(invert(c.branch()))
792 e.append(invert(c.branch()))
788 elif k == 'desc':
793 elif k == 'desc':
789 e.append(c.description())
794 e.append(c.description())
790 elif k == '-desc':
795 elif k == '-desc':
791 e.append(invert(c.description()))
796 e.append(invert(c.description()))
792 elif k in 'user author':
797 elif k in 'user author':
793 e.append(c.user())
798 e.append(c.user())
794 elif k in '-user -author':
799 elif k in '-user -author':
795 e.append(invert(c.user()))
800 e.append(invert(c.user()))
796 elif k == 'date':
801 elif k == 'date':
797 e.append(c.date()[0])
802 e.append(c.date()[0])
798 elif k == '-date':
803 elif k == '-date':
799 e.append(-c.date()[0])
804 e.append(-c.date()[0])
800 else:
805 else:
801 raise error.ParseError(_("unknown sort key %r") % k)
806 raise error.ParseError(_("unknown sort key %r") % k)
802 e.append(r)
807 e.append(r)
803 l.append(e)
808 l.append(e)
804 l.sort()
809 l.sort()
805 return [e[-1] for e in l]
810 return [e[-1] for e in l]
806
811
807 def tag(repo, subset, x):
812 def tag(repo, subset, x):
808 """``tag([name])``
813 """``tag([name])``
809 The specified tag by name, or all tagged revisions if no name is given.
814 The specified tag by name, or all tagged revisions if no name is given.
810 """
815 """
811 # i18n: "tag" is a keyword
816 # i18n: "tag" is a keyword
812 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
817 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
813 cl = repo.changelog
818 cl = repo.changelog
814 if args:
819 if args:
815 tn = getstring(args[0],
820 tn = getstring(args[0],
816 # i18n: "tag" is a keyword
821 # i18n: "tag" is a keyword
817 _('the argument to tag must be a string'))
822 _('the argument to tag must be a string'))
818 if not repo.tags().get(tn, None):
823 if not repo.tags().get(tn, None):
819 raise util.Abort(_("tag '%s' does not exist") % tn)
824 raise util.Abort(_("tag '%s' does not exist") % tn)
820 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
825 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
821 else:
826 else:
822 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
827 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
823 return [r for r in subset if r in s]
828 return [r for r in subset if r in s]
824
829
825 def tagged(repo, subset, x):
830 def tagged(repo, subset, x):
826 return tag(repo, subset, x)
831 return tag(repo, subset, x)
827
832
828 def user(repo, subset, x):
833 def user(repo, subset, x):
829 """``user(string)``
834 """``user(string)``
830 User name contains string. The match is case-insensitive.
835 User name contains string. The match is case-insensitive.
831 """
836 """
832 return author(repo, subset, x)
837 return author(repo, subset, x)
833
838
834 symbols = {
839 symbols = {
835 "adds": adds,
840 "adds": adds,
836 "all": getall,
841 "all": getall,
837 "ancestor": ancestor,
842 "ancestor": ancestor,
838 "ancestors": ancestors,
843 "ancestors": ancestors,
839 "author": author,
844 "author": author,
845 "bisect": bisect,
840 "bisected": bisected,
846 "bisected": bisected,
841 "bookmark": bookmark,
847 "bookmark": bookmark,
842 "branch": branch,
848 "branch": branch,
843 "children": children,
849 "children": children,
844 "closed": closed,
850 "closed": closed,
845 "contains": contains,
851 "contains": contains,
846 "date": date,
852 "date": date,
847 "desc": desc,
853 "desc": desc,
848 "descendants": descendants,
854 "descendants": descendants,
849 "file": hasfile,
855 "file": hasfile,
850 "filelog": filelog,
856 "filelog": filelog,
851 "first": first,
857 "first": first,
852 "follow": follow,
858 "follow": follow,
853 "grep": grep,
859 "grep": grep,
854 "head": head,
860 "head": head,
855 "heads": heads,
861 "heads": heads,
856 "id": node,
862 "id": node,
857 "keyword": keyword,
863 "keyword": keyword,
858 "last": last,
864 "last": last,
859 "limit": limit,
865 "limit": limit,
860 "max": maxrev,
866 "max": maxrev,
861 "merge": merge,
867 "merge": merge,
862 "min": minrev,
868 "min": minrev,
863 "modifies": modifies,
869 "modifies": modifies,
864 "outgoing": outgoing,
870 "outgoing": outgoing,
865 "p1": p1,
871 "p1": p1,
866 "p2": p2,
872 "p2": p2,
867 "parents": parents,
873 "parents": parents,
868 "present": present,
874 "present": present,
869 "removes": removes,
875 "removes": removes,
870 "rev": rev,
876 "rev": rev,
871 "reverse": reverse,
877 "reverse": reverse,
872 "roots": roots,
878 "roots": roots,
873 "sort": sort,
879 "sort": sort,
874 "tag": tag,
880 "tag": tag,
875 "tagged": tagged,
881 "tagged": tagged,
876 "user": user,
882 "user": user,
877 }
883 }
878
884
879 methods = {
885 methods = {
880 "range": rangeset,
886 "range": rangeset,
881 "string": stringset,
887 "string": stringset,
882 "symbol": symbolset,
888 "symbol": symbolset,
883 "and": andset,
889 "and": andset,
884 "or": orset,
890 "or": orset,
885 "not": notset,
891 "not": notset,
886 "list": listset,
892 "list": listset,
887 "func": func,
893 "func": func,
888 "ancestor": ancestorspec,
894 "ancestor": ancestorspec,
889 "parent": parentspec,
895 "parent": parentspec,
890 "parentpost": p1,
896 "parentpost": p1,
891 }
897 }
892
898
893 def optimize(x, small):
899 def optimize(x, small):
894 if x is None:
900 if x is None:
895 return 0, x
901 return 0, x
896
902
897 smallbonus = 1
903 smallbonus = 1
898 if small:
904 if small:
899 smallbonus = .5
905 smallbonus = .5
900
906
901 op = x[0]
907 op = x[0]
902 if op == 'minus':
908 if op == 'minus':
903 return optimize(('and', x[1], ('not', x[2])), small)
909 return optimize(('and', x[1], ('not', x[2])), small)
904 elif op == 'dagrange':
910 elif op == 'dagrange':
905 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
911 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
906 ('func', ('symbol', 'ancestors'), x[2])), small)
912 ('func', ('symbol', 'ancestors'), x[2])), small)
907 elif op == 'dagrangepre':
913 elif op == 'dagrangepre':
908 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
914 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
909 elif op == 'dagrangepost':
915 elif op == 'dagrangepost':
910 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
916 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
911 elif op == 'rangepre':
917 elif op == 'rangepre':
912 return optimize(('range', ('string', '0'), x[1]), small)
918 return optimize(('range', ('string', '0'), x[1]), small)
913 elif op == 'rangepost':
919 elif op == 'rangepost':
914 return optimize(('range', x[1], ('string', 'tip')), small)
920 return optimize(('range', x[1], ('string', 'tip')), small)
915 elif op == 'negate':
921 elif op == 'negate':
916 return optimize(('string',
922 return optimize(('string',
917 '-' + getstring(x[1], _("can't negate that"))), small)
923 '-' + getstring(x[1], _("can't negate that"))), small)
918 elif op in 'string symbol negate':
924 elif op in 'string symbol negate':
919 return smallbonus, x # single revisions are small
925 return smallbonus, x # single revisions are small
920 elif op == 'and' or op == 'dagrange':
926 elif op == 'and' or op == 'dagrange':
921 wa, ta = optimize(x[1], True)
927 wa, ta = optimize(x[1], True)
922 wb, tb = optimize(x[2], True)
928 wb, tb = optimize(x[2], True)
923 w = min(wa, wb)
929 w = min(wa, wb)
924 if wa > wb:
930 if wa > wb:
925 return w, (op, tb, ta)
931 return w, (op, tb, ta)
926 return w, (op, ta, tb)
932 return w, (op, ta, tb)
927 elif op == 'or':
933 elif op == 'or':
928 wa, ta = optimize(x[1], False)
934 wa, ta = optimize(x[1], False)
929 wb, tb = optimize(x[2], False)
935 wb, tb = optimize(x[2], False)
930 if wb < wa:
936 if wb < wa:
931 wb, wa = wa, wb
937 wb, wa = wa, wb
932 return max(wa, wb), (op, ta, tb)
938 return max(wa, wb), (op, ta, tb)
933 elif op == 'not':
939 elif op == 'not':
934 o = optimize(x[1], not small)
940 o = optimize(x[1], not small)
935 return o[0], (op, o[1])
941 return o[0], (op, o[1])
936 elif op == 'parentpost':
942 elif op == 'parentpost':
937 o = optimize(x[1], small)
943 o = optimize(x[1], small)
938 return o[0], (op, o[1])
944 return o[0], (op, o[1])
939 elif op == 'group':
945 elif op == 'group':
940 return optimize(x[1], small)
946 return optimize(x[1], small)
941 elif op in 'range list parent ancestorspec':
947 elif op in 'range list parent ancestorspec':
942 if op == 'parent':
948 if op == 'parent':
943 # x^:y means (x^) : y, not x ^ (:y)
949 # x^:y means (x^) : y, not x ^ (:y)
944 post = ('parentpost', x[1])
950 post = ('parentpost', x[1])
945 if x[2][0] == 'dagrangepre':
951 if x[2][0] == 'dagrangepre':
946 return optimize(('dagrange', post, x[2][1]), small)
952 return optimize(('dagrange', post, x[2][1]), small)
947 elif x[2][0] == 'rangepre':
953 elif x[2][0] == 'rangepre':
948 return optimize(('range', post, x[2][1]), small)
954 return optimize(('range', post, x[2][1]), small)
949
955
950 wa, ta = optimize(x[1], small)
956 wa, ta = optimize(x[1], small)
951 wb, tb = optimize(x[2], small)
957 wb, tb = optimize(x[2], small)
952 return wa + wb, (op, ta, tb)
958 return wa + wb, (op, ta, tb)
953 elif op == 'func':
959 elif op == 'func':
954 f = getstring(x[1], _("not a symbol"))
960 f = getstring(x[1], _("not a symbol"))
955 wa, ta = optimize(x[2], small)
961 wa, ta = optimize(x[2], small)
956 if f in ("author branch closed date desc file grep keyword "
962 if f in ("author branch closed date desc file grep keyword "
957 "outgoing user"):
963 "outgoing user"):
958 w = 10 # slow
964 w = 10 # slow
959 elif f in "modifies adds removes":
965 elif f in "modifies adds removes":
960 w = 30 # slower
966 w = 30 # slower
961 elif f == "contains":
967 elif f == "contains":
962 w = 100 # very slow
968 w = 100 # very slow
963 elif f == "ancestor":
969 elif f == "ancestor":
964 w = 1 * smallbonus
970 w = 1 * smallbonus
965 elif f in "reverse limit first":
971 elif f in "reverse limit first":
966 w = 0
972 w = 0
967 elif f in "sort":
973 elif f in "sort":
968 w = 10 # assume most sorts look at changelog
974 w = 10 # assume most sorts look at changelog
969 else:
975 else:
970 w = 1
976 w = 1
971 return w + wa, (op, x[1], ta)
977 return w + wa, (op, x[1], ta)
972 return 1, x
978 return 1, x
973
979
974 class revsetalias(object):
980 class revsetalias(object):
975 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
981 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
976 args = None
982 args = None
977
983
978 def __init__(self, name, value):
984 def __init__(self, name, value):
979 '''Aliases like:
985 '''Aliases like:
980
986
981 h = heads(default)
987 h = heads(default)
982 b($1) = ancestors($1) - ancestors(default)
988 b($1) = ancestors($1) - ancestors(default)
983 '''
989 '''
984 if isinstance(name, tuple): # parameter substitution
990 if isinstance(name, tuple): # parameter substitution
985 self.tree = name
991 self.tree = name
986 self.replacement = value
992 self.replacement = value
987 else: # alias definition
993 else: # alias definition
988 m = self.funcre.search(name)
994 m = self.funcre.search(name)
989 if m:
995 if m:
990 self.tree = ('func', ('symbol', m.group(1)))
996 self.tree = ('func', ('symbol', m.group(1)))
991 self.args = [x.strip() for x in m.group(2).split(',')]
997 self.args = [x.strip() for x in m.group(2).split(',')]
992 for arg in self.args:
998 for arg in self.args:
993 value = value.replace(arg, repr(arg))
999 value = value.replace(arg, repr(arg))
994 else:
1000 else:
995 self.tree = ('symbol', name)
1001 self.tree = ('symbol', name)
996
1002
997 self.replacement, pos = parse(value)
1003 self.replacement, pos = parse(value)
998 if pos != len(value):
1004 if pos != len(value):
999 raise error.ParseError(_('invalid token'), pos)
1005 raise error.ParseError(_('invalid token'), pos)
1000
1006
1001 def process(self, tree):
1007 def process(self, tree):
1002 if isinstance(tree, tuple):
1008 if isinstance(tree, tuple):
1003 if self.args is None:
1009 if self.args is None:
1004 if tree == self.tree:
1010 if tree == self.tree:
1005 return self.replacement
1011 return self.replacement
1006 elif tree[:2] == self.tree:
1012 elif tree[:2] == self.tree:
1007 l = getlist(tree[2])
1013 l = getlist(tree[2])
1008 if len(l) != len(self.args):
1014 if len(l) != len(self.args):
1009 raise error.ParseError(
1015 raise error.ParseError(
1010 _('invalid number of arguments: %s') % len(l))
1016 _('invalid number of arguments: %s') % len(l))
1011 result = self.replacement
1017 result = self.replacement
1012 for a, v in zip(self.args, l):
1018 for a, v in zip(self.args, l):
1013 valalias = revsetalias(('string', a), v)
1019 valalias = revsetalias(('string', a), v)
1014 result = valalias.process(result)
1020 result = valalias.process(result)
1015 return result
1021 return result
1016 return tuple(map(self.process, tree))
1022 return tuple(map(self.process, tree))
1017 return tree
1023 return tree
1018
1024
1019 def findaliases(ui, tree):
1025 def findaliases(ui, tree):
1020 for k, v in ui.configitems('revsetalias'):
1026 for k, v in ui.configitems('revsetalias'):
1021 alias = revsetalias(k, v)
1027 alias = revsetalias(k, v)
1022 tree = alias.process(tree)
1028 tree = alias.process(tree)
1023 return tree
1029 return tree
1024
1030
1025 parse = parser.parser(tokenize, elements).parse
1031 parse = parser.parser(tokenize, elements).parse
1026
1032
1027 def match(ui, spec):
1033 def match(ui, spec):
1028 if not spec:
1034 if not spec:
1029 raise error.ParseError(_("empty query"))
1035 raise error.ParseError(_("empty query"))
1030 tree, pos = parse(spec)
1036 tree, pos = parse(spec)
1031 if (pos != len(spec)):
1037 if (pos != len(spec)):
1032 raise error.ParseError(_("invalid token"), pos)
1038 raise error.ParseError(_("invalid token"), pos)
1033 if ui:
1039 if ui:
1034 tree = findaliases(ui, tree)
1040 tree = findaliases(ui, tree)
1035 weight, tree = optimize(tree, True)
1041 weight, tree = optimize(tree, True)
1036 def mfunc(repo, subset):
1042 def mfunc(repo, subset):
1037 return getset(repo, subset, tree)
1043 return getset(repo, subset, tree)
1038 return mfunc
1044 return mfunc
1039
1045
1040 def formatspec(expr, *args):
1046 def formatspec(expr, *args):
1041 '''
1047 '''
1042 This is a convenience function for using revsets internally, and
1048 This is a convenience function for using revsets internally, and
1043 escapes arguments appropriately. Aliases are intentionally ignored
1049 escapes arguments appropriately. Aliases are intentionally ignored
1044 so that intended expression behavior isn't accidentally subverted.
1050 so that intended expression behavior isn't accidentally subverted.
1045
1051
1046 Supported arguments:
1052 Supported arguments:
1047
1053
1048 %d = int(arg), no quoting
1054 %d = int(arg), no quoting
1049 %s = string(arg), escaped and single-quoted
1055 %s = string(arg), escaped and single-quoted
1050 %b = arg.branch(), escaped and single-quoted
1056 %b = arg.branch(), escaped and single-quoted
1051 %n = hex(arg), single-quoted
1057 %n = hex(arg), single-quoted
1052 %% = a literal '%'
1058 %% = a literal '%'
1053
1059
1054 >>> formatspec('%d:: and not %d::', 10, 20)
1060 >>> formatspec('%d:: and not %d::', 10, 20)
1055 '10:: and not 20::'
1061 '10:: and not 20::'
1056 >>> formatspec('keyword(%s)', 'foo\\xe9')
1062 >>> formatspec('keyword(%s)', 'foo\\xe9')
1057 "keyword('foo\\\\xe9')"
1063 "keyword('foo\\\\xe9')"
1058 >>> b = lambda: 'default'
1064 >>> b = lambda: 'default'
1059 >>> b.branch = b
1065 >>> b.branch = b
1060 >>> formatspec('branch(%b)', b)
1066 >>> formatspec('branch(%b)', b)
1061 "branch('default')"
1067 "branch('default')"
1062 '''
1068 '''
1063
1069
1064 def quote(s):
1070 def quote(s):
1065 return repr(str(s))
1071 return repr(str(s))
1066
1072
1067 ret = ''
1073 ret = ''
1068 pos = 0
1074 pos = 0
1069 arg = 0
1075 arg = 0
1070 while pos < len(expr):
1076 while pos < len(expr):
1071 c = expr[pos]
1077 c = expr[pos]
1072 if c == '%':
1078 if c == '%':
1073 pos += 1
1079 pos += 1
1074 d = expr[pos]
1080 d = expr[pos]
1075 if d == '%':
1081 if d == '%':
1076 ret += d
1082 ret += d
1077 elif d == 'd':
1083 elif d == 'd':
1078 ret += str(int(args[arg]))
1084 ret += str(int(args[arg]))
1079 arg += 1
1085 arg += 1
1080 elif d == 's':
1086 elif d == 's':
1081 ret += quote(args[arg])
1087 ret += quote(args[arg])
1082 arg += 1
1088 arg += 1
1083 elif d == 'n':
1089 elif d == 'n':
1084 ret += quote(node.hex(args[arg]))
1090 ret += quote(node.hex(args[arg]))
1085 arg += 1
1091 arg += 1
1086 elif d == 'b':
1092 elif d == 'b':
1087 ret += quote(args[arg].branch())
1093 ret += quote(args[arg].branch())
1088 arg += 1
1094 arg += 1
1089 else:
1095 else:
1090 raise util.Abort('unexpected revspec format character %s' % d)
1096 raise util.Abort('unexpected revspec format character %s' % d)
1091 else:
1097 else:
1092 ret += c
1098 ret += c
1093 pos += 1
1099 pos += 1
1094
1100
1095 return ret
1101 return ret
1096
1102
1097 # tell hggettext to extract docstrings from these functions:
1103 # tell hggettext to extract docstrings from these functions:
1098 i18nfunctions = symbols.values()
1104 i18nfunctions = symbols.values()
@@ -1,457 +1,466
1 $ hg init
1 $ hg init
2
2
3
3
4 committing changes
4 committing changes
5
5
6 $ count=0
6 $ count=0
7 $ echo > a
7 $ echo > a
8 $ while test $count -lt 32 ; do
8 $ while test $count -lt 32 ; do
9 > echo 'a' >> a
9 > echo 'a' >> a
10 > test $count -eq 0 && hg add
10 > test $count -eq 0 && hg add
11 > hg ci -m "msg $count" -d "$count 0"
11 > hg ci -m "msg $count" -d "$count 0"
12 > count=`expr $count + 1`
12 > count=`expr $count + 1`
13 > done
13 > done
14 adding a
14 adding a
15
15
16
16
17 $ hg log
17 $ hg log
18 changeset: 31:58c80a7c8a40
18 changeset: 31:58c80a7c8a40
19 tag: tip
19 tag: tip
20 user: test
20 user: test
21 date: Thu Jan 01 00:00:31 1970 +0000
21 date: Thu Jan 01 00:00:31 1970 +0000
22 summary: msg 31
22 summary: msg 31
23
23
24 changeset: 30:ed2d2f24b11c
24 changeset: 30:ed2d2f24b11c
25 user: test
25 user: test
26 date: Thu Jan 01 00:00:30 1970 +0000
26 date: Thu Jan 01 00:00:30 1970 +0000
27 summary: msg 30
27 summary: msg 30
28
28
29 changeset: 29:b5bd63375ab9
29 changeset: 29:b5bd63375ab9
30 user: test
30 user: test
31 date: Thu Jan 01 00:00:29 1970 +0000
31 date: Thu Jan 01 00:00:29 1970 +0000
32 summary: msg 29
32 summary: msg 29
33
33
34 changeset: 28:8e0c2264c8af
34 changeset: 28:8e0c2264c8af
35 user: test
35 user: test
36 date: Thu Jan 01 00:00:28 1970 +0000
36 date: Thu Jan 01 00:00:28 1970 +0000
37 summary: msg 28
37 summary: msg 28
38
38
39 changeset: 27:288867a866e9
39 changeset: 27:288867a866e9
40 user: test
40 user: test
41 date: Thu Jan 01 00:00:27 1970 +0000
41 date: Thu Jan 01 00:00:27 1970 +0000
42 summary: msg 27
42 summary: msg 27
43
43
44 changeset: 26:3efc6fd51aeb
44 changeset: 26:3efc6fd51aeb
45 user: test
45 user: test
46 date: Thu Jan 01 00:00:26 1970 +0000
46 date: Thu Jan 01 00:00:26 1970 +0000
47 summary: msg 26
47 summary: msg 26
48
48
49 changeset: 25:02a84173a97a
49 changeset: 25:02a84173a97a
50 user: test
50 user: test
51 date: Thu Jan 01 00:00:25 1970 +0000
51 date: Thu Jan 01 00:00:25 1970 +0000
52 summary: msg 25
52 summary: msg 25
53
53
54 changeset: 24:10e0acd3809e
54 changeset: 24:10e0acd3809e
55 user: test
55 user: test
56 date: Thu Jan 01 00:00:24 1970 +0000
56 date: Thu Jan 01 00:00:24 1970 +0000
57 summary: msg 24
57 summary: msg 24
58
58
59 changeset: 23:5ec79163bff4
59 changeset: 23:5ec79163bff4
60 user: test
60 user: test
61 date: Thu Jan 01 00:00:23 1970 +0000
61 date: Thu Jan 01 00:00:23 1970 +0000
62 summary: msg 23
62 summary: msg 23
63
63
64 changeset: 22:06c7993750ce
64 changeset: 22:06c7993750ce
65 user: test
65 user: test
66 date: Thu Jan 01 00:00:22 1970 +0000
66 date: Thu Jan 01 00:00:22 1970 +0000
67 summary: msg 22
67 summary: msg 22
68
68
69 changeset: 21:e5db6aa3fe2a
69 changeset: 21:e5db6aa3fe2a
70 user: test
70 user: test
71 date: Thu Jan 01 00:00:21 1970 +0000
71 date: Thu Jan 01 00:00:21 1970 +0000
72 summary: msg 21
72 summary: msg 21
73
73
74 changeset: 20:7128fb4fdbc9
74 changeset: 20:7128fb4fdbc9
75 user: test
75 user: test
76 date: Thu Jan 01 00:00:20 1970 +0000
76 date: Thu Jan 01 00:00:20 1970 +0000
77 summary: msg 20
77 summary: msg 20
78
78
79 changeset: 19:52798545b482
79 changeset: 19:52798545b482
80 user: test
80 user: test
81 date: Thu Jan 01 00:00:19 1970 +0000
81 date: Thu Jan 01 00:00:19 1970 +0000
82 summary: msg 19
82 summary: msg 19
83
83
84 changeset: 18:86977a90077e
84 changeset: 18:86977a90077e
85 user: test
85 user: test
86 date: Thu Jan 01 00:00:18 1970 +0000
86 date: Thu Jan 01 00:00:18 1970 +0000
87 summary: msg 18
87 summary: msg 18
88
88
89 changeset: 17:03515f4a9080
89 changeset: 17:03515f4a9080
90 user: test
90 user: test
91 date: Thu Jan 01 00:00:17 1970 +0000
91 date: Thu Jan 01 00:00:17 1970 +0000
92 summary: msg 17
92 summary: msg 17
93
93
94 changeset: 16:a2e6ea4973e9
94 changeset: 16:a2e6ea4973e9
95 user: test
95 user: test
96 date: Thu Jan 01 00:00:16 1970 +0000
96 date: Thu Jan 01 00:00:16 1970 +0000
97 summary: msg 16
97 summary: msg 16
98
98
99 changeset: 15:e7fa0811edb0
99 changeset: 15:e7fa0811edb0
100 user: test
100 user: test
101 date: Thu Jan 01 00:00:15 1970 +0000
101 date: Thu Jan 01 00:00:15 1970 +0000
102 summary: msg 15
102 summary: msg 15
103
103
104 changeset: 14:ce8f0998e922
104 changeset: 14:ce8f0998e922
105 user: test
105 user: test
106 date: Thu Jan 01 00:00:14 1970 +0000
106 date: Thu Jan 01 00:00:14 1970 +0000
107 summary: msg 14
107 summary: msg 14
108
108
109 changeset: 13:9d7d07bc967c
109 changeset: 13:9d7d07bc967c
110 user: test
110 user: test
111 date: Thu Jan 01 00:00:13 1970 +0000
111 date: Thu Jan 01 00:00:13 1970 +0000
112 summary: msg 13
112 summary: msg 13
113
113
114 changeset: 12:1941b52820a5
114 changeset: 12:1941b52820a5
115 user: test
115 user: test
116 date: Thu Jan 01 00:00:12 1970 +0000
116 date: Thu Jan 01 00:00:12 1970 +0000
117 summary: msg 12
117 summary: msg 12
118
118
119 changeset: 11:7b4cd9578619
119 changeset: 11:7b4cd9578619
120 user: test
120 user: test
121 date: Thu Jan 01 00:00:11 1970 +0000
121 date: Thu Jan 01 00:00:11 1970 +0000
122 summary: msg 11
122 summary: msg 11
123
123
124 changeset: 10:7c5eff49a6b6
124 changeset: 10:7c5eff49a6b6
125 user: test
125 user: test
126 date: Thu Jan 01 00:00:10 1970 +0000
126 date: Thu Jan 01 00:00:10 1970 +0000
127 summary: msg 10
127 summary: msg 10
128
128
129 changeset: 9:eb44510ef29a
129 changeset: 9:eb44510ef29a
130 user: test
130 user: test
131 date: Thu Jan 01 00:00:09 1970 +0000
131 date: Thu Jan 01 00:00:09 1970 +0000
132 summary: msg 9
132 summary: msg 9
133
133
134 changeset: 8:453eb4dba229
134 changeset: 8:453eb4dba229
135 user: test
135 user: test
136 date: Thu Jan 01 00:00:08 1970 +0000
136 date: Thu Jan 01 00:00:08 1970 +0000
137 summary: msg 8
137 summary: msg 8
138
138
139 changeset: 7:03750880c6b5
139 changeset: 7:03750880c6b5
140 user: test
140 user: test
141 date: Thu Jan 01 00:00:07 1970 +0000
141 date: Thu Jan 01 00:00:07 1970 +0000
142 summary: msg 7
142 summary: msg 7
143
143
144 changeset: 6:a3d5c6fdf0d3
144 changeset: 6:a3d5c6fdf0d3
145 user: test
145 user: test
146 date: Thu Jan 01 00:00:06 1970 +0000
146 date: Thu Jan 01 00:00:06 1970 +0000
147 summary: msg 6
147 summary: msg 6
148
148
149 changeset: 5:7874a09ea728
149 changeset: 5:7874a09ea728
150 user: test
150 user: test
151 date: Thu Jan 01 00:00:05 1970 +0000
151 date: Thu Jan 01 00:00:05 1970 +0000
152 summary: msg 5
152 summary: msg 5
153
153
154 changeset: 4:9b2ba8336a65
154 changeset: 4:9b2ba8336a65
155 user: test
155 user: test
156 date: Thu Jan 01 00:00:04 1970 +0000
156 date: Thu Jan 01 00:00:04 1970 +0000
157 summary: msg 4
157 summary: msg 4
158
158
159 changeset: 3:b53bea5e2fcb
159 changeset: 3:b53bea5e2fcb
160 user: test
160 user: test
161 date: Thu Jan 01 00:00:03 1970 +0000
161 date: Thu Jan 01 00:00:03 1970 +0000
162 summary: msg 3
162 summary: msg 3
163
163
164 changeset: 2:db07c04beaca
164 changeset: 2:db07c04beaca
165 user: test
165 user: test
166 date: Thu Jan 01 00:00:02 1970 +0000
166 date: Thu Jan 01 00:00:02 1970 +0000
167 summary: msg 2
167 summary: msg 2
168
168
169 changeset: 1:5cd978ea5149
169 changeset: 1:5cd978ea5149
170 user: test
170 user: test
171 date: Thu Jan 01 00:00:01 1970 +0000
171 date: Thu Jan 01 00:00:01 1970 +0000
172 summary: msg 1
172 summary: msg 1
173
173
174 changeset: 0:b99c7b9c8e11
174 changeset: 0:b99c7b9c8e11
175 user: test
175 user: test
176 date: Thu Jan 01 00:00:00 1970 +0000
176 date: Thu Jan 01 00:00:00 1970 +0000
177 summary: msg 0
177 summary: msg 0
178
178
179
179
180 $ hg up -C
180 $ hg up -C
181 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
181 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
182
182
183 bisect test
183 bisect test
184
184
185 $ hg bisect -r
185 $ hg bisect -r
186 $ hg bisect -b
186 $ hg bisect -b
187 $ hg bisect -g 1
187 $ hg bisect -g 1
188 Testing changeset 16:a2e6ea4973e9 (30 changesets remaining, ~4 tests)
188 Testing changeset 16:a2e6ea4973e9 (30 changesets remaining, ~4 tests)
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 $ hg bisect -g
190 $ hg bisect -g
191 Testing changeset 23:5ec79163bff4 (15 changesets remaining, ~3 tests)
191 Testing changeset 23:5ec79163bff4 (15 changesets remaining, ~3 tests)
192 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
193
193
194 skip
194 skip
195
195
196 $ hg bisect -s
196 $ hg bisect -s
197 Testing changeset 24:10e0acd3809e (15 changesets remaining, ~3 tests)
197 Testing changeset 24:10e0acd3809e (15 changesets remaining, ~3 tests)
198 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
198 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
199 $ hg bisect -g
199 $ hg bisect -g
200 Testing changeset 27:288867a866e9 (7 changesets remaining, ~2 tests)
200 Testing changeset 27:288867a866e9 (7 changesets remaining, ~2 tests)
201 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 $ hg bisect -g
202 $ hg bisect -g
203 Testing changeset 29:b5bd63375ab9 (4 changesets remaining, ~2 tests)
203 Testing changeset 29:b5bd63375ab9 (4 changesets remaining, ~2 tests)
204 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
204 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
205 $ hg bisect -b
205 $ hg bisect -b
206 Testing changeset 28:8e0c2264c8af (2 changesets remaining, ~1 tests)
206 Testing changeset 28:8e0c2264c8af (2 changesets remaining, ~1 tests)
207 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
207 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
208 $ hg bisect -g
208 $ hg bisect -g
209 The first bad revision is:
209 The first bad revision is:
210 changeset: 29:b5bd63375ab9
210 changeset: 29:b5bd63375ab9
211 user: test
211 user: test
212 date: Thu Jan 01 00:00:29 1970 +0000
212 date: Thu Jan 01 00:00:29 1970 +0000
213 summary: msg 29
213 summary: msg 29
214
214
215
215
216 mark revsets instead of single revs
216 mark revsets instead of single revs
217
217
218 $ hg bisect -r
218 $ hg bisect -r
219 $ hg bisect -b "0::3"
219 $ hg bisect -b "0::3"
220 $ hg bisect -s "13::16"
220 $ hg bisect -s "13::16"
221 $ hg bisect -g "26::tip"
221 $ hg bisect -g "26::tip"
222 Testing changeset 12:1941b52820a5 (23 changesets remaining, ~4 tests)
222 Testing changeset 12:1941b52820a5 (23 changesets remaining, ~4 tests)
223 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 $ cat .hg/bisect.state
224 $ cat .hg/bisect.state
225 skip 9d7d07bc967ca98ad0600c24953fd289ad5fa991
225 skip 9d7d07bc967ca98ad0600c24953fd289ad5fa991
226 skip ce8f0998e922c179e80819d5066fbe46e2998784
226 skip ce8f0998e922c179e80819d5066fbe46e2998784
227 skip e7fa0811edb063f6319531f0d0a865882138e180
227 skip e7fa0811edb063f6319531f0d0a865882138e180
228 skip a2e6ea4973e9196ddd3386493b0c214b41fd97d3
228 skip a2e6ea4973e9196ddd3386493b0c214b41fd97d3
229 bad b99c7b9c8e11558adef3fad9af211c58d46f325b
229 bad b99c7b9c8e11558adef3fad9af211c58d46f325b
230 bad 5cd978ea51499179507ee7b6f340d2dbaa401185
230 bad 5cd978ea51499179507ee7b6f340d2dbaa401185
231 bad db07c04beaca44cf24832541e7f4a2346a95275b
231 bad db07c04beaca44cf24832541e7f4a2346a95275b
232 bad b53bea5e2fcb30d3e00bd3409507a5659ce0fd8b
232 bad b53bea5e2fcb30d3e00bd3409507a5659ce0fd8b
233 good 3efc6fd51aeb8594398044c6c846ca59ae021203
233 good 3efc6fd51aeb8594398044c6c846ca59ae021203
234 good 288867a866e9adb7a29880b66936c874b80f4651
234 good 288867a866e9adb7a29880b66936c874b80f4651
235 good 8e0c2264c8af790daf3585ada0669d93dee09c83
235 good 8e0c2264c8af790daf3585ada0669d93dee09c83
236 good b5bd63375ab9a290419f2024b7f4ee9ea7ce90a8
236 good b5bd63375ab9a290419f2024b7f4ee9ea7ce90a8
237 good ed2d2f24b11c368fa8aa0da9f4e1db580abade59
237 good ed2d2f24b11c368fa8aa0da9f4e1db580abade59
238 good 58c80a7c8a4025a94cedaf7b4a4e3124e8909a96
238 good 58c80a7c8a4025a94cedaf7b4a4e3124e8909a96
239
239
240 bisect reverse test
240 bisect reverse test
241
241
242 $ hg bisect -r
242 $ hg bisect -r
243 $ hg bisect -b null
243 $ hg bisect -b null
244 $ hg bisect -g tip
244 $ hg bisect -g tip
245 Testing changeset 15:e7fa0811edb0 (32 changesets remaining, ~5 tests)
245 Testing changeset 15:e7fa0811edb0 (32 changesets remaining, ~5 tests)
246 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
246 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
247 $ hg bisect -g
247 $ hg bisect -g
248 Testing changeset 7:03750880c6b5 (16 changesets remaining, ~4 tests)
248 Testing changeset 7:03750880c6b5 (16 changesets remaining, ~4 tests)
249 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
249 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
250
250
251 skip
251 skip
252
252
253 $ hg bisect -s
253 $ hg bisect -s
254 Testing changeset 6:a3d5c6fdf0d3 (16 changesets remaining, ~4 tests)
254 Testing changeset 6:a3d5c6fdf0d3 (16 changesets remaining, ~4 tests)
255 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 $ hg bisect -g
256 $ hg bisect -g
257 Testing changeset 2:db07c04beaca (7 changesets remaining, ~2 tests)
257 Testing changeset 2:db07c04beaca (7 changesets remaining, ~2 tests)
258 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
258 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
259 $ hg bisect -g
259 $ hg bisect -g
260 Testing changeset 0:b99c7b9c8e11 (3 changesets remaining, ~1 tests)
260 Testing changeset 0:b99c7b9c8e11 (3 changesets remaining, ~1 tests)
261 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
261 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
262 $ hg bisect -b
262 $ hg bisect -b
263 Testing changeset 1:5cd978ea5149 (2 changesets remaining, ~1 tests)
263 Testing changeset 1:5cd978ea5149 (2 changesets remaining, ~1 tests)
264 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
264 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
265 $ hg bisect -g
265 $ hg bisect -g
266 The first good revision is:
266 The first good revision is:
267 changeset: 1:5cd978ea5149
267 changeset: 1:5cd978ea5149
268 user: test
268 user: test
269 date: Thu Jan 01 00:00:01 1970 +0000
269 date: Thu Jan 01 00:00:01 1970 +0000
270 summary: msg 1
270 summary: msg 1
271
271
272
272
273 $ hg bisect -r
273 $ hg bisect -r
274 $ hg bisect -g tip
274 $ hg bisect -g tip
275 $ hg bisect -b tip
275 $ hg bisect -b tip
276 abort: starting revisions are not directly related
276 abort: starting revisions are not directly related
277 [255]
277 [255]
278
278
279 $ hg bisect -r
279 $ hg bisect -r
280 $ hg bisect -g null
280 $ hg bisect -g null
281 $ hg bisect -bU tip
281 $ hg bisect -bU tip
282 Testing changeset 15:e7fa0811edb0 (32 changesets remaining, ~5 tests)
282 Testing changeset 15:e7fa0811edb0 (32 changesets remaining, ~5 tests)
283 $ hg id
283 $ hg id
284 5cd978ea5149
284 5cd978ea5149
285
285
286
286
287 Issue1228: hg bisect crashes when you skip the last rev in bisection
287 Issue1228: hg bisect crashes when you skip the last rev in bisection
288 Issue1182: hg bisect exception
288 Issue1182: hg bisect exception
289
289
290 $ hg bisect -r
290 $ hg bisect -r
291 $ hg bisect -b 4
291 $ hg bisect -b 4
292 $ hg bisect -g 0
292 $ hg bisect -g 0
293 Testing changeset 2:db07c04beaca (4 changesets remaining, ~2 tests)
293 Testing changeset 2:db07c04beaca (4 changesets remaining, ~2 tests)
294 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
294 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
295 $ hg bisect -s
295 $ hg bisect -s
296 Testing changeset 1:5cd978ea5149 (4 changesets remaining, ~2 tests)
296 Testing changeset 1:5cd978ea5149 (4 changesets remaining, ~2 tests)
297 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
298 $ hg bisect -s
298 $ hg bisect -s
299 Testing changeset 3:b53bea5e2fcb (4 changesets remaining, ~2 tests)
299 Testing changeset 3:b53bea5e2fcb (4 changesets remaining, ~2 tests)
300 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
300 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
301 $ hg bisect -s
301 $ hg bisect -s
302 Due to skipped revisions, the first bad revision could be any of:
302 Due to skipped revisions, the first bad revision could be any of:
303 changeset: 1:5cd978ea5149
303 changeset: 1:5cd978ea5149
304 user: test
304 user: test
305 date: Thu Jan 01 00:00:01 1970 +0000
305 date: Thu Jan 01 00:00:01 1970 +0000
306 summary: msg 1
306 summary: msg 1
307
307
308 changeset: 2:db07c04beaca
308 changeset: 2:db07c04beaca
309 user: test
309 user: test
310 date: Thu Jan 01 00:00:02 1970 +0000
310 date: Thu Jan 01 00:00:02 1970 +0000
311 summary: msg 2
311 summary: msg 2
312
312
313 changeset: 3:b53bea5e2fcb
313 changeset: 3:b53bea5e2fcb
314 user: test
314 user: test
315 date: Thu Jan 01 00:00:03 1970 +0000
315 date: Thu Jan 01 00:00:03 1970 +0000
316 summary: msg 3
316 summary: msg 3
317
317
318 changeset: 4:9b2ba8336a65
318 changeset: 4:9b2ba8336a65
319 user: test
319 user: test
320 date: Thu Jan 01 00:00:04 1970 +0000
320 date: Thu Jan 01 00:00:04 1970 +0000
321 summary: msg 4
321 summary: msg 4
322
322
323
323
324
324
325 reproduce non converging bisect, issue1182
325 reproduce non converging bisect, issue1182
326
326
327 $ hg bisect -r
327 $ hg bisect -r
328 $ hg bisect -g 0
328 $ hg bisect -g 0
329 $ hg bisect -b 2
329 $ hg bisect -b 2
330 Testing changeset 1:5cd978ea5149 (2 changesets remaining, ~1 tests)
330 Testing changeset 1:5cd978ea5149 (2 changesets remaining, ~1 tests)
331 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
331 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
332 $ hg bisect -s
332 $ hg bisect -s
333 Due to skipped revisions, the first bad revision could be any of:
333 Due to skipped revisions, the first bad revision could be any of:
334 changeset: 1:5cd978ea5149
334 changeset: 1:5cd978ea5149
335 user: test
335 user: test
336 date: Thu Jan 01 00:00:01 1970 +0000
336 date: Thu Jan 01 00:00:01 1970 +0000
337 summary: msg 1
337 summary: msg 1
338
338
339 changeset: 2:db07c04beaca
339 changeset: 2:db07c04beaca
340 user: test
340 user: test
341 date: Thu Jan 01 00:00:02 1970 +0000
341 date: Thu Jan 01 00:00:02 1970 +0000
342 summary: msg 2
342 summary: msg 2
343
343
344
344
345
345
346 test no action
346 test no action
347
347
348 $ hg bisect -r
348 $ hg bisect -r
349 $ hg bisect
349 $ hg bisect
350 abort: cannot bisect (no known good revisions)
350 abort: cannot bisect (no known good revisions)
351 [255]
351 [255]
352
352
353
353
354 reproduce AssertionError, issue1445
354 reproduce AssertionError, issue1445
355
355
356 $ hg bisect -r
356 $ hg bisect -r
357 $ hg bisect -b 6
357 $ hg bisect -b 6
358 $ hg bisect -g 0
358 $ hg bisect -g 0
359 Testing changeset 3:b53bea5e2fcb (6 changesets remaining, ~2 tests)
359 Testing changeset 3:b53bea5e2fcb (6 changesets remaining, ~2 tests)
360 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
360 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
361 $ hg bisect -s
361 $ hg bisect -s
362 Testing changeset 2:db07c04beaca (6 changesets remaining, ~2 tests)
362 Testing changeset 2:db07c04beaca (6 changesets remaining, ~2 tests)
363 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
363 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
364 $ hg bisect -s
364 $ hg bisect -s
365 Testing changeset 4:9b2ba8336a65 (6 changesets remaining, ~2 tests)
365 Testing changeset 4:9b2ba8336a65 (6 changesets remaining, ~2 tests)
366 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
366 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 $ hg bisect -s
367 $ hg bisect -s
368 Testing changeset 1:5cd978ea5149 (6 changesets remaining, ~2 tests)
368 Testing changeset 1:5cd978ea5149 (6 changesets remaining, ~2 tests)
369 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
369 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
370 $ hg bisect -s
370 $ hg bisect -s
371 Testing changeset 5:7874a09ea728 (6 changesets remaining, ~2 tests)
371 Testing changeset 5:7874a09ea728 (6 changesets remaining, ~2 tests)
372 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
372 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
373 $ hg bisect -g
373 $ hg bisect -g
374 The first bad revision is:
374 The first bad revision is:
375 changeset: 6:a3d5c6fdf0d3
375 changeset: 6:a3d5c6fdf0d3
376 user: test
376 user: test
377 date: Thu Jan 01 00:00:06 1970 +0000
377 date: Thu Jan 01 00:00:06 1970 +0000
378 summary: msg 6
378 summary: msg 6
379
379
380 $ hg log -r "bisected(good)"
380 $ hg log -r "bisect(good)"
381 changeset: 0:b99c7b9c8e11
381 changeset: 0:b99c7b9c8e11
382 user: test
382 user: test
383 date: Thu Jan 01 00:00:00 1970 +0000
383 date: Thu Jan 01 00:00:00 1970 +0000
384 summary: msg 0
384 summary: msg 0
385
385
386 changeset: 5:7874a09ea728
386 changeset: 5:7874a09ea728
387 user: test
387 user: test
388 date: Thu Jan 01 00:00:05 1970 +0000
388 date: Thu Jan 01 00:00:05 1970 +0000
389 summary: msg 5
389 summary: msg 5
390
390
391 $ hg log -r "bisected(bad)"
391 $ hg log -r "bisect(bad)"
392 changeset: 6:a3d5c6fdf0d3
392 changeset: 6:a3d5c6fdf0d3
393 user: test
393 user: test
394 date: Thu Jan 01 00:00:06 1970 +0000
394 date: Thu Jan 01 00:00:06 1970 +0000
395 summary: msg 6
395 summary: msg 6
396
396
397 $ hg log -r "bisected(skip)"
397 $ hg log -r "bisect(skip)"
398 changeset: 1:5cd978ea5149
398 changeset: 1:5cd978ea5149
399 user: test
399 user: test
400 date: Thu Jan 01 00:00:01 1970 +0000
400 date: Thu Jan 01 00:00:01 1970 +0000
401 summary: msg 1
401 summary: msg 1
402
402
403 changeset: 2:db07c04beaca
403 changeset: 2:db07c04beaca
404 user: test
404 user: test
405 date: Thu Jan 01 00:00:02 1970 +0000
405 date: Thu Jan 01 00:00:02 1970 +0000
406 summary: msg 2
406 summary: msg 2
407
407
408 changeset: 3:b53bea5e2fcb
408 changeset: 3:b53bea5e2fcb
409 user: test
409 user: test
410 date: Thu Jan 01 00:00:03 1970 +0000
410 date: Thu Jan 01 00:00:03 1970 +0000
411 summary: msg 3
411 summary: msg 3
412
412
413 changeset: 4:9b2ba8336a65
413 changeset: 4:9b2ba8336a65
414 user: test
414 user: test
415 date: Thu Jan 01 00:00:04 1970 +0000
415 date: Thu Jan 01 00:00:04 1970 +0000
416 summary: msg 4
416 summary: msg 4
417
417
418
418
419 test legacy bisected() keyword
420
421 $ hg log -r "bisected(bad)"
422 changeset: 6:a3d5c6fdf0d3
423 user: test
424 date: Thu Jan 01 00:00:06 1970 +0000
425 summary: msg 6
426
427
419 $ set +e
428 $ set +e
420
429
421 test invalid command
430 test invalid command
422 assuming that the shell returns 127 if command not found ...
431 assuming that the shell returns 127 if command not found ...
423
432
424 $ hg bisect -r
433 $ hg bisect -r
425 $ hg bisect --command 'exit 127'
434 $ hg bisect --command 'exit 127'
426 abort: failed to execute exit 127
435 abort: failed to execute exit 127
427 [255]
436 [255]
428
437
429
438
430 test bisecting command
439 test bisecting command
431
440
432 $ cat > script.py <<EOF
441 $ cat > script.py <<EOF
433 > #!/usr/bin/env python
442 > #!/usr/bin/env python
434 > import sys
443 > import sys
435 > from mercurial import ui, hg
444 > from mercurial import ui, hg
436 > repo = hg.repository(ui.ui(), '.')
445 > repo = hg.repository(ui.ui(), '.')
437 > if repo['.'].rev() < 6:
446 > if repo['.'].rev() < 6:
438 > sys.exit(1)
447 > sys.exit(1)
439 > EOF
448 > EOF
440 $ chmod +x script.py
449 $ chmod +x script.py
441 $ hg bisect -r
450 $ hg bisect -r
442 $ hg bisect --good tip
451 $ hg bisect --good tip
443 $ hg bisect --bad 0
452 $ hg bisect --bad 0
444 Testing changeset 15:e7fa0811edb0 (31 changesets remaining, ~4 tests)
453 Testing changeset 15:e7fa0811edb0 (31 changesets remaining, ~4 tests)
445 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
454 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
446 $ hg bisect --command "'`pwd`/script.py' and some parameters"
455 $ hg bisect --command "'`pwd`/script.py' and some parameters"
447 Changeset 15:e7fa0811edb0: good
456 Changeset 15:e7fa0811edb0: good
448 Changeset 7:03750880c6b5: good
457 Changeset 7:03750880c6b5: good
449 Changeset 3:b53bea5e2fcb: bad
458 Changeset 3:b53bea5e2fcb: bad
450 Changeset 5:7874a09ea728: bad
459 Changeset 5:7874a09ea728: bad
451 Changeset 6:a3d5c6fdf0d3: good
460 Changeset 6:a3d5c6fdf0d3: good
452 The first good revision is:
461 The first good revision is:
453 changeset: 6:a3d5c6fdf0d3
462 changeset: 6:a3d5c6fdf0d3
454 user: test
463 user: test
455 date: Thu Jan 01 00:00:06 1970 +0000
464 date: Thu Jan 01 00:00:06 1970 +0000
456 summary: msg 6
465 summary: msg 6
457
466
General Comments 0
You need to be logged in to leave comments. Login now