view hgext3rd/evolve/templatekw.py @ 2595:54ac473663ff mercurial-4.1

compat-test: merge with stable * Blackbox format seems to have change when displaying config CLI options. * As obsfate template is now only compatible with hg 4.2+, remove all references to it in test files.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Fri, 16 Jun 2017 11:42:00 +0200
parents d3d686f05458 a3fbe5293bf6
children 44dd9d76afb2 01e32f11f1ef
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, **args):
    """Returns a string containing the list if the closest successors
    displayed
    """
    precursors = sorted(closestprecursors(repo, ctx.node()))

    # <= hg-4.1 requires an explicite gen.
    # we can use None once the support is dropped
    #
    # They also requires an iterator instead of an iterable.
    gen = iter(" ".join(map(node.short, precursors)))
    return templatekw._hybrid(gen.__iter__(), precursors, lambda x: {'precursor': x},
                              lambda d: "%s" % node.short(d['precursor']))

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())

    data = []
    gen = []
    for ss in ssets:
        subgen = '[%s]' % ', '.join(map(node.short, ss))
        gen.append(subgen)
        h = templatekw._hybrid(iter(subgen), ss, lambda x: {'successor': x},
                               lambda d: "%s" % d["successor"])
        data.append(h)

    gen = ', '.join(gen)
    return templatekw._hybrid(iter(gen), data, lambda x: {'successorset': x},
                              lambda d: d["successorset"])

def obsfatedefaulttempl():
    """ Returns a dict with the default templates for obs fate
    """
    # Prepare templates
    verbtempl = '{verb}'
    usertempl = '{if(users, " by {join(users, ", ")}")}'
    succtempl = '{if(successors, " as ")}{successors}' # Bypass if limitation
    datetempleq = ' (at {min_date|isodate})'
    datetemplnoteq = ' (between {min_date|isodate} and {max_date|isodate})'
    datetempl = '{if(max_date, "{ifeq(min_date, max_date, "%s", "%s")}")}' % (datetempleq, datetemplnoteq)
    newline = '\n'

    # Assemble them
    return {
        'obsfate_quiet': verbtempl + succtempl + newline,
        'obsfate': verbtempl + usertempl + succtempl + newline,
        'obsfate_verbose': verbtempl + usertempl + succtempl + datetempl + newline
    }

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

    successorssets, pathcache = closestsuccessors(repo, ctx.node())

    # closestsuccessors returns an empty list for pruned revisions, remap it
    # into a list containing en empty list for future processing
    if successorssets == []:
        successorssets = [[]]

    values = []
    for successorset in successorssets:
        raw = obshistory._preparesuccessorset(successorset, pathcache)

        # As we can't do something like
        # "{join(map(nodeshort, successors), ', '}" in template, manually
        # create a correct textual representation
        gen = ', '.join(map(node.short, raw['successors']))

        makemap = lambda x: {'successor': x}
        joinfmt = lambda d: "%s" % d['successor']
        raw['successors'] = templatekw._hybrid(gen, raw['successors'], makemap,
                                               joinfmt)

        values.append(raw)

    # Insert default obsfate templates
    args['templ'].cache.update(obsfatedefaulttempl())

    if repo.ui.quiet:
        name = "obsfate_quiet"
    elif repo.ui.verbose:
        name = "obsfate_verbose"
    elif repo.ui.debugflag:
        name = "obsfate_debug"
    else:
        name = "obsfate"

    return templatekw.showlist(name, values, args, separator=' + ')

# 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)

    pathscache = {}

    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.
                            pathscache.setdefault(suc, []).append((current, mark))
                            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], pathscache