# HG changeset patch
# User Mads Kiilerich <m...@kiilerich.com>
# Date 1539424786 -7200
#      Sat Oct 13 11:59:46 2018 +0200
# Node ID 167e7e7bbead47fadfe4072143cc53b409e3d8b9
# Parent  38ac525b44c93fcadb3680d4ded56f1e5a0029b2
graft: introduce --base option for using custom base revision for merge

The graft command usually merges with the graft revision using its p1 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: it 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,7 @@ def forget(ui, repo, *pats, **opts):
 @command(
     'graft',
     [('r', 'rev', [], _('revisions to graft'), _('REV')),
+     ('', 'base', '', _('base revision when doing the graft merge'), _('REV')),
      ('c', 'continue', False, _('resume interrupted graft')),
      ('', 'stop', False, _('stop interrupted graft')),
      ('', 'abort', False, _('abort interrupted graft')),
@@ -2267,6 +2268,12 @@ def graft(ui, repo, *revs, **opts):
 
     .. container:: verbose
 
+      The --base option can be used to specify another merge base than the
+      first parent, thus allowing graft of the impact of a merge, or
+      collapsing of multiple changesets.
+
+    .. container:: verbose
+
       Examples:
 
       - copy a single change to the stable branch and edit its description::
@@ -2305,6 +2312,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 +2394,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 +2411,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 +2507,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,34 @@ 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=
+
 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

Reply via email to