##// END OF EJS Templates
run-tests: cache hghave results...
run-tests: cache hghave results Spawning a process on Windows is expensive. I've got a version of test-lfs-test-server.t locally which prints the http request/responses that totals 819 lines, with 149 conditional lines, 11 #if tests, and 2 test cases. It takes just under 1 minute with this change to run both cases, vs just over 2 minutes without this change. Worse, when I explored adding ui.debug to the test, it takes 13 minutes due to all of the mismatches and retests, vs less than 1 minute with this change. Overall, the difference when running all tests is negligible- 103 minutes with this change, vs 105 without when using -j9. It also looks like an exit value of 2 from `hghave` is treated specially, but there's nothing preventing 2 missing features from also using this value.

File last commit:

r36458:bb852a52 default
r36480:5c1cea8a default
Show More
remotenames.py
301 lines | 9.9 KiB | text/x-python | PythonLexer
Pulkit Goyal
remotenames: introduce class to encapsulate remotenames info in an extension...
r36077 # remotenames.py - extension to display remotenames
#
# Copyright 2017 Augie Fackler <raf@durin42.com>
# Copyright 2017 Sean Farley <sean@farley.io>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Pulkit Goyal
remotenames: add new namespaces for remotebookmarks and remotebranches...
r36079 """ showing remotebookmarks and remotebranches in UI
By default both remotebookmarks and remotebranches are turned on. Config knob to
control the individually are as follows.
Config options to tweak the default behaviour:
remotenames.bookmarks
Boolean value to enable or disable showing of remotebookmarks
remotenames.branches
Boolean value to enable or disable showing of remotebranches
"""
Pulkit Goyal
remotenames: introduce class to encapsulate remotenames info in an extension...
r36077
from __future__ import absolute_import
Augie Fackler
remotenames: port partway to python3 by using collections.MutableMapping...
r36266 import collections
Pulkit Goyal
remotenames: introduce a class to lazily resolve remotnames...
r36078
Pulkit Goyal
remotenames: add three new revsets related to remotenames...
r36167 from mercurial.i18n import _
Pulkit Goyal
remotenames: introduce a class to lazily resolve remotnames...
r36078 from mercurial.node import (
bin,
)
Pulkit Goyal
remotenames: introduce class to encapsulate remotenames info in an extension...
r36077 from mercurial import (
logexchange,
Pulkit Goyal
remotenames: add new namespaces for remotebookmarks and remotebranches...
r36079 namespaces,
Pulkit Goyal
remotenames: introduce new template keywords for remotenames...
r36080 pycompat,
Pulkit Goyal
remotenames: add new namespaces for remotebookmarks and remotebranches...
r36079 registrar,
Pulkit Goyal
remotenames: add three new revsets related to remotenames...
r36167 revsetlang,
smartset,
Pulkit Goyal
remotenames: introduce new template keywords for remotenames...
r36080 templatekw,
Pulkit Goyal
remotenames: introduce class to encapsulate remotenames info in an extension...
r36077 )
# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
# be specifying the version(s) of Mercurial they are tested with, or
# leave the attribute unspecified.
testedwith = 'ships-with-hg-core'
Pulkit Goyal
remotenames: add new namespaces for remotebookmarks and remotebranches...
r36079 configtable = {}
configitem = registrar.configitem(configtable)
Pulkit Goyal
remotenames: introduce new template keywords for remotenames...
r36080 templatekeyword = registrar.templatekeyword()
Pulkit Goyal
remotenames: add three new revsets related to remotenames...
r36167 revsetpredicate = registrar.revsetpredicate()
Pulkit Goyal
remotenames: add new namespaces for remotebookmarks and remotebranches...
r36079
configitem('remotenames', 'bookmarks',
default=True,
)
configitem('remotenames', 'branches',
default=True,
)
Augie Fackler
remotenames: port partway to python3 by using collections.MutableMapping...
r36266 class lazyremotenamedict(collections.MutableMapping):
Pulkit Goyal
remotenames: introduce a class to lazily resolve remotnames...
r36078 """
Read-only dict-like Class to lazily resolve remotename entries
We are doing that because remotenames startup was slow.
We lazily read the remotenames file once to figure out the potential entries
and store them in self.potentialentries. Then when asked to resolve an
entry, if it is not in self.potentialentries, then it isn't there, if it
is in self.potentialentries we resolve it and store the result in
self.cache. We cannot be lazy is when asked all the entries (keys).
"""
def __init__(self, kind, repo):
self.cache = {}
self.potentialentries = {}
self._kind = kind # bookmarks or branches
self._repo = repo
self.loaded = False
def _load(self):
""" Read the remotenames file, store entries matching selected kind """
self.loaded = True
repo = self._repo
for node, rpath, rname in logexchange.readremotenamefile(repo,
self._kind):
name = rpath + '/' + rname
self.potentialentries[name] = (node, rpath, name)
def _resolvedata(self, potentialentry):
""" Check that the node for potentialentry exists and return it """
if not potentialentry in self.potentialentries:
return None
node, remote, name = self.potentialentries[potentialentry]
repo = self._repo
binnode = bin(node)
# if the node doesn't exist, skip it
try:
repo.changelog.rev(binnode)
except LookupError:
return None
# Skip closed branches
if (self._kind == 'branches' and repo[binnode].closesbranch()):
return None
return [binnode]
def __getitem__(self, key):
if not self.loaded:
self._load()
val = self._fetchandcache(key)
if val is not None:
return val
else:
raise KeyError()
Augie Fackler
remotenames: port partway to python3 by using collections.MutableMapping...
r36266 def __iter__(self):
return iter(self.potentialentries)
def __len__(self):
return len(self.potentialentries)
def __setitem__(self):
raise NotImplementedError
def __delitem__(self):
raise NotImplementedError
Pulkit Goyal
remotenames: introduce a class to lazily resolve remotnames...
r36078 def _fetchandcache(self, key):
if key in self.cache:
return self.cache[key]
val = self._resolvedata(key)
if val is not None:
self.cache[key] = val
return val
else:
return None
def keys(self):
""" Get a list of bookmark or branch names """
if not self.loaded:
self._load()
return self.potentialentries.keys()
def iteritems(self):
""" Iterate over (name, node) tuples """
if not self.loaded:
self._load()
for k, vtup in self.potentialentries.iteritems():
yield (k, [bin(vtup[0])])
Pulkit Goyal
remotenames: introduce class to encapsulate remotenames info in an extension...
r36077 class remotenames(dict):
"""
This class encapsulates all the remotenames state. It also contains
Pulkit Goyal
remotenames: introduce a class to lazily resolve remotnames...
r36078 methods to access that state in convenient ways. Remotenames are lazy
loaded. Whenever client code needs to ensure the freshest copy of
remotenames, use the `clearnames` method to force an eventual load.
Pulkit Goyal
remotenames: introduce class to encapsulate remotenames info in an extension...
r36077 """
def __init__(self, repo, *args):
dict.__init__(self, *args)
self._repo = repo
Pulkit Goyal
remotenames: introduce a class to lazily resolve remotnames...
r36078 self.clearnames()
Pulkit Goyal
remotenames: introduce class to encapsulate remotenames info in an extension...
r36077
def clearnames(self):
""" Clear all remote names state """
Pulkit Goyal
remotenames: introduce a class to lazily resolve remotnames...
r36078 self['bookmarks'] = lazyremotenamedict("bookmarks", self._repo)
self['branches'] = lazyremotenamedict("branches", self._repo)
Pulkit Goyal
remotenames: introduce class to encapsulate remotenames info in an extension...
r36077 self._invalidatecache()
def _invalidatecache(self):
self._nodetobmarks = None
self._nodetobranch = None
def bmarktonodes(self):
return self['bookmarks']
def nodetobmarks(self):
if not self._nodetobmarks:
bmarktonodes = self.bmarktonodes()
self._nodetobmarks = {}
for name, node in bmarktonodes.iteritems():
self._nodetobmarks.setdefault(node[0], []).append(name)
return self._nodetobmarks
def branchtonodes(self):
return self['branches']
def nodetobranch(self):
if not self._nodetobranch:
branchtonodes = self.branchtonodes()
self._nodetobranch = {}
for name, nodes in branchtonodes.iteritems():
for node in nodes:
self._nodetobranch.setdefault(node, []).append(name)
return self._nodetobranch
Pulkit Goyal
remotenames: add new namespaces for remotebookmarks and remotebranches...
r36079
def reposetup(ui, repo):
if not repo.local():
return
repo._remotenames = remotenames(repo)
ns = namespaces.namespace
if ui.configbool('remotenames', 'bookmarks'):
remotebookmarkns = ns(
'remotebookmarks',
templatename='remotebookmarks',
colorname='remotebookmark',
Pulkit Goyal
remotenames: don't use the default value of logfmt for namespaces...
r36278 logfmt='remote bookmark: %s\n',
Pulkit Goyal
remotenames: add new namespaces for remotebookmarks and remotebranches...
r36079 listnames=lambda repo: repo._remotenames.bmarktonodes().keys(),
namemap=lambda repo, name:
repo._remotenames.bmarktonodes().get(name, []),
nodemap=lambda repo, node:
repo._remotenames.nodetobmarks().get(node, []))
repo.names.addnamespace(remotebookmarkns)
if ui.configbool('remotenames', 'branches'):
remotebranchns = ns(
'remotebranches',
templatename='remotebranches',
colorname='remotebranch',
Pulkit Goyal
remotenames: don't use the default value of logfmt for namespaces...
r36278 logfmt='remote branch: %s\n',
Pulkit Goyal
remotenames: add new namespaces for remotebookmarks and remotebranches...
r36079 listnames = lambda repo: repo._remotenames.branchtonodes().keys(),
namemap = lambda repo, name:
repo._remotenames.branchtonodes().get(name, []),
nodemap = lambda repo, node:
repo._remotenames.nodetobranch().get(node, []))
repo.names.addnamespace(remotebranchns)
Pulkit Goyal
remotenames: introduce new template keywords for remotenames...
r36080
@templatekeyword('remotenames')
def remotenameskw(**args):
Yuya Nishihara
remotenames: drop redundant templatekw names from help text...
r36458 """List of strings. Remote names associated with the changeset."""
Pulkit Goyal
remotenames: introduce new template keywords for remotenames...
r36080 args = pycompat.byteskwargs(args)
repo, ctx = args['repo'], args['ctx']
remotenames = []
if 'remotebookmarks' in repo.names:
remotenames = repo.names['remotebookmarks'].names(repo, ctx.node())
if 'remotebranches' in repo.names:
remotenames += repo.names['remotebranches'].names(repo, ctx.node())
return templatekw.showlist('remotename', remotenames, args,
plural='remotenames')
@templatekeyword('remotebookmarks')
def remotebookmarkskw(**args):
Yuya Nishihara
remotenames: drop redundant templatekw names from help text...
r36458 """List of strings. Remote bookmarks associated with the changeset."""
Pulkit Goyal
remotenames: introduce new template keywords for remotenames...
r36080 args = pycompat.byteskwargs(args)
repo, ctx = args['repo'], args['ctx']
remotebmarks = []
if 'remotebookmarks' in repo.names:
remotebmarks = repo.names['remotebookmarks'].names(repo, ctx.node())
return templatekw.showlist('remotebookmark', remotebmarks, args,
plural='remotebookmarks')
@templatekeyword('remotebranches')
def remotebrancheskw(**args):
Yuya Nishihara
remotenames: drop redundant templatekw names from help text...
r36458 """List of strings. Remote branches associated with the changeset."""
Pulkit Goyal
remotenames: introduce new template keywords for remotenames...
r36080 args = pycompat.byteskwargs(args)
repo, ctx = args['repo'], args['ctx']
remotebranches = []
if 'remotebranches' in repo.names:
remotebranches = repo.names['remotebranches'].names(repo, ctx.node())
return templatekw.showlist('remotebranch', remotebranches, args,
plural='remotebranches')
Pulkit Goyal
remotenames: add three new revsets related to remotenames...
r36167
def _revsetutil(repo, subset, x, rtypes):
"""utility function to return a set of revs based on the rtypes"""
revs = set()
cl = repo.changelog
for rtype in rtypes:
if rtype in repo.names:
ns = repo.names[rtype]
for name in ns.listnames(repo):
revs.update(ns.nodes(repo, name))
results = (cl.rev(n) for n in revs if cl.hasnode(n))
return subset & smartset.baseset(sorted(results))
@revsetpredicate('remotenames()')
def remotenamesrevset(repo, subset, x):
"""All changesets which have a remotename on them."""
revsetlang.getargs(x, 0, 0, _("remotenames takes no arguments"))
return _revsetutil(repo, subset, x, ('remotebookmarks', 'remotebranches'))
@revsetpredicate('remotebranches()')
def remotebranchesrevset(repo, subset, x):
"""All changesets which are branch heads on remotes."""
revsetlang.getargs(x, 0, 0, _("remotebranches takes no arguments"))
return _revsetutil(repo, subset, x, ('remotebranches',))
@revsetpredicate('remotebookmarks()')
def remotebmarksrevset(repo, subset, x):
"""All changesets which have bookmarks on remotes."""
revsetlang.getargs(x, 0, 0, _("remotebookmarks takes no arguments"))
return _revsetutil(repo, subset, x, ('remotebookmarks',))