diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py --- a/mercurial/debugcommands.py +++ b/mercurial/debugcommands.py @@ -2067,13 +2067,14 @@ def debugsub(ui, repo, rev=None): ui.write((' revision %s\n') % v[1]) @command('debugsuccessorssets', - [], + [('', 'closest', False, _('return closest successors sets only'))], _('[REV]')) -def debugsuccessorssets(ui, repo, *revs): +def debugsuccessorssets(ui, repo, *revs, **opts): """show set of successors for revision A successors set of changeset A is a consistent group of revisions that - succeed A. It contains non-obsolete changesets only. + succeed A. It contains non-obsolete changesets only unless closests + successors set is set. In most cases a changeset A has a single successors set containing a single successor (changeset A replaced by A'). @@ -2111,7 +2112,9 @@ def debugsuccessorssets(ui, repo, *revs) for rev in scmutil.revrange(repo, revs): ctx = repo[rev] ui.write('%s\n'% ctx2str(ctx)) - for succsset in obsutil.successorssets(repo, ctx.node(), cache=cache): + for succsset in obsutil.successorssets(repo, ctx.node(), + closest=opts['closest'], + cache=cache): if succsset: ui.write(' ') ui.write(node2str(succsset[0])) diff --git a/mercurial/obsutil.py b/mercurial/obsutil.py --- a/mercurial/obsutil.py +++ b/mercurial/obsutil.py @@ -311,12 +311,15 @@ def getobsoleted(repo, tr): obsoleted.add(rev) return obsoleted -def successorssets(repo, initialnode, cache=None): +def successorssets(repo, initialnode, closest=False, cache=None): """Return set of all latest successors of initial nodes The successors set of a changeset A are the group of revisions that succeed A. It succeeds A as a consistent whole, each revision being only a partial - replacement. The successors set contains non-obsolete changesets only. + replacement. By default, the successors set contains non-obsolete + changesets only, walking the obsolescence graph until reaching a leaf. If + 'closest' is set to True, closest successors-sets are return (the + obsolescence walk stops on known changesets). This function returns the full list of successor sets which is why it returns a list of tuples and not just a single tuple. Each tuple is a valid @@ -342,12 +345,19 @@ def successorssets(repo, initialnode, ca (pruned: obsoleted without any successors). (Final: successors not affected by markers). + The 'closest' mode respect the repoview filtering. For example, without + filter it will stop at the first locally known changeset, with 'visible' + filter it will stop on visible changesets). + The optional `cache` parameter is a dictionary that may contains precomputed successors sets. It is meant to reuse the computation of a previous call to `successorssets` when multiple calls are made at the same time. The cache dictionary is updated in place. The caller is responsible for its life span. Code that makes multiple calls to `successorssets` *should* use this cache mechanism or risk a performance hit. + + Since results are different depending of the 'closest' most, the same cache + cannot be reused for both mode. """ succmarkers = repo.obsstore.successors @@ -394,8 +404,10 @@ def successorssets(repo, initialnode, ca # # 1) We already know the successors sets of CURRENT: # -> mission accomplished, pop it from the stack. - # 2) Node is not obsolete: - # -> the node is its own successors sets. Add it to the cache. + # 2) Stop the walk: + # default case: Node is not obsolete + # closest case: Node is known at this repo filter level + # -> the node is its own successors sets. Add it to the cache. # 3) We do not know successors set of direct successors of CURRENT: # -> We add those successors to the stack. # 4) We know successors sets of all direct successors of CURRENT: @@ -403,13 +415,20 @@ def successorssets(repo, initialnode, ca # cache. # current = toproceed[-1] + + # case 2 condition is a bit hairy because of closest, + # we compute it on its own + case2condition = ((current not in succmarkers) + or (closest and current != initialnode + and current in repo)) + if current in cache: # case (1): We already know the successors sets stackedset.remove(toproceed.pop()) - elif current not in succmarkers: - # case (2): The node is not obsolete. + elif case2condition: + # case (2): end of walk. if current in repo: - # We have a valid last successors. + # We have a valid successors. cache[current] = [(current,)] else: # Final obsolete version is unknown locally. diff --git a/tests/test-completion.t b/tests/test-completion.t --- a/tests/test-completion.t +++ b/tests/test-completion.t @@ -284,7 +284,7 @@ Show all commands + options debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized debugsetparents: debugsub: rev - debugsuccessorssets: + debugsuccessorssets: closest debugtemplate: rev, define debugupdatecaches: debugupgraderepo: optimize, run diff --git a/tests/test-obsolete-divergent.t b/tests/test-obsolete-divergent.t --- a/tests/test-obsolete-divergent.t +++ b/tests/test-obsolete-divergent.t @@ -80,6 +80,23 @@ A_1 have two direct and divergent succes $ hg log -r 'divergent()' 2:82623d38b9ba A_1 3:392fd25390da A_2 + $ hg debugsuccessorssets 'all()' --closest + d20a80d4def3 + d20a80d4def3 + 82623d38b9ba + 82623d38b9ba + 392fd25390da + 392fd25390da + $ hg debugsuccessorssets 'all()' --closest --hidden + d20a80d4def3 + d20a80d4def3 + 007dc284c1f8 + 82623d38b9ba + 392fd25390da + 82623d38b9ba + 82623d38b9ba + 392fd25390da + 392fd25390da check that mercurial refuse to push @@ -128,6 +145,25 @@ indirect divergence with known changeset $ hg log -r 'divergent()' 2:82623d38b9ba A_1 4:01f36c5a8fda A_3 + $ hg debugsuccessorssets 'all()' --closest + d20a80d4def3 + d20a80d4def3 + 82623d38b9ba + 82623d38b9ba + 01f36c5a8fda + 01f36c5a8fda + $ hg debugsuccessorssets 'all()' --closest --hidden + d20a80d4def3 + d20a80d4def3 + 007dc284c1f8 + 82623d38b9ba + 392fd25390da + 82623d38b9ba + 82623d38b9ba + 392fd25390da + 392fd25390da + 01f36c5a8fda + 01f36c5a8fda $ cd .. @@ -160,6 +196,23 @@ indirect divergence with known changeset $ hg log -r 'divergent()' 2:82623d38b9ba A_1 3:392fd25390da A_2 + $ hg debugsuccessorssets 'all()' --closest + d20a80d4def3 + d20a80d4def3 + 82623d38b9ba + 82623d38b9ba + 392fd25390da + 392fd25390da + $ hg debugsuccessorssets 'all()' --closest --hidden + d20a80d4def3 + d20a80d4def3 + 007dc284c1f8 + 82623d38b9ba + 392fd25390da + 82623d38b9ba + 82623d38b9ba + 392fd25390da + 392fd25390da $ cd .. do not take unknown node in account if they are final @@ -175,6 +228,10 @@ do not take unknown node in account if t $ hg debugsuccessorssets --hidden 'desc('A_0')' 007dc284c1f8 392fd25390da + $ hg debugsuccessorssets 'desc('A_0')' --closest + $ hg debugsuccessorssets 'desc('A_0')' --closest --hidden + 007dc284c1f8 + 82623d38b9ba $ cd .. @@ -211,6 +268,23 @@ divergence that converge again is not di 01f36c5a8fda 01f36c5a8fda $ hg log -r 'divergent()' + $ hg debugsuccessorssets 'all()' --closest + d20a80d4def3 + d20a80d4def3 + 01f36c5a8fda + 01f36c5a8fda + $ hg debugsuccessorssets 'all()' --closest --hidden + d20a80d4def3 + d20a80d4def3 + 007dc284c1f8 + 82623d38b9ba + 392fd25390da + 82623d38b9ba + 82623d38b9ba + 392fd25390da + 392fd25390da + 01f36c5a8fda + 01f36c5a8fda $ cd .. split is not divergences @@ -237,6 +311,22 @@ split is not divergences 392fd25390da 392fd25390da $ hg log -r 'divergent()' + $ hg debugsuccessorssets 'all()' --closest + d20a80d4def3 + d20a80d4def3 + 82623d38b9ba + 82623d38b9ba + 392fd25390da + 392fd25390da + $ hg debugsuccessorssets 'all()' --closest --hidden + d20a80d4def3 + d20a80d4def3 + 007dc284c1f8 + 82623d38b9ba 392fd25390da + 82623d38b9ba + 82623d38b9ba + 392fd25390da + 392fd25390da Even when subsequent rewriting happen @@ -283,6 +373,28 @@ Even when subsequent rewriting happen e442cfc57690 e442cfc57690 e442cfc57690 + $ hg debugsuccessorssets 'all()' --closest + d20a80d4def3 + d20a80d4def3 + 01f36c5a8fda + 01f36c5a8fda + e442cfc57690 + e442cfc57690 + $ hg debugsuccessorssets 'all()' --closest --hidden + d20a80d4def3 + d20a80d4def3 + 007dc284c1f8 + 82623d38b9ba 392fd25390da + 82623d38b9ba + 82623d38b9ba + 392fd25390da + 392fd25390da + 01f36c5a8fda + 01f36c5a8fda + 6a411f0d7a0a + e442cfc57690 + e442cfc57690 + e442cfc57690 $ hg log -r 'divergent()' Check more complex obsolescence graft (with divergence) @@ -352,6 +464,40 @@ Check more complex obsolescence graft (w 14608b260df8 bed64f5d2f5a bed64f5d2f5a + $ hg debugsuccessorssets 'all()' --closest + d20a80d4def3 + d20a80d4def3 + 01f36c5a8fda + 01f36c5a8fda + 7ae126973a96 + 7ae126973a96 + 14608b260df8 + 14608b260df8 + bed64f5d2f5a + bed64f5d2f5a + $ hg debugsuccessorssets 'all()' --closest --hidden + d20a80d4def3 + d20a80d4def3 + 007dc284c1f8 + 82623d38b9ba 392fd25390da + 82623d38b9ba + 82623d38b9ba + 392fd25390da + 392fd25390da + 01f36c5a8fda + 01f36c5a8fda + 6a411f0d7a0a + e442cfc57690 + e442cfc57690 + e442cfc57690 + 3750ebee865d + 392fd25390da + 7ae126973a96 + 7ae126973a96 + 14608b260df8 + 14608b260df8 + bed64f5d2f5a + bed64f5d2f5a $ hg log -r 'divergent()' 4:01f36c5a8fda A_3 8:7ae126973a96 A_7 @@ -416,6 +562,38 @@ fix the divergence a139f71be9da a139f71be9da a139f71be9da + $ hg debugsuccessorssets 'all()' --closest + d20a80d4def3 + d20a80d4def3 + 01f36c5a8fda + 01f36c5a8fda + a139f71be9da + a139f71be9da + $ hg debugsuccessorssets 'all()' --closest --hidden + d20a80d4def3 + d20a80d4def3 + 007dc284c1f8 + 82623d38b9ba 392fd25390da + 82623d38b9ba + 82623d38b9ba + 392fd25390da + 392fd25390da + 01f36c5a8fda + 01f36c5a8fda + 6a411f0d7a0a + e442cfc57690 + e442cfc57690 + e442cfc57690 + 3750ebee865d + 392fd25390da + 7ae126973a96 + a139f71be9da + 14608b260df8 + a139f71be9da + bed64f5d2f5a + a139f71be9da + a139f71be9da + a139f71be9da $ hg log -r 'divergent()' $ cd .. @@ -433,5 +611,9 @@ successors-set. (report [A,B] not [A] + $ hg debugsuccessorssets --hidden 'desc('A_0')' 007dc284c1f8 82623d38b9ba 392fd25390da + $ hg debugsuccessorssets 'desc('A_0')' --closest + $ hg debugsuccessorssets 'desc('A_0')' --closest --hidden + 007dc284c1f8 + 82623d38b9ba 392fd25390da $ cd ..