src/topic/discovery.py
author Pierre-Yves David <pierre-yves.david@fb.com>
Mon, 14 Mar 2016 00:15:54 +0000
changeset 1889 d9b929bcc3ad
parent 1887 68125d026b07
child 1899 b65f39791f92
permissions -rw-r--r--
topicmap: ensure that 'served' view is updated with topicmap There is multiple place that explicitly update the 'served' branchmap to ensure it stay up to date. We ensure it use the proper topicmap in this case.

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)