graft: allow regrafting ancestors with --force (issue3220)
authorSiddharth Agarwal <sid0@fb.com>
Fri, 25 Jul 2014 18:21:16 -0700
changeset 21979 c2863cfe8a8a
parent 21978 c21c1c8c2017
child 21980 f4e5753745e9
graft: allow regrafting ancestors with --force (issue3220)
mercurial/commands.py
tests/test-completion.t
tests/test-graft.t
--- a/mercurial/commands.py	Fri Jul 04 19:52:39 2014 +0200
+++ b/mercurial/commands.py	Fri Jul 25 18:21:16 2014 -0700
@@ -3054,6 +3054,7 @@
      ('c', 'continue', False, _('resume interrupted graft')),
      ('e', 'edit', False, _('invoke editor on commit messages')),
      ('', 'log', None, _('append graft info to log message')),
+     ('f', 'force', False, _('force graft')),
      ('D', 'currentdate', False,
       _('record the current date as commit date')),
      ('U', 'currentuser', False,
@@ -3077,6 +3078,10 @@
 
       (grafted from CHANGESETHASH)
 
+    If --force is specified, revisions will be grafted even if they
+    are already ancestors of or have been grafted to the destination.
+    This is useful when the revisions have since been backed out.
+
     If a graft merge results in conflicts, the graft process is
     interrupted so that the current merge can be manually resolved.
     Once all conflicts are addressed, the graft process can be
@@ -3151,51 +3156,52 @@
         return -1
 
     # check for ancestors of dest branch
-    crev = repo['.'].rev()
-    ancestors = repo.changelog.ancestors([crev], inclusive=True)
-    # Cannot use x.remove(y) on smart set, this has to be a list.
-    # XXX make this lazy in the future
-    revs = list(revs)
-    # don't mutate while iterating, create a copy
-    for rev in list(revs):
-        if rev in ancestors:
-            ui.warn(_('skipping ancestor revision %s\n') % rev)
-            # XXX remove on list is slow
-            revs.remove(rev)
-    if not revs:
-        return -1
-
-    # analyze revs for earlier grafts
-    ids = {}
-    for ctx in repo.set("%ld", revs):
-        ids[ctx.hex()] = ctx.rev()
-        n = ctx.extra().get('source')
-        if n:
-            ids[n] = ctx.rev()
-
-    # check ancestors for earlier grafts
-    ui.debug('scanning for duplicate grafts\n')
-
-    for rev in repo.changelog.findmissingrevs(revs, [crev]):
-        ctx = repo[rev]
-        n = ctx.extra().get('source')
-        if n in ids:
-            r = repo[n].rev()
-            if r in revs:
-                ui.warn(_('skipping revision %s (already grafted to %s)\n')
-                        % (r, rev))
+    if not opts.get('force'):
+        crev = repo['.'].rev()
+        ancestors = repo.changelog.ancestors([crev], inclusive=True)
+        # Cannot use x.remove(y) on smart set, this has to be a list.
+        # XXX make this lazy in the future
+        revs = list(revs)
+        # don't mutate while iterating, create a copy
+        for rev in list(revs):
+            if rev in ancestors:
+                ui.warn(_('skipping ancestor revision %s\n') % rev)
+                # XXX remove on list is slow
+                revs.remove(rev)
+        if not revs:
+            return -1
+
+        # analyze revs for earlier grafts
+        ids = {}
+        for ctx in repo.set("%ld", revs):
+            ids[ctx.hex()] = ctx.rev()
+            n = ctx.extra().get('source')
+            if n:
+                ids[n] = ctx.rev()
+
+        # check ancestors for earlier grafts
+        ui.debug('scanning for duplicate grafts\n')
+
+        for rev in repo.changelog.findmissingrevs(revs, [crev]):
+            ctx = repo[rev]
+            n = ctx.extra().get('source')
+            if n in ids:
+                r = repo[n].rev()
+                if r in revs:
+                    ui.warn(_('skipping revision %s (already grafted to %s)\n')
+                            % (r, rev))
+                    revs.remove(r)
+                elif ids[n] in revs:
+                    ui.warn(_('skipping already grafted revision %s '
+                                '(%s also has origin %d)\n') % (ids[n], rev, r))
+                    revs.remove(ids[n])
+            elif ctx.hex() in ids:
+                r = ids[ctx.hex()]
+                ui.warn(_('skipping already grafted revision %s '
+                                '(was grafted from %d)\n') % (r, rev))
                 revs.remove(r)
-            elif ids[n] in revs:
-                ui.warn(_('skipping already grafted revision %s '
-                            '(%s also has origin %d)\n') % (ids[n], rev, r))
-                revs.remove(ids[n])
-        elif ctx.hex() in ids:
-            r = ids[ctx.hex()]
-            ui.warn(_('skipping already grafted revision %s '
-                            '(was grafted from %d)\n') % (r, rev))
-            revs.remove(r)
-    if not revs:
-        return -1
+        if not revs:
+            return -1
 
     wlock = repo.wlock()
     try:
--- a/tests/test-completion.t	Fri Jul 04 19:52:39 2014 +0200
+++ b/tests/test-completion.t	Fri Jul 25 18:21:16 2014 -0700
@@ -257,7 +257,7 @@
   debugsuccessorssets: 
   debugwalk: include, exclude
   debugwireargs: three, four, five, ssh, remotecmd, insecure
-  graft: rev, continue, edit, log, currentdate, currentuser, date, user, tool, dry-run
+  graft: rev, continue, edit, log, force, currentdate, currentuser, date, user, tool, dry-run
   grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
   heads: rev, topo, active, closed, style, template
   help: extension, command, keyword
--- a/tests/test-graft.t	Fri Jul 04 19:52:39 2014 +0200
+++ b/tests/test-graft.t	Fri Jul 25 18:21:16 2014 -0700
@@ -631,3 +631,33 @@
   grafting revision 13
   grafting revision 19
   merging b
+
+graft with --force (still doesn't graft merges)
+
+  $ hg graft 19 0 6
+  skipping ungraftable merge revision 6
+  skipping ancestor revision 0
+  skipping already grafted revision 19 (22 also has origin 2)
+  [255]
+  $ hg graft 19 0 6 --force
+  skipping ungraftable merge revision 6
+  grafting revision 19
+  merging b
+  grafting revision 0
+
+graft --force after backout
+
+  $ echo abc > a
+  $ hg ci -m 28
+  $ hg backout 28
+  reverting a
+  changeset 29:484c03b8dfa4 backs out changeset 28:6c56f0f7f033
+  $ hg graft 28
+  skipping ancestor revision 28
+  [255]
+  $ hg graft 28 --force
+  grafting revision 28
+  merging a
+  $ cat a
+  abc
+