comparison mercurial/commands.py @ 21979:c2863cfe8a8a

graft: allow regrafting ancestors with --force (issue3220)
author Siddharth Agarwal <sid0@fb.com>
date Fri, 25 Jul 2014 18:21:16 -0700
parents dccbf52ffe9f
children f4e5753745e9
comparison
equal deleted inserted replaced
21978:c21c1c8c2017 21979:c2863cfe8a8a
3052 'graft', 3052 'graft',
3053 [('r', 'rev', [], _('revisions to graft'), _('REV')), 3053 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3054 ('c', 'continue', False, _('resume interrupted graft')), 3054 ('c', 'continue', False, _('resume interrupted graft')),
3055 ('e', 'edit', False, _('invoke editor on commit messages')), 3055 ('e', 'edit', False, _('invoke editor on commit messages')),
3056 ('', 'log', None, _('append graft info to log message')), 3056 ('', 'log', None, _('append graft info to log message')),
3057 ('f', 'force', False, _('force graft')),
3057 ('D', 'currentdate', False, 3058 ('D', 'currentdate', False,
3058 _('record the current date as commit date')), 3059 _('record the current date as commit date')),
3059 ('U', 'currentuser', False, 3060 ('U', 'currentuser', False,
3060 _('record the current user as committer'), _('DATE'))] 3061 _('record the current user as committer'), _('DATE'))]
3061 + commitopts2 + mergetoolopts + dryrunopts, 3062 + commitopts2 + mergetoolopts + dryrunopts,
3074 3075
3075 If --log is specified, log messages will have a comment appended 3076 If --log is specified, log messages will have a comment appended
3076 of the form:: 3077 of the form::
3077 3078
3078 (grafted from CHANGESETHASH) 3079 (grafted from CHANGESETHASH)
3080
3081 If --force is specified, revisions will be grafted even if they
3082 are already ancestors of or have been grafted to the destination.
3083 This is useful when the revisions have since been backed out.
3079 3084
3080 If a graft merge results in conflicts, the graft process is 3085 If a graft merge results in conflicts, the graft process is
3081 interrupted so that the current merge can be manually resolved. 3086 interrupted so that the current merge can be manually resolved.
3082 Once all conflicts are addressed, the graft process can be 3087 Once all conflicts are addressed, the graft process can be
3083 continued with the -c/--continue option. 3088 continued with the -c/--continue option.
3149 revs.remove(rev) 3154 revs.remove(rev)
3150 if not revs: 3155 if not revs:
3151 return -1 3156 return -1
3152 3157
3153 # check for ancestors of dest branch 3158 # check for ancestors of dest branch
3154 crev = repo['.'].rev() 3159 if not opts.get('force'):
3155 ancestors = repo.changelog.ancestors([crev], inclusive=True) 3160 crev = repo['.'].rev()
3156 # Cannot use x.remove(y) on smart set, this has to be a list. 3161 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3157 # XXX make this lazy in the future 3162 # Cannot use x.remove(y) on smart set, this has to be a list.
3158 revs = list(revs) 3163 # XXX make this lazy in the future
3159 # don't mutate while iterating, create a copy 3164 revs = list(revs)
3160 for rev in list(revs): 3165 # don't mutate while iterating, create a copy
3161 if rev in ancestors: 3166 for rev in list(revs):
3162 ui.warn(_('skipping ancestor revision %s\n') % rev) 3167 if rev in ancestors:
3163 # XXX remove on list is slow 3168 ui.warn(_('skipping ancestor revision %s\n') % rev)
3164 revs.remove(rev) 3169 # XXX remove on list is slow
3165 if not revs: 3170 revs.remove(rev)
3166 return -1 3171 if not revs:
3167 3172 return -1
3168 # analyze revs for earlier grafts 3173
3169 ids = {} 3174 # analyze revs for earlier grafts
3170 for ctx in repo.set("%ld", revs): 3175 ids = {}
3171 ids[ctx.hex()] = ctx.rev() 3176 for ctx in repo.set("%ld", revs):
3172 n = ctx.extra().get('source') 3177 ids[ctx.hex()] = ctx.rev()
3173 if n: 3178 n = ctx.extra().get('source')
3174 ids[n] = ctx.rev() 3179 if n:
3175 3180 ids[n] = ctx.rev()
3176 # check ancestors for earlier grafts 3181
3177 ui.debug('scanning for duplicate grafts\n') 3182 # check ancestors for earlier grafts
3178 3183 ui.debug('scanning for duplicate grafts\n')
3179 for rev in repo.changelog.findmissingrevs(revs, [crev]): 3184
3180 ctx = repo[rev] 3185 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3181 n = ctx.extra().get('source') 3186 ctx = repo[rev]
3182 if n in ids: 3187 n = ctx.extra().get('source')
3183 r = repo[n].rev() 3188 if n in ids:
3184 if r in revs: 3189 r = repo[n].rev()
3185 ui.warn(_('skipping revision %s (already grafted to %s)\n') 3190 if r in revs:
3186 % (r, rev)) 3191 ui.warn(_('skipping revision %s (already grafted to %s)\n')
3192 % (r, rev))
3193 revs.remove(r)
3194 elif ids[n] in revs:
3195 ui.warn(_('skipping already grafted revision %s '
3196 '(%s also has origin %d)\n') % (ids[n], rev, r))
3197 revs.remove(ids[n])
3198 elif ctx.hex() in ids:
3199 r = ids[ctx.hex()]
3200 ui.warn(_('skipping already grafted revision %s '
3201 '(was grafted from %d)\n') % (r, rev))
3187 revs.remove(r) 3202 revs.remove(r)
3188 elif ids[n] in revs: 3203 if not revs:
3189 ui.warn(_('skipping already grafted revision %s ' 3204 return -1
3190 '(%s also has origin %d)\n') % (ids[n], rev, r))
3191 revs.remove(ids[n])
3192 elif ctx.hex() in ids:
3193 r = ids[ctx.hex()]
3194 ui.warn(_('skipping already grafted revision %s '
3195 '(was grafted from %d)\n') % (r, rev))
3196 revs.remove(r)
3197 if not revs:
3198 return -1
3199 3205
3200 wlock = repo.wlock() 3206 wlock = repo.wlock()
3201 try: 3207 try:
3202 current = repo['.'] 3208 current = repo['.']
3203 for pos, ctx in enumerate(repo.set("%ld", revs)): 3209 for pos, ctx in enumerate(repo.set("%ld", revs)):