diff --git a/mercurial/templatekw.py b/mercurial/templatekw.py --- a/mercurial/templatekw.py +++ b/mercurial/templatekw.py @@ -449,8 +449,8 @@ def showlatesttagdistance(context, mappi @templatekeyword('changessincelatesttag', requires={'repo', 'ctx', 'cache'}) def showchangessincelatesttag(context, mapping): """Integer. All ancestors not in the latest tag.""" - mapping = mapping.copy() - mapping['tag'] = getlatesttags(context, mapping)[2][0] + tag = getlatesttags(context, mapping)[2][0] + mapping = context.overlaymap(mapping, {'tag': tag}) return _showchangessincetag(context, mapping) def _showchangessincetag(context, mapping): @@ -480,8 +480,7 @@ def showmanifest(context, mapping): return mrev = repo.manifestlog._revlog.rev(mnode) mhex = hex(mnode) - mapping = mapping.copy() - mapping.update({'rev': mrev, 'node': mhex}) + mapping = context.overlaymap(mapping, {'rev': mrev, 'node': mhex}) f = context.process('manifest', mapping) # TODO: perhaps 'ctx' should be dropped from mapping because manifest # rev and node are completely different from changeset's. diff --git a/mercurial/templater.py b/mercurial/templater.py --- a/mercurial/templater.py +++ b/mercurial/templater.py @@ -613,6 +613,13 @@ class engine(object): self._aliasmap = _aliasrules.buildmap(aliases) self._cache = {} # key: (func, data) + def overlaymap(self, origmapping, newmapping): + """Create combined mapping from the original mapping and partial + mapping to override the original""" + mapping = origmapping.copy() + mapping.update(newmapping) + return mapping + def symbol(self, mapping, key): """Resolve symbol to value or function; None if nothing found""" v = None diff --git a/mercurial/templateutil.py b/mercurial/templateutil.py --- a/mercurial/templateutil.py +++ b/mercurial/templateutil.py @@ -202,8 +202,8 @@ def _showcompatlist(context, mapping, na startname = 'start_' + plural if context.preload(startname): yield context.process(startname, mapping) - vmapping = mapping.copy() def one(v, tag=name): + vmapping = {} try: vmapping.update(v) # Python 2 raises ValueError if the type of v is wrong. Python @@ -216,6 +216,7 @@ def _showcompatlist(context, mapping, na vmapping[a] = b except (TypeError, ValueError): vmapping[name] = v + vmapping = context.overlaymap(mapping, vmapping) return context.process(tag, vmapping) lastname = 'last_' + name if context.preload(lastname): @@ -399,10 +400,9 @@ def runmap(context, mapping, data): raise error.ParseError(_("%r is not iterable") % d) for i, v in enumerate(diter): - lm = mapping.copy() - lm['index'] = i if isinstance(v, dict): - lm.update(v) + lm = context.overlaymap(mapping, v) + lm['index'] = i lm['originalnode'] = mapping.get('node') yield evalrawexp(context, lm, targ) else: @@ -415,8 +415,7 @@ def runmember(context, mapping, data): darg, memb = data d = evalrawexp(context, mapping, darg) if util.safehasattr(d, 'tomap'): - lm = mapping.copy() - lm.update(d.tomap()) + lm = context.overlaymap(mapping, d.tomap()) return runsymbol(context, lm, memb) if util.safehasattr(d, 'get'): return getdictitem(d, memb)