Show More
@@ -24,8 +24,7 b' which contain an extra attribute `_vcs_k' | |||
|
24 | 24 | different error conditions. |
|
25 | 25 | """ |
|
26 | 26 | |
|
27 | import functools | |
|
28 | from pyramid.httpexceptions import HTTPLocked | |
|
27 | from pyramid.httpexceptions import HTTPLocked, HTTPForbidden | |
|
29 | 28 | |
|
30 | 29 | |
|
31 | 30 | def _make_exception(kind, org_exc, *args): |
@@ -71,6 +70,12 b' def RepositoryLockedException(org_exc=No' | |||
|
71 | 70 | return _make_exception_wrapper |
|
72 | 71 | |
|
73 | 72 | |
|
73 | def RepositoryBranchProtectedException(org_exc=None): | |
|
74 | def _make_exception_wrapper(*args): | |
|
75 | return _make_exception('repo_branch_protected', org_exc, *args) | |
|
76 | return _make_exception_wrapper | |
|
77 | ||
|
78 | ||
|
74 | 79 | def RequirementException(org_exc=None): |
|
75 | 80 | def _make_exception_wrapper(*args): |
|
76 | 81 | return _make_exception('requirement', org_exc, *args) |
@@ -104,3 +109,8 b' class HTTPRepoLocked(HTTPLocked):' | |||
|
104 | 109 | self.code = status_code or HTTPLocked.code |
|
105 | 110 | self.title = title |
|
106 | 111 | super(HTTPRepoLocked, self).__init__(**kwargs) |
|
112 | ||
|
113 | ||
|
114 | class HTTPRepoBranchProtected(HTTPForbidden): | |
|
115 | def __init__(self, *args, **kwargs): | |
|
116 | super(HTTPForbidden, self).__init__(*args, **kwargs) |
@@ -121,6 +121,8 b' def _handle_exception(result):' | |||
|
121 | 121 | |
|
122 | 122 | if exception_class == 'HTTPLockedRC': |
|
123 | 123 | raise exceptions.RepositoryLockedException()(*result['exception_args']) |
|
124 | elif exception_class == 'HTTPBranchProtected': | |
|
125 | raise exceptions.RepositoryBranchProtectedException()(*result['exception_args']) | |
|
124 | 126 | elif exception_class == 'RepositoryError': |
|
125 | 127 | raise exceptions.VcsException()(*result['exception_args']) |
|
126 | 128 | elif exception_class: |
@@ -161,17 +163,54 b' def _extras_from_ui(ui):' | |||
|
161 | 163 | return extras |
|
162 | 164 | |
|
163 | 165 | |
|
164 | def _rev_range_hash(repo, node): | |
|
166 | def _rev_range_hash(repo, node, check_heads=False): | |
|
165 | 167 | |
|
166 | 168 | commits = [] |
|
169 | revs = [] | |
|
167 | 170 | start = repo[node].rev() |
|
168 | for rev in xrange(start, len(repo)): | |
|
171 | end = len(repo) | |
|
172 | for rev in range(start, end): | |
|
173 | revs.append(rev) | |
|
169 | 174 | ctx = repo[rev] |
|
170 | 175 | commit_id = mercurial.node.hex(ctx.node()) |
|
171 | 176 | branch = ctx.branch() |
|
172 | 177 | commits.append((commit_id, branch)) |
|
173 | 178 | |
|
174 | return commits | |
|
179 | parent_heads = [] | |
|
180 | if check_heads: | |
|
181 | parent_heads = _check_heads(repo, start, end, revs) | |
|
182 | return commits, parent_heads | |
|
183 | ||
|
184 | ||
|
185 | def _check_heads(repo, start, end, commits): | |
|
186 | changelog = repo.changelog | |
|
187 | parents = set() | |
|
188 | ||
|
189 | for new_rev in commits: | |
|
190 | for p in changelog.parentrevs(new_rev): | |
|
191 | if p == mercurial.node.nullrev: | |
|
192 | continue | |
|
193 | if p < start: | |
|
194 | parents.add(p) | |
|
195 | ||
|
196 | for p in parents: | |
|
197 | branch = repo[p].branch() | |
|
198 | # The heads descending from that parent, on the same branch | |
|
199 | parent_heads = set([p]) | |
|
200 | reachable = set([p]) | |
|
201 | for x in xrange(p + 1, end): | |
|
202 | if repo[x].branch() != branch: | |
|
203 | continue | |
|
204 | for pp in changelog.parentrevs(x): | |
|
205 | if pp in reachable: | |
|
206 | reachable.add(x) | |
|
207 | parent_heads.discard(pp) | |
|
208 | parent_heads.add(x) | |
|
209 | # More than one head? Suggest merging | |
|
210 | if len(parent_heads) > 1: | |
|
211 | return list(parent_heads) | |
|
212 | ||
|
213 | return [] | |
|
175 | 214 | |
|
176 | 215 | |
|
177 | 216 | def repo_size(ui, repo, **kwargs): |
@@ -204,15 +243,20 b' def post_pull_ssh(ui, repo, **kwargs):' | |||
|
204 | 243 | |
|
205 | 244 | |
|
206 | 245 | def pre_push(ui, repo, node=None, **kwargs): |
|
246 | """ | |
|
247 | Mercurial pre_push hook | |
|
248 | """ | |
|
207 | 249 | extras = _extras_from_ui(ui) |
|
250 | detect_force_push = extras.get('detect_force_push') | |
|
208 | 251 | |
|
209 | 252 | rev_data = [] |
|
210 | 253 | if node and kwargs.get('hooktype') == 'pretxnchangegroup': |
|
211 | 254 | branches = collections.defaultdict(list) |
|
212 |
|
|
|
255 | commits, _heads = _rev_range_hash(repo, node, check_heads=detect_force_push) | |
|
256 | for commit_id, branch in commits: | |
|
213 | 257 | branches[branch].append(commit_id) |
|
214 | 258 | |
|
215 |
for branch, commits in branches. |
|
|
259 | for branch, commits in branches.items(): | |
|
216 | 260 | old_rev = kwargs.get('node_last') or commits[0] |
|
217 | 261 | rev_data.append({ |
|
218 | 262 | 'old_rev': old_rev, |
@@ -222,6 +266,9 b' def pre_push(ui, repo, node=None, **kwar' | |||
|
222 | 266 | 'name': branch, |
|
223 | 267 | }) |
|
224 | 268 | |
|
269 | for push_ref in rev_data: | |
|
270 | push_ref['multiple_heads'] = _heads | |
|
271 | ||
|
225 | 272 | extras['commit_ids'] = rev_data |
|
226 | 273 | return _call_hook('pre_push', extras, HgMessageWriter(ui)) |
|
227 | 274 | |
@@ -234,6 +281,9 b' def pre_push_ssh(ui, repo, node=None, **' | |||
|
234 | 281 | |
|
235 | 282 | |
|
236 | 283 | def pre_push_ssh_auth(ui, repo, node=None, **kwargs): |
|
284 | """ | |
|
285 | Mercurial pre_push hook for SSH | |
|
286 | """ | |
|
237 | 287 | extras = _extras_from_ui(ui) |
|
238 | 288 | if extras.get('SSH'): |
|
239 | 289 | permission = extras['SSH_PERMISSIONS'] |
@@ -248,6 +298,9 b' def pre_push_ssh_auth(ui, repo, node=Non' | |||
|
248 | 298 | |
|
249 | 299 | |
|
250 | 300 | def post_push(ui, repo, node, **kwargs): |
|
301 | """ | |
|
302 | Mercurial post_push hook | |
|
303 | """ | |
|
251 | 304 | extras = _extras_from_ui(ui) |
|
252 | 305 | |
|
253 | 306 | commit_ids = [] |
@@ -255,7 +308,8 b' def post_push(ui, repo, node, **kwargs):' | |||
|
255 | 308 | bookmarks = [] |
|
256 | 309 | tags = [] |
|
257 | 310 | |
|
258 |
|
|
|
311 | commits, _heads = _rev_range_hash(repo, node) | |
|
312 | for commit_id, branch in commits: | |
|
259 | 313 | commit_ids.append(commit_id) |
|
260 | 314 | if branch not in branches: |
|
261 | 315 | branches.append(branch) |
@@ -274,6 +328,9 b' def post_push(ui, repo, node, **kwargs):' | |||
|
274 | 328 | |
|
275 | 329 | |
|
276 | 330 | def post_push_ssh(ui, repo, node, **kwargs): |
|
331 | """ | |
|
332 | Mercurial post_push hook for SSH | |
|
333 | """ | |
|
277 | 334 | if _extras_from_ui(ui).get('SSH'): |
|
278 | 335 | return post_push(ui, repo, node, **kwargs) |
|
279 | 336 | return 0 |
@@ -390,6 +447,30 b' def git_pre_receive(unused_repo_path, re' | |||
|
390 | 447 | rev_data = _parse_git_ref_lines(revision_lines) |
|
391 | 448 | if 'push' not in extras['hooks']: |
|
392 | 449 | return 0 |
|
450 | empty_commit_id = '0' * 40 | |
|
451 | ||
|
452 | detect_force_push = extras.get('detect_force_push') | |
|
453 | ||
|
454 | for push_ref in rev_data: | |
|
455 | push_ref['pruned_sha'] = '' | |
|
456 | if not detect_force_push: | |
|
457 | # don't check for forced-push when we don't need to | |
|
458 | continue | |
|
459 | ||
|
460 | type_ = push_ref['type'] | |
|
461 | new_branch = push_ref['old_rev'] == empty_commit_id | |
|
462 | if type_ == 'heads' and not new_branch: | |
|
463 | old_rev = push_ref['old_rev'] | |
|
464 | new_rev = push_ref['new_rev'] | |
|
465 | cmd = [settings.GIT_EXECUTABLE, 'rev-list', | |
|
466 | old_rev, '^{}'.format(new_rev)] | |
|
467 | stdout, stderr = subprocessio.run_command( | |
|
468 | cmd, env=os.environ.copy()) | |
|
469 | # means we're having some non-reachable objects, this forced push | |
|
470 | # was used | |
|
471 | if stdout: | |
|
472 | push_ref['pruned_sha'] = stdout.splitlines() | |
|
473 | ||
|
393 | 474 | extras['commit_ids'] = rev_data |
|
394 | 475 | return _call_hook('pre_push', extras, GitMessageWriter()) |
|
395 | 476 |
@@ -50,7 +50,7 b' from vcsserver import remote_wsgi, scm_a' | |||
|
50 | 50 | from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT |
|
51 | 51 | from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub |
|
52 | 52 | from vcsserver.echo_stub.echo_app import EchoApp |
|
53 | from vcsserver.exceptions import HTTPRepoLocked | |
|
53 | from vcsserver.exceptions import HTTPRepoLocked, HTTPRepoBranchProtected | |
|
54 | 54 | from vcsserver.lib.exc_tracking import store_exception |
|
55 | 55 | from vcsserver.server import VcsServer |
|
56 | 56 | |
@@ -524,6 +524,10 b' class HTTPApplication(object):' | |||
|
524 | 524 | return HTTPRepoLocked( |
|
525 | 525 | title=exception.message, status_code=status_code) |
|
526 | 526 | |
|
527 | elif _vcs_kind == 'repo_branch_protected': | |
|
528 | # Get custom repo-branch-protected status code if present. | |
|
529 | return HTTPRepoBranchProtected(title=exception.message) | |
|
530 | ||
|
527 | 531 | exc_info = request.exc_info |
|
528 | 532 | store_exception(id(exc_info), exc_info) |
|
529 | 533 |
General Comments 0
You need to be logged in to leave comments.
Login now