##// END OF EJS Templates
Implemented generation of changesets based...
Implemented generation of changesets based on whole diff instead of per file diff. That can give a big speed improvement for large changesets in repositories with large history. - improved handling of binary files - show renames of binary files - implemented new diff limit functionality - unify diff generation between hg and git - Added binary indicators for changed files, - added diff lib tests

File last commit:

r2963:742d1b8c beta
r2995:32471bd1 beta
Show More
hooks.py
422 lines | 12.9 KiB | text/x-python | PythonLexer
filled in some docs for hooks
r913 # -*- coding: utf-8 -*-
"""
rhodecode.lib.hooks
~~~~~~~~~~~~~~~~~~~
Hooks runned by rhodecode
source code cleanup: remove trailing white space, normalize file endings
r1203
filled in some docs for hooks
r913 :created_on: Aug 6, 2010
:author: marcink
2012 copyrights
r1824 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
filled in some docs for hooks
r913 :license: GPLv3, see COPYING for more details.
"""
fixed license issue #149
r1206 # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
source code cleanup: remove trailing white space, normalize file endings
r1203 #
renamed project to rhodecode
r547 # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
source code cleanup: remove trailing white space, normalize file endings
r1203 #
renamed project to rhodecode
r547 # You should have received a copy of the GNU General Public License
fixed license issue #149
r1206 # along with this program. If not, see <http://www.gnu.org/licenses/>.
filled in some docs for hooks
r913 import os
import sys
#595 add rcextension hook for repository delete
r2904 import time
save full raw id in push log data for much faster revision lookup
r2324 import binascii
from inspect import isfunction
renamed project to rhodecode
r547
fixed hooks for mercurial 1.9
r1415 from mercurial.scmutil import revrange
#48 rewrite action loggers into hooks with all changesets that are inside a push
r654 from mercurial.node import nullrev
save full raw id in push log data for much faster revision lookup
r2324
#48 rewrite action loggers into hooks with all changesets that are inside a push
r654 from rhodecode.lib import helpers as h
from rhodecode.lib.utils import action_logger
Git Hooks are automatically installed in new repos...
r2404 from rhodecode.lib.vcs.backends.base import EmptyChangeset
use os.environ as a fallback for getting special info from hooks, this will allow...
r2716 from rhodecode.lib.compat import json
raise na OSERROR if repository data sent from git hook to hook handler is somehow invalid
r2818 from rhodecode.lib.exceptions import HTTPLockedRC
Use lightweight revisions in journal by default
r2958 from rhodecode.lib.utils2 import safe_str, datetime_to_time
raise na OSERROR if repository data sent from git hook to hook handler is somehow invalid
r2818 from rhodecode.model.db import Repository, User
pep8ify
r1307
#595 add rcextension hook for repository delete
r2904
made repo-size hook more generic
r2196 def _get_scm_size(alias, root_path):
if not alias.startswith('.'):
alias += '.'
size_scm, size_root = 0, 0
use str() on os.walk passing unicode can lead to UnicodeDecode errors when iterating
r2963 for path, dirs, files in os.walk(safe_str(root_path)):
made repo-size hook more generic
r2196 if path.find(alias) != -1:
for f in files:
try:
size_scm += os.path.getsize(os.path.join(path, f))
except OSError:
pass
else:
for f in files:
try:
size_root += os.path.getsize(os.path.join(path, f))
except OSError:
pass
size_scm_f = h.format_byte_size(size_scm)
size_root_f = h.format_byte_size(size_root)
size_total_f = h.format_byte_size(size_root + size_scm)
return size_scm_f, size_root_f, size_total_f
renamed project to rhodecode
r547 def repo_size(ui, repo, hooktype=None, **kwargs):
#235 forking page repo group selection...
r1722 """
Presents size of repository after push
source code cleanup: remove trailing white space, normalize file endings
r1203
filled in some docs for hooks
r913 :param ui:
:param repo:
:param hooktype:
"""
renamed project to rhodecode
r547
made repo-size hook more generic
r2196 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
small change for post update hook that displays repository size
r1814
last_cs = repo[len(repo) - 1]
msg = ('Repository size .hg:%s repo:%s total:%s\n'
'Last revision is now r%s:%s\n') % (
size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
)
sys.stdout.write(msg)
bugfix, repo_size crashed when broken symlinks where inside a repository.
r675
pep8ify
r1307
Implemented basic locking functionality....
r2726 def pre_push(ui, repo, **kwargs):
# pre push function, currently used to ban pushing when
# repository is locked
try:
rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
except:
rc_extras = {}
extras = dict(repo.ui.configitems('rhodecode_extras'))
if 'username' in extras:
username = extras['username']
repository = extras['repository']
scm = extras['scm']
locked_by = extras['locked_by']
elif 'username' in rc_extras:
username = rc_extras['username']
repository = rc_extras['repository']
scm = rc_extras['scm']
locked_by = rc_extras['locked_by']
else:
raise Exception('Missing data in repo.ui and os.environ')
usr = User.get_by_username(username)
if locked_by[0] and usr.user_id != int(locked_by[0]):
Fixed issue with mixed locking notification and bad username
r2750 locked_by = User.get(locked_by[0]).username
raise HTTPLockedRC(repository, locked_by)
Implemented basic locking functionality....
r2726
def pre_pull(ui, repo, **kwargs):
# pre push function, currently used to ban pushing when
# repository is locked
try:
rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
except:
rc_extras = {}
extras = dict(repo.ui.configitems('rhodecode_extras'))
if 'username' in extras:
username = extras['username']
repository = extras['repository']
scm = extras['scm']
locked_by = extras['locked_by']
elif 'username' in rc_extras:
username = rc_extras['username']
repository = rc_extras['repository']
scm = rc_extras['scm']
locked_by = rc_extras['locked_by']
else:
raise Exception('Missing data in repo.ui and os.environ')
if locked_by[0]:
Fixed issue with mixed locking notification and bad username
r2750 locked_by = User.get(locked_by[0]).username
raise HTTPLockedRC(repository, locked_by)
Implemented basic locking functionality....
r2726
#48 rewrite action loggers into hooks with all changesets that are inside a push
r654 def log_pull_action(ui, repo, **kwargs):
#235 forking page repo group selection...
r1722 """
Logs user last pull action
source code cleanup: remove trailing white space, normalize file endings
r1203
#48 rewrite action loggers into hooks with all changesets that are inside a push
r654 :param ui:
:param repo:
"""
use os.environ as a fallback for getting special info from hooks, this will allow...
r2716 try:
rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
except:
rc_extras = {}
added initial rc-extension module...
r2105 extras = dict(repo.ui.configitems('rhodecode_extras'))
use os.environ as a fallback for getting special info from hooks, this will allow...
r2716 if 'username' in extras:
username = extras['username']
repository = extras['repository']
scm = extras['scm']
Implemented basic locking functionality....
r2726 make_lock = extras['make_lock']
use os.environ as a fallback for getting special info from hooks, this will allow...
r2716 elif 'username' in rc_extras:
username = rc_extras['username']
repository = rc_extras['repository']
scm = rc_extras['scm']
Implemented basic locking functionality....
r2726 make_lock = rc_extras['make_lock']
use os.environ as a fallback for getting special info from hooks, this will allow...
r2716 else:
raise Exception('Missing data in repo.ui and os.environ')
Implemented basic locking functionality....
r2726 user = User.get_by_username(username)
#48 rewrite action loggers into hooks with all changesets that are inside a push
r654 action = 'pull'
Implemented basic locking functionality....
r2726 action_logger(user, action, repository, extras['ip'], commit=True)
added initial rc-extension module...
r2105 # extension hook call
changed scope of calling EXTENSIONS from rhodecode for githooks to be able to execute them
r2406 from rhodecode import EXTENSIONS
added initial rc-extension module...
r2105 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
bugfix, repo_size crashed when broken symlinks where inside a repository.
r675
added initial rc-extension module...
r2105 if isfunction(callback):
kw = {}
kw.update(extras)
callback(**kw)
Implemented basic locking functionality....
r2726
if make_lock is True:
Repository.lock(Repository.get_by_repo_name(repository), user.user_id)
#msg = 'Made lock on repo `%s`' % repository
#sys.stdout.write(msg)
#48 rewrite action loggers into hooks with all changesets that are inside a push
r654 return 0
renamed project to rhodecode
r547
pep8ify
r1307
#48 rewrite action loggers into hooks with all changesets that are inside a push
r654 def log_push_action(ui, repo, **kwargs):
#235 forking page repo group selection...
r1722 """
Maps user last push action to new changeset id, from mercurial
source code cleanup: remove trailing white space, normalize file endings
r1203
fixed @repo into :repo for docs...
r604 :param ui:
fixes issue #436 git push error
r2236 :param repo: repo object containing the `ui` object
renamed project to rhodecode
r547 """
bugfix, repo_size crashed when broken symlinks where inside a repository.
r675
use os.environ as a fallback for getting special info from hooks, this will allow...
r2716 try:
rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
except:
rc_extras = {}
added initial rc-extension module...
r2105 extras = dict(repo.ui.configitems('rhodecode_extras'))
use os.environ as a fallback for getting special info from hooks, this will allow...
r2716 if 'username' in extras:
username = extras['username']
repository = extras['repository']
scm = extras['scm']
Implemented basic locking functionality....
r2726 make_lock = extras['make_lock']
use os.environ as a fallback for getting special info from hooks, this will allow...
r2716 elif 'username' in rc_extras:
username = rc_extras['username']
repository = rc_extras['repository']
scm = rc_extras['scm']
Implemented basic locking functionality....
r2726 make_lock = rc_extras['make_lock']
use os.environ as a fallback for getting special info from hooks, this will allow...
r2716 else:
raise Exception('Missing data in repo.ui and os.environ')
action = 'push' + ':%s'
bugfix, repo_size crashed when broken symlinks where inside a repository.
r675
added emulation of pull hook for git-backend, and dummy git-push hook
r2203 if scm == 'hg':
node = kwargs['node']
def get_revs(repo, rev_opt):
if rev_opt:
revs = revrange(repo, rev_opt)
bugfix, repo_size crashed when broken symlinks where inside a repository.
r675
added emulation of pull hook for git-backend, and dummy git-push hook
r2203 if len(revs) == 0:
return (nullrev, nullrev)
return (max(revs), min(revs))
else:
return (len(repo) - 1, 0)
bugfix, repo_size crashed when broken symlinks where inside a repository.
r675
added emulation of pull hook for git-backend, and dummy git-push hook
r2203 stop, start = get_revs(repo, [node + ':'])
save full raw id in push log data for much faster revision lookup
r2324 h = binascii.hexlify
Change git & hg hooks to post. They shouldn't block as they are used just for logging actions. Futhermore post hooks have access to changesets, so it's much better flexible
r2407 revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
added emulation of pull hook for git-backend, and dummy git-push hook
r2203 elif scm == 'git':
Added handling of git hooks, extract pushed revisions and store them inside...
r2402 revs = kwargs.get('_git_revs', [])
if '_git_revs' in kwargs:
kwargs.pop('_git_revs')
bugfix, repo_size crashed when broken symlinks where inside a repository.
r675
#48 rewrite action loggers into hooks with all changesets that are inside a push
r654 action = action % ','.join(revs)
bugfix, repo_size crashed when broken symlinks where inside a repository.
r675
added initial rc-extension module...
r2105 action_logger(username, action, repository, extras['ip'], commit=True)
bugfix, repo_size crashed when broken symlinks where inside a repository.
r675
added initial rc-extension module...
r2105 # extension hook call
changed scope of calling EXTENSIONS from rhodecode for githooks to be able to execute them
r2406 from rhodecode import EXTENSIONS
added initial rc-extension module...
r2105 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
if isfunction(callback):
kw = {'pushed_revs': revs}
kw.update(extras)
callback(**kw)
Implemented basic locking functionality....
r2726
if make_lock is False:
Repository.unlock(Repository.get_by_repo_name(repository))
msg = 'Released lock on repo `%s`\n' % repository
sys.stdout.write(msg)
#48 rewrite action loggers into hooks with all changesets that are inside a push
r654 return 0
#348 added post-create repository hook
r1972
def log_create_repository(repository_dict, created_by, **kwargs):
"""
Post create repository Hook. This is a dummy function for admins to re-use
utils/conf...
r2109 if needed. It's taken from rhodecode-extensions module and executed
added initial rc-extension module...
r2105 if present
#348 added post-create repository hook
r1972
#227 Initial version of repository groups permissions system...
r1982 :param repository: dict dump of repository object
#348 added post-create repository hook
r1972 :param created_by: username who created repository
available keys of repository_dict:
'repo_type',
'description',
'private',
'created_on',
'enable_downloads',
'repo_id',
'user_id',
'enable_statistics',
'clone_uri',
'fork_id',
'group_id',
'repo_name'
"""
changed scope of calling EXTENSIONS from rhodecode for githooks to be able to execute them
r2406 from rhodecode import EXTENSIONS
added initial rc-extension module...
r2105 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
if isfunction(callback):
kw = {}
kw.update(repository_dict)
kw.update({'created_by': created_by})
kw.update(kwargs)
return callback(**kw)
#348 added post-create repository hook
r1972
#227 Initial version of repository groups permissions system...
r1982 return 0
Added handling of git hooks, extract pushed revisions and store them inside...
r2402
#595 add rcextension hook for repository delete
r2904
def log_delete_repository(repository_dict, deleted_by, **kwargs):
"""
Post delete repository Hook. This is a dummy function for admins to re-use
if needed. It's taken from rhodecode-extensions module and executed
if present
:param repository: dict dump of repository object
:param deleted_by: username who deleted the repository
available keys of repository_dict:
'repo_type',
'description',
'private',
'created_on',
'enable_downloads',
'repo_id',
'user_id',
'enable_statistics',
'clone_uri',
'fork_id',
'group_id',
'repo_name'
"""
from rhodecode import EXTENSIONS
callback = getattr(EXTENSIONS, 'DELETE_REPO_HOOK', None)
if isfunction(callback):
kw = {}
kw.update(repository_dict)
kw.update({'deleted_by': deleted_by,
'deleted_on': time.time()})
kw.update(kwargs)
return callback(**kw)
return 0
Implemented basic locking functionality....
r2726 handle_git_pre_receive = (lambda repo_path, revs, env:
handle_git_receive(repo_path, revs, env, hook_type='pre'))
handle_git_post_receive = (lambda repo_path, revs, env:
handle_git_receive(repo_path, revs, env, hook_type='post'))
Added handling of git hooks, extract pushed revisions and store them inside...
r2402
Implemented basic locking functionality....
r2726
def handle_git_receive(repo_path, revs, env, hook_type='post'):
Added handling of git hooks, extract pushed revisions and store them inside...
r2402 """
Change git & hg hooks to post. They shouldn't block as they are used just for logging actions. Futhermore post hooks have access to changesets, so it's much better flexible
r2407 A really hacky method that is runned by git post-receive hook and logs
whitespace cleanup
r2409 an push action together with pushed revisions. It's executed by subprocess
thus needs all info to be able to create a on the fly pylons enviroment,
connect to database and run the logging code. Hacky as sh*t but works.
Added handling of git hooks, extract pushed revisions and store them inside...
r2402
:param repo_path:
:type repo_path:
:param revs:
:type revs:
:param env:
:type env:
"""
from paste.deploy import appconfig
from sqlalchemy import engine_from_config
from rhodecode.config.environment import load_environment
from rhodecode.model import init_model
from rhodecode.model.db import RhodeCodeUi
from rhodecode.lib.utils import make_ui
There's no need to use special env variable for config file, it's already passed in in extras now
r2870 extras = json.loads(env['RHODECODE_EXTRAS'])
Added handling of git hooks, extract pushed revisions and store them inside...
r2402
There's no need to use special env variable for config file, it's already passed in in extras now
r2870 path, ini_name = os.path.split(extras['config'])
Added handling of git hooks, extract pushed revisions and store them inside...
r2402 conf = appconfig('config:%s' % ini_name, relative_to=path)
load_environment(conf.global_conf, conf.local_conf)
engine = engine_from_config(conf, 'sqlalchemy.db1.')
init_model(engine)
baseui = make_ui('db')
Fixed githooks for fetching multiple tags and branches....
r2617 # fix if it's not a bare repo
Stefan Engel
Make detecting bare Git repositories more robust...
r2872 if repo_path.endswith(os.sep + '.git'):
repo_path = repo_path[:-5]
raise na OSERROR if repository data sent from git hook to hook handler is somehow invalid
r2818
Added handling of git hooks, extract pushed revisions and store them inside...
r2402 repo = Repository.get_by_full_path(repo_path)
raise na OSERROR if repository data sent from git hook to hook handler is somehow invalid
r2818 if not repo:
raise OSError('Repository %s not found in database'
% (safe_str(repo_path)))
Added handling of git hooks, extract pushed revisions and store them inside...
r2402 _hooks = dict(baseui.configitems('hooks')) or {}
Implemented basic locking functionality....
r2726 for k, v in extras.items():
baseui.setconfig('rhodecode_extras', k, v)
repo = repo.scm_instance
repo.ui = baseui
if hook_type == 'pre':
pre_push(baseui, repo)
# if push hook is enabled via web interface
elif hook_type == 'post' and _hooks.get(RhodeCodeUi.HOOK_PUSH):
Fixed githooks for fetching multiple tags and branches....
r2617
rev_data = []
for l in revs:
old_rev, new_rev, ref = l.split(' ')
_ref_data = ref.split('/')
if _ref_data[1] in ['tags', 'heads']:
rev_data.append({'old_rev': old_rev,
'new_rev': new_rev,
'ref': ref,
'type': _ref_data[1],
'name': _ref_data[2].strip()})
git_revs = []
for push_ref in rev_data:
_type = push_ref['type']
if _type == 'heads':
if push_ref['old_rev'] == EmptyChangeset().raw_id:
cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
heads = repo.run_git_command(cmd)[0]
heads = heads.replace(push_ref['ref'], '')
heads = ' '.join(map(lambda c: c.strip('\n').strip(),
heads.splitlines()))
cmd = (('log %(new_rev)s' % push_ref) +
' --reverse --pretty=format:"%H" --not ' + heads)
else:
cmd = (('log %(old_rev)s..%(new_rev)s' % push_ref) +
' --reverse --pretty=format:"%H"')
git_revs += repo.run_git_command(cmd)[0].splitlines()
elif _type == 'tags':
git_revs += [push_ref['name']]
Added handling of git hooks, extract pushed revisions and store them inside...
r2402
log_push_action(baseui, repo, _git_revs=git_revs)