changeset 5801:e8a43f5929f6

rewind: abort when trying to rewind to multiple unrelated predecessors
author Anton Shestakov <av6@dwimlabs.net>
date Tue, 22 Dec 2020 19:06:36 +0800
parents 668817e8c007
children 84efc2e92228
files CHANGELOG hgext3rd/evolve/rewind.py tests/test-rewind.t
diffstat 3 files changed, 43 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG	Tue Dec 22 19:03:59 2020 +0800
+++ b/CHANGELOG	Tue Dec 22 19:06:36 2020 +0800
@@ -10,6 +10,8 @@
  * obslog: clarify the command name in the help,
  * evolve: add a experimental.evolution.in-memory config for running evolve
    in memory
+ * rewind: detect and abort on cases when we rewind to changesets that are
+   precessors / successors of each other
 
 10.2.0.post1 -- 2021-02-01
 --------------------------
--- a/hgext3rd/evolve/rewind.py	Tue Dec 22 19:03:59 2020 +0800
+++ b/hgext3rd/evolve/rewind.py	Tue Dec 22 19:06:36 2020 +0800
@@ -192,9 +192,30 @@
     if update_target is not None and not opts.get('keep'):
         ui.status(_(b'working directory is now at %s\n') % repo[b'.'])
 
+def _check_multiple_predecessors(targetnode, successor, targetset):
+    """ check if a successor of one rewind target is already another target
+
+        An example obsolescence marker tree:
+        A0  A1  A2
+        x - x - o
+
+        When user tries to rewind A2 to both its predecessors with e.g.
+        `hg rewind --to A0+A1`, this function will at one point be called with
+        (A0, A1, [A0, A1]) as arguments. In that case it will raise an Abort
+        and prevent rewind from succeeding.
+    """
+    if successor in targetset:
+        msg = _(b'not rewinding, %s is a successor of %s')
+        msg %= (nodemod.short(successor), nodemod.short(targetnode))
+        hint = _(b'pick only one of these changesets, possibly with --exact')
+        raise error.Abort(msg, hint=hint)
+
 def _walk_successors(ui, unfi, targetset):
     """follow successors of targets and find the latest successors
 
+    While doing that, check and abort if there are multiple unrelated
+    predecessors in targetset.
+
     We also keep track of "source": when we reach a successor by following the
     obsmarker-graph starting from a certain target, we add `successor: target`
     pair to `source`. But we don't care about having more than one `target`
@@ -218,6 +239,8 @@
                         # found if e.g. there's a fold (and that is fine).
                         # (Also basic cycle protection.)
                         continue
+                    # TODO: this could be moved to a post-processing stage
+                    _check_multiple_predecessors(targetnode, successor, targetset)
                     source[successor] = current
                     remaining.add(successor)
             if not markers:
--- a/tests/test-rewind.t	Tue Dec 22 19:03:59 2020 +0800
+++ b/tests/test-rewind.t	Tue Dec 22 19:06:36 2020 +0800
@@ -867,6 +867,19 @@
      summary:     c_ROOT
   
 
+  $ hg rewind --hidden --to 'allpredecessors(desc("c_B0"))' --dry-run
+  abort: not rewinding, a65fceb2324a is a successor of 7e594302a05d
+  (pick only one of these changesets, possibly with --exact)
+  [255]
+  $ hg rewind --hidden --to 'allpredecessors(desc("c_B0"))' --dry-run --exact
+  abort: not rewinding, a65fceb2324a is a successor of 7e594302a05d
+  (pick only one of these changesets, possibly with --exact)
+  [255]
+  $ hg rewind --hidden --to 'allpredecessors(desc("c_B0"))' --dry-run --as-divergence
+  abort: not rewinding, a65fceb2324a is a successor of 7e594302a05d
+  (pick only one of these changesets, possibly with --exact)
+  [255]
+
 Testing the defaults
 --------------------
 
@@ -1247,6 +1260,11 @@
   $ hg rewind --from 'desc("AB2")' --dry-run
   rewinding 7f9a5314ef94 to 2 changesets: 3748b241cad8 16429ed4b6cb
 
+  $ hg rewind --hidden --to 'allpredecessors(desc("AB2"))' --dry-run
+  abort: not rewinding, 3748b241cad8 is a successor of fa8956746c52
+  (pick only one of these changesets, possibly with --exact)
+  [255]
+
   $ cd ..
 
 folding with a changeset we rebased onto