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