changeset 5544:0bb75a6c29b1

rewind: add a --dry-run flag There are 3 cases handled separately in formatstatus(): - common-case rewind when we have X current successors rewinding to Y predecessors - rewinding with --as-divergence, when we don't obsolete current successors - rewinding pruned commits, when we don't have current successors to show In the common case, we might have sub-cases that change the output with --dry-run (but not the way the rewind operates): - more predecessors than successors (e.g. after a fold), in which case we simply use successorsmap, which maps successors to a set of predecessors - equal number of predecessors and successors, see the previous case - more successors than predecessors (e.g. after a split), in which case we use a reverse of successorsmap (rsm), which maps predecessors to a set of successors and exists only for the sake of --dry-run functionality These two dicts allow us to group rewind targets and output separate lines for separate predecessors<->successors relations.
author Anton Shestakov <av6@dwimlabs.net>
date Tue, 21 Jul 2020 01:04:43 +0800
parents 75ca371bd9d1
children 56e5dc7d6319
files hgext3rd/evolve/rewind.py tests/test-rewind.t
diffstat 2 files changed, 136 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/hgext3rd/evolve/rewind.py	Tue Jul 21 01:03:18 2020 +0800
+++ b/hgext3rd/evolve/rewind.py	Tue Jul 21 01:04:43 2020 +0800
@@ -7,6 +7,7 @@
     cmdutil,
     error,
     hg,
+    node as nodemod,
     obsolete,
     obsutil,
     scmutil,
@@ -36,6 +37,8 @@
       _(b"rewind these revisions to their predecessors"), _(b'REV')),
      (b'k', b'keep', None,
       _(b"do not modify working directory during rewind")),
+     (b'n', b'dry-run', False,
+      _(b'do not perform actions, just print what would be done')),
      ],
     _(b'[--as-divergence] [--exact] [--keep] [--to REV]... [--from REV]...'),
     helpbasic=True,
@@ -102,6 +105,10 @@
                     for succ in ssets[0]:
                         successorsmap[succ].add(ctx.node())
 
+        if opts['dry_run']:
+            ui.status(dryrun(unfi, targets, successorsmap, opts))
+            return
+
         # Check that we can rewind these changesets
         with repo.transaction(b'rewind'):
             oldctx = repo[b'.']
@@ -224,3 +231,51 @@
                            flag=identicalflag, operation=b'rewind')
 
     return new
+
+def formatstatus(sources, destinations, asdiv=False):
+    """format a status line for one group of changesets for rewind
+
+    sources is a tuple of current successors, or None in case of rewinding to
+    an earlier version of a pruned commit or when --as-divergence is used.
+
+    destinations is a tuple of predecessors to rewind to.
+    """
+    if sources:
+        return b'rewinding %s to %d changesets: %s\n' % (
+            b' '.join(nodemod.short(src) for src in sources),
+            len(destinations),
+            b' '.join(nodemod.short(dest) for dest in destinations)
+        )
+    elif asdiv:
+        msg = b'recreating %d changesets: %s\n'
+    else:
+        msg = b'rewinding a pruned commit to %d changesets: %s\n'
+    return msg % (
+        len(destinations),
+        b' '.join(nodemod.short(dest) for dest in destinations)
+    )
+
+def dryrun(unfi, targets, successorsmap, opts):
+    """explain what would rewind do, given targets, successorsmap and opts
+
+    Returns a bytestring with one line per predecessors<->successors relation.
+    """
+    cl = unfi.changelog
+    todo = b''
+    rsm = collections.defaultdict(set) # reverse successors map
+    for src, destinations in successorsmap.items():
+        for dest in destinations:
+            rsm[dest].add(src)
+    if rsm and successorsmap:
+        if len(rsm) < len(successorsmap):
+            for dest in sorted(rsm, key=cl.rev):
+                sources = sorted(rsm[dest], key=cl.rev)
+                todo += formatstatus(sources, (dest,))
+        else:
+            for src in sorted(successorsmap, key=cl.rev):
+                destinations = sorted(successorsmap[src], key=cl.rev)
+                todo += formatstatus((src,), destinations)
+    else:
+        destinations = [unfi[rev].node() for rev in sorted(targets)]
+        todo = formatstatus(None, destinations, opts['as_divergence'])
+    return todo
--- a/tests/test-rewind.t	Tue Jul 21 01:03:18 2020 +0800
+++ b/tests/test-rewind.t	Tue Jul 21 01:04:43 2020 +0800
@@ -74,6 +74,11 @@
      summary:     c_ROOT
   
 
+target selection
+
+  $ hg rewind --hidden --to 'desc("c_B0")' --dry-run
+  rewinding a pruned commit to 1 changesets: 7e594302a05d
+
 actual rewind
 
   $ hg rewind --hidden --to 'desc("c_B0")'
@@ -151,6 +156,11 @@
   $ hg prune 'desc("c_B0")'
   1 changesets pruned
 
+target selection
+
+  $ hg rewind --hidden --to 'min(desc("c_B0"))' --dry-run
+  rewinding a pruned commit to 1 changesets: 7e594302a05d
+
 actual rewind
 
   $ hg rewind --hidden --to 'min(desc("c_B0"))'
