mercurial/commands.py
changeset 40434 3c0d5016b2be
parent 40394 1feb4b2c8e40
child 40578 db61a18148a4
equal deleted inserted replaced
40433:808b762679cd 40434:3c0d5016b2be
  2247     return rejected and 1 or 0
  2247     return rejected and 1 or 0
  2248 
  2248 
  2249 @command(
  2249 @command(
  2250     'graft',
  2250     'graft',
  2251     [('r', 'rev', [], _('revisions to graft'), _('REV')),
  2251     [('r', 'rev', [], _('revisions to graft'), _('REV')),
       
  2252      ('', 'base', '',
       
  2253       _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
  2252      ('c', 'continue', False, _('resume interrupted graft')),
  2254      ('c', 'continue', False, _('resume interrupted graft')),
  2253      ('', 'stop', False, _('stop interrupted graft')),
  2255      ('', 'stop', False, _('stop interrupted graft')),
  2254      ('', 'abort', False, _('abort interrupted graft')),
  2256      ('', 'abort', False, _('abort interrupted graft')),
  2255      ('e', 'edit', False, _('invoke editor on commit messages')),
  2257      ('e', 'edit', False, _('invoke editor on commit messages')),
  2256      ('', 'log', None, _('append graft info to log message')),
  2258      ('', 'log', None, _('append graft info to log message')),
  2292 
  2294 
  2293     The -c/--continue option reapplies all the earlier options.
  2295     The -c/--continue option reapplies all the earlier options.
  2294 
  2296 
  2295     .. container:: verbose
  2297     .. container:: verbose
  2296 
  2298 
       
  2299       The --base option exposes more of how graft internally uses merge with a
       
  2300       custom base revision. --base can be used to specify another ancestor than
       
  2301       the first and only parent.
       
  2302 
       
  2303       The command::
       
  2304 
       
  2305         hg graft -r 345 --base 234
       
  2306 
       
  2307       is thus pretty much the same as::
       
  2308 
       
  2309         hg diff -r 234 -r 345 | hg import
       
  2310 
       
  2311       but using merge to resolve conflicts and track moved files.
       
  2312 
       
  2313       The result of a merge can thus be backported as a single commit by
       
  2314       specifying one of the merge parents as base, and thus effectively
       
  2315       grafting the changes from the other side.
       
  2316 
       
  2317       It is also possible to collapse multiple changesets and clean up history
       
  2318       by specifying another ancestor as base, much like rebase --collapse
       
  2319       --keep.
       
  2320 
       
  2321       The commit message can be tweaked after the fact using commit --amend .
       
  2322 
       
  2323       For using non-ancestors as the base to backout changes, see the backout
       
  2324       command and the hidden --parent option.
       
  2325 
       
  2326     .. container:: verbose
       
  2327 
  2297       Examples:
  2328       Examples:
  2298 
  2329 
  2299       - copy a single change to the stable branch and edit its description::
  2330       - copy a single change to the stable branch and edit its description::
  2300 
  2331 
  2301           hg update stable
  2332           hg update stable
  2314           hg log --debug -r .
  2345           hg log --debug -r .
  2315 
  2346 
  2316       - show revisions sorted by date::
  2347       - show revisions sorted by date::
  2317 
  2348 
  2318           hg log -r "sort(all(), date)"
  2349           hg log -r "sort(all(), date)"
       
  2350 
       
  2351       - backport the result of a merge as a single commit::
       
  2352 
       
  2353           hg graft -r 123 --base 123^
       
  2354 
       
  2355       - land a feature branch as one changeset::
       
  2356 
       
  2357           hg up -cr default
       
  2358           hg graft -r featureX --base "ancestor('featureX', 'default')"
  2319 
  2359 
  2320     See :hg:`help revisions` for more about specifying revisions.
  2360     See :hg:`help revisions` for more about specifying revisions.
  2321 
  2361 
  2322     Returns 0 on successful completion.
  2362     Returns 0 on successful completion.
  2323     '''
  2363     '''
  2330         ui.warn(_('warning: inconsistent use of --rev might give unexpected '
  2370         ui.warn(_('warning: inconsistent use of --rev might give unexpected '
  2331                   'revision ordering!\n'))
  2371                   'revision ordering!\n'))
  2332 
  2372 
  2333     revs = list(revs)
  2373     revs = list(revs)
  2334     revs.extend(opts.get('rev'))
  2374     revs.extend(opts.get('rev'))
       
  2375     basectx = None
       
  2376     if opts.get('base'):
       
  2377         basectx = scmutil.revsingle(repo, opts['base'], None)
  2335     # a dict of data to be stored in state file
  2378     # a dict of data to be stored in state file
  2336     statedata = {}
  2379     statedata = {}
  2337     # list of new nodes created by ongoing graft
  2380     # list of new nodes created by ongoing graft
  2338     statedata['newnodes'] = []
  2381     statedata['newnodes'] = []
  2339 
  2382 
  2409         cmdutil.checkunfinished(repo)
  2452         cmdutil.checkunfinished(repo)
  2410         cmdutil.bailifchanged(repo)
  2453         cmdutil.bailifchanged(repo)
  2411         revs = scmutil.revrange(repo, revs)
  2454         revs = scmutil.revrange(repo, revs)
  2412 
  2455 
  2413     skipped = set()
  2456     skipped = set()
  2414     # check for merges
  2457     if basectx is None:
  2415     for rev in repo.revs('%ld and merge()', revs):
  2458         # check for merges
  2416         ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
  2459         for rev in repo.revs('%ld and merge()', revs):
  2417         skipped.add(rev)
  2460             ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
       
  2461             skipped.add(rev)
  2418     revs = [r for r in revs if r not in skipped]
  2462     revs = [r for r in revs if r not in skipped]
  2419     if not revs:
  2463     if not revs:
  2420         return -1
  2464         return -1
       
  2465     if basectx is not None and len(revs) != 1:
       
  2466         raise error.Abort(_('only one revision allowed with --base '))
  2421 
  2467 
  2422     # Don't check in the --continue case, in effect retaining --force across
  2468     # Don't check in the --continue case, in effect retaining --force across
  2423     # --continues. That's because without --force, any revisions we decided to
  2469     # --continues. That's because without --force, any revisions we decided to
  2424     # skip would have been filtered out here, so they wouldn't have made their
  2470     # skip would have been filtered out here, so they wouldn't have made their
  2425     # way to the graftstate. With --force, any revisions we would have otherwise
  2471     # way to the graftstate. With --force, any revisions we would have otherwise
  2426     # skipped would not have been filtered out, and if they hadn't been applied
  2472     # skipped would not have been filtered out, and if they hadn't been applied
  2427     # already, they'd have been in the graftstate.
  2473     # already, they'd have been in the graftstate.
  2428     if not (cont or opts.get('force')):
  2474     if not (cont or opts.get('force')) and basectx is None:
  2429         # check for ancestors of dest branch
  2475         # check for ancestors of dest branch
  2430         crev = repo['.'].rev()
  2476         crev = repo['.'].rev()
  2431         ancestors = repo.changelog.ancestors([crev], inclusive=True)
  2477         ancestors = repo.changelog.ancestors([crev], inclusive=True)
  2432         # XXX make this lazy in the future
  2478         # XXX make this lazy in the future
  2433         # don't mutate while iterating, create a copy
  2479         # don't mutate while iterating, create a copy
  2519 
  2565 
  2520         # we don't merge the first commit when continuing
  2566         # we don't merge the first commit when continuing
  2521         if not cont:
  2567         if not cont:
  2522             # perform the graft merge with p1(rev) as 'ancestor'
  2568             # perform the graft merge with p1(rev) as 'ancestor'
  2523             overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
  2569             overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
       
  2570             base = ctx.p1() if basectx is None else basectx
  2524             with ui.configoverride(overrides, 'graft'):
  2571             with ui.configoverride(overrides, 'graft'):
  2525                 stats = mergemod.graft(repo, ctx, ctx.p1(), ['local', 'graft'])
  2572                 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
  2526             # report any conflicts
  2573             # report any conflicts
  2527             if stats.unresolvedcount > 0:
  2574             if stats.unresolvedcount > 0:
  2528                 # write out state for --continue
  2575                 # write out state for --continue
  2529                 nodes = [repo[rev].hex() for rev in revs[pos:]]
  2576                 nodes = [repo[rev].hex() for rev in revs[pos:]]
  2530                 statedata['nodes'] = nodes
  2577                 statedata['nodes'] = nodes