rewind: obsolete latest successors unless instructed otherwise
authorPierre-Yves David <pierre-yves.david@octobus.net>
Sun, 17 Jun 2018 00:15:18 +0200
changeset 3861 bbe635dfd75c
parent 3860 fa184c5edfe8
child 3862 8d3eed113b77
rewind: obsolete latest successors unless instructed otherwise To prevent the creations of divergence, we automatically mark the successors set of the rewinded changeset as obsoleted by the rewind result. A new flag `--as-divergence` is added to disable this behavior.
hgext3rd/evolve/rewind.py
tests/test-rewind.t
--- 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):
--- a/tests/test-rewind.t	Sat Jun 16 23:32:22 2018 +0200
+++ b/tests/test-rewind.t	Sun Jun 17 00:15:18 2018 +0200
@@ -260,7 +260,7 @@
 Actual rewind
 `````````````
 
-  $ hg rewind --hidden --to 'desc("c_B0")'
+  $ hg rewind --hidden --to 'desc("c_B0")' --as-divergence
   2 new content-divergent changesets
   rewinded to 1 changesets
   $ hg debugobsolete
@@ -355,3 +355,92 @@
      date:        Thu Jan 01 00:00:00 1970 +0000
      summary:     c_ROOT
   
+  $ echo 'default-date = 2 0' >> $HGRCPATH
+
+rewind a simple amend - obsoleting the current latest successors
+----------------------------------------------------------------
+
+  $ hg rewind --hidden --to 'min(desc("c_B0"))'
+  rewinded to 1 changesets
+  $ hg debugobsolete
+  7e594302a05d3769b27be88fc3cdfd39d7498498 25c8f5ab0c3bb569ec672570f1a901be4c6f032b 0 (Thu Jan 01 00:00:01 1970 +0000) {'ef1': '9', 'operation': 'amend', 'user': 'test'}
+  7e594302a05d3769b27be88fc3cdfd39d7498498 48acf2c0d9c8961859ce9a913671eb2adc9b057b 4 (Thu Jan 01 00:00:01 1970 +0000) {'ef1': '34', 'operation': 'rewind', 'user': 'test'}
+  48acf2c0d9c8961859ce9a913671eb2adc9b057b 0 {579f120ba91885449adc92eedf48ef3569742cee} (Thu Jan 01 00:00:01 1970 +0000) {'ef1': '0', 'operation': 'prune', 'user': 'test'}
+  7e594302a05d3769b27be88fc3cdfd39d7498498 d8b4471cfb3caa290e0a78ae6bc57d78656c9075 4 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '34', 'operation': 'rewind', 'user': 'test'}
+  25c8f5ab0c3bb569ec672570f1a901be4c6f032b d8b4471cfb3caa290e0a78ae6bc57d78656c9075 0 (Thu Jan 01 00:00:02 1970 +0000) {'ef1': '43', 'operation': 'rewind', 'user': 'test'}
+  $ hg obslog --rev 'desc("c_B0")'
+  o    d8b4471cfb3c (5) c_B0
+  |\
+  @ |  25c8f5ab0c3b (3) c_B1
+  |/     rewritten(description, meta, date, content) as d8b4471cfb3c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
+  |
+  x  7e594302a05d (2) c_B0
+       rewritten(description, content) as 25c8f5ab0c3b using amend by test (Thu Jan 01 00:00:01 1970 +0000)
+       rewritten(meta, date) as 48acf2c0d9c8 using rewind by test (Thu Jan 01 00:00:01 1970 +0000)
+       rewritten(meta, date) as d8b4471cfb3c using rewind by test (Thu Jan 01 00:00:02 1970 +0000)
+  
+  $ hg log -G
+  o  changeset:   5:d8b4471cfb3c
+  |  tag:         tip
+  |  parent:      1:579f120ba918
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:02 1970 +0000
+  |  summary:     c_B0
+  |
+  | @  changeset:   3:25c8f5ab0c3b
+  |/   parent:      1:579f120ba918
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    obsolete:    rewritten using rewind as 5:d8b4471cfb3c
+  |    summary:     c_B1
+  |
+  o  changeset:   1:579f120ba918
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     c_A0
+  |
+  o  changeset:   0:eba9c2249fe7
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     c_ROOT
+  
+  $ hg log -G --hidden
+  o  changeset:   5:d8b4471cfb3c
+  |  tag:         tip
+  |  parent:      1:579f120ba918
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:02 1970 +0000
+  |  summary:     c_B0
+  |
+  | x  changeset:   4:48acf2c0d9c8
+  |/   parent:      1:579f120ba918
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:01 1970 +0000
+  |    obsolete:    pruned using prune
+  |    summary:     c_B0
+  |
+  | @  changeset:   3:25c8f5ab0c3b
+  |/   parent:      1:579f120ba918
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    obsolete:    rewritten using rewind as 5:d8b4471cfb3c
+  |    summary:     c_B1
+  |
+  | x  changeset:   2:7e594302a05d
+  |/   user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    obsolete:    rewritten using rewind as 5:d8b4471cfb3c
+  |    obsolete:    rewritten using rewind as 4:48acf2c0d9c8
+  |    obsolete:    rewritten using amend as 3:25c8f5ab0c3b
+  |    summary:     c_B0
+  |
+  o  changeset:   1:579f120ba918
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     c_A0
+  |
+  o  changeset:   0:eba9c2249fe7
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     c_ROOT
+