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