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