Mercurial > evolve
view hgext3rd/evolve/templatekw.py @ 2591:1991935fb603
obsfate: add a new obsfate template
The obsfate template display for each obsolete changeset a line summarizing
what changed between the changeset and its successors.
This dict is computed in obshistory._preparesuccessorset. It uses
obshistory.FORMATSSETSFUNCTIONS which is a list of function that
individually compute a part of each dict. You can override fields or add new
ones by adding your own function in this list.
The format of obsfate is computed in templatekw.obsfatedefaulttempl and can be
wrapped if necessary, the code is not quite extendable for the moment but can
be refactored later.
author | Boris Feld <boris.feld@octobus.net> |
---|---|
date | Fri, 09 Jun 2017 00:52:54 +0100 |
parents | 8ac4ceac5d96 |
children | a3fbe5293bf6 df4a1b02308f |
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