bisect: handle search for bad to good transitions
authorMatt Mackall <mpm@selenic.com>
Mon, 31 Dec 2007 18:20:34 -0600
changeset 5776 35ec669cdd43
parent 5775 2dd202a6e15b
child 5777 51776e50bc8c
bisect: handle search for bad to good transitions Automatically detect whether we're looking for a bad to good transition rather than the usual good to bad transition by detecting when badrev is inside the good set and flipping good/bad.
mercurial/commands.py
mercurial/hbisect.py
tests/test-bisect
tests/test-bisect.out
--- a/mercurial/commands.py	Mon Dec 31 18:20:34 2007 -0600
+++ b/mercurial/commands.py	Mon Dec 31 18:20:34 2007 -0600
@@ -258,11 +258,6 @@
     working directory as bad or good and bisect will either update to
     another candidate changeset or announce that it has found the bad
     revision.
-
-    Note: bisect expects bad revisions to be descendants of good
-    revisions. If you are looking for the point at which a problem was
-    fixed, then make the problem-free state \"bad\" and the
-    problematic state \"good.\"
     """
     # backward compatibility
     if rev in "good bad reset init".split():
@@ -317,9 +312,9 @@
         return
 
     # actually bisect
-    node, changesets = hbisect.bisect(repo.changelog, state)
+    node, changesets, good = hbisect.bisect(repo.changelog, state)
     if changesets == 0:
-        ui.write(_("The first bad revision is:\n"))
+        ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
         displayer = cmdutil.show_changeset(ui, repo, {})
         displayer.show(changenode=node)
     elif node is not None:
--- a/mercurial/hbisect.py	Mon Dec 31 18:20:34 2007 -0600
+++ b/mercurial/hbisect.py	Mon Dec 31 18:20:34 2007 -0600
@@ -12,25 +12,36 @@
 
 def bisect(changelog, state):
     clparents = changelog.parentrevs
-    # only the earliest bad revision matters
-    badrev = min([changelog.rev(n) for n in state['bad']])
-    bad = changelog.node(badrev)
     skip = dict.fromkeys([changelog.rev(n) for n in state['skip']])
 
-    # build ancestors array
-    ancestors = [[]] * (changelog.count() + 1) # an extra for [-1]
+    def buildancestors(bad, good):
+        # only the earliest bad revision matters
+        badrev = min([changelog.rev(n) for n in bad])
+        goodrevs = [changelog.rev(n) for n in good]
+        # build ancestors array
+        ancestors = [[]] * (changelog.count() + 1) # an extra for [-1]
 
-    # clear good revs from array
-    for node in state['good']:
-        ancestors[changelog.rev(node)] = None
-    for rev in xrange(changelog.count(), -1, -1):
-        if ancestors[rev] is None:
-            for prev in clparents(rev):
-                ancestors[prev] = None
+        # clear good revs from array
+        for node in goodrevs:
+            ancestors[node] = None
+        for rev in xrange(changelog.count(), -1, -1):
+            if ancestors[rev] is None:
+                for prev in clparents(rev):
+                    ancestors[prev] = None
 
-    if ancestors[badrev] is None:
+        if ancestors[badrev] is None:
+            return None, None
+        return badrev, ancestors
+
+    good = 0
+    badrev, ancestors = buildancestors(state['bad'], state['good'])
+    if not ancestors: # looking for bad to good transition?
+        good = 1
+        badrev, ancestors = buildancestors(state['good'], state['bad'])
+    if not ancestors: # now we're confused
         raise util.Abort(_("Inconsistent state, %s:%s is good and bad")
                          % (badrev, hg.short(bad)))
+    bad = changelog.node(badrev)
 
     # build children dict
     children = {}
@@ -52,7 +63,7 @@
     # have we narrowed it down to one entry?
     tot = len(candidates)
     if tot == 1:
-        return (bad, 0)
+        return (bad, 0, good)
     perfect = tot / 2
 
     # find the best node to test
@@ -91,4 +102,4 @@
     assert best_rev is not None
     best_node = changelog.node(best_rev)
 
-    return (best_node, tot)
+    return (best_node, tot, good)
--- a/tests/test-bisect	Mon Dec 31 18:20:34 2007 -0600
+++ b/tests/test-bisect	Mon Dec 31 18:20:34 2007 -0600
@@ -31,3 +31,13 @@
 hg bisect -g
 hg bisect -b
 hg bisect -g
+
+echo % bisect reverse test
+hg bisect -r
+hg bisect -b null
+hg bisect -g tip
+hg bisect -g
+hg bisect -g
+hg bisect -g
+hg bisect -b
+hg bisect -g
--- a/tests/test-bisect.out	Mon Dec 31 18:20:34 2007 -0600
+++ b/tests/test-bisect.out	Mon Dec 31 18:20:34 2007 -0600
@@ -214,3 +214,20 @@
 date:        Thu Jan 01 00:00:29 1970 +0000
 summary:     msg 29
 
+% bisect reverse test
+Testing changeset 15:e7fa0811edb0 (32 changesets remaining, ~5 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+Testing changeset 7:03750880c6b5 (16 changesets remaining, ~4 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+Testing changeset 3:b53bea5e2fcb (8 changesets remaining, ~3 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+Testing changeset 1:5cd978ea5149 (4 changesets remaining, ~2 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+Testing changeset 2:db07c04beaca (2 changesets remaining, ~1 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+The first good revision is:
+changeset:   2:db07c04beaca
+user:        test
+date:        Thu Jan 01 00:00:02 1970 +0000
+summary:     msg 2
+