hgext/evolve.py
branchstable
changeset 855 97d8a652f9b9
parent 818 fcdd9b8c970b
parent 789 0d2bb0282e78
child 857 c479e24ebddf
--- a/hgext/evolve.py	Mon Mar 03 19:27:42 2014 -0800
+++ b/hgext/evolve.py	Tue Mar 04 11:02:42 2014 -0800
@@ -569,6 +569,7 @@
 # XXX this could wrap transaction code
 # XXX (but this is a bit a layer violation)
 @eh.wrapcommand("commit")
+@eh.wrapcommand("import")
 @eh.wrapcommand("push")
 @eh.wrapcommand("pull")
 @eh.wrapcommand("graft")
@@ -620,8 +621,7 @@
             return result
     repo.__class__ = evolvingrepo
 
-@eh.wrapcommand("summary")
-def obssummary(orig, ui, repo, *args, **kwargs):
+def summaryhook(ui, repo):
     def write(fmt, count):
         s = fmt % count
         if count:
@@ -629,14 +629,16 @@
         else:
             ui.note(s)
 
-    ret = orig(ui, repo, *args, **kwargs)
     nbunstable = len(getrevs(repo, 'unstable'))
     nbbumped = len(getrevs(repo, 'bumped'))
     nbdivergent = len(getrevs(repo, 'divergent'))
     write('unstable: %i changesets\n', nbunstable)
     write('bumped: %i changesets\n', nbbumped)
     write('divergent: %i changesets\n', nbdivergent)
-    return ret
+
+@eh.extsetup
+def obssummarysetup(ui):
+    cmdutil.summaryhooks.add('evolve', summaryhook)
 
 
 #####################################################################
@@ -766,7 +768,10 @@
     try:
         rebase = extensions.find('rebase')
         # dummy state to trick rebase node
-        assert orig.p2().rev() == node.nullrev, 'no support yet'
+        if not orig.p2().rev() == node.nullrev:
+            raise util.Abort(
+                'no support for evolution merge changesets yet',
+                hint="Redo the merge a use `hg prune` to obsolete the old one")
         destbookmarks = repo.nodebookmarks(dest.node())
         cmdutil.duplicatecopies(repo, orig.node(), dest.node())
         nodesrc = orig.node()
@@ -925,31 +930,53 @@
             ui.write_err(_('no troubled changesets\n'))
             return 1
 
+    def progresscb():
+        if allopt:
+            ui.progress('evolve', seen, unit='changesets', total=count)
+    seen = 1
+    count = allopt and _counttroubled(ui, repo) or 1
+
     while tr is not None:
-        result = _evolveany(ui, repo, tr, dryrunopt)
+        progresscb()
+        result = _evolveany(ui, repo, tr, dryrunopt, progresscb=progresscb)
+        progresscb()
+        seen += 1
         if not allopt:
             return result
+        progresscb()
         tr = _picknexttroubled(ui, repo, anyopt or allopt)
 
+    if allopt:
+        ui.progress('evolve', None)
 
-def _evolveany(ui, repo, tr, dryrunopt):
+
+def _evolveany(ui, repo, tr, dryrunopt, progresscb):
     repo = repo.unfiltered()
     tr = repo[tr.rev()]
     cmdutil.bailifchanged(repo)
     troubles = tr.troubles()
     if 'unstable' in troubles:
-        return _solveunstable(ui, repo, tr, dryrunopt)
+        return _solveunstable(ui, repo, tr, dryrunopt, progresscb)
     elif 'bumped' in troubles:
-        return _solvebumped(ui, repo, tr, dryrunopt)
+        return _solvebumped(ui, repo, tr, dryrunopt, progresscb)
     elif 'divergent' in troubles:
         repo = repo.unfiltered()
         tr = repo[tr.rev()]
-        return _solvedivergent(ui, repo, tr, dryrunopt)
+        return _solvedivergent(ui, repo, tr, dryrunopt, progresscb)
     else:
         assert False  # WHAT? unknown troubles
 
-def _picknexttroubled(ui, repo, pickany=False):
+def _counttroubled(ui, repo):
+    """Count the amount of troubled changesets"""
+    troubled = set()
+    troubled.update(getrevs(repo, 'unstable'))
+    troubled.update(getrevs(repo, 'bumped'))
+    troubled.update(getrevs(repo, 'divergent'))
+    return len(troubled)
+
+def _picknexttroubled(ui, repo, pickany=False, progresscb=None):
     """Pick a the next trouble changeset to solve"""
+    if progresscb: progresscb()
     tr = _stabilizableunstable(repo, repo['.'])
     if tr is None:
         wdp = repo['.']
@@ -988,7 +1015,7 @@
                 return child
     return None
 
-def _solveunstable(ui, repo, orig, dryrun=False):
+def _solveunstable(ui, repo, orig, dryrun=False, progresscb=None):
     """Stabilize a unstable changeset"""
     obs = orig.parents()[0]
     if not obs.obsolete():
@@ -1019,11 +1046,13 @@
     repo.ui.status(_('atop:'))
     if not ui.quiet:
         displayer.show(target)
+    if progresscb: progresscb()
     todo = 'hg rebase -r %s -d %s\n' % (orig, target)
     if dryrun:
         repo.ui.write(todo)
     else:
         repo.ui.note(todo)
+        if progresscb: progresscb()
         lock = repo.lock()
         try:
             relocate(repo, orig, target)
@@ -1035,7 +1064,7 @@
         finally:
             lock.release()
 
