Show More
@@ -2249,6 +2249,8 b' def forget(ui, repo, *pats, **opts):' | |||||
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')), | |
@@ -2294,6 +2296,35 b' def graft(ui, repo, *revs, **opts):' | |||||
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:: | |
@@ -2317,6 +2348,15 b' def graft(ui, repo, *revs, **opts):' | |||||
2317 |
|
2348 | |||
2318 | hg log -r "sort(all(), date)" |
|
2349 | hg log -r "sort(all(), date)" | |
2319 |
|
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')" | |||
|
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. | |
@@ -2332,6 +2372,9 b' def _dograft(ui, repo, *revs, **opts):' | |||||
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 | |
@@ -2411,13 +2454,16 b' def _dograft(ui, repo, *revs, **opts):' | |||||
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 | |
@@ -2425,7 +2471,7 b' def _dograft(ui, repo, *revs, **opts):' | |||||
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) | |
@@ -2521,8 +2567,9 b' def _dograft(ui, repo, *revs, **opts):' | |||||
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, |
|
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 |
@@ -308,7 +308,7 b' Show all commands + options' | |||||
308 | export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template |
|
308 | export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template | |
309 | files: rev, print0, include, exclude, template, subrepos |
|
309 | files: rev, print0, include, exclude, template, subrepos | |
310 | forget: interactive, include, exclude, dry-run |
|
310 | forget: interactive, include, exclude, dry-run | |
311 | graft: rev, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run |
|
311 | graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run | |
312 | grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude |
|
312 | grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude | |
313 | heads: rev, topo, active, closed, style, template |
|
313 | heads: rev, topo, active, closed, style, template | |
314 | help: extension, command, keyword, system |
|
314 | help: extension, command, keyword, system |
@@ -25,7 +25,7 b' Create a repo with some stuff in it:' | |||||
25 | $ echo b > e |
|
25 | $ echo b > e | |
26 | $ hg branch -q stable |
|
26 | $ hg branch -q stable | |
27 | $ hg ci -m5 |
|
27 | $ hg ci -m5 | |
28 | $ hg merge -q default --tool internal:local |
|
28 | $ hg merge -q default --tool internal:local # for conflicts in e, choose 5 and ignore 4 | |
29 | $ hg branch -q default |
|
29 | $ hg branch -q default | |
30 | $ hg ci -m6 |
|
30 | $ hg ci -m6 | |
31 | $ hg phase --public 3 |
|
31 | $ hg phase --public 3 | |
@@ -46,8 +46,40 b' Create a repo with some stuff in it:' | |||||
46 | | |
|
46 | | | |
47 | o test@0.public: 0 |
|
47 | o test@0.public: 0 | |
48 |
|
48 | |||
|
49 | Test --base for grafting the merge of 4 from the perspective of 5, thus only getting the change to d | |||
|
50 | ||||
|
51 | $ hg up -cqr 3 | |||
|
52 | $ hg graft -r 6 --base 5 | |||
|
53 | grafting 6:25a2b029d3ae "6" (tip) | |||
|
54 | merging e | |||
|
55 | $ hg st --change . | |||
|
56 | M d | |||
|
57 | ||||
|
58 | $ hg -q strip . --config extensions.strip= | |||
|
59 | ||||
|
60 | Test --base for collapsing changesets 2 and 3, thus getting both b and c | |||
|
61 | ||||
|
62 | $ hg up -cqr 0 | |||
|
63 | $ hg graft -r 3 --base 1 | |||
|
64 | grafting 3:4c60f11aa304 "3" | |||
|
65 | merging a and b to b | |||
|
66 | merging a and c to c | |||
|
67 | $ hg st --change . | |||
|
68 | A b | |||
|
69 | A c | |||
|
70 | R a | |||
|
71 | ||||
|
72 | $ hg -q strip . --config extensions.strip= | |||
|
73 | ||||
|
74 | Specifying child as --base revision fails safely (perhaps slightly confusing, but consistent) | |||
|
75 | ||||
|
76 | $ hg graft -r 2 --base 3 | |||
|
77 | grafting 2:5c095ad7e90f "2" | |||
|
78 | note: graft of 2:5c095ad7e90f created no changes to commit | |||
|
79 | ||||
49 | Can't continue without starting: |
|
80 | Can't continue without starting: | |
50 |
|
81 | |||
|
82 | $ hg -q up -cr tip | |||
51 | $ hg rm -q e |
|
83 | $ hg rm -q e | |
52 | $ hg graft --continue |
|
84 | $ hg graft --continue | |
53 | abort: no graft in progress |
|
85 | abort: no graft in progress |
General Comments 0
You need to be logged in to leave comments.
Login now