mercurial/commands.py
changeset 52328 f2fc0a91faca
parent 51859 f4733654f144
child 52416 0a81f3ef054c
equal deleted inserted replaced
52326:d24731850b2e 52328:f2fc0a91faca
    66     util,
    66     util,
    67     verify as verifymod,
    67     verify as verifymod,
    68     vfs as vfsmod,
    68     vfs as vfsmod,
    69     wireprotoserver,
    69     wireprotoserver,
    70 )
    70 )
       
    71 
       
    72 from .cmd_impls import graft as graft_impl
       
    73 
    71 from .utils import (
    74 from .utils import (
    72     dateutil,
    75     dateutil,
    73     procutil,
    76     procutil,
    74     stringutil,
    77     stringutil,
    75     urlutil,
    78     urlutil,
  3183     See :hg:`help revisions` for more about specifying revisions.
  3186     See :hg:`help revisions` for more about specifying revisions.
  3184 
  3187 
  3185     Returns 0 on successful completion, 1 if there are unresolved files.
  3188     Returns 0 on successful completion, 1 if there are unresolved files.
  3186     """
  3189     """
  3187     with repo.wlock():
  3190     with repo.wlock():
  3188         return _dograft(ui, repo, *revs, **opts)
  3191         return graft_impl.cmd_graft(ui, repo, *revs, **opts)
  3189 
       
  3190 
       
  3191 def _dograft(ui, repo, *revs, **opts):
       
  3192     if revs and opts.get('rev'):
       
  3193         ui.warn(
       
  3194             _(
       
  3195                 b'warning: inconsistent use of --rev might give unexpected '
       
  3196                 b'revision ordering!\n'
       
  3197             )
       
  3198         )
       
  3199 
       
  3200     revs = list(revs)
       
  3201     revs.extend(opts.get('rev'))
       
  3202     # a dict of data to be stored in state file
       
  3203     statedata = {}
       
  3204     # list of new nodes created by ongoing graft
       
  3205     statedata[b'newnodes'] = []
       
  3206 
       
  3207     cmdutil.resolve_commit_options(ui, opts)
       
  3208 
       
  3209     editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
       
  3210 
       
  3211     cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
       
  3212 
       
  3213     cont = False
       
  3214     if opts.get('no_commit'):
       
  3215         cmdutil.check_incompatible_arguments(
       
  3216             opts,
       
  3217             'no_commit',
       
  3218             ['edit', 'currentuser', 'currentdate', 'log'],
       
  3219         )
       
  3220 
       
  3221     graftstate = statemod.cmdstate(repo, b'graftstate')
       
  3222 
       
  3223     if opts.get('stop'):
       
  3224         cmdutil.check_incompatible_arguments(
       
  3225             opts,
       
  3226             'stop',
       
  3227             [
       
  3228                 'edit',
       
  3229                 'log',
       
  3230                 'user',
       
  3231                 'date',
       
  3232                 'currentdate',
       
  3233                 'currentuser',
       
  3234                 'rev',
       
  3235             ],
       
  3236         )
       
  3237         return _stopgraft(ui, repo, graftstate)
       
  3238     elif opts.get('abort'):
       
  3239         cmdutil.check_incompatible_arguments(
       
  3240             opts,
       
  3241             'abort',
       
  3242             [
       
  3243                 'edit',
       
  3244                 'log',
       
  3245                 'user',
       
  3246                 'date',
       
  3247                 'currentdate',
       
  3248                 'currentuser',
       
  3249                 'rev',
       
  3250             ],
       
  3251         )
       
  3252         return cmdutil.abortgraft(ui, repo, graftstate)
       
  3253     elif opts.get('continue'):
       
  3254         cont = True
       
  3255         if revs:
       
  3256             raise error.InputError(_(b"can't specify --continue and revisions"))
       
  3257         # read in unfinished revisions
       
  3258         if graftstate.exists():
       
  3259             statedata = cmdutil.readgraftstate(repo, graftstate)
       
  3260             if statedata.get(b'date'):
       
  3261                 opts['date'] = statedata[b'date']
       
  3262             if statedata.get(b'user'):
       
  3263                 opts['user'] = statedata[b'user']
       
  3264             if statedata.get(b'log'):
       
  3265                 opts['log'] = True
       
  3266             if statedata.get(b'no_commit'):
       
  3267                 opts['no_commit'] = statedata.get(b'no_commit')
       
  3268             if statedata.get(b'base'):
       
  3269                 opts['base'] = statedata.get(b'base')
       
  3270             nodes = statedata[b'nodes']
       
  3271             revs = [repo[node].rev() for node in nodes]
       
  3272         else:
       
  3273             cmdutil.wrongtooltocontinue(repo, _(b'graft'))
       
  3274     else:
       
  3275         if not revs:
       
  3276             raise error.InputError(_(b'no revisions specified'))
       
  3277         cmdutil.checkunfinished(repo)
       
  3278         cmdutil.bailifchanged(repo)
       
  3279         revs = logcmdutil.revrange(repo, revs)
       
  3280 
       
  3281     skipped = set()
       
  3282     basectx = None
       
  3283     if opts.get('base'):
       
  3284         basectx = logcmdutil.revsingle(repo, opts['base'], None)
       
  3285     if basectx is None:
       
  3286         # check for merges
       
  3287         for rev in repo.revs(b'%ld and merge()', revs):
       
  3288             ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
       
  3289             skipped.add(rev)
       
  3290     revs = [r for r in revs if r not in skipped]
       
  3291     if not revs:
       
  3292         return -1
       
  3293     if basectx is not None and len(revs) != 1:
       
  3294         raise error.InputError(_(b'only one revision allowed with --base '))
       
  3295 
       
  3296     # Don't check in the --continue case, in effect retaining --force across
       
  3297     # --continues. That's because without --force, any revisions we decided to
       
  3298     # skip would have been filtered out here, so they wouldn't have made their
       
  3299     # way to the graftstate. With --force, any revisions we would have otherwise
       
  3300     # skipped would not have been filtered out, and if they hadn't been applied
       
  3301     # already, they'd have been in the graftstate.
       
  3302     if not (cont or opts.get('force')) and basectx is None:
       
  3303         # check for ancestors of dest branch
       
  3304         ancestors = repo.revs(b'%ld & (::.)', revs)
       
  3305         for rev in ancestors:
       
  3306             ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
       
  3307 
       
  3308         revs = [r for r in revs if r not in ancestors]
       
  3309 
       
  3310         if not revs:
       
  3311             return -1
       
  3312 
       
  3313         # analyze revs for earlier grafts
       
  3314         ids = {}
       
  3315         for ctx in repo.set(b"%ld", revs):
       
  3316             ids[ctx.hex()] = ctx.rev()
       
  3317             n = ctx.extra().get(b'source')
       
  3318             if n:
       
  3319                 ids[n] = ctx.rev()
       
  3320 
       
  3321         # check ancestors for earlier grafts
       
  3322         ui.debug(b'scanning for duplicate grafts\n')
       
  3323 
       
  3324         # The only changesets we can be sure doesn't contain grafts of any
       
  3325         # revs, are the ones that are common ancestors of *all* revs:
       
  3326         for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
       
  3327             ctx = repo[rev]
       
  3328             n = ctx.extra().get(b'source')
       
  3329             if n in ids:
       
  3330                 try:
       
  3331                     r = repo[n].rev()
       
  3332                 except error.RepoLookupError:
       
  3333                     r = None
       
  3334                 if r in revs:
       
  3335                     ui.warn(
       
  3336                         _(
       
  3337                             b'skipping revision %d:%s '
       
  3338                             b'(already grafted to %d:%s)\n'
       
  3339                         )
       
  3340                         % (r, repo[r], rev, ctx)
       
  3341                     )
       
  3342                     revs.remove(r)
       
  3343                 elif ids[n] in revs:
       
  3344                     if r is None:
       
  3345                         ui.warn(
       
  3346                             _(
       
  3347                                 b'skipping already grafted revision %d:%s '
       
  3348                                 b'(%d:%s also has unknown origin %s)\n'
       
  3349                             )
       
  3350                             % (ids[n], repo[ids[n]], rev, ctx, n[:12])
       
  3351                         )
       
  3352                     else:
       
  3353                         ui.warn(
       
  3354                             _(
       
  3355                                 b'skipping already grafted revision %d:%s '
       
  3356                                 b'(%d:%s also has origin %d:%s)\n'
       
  3357                             )
       
  3358                             % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
       
  3359                         )
       
  3360                     revs.remove(ids[n])
       
  3361             elif ctx.hex() in ids:
       
  3362                 r = ids[ctx.hex()]
       
  3363                 if r in revs:
       
  3364                     ui.warn(
       
  3365                         _(
       
  3366                             b'skipping already grafted revision %d:%s '
       
  3367                             b'(was grafted from %d:%s)\n'
       
  3368                         )
       
  3369                         % (r, repo[r], rev, ctx)
       
  3370                     )
       
  3371                     revs.remove(r)
       
  3372         if not revs:
       
  3373             return -1
       
  3374 
       
  3375     if opts.get('no_commit'):
       
  3376         statedata[b'no_commit'] = True
       
  3377     if opts.get('base'):
       
  3378         statedata[b'base'] = opts['base']
       
  3379     for pos, ctx in enumerate(repo.set(b"%ld", revs)):
       
  3380         desc = b'%d:%s "%s"' % (
       
  3381             ctx.rev(),
       
  3382             ctx,
       
  3383             ctx.description().split(b'\n', 1)[0],
       
  3384         )
       
  3385         names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
       
  3386         if names:
       
  3387             desc += b' (%s)' % b' '.join(names)
       
  3388         ui.status(_(b'grafting %s\n') % desc)
       
  3389         if opts.get('dry_run'):
       
  3390             continue
       
  3391 
       
  3392         source = ctx.extra().get(b'source')
       
  3393         extra = {}
       
  3394         if source:
       
  3395             extra[b'source'] = source
       
  3396             extra[b'intermediate-source'] = ctx.hex()
       
  3397         else:
       
  3398             extra[b'source'] = ctx.hex()
       
  3399         user = ctx.user()
       
  3400         if opts.get('user'):
       
  3401             user = opts['user']
       
  3402             statedata[b'user'] = user
       
  3403         date = ctx.date()
       
  3404         if opts.get('date'):
       
  3405             date = opts['date']
       
  3406             statedata[b'date'] = date
       
  3407         message = ctx.description()
       
  3408         if opts.get('log'):
       
  3409             message += b'\n(grafted from %s)' % ctx.hex()
       
  3410             statedata[b'log'] = True
       
  3411 
       
  3412         # we don't merge the first commit when continuing
       
  3413         if not cont:
       
  3414             # perform the graft merge with p1(rev) as 'ancestor'
       
  3415             overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
       
  3416             base = ctx.p1() if basectx is None else basectx
       
  3417             with ui.configoverride(overrides, b'graft'):
       
  3418                 stats = mergemod.graft(
       
  3419                     repo, ctx, base, [b'local', b'graft', b'parent of graft']
       
  3420                 )
       
  3421             # report any conflicts
       
  3422             if stats.unresolvedcount > 0:
       
  3423                 # write out state for --continue
       
  3424                 nodes = [repo[rev].hex() for rev in revs[pos:]]
       
  3425                 statedata[b'nodes'] = nodes
       
  3426                 stateversion = 1
       
  3427                 graftstate.save(stateversion, statedata)
       
  3428                 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
       
  3429                 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
       
  3430                 return 1
       
  3431         else:
       
  3432             cont = False
       
  3433 
       
  3434         # commit if --no-commit is false
       
  3435         if not opts.get('no_commit'):
       
  3436             node = repo.commit(
       
  3437                 text=message, user=user, date=date, extra=extra, editor=editor
       
  3438             )
       
  3439             if node is None:
       
  3440                 ui.warn(
       
  3441                     _(b'note: graft of %d:%s created no changes to commit\n')
       
  3442                     % (ctx.rev(), ctx)
       
  3443                 )
       
  3444             # checking that newnodes exist because old state files won't have it
       
  3445             elif statedata.get(b'newnodes') is not None:
       
  3446                 nn = statedata[b'newnodes']
       
  3447                 assert isinstance(nn, list)  # list of bytes
       
  3448                 nn.append(node)
       
  3449 
       
  3450     # remove state when we complete successfully
       
  3451     if not opts.get('dry_run'):
       
  3452         graftstate.delete()
       
  3453 
       
  3454     return 0
       
  3455 
       
  3456 
       
  3457 def _stopgraft(ui, repo, graftstate):
       
  3458     """stop the interrupted graft"""
       
  3459     if not graftstate.exists():
       
  3460         raise error.StateError(_(b"no interrupted graft found"))
       
  3461     pctx = repo[b'.']
       
  3462     mergemod.clean_update(pctx)
       
  3463     graftstate.delete()
       
  3464     ui.status(_(b"stopped the interrupted graft\n"))
       
  3465     ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
       
  3466     return 0
       
  3467 
  3192 
  3468 
  3193 
  3469 statemod.addunfinished(
  3194 statemod.addunfinished(
  3470     b'graft',
  3195     b'graft',
  3471     fname=b'graftstate',
  3196     fname=b'graftstate',