Mercurial > public > mercurial-scm > hg
comparison mercurial/commands.py @ 15241:e4d135632f6d
graft: add --continue support
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Wed, 12 Oct 2011 18:48:57 -0500 |
parents | bfb93963bb39 |
children | dac2edce4e4a |
comparison
equal
deleted
inserted
replaced
15240:bfb93963bb39 | 15241:e4d135632f6d |
---|---|
2447 repo[None].forget(forget) | 2447 repo[None].forget(forget) |
2448 return errs | 2448 return errs |
2449 | 2449 |
2450 @command( | 2450 @command( |
2451 'graft', | 2451 'graft', |
2452 [('e', 'edit', False, _('invoke editor on commit messages')), | 2452 [('c', 'continue', False, _('resume interrupted graft')), |
2453 ('D', 'currentdate', False, | 2453 ('e', 'edit', False, _('invoke editor on commit messages')), |
2454 _('record the current date as commit date')), | 2454 ('D', 'currentdate', False, |
2455 ('U', 'currentuser', False, | 2455 _('record the current date as commit date')), |
2456 _('record the current user as committer'), _('DATE'))] | 2456 ('U', 'currentuser', False, |
2457 _('record the current user as committer'), _('DATE'))] | |
2457 + commitopts2 + mergetoolopts, | 2458 + commitopts2 + mergetoolopts, |
2458 _('[OPTION]... REVISION...')) | 2459 _('[OPTION]... REVISION...')) |
2459 def graft(ui, repo, rev, *revs, **opts): | 2460 def graft(ui, repo, *revs, **opts): |
2460 '''copy changes from other branches onto the current branch | 2461 '''copy changes from other branches onto the current branch |
2461 | 2462 |
2462 This command uses Mercurial's merge logic to copy individual | 2463 This command uses Mercurial's merge logic to copy individual |
2463 changes from other branches without merging branches in the | 2464 changes from other branches without merging branches in the |
2464 history graph. This is sometimes known as 'backporting' or | 2465 history graph. This is sometimes known as 'backporting' or |
2465 'cherry-picking'. | 2466 'cherry-picking'. |
2466 | 2467 |
2467 Changesets that are ancestors of the current revision, that have | 2468 Changesets that are ancestors of the current revision, that have |
2468 already been grafted, or that are merges will be skipped. | 2469 already been grafted, or that are merges will be skipped. |
2469 | 2470 |
2471 If a graft merge results in conflicts, the graft process is | |
2472 aborted so that the current merge can be manually resolved. Once | |
2473 all conflicts are addressed, the graft process can be continued | |
2474 with the -c/--continue option. | |
2475 | |
2476 .. note:: | |
2477 The -c/--continue option does not reapply earlier options. | |
2478 | |
2470 Returns 0 on successful completion. | 2479 Returns 0 on successful completion. |
2471 ''' | 2480 ''' |
2472 | |
2473 cmdutil.bailifchanged(repo) | |
2474 | 2481 |
2475 if not opts.get('user') and opts.get('currentuser'): | 2482 if not opts.get('user') and opts.get('currentuser'): |
2476 opts['user'] = ui.username() | 2483 opts['user'] = ui.username() |
2477 if not opts.get('date') and opts.get('currentdate'): | 2484 if not opts.get('date') and opts.get('currentdate'): |
2478 opts['date'] = "%d %d" % util.makedate() | 2485 opts['date'] = "%d %d" % util.makedate() |
2479 | 2486 |
2480 editor = None | 2487 editor = None |
2481 if opts.get('edit'): | 2488 if opts.get('edit'): |
2482 editor = cmdutil.commitforceeditor | 2489 editor = cmdutil.commitforceeditor |
2483 | 2490 |
2484 revs = [rev] + list(revs) | 2491 cont = False |
2485 revs = scmutil.revrange(repo, revs) | 2492 if opts['continue']: |
2493 cont = True | |
2494 if revs: | |
2495 raise util.Abort(_("can't specify --continue and revisions")) | |
2496 # read in unfinished revisions | |
2497 try: | |
2498 nodes = repo.opener.read('graftstate').splitlines() | |
2499 revs = [repo[node].rev() for node in nodes] | |
2500 except IOError, inst: | |
2501 if inst.errno != errno.ENOENT: | |
2502 raise | |
2503 raise util.Abort(_("no graft state found, can't continue")) | |
2504 else: | |
2505 cmdutil.bailifchanged(repo) | |
2506 if not revs: | |
2507 raise util.Abort(_('no revisions specified')) | |
2508 revs = scmutil.revrange(repo, revs) | |
2486 | 2509 |
2487 # check for merges | 2510 # check for merges |
2488 for ctx in repo.set('%ld and merge()', revs): | 2511 for ctx in repo.set('%ld and merge()', revs): |
2489 ui.warn(_('skipping ungraftable merge revision %s\n') % ctx.rev()) | 2512 ui.warn(_('skipping ungraftable merge revision %s\n') % ctx.rev()) |
2490 revs.remove(ctx.rev()) | 2513 revs.remove(ctx.rev()) |
2507 ui.warn(_('skipping already grafted revision %s\n') % r) | 2530 ui.warn(_('skipping already grafted revision %s\n') % r) |
2508 revs.remove(r) | 2531 revs.remove(r) |
2509 if not revs: | 2532 if not revs: |
2510 return -1 | 2533 return -1 |
2511 | 2534 |
2512 for ctx in repo.set("%ld", revs): | 2535 for pos, ctx in enumerate(repo.set("%ld", revs)): |
2513 current = repo['.'] | 2536 current = repo['.'] |
2514 ui.debug('grafting revision %s', ctx.rev()) | 2537 ui.debug('grafting revision %s', ctx.rev()) |
2515 # perform the graft merge with p1(rev) as 'ancestor' | 2538 |
2516 try: | 2539 # we don't merge the first commit when continuing |
2517 # ui.forcemerge is an internal variable, do not document | 2540 if not cont: |
2518 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', '')) | 2541 # perform the graft merge with p1(rev) as 'ancestor' |
2519 stats = mergemod.update(repo, ctx.node(), True, True, False, | 2542 try: |
2520 ctx.p1().node()) | 2543 # ui.forcemerge is an internal variable, do not document |
2521 finally: | 2544 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', '')) |
2522 ui.setconfig('ui', 'forcemerge', '') | 2545 stats = mergemod.update(repo, ctx.node(), True, True, False, |
2523 # drop the second merge parent | 2546 ctx.p1().node()) |
2524 repo.dirstate.setparents(current.node(), nullid) | 2547 finally: |
2525 repo.dirstate.write() | 2548 ui.setconfig('ui', 'forcemerge', '') |
2526 # fix up dirstate for copies and renames | 2549 # drop the second merge parent |
2527 cmdutil.duplicatecopies(repo, ctx.rev(), current.node(), nullid) | 2550 repo.dirstate.setparents(current.node(), nullid) |
2528 # report any conflicts | 2551 repo.dirstate.write() |
2529 if stats and stats[3] > 0: | 2552 # fix up dirstate for copies and renames |
2530 raise util.Abort(_("unresolved conflicts, can't continue"), | 2553 cmdutil.duplicatecopies(repo, ctx.rev(), current.node(), nullid) |
2531 hint=_('use hg resolve and hg graft --continue')) | 2554 # report any conflicts |
2555 if stats and stats[3] > 0: | |
2556 # write out state for --continue | |
2557 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]] | |
2558 repo.opener.write('graftstate', ''.join(nodelines)) | |
2559 raise util.Abort( | |
2560 _("unresolved conflicts, can't continue"), | |
2561 hint=_('use hg resolve and hg graft --continue')) | |
2562 else: | |
2563 cont = False | |
2564 | |
2532 # commit | 2565 # commit |
2533 extra = {'source': ctx.hex()} | 2566 extra = {'source': ctx.hex()} |
2534 user = ctx.user() | 2567 user = ctx.user() |
2535 if opts.get('user'): | 2568 if opts.get('user'): |
2536 user = opts['user'] | 2569 user = opts['user'] |
2537 date = ctx.date() | 2570 date = ctx.date() |
2538 if opts.get('date'): | 2571 if opts.get('date'): |
2539 date = opts['date'] | 2572 date = opts['date'] |
2540 repo.commit(text=ctx.description(), user=user, | 2573 repo.commit(text=ctx.description(), user=user, |
2541 date=date, extra=extra, editor=editor) | 2574 date=date, extra=extra, editor=editor) |
2575 | |
2576 # remove state when we complete successfully | |
2577 if os.path.exists(repo.join('graftstate')): | |
2578 util.unlinkpath(repo.join('graftstate')) | |
2542 | 2579 |
2543 return 0 | 2580 return 0 |
2544 | 2581 |
2545 @command('grep', | 2582 @command('grep', |
2546 [('0', 'print0', None, _('end fields with NUL')), | 2583 [('0', 'print0', None, _('end fields with NUL')), |