Show More
@@ -260,7 +260,7 def backout(ui, repo, node=None, rev=Non | |||||
260 | ui.status(_('(use "backout --merge" ' |
|
260 | ui.status(_('(use "backout --merge" ' | |
261 | 'if you want to auto-merge)\n')) |
|
261 | 'if you want to auto-merge)\n')) | |
262 |
|
262 | |||
263 | def bisect(ui, repo, rev=None, extra=None, |
|
263 | def bisect(ui, repo, rev=None, extra=None, command=None, | |
264 | reset=None, good=None, bad=None, skip=None, noupdate=None): |
|
264 | reset=None, good=None, bad=None, skip=None, noupdate=None): | |
265 | """subdivision search of changesets |
|
265 | """subdivision search of changesets | |
266 |
|
266 | |||
@@ -275,67 +275,12 def bisect(ui, repo, rev=None, extra=Non | |||||
275 |
|
275 | |||
276 | As a shortcut, you can also use the revision argument to mark a |
|
276 | As a shortcut, you can also use the revision argument to mark a | |
277 | revision as good or bad without checking it out first. |
|
277 | revision as good or bad without checking it out first. | |
|
278 | ||||
|
279 | If you supply a command it will be used for automatic bisection. Its | |||
|
280 | exit status will be used as flag to mark revision as bad or good (good | |||
|
281 | in case of 0 and bad in any other case). | |||
278 | """ |
|
282 | """ | |
279 | # backward compatibility |
|
283 | def print_result(nodes, good): | |
280 | if rev in "good bad reset init".split(): |
|
|||
281 | ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n")) |
|
|||
282 | cmd, rev, extra = rev, extra, None |
|
|||
283 | if cmd == "good": |
|
|||
284 | good = True |
|
|||
285 | elif cmd == "bad": |
|
|||
286 | bad = True |
|
|||
287 | else: |
|
|||
288 | reset = True |
|
|||
289 | elif extra or good + bad + skip + reset > 1: |
|
|||
290 | raise util.Abort(_('incompatible arguments')) |
|
|||
291 |
|
||||
292 | if reset: |
|
|||
293 | p = repo.join("bisect.state") |
|
|||
294 | if os.path.exists(p): |
|
|||
295 | os.unlink(p) |
|
|||
296 | return |
|
|||
297 |
|
||||
298 | # load state |
|
|||
299 | state = {'good': [], 'bad': [], 'skip': []} |
|
|||
300 | if os.path.exists(repo.join("bisect.state")): |
|
|||
301 | for l in repo.opener("bisect.state"): |
|
|||
302 | kind, node = l[:-1].split() |
|
|||
303 | node = repo.lookup(node) |
|
|||
304 | if kind not in state: |
|
|||
305 | raise util.Abort(_("unknown bisect kind %s") % kind) |
|
|||
306 | state[kind].append(node) |
|
|||
307 |
|
||||
308 | # update state |
|
|||
309 | node = repo.lookup(rev or '.') |
|
|||
310 | if good: |
|
|||
311 | state['good'].append(node) |
|
|||
312 | elif bad: |
|
|||
313 | state['bad'].append(node) |
|
|||
314 | elif skip: |
|
|||
315 | state['skip'].append(node) |
|
|||
316 |
|
||||
317 | # save state |
|
|||
318 | f = repo.opener("bisect.state", "w", atomictemp=True) |
|
|||
319 | wlock = repo.wlock() |
|
|||
320 | try: |
|
|||
321 | for kind in state: |
|
|||
322 | for node in state[kind]: |
|
|||
323 | f.write("%s %s\n" % (kind, hex(node))) |
|
|||
324 | f.rename() |
|
|||
325 | finally: |
|
|||
326 | del wlock |
|
|||
327 |
|
||||
328 | if not state['good'] or not state['bad']: |
|
|||
329 | if (good or bad or skip or reset): |
|
|||
330 | return |
|
|||
331 | if not state['good']: |
|
|||
332 | raise util.Abort(_('cannot bisect (no known good revisions)')) |
|
|||
333 | else: |
|
|||
334 | raise util.Abort(_('cannot bisect (no known bad revisions)')) |
|
|||
335 |
|
||||
336 | # actually bisect |
|
|||
337 | nodes, changesets, good = hbisect.bisect(repo.changelog, state) |
|
|||
338 | if changesets == 0: |
|
|||
339 | displayer = cmdutil.show_changeset(ui, repo, {}) |
|
284 | displayer = cmdutil.show_changeset(ui, repo, {}) | |
340 | transition = (good and "good" or "bad") |
|
285 | transition = (good and "good" or "bad") | |
341 | if len(nodes) == 1: |
|
286 | if len(nodes) == 1: | |
@@ -348,6 +293,74 def bisect(ui, repo, rev=None, extra=Non | |||||
348 | "%s revision could be any of:\n") % transition) |
|
293 | "%s revision could be any of:\n") % transition) | |
349 | for n in nodes: |
|
294 | for n in nodes: | |
350 | displayer.show(changenode=n) |
|
295 | displayer.show(changenode=n) | |
|
296 | ||||
|
297 | def check_state(state, interactive=True): | |||
|
298 | if not state['good'] or not state['bad']: | |||
|
299 | if (good or bad or skip or reset) and interactive: | |||
|
300 | return | |||
|
301 | if not state['good']: | |||
|
302 | raise util.Abort(_('cannot bisect (no known good revisions)')) | |||
|
303 | else: | |||
|
304 | raise util.Abort(_('cannot bisect (no known bad revisions)')) | |||
|
305 | return True | |||
|
306 | ||||
|
307 | # backward compatibility | |||
|
308 | if rev in "good bad reset init".split(): | |||
|
309 | ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n")) | |||
|
310 | cmd, rev, extra = rev, extra, None | |||
|
311 | if cmd == "good": | |||
|
312 | good = True | |||
|
313 | elif cmd == "bad": | |||
|
314 | bad = True | |||
|
315 | else: | |||
|
316 | reset = True | |||
|
317 | elif extra or good + bad + skip + reset + bool(command) > 1: | |||
|
318 | raise util.Abort(_('incompatible arguments')) | |||
|
319 | ||||
|
320 | if reset: | |||
|
321 | p = repo.join("bisect.state") | |||
|
322 | if os.path.exists(p): | |||
|
323 | os.unlink(p) | |||
|
324 | return | |||
|
325 | ||||
|
326 | state = hbisect.load_state(repo) | |||
|
327 | ||||
|
328 | if command: | |||
|
329 | changesets = 1 | |||
|
330 | while changesets: | |||
|
331 | # check state | |||
|
332 | status = bool(list(os.popen3(command)[2])) | |||
|
333 | node = repo.lookup(rev or '.') | |||
|
334 | transition = (status and 'bad' or 'good') | |||
|
335 | state[transition].append(node) | |||
|
336 | ui.note(_('Changeset %s: %s\n') % (short(node), transition)) | |||
|
337 | check_state(state, interactive=False) | |||
|
338 | # bisect | |||
|
339 | nodes, changesets, good = hbisect.bisect(repo.changelog, state) | |||
|
340 | # update to next check | |||
|
341 | cmdutil.bail_if_changed(repo) | |||
|
342 | hg.clean(repo, nodes[0], show_stats=False) | |||
|
343 | hbisect.save_state(repo, state) | |||
|
344 | return print_result(nodes, not status) | |||
|
345 | ||||
|
346 | # update state | |||
|
347 | node = repo.lookup(rev or '.') | |||
|
348 | if good: | |||
|
349 | state['good'].append(node) | |||
|
350 | elif bad: | |||
|
351 | state['bad'].append(node) | |||
|
352 | elif skip: | |||
|
353 | state['skip'].append(node) | |||
|
354 | ||||
|
355 | hbisect.save_state(repo, state) | |||
|
356 | ||||
|
357 | if not check_state(state): | |||
|
358 | return | |||
|
359 | ||||
|
360 | # actually bisect | |||
|
361 | nodes, changesets, good = hbisect.bisect(repo.changelog, state) | |||
|
362 | if changesets == 0: | |||
|
363 | print_result(nodes, good) | |||
351 | else: |
|
364 | else: | |
352 | assert len(nodes) == 1 # only a single node can be tested next |
|
365 | assert len(nodes) == 1 # only a single node can be tested next | |
353 | node = nodes[0] |
|
366 | node = nodes[0] | |
@@ -3008,8 +3021,9 table = { | |||||
3008 | ('g', 'good', False, _('mark changeset good')), |
|
3021 | ('g', 'good', False, _('mark changeset good')), | |
3009 | ('b', 'bad', False, _('mark changeset bad')), |
|
3022 | ('b', 'bad', False, _('mark changeset bad')), | |
3010 | ('s', 'skip', False, _('skip testing changeset')), |
|
3023 | ('s', 'skip', False, _('skip testing changeset')), | |
|
3024 | ('c', 'command', '', _('Use command to check changeset state')), | |||
3011 | ('U', 'noupdate', False, _('do not update to target'))], |
|
3025 | ('U', 'noupdate', False, _('do not update to target'))], | |
3012 | _("hg bisect [-gbsr] [REV]")), |
|
3026 | _("hg bisect [-gbsr] [REV] [-c COMMAND]")), | |
3013 | "branch": |
|
3027 | "branch": | |
3014 | (branch, |
|
3028 | (branch, | |
3015 | [('f', 'force', None, |
|
3029 | [('f', 'force', None, |
@@ -7,8 +7,9 | |||||
7 | # This software may be used and distributed according to the terms |
|
7 | # This software may be used and distributed according to the terms | |
8 | # of the GNU General Public License, incorporated herein by reference. |
|
8 | # of the GNU General Public License, incorporated herein by reference. | |
9 |
|
9 | |||
|
10 | import os | |||
10 | from i18n import _ |
|
11 | from i18n import _ | |
11 | from node import short |
|
12 | from node import short, hex | |
12 | import util |
|
13 | import util | |
13 |
|
14 | |||
14 | def bisect(changelog, state): |
|
15 | def bisect(changelog, state): | |
@@ -116,3 +117,28 def bisect(changelog, state): | |||||
116 | best_node = changelog.node(best_rev) |
|
117 | best_node = changelog.node(best_rev) | |
117 |
|
118 | |||
118 | return ([best_node], tot, good) |
|
119 | return ([best_node], tot, good) | |
|
120 | ||||
|
121 | ||||
|
122 | def load_state(repo): | |||
|
123 | state = {'good': [], 'bad': [], 'skip': []} | |||
|
124 | if os.path.exists(repo.join("bisect.state")): | |||
|
125 | for l in repo.opener("bisect.state"): | |||
|
126 | kind, node = l[:-1].split() | |||
|
127 | node = repo.lookup(node) | |||
|
128 | if kind not in state: | |||
|
129 | raise util.Abort(_("unknown bisect kind %s") % kind) | |||
|
130 | state[kind].append(node) | |||
|
131 | return state | |||
|
132 | ||||
|
133 | ||||
|
134 | def save_state(repo, state): | |||
|
135 | f = repo.opener("bisect.state", "w", atomictemp=True) | |||
|
136 | wlock = repo.wlock() | |||
|
137 | try: | |||
|
138 | for kind in state: | |||
|
139 | for node in state[kind]: | |||
|
140 | f.write("%s %s\n" % (kind, hex(node))) | |||
|
141 | f.rename() | |||
|
142 | finally: | |||
|
143 | del wlock | |||
|
144 |
General Comments 0
You need to be logged in to leave comments.
Login now