Mercurial > hg
comparison mercurial/shelve.py @ 42616:5162753c4c14
unshelve: add interactive mode
Until now, there is no way to `unshelve` selected changes only from
the stored shelve as given in issue6162. This patch makes `unshelve`
perform with certain changes only by adding an interactive mode.
Differential Revision: https://phab.mercurial-scm.org/D6596
author | Navaneeth Suresh <navaneeths1998@gmail.com> |
---|---|
date | Tue, 02 Jul 2019 18:02:12 +0530 |
parents | 117437f3f541 |
children | c9114885c14b |
comparison
equal
deleted
inserted
replaced
42615:56132ebd14c6 | 42616:5162753c4c14 |
---|---|
692 for filetype in shelvefileextensions: | 692 for filetype in shelvefileextensions: |
693 shfile = shelvedfile(repo, name, filetype) | 693 shfile = shelvedfile(repo, name, filetype) |
694 if shfile.exists(): | 694 if shfile.exists(): |
695 shfile.movetobackup() | 695 shfile.movetobackup() |
696 cleanupoldbackups(repo) | 696 cleanupoldbackups(repo) |
697 def unshelvecontinue(ui, repo, state, opts): | 697 def unshelvecontinue(ui, repo, state, opts, basename=None): |
698 """subcommand to continue an in-progress unshelve""" | 698 """subcommand to continue an in-progress unshelve""" |
699 # We're finishing off a merge. First parent is our original | 699 # We're finishing off a merge. First parent is our original |
700 # parent, second is the temporary "fake" commit we're unshelving. | 700 # parent, second is the temporary "fake" commit we're unshelving. |
701 interactive = opts.get('interactive') | |
701 with repo.lock(): | 702 with repo.lock(): |
702 checkparents(repo, state) | 703 checkparents(repo, state) |
703 ms = merge.mergestate.read(repo) | 704 ms = merge.mergestate.read(repo) |
704 if list(ms.unresolved()): | 705 if list(ms.unresolved()): |
705 raise error.Abort( | 706 raise error.Abort( |
718 targetphase = phases.secret | 719 targetphase = phases.secret |
719 overrides = {('phases', 'new-commit'): targetphase} | 720 overrides = {('phases', 'new-commit'): targetphase} |
720 with repo.ui.configoverride(overrides, 'unshelve'): | 721 with repo.ui.configoverride(overrides, 'unshelve'): |
721 with repo.dirstate.parentchange(): | 722 with repo.dirstate.parentchange(): |
722 repo.setparents(state.parents[0], nodemod.nullid) | 723 repo.setparents(state.parents[0], nodemod.nullid) |
723 newnode = repo.commit(text=shelvectx.description(), | 724 if not interactive: |
724 extra=shelvectx.extra(), | 725 ispartialunshelve = False |
725 user=shelvectx.user(), | 726 newnode = repo.commit(text=shelvectx.description(), |
726 date=shelvectx.date()) | 727 extra=shelvectx.extra(), |
728 user=shelvectx.user(), | |
729 date=shelvectx.date()) | |
730 else: | |
731 newnode, ispartialunshelve = _dounshelveinteractive(ui, | |
732 repo, shelvectx, basename, opts) | |
727 | 733 |
728 if newnode is None: | 734 if newnode is None: |
729 # If it ended up being a no-op commit, then the normal | 735 # If it ended up being a no-op commit, then the normal |
730 # merge state clean-up path doesn't happen, so do it | 736 # merge state clean-up path doesn't happen, so do it |
731 # here. Fix issue5494 | 737 # here. Fix issue5494 |
741 | 747 |
742 hg.updaterepo(repo, pendingctx.node(), overwrite=False) | 748 hg.updaterepo(repo, pendingctx.node(), overwrite=False) |
743 mergefiles(ui, repo, state.wctx, shelvectx) | 749 mergefiles(ui, repo, state.wctx, shelvectx) |
744 restorebranch(ui, repo, state.branchtorestore) | 750 restorebranch(ui, repo, state.branchtorestore) |
745 | 751 |
746 if not phases.supportinternal(repo): | 752 if not ispartialunshelve: |
747 repair.strip(ui, repo, state.nodestoremove, backup=False, | 753 if not phases.supportinternal(repo): |
748 topic='shelve') | 754 repair.strip(ui, repo, state.nodestoremove, backup=False, |
755 topic='shelve') | |
756 shelvedstate.clear(repo) | |
757 unshelvecleanup(ui, repo, state.name, opts) | |
749 _restoreactivebookmark(repo, state.activebookmark) | 758 _restoreactivebookmark(repo, state.activebookmark) |
750 shelvedstate.clear(repo) | |
751 unshelvecleanup(ui, repo, state.name, opts) | |
752 ui.status(_("unshelve of '%s' complete\n") % state.name) | 759 ui.status(_("unshelve of '%s' complete\n") % state.name) |
753 | 760 |
754 def hgcontinueunshelve(ui, repo): | 761 def hgcontinueunshelve(ui, repo): |
755 """logic to resume unshelve using 'hg continue'""" | 762 """logic to resume unshelve using 'hg continue'""" |
756 with repo.wlock(): | 763 with repo.wlock(): |
795 else: | 802 else: |
796 shelvectx = repo[node] | 803 shelvectx = repo[node] |
797 | 804 |
798 return repo, shelvectx | 805 return repo, shelvectx |
799 | 806 |
807 def _dounshelveinteractive(ui, repo, shelvectx, basename, opts): | |
808 """The user might want to unshelve certain changes only from the stored | |
809 shelve. So, we would create two commits. One with requested changes to | |
810 unshelve at that time and the latter is shelved for future. | |
811 """ | |
812 opts['message'] = shelvectx.description() | |
813 opts['interactive-unshelve'] = True | |
814 pats = [] | |
815 commitfunc = getcommitfunc(shelvectx.extra(), interactive=True, | |
816 editor=True) | |
817 newnode = cmdutil.dorecord(ui, repo, commitfunc, None, False, | |
818 cmdutil.recordfilter, *pats, | |
819 **pycompat.strkwargs(opts)) | |
820 snode = repo.commit(text=shelvectx.description(), | |
821 extra=shelvectx.extra(), | |
822 user=shelvectx.user(), | |
823 date=shelvectx.date()) | |
824 m = scmutil.matchfiles(repo, repo[snode].files()) | |
825 if snode: | |
826 _shelvecreatedcommit(repo, snode, basename, m) | |
827 | |
828 return newnode, bool(snode) | |
829 | |
800 def _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, basename, pctx, | 830 def _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, basename, pctx, |
801 tmpwctx, shelvectx, branchtorestore, | 831 tmpwctx, shelvectx, branchtorestore, |
802 activebookmark): | 832 activebookmark): |
803 """Rebase restored commit from its original location to a destination""" | 833 """Rebase restored commit from its original location to a destination""" |
804 # If the shelve is not immediately on top of the commit | 834 # If the shelve is not immediately on top of the commit |
805 # we'll be merging with, rebase it to be on top. | 835 # we'll be merging with, rebase it to be on top. |
806 if tmpwctx.node() == shelvectx.p1().node(): | 836 interactive = opts.get('interactive') |
807 return shelvectx | 837 if tmpwctx.node() == shelvectx.p1().node() and not interactive: |
838 # We won't skip on interactive mode because, the user might want to | |
839 # unshelve certain changes only. | |
840 return shelvectx, False | |
808 | 841 |
809 overrides = { | 842 overrides = { |
810 ('ui', 'forcemerge'): opts.get('tool', ''), | 843 ('ui', 'forcemerge'): opts.get('tool', ''), |
811 ('phases', 'new-commit'): phases.secret, | 844 ('phases', 'new-commit'): phases.secret, |
812 } | 845 } |
826 _("unresolved conflicts (see 'hg resolve', then " | 859 _("unresolved conflicts (see 'hg resolve', then " |
827 "'hg unshelve --continue')")) | 860 "'hg unshelve --continue')")) |
828 | 861 |
829 with repo.dirstate.parentchange(): | 862 with repo.dirstate.parentchange(): |
830 repo.setparents(tmpwctx.node(), nodemod.nullid) | 863 repo.setparents(tmpwctx.node(), nodemod.nullid) |
831 newnode = repo.commit(text=shelvectx.description(), | 864 if not interactive: |
832 extra=shelvectx.extra(), | 865 ispartialunshelve = False |
833 user=shelvectx.user(), | 866 newnode = repo.commit(text=shelvectx.description(), |
834 date=shelvectx.date()) | 867 extra=shelvectx.extra(), |
868 user=shelvectx.user(), | |
869 date=shelvectx.date()) | |
870 else: | |
871 newnode, ispartialunshelve = _dounshelveinteractive(ui, repo, | |
872 shelvectx, basename, opts) | |
835 | 873 |
836 if newnode is None: | 874 if newnode is None: |
837 # If it ended up being a no-op commit, then the normal | 875 # If it ended up being a no-op commit, then the normal |
838 # merge state clean-up path doesn't happen, so do it | 876 # merge state clean-up path doesn't happen, so do it |
839 # here. Fix issue5494 | 877 # here. Fix issue5494 |
844 ui.status(msg) | 882 ui.status(msg) |
845 else: | 883 else: |
846 shelvectx = repo[newnode] | 884 shelvectx = repo[newnode] |
847 hg.updaterepo(repo, tmpwctx.node(), False) | 885 hg.updaterepo(repo, tmpwctx.node(), False) |
848 | 886 |
849 return shelvectx | 887 return shelvectx, ispartialunshelve |
850 | 888 |
851 def _forgetunknownfiles(repo, shelvectx, addedbefore): | 889 def _forgetunknownfiles(repo, shelvectx, addedbefore): |
852 # Forget any files that were unknown before the shelve, unknown before | 890 # Forget any files that were unknown before the shelve, unknown before |
853 # unshelve started, but are now added. | 891 # unshelve started, but are now added. |
854 shelveunknown = shelvectx.extra().get('shelve_unknown') | 892 shelveunknown = shelvectx.extra().get('shelve_unknown') |
881 | 919 |
882 def dounshelve(ui, repo, *shelved, **opts): | 920 def dounshelve(ui, repo, *shelved, **opts): |
883 opts = pycompat.byteskwargs(opts) | 921 opts = pycompat.byteskwargs(opts) |
884 abortf = opts.get('abort') | 922 abortf = opts.get('abort') |
885 continuef = opts.get('continue') | 923 continuef = opts.get('continue') |
924 interactive = opts.get('interactive') | |
886 if not abortf and not continuef: | 925 if not abortf and not continuef: |
887 cmdutil.checkunfinished(repo) | 926 cmdutil.checkunfinished(repo) |
888 shelved = list(shelved) | 927 shelved = list(shelved) |
889 if opts.get("name"): | 928 if opts.get("name"): |
890 shelved.append(opts["name"]) | 929 shelved.append(opts["name"]) |
891 | 930 |
892 if abortf or continuef: | 931 if abortf or continuef and not interactive: |
893 if abortf and continuef: | 932 if abortf and continuef: |
894 raise error.Abort(_('cannot use both abort and continue')) | 933 raise error.Abort(_('cannot use both abort and continue')) |
895 if shelved: | 934 if shelved: |
896 raise error.Abort(_('cannot combine abort/continue with ' | 935 raise error.Abort(_('cannot combine abort/continue with ' |
897 'naming a shelved change')) | 936 'naming a shelved change')) |
909 shelved = listshelves(repo) | 948 shelved = listshelves(repo) |
910 if not shelved: | 949 if not shelved: |
911 raise error.Abort(_('no shelved changes to apply!')) | 950 raise error.Abort(_('no shelved changes to apply!')) |
912 basename = util.split(shelved[0][1])[1] | 951 basename = util.split(shelved[0][1])[1] |
913 ui.status(_("unshelving change '%s'\n") % basename) | 952 ui.status(_("unshelving change '%s'\n") % basename) |
914 else: | 953 elif shelved: |
915 basename = shelved[0] | 954 basename = shelved[0] |
955 if continuef and interactive: | |
956 state = _loadshelvedstate(ui, repo, opts) | |
957 return unshelvecontinue(ui, repo, state, opts, basename) | |
916 | 958 |
917 if not shelvedfile(repo, basename, patchextension).exists(): | 959 if not shelvedfile(repo, basename, patchextension).exists(): |
918 raise error.Abort(_("shelved change '%s' not found") % basename) | 960 raise error.Abort(_("shelved change '%s' not found") % basename) |
919 | 961 |
920 repo = repo.unfiltered() | 962 repo = repo.unfiltered() |
939 _checkunshelveuntrackedproblems(ui, repo, shelvectx) | 981 _checkunshelveuntrackedproblems(ui, repo, shelvectx) |
940 branchtorestore = '' | 982 branchtorestore = '' |
941 if shelvectx.branch() != shelvectx.p1().branch(): | 983 if shelvectx.branch() != shelvectx.p1().branch(): |
942 branchtorestore = shelvectx.branch() | 984 branchtorestore = shelvectx.branch() |
943 | 985 |
944 shelvectx = _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, | 986 shelvectx, ispartialunshelve = _rebaserestoredcommit(ui, repo, opts, |
945 basename, pctx, tmpwctx, | 987 tr, oldtiprev, basename, pctx, tmpwctx, shelvectx, |
946 shelvectx, branchtorestore, | 988 branchtorestore, activebookmark) |
947 activebookmark) | |
948 overrides = {('ui', 'forcemerge'): opts.get('tool', '')} | 989 overrides = {('ui', 'forcemerge'): opts.get('tool', '')} |
949 with ui.configoverride(overrides, 'unshelve'): | 990 with ui.configoverride(overrides, 'unshelve'): |
950 mergefiles(ui, repo, pctx, shelvectx) | 991 mergefiles(ui, repo, pctx, shelvectx) |
951 restorebranch(ui, repo, branchtorestore) | 992 restorebranch(ui, repo, branchtorestore) |
952 _forgetunknownfiles(repo, shelvectx, addedbefore) | 993 if not ispartialunshelve: |
953 | 994 _forgetunknownfiles(repo, shelvectx, addedbefore) |
954 shelvedstate.clear(repo) | 995 |
955 _finishunshelve(repo, oldtiprev, tr, activebookmark) | 996 shelvedstate.clear(repo) |
956 unshelvecleanup(ui, repo, basename, opts) | 997 _finishunshelve(repo, oldtiprev, tr, activebookmark) |
998 unshelvecleanup(ui, repo, basename, opts) | |
957 finally: | 999 finally: |
958 if tr: | 1000 if tr: |
959 tr.release() | 1001 tr.release() |
960 lockmod.release(lock) | 1002 lockmod.release(lock) |