comparison mercurial/commands.py @ 7227:e1afb50ec2aa

bisect: ability to check revision with command
author Alexander Solovyov <piranha@piranha.org.ua>
date Fri, 10 Oct 2008 16:58:14 +0300
parents b4c035057d34
children 9b72c732ed2f
comparison
equal deleted inserted replaced
7226:b71a52f101dc 7227:e1afb50ec2aa
258 ui.status(_('the backout changeset is a new head - ' 258 ui.status(_('the backout changeset is a new head - '
259 'do not forget to merge\n')) 259 'do not forget to merge\n'))
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
267 This command helps to find changesets which introduce problems. 267 This command helps to find changesets which introduce problems.
268 To use, mark the earliest changeset you know exhibits the problem 268 To use, mark the earliest changeset you know exhibits the problem
273 or good and bisect will either update to another candidate changeset 273 or good and bisect will either update to another candidate changeset
274 or announce that it has found the bad revision. 274 or announce that it has found the bad revision.
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 """ 278
279 # backward compatibility 279 If you supply a command it will be used for automatic bisection. Its
280 if rev in "good bad reset init".split(): 280 exit status will be used as flag to mark revision as bad or good (good
281 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n")) 281 in case of 0 and bad in any other case).
282 cmd, rev, extra = rev, extra, None 282 """
283 if cmd == "good": 283 def print_result(nodes, 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:
342 # narrowed it down to a single revision 287 # narrowed it down to a single revision
343 ui.write(_("The first %s revision is:\n") % transition) 288 ui.write(_("The first %s revision is:\n") % transition)
346 # multiple possible revisions 291 # multiple possible revisions
347 ui.write(_("Due to skipped revisions, the first " 292 ui.write(_("Due to skipped revisions, the first "
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]
354 # compute the approximate number of remaining tests 367 # compute the approximate number of remaining tests
355 tests, size = 0, 2 368 tests, size = 0, 2
3006 (bisect, 3019 (bisect,
3007 [('r', 'reset', False, _('reset bisect state')), 3020 [('r', 'reset', False, _('reset bisect state')),
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,
3016 _('set branch name even if it shadows an existing branch')), 3030 _('set branch name even if it shadows an existing branch')),
3017 ('C', 'clean', None, _('reset branch name to parent branch name'))], 3031 ('C', 'clean', None, _('reset branch name to parent branch name'))],