changeset 2735:9d8f63ff9219 beta

Merged in domruf/rhodecode (pull request #66)
author Marcin Kuzminski <marcin@python-works.com>
date Fri, 24 Aug 2012 14:58:26 +0200
parents c25cc1c7c65f (diff) d2f552429ef3 (current diff)
children 3aad896d9a02
files rhodecode/lib/helpers.py
diffstat 45 files changed, 1082 insertions(+), 496 deletions(-) [+]
line wrap: on
line diff
--- a/docs/index.rst	Fri Aug 24 10:37:17 2012 +0200
+++ b/docs/index.rst	Fri Aug 24 14:58:26 2012 +0200
@@ -22,6 +22,7 @@
    usage/general
    usage/git_support
    usage/performance
+   usage/locking
    usage/statistics
    usage/backup
    usage/debugging
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/usage/locking.rst	Fri Aug 24 14:58:26 2012 +0200
@@ -0,0 +1,41 @@
+.. _locking:
+
+===================================
+RhodeCode repository locking system
+===================================
+
+
+| Repos with **locking function=disabled** is the default, that's how repos work 
+  today.
+| Repos with **locking function=enabled** behaves like follows:
+
+Repos have a state called `locked` that can be true or false.
+The hg/git commands `hg/git clone`, `hg/git pull`, and `hg/git push` 
+influence this state:
+
+- The command `hg/git pull <repo>` will lock that repo (locked=true) 
+  if the user has write/admin permissions on this repo
+
+- The command `hg/git clone <repo>` will lock that repo (locked=true) if the 
+  user has write/admin permissions on this repo
+
+
+RhodeCode will remember the user id who locked the repo
+only this specific user can unlock the repo (locked=false) by calling 
+
+- `hg/git push <repo>` 
+
+every other command on that repo from this user and 
+every command from any other user will result in http return code 423 (locked)
+
+
+additionally the http error includes the <user> that locked the repo 
+(e.g. “repository <repo> locked by user <user>”)
+
+
+So the scenario of use for repos with `locking function` enabled is that 
+every initial clone and every pull gives users (with write permission)
+the exclusive right to do a push.
+
+
+Each repo can be manually unlocked by admin from the repo settings menu.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rhodecode/config/pre_receive_tmpl.py	Fri Aug 24 14:58:26 2012 +0200
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+import os
+import sys
+
+try:
+    import rhodecode
+    RC_HOOK_VER = '_TMPL_'
+    os.environ['RC_HOOK_VER'] = RC_HOOK_VER
+    from rhodecode.lib.hooks import handle_git_pre_receive
+except ImportError:
+    rhodecode = None
+
+
+def main():
+    if rhodecode is None:
+        # exit with success if we cannot import rhodecode !!
+        # this allows simply push to this repo even without
+        # rhodecode
+        sys.exit(0)
+
+    repo_path = os.path.abspath('.')
+    push_data = sys.stdin.readlines()
+    # os.environ is modified here by a subprocess call that
+    # runs git and later git executes this hook.
+    # Environ get's some additional info from rhodecode system
+    # like IP or username from basic-auth
+    handle_git_pre_receive(repo_path, push_data, os.environ)
+    sys.exit(0)
+
+if __name__ == '__main__':
+    main()
--- a/rhodecode/config/routing.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/config/routing.py	Fri Aug 24 14:58:26 2012 +0200
@@ -138,7 +138,9 @@
         m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*?}",
                   action="repo_as_fork", conditions=dict(method=["PUT"],
                                                       function=check_repo))
