Mercurial > evolve
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 |