mercurial/commands.py
changeset 13601 0388e3e36693
parent 13591 264f292a0c6f
child 13608 63ab6b0ccedc
equal deleted inserted replaced
13599:0bef8f69c078 13601:0388e3e36693
   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"))
   374             good = True
   384             good = True
   375         elif cmd == "bad":
   385         elif cmd == "bad":
   376             bad = True
   386             bad = True
   377         else:
   387         else:
   378             reset = True
   388             reset = True
   379     elif extra or good + bad + skip + reset + bool(command) > 1:
   389     elif extra or good + bad + skip + reset + extend + bool(command) > 1:
   380         raise util.Abort(_('incompatible arguments'))
   390         raise util.Abort(_('incompatible arguments'))
   381 
   391 
   382     if reset:
   392     if reset:
   383         p = repo.join("bisect.state")
   393         p = repo.join("bisect.state")
   384         if os.path.exists(p):
   394         if os.path.exists(p):
   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":