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'))], |