##// END OF EJS Templates
hooks: adjust to handle protected branch cases, and force push.
marcink -
r509:1db1b667 default
parent child Browse files
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 for commit_id, branch in _rev_range_hash(repo, node):
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.iteritems():
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 for commit_id, branch in _rev_range_hash(repo, node):
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