# HG changeset patch # User Matt Mackall # Date 1318463136 18000 # Node ID 2d710c12ffc0cef637fe19d53aed48803047c090 # Parent 7196ed7a150534d29c159b9cb68461325bd7ee67 graft: add initial implementation diff -r 7196ed7a1505 -r 2d710c12ffc0 mercurial/commands.py --- 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')), diff -r 7196ed7a1505 -r 2d710c12ffc0 tests/test-debugcomplete.t --- 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 diff -r 7196ed7a1505 -r 2d710c12ffc0 tests/test-globalopts.t --- 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 diff -r 7196ed7a1505 -r 2d710c12ffc0 tests/test-help.t --- 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