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.
--- 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
+