graft: add initial implementation
authorMatt Mackall <mpm@selenic.com>
Wed, 12 Oct 2011 18:45:36 -0500
changeset 15238 2d710c12ffc0
parent 15237 7196ed7a1505
child 15239 f5d9d0d0f588
graft: add initial implementation
mercurial/commands.py
tests/test-debugcomplete.t
tests/test-globalopts.t
tests/test-help.t
--- a/mercurial/commands.py	Wed Oct 12 11:09:57 2011 -0500
+++ b/mercurial/commands.py	Wed Oct 12 18:45:36 2011 -0500
@@ -2447,6 +2447,75 @@
     repo[None].forget(forget)
     return errs
 
+@command('graft',
+    [],
+    _('[OPTION]... REVISION...'))
+def graft(ui, repo, rev, *revs, **opts):
+    '''copy changes from other branches onto the current branch
+
+    This command uses Mercurial's merge logic to copy individual
+    changes from other branches without merging branches in the
+    history graph. This is sometimes known as 'backporting' or
+    'cherry-picking'.
+
+    Changesets that are ancestors of the current revision, that have
+    already been grafted, or that are merges will be skipped.
+
+    Returns 0 on successful completion.
+    '''
+
+    cmdutil.bailifchanged(repo)
+
+    revs = [rev] + list(revs)
+    revs = scmutil.revrange(repo, revs)
+
+    # check for merges
+    for ctx in repo.set('%ld and merge()', revs):
+        ui.warn(_('skipping ungraftable merge revision %s\n') % ctx.rev())
+        revs.remove(ctx.rev())
+    if not revs:
+        return -1
+
+    # check for ancestors of dest branch
+    for ctx in repo.set('::. and %ld', revs):
+        ui.warn(_('skipping ancestor revision %s\n') % ctx.rev())
+        revs.remove(ctx.rev())
+    if not revs:
+        return -1
+
+    # check ancestors for earlier grafts
+    ui.debug('scanning for existing transplants')
+    for ctx in repo.set("::. - ::%ld", revs):
+        n = ctx.extra().get('source')
+        if n and n in repo:
+            r = repo[n].rev()
+            ui.warn(_('skipping already grafted revision %s\n') % r)
+            revs.remove(r)
+    if not revs:
+        return -1
+
+    for ctx in repo.set("%ld", revs):
+        current = repo['.']
+        ui.debug('grafting revision %s', ctx.rev())
+        # perform the graft merge with p1(rev) as 'ancestor'
+        stats = mergemod.update(repo, ctx.node(), True, True, False,
+                             ctx.p1().node())
+        # drop the second merge parent
+        repo.dirstate.setparents(current.node(), nullid)
+        repo.dirstate.write()
+        # fix up dirstate for copies and renames
+        cmdutil.duplicatecopies(repo, ctx.rev(), current.node(), nullid)
+        # report any conflicts
+        if stats and stats[3] > 0:
+            raise util.Abort(_("unresolved conflicts, can't continue"),
+                             hint=_('use hg resolve and hg graft --continue'))
+        # commit
+        extra = {'source': ctx.hex()}
+        repo.commit(text=ctx.description(), user=ctx.user(),
+                    date=ctx.date(), extra=extra)
+
+    return 0
+
 @command('grep',
     [('0', 'print0', None, _('end fields with NUL')),
     ('', 'all', None, _('print all revisions that match')),
--- a/tests/test-debugcomplete.t	Wed Oct 12 11:09:57 2011 -0500
+++ b/tests/test-debugcomplete.t	Wed Oct 12 18:45:36 2011 -0500
@@ -17,6 +17,7 @@
   diff
   export
   forget
+  graft
   grep
   heads
   help
@@ -242,6 +243,7 @@
   debugsub: rev
   debugwalk: include, exclude
   debugwireargs: three, four, five, ssh, remotecmd, insecure
+  graft: 
   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
--- a/tests/test-globalopts.t	Wed Oct 12 11:09:57 2011 -0500
+++ b/tests/test-globalopts.t	Wed Oct 12 18:45:36 2011 -0500
@@ -296,6 +296,7 @@
    diff         diff repository (or selected files)
    export       dump the header and diffs for one or more changesets
    forget       forget the specified files on the next commit
+   graft        copy changes from other branches onto the current branch
    grep         search for a pattern in specified files and revisions
    heads        show current repository heads or show branch heads
    help         show help for a given topic or a help overview
@@ -377,6 +378,7 @@
    diff         diff repository (or selected files)
    export       dump the header and diffs for one or more changesets
    forget       forget the specified files on the next commit
+   graft        copy changes from other branches onto the current branch
    grep         search for a pattern in specified files and revisions
    heads        show current repository heads or show branch heads
    help         show help for a given topic or a help overview
--- a/tests/test-help.t	Wed Oct 12 11:09:57 2011 -0500
+++ b/tests/test-help.t	Wed Oct 12 18:45:36 2011 -0500
@@ -66,6 +66,7 @@
    diff         diff repository (or selected files)
    export       dump the header and diffs for one or more changesets
    forget       forget the specified files on the next commit
+   graft        copy changes from other branches onto the current branch
    grep         search for a pattern in specified files and revisions
    heads        show current repository heads or show branch heads
    help         show help for a given topic or a help overview
@@ -141,6 +142,7 @@
    diff         diff repository (or selected files)
    export       dump the header and diffs for one or more changesets
    forget       forget the specified files on the next commit
+   graft        copy changes from other branches onto the current branch
    grep         search for a pattern in specified files and revisions
    heads        show current repository heads or show branch heads
    help         show help for a given topic or a help overview
@@ -626,6 +628,7 @@
    diff         diff repository (or selected files)
    export       dump the header and diffs for one or more changesets
    forget       forget the specified files on the next commit
+   graft        copy changes from other branches onto the current branch
    grep         search for a pattern in specified files and revisions
    heads        show current repository heads or show branch heads
    help         show help for a given topic or a help overview