-
+        m.connect('repo_locking', "/repo_locking/{repo_name:.*?}",
+                  action="repo_locking", conditions=dict(method=["PUT"],
+                                                      function=check_repo))
     with rmap.submapper(path_prefix=ADMIN_PREFIX,
                         controller='admin/repos_groups') as m:
         m.connect("repos_groups", "/repos_groups",
--- a/rhodecode/controllers/admin/repos.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/controllers/admin/repos.py	Fri Aug 24 14:58:26 2012 +0200
@@ -381,6 +381,7 @@
             RepoModel().delete_stats(repo_name)
             Session().commit()
         except Exception, e:
+            log.error(traceback.format_exc())
             h.flash(_('An error occurred during deletion of repository stats'),
                     category='error')
         return redirect(url('edit_repo', repo_name=repo_name))
@@ -397,11 +398,32 @@
             ScmModel().mark_for_invalidation(repo_name)
             Session().commit()
         except Exception, e:
+            log.error(traceback.format_exc())
             h.flash(_('An error occurred during cache invalidation'),
                     category='error')
         return redirect(url('edit_repo', repo_name=repo_name))
 
     @HasPermissionAllDecorator('hg.admin')
+    def repo_locking(self, repo_name):
+        """
+        Unlock repository when it is locked !
+
+        :param repo_name:
+        """
+
+        try:
+            repo = Repository.get_by_repo_name(repo_name)
+            if request.POST.get('set_lock'):
+                Repository.lock(repo, c.rhodecode_user.user_id)
+            elif request.POST.get('set_unlock'):
+                Repository.unlock(repo)
+        except Exception, e:
+            log.error(traceback.format_exc())
+            h.flash(_('An error occurred during unlocking'),
+                    category='error')
+        return redirect(url('edit_repo', repo_name=repo_name))
+
+    @HasPermissionAllDecorator('hg.admin')
     def repo_public_journal(self, repo_name):
         """
         Set's this repository to be visible in public journal,
--- a/rhodecode/controllers/files.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/controllers/files.py	Fri Aug 24 14:58:26 2012 +0200
@@ -234,6 +234,15 @@
     @LoginRequired()
     @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
     def edit(self, repo_name, revision, f_path):
+        repo = Repository.get_by_repo_name(repo_name)
+        if repo.enable_locking and repo.locked[0]:
+            h.flash(_('This repository is has been locked by %s on %s')
+                % (h.person_by_id(repo.locked[0]),
+                   h.fmt_date(h.time_to_datetime(repo.locked[1]))),
+                  'warning')
+            return redirect(h.url('files_home',
+                                  repo_name=repo_name, revision='tip'))
+
         r_post = request.POST
 
         c.cs = self.__get_cs_or_redirect(revision, repo_name)
@@ -284,6 +293,16 @@
     @LoginRequired()
     @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
     def add(self, repo_name, revision, f_path):
+
+        repo = Repository.get_by_repo_name(repo_name)
+        if repo.enable_locking and repo.locked[0]:
+            h.flash(_('This repository is has been locked by %s on %s')
+                % (h.person_by_id(repo.locked[0]),
+                   h.fmt_date(h.time_to_datetime(repo.locked[1]))),
+                  'warning')
+            return redirect(h.url('files_home',
+                                  repo_name=repo_name, revision='tip'))
+
         r_post = request.POST
         c.cs = self.__get_cs_or_redirect(revision, repo_name,
                                          redirect_after=False)
--- a/rhodecode/controllers/pullrequests.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/controllers/pullrequests.py	Fri Aug 24 14:58:26 2012 +0200
@@ -103,16 +103,18 @@
                                 org_repo.user.username, c.repo_name))
                            )
 
-        c.other_refs = c.org_refs
+        # add org repo to other so we can open pull request agains itself
         c.other_repos.extend(c.org_repos)
 
+        c.default_pull_request = org_repo.repo_name
+        c.default_revs = self._get_repo_refs(org_repo.scm_instance)
         #add orginal repo
         other_repos_info[org_repo.repo_name] = {
             'gravatar': h.gravatar_url(org_repo.user.email, 24),
-            'description': org_repo.description
+            'description': org_repo.description,
+            'revs': h.select('other_ref', '', c.default_revs, class_='refs')
         }
 
-        c.default_pull_request = org_repo.repo_name
         #gather forks and add to this list
         for fork in org_repo.forks:
             c.other_repos.append((fork.repo_name, '%s/%s' % (
@@ -120,7 +122,10 @@
                                  )
             other_repos_info[fork.repo_name] = {
                 'gravatar': h.gravatar_url(fork.user.email, 24),
-                'description': fork.description
+                'description': fork.description,
+                'revs': h.select('other_ref', '',
+                                 self._get_repo_refs(fork.scm_instance),
+                                 class_='refs')
             }
         #add parents of this fork also
         if org_repo.parent:
@@ -131,7 +136,10 @@
                                      )
             other_repos_info[org_repo.parent.repo_name] = {
                 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
-                'description': org_repo.parent.description
+                'description': org_repo.parent.description,
+                'revs': h.select('other_ref', '',
+                                 self._get_repo_refs(org_repo.parent.scm_instance),
+                                 class_='refs')
             }
 
         c.other_repos_info = json.dumps(other_repos_info)
@@ -140,14 +148,12 @@
 
     @NotAnonymous()
     def create(self, repo_name):
-
         try:
             _form = PullRequestForm()().to_python(request.POST)
         except formencode.Invalid, errors:
             log.error(traceback.format_exc())
             if errors.error_dict.get('revisions'):
-                msg = _('Cannot open a pull request with '
-                        'empty list of changesets')
+                msg = 'Revisions: %s' % errors.error_dict['revisions']
             elif errors.error_dict.get('pullrequest_title'):
                 msg = _('Pull request requires a title with min. 3 chars')
             else:
@@ -215,7 +221,7 @@
          other_ref_name,
          other_ref_rev) = pull_request.other_ref.split(':')
 
-        # dispite opening revisions for bookmarks/branches/tags, we always
+        # despite opening revisions for bookmarks/branches/tags, we always
         # convert this to rev to prevent changes after book or branch change
         org_ref = ('rev', org_ref_rev)
         other_ref = ('rev', other_ref_rev)
--- a/rhodecode/lib/auth.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/auth.py	Fri Aug 24 14:58:26 2012 +0200
@@ -807,7 +807,7 @@
         return self.check_permissions()
 
     def check_permissions(self):
-        log.debug('checking mercurial protocol '
+        log.debug('checking VCS protocol '
                   'permissions %s for user:%s repository:%s', self.user_perms,
                                                 self.username, self.repo_name)
         if self.required_perms.intersection(self.user_perms):
--- a/rhodecode/lib/base.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/base.py	Fri Aug 24 14:58:26 2012 +0200
@@ -8,6 +8,7 @@
 
 from paste.auth.basic import AuthBasicAuthenticator
 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
+from webob.exc import HTTPClientError
 from paste.httpheaders import WWW_AUTHENTICATE
 
 from pylons import config, tmpl_context as c, request, session, url
@@ -17,15 +18,17 @@
 
 from rhodecode import __version__, BACKENDS
 
-from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
+from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict,\
+    safe_str
 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
     HasPermissionAnyMiddleware, CookieStoreWrapper
 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
 from rhodecode.model import meta
 
-from rhodecode.model.db import Repository, RhodeCodeUi
+from rhodecode.model.db import Repository, RhodeCodeUi, User
 from rhodecode.model.notification import NotificationModel
 from rhodecode.model.scm import ScmModel
+from rhodecode.model.meta import Session
 
 log = logging.getLogger(__name__)
 
@@ -159,6 +162,49 @@
             return False
         return True
 
+    def _check_locking_state(self, environ, action, repo, user_id):
+        """
+        Checks locking on this repository, if locking is enabled and lock is
+        present returns a tuple of make_lock, locked, locked_by.
+        make_lock can have 3 states None (do nothing) True, make lock
+        False release lock, This value is later propagated to hooks, which
+        do the locking. Think about this as signals passed to hooks what to do.
+
+        """
+        locked = False
+        make_lock = None
+        repo = Repository.get_by_repo_name(repo)
+        user = User.get(user_id)
+
+        # this is kind of hacky, but due to how mercurial handles client-server
+        # server see all operation on changeset; bookmarks, phases and
+        # obsolescence marker in different transaction, we don't want to check
+        # locking on those
+        obsolete_call = environ['QUERY_STRING'] in ['cmd=listkeys',]
+        locked_by = repo.locked
+        if repo and repo.enable_locking and not obsolete_call:
+            if action == 'push':
+                #check if it's already locked !, if it is compare users
+                user_id, _date = repo.locked
+                if user.user_id == user_id:
+                    log.debug('Got push from user, now unlocking' % (user))
+                    # unlock if we have push from user who locked
+                    make_lock = False
+                else:
+                    # we're not the same user who locked, ban with 423 !
+                    locked = True
+            if action == 'pull':
+                if repo.locked[0] and repo.locked[1]:
+                    locked = True
+                else:
+                    log.debug('Setting lock on repo %s by %s' % (repo, user))
+                    make_lock = True
+
+        else:
+            log.debug('Repository %s do not have locking enabled' % (repo))
+
+        return make_lock, locked, locked_by
+
     def __call__(self, environ, start_response):
         start = time.time()
         try:
--- a/rhodecode/lib/compat.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/compat.py	Fri Aug 24 14:58:26 2012 +0200
@@ -25,7 +25,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
-from rhodecode import __platform__, PLATFORM_WIN
+from rhodecode import __platform__, PLATFORM_WIN, __py_version__
 
 #==============================================================================
 # json
@@ -404,3 +404,182 @@
     from io import BytesIO
 except ImportError:
     from cStringIO import StringIO as BytesIO
+
+
+#==============================================================================
+# deque
+#==============================================================================
+
+if __py_version__ >= (2, 6):
+    from collections import deque
+else:
+    #need to implement our own deque with maxlen
+    class deque(object):
+
+        def __init__(self, iterable=(), maxlen=-1):
+            if not hasattr(self, 'data'):
+                self.left = self.right = 0
+                self.data = {}
+            self.maxlen = maxlen
+            self.extend(iterable)
+
+        def append(self, x):
+            self.data[self.right] = x
+            self.right += 1
+            if self.maxlen != -1 and len(self) > self.maxlen:
+                self.popleft()
+
+        def appendleft(self, x):
+            self.left -= 1
+            self.data[self.left] = x
+            if self.maxlen != -1 and len(self) > self.maxlen:
+                self.pop()
+
+        def pop(self):
+            if self.left == self.right:
+                raise IndexError('cannot pop from empty deque')
+            self.right -= 1
+            elem = self.data[self.right]
+            del self.data[self.right]
+            return elem
+
+        def popleft(self):
+            if self.left == self.right:
+                raise IndexError('cannot pop from empty deque')
+            elem = self.data[self.left]
+            del self.data[self.left]
+            self.left += 1
+            return elem
+
+        def clear(self):
+            self.data.clear()
+            self.left = self.right = 0
+
+        def extend(self, iterable):
+            for elem in iterable:
+                self.append(elem)
+
+        def extendleft(self, iterable):
+            for elem in iterable:
+                self.appendleft(elem)
+
+        def rotate(self, n=1):
+            if self:
+                n %= len(self)
+                for i in xrange(n):
+                    self.appendleft(self.pop())
+
+        def __getitem__(self, i):
+            if i < 0:
+                i += len(self)
+            try:
+                return self.data[i + self.left]
+            except KeyError:
+                raise IndexError
+
+        def __setitem__(self, i, value):
+            if i < 0:
+                i += len(self)
+            try:
+                self.data[i + self.left] = value
+            except KeyError:
+                raise IndexError
+
+        def __delitem__(self, i):
+            size = len(self)
+            if not (-size <= i < size):
+                raise IndexError
+            data = self.data
+            if i < 0:
+                i += size
+            for j in xrange(self.left + i, self.right - 1):
+                data[j] = data[j + 1]
+            self.pop()
+
+        def __len__(self):
+            return self.right - self.left
+
+        def __cmp__(self, other):
+            if type(self) != type(other):
+                return cmp(type(self), type(other))
+            return cmp(list(self), list(other))
+
+        def __repr__(self, _track=[]):
+            if id(self) in _track:
+                return '...'
+            _track.append(id(self))
+            r = 'deque(%r, maxlen=%s)' % (list(self), self.maxlen)
+            _track.remove(id(self))
+            return r
+
+        def __getstate__(self):
+            return (tuple(self),)
+
+        def __setstate__(self, s):
+            self.__init__(s[0])
+
+        def __hash__(self):
+            raise TypeError
+
+        def __copy__(self):
+            return self.__class__(self)
+
+        def __deepcopy__(self, memo={}):
+            from copy import deepcopy
+            result = self.__class__()
+            memo[id(self)] = result
+            result.__init__(deepcopy(tuple(self), memo))
+            return result
+
+
+#==============================================================================
+# threading.Event
+#==============================================================================
+
+if __py_version__ >= (2, 6):
+    from threading import Event
+else:
+    from threading import _Verbose, Condition, Lock
+
+    def Event(*args, **kwargs):
+        return _Event(*args, **kwargs)
+
+    class _Event(_Verbose):
+
+        # After Tim Peters' event class (without is_posted())
+
+        def __init__(self, verbose=None):
+            _Verbose.__init__(self, verbose)
+            self.__cond = Condition(Lock())
+            self.__flag = False
+
+        def isSet(self):
+            return self.__flag
+
+        is_set = isSet
+
+        def set(self):
+            self.__cond.acquire()
+            try:
+                self.__flag = True
+                self.__cond.notify_all()
+            finally:
+                self.__cond.release()
+
+        def clear(self):
+            self.__cond.acquire()
+            try:
+                self.__flag = False
+            finally:
+                self.__cond.release()
+
+        def wait(self, timeout=None):
+            self.__cond.acquire()
+            try:
+                if not self.__flag:
+                    self.__cond.wait(timeout)
+            finally:
+                self.__cond.release()
+
+
+
--- a/rhodecode/lib/db_manage.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/db_manage.py	Fri Aug 24 14:58:26 2012 +0200
@@ -307,37 +307,47 @@
         hooks1.ui_key = hooks1_key
         hooks1.ui_value = 'hg update >&2'
         hooks1.ui_active = False
+        self.sa.add(hooks1)
 
         hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
         hooks2_ = self.sa.query(RhodeCodeUi)\
             .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
-
         hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
         hooks2.ui_section = 'hooks'
         hooks2.ui_key = hooks2_key
         hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
+        self.sa.add(hooks2)
 
         hooks3 = RhodeCodeUi()
         hooks3.ui_section = 'hooks'
         hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
         hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
+        self.sa.add(hooks3)
 
         hooks4 = RhodeCodeUi()
         hooks4.ui_section = 'hooks'
-        hooks4.ui_key = RhodeCodeUi.HOOK_PULL
-        hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
+        hooks4.ui_key = RhodeCodeUi.HOOK_PRE_PUSH
+        hooks4.ui_value = 'python:rhodecode.lib.hooks.pre_push'
+        self.sa.add(hooks4)
 
-        # For mercurial 1.7 set backward comapatibility with format
-        dotencode_disable = RhodeCodeUi()
-        dotencode_disable.ui_section = 'format'
-        dotencode_disable.ui_key = 'dotencode'
-        dotencode_disable.ui_value = 'false'
+        hooks5 = RhodeCodeUi()
+        hooks5.ui_section = 'hooks'
+        hooks5.ui_key = RhodeCodeUi.HOOK_PULL
+        hooks5.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
+        self.sa.add(hooks5)
+
+        hooks6 = RhodeCodeUi()
+        hooks6.ui_section = 'hooks'
+        hooks6.ui_key = RhodeCodeUi.HOOK_PRE_PULL
+        hooks6.ui_value = 'python:rhodecode.lib.hooks.pre_pull'
+        self.sa.add(hooks6)
 
         # enable largefiles
         largefiles = RhodeCodeUi()
         largefiles.ui_section = 'extensions'
         largefiles.ui_key = 'largefiles'
         largefiles.ui_value = ''
+        self.sa.add(largefiles)
 
         # enable hgsubversion disabled by default
         hgsubversion = RhodeCodeUi()
@@ -345,6 +355,7 @@
         hgsubversion.ui_key = 'hgsubversion'
         hgsubversion.ui_value = ''
         hgsubversion.ui_active = False
+        self.sa.add(hgsubversion)
 
         # enable hggit disabled by default
         hggit = RhodeCodeUi()
@@ -352,13 +363,6 @@
         hggit.ui_key = 'hggit'
         hggit.ui_value = ''
         hggit.ui_active = False
-
-        self.sa.add(hooks1)
-        self.sa.add(hooks2)
-        self.sa.add(hooks3)
-        self.sa.add(hooks4)
-        self.sa.add(largefiles)
-        self.sa.add(hgsubversion)
         self.sa.add(hggit)
 
     def create_ldap_options(self, skip_existing=False):
@@ -461,6 +465,11 @@
         paths.ui_key = '/'
         paths.ui_value = path
 
+        phases = RhodeCodeUi()
+        phases.ui_section = 'phases'
+        phases.ui_key = 'publish'
+        phases.ui_value = False
+
         sett1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
         sett2 = RhodeCodeSetting('title', 'RhodeCode')
         sett3 = RhodeCodeSetting('ga_code', '')
--- a/rhodecode/lib/exceptions.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/exceptions.py	Fri Aug 24 14:58:26 2012 +0200
@@ -23,6 +23,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+from webob.exc import HTTPClientError
+
 
 class LdapUsernameError(Exception):
     pass
@@ -53,4 +55,17 @@
 
 
 class StatusChangeOnClosedPullRequestError(Exception):
-    pass
\ No newline at end of file
+    pass
+
+
+class HTTPLockedRC(HTTPClientError):
+    """
+    Special Exception For locked Repos in RhodeCode
+    """
+    code = 423
+    title = explanation = 'Repository Locked'
+
+    def __init__(self, reponame, username, *args, **kwargs):
+        self.title = self.explanation = ('Repository `%s` locked by '
+                                         'user `%s`' % (reponame, username))
+        super(HTTPLockedRC, self).__init__(*args, **kwargs)
--- a/rhodecode/lib/helpers.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/helpers.py	Fri Aug 24 14:58:26 2012 +0200
@@ -41,7 +41,7 @@
 from rhodecode.lib.annotate import annotate_highlight
 from rhodecode.lib.utils import repo_name_slug
 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
-    get_changeset_safe
+    get_changeset_safe, datetime_to_time, time_to_datetime
 from rhodecode.lib.markup_renderer import MarkupRenderer
 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
 from rhodecode.lib.vcs.backends.base import BaseChangeset
@@ -396,8 +396,14 @@
 
 
 def email_or_none(author):
+    # extract email from the commit string
     _email = email(author)
     if _email != '':
+        # check it against RhodeCode database, and use the MAIN email for this
+        # user
+        user = User.get_by_email(_email, case_insensitive=True, cache=True)
+        if user is not None:
+            return user.email
         return _email
 
     # See if it contains a username we can get an email from
@@ -410,9 +416,9 @@
     return None
 
 
-def person(author):
+def person(author, show_attr="username_and_name"):
     # attr to return from fetched user
-    person_getter = lambda usr: usr.username
+    person_getter = lambda usr: getattr(usr, show_attr)
 
     # Valid email in the attribute passed, see if they're in the system
     _email = email(author)
@@ -433,6 +439,19 @@
     return _author
 
 
+def person_by_id(id_, show_attr="username_and_name"):
+    # attr to return from fetched user
+    person_getter = lambda usr: getattr(usr, show_attr)
+
+    #maybe it's an ID ?
+    if str(id_).isdigit() or isinstance(id_, int):
+        id_ = int(id_)
+        user = User.get(id_)
+        if user is not None:
+            return person_getter(user)
+    return id_
+
+
 def desc_stylize(value):
     """
     converts tags from value into html equivalent
--- a/rhodecode/lib/hooks.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/hooks.py	Fri Aug 24 14:58:26 2012 +0200
@@ -33,6 +33,10 @@
 from rhodecode.lib import helpers as h
 from rhodecode.lib.utils import action_logger
 from rhodecode.lib.vcs.backends.base import EmptyChangeset
+from rhodecode.lib.compat import json
+from rhodecode.model.db import Repository, User
+from rhodecode.lib.utils2 import safe_str
+from rhodecode.lib.exceptions import HTTPLockedRC
 
 
 def _get_scm_size(alias, root_path):
@@ -83,6 +87,59 @@
     sys.stdout.write(msg)
 
 
+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]):
+        raise HTTPLockedRC(username, repository)
+
+
+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]:
+        raise HTTPLockedRC(username, repository)
+
+
 def log_pull_action(ui, repo, **kwargs):
     """
     Logs user last pull action
@@ -90,13 +147,26 @@
     :param ui:
     :param repo:
     """
