comparison mercurial/commands.py @ 5775:2dd202a6e15b

bisect: make bisect a built-in command
author Matt Mackall <mpm@selenic.com>
date Mon, 31 Dec 2007 18:20:34 -0600
parents 8a5d8fb59ed2
children 35ec669cdd43
comparison
equal deleted inserted replaced
5774:c850a8640981 5775:2dd202a6e15b
9 from i18n import _ 9 from i18n import _
10 import os, re, sys, urllib 10 import os, re, sys, urllib
11 import hg, util, revlog, bundlerepo, extensions 11 import hg, util, revlog, bundlerepo, extensions
12 import difflib, patch, time, help, mdiff, tempfile 12 import difflib, patch, time, help, mdiff, tempfile
13 import errno, version, socket 13 import errno, version, socket
14 import archival, changegroup, cmdutil, hgweb.server, sshserver 14 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
15 15
16 # Commands start here, listed alphabetically 16 # Commands start here, listed alphabetically
17 17
18 def add(ui, repo, *pats, **opts): 18 def add(ui, repo, *pats, **opts):
19 """add the specified files on the next commit 19 """add the specified files on the next commit
243 else: 243 else:
244 ui.status(_('the backout changeset is a new head - ' 244 ui.status(_('the backout changeset is a new head - '
245 'do not forget to merge\n')) 245 'do not forget to merge\n'))
246 ui.status(_('(use "backout --merge" ' 246 ui.status(_('(use "backout --merge" '
247 'if you want to auto-merge)\n')) 247 'if you want to auto-merge)\n'))
248
249 def bisect(ui, repo, rev=None, extra=None,
250 reset=None, good=None, bad=None, skip=None, noupdate=None):
251 """subdivision search of changesets
252
253 This command helps to find changesets which introduce problems.
254 To use, mark the earliest changeset you know exhibits the problem
255 as bad, then mark the latest changeset which is free from the
256 problem as good. Bisect will update your working directory to a
257 revision for testing. Once you have performed tests, mark the
258 working directory as bad or good and bisect will either update to
259 another candidate changeset or announce that it has found the bad
260 revision.
261
262 Note: bisect expects bad revisions to be descendants of good
263 revisions. If you are looking for the point at which a problem was
264 fixed, then make the problem-free state \"bad\" and the
265 problematic state \"good.\"
266 """
267 # backward compatibility
268 if rev in "good bad reset init".split():
269 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
270 cmd, rev, extra = rev, extra, None
271 if cmd == "good":
272 good = True
273 elif cmd == "bad":
274 bad = True
275 else:
276 reset = True
277 elif extra or good + bad + skip + reset > 1:
278 raise util.Abort("Incompatible arguments")
279
280 if reset:
281 p = repo.join("bisect.state")
282 if os.path.exists(p):
283 os.unlink(p)
284 return
285
286 # load state
287 state = {'good': [], 'bad': [], 'skip': []}
288 if os.path.exists(repo.join("bisect.state")):
289 for l in repo.opener("bisect.state"):
290 kind, node = l[:-1].split()
291 node = repo.lookup(node)
292 if kind not in state:
293 raise util.Abort(_("unknown bisect kind %s") % kind)
294 state[kind].append(node)
295
296 # update state
297 node = repo.lookup(rev or '.')
298 if good:
299 state['good'].append(node)
300 elif bad:
301 state['bad'].append(node)
302 elif skip:
303 state['skip'].append(node)
304
305 # save state
306 f = repo.opener("bisect.state", "w", atomictemp=True)
307 wlock = repo.wlock()
308 try:
309 for kind in state:
310 for node in state[kind]:
311 f.write("%s %s\n" % (kind, hg.hex(node)))
312 f.rename()
313 finally:
314 del wlock
315
316 if not state['good'] or not state['bad']:
317 return
318
319 # actually bisect
320 node, changesets = hbisect.bisect(repo.changelog, state)
321 if changesets == 0:
322 ui.write(_("The first bad revision is:\n"))
323 displayer = cmdutil.show_changeset(ui, repo, {})
324 displayer.show(changenode=node)
325 elif node is not None:
326 # compute the approximate number of remaining tests
327 tests, size = 0, 2
328 while size <= changesets:
329 tests, size = tests + 1, size * 2
330 rev = repo.changelog.rev(node)
331 ui.write(_("Testing changeset %s:%s "
332 "(%s changesets remaining, ~%s tests)\n")
333 % (rev, hg.short(node), changesets, tests))
334 if not noupdate:
335 cmdutil.bail_if_changed(repo)
336 return hg.clean(repo, node)
248 337
249 def branch(ui, repo, label=None, **opts): 338 def branch(ui, repo, label=None, **opts):
250 """set or show the current branch name 339 """set or show the current branch name
251 340
252 With no argument, show the current branch name. With one argument, 341 With no argument, show the current branch name. With one argument,
2656 _('merge with old dirstate parent after backout')), 2745 _('merge with old dirstate parent after backout')),
2657 ('', 'parent', '', _('parent to choose when backing out merge')), 2746 ('', 'parent', '', _('parent to choose when backing out merge')),
2658 ('r', 'rev', '', _('revision to backout')), 2747 ('r', 'rev', '', _('revision to backout')),
2659 ] + walkopts + commitopts + commitopts2, 2748 ] + walkopts + commitopts + commitopts2,
2660 _('hg backout [OPTION]... [-r] REV')), 2749 _('hg backout [OPTION]... [-r] REV')),
2750 "bisect": (bisect,
2751 [('r', 'reset', False, _('reset bisect state')),
2752 ('g', 'good', False, _('mark changeset good')),
2753 ('b', 'bad', False, _('mark changeset bad')),
2754 ('s', 'skip', False, _('skip testing changeset')),
2755 ('U', 'noupdate', False, _('do not update to target'))],
2756 _("hg bisect [-gbsr] [REV]")),
2661 "branch": 2757 "branch":
2662 (branch, 2758 (branch,
2663 [('f', 'force', None, 2759 [('f', 'force', None,
2664 _('set branch name even if it shadows an existing branch'))], 2760 _('set branch name even if it shadows an existing branch'))],
2665 _('hg branch [NAME]')), 2761 _('hg branch [NAME]')),