Mercurial > hg
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(): |