Show More
@@ -24,8 +24,7 b' which contain an extra attribute `_vcs_k' | |||||
24 | different error conditions. |
|
24 | different error conditions. | |
25 | """ |
|
25 | """ | |
26 |
|
26 | |||
27 | import functools |
|
27 | from pyramid.httpexceptions import HTTPLocked, HTTPForbidden | |
28 | from pyramid.httpexceptions import HTTPLocked |
|
|||
29 |
|
28 | |||
30 |
|
29 | |||
31 | def _make_exception(kind, org_exc, *args): |
|
30 | def _make_exception(kind, org_exc, *args): | |
@@ -71,6 +70,12 b' def RepositoryLockedException(org_exc=No' | |||||
71 | return _make_exception_wrapper |
|
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 | def RequirementException(org_exc=None): |
|
79 | def RequirementException(org_exc=None): | |
75 | def _make_exception_wrapper(*args): |
|
80 | def _make_exception_wrapper(*args): | |
76 | return _make_exception('requirement', org_exc, *args) |
|
81 | return _make_exception('requirement', org_exc, *args) | |
@@ -104,3 +109,8 b' class HTTPRepoLocked(HTTPLocked):' | |||||
104 | self.code = status_code or HTTPLocked.code |
|
109 | self.code = status_code or HTTPLocked.code | |
105 | self.title = title |
|
110 | self.title = title | |
106 | super(HTTPRepoLocked, self).__init__(**kwargs) |
|
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 | if exception_class == 'HTTPLockedRC': |
|
122 | if exception_class == 'HTTPLockedRC': | |
123 | raise exceptions.RepositoryLockedException()(*result['exception_args']) |
|
123 | raise exceptions.RepositoryLockedException()(*result['exception_args']) | |
|
124 | elif exception_class == 'HTTPBranchProtected': | |||
|
125 | raise exceptions.RepositoryBranchProtectedException()(*result['exception_args']) | |||
124 | elif exception_class == 'RepositoryError': |
|
126 | elif exception_class == 'RepositoryError': | |
125 | raise exceptions.VcsException()(*result['exception_args']) |
|
127 | raise exceptions.VcsException()(*result['exception_args']) | |
126 | elif exception_class: |
|
128 | elif exception_class: | |
@@ -161,17 +163,54 b' def _extras_from_ui(ui):' | |||||
161 | return extras |
|
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 | commits = [] |
|
168 | commits = [] | |
|
169 | revs = [] | |||
167 | start = repo[node].rev() |
|
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 | ctx = repo[rev] |
|
174 | ctx = repo[rev] | |
170 | commit_id = mercurial.node.hex(ctx.node()) |
|
175 | commit_id = mercurial.node.hex(ctx.node()) | |
171 | branch = ctx.branch() |
|
176 | branch = ctx.branch() | |
172 | commits.append((commit_id, branch)) |
|
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 | def repo_size(ui, repo, **kwargs): |
|
216 | def repo_size(ui, repo, **kwargs): | |
@@ -204,15 +243,20 b' def post_pull_ssh(ui, repo, **kwargs):' | |||||
204 |
|
243 | |||
205 |
|
244 | |||
206 | def pre_push(ui, repo, node=None, **kwargs): |
|
245 | def pre_push(ui, repo, node=None, **kwargs): | |
|
246 | """ | |||
|
247 | Mercurial pre_push hook | |||
|
248 | """ | |||
207 | extras = _extras_from_ui(ui) |
|
249 | extras = _extras_from_ui(ui) | |
|
250 | detect_force_push = extras.get('detect_force_push') | |||
208 |
|
251 | |||
209 | rev_data = [] |
|
252 | rev_data = [] | |
210 | if node and kwargs.get('hooktype') == 'pretxnchangegroup': |
|
253 | if node and kwargs.get('hooktype') == 'pretxnchangegroup': | |
211 | branches = collections.defaultdict(list) |
|
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 | branches[branch].append(commit_id) |
|
257 | branches[branch].append(commit_id) | |
214 |
|
258 | |||
215 |
for branch, commits in branches. |
|
259 | for branch, commits in branches.items(): | |
216 | old_rev = kwargs.get('node_last') or commits[0] |
|
260 | old_rev = kwargs.get('node_last') or commits[0] | |
217 | rev_data.append({ |
|
261 | rev_data.append({ | |
218 | 'old_rev': old_rev, |
|
262 | 'old_rev': old_rev, | |
@@ -222,6 +266,9 b' def pre_push(ui, repo, node=None, **kwar' | |||||
222 | 'name': branch, |
|
266 | 'name': branch, | |
223 | }) |
|
267 | }) | |
224 |
|
268 | |||
|
269 | for push_ref in rev_data: | |||
|
270 | push_ref['multiple_heads'] = _heads | |||
|
271 | ||||
225 | extras['commit_ids'] = rev_data |
|
272 | extras['commit_ids'] = rev_data | |
226 | return _call_hook('pre_push', extras, HgMessageWriter(ui)) |
|
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 | def pre_push_ssh_auth(ui, repo, node=None, **kwargs): |
|
283 | def pre_push_ssh_auth(ui, repo, node=None, **kwargs): | |
|
284 | """ | |||
|
285 | Mercurial pre_push hook for SSH | |||
|
286 | """ | |||
237 | extras = _extras_from_ui(ui) |
|
287 | extras = _extras_from_ui(ui) | |
238 | if extras.get('SSH'): |
|
288 | if extras.get('SSH'): | |
239 | permission = extras['SSH_PERMISSIONS'] |
|
289 | permission = extras['SSH_PERMISSIONS'] | |
@@ -248,6 +298,9 b' def pre_push_ssh_auth(ui, repo, node=Non' | |||||
248 |
|
298 | |||
249 |
|
299 | |||
250 | def post_push(ui, repo, node, **kwargs): |
|
300 | def post_push(ui, repo, node, **kwargs): | |
|
301 | """ | |||
|
302 | Mercurial post_push hook | |||
|
303 | """ | |||
251 | extras = _extras_from_ui(ui) |
|
304 | extras = _extras_from_ui(ui) | |
252 |
|
305 | |||
253 | commit_ids = [] |
|
306 | commit_ids = [] | |
@@ -255,7 +308,8 b' def post_push(ui, repo, node, **kwargs):' | |||||
255 | bookmarks = [] |
|
308 | bookmarks = [] | |
256 | tags = [] |
|
309 | tags = [] | |
257 |
|
310 | |||
258 |
|
|
311 | commits, _heads = _rev_range_hash(repo, node) | |
|
312 | for commit_id, branch in commits: | |||
259 | commit_ids.append(commit_id) |
|
313 | commit_ids.append(commit_id) | |
260 | if branch not in branches: |
|
314 | if branch not in branches: | |
261 | branches.append(branch) |
|
315 | branches.append(branch) | |
@@ -274,6 +328,9 b' def post_push(ui, repo, node, **kwargs):' | |||||
274 |
|
328 | |||
275 |
|
329 | |||
276 | def post_push_ssh(ui, repo, node, **kwargs): |
|
330 | def post_push_ssh(ui, repo, node, **kwargs): | |
|
331 | """ | |||
|
332 | Mercurial post_push hook for SSH | |||
|
333 | """ | |||
277 | if _extras_from_ui(ui).get('SSH'): |
|
334 | if _extras_from_ui(ui).get('SSH'): | |
278 | return post_push(ui, repo, node, **kwargs) |
|
335 | return post_push(ui, repo, node, **kwargs) | |
279 | return 0 |
|
336 | return 0 | |
@@ -390,6 +447,30 b' def git_pre_receive(unused_repo_path, re' | |||||
390 | rev_data = _parse_git_ref_lines(revision_lines) |
|
447 | rev_data = _parse_git_ref_lines(revision_lines) | |
391 | if 'push' not in extras['hooks']: |
|
448 | if 'push' not in extras['hooks']: | |
392 | return 0 |
|
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 | extras['commit_ids'] = rev_data |
|
474 | extras['commit_ids'] = rev_data | |
394 | return _call_hook('pre_push', extras, GitMessageWriter()) |
|
475 | return _call_hook('pre_push', extras, GitMessageWriter()) | |
395 |
|
476 |
@@ -50,7 +50,7 b' from vcsserver import remote_wsgi, scm_a' | |||||
50 | from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT |
|
50 | from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT | |
51 | from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub |
|
51 | from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub | |
52 | from vcsserver.echo_stub.echo_app import EchoApp |
|
52 | from vcsserver.echo_stub.echo_app import EchoApp | |
53 | from vcsserver.exceptions import HTTPRepoLocked |
|
53 | from vcsserver.exceptions import HTTPRepoLocked, HTTPRepoBranchProtected | |
54 | from vcsserver.lib.exc_tracking import store_exception |
|
54 | from vcsserver.lib.exc_tracking import store_exception | |
55 | from vcsserver.server import VcsServer |
|
55 | from vcsserver.server import VcsServer | |
56 |
|
56 | |||
@@ -524,6 +524,10 b' class HTTPApplication(object):' | |||||
524 | return HTTPRepoLocked( |
|
524 | return HTTPRepoLocked( | |
525 | title=exception.message, status_code=status_code) |
|
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 | exc_info = request.exc_info |
|
531 | exc_info = request.exc_info | |
528 | store_exception(id(exc_info), exc_info) |
|
532 | store_exception(id(exc_info), exc_info) | |
529 |
|
533 |
General Comments 0
You need to be logged in to leave comments.
Login now