-def _solvebumped(ui, repo, bumped, dryrun=False):
+def _solvebumped(ui, repo, bumped, dryrun=False, progresscb=None):
     """Stabilize a bumped changeset"""
     # For now we deny bumped merge
     if len(bumped.parents()) > 1:
@@ -1061,6 +1090,7 @@
         repo.ui.write('hg revert --all --rev %s;\n' % bumped)
         repo.ui.write('hg commit --msg "bumped update to %s"')
         return 0
+    if progresscb: progresscb()
     wlock = repo.wlock()
     try:
         newid = tmpctx = None
@@ -1137,17 +1167,40 @@
     finally:
         wlock.release()
 
-def _solvedivergent(ui, repo, divergent, dryrun=False):
+def _solvedivergent(ui, repo, divergent, dryrun=False, progresscb=None):
     base, others = divergentdata(divergent)
     if len(others) > 1:
-        raise util.Abort("We do not handle split yet")
+        othersstr = "[%s]" % (','.join([str(i) for i in others]))
+        hint = ("changeset %d is divergent with a changeset that got splitted "
+                "| into multiple ones:\n[%s]\n"
+                "| This is not handled by automatic evolution yet\n"
+                "| You have to fallback to manual handling with commands as:\n"
+                "| - hg touch -D\n"
+                "| - hg prune\n"
+                "| \n"
+                "| You should contact your local evolution Guru for help.\n"
+                % (divergent, othersstr))
+        raise util.Abort("We do not handle divergence with split yet",
+                         hint='')
     other = others[0]
     if divergent.phase() <= phases.public:
-        raise util.Abort("We can't resolve this conflict from the public side")
+        raise util.Abort("We can't resolve this conflict from the public side",
+                         hint="%s is public, try from %s" % (divergent, other))
     if len(other.parents()) > 1:
-        raise util.Abort("divergent changeset can't be a merge (yet)")
+        raise util.Abort("divergent changeset can't be a merge (yet)",
+                          hint="You have to fallback to solving this by hand...\n"
+                               "| This probably mean to redo the merge and use "
+                               "| `hg prune` to kill older version.")
     if other.p1() not in divergent.parents():
-        raise util.Abort("parents are not common (not handled yet)")
+        raise util.Abort("parents are not common (not handled yet)",
+                         hint="| %(d)s, %(o)s are not based on the same changeset."
+                              "| With the current state of its implementation, "
+                              "| evolve does not work in that case.\n"
+                              "| rebase one of them next to the other and run "
+                              "| this command again.\n"
+                              "| - either: hg rebase -dest 'p1(%(d)s)' -r %(o)s"
+                              "| - or:     hg rebase -dest 'p1(%(d)s)' -r %(o)s"
+                              % {'d': divergent, 'o': other})
 
     displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
     ui.status(_('merge:'))
@@ -1177,6 +1230,7 @@
             repo.ui.status(_('updating to "local" conflict\n'))
             hg.update(repo, divergent.rev())
         repo.ui.note(_('merging divergent changeset\n'))
+        if progresscb: progresscb()
         stats = merge.update(repo,
                              other.node(),
                              branchmerge=True,
@@ -1197,6 +1251,7 @@
 /!\ * hg ci -m "same message as the amended changeset" => new cset Y
 /!\ * hg kill -n Y W Z
 """)
+        if progresscb: progresscb()
         tr = repo.transaction('stabilize-divergent')
         try:
             repo.dirstate.setparents(divergent.node(), node.nullid)
@@ -1332,6 +1387,7 @@
     [('n', 'new', [], _("successor changeset (DEPRECATED)")),
      ('s', 'succ', [], _("successor changeset")),
      ('r', 'rev', [], _("revisions to prune")),
+     ('', 'biject', False, _("do a 1-1 map between rev and successor ranges")),
      ('B', 'bookmark', '', _("remove revs only reachable from given"
                              " bookmark"))] + metadataopts,
     _('[OPTION] [-r] REV...'))
@@ -1347,13 +1403,19 @@
     When the working directory parent is pruned the repository is updated to a
     non obsolete parents.
 
-    you can use the ``--succ`` option to informs mercurial that a newer version
+    You can use the ``--succ`` option to informs mercurial that a newer version
     of the pruned changeset exists.
+
+    You can use the ``--biject`` option to specify a 1-1 (bijection) between
+    revisions to prune and successor changesets. This option may be removed in
+    a future release (with the functionality absored automatically).
+
     """
     revs = set(scmutil.revrange(repo, list(revs) + opts.get('rev')))
     succs = opts['new'] + opts['succ']
     bookmark = opts.get('bookmark')
     metadata = _getmetadata(**opts)
+    biject = opts.get('biject')
 
     if bookmark:
         marks,revs = _reachablefrombookmark(repo, revs, bookmark)
@@ -1383,11 +1445,20 @@
 
         # defines successors changesets
         sucs = tuple(repo[n] for n in sortedrevs(succs))
-        if len(sucs) > 1 and len(precs) > 1:
+        if not biject and len(sucs) > 1 and len(precs) > 1:
             msg = "Can't use multiple successors for multiple precursors"
             raise util.Abort(msg)
+
+        if biject and len(sucs) != len(precs):
+            msg = "Can't use %d successors for %d precursors" % (len(sucs), len(precs))
+            raise util.Abort(msg)
+
+        relations = [(p, sucs) for p in precs]
+        if biject:
+            relations = [(p, (s,)) for p, s in zip(precs, sucs)]
+
         # create markers
-        createmarkers(repo, [(p, sucs) for p in precs], metadata=metadata)
+        createmarkers(repo, relations, metadata=metadata)
 
         # informs that changeset have been pruned
         ui.status(_('%i changesets pruned\n') % len(precs))