158 """ |
158 """ |
159 Return a list of revision(s) that match the given status: |
159 Return a list of revision(s) that match the given status: |
160 |
160 |
161 - ``good``, ``bad``, ``skip``: as the names imply |
161 - ``good``, ``bad``, ``skip``: as the names imply |
162 - ``range`` : all csets taking part in the bisection |
162 - ``range`` : all csets taking part in the bisection |
163 - ``pruned`` : good|bad|skip, excluding out-of-range csets |
163 - ``pruned`` : csets that are good, bad or skipped |
164 - ``untested`` : csets whose fate is yet unknown |
164 - ``untested`` : csets whose fate is yet unknown |
165 """ |
165 """ |
166 state = load_state(repo) |
166 state = load_state(repo) |
167 if status in ('good', 'bad', 'skip'): |
167 if status in ('good', 'bad', 'skip'): |
168 return [repo.changelog.rev(n) for n in state[status]] |
168 return [repo.changelog.rev(n) for n in state[status]] |
169 else: |
169 else: |
170 # Build sets of good, bad, and skipped csets |
170 # In the floowing sets, we do *not* call 'bisect()' with more |
171 goods = set(repo.changelog.rev(n) for n in state['good']) |
171 # than one level of recusrsion, because that can be very, very |
172 bads = set(repo.changelog.rev(n) for n in state['bad']) |
172 # time consuming. Instead, we always develop the expression as |
173 skips = set(repo.changelog.rev(n) for n in state['skip']) |
173 # much as possible. |
174 |
174 |
175 # Build kinship of good csets |
175 # 'range' is all csets that make the bisection: |
176 ga = goods.copy() # Goods' Ancestors |
176 # - have a good ancestor and a bad descendant, or conversely |
177 gd = goods.copy() # Goods' Descendants |
177 # that's because the bisection can go either way |
178 for g in goods: |
178 range = '( bisect(bad)::bisect(good) | bisect(good)::bisect(bad) )' |
179 ga |= set(repo.changelog.ancestors(g)) |
179 |
180 gd |= set(repo.changelog.descendants(g)) |
180 # 'pruned' is all csets whose fate is already known: |
181 |
181 # - a good ancestor and a good ascendant, or |
182 # Build kinship of bad csets |
182 # - a bad ancestor and a bad descendant, or |
183 ba = bads.copy() # Bads' Ancestors |
183 # - skipped |
184 bd = bads.copy() # Bads' Descendants |
184 # But in case of irrelevant goods/bads, we also need to |
185 for b in bads: |
185 # include them. |
186 ba |= set(repo.changelog.ancestors(b)) |
186 pg = 'bisect(good)::bisect(good)' # Pruned goods |
187 bd |= set(repo.changelog.descendants(b)) |
187 pb = 'bisect(bad)::bisect(bad)' # Pruned bads |
188 |
188 ps = 'bisect(skip)' # Pruned skipped |
189 # Build the range of the bisection |
189 pruned = '( (%s) | (%s) | (%s) )' % (pg, pb, ps) |
190 range = set(c for c in ba if c in gd) |
190 |
191 range |= set(c for c in ga if c in bd) |
191 # 'untested' is all cset that are- in 'range', but not in 'pruned' |
|
192 untested = '( (%s) - (%s) )' % (range, pruned) |
192 |
193 |
193 if status == 'range': |
194 if status == 'range': |
194 return [c for c in range] |
195 return [c.rev() for c in repo.set(range)] |
195 elif status == 'pruned': |
196 elif status == 'pruned': |
196 # We do not want skipped csets that are out-of-range |
197 return [c.rev() for c in repo.set(pruned)] |
197 return [c for c in range if c in (goods | bads | skips)] |
|
198 elif status == 'untested': |
198 elif status == 'untested': |
199 # Return the csets in range that are not pruned |
199 return [c.rev() for c in repo.set(untested)] |
200 return [c for c in range if not c in (goods | bads | skips)] |
|
201 |
200 |
202 else: |
201 else: |
203 raise error.ParseError(_('invalid bisect state')) |
202 raise error.ParseError(_('invalid bisect state')) |