mercurial/cmdutil.py
changeset 34085 e8a7c1a0565a
parent 34083 08346a8fa65f
child 34086 a39dce4a76b8
equal deleted inserted replaced
34084:6c5cdb02f2f9 34085:e8a7c1a0565a
  3024         else:
  3024         else:
  3025             return False
  3025             return False
  3026     else:
  3026     else:
  3027         return f not in ctx2.manifest()
  3027         return f not in ctx2.manifest()
  3028 
  3028 
       
  3029 # TODO: remove the commitfunc parameter because it is no longer used
  3029 def amend(ui, repo, commitfunc, old, extra, pats, opts):
  3030 def amend(ui, repo, commitfunc, old, extra, pats, opts):
  3030     # avoid cycle context -> subrepo -> cmdutil
  3031     # avoid cycle context -> subrepo -> cmdutil
  3031     from . import context
  3032     from . import context
  3032 
  3033 
  3033     # amend will reuse the existing user if not specified, but the obsolete
  3034     # amend will reuse the existing user if not specified, but the obsolete
  3037 
  3038 
  3038     ui.note(_('amending changeset %s\n') % old)
  3039     ui.note(_('amending changeset %s\n') % old)
  3039     base = old.p1()
  3040     base = old.p1()
  3040 
  3041 
  3041     with repo.wlock(), repo.lock(), repo.transaction('amend'):
  3042     with repo.wlock(), repo.lock(), repo.transaction('amend'):
  3042         # See if we got a message from -m or -l, if not, open the editor
       
  3043         # with the message of the changeset to amend
       
  3044         message = logmessage(ui, opts)
       
  3045         # ensure logfile does not conflict with later enforcement of the
       
  3046         # message. potential logfile content has been processed by
       
  3047         # `logmessage` anyway.
       
  3048         opts.pop('logfile')
       
  3049         # First, do a regular commit to record all changes in the working
       
  3050         # directory (if there are any)
       
  3051         ui.callhooks = False
       
  3052         activebookmark = repo._bookmarks.active
       
  3053         try:
       
  3054             repo._bookmarks.active = None
       
  3055             opts['message'] = 'temporary amend commit for %s' % old
       
  3056             node = commit(ui, repo, commitfunc, pats, opts)
       
  3057         finally:
       
  3058             repo._bookmarks.active = activebookmark
       
  3059             ui.callhooks = True
       
  3060         ctx = repo[node]
       
  3061 
       
  3062         # Participating changesets:
  3043         # Participating changesets:
  3063         #
  3044         #
  3064         # node/ctx o - new (intermediate) commit that contains changes
  3045         # wctx     o - workingctx that contains changes from working copy
  3065         #          |   from working dir to go into amending commit
  3046         #          |   to go into amending commit
  3066         #          |   (or a workingctx if there were no changes)
       
  3067         #          |
  3047         #          |
  3068         # old      o - changeset to amend
  3048         # old      o - changeset to amend
  3069         #          |
  3049         #          |
  3070         # base     o - first parent of the changeset to amend
  3050         # base     o - first parent of the changeset to amend
       
  3051         wctx = repo[None]
  3071 
  3052 
  3072         # Update extra dict from amended commit (e.g. to preserve graft
  3053         # Update extra dict from amended commit (e.g. to preserve graft
  3073         # source)
  3054         # source)
  3074         extra.update(old.extra())
  3055         extra.update(old.extra())
  3075 
  3056 
  3076         # Also update it from the intermediate commit or from the wctx
  3057         # Also update it from the from the wctx
  3077         extra.update(ctx.extra())
  3058         extra.update(wctx.extra())
       
  3059 
       
  3060         user = opts.get('user') or old.user()
       
  3061         date = opts.get('date') or old.date()
  3078 
  3062 
  3079         if len(old.parents()) > 1:
  3063         if len(old.parents()) > 1:
  3080             # ctx.files() isn't reliable for merges, so fall back to the
  3064             # ctx.files() isn't reliable for merges, so fall back to the
  3081             # slower repo.status() method
  3065             # slower repo.status() method
  3082             files = set([fn for st in repo.status(base, old)[:3]
  3066             files = set([fn for st in repo.status(base, old)[:3]
  3083                          for fn in st])
  3067                          for fn in st])
  3084         else:
  3068         else:
  3085             files = set(old.files())
  3069             files = set(old.files())
  3086 
  3070 
  3087         # Second, we use either the commit we just did, or if there were no
  3071         # add/remove the files to the working copy if the "addremove" option
  3088         # changes the parent of the working directory as the version of the
  3072         # was specified.
  3089         # files in the final amend commit
  3073         matcher = scmutil.match(wctx, pats, opts)
  3090         if node:
  3074         if (opts.get('addremove')
  3091             ui.note(_('copying changeset %s to %s\n') % (ctx, base))
  3075             and scmutil.addremove(repo, matcher, "", opts)):
  3092 
  3076             raise error.Abort(
  3093             user = ctx.user()
  3077                 _("failed to mark all new/missing files as added/removed"))
  3094             date = ctx.date()
  3078 
       
  3079         filestoamend = set(f for f in wctx.files() if matcher(f))
       
  3080 
       
  3081         changes = (len(filestoamend) > 0)
       
  3082         if changes:
  3095             # Recompute copies (avoid recording a -> b -> a)
  3083             # Recompute copies (avoid recording a -> b -> a)
  3096             copied = copies.pathcopies(base, ctx)
  3084             copied = copies.pathcopies(base, wctx, matcher)
  3097             if old.p2:
  3085             if old.p2:
  3098                 copied.update(copies.pathcopies(old.p2(), ctx))
  3086                 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
  3099 
  3087 
  3100             # Prune files which were reverted by the updates: if old
  3088             # Prune files which were reverted by the updates: if old
  3101             # introduced file X and our intermediate commit, node,
  3089             # introduced file X and the file was renamed in the working
  3102             # renamed that file, then those two files are the same and
  3090             # copy, then those two files are the same and
  3103             # we can discard X from our list of files. Likewise if X
  3091             # we can discard X from our list of files. Likewise if X
  3104             # was deleted, it's no longer relevant
  3092             # was deleted, it's no longer relevant
  3105             files.update(ctx.files())
  3093             files.update(filestoamend)
  3106             files = [f for f in files if not samefile(f, ctx, base)]
  3094             files = [f for f in files if not samefile(f, wctx, base)]
  3107 
  3095 
  3108             def filectxfn(repo, ctx_, path):
  3096             def filectxfn(repo, ctx_, path):
  3109                 try:
  3097                 try:
  3110                     fctx = ctx[path]
  3098                     # If the file being considered is not amongst the files
       
  3099                     # to be amended, we should return the file context from the
       
  3100                     # old changeset. This avoids issues when only some files in
       
  3101                     # the working copy are being amended but there are also
       
  3102                     # changes to other files from the old changeset.
       
  3103                     if path not in filestoamend:
       
  3104                         return old.filectx(path)
       
  3105 
       
  3106                     fctx = wctx[path]
       
  3107 
       
  3108                     # Return None for removed files.
       
  3109                     if not fctx.exists():
       
  3110                         return None
       
  3111 
  3111                     flags = fctx.flags()
  3112                     flags = fctx.flags()
  3112                     mctx = context.memfilectx(repo,
  3113                     mctx = context.memfilectx(repo,
  3113                                               fctx.path(), fctx.data(),
  3114                                               fctx.path(), fctx.data(),
  3114                                               islink='l' in flags,
  3115                                               islink='l' in flags,
  3115                                               isexec='x' in flags,
  3116                                               isexec='x' in flags,
  3125                 try:
  3126                 try:
  3126                     return old.filectx(path)
  3127                     return old.filectx(path)
  3127                 except KeyError:
  3128                 except KeyError:
  3128                     return None
  3129                     return None
  3129 
  3130 
  3130             user = opts.get('user') or old.user()
  3131         # See if we got a message from -m or -l, if not, open the editor with
  3131             date = opts.get('date') or old.date()
  3132         # the message of the changeset to amend.
       
  3133         message = logmessage(ui, opts)
       
  3134 
  3132         editform = mergeeditform(old, 'commit.amend')
  3135         editform = mergeeditform(old, 'commit.amend')
  3133         editor = getcommiteditor(editform=editform,
  3136         editor = getcommiteditor(editform=editform,
  3134                                  **pycompat.strkwargs(opts))
  3137                                  **pycompat.strkwargs(opts))
       
  3138 
  3135         if not message:
  3139         if not message:
  3136             editor = getcommiteditor(edit=True, editform=editform)
  3140             editor = getcommiteditor(edit=True, editform=editform)
  3137             message = old.description()
  3141             message = old.description()
  3138 
  3142 
  3139         pureextra = extra.copy()
  3143         pureextra = extra.copy()
  3148                              date=date,
  3152                              date=date,
  3149                              extra=extra,
  3153                              extra=extra,
  3150                              editor=editor)
  3154                              editor=editor)
  3151 
  3155 
  3152         newdesc = changelog.stripdesc(new.description())
  3156         newdesc = changelog.stripdesc(new.description())
  3153         if ((not node)
  3157         if ((not changes)
  3154             and newdesc == old.description()
  3158             and newdesc == old.description()
  3155             and user == old.user()
  3159             and user == old.user()
  3156             and date == old.date()
  3160             and date == old.date()
  3157             and pureextra == old.extra()):
  3161             and pureextra == old.extra()):
  3158             # nothing changed. continuing here would create a new node
  3162             # nothing changed. continuing here would create a new node
  3170             newid = repo.commitctx(new)
  3174             newid = repo.commitctx(new)
  3171 
  3175 
  3172         # Reroute the working copy parent to the new changeset
  3176         # Reroute the working copy parent to the new changeset
  3173         repo.setparents(newid, nullid)
  3177         repo.setparents(newid, nullid)
  3174         mapping = {old.node(): (newid,)}
  3178         mapping = {old.node(): (newid,)}
  3175         if node:
       
  3176             mapping[node] = ()
       
  3177         scmutil.cleanupnodes(repo, mapping, 'amend')
  3179         scmutil.cleanupnodes(repo, mapping, 'amend')
       
  3180 
       
  3181         # Fixing the dirstate because localrepo.commitctx does not update
       
  3182         # it. This is rather convenient because we did not need to update
       
  3183         # the dirstate for all the files in the new commit which commitctx
       
  3184         # could have done if it updated the dirstate. Now, we can
       
  3185         # selectively update the dirstate only for the amended files.
       
  3186         dirstate = repo.dirstate
       
  3187 
       
  3188         # Update the state of the files which were added and
       
  3189         # and modified in the amend to "normal" in the dirstate.
       
  3190         normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
       
  3191         for f in normalfiles:
       
  3192             dirstate.normal(f)
       
  3193 
       
  3194         # Update the state of files which were removed in the amend
       
  3195         # to "removed" in the dirstate.
       
  3196         removedfiles = set(wctx.removed()) & filestoamend
       
  3197         for f in removedfiles:
       
  3198             dirstate.drop(f)
  3178 
  3199 
  3179     return newid
  3200     return newid
  3180 
  3201 
  3181 def commiteditor(repo, ctx, subs, editform=''):
  3202 def commiteditor(repo, ctx, subs, editform=''):
  3182     if ctx.description():
  3203     if ctx.description():