# HG changeset patch # User Anton Shestakov # Date 1595264683 -28800 # Node ID 0bb75a6c29b17c167f3a7c264889fa5cb22d0af7 # Parent 75ca371bd9d105ee0ff08a67ce337e5cecfad9e3 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. diff -r 75ca371bd9d1 -r 0bb75a6c29b1 hgext3rd/evolve/rewind.py --- 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 diff -r 75ca371bd9d1 -r 0bb75a6c29b1 tests/test-rewind.t --- 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