+    try:
+        rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
+    except:
+        rc_extras = {}
     extras = dict(repo.ui.configitems('rhodecode_extras'))
-    username = extras['username']
-    repository = extras['repository']
-    scm = extras['scm']
+    if 'username' in extras:
+        username = extras['username']
+        repository = extras['repository']
+        scm = extras['scm']
+        make_lock = extras['make_lock']
+    elif 'username' in rc_extras:
+        username = rc_extras['username']
+        repository = rc_extras['repository']
+        scm = rc_extras['scm']
+        make_lock = rc_extras['make_lock']
+    else:
+        raise Exception('Missing data in repo.ui and os.environ')
+    user = User.get_by_username(username)
     action = 'pull'
-
-    action_logger(username, action, repository, extras['ip'], commit=True)
+    action_logger(user, action, repository, extras['ip'], commit=True)
     # extension hook call
     from rhodecode import EXTENSIONS
     callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
@@ -105,6 +175,12 @@
         kw = {}
         kw.update(extras)
         callback(**kw)
+
+    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)
+
     return 0
 
 
@@ -116,11 +192,26 @@
     :param repo: repo object containing the `ui` object
     """
 
+    try:
+        rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
+    except:
+        rc_extras = {}
+
     extras = dict(repo.ui.configitems('rhodecode_extras'))
-    username = extras['username']
-    repository = extras['repository']
-    action = extras['action'] + ':%s'
-    scm = extras['scm']
+    if 'username' in extras:
+        username = extras['username']
+        repository = extras['repository']
+        scm = extras['scm']
+        make_lock = extras['make_lock']
+    elif 'username' in rc_extras:
+        username = rc_extras['username']
+        repository = rc_extras['repository']
+        scm = rc_extras['scm']
+        make_lock = rc_extras['make_lock']
+    else:
+        raise Exception('Missing data in repo.ui and os.environ')
+
+    action = 'push' + ':%s'
 
     if scm == 'hg':
         node = kwargs['node']
@@ -154,6 +245,12 @@
         kw = {'pushed_revs': revs}
         kw.update(extras)
         callback(**kw)
+
+    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)
+
     return 0
 
 
@@ -194,8 +291,13 @@
 
     return 0
 
+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'))
 
-def handle_git_post_receive(repo_path, revs, env):
+
+def handle_git_receive(repo_path, revs, env, hook_type='post'):
     """
     A really hacky method that is runned by git post-receive hook and logs
     an push action together with pushed revisions. It's executed by subprocess
@@ -215,7 +317,6 @@
     from rhodecode.model import init_model
     from rhodecode.model.db import RhodeCodeUi
     from rhodecode.lib.utils import make_ui
-    from rhodecode.model.db import Repository
 
     path, ini_name = os.path.split(env['RHODECODE_CONFIG_FILE'])
     conf = appconfig('config:%s' % ini_name, relative_to=path)
@@ -230,20 +331,18 @@
         repo_path = repo_path[:-4]
     repo = Repository.get_by_full_path(repo_path)
     _hooks = dict(baseui.configitems('hooks')) or {}
-    # if push hook is enabled via web interface
-    if repo and _hooks.get(RhodeCodeUi.HOOK_PUSH):
 
-        extras = {
-         'username': env['RHODECODE_USER'],
-         'repository': repo.repo_name,
-         'scm': 'git',
-         'action': 'push',
-         'ip': env['RHODECODE_CONFIG_IP'],
-        }
-        for k, v in extras.items():
-            baseui.setconfig('rhodecode_extras', k, v)
-        repo = repo.scm_instance
-        repo.ui = baseui
+    extras = json.loads(env['RHODECODE_EXTRAS'])
+    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):
 
         rev_data = []
         for l in revs:
--- a/rhodecode/lib/indexers/__init__.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/indexers/__init__.py	Fri Aug 24 14:58:26 2012 +0200
@@ -89,6 +89,7 @@
 
 CHGSET_IDX_NAME = 'CHGSET_INDEX'
 
+
 class MakeIndex(BasePasterCommand):
 
     max_args = 1
@@ -218,6 +219,11 @@
                         'content_short_hl': self.highlight(content_short),
                         'f_path': f_path
                       })
+        elif self.search_type == 'path':
+            full_repo_path = jn(self.repo_location, res['repository'])
+            f_path = res['path'].split(full_repo_path)[-1]
+            f_path = f_path.lstrip(os.sep)
+            res.update({'f_path': f_path})
         elif self.search_type == 'message':
             res.update({'message_hl': self.highlight(res['message'])})
 
--- a/rhodecode/lib/middleware/pygrack.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/middleware/pygrack.py	Fri Aug 24 14:58:26 2012 +0200
@@ -41,7 +41,7 @@
     git_folder_signature = set(['config', 'head', 'info', 'objects', 'refs'])
     commands = ['git-upload-pack', 'git-receive-pack']
 
-    def __init__(self, repo_name, content_path, username):
+    def __init__(self, repo_name, content_path, extras):
         files = set([f.lower() for f in os.listdir(content_path)])
         if  not (self.git_folder_signature.intersection(files)
                 == self.git_folder_signature):
@@ -50,7 +50,7 @@
         self.valid_accepts = ['application/x-%s-result' %
                               c for c in self.commands]
         self.repo_name = repo_name
-        self.username = username
+        self.extras = extras
 
     def _get_fixedpath(self, path):
         """
@@ -67,7 +67,7 @@
         HTTP /info/refs request.
         """
 
-        git_command = request.GET['service']
+        git_command = request.GET.get('service')
         if git_command not in self.commands:
             log.debug('command %s not allowed' % git_command)
             return exc.HTTPMethodNotAllowed()
@@ -119,9 +119,8 @@
         try:
             gitenv = os.environ
             from rhodecode import CONFIG
-            from rhodecode.lib.base import _get_ip_addr
-            gitenv['RHODECODE_USER'] = self.username
-            gitenv['RHODECODE_CONFIG_IP'] = _get_ip_addr(environ)
+            from rhodecode.lib.compat import json
+            gitenv['RHODECODE_EXTRAS'] = json.dumps(self.extras)
             # forget all configs
             gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
             # we need current .ini file used to later initialize rhodecode
@@ -174,7 +173,7 @@
 
 class GitDirectory(object):
 
-    def __init__(self, repo_root, repo_name, username):
+    def __init__(self, repo_root, repo_name, extras):
         repo_location = os.path.join(repo_root, repo_name)
         if not os.path.isdir(repo_location):
             raise OSError(repo_location)
@@ -182,12 +181,12 @@
         self.content_path = repo_location
         self.repo_name = repo_name
         self.repo_location = repo_location
-        self.username = username
+        self.extras = extras
 
     def __call__(self, environ, start_response):
         content_path = self.content_path
         try:
-            app = GitRepository(self.repo_name, content_path, self.username)
+            app = GitRepository(self.repo_name, content_path, self.extras)
         except (AssertionError, OSError):
             if os.path.isdir(os.path.join(content_path, '.git')):
                 app = GitRepository(self.repo_name,
@@ -198,5 +197,5 @@
         return app(environ, start_response)
 
 
-def make_wsgi_app(repo_name, repo_root, username):
-    return GitDirectory(repo_root, repo_name, username)
+def make_wsgi_app(repo_name, repo_root, extras):
+    return GitDirectory(repo_root, repo_name, extras)
--- a/rhodecode/lib/middleware/simplegit.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/middleware/simplegit.py	Fri Aug 24 14:58:26 2012 +0200
@@ -31,6 +31,8 @@
 
 from dulwich import server as dulserver
 from dulwich.web import LimitedInputFilter, GunzipFilter
+from rhodecode.lib.exceptions import HTTPLockedRC
+from rhodecode.lib.hooks import pre_pull
 
 
 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
@@ -81,6 +83,7 @@
 from rhodecode.lib.base import BaseVCSController
 from rhodecode.lib.auth import get_container_username
 from rhodecode.lib.utils import is_valid_repo, make_ui
+from rhodecode.lib.compat import json
 from rhodecode.model.db import User, RhodeCodeUi
 
 log = logging.getLogger(__name__)
@@ -101,11 +104,11 @@
 class SimpleGit(BaseVCSController):
 
     def _handle_request(self, environ, start_response):
-
         if not is_git(environ):
             return self.application(environ, start_response)
         if not self._check_ssl(environ, start_response):
             return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response)
+
         ipaddr = self._get_ip_addr(environ)
         username = None
         self._git_first_op = False
@@ -122,7 +125,7 @@
             return HTTPInternalServerError()(environ, start_response)
 
         # quick check if that dir exists...
-        if is_valid_repo(repo_name, self.basepath) is False:
+        if is_valid_repo(repo_name, self.basepath, 'git') is False:
             return HTTPNotFound()(environ, start_response)
 
         #======================================================================
@@ -183,12 +186,16 @@
                 if perm is not True:
                     return HTTPForbidden()(environ, start_response)
 
+        # extras are injected into UI object and later available
+        # in hooks executed by rhodecode
         extras = {
             'ip': ipaddr,
             'username': username,
             'action': action,
             'repository': repo_name,
             'scm': 'git',
+            'make_lock': None,
+            'locked_by': [None, None]
         }
 
         #===================================================================
@@ -197,6 +204,21 @@
         repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
         log.debug('Repository path is %s' % repo_path)
 
