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