changeset 5802:84efc2e92228

rewind: abort if some parts of a fold are unknown locally
author Anton Shestakov <av6@dwimlabs.net>
date Tue, 22 Dec 2020 19:07:46 +0800
parents e8a43f5929f6
children abbc021c6f68
files CHANGELOG hgext3rd/evolve/rewind.py tests/test-rewind.t
diffstat 3 files changed, 140 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG	Tue Dec 22 19:06:36 2020 +0800
+++ b/CHANGELOG	Tue Dec 22 19:07:46 2020 +0800
@@ -12,6 +12,8 @@
    in memory
  * rewind: detect and abort on cases when we rewind to changesets that are
    precessors / successors of each other
+ * rewind: when user gives only some parts of a fold, include the other parts
+   as well, or abort if they are missing from local repo
 
 10.2.0.post1 -- 2021-02-01
 --------------------------
--- a/hgext3rd/evolve/rewind.py	Tue Dec 22 19:06:36 2020 +0800
+++ b/hgext3rd/evolve/rewind.py	Tue Dec 22 19:07:46 2020 +0800
@@ -248,9 +248,29 @@
 
     return latest, source
 
+def _check_unknown_predecessors(unfi, nodes, source):
+    """ check if any nodes are absent from both source and unfiltered repo
+
+    If node is not in source, we might want it as an extra rewind target. But
+    if it's also absent from local (unfiltered) repo, we need to warn user and
+    abort.
+    """
+    missing = {
+        node for node in nodes
+        if node not in source and node not in unfi
+    }
+    if missing:
+        msg = _(b'not rewinding, some predecessors are unknown locally: %s')
+        msg %= b' '.join(nodemod.short(m) for m in missing)
+        hint = _(b'try selecting all changesets to rewind to manually, '
+                 b'possibly with --exact')
+        raise error.Abort(msg, hint=hint)
+
 def _walk_predecessors(ui, unfi, targetset, latest, source):
     """follow predecessors of targets, validate and suggest extra targets
 
+    While doing that, check and abort if any fold components are unknown.
+
     Skip predecessors that are only reachable by following obsmarkers with
     "identical" flag, because these markers are for internal use and actual
     users don't want to revive such predecessors.
@@ -272,6 +292,8 @@
         markers = unfi.obsstore.predecessors.get(successor, ())
         data = (((marker[0],), (marker,)) for marker in markers)
         for (nodes, markers) in sorted(obshistory.groupbyfoldid(data)):
+            # TODO: this could be moved to a post-processing stage
+            _check_unknown_predecessors(unfi, nodes, source)
             for node in nodes:
                 if node in seen:
                     # basic cycle protection
--- a/tests/test-rewind.t	Tue Dec 22 19:06:36 2020 +0800
+++ b/tests/test-rewind.t	Tue Dec 22 19:07:46 2020 +0800
@@ -1517,8 +1517,123 @@
 
   $ hg rewind --hidden --to 'desc("A0")+desc("B0")' --exact --dry-run
   rewinding 1988e9fe9517 to 2 changesets: fa8956746c52 a07c12c45197
+  $ hg rewind --hidden --to 'desc("A0")' --dry-run
+  abort: not rewinding, some predecessors are unknown locally: 25210d726f52
+  (try selecting all changesets to rewind to manually, possibly with --exact)
+  [255]
+  $ hg rewind --hidden --to 'desc("A1")' --dry-run
+  abort: not rewinding, some predecessors are unknown locally: 25210d726f52
+  (try selecting all changesets to rewind to manually, possibly with --exact)
+  [255]
+
+XXX the semantic of --exact might need clarification here,
+XXX for example, shouln't --exact make sure we only rewind to the `--to` target ?
+
   $ hg rewind --hidden --to 'desc("A1")' --exact --dry-run
-  abort: unknown revision '25210d726f527e03f1f148bb8048d571512146d9'
+  abort: not rewinding, some predecessors are unknown locally: 25210d726f52
+  (try selecting all changesets to rewind to manually, possibly with --exact)
+  [255]
+  $ hg rewind --from 'desc("AB2")' --dry-run
+  abort: not rewinding, some predecessors are unknown locally: 25210d726f52
+  (try selecting all changesets to rewind to manually, possibly with --exact)
+  [255]
+  $ hg rewind --from 'desc("AB2")' --exact --dry-run
+  abort: not rewinding, some predecessors are unknown locally: 25210d726f52
+  (try selecting all changesets to rewind to manually, possibly with --exact)
   [255]
 
   $ cd ..
+
+split and then fold with a missing part
+
+..      /⇠ (C1) ⇠\
+.. BC0 ⇠    |     \
+..  |   \⇠  B1     ⇠ AC2
+..  A0 ⇠⇠⇠⇠⇠⇠⇠⇠⇠⇠⇠/
+
+here we have a case when walking successors and then predecessors of target
+revisions just once might not be enough, because it's a more complex DAG with a
+changeset missing from local repo
+
+  $ hg init extra-fold-case-5
+  $ cd extra-fold-case-5
+
+  $ echo R > R
+  $ hg ci -qAm R
+  $ echo A > A
+  $ hg ci -qAm A0
+  $ echo B > B
+  $ echo C > C
+  $ hg ci -qAm BC0
+  $ hg up 'desc("A0")' -q
+  $ echo B > B
+  $ hg ci -qAm B1
+  $ echo C > C
+  $ hg ci -qAm C1
+
+  $ hg prune -r 'desc("BC0")' -s 'desc("B1")+desc("C1")' --split
+  1 changesets pruned
+
+  $ hg up 'desc("R")' -q
+  $ echo A > A
+  $ echo C > C
+  $ hg ci -qAm AC2
+
+  $ hg prune -r 'desc("A0")+desc("C1")' -s 'desc("AC2")' --fold
+  2 changesets pruned
+  1 new orphan changesets
+
+  $ hg glhf --hidden
+  @  5:9ccaac2e5fbb AC2 (A C)
+  |
+  | x  4:2e4ab803d8ae C1 (C)
+  | |
+  | *  3:44774eafdc1c B1 (B)
+  | |
+  | | x  2:883d75400657 BC0 (B C)
+  | |/
+  | x  1:fa8956746c52 A0 (A)
+  |/
+  o  0:167e04d3d1b2 R (R)
+  
+
+target selection
+
+  $ hg rewind --hidden --to 'desc("A0")' --dry-run
+  rewinding 9ccaac2e5fbb to 2 changesets: fa8956746c52 2e4ab803d8ae
+  $ hg rewind --hidden --to 'desc("BC0")' --dry-run
+  rewinding 44774eafdc1c to 1 changesets: 883d75400657
+  rewinding 9ccaac2e5fbb to 2 changesets: fa8956746c52 883d75400657
+  $ hg rewind --from 'desc("AC2")' --dry-run
+  rewinding 9ccaac2e5fbb to 2 changesets: fa8956746c52 2e4ab803d8ae
+
+stripping a component of AC2 fold
+
+  $ hg strip --config extensions.strip= --hidden -r 'desc("C1")' -q
+  warning: ignoring unknown working parent 9ccaac2e5fbb!
+
+target selection
+
+at the moment, there's not much that we can do here because of missing C1
+
+in future we might have a way to allow rewind to skip changesets unknown
+locally and still proceed (and lose the least amount of work possible)
+
+XXX the semantic of --exact might need clarification here,
+XXX for example, shouln't --exact make sure we only rewind to the `--to` target ?
+
+  $ hg rewind --hidden --to 'desc("A0")' --dry-run
+  abort: not rewinding, some predecessors are unknown locally: 2e4ab803d8ae
+  (try selecting all changesets to rewind to manually, possibly with --exact)
+  [255]
+  $ hg rewind --hidden --to 'desc("BC0")' --dry-run
+  rewinding 44774eafdc1c to 1 changesets: 883d75400657
+  rewinding 9ccaac2e5fbb to 2 changesets: fa8956746c52 883d75400657
+  $ hg rewind --from 'desc("AC2")' --dry-run
+  abort: not rewinding, some predecessors are unknown locally: 2e4ab803d8ae
+  (try selecting all changesets to rewind to manually, possibly with --exact)
+  [255]
+  $ hg rewind --from 'desc("AC2")' --exact --dry-run
+  abort: not rewinding, some predecessors are unknown locally: 2e4ab803d8ae
+  (try selecting all changesets to rewind to manually, possibly with --exact)
+  [255]