comparison hgext/hbisect.py @ 5721:8d63fa48d44a

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
author Matt Mackall <mpm@selenic.com>
date Thu, 27 Dec 2007 23:55:39 -0600
parents bd86c9f2f697
children 862239055c2e
comparison
equal deleted inserted replaced
5720:bd86c9f2f697 5721:8d63fa48d44a
64 #self.ui.write("Going back to tip\n") 64 #self.ui.write("Going back to tip\n")
65 #self.repo.update(self.repo.changelog.tip()) 65 #self.repo.update(self.repo.changelog.tip())
66 self.is_reset = True 66 self.is_reset = True
67 return 0 67 return 0
68 68
69 def __ancestors_and_nb_ancestors(self, head): 69 def candidates(self):
70 """ 70 """
71 returns (anc, n_child) where anc is the set of the ancestors of head 71 returns (anc, n_child) where anc is the set of the ancestors of head
72 and n_child is a dictionary with the following mapping: 72 and n_child is a dictionary with the following mapping:
73 node -> number of ancestors (self included) 73 node -> number of ancestors (self included)
74 74
75 ancestors of goodrevs are used as lower limit. 75 ancestors of goodrevs are used as lower limit.
76 """ 76 """
77 cl = self.repo.changelog 77 cl = self.repo.changelog
78 stop = {} 78 stop = {}
79 bad = self.badrev
79 for i in xrange(len(self.goodrevs)-1, -1, -1): 80 for i in xrange(len(self.goodrevs)-1, -1, -1):
80 g = self.goodrevs[i] 81 g = self.goodrevs[i]
81 if g in stop: 82 if g in stop:
82 continue 83 continue
83 stop.update(cl.reachable(g)) 84 stop.update(cl.reachable(g))
84 85
85 if head in stop: 86 if bad in stop:
86 raise util.Abort(_("Inconsistent state, %s:%s is good and bad") 87 raise util.Abort(_("Inconsistent state, %s:%s is good and bad")
87 % (cl.rev(head), hg.short(head))) 88 % (cl.rev(bad), hg.short(bad)))
88 89
89 # build a dict of node -> [number of children, {}] 90 # build a dict of node -> [number of children, {}]
90 n_child = {head: [0, {}]} 91 n_child = {bad: [0, {}]}
91 for i in xrange(cl.rev(head)+1): 92 for i in xrange(cl.rev(bad)+1):
92 n = cl.node(i) 93 n = cl.node(i)
93 if n not in d: 94 if n not in n_child:
94 n_child[n] = [0, {}] 95 n_child[n] = [0, {}]
95 parents = [p for p in cl.parents(n) if p != hg.nullid] 96 # increment count of each parent
96 for p in parents: 97 for p in cl.parents(n):
97 n_child[p][0] += 1 98 if p != hg.nullid:
98 99 n_child[p][0] += 1
99 for i in xrange(cl.rev(head)+1): 100
101 for i in xrange(cl.rev(bad)+1):
100 n = cl.node(i) 102 n = cl.node(i)
101 parents = [p for p in cl.parents(n) if p != hg.nullid] 103 for p in cl.parents(n):
102 for p in parents: 104 if p != hg.nullid:
103 n_child[p][0] -= 1 105 n_child[p][0] -= 1
104 if not n in stop: 106 if not n in stop:
105 n_child[n][1].update(n_child[p][1]) 107 n_child[n][1].update(n_child[p][1])
106 if n_child[p][0] == 0: 108 if n_child[p][0] == 0:
107 n_child[p] = len(n_child[p][1]) 109 n_child[p] = len(n_child[p][1])
108 if not n in stop: 110 if not n in stop:
109 n_child[n][1][n] = 1 111 n_child[n][1][n] = 1
110 if n_child[n][0] == 0: 112 if n_child[n][0] == 0:
111 if n == head: 113 if n == bad:
112 anc = n_child[n][1] 114 anc = n_child[n][1]
113 n_child[n] = len(n_child[n][1]) 115 n_child[n] = len(n_child[n][1])
114 return anc, n_child 116 return anc, n_child
115 117
116 def next(self): 118 def next(self):
117 if not self.badrev: 119 if not self.badrev:
118 raise util.Abort(_("You should give at least one bad revision")) 120 raise util.Abort(_("You should give at least one bad revision"))
119 if not self.goodrevs: 121 if not self.goodrevs:
120 self.ui.warn(_("No good revision given\n")) 122 self.ui.warn(_("No good revision given\n"))
121 self.ui.warn(_("Marking the first revision as good\n")) 123 self.ui.warn(_("Marking the first revision as good\n"))
122 ancestors, num_ancestors = self.__ancestors_and_nb_ancestors( 124 ancestors, n_child = self.candidates()
123 self.badrev) 125
126 # have we narrowed it down to one entry?
124 tot = len(ancestors) 127 tot = len(ancestors)
125 if tot == 1: 128 if tot == 1:
126 if self.badrev not in ancestors: 129 if self.badrev not in ancestors:
127 raise util.Abort(_("Could not find the first bad revision")) 130 raise util.Abort(_("Could not find the first bad revision"))
128 self.ui.write(_("The first bad revision is:\n")) 131 self.ui.write(_("The first bad revision is:\n"))
129 displayer = cmdutil.show_changeset(self.ui, self.repo, {}) 132 displayer = cmdutil.show_changeset(self.ui, self.repo, {})
130 displayer.show(changenode=self.badrev) 133 displayer.show(changenode=self.badrev)
131 return None 134 return None
135
136 # find the best node to test
132 best_rev = None 137 best_rev = None
133 best_len = -1 138 best_len = -1
134 for n in ancestors: 139 for n in ancestors:
135 l = num_ancestors[n] 140 a = n_child[n] # number of children
136 l = min(l, tot - l) 141 b = tot - a # number of ancestors
137 if l > best_len: 142 value = min(a, b) # how good is this test?
138 best_len = l 143 if value > best_len:
144 best_len = value
139 best_rev = n 145 best_rev = n
140 assert best_rev is not None 146 assert best_rev is not None
147
148 # compute the approximate number of remaining tests
141 nb_tests = 0 149 nb_tests = 0
142 q, r = divmod(tot, 2) 150 q, r = divmod(tot, 2)
143 while q: 151 while q:
144 nb_tests += 1 152 nb_tests += 1
145 q, r = divmod(q, 2) 153 q, r = divmod(q, 2)
154
146 msg = _("Testing changeset %s:%s (%s changesets remaining, " 155 msg = _("Testing changeset %s:%s (%s changesets remaining, "
147 "~%s tests)\n") % (self.repo.changelog.rev(best_rev), 156 "~%s tests)\n") % (self.repo.changelog.rev(best_rev),
148 hg.short(best_rev), tot, nb_tests) 157 hg.short(best_rev), tot, nb_tests)
149 self.ui.write(msg) 158 self.ui.write(msg)
150 return best_rev 159 return best_rev