bisect: clarify some bisection code
authorMatt Mackall <mpm@selenic.com>
Thu, 27 Dec 2007 23:55:39 -0600
changeset 5721 8d63fa48d44a
parent 5720 bd86c9f2f697
child 5722 862239055c2e
bisect: clarify some bisection code - rename __ancestors... to candidates - remove head parameter (it's always badrev) - eliminate comprehensions to shrink parents - num_ancestors -> n_child name fix - clarify node selection
hgext/hbisect.py
--- a/hgext/hbisect.py	Thu Dec 27 23:55:39 2007 -0600
+++ b/hgext/hbisect.py	Thu Dec 27 23:55:39 2007 -0600
@@ -66,7 +66,7 @@
         self.is_reset = True
         return 0
 
-    def __ancestors_and_nb_ancestors(self, head):
+    def candidates(self):
         """
         returns (anc, n_child) where anc is the set of the ancestors of head
         and n_child is a dictionary with the following mapping:
@@ -76,39 +76,41 @@
         """
         cl = self.repo.changelog
         stop = {}
+        bad = self.badrev
         for i in xrange(len(self.goodrevs)-1, -1, -1):
             g = self.goodrevs[i]
             if g in stop:
                 continue
             stop.update(cl.reachable(g))
 
-        if head in stop:
+        if bad in stop:
             raise util.Abort(_("Inconsistent state, %s:%s is good and bad")
-                             % (cl.rev(head), hg.short(head)))
+                             % (cl.rev(bad), hg.short(bad)))
 
         # build a dict of node -> [number of children, {}]
-        n_child = {head: [0, {}]}
-        for i in xrange(cl.rev(head)+1):
+        n_child = {bad: [0, {}]}
+        for i in xrange(cl.rev(bad)+1):
             n = cl.node(i)
-            if n not in d:
+            if n not in n_child:
                 n_child[n] = [0, {}]
-            parents = [p for p in cl.parents(n) if p != hg.nullid]
-            for p in parents:
-                n_child[p][0] += 1
+            # increment count of each parent
+            for p in cl.parents(n):
+                if p != hg.nullid:
+                    n_child[p][0] += 1
 
-        for i in xrange(cl.rev(head)+1):
+        for i in xrange(cl.rev(bad)+1):
             n = cl.node(i)
-            parents = [p for p in cl.parents(n) if p != hg.nullid]
-            for p in parents:
-                n_child[p][0] -= 1
-                if not n in stop:
-                    n_child[n][1].update(n_child[p][1])
-                if n_child[p][0] == 0:
-                    n_child[p] = len(n_child[p][1])
+            for p in cl.parents(n):
+                if p != hg.nullid:
+                    n_child[p][0] -= 1
+                    if not n in stop:
+                        n_child[n][1].update(n_child[p][1])
+                    if n_child[p][0] == 0:
+                        n_child[p] = len(n_child[p][1])
             if not n in stop:
                 n_child[n][1][n] = 1
             if n_child[n][0] == 0:
-                if n == head:
+                if n == bad:
                     anc = n_child[n][1]
                 n_child[n] = len(n_child[n][1])
         return anc, n_child
@@ -119,8 +121,9 @@
         if not self.goodrevs:
             self.ui.warn(_("No good revision given\n"))
             self.ui.warn(_("Marking the first revision as good\n"))
-        ancestors, num_ancestors = self.__ancestors_and_nb_ancestors(
-                                    self.badrev)
+        ancestors, n_child = self.candidates()
+
+        # have we narrowed it down to one entry?
         tot = len(ancestors)
         if tot == 1:
             if self.badrev not in ancestors:
@@ -129,20 +132,26 @@
             displayer = cmdutil.show_changeset(self.ui, self.repo, {})
             displayer.show(changenode=self.badrev)
             return None
+
+        # find the best node to test
         best_rev = None
         best_len = -1
         for n in ancestors:
-            l = num_ancestors[n]
-            l = min(l, tot - l)
-            if l > best_len:
-                best_len = l
+            a = n_child[n] # number of children
+            b = tot - a # number of ancestors
+            value = min(a, b) # how good is this test?
+            if value > best_len:
+                best_len = value
                 best_rev = n
         assert best_rev is not None
+
+        # compute the approximate number of remaining tests
         nb_tests = 0
         q, r = divmod(tot, 2)
         while q:
             nb_tests += 1
             q, r = divmod(q, 2)
+
         msg = _("Testing changeset %s:%s (%s changesets remaining, "
                 "~%s tests)\n") % (self.repo.changelog.rev(best_rev),
                                    hg.short(best_rev), tot, nb_tests)