hgext3rd/evolve/rewind.py
changeset 3861 bbe635dfd75c
parent 3859 6e3d844b56f2
child 3862 8d3eed113b77
--- a/hgext3rd/evolve/rewind.py	Sat Jun 16 23:32:22 2018 +0200
+++ b/hgext3rd/evolve/rewind.py	Sun Jun 17 00:15:18 2018 +0200
@@ -1,10 +1,12 @@
 from __future__ import absolute_import
 
+import collections
 import hashlib
 
 from mercurial import (
     error,
     obsolete,
+    obsutil,
     scmutil,
 )
 
@@ -24,10 +26,19 @@
 @eh.command(
     '^rewind',
     [('', 'to', [], _("rewind to these revision")),
+     ('', 'as-divergence', None, _("preserve current latest successors")),
     ],
     _(''))
 def rewind(ui, repo, **opts):
     """rewind stacks of changeset to a previous content
+
+    This command can be used to restore stacks of changesets to an obsolete
+    state, creating identical identical copies.
+
+    The latest successors the obsolete changesets will be superseed by these
+    new copies. This behavior can be disabled using `--as-divergence`, the
+    current latest successors won't be affected and content-divergence will
+    appears between them and the restored version of the obsolete changesets.
     """
     unfi = repo.unfiltered()
 
@@ -36,11 +47,35 @@
 
     rewinded = scmutil.revrange(repo, opts.get('to'))
 
+    successorsmap = collections.defaultdict(set)
+    rewindmap = {}
+    sscache = {}
     with repo.wlock(), repo.lock():
+        if not opts['as_divergence']:
+            for rev in rewinded:
+                ctx = unfi[rev]
+                ssets = obsutil.successorssets(repo, ctx.node(), sscache)
+                if 1 < len(ssets):
+                    msg = _('rewind confused by divergence on %s') % ctx
+                    hint = _('solve divergence first or use "--as-divergence"')
+                    raise error.Abort(msg, hint=hint)
+                if ssets and ssets[0]:
+                    for succ in ssets[0]:
+                        successorsmap[succ].add(ctx.node())
+
         # Check that we can rewind these changesets
         with repo.transaction('rewind'):
             for rev in rewinded:
-                _revive_revision(unfi, rev)
+                ctx = unfi[rev]
+                rewindmap[ctx.node()] = _revive_revision(unfi, rev)
+
+            relationships = []
+            cl = unfi.changelog
+            for (source, dest) in sorted(successorsmap.items()):
+                newdest = [rewindmap[d] for d in sorted(dest, key=cl.rev)]
+                rel = (unfi[source], tuple(unfi[d] for d in newdest))
+                relationships.append(rel)
+            obsolete.createmarkers(unfi, relationships, operation='rewind')
     repo.ui.status(_('rewinded to %d changesets\n') % len(rewinded))
 
 def _revive_revision(unfi, rev):