Mercurial > public > mercurial-scm > hg-stable
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 |