Mercurial > evolve
view hgext3rd/topic/discovery.py @ 2676:10dedac0d82e
topic: also insert the extra head check with using the new head checking
This will get the protection in place in all cases.
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Wed, 28 Jun 2017 17:28:56 +0200 |
parents | 304232cc14b6 |
children | b4824e169f18 |
line wrap: on
line source
from __future__ import absolute_import import weakref from mercurial.i18n import _ from mercurial import ( bundle2, discovery, error, exchange, extensions, util, wireproto, ) def _headssummary(orig, *args): # In mercurial < 4.2, we receive repo, remote and outgoing as arguments if len(args) == 3: pushoparg = False repo, remote, outgoing = args # In mercurial > 4.3, we receive the pushop as arguments elif len(args) == 1: pushoparg = True pushop = args[0] repo = pushop.repo.unfiltered() remote = pushop.remote else: msg = 'topic-ext _headssummary() takes 1 or 3 arguments (%d given)' raise TypeError(msg % len(args)) publishing = ('phases' not in remote.listkeys('namespaces') or bool(remote.listkeys('phases').get('publishing', False))) if publishing or not remote.capable('topics'): return orig(*args) 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 oldrepo = repo.__class__ try: repo.__class__ = repocls unxx = repo.filtered('unfiltered-topic') repo.unfiltered = lambda: unxx if pushoparg: try: pushop.repo = repo summary = orig(pushop) finally: pushop.repo = repo else: 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][0]], ) + value[1:] return summary finally: if 'unfiltered' in vars(repo): del repo.unfiltered repo.__class__ = oldrepo def wireprotobranchmap(orig, repo, proto): oldrepo = repo.__class__ 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): """This is used to check for new heads when publishing changeset""" 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): checktypes = ('check:heads', 'check:updated-heads') hascheck = any(p.type in checktypes for p in bundler._parts) if not hascheck and pushop.outdatedphases: exchange._pushb2ctxcheckheads(pushop, bundler) return orig(pushop, bundler) def wireprotocaps(orig, repo, proto): caps = orig(repo, proto) if repo.peer().capable('topics'): caps.append('topics') return caps def modsetup(ui): """run at uisetup time to install all destinations wrapping""" extensions.wrapfunction(discovery, '_headssummary', _headssummary) extensions.wrapfunction(wireproto, 'branchmap', wireprotobranchmap) extensions.wrapfunction(wireproto, '_capabilities', wireprotocaps) # we need a proper wrap b2 part stuff extensions.wrapfunction(bundle2, 'handlecheckheads', handlecheckheads) bundle2.handlecheckheads.params = frozenset() bundle2.parthandlermapping['check:heads'] = bundle2.handlecheckheads if util.safehasattr(bundle2, 'handlecheckupdatedheads'): # we still need a proper wrap b2 part stuff extensions.wrapfunction(bundle2, 'handlecheckupdatedheads', handlecheckheads) bundle2.handlecheckupdatedheads.params = frozenset() bundle2.parthandlermapping['check:updated-heads'] = bundle2.handlecheckupdatedheads extensions.wrapfunction(exchange, '_pushb2phases', _pushb2phases) exchange.b2partsgenmapping['phase'] = exchange._pushb2phases