topicmap: write and read format from disc
To prevent too awful performance we allow writing and reading topicmap cache.
This is done with a lot of code duplication from core because core is not
extensible enough.
import weakref
from mercurial import branchmap
from mercurial import error
from mercurial import exchange
from mercurial.i18n import _
from . import topicmap
def _headssummary(orig, repo, remote, outgoing):
publishing = ('phases' not in remote.listkeys('namespaces')
or bool(remote.listkeys('phases').get('publishing', False)))
if publishing:
return orig(repo, remote, outgoing)
oldgetitem = repo.__getitem__
oldrepo = repo.__class__
oldbranchcache = branchmap.branchcache
oldfilename = branchmap._filename
try:
class repocls(repo.__class__):
def __getitem__(self, key):
ctx = super(repocls, self).__getitem__(key)
oldbranch = ctx.branch
def branch():
branch = oldbranch()
topic = ctx.topic()
if topic:
branch = "%s:%s" % (branch, topic)
return branch
ctx.branch = branch
return ctx
repo.__class__ = repocls
branchmap.branchcache = topicmap.topiccache
branchmap._filename = topicmap._filename
summary = orig(repo, remote, outgoing)
for key, value in summary.iteritems():
if ':' in key: # This is a topic
if value[0] is None and value[1]:
summary[key] = ([value[1].pop(0)], ) + value[1:]
return summary
finally:
repo.__class__ = oldrepo
branchmap.branchcache = oldbranchcache
branchmap._filename = oldfilename
def wireprotobranchmap(orig, repo, proto):
oldrepo = repo.__class__
print repo
try:
class repocls(repo.__class__):
def branchmap(self):
usetopic = not self.publishing()
return super(repocls, self).branchmap(topic=usetopic)
repo.__class__ = repocls
return orig(repo, proto)
finally:
repo.__class__ = oldrepo
# Discovery have deficiency around phases, branch can get new heads with pure
# phases change. This happened with a changeset was allowed to be pushed
# because it had a topic, but it later become public and create a new branch
# head.
#
# Handle this by doing an extra check for new head creation server side
def _nbheads(repo):
data = {}
for b in repo.branchmap().iterbranches():
if ':' in b[0]:
continue
data[b[0]] = len(b[1])
return data
def handlecheckheads(orig, op, inpart):
orig(op, inpart)
if op.repo.publishing():
return
tr = op.gettransaction()
if tr.hookargs['source'] not in ('push', 'serve'): # not a push
return
tr._prepushheads = _nbheads(op.repo)
reporef = weakref.ref(op.repo)
oldvalidator = tr.validator
def validator(tr):
repo = reporef()
if repo is not None:
repo.invalidatecaches()
finalheads = _nbheads(repo)
for branch, oldnb in tr._prepushheads.iteritems():
newnb = finalheads.pop(branch, 0)
if oldnb < newnb:
msg = _('push create a new head on branch "%s"' % branch)
raise error.Abort(msg)
for branch, newnb in finalheads.iteritems():
if 1 < newnb:
msg = _('push create more than 1 head on new branch "%s"' % branch)
raise error.Abort(msg)
return oldvalidator(tr)
tr.validator = validator
handlecheckheads.params = frozenset()
def _pushb2phases(orig, pushop, bundler):
hascheck = any(p.type == 'check:heads' for p in bundler._parts)
if pushop.outdatedphases and not hascheck:
exchange._pushb2ctxcheckheads(pushop, bundler)
return orig(pushop, bundler)