> On Oct 14, 2018, at 11:15, Mads Kiilerich <m...@kiilerich.com> wrote:
> 
> # HG changeset patch
> # User Mads Kiilerich <m...@kiilerich.com>
> # Date 1539529698 -7200
> #      Sun Oct 14 17:08:18 2018 +0200
> # Node ID 258029c642d97ef663396476c63ce34dbef89b13
> # Parent  38ac525b44c93fcadb3680d4ded56f1e5a0029b2
> graft: introduce --base option for using custom base revision while merging

queued

At the sprint you had mentioned maybe being able to use this to help backing 
out a merge - if you know the incantation for that maybe send that as a 
follow-up for the verbose help?

> 
> The graft command usually performs an internal merge of the current parent
> revision with the graft revision, using p1 of the grafted revision as base for
> the merge.
> 
> As a trivial extension of this, we introduce the --base option to allow for
> using another base revision.
> 
> This can be used as a building block for grafting and collapsing multiple
> changesets at once, or for grafting the resulting change from a merge as a
> single simple change. (This is kind of similar to backout --parent ... only
> different: this graft base must be an ancestor, but is usually *not* a 
> parent.)
> 
> This is probably an advanced use case, and we do thus not show it in the
> non-verbose help.
> 
> diff --git a/mercurial/commands.py b/mercurial/commands.py
> --- a/mercurial/commands.py
> +++ b/mercurial/commands.py
> @@ -2223,6 +2223,8 @@ def forget(ui, repo, *pats, **opts):
> @command(
>     'graft',
>     [('r', 'rev', [], _('revisions to graft'), _('REV')),
> +     ('', 'base', '',
> +      _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
>      ('c', 'continue', False, _('resume interrupted graft')),
>      ('', 'stop', False, _('stop interrupted graft')),
>      ('', 'abort', False, _('abort interrupted graft')),
> @@ -2267,6 +2269,35 @@ def graft(ui, repo, *revs, **opts):
> 
>     .. container:: verbose
> 
> +      The --base option exposes more of how graft internally uses merge with 
> a
> +      custom base revision. --base can be used to specify another ancestor 
> than
> +      the first and only parent.
> +
> +      The command::
> +
> +        hg graft -r 345 --base 234
> +
> +      is thus pretty much the same as::
> +
> +        hg diff -r 234 -r 345 | hg import
> +
> +      but using merge to resolve conflicts and track moved files.
> +
> +      The result of a merge can thus be backported as a single commit by
> +      specifying one of the merge parents as base, and thus effectively
> +      grafting the changes from the other side.
> +
> +      It is also possible to collapse multiple changesets and clean up 
> history
> +      by specifying another ancestor as base, much like rebase --collapse
> +      --keep.
> +
> +      The commit message can be tweaked after the fact using commit --amend .
> +
> +      For using non-ancestors as the base to backout changes, see the backout
> +      command and the hidden --parent option.
> +
> +    .. container:: verbose
> +
>       Examples:
> 
>       - copy a single change to the stable branch and edit its description::
> @@ -2290,6 +2321,15 @@ def graft(ui, repo, *revs, **opts):
> 
>           hg log -r "sort(all(), date)"
> 
> +      - backport the result of a merge as a single commit::
> +
> +          hg graft -r 123 --base 123^
> +
> +      - land a feature branch as one changeset::
> +
> +          hg up -cr default
> +          hg graft -r featureX --base "ancestor('featureX', 'default')"
> +
>     See :hg:`help revisions` for more about specifying revisions.
> 
>     Returns 0 on successful completion.
> @@ -2305,6 +2345,9 @@ def _dograft(ui, repo, *revs, **opts):
> 
>     revs = list(revs)
>     revs.extend(opts.get('rev'))
> +    basectx = None
> +    if opts.get('base'):
> +        basectx = scmutil.revsingle(repo, opts['base'], None)
>     # a dict of data to be stored in state file
>     statedata = {}
>     # list of new nodes created by ongoing graft
> @@ -2384,13 +2427,16 @@ def _dograft(ui, repo, *revs, **opts):
>         revs = scmutil.revrange(repo, revs)
> 
>     skipped = set()
> -    # check for merges
> -    for rev in repo.revs('%ld and merge()', revs):
> -        ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
> -        skipped.add(rev)
> +    if basectx is None:
> +        # check for merges
> +        for rev in repo.revs('%ld and merge()', revs):
> +            ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
> +            skipped.add(rev)
>     revs = [r for r in revs if r not in skipped]
>     if not revs:
>         return -1
> +    if basectx is not None and len(revs) != 1:
> +        raise error.Abort(_('only one revision allowed with --base '))
> 
>     # Don't check in the --continue case, in effect retaining --force across
>     # --continues. That's because without --force, any revisions we decided to
> @@ -2398,7 +2444,7 @@ def _dograft(ui, repo, *revs, **opts):
>     # way to the graftstate. With --force, any revisions we would have 
> otherwise
>     # skipped would not have been filtered out, and if they hadn't been 
> applied
>     # already, they'd have been in the graftstate.
> -    if not (cont or opts.get('force')):
> +    if not (cont or opts.get('force')) and basectx is None:
>         # check for ancestors of dest branch
>         crev = repo['.'].rev()
>         ancestors = repo.changelog.ancestors([crev], inclusive=True)
> @@ -2494,8 +2540,9 @@ def _dograft(ui, repo, *revs, **opts):
>         if not cont:
>             # perform the graft merge with p1(rev) as 'ancestor'
>             overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
> +            base = ctx.p1() if basectx is None else basectx
>             with ui.configoverride(overrides, 'graft'):
> -                stats = mergemod.graft(repo, ctx, ctx.p1(), ['local', 
> 'graft'])
> +                stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
>             # report any conflicts
>             if stats.unresolvedcount > 0:
>                 # write out state for --continue
> diff --git a/tests/test-completion.t b/tests/test-completion.t
> --- a/tests/test-completion.t
> +++ b/tests/test-completion.t
> @@ -318,7 +318,7 @@ Show all commands + options
>   debugwireargs: three, four, five, ssh, remotecmd, insecure
>   debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, 
> remotecmd, insecure
>   files: rev, print0, include, exclude, template, subrepos
> -  graft: rev, continue, stop, abort, edit, log, no-commit, force, 
> currentdate, currentuser, date, user, tool, dry-run
> +  graft: rev, base, continue, stop, abort, edit, log, no-commit, force, 
> currentdate, currentuser, date, user, tool, dry-run
>   grep: print0, all, diff, text, follow, ignore-case, files-with-matches, 
> line-number, rev, all-files, user, date, template, include, exclude
>   heads: rev, topo, active, closed, style, template
>   help: extension, command, keyword, system
> diff --git a/tests/test-graft.t b/tests/test-graft.t
> --- a/tests/test-graft.t
> +++ b/tests/test-graft.t
> @@ -25,7 +25,7 @@ Create a repo with some stuff in it:
>   $ echo b > e
>   $ hg branch -q stable
>   $ hg ci -m5
> -  $ hg merge -q default --tool internal:local
> +  $ hg merge -q default --tool internal:local # for conflicts in e, choose 5 
> and ignore 4
>   $ hg branch -q default
>   $ hg ci -m6
>   $ hg phase --public 3
> @@ -46,8 +46,40 @@ Create a repo with some stuff in it:
>   |
>   o  test@0.public: 0
> 
> +Test --base for grafting the merge of 4 from the perspective of 5, thus only 
> getting the change to d
> +
> +  $ hg up -cqr 3
> +  $ hg graft -r 6 --base 5
> +  grafting 6:25a2b029d3ae "6" (tip)
> +  merging e
> +  $ hg st --change .
> +  M d
> +
> +  $ hg -q strip . --config extensions.strip=
> +
> +Test --base for collapsing changesets 2 and 3, thus getting both b and c
> +
> +  $ hg up -cqr 0
> +  $ hg graft -r 3 --base 1
> +  grafting 3:4c60f11aa304 "3"
> +  merging a and b to b
> +  merging a and c to c
> +  $ hg st --change .
> +  A b
> +  A c
> +  R a
> +
> +  $ hg -q strip . --config extensions.strip=
> +
> +Specifying child as --base revision fails safely (perhaps slightly 
> confusing, but consistent)
> +
> +  $ hg graft -r 2 --base 3
> +  grafting 2:5c095ad7e90f "2"
> +  note: graft of 2:5c095ad7e90f created no changes to commit
> +
> Can't continue without starting:
> 
> +  $ hg -q up -cr tip
>   $ hg rm -q e
>   $ hg graft --continue
>   abort: no graft in progress
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel

_______________________________________________
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel

Reply via email to