evolvecmd: move more code from __init__.py to evolvecmd.py
We are now very close to have most of the evolve related code in evolvecmd.py.
--- a/hgext3rd/evolve/__init__.py Fri Jan 19 15:22:28 2018 +0530
+++ b/hgext3rd/evolve/__init__.py Fri Jan 19 15:55:38 2018 +0530
@@ -347,7 +347,6 @@
aliases, entry = cmdutil.findcmd('commit', commands.table)
commitopts3 = cmdrewrite.commitopts3
interactiveopt = cmdrewrite.interactiveopt
-_bookmarksupdater = rewriteutil.bookmarksupdater
rewrite = rewriteutil.rewrite
# This extension contains the following code
@@ -1113,60 +1112,6 @@
rdependencies[succ].add(r)
return dependencies, rdependencies
-def _dedupedivergents(repo, revs):
- """Dedupe the divergents revs in revs to get one from each group with the
- lowest revision numbers
- """
- repo = repo.unfiltered()
- res = set()
- # To not reevaluate divergents of the same group once one is encountered
- discarded = set()
- for rev in revs:
- if rev in discarded:
- continue
- divergent = repo[rev]
- base, others = divergentdata(divergent)
- othersrevs = [o.rev() for o in others]
- res.add(min([divergent.rev()] + othersrevs))
- discarded.update(othersrevs)
- return res
-
-instabilities_map = {
- 'contentdivergent': "content-divergent",
- 'phasedivergent': "phase-divergent"
-}
-
-def _selectrevs(repo, allopt, revopt, anyopt, targetcat):
- """select troubles in repo matching according to given options"""
- revs = set()
- if allopt or revopt:
- revs = repo.revs("%s()" % targetcat)
- if revopt:
- revs = scmutil.revrange(repo, revopt) & revs
- elif not anyopt:
- topic = getattr(repo, 'currenttopic', '')
- if topic:
- revs = repo.revs('topic(%s)', topic) & revs
- elif targetcat == 'orphan':
- revs = _aspiringdescendant(repo,
- repo.revs('(.::) - obsolete()::'))
- revs = set(revs)
- if targetcat == 'contentdivergent':
- # Pick one divergent per group of divergents
- revs = _dedupedivergents(repo, revs)
- elif anyopt:
- revs = repo.revs('first(%s())' % (targetcat))
- elif targetcat == 'orphan':
- revs = set(_aspiringchildren(repo, repo.revs('(.::) - obsolete()::')))
- if 1 < len(revs):
- msg = "multiple evolve candidates"
- hint = (_("select one of %s with --rev")
- % ', '.join([str(repo[r]) for r in sorted(revs)]))
- raise error.Abort(msg, hint=hint)
- elif instabilities_map.get(targetcat, targetcat) in repo['.'].instabilities():
- revs = set([repo['.'].rev()])
- return revs
-
def _orderrevs(repo, revs):
"""Compute an ordering to solve instability for the given revs
@@ -1505,7 +1450,7 @@
cmdutil.bailifchanged(repo)
- revs = _selectrevs(repo, allopt, revopt, anyopt, targetcat)
+ revs = evolvecmd._selectrevs(repo, allopt, revopt, anyopt, targetcat)
if not revs:
return _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat)
@@ -1567,75 +1512,6 @@
return opts
-def _possibledestination(repo, rev):
- """return all changesets that may be a new parent for REV"""
- tonode = repo.changelog.node
- parents = repo.changelog.parentrevs
- torev = repo.changelog.rev
- dest = set()
- tovisit = list(parents(rev))
- while tovisit:
- r = tovisit.pop()
- succsets = compat.successorssets(repo, tonode(r))
- if not succsets:
- tovisit.extend(parents(r))
- else:
- # We should probably pick only one destination from split
- # (case where '1 < len(ss)'), This could be the currently tipmost
- # but logic is less clear when result of the split are now on
- # multiple branches.
- for ss in succsets:
- for n in ss:
- dest.add(torev(n))
- return dest
-
-def _aspiringchildren(repo, revs):
- """Return a list of changectx which can be stabilized on top of pctx or
- one of its descendants. Empty list if none can be found."""
- target = set(revs)
- result = []
- for r in repo.revs('orphan() - %ld', revs):
- dest = _possibledestination(repo, r)
- if target & dest:
- result.append(r)
- return result
-
-def _aspiringdescendant(repo, revs):
- """Return a list of changectx which can be stabilized on top of pctx or
- one of its descendants recursively. Empty list if none can be found."""
- target = set(revs)
- result = set(target)
- paths = collections.defaultdict(set)
- for r in repo.revs('orphan() - %ld', revs):
- for d in _possibledestination(repo, r):
- paths[d].add(r)
-
- result = set(target)
- tovisit = list(revs)
- while tovisit:
- base = tovisit.pop()
- for unstable in paths[base]:
- if unstable not in result:
- tovisit.append(unstable)
- result.add(unstable)
- return sorted(result - target)
-
-def divergentdata(ctx):
- """return base, other part of a conflict
-
- This only return the first one.
-
- XXX this woobly function won't survive XXX
- """
- repo = ctx._repo.unfiltered()
- for base in repo.set('reverse(allprecursors(%d))', ctx):
- newer = compat.successorssets(ctx._repo, base.node())
- # drop filter and solution including the original ctx
- newer = [n for n in newer if n and ctx.node() not in n]
- if newer:
- return base, tuple(ctx._repo[o] for o in newer[0])
- raise error.Abort("base of divergent changeset %s not found" % ctx,
- hint='this case is not yet handled')
def _gettopic(ctx):
"""handle topic fetching with or without the extension"""
@@ -1826,7 +1702,7 @@
ui.warn(_('explicitly update to one of them\n'))
result = 1
else:
- aspchildren = _aspiringchildren(repo, [repo['.'].rev()])
+ aspchildren = evolvecmd._aspiringchildren(repo, [repo['.'].rev()])
if topic:
filtered.extend(repo[c] for c in children
if repo[c].topic() != topic)
--- a/hgext3rd/evolve/evolvecmd.py Fri Jan 19 15:22:28 2018 +0530
+++ b/hgext3rd/evolve/evolvecmd.py Fri Jan 19 15:55:38 2018 +0530
@@ -8,6 +8,7 @@
"""logic related to hg evolve command"""
+import collections
import re
from mercurial import (
@@ -22,6 +23,7 @@
node,
obsolete,
phases,
+ scmutil,
util,
)
@@ -40,7 +42,7 @@
_bookmarksupdater = rewriteutil.bookmarksupdater
sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
-from . import divergentdata
+_bookmarksupdater = rewriteutil.bookmarksupdater
def _solveone(ui, repo, ctx, dryrun, confirm, progresscb, category):
"""Resolve the troubles affecting one revision
@@ -526,3 +528,127 @@
f.write(orig.topic())
return merge.graft(repo, orig, pctx, ['destination', 'evolving'], True)
+
+instabilities_map = {
+ 'contentdivergent': "content-divergent",
+ 'phasedivergent': "phase-divergent"
+}
+
+def _selectrevs(repo, allopt, revopt, anyopt, targetcat):
+ """select troubles in repo matching according to given options"""
+ revs = set()
+ if allopt or revopt:
+ revs = repo.revs("%s()" % targetcat)
+ if revopt:
+ revs = scmutil.revrange(repo, revopt) & revs
+ elif not anyopt:
+ topic = getattr(repo, 'currenttopic', '')
+ if topic:
+ revs = repo.revs('topic(%s)', topic) & revs
+ elif targetcat == 'orphan':
+ revs = _aspiringdescendant(repo,
+ repo.revs('(.::) - obsolete()::'))
+ revs = set(revs)
+ if targetcat == 'contentdivergent':
+ # Pick one divergent per group of divergents
+ revs = _dedupedivergents(repo, revs)
+ elif anyopt:
+ revs = repo.revs('first(%s())' % (targetcat))
+ elif targetcat == 'orphan':
+ revs = set(_aspiringchildren(repo, repo.revs('(.::) - obsolete()::')))
+ if 1 < len(revs):
+ msg = "multiple evolve candidates"
+ hint = (_("select one of %s with --rev")
+ % ', '.join([str(repo[r]) for r in sorted(revs)]))
+ raise error.Abort(msg, hint=hint)
+ elif instabilities_map.get(targetcat, targetcat) in repo['.'].instabilities():
+ revs = set([repo['.'].rev()])
+ return revs
+
+def _dedupedivergents(repo, revs):
+ """Dedupe the divergents revs in revs to get one from each group with the
+ lowest revision numbers
+ """
+ repo = repo.unfiltered()
+ res = set()
+ # To not reevaluate divergents of the same group once one is encountered
+ discarded = set()
+ for rev in revs:
+ if rev in discarded:
+ continue
+ divergent = repo[rev]
+ base, others = divergentdata(divergent)
+ othersrevs = [o.rev() for o in others]
+ res.add(min([divergent.rev()] + othersrevs))
+ discarded.update(othersrevs)
+ return res
+
+def divergentdata(ctx):
+ """return base, other part of a conflict
+
+ This only return the first one.
+
+ XXX this woobly function won't survive XXX
+ """
+ repo = ctx._repo.unfiltered()
+ for base in repo.set('reverse(allprecursors(%d))', ctx):
+ newer = compat.successorssets(ctx._repo, base.node())
+ # drop filter and solution including the original ctx
+ newer = [n for n in newer if n and ctx.node() not in n]
+ if newer:
+ return base, tuple(ctx._repo[o] for o in newer[0])
+ raise error.Abort("base of divergent changeset %s not found" % ctx,
+ hint='this case is not yet handled')
+
+def _aspiringdescendant(repo, revs):
+ """Return a list of changectx which can be stabilized on top of pctx or
+ one of its descendants recursively. Empty list if none can be found."""
+ target = set(revs)
+ result = set(target)
+ paths = collections.defaultdict(set)
+ for r in repo.revs('orphan() - %ld', revs):
+ for d in _possibledestination(repo, r):
+ paths[d].add(r)
+
+ result = set(target)
+ tovisit = list(revs)
+ while tovisit:
+ base = tovisit.pop()
+ for unstable in paths[base]:
+ if unstable not in result:
+ tovisit.append(unstable)
+ result.add(unstable)
+ return sorted(result - target)
+
+def _aspiringchildren(repo, revs):
+ """Return a list of changectx which can be stabilized on top of pctx or
+ one of its descendants. Empty list if none can be found."""
+ target = set(revs)
+ result = []
+ for r in repo.revs('orphan() - %ld', revs):
+ dest = _possibledestination(repo, r)
+ if target & dest:
+ result.append(r)
+ return result
+
+def _possibledestination(repo, rev):
+ """return all changesets that may be a new parent for REV"""
+ tonode = repo.changelog.node
+ parents = repo.changelog.parentrevs
+ torev = repo.changelog.rev
+ dest = set()
+ tovisit = list(parents(rev))
+ while tovisit:
+ r = tovisit.pop()
+ succsets = compat.successorssets(repo, tonode(r))
+ if not succsets:
+ tovisit.extend(parents(r))
+ else:
+ # We should probably pick only one destination from split
+ # (case where '1 < len(ss)'), This could be the currently tipmost
+ # but logic is less clear when result of the split are now on
+ # multiple branches.
+ for ss in succsets:
+ for n in ss:
+ dest.add(torev(n))
+ return dest