73 node -> number of ancestors (self included) |
73 node -> number of ancestors (self included) |
74 |
74 |
75 ancestors of goodnodes are used as lower limit. |
75 ancestors of goodnodes are used as lower limit. |
76 """ |
76 """ |
77 cl = self.repo.changelog |
77 cl = self.repo.changelog |
78 stop = {} |
78 clparents = cl.parentrevs |
79 bad = self.badnode |
79 bad = self.badnode |
80 for i in xrange(len(self.goodnodes)-1, -1, -1): |
80 badrev = cl.rev(bad) |
81 g = self.goodnodes[i] |
81 |
82 if g in stop: |
82 # add goodrevs and all ancestors to stop |
83 continue |
83 stop = dict.fromkeys([cl.rev(n) for n in self.goodnodes]) |
84 stop.update(cl.reachable(g)) |
84 for rev in xrange(cl.count(), -1, -1): |
85 |
85 if rev in stop: |
86 if bad in stop: |
86 for prev in clparents(rev): |
|
87 stop[prev] = None |
|
88 |
|
89 if badrev in stop: |
87 raise util.Abort(_("Inconsistent state, %s:%s is good and bad") |
90 raise util.Abort(_("Inconsistent state, %s:%s is good and bad") |
88 % (cl.rev(bad), hg.short(bad))) |
91 % (badrev, hg.short(bad))) |
89 |
92 |
90 # build a dict of node -> [number of children, {}] |
93 # build a dict of node -> [number of children, {}] |
91 n_child = {bad: [0, {}]} |
94 n_child = {badrev: [0, {}]} |
92 for i in xrange(cl.rev(bad)+1): |
95 for rev in xrange(badrev + 1): |
93 n = cl.node(i) |
96 if rev not in n_child: |
94 if n not in n_child: |
97 n_child[rev] = [0, {}] |
95 n_child[n] = [0, {}] |
|
96 # increment count of each parent |
98 # increment count of each parent |
97 for p in cl.parents(n): |
99 for p in clparents(rev): |
98 if p != hg.nullid: |
100 if p != hg.nullrev: |
99 n_child[p][0] += 1 |
101 n_child[p][0] += 1 |
100 |
102 |
101 for i in xrange(cl.rev(bad)+1): |
103 for rev in xrange(badrev + 1): |
102 n = cl.node(i) |
104 for p in clparents(rev): |
103 for p in cl.parents(n): |
105 if p != hg.nullrev: |
104 if p != hg.nullid: |
|
105 n_child[p][0] -= 1 |
106 n_child[p][0] -= 1 |
106 if not n in stop: |
107 if not rev in stop: |
107 n_child[n][1].update(n_child[p][1]) |
108 n_child[rev][1].update(n_child[p][1]) |
108 if n_child[p][0] == 0: |
109 if n_child[p][0] == 0: |
109 n_child[p] = len(n_child[p][1]) |
110 n_child[p] = len(n_child[p][1]) |
110 if not n in stop: |
111 if not rev in stop: |
111 n_child[n][1][n] = 1 |
112 n_child[rev][1][rev] = 1 |
112 if n_child[n][0] == 0: |
113 if n_child[rev][0] == 0: |
113 if n == bad: |
114 if rev == badrev: |
114 anc = n_child[n][1] |
115 anc = n_child[rev][1] |
115 n_child[n] = len(n_child[n][1]) |
116 n_child[rev] = len(n_child[rev][1]) |
|
117 |
|
118 if badrev not in anc: |
|
119 raise util.Abort(_("Could not find the first bad revision")) |
|
120 |
116 return anc, n_child |
121 return anc, n_child |
117 |
122 |
118 def next(self): |
123 def next(self): |
119 if not self.badnode: |
124 if not self.badnode: |
120 raise util.Abort(_("You should give at least one bad revision")) |
125 raise util.Abort(_("You should give at least one bad revision")) |
124 ancestors, n_child = self.candidates() |
129 ancestors, n_child = self.candidates() |
125 |
130 |
126 # have we narrowed it down to one entry? |
131 # have we narrowed it down to one entry? |
127 tot = len(ancestors) |
132 tot = len(ancestors) |
128 if tot == 1: |
133 if tot == 1: |
129 if self.badnode not in ancestors: |
|
130 raise util.Abort(_("Could not find the first bad revision")) |
|
131 self.ui.write(_("The first bad revision is:\n")) |
134 self.ui.write(_("The first bad revision is:\n")) |
132 displayer = cmdutil.show_changeset(self.ui, self.repo, {}) |
135 displayer = cmdutil.show_changeset(self.ui, self.repo, {}) |
133 displayer.show(changenode=self.badnode) |
136 displayer.show(changenode=self.badnode) |
134 return None |
137 return None |
135 |
138 |
136 # find the best node to test |
139 # find the best node to test |
137 best_node = None |
140 best_rev = None |
138 best_len = -1 |
141 best_len = -1 |
139 for n in ancestors: |
142 for n in ancestors: |
140 a = n_child[n] # number of children |
143 a = n_child[n] # number of children |
141 b = tot - a # number of ancestors |
144 b = tot - a # number of ancestors |
142 value = min(a, b) # how good is this test? |
145 value = min(a, b) # how good is this test? |
143 if value > best_len: |
146 if value > best_len: |
144 best_len = value |
147 best_len = value |
145 best_node = n |
148 best_rev = n |
146 assert best_node is not None |
149 assert best_rev is not None |
|
150 best_node = self.repo.changelog.node(best_rev) |
147 |
151 |
148 # compute the approximate number of remaining tests |
152 # compute the approximate number of remaining tests |
149 nb_tests = 0 |
153 nb_tests = 0 |
150 q, r = divmod(tot, 2) |
154 q, r = divmod(tot, 2) |
151 while q: |
155 while q: |
152 nb_tests += 1 |
156 nb_tests += 1 |
153 q, r = divmod(q, 2) |
157 q, r = divmod(q, 2) |
154 |
158 |
155 msg = _("Testing changeset %s:%s (%s changesets remaining, " |
159 self.ui.write(_("Testing changeset %s:%s " |
156 "~%s tests)\n") % (self.repo.changelog.rev(best_node), |
160 "(%s changesets remaining, ~%s tests)\n") |
157 hg.short(best_node), tot, nb_tests) |
161 % (best_rev, hg.short(best_node), tot, nb_tests)) |
158 self.ui.write(msg) |
|
159 return best_node |
162 return best_node |
160 |
163 |
161 def autonext(self): |
164 def autonext(self): |
162 """find and update to the next revision to test""" |
165 """find and update to the next revision to test""" |
163 node = self.next() |
166 node = self.next() |