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