943 |
942 |
944 @eh.extsetup |
943 @eh.extsetup |
945 def deprecatealiases(ui): |
944 def deprecatealiases(ui): |
946 _deprecatealias('gup', 'next') |
945 _deprecatealias('gup', 'next') |
947 _deprecatealias('gdown', 'previous') |
946 _deprecatealias('gdown', 'previous') |
948 |
|
949 @eh.command( |
|
950 '^evolve|stabilize|solve', |
|
951 [('n', 'dry-run', False, |
|
952 _('do not perform actions, just print what would be done')), |
|
953 ('', 'confirm', False, |
|
954 _('ask for confirmation before performing the action')), |
|
955 ('A', 'any', False, |
|
956 _('also consider troubled changesets unrelated to current working ' |
|
957 'directory')), |
|
958 ('r', 'rev', [], _('solves troubles of these revisions')), |
|
959 ('', 'bumped', False, _('solves only bumped changesets')), |
|
960 ('', 'phase-divergent', False, _('solves only phase-divergent changesets')), |
|
961 ('', 'divergent', False, _('solves only divergent changesets')), |
|
962 ('', 'content-divergent', False, _('solves only content-divergent changesets')), |
|
963 ('', 'unstable', False, _('solves only unstable changesets')), |
|
964 ('', 'orphan', False, _('solves only orphan changesets (default)')), |
|
965 ('a', 'all', False, _('evolve all troubled changesets related to the ' |
|
966 'current working directory and its descendants')), |
|
967 ('c', 'continue', False, _('continue an interrupted evolution')), |
|
968 ('l', 'list', False, 'provide details on troubled changesets in the repo'), |
|
969 ] + mergetoolopts, |
|
970 _('[OPTIONS]...') |
|
971 ) |
|
972 def evolve(ui, repo, **opts): |
|
973 """solve troubled changesets in your repository |
|
974 |
|
975 Modifying history can lead to various types of troubled changesets: |
|
976 unstable, bumped, or divergent. The evolve command resolves your troubles |
|
977 by executing one of the following actions: |
|
978 |
|
979 - update working copy to a successor |
|
980 - rebase an unstable changeset |
|
981 - extract the desired changes from a bumped changeset |
|
982 - fuse divergent changesets back together |
|
983 |
|
984 If you pass no arguments, evolve works in automatic mode: it will execute a |
|
985 single action to reduce instability related to your working copy. There are |
|
986 two cases for this action. First, if the parent of your working copy is |
|
987 obsolete, evolve updates to the parent's successor. Second, if the working |
|
988 copy parent is not obsolete but has obsolete predecessors, then evolve |
|
989 determines if there is an unstable changeset that can be rebased onto the |
|
990 working copy parent in order to reduce instability. |
|
991 If so, evolve rebases that changeset. If not, evolve refuses to guess your |
|
992 intention, and gives a hint about what you might want to do next. |
|
993 |
|
994 Any time evolve creates a changeset, it updates the working copy to the new |
|
995 changeset. (Currently, every successful evolve operation involves an update |
|
996 as well; this may change in future.) |
|
997 |
|
998 Automatic mode only handles common use cases. For example, it avoids taking |
|
999 action in the case of ambiguity, and it ignores unstable changesets that |
|
1000 are not related to your working copy. |
|
1001 It also refuses to solve bumped or divergent changesets unless you |
|
1002 explicitly request such behavior (see below). |
|
1003 |
|
1004 Eliminating all instability around your working copy may require multiple |
|
1005 invocations of :hg:`evolve`. Alternately, use ``--all`` to recursively |
|
1006 select and evolve all unstable changesets that can be rebased onto the |
|
1007 working copy parent. |
|
1008 This is more powerful than successive invocations, since ``--all`` handles |
|
1009 ambiguous cases (e.g. unstable changesets with multiple children) by |
|
1010 evolving all branches. |
|
1011 |
|
1012 When your repository cannot be handled by automatic mode, you might need to |
|
1013 use ``--rev`` to specify a changeset to evolve. For example, if you have |
|
1014 an unstable changeset that is not related to the working copy parent, |
|
1015 you could use ``--rev`` to evolve it. Or, if some changeset has multiple |
|
1016 unstable children, evolve in automatic mode refuses to guess which one to |
|
1017 evolve; you have to use ``--rev`` in that case. |
|
1018 |
|
1019 Alternately, ``--any`` makes evolve search for the next evolvable changeset |
|
1020 regardless of whether it is related to the working copy parent. |
|
1021 |
|
1022 You can supply multiple revisions to evolve multiple troubled changesets |
|
1023 in a single invocation. In revset terms, ``--any`` is equivalent to ``--rev |
|
1024 first(unstable())``. ``--rev`` and ``--all`` are mutually exclusive, as are |
|
1025 ``--rev`` and ``--any``. |
|
1026 |
|
1027 ``hg evolve --any --all`` is useful for cleaning up instability across all |
|
1028 branches, letting evolve figure out the appropriate order and destination. |
|
1029 |
|
1030 When you have troubled changesets that are not unstable, :hg:`evolve` |
|
1031 refuses to consider them unless you specify the category of trouble you |
|
1032 wish to resolve, with ``--bumped`` or ``--divergent``. These options are |
|
1033 currently mutually exclusive with each other and with ``--unstable`` |
|
1034 (the default). You can combine ``--bumped`` or ``--divergent`` with |
|
1035 ``--rev``, ``--all``, or ``--any``. |
|
1036 |
|
1037 You can also use the evolve command to list the troubles affecting your |
|
1038 repository by using the --list flag. You can choose to display only some |
|
1039 categories of troubles with the --unstable, --divergent or --bumped flags. |
|
1040 """ |
|
1041 |
|
1042 opts = evolvecmd._checkevolveopts(repo, opts) |
|
1043 # Options |
|
1044 contopt = opts['continue'] |
|
1045 anyopt = opts['any'] |
|
1046 allopt = opts['all'] |
|
1047 startnode = repo['.'] |
|
1048 dryrunopt = opts['dry_run'] |
|
1049 confirmopt = opts['confirm'] |
|
1050 revopt = opts['rev'] |
|
1051 |
|
1052 troublecategories = ['phase_divergent', 'content_divergent', 'orphan'] |
|
1053 specifiedcategories = [t.replace('_', '') |
|
1054 for t in troublecategories |
|
1055 if opts[t]] |
|
1056 if opts['list']: |
|
1057 compat.startpager(ui, 'evolve') |
|
1058 evolvecmd.listtroubles(ui, repo, specifiedcategories, **opts) |
|
1059 return |
|
1060 |
|
1061 targetcat = 'orphan' |
|
1062 if 1 < len(specifiedcategories): |
|
1063 msg = _('cannot specify more than one trouble category to solve (yet)') |
|
1064 raise error.Abort(msg) |
|
1065 elif len(specifiedcategories) == 1: |
|
1066 targetcat = specifiedcategories[0] |
|
1067 elif repo['.'].obsolete(): |
|
1068 displayer = cmdutil.show_changeset(ui, repo, |
|
1069 {'template': shorttemplate}) |
|
1070 # no args and parent is obsolete, update to successors |
|
1071 try: |
|
1072 ctx = repo[utility._singlesuccessor(repo, repo['.'])] |
|
1073 except utility.MultipleSuccessorsError as exc: |
|
1074 repo.ui.write_err('parent is obsolete with multiple successors:\n') |
|
1075 for ln in exc.successorssets: |
|
1076 for n in ln: |
|
1077 displayer.show(repo[n]) |
|
1078 return 2 |
|
1079 |
|
1080 ui.status(_('update:')) |
|
1081 if not ui.quiet: |
|
1082 displayer.show(ctx) |
|
1083 |
|
1084 if dryrunopt: |
|
1085 return 0 |
|
1086 res = hg.update(repo, ctx.rev()) |
|
1087 if ctx != startnode: |
|
1088 ui.status(_('working directory is now at %s\n') % ctx) |
|
1089 return res |
|
1090 |
|
1091 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'evolve') |
|
1092 troubled = set(repo.revs('troubled()')) |
|
1093 |
|
1094 # Progress handling |
|
1095 seen = 1 |
|
1096 count = allopt and len(troubled) or 1 |
|
1097 showprogress = allopt |
|
1098 |
|
1099 def progresscb(): |
|
1100 if revopt or allopt: |
|
1101 ui.progress(_('evolve'), seen, unit=_('changesets'), total=count) |
|
1102 |
|
1103 # Continuation handling |
|
1104 if contopt: |
|
1105 evolvestate = state.cmdstate(repo) |
|
1106 if not evolvestate: |
|
1107 raise error.Abort('no evolve to continue') |
|
1108 evolvestate.load() |
|
1109 orig = repo[evolvestate['current']] |
|
1110 with repo.wlock(), repo.lock(): |
|
1111 ctx = orig |
|
1112 source = ctx.extra().get('source') |
|
1113 extra = {} |
|
1114 if source: |
|
1115 extra['source'] = source |
|
1116 extra['intermediate-source'] = ctx.hex() |
|
1117 else: |
|
1118 extra['source'] = ctx.hex() |
|
1119 user = ctx.user() |
|
1120 date = ctx.date() |
|
1121 message = ctx.description() |
|
1122 ui.status(_('evolving %d:%s "%s"\n') % (ctx.rev(), ctx, |
|
1123 message.split('\n', 1)[0])) |
|
1124 targetphase = max(ctx.phase(), phases.draft) |
|
1125 overrides = {('phases', 'new-commit'): targetphase} |
|
1126 |
|
1127 with repo.ui.configoverride(overrides, 'evolve-continue'): |
|
1128 node = repo.commit(text=message, user=user, |
|
1129 date=date, extra=extra) |
|
1130 |
|
1131 obsolete.createmarkers(repo, [(ctx, (repo[node],))]) |
|
1132 evolvestate.delete() |
|
1133 return |
|
1134 |
|
1135 cmdutil.bailifchanged(repo) |
|
1136 |
|
1137 revs = evolvecmd._selectrevs(repo, allopt, revopt, anyopt, targetcat) |
|
1138 |
|
1139 if not revs: |
|
1140 return evolvecmd._handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat) |
|
1141 |
|
1142 # For the progress bar to show |
|
1143 count = len(revs) |
|
1144 replacements = {} |
|
1145 # Order the revisions |
|
1146 if targetcat == 'orphan': |
|
1147 revs = evolvecmd._orderrevs(repo, revs) |
|
1148 for rev in revs: |
|
1149 curctx = repo[rev] |
|
1150 progresscb() |
|
1151 ret = evolvecmd._solveone(ui, repo, curctx, dryrunopt, confirmopt, |
|
1152 progresscb, targetcat) |
|
1153 seen += 1 |
|
1154 if ret[0]: |
|
1155 replacements[curctx.node()] = [ret[1]] |
|
1156 progresscb() |
|
1157 evolvecmd._cleanup(ui, repo, startnode, showprogress) |
|
1158 |
947 |
1159 def _gettopic(ctx): |
948 def _gettopic(ctx): |
1160 """handle topic fetching with or without the extension""" |
949 """handle topic fetching with or without the extension""" |
1161 return getattr(ctx, 'topic', lambda: '')() |
950 return getattr(ctx, 'topic', lambda: '')() |
1162 |
951 |