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, |
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(): |