Mercurial > hg-stable
annotate hgext/hbisect.py @ 5768:78d14403bdc7
bisect: use a dict for children
We fill in the children only for ancestors of badrev
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Mon, 31 Dec 2007 18:20:33 -0600 |
parents | dd5f8ed31057 |
children | 49809f4a38d8 |
rev | line source |
---|---|
1855
0ba9dee8cfbd
Fixed spacing/indentation, removed #! script header, added short description.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1854
diff
changeset
|
1 # bisect extension for mercurial |
1367
a7678cbd7c28
bisect extension for mercurial
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
diff
changeset
|
2 # |
1861
65949d1c9bf7
Added copyright information to hbisect.py
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1856
diff
changeset
|
3 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org> |
65949d1c9bf7
Added copyright information to hbisect.py
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1856
diff
changeset
|
4 # Inspired by git bisect, extension skeleton taken from mq.py. |
65949d1c9bf7
Added copyright information to hbisect.py
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1856
diff
changeset
|
5 # |
1367
a7678cbd7c28
bisect extension for mercurial
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
diff
changeset
|
6 # This software may be used and distributed according to the terms |
a7678cbd7c28
bisect extension for mercurial
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
diff
changeset
|
7 # of the GNU General Public License, incorporated herein by reference. |
a7678cbd7c28
bisect extension for mercurial
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
diff
changeset
|
8 |
3891 | 9 from mercurial.i18n import _ |
5731
19691160d7f5
bisect: remove unused imports
Matt Mackall <mpm@selenic.com>
parents:
5730
diff
changeset
|
10 from mercurial import hg, util, cmdutil |
5767
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
11 import os |
1367
a7678cbd7c28
bisect extension for mercurial
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
diff
changeset
|
12 |
5737 | 13 def _bisect(changelog, state): |
14 clparents = changelog.parentrevs | |
15 # only the earliest bad revision matters | |
16 badrev = min([changelog.rev(n) for n in state['bad']]) | |
17 bad = changelog.node(badrev) | |
1367
a7678cbd7c28
bisect extension for mercurial
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
diff
changeset
|
18 |
5737 | 19 # build ancestors array |
20 ancestors = [[]] * (changelog.count() + 1) # an extra for [-1] | |
1367
a7678cbd7c28
bisect extension for mercurial
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
diff
changeset
|
21 |
5737 | 22 # clear good revs from array |
23 for node in state['good']: | |
24 ancestors[changelog.rev(node)] = None | |
25 for rev in xrange(changelog.count(), -1, -1): | |
26 if ancestors[rev] is None: | |
27 for prev in clparents(rev): | |
28 ancestors[prev] = None | |
1367
a7678cbd7c28
bisect extension for mercurial
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
diff
changeset
|
29 |
5737 | 30 if ancestors[badrev] is None: |
31 raise util.Abort(_("Inconsistent state, %s:%s is good and bad") | |
32 % (badrev, hg.short(bad))) | |
5723
e3b09819496b
bisect: switch to rev-based calculation
Matt Mackall <mpm@selenic.com>
parents:
5722
diff
changeset
|
33 |
5768
78d14403bdc7
bisect: use a dict for children
Matt Mackall <mpm@selenic.com>
parents:
5767
diff
changeset
|
34 # build children dict |
78d14403bdc7
bisect: use a dict for children
Matt Mackall <mpm@selenic.com>
parents:
5767
diff
changeset
|
35 children = {} |
78d14403bdc7
bisect: use a dict for children
Matt Mackall <mpm@selenic.com>
parents:
5767
diff
changeset
|
36 visit = [badrev] |
78d14403bdc7
bisect: use a dict for children
Matt Mackall <mpm@selenic.com>
parents:
5767
diff
changeset
|
37 while visit: |
78d14403bdc7
bisect: use a dict for children
Matt Mackall <mpm@selenic.com>
parents:
5767
diff
changeset
|
38 rev = visit.pop(0) |
5767
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
39 if ancestors[rev] == []: |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
40 for prev in clparents(rev): |
5768
78d14403bdc7
bisect: use a dict for children
Matt Mackall <mpm@selenic.com>
parents:
5767
diff
changeset
|
41 if prev != -1: |
78d14403bdc7
bisect: use a dict for children
Matt Mackall <mpm@selenic.com>
parents:
5767
diff
changeset
|
42 if prev in children: |
78d14403bdc7
bisect: use a dict for children
Matt Mackall <mpm@selenic.com>
parents:
5767
diff
changeset
|
43 children[prev].append(rev) |
78d14403bdc7
bisect: use a dict for children
Matt Mackall <mpm@selenic.com>
parents:
5767
diff
changeset
|
44 else: |
78d14403bdc7
bisect: use a dict for children
Matt Mackall <mpm@selenic.com>
parents:
5767
diff
changeset
|
45 children[prev] = [rev] |
78d14403bdc7
bisect: use a dict for children
Matt Mackall <mpm@selenic.com>
parents:
5767
diff
changeset
|
46 visit.append(prev) |
5767
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
47 |
5737 | 48 # accumulate ancestor lists |
49 for rev in xrange(badrev + 1): | |
5767
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
50 l = ancestors[rev] |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
51 if l != None: |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
52 if not l: |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
53 a = [rev] |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
54 elif len(l) == 1: |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
55 a = l[0] + [rev] |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
56 else: |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
57 a = {} |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
58 for s in l: |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
59 a.update(dict.fromkeys(s)) |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
60 a[rev] = None |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
61 a = a.keys() |
5768
78d14403bdc7
bisect: use a dict for children
Matt Mackall <mpm@selenic.com>
parents:
5767
diff
changeset
|
62 for c in children.get(rev, []): |
5767
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
63 if ancestors[c]: |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
64 ancestors[c].append(a) |
5726
19cbe2aea2bc
bisect: switch individual ancestor lists from dict to list
Matt Mackall <mpm@selenic.com>
parents:
5725
diff
changeset
|
65 else: |
5767
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
66 ancestors[c] = [a] |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
67 ancestors[rev] = len(a) |
5721
8d63fa48d44a
bisect: clarify some bisection code
Matt Mackall <mpm@selenic.com>
parents:
5720
diff
changeset
|
68 |
5767
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
69 candidates = a # ancestors of badrev, last rev visited |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
70 del children |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
71 |
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
72 if badrev not in candidates: |
5737 | 73 raise util.Abort(_("Could not find the first bad revision")) |
5721
8d63fa48d44a
bisect: clarify some bisection code
Matt Mackall <mpm@selenic.com>
parents:
5720
diff
changeset
|
74 |
5737 | 75 # have we narrowed it down to one entry? |
5767
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
76 tot = len(candidates) |
5737 | 77 if tot == 1: |
78 return (bad, 0) | |
5734
944b231fa0e7
bisect: move reporting out of core bisect function
Matt Mackall <mpm@selenic.com>
parents:
5733
diff
changeset
|
79 |
5737 | 80 # find the best node to test |
81 best_rev = None | |
82 best_len = -1 | |
83 skip = dict.fromkeys([changelog.rev(n) for n in state['skip']]) | |
5767
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
84 for n in candidates: |
5737 | 85 if n in skip: |
86 continue | |
5767
dd5f8ed31057
bisect: propagate ancestor lists directly to children
Matt Mackall <mpm@selenic.com>
parents:
5766
diff
changeset
|
87 a = ancestors[n] # number of ancestors |
5737 | 88 b = tot - a # number of non-ancestors |
89 value = min(a, b) # how good is this test? | |
90 if value > best_len: | |
91 best_len = value | |
92 best_rev = n | |
93 assert best_rev is not None | |
94 best_node = changelog.node(best_rev) | |
1367
a7678cbd7c28
bisect extension for mercurial
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
diff
changeset
|
95 |
5737 | 96 return (best_node, tot) |
5733 | 97 |
5737 | 98 def bisect(ui, repo, rev=None, extra=None, |
5766
23caedc5a28f
bisect: add noupdate option
Matt Mackall <mpm@selenic.com>
parents:
5738
diff
changeset
|
99 reset=None, good=None, bad=None, skip=None, noupdate=None): |
5729
73646515c435
bisect: slightly improve the help message
Matt Mackall <mpm@selenic.com>
parents:
5728
diff
changeset
|
100 """Subdivision search of changesets |
4390
052062b98f26
Flesh out bisect help text
Brendan Cully <brendan@kublai.com>
parents:
3891
diff
changeset
|
101 |
5729
73646515c435
bisect: slightly improve the help message
Matt Mackall <mpm@selenic.com>
parents:
5728
diff
changeset
|
102 This extension helps to find changesets which introduce problems. |
73646515c435
bisect: slightly improve the help message
Matt Mackall <mpm@selenic.com>
parents:
5728
diff
changeset
|
103 To use, mark the earliest changeset you know exhibits the problem |
4390
052062b98f26
Flesh out bisect help text
Brendan Cully <brendan@kublai.com>
parents:
3891
diff
changeset
|
104 as bad, then mark the latest changeset which is free from the problem |
052062b98f26
Flesh out bisect help text
Brendan Cully <brendan@kublai.com>
parents:
3891
diff
changeset
|
105 as good. Bisect will update your working directory to a revision for |
052062b98f26
Flesh out bisect help text
Brendan Cully <brendan@kublai.com>
parents:
3891
diff
changeset
|
106 testing. Once you have performed tests, mark the working directory |
052062b98f26
Flesh out bisect help text
Brendan Cully <brendan@kublai.com>
parents:
3891
diff
changeset
|
107 as bad or good and bisect will either update to another candidate |
052062b98f26
Flesh out bisect help text
Brendan Cully <brendan@kublai.com>
parents:
3891
diff
changeset
|
108 changeset or announce that it has found the bad revision. |
052062b98f26
Flesh out bisect help text
Brendan Cully <brendan@kublai.com>
parents:
3891
diff
changeset
|
109 |
052062b98f26
Flesh out bisect help text
Brendan Cully <brendan@kublai.com>
parents:
3891
diff
changeset
|
110 Note: bisect expects bad revisions to be descendants of good revisions. |
052062b98f26
Flesh out bisect help text
Brendan Cully <brendan@kublai.com>
parents:
3891
diff
changeset
|
111 If you are looking for the point at which a problem was fixed, then make |
052062b98f26
Flesh out bisect help text
Brendan Cully <brendan@kublai.com>
parents:
3891
diff
changeset
|
112 the problem-free state "bad" and the problematic state "good." |
052062b98f26
Flesh out bisect help text
Brendan Cully <brendan@kublai.com>
parents:
3891
diff
changeset
|
113 |
1367
a7678cbd7c28
bisect extension for mercurial
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
diff
changeset
|
114 """ |
5735
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
115 # backward compatibility |
5737 | 116 if rev in "good bad reset init".split(): |
5735
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
117 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n")) |
5737 | 118 cmd, rev, extra = rev, extra, None |
5735
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
119 if cmd == "good": |
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
120 good = True |
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
121 elif cmd == "bad": |
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
122 bad = True |
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
123 else: |
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
124 reset = True |
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
125 elif extra or good + bad + skip + reset > 1: |
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
126 raise util.Abort("Incompatible arguments") |
1855
0ba9dee8cfbd
Fixed spacing/indentation, removed #! script header, added short description.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1854
diff
changeset
|
127 |
5737 | 128 if reset: |
129 p = repo.join("bisect.state") | |
130 if os.path.exists(p): | |
131 os.unlink(p) | |
132 return | |
133 | |
134 # load state | |
135 state = {'good': [], 'bad': [], 'skip': []} | |
136 if os.path.exists(repo.join("bisect.state")): | |
137 for l in repo.opener("bisect.state"): | |
138 kind, node = l[:-1].split() | |
139 node = repo.lookup(node) | |
140 if kind not in state: | |
141 raise util.Abort(_("unknown bisect kind %s") % kind) | |
142 state[kind].append(node) | |
143 | |
144 # update state | |
145 node = repo.lookup(rev or '.') | |
5735
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
146 if good: |
5737 | 147 state['good'].append(node) |
5735
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
148 elif bad: |
5737 | 149 state['bad'].append(node) |
5735
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
150 elif skip: |
5737 | 151 state['skip'].append(node) |
152 | |
153 # save state | |
5738
2a54e2b177b6
bisect: use proper locking when updating bisect.state
Matt Mackall <mpm@selenic.com>
parents:
5737
diff
changeset
|
154 f = repo.opener("bisect.state", "w", atomictemp=True) |
2a54e2b177b6
bisect: use proper locking when updating bisect.state
Matt Mackall <mpm@selenic.com>
parents:
5737
diff
changeset
|
155 wlock = repo.wlock() |
2a54e2b177b6
bisect: use proper locking when updating bisect.state
Matt Mackall <mpm@selenic.com>
parents:
5737
diff
changeset
|
156 try: |
2a54e2b177b6
bisect: use proper locking when updating bisect.state
Matt Mackall <mpm@selenic.com>
parents:
5737
diff
changeset
|
157 for kind in state: |
2a54e2b177b6
bisect: use proper locking when updating bisect.state
Matt Mackall <mpm@selenic.com>
parents:
5737
diff
changeset
|
158 for node in state[kind]: |
2a54e2b177b6
bisect: use proper locking when updating bisect.state
Matt Mackall <mpm@selenic.com>
parents:
5737
diff
changeset
|
159 f.write("%s %s\n" % (kind, hg.hex(node))) |
2a54e2b177b6
bisect: use proper locking when updating bisect.state
Matt Mackall <mpm@selenic.com>
parents:
5737
diff
changeset
|
160 f.rename() |
2a54e2b177b6
bisect: use proper locking when updating bisect.state
Matt Mackall <mpm@selenic.com>
parents:
5737
diff
changeset
|
161 finally: |
2a54e2b177b6
bisect: use proper locking when updating bisect.state
Matt Mackall <mpm@selenic.com>
parents:
5737
diff
changeset
|
162 del wlock |
5737 | 163 |
164 if not state['good'] or not state['bad']: | |
165 return | |
166 | |
167 # actually bisect | |
168 node, changesets = _bisect(repo.changelog, state) | |
169 if changesets == 0: | |
170 ui.write(_("The first bad revision is:\n")) | |
171 displayer = cmdutil.show_changeset(ui, repo, {}) | |
172 displayer.show(changenode=node) | |
173 elif node is not None: | |
174 # compute the approximate number of remaining tests | |
175 tests, size = 0, 2 | |
176 while size <= changesets: | |
177 tests, size = tests + 1, size * 2 | |
178 rev = repo.changelog.rev(node) | |
179 ui.write(_("Testing changeset %s:%s " | |
180 "(%s changesets remaining, ~%s tests)\n") | |
181 % (rev, hg.short(node), changesets, tests)) | |
5766
23caedc5a28f
bisect: add noupdate option
Matt Mackall <mpm@selenic.com>
parents:
5738
diff
changeset
|
182 if not noupdate: |
23caedc5a28f
bisect: add noupdate option
Matt Mackall <mpm@selenic.com>
parents:
5738
diff
changeset
|
183 cmdutil.bail_if_changed(repo) |
23caedc5a28f
bisect: add noupdate option
Matt Mackall <mpm@selenic.com>
parents:
5738
diff
changeset
|
184 return hg.clean(repo, node) |
1367
a7678cbd7c28
bisect extension for mercurial
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
diff
changeset
|
185 |
a7678cbd7c28
bisect extension for mercurial
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
diff
changeset
|
186 cmdtable = { |
5737 | 187 "bisect": (bisect, |
5735
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
188 [('r', 'reset', False, _('reset bisect state')), |
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
189 ('g', 'good', False, _('mark changeset good')), |
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
190 ('b', 'bad', False, _('mark changeset bad')), |
5766
23caedc5a28f
bisect: add noupdate option
Matt Mackall <mpm@selenic.com>
parents:
5738
diff
changeset
|
191 ('s', 'skip', False, _('skip testing changeset')), |
23caedc5a28f
bisect: add noupdate option
Matt Mackall <mpm@selenic.com>
parents:
5738
diff
changeset
|
192 ('U', 'noupdate', False, _('do not update to target'))], |
5735
9079081b8982
bisect: use more standard command syntax and help
Matt Mackall <mpm@selenic.com>
parents:
5734
diff
changeset
|
193 _("hg bisect [-gbsr] [REV]")) |
1367
a7678cbd7c28
bisect extension for mercurial
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
diff
changeset
|
194 } |