+        # CHECK LOCKING only if it's not ANONYMOUS USER
+        if username != User.DEFAULT_USER:
+            log.debug('Checking locking on repository')
+            (make_lock,
+             locked,
+             locked_by) = self._check_locking_state(
+                            environ=environ, action=action,
+                            repo=repo_name, user_id=user.user_id
+                       )
+            # store the make_lock for later evaluation in hooks
+            extras.update({'make_lock': make_lock,
+                           'locked_by': locked_by})
+        # set the environ variables for this request
+        os.environ['RC_SCM_DATA'] = json.dumps(extras)
+        log.debug('HOOKS extras is %s' % extras)
         baseui = make_ui('db')
         self.__inject_extras(repo_path, baseui, extras)
 
@@ -207,13 +229,16 @@
             self._handle_githooks(repo_name, action, baseui, environ)
 
             log.info('%s action on GIT repo "%s"' % (action, repo_name))
-            app = self.__make_app(repo_name, repo_path, username)
+            app = self.__make_app(repo_name, repo_path, extras)
             return app(environ, start_response)
+        except HTTPLockedRC, e:
+            log.debug('Repositry LOCKED ret code 423!')
+            return e(environ, start_response)
         except Exception:
             log.error(traceback.format_exc())
             return HTTPInternalServerError()(environ, start_response)
 
-    def __make_app(self, repo_name, repo_path, username):
+    def __make_app(self, repo_name, repo_path, extras):
         """
         Make an wsgi application using dulserver
 
@@ -225,7 +250,7 @@
         app = make_wsgi_app(
             repo_root=safe_str(self.basepath),
             repo_name=repo_name,
-            username=username,
+            extras=extras,
         )
         app = GunzipFilter(LimitedInputFilter(app))
         return app
@@ -277,6 +302,7 @@
         """
         from rhodecode.lib.hooks import log_pull_action
         service = environ['QUERY_STRING'].split('=')
+
         if len(service) < 2:
             return
 
@@ -286,6 +312,9 @@
         _repo._repo.ui = baseui
 
         _hooks = dict(baseui.configitems('hooks')) or {}
+        if action == 'pull':
+            # stupid git, emulate pre-pull hook !
+            pre_pull(ui=baseui, repo=_repo._repo)
         if action == 'pull' and _hooks.get(RhodeCodeUi.HOOK_PULL):
             log_pull_action(ui=baseui, repo=_repo._repo)
 
--- a/rhodecode/lib/middleware/simplehg.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/middleware/simplehg.py	Fri Aug 24 14:58:26 2012 +0200
@@ -40,7 +40,9 @@
 from rhodecode.lib.base import BaseVCSController
 from rhodecode.lib.auth import get_container_username
 from rhodecode.lib.utils import make_ui, is_valid_repo, ui_sections
+from rhodecode.lib.compat import json
 from rhodecode.model.db import User
+from rhodecode.lib.exceptions import HTTPLockedRC
 
 
 log = logging.getLogger(__name__)
@@ -87,7 +89,7 @@
             return HTTPInternalServerError()(environ, start_response)
 
         # quick check if that dir exists...
-        if is_valid_repo(repo_name, self.basepath) is False:
+        if is_valid_repo(repo_name, self.basepath, 'hg') is False:
             return HTTPNotFound()(environ, start_response)
 
         #======================================================================
@@ -156,14 +158,31 @@
             'action': action,
             'repository': repo_name,
             'scm': 'hg',
+            'make_lock': None,
+            'locked_by': [None, None]
         }
-
         #======================================================================
         # MERCURIAL REQUEST HANDLING
         #======================================================================
         repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
         log.debug('Repository path is %s' % repo_path)
 
+        # CHECK LOCKING only if it's not ANONYMOUS USER
+        if username != User.DEFAULT_USER:
+            log.debug('Checking locking on repository')
+            (make_lock,
+             locked,
+             locked_by) = self._check_locking_state(
+                            environ=environ, action=action,
+                            repo=repo_name, user_id=user.user_id
+                       )
+            # store the make_lock for later evaluation in hooks
+            extras.update({'make_lock': make_lock,
+                           'locked_by': locked_by})
+
+        # set the environ variables for this request
+        os.environ['RC_SCM_DATA'] = json.dumps(extras)
+        log.debug('HOOKS extras is %s' % extras)
         baseui = make_ui('db')
         self.__inject_extras(repo_path, baseui, extras)
 
@@ -177,6 +196,9 @@
         except RepoError, e:
             if str(e).find('not found') != -1:
                 return HTTPNotFound()(environ, start_response)
+        except HTTPLockedRC, e:
+            log.debug('Repositry LOCKED ret code 423!')
+            return e(environ, start_response)
         except Exception:
             log.error(traceback.format_exc())
             return HTTPInternalServerError()(environ, start_response)
--- a/rhodecode/lib/subprocessio.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/subprocessio.py	Fri Aug 24 14:58:26 2012 +0200
@@ -25,7 +25,7 @@
 import os
 import subprocess
 import threading
-from collections import deque
+from rhodecode.lib.compat import deque, Event
 
 
 class StreamFeeder(threading.Thread):
@@ -89,16 +89,16 @@
         self.chunk_count_max = int(buffer_size / chunk_size) + 1
         self.chunk_size = chunk_size
 
-        self.data_added = threading.Event()
+        self.data_added = Event()
         self.data_added.clear()
 
-        self.keep_reading = threading.Event()
+        self.keep_reading = Event()
         self.keep_reading.set()
 
-        self.EOF = threading.Event()
+        self.EOF = Event()
         self.EOF.clear()
 
-        self.go = threading.Event()
+        self.go = Event()
         self.go.set()
 
     def stop(self):
--- a/rhodecode/lib/utils.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/utils.py	Fri Aug 24 14:58:26 2012 +0200
@@ -203,19 +203,24 @@
     return _get_repos(path)
 
 
-def is_valid_repo(repo_name, base_path):
+def is_valid_repo(repo_name, base_path, scm=None):
     """
-    Returns True if given path is a valid repository False otherwise
+    Returns True if given path is a valid repository False otherwise.
+    If scm param is given also compare if given scm is the same as expected 
+    from scm parameter
 
     :param repo_name:
     :param base_path:
+    :param scm:
 
     :return True: if given path is a valid repository
     """
     full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
 
     try:
-        get_scm(full_path)
+        scm_ = get_scm(full_path)
+        if scm:
+            return scm_[0] == scm
         return True
     except VCSError:
         return False
@@ -275,7 +280,7 @@
                 'ui', 'web', ]
 
 
