##// END OF EJS Templates
hooks: fixed SVN hook problem
marcink -
r575:f7204ad7 stable
parent child Browse files
Show More
@@ -1,700 +1,702 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # RhodeCode VCSServer provides access to different vcs backends via network.
4 4 # Copyright (C) 2014-2018 RhodeCode GmbH
5 5 #
6 6 # This program is free software; you can redistribute it and/or modify
7 7 # it under the terms of the GNU General Public License as published by
8 8 # the Free Software Foundation; either version 3 of the License, or
9 9 # (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software Foundation,
18 18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 19
20 20 import io
21 21 import os
22 22 import sys
23 23 import logging
24 24 import collections
25 25 import importlib
26 26 import base64
27 27
28 28 from httplib import HTTPConnection
29 29
30 30
31 31 import mercurial.scmutil
32 32 import mercurial.node
33 33 import simplejson as json
34 34
35 35 from vcsserver import exceptions, subprocessio, settings
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39
40 40 class HooksHttpClient(object):
41 41 connection = None
42 42
43 43 def __init__(self, hooks_uri):
44 44 self.hooks_uri = hooks_uri
45 45
46 46 def __call__(self, method, extras):
47 47 connection = HTTPConnection(self.hooks_uri)
48 48 body = self._serialize(method, extras)
49 49 try:
50 50 connection.request('POST', '/', body)
51 51 except Exception:
52 52 log.error('Connection failed on %s', connection)
53 53 raise
54 54 response = connection.getresponse()
55 55 return json.loads(response.read())
56 56
57 57 def _serialize(self, hook_name, extras):
58 58 data = {
59 59 'method': hook_name,
60 60 'extras': extras
61 61 }
62 62 return json.dumps(data)
63 63
64 64
65 65 class HooksDummyClient(object):
66 66 def __init__(self, hooks_module):
67 67 self._hooks_module = importlib.import_module(hooks_module)
68 68
69 69 def __call__(self, hook_name, extras):
70 70 with self._hooks_module.Hooks() as hooks:
71 71 return getattr(hooks, hook_name)(extras)
72 72
73 73
74 74 class RemoteMessageWriter(object):
75 75 """Writer base class."""
76 76 def write(self, message):
77 77 raise NotImplementedError()
78 78
79 79
80 80 class HgMessageWriter(RemoteMessageWriter):
81 81 """Writer that knows how to send messages to mercurial clients."""
82 82
83 83 def __init__(self, ui):
84 84 self.ui = ui
85 85
86 86 def write(self, message):
87 87 # TODO: Check why the quiet flag is set by default.
88 88 old = self.ui.quiet
89 89 self.ui.quiet = False
90 90 self.ui.status(message.encode('utf-8'))
91 91 self.ui.quiet = old
92 92
93 93
94 94 class GitMessageWriter(RemoteMessageWriter):
95 95 """Writer that knows how to send messages to git clients."""
96 96
97 97 def __init__(self, stdout=None):
98 98 self.stdout = stdout or sys.stdout
99 99
100 100 def write(self, message):
101 101 self.stdout.write(message.encode('utf-8'))
102 102
103 103
104 104 class SvnMessageWriter(RemoteMessageWriter):
105 105 """Writer that knows how to send messages to svn clients."""
106 106
107 107 def __init__(self, stderr=None):
108 108 # SVN needs data sent to stderr for back-to-client messaging
109 109 self.stderr = stderr or sys.stderr
110 110
111 111 def write(self, message):
112 112 self.stderr.write(message.encode('utf-8'))
113 113
114 114
115 115 def _handle_exception(result):
116 116 exception_class = result.get('exception')
117 117 exception_traceback = result.get('exception_traceback')
118 118
119 119 if exception_traceback:
120 120 log.error('Got traceback from remote call:%s', exception_traceback)
121 121
122 122 if exception_class == 'HTTPLockedRC':
123 123 raise exceptions.RepositoryLockedException()(*result['exception_args'])
124 124 elif exception_class == 'HTTPBranchProtected':
125 125 raise exceptions.RepositoryBranchProtectedException()(*result['exception_args'])
126 126 elif exception_class == 'RepositoryError':
127 127 raise exceptions.VcsException()(*result['exception_args'])
128 128 elif exception_class:
129 129 raise Exception('Got remote exception "%s" with args "%s"' %
130 130 (exception_class, result['exception_args']))
131 131
132 132
133 133 def _get_hooks_client(extras):
134 134 if 'hooks_uri' in extras:
135 135 protocol = extras.get('hooks_protocol')
136 136 return HooksHttpClient(extras['hooks_uri'])
137 137 else:
138 138 return HooksDummyClient(extras['hooks_module'])
139 139
140 140
141 141 def _call_hook(hook_name, extras, writer):
142 142 hooks_client = _get_hooks_client(extras)
143 143 log.debug('Hooks, using client:%s', hooks_client)
144 144 result = hooks_client(hook_name, extras)
145 145 log.debug('Hooks got result: %s', result)
146 146
147 147 _handle_exception(result)
148 148 writer.write(result['output'])
149 149
150 150 return result['status']
151 151
152 152
153 153 def _extras_from_ui(ui):
154 154 hook_data = ui.config('rhodecode', 'RC_SCM_DATA')
155 155 if not hook_data:
156 156 # maybe it's inside environ ?
157 157 env_hook_data = os.environ.get('RC_SCM_DATA')
158 158 if env_hook_data:
159 159 hook_data = env_hook_data
160 160
161 161 extras = {}
162 162 if hook_data:
163 163 extras = json.loads(hook_data)
164 164 return extras
165 165
166 166
167 167 def _rev_range_hash(repo, node, check_heads=False):
168 168
169 169 commits = []
170 170 revs = []
171 171 start = repo[node].rev()
172 172 end = len(repo)
173 173 for rev in range(start, end):
174 174 revs.append(rev)
175 175 ctx = repo[rev]
176 176 commit_id = mercurial.node.hex(ctx.node())
177 177 branch = ctx.branch()
178 178 commits.append((commit_id, branch))
179 179
180 180 parent_heads = []
181 181 if check_heads:
182 182 parent_heads = _check_heads(repo, start, end, revs)
183 183 return commits, parent_heads
184 184
185 185
186 186 def _check_heads(repo, start, end, commits):
187 187 changelog = repo.changelog
188 188 parents = set()
189 189
190 190 for new_rev in commits:
191 191 for p in changelog.parentrevs(new_rev):
192 192 if p == mercurial.node.nullrev:
193 193 continue
194 194 if p < start:
195 195 parents.add(p)
196 196
197 197 for p in parents:
198 198 branch = repo[p].branch()
199 199 # The heads descending from that parent, on the same branch
200 200 parent_heads = set([p])
201 201 reachable = set([p])
202 202 for x in xrange(p + 1, end):
203 203 if repo[x].branch() != branch:
204 204 continue
205 205 for pp in changelog.parentrevs(x):
206 206 if pp in reachable:
207 207 reachable.add(x)
208 208 parent_heads.discard(pp)
209 209 parent_heads.add(x)
210 210 # More than one head? Suggest merging
211 211 if len(parent_heads) > 1:
212 212 return list(parent_heads)
213 213
214 214 return []
215 215
216 216
217 217 def _get_git_env():
218 218 env = {}
219 219 for k, v in os.environ.items():
220 220 if k.startswith('GIT'):
221 221 env[k] = v
222 222
223 223 # serialized version
224 224 return [(k, v) for k, v in env.items()]
225 225
226 226
227 227 def _get_hg_env(old_rev, new_rev, txnid, repo_path):
228 228 env = {}
229 229 for k, v in os.environ.items():
230 230 if k.startswith('HG'):
231 231 env[k] = v
232 232
233 233 env['HG_NODE'] = old_rev
234 234 env['HG_NODE_LAST'] = new_rev
235 235 env['HG_TXNID'] = txnid
236 236 env['HG_PENDING'] = repo_path
237 237
238 238 return [(k, v) for k, v in env.items()]
239 239
240 240
241 241 def repo_size(ui, repo, **kwargs):
242 242 extras = _extras_from_ui(ui)
243 243 return _call_hook('repo_size', extras, HgMessageWriter(ui))
244 244
245 245
246 246 def pre_pull(ui, repo, **kwargs):
247 247 extras = _extras_from_ui(ui)
248 248 return _call_hook('pre_pull', extras, HgMessageWriter(ui))
249 249
250 250
251 251 def pre_pull_ssh(ui, repo, **kwargs):
252 252 extras = _extras_from_ui(ui)
253 253 if extras and extras.get('SSH'):
254 254 return pre_pull(ui, repo, **kwargs)
255 255 return 0
256 256
257 257
258 258 def post_pull(ui, repo, **kwargs):
259 259 extras = _extras_from_ui(ui)
260 260 return _call_hook('post_pull', extras, HgMessageWriter(ui))
261 261
262 262
263 263 def post_pull_ssh(ui, repo, **kwargs):
264 264 extras = _extras_from_ui(ui)
265 265 if extras and extras.get('SSH'):
266 266 return post_pull(ui, repo, **kwargs)
267 267 return 0
268 268
269 269
270 270 def pre_push(ui, repo, node=None, **kwargs):
271 271 """
272 272 Mercurial pre_push hook
273 273 """
274 274 extras = _extras_from_ui(ui)
275 275 detect_force_push = extras.get('detect_force_push')
276 276
277 277 rev_data = []
278 278 if node and kwargs.get('hooktype') == 'pretxnchangegroup':
279 279 branches = collections.defaultdict(list)
280 280 commits, _heads = _rev_range_hash(repo, node, check_heads=detect_force_push)
281 281 for commit_id, branch in commits:
282 282 branches[branch].append(commit_id)
283 283
284 284 for branch, commits in branches.items():
285 285 old_rev = kwargs.get('node_last') or commits[0]
286 286 rev_data.append({
287 287 'total_commits': len(commits),
288 288 'old_rev': old_rev,
289 289 'new_rev': commits[-1],
290 290 'ref': '',
291 291 'type': 'branch',
292 292 'name': branch,
293 293 })
294 294
295 295 for push_ref in rev_data:
296 296 push_ref['multiple_heads'] = _heads
297 297
298 298 repo_path = os.path.join(
299 299 extras.get('repo_store', ''), extras.get('repository', ''))
300 300 push_ref['hg_env'] = _get_hg_env(
301 301 old_rev=push_ref['old_rev'],
302 302 new_rev=push_ref['new_rev'], txnid=kwargs.get('txnid'),
303 303 repo_path=repo_path)
304 304
305 305 extras['hook_type'] = kwargs.get('hooktype', 'pre_push')
306 306 extras['commit_ids'] = rev_data
307 307
308 308 return _call_hook('pre_push', extras, HgMessageWriter(ui))
309 309
310 310
311 311 def pre_push_ssh(ui, repo, node=None, **kwargs):
312 312 extras = _extras_from_ui(ui)
313 313 if extras.get('SSH'):
314 314 return pre_push(ui, repo, node, **kwargs)
315 315
316 316 return 0
317 317
318 318
319 319 def pre_push_ssh_auth(ui, repo, node=None, **kwargs):
320 320 """
321 321 Mercurial pre_push hook for SSH
322 322 """
323 323 extras = _extras_from_ui(ui)
324 324 if extras.get('SSH'):
325 325 permission = extras['SSH_PERMISSIONS']
326 326
327 327 if 'repository.write' == permission or 'repository.admin' == permission:
328 328 return 0
329 329
330 330 # non-zero ret code
331 331 return 1
332 332
333 333 return 0
334 334
335 335
336 336 def post_push(ui, repo, node, **kwargs):
337 337 """
338 338 Mercurial post_push hook
339 339 """
340 340 extras = _extras_from_ui(ui)
341 341
342 342 commit_ids = []
343 343 branches = []
344 344 bookmarks = []
345 345 tags = []
346 346
347 347 commits, _heads = _rev_range_hash(repo, node)
348 348 for commit_id, branch in commits:
349 349 commit_ids.append(commit_id)
350 350 if branch not in branches:
351 351 branches.append(branch)
352 352
353 353 if hasattr(ui, '_rc_pushkey_branches'):
354 354 bookmarks = ui._rc_pushkey_branches
355 355
356 356 extras['hook_type'] = kwargs.get('hooktype', 'post_push')
357 357 extras['commit_ids'] = commit_ids
358 358 extras['new_refs'] = {
359 359 'branches': branches,
360 360 'bookmarks': bookmarks,
361 361 'tags': tags
362 362 }
363 363
364 364 return _call_hook('post_push', extras, HgMessageWriter(ui))
365 365
366 366
367 367 def post_push_ssh(ui, repo, node, **kwargs):
368 368 """
369 369 Mercurial post_push hook for SSH
370 370 """
371 371 if _extras_from_ui(ui).get('SSH'):
372 372 return post_push(ui, repo, node, **kwargs)
373 373 return 0
374 374
375 375
376 376 def key_push(ui, repo, **kwargs):
377 377 if kwargs['new'] != '0' and kwargs['namespace'] == 'bookmarks':
378 378 # store new bookmarks in our UI object propagated later to post_push
379 379 ui._rc_pushkey_branches = repo[kwargs['key']].bookmarks()
380 380 return
381 381
382 382
383 383 # backward compat
384 384 log_pull_action = post_pull
385 385
386 386 # backward compat
387 387 log_push_action = post_push
388 388
389 389
390 390 def handle_git_pre_receive(unused_repo_path, unused_revs, unused_env):
391 391 """
392 392 Old hook name: keep here for backward compatibility.
393 393
394 394 This is only required when the installed git hooks are not upgraded.
395 395 """
396 396 pass
397 397
398 398
399 399 def handle_git_post_receive(unused_repo_path, unused_revs, unused_env):
400 400 """
401 401 Old hook name: keep here for backward compatibility.
402 402
403 403 This is only required when the installed git hooks are not upgraded.
404 404 """
405 405 pass
406 406
407 407
408 408 HookResponse = collections.namedtuple('HookResponse', ('status', 'output'))
409 409
410 410
411 411 def git_pre_pull(extras):
412 412 """
413 413 Pre pull hook.
414 414
415 415 :param extras: dictionary containing the keys defined in simplevcs
416 416 :type extras: dict
417 417
418 418 :return: status code of the hook. 0 for success.
419 419 :rtype: int
420 420 """
421 421 if 'pull' not in extras['hooks']:
422 422 return HookResponse(0, '')
423 423
424 424 stdout = io.BytesIO()
425 425 try:
426 426 status = _call_hook('pre_pull', extras, GitMessageWriter(stdout))
427 427 except Exception as error:
428 428 status = 128
429 429 stdout.write('ERROR: %s\n' % str(error))
430 430
431 431 return HookResponse(status, stdout.getvalue())
432 432
433 433
434 434 def git_post_pull(extras):
435 435 """
436 436 Post pull hook.
437 437
438 438 :param extras: dictionary containing the keys defined in simplevcs
439 439 :type extras: dict
440 440
441 441 :return: status code of the hook. 0 for success.
442 442 :rtype: int
443 443 """
444 444 if 'pull' not in extras['hooks']:
445 445 return HookResponse(0, '')
446 446
447 447 stdout = io.BytesIO()
448 448 try:
449 449 status = _call_hook('post_pull', extras, GitMessageWriter(stdout))
450 450 except Exception as error:
451 451 status = 128
452 452 stdout.write('ERROR: %s\n' % error)
453 453
454 454 return HookResponse(status, stdout.getvalue())
455 455
456 456
457 457 def _parse_git_ref_lines(revision_lines):
458 458 rev_data = []
459 459 for revision_line in revision_lines or []:
460 460 old_rev, new_rev, ref = revision_line.strip().split(' ')
461 461 ref_data = ref.split('/', 2)
462 462 if ref_data[1] in ('tags', 'heads'):
463 463 rev_data.append({
464 464 # NOTE(marcink):
465 465 # we're unable to tell total_commits for git at this point
466 466 # but we set the variable for consistency with GIT
467 467 'total_commits': -1,
468 468 'old_rev': old_rev,
469 469 'new_rev': new_rev,
470 470 'ref': ref,
471 471 'type': ref_data[1],
472 472 'name': ref_data[2],
473 473 })
474 474 return rev_data
475 475
476 476
477 477 def git_pre_receive(unused_repo_path, revision_lines, env):
478 478 """
479 479 Pre push hook.
480 480
481 481 :param extras: dictionary containing the keys defined in simplevcs
482 482 :type extras: dict
483 483
484 484 :return: status code of the hook. 0 for success.
485 485 :rtype: int
486 486 """
487 487 extras = json.loads(env['RC_SCM_DATA'])
488 488 rev_data = _parse_git_ref_lines(revision_lines)
489 489 if 'push' not in extras['hooks']:
490 490 return 0
491 491 empty_commit_id = '0' * 40
492 492
493 493 detect_force_push = extras.get('detect_force_push')
494 494
495 495 for push_ref in rev_data:
496 496 # store our git-env which holds the temp store
497 497 push_ref['git_env'] = _get_git_env()
498 498 push_ref['pruned_sha'] = ''
499 499 if not detect_force_push:
500 500 # don't check for forced-push when we don't need to
501 501 continue
502 502
503 503 type_ = push_ref['type']
504 504 new_branch = push_ref['old_rev'] == empty_commit_id
505 505 if type_ == 'heads' and not new_branch:
506 506 old_rev = push_ref['old_rev']
507 507 new_rev = push_ref['new_rev']
508 508 cmd = [settings.GIT_EXECUTABLE, 'rev-list',
509 509 old_rev, '^{}'.format(new_rev)]
510 510 stdout, stderr = subprocessio.run_command(
511 511 cmd, env=os.environ.copy())
512 512 # means we're having some non-reachable objects, this forced push
513 513 # was used
514 514 if stdout:
515 515 push_ref['pruned_sha'] = stdout.splitlines()
516 516
517 517 extras['hook_type'] = 'pre_receive'
518 518 extras['commit_ids'] = rev_data
519 519 return _call_hook('pre_push', extras, GitMessageWriter())
520 520
521 521
522 522 def git_post_receive(unused_repo_path, revision_lines, env):
523 523 """
524 524 Post push hook.
525 525
526 526 :param extras: dictionary containing the keys defined in simplevcs
527 527 :type extras: dict
528 528
529 529 :return: status code of the hook. 0 for success.
530 530 :rtype: int
531 531 """
532 532 extras = json.loads(env['RC_SCM_DATA'])
533 533 if 'push' not in extras['hooks']:
534 534 return 0
535 535
536 536 rev_data = _parse_git_ref_lines(revision_lines)
537 537
538 538 git_revs = []
539 539
540 540 # N.B.(skreft): it is ok to just call git, as git before calling a
541 541 # subcommand sets the PATH environment variable so that it point to the
542 542 # correct version of the git executable.
543 543 empty_commit_id = '0' * 40
544 544 branches = []
545 545 tags = []
546 546 for push_ref in rev_data:
547 547 type_ = push_ref['type']
548 548
549 549 if type_ == 'heads':
550 550 if push_ref['old_rev'] == empty_commit_id:
551 551 # starting new branch case
552 552 if push_ref['name'] not in branches:
553 553 branches.append(push_ref['name'])
554 554
555 555 # Fix up head revision if needed
556 556 cmd = [settings.GIT_EXECUTABLE, 'show', 'HEAD']
557 557 try:
558 558 subprocessio.run_command(cmd, env=os.environ.copy())
559 559 except Exception:
560 560 cmd = [settings.GIT_EXECUTABLE, 'symbolic-ref', 'HEAD',
561 561 'refs/heads/%s' % push_ref['name']]
562 562 print("Setting default branch to %s" % push_ref['name'])
563 563 subprocessio.run_command(cmd, env=os.environ.copy())
564 564
565 565 cmd = [settings.GIT_EXECUTABLE, 'for-each-ref',
566 566 '--format=%(refname)', 'refs/heads/*']
567 567 stdout, stderr = subprocessio.run_command(
568 568 cmd, env=os.environ.copy())
569 569 heads = stdout
570 570 heads = heads.replace(push_ref['ref'], '')
571 571 heads = ' '.join(head for head
572 572 in heads.splitlines() if head) or '.'
573 573 cmd = [settings.GIT_EXECUTABLE, 'log', '--reverse',
574 574 '--pretty=format:%H', '--', push_ref['new_rev'],
575 575 '--not', heads]
576 576 stdout, stderr = subprocessio.run_command(
577 577 cmd, env=os.environ.copy())
578 578 git_revs.extend(stdout.splitlines())
579 579 elif push_ref['new_rev'] == empty_commit_id:
580 580 # delete branch case
581 581 git_revs.append('delete_branch=>%s' % push_ref['name'])
582 582 else:
583 583 if push_ref['name'] not in branches:
584 584 branches.append(push_ref['name'])
585 585
586 586 cmd = [settings.GIT_EXECUTABLE, 'log',
587 587 '{old_rev}..{new_rev}'.format(**push_ref),
588 588 '--reverse', '--pretty=format:%H']
589 589 stdout, stderr = subprocessio.run_command(
590 590 cmd, env=os.environ.copy())
591 591 git_revs.extend(stdout.splitlines())
592 592 elif type_ == 'tags':
593 593 if push_ref['name'] not in tags:
594 594 tags.append(push_ref['name'])
595 595 git_revs.append('tag=>%s' % push_ref['name'])
596 596
597 597 extras['hook_type'] = 'post_receive'
598 598 extras['commit_ids'] = git_revs
599 599 extras['new_refs'] = {
600 600 'branches': branches,
601 601 'bookmarks': [],
602 602 'tags': tags,
603 603 }
604 604
605 605 if 'repo_size' in extras['hooks']:
606 606 try:
607 607 _call_hook('repo_size', extras, GitMessageWriter())
608 608 except:
609 609 pass
610 610
611 611 return _call_hook('post_push', extras, GitMessageWriter())
612 612
613 613
614 614 def _get_extras_from_txn_id(path, txn_id):
615 615 extras = {}
616 616 try:
617 617 cmd = ['svnlook', 'pget',
618 618 '-t', txn_id,
619 619 '--revprop', path, 'rc-scm-extras']
620 620 stdout, stderr = subprocessio.run_command(
621 621 cmd, env=os.environ.copy())
622 622 extras = json.loads(base64.urlsafe_b64decode(stdout))
623 623 except Exception:
624 624 log.exception('Failed to extract extras info from txn_id')
625 625
626 626 return extras
627 627
628 628
629 629 def _get_extras_from_commit_id(commit_id, path):
630 630 extras = {}
631 631 try:
632 632 cmd = ['svnlook', 'pget',
633 633 '-r', commit_id,
634 634 '--revprop', path, 'rc-scm-extras']
635 635 stdout, stderr = subprocessio.run_command(
636 636 cmd, env=os.environ.copy())
637 637 extras = json.loads(base64.urlsafe_b64decode(stdout))
638 638 except Exception:
639 639 log.exception('Failed to extract extras info from commit_id')
640 640
641 641 return extras
642 642
643 643
644 644 def svn_pre_commit(repo_path, commit_data, env):
645 645 path, txn_id = commit_data
646 646 branches = []
647 647 tags = []
648 648
649 649 if env.get('RC_SCM_DATA'):
650 650 extras = json.loads(env['RC_SCM_DATA'])
651 651 else:
652 652 # fallback method to read from TXN-ID stored data
653 653 extras = _get_extras_from_txn_id(path, txn_id)
654 654 if not extras:
655 655 return 0
656 656
657 extras['hook_type'] = 'pre_commit'
657 658 extras['commit_ids'] = []
658 659 extras['txn_id'] = txn_id
659 660 extras['new_refs'] = {
660 661 'total_commits': 1,
661 662 'branches': branches,
662 663 'bookmarks': [],
663 664 'tags': tags,
664 665 }
665 666
666 667 return _call_hook('pre_push', extras, SvnMessageWriter())
667 668
668 669
669 670 def svn_post_commit(repo_path, commit_data, env):
670 671 """
671 672 commit_data is path, rev, txn_id
672 673 """
673 674 path, commit_id, txn_id = commit_data
674 675 branches = []
675 676 tags = []
676 677
677 678 if env.get('RC_SCM_DATA'):
678 679 extras = json.loads(env['RC_SCM_DATA'])
679 680 else:
680 681 # fallback method to read from TXN-ID stored data
681 682 extras = _get_extras_from_commit_id(commit_id, path)
682 683 if not extras:
683 684 return 0
684 685
686 extras['hook_type'] = 'post_commit'
685 687 extras['commit_ids'] = [commit_id]
686 688 extras['txn_id'] = txn_id
687 689 extras['new_refs'] = {
688 690 'branches': branches,
689 691 'bookmarks': [],
690 692 'tags': tags,
691 693 'total_commits': 1,
692 694 }
693 695
694 696 if 'repo_size' in extras['hooks']:
695 697 try:
696 698 _call_hook('repo_size', extras, SvnMessageWriter())
697 699 except Exception:
698 700 pass
699 701
700 702 return _call_hook('post_push', extras, SvnMessageWriter())
General Comments 0
You need to be logged in to leave comments. Login now