comparison mercurial/namespaces.py @ 38490:4c0683655599

namespaces: let namespaces override singlenode() definition Some namespaces have multiple nodes per name (meaning that their namemap() returns multiple nodes). One such namespace is the "topics" namespace (from the evolve repo). We also have our own internal namespace at Google (for review units) that has multiple nodes per name. These namespaces may not want to use the default "pick highest revnum" resolution that we currently use when resolving a name to a single node. As an example, they may decide that `hg co <name>` should check out a commit that's last in some sense even if an earlier commit had just been amended and thus had a higher revnum [1]. This patch gives the namespace the option to continue to return multiple nodes and to override how the best node is picked. Allowing namespaces to override that may also be useful as an optimization (it may be cheaper for the namespace to find just that node). I have been arguing (in D3715) for using all the nodes returned from namemap() when resolving the symbol to a revset, so e.g. `hg log -r stable` would resolve to *all* nodes on stable, not just the one with the highest revnum (except that I don't actually think we should change it for the branch namespace because of BC). Most people seem opposed to that. If we decide not to do it, I think we can deprecate the namemap() function in favor of the new singlenode() (I find it weird to have namespaces, like the branch namespace, where namemap() isn't nodemap()'s inverse). I therefore think this patch makes sense regardless of what we decide on that issue. [1] Actually, even the branch namespace would have wanted to override singlenode() if it had supported multiple nodes. That's because closes branch heads are mostly ignored, so "hg co default" will not check out the highest-revnum node if that's a closed head. Differential Revision: https://phab.mercurial-scm.org/D3852
author Martin von Zweigbergk <martinvonz@google.com>
date Tue, 26 Jun 2018 10:02:01 -0700
parents aa97e06a1912
children 2372284d9457
comparison
equal deleted inserted replaced
38489:56b2074114b1 38490:4c0683655599
93 def generatekw(context, mapping): 93 def generatekw(context, mapping):
94 return templatekw.shownames(context, mapping, namespace.name) 94 return templatekw.shownames(context, mapping, namespace.name)
95 95
96 def singlenode(self, repo, name): 96 def singlenode(self, repo, name):
97 """ 97 """
98 Return the 'best' node for the given name. Best means the first node 98 Return the 'best' node for the given name. What's best is defined
99 in the first nonempty list returned by a name-to-nodes mapping function 99 by the namespace's singlenode() function. The first match returned by
100 in the defined precedence order. 100 a namespace in the defined precedence order is used.
101 101
102 Raises a KeyError if there is no such node. 102 Raises a KeyError if there is no such node.
103 """ 103 """
104 for ns, v in self._names.iteritems(): 104 for ns, v in self._names.iteritems():
105 n = v.namemap(repo, name) 105 n = v.singlenode(repo, name)
106 if n: 106 if n:
107 # return max revision number 107 return n
108 if len(n) > 1:
109 cl = repo.changelog
110 maxrev = max(cl.rev(node) for node in n)
111 return cl.node(maxrev)
112 return n[0]
113 raise KeyError(_('no such name: %s') % name) 108 raise KeyError(_('no such name: %s') % name)
114 109
115 class namespace(object): 110 class namespace(object):
116 """provides an interface to a namespace 111 """provides an interface to a namespace
117 112
140 Mercurial. 135 Mercurial.
141 """ 136 """
142 137
143 def __init__(self, name, templatename=None, logname=None, colorname=None, 138 def __init__(self, name, templatename=None, logname=None, colorname=None,
144 logfmt=None, listnames=None, namemap=None, nodemap=None, 139 logfmt=None, listnames=None, namemap=None, nodemap=None,
145 deprecated=None, builtin=False): 140 deprecated=None, builtin=False, singlenode=None):
146 """create a namespace 141 """create a namespace
147 142
148 name: the namespace to be registered (in plural form) 143 name: the namespace to be registered (in plural form)
149 templatename: the name to use for templating 144 templatename: the name to use for templating
150 logname: the name to use for log output; if not specified templatename 145 logname: the name to use for log output; if not specified templatename
156 listnames: function to list all names 151 listnames: function to list all names
157 namemap: function that inputs a name, output node(s) 152 namemap: function that inputs a name, output node(s)
158 nodemap: function that inputs a node, output name(s) 153 nodemap: function that inputs a node, output name(s)
159 deprecated: set of names to be masked for ordinary use 154 deprecated: set of names to be masked for ordinary use
160 builtin: whether namespace is implemented by core Mercurial 155 builtin: whether namespace is implemented by core Mercurial
156 singlenode: function that inputs a name, output best node (or None)
161 """ 157 """
162 self.name = name 158 self.name = name
163 self.templatename = templatename 159 self.templatename = templatename
164 self.logname = logname 160 self.logname = logname
165 self.colorname = colorname 161 self.colorname = colorname
166 self.logfmt = logfmt 162 self.logfmt = logfmt
167 self.listnames = listnames 163 self.listnames = listnames
168 self.namemap = namemap 164 self.namemap = namemap
169 self.nodemap = nodemap 165 self.nodemap = nodemap
166 if singlenode:
167 self.singlenode = singlenode
170 168
171 # if logname is not specified, use the template name as backup 169 # if logname is not specified, use the template name as backup
172 if self.logname is None: 170 if self.logname is None:
173 self.logname = self.templatename 171 self.logname = self.templatename
174 172
197 """method that returns a list of nodes in a namespace that 195 """method that returns a list of nodes in a namespace that
198 match a given name. 196 match a given name.
199 197
200 """ 198 """
201 return sorted(self.namemap(repo, name)) 199 return sorted(self.namemap(repo, name))
200
201 def singlenode(self, repo, name):
202 """returns the best node for the given name
203
204 By default, the best node is the node from nodes() with the highest
205 revision number. It can be overriden by the namespace."""
206 n = self.namemap(repo, name)
207 if n:
208 # return max revision number
209 if len(n) > 1:
210 cl = repo.changelog
211 maxrev = max(cl.rev(node) for node in n)
212 return cl.node(maxrev)
213 return n[0]
214 return None