-def make_ui(read_from='file', path=None, checkpaths=True):
+def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
     """
     A function that will read python rc files or database
     and make an mercurial ui object from read options
@@ -320,8 +325,8 @@
                 # force set push_ssl requirement to False, rhodecode
                 # handles that
                 baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
-
-        meta.Session.remove()
+        if clear_session:
+            meta.Session.remove()
     return baseui
 
 
--- a/rhodecode/lib/utils2.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/lib/utils2.py	Fri Aug 24 14:58:26 2012 +0200
@@ -25,7 +25,7 @@
 
 import re
 import time
-from datetime import datetime
+import datetime
 from pylons.i18n.translation import _, ungettext
 from rhodecode.lib.vcs.utils.lazy import LazyProperty
 
@@ -300,7 +300,7 @@
     deltas = {}
 
     # Get date parts deltas
-    now = datetime.now()
+    now = datetime.datetime.now()
     for part in order:
         deltas[part] = getattr(now, part) - getattr(prevdate, part)
 
@@ -435,6 +435,15 @@
         return time.mktime(dt.timetuple())
 
 
+def time_to_datetime(tm):
+    if tm:
+        if isinstance(tm, basestring):
+            try:
+                tm = float(tm)
+            except ValueError:
+                return
+        return datetime.datetime.fromtimestamp(tm)
+
 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
 
 
--- a/rhodecode/model/db.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/model/db.py	Fri Aug 24 14:58:26 2012 +0200
@@ -28,6 +28,7 @@
 import datetime
 import traceback
 import hashlib
+import time
 from collections import defaultdict
 
 from sqlalchemy import *
@@ -232,7 +233,9 @@
     HOOK_UPDATE = 'changegroup.update'
     HOOK_REPO_SIZE = 'changegroup.repo_size'
     HOOK_PUSH = 'changegroup.push_logger'
-    HOOK_PULL = 'preoutgoing.pull_logger'
+    HOOK_PRE_PUSH = 'prechangegroup.pre_push'
+    HOOK_PULL = 'outgoing.pull_logger'
+    HOOK_PRE_PULL = 'preoutgoing.pre_pull'
 
     ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
     ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
@@ -247,17 +250,17 @@
     @classmethod
     def get_builtin_hooks(cls):
         q = cls.query()
-        q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
-                                    cls.HOOK_REPO_SIZE,
-                                    cls.HOOK_PUSH, cls.HOOK_PULL]))
+        q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
+                                     cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
+                                     cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
         return q.all()
 
     @classmethod
     def get_custom_hooks(cls):
         q = cls.query()
-        q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
-                                    cls.HOOK_REPO_SIZE,
-                                    cls.HOOK_PUSH, cls.HOOK_PULL]))
+        q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
+                                      cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
+                                      cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
         q = q.filter(cls.ui_section == 'hooks')
         return q.all()
 
@@ -280,9 +283,13 @@
     __tablename__ = 'users'
     __table_args__ = (
         UniqueConstraint('username'), UniqueConstraint('email'),
+        Index('u_username_idx', 'username'),
+        Index('u_email_idx', 'email'),
         {'extend_existing': True, 'mysql_engine': 'InnoDB',
          'mysql_charset': 'utf8'}
     )
+    DEFAULT_USER = 'default'
+
     user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
     username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
     password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
@@ -323,26 +330,35 @@
         self._email = val.lower() if val else None
 
     @property
+    def firstname(self):
+        # alias for future
+        return self.name
+
+    @property
     def emails(self):
         other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
         return [self.email] + [x.email for x in other]
 
     @property
+    def username_and_name(self):
+        return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
+
+    @property
     def full_name(self):
-        return '%s %s' % (self.name, self.lastname)
+        return '%s %s' % (self.firstname, self.lastname)
 
     @property
     def full_name_or_username(self):
-        return ('%s %s' % (self.name, self.lastname)
-                if (self.name and self.lastname) else self.username)
+        return ('%s %s' % (self.firstname, self.lastname)
+                if (self.firstname and self.lastname) else self.username)
 
     @property
     def full_contact(self):
-        return '%s %s <%s>' % (self.name, self.lastname, self.email)
+        return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
 
     @property
     def short_contact(self):
-        return '%s %s' % (self.name, self.lastname)
+        return '%s %s' % (self.firstname, self.lastname)
 
     @property
     def is_admin(self):
@@ -572,6 +588,7 @@
     __tablename__ = 'repositories'
     __table_args__ = (
         UniqueConstraint('repo_name'),
+        Index('r_repo_name_idx', 'repo_name'),
         {'extend_existing': True, 'mysql_engine': 'InnoDB',
          'mysql_charset': 'utf8'},
     )
@@ -587,6 +604,8 @@
     description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
     created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
     landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
+    enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
+    _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
 
     fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
     group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
@@ -617,6 +636,21 @@
         return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
                                    self.repo_name)
 
+    @hybrid_property
+    def locked(self):
+        # always should return [user_id, timelocked]
+        if self._locked:
+            _lock_info = self._locked.split(':')
+            return int(_lock_info[0]), _lock_info[1]
+        return [None, None]
+
+    @locked.setter
+    def locked(self, val):
+        if val and isinstance(val, (list, tuple)):
+            self._locked = ':'.join(map(str, val))
+        else:
+            self._locked = None
+
     @classmethod
     def url_sep(cls):
         return URL_SEP
@@ -744,7 +778,7 @@
             if ui_.ui_key == 'push_ssl':
                 # force set push_ssl requirement to False, rhodecode
                 # handles that
-                baseui.setconfig(ui_.ui_section, ui_.ui_key, False)                
+                baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
 
         return baseui
 
@@ -793,6 +827,18 @@
 
         return data
 
+    @classmethod
+    def lock(cls, repo, user_id):
+        repo.locked = [user_id, time.time()]
+        Session().add(repo)
+        Session().commit()
+
+    @classmethod
+    def unlock(cls, repo):
+        repo.locked = None
+        Session().add(repo)
+        Session().commit()
+
     #==========================================================================
     # SCM PROPERTIES
     #==========================================================================
@@ -1467,6 +1513,7 @@
     revision = Column('revision', String(40), nullable=True)
     pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
     line_no = Column('line_no', Unicode(10), nullable=True)
+    hl_lines = Column('hl_lines', Unicode(512), nullable=True)
     f_path = Column('f_path', Unicode(1000), nullable=True)
     user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
     text = Column('text', Unicode(25000), nullable=False)
--- a/rhodecode/model/forms.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/model/forms.py	Fri Aug 24 14:58:26 2012 +0200
@@ -182,6 +182,7 @@
         private = v.StringBoolean(if_missing=False)
         enable_statistics = v.StringBoolean(if_missing=False)
         enable_downloads = v.StringBoolean(if_missing=False)
+        enable_locking = v.StringBoolean(if_missing=False)
         landing_rev = v.OneOf(landing_revs, hideList=True)
 
         if edit:
@@ -265,7 +266,7 @@
         hooks_changegroup_update = v.StringBoolean(if_missing=False)
         hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
         hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
-        hooks_preoutgoing_pull_logger = v.StringBoolean(if_missing=False)
+        hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
 
         extensions_largefiles = v.StringBoolean(if_missing=False)
         extensions_hgsubversion = v.StringBoolean(if_missing=False)
@@ -333,8 +334,8 @@
         org_ref = v.UnicodeString(strip=True, required=True)
         other_repo = v.UnicodeString(strip=True, required=True)
         other_ref = v.UnicodeString(strip=True, required=True)
-        revisions = v.Set(required=True)
-        review_members = v.Set(required=True)
+        revisions = All(v.NotReviewedRevisions()(), v.UniqueList(not_empty=True))
+        review_members = v.UniqueList(not_empty=True)
 
         pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
         pullrequest_desc = v.UnicodeString(strip=True, required=False)
--- a/rhodecode/model/scm.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/model/scm.py	Fri Aug 24 14:58:26 2012 +0200
@@ -571,34 +571,41 @@
         if not os.path.isdir(loc):
             os.makedirs(loc)
 
-        tmpl = pkg_resources.resource_string(
+        tmpl_post = pkg_resources.resource_string(
             'rhodecode', jn('config', 'post_receive_tmpl.py')
         )
+        tmpl_pre = pkg_resources.resource_string(
+            'rhodecode', jn('config', 'pre_receive_tmpl.py')
+        )
 
-        _hook_file = jn(loc, 'post-receive')
-        _rhodecode_hook = False
-        log.debug('Installing git hook in repo %s' % repo)
-        if os.path.exists(_hook_file):
-            # let's take a look at this hook, maybe it's rhodecode ?
-            log.debug('hook exists, checking if it is from rhodecode')
-            _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
-            with open(_hook_file, 'rb') as f:
-                data = f.read()
-                matches = re.compile(r'(?:%s)\s*=\s*(.*)'
-                                     % 'RC_HOOK_VER').search(data)
-                if matches:
-                    try:
-                        ver = matches.groups()[0]
-                        log.debug('got %s it is rhodecode' % (ver))
-                        _rhodecode_hook = True
-                    except:
-                        log.error(traceback.format_exc())
+        for h_type, tmpl in [('pre', tmpl_pre), ('post', tmpl_post)]:
+            _hook_file = jn(loc, '%s-receive' % h_type)
+            _rhodecode_hook = False
+            log.debug('Installing git hook in repo %s' % repo)
+            if os.path.exists(_hook_file):
+                # let's take a look at this hook, maybe it's rhodecode ?
+                log.debug('hook exists, checking if it is from rhodecode')
+                _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
+                with open(_hook_file, 'rb') as f:
+                    data = f.read()
+                    matches = re.compile(r'(?:%s)\s*=\s*(.*)'
+                                         % 'RC_HOOK_VER').search(data)
+                    if matches:
+                        try:
+                            ver = matches.groups()[0]
+                            log.debug('got %s it is rhodecode' % (ver))
+                            _rhodecode_hook = True
+                        except:
+                            log.error(traceback.format_exc())
+            else:
+                # there is no hook in this dir, so we want to create one
+                _rhodecode_hook = True
 
-        if _rhodecode_hook or force_create:
-            log.debug('writing hook file !')
-            with open(_hook_file, 'wb') as f:
-                tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
-                f.write(tmpl)
-            os.chmod(_hook_file, 0755)
-        else:
-            log.debug('skipping writing hook file')
+            if _rhodecode_hook or force_create:
+                log.debug('writing %s hook file !' % h_type)
+                with open(_hook_file, 'wb') as f:
+                    tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
+                    f.write(tmpl)
+                os.chmod(_hook_file, 0755)
+            else:
+                log.debug('skipping writing hook file')
--- a/rhodecode/model/validators.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/model/validators.py	Fri Aug 24 14:58:26 2012 +0200
@@ -10,17 +10,46 @@
 
 from formencode.validators import (
     UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
+    NotEmpty
 )
 from rhodecode.lib.utils import repo_name_slug
-from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User
+from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User,\
+    ChangesetStatus
 from rhodecode.lib.exceptions import LdapImportError
 from rhodecode.config.routing import ADMIN_PREFIX
+
 # silence warnings and pylint
-UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set
+UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
+    NotEmpty
 
 log = logging.getLogger(__name__)
 
 
+class UniqueList(formencode.FancyValidator):
+    """
+    Unique List !
+    """
+    messages = dict(
+        empty=_('Value cannot be an empty list'),
+        missing_value=_('Value cannot be an empty list'),
+    )
+
+    def _to_python(self, value, state):
+        if isinstance(value, list):
+            return value
+        elif isinstance(value, set):
+            return list(value)
+        elif isinstance(value, tuple):
+            return list(value)
+        elif value is None:
+            return []
+        else:
+            return [value]
+
+    def empty_value(self, value):
+        return []
+
+
 class StateObj(object):
     """
     this is needed to translate the messages using _() in validators
@@ -377,10 +406,10 @@
                 ## initially check if it's at least the proper URL
                 ## or does it pass basic auth
                 MercurialRepository._check_url(url)
-                httppeer(make_ui('db'), url)._capabilities()
+                httppeer(ui, url)._capabilities()
             elif url.startswith('svn+http'):
                 from hgsubversion.svnrepo import svnremoterepo
-                svnremoterepo(make_ui('db'), url).capabilities
+                svnremoterepo(ui, url).capabilities
             elif url.startswith('git+http'):
                 raise NotImplementedError()
 
@@ -410,7 +439,7 @@
                 pass
             else:
                 try:
-                    url_handler(repo_type, url, make_ui('db'))
+                    url_handler(repo_type, url, make_ui('db', clear_session=False))
                 except Exception:
                     log.exception('Url validation failed')
                     msg = M(self, 'clone_uri')
@@ -599,3 +628,33 @@
                 )
 
     return _validator
+
+
+def NotReviewedRevisions():
+    class _validator(formencode.validators.FancyValidator):
+        messages = {
+            'rev_already_reviewed':
+                  _(u'Revisions %(revs)s are already part of pull request '
+                    'or have set status')
+        }
+
+        def validate_python(self, value, state):
+            # check revisions if they are not reviewed, or a part of another
+            # pull request
+            statuses = ChangesetStatus.query()\
+                .filter(ChangesetStatus.revision.in_(value)).all()
+            errors = []
+            for cs in statuses:
+                if cs.pull_request_id:
+                    errors.append(['pull_req', cs.revision[:12]])
+                elif cs.status:
+                    errors.append(['status', cs.revision[:12]])
+
+            if errors:
+                revs = ','.join([x[1] for x in errors])
+                msg = M(self, 'rev_already_reviewed', state, revs=revs)
+                raise formencode.Invalid(msg, value, state,
+                    error_dict=dict(revisions=revs)
+                )
+
+    return _validator
--- a/rhodecode/public/css/style.css	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/public/css/style.css	Fri Aug 24 14:58:26 2012 +0200
@@ -219,6 +219,11 @@
     margin-bottom: 0;
     margin-top: 5px;
 }
+
+.empty_data{
+    color:#B9B9B9;	
+}
+
 a.permalink{
 	visibility: hidden;
 }
--- a/rhodecode/public/js/rhodecode.js	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/public/js/rhodecode.js	Fri Aug 24 14:58:26 2012 +0200
@@ -398,7 +398,7 @@
 	
 	// create event for hide button
 	form = new YAHOO.util.Element(form);
