301 finally: |
301 finally: |
302 ui.setconfig('ui', 'forcemerge', '') |
302 ui.setconfig('ui', 'forcemerge', '') |
303 return 0 |
303 return 0 |
304 |
304 |
305 def bisect(ui, repo, rev=None, extra=None, command=None, |
305 def bisect(ui, repo, rev=None, extra=None, command=None, |
306 reset=None, good=None, bad=None, skip=None, noupdate=None): |
306 reset=None, good=None, bad=None, skip=None, extend=None, |
|
307 noupdate=None): |
307 """subdivision search of changesets |
308 """subdivision search of changesets |
308 |
309 |
309 This command helps to find changesets which introduce problems. To |
310 This command helps to find changesets which introduce problems. To |
310 use, mark the earliest changeset you know exhibits the problem as |
311 use, mark the earliest changeset you know exhibits the problem as |
311 bad, then mark the latest changeset which is free from the problem |
312 bad, then mark the latest changeset which is free from the problem |
324 (command not found) will abort the bisection, and any other |
325 (command not found) will abort the bisection, and any other |
325 non-zero exit status means the revision is bad. |
326 non-zero exit status means the revision is bad. |
326 |
327 |
327 Returns 0 on success. |
328 Returns 0 on success. |
328 """ |
329 """ |
|
330 def extendbisectrange(nodes, good): |
|
331 # bisect is incomplete when it ends on a merge node and |
|
332 # one of the parent was not checked. |
|
333 parents = repo[nodes[0]].parents() |
|
334 if len(parents) > 1: |
|
335 side = good and state['bad'] or state['good'] |
|
336 num = len(set(i.node() for i in parents) & set(side)) |
|
337 if num == 1: |
|
338 return parents[0].ancestor(parents[1]) |
|
339 return None |
|
340 |
329 def print_result(nodes, good): |
341 def print_result(nodes, good): |
330 displayer = cmdutil.show_changeset(ui, repo, {}) |
342 displayer = cmdutil.show_changeset(ui, repo, {}) |
331 if len(nodes) == 1: |
343 if len(nodes) == 1: |
332 # narrowed it down to a single revision |
344 # narrowed it down to a single revision |
333 if good: |
345 if good: |
334 ui.write(_("The first good revision is:\n")) |
346 ui.write(_("The first good revision is:\n")) |
335 else: |
347 else: |
336 ui.write(_("The first bad revision is:\n")) |
348 ui.write(_("The first bad revision is:\n")) |
337 displayer.show(repo[nodes[0]]) |
349 displayer.show(repo[nodes[0]]) |
338 parents = repo[nodes[0]].parents() |
350 parents = repo[nodes[0]].parents() |
339 if len(parents) > 1: |
351 extendnode = extendbisectrange(nodes, good) |
340 side = good and state['bad'] or state['good'] |
352 if extendnode is not None: |
341 num = len(set(i.node() for i in parents) & set(side)) |
353 ui.write(_('Not all ancestors of this changeset have been' |
342 if num == 1: |
354 ' checked.\nUse bisect --extend to continue the ' |
343 common = parents[0].ancestor(parents[1]) |
355 'bisection from\nthe common ancestor, %s.\n') |
344 ui.write(_('Not all ancestors of this changeset have been' |
356 % short(extendnode.node())) |
345 ' checked.\nTo check the other ancestors, start' |
|
346 ' from the common ancestor, %s.\n' % common)) |
|
347 else: |
357 else: |
348 # multiple possible revisions |
358 # multiple possible revisions |
349 if good: |
359 if good: |
350 ui.write(_("Due to skipped revisions, the first " |
360 ui.write(_("Due to skipped revisions, the first " |
351 "good revision could be any of:\n")) |
361 "good revision could be any of:\n")) |
438 if not check_state(state): |
448 if not check_state(state): |
439 return |
449 return |
440 |
450 |
441 # actually bisect |
451 # actually bisect |
442 nodes, changesets, good = hbisect.bisect(repo.changelog, state) |
452 nodes, changesets, good = hbisect.bisect(repo.changelog, state) |
|
453 if extend: |
|
454 if not changesets: |
|
455 extendnode = extendbisectrange(nodes, good) |
|
456 if extendnode is not None: |
|
457 ui.write(_("Extending search to changeset %d:%s\n" |
|
458 % (extendnode.rev(), short(extendnode.node())))) |
|
459 if noupdate: |
|
460 return |
|
461 cmdutil.bail_if_changed(repo) |
|
462 return hg.clean(repo, extendnode.node()) |
|
463 raise util.Abort(_("nothing to extend")) |
|
464 |
443 if changesets == 0: |
465 if changesets == 0: |
444 print_result(nodes, good) |
466 print_result(nodes, good) |
445 else: |
467 else: |
446 assert len(nodes) == 1 # only a single node can be tested next |
468 assert len(nodes) == 1 # only a single node can be tested next |
447 node = nodes[0] |
469 node = nodes[0] |
4270 (bisect, |
4292 (bisect, |
4271 [('r', 'reset', False, _('reset bisect state')), |
4293 [('r', 'reset', False, _('reset bisect state')), |
4272 ('g', 'good', False, _('mark changeset good')), |
4294 ('g', 'good', False, _('mark changeset good')), |
4273 ('b', 'bad', False, _('mark changeset bad')), |
4295 ('b', 'bad', False, _('mark changeset bad')), |
4274 ('s', 'skip', False, _('skip testing changeset')), |
4296 ('s', 'skip', False, _('skip testing changeset')), |
|
4297 ('e', 'extend', False, _('extend the bisect range')), |
4275 ('c', 'command', '', |
4298 ('c', 'command', '', |
4276 _('use command to check changeset state'), _('CMD')), |
4299 _('use command to check changeset state'), _('CMD')), |
4277 ('U', 'noupdate', False, _('do not update to target'))], |
4300 ('U', 'noupdate', False, _('do not update to target'))], |
4278 _("[-gbsr] [-U] [-c CMD] [REV]")), |
4301 _("[-gbsr] [-U] [-c CMD] [REV]")), |
4279 "bookmarks": |
4302 "bookmarks": |