graft: allow regrafting ancestors with --force (
issue3220)
--- 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
+