-	var form_hide_button = new YAHOO.util.Element(form.getElementsByClassName('hide-inline-form')[0]);
+	var form_hide_button = new YAHOO.util.Element(YUD.getElementsByClassName('hide-inline-form',null,form)[0]);
 	form_hide_button.on('click', function(e) {
 		var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
 		if(YUD.hasClass(newtr.nextElementSibling,'inline-comments-button')){
@@ -422,12 +422,12 @@
 		  return
 	  }
 	  var submit_url = AJAX_COMMENT_URL;
-	  var _td = tr.getElementsByClassName('code')[0];
+	  var _td = YUD.getElementsByClassName('code',null,tr)[0];
 	  if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context') || YUD.hasClass(_td,'no-comment')){
 		  return
 	  }	
 	  YUD.addClass(tr,'form-open');
-	  var node = tr.parentNode.parentNode.parentNode.getElementsByClassName('full_f_path')[0];
+	  var node = YUD.getElementsByClassName('full_f_path',null,tr.parentNode.parentNode.parentNode)[0];
 	  var f_path = YUD.getAttribute(node,'path');
 	  var lineno = getLineNo(tr);
 	  var form = createInlineForm(tr, f_path, lineno, submit_url);
@@ -447,8 +447,8 @@
 	  
 	  var f = YUD.get(form);
 	  
-	  var overlay = f.getElementsByClassName('overlay')[0];
-	  var _form = f.getElementsByClassName('inline-form')[0];
+	  var overlay = YUD.getElementsByClassName('overlay',null,f)[0];
+	  var _form = YUD.getElementsByClassName('inline-form',null,f)[0];
 	  
 	  form.on('submit',function(e){
 		  YUE.preventDefault(e);
@@ -568,8 +568,8 @@
 		  // next element are comments !
 		  if(YUD.hasClass(n,'inline-comments')){
 			  last_node = n;
-			  //also remove the comment button from previos
-			  var comment_add_buttons = last_node.getElementsByClassName('add-comment');
+			  //also remove the comment button from previous
+			  var comment_add_buttons = YUD.getElementsByClassName('add-comment',null,last_node);
 			  for(var i=0;i<comment_add_buttons.length;i++){
 				  var b = comment_add_buttons[i];
 				  b.parentNode.removeChild(b);
@@ -582,7 +582,7 @@
 	  
     var add = createInlineAddButton(target_tr);
     // get the comment div
-    var comment_block = last_node.getElementsByClassName('comment')[0];
+    var comment_block = YUD.getElementsByClassName('comment',null,last_node)[0];
     // attach add button
     YUD.insertAfter(add,comment_block);	
 }
@@ -889,7 +889,7 @@
 		success:function(o){
 		    var obj = YUD.get(String("notification_"+notification_id));
 		    YUD.removeClass(obj, 'unread');
-		    var r_button = obj.children[0].getElementsByClassName('read-notification')[0]
+		    var r_button = YUD.getElementsByClassName('read-notification',null,obj.children[0])[0];
 		    
 		    if(r_button.parentNode !== undefined){
 		    	r_button.parentNode.removeChild(r_button);
@@ -1737,4 +1737,4 @@
 		    }
 		});
 	}
-}
\ No newline at end of file
+}
--- a/rhodecode/templates/admin/notifications/notifications_data.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/admin/notifications/notifications_data.html	Fri Aug 24 14:58:26 2012 +0200
@@ -10,7 +10,7 @@
   <div id="notification_${notification.notification.notification_id}" class="container ${unread(notification.read)}">
     <div class="notification-header">
       <div class="gravatar">
-          <img alt="gravatar" src="${h.gravatar_url(h.email(notification.notification.created_by_user.email),24)}"/>
+          <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(notification.notification.created_by_user.email),24)}"/>
       </div>
       <div class="desc ${unread(notification.read)}">
       <a href="${url('notification', notification_id=notification.notification.notification_id)}">${notification.notification.description}</a>
--- a/rhodecode/templates/admin/notifications/show_notification.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/admin/notifications/show_notification.html	Fri Aug 24 14:58:26 2012 +0200
@@ -30,7 +30,7 @@
       <div id="notification_${c.notification.notification_id}">
         <div class="notification-header">
           <div class="gravatar">
-              <img alt="gravatar" src="${h.gravatar_url(h.email(c.notification.created_by_user.email),24)}"/>
+              <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(c.notification.created_by_user.email),24)}"/>
           </div>
           <div class="desc">
               ${c.notification.description}
--- a/rhodecode/templates/admin/repos/repo_edit.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/admin/repos/repo_edit.html	Fri Aug 24 14:58:26 2012 +0200
@@ -108,6 +108,15 @@
                 </div>
             </div>
             <div class="field">
+                <div class="label label-checkbox">
+                    <label for="enable_locking">${_('Enable locking')}:</label>
+                </div>
+                <div class="checkboxes">
+                    ${h.checkbox('enable_locking',value="True")}
+                    <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span>
+                </div>
+            </div>            
+            <div class="field">
                 <div class="label">
                     <label for="user">${_('Owner')}:</label>
                 </div>
@@ -196,26 +205,31 @@
                 </div>
                <div class="field" style="border:none;color:#888">
                <ul>
-                    <li>${_('''All actions made on this repository will be accessible to everyone in public journal''')}
+                    <li>${_('All actions made on this repository will be accessible to everyone in public journal')}
                     </li>
                </ul>
                </div>
         </div>
         ${h.end_form()}
 
-        <h3>${_('Delete')}</h3>
-        ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
+        <h3>${_('Locking')}</h3>
+        ${h.form(url('repo_locking', repo_name=c.repo_info.repo_name),method='put')}
         <div class="form">
            <div class="fields">
-               ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
+              %if c.repo_info.locked[0]:
+               ${h.submit('set_unlock' ,_('Unlock locked repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to unlock repository')+"');")}
+               ${'Locked by %s on %s' % (h.person_by_id(c.repo_info.locked[0]),h.fmt_date(h.time_to_datetime(c.repo_info.locked[1])))}
+              %else:
+                ${h.submit('set_lock',_('lock repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to lock repository')+"');")}
+                ${_('Repository is not locked')}
+              %endif
            </div>
            <div class="field" style="border:none;color:#888">
            <ul>
-                <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
-                         If you need fully delete it from filesystem please do it manually''')}
+                <li>${_('Force locking on repository. Works only when anonymous access is disabled')}
                 </li>
            </ul>
-           </div>
+           </div>           
         </div>
         ${h.end_form()}
 
@@ -231,10 +245,24 @@
                     <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
                </ul>
                </div>
-        </div>
+        </div>        
         ${h.end_form()}
-
+        
+        <h3>${_('Delete')}</h3>
+        ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
+        <div class="form">
+           <div class="fields">
+               ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
+           </div>
+           <div class="field" style="border:none;color:#888">
+           <ul>
+                <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
+                         If you need fully delete it from filesystem please do it manually''')}
+                </li>
+           </ul>
+           </div>
+        </div>
+        ${h.end_form()}        
 </div>
 
-
 </%def>
--- a/rhodecode/templates/admin/settings/settings.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/admin/settings/settings.html	Fri Aug 24 14:58:26 2012 +0200
@@ -211,8 +211,8 @@
                         <label for="hooks_changegroup_push_logger">${_('Log user push commands')}</label>
                     </div>
                     <div class="checkbox">
-                        ${h.checkbox('hooks_preoutgoing_pull_logger','True')}
-                        <label for="hooks_preoutgoing_pull_logger">${_('Log user pull commands')}</label>
+                        ${h.checkbox('hooks_outgoing_pull_logger','True')}
+                        <label for="hooks_outgoing_pull_logger">${_('Log user pull commands')}</label>
                     </div>
 				</div>
                 <div class="input" style="margin-top:10px">
--- a/rhodecode/templates/admin/users/user_edit.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/admin/users/user_edit.html	Fri Aug 24 14:58:26 2012 +0200
@@ -183,7 +183,7 @@
            %for section in sorted(c.perm_user.permissions.keys()):
               <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
               %if not c.perm_user.permissions[section]:
-                  <span style="color:#B9B9B9">${_('Nothing here yet')}</span>
+                  <span class="empty_data">${_('Nothing here yet')}</span>
               %else:
               <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
                <table id="tbl_list_${section}">
--- a/rhodecode/templates/admin/users/user_edit_my_account.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/admin/users/user_edit_my_account.html	Fri Aug 24 14:58:26 2012 +0200
@@ -96,8 +96,11 @@
     </div>
     <div id="my" class="table" style="display:none">
     </div>
-    <div id="pullrequests" class="table" style="display:none">
+    <div id="pullrequests" class="table" style="display:none"></div>
 </div>
+
+
+
 <script type="text/javascript">
 var filter_activate = function(){
     var nodes = YUQ('#my tr td a.repo_name');
--- a/rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/admin/users/user_edit_my_account_pullrequests.html	Fri Aug 24 14:58:26 2012 +0200
@@ -1,22 +1,30 @@
 
 <div class="pullrequests_section_head">${_('Opened by me')}</div>
 <ul>
-    %for pull_request in c.my_pull_requests:
-    <li>
-    <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
-    ${_('Pull request #%s opened on %s') % (pull_request.pull_request_id, h.fmt_date(pull_request.created_on))}
-    </a>
-    </li>
-    %endfor
+    %if c.my_pull_requests:
+      %for pull_request in c.my_pull_requests:
+      <li>
+      <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
+      ${_('Pull request #%s opened on %s') % (pull_request.pull_request_id, h.fmt_date(pull_request.created_on))}
+      </a>
+      </li>
+      %endfor
+   %else:
+    <li><span class="empty_data">${_('Nothing here yet')}</span></li>
+   %endif
 </ul>
 
 <div class="pullrequests_section_head">${_('I participate in')}</div>
 <ul>
-    %for pull_request in c.participate_in_pull_requests:
-    <li>
-    <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
-    ${_('Pull request #%s opened by %s on %s') % (pull_request.pull_request_id, pull_request.author.full_name, h.fmt_date(pull_request.created_on))}
-    </a>
-    </li>
-    %endfor
+    %if c.my_pull_requests:
+      %for pull_request in c.participate_in_pull_requests:
+      <li>
+      <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
+      ${_('Pull request #%s opened by %s on %s') % (pull_request.pull_request_id, pull_request.author.full_name, h.fmt_date(pull_request.created_on))}
+      </a>
+      </li>
+      %endfor
+    %else:
+     <li><span class="empty_data">${_('Nothing here yet')}</span></li>
+    %endif
 </ul>
--- a/rhodecode/templates/changelog/changelog.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/changelog/changelog.html	Fri Aug 24 14:58:26 2012 +0200
@@ -59,7 +59,7 @@
 							</div>
 							<div class="author">
 								<div class="gravatar">
-									<img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),16)}"/>
+									<img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),16)}"/>
 								</div>
 								<div title="${cs.author}" class="user">${h.shorter(h.person(cs.author),22)}</div>
 							</div>
--- a/rhodecode/templates/changeset/changeset.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/changeset/changeset.html	Fri Aug 24 14:58:26 2012 +0200
@@ -53,7 +53,7 @@
 	             <div class="left">
 	                 <div class="author">
 	                     <div class="gravatar">
-	                         <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),20)}"/>
+	                         <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(c.changeset.author),20)}"/>
 	                     </div>
 	                     <span>${h.person(c.changeset.author)}</span><br/>
 	                     <span><a href="mailto:${h.email_or_none(c.changeset.author)}">${h.email_or_none(c.changeset.author)}</a></span><br/>
@@ -149,6 +149,9 @@
           YUE.on(YUQ('.show-inline-comments'),'change',function(e){
               var show = 'none';
               var target = e.currentTarget;
+              if(target == null){
+                  target = this;
+              }
               if(target.checked){
                   var show = ''
               }
@@ -165,6 +168,9 @@
 
           YUE.on(YUQ('.line'),'click',function(e){
               var tr = e.currentTarget;
+              if(tr == null){
+                  tr = this;
+              }
               injectInlineForm(tr);
           });
 
--- a/rhodecode/templates/changeset/changeset_range.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/changeset/changeset_range.html	Fri Aug 24 14:58:26 2012 +0200
@@ -37,7 +37,7 @@
 			<table class="compare_view_commits noborder">
             %for cnt,cs in enumerate(c.cs_ranges):
                 <tr>
-                <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),14)}"/></div></td>
+                <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),14)}"/></div></td>
                 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</td>
                 <td><div class="author">${h.person(cs.author)}</div></td>
                 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
@@ -71,7 +71,7 @@
           <h3 style="padding-top:8px;">
           <a class="tooltip" title="${h.tooltip(cs.message)}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}">${'r%s:%s' % (cs.revision,h.short_id(cs.raw_id))}</a>
            <div class="gravatar">
-               <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),20)}"/>
+               <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),20)}"/>
            </div>
            </h3>
           ${diff_block.diff_block(c.changes[cs.raw_id])}
