# HG changeset patch # User Alexander Solovyov # Date 1223647094 -10800 # Node ID e1afb50ec2aa18e7c849e3686a091c60c84b0d88 # Parent b71a52f101dce555a6506cbe8b98638709ae4efa bisect: ability to check revision with command diff -r b71a52f101dc -r e1afb50ec2aa mercurial/commands.py --- a/mercurial/commands.py Tue Oct 14 21:28:49 2008 +0200 +++ b/mercurial/commands.py Fri Oct 10 16:58:14 2008 +0300 @@ -260,7 +260,7 @@ ui.status(_('(use "backout --merge" ' 'if you want to auto-merge)\n')) -def bisect(ui, repo, rev=None, extra=None, +def bisect(ui, repo, rev=None, extra=None, command=None, reset=None, good=None, bad=None, skip=None, noupdate=None): """subdivision search of changesets @@ -275,67 +275,12 @@ As a shortcut, you can also use the revision argument to mark a revision as good or bad without checking it out first. + + If you supply a command it will be used for automatic bisection. Its + exit status will be used as flag to mark revision as bad or good (good + in case of 0 and bad in any other case). """ - # backward compatibility - if rev in "good bad reset init".split(): - ui.warn(_("(use of 'hg bisect ' is deprecated)\n")) - cmd, rev, extra = rev, extra, None - if cmd == "good": - good = True - elif cmd == "bad": - bad = True - else: - reset = True - elif extra or good + bad + skip + reset > 1: - raise util.Abort(_('incompatible arguments')) - - if reset: - p = repo.join("bisect.state") - if os.path.exists(p): - os.unlink(p) - return - - # load state - state = {'good': [], 'bad': [], 'skip': []} - if os.path.exists(repo.join("bisect.state")): - for l in repo.opener("bisect.state"): - kind, node = l[:-1].split() - node = repo.lookup(node) - if kind not in state: - raise util.Abort(_("unknown bisect kind %s") % kind) - state[kind].append(node) - - # update state - node = repo.lookup(rev or '.') - if good: - state['good'].append(node) - elif bad: - state['bad'].append(node) - elif skip: - state['skip'].append(node) - - # save state - f = repo.opener("bisect.state", "w", atomictemp=True) - wlock = repo.wlock() - try: - for kind in state: - for node in state[kind]: - f.write("%s %s\n" % (kind, hex(node))) - f.rename() - finally: - del wlock - - if not state['good'] or not state['bad']: - if (good or bad or skip or reset): - return - if not state['good']: - raise util.Abort(_('cannot bisect (no known good revisions)')) - else: - raise util.Abort(_('cannot bisect (no known bad revisions)')) - - # actually bisect - nodes, changesets, good = hbisect.bisect(repo.changelog, state) - if changesets == 0: + def print_result(nodes, good): displayer = cmdutil.show_changeset(ui, repo, {}) transition = (good and "good" or "bad") if len(nodes) == 1: @@ -348,6 +293,74 @@ "%s revision could be any of:\n") % transition) for n in nodes: displayer.show(changenode=n) + + def check_state(state, interactive=True): + if not state['good'] or not state['bad']: + if (good or bad or skip or reset) and interactive: + return + if not state['good']: + raise util.Abort(_('cannot bisect (no known good revisions)')) + else: + raise util.Abort(_('cannot bisect (no known bad revisions)')) + return True + + # backward compatibility + if rev in "good bad reset init".split(): + ui.warn(_("(use of 'hg bisect ' is deprecated)\n")) + cmd, rev, extra = rev, extra, None + if cmd == "good": + good = True + elif cmd == "bad": + bad = True + else: + reset = True + elif extra or good + bad + skip + reset + bool(command) > 1: + raise util.Abort(_('incompatible arguments')) + + if reset: + p = repo.join("bisect.state") + if os.path.exists(p): + os.unlink(p) + return + + state = hbisect.load_state(repo) + + if command: + changesets = 1 + while changesets: + # check state + status = bool(list(os.popen3(command)[2])) + node = repo.lookup(rev or '.') + transition = (status and 'bad' or 'good') + state[transition].append(node) + ui.note(_('Changeset %s: %s\n') % (short(node), transition)) + check_state(state, interactive=False) + # bisect + nodes, changesets, good = hbisect.bisect(repo.changelog, state) + # update to next check + cmdutil.bail_if_changed(repo) + hg.clean(repo, nodes[0], show_stats=False) + hbisect.save_state(repo, state) + return print_result(nodes, not status) + + # update state + node = repo.lookup(rev or '.') + if good: + state['good'].append(node) + elif bad: + state['bad'].append(node) + elif skip: + state['skip'].append(node) + + hbisect.save_state(repo, state) + + if not check_state(state): + return + + # actually bisect + nodes, changesets, good = hbisect.bisect(repo.changelog, state) + if changesets == 0: + print_result(nodes, good) else: assert len(nodes) == 1 # only a single node can be tested next node = nodes[0] @@ -3008,8 +3021,9 @@ ('g', 'good', False, _('mark changeset good')), ('b', 'bad', False, _('mark changeset bad')), ('s', 'skip', False, _('skip testing changeset')), + ('c', 'command', '', _('Use command to check changeset state')), ('U', 'noupdate', False, _('do not update to target'))], - _("hg bisect [-gbsr] [REV]")), + _("hg bisect [-gbsr] [REV] [-c COMMAND]")), "branch": (branch, [('f', 'force', None, diff -r b71a52f101dc -r e1afb50ec2aa mercurial/hbisect.py --- a/mercurial/hbisect.py Tue Oct 14 21:28:49 2008 +0200 +++ b/mercurial/hbisect.py Fri Oct 10 16:58:14 2008 +0300 @@ -7,8 +7,9 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. +import os from i18n import _ -from node import short +from node import short, hex import util def bisect(changelog, state): @@ -116,3 +117,28 @@ best_node = changelog.node(best_rev) return ([best_node], tot, good) + + +def load_state(repo): + state = {'good': [], 'bad': [], 'skip': []} + if os.path.exists(repo.join("bisect.state")): + for l in repo.opener("bisect.state"): + kind, node = l[:-1].split() + node = repo.lookup(node) + if kind not in state: + raise util.Abort(_("unknown bisect kind %s") % kind) + state[kind].append(node) + return state + + +def save_state(repo, state): + f = repo.opener("bisect.state", "w", atomictemp=True) + wlock = repo.wlock() + try: + for kind in state: + for node in state[kind]: + f.write("%s %s\n" % (kind, hex(node))) + f.rename() + finally: + del wlock +