comparison src/topic/discovery.py @ 1887:68125d026b07

push: hackish handeling of new branch head from phase move The current head checking mechanism is not expecting "head change" from phase movement. Topic allows that, changeset with a topic moving to public can create a new head. We introduce a hack to double check that no head were added at the transaction level to work around this.
author Pierre-Yves David <pierre-yves.david@fb.com>
date Sat, 12 Mar 2016 18:42:16 +0000
parents 0504e76bfbd9
children b65f39791f92
comparison
equal deleted inserted replaced
1886:0504e76bfbd9 1887:68125d026b07
1 import weakref
1 from mercurial import branchmap 2 from mercurial import branchmap
3 from mercurial import error
4 from mercurial import exchange
5 from mercurial.i18n import _
2 from . import topicmap 6 from . import topicmap
3 7
4 def _headssummary(orig, repo, remote, outgoing): 8 def _headssummary(orig, repo, remote, outgoing):
5 publishing = ('phases' not in remote.listkeys('namespaces') 9 publishing = ('phases' not in remote.listkeys('namespaces')
6 or bool(remote.listkeys('phases').get('publishing', False))) 10 or bool(remote.listkeys('phases').get('publishing', False)))
47 return super(repocls, self).branchmap(topic=usetopic) 51 return super(repocls, self).branchmap(topic=usetopic)
48 repo.__class__ = repocls 52 repo.__class__ = repocls
49 return orig(repo, proto) 53 return orig(repo, proto)
50 finally: 54 finally:
51 repo.__class__ = oldrepo 55 repo.__class__ = oldrepo
56
57
58 # Discovery have deficiency around phases, branch can get new heads with pure
59 # phases change. This happened with a changeset was allowed to be pushed
60 # because it had a topic, but it later become public and create a new branch
61 # head.
62 #
63 # Handle this by doing an extra check for new head creation server side
64 def _nbheads(repo):
65 data = {}
66 for b in repo.branchmap().iterbranches():
67 if ':' in b[0]:
68 continue
69 data[b[0]] = len(b[1])
70 return data
71
72 def handlecheckheads(orig, op, inpart):
73 orig(op, inpart)
74 if op.repo.publishing():
75 return
76 tr = op.gettransaction()
77 if tr.hookargs['source'] not in ('push', 'serve'): # not a push
78 return
79 tr._prepushheads = _nbheads(op.repo)
80 reporef = weakref.ref(op.repo)
81 oldvalidator = tr.validator
82 def validator(tr):
83 repo = reporef()
84 if repo is not None:
85 repo.invalidatecaches()
86 finalheads = _nbheads(repo)
87 for branch, oldnb in tr._prepushheads.iteritems():
88 newnb = finalheads.pop(branch, 0)
89 if oldnb < newnb:
90 msg = _('push create a new head on branch "%s"' % branch)
91 raise error.Abort(msg)
92 for branch, newnb in finalheads.iteritems():
93 if 1 < newnb:
94 msg = _('push create more than 1 head on new branch "%s"' % branch)
95 raise error.Abort(msg)
96 return oldvalidator(tr)
97 tr.validator = validator
98 handlecheckheads.params = frozenset()
99
100 def _pushb2phases(orig, pushop, bundler):
101 hascheck = any(p.type == 'check:heads' for p in bundler._parts)
102 if pushop.outdatedphases and not hascheck:
103 exchange._pushb2ctxcheckheads(pushop, bundler)
104 return orig(pushop, bundler)
105
106
107