--- a/rhodecode/templates/compare/compare_cs.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/compare/compare_cs.html	Fri Aug 24 14:58:26 2012 +0200
@@ -6,7 +6,7 @@
   %else:
     %for cnt, cs in enumerate(c.cs_ranges):
         <tr>
-        <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),14)}"/></div></td>
+        <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),14)}"/></div></td>
         <td>
           %if cs.raw_id in c.statuses:
             <div title="${c.statuses[cs.raw_id][1]}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cs.raw_id][0])}" /></div>
--- a/rhodecode/templates/files/files_source.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/files/files_source.html	Fri Aug 24 14:58:26 2012 +0200
@@ -48,7 +48,7 @@
         </div>
         <div class="author">
             <div class="gravatar">
-                <img alt="gravatar" src="${h.gravatar_url(h.email(c.file.changeset.author),16)}"/>
+                <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(c.file.changeset.author),16)}"/>
             </div>
             <div title="${c.file.changeset.author}" class="user">${h.person(c.file.changeset.author)}</div>
         </div>
--- a/rhodecode/templates/pullrequests/pullrequest.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/pullrequests/pullrequest.html	Fri Aug 24 14:58:26 2012 +0200
@@ -53,7 +53,7 @@
                     <img id="other_repo_gravatar" alt="gravatar" src=""/>
                 </div>
                 <span style="font-size: 20px">
-                ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref','',c.other_refs,class_='refs')}
+                ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref','',c.default_revs,class_='refs')}
                 </span>
                  <div id="other_repo_desc" style="padding:5px 3px 3px 42px;"></div>
             </div>
@@ -174,6 +174,7 @@
     	  YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
     	  YUD.get('other_repo_gravatar').src = other_repos_info[repo_name]['gravatar'];
     	  YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description'];
+    	  YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
       })
   }
   YUE.on('refresh','click',function(e){
--- a/rhodecode/templates/search/search_commit.html	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/templates/search/search_commit.html	Fri Aug 24 14:58:26 2012 +0200
@@ -12,7 +12,7 @@
             <div class="left">
                 <div class="author">
                     <div class="gravatar">
-                        <img alt="gravatar" src="${h.gravatar_url(h.email(sr['author']),20)}"/>
+                        <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(sr['author']),20)}"/>
                     </div>
                     <span>${h.person(sr['author'])}</span><br/>
                     <span><a href="mailto:${h.email_or_none(sr['author'])}">${h.email_or_none(sr['author'])}</a></span><br/>
--- a/rhodecode/tests/__init__.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/tests/__init__.py	Fri Aug 24 14:58:26 2012 +0200
@@ -41,12 +41,13 @@
 __all__ = [
     'parameterized', 'environ', 'url', 'get_new_dir', 'TestController',
     'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO',
-    'HG_FORK', 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_REGULAR_LOGIN',
-    'TEST_USER_REGULAR_PASS', 'TEST_USER_REGULAR_EMAIL',
-    'TEST_USER_REGULAR2_LOGIN', 'TEST_USER_REGULAR2_PASS',
-    'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO', 'TEST_HG_REPO_CLONE',
-    'TEST_HG_REPO_PULL', 'TEST_GIT_REPO', 'TEST_GIT_REPO_CLONE',
-    'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO', 'GIT_REMOTE_REPO', 'SCM_TESTS',
+    'HG_FORK', 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS',
+    'TEST_USER_REGULAR_LOGIN', 'TEST_USER_REGULAR_PASS',
+    'TEST_USER_REGULAR_EMAIL', 'TEST_USER_REGULAR2_LOGIN',
+    'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO',
+    'TEST_HG_REPO_CLONE', 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO',
+    'TEST_GIT_REPO_CLONE', 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO',
+    'GIT_REMOTE_REPO', 'SCM_TESTS',
 ]
 
 # Invoke websetup with the current config file
--- a/rhodecode/tests/functional/test_search.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/tests/functional/test_search.py	Fri Aug 24 14:58:26 2012 +0200
@@ -90,3 +90,10 @@
                      'type': 'commit'})
 
         response.mustcontain('1 results')
+
+    def test_search_file_name(self):
+        self.log_user()
+        response = self.app.get(url(controller='search', action='index'),
+                    {'q': 'README.rst', 'type': 'path'})
+
+        response.mustcontain('2 results')
\ No newline at end of file
--- a/rhodecode/tests/scripts/test_scm_operations.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/tests/scripts/test_scm_operations.py	Fri Aug 24 14:58:26 2012 +0200
@@ -1,9 +1,12 @@
 # -*- coding: utf-8 -*-
 """
-    rhodecode.tests.test_hg_operations
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    rhodecode.tests.test_scm_operations
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-    Test suite for making push/pull operations
+    Test suite for making push/pull operations.
+    Run using::
+
+     RC_WHOOSH_TEST_DISABLE=1 nosetests rhodecode/tests/scripts/test_scm_operations.py
 
     :created_on: Dec 30, 2010
     :author: marcink
@@ -24,47 +27,19 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
-import time
-import sys
-import shutil
-import logging
-
+import tempfile
 from os.path import join as jn
 from os.path import dirname as dn
 
 from tempfile import _RandomNameSequence
 from subprocess import Popen, PIPE
 
-from paste.deploy import appconfig
-from pylons import config
-from sqlalchemy import engine_from_config
-
-from rhodecode.lib.utils import add_cache
-from rhodecode.model import init_model
-from rhodecode.model import meta
+from rhodecode.tests import *
 from rhodecode.model.db import User, Repository, UserLog
-from rhodecode.lib.auth import get_crypt_password
-
-from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
-from rhodecode.config.environment import load_environment
-
-rel_path = dn(dn(dn(os.path.abspath(__file__))))
+from rhodecode.model.meta import Session
 
-conf = appconfig('config:%s' % sys.argv[1], relative_to=rel_path)
-load_environment(conf.global_conf, conf.local_conf)
-
-add_cache(conf)
-
-USER = 'test_admin'
-PASS = 'test12'
-HOST = '127.0.0.1:5000'
-DEBUG = False
-print 'DEBUG:', DEBUG
-log = logging.getLogger(__name__)
-
-engine = engine_from_config(conf, 'sqlalchemy.db1.')
-init_model(engine)
-sa = meta.Session()
+DEBUG = True
+HOST = '127.0.0.1:5000'  # test host
 
 
 class Command(object):
@@ -73,13 +48,13 @@
         self.cwd = cwd
 
     def execute(self, cmd, *args):
-        """Runs command on the system with given ``args``.
+        """
+        Runs command on the system with given ``args``.
         """
 
         command = cmd + ' ' + ' '.join(args)
-        log.debug('Executing %s' % command)
         if DEBUG:
-            print command
+            print '*** CMD %s ***' % command
         p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
         stdout, stderr = p.communicate()
         if DEBUG:
@@ -87,324 +62,181 @@
         return stdout, stderr
 
 
-def test_wrapp(func):
-
-    def __wrapp(*args, **kwargs):
-        print '>>>%s' % func.__name__
-        try:
-            res = func(*args, **kwargs)
-        except Exception, e:
-            print ('###############\n-'
-                   '--%s failed %s--\n'
-                   '###############\n' % (func.__name__, e))
-            sys.exit()
-        print '++OK++'
-        return res
-    return __wrapp
+def _get_tmp_dir():
+    return tempfile.mkdtemp(prefix='rc_integration_test')
 
 
-def create_test_user(force=True):
-    print '\tcreating test user'
-
-    user = User.get_by_username(USER)
-
-    if force and user is not None:
-        print '\tremoving current user'
-        for repo in Repository.query().filter(Repository.user == user).all():
-            sa.delete(repo)
-        sa.delete(user)
-        sa.commit()
-
-    if user is None or force:
-        print '\tcreating new one'
-        new_usr = User()
-        new_usr.username = USER
-        new_usr.password = get_crypt_password(PASS)
-        new_usr.email = 'mail@mail.com'
-        new_usr.name = 'test'
-        new_usr.lastname = 'lasttestname'
-        new_usr.active = True
-        new_usr.admin = True
-        sa.add(new_usr)
-        sa.commit()
-
-    print '\tdone'
-
-
-def create_test_repo(force=True):
-    from rhodecode.model.repo import RepoModel
-
-    user = User.get_by_username(USER)
-    if user is None:
-        raise Exception('user not found')
-
-    repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
-
-    if repo is None:
-        print '\trepo not found creating'
-
-        form_data = {'repo_name':HG_REPO,
-                     'repo_type':'hg',
-                     'private':False,
-                     'clone_uri':'' }
-        rm = RepoModel(sa)
-        rm.base_path = '/home/hg'
-        rm.create(form_data, user)
+def _construct_url(repo, dest=None, **kwargs):
+    if dest is None:
+        #make temp clone
+        dest = _get_tmp_dir()
+    params = {
+        'user': TEST_USER_ADMIN_LOGIN,
+        'passwd': TEST_USER_ADMIN_PASS,
+        'host': HOST,
+        'cloned_repo': repo,
+        'dest': dest
+    }
+    params.update(**kwargs)
+    if params['user'] and params['passwd']:
+        _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s %(dest)s' % params
+    else:
+        _url = 'http://(host)s/%(cloned_repo)s %(dest)s' % params
+    return _url
 
 
 def set_anonymous_access(enable=True):
-    user = User.get_by_username('default')
+    user = User.get_by_username(User.DEFAULT_USER)
     user.active = enable
-    sa.add(user)
-    sa.commit()
+    Session().add(user)
+    Session().commit()
     print '\tanonymous access is now:', enable
-    if enable != User.get_by_username('default').active:
+    if enable != User.get_by_username(User.DEFAULT_USER).active:
         raise Exception('Cannot set anonymous access')
 
 
-def get_anonymous_access():
-    user = User.get_by_username('default')
-    return user.active
+def setup_module():
+    #DISABLE ANONYMOUS ACCESS
+    set_anonymous_access(False)
+
+
+def test_clone_hg_repo_by_admin():
+    clone_url = _construct_url(HG_REPO)
+    stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+
+    assert 'requesting all changes' in stdout
+    assert 'adding changesets' in stdout
+    assert 'adding manifests' in stdout
+    assert 'adding file changes' in stdout
+
+    assert stderr == ''
 
 
-#==============================================================================
-# TESTS
-#==============================================================================
-@test_wrapp
-def test_clone_with_credentials(no_errors=False):
-    cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
-
-    try:
-        shutil.rmtree(path, ignore_errors=True)
-        os.makedirs(path)
-        #print 'made dirs %s' % jn(path)
-    except OSError:
-        raise
+def test_clone_git_repo_by_admin():
+    clone_url = _construct_url(GIT_REPO)
+    stdout, stderr = Command('/tmp').execute('git clone', clone_url)
 
-    print '\tchecking if anonymous access is enabled'
-    anonymous_access = get_anonymous_access()
-    if anonymous_access:
-        print '\tenabled, disabling it '
-        set_anonymous_access(enable=False)
-
-    clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
-                  {'user':USER,
-                   'pass':PASS,
-                   'host':HOST,
-                   'cloned_repo':HG_REPO,
-                   'dest':path}
-
-    stdout, stderr = Command(cwd).execute('hg clone', clone_url)
-
-    if no_errors is False:
-        assert """adding file changes""" in stdout, 'no messages about cloning'
-        assert """abort""" not in stderr , 'got error from clone'
+    assert 'Cloning into' in stdout
+    assert stderr == ''
 
 
-@test_wrapp
-def test_clone_anonymous():
-    cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
+def test_clone_wrong_credentials_hg():
+    clone_url = _construct_url(HG_REPO, passwd='bad!')
+    stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+    assert 'abort: authorization failed' in stderr
 
