view hgext3rd/evolve/templatekw.py @ 2489:84a8219a2f9a

template: add the obsfate template Reuse _getobsoletefate and nextvisiblesuccessors to easily get the obsolescence fate of any node. The limitation is that obsfate is as precise as these two functions, which is not great for nextvisiblesuccessors.
author Boris Feld <boris.feld@octobus.net>
date Fri, 26 May 2017 13:28:49 +0200
parents 590da9c523ae
children 94f171534918
line wrap: on
line source

# Copyright 2011 Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
#                Logilab SA        <contact@logilab.fr>
#                Pierre-Yves David <pierre-yves.david@ens-lyon.org>
#                Patrick Mezard <patrick@mezard.eu>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
"""evolve templates
"""

from . import (
    exthelper,
    obshistory
)

from mercurial import (
    templatekw,
    node,
)

eh = exthelper.exthelper()

### template keywords
# XXX it does not handle troubles well :-/

@eh.templatekw('obsolete')
def obsoletekw(repo, ctx, templ, **args):
    """:obsolete: String. Whether the changeset is ``obsolete``.
    """
    if ctx.obsolete():
        return 'obsolete'
    return ''

@eh.templatekw('troubles')
def showtroubles(**args):
    """:troubles: List of strings. Evolution troubles affecting the changeset
    (zero or more of "unstable", "divergent" or "bumped")."""
    ctx = args['ctx']
    try:
        # specify plural= explicitly to trigger TypeError on hg < 4.2
        return templatekw.showlist('trouble', ctx.troubles(), args,
                                   plural='troubles')
    except TypeError:
        return templatekw.showlist('trouble', ctx.troubles(), plural='troubles',
                                   **args)

def closestprecursors(repo, nodeid):
    """ Yield the list of next precursors pointing on visible changectx nodes
    """

    precursors = repo.obsstore.precursors
    stack = [nodeid]

    while stack:
        current = stack.pop()
        currentpreccs = precursors.get(current, ())

        for prec in currentpreccs:
            precnodeid = prec[0]

            if precnodeid in repo:
                yield precnodeid
            else:
                stack.append(precnodeid)

@eh.templatekw("precursors")
def shownextvisibleprecursors(repo, ctx, templ, **args):
    """Returns a string containing the list if the closest successors
    displayed
    """
    # XXX-review: I've added a couple of 'XXX' for future work.

    # XXX: template logic supports lists, so we should more to using a list at some point

    # XXX: I think we could returns something close to a "changectx" that would allow template to alter the way we render this.
    shortnodes = map(node.short, sorted(closestprecursors(repo, ctx.node())))
    return ', '.join(shortnodes)

def closestsuccessors(repo, nodeid):
    """ returns the closest visible successors sets instead.
    """
    return directsuccessorssets(repo, nodeid)

@eh.templatekw("successors")
def shownextvisiblesuccessors(repo, ctx, templ, **args):
    """Returns a string of sets of successors for a changectx in this format:
    [ctx1, ctx2], [ctx3] if ctx has been splitted into ctx1 and ctx2 while
    also diverged into ctx3"""
    if not ctx.obsolete():
        return ''

    ssets = closestsuccessors(repo, ctx.node())

    final = []
    for ss in ssets:
        final.append('[%s]' % ', '.join(map(node.short, ss)))

    return ', '.join(final)

@eh.templatekw("obsfate")
def showobsfate(repo, ctx, templ, **args):
    if not ctx.obsolete():
        return ''

    successorssets = closestsuccessors(repo, ctx.node())
    return obshistory._getobsfate(successorssets)

# copy from mercurial.obsolete with a small change to stop at first known changeset.

def directsuccessorssets(repo, initialnode, cache=None):
    """return set of all direct successors of initial nodes
    """

    succmarkers = repo.obsstore.successors

    # Stack of nodes we search successors sets for
    toproceed = [initialnode]
    # set version of above list for fast loop detection
    # element added to "toproceed" must be added here
    stackedset = set(toproceed)
    if cache is None:
        cache = {}
    while toproceed:
        current = toproceed[-1]
        if current in cache:
            stackedset.remove(toproceed.pop())
        elif current != initialnode and current in repo:
            # We have a valid direct successors.
            cache[current] = [(current,)]
        elif current not in succmarkers:
            if current in repo:
                # We have a valid last successors.
                cache[current] = [(current,)]
            else:
                # Final obsolete version is unknown locally.
                # Do not count that as a valid successors
                cache[current] = []
        else:
            for mark in sorted(succmarkers[current]):
                for suc in mark[1]:
                    if suc not in cache:
                        if suc in stackedset:
                            # cycle breaking
                            cache[suc] = []
                        else:
                            # case (3) If we have not computed successors sets
                            # of one of those successors we add it to the
                            # `toproceed` stack and stop all work for this
                            # iteration.
                            toproceed.append(suc)
                            stackedset.add(suc)
                            break
                else:
                    continue
                break
            else:
                succssets = []
                for mark in sorted(succmarkers[current]):
                    # successors sets contributed by this marker
                    markss = [[]]
                    for suc in mark[1]:
                        # cardinal product with previous successors
                        productresult = []
                        for prefix in markss:
                            for suffix in cache[suc]:
                                newss = list(prefix)
                                for part in suffix:
                                    # do not duplicated entry in successors set
                                    # first entry wins.
                                    if part not in newss:
                                        newss.append(part)
                                productresult.append(newss)
                        markss = productresult
                    succssets.extend(markss)
                # remove duplicated and subset
                seen = []
                final = []
                candidate = sorted(((set(s), s) for s in succssets if s),
                                   key=lambda x: len(x[1]), reverse=True)
                for setversion, listversion in candidate:
                    for seenset in seen:
                        if setversion.issubset(seenset):
                            break
                    else:
                        final.append(listversion)
                        seen.append(setversion)
                final.reverse() # put small successors set first
                cache[current] = final
    return cache[initialnode]