evolve: move the evolve command to evolvecmd.py
This patch moves finishes the code movement by moving the evolve command to
evolvecmd.py.
--- a/hgext3rd/evolve/__init__.py Sun Jan 21 20:33:39 2018 +0530
+++ b/hgext3rd/evolve/__init__.py Fri Jan 19 19:32:34 2018 +0530
@@ -292,12 +292,10 @@
node,
obsolete,
patch,
- phases,
revset,
scmutil,
)
-from mercurial.commands import mergetoolopts
from mercurial.i18n import _
from mercurial.node import nullid
@@ -357,6 +355,7 @@
eh = exthelper.exthelper()
eh.merge(debugcmd.eh)
+eh.merge(evolvecmd.eh)
eh.merge(obsexchange.eh)
eh.merge(checkheads.eh)
eh.merge(safeguard.eh)
@@ -946,216 +945,6 @@
_deprecatealias('gup', 'next')
_deprecatealias('gdown', 'previous')
-@eh.command(
- '^evolve|stabilize|solve',
- [('n', 'dry-run', False,
- _('do not perform actions, just print what would be done')),
- ('', 'confirm', False,
- _('ask for confirmation before performing the action')),
- ('A', 'any', False,
- _('also consider troubled changesets unrelated to current working '
- 'directory')),
- ('r', 'rev', [], _('solves troubles of these revisions')),
- ('', 'bumped', False, _('solves only bumped changesets')),
- ('', 'phase-divergent', False, _('solves only phase-divergent changesets')),
- ('', 'divergent', False, _('solves only divergent changesets')),
- ('', 'content-divergent', False, _('solves only content-divergent changesets')),
- ('', 'unstable', False, _('solves only unstable changesets')),
- ('', 'orphan', False, _('solves only orphan changesets (default)')),
- ('a', 'all', False, _('evolve all troubled changesets related to the '
- 'current working directory and its descendants')),
- ('c', 'continue', False, _('continue an interrupted evolution')),
- ('l', 'list', False, 'provide details on troubled changesets in the repo'),
- ] + mergetoolopts,
- _('[OPTIONS]...')
-)
-def evolve(ui, repo, **opts):
- """solve troubled changesets in your repository
-
- Modifying history can lead to various types of troubled changesets:
- unstable, bumped, or divergent. The evolve command resolves your troubles
- by executing one of the following actions:
-
- - update working copy to a successor
- - rebase an unstable changeset
- - extract the desired changes from a bumped changeset
- - fuse divergent changesets back together
-
- If you pass no arguments, evolve works in automatic mode: it will execute a
- single action to reduce instability related to your working copy. There are
- two cases for this action. First, if the parent of your working copy is
- obsolete, evolve updates to the parent's successor. Second, if the working
- copy parent is not obsolete but has obsolete predecessors, then evolve
- determines if there is an unstable changeset that can be rebased onto the
- working copy parent in order to reduce instability.
- If so, evolve rebases that changeset. If not, evolve refuses to guess your
- intention, and gives a hint about what you might want to do next.
-
- Any time evolve creates a changeset, it updates the working copy to the new
- changeset. (Currently, every successful evolve operation involves an update
- as well; this may change in future.)
-
- Automatic mode only handles common use cases. For example, it avoids taking
- action in the case of ambiguity, and it ignores unstable changesets that
- are not related to your working copy.
- It also refuses to solve bumped or divergent changesets unless you
- explicitly request such behavior (see below).
-
- Eliminating all instability around your working copy may require multiple
- invocations of :hg:`evolve`. Alternately, use ``--all`` to recursively
- select and evolve all unstable changesets that can be rebased onto the
- working copy parent.
- This is more powerful than successive invocations, since ``--all`` handles
- ambiguous cases (e.g. unstable changesets with multiple children) by
- evolving all branches.
-
- When your repository cannot be handled by automatic mode, you might need to
- use ``--rev`` to specify a changeset to evolve. For example, if you have
- an unstable changeset that is not related to the working copy parent,
- you could use ``--rev`` to evolve it. Or, if some changeset has multiple
- unstable children, evolve in automatic mode refuses to guess which one to
- evolve; you have to use ``--rev`` in that case.
-
- Alternately, ``--any`` makes evolve search for the next evolvable changeset
- regardless of whether it is related to the working copy parent.
-
- You can supply multiple revisions to evolve multiple troubled changesets
- in a single invocation. In revset terms, ``--any`` is equivalent to ``--rev
- first(unstable())``. ``--rev`` and ``--all`` are mutually exclusive, as are
- ``--rev`` and ``--any``.
-
- ``hg evolve --any --all`` is useful for cleaning up instability across all
- branches, letting evolve figure out the appropriate order and destination.
-
- When you have troubled changesets that are not unstable, :hg:`evolve`
- refuses to consider them unless you specify the category of trouble you
- wish to resolve, with ``--bumped`` or ``--divergent``. These options are
- currently mutually exclusive with each other and with ``--unstable``
- (the default). You can combine ``--bumped`` or ``--divergent`` with
- ``--rev``, ``--all``, or ``--any``.
-
- You can also use the evolve command to list the troubles affecting your
- repository by using the --list flag. You can choose to display only some
- categories of troubles with the --unstable, --divergent or --bumped flags.
- """
-
- opts = evolvecmd._checkevolveopts(repo, opts)
- # Options
- contopt = opts['continue']
- anyopt = opts['any']
- allopt = opts['all']
- startnode = repo['.']
- dryrunopt = opts['dry_run']
- confirmopt = opts['confirm']
- revopt = opts['rev']
-
- troublecategories = ['phase_divergent', 'content_divergent', 'orphan']
- specifiedcategories = [t.replace('_', '')
- for t in troublecategories
- if opts[t]]
- if opts['list']:
- compat.startpager(ui, 'evolve')
- evolvecmd.listtroubles(ui, repo, specifiedcategories, **opts)
- return
-
- targetcat = 'orphan'
- if 1 < len(specifiedcategories):
- msg = _('cannot specify more than one trouble category to solve (yet)')
- raise error.Abort(msg)
- elif len(specifiedcategories) == 1:
- targetcat = specifiedcategories[0]
- elif repo['.'].obsolete():
- displayer = cmdutil.show_changeset(ui, repo,
- {'template': shorttemplate})
- # no args and parent is obsolete, update to successors
- try:
- ctx = repo[utility._singlesuccessor(repo, repo['.'])]
- except utility.MultipleSuccessorsError as exc:
- repo.ui.write_err('parent is obsolete with multiple successors:\n')
- for ln in exc.successorssets:
- for n in ln:
- displayer.show(repo[n])
- return 2
-
- ui.status(_('update:'))
- if not ui.quiet:
- displayer.show(ctx)
-
- if dryrunopt:
- return 0
- res = hg.update(repo, ctx.rev())
- if ctx != startnode:
- ui.status(_('working directory is now at %s\n') % ctx)
- return res
-
- ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'evolve')
- troubled = set(repo.revs('troubled()'))
-
- # Progress handling
- seen = 1
- count = allopt and len(troubled) or 1
- showprogress = allopt
-
- def progresscb():
- if revopt or allopt:
- ui.progress(_('evolve'), seen, unit=_('changesets'), total=count)
-
- # Continuation handling
- if contopt:
- evolvestate = state.cmdstate(repo)
- if not evolvestate:
- raise error.Abort('no evolve to continue')
- evolvestate.load()
- orig = repo[evolvestate['current']]
- with repo.wlock(), repo.lock():
- ctx = orig
- source = ctx.extra().get('source')
- extra = {}
- if source:
- extra['source'] = source
- extra['intermediate-source'] = ctx.hex()
- else:
- extra['source'] = ctx.hex()
- user = ctx.user()
- date = ctx.date()
- message = ctx.description()
- ui.status(_('evolving %d:%s "%s"\n') % (ctx.rev(), ctx,
- message.split('\n', 1)[0]))
- targetphase = max(ctx.phase(), phases.draft)
- overrides = {('phases', 'new-commit'): targetphase}
-
- with repo.ui.configoverride(overrides, 'evolve-continue'):
- node = repo.commit(text=message, user=user,
- date=date, extra=extra)
-
- obsolete.createmarkers(repo, [(ctx, (repo[node],))])
- evolvestate.delete()
- return
-
- cmdutil.bailifchanged(repo)
-
- revs = evolvecmd._selectrevs(repo, allopt, revopt, anyopt, targetcat)
-
- if not revs:
- return evolvecmd._handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat)
-
- # For the progress bar to show
- count = len(revs)
- replacements = {}
- # Order the revisions
- if targetcat == 'orphan':
- revs = evolvecmd._orderrevs(repo, revs)
- for rev in revs:
- curctx = repo[rev]
- progresscb()
- ret = evolvecmd._solveone(ui, repo, curctx, dryrunopt, confirmopt,
- progresscb, targetcat)
- seen += 1
- if ret[0]:
- replacements[curctx.node()] = [ret[1]]
- progresscb()
- evolvecmd._cleanup(ui, repo, startnode, showprogress)
-
def _gettopic(ctx):
"""handle topic fetching with or without the extension"""
return getattr(ctx, 'topic', lambda: '')()
--- a/hgext3rd/evolve/evolvecmd.py Sun Jan 21 20:33:39 2018 +0530
+++ b/hgext3rd/evolve/evolvecmd.py Fri Jan 19 19:32:34 2018 +0530
@@ -32,6 +32,7 @@
from . import (
cmdrewrite,
compat,
+ exthelper,
rewriteutil,
state,
utility,
@@ -42,7 +43,9 @@
_bookmarksupdater = rewriteutil.bookmarksupdater
sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
+eh = exthelper.exthelper()
_bookmarksupdater = rewriteutil.bookmarksupdater
+mergetoolopts = cmdutil.mergetoolopts
def _solveone(ui, repo, ctx, dryrun, confirm, progresscb, category):
"""Resolve the troubles affecting one revision
@@ -949,3 +952,213 @@
})
return divergence
+
+@eh.command(
+ '^evolve|stabilize|solve',
+ [('n', 'dry-run', False,
+ _('do not perform actions, just print what would be done')),
+ ('', 'confirm', False,
+ _('ask for confirmation before performing the action')),
+ ('A', 'any', False,
+ _('also consider troubled changesets unrelated to current working '
+ 'directory')),
+ ('r', 'rev', [], _('solves troubles of these revisions')),
+ ('', 'bumped', False, _('solves only bumped changesets')),
+ ('', 'phase-divergent', False, _('solves only phase-divergent changesets')),
+ ('', 'divergent', False, _('solves only divergent changesets')),
+ ('', 'content-divergent', False, _('solves only content-divergent changesets')),
+ ('', 'unstable', False, _('solves only unstable changesets')),
+ ('', 'orphan', False, _('solves only orphan changesets (default)')),
+ ('a', 'all', False, _('evolve all troubled changesets related to the '
+ 'current working directory and its descendants')),
+ ('c', 'continue', False, _('continue an interrupted evolution')),
+ ('l', 'list', False, 'provide details on troubled changesets in the repo'),
+ ] + mergetoolopts,
+ _('[OPTIONS]...')
+)
+def evolve(ui, repo, **opts):
+ """solve troubled changesets in your repository
+
+ Modifying history can lead to various types of troubled changesets:
+ unstable, bumped, or divergent. The evolve command resolves your troubles
+ by executing one of the following actions:
+
+ - update working copy to a successor
+ - rebase an unstable changeset
+ - extract the desired changes from a bumped changeset
+ - fuse divergent changesets back together
+
+ If you pass no arguments, evolve works in automatic mode: it will execute a
+ single action to reduce instability related to your working copy. There are
+ two cases for this action. First, if the parent of your working copy is
+ obsolete, evolve updates to the parent's successor. Second, if the working
+ copy parent is not obsolete but has obsolete predecessors, then evolve
+ determines if there is an unstable changeset that can be rebased onto the
+ working copy parent in order to reduce instability.
+ If so, evolve rebases that changeset. If not, evolve refuses to guess your
+ intention, and gives a hint about what you might want to do next.
+
+ Any time evolve creates a changeset, it updates the working copy to the new
+ changeset. (Currently, every successful evolve operation involves an update
+ as well; this may change in future.)
+
+ Automatic mode only handles common use cases. For example, it avoids taking
+ action in the case of ambiguity, and it ignores unstable changesets that
+ are not related to your working copy.
+ It also refuses to solve bumped or divergent changesets unless you
+ explicitly request such behavior (see below).
+
+ Eliminating all instability around your working copy may require multiple
+ invocations of :hg:`evolve`. Alternately, use ``--all`` to recursively
+ select and evolve all unstable changesets that can be rebased onto the
+ working copy parent.
+ This is more powerful than successive invocations, since ``--all`` handles
+ ambiguous cases (e.g. unstable changesets with multiple children) by
+ evolving all branches.
+
+ When your repository cannot be handled by automatic mode, you might need to
+ use ``--rev`` to specify a changeset to evolve. For example, if you have
+ an unstable changeset that is not related to the working copy parent,
+ you could use ``--rev`` to evolve it. Or, if some changeset has multiple
+ unstable children, evolve in automatic mode refuses to guess which one to
+ evolve; you have to use ``--rev`` in that case.
+
+ Alternately, ``--any`` makes evolve search for the next evolvable changeset
+ regardless of whether it is related to the working copy parent.
+
+ You can supply multiple revisions to evolve multiple troubled changesets
+ in a single invocation. In revset terms, ``--any`` is equivalent to ``--rev
+ first(unstable())``. ``--rev`` and ``--all`` are mutually exclusive, as are
+ ``--rev`` and ``--any``.
+
+ ``hg evolve --any --all`` is useful for cleaning up instability across all
+ branches, letting evolve figure out the appropriate order and destination.
+
+ When you have troubled changesets that are not unstable, :hg:`evolve`
+ refuses to consider them unless you specify the category of trouble you
+ wish to resolve, with ``--bumped`` or ``--divergent``. These options are
+ currently mutually exclusive with each other and with ``--unstable``
+ (the default). You can combine ``--bumped`` or ``--divergent`` with
+ ``--rev``, ``--all``, or ``--any``.
+
+ You can also use the evolve command to list the troubles affecting your
+ repository by using the --list flag. You can choose to display only some
+ categories of troubles with the --unstable, --divergent or --bumped flags.
+ """
+
+ opts = _checkevolveopts(repo, opts)
+ # Options
+ contopt = opts['continue']
+ anyopt = opts['any']
+ allopt = opts['all']
+ startnode = repo['.']
+ dryrunopt = opts['dry_run']
+ confirmopt = opts['confirm']
+ revopt = opts['rev']
+
+ troublecategories = ['phase_divergent', 'content_divergent', 'orphan']
+ specifiedcategories = [t.replace('_', '')
+ for t in troublecategories
+ if opts[t]]
+ if opts['list']:
+ compat.startpager(ui, 'evolve')
+ listtroubles(ui, repo, specifiedcategories, **opts)
+ return
+
+ targetcat = 'orphan'
+ if 1 < len(specifiedcategories):
+ msg = _('cannot specify more than one trouble category to solve (yet)')
+ raise error.Abort(msg)
+ elif len(specifiedcategories) == 1:
+ targetcat = specifiedcategories[0]
+ elif repo['.'].obsolete():
+ displayer = cmdutil.show_changeset(ui, repo,
+ {'template': shorttemplate})
+ # no args and parent is obsolete, update to successors
+ try:
+ ctx = repo[utility._singlesuccessor(repo, repo['.'])]
+ except utility.MultipleSuccessorsError as exc:
+ repo.ui.write_err('parent is obsolete with multiple successors:\n')
+ for ln in exc.successorssets:
+ for n in ln:
+ displayer.show(repo[n])
+ return 2
+
+ ui.status(_('update:'))
+ if not ui.quiet:
+ displayer.show(ctx)
+
+ if dryrunopt:
+ return 0
+ res = hg.update(repo, ctx.rev())
+ if ctx != startnode:
+ ui.status(_('working directory is now at %s\n') % ctx)
+ return res
+
+ ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'evolve')
+ troubled = set(repo.revs('troubled()'))
+
+ # Progress handling
+ seen = 1
+ count = allopt and len(troubled) or 1
+ showprogress = allopt
+
+ def progresscb():
+ if revopt or allopt:
+ ui.progress(_('evolve'), seen, unit=_('changesets'), total=count)
+
+ # Continuation handling
+ if contopt:
+ evolvestate = state.cmdstate(repo)
+ if not evolvestate:
+ raise error.Abort('no evolve to continue')
+ evolvestate.load()
+ orig = repo[evolvestate['current']]
+ with repo.wlock(), repo.lock():
+ ctx = orig
+ source = ctx.extra().get('source')
+ extra = {}
+ if source:
+ extra['source'] = source
+ extra['intermediate-source'] = ctx.hex()
+ else:
+ extra['source'] = ctx.hex()
+ user = ctx.user()
+ date = ctx.date()
+ message = ctx.description()
+ ui.status(_('evolving %d:%s "%s"\n') % (ctx.rev(), ctx,
+ message.split('\n', 1)[0]))
+ targetphase = max(ctx.phase(), phases.draft)
+ overrides = {('phases', 'new-commit'): targetphase}
+
+ with repo.ui.configoverride(overrides, 'evolve-continue'):
+ node = repo.commit(text=message, user=user,
+ date=date, extra=extra)
+
+ obsolete.createmarkers(repo, [(ctx, (repo[node],))])
+ evolvestate.delete()
+ return
+
+ cmdutil.bailifchanged(repo)
+
+ revs = _selectrevs(repo, allopt, revopt, anyopt, targetcat)
+
+ if not revs:
+ return _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat)
+
+ # For the progress bar to show
+ count = len(revs)
+ replacements = {}
+ # Order the revisions
+ if targetcat == 'orphan':
+ revs = _orderrevs(repo, revs)
+ for rev in revs:
+ curctx = repo[rev]
+ progresscb()
+ ret = _solveone(ui, repo, curctx, dryrunopt, confirmopt,
+ progresscb, targetcat)
+ seen += 1
+ if ret[0]:
+ replacements[curctx.node()] = [ret[1]]
+ progresscb()
+ _cleanup(ui, repo, startnode, showprogress)