comparison mercurial/cmdutil.py @ 34085:e8a7c1a0565a

cmdutil: remove the redundant commit during amend There was an extra commit made during the amend operation to track the changes to the working copy. However, this logic was written a long time back and newer API's make this extra commit redundant. Therefore, I am removing the extra commit. After this change, I noticed that - Execution time of the cmdutil.amend improved by over 40%. - Execution time of "hg commit --amend" improved by over 20%. Test Plan: I ensured that the all the hg tests passed after the change. I had to fix a few tests which were aware of the extra commit made during the amend. Differential Revision: https://phab.mercurial-scm.org/D636
author Saurabh Singh <singhsrb@fb.com>
date Fri, 01 Sep 2017 12:34:36 -0700
parents 08346a8fa65f
children a39dce4a76b8
comparison
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():