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 |