-    try:
-        shutil.rmtree(path, ignore_errors=True)
-        os.makedirs(path)
-        #print 'made dirs %s' % jn(path)
-    except OSError:
-        raise
+
+def test_clone_wrong_credentials_git():
+    clone_url = _construct_url(GIT_REPO, passwd='bad!')
+    stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+    assert 'fatal: Authentication failed' in stderr
 
 
-    print '\tchecking if anonymous access is enabled'
-    anonymous_access = get_anonymous_access()
-    if not anonymous_access:
-        print '\tnot enabled, enabling it '
-        set_anonymous_access(enable=True)
+def test_clone_git_dir_as_hg():
+    clone_url = _construct_url(GIT_REPO)
+    stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+    assert 'HTTP Error 404: Not Found' in stderr
+
 
-    clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \
-                  {'user':USER,
-                   'pass':PASS,
-                   'host':HOST,
-                   'cloned_repo':HG_REPO,
-                   'dest':path}
+def test_clone_hg_repo_as_git():
+    clone_url = _construct_url(HG_REPO)
+    stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+    assert 'not found: did you run git update-server-info on the server' in stderr
 
-    stdout, stderr = Command(cwd).execute('hg clone', clone_url)
 
-    assert """adding file changes""" in stdout, 'no messages about cloning'
-    assert """abort""" not in stderr , 'got error from clone'
-
-    #disable if it was enabled
-    if not anonymous_access:
-        print '\tdisabling anonymous access'
-        set_anonymous_access(enable=False)
+def test_clone_non_existing_path_hg():
+    clone_url = _construct_url('trololo')
+    stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+    assert 'HTTP Error 404: Not Found' in stderr
 
 
-@test_wrapp
-def test_clone_wrong_credentials():
-    cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
-
-    try:
-        shutil.rmtree(path, ignore_errors=True)
-        os.makedirs(path)
-        #print 'made dirs %s' % jn(path)
-    except OSError:
-        raise
-
-    print '\tchecking if anonymous access is enabled'
-    anonymous_access = get_anonymous_access()
-    if anonymous_access:
-        print '\tenabled, disabling it '
-        set_anonymous_access(enable=False)
-
-    clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
-                  {'user':USER + 'error',
-                   'pass':PASS,
-                   'host':HOST,
-                   'cloned_repo':HG_REPO,
-                   'dest':path}
-
-    stdout, stderr = Command(cwd).execute('hg clone', clone_url)
-
-    if not """abort: authorization failed"""  in stderr:
-        raise Exception('Failure')
+def test_clone_non_existing_path_git():
+    clone_url = _construct_url('trololo')
+    stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+    assert 'not found: did you run git update-server-info on the server' in stderr
 
 
-@test_wrapp
-def test_pull():
-    pass
-
-
-@test_wrapp
-def test_push_modify_file(f_name='setup.py'):
-    cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
-    modified_file = jn(TESTS_TMP_PATH, HG_REPO, f_name)
-    for i in xrange(5):
-        cmd = """echo 'added_line%s' >> %s""" % (i, modified_file)
-        Command(cwd).execute(cmd)
+def test_push_new_file_hg():
+    DEST = _get_tmp_dir()
+    clone_url = _construct_url(HG_REPO, dest=DEST)
+    stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
 
-        cmd = """hg ci -m 'changed file %s' %s """ % (i, modified_file)
-        Command(cwd).execute(cmd)
-
-    Command(cwd).execute('hg push %s' % jn(TESTS_TMP_PATH, HG_REPO))
-
-
-@test_wrapp
-def test_push_new_file(commits=15, with_clone=True):
-
-    if with_clone:
-        test_clone_with_credentials(no_errors=True)
-
-    cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
+    # commit some stuff into this repo
+    cwd = path = jn(DEST)
     added_file = jn(path, '%ssetupążźć.py' % _RandomNameSequence().next())
-
     Command(cwd).execute('touch %s' % added_file)
-
     Command(cwd).execute('hg add %s' % added_file)
 
-    for i in xrange(commits):
+    for i in xrange(3):
         cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
         Command(cwd).execute(cmd)
 
-        cmd = """hg ci -m 'commited new %s' -u '%s' %s """ % (i,
-                                'Marcin Kuźminski <marcin@python-blog.com>',
-                                added_file)
+        cmd = """hg ci -m 'commited new %s' -u '%s' %s """ % (
+                i,
+                'Marcin Kuźminski <marcin@python-blog.com>',
+                added_file
+        )
         Command(cwd).execute(cmd)
+    # PUSH it back
+    clone_url = _construct_url(HG_REPO, dest='')
+    stdout, stderr = Command(cwd).execute('hg push --verbose', clone_url)
 
-    push_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
-                  {'user':USER,
-                   'pass':PASS,
-                   'host':HOST,
-                   'cloned_repo':HG_REPO,
-                   'dest':jn(TESTS_TMP_PATH, HG_REPO)}
-
-    Command(cwd).execute('hg push --verbose --debug %s' % push_url)
+    assert 'pushing to' in stdout
+    assert 'Repository size' in stdout
+    assert 'Last revision is now' in stdout
 
 
-@test_wrapp
-def test_push_wrong_credentials():
-    cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
-    clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
-                  {'user':USER + 'xxx',
-                   'pass':PASS,
-                   'host':HOST,
-                   'cloned_repo':HG_REPO,
-                   'dest':jn(TESTS_TMP_PATH, HG_REPO)}
-
-    modified_file = jn(TESTS_TMP_PATH, HG_REPO, 'setup.py')
-    for i in xrange(5):
-        cmd = """echo 'added_line%s' >> %s""" % (i, modified_file)
-        Command(cwd).execute(cmd)
-
-        cmd = """hg ci -m 'commited %s' %s """ % (i, modified_file)
-        Command(cwd).execute(cmd)
+def test_push_new_file_git():
+    DEST = _get_tmp_dir()
+    clone_url = _construct_url(GIT_REPO, dest=DEST)
+    stdout, stderr = Command('/tmp').execute('git clone', clone_url)
 
-    Command(cwd).execute('hg push %s' % clone_url)
-
-
-@test_wrapp
-def test_push_wrong_path():
-    cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
-    added_file = jn(path, 'somefile.py')
+    # commit some stuff into this repo
+    cwd = path = jn(DEST)
+    added_file = jn(path, '%ssetupążźć.py' % _RandomNameSequence().next())
+    Command(cwd).execute('touch %s' % added_file)
+    Command(cwd).execute('git add %s' % added_file)
 
-    try:
-        shutil.rmtree(path, ignore_errors=True)
-        os.makedirs(path)
-        print '\tmade dirs %s' % jn(path)
-    except OSError:
-        raise
-
-    Command(cwd).execute("""echo '' > %s""" % added_file)
-    Command(cwd).execute("""hg init %s""" % path)
-    Command(cwd).execute("""hg add %s""" % added_file)
-
-    for i in xrange(2):
+    for i in xrange(3):
         cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
         Command(cwd).execute(cmd)
 
-        cmd = """hg ci -m 'commited new %s' %s """ % (i, added_file)
+        cmd = """git ci -m 'commited new %s' --author '%s' %s """ % (
+                i,
+                'Marcin Kuźminski <marcin@python-blog.com>',
+                added_file
+        )
         Command(cwd).execute(cmd)
+    # PUSH it back
+    clone_url = _construct_url(GIT_REPO, dest='')
+    stdout, stderr = Command(cwd).execute('git push --verbose', clone_url)
 
-    clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
-                  {'user':USER,
-                   'pass':PASS,
-                   'host':HOST,
-                   'cloned_repo':HG_REPO + '_error',
-                   'dest':jn(TESTS_TMP_PATH, HG_REPO)}
-
-    stdout, stderr = Command(cwd).execute('hg push %s' % clone_url)
-    if not """abort: HTTP Error 403: Forbidden"""  in stderr:
-        raise Exception('Failure')
+    #WTF git stderr ?!
+    assert 'master -> master' in stderr
 
 
-@test_wrapp
-def get_logs():
-    return UserLog.query().all()
+def test_push_modify_existing_file_hg():
+    assert 0
 
 
-@test_wrapp
-def test_logs(initial):
-    logs = UserLog.query().all()
-    operations = 4
-    if len(initial) + operations != len(logs):
-        raise Exception("missing number of logs initial:%s vs current:%s" % \
-                            (len(initial), len(logs)))
+def test_push_modify_existing_file_git():
+    assert 0
+
+
+def test_push_wrong_credentials_hg():
+    assert 0
 
 
-if __name__ == '__main__':
-    create_test_user(force=False)
-    create_test_repo()
+def test_push_wrong_credentials_git():
+    assert 0
+
+
+def test_push_back_to_wrong_url_hg():
+    assert 0
 
-    initial_logs = get_logs()
-    print 'initial activity logs: %s' % len(initial_logs)
-    s = time.time()
-    #test_push_modify_file()
-    test_clone_with_credentials()
-    test_clone_wrong_credentials()
 
-    test_push_new_file(commits=2, with_clone=True)
-
-    test_clone_anonymous()
-    test_push_wrong_path()
+def test_push_back_to_wrong_url_git():
+    assert 0
 
-    test_push_wrong_credentials()
 
-    test_logs(initial_logs)
-    print 'finished ok in %.3f' % (time.time() - s)
+#TODO: write all locking tests
--- a/rhodecode/tests/test_validators.py	Fri Aug 24 10:37:17 2012 +0200
+++ b/rhodecode/tests/test_validators.py	Fri Aug 24 14:58:26 2012 +0200
@@ -10,6 +10,9 @@
 from rhodecode.model.meta import Session
 from rhodecode.model.repos_group import ReposGroupModel
 from rhodecode.config.routing import ADMIN_PREFIX
+from rhodecode.model.db import ChangesetStatus
+from rhodecode.model.changeset_status import ChangesetStatusModel
+from rhodecode.model.comment import ChangesetCommentsModel
 
 
 class TestReposGroups(unittest.TestCase):
@@ -222,3 +225,22 @@
     def test_AttrLoginValidator(self):
         validator = v.AttrLoginValidator()
         self.assertRaises(formencode.Invalid, validator.to_python, 123)
+
+    def test_NotReviewedRevisions(self):
+        validator = v.NotReviewedRevisions()
+        rev = '0' * 40
+        # add status for a rev, that should throw an error because it is already
+        # reviewed
+        new_status = ChangesetStatus()
+        new_status.author = ChangesetStatusModel()._get_user(TEST_USER_ADMIN_LOGIN)
+        new_status.repo = ChangesetStatusModel()._get_repo(HG_REPO)
+        new_status.status = ChangesetStatus.STATUS_APPROVED
+        new_status.comment = None
+        new_status.revision = rev
+        Session().add(new_status)
+        Session().commit()
+        try:
+            self.assertRaises(formencode.Invalid, validator.to_python, [rev])
+        finally:
+            Session().delete(new_status)
+            Session().commit()