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