##// END OF EJS Templates
bisect: ability to check revision with command
Alexander Solovyov -
r7227:e1afb50e default
parent child Browse files
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