mercurial/cmdutil.py
changeset 33438 8056481caa81
parent 33362 e48fb90f80c8
child 33499 0407a51b9d8c
equal deleted inserted replaced
33437:0720e6265c8a 33438:8056481caa81
  2748 
  2748 
  2749     ui.note(_('amending changeset %s\n') % old)
  2749     ui.note(_('amending changeset %s\n') % old)
  2750     base = old.p1()
  2750     base = old.p1()
  2751 
  2751 
  2752     newid = None
  2752     newid = None
  2753     with repo.wlock(), repo.lock():
  2753     with repo.wlock(), repo.lock(), repo.transaction('amend') as tr:
  2754         with repo.transaction('amend') as tr:
  2754         # See if we got a message from -m or -l, if not, open the editor
  2755             # See if we got a message from -m or -l, if not, open the editor
  2755         # with the message of the changeset to amend
  2756             # with the message of the changeset to amend
  2756         message = logmessage(ui, opts)
  2757             message = logmessage(ui, opts)
  2757         # ensure logfile does not conflict with later enforcement of the
  2758             # ensure logfile does not conflict with later enforcement of the
  2758         # message. potential logfile content has been processed by
  2759             # message. potential logfile content has been processed by
  2759         # `logmessage` anyway.
  2760             # `logmessage` anyway.
  2760         opts.pop('logfile')
  2761             opts.pop('logfile')
  2761         # First, do a regular commit to record all changes in the working
  2762             # First, do a regular commit to record all changes in the working
  2762         # directory (if there are any)
  2763             # directory (if there are any)
  2763         ui.callhooks = False
  2764             ui.callhooks = False
  2764         activebookmark = repo._bookmarks.active
  2765             activebookmark = repo._bookmarks.active
  2765         try:
  2766             try:
  2766             repo._bookmarks.active = None
  2767                 repo._bookmarks.active = None
  2767             opts['message'] = 'temporary amend commit for %s' % old
  2768                 opts['message'] = 'temporary amend commit for %s' % old
  2768             node = commit(ui, repo, commitfunc, pats, opts)
  2769                 node = commit(ui, repo, commitfunc, pats, opts)
  2769         finally:
  2770             finally:
  2770             repo._bookmarks.active = activebookmark
  2771                 repo._bookmarks.active = activebookmark
  2771             repo._bookmarks.recordchange(tr)
  2772                 repo._bookmarks.recordchange(tr)
  2772             ui.callhooks = True
  2773                 ui.callhooks = True
  2773         ctx = repo[node]
  2774             ctx = repo[node]
  2774 
  2775 
  2775         # Participating changesets:
  2776             # Participating changesets:
  2776         #
       
  2777         # node/ctx o - new (intermediate) commit that contains changes
       
  2778         #          |   from working dir to go into amending commit
       
  2779         #          |   (or a workingctx if there were no changes)
       
  2780         #          |
       
  2781         # old      o - changeset to amend
       
  2782         #          |
       
  2783         # base     o - parent of amending changeset
       
  2784 
       
  2785         # Update extra dict from amended commit (e.g. to preserve graft
       
  2786         # source)
       
  2787         extra.update(old.extra())
       
  2788 
       
  2789         # Also update it from the intermediate commit or from the wctx
       
  2790         extra.update(ctx.extra())
       
  2791 
       
  2792         if len(old.parents()) > 1:
       
  2793             # ctx.files() isn't reliable for merges, so fall back to the
       
  2794             # slower repo.status() method
       
  2795             files = set([fn for st in repo.status(base, old)[:3]
       
  2796                          for fn in st])
       
  2797         else:
       
  2798             files = set(old.files())
       
  2799 
       
  2800         # Second, we use either the commit we just did, or if there were no
       
  2801         # changes the parent of the working directory as the version of the
       
  2802         # files in the final amend commit
       
  2803         if node:
       
  2804             ui.note(_('copying changeset %s to %s\n') % (ctx, base))
       
  2805 
       
  2806             user = ctx.user()
       
  2807             date = ctx.date()
       
  2808             # Recompute copies (avoid recording a -> b -> a)
       
  2809             copied = copies.pathcopies(base, ctx)
       
  2810             if old.p2:
       
  2811                 copied.update(copies.pathcopies(old.p2(), ctx))
       
  2812 
       
  2813             # Prune files which were reverted by the updates: if old
       
  2814             # introduced file X and our intermediate commit, node,
       
  2815             # renamed that file, then those two files are the same and
       
  2816             # we can discard X from our list of files. Likewise if X
       
  2817             # was deleted, it's no longer relevant
       
  2818             files.update(ctx.files())
       
  2819             files = [f for f in files if not samefile(f, ctx, base)]
       
  2820 
       
  2821             def filectxfn(repo, ctx_, path):
       
  2822                 try:
       
  2823                     fctx = ctx[path]
       
  2824                     flags = fctx.flags()
       
  2825                     mctx = context.memfilectx(repo,
       
  2826                                               fctx.path(), fctx.data(),
       
  2827                                               islink='l' in flags,
       
  2828                                               isexec='x' in flags,
       
  2829                                               copied=copied.get(path))
       
  2830                     return mctx
       
  2831                 except KeyError:
       
  2832                     return None
       
  2833         else:
       
  2834             ui.note(_('copying changeset %s to %s\n') % (old, base))
       
  2835 
       
  2836             # Use version of files as in the old cset
       
  2837             def filectxfn(repo, ctx_, path):
       
  2838                 try:
       
  2839                     return old.filectx(path)
       
  2840                 except KeyError:
       
  2841                     return None
       
  2842 
       
  2843             user = opts.get('user') or old.user()
       
  2844             date = opts.get('date') or old.date()
       
  2845         editform = mergeeditform(old, 'commit.amend')
       
  2846         editor = getcommiteditor(editform=editform,
       
  2847                                  **pycompat.strkwargs(opts))
       
  2848         if not message:
       
  2849             editor = getcommiteditor(edit=True, editform=editform)
       
  2850             message = old.description()
       
  2851 
       
  2852         pureextra = extra.copy()
       
  2853         extra['amend_source'] = old.hex()
       
  2854 
       
  2855         new = context.memctx(repo,
       
  2856                              parents=[base.node(), old.p2().node()],
       
  2857                              text=message,
       
  2858                              files=files,
       
  2859                              filectxfn=filectxfn,
       
  2860                              user=user,
       
  2861                              date=date,
       
  2862                              extra=extra,
       
  2863                              editor=editor)
       
  2864 
       
  2865         newdesc = changelog.stripdesc(new.description())
       
  2866         if ((not node)
       
  2867             and newdesc == old.description()
       
  2868             and user == old.user()
       
  2869             and date == old.date()
       
  2870             and pureextra == old.extra()):
       
  2871             # nothing changed. continuing here would create a new node
       
  2872             # anyway because of the amend_source noise.
  2777             #
  2873             #
  2778             # node/ctx o - new (intermediate) commit that contains changes
  2874             # This not what we expect from amend.
  2779             #          |   from working dir to go into amending commit
  2875             return old.node()
  2780             #          |   (or a workingctx if there were no changes)
  2876 
  2781             #          |
  2877         ph = repo.ui.config('phases', 'new-commit', phases.draft)
  2782             # old      o - changeset to amend
  2878         try:
  2783             #          |
  2879             if opts.get('secret'):
  2784             # base     o - parent of amending changeset
  2880                 commitphase = 'secret'
  2785 
       
  2786             # Update extra dict from amended commit (e.g. to preserve graft
       
  2787             # source)
       
  2788             extra.update(old.extra())
       
  2789 
       
  2790             # Also update it from the intermediate commit or from the wctx
       
  2791             extra.update(ctx.extra())
       
  2792 
       
  2793             if len(old.parents()) > 1:
       
  2794                 # ctx.files() isn't reliable for merges, so fall back to the
       
  2795                 # slower repo.status() method
       
  2796                 files = set([fn for st in repo.status(base, old)[:3]
       
  2797                              for fn in st])
       
  2798             else:
  2881             else:
  2799                 files = set(old.files())
  2882                 commitphase = old.phase()
  2800 
  2883             repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
  2801             # Second, we use either the commit we just did, or if there were no
  2884             newid = repo.commitctx(new)
  2802             # changes the parent of the working directory as the version of the
  2885         finally:
  2803             # files in the final amend commit
  2886             repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
       
  2887         if newid != old.node():
       
  2888             # Reroute the working copy parent to the new changeset
       
  2889             repo.setparents(newid, nullid)
       
  2890             mapping = {old.node(): (newid,)}
  2804             if node:
  2891             if node:
  2805                 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
  2892                 mapping[node] = ()
  2806 
  2893             scmutil.cleanupnodes(repo, mapping, 'amend')
  2807                 user = ctx.user()
       
  2808                 date = ctx.date()
       
  2809                 # Recompute copies (avoid recording a -> b -> a)
       
  2810                 copied = copies.pathcopies(base, ctx)
       
  2811                 if old.p2:
       
  2812                     copied.update(copies.pathcopies(old.p2(), ctx))
       
  2813 
       
  2814                 # Prune files which were reverted by the updates: if old
       
  2815                 # introduced file X and our intermediate commit, node,
       
  2816                 # renamed that file, then those two files are the same and
       
  2817                 # we can discard X from our list of files. Likewise if X
       
  2818                 # was deleted, it's no longer relevant
       
  2819                 files.update(ctx.files())
       
  2820                 files = [f for f in files if not samefile(f, ctx, base)]
       
  2821 
       
  2822                 def filectxfn(repo, ctx_, path):
       
  2823                     try:
       
  2824                         fctx = ctx[path]
       
  2825                         flags = fctx.flags()
       
  2826                         mctx = context.memfilectx(repo,
       
  2827                                                   fctx.path(), fctx.data(),
       
  2828                                                   islink='l' in flags,
       
  2829                                                   isexec='x' in flags,
       
  2830                                                   copied=copied.get(path))
       
  2831                         return mctx
       
  2832                     except KeyError:
       
  2833                         return None
       
  2834             else:
       
  2835                 ui.note(_('copying changeset %s to %s\n') % (old, base))
       
  2836 
       
  2837                 # Use version of files as in the old cset
       
  2838                 def filectxfn(repo, ctx_, path):
       
  2839                     try:
       
  2840                         return old.filectx(path)
       
  2841                     except KeyError:
       
  2842                         return None
       
  2843 
       
  2844                 user = opts.get('user') or old.user()
       
  2845                 date = opts.get('date') or old.date()
       
  2846             editform = mergeeditform(old, 'commit.amend')
       
  2847             editor = getcommiteditor(editform=editform,
       
  2848                                      **pycompat.strkwargs(opts))
       
  2849             if not message:
       
  2850                 editor = getcommiteditor(edit=True, editform=editform)
       
  2851                 message = old.description()
       
  2852 
       
  2853             pureextra = extra.copy()
       
  2854             extra['amend_source'] = old.hex()
       
  2855 
       
  2856             new = context.memctx(repo,
       
  2857                                  parents=[base.node(), old.p2().node()],
       
  2858                                  text=message,
       
  2859                                  files=files,
       
  2860                                  filectxfn=filectxfn,
       
  2861                                  user=user,
       
  2862                                  date=date,
       
  2863                                  extra=extra,
       
  2864                                  editor=editor)
       
  2865 
       
  2866             newdesc = changelog.stripdesc(new.description())
       
  2867             if ((not node)
       
  2868                 and newdesc == old.description()
       
  2869                 and user == old.user()
       
  2870                 and date == old.date()
       
  2871                 and pureextra == old.extra()):
       
  2872                 # nothing changed. continuing here would create a new node
       
  2873                 # anyway because of the amend_source noise.
       
  2874                 #
       
  2875                 # This not what we expect from amend.
       
  2876                 return old.node()
       
  2877 
       
  2878             ph = repo.ui.config('phases', 'new-commit', phases.draft)
       
  2879             try:
       
  2880                 if opts.get('secret'):
       
  2881                     commitphase = 'secret'
       
  2882                 else:
       
  2883                     commitphase = old.phase()
       
  2884                 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
       
  2885                 newid = repo.commitctx(new)
       
  2886             finally:
       
  2887                 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
       
  2888             if newid != old.node():
       
  2889                 # Reroute the working copy parent to the new changeset
       
  2890                 repo.setparents(newid, nullid)
       
  2891                 mapping = {old.node(): (newid,)}
       
  2892                 if node:
       
  2893                     mapping[node] = ()
       
  2894                 scmutil.cleanupnodes(repo, mapping, 'amend')
       
  2895     return newid
  2894     return newid
  2896 
  2895 
  2897 def commiteditor(repo, ctx, subs, editform=''):
  2896 def commiteditor(repo, ctx, subs, editform=''):
  2898     if ctx.description():
  2897     if ctx.description():
  2899         return ctx.description()
  2898         return ctx.description()