@@ -6,6 +6,7 @@
> glog = log -G -T '{rev}:{node|short} {desc}'
> [experimental]
> evolution = createmarkers, allowunstable
+ > evolution.allowdivergence = true
> [extensions]
> rebase =
> amend =
@@ -11,6 +11,7 @@
> publish=False
> [experimental]
> evolution=true
+ > evolution.allowdivergence=true
> [templates]
> obsfatesuccessors = "{if(successors, " as ")}{join(successors, ", ")}"
> obsfateverb = "{obsfateverb(successors, markers)}"
@@ -13,6 +13,7 @@
> [experimental]
> evolution.createmarkers = yes
> evolution.effect-flags = yes
+ > evolution.allowdivergence=true
> EOF
Test output on amended commit
@@ -150,7 +150,8 @@
[255]
$ hg branch -r 4 --hidden foobar
- abort: cannot change branch of a obsolete changeset
+ abort: change branch of of 3938acfb5c0f creates content-divergence with 7c1991464886
+ (add --verbose for details)
[10]
Make sure bookmark movement is correct
@@ -232,6 +232,17 @@
$ hg debugobsolete -r .
112478962961147124edd43549aedd1a335e44bf be169c7e8dbe21cd10b3d79691cbe7f241e3c21c 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
be169c7e8dbe21cd10b3d79691cbe7f241e3c21c 16084da537dd8f84cfdb3055c633772269d62e1b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'note': 'adding bar', 'operation': 'amend', 'user': 'test'}
+
+Cannot cause divergence by default
+
+ $ hg co --hidden 1
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg amend -m divergent
+ abort: amend of 112478962961 creates content-divergence with 16084da537dd
+ (add --verbose for details)
+ [10]
+ $ hg amend -m divergent --config experimental.evolution.allowdivergence=true
+ 2 new content-divergent changesets
#endif
Cannot amend public changeset
@@ -59,6 +59,36 @@
_(b"cannot %s changeset with children") % action, hint=hint
)
+ if not obsolete.isenabled(repo, obsolete.allowdivergenceopt):
+ new_divergence = _find_new_divergence(repo, revs)
+ if new_divergence:
+ local_ctx, other_ctx, base_ctx = new_divergence
+ msg = _(b'%s of %s creates content-divergence with %s') % (
+ action,
+ local_ctx,
+ other_ctx,
+ )
+ if local_ctx.rev() != base_ctx.rev():
+ msg += _(b', from %s') % base_ctx
+ if repo.ui.verbose:
+ if local_ctx.rev() != base_ctx.rev():
+ msg += _(
+ b'\n changeset %s is a successor of ' b'changeset %s'
+ ) % (local_ctx, base_ctx)
+ msg += _(
+ b'\n changeset %s already has a successor in '
+ b'changeset %s\n'
+ b' rewriting changeset %s would create '
+ b'"content-divergence"\n'
+ b' set experimental.evolution.allowdivergence=True to '
+ b'skip this check'
+ ) % (base_ctx, other_ctx, local_ctx)
+ raise error.InputError(msg)
+ else:
+ raise error.InputError(
+ msg, hint=_(b"add --verbose for details")
+ )
+
def disallowednewunstable(repo, revs):
"""Checks whether editing the revs will create new unstable changesets and
@@ -73,6 +103,40 @@
return repo.revs(b"(%ld::) - %ld", revs, revs)
+def _find_new_divergence(repo, revs):
+ obsrevs = repo.revs(b'%ld and obsolete()', revs)
+ for r in obsrevs:
+ div = find_new_divergence_from(repo, repo[r])
+ if div:
+ return (repo[r], repo[div[0]], repo[div[1]])
+ return None
+
+
+def find_new_divergence_from(repo, ctx):
+ """return divergent revision if rewriting an obsolete cset (ctx) will
+ create divergence
+
+ Returns (<other node>, <common ancestor node>) or None
+ """
+ if not ctx.obsolete():
+ return None
+ # We need to check two cases that can cause divergence:
+ # case 1: the rev being rewritten has a non-obsolete successor (easily
+ # detected by successorssets)
+ sset = obsutil.successorssets(repo, ctx.node())
+ if sset:
+ return (sset[0][0], ctx.node())
+ else:
+ # case 2: one of the precursors of the rev being revived has a
+ # non-obsolete successor (we need divergentsets for this)
+ divsets = obsutil.divergentsets(repo, ctx)
+ if divsets:
+ nsuccset = divsets[0][b'divergentnodes']
+ prec = divsets[0][b'commonpredecessor']
+ return (nsuccset[0], prec)
+ return None
+
+
def skip_empty_successor(ui, command):
empty_successor = ui.config(b'rewrite', b'empty-successor')
if empty_successor == b'skip':
@@ -103,6 +103,7 @@
# Options for obsolescence
createmarkersopt = b'createmarkers'
allowunstableopt = b'allowunstable'
+allowdivergenceopt = b'allowdivergence'
exchangeopt = b'exchange'
@@ -141,10 +142,13 @@
createmarkersvalue = _getoptionvalue(repo, createmarkersopt)
unstablevalue = _getoptionvalue(repo, allowunstableopt)
+ divergencevalue = _getoptionvalue(repo, allowdivergenceopt)
exchangevalue = _getoptionvalue(repo, exchangeopt)
# createmarkers must be enabled if other options are enabled
- if (unstablevalue or exchangevalue) and not createmarkersvalue:
+ if (
+ unstablevalue or divergencevalue or exchangevalue
+ ) and not createmarkersvalue:
raise error.Abort(
_(
b"'createmarkers' obsolete option must be enabled "
@@ -155,6 +159,7 @@
return {
createmarkersopt: createmarkersvalue,
allowunstableopt: unstablevalue,
+ allowdivergenceopt: divergencevalue,
exchangeopt: exchangevalue,
}
@@ -446,8 +446,15 @@
rebaseset = set(destmap.keys())
rebaseset -= set(self.obsolete_with_successor_in_destination)
rebaseset -= self.obsolete_with_successor_in_rebase_set
+ # We have our own divergence-checking in the rebase extension
+ overrides = {}
+ if obsolete.isenabled(self.repo, obsolete.createmarkersopt):
+ overrides = {
+ (b'experimental', b'evolution.allowdivergence'): b'true'
+ }
try:
- rewriteutil.precheck(self.repo, rebaseset, action=b'rebase')
+ with self.ui.configoverride(overrides):
+ rewriteutil.precheck(self.repo, rebaseset, action=b'rebase')
except error.Abort as e:
if e.hint is None:
e.hint = _(b'use --keep to keep original changesets')