@@ -263,6 +273,13 @@
      summary:     c_ROOT
   
 
+target selection
+
+  $ hg rewind --from 'desc("c_B1")' --as-divergence --dry-run
+  recreating 1 changesets: 7e594302a05d
+  $ hg rewind --hidden --to 'desc("c_B0")' --as-divergence --dry-run
+  recreating 1 changesets: 7e594302a05d
+
 actual rewind
 
   $ hg rewind --hidden --to 'desc("c_B0")' --as-divergence
@@ -344,6 +361,15 @@
      summary:     c_ROOT
   
 
+rewind --dry-run output when rewinding with relevant divergence
+
+  $ hg rewind --to 'min(desc("c_B0"))' --hidden --dry-run
+  abort: rewind confused by divergence on 7e594302a05d
+  (solve divergence first or use "--as-divergence")
+  [255]
+  $ hg rewind --to 'min(desc("c_B0"))' --hidden --as-divergence --dry-run
+  recreating 1 changesets: 7e594302a05d
+
 cleanup
 
   $ hg prune 'max(desc("c_B0"))'
@@ -370,6 +396,11 @@
 Rewind a simple amend - obsoleting the current latest successor
 ---------------------------------------------------------------
 
+target selection
+
+  $ hg rewind --hidden --to 'min(desc("c_B0"))' --dry-run
+  rewinding 25c8f5ab0c3b to 1 changesets: 7e594302a05d
+
 actual rewind
 
   $ echo 'default-date = 2 0' >> $HGRCPATH
@@ -549,6 +580,13 @@
   x  49fb7d900906 (3) c_CD0
   
 
+target selection
+
+  $ hg rewind --from 'desc("c_CD0")' --dry-run
+  rewinding a0316c4c5417 9576e80d6851 to 1 changesets: 49fb7d900906
+  $ hg rewind --hidden --to 'min(desc("c_CD0"))' --dry-run
+  rewinding a0316c4c5417 9576e80d6851 to 1 changesets: 49fb7d900906
+
 actual rewind
 
   $ hg rewind --hidden --to 'min(desc("c_CD0"))'
@@ -620,6 +658,26 @@
 
   $ echo 'default-date = 3 0' >> $HGRCPATH
 
+target selection
+
+  $ hg rewind --from . --hidden --dry-run
+  rewinding 4535d0af405c to 2 changesets: a0316c4c5417 9576e80d6851
+
+  $ hg rewind --to '9576e80d6851+a0316c4c5417' --hidden --dry-run
+  rewinding 4535d0af405c to 2 changesets: a0316c4c5417 9576e80d6851
+  $ hg rewind --to '9576e80d6851' --hidden --dry-run
+  rewinding 4535d0af405c to 2 changesets: a0316c4c5417 9576e80d6851
+
+XXX this should also give us 2 changesets
+
+  $ hg rewind --to 'a0316c4c5417' --hidden --dry-run
+  rewinding 4535d0af405c to 1 changesets: a0316c4c5417
+
+  $ hg rewind --to '9576e80d6851' --exact --hidden --dry-run
+  rewinding 4535d0af405c to 1 changesets: 9576e80d6851
+  $ hg rewind --to 'a0316c4c5417' --exact --hidden --dry-run
+  rewinding 4535d0af405c to 1 changesets: a0316c4c5417
+
 actual rewind
 
   $ hg rewind --to '9576e80d6851+a0316c4c5417' --hidden
@@ -748,6 +806,11 @@
      summary:     c_ROOT
   
 
+target selection
+
+  $ hg rewind --hidden --to 'min(desc(c_B0))' --exact --dry-run
+  rewinding a65fceb2324a to 1 changesets: 7e594302a05d
+
 actual rewind
 
   $ hg rewind --hidden --to 'min(desc(c_B0))' --exact
@@ -813,6 +876,8 @@
 rewind with no arguments should be equivalent to `--from .`
 
   $ echo 'default-date = 4 0' >> $HGRCPATH
+  $ hg rewind --dry-run
+  rewinding ac979e0aac4e to 1 changesets: a65fceb2324a
   $ hg rewind
   rewound to 1 changesets
   (1 changesets obsoleted)
@@ -860,6 +925,14 @@
 Automatically rewinding the full stack (with --to)
 --------------------------------------------------
 
+target selection
+
+  $ hg rewind --hidden --to 'predecessors(.)' --dry-run
+  rewinding d952d1794ff6 to 1 changesets: 579f120ba918
+  rewinding a5dd64adbb2a to 1 changesets: ac979e0aac4e
+
+actual rewind
+
   $ echo 'default-date = 5 0' >> $HGRCPATH
   $ hg rewind --hidden --to 'predecessors(.)'
   rewound to 2 changesets
@@ -916,6 +989,14 @@
 Automatically rewinding the full stack (with --from)
 ----------------------------------------------------
 
+target selection
+
+  $ hg rewind --from '.' --dry-run
+  rewinding 9c28b7ed3951 to 1 changesets: d952d1794ff6
+  rewinding 3f2d8862657d to 1 changesets: a5dd64adbb2a
+
+actual rewind
+
   $ echo 'default-date = 6 0' >> $HGRCPATH
   $ hg rewind --from '.'
   rewound to 2 changesets