changeset 5758:d5e16407bdbb

Merge stable
author Mads Kiilerich <madski@unity3d.com>
date Mon, 14 Mar 2016 00:36:08 +0100
parents 1a7611b9730e (diff) c92b6787c843 (current diff)
children 272cb88d97c2
files docs/contributing.rst kallithea/__init__.py
diffstat 215 files changed, 11367 insertions(+), 4724 deletions(-) [+]
line wrap: on
line diff
--- a/CONTRIBUTORS	Sun Mar 13 15:18:08 2016 +0100
+++ b/CONTRIBUTORS	Mon Mar 14 00:36:08 2016 +0100
@@ -3,14 +3,20 @@
     Mads Kiilerich <madski@unity3d.com> 2012-2016
     Takumi IINO <trot.thunder@gmail.com> 2012-2016
     Unity Technologies 2012-2016
+    Andrew Shadura <andrew@shadura.me> 2012 2014-2016
+    Thomas De Schampheleire <thomas.de.schampheleire@gmail.com> 2014-2016
+    Jan Heylen <heyleke@gmail.com> 2015-2016
+    Robert Rauch <mail@robertrauch.de> 2015-2016
+    Søren Løvborg <sorenl@unity3d.com> 2015-2016
+    Angel Ezquerra <angel.ezquerra@gmail.com> 2016
+    Asterios Dimitriou <steve@pci.gr> 2016
     Robert James Dennington <tinytimrob@googlemail.com> 2016
-    Andrew Shadura <andrew@shadura.me> 2012 2014-2015
     Aras Pranckevičius <aras@unity3d.com> 2012-2013 2015
+    Dominik Ruf <dominikruf@gmail.com> 2012 2014-2015
     Sean Farley <sean.michael.farley@gmail.com> 2013-2015
     Christian Oyarzun <oyarzun@gmail.com> 2014-2015
     Joseph Rivera <rivera.d.joseph@gmail.com> 2014-2015
     Michal Čihař <michal@cihar.com> 2014-2015
-    Thomas De Schampheleire <thomas.de.schampheleire@gmail.com> 2014-2015
     Anatoly Bubenkov <bubenkoff@gmail.com> 2015
     Andrew Bartlett <abartlet@catalyst.net.nz> 2015
     Balázs Úr <urbalazs@gmail.com> 2015
@@ -23,7 +29,7 @@
     EriCSN Chang <ericsning@gmail.com> 2015
     Étienne Gilli <etienne.gilli@gmail.com> 2015
     Grzegorz Krason <grzegorz.krason@gmail.com> 2015
-    Jan Heylen <heyleke@gmail.com> 2015
+    Jiří Suchan <yed@vanyli.net> 2015
     Kazunari Kobayashi <kobanari@nifty.com> 2015
     Kevin Bullock <kbullock@ringworld.org> 2015
     kobanari <kobanari@nifty.com> 2015
@@ -37,13 +43,11 @@
     Niemand Jedermann <predatorix@web.de> 2015
     Peter Vitt <petervitt@web.de> 2015
     Robert Martinez <ntttq@inboxen.org> 2015
-    Robert Rauch <mail@robertrauch.de> 2015
     Ronny Pfannschmidt <opensource@ronnypfannschmidt.de> 2015
     Sam Jaques <sam.jaques@me.com> 2015
-    Søren Løvborg <sorenl@unity3d.com> 2015
     Tuux <tuxa@galaxie.eu.org> 2015
     Viktar Palstsiuk <vipals@gmail.com> 2015
-    Dominik Ruf <dominikruf@gmail.com> 2012 2014
+    Ante Ilic <ante@unity3d.com> 2014
     Bradley M. Kuhn <bkuhn@sfconservancy.org> 2014
     Calinou <calinou@opmbx.org> 2014
     Daniel Anderson <daniel@dattrix.com> 2014
--- a/README.rst	Sun Mar 13 15:18:08 2016 +0100
+++ b/README.rst	Mon Mar 14 00:36:08 2016 +0100
@@ -172,7 +172,7 @@
 - convert the database in a one-time step
 
 Maintaining interoperability
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Interoperability with RhodeCode 2.2.X installations is provided so you don't
 have to immediately commit to switching to Kallithea. This option will most
@@ -191,7 +191,7 @@
 ``$VIRTUAL_ENV/lib/python2.7/site-packages/Kallithea-0.1-py2.7.egg/kallithea``.
 
 One-time conversion
-~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^
 
 Alternatively, if you would like to convert the database for good, you can use
 a helper script provided by Kallithea. This script will operate directly on the
@@ -209,7 +209,7 @@
    above, watch out for stray brand.pyc after removing brand.py.
 
 Git hooks
-~~~~~~~~~
+^^^^^^^^^
 
 After switching to Kallithea, it will be necessary to update the Git_ hooks in
 your repositories. If not, the Git_ hooks from RhodeCode will still be called,
--- a/development.ini	Sun Mar 13 15:18:08 2016 +0100
+++ b/development.ini	Mon Mar 14 00:36:08 2016 +0100
@@ -163,6 +163,7 @@
 #cheaper-step = 1
 
 ## COMMON ##
+#host = 127.0.0.1
 host = 0.0.0.0
 port = 5000
 
@@ -280,12 +281,6 @@
 #issue_server_link_wiki = https://wiki.example.com/{id}
 #issue_prefix_wiki = WIKI-
 
-## instance-id prefix
-## a prefix key for this instance used for cache invalidation when running
-## multiple instances of kallithea, make sure it's globally unique for
-## all running kallithea instances. Leave empty if you don't use it
-instance_id =
-
 ## alternative return HTTP header for failed authentication. Default HTTP
 ## response is 401 HTTPUnauthorized. Currently Mercurial clients have trouble with
 ## handling that. Set this variable to 403 to return HTTPForbidden
@@ -301,6 +296,18 @@
 ## allows to setup custom hooks in settings page
 allow_custom_hooks_settings = True
 
+## extra extensions for indexing, space separated and without the leading '.'.
+# index.extensions =
+#    gemfile
+#    lock
+
+## extra filenames for indexing, space separated
+# index.filenames =
+#    .dockerignore
+#    .editorconfig
+#    INSTALL
+#    CHANGELOG
+
 ####################################
 ###        CELERY CONFIG        ####
 ####################################
@@ -486,13 +493,12 @@
 #sqlalchemy.db1.url = postgresql://user:pass@localhost/kallithea
 
 # MySQL
-#sqlalchemy.db1.url = mysql://user:pass@localhost/kallithea
+#sqlalchemy.db1.url = mysql://user:pass@localhost/kallithea?charset=utf8
 
 # see sqlalchemy docs for others
 
 sqlalchemy.db1.echo = false
 sqlalchemy.db1.pool_recycle = 3600
-sqlalchemy.db1.convert_unicode = true
 
 ################################
 ### LOGGING CONFIGURATION   ####
@@ -560,16 +566,16 @@
 class = StreamHandler
 args = (sys.stderr,)
 #level = INFO
+level = DEBUG
 #formatter = generic
-level = DEBUG
 formatter = color_formatter
 
 [handler_console_sql]
 class = StreamHandler
 args = (sys.stderr,)
 #level = WARN
+level = DEBUG
 #formatter = generic
-level = DEBUG
 formatter = color_formatter_sql
 
 ################
--- a/docs/api/api.rst	Sun Mar 13 15:18:08 2016 +0100
+++ b/docs/api/api.rst	Mon Mar 14 00:36:08 2016 +0100
@@ -10,7 +10,7 @@
 
 
 API access for web views
-++++++++++++++++++++++++
+------------------------
 
 API access can also be turned on for each web view in Kallithea that is
 decorated with the ``@LoginRequired`` decorator. Some views use
@@ -36,7 +36,7 @@
 
 
 API access
-++++++++++
+----------
 
 Clients must send JSON encoded JSON-RPC requests::
 
@@ -76,7 +76,7 @@
 
 
 API client
-++++++++++
+----------
 
 Kallithea comes with a ``kallithea-api`` command line tool, providing a convenient
 way to call the JSON-RPC API.
@@ -110,11 +110,11 @@
 
 
 API methods
-+++++++++++
+-----------
 
 
 pull
-----
+^^^^
 
 Pull the given repo from remote location. Can be used to automatically keep
 remote repos up to date.
@@ -136,7 +136,7 @@
     error :  null
 
 rescan_repos
-------------
+^^^^^^^^^^^^
 
 Rescan repositories. If ``remove_obsolete`` is set,
 Kallithea will delete repos that are in the database but not in the filesystem.
@@ -159,7 +159,7 @@
     error :  null
 
 invalidate_cache
-----------------
+^^^^^^^^^^^^^^^^
 
 Invalidate the cache for a repository.
 This command can only be executed using the api_key of a user with admin rights,
@@ -181,7 +181,7 @@
     error :  null
 
 lock
-----
+^^^^
 
 Set the locking state on the given repository by the given user.
 If the param ``userid`` is skipped, it is set to the ID of the user who is calling this method.
@@ -212,7 +212,7 @@
     error :  null
 
 get_ip
-------
+^^^^^^
 
 Return IP address as seen from Kallithea server, together with all
 defined IP addresses for given user.
@@ -244,7 +244,7 @@
     error :  null
 
 get_user
---------
+^^^^^^^^
 
 Get a user by username or userid. The result is empty if user can't be found.
 If userid param is skipped, it is set to id of user who is calling this method.
@@ -288,7 +288,7 @@
     error:  null
 
 get_users
----------
+^^^^^^^^^
 
 List all existing users.
 This command can only be executed using the api_key of a user with admin rights.
@@ -325,7 +325,7 @@
 .. _create-user:
 
 create_user
------------
+^^^^^^^^^^^
 
 Create new user.
 This command can only be executed using the api_key of a user with admin rights.
@@ -371,7 +371,7 @@
     kallithea-api create_user username:bent email:bent@example.com firstname:Bent lastname:Bentsen extern_type:ldap extern_name:uid=bent,dc=example,dc=com
 
 update_user
------------
+^^^^^^^^^^^
 
 Update the given user if such user exists.
 This command can only be executed using the api_key of a user with admin rights.
@@ -415,7 +415,7 @@
     error:  null
 
 delete_user
------------
+^^^^^^^^^^^
 
 Delete the given user if such a user exists.
 This command can only be executed using the api_key of a user with admin rights.
@@ -439,7 +439,7 @@
     error:  null
 
 get_user_group
---------------
+^^^^^^^^^^^^^^
 
 Get an existing user group.
 This command can only be executed using the api_key of a user with admin rights.
@@ -481,7 +481,7 @@
     error : null
 
 get_user_groups
----------------
+^^^^^^^^^^^^^^^
 
 List all existing user groups.
 This command can only be executed using the api_key of a user with admin rights.
@@ -507,7 +507,7 @@
     error : null
 
 create_user_group
------------------
+^^^^^^^^^^^^^^^^^
 
 Create a new user group.
 This command can only be executed using the api_key of a user with admin rights.
@@ -537,7 +537,7 @@
     error:  null
 
 add_user_to_user_group
-----------------------
+^^^^^^^^^^^^^^^^^^^^^^
 
 Adds a user to a user group. If the user already is in that group, success will be
 ``false``.
@@ -564,7 +564,7 @@
     error:  null
 
 remove_user_from_user_group
----------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Remove a user from a user group. If the user isn't in the given group, success will
 be ``false``.
@@ -591,7 +591,7 @@
     error:  null
 
 get_repo
---------
+^^^^^^^^
 
 Get an existing repository by its name or repository_id. Members will contain
 either users_group or users associated to that repository.
@@ -680,7 +680,7 @@
     error:  null
 
 get_repos
----------
+^^^^^^^^^
 
 List all existing repositories.
 This command can only be executed using the api_key of a user with admin rights,
@@ -717,7 +717,7 @@
     error:  null
 
 get_repo_nodes
---------------
+^^^^^^^^^^^^^^
 
 Return a list of files and directories for a given path at the given revision.
 It is possible to specify ret_type to show only ``files`` or ``dirs``.
@@ -748,7 +748,7 @@
     error:  null
 
 create_repo
------------
+^^^^^^^^^^^
 
 Create a repository. If the repository name contains "/", all needed repository
 groups will be created. For example "foo/bar/baz" will create repository groups
@@ -800,7 +800,7 @@
     error:  null
 
 update_repo
------------
+^^^^^^^^^^^
 
 Update a repository.
 This command can only be executed using the api_key of a user with admin rights,
@@ -860,7 +860,7 @@
     error:  null
 
 fork_repo
----------
+^^^^^^^^^
 
 Create a fork of the given repo. If using Celery, this will
 return success message immediately and a fork will be created
@@ -896,7 +896,7 @@
     error:  null
 
 delete_repo
------------
+^^^^^^^^^^^
 
 Delete a repository.
 This command can only be executed using the api_key of a user with admin rights,
@@ -923,7 +923,7 @@
     error:  null
 
 grant_user_permission
----------------------
+^^^^^^^^^^^^^^^^^^^^^
 
 Grant permission for a user on the given repository, or update the existing one if found.
 This command can only be executed using the api_key of a user with admin rights.
@@ -949,7 +949,7 @@
     error:  null
 
 revoke_user_permission
-----------------------
+^^^^^^^^^^^^^^^^^^^^^^
 
 Revoke permission for a user on the given repository.
 This command can only be executed using the api_key of a user with admin rights.
@@ -974,7 +974,7 @@
     error:  null
 
 grant_user_group_permission
----------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Grant permission for a user group on the given repository, or update the
 existing one if found.
@@ -1001,7 +1001,7 @@
     error:  null
 
 revoke_user_group_permission
-----------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Revoke permission for a user group on the given repository.
 This command can only be executed using the api_key of a user with admin rights.
--- a/docs/contributing.rst	Sun Mar 13 15:18:08 2016 +0100
+++ b/docs/contributing.rst	Mon Mar 14 00:36:08 2016 +0100
@@ -49,7 +49,7 @@
 -------------
 
 After finishing your changes make sure all tests pass cleanly. You can run
-the testsuite running ``nosetests`` from the project root, or if you use tox
+the testsuite running ``py.test`` from the project root, or if you use tox
 run ``tox`` for Python 2.6--2.7 with multiple database test.
 
 When running tests, Kallithea uses `kallithea/tests/test.ini` and populates the
@@ -59,17 +59,31 @@
 the tests, thus eliminating the initial delay. To achieve this, run the tests as::
 
     paster serve kallithea/tests/test.ini --pid-file=test.pid --daemon
-    KALLITHEA_WHOOSH_TEST_DISABLE=1 KALLITHEA_NO_TMP_PATH=1 nosetests
+    KALLITHEA_WHOOSH_TEST_DISABLE=1 KALLITHEA_NO_TMP_PATH=1 py.test
     kill -9 $(cat test.pid)
 
-You can run individual tests by specifying their path as argument to nosetests.
-nosetests also has many more options, see `nosetests -h`. Some useful options
+In these commands, the following variables are used::
+
+    KALLITHEA_WHOOSH_TEST_DISABLE=1 - skip whoosh index building and tests
+    KALLITHEA_NO_TMP_PATH=1 - disable new temp path for tests, used mostly for testing_vcs_operations
+
+You can run individual tests by specifying their path as argument to py.test.
+py.test also has many more options, see `py.test -h`. Some useful options
 are::
 
-    -x, --stop            Stop running tests after the first error or failure
-    -s, --nocapture       Don't capture stdout (any stdout output will be
-                          printed immediately) [NOSE_NOCAPTURE]
-    --failed              Run the tests that failed in the last test run.
+    -k EXPRESSION         only run tests which match the given substring
+                          expression. An expression is a python evaluatable
+                          expression where all names are substring-matched
+                          against test names and their parent classes. Example:
+    -x, --exitfirst       exit instantly on first error or failed test.
+    --lf                  rerun only the tests that failed at the last run (or
+                          all if none failed)
+    --ff                  run all tests but run the last failures first. This
+                          may re-order tests and thus lead to repeated fixture
+                          setup/teardown
+    --pdb                 start the interactive Python debugger on errors.
+    -s, --capture=no      don't capture stdout (any stdout output will be
+                          printed immediately)
 
 
 Coding/contribution guidelines
--- a/docs/index.rst	Sun Mar 13 15:18:08 2016 +0100
+++ b/docs/index.rst	Mon Mar 14 00:36:08 2016 +0100
@@ -63,7 +63,7 @@
 
 
 Other topics
-------------
+************
 
 * :ref:`genindex`
 * :ref:`search`
--- a/docs/installation_iis.rst	Sun Mar 13 15:18:08 2016 +0100
+++ b/docs/installation_iis.rst	Mon Mar 14 00:36:08 2016 +0100
@@ -28,7 +28,7 @@
 own virtual folder will be noted where appropriate.
 
 Application pool
-................
+^^^^^^^^^^^^^^^^
 
 Make sure that there is a unique application pool for the Kallithea application
 with an identity that has read access to the Kallithea distribution.
@@ -44,7 +44,7 @@
     as long as the Kallithea requirements are met by the existing pool.
 
 ISAPI handler
-.............
+^^^^^^^^^^^^^
 
 The ISAPI handler can be generated using::
 
@@ -68,7 +68,7 @@
 site will be processed through this logic henceforth.
 
 Authentication with Kallithea using IIS authentication modules
-..............................................................
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 The recommended way to handle authentication with Kallithea using IIS is to let
 IIS handle all the authentication and just pass it to Kallithea.
--- a/docs/installation_win.rst	Sun Mar 13 15:18:08 2016 +0100
+++ b/docs/installation_win.rst	Mon Mar 14 00:36:08 2016 +0100
@@ -6,7 +6,7 @@
 
 
 First time install
-::::::::::::::::::
+------------------
 
 Target OS: Windows 7 and newer or Windows Server 2008 R2 and newer
 
@@ -15,7 +15,7 @@
 To install on an older version of Windows, see `<installation_win_old.html>`_
 
 Step 1 -- Install Python
-------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^
 
 Install Python 2.x.y (x = 6 or 7). Latest version is recommended. If you need another version, they can run side by side.
 
@@ -31,7 +31,7 @@
 be needed in the next step. In this case, it is "2.7".
 
 Step 2 -- Python BIN
---------------------
+^^^^^^^^^^^^^^^^^^^^
 
 Add Python BIN folder to the path. This can be done manually (editing
 "PATH" environment variable) or by using Windows Support Tools that
@@ -45,7 +45,7 @@
 path. Typically this is ``C:\\Python27``.
 
 Step 3 -- Install pywin32 extensions
-------------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Download pywin32 from:
 http://sourceforge.net/projects/pywin32/files/
@@ -61,7 +61,7 @@
   (Win32)
 
 Step 4 -- Install pip
----------------------
+^^^^^^^^^^^^^^^^^^^^^
 
 pip is a package management system for Python. You will need it to install Kallithea and its dependencies.
 
@@ -85,7 +85,7 @@
   SETX PATH "%PATH%;[your-python-path]\Scripts" /M
 
 Step 5 -- Kallithea folder structure
-------------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Create a Kallithea folder structure.
 
@@ -102,7 +102,7 @@
   C:\Kallithea\Repos
 
 Step 6 -- Install virtualenv
-----------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. note::
    A python virtual environment will allow for isolation between the Python packages of your system and those used for Kallithea.
@@ -119,7 +119,7 @@
   virtualenv C:\Kallithea\Env
 
 Step 7 -- Install Kallithea
----------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 In order to install Kallithea, you need to be able to run "pip install kallithea". It will use pip to install the Kallithea Python package and its dependencies.
 Some Python packages use managed code and need to be compiled.
@@ -146,7 +146,7 @@
           normal.
 
 Step 8 -- Install git (optional)
---------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Mercurial being a python package, it was installed automatically when doing "pip install kallithea".
 
@@ -155,7 +155,7 @@
 See http://git-scm.com/book/en/v2/Getting-Started-Installing-Git#Installing-on-Windows for instructions.
 
 Step 9 -- Configuring Kallithea
--------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Steps taken from `<setup.html>`_
 
@@ -194,7 +194,7 @@
 If you decided not to install git, you will get errors about it that you can ignore.
 
 Step 10 -- Running Kallithea
-----------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 In the previous command prompt, being in the C:\\Kallithea\\Bin folder, type::
 
@@ -222,7 +222,7 @@
 
 
 Upgrading
-:::::::::
+---------
 
 Stop running Kallithea
 Open a CommandPrompt like in Step 7 (cd to C:\Kallithea\Env\Scripts and activate) and type::
--- a/docs/installation_win_old.rst	Sun Mar 13 15:18:08 2016 +0100
+++ b/docs/installation_win_old.rst	Mon Mar 14 00:36:08 2016 +0100
@@ -6,7 +6,7 @@
 
 
 First-time install
-::::::::::::::::::
+------------------
 
 Target OS: Windows XP SP3 32-bit English (Clean installation)
 + All Windows Updates until 24-may-2012
@@ -24,7 +24,7 @@
    - http://bugs.python.org/issue7511
 
 Step 1 -- Install Visual Studio 2008 Express
---------------------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Optional: You can also install MinGW, but VS2008 installation is easier.
 
@@ -58,7 +58,7 @@
    Copy C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat to C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\amd64\vcvarsamd64.bat
 
 Step 2 -- Install Python
-------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^
 
 Install Python 2.x.y (x = 6 or 7) x86 version (32-bit). DO NOT USE A 3.x version.
 Download Python 2.x.y from:
@@ -74,7 +74,7 @@
    64-bit: Just download and install the 64-bit version of python.
 
 Step 3 -- Install Win32py extensions
-------------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Download pywin32 from:
 http://sourceforge.net/projects/pywin32/files/
@@ -93,7 +93,7 @@
      http://sourceforge.net/projects/pywin32/files/pywin32/Build%20218/pywin32-218.win-amd64-py2.7.exe/download
 
 Step 4 -- Python BIN
---------------------
+^^^^^^^^^^^^^^^^^^^^
 
 Add Python BIN folder to the path
 
@@ -120,7 +120,7 @@
   Typically: C:\\Python27
 
 Step 5 -- Kallithea folder structure
-------------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Create a Kallithea folder structure
 
@@ -137,7 +137,7 @@
   C:\Kallithea\Repos
 
 Step 6 -- Install virtualenv
-----------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Install Virtual Env for Python
 
@@ -157,7 +157,7 @@
 to include it)
 
 Step 7 -- Install Kallithea
----------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Finally, install Kallithea
 
@@ -195,7 +195,7 @@
 Some warnings will appear, don't worry as they are normal.
 
 Step 8 -- Configuring Kallithea
--------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 steps taken from http://packages.python.org/Kallithea/setup.html
 
@@ -233,7 +233,7 @@
 it again.
 
 Step 9 -- Running Kallithea
----------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 In the previous command prompt, being in the C:\\Kallithea\\Bin folder,
 just type::
@@ -263,7 +263,7 @@
 
 
 Upgrading
-:::::::::
+---------
 
 Stop running Kallithea
 Open a CommandPrompt like in Step7 (VS2008 path + activate) and type::
--- a/docs/setup.rst	Sun Mar 13 15:18:08 2016 +0100
+++ b/docs/setup.rst	Mon Mar 14 00:36:08 2016 +0100
@@ -346,7 +346,7 @@
 will be saved there.
 
 Active Directory
-''''''''''''''''
+^^^^^^^^^^^^^^^^
 
 Kallithea can use Microsoft Active Directory for user authentication.  This
 is done through an LDAP or LDAPS connection to Active Directory.  The
@@ -383,7 +383,7 @@
 permissions before the user logs in for the first time, using the :ref:`create-user` API.
 
 Container-based authentication
-''''''''''''''''''''''''''''''
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 In a container-based authentication setup, Kallithea reads the user name from
 the ``REMOTE_USER`` server variable provided by the WSGI container.
@@ -393,7 +393,7 @@
 Kallithea.
 
 Proxy pass-through authentication
-'''''''''''''''''''''''''''''''''
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 In a proxy pass-through authentication setup, Kallithea reads the user name
 from the ``X-Forwarded-User`` request header, which should be configured to be
@@ -427,6 +427,74 @@
       RequestHeader set X-Forwarded-User %{RU}e
     </Location>
 
+Setting metadata in container/reverse-proxy
+'''''''''''''''''''''''''''''''''''''''''''
+
+When a new user account is created on the first login, Kallithea has no information about
+the user's email and full name. So you can set some additional request headers like in the
+example below. In this example the user is authenticated via Kerberos and an Apache
+mod_python fixup handler is used to get the user information from a LDAP server. But you
+could set the request headers however you want.
+
+.. code-block:: apache
+
+    <Location /someprefix>
+      ProxyPass http://127.0.0.1:5000/someprefix
+      ProxyPassReverse http://127.0.0.1:5000/someprefix
+      SetEnvIf X-Url-Scheme https HTTPS=1
+
+      AuthName "Kerberos Login"
+      AuthType Kerberos
+      Krb5Keytab /etc/apache2/http.keytab
+      KrbMethodK5Passwd off
+      KrbVerifyKDC on
+      Require valid-user
+
+      PythonFixupHandler ldapmetadata
+
+      RequestHeader set X_REMOTE_USER %{X_REMOTE_USER}e
+      RequestHeader set X_REMOTE_EMAIL %{X_REMOTE_EMAIL}e
+      RequestHeader set X_REMOTE_FIRSTNAME %{X_REMOTE_FIRSTNAME}e
+      RequestHeader set X_REMOTE_LASTNAME %{X_REMOTE_LASTNAME}e
+    </Location>
+
+.. code-block:: python
+
+    from mod_python import apache
+    import ldap
+
+    LDAP_SERVER = "ldap://server.mydomain.com:389"
+    LDAP_USER = ""
+    LDAP_PASS = ""
+    LDAP_ROOT = "dc=mydomain,dc=com"
+    LDAP_FILTER = "sAMAcountName=%s"
+    LDAP_ATTR_LIST = ['sAMAcountName','givenname','sn','mail']
+
+    def fixuphandler(req):
+        if req.user is None:
+            # no user to search for
+            return apache.OK
+        else:
+            try:
+                if('\\' in req.user):
+                    username = req.user.split('\\')[1]
+                elif('@' in req.user):
+                    username = req.user.split('@')[0]
+                else:
+                    username = req.user
+                l = ldap.initialize(LDAP_SERVER)
+                l.simple_bind_s(LDAP_USER, LDAP_PASS)
+                r = l.search_s(LDAP_ROOT, ldap.SCOPE_SUBTREE, LDAP_FILTER % username, attrlist=LDAP_ATTR_LIST)
+
+                req.subprocess_env['X_REMOTE_USER'] = username
+                req.subprocess_env['X_REMOTE_EMAIL'] = r[0][1]['mail'][0].lower()
+                req.subprocess_env['X_REMOTE_FIRSTNAME'] = "%s" % r[0][1]['givenname'][0]
+                req.subprocess_env['X_REMOTE_LASTNAME'] = "%s" % r[0][1]['sn'][0]
+            except Exception, e:
+                apache.log_error("error getting data from ldap %s" % str(e), apache.APLOG_ERR)
+
+            return apache.OK
+
 .. note::
    If you enable proxy pass-through authentication, make sure your server is
    only accessible through the proxy. Otherwise, any client would be able to
@@ -746,11 +814,6 @@
    When running apache as root, please make sure it doesn't run Kallithea as
    root, for examply by adding: ``user=www-data group=www-data`` to the configuration.
 
-.. note::
-   If running Kallithea in multiprocess mode,
-   make sure you set ``instance_id = *`` in the configuration so each process
-   gets it's own cache invalidation key.
-
 Example WSGI dispatch script:
 
 .. code-block:: python
--- a/docs/usage/general.rst	Sun Mar 13 15:18:08 2016 +0100
+++ b/docs/usage/general.rst	Mon Mar 14 00:36:08 2016 +0100
@@ -151,7 +151,7 @@
 features that merit further explanation.
 
 Repository extra fields
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
 
 In the *Visual* tab, there is an option "Use repository extra
 fields", which allows to set custom fields for each repository in the system.
@@ -165,7 +165,7 @@
 Newly created fields are accessible via the API.
 
 Meta tagging
-~~~~~~~~~~~~
+^^^^^^^^^^^^
 
 In the *Visual* tab, option "Stylify recognised meta tags" will cause Kallithea
 to turn certain text fragments in repository and repository group
--- a/docs/usage/performance.rst	Sun Mar 13 15:18:08 2016 +0100
+++ b/docs/usage/performance.rst	Mon Mar 14 00:36:08 2016 +0100
@@ -38,7 +38,6 @@
     scaled horizontally on one (recommended) or multiple machines. In order
     to scale horizontally you need to do the following:
 
-    - Each instance needs its own .ini file and unique ``instance_id`` set.
     - Each instance's ``data`` storage needs to be configured to be stored on a
       shared disk storage, preferably together with repositories. This ``data``
       dir contains template caches, sessions, whoosh index and is used for
--- a/docs/usage/vcs_support.rst	Sun Mar 13 15:18:08 2016 +0100
+++ b/docs/usage/vcs_support.rst	Mon Mar 14 00:36:08 2016 +0100
@@ -23,7 +23,7 @@
 
 
 Web server with chunked encoding
-````````````````````````````````
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Large Git pushes require an HTTP server with support for
 chunked encoding for POST. The Python web servers waitress_ and
@@ -51,7 +51,7 @@
 
 
 Working with Mercurial subrepositories
-``````````````````````````````````````
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 This section explains how to use Mercurial subrepositories_ in Kallithea.
 
--- a/kallithea/__init__.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/__init__.py	Mon Mar 14 00:36:08 2016 +0100
@@ -29,7 +29,7 @@
 import sys
 import platform
 
-VERSION = (0, 3, 1)
+VERSION = (0, 3, 99)
 BACKENDS = {
     'hg': 'Mercurial repository',
     'git': 'Git repository',
--- a/kallithea/bin/kallithea_backup.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/bin/kallithea_backup.py	Mon Mar 14 00:36:08 2016 +0100
@@ -28,11 +28,11 @@
 
 import os
 import sys
-
 import logging
 import tarfile
 import datetime
 import subprocess
+import tempfile
 
 logging.basicConfig(level=logging.DEBUG,
                     format="%(asctime)s %(levelname)-5.5s %(message)s")
@@ -47,7 +47,7 @@
         self.repos_path = self.get_repos_path(repos_location)
         self.backup_server = backup_server
 
-        self.backup_file_path = '/tmp'
+        self.backup_file_path = tempfile.gettempdir()
 
         logging.info('starting backup for %s', self.repos_path)
         logging.info('backup target %s', self.backup_file_path)
--- a/kallithea/bin/template.ini.mako	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/bin/template.ini.mako	Mon Mar 14 00:36:08 2016 +0100
@@ -278,12 +278,6 @@
 #issue_server_link_wiki = https://wiki.example.com/{id}
 #issue_prefix_wiki = WIKI-
 
-<%text>## instance-id prefix</%text>
-<%text>## a prefix key for this instance used for cache invalidation when running</%text>
-<%text>## multiple instances of kallithea, make sure it's globally unique for</%text>
-<%text>## all running kallithea instances. Leave empty if you don't use it</%text>
-instance_id =
-
 <%text>## alternative return HTTP header for failed authentication. Default HTTP</%text>
 <%text>## response is 401 HTTPUnauthorized. Currently Mercurial clients have trouble with</%text>
 <%text>## handling that. Set this variable to 403 to return HTTPForbidden</%text>
@@ -299,6 +293,18 @@
 <%text>## allows to setup custom hooks in settings page</%text>
 allow_custom_hooks_settings = True
 
+<%text>## extra extensions for indexing, space separated and without the leading '.'.</%text>
+# index.extensions =
+#    gemfile
+#    lock
+
+<%text>## extra filenames for indexing, space separated</%text>
+# index.filenames =
+#    .dockerignore
+#    .editorconfig
+#    INSTALL
+#    CHANGELOG
+
 <%text>####################################</%text>
 <%text>###        CELERY CONFIG        ####</%text>
 <%text>####################################</%text>
@@ -489,14 +495,13 @@
 
 %elif database_engine == 'mysql':
 # MySQL
-sqlalchemy.db1.url = mysql://user:pass@localhost/kallithea
+sqlalchemy.db1.url = mysql://user:pass@localhost/kallithea?charset=utf8
 
 %endif
 # see sqlalchemy docs for others
 
 sqlalchemy.db1.echo = false
 sqlalchemy.db1.pool_recycle = 3600
-sqlalchemy.db1.convert_unicode = true
 
 <%text>################################</%text>
 <%text>### LOGGING CONFIGURATION   ####</%text>
--- a/kallithea/config/conf.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/config/conf.py	Mon Mar 14 00:36:08 2016 +0100
@@ -25,19 +25,21 @@
 :license: GPLv3, see LICENSE.md for more details.
 """
 
-from kallithea.lib.utils2 import __get_lem
+from kallithea.lib.utils2 import __get_lem, __get_index_filenames
 
 
 # language map is also used by whoosh indexer, which for those specified
 # extensions will index it's content
 LANGUAGES_EXTENSIONS_MAP = __get_lem()
 
-#==============================================================================
-# WHOOSH INDEX EXTENSIONS
-#==============================================================================
-# EXTENSIONS WE WANT TO INDEX CONTENT OFF USING WHOOSH
+# Whoosh index targets
+
+# Extensions we want to index content of using whoosh
 INDEX_EXTENSIONS = LANGUAGES_EXTENSIONS_MAP.keys()
 
+# Filenames we want to index content of using whoosh
+INDEX_FILENAMES = __get_index_filenames()
+
 # list of readme files to search in file tree and display in summary
 # attached weights defines the search  order lower is first
 ALL_READMES = [
--- a/kallithea/config/deployment.ini_tmpl	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/config/deployment.ini_tmpl	Mon Mar 14 00:36:08 2016 +0100
@@ -274,12 +274,6 @@
 #issue_server_link_wiki = https://wiki.example.com/{id}
 #issue_prefix_wiki = WIKI-
 
-## instance-id prefix
-## a prefix key for this instance used for cache invalidation when running
-## multiple instances of kallithea, make sure it's globally unique for
-## all running kallithea instances. Leave empty if you don't use it
-instance_id =
-
 ## alternative return HTTP header for failed authentication. Default HTTP
 ## response is 401 HTTPUnauthorized. Currently Mercurial clients have trouble with
 ## handling that. Set this variable to 403 to return HTTPForbidden
@@ -295,6 +289,18 @@
 ## allows to setup custom hooks in settings page
 allow_custom_hooks_settings = True
 
+## extra extensions for indexing, space separated and without the leading '.'.
+# index.extensions =
+#    gemfile
+#    lock
+
+## extra filenames for indexing, space separated
+# index.filenames =
+#    .dockerignore
+#    .editorconfig
+#    INSTALL
+#    CHANGELOG
+
 ####################################
 ###        CELERY CONFIG        ####
 ####################################
@@ -479,13 +485,12 @@
 #sqlalchemy.db1.url = postgresql://user:pass@localhost/kallithea
 
 # MySQL
-#sqlalchemy.db1.url = mysql://user:pass@localhost/kallithea
+#sqlalchemy.db1.url = mysql://user:pass@localhost/kallithea?charset=utf8
 
 # see sqlalchemy docs for others
 
 sqlalchemy.db1.echo = false
 sqlalchemy.db1.pool_recycle = 3600
-sqlalchemy.db1.convert_unicode = true
 
 ################################
 ### LOGGING CONFIGURATION   ####
--- a/kallithea/config/environment.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/config/environment.py	Mon Mar 14 00:36:08 2016 +0100
@@ -23,6 +23,7 @@
 import pylons
 import mako.lookup
 import beaker
+import formencode
 
 # don't remove this import it does magic for celery
 from kallithea.lib import celerypylons
@@ -33,8 +34,8 @@
 
 from kallithea.lib import helpers
 from kallithea.lib.auth import set_available_permissions
-from kallithea.lib.utils import repo2db_mapper, make_ui, set_app_settings,\
-    load_rcextensions, check_git_version, set_vcs_config
+from kallithea.lib.utils import repo2db_mapper, make_ui, set_app_settings, \
+    load_rcextensions, check_git_version, set_vcs_config, set_indexer_config
 from kallithea.lib.utils2 import engine_from_config, str2bool
 from kallithea.lib.db_manage import DbManage
 from kallithea.model import init_model
@@ -118,7 +119,7 @@
     config['base_path'] = repos_path
     set_app_settings(config)
 
-    instance_id = kallithea.CONFIG.get('instance_id')
+    instance_id = kallithea.CONFIG.get('instance_id', '*')
     if instance_id == '*':
         instance_id = '%s-%s' % (platform.uname()[1], os.getpid())
         kallithea.CONFIG['instance_id'] = instance_id
@@ -130,6 +131,7 @@
     # pylons
     kallithea.CONFIG.update(config)
     set_vcs_config(kallithea.CONFIG)
+    set_indexer_config(kallithea.CONFIG)
 
     #check git version
     check_git_version()
@@ -137,4 +139,5 @@
     if str2bool(config.get('initial_repo_scan', True)):
         repo2db_mapper(ScmModel().repo_scan(repos_path),
                        remove_obsolete=False, install_git_hooks=False)
+    formencode.api.set_stdtranslation(languages=[config.get('lang')])
     return config
--- a/kallithea/config/routing.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/config/routing.py	Mon Mar 14 00:36:08 2016 +0100
@@ -653,11 +653,6 @@
                 controller='changeset', revision='tip', action='comment',
                 conditions=dict(function=check_repo))
 
-    rmap.connect('changeset_comment_preview',
-                 '/{repo_name:.*?}/changeset-comment-preview',
-                controller='changeset', action='preview_comment',
-                conditions=dict(function=check_repo, method=["POST"]))
-
     rmap.connect('changeset_comment_delete',
                  '/{repo_name:.*?}/changeset-comment-delete/{comment_id}',
                 controller='changeset', action='delete_comment',
--- a/kallithea/controllers/admin/admin.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/admin/admin.py	Mon Mar 14 00:36:08 2016 +0100
@@ -125,8 +125,8 @@
 
     @HasPermissionAllDecorator('hg.admin')
     def index(self):
-        users_log = UserLog.query()\
-                .options(joinedload(UserLog.user))\
+        users_log = UserLog.query() \
+                .options(joinedload(UserLog.user)) \
                 .options(joinedload(UserLog.repository))
 
         #FILTERING
--- a/kallithea/controllers/admin/auth_settings.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/admin/auth_settings.py	Mon Mar 14 00:36:08 2016 +0100
@@ -28,8 +28,8 @@
 import traceback
 
 from pylons import request, tmpl_context as c, url
-from pylons.controllers.util import redirect
 from pylons.i18n.translation import _
+from webob.exc import HTTPFound
 
 from kallithea.lib import helpers as h
 from kallithea.lib.compat import formatted_json
@@ -146,4 +146,4 @@
             h.flash(_('error occurred during update of auth settings'),
                     category='error')
 
-        return redirect(url('auth_home'))
+        raise HTTPFound(location=url('auth_home'))
--- a/kallithea/controllers/admin/defaults.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/admin/defaults.py	Mon Mar 14 00:36:08 2016 +0100
@@ -31,8 +31,8 @@
 from formencode import htmlfill
 
 from pylons import request, tmpl_context as c, url
-from pylons.controllers.util import redirect
 from pylons.i18n.translation import _
+from webob.exc import HTTPFound
 
 from kallithea.lib import helpers as h
 from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator
@@ -112,7 +112,7 @@
             h.flash(_('Error occurred during update of defaults'),
                     category='error')
 
-        return redirect(url('defaults'))
+        raise HTTPFound(location=url('defaults'))
 
     def delete(self, id):
         """DELETE /defaults/id: Delete an existing item"""
--- a/kallithea/controllers/admin/gists.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/admin/gists.py	Mon Mar 14 00:36:08 2016 +0100
@@ -31,8 +31,8 @@
 import formencode.htmlfill
 
 from pylons import request, response, tmpl_context as c, url
-from pylons.controllers.util import redirect
 from pylons.i18n.translation import _
+from webob.exc import HTTPFound, HTTPNotFound, HTTPForbidden
 
 from kallithea.model.forms import GistForm
 from kallithea.model.gist import GistModel
@@ -44,7 +44,6 @@
 from kallithea.lib.utils import jsonify
 from kallithea.lib.utils2 import safe_int, safe_unicode, time_to_datetime
 from kallithea.lib.helpers import Page
-from webob.exc import HTTPNotFound, HTTPForbidden
 from sqlalchemy.sql.expression import or_
 from kallithea.lib.vcs.exceptions import VCSError, NodeNotChangedError
 
@@ -70,27 +69,27 @@
     def index(self):
         """GET /admin/gists: All items in the collection"""
         # url('gists')
-        not_default_user = c.authuser.username != User.DEFAULT_USER
+        not_default_user = not c.authuser.is_default_user
         c.show_private = request.GET.get('private') and not_default_user
         c.show_public = request.GET.get('public') and not_default_user
 
-        gists = Gist().query()\
-            .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\
+        gists = Gist().query() \
+            .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time())) \
             .order_by(Gist.created_on.desc())
 
         # MY private
         if c.show_private and not c.show_public:
-            gists = gists.filter(Gist.gist_type == Gist.GIST_PRIVATE)\
+            gists = gists.filter(Gist.gist_type == Gist.GIST_PRIVATE) \
                              .filter(Gist.gist_owner == c.authuser.user_id)
         # MY public
         elif c.show_public and not c.show_private:
-            gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)\
+            gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC) \
                              .filter(Gist.gist_owner == c.authuser.user_id)
 
         # MY public+private
         elif c.show_private and c.show_public:
             gists = gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC,
-                                     Gist.gist_type == Gist.GIST_PRIVATE))\
+                                     Gist.gist_type == Gist.GIST_PRIVATE)) \
                              .filter(Gist.gist_owner == c.authuser.user_id)
 
         # default show ALL public gists
@@ -144,8 +143,8 @@
         except Exception as e:
             log.error(traceback.format_exc())
             h.flash(_('Error occurred during gist creation'), category='error')
-            return redirect(url('new_gist'))
-        return redirect(url('gist', gist_id=new_gist_id))
+            raise HTTPFound(location=url('new_gist'))
+        raise HTTPFound(location=url('gist', gist_id=new_gist_id))
 
     @LoginRequired()
     @NotAnonymous()
@@ -185,7 +184,7 @@
         else:
             raise HTTPForbidden()
 
-        return redirect(url('gists'))
+        raise HTTPFound(location=url('gists'))
 
     @LoginRequired()
     def show(self, gist_id, revision='tip', format='html', f_path=None):
@@ -270,7 +269,7 @@
                 h.flash(_('Error occurred during update of gist %s') % gist_id,
                         category='error')
 
-            return redirect(url('gist', gist_id=gist_id))
+            raise HTTPFound(location=url('gist', gist_id=gist_id))
 
         return rendered
 
--- a/kallithea/controllers/admin/my_account.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/admin/my_account.py	Mon Mar 14 00:36:08 2016 +0100
@@ -32,8 +32,8 @@
 from sqlalchemy import func
 from formencode import htmlfill
 from pylons import request, tmpl_context as c, url
-from pylons.controllers.util import redirect
 from pylons.i18n.translation import _
+from webob.exc import HTTPFound
 
 from kallithea import EXTERN_TYPE_INTERNAL
 from kallithea.lib import helpers as h
@@ -69,7 +69,7 @@
         if c.user.username == User.DEFAULT_USER:
             h.flash(_("You can't edit this user since it's"
                       " crucial for entire application"), category='warning')
-            return redirect(url('users'))
+            raise HTTPFound(location=url('users'))
         c.EXTERN_TYPE_INTERNAL = EXTERN_TYPE_INTERNAL
 
     def _load_my_repos_data(self, watched=False):
@@ -81,9 +81,9 @@
                               self.authuser.user_id).all()]
         else:
             admin = True
-            repos_list = Session().query(Repository)\
+            repos_list = Session().query(Repository) \
                          .filter(Repository.user_id ==
-                                 self.authuser.user_id)\
+                                 self.authuser.user_id) \
                          .order_by(func.lower(Repository.repo_name)).all()
 
         repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
@@ -144,7 +144,7 @@
                 h.flash(_('Error occurred during update of user %s') \
                         % form_result.get('username'), category='error')
         if update:
-            return redirect('my_account')
+            raise HTTPFound(location='my_account')
         return htmlfill.render(
             render('admin/my_account/my_account.html'),
             defaults=defaults,
@@ -207,7 +207,7 @@
         c.active = 'emails'
         self.__load_data()
 
-        c.user_email_map = UserEmailMap.query()\
+        c.user_email_map = UserEmailMap.query() \
             .filter(UserEmailMap.user == c.user).all()
         return render('admin/my_account/my_account.html')
 
@@ -225,7 +225,7 @@
             log.error(traceback.format_exc())
             h.flash(_('An error occurred during email saving'),
                     category='error')
-        return redirect(url('my_account_emails'))
+        raise HTTPFound(location=url('my_account_emails'))
 
     def my_account_emails_delete(self):
         email_id = request.POST.get('del_email_id')
@@ -233,7 +233,7 @@
         user_model.delete_extra_email(self.authuser.user_id, email_id)
         Session().commit()
         h.flash(_("Removed email from user"), category='success')
-        return redirect(url('my_account_emails'))
+        raise HTTPFound(location=url('my_account_emails'))
 
     def my_account_api_keys(self):
         c.active = 'api_keys'
@@ -257,7 +257,7 @@
         ApiKeyModel().create(self.authuser.user_id, description, lifetime)
         Session().commit()
         h.flash(_("API key successfully created"), category='success')
-        return redirect(url('my_account_api_keys'))
+        raise HTTPFound(location=url('my_account_api_keys'))
 
     def my_account_api_keys_delete(self):
         api_key = request.POST.get('del_api_key')
@@ -274,4 +274,4 @@
             Session().commit()
             h.flash(_("API key successfully deleted"), category='success')
 
-        return redirect(url('my_account_api_keys'))
+        raise HTTPFound(location=url('my_account_api_keys'))
--- a/kallithea/controllers/admin/notifications.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/admin/notifications.py	Mon Mar 14 00:36:08 2016 +0100
@@ -30,8 +30,7 @@
 
 from pylons import request
 from pylons import tmpl_context as c
-from pylons.controllers.util import abort
-from webob.exc import HTTPBadRequest
+from webob.exc import HTTPBadRequest, HTTPForbidden
 
 from kallithea.model.db import Notification
 from kallithea.model.notification import NotificationModel
@@ -155,7 +154,7 @@
                     for un in no.notifications_to_users)
         repo_admin = h.HasRepoPermissionAny('repository.admin')
         if no and (h.HasPermissionAny('hg.admin')() or repo_admin or owner):
-            unotification = NotificationModel()\
+            unotification = NotificationModel() \
                             .get_user_notification(c.user.user_id, no)
 
             # if this association to user is not valid, we don't want to show
@@ -168,7 +167,7 @@
 
                 return render('admin/notifications/show_notification.html')
 
-        return abort(403)
+        raise HTTPForbidden()
 
     def edit(self, notification_id, format='html'):
         """GET /_admin/notifications/id/edit: Form to edit an existing item"""
--- a/kallithea/controllers/admin/permissions.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/admin/permissions.py	Mon Mar 14 00:36:08 2016 +0100
@@ -32,8 +32,8 @@
 from formencode import htmlfill
 
 from pylons import request, tmpl_context as c, url
-from pylons.controllers.util import redirect
 from pylons.i18n.translation import _
+from webob.exc import HTTPFound
 
 from kallithea.lib import helpers as h
 from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator
@@ -139,7 +139,7 @@
                 h.flash(_('Error occurred during update of permissions'),
                         category='error')
 
-            return redirect(url('admin_permissions'))
+            raise HTTPFound(location=url('admin_permissions'))
 
         c.user = User.get_default_user()
         defaults = {'anonymous': c.user.active}
@@ -184,7 +184,7 @@
     def permission_ips(self):
         c.active = 'ips'
         c.user = User.get_default_user()
-        c.user_ip_map = UserIpMap.query()\
+        c.user_ip_map = UserIpMap.query() \
                         .filter(UserIpMap.user == c.user).all()
 
         return render('admin/permissions/permissions.html')
--- a/kallithea/controllers/admin/repo_groups.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/admin/repo_groups.py	Mon Mar 14 00:36:08 2016 +0100
@@ -33,8 +33,8 @@
 from formencode import htmlfill
 
 from pylons import request, tmpl_context as c, url
-from pylons.controllers.util import abort, redirect
 from pylons.i18n.translation import _, ungettext
+from webob.exc import HTTPFound, HTTPForbidden, HTTPNotFound, HTTPInternalServerError
 
 import kallithea
 from kallithea.lib import helpers as h
@@ -49,7 +49,6 @@
 from kallithea.model.forms import RepoGroupForm, RepoGroupPermsForm
 from kallithea.model.meta import Session
 from kallithea.model.repo import RepoModel
-from webob.exc import HTTPInternalServerError, HTTPNotFound
 from kallithea.lib.utils2 import safe_int
 from sqlalchemy.sql.expression import func
 
@@ -111,8 +110,8 @@
     def index(self, format='html'):
         """GET /repo_groups: All items in the collection"""
         # url('repos_groups')
-        _list = RepoGroup.query()\
-                    .order_by(func.lower(RepoGroup.group_name))\
+        _list = RepoGroup.query() \
+                    .order_by(func.lower(RepoGroup.group_name)) \
                     .all()
         group_iter = RepoGroupList(_list, perm_set=['group.admin'])
         repo_groups_data = []
@@ -189,10 +188,10 @@
                     % request.POST.get('group_name'), category='error')
             parent_group_id = form_result['group_parent_id']
             #TODO: maybe we should get back to the main view, not the admin one
-            return redirect(url('repos_groups', parent_group=parent_group_id))
+            raise HTTPFound(location=url('repos_groups', parent_group=parent_group_id))
         h.flash(_('Created repository group %s') % gr.group_name,
                 category='success')
-        return redirect(url('repos_group_home', group_name=gr.group_name))
+        raise HTTPFound(location=url('repos_group_home', group_name=gr.group_name))
 
     def new(self):
         """GET /repo_groups/new: Form to create a new item"""
@@ -209,7 +208,7 @@
             if HasRepoGroupPermissionAll('group.admin')(group_name, 'group create'):
                 pass
             else:
-                return abort(403)
+                raise HTTPForbidden()
 
         self.__load_defaults()
         return render('admin/repo_groups/repo_group_add.html')
@@ -266,7 +265,7 @@
             h.flash(_('Error occurred during update of repository group %s') \
                     % request.POST.get('group_name'), category='error')
 
-        return redirect(url('edit_repo_group', group_name=group_name))
+        raise HTTPFound(location=url('edit_repo_group', group_name=group_name))
 
     @HasRepoGroupPermissionAnyDecorator('group.admin')
     def delete(self, group_name):
@@ -283,13 +282,13 @@
         if repos:
             h.flash(_('This group contains %s repositories and cannot be '
                       'deleted') % len(repos), category='warning')
-            return redirect(url('repos_groups'))
+            raise HTTPFound(location=url('repos_groups'))
 
         children = gr.children.all()
         if children:
             h.flash(_('This group contains %s subgroups and cannot be deleted'
                       % (len(children))), category='warning')
-            return redirect(url('repos_groups'))
+            raise HTTPFound(location=url('repos_groups'))
 
         try:
             RepoGroupModel().delete(group_name)
@@ -303,8 +302,8 @@
                     % group_name, category='error')
 
         if gr.parent_group:
-            return redirect(url('repos_group_home', group_name=gr.parent_group.group_name))
-        return redirect(url('repos_groups'))
+            raise HTTPFound(location=url('repos_group_home', group_name=gr.parent_group.group_name))
+        raise HTTPFound(location=url('repos_groups'))
 
     def show_by_name(self, group_name):
         """
@@ -330,13 +329,13 @@
         #overwrite our cached list with current filter
         c.repo_cnt = 0
 
-        groups = RepoGroup.query().order_by(RepoGroup.group_name)\
+        groups = RepoGroup.query().order_by(RepoGroup.group_name) \
             .filter(RepoGroup.group_parent_id == c.group.group_id).all()
         c.groups = self.scm_model.get_repo_groups(groups)
 
-        c.repos_list = Repository.query()\
-                        .filter(Repository.group_id == c.group.group_id)\
-                        .order_by(func.lower(Repository.repo_name))\
+        c.repos_list = Repository.query() \
+                        .filter(Repository.group_id == c.group.group_id) \
+                        .order_by(func.lower(Repository.repo_name)) \
                         .all()
 
         repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
@@ -404,7 +403,7 @@
             if self._revoke_perms_on_yourself(form_result):
                 msg = _('Cannot revoke permission for yourself as admin')
                 h.flash(msg, category='warning')
-                return redirect(url('edit_repo_group_perms', group_name=group_name))
+                raise HTTPFound(location=url('edit_repo_group_perms', group_name=group_name))
         recursive = form_result['recursive']
         # iterate over all members(if in recursive mode) of this groups and
         # set the permissions !
@@ -418,7 +417,7 @@
         #              repo_name, self.ip_addr, self.sa)
         Session().commit()
         h.flash(_('Repository group permissions updated'), category='success')
-        return redirect(url('edit_repo_group_perms', group_name=group_name))
+        raise HTTPFound(location=url('edit_repo_group_perms', group_name=group_name))
 
     @HasRepoGroupPermissionAnyDecorator('group.admin')
     def delete_perms(self, group_name):
--- a/kallithea/controllers/admin/repos.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/admin/repos.py	Mon Mar 14 00:36:08 2016 +0100
@@ -29,11 +29,10 @@
 import traceback
 import formencode
 from formencode import htmlfill
-from webob.exc import HTTPInternalServerError, HTTPForbidden, HTTPNotFound
 from pylons import request, tmpl_context as c, url
-from pylons.controllers.util import redirect
 from pylons.i18n.translation import _
 from sqlalchemy.sql.expression import func
+from webob.exc import HTTPFound, HTTPInternalServerError, HTTPForbidden, HTTPNotFound
 
 from kallithea.lib import helpers as h
 from kallithea.lib.auth import LoginRequired, \
@@ -43,7 +42,7 @@
 from kallithea.lib.utils import action_logger, jsonify
 from kallithea.lib.vcs import RepositoryError
 from kallithea.model.meta import Session
-from kallithea.model.db import User, Repository, UserFollowing, RepoGroup,\
+from kallithea.model.db import User, Repository, UserFollowing, RepoGroup, \
     Setting, RepositoryField
 from kallithea.model.forms import RepoForm, RepoFieldForm, RepoPermsForm
 from kallithea.model.scm import ScmModel, AvailableRepoGroupChoices, RepoList
@@ -71,7 +70,7 @@
 
         if repo_obj is None:
             h.not_mapped_error(repo_name)
-            return redirect(url('repos'))
+            raise HTTPFound(location=url('repos'))
 
         return repo_obj
 
@@ -103,8 +102,8 @@
     def index(self, format='html'):
         """GET /repos: All items in the collection"""
         # url('repos')
-        _list = Repository.query()\
-                        .order_by(func.lower(Repository.repo_name))\
+        _list = Repository.query() \
+                        .order_by(func.lower(Repository.repo_name)) \
                         .all()
 
         c.repos_list = RepoList(_list, perm_set=['repository.admin'])
@@ -128,7 +127,7 @@
         try:
             # CanWriteGroup validators checks permissions of this POST
             form_result = RepoForm(repo_groups=c.repo_groups,
-                                   landing_revs=c.landing_revs_choices)()\
+                                   landing_revs=c.landing_revs_choices)() \
                             .to_python(dict(request.POST))
 
             # create is done sometimes async on celery, db transaction
@@ -152,9 +151,9 @@
             msg = (_('Error creating repository %s')
                    % form_result.get('repo_name'))
             h.flash(msg, category='error')
-            return redirect(url('home'))
+            raise HTTPFound(location=url('home'))
 
-        return redirect(h.url('repo_creating_home',
+        raise HTTPFound(location=h.url('repo_creating_home',
                               repo_name=form_result['repo_name_full'],
                               task_id=task_id))
 
@@ -240,7 +239,7 @@
         c.repo_info = self._load_repo(repo_name)
         self.__load_defaults(c.repo_info)
         c.active = 'settings'
-        c.repo_fields = RepositoryField.query()\
+        c.repo_fields = RepositoryField.query() \
             .filter(RepositoryField.repository == c.repo_info).all()
 
         repo_model = RepoModel()
@@ -282,7 +281,7 @@
             log.error(traceback.format_exc())
             h.flash(_('Error occurred during update of repository %s') \
                     % repo_name, category='error')
-        return redirect(url('edit_repo', repo_name=changed_name))
+        raise HTTPFound(location=url('edit_repo', repo_name=changed_name))
 
     @HasRepoPermissionAllDecorator('repository.admin')
     def delete(self, repo_name):
@@ -299,7 +298,7 @@
         repo = repo_model.get_by_repo_name(repo_name)
         if not repo:
             h.not_mapped_error(repo_name)
-            return redirect(url('repos'))
+            raise HTTPFound(location=url('repos'))
         try:
             _forks = repo.forks.count()
             handle_forks = None
@@ -327,15 +326,15 @@
                     category='error')
 
         if repo.group:
-            return redirect(url('repos_group_home', group_name=repo.group.group_name))
-        return redirect(url('repos'))
+            raise HTTPFound(location=url('repos_group_home', group_name=repo.group.group_name))
+        raise HTTPFound(location=url('repos'))
 
     @HasRepoPermissionAllDecorator('repository.admin')
     def edit(self, repo_name):
         """GET /repo_name/settings: Form to edit an existing item"""
         # url('edit_repo', repo_name=ID)
         defaults = self.__load_data(repo_name)
-        c.repo_fields = RepositoryField.query()\
+        c.repo_fields = RepositoryField.query() \
             .filter(RepositoryField.repository == c.repo_info).all()
         repo_model = RepoModel()
         c.users_array = repo_model.get_users_js()
@@ -372,7 +371,7 @@
         #              repo_name, self.ip_addr, self.sa)
         Session().commit()
         h.flash(_('Repository permissions updated'), category='success')
-        return redirect(url('edit_repo_perms', repo_name=repo_name))
+        raise HTTPFound(location=url('edit_repo_perms', repo_name=repo_name))
 
     def edit_permissions_revoke(self, repo_name):
         try:
@@ -404,12 +403,12 @@
         """GET /repo_name/settings: Form to edit an existing item"""
         # url('edit_repo', repo_name=ID)
         c.repo_info = self._load_repo(repo_name)
-        c.repo_fields = RepositoryField.query()\
+        c.repo_fields = RepositoryField.query() \
             .filter(RepositoryField.repository == c.repo_info).all()
         c.active = 'fields'
         if request.POST:
 
-            return redirect(url('repo_edit_fields'))
+            raise HTTPFound(location=url('repo_edit_fields'))
         return render('admin/repos/repo_edit.html')
 
     @HasRepoPermissionAllDecorator('repository.admin')
@@ -431,7 +430,7 @@
             if isinstance(e, formencode.Invalid):
                 msg += ". " + e.msg
             h.flash(msg, category='error')
-        return redirect(url('edit_repo_fields', repo_name=repo_name))
+        raise HTTPFound(location=url('edit_repo_fields', repo_name=repo_name))
 
     @HasRepoPermissionAllDecorator('repository.admin')
     def delete_repo_field(self, repo_name, field_id):
@@ -443,7 +442,7 @@
             log.error(traceback.format_exc())
             msg = _('An error occurred during removal of field')
             h.flash(msg, category='error')
-        return redirect(url('edit_repo_fields', repo_name=repo_name))
+        raise HTTPFound(location=url('edit_repo_fields', repo_name=repo_name))
 
     @HasRepoPermissionAllDecorator('repository.admin')
     def edit_advanced(self, repo_name):
@@ -451,8 +450,8 @@
         # url('edit_repo', repo_name=ID)
         c.repo_info = self._load_repo(repo_name)
         c.default_user_id = User.get_default_user().user_id
-        c.in_public_journal = UserFollowing.query()\
-            .filter(UserFollowing.user_id == c.default_user_id)\
+        c.in_public_journal = UserFollowing.query() \
+            .filter(UserFollowing.user_id == c.default_user_id) \
             .filter(UserFollowing.follows_repository == c.repo_info).scalar()
 
         _repos = Repository.query().order_by(Repository.repo_name).all()
@@ -468,7 +467,7 @@
 
         c.active = 'advanced'
         if request.POST:
-            return redirect(url('repo_edit_advanced'))
+            raise HTTPFound(location=url('repo_edit_advanced'))
         return htmlfill.render(
             render('admin/repos/repo_edit.html'),
             defaults=defaults,
@@ -495,7 +494,7 @@
             h.flash(_('An error occurred during setting this'
                       ' repository in public journal'),
                     category='error')
-        return redirect(url('edit_repo_advanced', repo_name=repo_name))
+        raise HTTPFound(location=url('edit_repo_advanced', repo_name=repo_name))
 
 
     @HasRepoPermissionAllDecorator('repository.admin')
@@ -521,7 +520,7 @@
             h.flash(_('An error occurred during this operation'),
                     category='error')
 
-        return redirect(url('edit_repo_advanced', repo_name=repo_name))
+        raise HTTPFound(location=url('edit_repo_advanced', repo_name=repo_name))
 
     @HasRepoPermissionAllDecorator('repository.admin')
     def edit_advanced_locking(self, repo_name):
@@ -542,7 +541,7 @@
             log.error(traceback.format_exc())
             h.flash(_('An error occurred during unlocking'),
                     category='error')
-        return redirect(url('edit_repo_advanced', repo_name=repo_name))
+        raise HTTPFound(location=url('edit_repo_advanced', repo_name=repo_name))
 
     @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
     def toggle_locking(self, repo_name):
@@ -567,7 +566,7 @@
             log.error(traceback.format_exc())
             h.flash(_('An error occurred during unlocking'),
                     category='error')
-        return redirect(url('summary_home', repo_name=repo_name))
+        raise HTTPFound(location=url('summary_home', repo_name=repo_name))
 
     @HasRepoPermissionAllDecorator('repository.admin')
     def edit_caches(self, repo_name):
@@ -577,7 +576,7 @@
         c.active = 'caches'
         if request.POST:
             try:
-                ScmModel().mark_for_invalidation(repo_name, delete=True)
+                ScmModel().mark_for_invalidation(repo_name)
                 Session().commit()
                 h.flash(_('Cache invalidation successful'),
                         category='success')
@@ -586,7 +585,7 @@
                 h.flash(_('An error occurred during cache invalidation'),
                         category='error')
 
-            return redirect(url('edit_repo_caches', repo_name=c.repo_name))
+            raise HTTPFound(location=url('edit_repo_caches', repo_name=c.repo_name))
         return render('admin/repos/repo_edit.html')
 
     @HasRepoPermissionAllDecorator('repository.admin')
@@ -603,7 +602,7 @@
                 log.error(traceback.format_exc())
                 h.flash(_('An error occurred during pull from remote location'),
                         category='error')
-            return redirect(url('edit_repo_remote', repo_name=c.repo_name))
+            raise HTTPFound(location=url('edit_repo_remote', repo_name=c.repo_name))
         return render('admin/repos/repo_edit.html')
 
     @HasRepoPermissionAllDecorator('repository.admin')
@@ -636,6 +635,6 @@
                 log.error(traceback.format_exc())
                 h.flash(_('An error occurred during deletion of repository stats'),
                         category='error')
-            return redirect(url('edit_repo_statistics', repo_name=c.repo_name))
+            raise HTTPFound(location=url('edit_repo_statistics', repo_name=c.repo_name))
 
         return render('admin/repos/repo_edit.html')
--- a/kallithea/controllers/admin/settings.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/admin/settings.py	Mon Mar 14 00:36:08 2016 +0100
@@ -31,8 +31,8 @@
 
 from formencode import htmlfill
 from pylons import request, tmpl_context as c, url, config
-from pylons.controllers.util import redirect
 from pylons.i18n.translation import _
+from webob.exc import HTTPFound
 
 from kallithea.lib import helpers as h
 from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator
@@ -64,25 +64,22 @@
     def _get_hg_ui_settings(self):
         ret = Ui.query().all()
 
-        if not ret:
-            raise Exception('Could not get application ui settings !')
         settings = {}
         for each in ret:
-            k = each.ui_key
+            k = each.ui_section + '_' + each.ui_key
             v = each.ui_value
-            if k == '/':
-                k = 'root_path'
+            if k == 'paths_/':
+                k = 'paths_root_path'
 
-            if k == 'push_ssl':
+            if k == 'web_push_ssl':
                 v = str2bool(v)
 
-            if k.find('.') != -1:
-                k = k.replace('.', '_')
+            k = k.replace('.', '_')
 
             if each.ui_section in ['hooks', 'extensions']:
                 v = each.ui_active
 
-            settings[each.ui_section + '_' + k] = v
+            settings[k] = v
         return settings
 
     @HasPermissionAllDecorator('hg.admin')
@@ -104,66 +101,40 @@
                      force_defaults=False)
 
             try:
-                sett = Ui.get_by_key('push_ssl')
+                sett = Ui.get_by_key('web', 'push_ssl')
                 sett.ui_value = form_result['web_push_ssl']
-                Session().add(sett)
+
                 if c.visual.allow_repo_location_change:
-                    sett = Ui.get_by_key('/')
+                    sett = Ui.get_by_key('paths', '/')
                     sett.ui_value = form_result['paths_root_path']
-                    Session().add(sett)
 
                 #HOOKS
-                sett = Ui.get_by_key(Ui.HOOK_UPDATE)
+                sett = Ui.get_by_key('hooks', Ui.HOOK_UPDATE)
                 sett.ui_active = form_result['hooks_changegroup_update']
-                Session().add(sett)
 
-                sett = Ui.get_by_key(Ui.HOOK_REPO_SIZE)
+                sett = Ui.get_by_key('hooks', Ui.HOOK_REPO_SIZE)
                 sett.ui_active = form_result['hooks_changegroup_repo_size']
-                Session().add(sett)
 
-                sett = Ui.get_by_key(Ui.HOOK_PUSH)
+                sett = Ui.get_by_key('hooks', Ui.HOOK_PUSH)
                 sett.ui_active = form_result['hooks_changegroup_push_logger']
-                Session().add(sett)
 
-                sett = Ui.get_by_key(Ui.HOOK_PULL)
+                sett = Ui.get_by_key('hooks', Ui.HOOK_PULL)
                 sett.ui_active = form_result['hooks_outgoing_pull_logger']
 
-                Session().add(sett)
-
                 ## EXTENSIONS
-                sett = Ui.get_by_key('largefiles')
-                if not sett:
-                    #make one if it's not there !
-                    sett = Ui()
-                    sett.ui_key = 'largefiles'
-                    sett.ui_section = 'extensions'
+                sett = Ui.get_or_create('extensions', 'largefiles')
                 sett.ui_active = form_result['extensions_largefiles']
-                Session().add(sett)
 
-                sett = Ui.get_by_key('hgsubversion')
-                if not sett:
-                    #make one if it's not there !
-                    sett = Ui()
-                    sett.ui_key = 'hgsubversion'
-                    sett.ui_section = 'extensions'
-
+                sett = Ui.get_or_create('extensions', 'hgsubversion')
                 sett.ui_active = form_result['extensions_hgsubversion']
                 if sett.ui_active:
                     try:
                         import hgsubversion  # pragma: no cover
                     except ImportError:
                         raise HgsubversionImportError
-                Session().add(sett)
 
-#                sett = Ui.get_by_key('hggit')
-#                if not sett:
-#                    #make one if it's not there !
-#                    sett = Ui()
-#                    sett.ui_key = 'hggit'
-#                    sett.ui_section = 'extensions'
-#
+#                sett = Ui.get_or_create('extensions', 'hggit')
 #                sett.ui_active = form_result['extensions_hggit']
-#                Session().add(sett)
 
                 Session().commit()
 
@@ -206,7 +177,7 @@
             if invalidate_cache:
                 log.debug('invalidating all repositories cache')
                 for repo in Repository.get_all():
-                    ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
+                    ScmModel().mark_for_invalidation(repo.repo_name)
 
             filesystem_repos = ScmModel().repo_scan()
             added, removed = repo2db_mapper(filesystem_repos, rm_obsolete,
@@ -218,7 +189,7 @@
                  for repo_name in added) or '-',
                  ', '.join(h.escape(safe_unicode(repo_name)) for repo_name in removed) or '-')),
                 category='success')
-            return redirect(url('admin_settings_mapping'))
+            raise HTTPFound(location=url('admin_settings_mapping'))
 
         defaults = Setting.get_app_settings()
         defaults.update(self._get_hg_ui_settings())
@@ -278,7 +249,7 @@
                           'application settings'),
                           category='error')
 
-            return redirect(url('admin_settings_global'))
+            raise HTTPFound(location=url('admin_settings_global'))
 
         defaults = Setting.get_app_settings()
         defaults.update(self._get_hg_ui_settings())
@@ -336,7 +307,7 @@
                           'visualisation settings'),
                         category='error')
 
-            return redirect(url('admin_settings_visual'))
+            raise HTTPFound(location=url('admin_settings_visual'))
 
         defaults = Setting.get_app_settings()
         defaults.update(self._get_hg_ui_settings())
@@ -359,12 +330,12 @@
                                'Kallithea version: %s' % c.kallithea_version)
             if not test_email:
                 h.flash(_('Please enter email address'), category='error')
-                return redirect(url('admin_settings_email'))
+                raise HTTPFound(location=url('admin_settings_email'))
 
-            test_email_txt_body = EmailNotificationModel()\
+            test_email_txt_body = EmailNotificationModel() \
                 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
                                 'txt', body=test_body)
-            test_email_html_body = EmailNotificationModel()\
+            test_email_html_body = EmailNotificationModel() \
                 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
                                 'html', body=test_body)
 
@@ -374,7 +345,7 @@
                      test_email_txt_body, test_email_html_body)
 
             h.flash(_('Send email task created'), category='success')
-            return redirect(url('admin_settings_email'))
+            raise HTTPFound(location=url('admin_settings_email'))
 
         defaults = Setting.get_app_settings()
         defaults.update(self._get_hg_ui_settings())
@@ -425,7 +396,7 @@
                     h.flash(_('Error occurred during hook creation'),
                             category='error')
 
-                return redirect(url('admin_settings_hooks'))
+                raise HTTPFound(location=url('admin_settings_hooks'))
 
         defaults = Setting.get_app_settings()
         defaults.update(self._get_hg_ui_settings())
@@ -449,7 +420,7 @@
             full_index = request.POST.get('full_index', False)
             run_task(tasks.whoosh_index, repo_location, full_index)
             h.flash(_('Whoosh reindex task scheduled'), category='success')
-            return redirect(url('admin_settings_search'))
+            raise HTTPFound(location=url('admin_settings_search'))
 
         defaults = Setting.get_app_settings()
         defaults.update(self._get_hg_ui_settings())
--- a/kallithea/controllers/admin/user_groups.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/admin/user_groups.py	Mon Mar 14 00:36:08 2016 +0100
@@ -31,8 +31,8 @@
 
 from formencode import htmlfill
 from pylons import request, tmpl_context as c, url, config
-from pylons.controllers.util import redirect
 from pylons.i18n.translation import _
+from webob.exc import HTTPFound
 
 from sqlalchemy.orm import joinedload
 from sqlalchemy.sql.expression import func
@@ -90,8 +90,8 @@
     def index(self, format='html'):
         """GET /users_groups: All items in the collection"""
         # url('users_groups')
-        _list = UserGroup.query()\
-                        .order_by(func.lower(UserGroup.users_group_name))\
+        _list = UserGroup.query() \
+                        .order_by(func.lower(UserGroup.users_group_name)) \
                         .all()
         group_iter = UserGroupList(_list, perm_set=['usergroup.admin'])
         user_groups_data = []
@@ -163,7 +163,7 @@
             h.flash(_('Error occurred during creation of user group %s') \
                     % request.POST.get('users_group_name'), category='error')
 
-        return redirect(url('users_groups'))
+        raise HTTPFound(location=url('users_groups'))
 
     @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
     def new(self, format='html'):
@@ -224,7 +224,7 @@
             h.flash(_('Error occurred during update of user group %s') \
                     % request.POST.get('users_group_name'), category='error')
 
-        return redirect(url('edit_users_group', id=id))
+        raise HTTPFound(location=url('edit_users_group', id=id))
 
     @HasUserGroupPermissionAnyDecorator('usergroup.admin')
     def delete(self, id):
@@ -246,7 +246,7 @@
             log.error(traceback.format_exc())
             h.flash(_('An error occurred during deletion of user group'),
                     category='error')
-        return redirect(url('users_groups'))
+        raise HTTPFound(location=url('users_groups'))
 
     def show(self, id, format='html'):
         """GET /user_groups/id: Show a specific item"""
@@ -312,13 +312,13 @@
                                                  form['perms_updates'])
         except RepoGroupAssignmentError:
             h.flash(_('Target group cannot be the same'), category='error')
-            return redirect(url('edit_user_group_perms', id=id))
+            raise HTTPFound(location=url('edit_user_group_perms', id=id))
         #TODO: implement this
         #action_logger(self.authuser, 'admin_changed_repo_permissions',
         #              repo_name, self.ip_addr, self.sa)
         Session().commit()
         h.flash(_('User group permissions updated'), category='success')
-        return redirect(url('edit_user_group_perms', id=id))
+        raise HTTPFound(location=url('edit_user_group_perms', id=id))
 
     @HasUserGroupPermissionAnyDecorator('usergroup.admin')
     def delete_perms(self, id):
@@ -362,20 +362,20 @@
             'repositories': {},
             'repositories_groups': {}
         }
-        ugroup_repo_perms = UserGroupRepoToPerm.query()\
-            .options(joinedload(UserGroupRepoToPerm.permission))\
-            .options(joinedload(UserGroupRepoToPerm.repository))\
-            .filter(UserGroupRepoToPerm.users_group_id == id)\
+        ugroup_repo_perms = UserGroupRepoToPerm.query() \
+            .options(joinedload(UserGroupRepoToPerm.permission)) \
+            .options(joinedload(UserGroupRepoToPerm.repository)) \
+            .filter(UserGroupRepoToPerm.users_group_id == id) \
             .all()
 
         for gr in ugroup_repo_perms:
             permissions['repositories'][gr.repository.repo_name]  \
                 = gr.permission.permission_name
 
-        ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
-            .options(joinedload(UserGroupRepoGroupToPerm.permission))\
-            .options(joinedload(UserGroupRepoGroupToPerm.group))\
-            .filter(UserGroupRepoGroupToPerm.users_group_id == id)\
+        ugroup_group_perms = UserGroupRepoGroupToPerm.query() \
+            .options(joinedload(UserGroupRepoGroupToPerm.permission)) \
+            .options(joinedload(UserGroupRepoGroupToPerm.group)) \
+            .filter(UserGroupRepoGroupToPerm.users_group_id == id) \
             .all()
 
         for gr in ugroup_group_perms:
@@ -418,8 +418,8 @@
             Session().add(user_group)
             usergroup_model = UserGroupModel()
 
-            defs = UserGroupToPerm.query()\
-                .filter(UserGroupToPerm.users_group == user_group)\
+            defs = UserGroupToPerm.query() \
+                .filter(UserGroupToPerm.users_group == user_group) \
                 .all()
             for ug in defs:
                 Session().delete(ug)
@@ -444,7 +444,7 @@
             h.flash(_('An error occurred during permissions saving'),
                     category='error')
 
-        return redirect(url('edit_user_group_default_perms', id=id))
+        raise HTTPFound(location=url('edit_user_group_default_perms', id=id))
 
     @HasUserGroupPermissionAnyDecorator('usergroup.admin')
     def edit_advanced(self, id):
--- a/kallithea/controllers/admin/users.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/admin/users.py	Mon Mar 14 00:36:08 2016 +0100
@@ -31,10 +31,9 @@
 
 from formencode import htmlfill
 from pylons import request, tmpl_context as c, url, config
-from pylons.controllers.util import redirect
 from pylons.i18n.translation import _
 from sqlalchemy.sql.expression import func
-from webob.exc import HTTPNotFound
+from webob.exc import HTTPFound, HTTPNotFound
 
 import kallithea
 from kallithea.lib.exceptions import DefaultUserException, \
@@ -72,9 +71,9 @@
         """GET /users: All items in the collection"""
         # url('users')
 
-        c.users_list = User.query().order_by(User.username)\
-                        .filter(User.username != User.DEFAULT_USER)\
-                        .order_by(func.lower(User.username))\
+        c.users_list = User.query().order_by(User.username) \
+                        .filter(User.username != User.DEFAULT_USER) \
+                        .order_by(func.lower(User.username)) \
                         .all()
 
         users_data = []
@@ -148,7 +147,7 @@
             log.error(traceback.format_exc())
             h.flash(_('Error occurred during creation of user %s') \
                     % request.POST.get('username'), category='error')
-        return redirect(url('users'))
+        raise HTTPFound(location=url('users'))
 
     def new(self, format='html'):
         """GET /users/new: Form to create a new item"""
@@ -201,7 +200,7 @@
             log.error(traceback.format_exc())
             h.flash(_('Error occurred during update of user %s') \
                     % form_result.get('username'), category='error')
-        return redirect(url('edit_user', id=id))
+        raise HTTPFound(location=url('edit_user', id=id))
 
     def delete(self, id):
         """DELETE /users/id: Delete an existing item"""
@@ -222,7 +221,7 @@
             log.error(traceback.format_exc())
             h.flash(_('An error occurred during deletion of user'),
                     category='error')
-        return redirect(url('users'))
+        raise HTTPFound(location=url('users'))
 
     def show(self, id, format='html'):
         """GET /users/id: Show a specific item"""
@@ -306,7 +305,7 @@
         ApiKeyModel().create(c.user.user_id, description, lifetime)
         Session().commit()
         h.flash(_("API key successfully created"), category='success')
-        return redirect(url('edit_user_api_keys', id=c.user.user_id))
+        raise HTTPFound(location=url('edit_user_api_keys', id=c.user.user_id))
 
     def delete_api_key(self, id):
         c.user = self._get_user_or_raise_if_default(id)
@@ -324,7 +323,7 @@
             Session().commit()
             h.flash(_("API key successfully deleted"), category='success')
 
-        return redirect(url('edit_user_api_keys', id=c.user.user_id))
+        raise HTTPFound(location=url('edit_user_api_keys', id=c.user.user_id))
 
     def update_account(self, id):
         pass
@@ -363,8 +362,8 @@
             Session().add(user)
             user_model = UserModel()
 
-            defs = UserToPerm.query()\
-                .filter(UserToPerm.user == user)\
+            defs = UserToPerm.query() \
+                .filter(UserToPerm.user == user) \
                 .all()
             for ug in defs:
                 Session().delete(ug)
@@ -387,12 +386,12 @@
             log.error(traceback.format_exc())
             h.flash(_('An error occurred during permissions saving'),
                     category='error')
-        return redirect(url('edit_user_perms', id=id))
+        raise HTTPFound(location=url('edit_user_perms', id=id))
 
     def edit_emails(self, id):
         c.user = self._get_user_or_raise_if_default(id)
         c.active = 'emails'
-        c.user_email_map = UserEmailMap.query()\
+        c.user_email_map = UserEmailMap.query() \
             .filter(UserEmailMap.user == c.user).all()
 
         defaults = c.user.get_dict()
@@ -420,7 +419,7 @@
             log.error(traceback.format_exc())
             h.flash(_('An error occurred during email saving'),
                     category='error')
-        return redirect(url('edit_user_emails', id=id))
+        raise HTTPFound(location=url('edit_user_emails', id=id))
 
     def delete_email(self, id):
         """DELETE /user_emails_delete/id: Delete an existing item"""
@@ -431,16 +430,16 @@
         user_model.delete_extra_email(id, email_id)
         Session().commit()
         h.flash(_("Removed email from user"), category='success')
-        return redirect(url('edit_user_emails', id=id))
+        raise HTTPFound(location=url('edit_user_emails', id=id))
 
     def edit_ips(self, id):
         c.user = self._get_user_or_raise_if_default(id)
         c.active = 'ips'
-        c.user_ip_map = UserIpMap.query()\
+        c.user_ip_map = UserIpMap.query() \
             .filter(UserIpMap.user == c.user).all()
 
         c.inherit_default_ips = c.user.inherit_default_permissions
-        c.default_user_ip_map = UserIpMap.query()\
+        c.default_user_ip_map = UserIpMap.query() \
             .filter(UserIpMap.user == User.get_default_user()).all()
 
         defaults = c.user.get_dict()
@@ -470,8 +469,8 @@
                     category='error')
 
         if 'default_user' in request.POST:
-            return redirect(url('admin_permissions_ips'))
-        return redirect(url('edit_user_ips', id=id))
+            raise HTTPFound(location=url('admin_permissions_ips'))
+        raise HTTPFound(location=url('edit_user_ips', id=id))
 
     def delete_ip(self, id):
         """DELETE /user_ips_delete/id: Delete an existing item"""
@@ -483,5 +482,5 @@
         h.flash(_("Removed IP address from user whitelist"), category='success')
 
         if 'default_user' in request.POST:
-            return redirect(url('admin_permissions_ips'))
-        return redirect(url('edit_user_ips', id=id))
+            raise HTTPFound(location=url('admin_permissions_ips'))
+        raise HTTPFound(location=url('edit_user_ips', id=id))
--- a/kallithea/controllers/api/api.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/api/api.py	Mon Mar 14 00:36:08 2016 +0100
@@ -25,7 +25,6 @@
 :license: GPLv3, see LICENSE.md for more details.
 """
 
-
 import time
 import traceback
 import logging
@@ -675,7 +674,7 @@
         if User.get_by_username(username):
             raise JSONRPCError("user `%s` already exist" % (username,))
 
-        if User.get_by_email(email, case_insensitive=True):
+        if User.get_by_email(email):
             raise JSONRPCError("email `%s` already exist" % (email,))
 
         if Optional.extract(extern_name):
@@ -705,10 +704,10 @@
 
     @HasPermissionAllDecorator('hg.admin')
     def update_user(self, apiuser, userid, username=Optional(None),
-                    email=Optional(None),password=Optional(None),
+                    email=Optional(None), password=Optional(None),
                     firstname=Optional(None), lastname=Optional(None),
                     active=Optional(None), admin=Optional(None),
-                    extern_type=Optional(None), extern_name=Optional(None),):
+                    extern_type=Optional(None), extern_name=Optional(None)):
         """
         updates given user if such user exists. This command can
         be executed only using api_key belonging to user with admin rights.
@@ -1074,7 +1073,7 @@
             raise JSONRPCError('failed to delete user group ID:%s %s' %
                                (user_group.users_group_id,
                                 user_group.users_group_name)
-            )
+                               )
 
     # permission check inside
     def add_user_to_user_group(self, apiuser, usergroupid, userid):
@@ -1455,7 +1454,7 @@
         """
         if not HasPermissionAnyApi('hg.admin')(user=apiuser):
             if not isinstance(owner, Optional):
-                #forbid setting owner for non-admins
+                # forbid setting owner for non-admins
                 raise JSONRPCError(
                     'Only Kallithea admin can specify `owner` param'
                 )
@@ -1562,7 +1561,7 @@
                 raise JSONRPCError('no permission to create (or move) repositories')
 
             if not isinstance(owner, Optional):
-                #forbid setting owner for non-admins
+                # forbid setting owner for non-admins
                 raise JSONRPCError(
                     'Only Kallithea admin can specify `owner` param'
                 )
@@ -1660,7 +1659,7 @@
                                      'repository.read')(user=apiuser,
                                                         repo_name=repo.repo_name):
             if not isinstance(owner, Optional):
-                #forbid setting owner for non-admins
+                # forbid setting owner for non-admins
                 raise JSONRPCError(
                     'Only Kallithea admin can specify `owner` param'
                 )
@@ -1741,7 +1740,7 @@
         if not HasPermissionAnyApi('hg.admin')(user=apiuser):
             # check if we have admin permission for this repo !
             if not HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
-                                                           repo_name=repo.repo_name):
+                                                               repo_name=repo.repo_name):
                 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 
         try:
@@ -2175,7 +2174,7 @@
             log.error(traceback.format_exc())
             raise JSONRPCError('failed to delete repo group ID:%s %s' %
                                (repo_group.group_id, repo_group.group_name)
-            )
+                               )
 
     # permission check inside
     def grant_user_permission_to_repo_group(self, apiuser, repogroupid, userid,
@@ -2315,7 +2314,7 @@
     # permission check inside
     def grant_user_group_permission_to_repo_group(
             self, apiuser, repogroupid, usergroupid, perm,
-            apply_to_children=Optional('none'),):
+            apply_to_children=Optional('none')):
         """
         Grant permission for user group on given repository group, or update
         existing one if found. This command can be executed only using
@@ -2381,9 +2380,9 @@
             Session().commit()
             return dict(
                 msg='Granted perm: `%s` (recursive:%s) for user group: `%s` in repo group: `%s`' % (
-                        perm.permission_name, apply_to_children,
-                        user_group.users_group_name, repo_group.name
-                    ),
+                    perm.permission_name, apply_to_children,
+                    user_group.users_group_name, repo_group.name
+                ),
                 success=True
             )
         except Exception:
@@ -2509,9 +2508,9 @@
             user_id = get_user_or_error(userid).user_id
 
         gists = []
-        _gists = Gist().query()\
-            .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\
-            .filter(Gist.gist_owner == user_id)\
+        _gists = Gist().query() \
+            .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time())) \
+            .filter(Gist.gist_owner == user_id) \
             .order_by(Gist.created_on.desc())
         for gist in _gists:
             gists.append(gist.get_api_data())
--- a/kallithea/controllers/changelog.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/changelog.py	Mon Mar 14 00:36:08 2016 +0100
@@ -29,9 +29,8 @@
 import traceback
 
 from pylons import request, url, session, tmpl_context as c
-from pylons.controllers.util import redirect
 from pylons.i18n.translation import _
-from webob.exc import HTTPNotFound, HTTPBadRequest
+from webob.exc import HTTPFound, HTTPNotFound, HTTPBadRequest
 
 import kallithea.lib.helpers as h
 from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
@@ -39,7 +38,7 @@
 from kallithea.lib.helpers import RepoPage
 from kallithea.lib.compat import json
 from kallithea.lib.graphmod import graph_data
-from kallithea.lib.vcs.exceptions import RepositoryError, ChangesetDoesNotExistError,\
+from kallithea.lib.vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
     ChangesetError, NodeDoesNotExistError, EmptyRepositoryError
 from kallithea.lib.utils2 import safe_int, safe_str
 
@@ -99,8 +98,8 @@
         if request.GET.get('set'):
             request.GET.pop('set', None)
             if revision is None:
-                return redirect(url('changelog_home', repo_name=repo_name, **request.GET))
-            return redirect(url('changelog_file_home', repo_name=repo_name, revision=revision, f_path=f_path, **request.GET))
+                raise HTTPFound(location=url('changelog_home', repo_name=repo_name, **request.GET))
+            raise HTTPFound(location=url('changelog_file_home', repo_name=repo_name, revision=revision, f_path=f_path, **request.GET))
 
         limit = 2000
         default = 100
@@ -118,7 +117,7 @@
             branch_name not in c.db_repo_scm_instance.branches and
             branch_name not in c.db_repo_scm_instance.closed_branches and
             not revision):
-            return redirect(url('changelog_file_home', repo_name=c.repo_name,
+            raise HTTPFound(location=url('changelog_file_home', repo_name=c.repo_name,
                                     revision=branch_name, f_path=f_path or ''))
 
         if revision == 'tip':
@@ -140,7 +139,7 @@
                         collection = cs.get_file_history(f_path)
                     except RepositoryError as e:
                         h.flash(safe_str(e), category='warning')
-                        redirect(h.url('changelog_home', repo_name=repo_name))
+                        raise HTTPFound(location=h.url('changelog_home', repo_name=repo_name))
                 collection = list(reversed(collection))
             else:
                 collection = c.db_repo_scm_instance.get_changesets(start=0, end=revision,
@@ -155,11 +154,11 @@
             c.statuses = c.db_repo.statuses(page_revisions)
         except EmptyRepositoryError as e:
             h.flash(safe_str(e), category='warning')
-            return redirect(url('summary_home', repo_name=c.repo_name))
+            raise HTTPFound(location=url('summary_home', repo_name=c.repo_name))
         except (RepositoryError, ChangesetDoesNotExistError, Exception) as e:
             log.error(traceback.format_exc())
             h.flash(safe_str(e), category='error')
-            return redirect(url('changelog_home', repo_name=c.repo_name))
+            raise HTTPFound(location=url('changelog_home', repo_name=c.repo_name))
 
         c.branch_name = branch_name
         c.branch_filters = [('', _('None'))] + \
--- a/kallithea/controllers/changeset.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/changeset.py	Mon Mar 14 00:36:08 2016 +0100
@@ -29,19 +29,18 @@
 import logging
 import traceback
 from collections import defaultdict
-from webob.exc import HTTPForbidden, HTTPBadRequest, HTTPNotFound
 
 from pylons import tmpl_context as c, request, response
 from pylons.i18n.translation import _
-from pylons.controllers.util import redirect
+from webob.exc import HTTPFound, HTTPForbidden, HTTPBadRequest, HTTPNotFound
+
 from kallithea.lib.utils import jsonify
-
 from kallithea.lib.vcs.exceptions import RepositoryError, \
     ChangesetDoesNotExistError
 
 from kallithea.lib.compat import json
 import kallithea.lib.helpers as h
-from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
+from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
     NotAnonymous
 from kallithea.lib.base import BaseRepoController, render
 from kallithea.lib.utils import action_logger
@@ -173,6 +172,27 @@
     return h.link_to(icon, h.url.current(**params), title=lbl, class_='tooltip')
 
 
+# Could perhaps be nice to have in the model but is too high level ...
+def create_comment(text, status, f_path, line_no, revision=None, pull_request_id=None, closing_pr=None):
+    """Comment functionality shared between changesets and pullrequests"""
+    f_path = f_path or None
+    line_no = line_no or None
+
+    comment = ChangesetCommentsModel().create(
+        text=text,
+        repo=c.db_repo.repo_id,
+        user=c.authuser.user_id,
+        revision=revision,
+        pull_request=pull_request_id,
+        f_path=f_path,
+        line_no=line_no,
+        status_change=ChangesetStatus.get_status_lbl(status) if status else None,
+        closing_pr=closing_pr,
+    )
+
+    return comment
+
+
 class ChangesetController(BaseRepoController):
 
     def __before__(self):
@@ -185,6 +205,7 @@
         c.user_groups_array = repo_model.get_user_groups_js()
 
     def _index(self, revision, method):
+        c.pull_request = None
         c.anchor_url = anchor_url
         c.ignorews_url = _ignorews_url
         c.context_url = _context_url
@@ -207,7 +228,7 @@
             if not c.cs_ranges:
                 raise RepositoryError('Changeset range returned empty result')
 
-        except(ChangesetDoesNotExistError,), e:
+        except ChangesetDoesNotExistError:
             log.debug(traceback.format_exc())
             msg = _('Such revision does not exist for this repository')
             h.flash(msg, category='error')
@@ -226,7 +247,6 @@
 
         # Iterate over ranges (default changeset view is always one changeset)
         for changeset in c.cs_ranges:
-            inlines = []
             if method == 'show':
                 c.statuses.extend([ChangesetStatusModel().get_status(
                             c.db_repo.repo_id, changeset.raw_id)])
@@ -244,7 +264,7 @@
                                               changeset.raw_id, with_revisions=True)
                                 if st.changeset_comment_id is not None)
 
-                inlines = ChangesetCommentsModel()\
+                inlines = ChangesetCommentsModel() \
                             .get_inline_comments(c.db_repo.repo_id,
                                                  revision=changeset.raw_id)
                 c.inline_comments.extend(inlines)
@@ -254,7 +274,7 @@
             cs2 = changeset.raw_id
             cs1 = changeset.parents[0].raw_id if changeset.parents else EmptyChangeset().raw_id
             context_lcl = get_line_ctx('', request.GET)
-            ign_whitespace_lcl = ign_whitespace_lcl = get_ignore_ws('', request.GET)
+            ign_whitespace_lcl = get_ignore_ws('', request.GET)
 
             _diff = c.db_repo_scm_instance.get_diff(cs1, cs2,
                 ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
@@ -349,57 +369,47 @@
                                    'repository.admin')
     @jsonify
     def comment(self, repo_name, revision):
+        assert request.environ.get('HTTP_X_PARTIAL_XHR')
+
         status = request.POST.get('changeset_status')
         text = request.POST.get('text', '').strip()
 
-        c.comment = comment = ChangesetCommentsModel().create(
-            text=text,
-            repo=c.db_repo.repo_id,
-            user=c.authuser.user_id,
+        c.comment = create_comment(
+            text,
+            status,
             revision=revision,
             f_path=request.POST.get('f_path'),
             line_no=request.POST.get('line'),
-            status_change=(ChangesetStatus.get_status_lbl(status)
-                           if status else None)
         )
 
         # get status if set !
         if status:
             # if latest status was from pull request and it's closed
-            # disallow changing status !
-            # dont_allow_on_closed_pull_request = True !
-
+            # disallow changing status ! RLY?
             try:
                 ChangesetStatusModel().set_status(
                     c.db_repo.repo_id,
                     status,
                     c.authuser.user_id,
-                    comment,
+                    c.comment,
                     revision=revision,
-                    dont_allow_on_closed_pull_request=True
+                    dont_allow_on_closed_pull_request=True,
                 )
             except StatusChangeOnClosedPullRequestError:
-                log.debug(traceback.format_exc())
-                msg = _('Changing status on a changeset associated with '
-                        'a closed pull request is not allowed')
-                h.flash(msg, category='warning')
-                return redirect(h.url('changeset_home', repo_name=repo_name,
-                                      revision=revision))
+                log.debug('cannot change status on %s with closed pull request', revision)
+                raise HTTPBadRequest()
+
         action_logger(self.authuser,
                       'user_commented_revision:%s' % revision,
                       c.db_repo, self.ip_addr, self.sa)
 
         Session().commit()
 
-        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
-            return redirect(h.url('changeset_home', repo_name=repo_name,
-                                  revision=revision))
-        #only ajax below
         data = {
            'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
         }
-        if comment is not None:
-            data.update(comment.get_dict())
+        if c.comment is not None:
+            data.update(c.comment.get_dict())
             data.update({'rendered_text':
                          render('changeset/changeset_comment_block.html')})
 
@@ -409,18 +419,6 @@
     @NotAnonymous()
     @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
                                    'repository.admin')
-    def preview_comment(self):
-        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
-            raise HTTPBadRequest()
-        text = request.POST.get('text')
-        if text:
-            return h.rst_w_mentions(text)
-        return ''
-
-    @LoginRequired()
-    @NotAnonymous()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
     @jsonify
     def delete_comment(self, repo_name, comment_id):
         co = ChangesetComment.get(comment_id)
--- a/kallithea/controllers/compare.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/compare.py	Mon Mar 14 00:36:08 2016 +0100
@@ -30,11 +30,11 @@
 import logging
 import re
 
-from webob.exc import HTTPBadRequest
 from pylons import request, tmpl_context as c, url
-from pylons.controllers.util import redirect
 from pylons.i18n.translation import _
+from webob.exc import HTTPFound, HTTPBadRequest
 
+from kallithea.lib.utils2 import safe_str
 from kallithea.lib.vcs.utils.hgcompat import unionrepo
 from kallithea.lib import helpers as h
 from kallithea.lib.base import BaseRepoController, render
@@ -114,10 +114,10 @@
                 from dulwich.client import SubprocessGitClient
 
                 gitrepo = Repo(org_repo.path)
-                SubprocessGitClient(thin_packs=False).fetch(other_repo.path, gitrepo)
+                SubprocessGitClient(thin_packs=False).fetch(safe_str(other_repo.path), gitrepo)
 
                 gitrepo_remote = Repo(other_repo.path)
-                SubprocessGitClient(thin_packs=False).fetch(org_repo.path, gitrepo_remote)
+                SubprocessGitClient(thin_packs=False).fetch(safe_str(org_repo.path), gitrepo_remote)
 
                 revs = []
                 for x in gitrepo_remote.get_walker(include=[other_rev],
@@ -206,19 +206,19 @@
             msg = 'Could not find org repo %s' % org_repo
             log.error(msg)
             h.flash(msg, category='error')
-            return redirect(url('compare_home', repo_name=c.repo_name))
+            raise HTTPFound(location=url('compare_home', repo_name=c.repo_name))
 
         if other_repo is None:
             msg = 'Could not find other repo %s' % other_repo
             log.error(msg)
             h.flash(msg, category='error')
-            return redirect(url('compare_home', repo_name=c.repo_name))
+            raise HTTPFound(location=url('compare_home', repo_name=c.repo_name))
 
         if org_repo.scm_instance.alias != other_repo.scm_instance.alias:
             msg = 'compare of two different kind of remote repos not available'
             log.error(msg)
             h.flash(msg, category='error')
-            return redirect(url('compare_home', repo_name=c.repo_name))
+            raise HTTPFound(location=url('compare_home', repo_name=c.repo_name))
 
         c.a_rev = self._get_ref_rev(org_repo, org_ref_type, org_ref_name,
             returnempty=True)
--- a/kallithea/controllers/files.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/files.py	Mon Mar 14 00:36:08 2016 +0100
@@ -26,6 +26,7 @@
 """
 
 import os
+import posixpath
 import logging
 import traceback
 import tempfile
@@ -33,14 +34,14 @@
 
 from pylons import request, response, tmpl_context as c, url
 from pylons.i18n.translation import _
-from pylons.controllers.util import redirect
+from webob.exc import HTTPFound
+
 from kallithea.lib.utils import jsonify, action_logger
-
 from kallithea.lib import diffs
 from kallithea.lib import helpers as h
 
 from kallithea.lib.compat import OrderedDict
-from kallithea.lib.utils2 import convert_line_endings, detect_mode, safe_str,\
+from kallithea.lib.utils2 import convert_line_endings, detect_mode, safe_str, \
     str2bool
 from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 from kallithea.lib.base import BaseRepoController, render
@@ -48,7 +49,7 @@
 from kallithea.lib.vcs.conf import settings
 from kallithea.lib.vcs.exceptions import RepositoryError, \
     ChangesetDoesNotExistError, EmptyRepositoryError, \
-    ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,\
+    ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError, \
     NodeDoesNotExistError, ChangesetError, NodeError
 from kallithea.lib.vcs.nodes import FileNode
 
@@ -56,7 +57,7 @@
 from kallithea.model.scm import ScmModel
 from kallithea.model.db import Repository
 
-from kallithea.controllers.changeset import anchor_url, _ignorews_url,\
+from kallithea.controllers.changeset import anchor_url, _ignorews_url, \
     _context_url, get_line_ctx, get_ignore_ws
 from webob.exc import HTTPNotFound
 from kallithea.lib.exceptions import NonRelativePathError
@@ -92,7 +93,7 @@
             h.flash(h.literal(_('There are no files yet. %s') % add_new),
                     category='warning')
             raise HTTPNotFound()
-        except(ChangesetDoesNotExistError, LookupError), e:
+        except (ChangesetDoesNotExistError, LookupError):
             msg = _('Such revision does not exist for this repository')
             h.flash(msg, category='error')
             raise HTTPNotFound()
@@ -112,7 +113,7 @@
             file_node = cs.get_node(path)
             if file_node.is_dir():
                 raise RepositoryError('given path is a directory')
-        except(ChangesetDoesNotExistError,), e:
+        except ChangesetDoesNotExistError:
             msg = _('Such revision does not exist for this repository')
             h.flash(msg, category='error')
             raise HTTPNotFound()
@@ -137,6 +138,7 @@
         c.f_path = f_path
         c.annotate = annotate
         cur_rev = c.changeset.revision
+        c.fulldiff = request.GET.get('fulldiff')
 
         # prev link
         try:
@@ -306,7 +308,7 @@
                 % (h.person_by_id(repo.locked[0]),
                    h.fmt_date(h.time_to_datetime(repo.locked[1]))),
                 'warning')
-            return redirect(h.url('files_home',
+            raise HTTPFound(location=h.url('files_home',
                                   repo_name=repo_name, revision='tip'))
 
         # check if revision is a branch identifier- basically we cannot
@@ -316,7 +318,7 @@
         if revision not in _branches.keys() + _branches.values():
             h.flash(_('You can only delete files with revision '
                       'being a valid branch'), category='warning')
-            return redirect(h.url('files_home',
+            raise HTTPFound(location=h.url('files_home',
                                   repo_name=repo_name, revision='tip',
                                   f_path=f_path))
 
@@ -352,7 +354,7 @@
             except Exception:
                 log.error(traceback.format_exc())
                 h.flash(_('Error occurred during commit'), category='error')
-            return redirect(url('changeset_home',
+            raise HTTPFound(location=url('changeset_home',
                                 repo_name=c.repo_name, revision='tip'))
 
         return render('files/files_delete.html')
@@ -366,7 +368,7 @@
                 % (h.person_by_id(repo.locked[0]),
                    h.fmt_date(h.time_to_datetime(repo.locked[1]))),
                 'warning')
-            return redirect(h.url('files_home',
+            raise HTTPFound(location=h.url('files_home',
                                   repo_name=repo_name, revision='tip'))
 
         # check if revision is a branch identifier- basically we cannot
@@ -376,7 +378,7 @@
         if revision not in _branches.keys() + _branches.values():
             h.flash(_('You can only edit files with revision '
                       'being a valid branch'), category='warning')
-            return redirect(h.url('files_home',
+            raise HTTPFound(location=h.url('files_home',
                                   repo_name=repo_name, revision='tip',
                                   f_path=f_path))
 
@@ -386,7 +388,7 @@
         c.file = self.__get_filenode(c.cs, f_path)
 
         if c.file.is_binary:
-            return redirect(url('files_home', repo_name=c.repo_name,
+            raise HTTPFound(location=url('files_home', repo_name=c.repo_name,
                             revision=c.cs.raw_id, f_path=f_path))
         c.default_message = _('Edited file %s via Kallithea') % (f_path)
         c.f_path = f_path
@@ -405,7 +407,7 @@
 
             if content == old_content:
                 h.flash(_('No changes'), category='warning')
-                return redirect(url('changeset_home', repo_name=c.repo_name,
+                raise HTTPFound(location=url('changeset_home', repo_name=c.repo_name,
                                     revision='tip'))
             try:
                 self.scm_model.commit_change(repo=c.db_repo_scm_instance,
@@ -418,7 +420,7 @@
             except Exception:
                 log.error(traceback.format_exc())
                 h.flash(_('Error occurred during commit'), category='error')
-            return redirect(url('changeset_home',
+            raise HTTPFound(location=url('changeset_home',
                                 repo_name=c.repo_name, revision='tip'))
 
         return render('files/files_edit.html')
@@ -433,7 +435,7 @@
                 % (h.person_by_id(repo.locked[0]),
                    h.fmt_date(h.time_to_datetime(repo.locked[1]))),
                   'warning')
-            return redirect(h.url('files_home',
+            raise HTTPFound(location=h.url('files_home',
                                   repo_name=repo_name, revision='tip'))
 
         r_post = request.POST
@@ -462,15 +464,15 @@
 
             if not content:
                 h.flash(_('No content'), category='warning')
-                return redirect(url('changeset_home', repo_name=c.repo_name,
+                raise HTTPFound(location=url('changeset_home', repo_name=c.repo_name,
                                     revision='tip'))
             if not filename:
                 h.flash(_('No filename'), category='warning')
-                return redirect(url('changeset_home', repo_name=c.repo_name,
+                raise HTTPFound(location=url('changeset_home', repo_name=c.repo_name,
                                     revision='tip'))
             #strip all crap out of file, just leave the basename
             filename = os.path.basename(filename)
-            node_path = os.path.join(location, filename)
+            node_path = posixpath.join(location, filename)
             author = self.authuser.full_contact
 
             try:
@@ -492,14 +494,14 @@
             except NonRelativePathError as e:
                 h.flash(_('Location must be relative path and must not '
                           'contain .. in path'), category='warning')
-                return redirect(url('changeset_home', repo_name=c.repo_name,
+                raise HTTPFound(location=url('changeset_home', repo_name=c.repo_name,
                                     revision='tip'))
             except (NodeError, NodeAlreadyExistsError) as e:
                 h.flash(_(e), category='error')
             except Exception:
                 log.error(traceback.format_exc())
                 h.flash(_('Error occurred during commit'), category='error')
-            return redirect(url('changeset_home',
+            raise HTTPFound(location=url('changeset_home',
                                 repo_name=c.repo_name, revision='tip'))
 
         return render('files/files_add.html')
@@ -620,7 +622,7 @@
                 _url = url('files_home', repo_name=c.repo_name,
                            revision=diff1, f_path=c.f_path)
 
-            return redirect(_url)
+            raise HTTPFound(location=_url)
         try:
             if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
                 c.changeset_1 = c.db_repo_scm_instance.get_changeset(diff1)
@@ -655,7 +657,7 @@
                 node2 = FileNode(f_path, '', changeset=c.changeset_2)
         except (RepositoryError, NodeError):
             log.error(traceback.format_exc())
-            return redirect(url('files_home', repo_name=c.repo_name,
+            raise HTTPFound(location=url('files_home', repo_name=c.repo_name,
                                 f_path=f_path))
 
         if c.action == 'download':
--- a/kallithea/controllers/followers.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/followers.py	Mon Mar 14 00:36:08 2016 +0100
@@ -49,7 +49,7 @@
     def followers(self, repo_name):
         p = safe_int(request.GET.get('page', 1), 1)
         repo_id = c.db_repo.repo_id
-        d = UserFollowing.get_repo_followers(repo_id)\
+        d = UserFollowing.get_repo_followers(repo_id) \
             .order_by(UserFollowing.follows_from)
         c.followers_pager = Page(d, page=p, items_per_page=20)
 
--- a/kallithea/controllers/forks.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/forks.py	Mon Mar 14 00:36:08 2016 +0100
@@ -31,8 +31,8 @@
 from formencode import htmlfill
 
 from pylons import tmpl_context as c, request, url
-from pylons.controllers.util import redirect
 from pylons.i18n.translation import _
+from webob.exc import HTTPFound
 
 import kallithea.lib.helpers as h
 
@@ -62,7 +62,7 @@
 
         c.landing_revs_choices, c.landing_revs = ScmModel().get_repo_landing_revs()
 
-        c.can_update = Ui.get_by_key(Ui.HOOK_UPDATE).ui_active
+        c.can_update = Ui.get_by_key('hooks', Ui.HOOK_UPDATE).ui_active
 
     def __load_data(self, repo_name=None):
         """
@@ -77,11 +77,11 @@
 
         if c.repo_info is None:
             h.not_mapped_error(repo_name)
-            return redirect(url('repos'))
+            raise HTTPFound(location=url('repos'))
 
         c.default_user_id = User.get_default_user().user_id
-        c.in_public_journal = UserFollowing.query()\
-            .filter(UserFollowing.user_id == c.default_user_id)\
+        c.in_public_journal = UserFollowing.query() \
+            .filter(UserFollowing.user_id == c.default_user_id) \
             .filter(UserFollowing.follows_repository == c.repo_info).scalar()
 
         if c.repo_info.stats:
@@ -137,7 +137,7 @@
         c.repo_info = Repository.get_by_repo_name(repo_name)
         if not c.repo_info:
             h.not_mapped_error(repo_name)
-            return redirect(url('home'))
+            raise HTTPFound(location=url('home'))
 
         defaults = self.__load_data(repo_name)
 
@@ -164,7 +164,7 @@
             form_result = _form.to_python(dict(request.POST))
 
             # an approximation that is better than nothing
-            if not Ui.get_by_key(Ui.HOOK_UPDATE).ui_active:
+            if not Ui.get_by_key('hooks', Ui.HOOK_UPDATE).ui_active:
                 form_result['update_after_clone'] = False
 
             # create fork is done sometimes async on celery, db transaction
@@ -186,6 +186,6 @@
             h.flash(_('An error occurred during repository forking %s') %
                     repo_name, category='error')
 
-        return redirect(h.url('repo_creating_home',
+        raise HTTPFound(location=h.url('repo_creating_home',
                               repo_name=form_result['repo_name_full'],
                               task_id=task_id))
--- a/kallithea/controllers/home.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/home.py	Mon Mar 14 00:36:08 2016 +0100
@@ -57,9 +57,9 @@
         c.groups = self.scm_model.get_repo_groups()
         c.group = None
 
-        c.repos_list = Repository.query()\
-                        .filter(Repository.group_id == None)\
-                        .order_by(func.lower(Repository.repo_name))\
+        c.repos_list = Repository.query() \
+                        .filter(Repository.group_id == None) \
+                        .order_by(func.lower(Repository.repo_name)) \
                         .all()
 
         repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
@@ -130,6 +130,12 @@
                 'text': _('Branch'),
                 'children': [{'id': rev, 'text': name, 'type': 'branch'} for name, rev in _branches]
             })
+        _closed_branches = repo.closed_branches.items()
+        if _closed_branches:
+            res.append({
+                'text': _('Closed Branches'),
+                'children': [{'id': rev, 'text': name, 'type': 'closed-branch'} for name, rev in _closed_branches]
+            })
         _tags = repo.tags.items()
         if _tags:
             res.append({
--- a/kallithea/controllers/journal.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/journal.py	Mon Mar 14 00:36:08 2016 +0100
@@ -92,12 +92,12 @@
         if not repo_ids and user_ids:
             filtering_criterion = UserLog.user_id.in_(user_ids)
         if filtering_criterion is not None:
-            journal = self.sa.query(UserLog)\
-                .options(joinedload(UserLog.user))\
+            journal = self.sa.query(UserLog) \
+                .options(joinedload(UserLog.user)) \
                 .options(joinedload(UserLog.repository))
             #filter
             journal = _journal_filter(journal, c.search_term)
-            journal = journal.filter(filtering_criterion)\
+            journal = journal.filter(filtering_criterion) \
                         .order_by(UserLog.action_date.desc())
         else:
             journal = []
@@ -194,9 +194,9 @@
         # Return a rendered template
         p = safe_int(request.GET.get('page', 1), 1)
         c.user = User.get(self.authuser.user_id)
-        c.following = self.sa.query(UserFollowing)\
-            .filter(UserFollowing.user_id == self.authuser.user_id)\
-            .options(joinedload(UserFollowing.follows_repository))\
+        c.following = self.sa.query(UserFollowing) \
+            .filter(UserFollowing.user_id == self.authuser.user_id) \
+            .options(joinedload(UserFollowing.follows_repository)) \
             .all()
 
         journal = self._get_journal_data(c.following)
@@ -210,9 +210,9 @@
         if request.environ.get('HTTP_X_PARTIAL_XHR'):
             return render('journal/journal_data.html')
 
-        repos_list = Session().query(Repository)\
+        repos_list = Session().query(Repository) \
                      .filter(Repository.user_id ==
-                             self.authuser.user_id)\
+                             self.authuser.user_id) \
                      .order_by(func.lower(Repository.repo_name)).all()
 
         repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
@@ -280,9 +280,9 @@
         """
         Produce an atom-1.0 feed via feedgenerator module
         """
-        following = self.sa.query(UserFollowing)\
-            .filter(UserFollowing.user_id == self.authuser.user_id)\
-            .options(joinedload(UserFollowing.follows_repository))\
+        following = self.sa.query(UserFollowing) \
+            .filter(UserFollowing.user_id == self.authuser.user_id) \
+            .options(joinedload(UserFollowing.follows_repository)) \
             .all()
         return self._atom_feed(following, public=False)
 
@@ -292,9 +292,9 @@
         """
         Produce an rss feed via feedgenerator module
         """
-        following = self.sa.query(UserFollowing)\
-            .filter(UserFollowing.user_id == self.authuser.user_id)\
-            .options(joinedload(UserFollowing.follows_repository))\
+        following = self.sa.query(UserFollowing) \
+            .filter(UserFollowing.user_id == self.authuser.user_id) \
+            .options(joinedload(UserFollowing.follows_repository)) \
             .all()
         return self._rss_feed(following, public=False)
 
@@ -330,9 +330,9 @@
         # Return a rendered template
         p = safe_int(request.GET.get('page', 1), 1)
 
-        c.following = self.sa.query(UserFollowing)\
-            .filter(UserFollowing.user_id == self.authuser.user_id)\
-            .options(joinedload(UserFollowing.follows_repository))\
+        c.following = self.sa.query(UserFollowing) \
+            .filter(UserFollowing.user_id == self.authuser.user_id) \
+            .options(joinedload(UserFollowing.follows_repository)) \
             .all()
 
         journal = self._get_journal_data(c.following)
@@ -351,9 +351,9 @@
         """
         Produce an atom-1.0 feed via feedgenerator module
         """
-        c.following = self.sa.query(UserFollowing)\
-            .filter(UserFollowing.user_id == self.authuser.user_id)\
-            .options(joinedload(UserFollowing.follows_repository))\
+        c.following = self.sa.query(UserFollowing) \
+            .filter(UserFollowing.user_id == self.authuser.user_id) \
+            .options(joinedload(UserFollowing.follows_repository)) \
             .all()
 
         return self._atom_feed(c.following)
@@ -363,9 +363,9 @@
         """
         Produce an rss2 feed via feedgenerator module
         """
-        c.following = self.sa.query(UserFollowing)\
-            .filter(UserFollowing.user_id == self.authuser.user_id)\
-            .options(joinedload(UserFollowing.follows_repository))\
+        c.following = self.sa.query(UserFollowing) \
+            .filter(UserFollowing.user_id == self.authuser.user_id) \
+            .options(joinedload(UserFollowing.follows_repository)) \
             .all()
 
         return self._rss_feed(c.following)
--- a/kallithea/controllers/login.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/login.py	Mon Mar 14 00:36:08 2016 +0100
@@ -31,10 +31,9 @@
 import formencode
 
 from formencode import htmlfill
+from pylons.i18n.translation import _
+from pylons import request, session, tmpl_context as c, url
 from webob.exc import HTTPFound, HTTPBadRequest
-from pylons.i18n.translation import _
-from pylons.controllers.util import redirect
-from pylons import request, session, tmpl_context as c, url
 
 import kallithea.lib.helpers as h
 from kallithea.lib.auth import AuthUser, HasPermissionAnyDecorator
@@ -79,11 +78,10 @@
         else:
             c.came_from = url('home')
 
-        not_default = self.authuser.username != User.DEFAULT_USER
         ip_allowed = AuthUser.check_ip_allowed(self.authuser, self.ip_addr)
 
         # redirect if already logged in
-        if self.authuser.is_authenticated and not_default and ip_allowed:
+        if self.authuser.is_authenticated and ip_allowed:
             raise HTTPFound(location=c.came_from)
 
         if request.POST:
@@ -93,7 +91,7 @@
                 c.form_result = login_form.to_python(dict(request.POST))
                 # form checks for username/password, now we're authenticated
                 username = c.form_result['username']
-                user = User.get_by_username(username, case_insensitive=True)
+                user = User.get_by_username_or_email(username, case_insensitive=True)
             except formencode.Invalid as errors:
                 defaults = errors.value
                 # remove password from filling in form again
@@ -121,7 +119,7 @@
     @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
                                'hg.register.manual_activate')
     def register(self):
-        c.auto_active = 'hg.register.auto_activate' in User.get_default_user()\
+        c.auto_active = 'hg.register.auto_activate' in User.get_default_user() \
             .AuthUser.permissions['global']
 
         settings = Setting.get_app_settings()
@@ -152,7 +150,7 @@
                 h.flash(_('You have successfully registered into Kallithea'),
                         category='success')
                 Session().commit()
-                return redirect(url('login_home'))
+                raise HTTPFound(location=url('login_home'))
 
             except formencode.Invalid as errors:
                 return htmlfill.render(
@@ -196,7 +194,7 @@
                 redirect_link = UserModel().send_reset_password_email(form_result)
                 h.flash(_('A password reset confirmation code has been sent'),
                             category='success')
-                return redirect(redirect_link)
+                raise HTTPFound(location=redirect_link)
 
             except formencode.Invalid as errors:
                 return htmlfill.render(
@@ -249,12 +247,12 @@
 
         UserModel().reset_password(form_result['email'], form_result['password'])
         h.flash(_('Successfully updated password'), category='success')
-        return redirect(url('login_home'))
+        raise HTTPFound(location=url('login_home'))
 
     def logout(self):
         session.delete()
         log.info('Logging out and deleting session for user')
-        redirect(url('home'))
+        raise HTTPFound(location=url('home'))
 
     def authentication_token(self):
         """Return the CSRF protection token for the session - just like it
--- a/kallithea/controllers/pullrequests.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/pullrequests.py	Mon Mar 14 00:36:08 2016 +0100
@@ -30,16 +30,14 @@
 import formencode
 import re
 
-from webob.exc import HTTPNotFound, HTTPForbidden, HTTPBadRequest
-
 from pylons import request, tmpl_context as c, url
-from pylons.controllers.util import redirect
 from pylons.i18n.translation import _
+from webob.exc import HTTPFound, HTTPNotFound, HTTPForbidden, HTTPBadRequest
 
 from kallithea.lib.vcs.utils.hgcompat import unionrepo
 from kallithea.lib.compat import json
 from kallithea.lib.base import BaseRepoController, render
-from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
+from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
     NotAnonymous
 from kallithea.lib.helpers import Page
 from kallithea.lib import helpers as h
@@ -49,7 +47,7 @@
 from kallithea.lib.vcs.utils import safe_str
 from kallithea.lib.vcs.exceptions import EmptyRepositoryError
 from kallithea.lib.diffs import LimitedDiffContainer
-from kallithea.model.db import PullRequest, ChangesetStatus, ChangesetComment,\
+from kallithea.model.db import PullRequest, ChangesetStatus, ChangesetComment, \
     PullRequestReviewers, User
 from kallithea.model.pull_request import PullRequestModel
 from kallithea.model.meta import Session
@@ -58,7 +56,8 @@
 from kallithea.model.changeset_status import ChangesetStatusModel
 from kallithea.model.forms import PullRequestForm, PullRequestPostForm
 from kallithea.lib.utils2 import safe_int
-from kallithea.controllers.changeset import _ignorews_url, _context_url
+from kallithea.controllers.changeset import _ignorews_url, _context_url, \
+    create_comment
 from kallithea.controllers.compare import CompareController
 from kallithea.lib.graphmod import graph_data
 
@@ -212,15 +211,15 @@
                 s = filter(lambda p: p.status != PullRequest.STATUS_CLOSED, s)
             return s
 
-        c.my_pull_requests = _filter(PullRequest.query()\
+        c.my_pull_requests = _filter(PullRequest.query() \
                                 .filter(PullRequest.user_id ==
-                                        self.authuser.user_id)\
+                                        self.authuser.user_id) \
                                 .all())
 
-        c.participate_in_pull_requests = _filter(PullRequest.query()\
-                                .join(PullRequestReviewers)\
+        c.participate_in_pull_requests = _filter(PullRequest.query() \
+                                .join(PullRequestReviewers) \
                                 .filter(PullRequestReviewers.user_id ==
-                                        self.authuser.user_id)\
+                                        self.authuser.user_id) \
                                                  )
 
         return render('/pullrequests/pullrequest_show_my.html')
@@ -237,7 +236,7 @@
         except EmptyRepositoryError as e:
             h.flash(h.literal(_('There are no changesets yet')),
                     category='warning')
-            redirect(url('summary_home', repo_name=org_repo.repo_name))
+            raise HTTPFound(location=url('summary_home', repo_name=org_repo.repo_name))
 
         org_rev = request.GET.get('rev_end')
         # rev_start is not directly useful - its parent could however be used
@@ -369,9 +368,9 @@
             h.flash(_('Error occurred while creating pull request'),
                     category='error')
             log.error(traceback.format_exc())
-            return redirect(url('pullrequest_home', repo_name=repo_name))
+            raise HTTPFound(location=url('pullrequest_home', repo_name=repo_name))
 
-        return redirect(pull_request.url())
+        raise HTTPFound(location=pull_request.url())
 
     def create_update(self, old_pull_request, updaterev, title, description, reviewers_ids):
         org_repo = RepoModel()._get_repo(old_pull_request.org_repo.repo_name)
@@ -456,7 +455,7 @@
             h.flash(_('Error occurred while creating pull request'),
                     category='error')
             log.error(traceback.format_exc())
-            return redirect(old_pull_request.url())
+            raise HTTPFound(location=old_pull_request.url())
 
         ChangesetCommentsModel().create(
             text=_('Closed, replaced by %s .') % pull_request.url(canonical=True),
@@ -470,7 +469,7 @@
         h.flash(_('Pull request update created'),
                 category='success')
 
-        return redirect(pull_request.url())
+        raise HTTPFound(location=pull_request.url())
 
     # pullrequest_post for PR editing
     @LoginRequired()
@@ -513,7 +512,7 @@
         Session().commit()
         h.flash(_('Pull request updated'), category='success')
 
-        return redirect(pull_request.url())
+        raise HTTPFound(location=pull_request.url())
 
     @LoginRequired()
     @NotAnonymous()
@@ -528,7 +527,7 @@
             Session().commit()
             h.flash(_('Successfully deleted pull request'),
                     category='success')
-            return redirect(url('my_pullrequests'))
+            raise HTTPFound(location=url('my_pullrequests'))
         raise HTTPForbidden()
 
     @LoginRequired()
@@ -610,9 +609,9 @@
                                                            other_scm_instance.path)
                     else:
                         hgrepo = org_scm_instance._repo
-                    show = set(hgrepo.revs('::%ld & !::%s & !::%s',
+                    show = set(hgrepo.revs('::%ld & !::parents(%s) & !::%s',
                                            avail_revs, revs[0], targethead))
-                    c.update_msg = _('This pull request can be updated with changes on %s:') % c.cs_branch_name
+                    c.update_msg = _('The following changes are available on %s:') % c.cs_branch_name
                 else:
                     show = set()
                     avail_revs = set() # drop revs[0]
@@ -711,10 +710,11 @@
 
         status = request.POST.get('changeset_status')
         close_pr = request.POST.get('save_close')
+        delete = request.POST.get('save_delete')
         f_path = request.POST.get('f_path')
         line_no = request.POST.get('line')
 
-        if (status or close_pr) and (f_path or line_no):
+        if (status or close_pr or delete) and (f_path or line_no):
             # status votes and closing is only possible in general comments
             raise HTTPBadRequest()
 
@@ -724,20 +724,33 @@
                 h.flash(_('No permission to change pull request status'), 'error')
                 raise HTTPForbidden()
 
+        if delete == "delete":
+            if (pull_request.owner.user_id == c.authuser.user_id or
+                h.HasPermissionAny('hg.admin')() or
+                h.HasRepoPermissionAny('repository.admin')(pull_request.org_repo.repo_name) or
+                h.HasRepoPermissionAny('repository.admin')(pull_request.other_repo.repo_name)
+                ) and not pull_request.is_closed():
+                PullRequestModel().delete(pull_request)
+                Session().commit()
+                h.flash(_('Successfully deleted pull request %s') % pull_request_id,
+                        category='success')
+                return {
+                   'location': url('my_pullrequests'), # or repo pr list?
+                }
+                raise HTTPFound(location=url('my_pullrequests')) # or repo pr list?
+            raise HTTPForbidden()
+
         text = request.POST.get('text', '').strip()
         if close_pr:
             text = _('Closing.') + '\n' + text
 
-        comment = ChangesetCommentsModel().create(
-            text=text,
-            repo=c.db_repo.repo_id,
-            user=c.authuser.user_id,
-            pull_request=pull_request_id,
+        comment = create_comment(
+            text,
+            status,
+            pull_request_id=pull_request_id,
             f_path=f_path,
             line_no=line_no,
-            status_change=(ChangesetStatus.get_status_lbl(status)
-                           if status and allowed_to_change_status else None),
-            closing_pr=close_pr
+            closing_pr=close_pr,
         )
 
         action_logger(self.authuser,
@@ -762,7 +775,7 @@
         Session().commit()
 
         if not request.environ.get('HTTP_X_PARTIAL_XHR'):
-            return redirect(pull_request.url())
+            raise HTTPFound(location=pull_request.url())
 
         data = {
            'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
--- a/kallithea/controllers/search.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/search.py	Mon Mar 14 00:36:08 2016 +0100
@@ -139,7 +139,7 @@
                 log.error('Empty Index data')
                 c.runtime = _('There is no index to search in. '
                               'Please run whoosh indexer')
-            except (Exception):
+            except Exception:
                 log.error(traceback.format_exc())
                 c.runtime = _('An error occurred during search operation.')
 
--- a/kallithea/controllers/summary.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/controllers/summary.py	Mon Mar 14 00:36:08 2016 +0100
@@ -44,7 +44,7 @@
 from kallithea.model.db import Statistics, CacheInvalidation, User
 from kallithea.lib.utils import jsonify
 from kallithea.lib.utils2 import safe_str
-from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
+from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
     NotAnonymous
 from kallithea.lib.base import BaseRepoController, render
 from kallithea.lib.vcs.backends.base import EmptyChangeset
@@ -114,8 +114,9 @@
     def index(self, repo_name):
         _load_changelog_summary()
 
-        username = ''
-        if self.authuser.username != User.DEFAULT_USER:
+        if self.authuser.is_default_user:
+            username = ''
+        else:
             username = safe_str(self.authuser.username)
 
         _def_clone_uri = _def_clone_uri_by_id = c.clone_uri_tmpl
@@ -134,8 +135,8 @@
         else:
             c.show_stats = False
 
-        stats = self.sa.query(Statistics)\
-            .filter(Statistics.repository == c.db_repo)\
+        stats = self.sa.query(Statistics) \
+            .filter(Statistics.repository == c.db_repo) \
             .scalar()
 
         c.stats_percentage = 0
@@ -192,8 +193,8 @@
         c.ts_min = ts_min_m
         c.ts_max = ts_max_y
 
-        stats = self.sa.query(Statistics)\
-            .filter(Statistics.repository == c.db_repo)\
+        stats = self.sa.query(Statistics) \
+            .filter(Statistics.repository == c.db_repo) \
             .scalar()
         c.stats_percentage = 0
         if stats and stats.languages:
@@ -210,7 +211,7 @@
                 sorted(lang_stats, reverse=True, key=lambda k: k[1])[:10]
             )
             last_rev = stats.stat_on_revision + 1
-            c.repo_last_rev = c.db_repo_scm_instance.count()\
+            c.repo_last_rev = c.db_repo_scm_instance.count() \
                 if c.db_repo_scm_instance.revisions else 0
             if last_rev == 0 or c.repo_last_rev == 0:
                 pass
--- a/kallithea/i18n/be/LC_MESSAGES/kallithea.po	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/i18n/be/LC_MESSAGES/kallithea.po	Mon Mar 14 00:36:08 2016 +0100
@@ -1,14 +1,14 @@
 # Belarusian translations for Kallithea.
-# Copyright (C) 2015 Various authors, licensing as GPLv3
+# Copyright (C) 2016 Various authors, licensing as GPLv3
 # This file is distributed under the same license as the Kallithea project.
-# Automatically generated, 2015.
+# Automatically generated, 2016.
 # #, fuzzy
 msgid ""
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2015-09-08 10:34+0200\n"
-"PO-Revision-Date: 2015-08-08 12:03+0300\n"
+"POT-Creation-Date: 2016-02-22 19:35+0100\n"
+"PO-Revision-Date: 2016-02-24 16:36+0100\n"
 "Last-Translator: Andrew Shadura <andrew@shadura.me>\n"
 "Language-Team: Belarusian "
 "<https://hosted.weblate.org/projects/kallithea/kallithea/be/>\n"
@@ -20,12 +20,12 @@
 "4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
 "X-Generator: Weblate 2.4-dev\n"
 
-#: kallithea/controllers/changelog.py:86
-#: kallithea/controllers/pullrequests.py:238 kallithea/lib/base.py:512
+#: kallithea/controllers/changelog.py:85
+#: kallithea/controllers/pullrequests.py:237 kallithea/lib/base.py:515
 msgid "There are no changesets yet"
 msgstr "Яшчэ не было змен"
 
-#: kallithea/controllers/changelog.py:166
+#: kallithea/controllers/changelog.py:164
 #: kallithea/controllers/admin/permissions.py:61
 #: kallithea/controllers/admin/permissions.py:65
 #: kallithea/controllers/admin/permissions.py:69
@@ -37,35 +37,29 @@
 msgid "None"
 msgstr "Нічога"
 
-#: kallithea/controllers/changelog.py:169 kallithea/controllers/files.py:196
+#: kallithea/controllers/changelog.py:167 kallithea/controllers/files.py:197
 msgid "(closed)"
 msgstr "(зачынена)"
 
-#: kallithea/controllers/changeset.py:89
+#: kallithea/controllers/changeset.py:88
 msgid "Show whitespace"
-msgstr "Адлюстроўваць прабелы"
-
-#: kallithea/controllers/changeset.py:96 kallithea/controllers/changeset.py:103
+msgstr "Паказваць прабелы"
+
+#: kallithea/controllers/changeset.py:95 kallithea/controllers/changeset.py:102
 #: kallithea/templates/files/diff_2way.html:55
 msgid "Ignore whitespace"
 msgstr "Ігнараваць прабелы"
 
-#: kallithea/controllers/changeset.py:169
+#: kallithea/controllers/changeset.py:168
 #, python-format
 msgid "Increase diff context to %(num)s lines"
 msgstr "Павялічыць кантэкст да %(num)s радкоў"
 
-#: kallithea/controllers/changeset.py:212 kallithea/controllers/files.py:96
-#: kallithea/controllers/files.py:116 kallithea/controllers/files.py:742
+#: kallithea/controllers/changeset.py:233 kallithea/controllers/files.py:96
+#: kallithea/controllers/files.py:116 kallithea/controllers/files.py:743
 msgid "Such revision does not exist for this repository"
 msgstr "Няма такой рэвізіі ў гэтым рэпазітары"
 
-#: kallithea/controllers/changeset.py:383
-msgid ""
-"Changing status on a changeset associated with a closed pull request is "
-"not allowed"
-msgstr "Нельга рэдагаваць статус змен, злучаных з зачыненымі pull-request'ами"
-
 #: kallithea/controllers/compare.py:161 kallithea/templates/base/root.html:41
 msgid "Select changeset"
 msgstr "Выбраць набор змен"
@@ -119,12 +113,12 @@
 #: kallithea/controllers/feed.py:87
 #: kallithea/templates/changeset/changeset.html:182
 #: kallithea/templates/changeset/changeset.html:195
-#: kallithea/templates/compare/compare_diff.html:78
-#: kallithea/templates/compare/compare_diff.html:89
-#: kallithea/templates/pullrequests/pullrequest_show.html:339
-#: kallithea/templates/pullrequests/pullrequest_show.html:363
+#: kallithea/templates/compare/compare_diff.html:84
+#: kallithea/templates/compare/compare_diff.html:93
+#: kallithea/templates/pullrequests/pullrequest_show.html:350
+#: kallithea/templates/pullrequests/pullrequest_show.html:372
 msgid "Changeset was too big and was cut off..."
-msgstr "Змены апынуліся занадта вялікімі і былі выразаныя..."
+msgstr "Змены апынуліся занадта вялікімі і былі скарочаныя..."
 
 #: kallithea/controllers/feed.py:91
 #, python-format
@@ -140,103 +134,105 @@
 msgid "There are no files yet. %s"
 msgstr "Няма файлаў. %s"
 
-#: kallithea/controllers/files.py:193
+#: kallithea/controllers/files.py:194
 #, python-format
 msgid "%s at %s"
 msgstr "%s (%s)"
 
-#: kallithea/controllers/files.py:305 kallithea/controllers/files.py:365
-#: kallithea/controllers/files.py:432
+#: kallithea/controllers/files.py:306 kallithea/controllers/files.py:366
+#: kallithea/controllers/files.py:433
 #, python-format
 msgid "This repository has been locked by %s on %s"
 msgstr "Рэпазітар заблакаваў %s у %s"
 
-#: kallithea/controllers/files.py:317
-msgid "You can only delete files with revision being a valid branch "
-msgstr "Вы можаце выдаляць файлы толькі ў рэвізіі, злучанай з існай галінкай "
-
-#: kallithea/controllers/files.py:328
+#: kallithea/controllers/files.py:318
+#, fuzzy
+msgid "You can only delete files with revision being a valid branch"
+msgstr "Вы можаце выдаляць файлы толькі ў рэвізіі, злучанай з існай галінай "
+
+#: kallithea/controllers/files.py:329
 #, python-format
 msgid "Deleted file %s via Kallithea"
 msgstr "Файл %s выдалены з дапамогай Kallithea"
 
-#: kallithea/controllers/files.py:350
+#: kallithea/controllers/files.py:351
 #, python-format
 msgid "Successfully deleted file %s"
 msgstr "Файл %s выдалены"
 
-#: kallithea/controllers/files.py:354 kallithea/controllers/files.py:420
-#: kallithea/controllers/files.py:501
+#: kallithea/controllers/files.py:355 kallithea/controllers/files.py:421
+#: kallithea/controllers/files.py:502
 msgid "Error occurred during commit"
 msgstr "Падчас каміта адбылася памылка"
 
-#: kallithea/controllers/files.py:377
-msgid "You can only edit files with revision being a valid branch "
-msgstr "Вы можаце рэдагаваць файлы толькі ў рэвізіі, злучанай з існай галінкай "
-
-#: kallithea/controllers/files.py:391
+#: kallithea/controllers/files.py:378
+#, fuzzy
+msgid "You can only edit files with revision being a valid branch"
+msgstr "Вы можаце рэдагаваць файлы толькі ў рэвізіі, злучанай з існай галінай "
+
+#: kallithea/controllers/files.py:392
 #, python-format
 msgid "Edited file %s via Kallithea"
 msgstr "Файл %s адрэдагаваны з дапамогай Kallithea"
 
-#: kallithea/controllers/files.py:407
+#: kallithea/controllers/files.py:408
 msgid "No changes"
 msgstr "Без змен"
 
-#: kallithea/controllers/files.py:416 kallithea/controllers/files.py:490
+#: kallithea/controllers/files.py:417 kallithea/controllers/files.py:491
 #, python-format
 msgid "Successfully committed to %s"
-msgstr "Змены ўжыты ў %s"
-
-#: kallithea/controllers/files.py:443
+msgstr "Змены захаваныя ў %s"
+
+#: kallithea/controllers/files.py:444
 msgid "Added file via Kallithea"
 msgstr "Файл дададзены з дапамогай Kallithea"
 
-#: kallithea/controllers/files.py:464
+#: kallithea/controllers/files.py:465
 msgid "No content"
 msgstr "Пуста"
 
-#: kallithea/controllers/files.py:468
+#: kallithea/controllers/files.py:469
 msgid "No filename"
 msgstr "Безназоўны"
 
-#: kallithea/controllers/files.py:493
+#: kallithea/controllers/files.py:494
 msgid "Location must be relative path and must not contain .. in path"
 msgstr ""
 "Размяшчэнне павінна быць адносным шляхам, і не можа ўтрымліваць \"..\" у "
 "шляхі"
 
-#: kallithea/controllers/files.py:526
+#: kallithea/controllers/files.py:527
 msgid "Downloads disabled"
-msgstr "Магчымасць спампоўваць адключана"
-
-#: kallithea/controllers/files.py:537
+msgstr "Магчымасць спампоўваць адключаная"
+
+#: kallithea/controllers/files.py:538
 #, python-format
 msgid "Unknown revision %s"
 msgstr "Невядомая рэвізія %s"
 
-#: kallithea/controllers/files.py:539
+#: kallithea/controllers/files.py:540
 msgid "Empty repository"
 msgstr "Пусты рэпазітар"
 
-#: kallithea/controllers/files.py:541
+#: kallithea/controllers/files.py:542
 msgid "Unknown archive type"
 msgstr "Невядомы тып архіва"
 
-#: kallithea/controllers/files.py:771
+#: kallithea/controllers/files.py:772
 #: kallithea/templates/changeset/changeset_range.html:9
 #: kallithea/templates/email_templates/pull_request.html:15
 #: kallithea/templates/pullrequests/pullrequest.html:97
 msgid "Changesets"
 msgstr "Набор змен"
 
-#: kallithea/controllers/files.py:772 kallithea/controllers/pullrequests.py:176
+#: kallithea/controllers/files.py:773 kallithea/controllers/pullrequests.py:175
 #: kallithea/model/scm.py:820 kallithea/templates/switch_to_list.html:3
 #: kallithea/templates/branches/branches.html:10
 msgid "Branches"
-msgstr "Галінкі"
-
-#: kallithea/controllers/files.py:773 kallithea/controllers/pullrequests.py:177
+msgstr "Галіны"
+
+#: kallithea/controllers/files.py:774 kallithea/controllers/pullrequests.py:176
 #: kallithea/model/scm.py:831 kallithea/templates/switch_to_list.html:25
 #: kallithea/templates/tags/tags.html:10
 msgid "Tags"
@@ -245,7 +241,7 @@
 #: kallithea/controllers/forks.py:186
 #, python-format
 msgid "An error occurred during repository forking %s"
-msgstr "Адбылася памылка падчас стварэння форка рэпазітара %s"
+msgstr "Памылка падчас стварэння форка рэпазітара %s"
 
 #: kallithea/controllers/home.py:84
 msgid "Groups"
@@ -259,8 +255,8 @@
 #: kallithea/templates/admin/users/user_edit_advanced.html:6
 #: kallithea/templates/base/base.html:60 kallithea/templates/base/base.html:77
 #: kallithea/templates/base/base.html:124
-#: kallithea/templates/base/base.html:390
-#: kallithea/templates/base/base.html:562
+#: kallithea/templates/base/base.html:479
+#: kallithea/templates/base/base.html:653
 msgid "Repositories"
 msgstr "Рэпазітары"
 
@@ -269,13 +265,17 @@
 #: kallithea/templates/files/files_delete.html:23
 #: kallithea/templates/files/files_edit.html:32
 msgid "Branch"
-msgstr "Галінка"
-
-#: kallithea/controllers/home.py:136
+msgstr "Галіна"
+
+#: kallithea/controllers/home.py:136 kallithea/templates/switch_to_list.html:16
+msgid "Closed Branches"
+msgstr "Зачыненыя галіны"
+
+#: kallithea/controllers/home.py:142
 msgid "Tag"
 msgstr "Тэгі"
 
-#: kallithea/controllers/home.py:142
+#: kallithea/controllers/home.py:148
 msgid "Bookmark"
 msgstr "Закладкі"
 
@@ -283,122 +283,118 @@
 #: kallithea/templates/journal/public_journal.html:4
 #: kallithea/templates/journal/public_journal.html:21
 msgid "Public Journal"
-msgstr "Публічны часопіс"
+msgstr "Публічны журнал"
 
 #: kallithea/controllers/journal.py:115 kallithea/controllers/journal.py:157
-#: kallithea/templates/base/base.html:222
+#: kallithea/templates/base/base.html:306
 #: kallithea/templates/journal/journal.html:4
 #: kallithea/templates/journal/journal.html:12
 msgid "Journal"
-msgstr "Часопіс"
-
-#: kallithea/controllers/login.py:151 kallithea/controllers/login.py:197
+msgstr "Журнал"
+
+#: kallithea/controllers/login.py:144 kallithea/controllers/login.py:190
 msgid "Bad captcha"
 msgstr "Няслушная капча"
 
-#: kallithea/controllers/login.py:157
+#: kallithea/controllers/login.py:150
 msgid "You have successfully registered into Kallithea"
 msgstr "Рэгістрацыя ў Kallithea прайшла паспяхова"
 
-#: kallithea/controllers/login.py:202
-#, fuzzy
-#| msgid "Your password reset link was sent"
+#: kallithea/controllers/login.py:195
 msgid "A password reset confirmation code has been sent"
-msgstr "Спасылка для скідання пароля адпраўлена"
-
-#: kallithea/controllers/login.py:251
-#, fuzzy
-#| msgid "Password reset link"
+msgstr "Код для скідання пароля адпраўлены"
+
+#: kallithea/controllers/login.py:244
 msgid "Invalid password reset token"
-msgstr "Спасылка скіду пароля"
-
-#: kallithea/controllers/login.py:256
+msgstr "Няслушны код скідання пароля"
+
+#: kallithea/controllers/login.py:249
 #: kallithea/controllers/admin/my_account.py:167
 msgid "Successfully updated password"
 msgstr "Пароль абноўлены"
 
-#: kallithea/controllers/pullrequests.py:124
+#: kallithea/controllers/pullrequests.py:123
 #, python-format
 msgid "%s (closed)"
 msgstr "%s (зачынена)"
 
-#: kallithea/controllers/pullrequests.py:152
+#: kallithea/controllers/pullrequests.py:151
 #: kallithea/templates/changeset/changeset.html:12
 #: kallithea/templates/email_templates/changeset_comment.html:17
 msgid "Changeset"
 msgstr "Змены"
 
-#: kallithea/controllers/pullrequests.py:173
+#: kallithea/controllers/pullrequests.py:172
 msgid "Special"
 msgstr "Адмысловы"
 
-#: kallithea/controllers/pullrequests.py:174
+#: kallithea/controllers/pullrequests.py:173
 msgid "Peer branches"
-msgstr "Галінкі ўдзельніка"
-
-#: kallithea/controllers/pullrequests.py:175 kallithea/model/scm.py:826
+msgstr "Галіны ўдзельніка"
+
+#: kallithea/controllers/pullrequests.py:174 kallithea/model/scm.py:826
 #: kallithea/templates/switch_to_list.html:38
 #: kallithea/templates/bookmarks/bookmarks.html:10
 msgid "Bookmarks"
 msgstr "Закладкі"
 
-#: kallithea/controllers/pullrequests.py:310
+#: kallithea/controllers/pullrequests.py:309
 #, python-format
 msgid "Error creating pull request: %s"
 msgstr "Памылка пры стварэнні pull-запыту: %s"
 
-#: kallithea/controllers/pullrequests.py:356
-#: kallithea/controllers/pullrequests.py:503
+#: kallithea/controllers/pullrequests.py:355
+#: kallithea/controllers/pullrequests.py:502
 msgid "No description"
 msgstr "Няма апісання"
 
-#: kallithea/controllers/pullrequests.py:363
+#: kallithea/controllers/pullrequests.py:362
 msgid "Successfully opened new pull request"
 msgstr "Pull-запыт створаны паспяхова"
 
-#: kallithea/controllers/pullrequests.py:366
-#: kallithea/controllers/pullrequests.py:453
+#: kallithea/controllers/pullrequests.py:365
+#: kallithea/controllers/pullrequests.py:452
 #: kallithea/controllers/pullrequests.py:509
 #, python-format
 msgid "Invalid reviewer \"%s\" specified"
-msgstr ""
-
-#: kallithea/controllers/pullrequests.py:369
-#: kallithea/controllers/pullrequests.py:456
+msgstr "Няслушны рэцэнзент \"%s\""
+
+#: kallithea/controllers/pullrequests.py:368
+#: kallithea/controllers/pullrequests.py:455
 msgid "Error occurred while creating pull request"
 msgstr "Адбылася памылка пры стварэнні pull-запыту"
 
-#: kallithea/controllers/pullrequests.py:401
+#: kallithea/controllers/pullrequests.py:400
 msgid "Missing changesets since the previous pull request:"
 msgstr "Адсутныя рэвізіі адносна папярэдняга pull-запыту:"
 
-#: kallithea/controllers/pullrequests.py:408
+#: kallithea/controllers/pullrequests.py:407
 #, python-format
 msgid "New changesets on %s %s since the previous pull request:"
 msgstr "Новыя рэвізіі на %s %s адносна папярэдняга pull-запыту:"
 
-#: kallithea/controllers/pullrequests.py:415
+#: kallithea/controllers/pullrequests.py:414
 msgid "Ancestor didn't change - show diff since previous version:"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:422
+#: kallithea/controllers/pullrequests.py:421
 #, python-format
 msgid ""
 "This pull request is based on another %s revision and there is no simple "
 "diff."
 msgstr "Гэты pull-запыт заснаваны на іншай рэвізіі %s, просты diff немагчымы."
 
-#: kallithea/controllers/pullrequests.py:424
+#: kallithea/controllers/pullrequests.py:423
 #, python-format
 msgid "No changes found on %s %s since previous version."
 msgstr "Няма змен на %s %s адносна папярэдняй версіі."
 
-#: kallithea/controllers/pullrequests.py:462
+#: kallithea/controllers/pullrequests.py:461
 #, python-format
 msgid "Closed, replaced by %s ."
-msgstr "Зачынены, замешчаны %s."
-
-#: kallithea/controllers/pullrequests.py:470
+msgstr "Зачынены, заменены %s."
+
+#: kallithea/controllers/pullrequests.py:469
 msgid "Pull request update created"
 msgstr "Абнаўленне для pull-запыту створана"
 
@@ -413,7 +409,7 @@
 #: kallithea/controllers/pullrequests.py:594
 #, python-format
 msgid "This pull request has already been merged to %s."
-msgstr "Гэты pull-запыт ужо прыняты на галінку %s."
+msgstr "Гэты pull-запыт ужо прыняты на галіну %s."
 
 #: kallithea/controllers/pullrequests.py:596
 msgid "This pull request has been closed and can not be updated."
@@ -421,27 +417,32 @@
 
 #: kallithea/controllers/pullrequests.py:614
 #, python-format
-msgid "This pull request can be updated with changes on %s:"
-msgstr "Гэты pull-запыт можа быць абноўлены з %s:"
-
-#: kallithea/controllers/pullrequests.py:617
+msgid "The following changes are available on %s:"
+msgstr "Гэтыя змены даступныя на %s:"
+
+#: kallithea/controllers/pullrequests.py:618
 msgid "No changesets found for updating this pull request."
 msgstr "Няма змен для абнаўлення гэтага pull-запыту."
 
-#: kallithea/controllers/pullrequests.py:625
+#: kallithea/controllers/pullrequests.py:626
 #, python-format
 msgid "Note: Branch %s has another head: %s."
-msgstr "Увага: Галінка %s мае яшчэ адну верхавіну: %s."
-
-#: kallithea/controllers/pullrequests.py:631
+msgstr "Увага: Галіна %s мае яшчэ адну верхавіну: %s."
+
+#: kallithea/controllers/pullrequests.py:632
 msgid "Git pull requests don't support updates yet."
-msgstr "Абнаўленне pull-запытаў git не падтрымліваецца."
-
-#: kallithea/controllers/pullrequests.py:722
+msgstr "Абнаўленне pull-запытаў git яшчэ не падтрымліваецца."
+
+#: kallithea/controllers/pullrequests.py:724
 msgid "No permission to change pull request status"
 msgstr "Няма правоў змяняць статус pull-запыту"
 
-#: kallithea/controllers/pullrequests.py:727
+#: kallithea/controllers/pullrequests.py:735
+#, fuzzy, python-format
+msgid "Successfully deleted pull request %s"
+msgstr "Pull-запыт паспяхова выдалены"
+
+#: kallithea/controllers/pullrequests.py:745
 msgid "Closing."
 msgstr "Зачынены."
 
@@ -455,14 +456,14 @@
 
 #: kallithea/controllers/search.py:144
 msgid "An error occurred during search operation."
-msgstr "Адбылася памылка пры выкананні гэтага пошуку."
-
-#: kallithea/controllers/summary.py:180
+msgstr "Памылка пры выкананні гэтага пошуку."
+
+#: kallithea/controllers/summary.py:181
 #: kallithea/templates/summary/summary.html:384
 msgid "No data ready yet"
 msgstr "Няма дадзеных"
 
-#: kallithea/controllers/summary.py:183
+#: kallithea/controllers/summary.py:184
 #: kallithea/templates/summary/summary.html:98
 msgid "Statistics are disabled for this repository"
 msgstr "Статыстычныя дадзеныя адключаны для гэтага рэпазітара"
@@ -473,7 +474,7 @@
 
 #: kallithea/controllers/admin/auth_settings.py:146
 msgid "error occurred during update of auth settings"
-msgstr "адбылася памылка пры абнаўленні налад аўтарызацыі"
+msgstr "памылка пры абнаўленні налад аўтарызацыі"
 
 #: kallithea/controllers/admin/defaults.py:97
 msgid "Default settings updated successfully"
@@ -481,119 +482,119 @@
 
 #: kallithea/controllers/admin/defaults.py:112
 msgid "Error occurred during update of defaults"
-msgstr "Адбылася памылка пры абнаўленні стандартных налад"
+msgstr "Памылка пры абнаўленні стандартных налад"
+
+#: kallithea/controllers/admin/gists.py:58
+#: kallithea/controllers/admin/my_account.py:243
+#: kallithea/controllers/admin/users.py:284
+msgid "Forever"
+msgstr "Назаўжды"
 
 #: kallithea/controllers/admin/gists.py:59
-#: kallithea/controllers/admin/my_account.py:243
+#: kallithea/controllers/admin/my_account.py:244
 #: kallithea/controllers/admin/users.py:285
-msgid "Forever"
-msgstr "Назаўжды"
+msgid "5 minutes"
+msgstr "5 хвілін"
 
 #: kallithea/controllers/admin/gists.py:60
-#: kallithea/controllers/admin/my_account.py:244
+#: kallithea/controllers/admin/my_account.py:245
 #: kallithea/controllers/admin/users.py:286
-msgid "5 minutes"
-msgstr "5 хвілін"
+msgid "1 hour"
+msgstr "1 гадзіна"
 
 #: kallithea/controllers/admin/gists.py:61
-#: kallithea/controllers/admin/my_account.py:245
+#: kallithea/controllers/admin/my_account.py:246
 #: kallithea/controllers/admin/users.py:287
-msgid "1 hour"
-msgstr "1 гадзіна"
+msgid "1 day"
+msgstr "1 дзень"
 
 #: kallithea/controllers/admin/gists.py:62
-#: kallithea/controllers/admin/my_account.py:246
+#: kallithea/controllers/admin/my_account.py:247
 #: kallithea/controllers/admin/users.py:288
-msgid "1 day"
-msgstr "1 дзень"
-
-#: kallithea/controllers/admin/gists.py:63
-#: kallithea/controllers/admin/my_account.py:247
-#: kallithea/controllers/admin/users.py:289
 msgid "1 month"
 msgstr "1 месяц"
 
-#: kallithea/controllers/admin/gists.py:67
+#: kallithea/controllers/admin/gists.py:66
 #: kallithea/controllers/admin/my_account.py:249
-#: kallithea/controllers/admin/users.py:291
+#: kallithea/controllers/admin/users.py:290
 msgid "Lifetime"
 msgstr "Тэрмін"
 
-#: kallithea/controllers/admin/gists.py:146
+#: kallithea/controllers/admin/gists.py:145
 msgid "Error occurred during gist creation"
 msgstr "Адбылася памылка падчас стварэння gist-запіса"
 
-#: kallithea/controllers/admin/gists.py:184
+#: kallithea/controllers/admin/gists.py:183
 #, python-format
 msgid "Deleted gist %s"
 msgstr "Gist-запіс %s выдалены"
 
-#: kallithea/controllers/admin/gists.py:233
+#: kallithea/controllers/admin/gists.py:232
 msgid "Unmodified"
 msgstr "Без змен"
 
-#: kallithea/controllers/admin/gists.py:262
+#: kallithea/controllers/admin/gists.py:261
 msgid "Successfully updated gist content"
-msgstr ""
-
-#: kallithea/controllers/admin/gists.py:267
+msgstr "Gist-запіс абноўлены"
+
+#: kallithea/controllers/admin/gists.py:266
 msgid "Successfully updated gist data"
-msgstr "Дадзеныя gist-запісы абноўлены"
-
-#: kallithea/controllers/admin/gists.py:270
+msgstr "Gist-запіс абноўлены"
+
+#: kallithea/controllers/admin/gists.py:269
 #, python-format
 msgid "Error occurred during update of gist %s"
-msgstr "Адбылася памылка пры абнаўленні gist-запісы %s"
+msgstr "Памылка пры абнаўленні gist-запісу %s"
 
 #: kallithea/controllers/admin/my_account.py:70 kallithea/model/user.py:215
 #: kallithea/model/user.py:237
 msgid "You can't edit this user since it's crucial for entire application"
 msgstr ""
-"Вы не можаце змяніць дадзеныя гэтага карыстача, паколькі ён важны для "
-"працы ўсяго прыкладання"
+"Вы не можаце змяніць дадзеныя гэтага карыстальніка, паколькі ён важны для "
+"працы ўсёй праграмы"
 
 #: kallithea/controllers/admin/my_account.py:129
 msgid "Your account was updated successfully"
 msgstr "Ваш уліковы запіс паспяхова абноўлены"
 
 #: kallithea/controllers/admin/my_account.py:144
-#: kallithea/controllers/admin/users.py:202
+#: kallithea/controllers/admin/users.py:201
 #, python-format
 msgid "Error occurred during update of user %s"
-msgstr "Адбылася памылка пры абнаўленні карыстача %s"
+msgstr "Памылка пры абнаўленні карыстальніка %s"
 
 #: kallithea/controllers/admin/my_account.py:178
 msgid "Error occurred during update of user password"
 msgstr "Памылка пры абнаўленні пароля"
 
 #: kallithea/controllers/admin/my_account.py:220
-#: kallithea/controllers/admin/users.py:415
+#: kallithea/controllers/admin/users.py:414
 #, python-format
 msgid "Added email %s to user"
-msgstr "Карыстачу дададзены e-mail %s"
+msgstr "Карыстальніку дададзены e-mail %s"
 
 #: kallithea/controllers/admin/my_account.py:226
-#: kallithea/controllers/admin/users.py:421
+#: kallithea/controllers/admin/users.py:420
 msgid "An error occurred during email saving"
-msgstr "Адбылася памылка пры захаванні e-mail"
+msgstr "Памылка пры захаванні e-mail"
 
 #: kallithea/controllers/admin/my_account.py:235
-#: kallithea/controllers/admin/users.py:433
+#: kallithea/controllers/admin/users.py:432
 msgid "Removed email from user"
-msgstr "E-mail карыстача выдалены"
+msgstr "E-mail карыстальніка выдалены"
 
 #: kallithea/controllers/admin/my_account.py:259
-#: kallithea/controllers/admin/users.py:308
+#: kallithea/controllers/admin/users.py:307
 msgid "API key successfully created"
 msgstr "API-ключ паспяхова створаны"
 
 #: kallithea/controllers/admin/my_account.py:271
-#: kallithea/controllers/admin/users.py:321
+#: kallithea/controllers/admin/users.py:320
 msgid "API key successfully reset"
 msgstr "API-ключ паспяхова скінуты"
 
 #: kallithea/controllers/admin/my_account.py:275
-#: kallithea/controllers/admin/users.py:325
+#: kallithea/controllers/admin/users.py:324
 msgid "API key successfully deleted"
 msgstr "API-ключ паспяхова выдалены"
 
@@ -643,10 +644,10 @@
 #: kallithea/templates/admin/users/user_edit_profile.html:105
 #: kallithea/templates/admin/users/users.html:10
 #: kallithea/templates/admin/users/users.html:55
-#: kallithea/templates/base/base.html:252
-#: kallithea/templates/base/base.html:253
-#: kallithea/templates/base/base.html:259
-#: kallithea/templates/base/base.html:260
+#: kallithea/templates/base/base.html:336
+#: kallithea/templates/base/base.html:337
+#: kallithea/templates/base/base.html:343
+#: kallithea/templates/base/base.html:344
 #: kallithea/templates/base/perms_summary.html:17
 msgid "Admin"
 msgstr "Адміністратар"
@@ -677,7 +678,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1564
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1603
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1655
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1682 kallithea/model/db.py:1701
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1682 kallithea/model/db.py:1707
 msgid "Manual activation of external account"
 msgstr "Ручная актывацыя вонкавага ўліковага запісу"
 
@@ -689,7 +690,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1565
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1604
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1656
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1683 kallithea/model/db.py:1702
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1683 kallithea/model/db.py:1708
 msgid "Automatic activation of external account"
 msgstr "Аўтаматычная актывацыя вонкавага ўліковага запісу"
 
@@ -704,360 +705,364 @@
 
 #: kallithea/controllers/admin/permissions.py:124
 msgid "Global permissions updated successfully"
-msgstr "Глабальныя прывілеі паспяхова абноўлены"
+msgstr "Глабальныя прывілеі паспяхова абноўленыя"
 
 #: kallithea/controllers/admin/permissions.py:139
 msgid "Error occurred during update of permissions"
 msgstr "Адбылася памылка падчас абнаўлення прывілеяў"
 
-#: kallithea/controllers/admin/repo_groups.py:188
+#: kallithea/controllers/admin/repo_groups.py:187
 #, python-format
 msgid "Error occurred during creation of repository group %s"
 msgstr "Адбылася памылка пры стварэнні групы рэпазітароў %s"
 
-#: kallithea/controllers/admin/repo_groups.py:193
+#: kallithea/controllers/admin/repo_groups.py:192
 #, python-format
 msgid "Created repository group %s"
-msgstr "Створана новая група рэпазітароў %s"
-
-#: kallithea/controllers/admin/repo_groups.py:250
+msgstr "Створаная новая група рэпазітароў %s"
+
+#: kallithea/controllers/admin/repo_groups.py:249
 #, python-format
 msgid "Updated repository group %s"
-msgstr "Група рэпазітароў %s абноўлена"
-
-#: kallithea/controllers/admin/repo_groups.py:266
+msgstr "Група рэпазітароў %s абноўленая"
+
+#: kallithea/controllers/admin/repo_groups.py:265
 #, python-format
 msgid "Error occurred during update of repository group %s"
 msgstr "Адбылася памылка пры абнаўленні групы рэпазітароў %s"
 
-#: kallithea/controllers/admin/repo_groups.py:284
+#: kallithea/controllers/admin/repo_groups.py:283
 #, python-format
 msgid "This group contains %s repositories and cannot be deleted"
-msgstr "Дадзеная група ўтрымоўвае %s рэпазітароў і не можа быць выдалена"
-
-#: kallithea/controllers/admin/repo_groups.py:291
+msgstr "Група ўтрымлівае %s рэпазітароў і не можа быць выдаленая"
+
+#: kallithea/controllers/admin/repo_groups.py:290
 #, python-format
 msgid "This group contains %s subgroups and cannot be deleted"
-msgstr "Група ўтрымоўвае ў сабе %s падгруп і не можа быць выдалены"
-
-#: kallithea/controllers/admin/repo_groups.py:297
+msgstr "Група ўтрымлівае ў сабе %s падгруп і не можа быць выдаленая"
+
+#: kallithea/controllers/admin/repo_groups.py:296
 #, python-format
 msgid "Removed repository group %s"
-msgstr "Група рэпазітароў %s выдалена"
-
-#: kallithea/controllers/admin/repo_groups.py:302
+msgstr "Група рэпазітароў %s выдаленая"
+
+#: kallithea/controllers/admin/repo_groups.py:301
 #, python-format
 msgid "Error occurred during deletion of repository group %s"
-msgstr "Адбылася памылка пры выдаленні групы рэпазітароў %s"
-
-#: kallithea/controllers/admin/repo_groups.py:405
-#: kallithea/controllers/admin/repo_groups.py:440
+msgstr "Памылка пры выдаленні групы рэпазітароў %s"
+
+#: kallithea/controllers/admin/repo_groups.py:404
+#: kallithea/controllers/admin/repo_groups.py:439
 #: kallithea/controllers/admin/user_groups.py:340
 msgid "Cannot revoke permission for yourself as admin"
 msgstr "Адміністратар не можа адклікаць свае прывелеі"
 
-#: kallithea/controllers/admin/repo_groups.py:420
+#: kallithea/controllers/admin/repo_groups.py:419
 msgid "Repository group permissions updated"
-msgstr "Прывілеі групы рэпазітароў абноўлены"
-
-#: kallithea/controllers/admin/repo_groups.py:457
-#: kallithea/controllers/admin/repos.py:398
+msgstr "Прывілеі групы рэпазітароў абноўленыя"
+
+#: kallithea/controllers/admin/repo_groups.py:456
+#: kallithea/controllers/admin/repos.py:397
 #: kallithea/controllers/admin/user_groups.py:352
 msgid "An error occurred during revoking of permission"
-msgstr "Адбылася памылка пры водгуку прывелеі"
-
-#: kallithea/controllers/admin/repos.py:152
+msgstr "Памылка пры водгуку прывелея"
+
+#: kallithea/controllers/admin/repos.py:151
 #, python-format
 msgid "Error creating repository %s"
-msgstr "Адбылася памылка пры стварэнні рэпазітара %s"
-
-#: kallithea/controllers/admin/repos.py:213
+msgstr "Памылка пры стварэнні рэпазітара %s"
+
+#: kallithea/controllers/admin/repos.py:212
 #, python-format
 msgid "Created repository %s from %s"
 msgstr "Рэпазітар %s створаны з %s"
 
-#: kallithea/controllers/admin/repos.py:222
+#: kallithea/controllers/admin/repos.py:221
 #, python-format
 msgid "Forked repository %s as %s"
-msgstr "Зроблены форк(копія) рэпазітара %s на %s"
-
-#: kallithea/controllers/admin/repos.py:225
+msgstr "Зроблены форк рэпазітара %s на %s"
+
+#: kallithea/controllers/admin/repos.py:224
 #, python-format
 msgid "Created repository %s"
 msgstr "Рэпазітар %s створаны"
 
-#: kallithea/controllers/admin/repos.py:262
+#: kallithea/controllers/admin/repos.py:261
 #, python-format
 msgid "Repository %s updated successfully"
 msgstr "Рэпазітар %s паспяхова абноўлены"
 
-#: kallithea/controllers/admin/repos.py:283
+#: kallithea/controllers/admin/repos.py:282
 #, python-format
 msgid "Error occurred during update of repository %s"
-msgstr "Адбылася памылка падчас абнаўлення рэпазітара %s"
-
-#: kallithea/controllers/admin/repos.py:310
+msgstr "Памылка падчас абнаўлення рэпазітара %s"
+
+#: kallithea/controllers/admin/repos.py:309
 #, python-format
 msgid "Detached %s forks"
-msgstr "Форки %s адлучаны"
-
-#: kallithea/controllers/admin/repos.py:313
+msgstr "Форкі %s адлучаныя"
+
+#: kallithea/controllers/admin/repos.py:312
 #, python-format
 msgid "Deleted %s forks"
-msgstr "Выдалены форки рэпазітара %s"
-
-#: kallithea/controllers/admin/repos.py:318
+msgstr "Выдаленыя форки рэпазітара %s"
+
+#: kallithea/controllers/admin/repos.py:317
 #, python-format
 msgid "Deleted repository %s"
 msgstr "Рэпазітар %s выдалены"
 
-#: kallithea/controllers/admin/repos.py:321
+#: kallithea/controllers/admin/repos.py:320
 #, python-format
 msgid "Cannot delete repository %s which still has forks"
 msgstr "Немагчыма выдаліць %s, ён усё яшчэ мае форкі"
 
-#: kallithea/controllers/admin/repos.py:326
+#: kallithea/controllers/admin/repos.py:325
 #, python-format
 msgid "An error occurred during deletion of %s"
-msgstr "Адбылася памылка падчас выдалення %s"
-
-#: kallithea/controllers/admin/repos.py:374
+msgstr "Памылка падчас выдалення %s"
+
+#: kallithea/controllers/admin/repos.py:373
 msgid "Repository permissions updated"
-msgstr "Прывілеі рэпазітара абноўлены"
-
-#: kallithea/controllers/admin/repos.py:430
+msgstr "Прывілеі рэпазітара абноўленыя"
+
+#: kallithea/controllers/admin/repos.py:429
 msgid "An error occurred during creation of field"
-msgstr "Адбылася памылка пры стварэнні поля"
-
-#: kallithea/controllers/admin/repos.py:444
+msgstr "Памылка пры стварэнні поля"
+
+#: kallithea/controllers/admin/repos.py:443
 msgid "An error occurred during removal of field"
-msgstr "Адбылася памылка пры выдаленні поля"
-
-#: kallithea/controllers/admin/repos.py:460
+msgstr "Памылка пры выдаленні поля"
+
+#: kallithea/controllers/admin/repos.py:459
 msgid "-- Not a fork --"
 msgstr "-- Не форк --"
 
-#: kallithea/controllers/admin/repos.py:491
+#: kallithea/controllers/admin/repos.py:490
 msgid "Updated repository visibility in public journal"
 msgstr "Бачнасць рэпазітара ў публічным часопісе абноўлена"
 
-#: kallithea/controllers/admin/repos.py:495
+#: kallithea/controllers/admin/repos.py:494
 msgid "An error occurred during setting this repository in public journal"
-msgstr "Адбылася памылка пры ўсталёўцы рэпазітара ў агульнадаступны часопіс"
-
-#: kallithea/controllers/admin/repos.py:512
+msgstr "Памылка пры даданні рэпазітара ў агульнадаступны часопіс"
+
+#: kallithea/controllers/admin/repos.py:511
 msgid "Nothing"
 msgstr "Нічога"
 
-#: kallithea/controllers/admin/repos.py:514
+#: kallithea/controllers/admin/repos.py:513
 #, python-format
 msgid "Marked repository %s as fork of %s"
 msgstr "Рэпазітар %s адзначаны як форк %s"
 
-#: kallithea/controllers/admin/repos.py:521
+#: kallithea/controllers/admin/repos.py:520
 msgid "An error occurred during this operation"
-msgstr "Адбылася памылка пры выкананні аперацыі"
-
-#: kallithea/controllers/admin/repos.py:537
-#: kallithea/controllers/admin/repos.py:564
+msgstr "Памылка пры выкананні аперацыі"
+
+#: kallithea/controllers/admin/repos.py:536
+#: kallithea/controllers/admin/repos.py:563
 msgid "Repository has been locked"
 msgstr "Рэпазітар заблакаваны"
 
-#: kallithea/controllers/admin/repos.py:540
-#: kallithea/controllers/admin/repos.py:561
+#: kallithea/controllers/admin/repos.py:539
+#: kallithea/controllers/admin/repos.py:560
 msgid "Repository has been unlocked"
-msgstr "Рэпазітар адблакаваны"
-
-#: kallithea/controllers/admin/repos.py:543
-#: kallithea/controllers/admin/repos.py:568
+msgstr "Рэпазітар разблакаваны"
+
+#: kallithea/controllers/admin/repos.py:542
+#: kallithea/controllers/admin/repos.py:567
 msgid "An error occurred during unlocking"
-msgstr "Адбылася памылка падчас разблакавання"
-
-#: kallithea/controllers/admin/repos.py:582
+msgstr "Памылка падчас разблакавання"
+
+#: kallithea/controllers/admin/repos.py:581
 msgid "Cache invalidation successful"
 msgstr "Кэш скінуты"
 
-#: kallithea/controllers/admin/repos.py:586
+#: kallithea/controllers/admin/repos.py:585
 msgid "An error occurred during cache invalidation"
-msgstr "Адбылася памылка пры ачыстцы кэша"
-
-#: kallithea/controllers/admin/repos.py:601
+msgstr "Памылка пры скіданні кэша"
+
+#: kallithea/controllers/admin/repos.py:600
 msgid "Pulled from remote location"
-msgstr "Занесены змены з выдаленага рэпазітара"
-
-#: kallithea/controllers/admin/repos.py:604
+msgstr "Занесеныя змены з аддаленага рэпазітара"
+
+#: kallithea/controllers/admin/repos.py:603
 msgid "An error occurred during pull from remote location"
-msgstr "Адбылася памылка пры занясенні змен з выдаленага рэпазітара"
-
-#: kallithea/controllers/admin/repos.py:637
+msgstr "Памылка пры занясенні змен з аддаленага рэпазітара"
+
+#: kallithea/controllers/admin/repos.py:636
 msgid "An error occurred during deletion of repository stats"
 msgstr "Адбылася памылка пры выдаленні статыстыкі рэпазітара"
 
-#: kallithea/controllers/admin/settings.py:170
+#: kallithea/controllers/admin/settings.py:141
 msgid "Updated VCS settings"
 msgstr "Абноўлены налады VCS"
 
-#: kallithea/controllers/admin/settings.py:174
+#: kallithea/controllers/admin/settings.py:145
 msgid ""
 "Unable to activate hgsubversion support. The \"hgsubversion\" library is "
 "missing"
 msgstr ""
-"Немагчыма ўключыць падтрымку hgsubversion. Бібліятэка «hgsubversion» "
+"Немагчыма ўключыць падтрымку hgsubversion. Бібліятэка hgsubversion "
 "адсутнічае"
 
-#: kallithea/controllers/admin/settings.py:180
-#: kallithea/controllers/admin/settings.py:277
+#: kallithea/controllers/admin/settings.py:151
+#: kallithea/controllers/admin/settings.py:248
 msgid "Error occurred while updating application settings"
-msgstr "Адбылася памылка пры абнаўленні налад прыкладання"
-
-#: kallithea/controllers/admin/settings.py:216
+msgstr "Памылка пры абнаўленні наладаў праграмы"
+
+#: kallithea/controllers/admin/settings.py:187
 #, python-format
 msgid "Repositories successfully rescanned. Added: %s. Removed: %s."
-msgstr "Рэпазітары паспяхова перасканіраваны, дададзена: %s, выдалена: %s."
-
-#: kallithea/controllers/admin/settings.py:273
+msgstr "Рэпазітары паспяхова перасканаваныя, дададзена: %s, выдалена: %s."
+
+#: kallithea/controllers/admin/settings.py:244
 msgid "Updated application settings"
-msgstr "Абноўленыя параметры налады прыкладання"
-
-#: kallithea/controllers/admin/settings.py:330
+msgstr "Абноўленыя налады праграмы"
+
+#: kallithea/controllers/admin/settings.py:301
 msgid "Updated visualisation settings"
 msgstr "Налады візуалізацыі абноўленыя"
 
-#: kallithea/controllers/admin/settings.py:335
+#: kallithea/controllers/admin/settings.py:306
 msgid "Error occurred during updating visualisation settings"
-msgstr "Адбылася памылка пры абнаўленні налад візуалізацыі"
-
-#: kallithea/controllers/admin/settings.py:361
+msgstr "Адбылася памылка пры абнаўленні наладаў візуалізацыі"
+
+#: kallithea/controllers/admin/settings.py:332
 msgid "Please enter email address"
-msgstr "Калі ласка, увядзіце email-адрас"
-
-#: kallithea/controllers/admin/settings.py:376
+msgstr "Калі ласка, увядзіце e-mail-адрас"
+
+#: kallithea/controllers/admin/settings.py:347
 msgid "Send email task created"
 msgstr "Задача адпраўкі e-mail створаная"
 
-#: kallithea/controllers/admin/settings.py:407
+#: kallithea/controllers/admin/settings.py:378
 msgid "Added new hook"
 msgstr "Дададзены новы хук"
 
-#: kallithea/controllers/admin/settings.py:421
+#: kallithea/controllers/admin/settings.py:392
 msgid "Updated hooks"
 msgstr "Абноўленыя хукі"
 
-#: kallithea/controllers/admin/settings.py:425
+#: kallithea/controllers/admin/settings.py:396
 msgid "Error occurred during hook creation"
-msgstr "адбылася памылка пры стварэнні хука"
-
-#: kallithea/controllers/admin/settings.py:451
+msgstr "Памылка пры стварэнні хука"
+
+#: kallithea/controllers/admin/settings.py:422
 msgid "Whoosh reindex task scheduled"
-msgstr "Запланавана пераіндэксаванне базы Whoosh"
+msgstr "Запланаванае пераіндэксаванне базы Whoosh"
 
 #: kallithea/controllers/admin/user_groups.py:150
 #, python-format
 msgid "Created user group %s"
-msgstr "Створана група карыстачоў %s"
+msgstr "Створана група карыстальнікаў %s"
 
 #: kallithea/controllers/admin/user_groups.py:163
 #, python-format
 msgid "Error occurred during creation of user group %s"
-msgstr "Адбылася памылка пры стварэнні групы карыстачоў %s"
+msgstr "Памылка пры стварэнні групы карыстальнікаў %s"
 
 #: kallithea/controllers/admin/user_groups.py:201
 #, python-format
 msgid "Updated user group %s"
-msgstr "Група карыстачоў %s абноўлена"
+msgstr "Група карыстальнікаў %s абноўленая"
 
 #: kallithea/controllers/admin/user_groups.py:224
 #, python-format
 msgid "Error occurred during update of user group %s"
-msgstr "Адбылася памылка пры абнаўленні групы карыстачоў %s"
+msgstr "Памылка пры абнаўленні групы карыстальнікаў %s"
 
 #: kallithea/controllers/admin/user_groups.py:242
 msgid "Successfully deleted user group"
-msgstr "Група карыстачоў паспяхова выдалена"
+msgstr "Група карыстальнікаў паспяхова выдаленая"
 
 #: kallithea/controllers/admin/user_groups.py:247
 msgid "An error occurred during deletion of user group"
-msgstr "Адбылася памылка пры выдаленні групы карыстачоў"
+msgstr "Памылка пры выдаленні групы карыстальнікаў"
 
 #: kallithea/controllers/admin/user_groups.py:314
 msgid "Target group cannot be the same"
-msgstr "Мэтавая група не можа быць такі ж"
+msgstr "Мэтавая група не можа быць той жа самай"
 
 #: kallithea/controllers/admin/user_groups.py:320
 msgid "User group permissions updated"
-msgstr "Прывілеі групы карыстачоў абноўлены"
+msgstr "Прывілеі групы карыстальнікаў абноўленыя"
 
 #: kallithea/controllers/admin/user_groups.py:440
-#: kallithea/controllers/admin/users.py:384
+#: kallithea/controllers/admin/users.py:383
 msgid "Updated permissions"
-msgstr "Абноўлены прывілеі"
+msgstr "Абноўленыя прывілеі"
 
 #: kallithea/controllers/admin/user_groups.py:444
-#: kallithea/controllers/admin/users.py:388
+#: kallithea/controllers/admin/users.py:387
 msgid "An error occurred during permissions saving"
-msgstr "Адбылася памылка пры захаванні прывілеяў"
-
-#: kallithea/controllers/admin/users.py:134
+msgstr "Памылка пры захаванні прывілеяў"
+
+#: kallithea/controllers/admin/users.py:133
 #, python-format
 msgid "Created user %s"
-msgstr "Карыстач %s створаны"
-
-#: kallithea/controllers/admin/users.py:149
+msgstr "Карыстальнік %s створаны"
+
+#: kallithea/controllers/admin/users.py:148
 #, python-format
 msgid "Error occurred during creation of user %s"
-msgstr "Адбылася памылка пры стварэнні карыстача %s"
-
-#: kallithea/controllers/admin/users.py:182
+msgstr "Памылка пры стварэнні карыстальніка %s"
+
+#: kallithea/controllers/admin/users.py:181
 msgid "User updated successfully"
-msgstr "Карыстач паспяхова абноўлены"
-
-#: kallithea/controllers/admin/users.py:218
+msgstr "Карыстальнік паспяхова абноўлены"
+
+#: kallithea/controllers/admin/users.py:217
 msgid "Successfully deleted user"
-msgstr "Карыстач паспяхова выдалены"
-
-#: kallithea/controllers/admin/users.py:223
+msgstr "Карыстальнік паспяхова выдалены"
+
+#: kallithea/controllers/admin/users.py:222
 msgid "An error occurred during deletion of user"
-msgstr "Адбылася памылка пры выдаленні карыстача"
-
-#: kallithea/controllers/admin/users.py:236
+msgstr "Памылка пры выдаленні карыстальніка"
+
+#: kallithea/controllers/admin/users.py:235
 msgid "The default user cannot be edited"
 msgstr ""
 
-#: kallithea/controllers/admin/users.py:463
+#: kallithea/controllers/admin/users.py:462
 #, python-format
 msgid "Added IP address %s to user whitelist"
-msgstr "Дададзены IP %s у белы спіс карыстача"
-
-#: kallithea/controllers/admin/users.py:469
+msgstr "Дададзены IP %s у белы спіс карыстальніка"
+
+#: kallithea/controllers/admin/users.py:468
 msgid "An error occurred while adding IP address"
 msgstr "Адбылася памылка пры захаванні IP"
 
-#: kallithea/controllers/admin/users.py:483
+#: kallithea/controllers/admin/users.py:482
 msgid "Removed IP address from user whitelist"
-msgstr "Выдалены IP %s з белага спісу карыстача"
-
-#: kallithea/lib/auth.py:743
+msgstr "Выдалены IP %s з белага спісу карыстальніка"
+
+#: kallithea/lib/auth.py:737
 #, python-format
 msgid "IP %s not allowed"
 msgstr "IP %s заблакаваны"
 
-#: kallithea/lib/auth.py:756
+#: kallithea/lib/auth.py:750
 msgid "Invalid API key"
 msgstr "Няслушны API-ключ"
 
-#: kallithea/lib/auth.py:812
+#: kallithea/lib/auth.py:768
+msgid "CSRF token leak has been detected - all form tokens have been expired"
+msgstr ""
+
+#: kallithea/lib/auth.py:813
 msgid "You need to be a registered user to perform this action"
-msgstr "Вы павінны быць зарэгістраваным карыстачом, каб выканаць гэта дзеянне"
-
-#: kallithea/lib/auth.py:844
+msgstr "Вы павінны быць зарэгістраваным карыстальнікам, каб выканаць гэта дзеянне"
+
+#: kallithea/lib/auth.py:843
 msgid "You need to be signed in to view this page"
-msgstr "Старонка даступная толькі аўтарызаваным карыстачам"
-
-#: kallithea/lib/base.py:490
+msgstr "Старонка даступная толькі аўтарызаваным карыстальнікам"
+
+#: kallithea/lib/base.py:493
 msgid "Repository not found in the filesystem"
 msgstr "Рэпазітар не знойдзены на файлавай сістэме"
 
-#: kallithea/lib/base.py:516 kallithea/lib/helpers.py:622
+#: kallithea/lib/base.py:519 kallithea/lib/helpers.py:618
 msgid "Changeset not found"
 msgstr "Набор змен не знойдзены"
 
@@ -1075,161 +1080,160 @@
 msgid "No changes detected"
 msgstr "Змен не выяўлена"
 
-#: kallithea/lib/helpers.py:609
+#: kallithea/lib/helpers.py:605
 #, python-format
 msgid "Deleted branch: %s"
-msgstr "Выдалена галінка: %s"
-
-#: kallithea/lib/helpers.py:611
+msgstr "Выдаленая галіна: %s"
+
+#: kallithea/lib/helpers.py:607
 #, python-format
 msgid "Created tag: %s"
 msgstr "Створаны тэг: %s"
 
-#: kallithea/lib/helpers.py:671
+#: kallithea/lib/helpers.py:667
 #, python-format
 msgid "Show all combined changesets %s->%s"
 msgstr "Паказаць адрозненні разам %s->%s"
 
-#: kallithea/lib/helpers.py:677
-#, fuzzy
+#: kallithea/lib/helpers.py:673
 msgid "Compare view"
-msgstr "параўнанне"
-
-#: kallithea/lib/helpers.py:696
+msgstr "Параўнанне"
+
+#: kallithea/lib/helpers.py:692
 msgid "and"
 msgstr "і"
 
-#: kallithea/lib/helpers.py:697
+#: kallithea/lib/helpers.py:693
 #, python-format
 msgid "%s more"
 msgstr "на %s больш"
 
-#: kallithea/lib/helpers.py:698 kallithea/templates/changelog/changelog.html:44
+#: kallithea/lib/helpers.py:694 kallithea/templates/changelog/changelog.html:44
 msgid "revisions"
 msgstr "версіі"
 
-#: kallithea/lib/helpers.py:722
+#: kallithea/lib/helpers.py:718
 #, python-format
 msgid "Fork name %s"
 msgstr "Імя форка %s"
 
-#: kallithea/lib/helpers.py:742
+#: kallithea/lib/helpers.py:738
 #, python-format
 msgid "Pull request %s"
 msgstr "Pull-запыт %s"
 
-#: kallithea/lib/helpers.py:752
+#: kallithea/lib/helpers.py:748
 msgid "[deleted] repository"
 msgstr "[выдалены] рэпазітар"
 
-#: kallithea/lib/helpers.py:754 kallithea/lib/helpers.py:766
+#: kallithea/lib/helpers.py:750 kallithea/lib/helpers.py:762
 msgid "[created] repository"
 msgstr "[створаны] рэпазітар"
 
-#: kallithea/lib/helpers.py:756
+#: kallithea/lib/helpers.py:752
 msgid "[created] repository as fork"
 msgstr "[створаны] рэпазітар як форк"
 
-#: kallithea/lib/helpers.py:758 kallithea/lib/helpers.py:768
+#: kallithea/lib/helpers.py:754 kallithea/lib/helpers.py:764
 msgid "[forked] repository"
 msgstr "[форкнуты] рэпазітар"
 
-#: kallithea/lib/helpers.py:760 kallithea/lib/helpers.py:770
+#: kallithea/lib/helpers.py:756 kallithea/lib/helpers.py:766
 msgid "[updated] repository"
 msgstr "[абноўлены] рэпазітар"
 
-#: kallithea/lib/helpers.py:762
+#: kallithea/lib/helpers.py:758
 msgid "[downloaded] archive from repository"
 msgstr "[загружаны] архіў з рэпазітара"
 
-#: kallithea/lib/helpers.py:764
+#: kallithea/lib/helpers.py:760
 msgid "[delete] repository"
 msgstr "[выдалены] рэпазітар"
 
+#: kallithea/lib/helpers.py:768
+msgid "[created] user"
+msgstr "[створаны] карыстальнік"
+
+#: kallithea/lib/helpers.py:770
+msgid "[updated] user"
+msgstr "[абноўлены] карыстальнік"
+
 #: kallithea/lib/helpers.py:772
-msgid "[created] user"
-msgstr "[створаны] карыстач"
+msgid "[created] user group"
+msgstr "[створана] група карыстальнікаў"
 
 #: kallithea/lib/helpers.py:774
-msgid "[updated] user"
-msgstr "[абноўлены] карыстач"
+msgid "[updated] user group"
+msgstr "[абноўлена] група карыстальнікаў"
 
 #: kallithea/lib/helpers.py:776
-msgid "[created] user group"
-msgstr "[створана] група карыстачоў"
+msgid "[commented] on revision in repository"
+msgstr "[каментар] да рэвізіі ў рэпазітары"
 
 #: kallithea/lib/helpers.py:778
-msgid "[updated] user group"
-msgstr "[абноўлена] група карыстачоў"
+msgid "[commented] on pull request for"
+msgstr "[каментар] у pull-запыце для"
 
 #: kallithea/lib/helpers.py:780
-msgid "[commented] on revision in repository"
-msgstr "[каментар] да рэвізіі ў рэпазітары"
+msgid "[closed] pull request for"
+msgstr "[зачынены] pull-запыт для"
 
 #: kallithea/lib/helpers.py:782
-msgid "[commented] on pull request for"
-msgstr "[пракаменціравана] у запыце на занясенне змен для"
+msgid "[pushed] into"
+msgstr "[адпраўлена] у"
 
 #: kallithea/lib/helpers.py:784
-msgid "[closed] pull request for"
-msgstr "[зачынены] Pull-запыт для"
+msgid "[committed via Kallithea] into repository"
+msgstr "[каміт праз Kallithea] у рэпазітары"
 
 #: kallithea/lib/helpers.py:786
-msgid "[pushed] into"
-msgstr "[адпраўлена] у"
+msgid "[pulled from remote] into repository"
+msgstr "[занесены з аддаленага рэпазітара] у рэпазітар"
 
 #: kallithea/lib/helpers.py:788
-msgid "[committed via Kallithea] into repository"
-msgstr "[занесены змены з дапамогай Kallithea] у рэпазітары"
+msgid "[pulled] from"
+msgstr "[занесены] з"
 
 #: kallithea/lib/helpers.py:790
-msgid "[pulled from remote] into repository"
-msgstr "[занесены змены з выдаленага рэпазітара] у рэпазітар"
+msgid "[started following] repository"
+msgstr "[дададзены ў назіранні] рэпазітар"
 
 #: kallithea/lib/helpers.py:792
-msgid "[pulled] from"
-msgstr "[занесены змены] з"
-
-#: kallithea/lib/helpers.py:794
-msgid "[started following] repository"
-msgstr "[дададзены ў назіранні] рэпазітар"
-
-#: kallithea/lib/helpers.py:796
 msgid "[stopped following] repository"
 msgstr "[выдалены з назірання] рэпазітар"
 
-#: kallithea/lib/helpers.py:1124
+#: kallithea/lib/helpers.py:1119
 #, python-format
 msgid " and %s more"
 msgstr " і на %s больш"
 
-#: kallithea/lib/helpers.py:1128
-#: kallithea/templates/compare/compare_diff.html:65
-#: kallithea/templates/pullrequests/pullrequest_show.html:326
+#: kallithea/lib/helpers.py:1123
+#: kallithea/templates/compare/compare_diff.html:71
+#: kallithea/templates/pullrequests/pullrequest_show.html:337
 msgid "No files"
 msgstr "Няма файлаў"
 
-#: kallithea/lib/helpers.py:1194
+#: kallithea/lib/helpers.py:1189
 msgid "new file"
 msgstr "новы файл"
 
-#: kallithea/lib/helpers.py:1197
+#: kallithea/lib/helpers.py:1192
 msgid "mod"
 msgstr "зменены"
 
-#: kallithea/lib/helpers.py:1200
+#: kallithea/lib/helpers.py:1195
 msgid "del"
 msgstr "выдалены"
 
-#: kallithea/lib/helpers.py:1203
+#: kallithea/lib/helpers.py:1198
 msgid "rename"
 msgstr "пераназваны"
 
-#: kallithea/lib/helpers.py:1208
+#: kallithea/lib/helpers.py:1203
 msgid "chmod"
 msgstr "chmod"
 
-#: kallithea/lib/helpers.py:1444
+#: kallithea/lib/helpers.py:1464
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
@@ -1240,39 +1244,39 @@
 "пераназваны з файлавай сістэмы. Калі ласка, перазапусціце прыкладанне для"
 " сканавання рэпазітароў"
 
-#: kallithea/lib/utils2.py:415
+#: kallithea/lib/utils2.py:434
 #, python-format
 msgid "%d year"
 msgid_plural "%d years"
 msgstr[0] "%d год"
-msgstr[1] "%d гадоў"
-msgstr[2] "%d гады"
-
-#: kallithea/lib/utils2.py:416
+msgstr[1] "%d гады"
+msgstr[2] "%d гадоў"
+
+#: kallithea/lib/utils2.py:435
 #, python-format
 msgid "%d month"
 msgid_plural "%d months"
 msgstr[0] "%d месяц"
-msgstr[1] "%d месяца"
+msgstr[1] "%d месяцы"
 msgstr[2] "%d месяцаў"
 
-#: kallithea/lib/utils2.py:417
+#: kallithea/lib/utils2.py:436
 #, python-format
 msgid "%d day"
 msgid_plural "%d days"
 msgstr[0] "%d дзень"
-msgstr[1] "%d дня"
+msgstr[1] "%d дні"
 msgstr[2] "%d дзён"
 
-#: kallithea/lib/utils2.py:418
+#: kallithea/lib/utils2.py:437
 #, python-format
 msgid "%d hour"
 msgid_plural "%d hours"
 msgstr[0] "%d гадзіна"
-msgstr[1] "%d гадзін"
-msgstr[2] "%d гадзіны"
-
-#: kallithea/lib/utils2.py:419
+msgstr[1] "%d гадзіны"
+msgstr[2] "%d гадзін"
+
+#: kallithea/lib/utils2.py:438
 #, python-format
 msgid "%d minute"
 msgid_plural "%d minutes"
@@ -1280,37 +1284,37 @@
 msgstr[1] "%d хвіліны"
 msgstr[2] "%d хвілін"
 
-#: kallithea/lib/utils2.py:420
+#: kallithea/lib/utils2.py:439
 #, python-format
 msgid "%d second"
 msgid_plural "%d seconds"
-msgstr[0] "%d секунды"
+msgstr[0] "%d секунда"
 msgstr[1] "%d секунды"
-msgstr[2] "%d секунды"
-
-#: kallithea/lib/utils2.py:436
+msgstr[2] "%d секунд"
+
+#: kallithea/lib/utils2.py:455
 #, python-format
 msgid "in %s"
 msgstr "у %s"
 
-#: kallithea/lib/utils2.py:438
+#: kallithea/lib/utils2.py:457
 #, python-format
 msgid "%s ago"
 msgstr "%s назад"
 
-#: kallithea/lib/utils2.py:440
+#: kallithea/lib/utils2.py:459
 #, python-format
 msgid "in %s and %s"
 msgstr "у %s і %s"
 
-#: kallithea/lib/utils2.py:443
+#: kallithea/lib/utils2.py:462
 #, python-format
 msgid "%s and %s ago"
 msgstr "%s і %s назад"
 
-#: kallithea/lib/utils2.py:446
+#: kallithea/lib/utils2.py:465
 msgid "just now"
-msgstr "прама цяпер"
+msgstr "цяпер"
 
 #: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1163
 #: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1182
@@ -1407,7 +1411,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1531
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1570
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1620
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1647 kallithea/model/db.py:1665
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1647 kallithea/model/db.py:1671
 msgid "Kallithea Administrator"
 msgstr "Адміністратар Kallithea"
 
@@ -1424,7 +1428,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1643
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1670
 msgid "Repository creation disabled"
-msgstr "Стварэнне рэпазітароў адключана"
+msgstr "Стварэнне рэпазітароў адключанае"
 
 #: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1175
 #: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1194
@@ -1439,7 +1443,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1644
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1671
 msgid "Repository creation enabled"
-msgstr "Стварэнне рэпазітароў уключана"
+msgstr "Стварэнне рэпазітароў уключанае"
 
 #: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1176
 #: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1195
@@ -1454,7 +1458,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1648
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1675
 msgid "Repository forking disabled"
-msgstr "Магчымасць ствараць форк рэпазітара адключана"
+msgstr "Магчымасць ствараць форк рэпазітара адключаная"
 
 #: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1177
 #: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1196
@@ -1469,28 +1473,28 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1649
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1676
 msgid "Repository forking enabled"
-msgstr "Магчымасць ствараць форк рэпазітара ўключана"
+msgstr "Магчымасць ствараць форк рэпазітара ўключаная"
 
 #: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1178
 #: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1197
 #: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1318
 #: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1403
 msgid "Register disabled"
-msgstr "Рэгістрацыя адключана"
+msgstr "Рэгістрацыя адключаная"
 
 #: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1179
 #: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1198
 #: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1319
 #: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1404
 msgid "Register new user with Kallithea with manual activation"
-msgstr "Рэгістрацыя новага карыстача ў Kallithea з ручной актывацыяй"
+msgstr "Рэгістрацыя новага карыстальніка ў Kallithea з ручной актывацыяй"
 
 #: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1182
 #: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1201
 #: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1322
 #: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1407
 msgid "Register new user with Kallithea with auto activation"
-msgstr "Рэгістрацыя новага карыстача ў Kallithea з аўтаматычнай актывацыяй"
+msgstr "Рэгістрацыя новага карыстальніка ў Kallithea з аўтаматычнай актывацыяй"
 
 #: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1623
 #: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1650
@@ -1518,7 +1522,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:2063
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:2102
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:2155
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:2201 kallithea/model/db.py:2229
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:2201 kallithea/model/db.py:2238
 msgid "Approved"
 msgstr "Ухвалена"
 
@@ -1533,7 +1537,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:2064
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:2103
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:2156
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:2202 kallithea/model/db.py:2230
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:2202 kallithea/model/db.py:2239
 msgid "Rejected"
 msgstr "Адхілена"
 
@@ -1560,7 +1564,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1379
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1418
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1471
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1498 kallithea/model/db.py:1514
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1498 kallithea/model/db.py:1520
 msgid "top level"
 msgstr "верхні ўзровень"
 
@@ -1621,7 +1625,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1632
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1659
 msgid "User group no access"
-msgstr "Група карыстачоў - няма доступу"
+msgstr "Група карыстальнікаў - няма доступу"
 
 #: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1419
 #: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1465
@@ -1632,7 +1636,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1633
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1660
 msgid "User group read access"
-msgstr "Група карыстачоў - доступ на чытанне"
+msgstr "Група карыстальнікаў - доступ на чытанне"
 
 #: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1420
 #: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1466
@@ -1643,7 +1647,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1634
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1661
 msgid "User group write access"
-msgstr "Група карыстачоў - доступ на запіс"
+msgstr "Група карыстальнікаў - доступ на запіс"
 
 #: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1421
 #: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1467
@@ -1654,7 +1658,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1635
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1662
 msgid "User group admin access"
-msgstr "Група карыстачоў - адміністраванне"
+msgstr "Група карыстальнікаў - адміністраванне"
 
 #: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1423
 #: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1469
@@ -1665,7 +1669,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1637
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1664
 msgid "Repository Group creation disabled"
-msgstr "Стварэнне груп рэпазітароў адключана"
+msgstr "Стварэнне груп рэпазітароў адключанае"
 
 #: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1424
 #: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1470
@@ -1676,7 +1680,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1638
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1665
 msgid "Repository Group creation enabled"
-msgstr "Стварэнне груп рэпазітароў уключана"
+msgstr "Стварэнне груп рэпазітароў уключанае"
 
 #: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1426
 #: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1472
@@ -1687,7 +1691,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1640
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1667
 msgid "User Group creation disabled"
-msgstr "Стварэнне груп карыстачоў адключана"
+msgstr "Стварэнне груп карыстальнікаў адключанае"
 
 #: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1427
 #: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1473
@@ -1698,7 +1702,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1641
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1668
 msgid "User Group creation enabled"
-msgstr "Стварэнне груп карыстачоў уключана"
+msgstr "Стварэнне груп карыстальнікаў уключанае"
 
 #: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1435
 #: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1481
@@ -1707,9 +1711,9 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1560
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1599
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1651
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1678 kallithea/model/db.py:1697
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1678 kallithea/model/db.py:1703
 msgid "Registration disabled"
-msgstr "Рэгістрацыя адключана"
+msgstr "Рэгістрацыя адключаная"
 
 #: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1436
 #: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1482
@@ -1720,7 +1724,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1652
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1679
 msgid "User Registration with manual account activation"
-msgstr "Рэгістрацыя карыстача з ручной актывацыяй уліковага запісу"
+msgstr "Рэгістрацыя карыстальніка з ручной актывацыяй уліковага запісу"
 
 #: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1437
 #: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1483
@@ -1731,15 +1735,15 @@
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1653
 #: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1680
 msgid "User Registration with automatic account activation"
-msgstr "Рэгістрацыя карыстача з аўтаматычнай актывацыяй"
+msgstr "Рэгістрацыя карыстальніка з аўтаматычнай актывацыяй"
 
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1645
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1672 kallithea/model/db.py:1691
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1672 kallithea/model/db.py:1697
 msgid "Repository creation enabled with write permission to a repository group"
 msgstr ""
 
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1646
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1673 kallithea/model/db.py:1692
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1673 kallithea/model/db.py:1698
 msgid "Repository creation disabled with write permission to a repository group"
 msgstr ""
 
@@ -1748,117 +1752,103 @@
 msgid "on line %s"
 msgstr "на радку %s"
 
-#: kallithea/model/comment.py:217 kallithea/model/pull_request.py:169
+#: kallithea/model/comment.py:217 kallithea/model/pull_request.py:170
 msgid "[Mention]"
 msgstr "[Згадванне]"
 
-#: kallithea/model/db.py:1667
+#: kallithea/model/db.py:1673
 msgid "Default user has no access to new repositories"
 msgstr ""
 
-#: kallithea/model/db.py:1668
-#, fuzzy
-msgid "Default user has read access to new repositories"
-msgstr "Несанкцыянаваны доступ да рэсурсу"
-
-#: kallithea/model/db.py:1669
-#, fuzzy
-msgid "Default user has write access to new repositories"
-msgstr "Несанкцыянаваны доступ да рэсурсу"
-
-#: kallithea/model/db.py:1670
-msgid "Default user has admin access to new repositories"
-msgstr ""
-
-#: kallithea/model/db.py:1672
-msgid "Default user has no access to new repository groups"
-msgstr ""
-
-#: kallithea/model/db.py:1673
-msgid "Default user has read access to new repository groups"
-msgstr ""
-
 #: kallithea/model/db.py:1674
-msgid "Default user has write access to new repository groups"
+msgid "Default user has read access to new repositories"
 msgstr ""
 
 #: kallithea/model/db.py:1675
-msgid "Default user has admin access to new repository groups"
-msgstr ""
-
-#: kallithea/model/db.py:1677
-msgid "Default user has no access to new user groups"
+msgid "Default user has write access to new repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1676
+msgid "Default user has admin access to new repositories"
 msgstr ""
 
 #: kallithea/model/db.py:1678
-msgid "Default user has read access to new user groups"
+msgid "Default user has no access to new repository groups"
 msgstr ""
 
 #: kallithea/model/db.py:1679
-msgid "Default user has write access to new user groups"
+msgid "Default user has read access to new repository groups"
 msgstr ""
 
 #: kallithea/model/db.py:1680
-msgid "Default user has admin access to new user groups"
-msgstr ""
-
-#: kallithea/model/db.py:1682
-#, fuzzy
-msgid "Only admins can create repository groups"
-msgstr "Створана новая група рэпазітароў %s"
+msgid "Default user has write access to new repository groups"
+msgstr ""
+
+#: kallithea/model/db.py:1681
+msgid "Default user has admin access to new repository groups"
+msgstr ""
 
 #: kallithea/model/db.py:1683
-#, fuzzy
-msgid "Non-admins can create repository groups"
-msgstr "Створана новая група рэпазітароў %s"
+msgid "Default user has no access to new user groups"
+msgstr ""
+
+#: kallithea/model/db.py:1684
+msgid "Default user has read access to new user groups"
+msgstr ""
 
 #: kallithea/model/db.py:1685
-#, fuzzy
-msgid "Only admins can create user groups"
-msgstr "Ствараць групы карыстачоў"
+msgid "Default user has write access to new user groups"
+msgstr ""
 
 #: kallithea/model/db.py:1686
-#, fuzzy
-msgid "Non-admins can create user groups"
-msgstr "Ствараць групы карыстачоў"
+msgid "Default user has admin access to new user groups"
+msgstr ""
 
 #: kallithea/model/db.py:1688
-#, fuzzy
-msgid "Only admins can create top level repositories"
-msgstr "Рэпазітары верхняга ўзроўню"
+msgid "Only admins can create repository groups"
+msgstr "Толькі адміністратары могуць ствараць групы репазітароў"
 
 #: kallithea/model/db.py:1689
-#, fuzzy
-msgid "Non-admins can create top level repositories"
-msgstr "Рэпазітары верхняга ўзроўню"
+msgid "Non-admins can create repository groups"
+msgstr "Неадміністратары могуць ствараць групы репазітароў"
+
+#: kallithea/model/db.py:1691
+msgid "Only admins can create user groups"
+msgstr "Толькі адміністратары могуць ствараць групы карыстальнікаў"
+
+#: kallithea/model/db.py:1692
+msgid "Non-admins can create user groups"
+msgstr "Неадміністратары могуць ствараць групы карыстальнікаў"
 
 #: kallithea/model/db.py:1694
-#, fuzzy
-msgid "Only admins can fork repositories"
-msgstr "Месцазнаходжанне рэпазітароў"
+msgid "Only admins can create top level repositories"
+msgstr "Толькі адміністратары могуць ствараць рэпазітары верхняга ўзроўню"
 
 #: kallithea/model/db.py:1695
-#, fuzzy
-msgid "Non-admins can can fork repositories"
-msgstr "Скінуць кэш для ўсіх рэпазітароў"
-
-#: kallithea/model/db.py:1698
-#, fuzzy
+msgid "Non-admins can create top level repositories"
+msgstr "Неадміністратары могуць ствараць рэпазітары верхняга ўзроўню"
+
+#: kallithea/model/db.py:1700
+msgid "Only admins can fork repositories"
+msgstr "Месцазнаходжанне рэпазітароў"
+
+#: kallithea/model/db.py:1701
+msgid "Non-admins can fork repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1704
 msgid "User registration with manual account activation"
-msgstr "Рэгістрацыя карыстача з ручной актывацыяй уліковага запісу"
-
-#: kallithea/model/db.py:1699
-#, fuzzy
+msgstr "Рэгістрацыя карыстальніка з ручной актывацыяй уліковага запісу"
+
+#: kallithea/model/db.py:1705
 msgid "User registration with automatic account activation"
-msgstr "Рэгістрацыя карыстача з аўтаматычнай актывацыяй"
-
-#: kallithea/model/db.py:2228
-#, fuzzy
+msgstr "Рэгістрацыя карыстальніка з аўтаматычнай актывацыяй"
+
+#: kallithea/model/db.py:2237
 msgid "Not reviewed"
 msgstr "Не прагледжана"
 
-#: kallithea/model/db.py:2231
-#, fuzzy
+#: kallithea/model/db.py:2240
 msgid "Under review"
 msgstr "На разглядзе"
 
@@ -1880,7 +1870,7 @@
 msgid "Enter %(min)i characters or more"
 msgstr "Увядзіце не меней %(min)i знакаў"
 
-#: kallithea/model/forms.py:160
+#: kallithea/model/forms.py:165
 msgid "Name must not contain only digits"
 msgstr "Імя не можа ўтрымліваць толькі лічбы"
 
@@ -1952,20 +1942,17 @@
 #: kallithea/model/notification.py:305
 #, python-format
 msgid "New user %(new_username)s registered"
-msgstr "Новы карыстач \"%(new_username)s\" зарэгістраваны"
+msgstr "Новы карыстальнік \"%(new_username)s\" зарэгістраваны"
 
 #: kallithea/model/notification.py:307
 #, fuzzy, python-format
-#| msgid "%(user)s wants you to review pull request %(pr_nice_id)s:
-#| %(pr_title)s"
 msgid "[Added] %(repo_name)s pull request %(pr_nice_id)s from %(ref)s"
 msgstr "%(user)s просіць вас разгледзець pull request %(pr_nice_id)s: %(pr_title)s"
 
 #: kallithea/model/notification.py:308
 #, fuzzy, python-format
-#| msgid "[commented] on pull request for"
 msgid "[Comment] %(repo_name)s pull request %(pr_nice_id)s from %(ref)s"
-msgstr "[пракаменціравана] у запыце на занясенне змен для"
+msgstr "[пракаментавана] у запыце на занясенне змен для"
 
 #: kallithea/model/notification.py:321
 msgid "Closing"
@@ -1982,13 +1969,13 @@
 
 #: kallithea/model/user.py:192
 msgid "New user registration"
-msgstr "Рэгістрацыя новага карыстача"
+msgstr "Рэгістрацыя новага карыстальніка"
 
 #: kallithea/model/user.py:256
 msgid "You can't remove this user since it is crucial for the entire application"
 msgstr ""
-"Вы не можаце выдаліць карыстача, паколькі гэта крытычна для працы ўсяго "
-"прыкладання"
+"Вы не можаце выдаліць карыстальніка, паколькі гэта крытычна для працы ўсёй "
+"праграмы"
 
 #: kallithea/model/user.py:261
 #, python-format
@@ -1996,8 +1983,8 @@
 "User \"%s\" still owns %s repositories and cannot be removed. Switch "
 "owners or remove those repositories: %s"
 msgstr ""
-"Карыстач \"%s\" усё яшчэ з'яўляецца ўладальнікам %s рэпазітароў і таму не"
-" можа быць выдалены. Зменіце ўладальніка ці выдаліце гэтыя рэпазітары: %s"
+"Карыстальнік \"%s\" усё яшчэ з'яўляецца ўладальнікам %s рэпазітароў і таму не"
+" можа быць выдалены. Змяніце ўладальніка ці выдаліце гэтыя рэпазітары: %s"
 
 #: kallithea/model/user.py:266
 #, python-format
@@ -2005,8 +1992,8 @@
 "User \"%s\" still owns %s repository groups and cannot be removed. Switch"
 " owners or remove those repository groups: %s"
 msgstr ""
-"Карыстач \"%s\" усё яшчэ з'яўляецца ўладальнікам %s груп рэпазітароў і "
-"таму не можа быць выдалены. Зменіце ўладальніка ці выдаліце дадзеныя "
+"Карыстальнік \"%s\" усё яшчэ з'яўляецца ўладальнікам %s груп рэпазітароў і "
+"таму не можа быць выдалены. Змяніце ўладальніка ці выдаліце гэтая "
 "групы: %s"
 
 #: kallithea/model/user.py:273
@@ -2015,21 +2002,19 @@
 "User \"%s\" still owns %s user groups and cannot be removed. Switch "
 "owners or remove those user groups: %s"
 msgstr ""
-"Карыстач \"%s\" усё яшчэ з'яўляецца ўладальнікам %s груп карыстачоў і "
-"таму не можа быць выдалены. Зменіце ўладальніка ці выдаліце дадзеныя "
+"Карыстальнік \"%s\" усё яшчэ з'яўляецца ўладальнікам %s груп карыстальнікаў і "
+"таму не можа быць выдалены. Змяніце ўладальніка ці выдаліце гэтыя "
 "групы: %s"
 
-#: kallithea/model/user.py:360
+#: kallithea/model/user.py:368
 msgid "Password reset link"
 msgstr "Спасылка скіду пароля"
 
-#: kallithea/model/user.py:408
-#, fuzzy
-#| msgid "Password reset link"
+#: kallithea/model/user.py:418
 msgid "Password reset notification"
-msgstr "Спасылка скіду пароля"
-
-#: kallithea/model/user.py:409
+msgstr "Паведамленне пра скіданне пароля"
+
+#: kallithea/model/user.py:419
 #, python-format
 msgid ""
 "The password to your account %s has been changed using password reset "
@@ -2040,112 +2025,111 @@
 msgid "Value cannot be an empty list"
 msgstr "Значэнне не можа быць пустым спісам"
 
-#: kallithea/model/validators.py:95
+#: kallithea/model/validators.py:96
 #, python-format
 msgid "Username \"%(username)s\" already exists"
-msgstr "Карыстач з імем \"%(username)s\" ужо існуе"
-
-#: kallithea/model/validators.py:97
+msgstr "Карыстальнік з імем \"%(username)s\" ужо існуе"
+
+#: kallithea/model/validators.py:98
 #, python-format
 msgid "Username \"%(username)s\" cannot be used"
 msgstr "Імя \"%(username)s\" недапушчальнае"
 
-#: kallithea/model/validators.py:99
+#: kallithea/model/validators.py:100
 msgid ""
 "Username may only contain alphanumeric characters underscores, periods or"
 " dashes and must begin with an alphanumeric character or underscore"
 msgstr ""
-"Імя карыстача можа ўтрымоўваць толькі літары, лічбы, знакі падкрэслення, "
+"Імя карыстальніка можа ўтрымоўваць толькі літары, лічбы, знакі падкрэслення, "
 "кропкі і працяжнік; а гэтак жа павінна пачынацца з літары, лічбы або са "
 "знака падкрэслення"
 
-#: kallithea/model/validators.py:126
+#: kallithea/model/validators.py:127
 msgid "The input is not valid"
 msgstr ""
 
-#: kallithea/model/validators.py:133
+#: kallithea/model/validators.py:134
 #, python-format
 msgid "Username %(username)s is not valid"
-msgstr "Імя \"%(username)s\" недапушчальна"
-
-#: kallithea/model/validators.py:152
+msgstr "Імя \"%(username)s\" недапушчальнае"
+
+#: kallithea/model/validators.py:154
 msgid "Invalid user group name"
-msgstr "Няслушнае імя групы карыстачоў"
-
-#: kallithea/model/validators.py:153
+msgstr "Няслушнае імя групы карыстальнікаў"
+
+#: kallithea/model/validators.py:155
 #, python-format
 msgid "User group \"%(usergroup)s\" already exists"
-msgstr "Група карыстачоў \"%(usergroup)s\" ужо існуе"
-
-#: kallithea/model/validators.py:155
+msgstr "Група карыстальнікаў \"%(usergroup)s\" ужо існуе"
+
+#: kallithea/model/validators.py:157
 msgid ""
 "user group name may only contain alphanumeric characters underscores, "
 "periods or dashes and must begin with alphanumeric character"
 msgstr ""
-"імя групы карыстачоў можа ўтрымоўваць толькі літары, лічбы, знакі "
+"імя групы карыстальнікаў можа ўтрымоўваць толькі літары, лічбы, знакі "
 "падкрэслення, кропкі і працяжнік; а гэтак жа павінна пачынацца з літары "
 "ці лічбы"
 
-#: kallithea/model/validators.py:193
+#: kallithea/model/validators.py:197
 msgid "Cannot assign this group as parent"
 msgstr "Немагчыма выкарыстоўваць гэту групу як бацькоўскую"
 
-#: kallithea/model/validators.py:194
+#: kallithea/model/validators.py:198
 #, python-format
 msgid "Group \"%(group_name)s\" already exists"
 msgstr "Група \"%(group_name)s\" ужо існуе"
 
-#: kallithea/model/validators.py:196
+#: kallithea/model/validators.py:200
 #, python-format
 msgid "Repository with name \"%(group_name)s\" already exists"
 msgstr "Рэпазітар з  імем \"%(group_name)s\" ужо існуе"
 
-#: kallithea/model/validators.py:254
+#: kallithea/model/validators.py:258
 msgid "Invalid characters (non-ascii) in password"
 msgstr "Недапушчальныя знакі (не ascii) у паролі"
 
-#: kallithea/model/validators.py:269
+#: kallithea/model/validators.py:273
 msgid "Invalid old password"
 msgstr "Няслушна зададзены стары пароль"
 
-#: kallithea/model/validators.py:285
+#: kallithea/model/validators.py:289
 msgid "Passwords do not match"
 msgstr "Паролі не супадаюць"
 
-#: kallithea/model/validators.py:300
+#: kallithea/model/validators.py:304
 msgid "Invalid username or password"
 msgstr "Няслушнае імя ці пароль"
 
-#: kallithea/model/validators.py:331
+#: kallithea/model/validators.py:335
 msgid "Token mismatch"
 msgstr "Несупадзенне токенаў"
 
-#: kallithea/model/validators.py:345
+#: kallithea/model/validators.py:351
 #, python-format
 msgid "Repository name %(repo)s is not allowed"
 msgstr "Імя рэпазітара %(repo)s забароненае"
 
-#: kallithea/model/validators.py:347
+#: kallithea/model/validators.py:353
 #, python-format
 msgid "Repository named %(repo)s already exists"
 msgstr "Рэпазітар %(repo)s ужо існуе"
 
-#: kallithea/model/validators.py:348
+#: kallithea/model/validators.py:354
 #, python-format
 msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
 msgstr "Рэпазітар \"%(repo)s\" ужо існуе ў групе \"%(group)s\""
 
-#: kallithea/model/validators.py:350
+#: kallithea/model/validators.py:356
 #, python-format
 msgid "Repository group with name \"%(repo)s\" already exists"
 msgstr "Група рэпазітароў \"%(repo)s\" ужо існуе"
 
-#: kallithea/model/validators.py:465
+#: kallithea/model/validators.py:470
 msgid "Invalid repository URL"
 msgstr "Няслушны URL рэпазітара"
 
-#: kallithea/model/validators.py:466
-#, fuzzy
+#: kallithea/model/validators.py:471
 msgid ""
 "Invalid repository URL. It must be a valid http, https, ssh, svn+http or "
 "svn+https URL"
@@ -2153,69 +2137,68 @@
 "Няслушны URL рэпазітара. Ён мусіць быць карэктным URL http, https, ssh, "
 "svn+http ці svn+https"
 
-#: kallithea/model/validators.py:489
+#: kallithea/model/validators.py:496
 msgid "Fork has to be the same type as parent"
 msgstr "Тып форка будзе супадаць з бацькоўскім"
 
-#: kallithea/model/validators.py:504
+#: kallithea/model/validators.py:511
 msgid "You don't have permissions to create repository in this group"
-msgstr "У вас недастаткова мае рацыю для стварэння рэпазітароў у гэтай групе"
-
-#: kallithea/model/validators.py:506
+msgstr "У вас недастаткова правоў для стварэння рэпазітароў у гэтай групе"
+
+#: kallithea/model/validators.py:513
 msgid "no permission to create repository in root location"
-msgstr "недастаткова мае рацыю для стварэння рэпазітара ў каранёвым каталогу"
-
-#: kallithea/model/validators.py:556
+msgstr "недастаткова правоў для стварэння рэпазітара ў каранёвым каталогу"
+
+#: kallithea/model/validators.py:563
 msgid "You don't have permissions to create a group in this location"
 msgstr "У Вас недастаткова прывілеяў для стварэння групы ў гэтым месцы"
 
-#: kallithea/model/validators.py:597
+#: kallithea/model/validators.py:604
 msgid "This username or user group name is not valid"
-msgstr "Дадзенае імя карыстача ці групы карыстачоў недапушчальна"
-
-#: kallithea/model/validators.py:690
+msgstr "Дадзенае імя карыстальніка ці групы карыстальнікаў недапушчальна"
+
+#: kallithea/model/validators.py:697
 msgid "This is not a valid path"
 msgstr "Гэты шлях хібны"
 
-#: kallithea/model/validators.py:705
+#: kallithea/model/validators.py:714
 msgid "This email address is already in use"
-msgstr "Гэты E-mail ужо заняты"
-
-#: kallithea/model/validators.py:725
-#, fuzzy, python-format
+msgstr "Гэты e-mail ужо ўжываецца"
+
+#: kallithea/model/validators.py:734
 msgid "Email address \"%(email)s\" not found"
-msgstr "\"%(email)s\" не існуе."
-
-#: kallithea/model/validators.py:762
+msgstr "e-mail \"%(email)s\" не існуе."
+
+#: kallithea/model/validators.py:771
 msgid ""
 "The LDAP Login attribute of the CN must be specified - this is the name "
 "of the attribute that is equivalent to \"username\""
 msgstr ""
 "Для ўваходу па LDAP павінна быць паказана значэнне атрыбута CN - гэта "
-"эквівалент імя карыстача"
-
-#: kallithea/model/validators.py:774
+"эквівалент імя карыстальніка"
+
+#: kallithea/model/validators.py:783
 msgid "Please enter a valid IPv4 or IPv6 address"
 msgstr "Калі ласка, увядзіце існы IPv4 ці IPv6 адрас"
 
-#: kallithea/model/validators.py:775
+#: kallithea/model/validators.py:784
 #, python-format
 msgid "The network size (bits) must be within the range of 0-32 (not %(bits)r)"
 msgstr ""
 "Значэнне маскі падсеткі павінна быць у межах ад 0 да 32 (%(bits)r - "
 "няслушна)"
 
-#: kallithea/model/validators.py:808
+#: kallithea/model/validators.py:817
 msgid "Key name can only consist of letters, underscore, dash or numbers"
 msgstr ""
 "Ключавое імя можа толькі складацца з літар, знака падкрэслення, працяжнік"
 " ці лікаў"
 
-#: kallithea/model/validators.py:822
+#: kallithea/model/validators.py:831
 msgid "Filename cannot be inside a directory"
 msgstr "Файла няма ў каталогу"
 
-#: kallithea/model/validators.py:838
+#: kallithea/model/validators.py:847
 #, python-format
 msgid "Plugins %(loaded)s and %(next_to_load)s both export the same name"
 msgstr ""
@@ -2331,7 +2314,7 @@
 #: kallithea/templates/journal/journal.html:189
 #: kallithea/templates/journal/journal.html:280
 msgid "Tip"
-msgstr "Стан"
+msgstr ""
 
 #: kallithea/templates/index_base.html:132
 #: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:10
@@ -2342,7 +2325,7 @@
 #: kallithea/templates/admin/user_groups/user_groups.html:50
 #: kallithea/templates/pullrequests/pullrequest_data.html:16
 #: kallithea/templates/pullrequests/pullrequest_show.html:156
-#: kallithea/templates/pullrequests/pullrequest_show.html:233
+#: kallithea/templates/pullrequests/pullrequest_show.html:244
 #: kallithea/templates/summary/summary.html:134
 msgid "Owner"
 msgstr "Уладальнік"
@@ -2373,7 +2356,7 @@
 
 #: kallithea/templates/index_base.html:142
 msgid "No repositories found."
-msgstr "Рэпазітары не знойдзены."
+msgstr "Рэпазітары не знойдзеныя."
 
 #: kallithea/templates/index_base.html:143
 #: kallithea/templates/admin/my_account/my_account_repos.html:60
@@ -2390,7 +2373,7 @@
 #: kallithea/templates/index_base.html:144
 #: kallithea/templates/admin/my_account/my_account_repos.html:61
 #: kallithea/templates/admin/my_account/my_account_watched.html:61
-#: kallithea/templates/base/base.html:140 kallithea/templates/base/root.html:47
+#: kallithea/templates/base/root.html:47
 #: kallithea/templates/bookmarks/bookmarks.html:83
 #: kallithea/templates/branches/branches.html:83
 #: kallithea/templates/journal/journal.html:202
@@ -2400,7 +2383,7 @@
 msgstr "Загрузка..."
 
 #: kallithea/templates/login.html:5 kallithea/templates/login.html:15
-#: kallithea/templates/base/base.html:326
+#: kallithea/templates/base/base.html:414
 msgid "Log In"
 msgstr "Увайсці"
 
@@ -2415,15 +2398,15 @@
 #: kallithea/templates/admin/users/user_add.html:32
 #: kallithea/templates/admin/users/user_edit_profile.html:24
 #: kallithea/templates/admin/users/users.html:50
-#: kallithea/templates/base/base.html:302
+#: kallithea/templates/base/base.html:390
 #: kallithea/templates/pullrequests/pullrequest_show.html:166
 msgid "Username"
-msgstr "Імя карыстача"
+msgstr "Імя карыстальніка"
 
 #: kallithea/templates/login.html:33 kallithea/templates/register.html:33
 #: kallithea/templates/admin/my_account/my_account.html:37
 #: kallithea/templates/admin/users/user_add.html:41
-#: kallithea/templates/base/base.html:311
+#: kallithea/templates/base/base.html:399
 msgid "Password"
 msgstr "Пароль"
 
@@ -2433,9 +2416,9 @@
 
 #: kallithea/templates/login.html:53
 msgid "Forgot your password ?"
-msgstr "Забыліся пароль?"
-
-#: kallithea/templates/login.html:56 kallithea/templates/base/base.html:322
+msgstr "Забыліся на пароль?"
+
+#: kallithea/templates/login.html:56 kallithea/templates/base/base.html:410
 msgid "Don't have an account ?"
 msgstr "Няма акаўнта?"
 
@@ -2445,13 +2428,13 @@
 
 #: kallithea/templates/password_reset.html:5
 msgid "Password Reset"
-msgstr "Скід пароля"
+msgstr "Скінуць пароль"
 
 #: kallithea/templates/password_reset.html:12
 #: kallithea/templates/password_reset_confirmation.html:12
 #, python-format
 msgid "Reset Your Password to %s"
-msgstr "Забыліся пароль для %s?"
+msgstr "Забыліся на пароль для %s?"
 
 #: kallithea/templates/password_reset.html:14
 #: kallithea/templates/password_reset_confirmation.html:5
@@ -2470,16 +2453,14 @@
 
 #: kallithea/templates/password_reset.html:46
 msgid "Send Password Reset Email"
-msgstr "Паслаць спасылку скіду пароля"
+msgstr "Паслаць спасылку для скідання пароля"
 
 #: kallithea/templates/password_reset.html:47
 #, fuzzy
-#| msgid "" "Password reset link will be sent to the email address matching
-#| your " "username."
 msgid ""
 "A password reset link will be sent to the specified email address if it "
 "is registered in the system."
-msgstr "Спасылка для скіду пароля была адпраўлена на адпаведны e-mail."
+msgstr "Спасылка для скіду пароля будзе адпраўленая на адпаведны e-mail."
 
 #: kallithea/templates/password_reset_confirmation.html:19
 #, python-format
@@ -2497,14 +2478,10 @@
 msgstr ""
 
 #: kallithea/templates/password_reset_confirmation.html:39
-#, fuzzy
-#| msgid "New password"
 msgid "New Password"
 msgstr "Новы пароль"
 
 #: kallithea/templates/password_reset_confirmation.html:48
-#, fuzzy
-#| msgid "Confirm new password"
 msgid "Confirm New Password"
 msgstr "Пацвердзіце новы пароль"
 
@@ -2520,11 +2497,11 @@
 #: kallithea/templates/register.html:12
 #, python-format
 msgid "Sign Up to %s"
-msgstr "Рэгістра на %s"
+msgstr "Рэгістрацыя на %s"
 
 #: kallithea/templates/register.html:42
 msgid "Re-enter password"
-msgstr "Паўторыце пароль"
+msgstr "Паўтарыце пароль"
 
 #: kallithea/templates/register.html:51
 #: kallithea/templates/admin/my_account/my_account_profile.html:34
@@ -2561,11 +2538,7 @@
 #: kallithea/templates/switch_to_list.html:10
 #: kallithea/templates/branches/branches_data.html:69
 msgid "There are no branches yet"
-msgstr "Галінкі яшчэ не створаны"
-
-#: kallithea/templates/switch_to_list.html:16
-msgid "Closed Branches"
-msgstr "Зачыненыя галінкі"
+msgstr "Галіны яшчэ не створаныя"
 
 #: kallithea/templates/switch_to_list.html:32
 #: kallithea/templates/tags/tags_data.html:44
@@ -2581,11 +2554,11 @@
 #: kallithea/templates/admin/admin.html:13
 #: kallithea/templates/base/base.html:59
 msgid "Admin Journal"
-msgstr "Часопіс адміністратара"
+msgstr "Журнал адміністратара"
 
 #: kallithea/templates/admin/admin.html:10
 msgid "journal filter..."
-msgstr "Фільтр часопіса..."
+msgstr "Фільтр журнала..."
 
 #: kallithea/templates/admin/admin.html:12
 #: kallithea/templates/journal/journal.html:11
@@ -2635,7 +2608,7 @@
 
 #: kallithea/templates/admin/admin_log.html:63
 msgid "No actions yet"
-msgstr "Дзеянні яшчэ не вырабляліся"
+msgstr "Няма інфармацыі"
 
 #: kallithea/templates/admin/auth/auth_settings.html:5
 msgid "Authentication Settings"
@@ -2648,11 +2621,11 @@
 
 #: kallithea/templates/admin/auth/auth_settings.html:28
 msgid "Authentication Plugins"
-msgstr "Убудовы аўтэнтыфікацыі"
+msgstr "Плагіны аўтэнтыфікацыі"
 
 #: kallithea/templates/admin/auth/auth_settings.html:31
 msgid "Enabled Plugins"
-msgstr "Уключаныя ўбудовы"
+msgstr "Уключаныя плагіны"
 
 #: kallithea/templates/admin/auth/auth_settings.html:33
 #, fuzzy
@@ -2660,16 +2633,16 @@
 "Comma-separated list of plugins; Kallithea will try user authentication "
 "in plugin order"
 msgstr ""
-"Спіс убудоў, падзеленых коскі. Kallithea будзе спрабаваць аўтэнтыфікаваць"
-" карыстача ў парадку ўказання ўбудоў"
+"Спіс плагінаў, падзеленых коскамі. Kallithea будзе спрабаваць аўтэнтыфікаваць"
+" карыстальніка ў парадку ўказання плагінаў"
 
 #: kallithea/templates/admin/auth/auth_settings.html:34
 msgid "Available built-in plugins"
-msgstr "Даступныя ўбудаваныя ўбудовы"
+msgstr "Даступныя ўбудаваныя плагіны"
 
 #: kallithea/templates/admin/auth/auth_settings.html:51
 msgid "Plugin"
-msgstr "Убудова"
+msgstr "Плагін"
 
 #: kallithea/templates/admin/auth/auth_settings.html:101
 #: kallithea/templates/admin/defaults/defaults.html:82
@@ -2695,7 +2668,7 @@
 #: kallithea/templates/admin/defaults/defaults.html:11
 #: kallithea/templates/base/base.html:66
 msgid "Repository Defaults"
-msgstr "Значэнні па змаўчанні"
+msgstr "Значэнні па змоўчанні"
 
 #: kallithea/templates/admin/defaults/defaults.html:33
 #: kallithea/templates/admin/repos/repo_add_base.html:55
@@ -2748,12 +2721,12 @@
 #: kallithea/templates/admin/defaults/defaults.html:77
 #: kallithea/templates/admin/repos/repo_edit_settings.html:106
 msgid "Enable lock-by-pulling on repository."
-msgstr "Уключыць аўтаблакоўку для рэпазітара."
+msgstr "Уключыць аўтаблакаванне для рэпазітара."
 
 #: kallithea/templates/admin/gists/edit.html:5
 #: kallithea/templates/admin/gists/edit.html:18
 msgid "Edit Gist"
-msgstr "Праўка gist-запісы"
+msgstr "Правіць gist-запіс"
 
 #: kallithea/templates/admin/gists/edit.html:36
 #, python-format
@@ -2785,7 +2758,7 @@
 #: kallithea/templates/admin/users/user_edit_api_keys.html:27
 #: kallithea/templates/admin/users/user_edit_api_keys.html:32
 msgid "Expires"
-msgstr "Мінае"
+msgstr ""
 
 #: kallithea/templates/admin/gists/edit.html:61
 #: kallithea/templates/admin/gists/index.html:57
@@ -2794,16 +2767,15 @@
 #: kallithea/templates/admin/my_account/my_account_api_keys.html:27
 #: kallithea/templates/admin/users/user_edit_api_keys.html:8
 #: kallithea/templates/admin/users/user_edit_api_keys.html:27
-#, fuzzy
 msgid "Never"
-msgstr "ніколі"
-
-#: kallithea/templates/admin/gists/edit.html:145
+msgstr "Ніколі"
+
+#: kallithea/templates/admin/gists/edit.html:146
 msgid "Update Gist"
 msgstr "Абнавіць"
 
-#: kallithea/templates/admin/gists/edit.html:146
-#: kallithea/templates/changeset/changeset_file_comment.html:81
+#: kallithea/templates/admin/gists/edit.html:147
+#: kallithea/templates/changeset/changeset_file_comment.html:105
 msgid "Cancel"
 msgstr "Адмена"
 
@@ -2811,13 +2783,13 @@
 #: kallithea/templates/admin/gists/index.html:16
 #, python-format
 msgid "Private Gists for User %s"
-msgstr "Прыватная gist-запіс для карыстача %s"
+msgstr "Прыватны gist-запіс для карыстальніка %s"
 
 #: kallithea/templates/admin/gists/index.html:8
 #: kallithea/templates/admin/gists/index.html:18
 #, python-format
 msgid "Public Gists for User %s"
-msgstr "Публічная gist-запіс для карыстача %s"
+msgstr "Публічны gist-запіс для карыстальніка %s"
 
 #: kallithea/templates/admin/gists/index.html:10
 #: kallithea/templates/admin/gists/index.html:20
@@ -2826,14 +2798,14 @@
 
 #: kallithea/templates/admin/gists/index.html:37
 #: kallithea/templates/admin/gists/show.html:25
-#: kallithea/templates/base/base.html:237
+#: kallithea/templates/base/base.html:321
 msgid "Create New Gist"
-msgstr "Стварыць новую gist-запіс"
+msgstr "Стварыць новы gist-запіс"
 
 #: kallithea/templates/admin/gists/index.html:54
 #: kallithea/templates/data_table/_dt_elements.html:141
 msgid "Created"
-msgstr "Створана"
+msgstr "Створаны"
 
 #: kallithea/templates/admin/gists/index.html:74
 msgid "There are no gists yet"
@@ -2842,7 +2814,7 @@
 #: kallithea/templates/admin/gists/new.html:5
 #: kallithea/templates/admin/gists/new.html:18
 msgid "New Gist"
-msgstr ""
+msgstr "Новы gist-запіс"
 
 #: kallithea/templates/admin/gists/new.html:47
 msgid "name this file..."
@@ -2884,7 +2856,7 @@
 #: kallithea/templates/files/files_edit.html:68
 #: kallithea/templates/pullrequests/pullrequest.html:89
 msgid "Reset"
-msgstr "Скід"
+msgstr "Скінуць"
 
 #: kallithea/templates/admin/gists/show.html:5
 #: kallithea/templates/admin/gists/show.html:9
@@ -2914,7 +2886,8 @@
 #: kallithea/templates/admin/settings/settings_hooks.html:36
 #: kallithea/templates/admin/users/user_edit_emails.html:19
 #: kallithea/templates/admin/users/user_edit_ips.html:22
-#: kallithea/templates/changeset/changeset_file_comment.html:30
+#: kallithea/templates/changeset/changeset_file_comment.html:31
+#: kallithea/templates/changeset/changeset_file_comment.html:95
 #: kallithea/templates/data_table/_dt_elements.html:129
 #: kallithea/templates/data_table/_dt_elements.html:157
 #: kallithea/templates/data_table/_dt_elements.html:173
@@ -2928,14 +2901,12 @@
 
 #: kallithea/templates/admin/gists/show.html:56
 msgid "Confirm to delete this Gist"
-msgstr "Пацвердзіце выдаленне гэтай gist-запісы"
+msgstr "Пацвердзіце выдаленне гэтага gist-запісу"
 
 #: kallithea/templates/admin/gists/show.html:63
 #: kallithea/templates/base/perms_summary.html:43
 #: kallithea/templates/base/perms_summary.html:79
 #: kallithea/templates/base/perms_summary.html:81
-#: kallithea/templates/changeset/changeset_file_comment.html:83
-#: kallithea/templates/changeset/changeset_file_comment.html:192
 #: kallithea/templates/data_table/_dt_elements.html:122
 #: kallithea/templates/data_table/_dt_elements.html:123
 #: kallithea/templates/data_table/_dt_elements.html:150
@@ -2962,13 +2933,12 @@
 msgstr "створана"
 
 #: kallithea/templates/admin/gists/show.html:86
-#: kallithea/templates/files/files_source.html:73
 msgid "Show as raw"
 msgstr "Паказаць толькі тэкст"
 
 #: kallithea/templates/admin/my_account/my_account.html:5
 #: kallithea/templates/admin/my_account/my_account.html:9
-#: kallithea/templates/base/base.html:343
+#: kallithea/templates/base/base.html:431
 msgid "My Account"
 msgstr "Мой Акаўнт"
 
@@ -2978,9 +2948,8 @@
 msgstr "Профіль"
 
 #: kallithea/templates/admin/my_account/my_account.html:36
-#, fuzzy
 msgid "Email Addresses"
-msgstr "Новы E-mail"
+msgstr "E-mail адрэсы"
 
 #: kallithea/templates/admin/my_account/my_account.html:38
 #: kallithea/templates/admin/users/user_edit.html:31
@@ -2988,9 +2957,8 @@
 msgstr "API-ключы"
 
 #: kallithea/templates/admin/my_account/my_account.html:39
-#, fuzzy
 msgid "Owned Repositories"
-msgstr "рэпазітары"
+msgstr "Мае рэпазітары"
 
 #: kallithea/templates/admin/my_account/my_account.html:40
 #: kallithea/templates/journal/journal.html:53
@@ -3002,9 +2970,8 @@
 #: kallithea/templates/admin/permissions/permissions.html:30
 #: kallithea/templates/admin/user_groups/user_group_edit.html:32
 #: kallithea/templates/admin/users/user_edit.html:34
-#, fuzzy
 msgid "Show Permissions"
-msgstr "Скапіяваць прывілеі"
+msgstr "Паказаць прывілеі"
 
 #: kallithea/templates/admin/my_account/my_account_api_keys.html:6
 #: kallithea/templates/admin/users/user_edit_api_keys.html:6
@@ -3015,7 +2982,7 @@
 #: kallithea/templates/admin/users/user_edit_api_keys.html:14
 #, fuzzy, python-format
 msgid "Confirm to reset this API key: %s"
-msgstr "Пацвердзіце скід гэтага API-ключа: %s"
+msgstr "Пацвердзіце скіданне гэтага API-ключа: %s"
 
 #: kallithea/templates/admin/my_account/my_account_api_keys.html:30
 #: kallithea/templates/admin/users/user_edit_api_keys.html:30
@@ -3025,15 +2992,14 @@
 
 #: kallithea/templates/admin/my_account/my_account_api_keys.html:40
 #: kallithea/templates/admin/users/user_edit_api_keys.html:40
-#, fuzzy, python-format
+#, python-format
 msgid "Confirm to remove this API key: %s"
 msgstr "Пацвердзіце выдаленне гэтага API-ключа: %s"
 
 #: kallithea/templates/admin/my_account/my_account_api_keys.html:42
 #: kallithea/templates/admin/users/user_edit_api_keys.html:42
-#, fuzzy
 msgid "Remove"
-msgstr "выдаліць"
+msgstr "Выдаліць"
 
 #: kallithea/templates/admin/my_account/my_account_api_keys.html:49
 #: kallithea/templates/admin/users/user_edit_api_keys.html:49
@@ -3043,9 +3009,8 @@
 
 #: kallithea/templates/admin/my_account/my_account_api_keys.html:61
 #: kallithea/templates/admin/users/user_edit_api_keys.html:61
-#, fuzzy
 msgid "New API key"
-msgstr "Ключ"
+msgstr "Новы API-ключ"
 
 #: kallithea/templates/admin/my_account/my_account_api_keys.html:69
 #: kallithea/templates/admin/my_account/my_account_emails.html:45
@@ -3067,7 +3032,7 @@
 #: kallithea/templates/admin/users/user_edit_emails.html:20
 #, python-format
 msgid "Confirm to delete this email: %s"
-msgstr "Пацвердзіце выдаленне E-mail: %s"
+msgstr "Пацвердзіце выдаленне e-mail: %s"
 
 #: kallithea/templates/admin/my_account/my_account_emails.html:26
 #: kallithea/templates/admin/users/user_edit_emails.html:26
@@ -3077,7 +3042,7 @@
 #: kallithea/templates/admin/my_account/my_account_emails.html:38
 #: kallithea/templates/admin/users/user_edit_emails.html:38
 msgid "New email address"
-msgstr "Новы E-mail"
+msgstr "Новы e-mail"
 
 #: kallithea/templates/admin/my_account/my_account_password.html:1
 msgid "Change Your Account Password"
@@ -3085,7 +3050,7 @@
 
 #: kallithea/templates/admin/my_account/my_account_password.html:10
 msgid "Current password"
-msgstr "Бягучы пароль"
+msgstr "Цяперашні пароль"
 
 #: kallithea/templates/admin/my_account/my_account_password.html:19
 #: kallithea/templates/admin/users/user_edit_profile.html:60
@@ -3103,7 +3068,7 @@
 
 #: kallithea/templates/admin/my_account/my_account_profile.html:11
 msgid "Change your avatar at"
-msgstr "Зменіце аватар праз сайт"
+msgstr "Змяніць аватар можна праз"
 
 #: kallithea/templates/admin/my_account/my_account_profile.html:12
 #: kallithea/templates/admin/users/user_edit_profile.html:9
@@ -3113,17 +3078,16 @@
 #: kallithea/templates/admin/my_account/my_account_profile.html:14
 #: kallithea/templates/admin/users/user_edit_profile.html:11
 msgid "Avatars are disabled"
-msgstr "Аватары адключаны"
+msgstr "Аватары адключаныя"
 
 #: kallithea/templates/admin/my_account/my_account_profile.html:15
 msgid "Missing email, please update your user email address."
-msgstr "Не паказаны email. Калі ласка, абновіце ваш email."
+msgstr "Няма email адрэсы, калі ласка, абнавіце ваш email."
 
 #: kallithea/templates/admin/my_account/my_account_profile.html:16
 #: kallithea/templates/admin/users/user_edit_profile.html:15
-#, fuzzy
 msgid "Current IP"
-msgstr "бягучы IP-адрас"
+msgstr "Цяперашні IP-адрас"
 
 #: kallithea/templates/admin/my_account/my_account_repos.html:1
 msgid "Repositories You Own"
@@ -3138,7 +3102,7 @@
 #: kallithea/templates/journal/journal.html:291
 #: kallithea/templates/tags/tags.html:81
 msgid "No records found."
-msgstr "Запісы не знойдзены."
+msgstr "Запісы не знойдзеныя."
 
 #: kallithea/templates/admin/my_account/my_account_watched.html:1
 msgid "Repositories You are Watching"
@@ -3151,20 +3115,20 @@
 
 #: kallithea/templates/admin/notifications/notifications.html:24
 msgid "All"
-msgstr "Усё"
+msgstr "Усе"
 
 #: kallithea/templates/admin/notifications/notifications.html:25
 msgid "Comments"
 msgstr "Каментары"
 
 #: kallithea/templates/admin/notifications/notifications.html:26
-#: kallithea/templates/base/base.html:183
+#: kallithea/templates/base/base.html:180
 msgid "Pull Requests"
 msgstr "Pull-запыты"
 
 #: kallithea/templates/admin/notifications/notifications.html:30
 msgid "Mark All Read"
-msgstr "Адзначыць усё як прачытаныя"
+msgstr "Адзначыць усе як прачытаныя"
 
 #: kallithea/templates/admin/notifications/notifications_data.html:40
 msgid "No notifications here yet"
@@ -3176,7 +3140,7 @@
 msgstr "Паказаць апавяшчэнне"
 
 #: kallithea/templates/admin/notifications/show_notification.html:9
-#: kallithea/templates/base/base.html:342
+#: kallithea/templates/base/base.html:430
 msgid "Notifications"
 msgstr "Апавяшчэнні"
 
@@ -3213,13 +3177,12 @@
 "permission, note that all custom default permission on repositories will "
 "be lost"
 msgstr ""
-"Абраныя прывілеі будуць усталяваны па змаўчанні для кожнага рэпазітара. "
-"Улічыце, што раней усталяваныя прывілеі па змаўчанні будуць скінуты"
+"Выбраныя прывілеі будуць усталяваныя па змоўчанні для кожнага рэпазітара. "
+"Улічыце, што раней усталяваныя прывілеі па змоўчанні будуць скінутыя"
 
 #: kallithea/templates/admin/permissions/permissions_globals.html:26
-#, fuzzy
 msgid "Apply to all existing repositories"
-msgstr "Імпартаваць існы рэпазітар?"
+msgstr ""
 
 #: kallithea/templates/admin/permissions/permissions_globals.html:27
 msgid "Permissions for the Default user on new repositories."
@@ -3239,14 +3202,13 @@
 "permission, note that all custom default permission on repository groups "
 "will be lost"
 msgstr ""
-"Абраныя прывілеі будуць усталяваны па змаўчанні для кожнай групы "
-"рэпазітароў. Улічыце, што раней усталяваныя прывілеі па змаўчанні для "
-"груп рэпазітароў будуць скінуты"
+"Выбраныя прывілеі будуць усталяваныя па змоўчанні для кожнай групы "
+"рэпазітароў. Улічыце, што раней усталяваныя прывілеі па змоўчанні для "
+"груп рэпазітароў будуць скінутыя"
 
 #: kallithea/templates/admin/permissions/permissions_globals.html:40
-#, fuzzy
 msgid "Apply to all existing repository groups"
-msgstr "Імпартаваць існы рэпазітар?"
+msgstr ""
 
 #: kallithea/templates/admin/permissions/permissions_globals.html:41
 msgid "Permissions for the Default user on new repository groups."
@@ -3255,18 +3217,17 @@
 #: kallithea/templates/admin/permissions/permissions_globals.html:46
 #: kallithea/templates/data_table/_dt_elements.html:209
 msgid "User group"
-msgstr "Група карыстачоў"
+msgstr "Група карыстальнікаў"
 
 #: kallithea/templates/admin/permissions/permissions_globals.html:53
-#, fuzzy
 msgid ""
 "All default permissions on each user group will be reset to chosen "
 "permission, note that all custom default permission on user groups will "
 "be lost"
 msgstr ""
-"Абраныя прывілеі будуць усталяваны па змаўчанні для кожнай групы "
-"карыстачоў. Улічыце, што раней усталяваныя прывілеі па змаўчанні для груп"
-" карыстачоў будуць скінуты"
+"Выбраныя прывілеі будуць усталяваныя па змоўчанні для кожнай групы "
+"карыстальнікаў. Улічыце, што раней усталяваныя прывілеі па змоўчанні для груп"
+" карыстальнікаў будуць скінутыя"
 
 #: kallithea/templates/admin/permissions/permissions_globals.html:54
 msgid "Apply to all existing user groups"
@@ -3277,9 +3238,8 @@
 msgstr ""
 
 #: kallithea/templates/admin/permissions/permissions_globals.html:60
-#, fuzzy
 msgid "Top level repository creation"
-msgstr "Стварэнне рэпазітара"
+msgstr ""
 
 #: kallithea/templates/admin/permissions/permissions_globals.html:64
 msgid "Enable this to allow non-admins to create repositories at the top level."
@@ -3304,7 +3264,7 @@
 
 #: kallithea/templates/admin/permissions/permissions_globals.html:79
 msgid "User group creation"
-msgstr "Стварэнне груп карыстачоў"
+msgstr "Стварэнне груп карыстальнікаў"
 
 #: kallithea/templates/admin/permissions/permissions_globals.html:83
 msgid "Enable this to allow non-admins to create user groups."
@@ -3328,7 +3288,6 @@
 
 #: kallithea/templates/admin/permissions/permissions_ips.html:13
 #: kallithea/templates/admin/users/user_edit_ips.html:23
-#, fuzzy, python-format
 msgid "Confirm to delete this IP address: %s"
 msgstr "Пацвердзіце выдаленне IP %s"
 
@@ -3386,7 +3345,7 @@
 #: kallithea/templates/admin/repos/repo_edit.html:40
 #: kallithea/templates/admin/settings/settings.html:11
 #: kallithea/templates/admin/user_groups/user_group_edit.html:29
-#: kallithea/templates/base/base.html:67 kallithea/templates/base/base.html:151
+#: kallithea/templates/base/base.html:67 kallithea/templates/base/base.html:148
 #: kallithea/templates/data_table/_dt_elements.html:45
 #: kallithea/templates/data_table/_dt_elements.html:49
 msgid "Settings"
@@ -3445,9 +3404,8 @@
 #: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:11
 #: kallithea/templates/admin/repos/repo_edit_permissions.html:12
 #: kallithea/templates/admin/user_groups/user_group_edit_perms.html:11
-#, fuzzy
 msgid "User/User Group"
-msgstr "групы карыстальнікаў"
+msgstr "Карыстальнік/група карыстальнікаў"
 
 #: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:28
 #: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:45
@@ -3455,9 +3413,8 @@
 #: kallithea/templates/admin/repos/repo_edit_permissions.html:37
 #: kallithea/templates/admin/user_groups/user_group_edit_perms.html:28
 #: kallithea/templates/admin/user_groups/user_group_edit_perms.html:45
-#, fuzzy
 msgid "Default"
-msgstr "па змаўчанні"
+msgstr "Па змоўчанні"
 
 #: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:34
 #: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:71
@@ -3465,9 +3422,8 @@
 #: kallithea/templates/admin/repos/repo_edit_permissions.html:68
 #: kallithea/templates/admin/user_groups/user_group_edit_perms.html:34
 #: kallithea/templates/admin/user_groups/user_group_edit_perms.html:71
-#, fuzzy
 msgid "Revoke"
-msgstr "адклікаць"
+msgstr "Адклікаць"
 
 #: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:97
 #: kallithea/templates/admin/repos/repo_edit_permissions.html:94
@@ -3476,9 +3432,8 @@
 msgstr "Дадаць новы"
 
 #: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:103
-#, fuzzy
 msgid "Apply to children"
-msgstr "дастасаваць да даччыным"
+msgstr ""
 
 #: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:107
 msgid "Both"
@@ -3504,7 +3459,7 @@
 
 #: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:53
 msgid "Confirm to delete this group"
-msgstr "Пацвердзіце выдаленне гэтай групы карыстачоў"
+msgstr "Пацвердзіце выдаленне гэтай групы карыстальнікаў"
 
 #: kallithea/templates/admin/repo_groups/repo_group_show.html:4
 #, python-format
@@ -3513,7 +3468,7 @@
 
 #: kallithea/templates/admin/repo_groups/repo_group_show.html:9
 msgid "Home"
-msgstr "Дахаты"
+msgstr "Дамоў"
 
 #: kallithea/templates/admin/repo_groups/repo_group_show.html:13
 msgid "with"
@@ -3528,9 +3483,8 @@
 msgstr "Лік рэпазітароў верхняга ўзроўня"
 
 #: kallithea/templates/admin/repos/repo_add_base.html:17
-#, fuzzy
 msgid "Clone remote repository"
-msgstr "[створаны] рэпазітар"
+msgstr ""
 
 #: kallithea/templates/admin/repos/repo_add_base.html:22
 msgid ""
@@ -3626,19 +3580,19 @@
 #: kallithea/templates/admin/repos/repo_edit_advanced.html:8
 #: kallithea/templates/admin/repos/repo_edit_fork.html:9
 msgid "Manually set this repository as a fork of another from the list."
-msgstr "Уручную зрабіць гэты рэпазітар форкам абранага са спісу."
+msgstr "Уручную зрабіць гэты рэпазітар форкам выбранага са спісу."
 
 #: kallithea/templates/admin/repos/repo_edit_advanced.html:22
 msgid "Public Journal Visibility"
-msgstr "Доступ да публічнага часопіса"
+msgstr "Доступ да публічнага журналу"
 
 #: kallithea/templates/admin/repos/repo_edit_advanced.html:29
 msgid "Remove from public journal"
-msgstr "Выдаліць з агульнадаступнага часопіса"
+msgstr "Выдаліць з агульнадаступнага журналу"
 
 #: kallithea/templates/admin/repos/repo_edit_advanced.html:34
 msgid "Add to Public Journal"
-msgstr "Дадаць у публічны часопіс"
+msgstr "Дадаць у публічны журнал"
 
 #: kallithea/templates/admin/repos/repo_edit_advanced.html:40
 msgid ""
@@ -3646,7 +3600,7 @@
 "public journal."
 msgstr ""
 "Усе выконваемыя з гэтым рэпазітаром дзеянні будуць адлюстроўвацца ў "
-"публічным часопісе."
+"публічным журнал."
 
 #: kallithea/templates/admin/repos/repo_edit_advanced.html:46
 msgid "Change Locking"
@@ -3654,12 +3608,17 @@
 
 #: kallithea/templates/admin/repos/repo_edit_advanced.html:52
 msgid "Confirm to unlock repository."
-msgstr "Пацвердзіце здыманне блакавання з рэпазітара."
+msgstr "Пацвердзіце разблакаванне рэпазітара."
 
 #: kallithea/templates/admin/repos/repo_edit_advanced.html:54
 msgid "Unlock Repository"
 msgstr "Разблакаваць рэпазітар"
 
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:56
+#, python-format
+msgid "Locked by %s on %s"
+msgstr "Заблакавана %s %s"
+
 #: kallithea/templates/admin/repos/repo_edit_advanced.html:60
 msgid "Confirm to lock repository."
 msgstr "Пацвердзіце блакаванне рэпазітара."
@@ -3696,15 +3655,15 @@
 msgid_plural "This repository has %s forks"
 msgstr[0] "Дадзены рэпазітар мае %s копію"
 msgstr[1] "Дадзены рэпазітар мае %s копіі"
-msgstr[2] "Дадзены рэпазітар мае %s дзід"
+msgstr[2] "Дадзены рэпазітар мае %s копій"
 
 #: kallithea/templates/admin/repos/repo_edit_advanced.html:85
 msgid "Detach forks"
-msgstr "Адлучыць fork'і"
+msgstr "Адлучыць форкі"
 
 #: kallithea/templates/admin/repos/repo_edit_advanced.html:86
 msgid "Delete forks"
-msgstr "Выдаліць fork'і"
+msgstr "Выдаліць форкі"
 
 #: kallithea/templates/admin/repos/repo_edit_advanced.html:90
 msgid ""
@@ -3717,15 +3676,11 @@
 msgid "Invalidate Repository Cache"
 msgstr "Скінуць кэш рэпазітара"
 
-#: kallithea/templates/admin/repos/repo_edit_caches.html:4
-msgid "Confirm to invalidate repository cache."
-msgstr "Пацвердзіце скід кэша."
-
 #: kallithea/templates/admin/repos/repo_edit_caches.html:7
 msgid ""
 "Manually invalidate cache for this repository. On first access, the "
 "repository will be cached again."
-msgstr "Ручны скід кэша рэпазітара. Пры першым доступе кэш адновіцца."
+msgstr "Ручное скіданне кэша рэпазітара. Пры першым доступе кэш адновіцца."
 
 #: kallithea/templates/admin/repos/repo_edit_caches.html:12
 msgid "List of Cached Values"
@@ -3781,36 +3736,31 @@
 
 #: kallithea/templates/admin/repos/repo_edit_fields.html:66
 msgid "Extra fields are disabled."
-msgstr "Дадатковыя палі адключаны."
+msgstr "Дадатковыя палі адключаныя."
 
 #: kallithea/templates/admin/repos/repo_edit_permissions.html:21
-#, fuzzy
 msgid "Private Repository"
-msgstr "прыватны рэпазітар"
+msgstr "Прыватны рэпазітар"
 
 #: kallithea/templates/admin/repos/repo_edit_remote.html:3
-#, fuzzy
 msgid "Remote repository URL"
-msgstr "Рэпазітар %s створаны"
+msgstr "URL аддаленага рэпазітара"
 
 #: kallithea/templates/admin/repos/repo_edit_remote.html:9
-#, fuzzy
 msgid "Pull Changes from Remote Repository"
-msgstr "[занесены змены з выдаленага рэпазітара] у рэпазітар"
+msgstr "Занесці змены з аддаленага рэпазітара"
 
 #: kallithea/templates/admin/repos/repo_edit_remote.html:11
-#, fuzzy
 msgid "Confirm to pull changes from remote repository."
-msgstr "Пацвердзіце спампоўку змен."
+msgstr "Пацвердзіце спампоўку змен з аддаленага рэпазітара."
 
 #: kallithea/templates/admin/repos/repo_edit_remote.html:17
 msgid "This repository does not have a remote repository URL."
 msgstr ""
 
 #: kallithea/templates/admin/repos/repo_edit_settings.html:11
-#, fuzzy
 msgid "Permanent Repository ID"
-msgstr "прыватны рэпазітар"
+msgstr "Пастаяннае ШВ рэпазітара"
 
 #: kallithea/templates/admin/repos/repo_edit_settings.html:11
 msgid "What is that?"
@@ -3831,14 +3781,12 @@
 msgstr ""
 
 #: kallithea/templates/admin/repos/repo_edit_settings.html:21
-#, fuzzy
 msgid "Remote repository"
-msgstr "[створаны] рэпазітар"
+msgstr "Аддалены рэпазітар"
 
 #: kallithea/templates/admin/repos/repo_edit_settings.html:25
-#, fuzzy
 msgid "Repository URL"
-msgstr "Рэпазітар"
+msgstr "URL рэпазітара"
 
 #: kallithea/templates/admin/repos/repo_edit_settings.html:29
 msgid ""
@@ -3849,7 +3797,7 @@
 #: kallithea/templates/admin/repos/repo_edit_settings.html:48
 msgid "Default revision for files page, downloads, whoosh and readme"
 msgstr ""
-"Рэвізія па змаўчанні, з якой будзе вырабляцца выгрузка файлаў пры "
+"Рэвізія па змоўчанні, з якой будзе рабіцца выгрузка файлаў пры "
 "спампоўцы"
 
 #: kallithea/templates/admin/repos/repo_edit_settings.html:58
@@ -3870,7 +3818,7 @@
 
 #: kallithea/templates/admin/repos/repo_edit_statistics.html:10
 msgid "Confirm to remove current statistics."
-msgstr "Пацвердзіце скід статыстыкі."
+msgstr "Пацвердзіце скіданне статыстыкі."
 
 #: kallithea/templates/admin/repos/repos.html:5
 msgid "Repositories Administration"
@@ -3975,7 +3923,7 @@
 
 #: kallithea/templates/admin/settings/settings_hooks.html:19
 msgid "Custom Hooks"
-msgstr "Карыстацкія хуки"
+msgstr "Карыстальніцкія хукі"
 
 #: kallithea/templates/admin/settings/settings_hooks.html:67
 msgid "Failed to remove hook"
@@ -3986,9 +3934,8 @@
 msgstr "Опцыі перасканіравання"
 
 #: kallithea/templates/admin/settings/settings_mapping.html:11
-#, fuzzy
 msgid "Delete records of missing repositories"
-msgstr "Пошук па рэпазітарах"
+msgstr "Сцерці запісы пра выдаленыя рэпазітары"
 
 #: kallithea/templates/admin/settings/settings_mapping.html:13
 msgid ""
@@ -4049,12 +3996,11 @@
 
 #: kallithea/templates/admin/settings/settings_system.html:4
 msgid "Kallithea version"
-msgstr ""
+msgstr "Версія Kallithea"
 
 #: kallithea/templates/admin/settings/settings_system.html:4
-#, fuzzy
 msgid "Check for updates"
-msgstr "праверыць наяўнасць абнаўленняў"
+msgstr "Праверыць наяўнасць абнаўленняў"
 
 #: kallithea/templates/admin/settings/settings_system.html:5
 msgid "Kallithea configuration file"
@@ -4112,11 +4058,11 @@
 
 #: kallithea/templates/admin/settings/settings_vcs.html:28
 msgid "Log user push commands"
-msgstr "Лагіраваць карыстацкія каманды адпраўкі"
+msgstr "Лагаваць карыстацкія каманды адпраўкі"
 
 #: kallithea/templates/admin/settings/settings_vcs.html:32
 msgid "Log user pull commands"
-msgstr "Лагіраваць карыстацкія каманды атрымання"
+msgstr "Лагаваць карыстацкія каманды атрымання"
 
 #: kallithea/templates/admin/settings/settings_vcs.html:36
 msgid "Update repository after push (hg update)"
@@ -4256,9 +4202,8 @@
 msgstr "Паказваць абразкі публічных рэпазітароў."
 
 #: kallithea/templates/admin/settings/settings_visual.html:92
-#, fuzzy
 msgid "Meta Tagging"
-msgstr "Метатэгіраванне"
+msgstr "Метатэгаванне"
 
 #: kallithea/templates/admin/settings/settings_visual.html:97
 msgid "Stylify recognised meta tags:"
@@ -4272,7 +4217,7 @@
 
 #: kallithea/templates/admin/user_groups/user_group_add.html:5
 msgid "Add user group"
-msgstr "Дадаць групу карыстачоў"
+msgstr "Дадаць групу карыстальнікаў"
 
 #: kallithea/templates/admin/user_groups/user_group_add.html:10
 #: kallithea/templates/admin/user_groups/user_group_edit.html:11
@@ -4297,9 +4242,8 @@
 msgstr ""
 
 #: kallithea/templates/admin/user_groups/user_group_edit.html:33
-#, fuzzy
 msgid "Show Members"
-msgstr "удзельнікі"
+msgstr "Паказаць удзельнікаў"
 
 #: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:1
 #, python-format
@@ -4316,7 +4260,7 @@
 #: kallithea/templates/data_table/_dt_elements.html:174
 #, python-format
 msgid "Confirm to delete this user group: %s"
-msgstr "Пацвердзіце выдаленне наступнай групы карыстачоў: %s"
+msgstr "Пацвердзіце выдаленне наступнай групы карыстальнікаў: %s"
 
 #: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:21
 msgid "Delete this user group"
@@ -4336,23 +4280,23 @@
 
 #: kallithea/templates/admin/user_groups/user_groups.html:5
 msgid "User Groups Administration"
-msgstr "Адміністраванне груп карыстачоў"
+msgstr "Адміністраванне груп карыстальнікаў"
 
 #: kallithea/templates/admin/users/user_add.html:5
 msgid "Add user"
-msgstr "Дадаць карыстача"
+msgstr "Дадаць карыстальніка"
 
 #: kallithea/templates/admin/users/user_add.html:10
 #: kallithea/templates/admin/users/user_edit.html:11
 #: kallithea/templates/admin/users/users.html:10
 #: kallithea/templates/base/base.html:62
 msgid "Users"
-msgstr "Карыстачы"
+msgstr "Карыстальнікі"
 
 #: kallithea/templates/admin/users/user_add.html:12
 #: kallithea/templates/admin/users/users.html:24
 msgid "Add User"
-msgstr "Дадаць карыстача"
+msgstr "Дадаць карыстальніка"
 
 #: kallithea/templates/admin/users/user_add.html:50
 msgid "Password confirmation"
@@ -4390,7 +4334,7 @@
 #: kallithea/templates/data_table/_dt_elements.html:158
 #, python-format
 msgid "Confirm to delete this user: %s"
-msgstr "Пацвердзіце выдаленне карыстача %s"
+msgstr "Пацвердзіце выдаленне карыстальніка %s"
 
 #: kallithea/templates/admin/users/user_edit_advanced.html:23
 msgid "Delete this user"
@@ -4419,7 +4363,7 @@
 
 #: kallithea/templates/admin/users/users.html:5
 msgid "Users Administration"
-msgstr "Адміністраванне карыстачоў"
+msgstr "Адміністраванне карыстальнікаў"
 
 #: kallithea/templates/admin/users/users.html:56
 msgid "Auth Type"
@@ -4468,21 +4412,17 @@
 msgid "Files"
 msgstr "Файлы"
 
-#: kallithea/templates/base/base.html:138
-msgid "Switch To"
-msgstr "Пераключыцца на"
-
-#: kallithea/templates/base/base.html:145
-#: kallithea/templates/base/base.html:147
+#: kallithea/templates/base/base.html:142
+#: kallithea/templates/base/base.html:144
 msgid "Options"
 msgstr "Опцыі"
 
-#: kallithea/templates/base/base.html:155
+#: kallithea/templates/base/base.html:152
 #: kallithea/templates/forks/forks_data.html:21
 msgid "Compare Fork"
 msgstr "Параўнаць форк"
 
-#: kallithea/templates/base/base.html:157
+#: kallithea/templates/base/base.html:154
 #: kallithea/templates/bookmarks/bookmarks.html:56
 #: kallithea/templates/bookmarks/bookmarks_data.html:13
 #: kallithea/templates/branches/branches.html:56
@@ -4492,118 +4432,122 @@
 msgid "Compare"
 msgstr "Параўнаць"
 
-#: kallithea/templates/base/base.html:159
-#: kallithea/templates/base/base.html:247
+#: kallithea/templates/base/base.html:156
+#: kallithea/templates/base/base.html:331
 #: kallithea/templates/search/search.html:14
 #: kallithea/templates/search/search.html:54
 msgid "Search"
 msgstr "Пошук"
 
-#: kallithea/templates/base/base.html:163
+#: kallithea/templates/base/base.html:160
 msgid "Unlock"
 msgstr "Разблакаваць"
 
-#: kallithea/templates/base/base.html:165
+#: kallithea/templates/base/base.html:162
 msgid "Lock"
 msgstr "Заблакаваць"
 
-#: kallithea/templates/base/base.html:173
+#: kallithea/templates/base/base.html:170
 msgid "Follow"
 msgstr "Назіраць"
 
+#: kallithea/templates/base/base.html:171
+msgid "Unfollow"
+msgstr "Не назіраць"
+
 #: kallithea/templates/base/base.html:174
-msgid "Unfollow"
-msgstr "Не назіраць"
-
-#: kallithea/templates/base/base.html:177
 #: kallithea/templates/data_table/_dt_elements.html:37
 #: kallithea/templates/data_table/_dt_elements.html:41
 #: kallithea/templates/forks/fork.html:9
 msgid "Fork"
 msgstr "Форк"
 
-#: kallithea/templates/base/base.html:178
+#: kallithea/templates/base/base.html:175
 #: kallithea/templates/pullrequests/pullrequest.html:88
 msgid "Create Pull Request"
-msgstr "Стварыць Pull запыт"
-
-#: kallithea/templates/base/base.html:183
+msgstr "Стварыць pull-запыт"
+
+#: kallithea/templates/base/base.html:180
 #, python-format
 msgid "Show Pull Requests for %s"
 msgstr "Паказаць pull-запыты для %s"
 
-#: kallithea/templates/base/base.html:221
+#: kallithea/templates/base/base.html:193
+msgid "Switch To"
+msgstr "Пераключыцца на"
+
+#: kallithea/templates/base/base.html:203
+#: kallithea/templates/base/base.html:485
+msgid "No matches found"
+msgstr "Супадзенняў не знойдзена"
+
+#: kallithea/templates/base/base.html:305
 msgid "Show recent activity"
 msgstr "Паказаць апошнюю актыўнасць"
 
-#: kallithea/templates/base/base.html:227
-#: kallithea/templates/base/base.html:228
+#: kallithea/templates/base/base.html:311
+#: kallithea/templates/base/base.html:312
 msgid "Public journal"
-msgstr "Агульнадаступны часопіс"
-
-#: kallithea/templates/base/base.html:233
+msgstr "Агульнадаступны журнал"
+
+#: kallithea/templates/base/base.html:317
 msgid "Show public gists"
 msgstr "Паказаць публічныя запісы"
 
-#: kallithea/templates/base/base.html:234
+#: kallithea/templates/base/base.html:318
 msgid "Gists"
 msgstr "Gist"
 
-#: kallithea/templates/base/base.html:238
+#: kallithea/templates/base/base.html:322
 msgid "All Public Gists"
 msgstr "Усе публічныя Gist-запісы"
 
-#: kallithea/templates/base/base.html:240
+#: kallithea/templates/base/base.html:324
 msgid "My Public Gists"
 msgstr "Мае публічныя Gist-запісы"
 
-#: kallithea/templates/base/base.html:241
+#: kallithea/templates/base/base.html:325
 msgid "My Private Gists"
 msgstr "Мае прыватныя Gist-запісы"
 
-#: kallithea/templates/base/base.html:246
+#: kallithea/templates/base/base.html:330
 msgid "Search in repositories"
 msgstr "Пошук па рэпазітарах"
 
-#: kallithea/templates/base/base.html:269
-#: kallithea/templates/base/base.html:270
+#: kallithea/templates/base/base.html:353
+#: kallithea/templates/base/base.html:354
 #: kallithea/templates/pullrequests/pullrequest_show_my.html:6
 #: kallithea/templates/pullrequests/pullrequest_show_my.html:10
 msgid "My Pull Requests"
-msgstr "Мае Pull-запыты"
-
-#: kallithea/templates/base/base.html:289
+msgstr "Мае pull-запыты"
+
+#: kallithea/templates/base/base.html:377
 msgid "Not Logged In"
 msgstr "Не аўтарызаваны"
 
-#: kallithea/templates/base/base.html:296
+#: kallithea/templates/base/base.html:384
 msgid "Login to Your Account"
 msgstr "Аўтарызавацца"
 
-#: kallithea/templates/base/base.html:319
+#: kallithea/templates/base/base.html:407
 msgid "Forgot password ?"
-msgstr "Забыліся пароль?"
-
-#: kallithea/templates/base/base.html:346
+msgstr "Забыліся на пароль?"
+
+#: kallithea/templates/base/base.html:434
 msgid "Log Out"
-msgstr "Выйсце"
-
-#: kallithea/templates/base/base.html:395
-msgid "No matches found"
-msgstr "Супадзенняў не знойдзена"
-
-#: kallithea/templates/base/base.html:524
+msgstr "Выйсці"
+
+#: kallithea/templates/base/base.html:615
 msgid "Keyboard shortcuts"
 msgstr "Гарачыя клавішы"
 
-#: kallithea/templates/base/base.html:533
+#: kallithea/templates/base/base.html:624
 msgid "Site-wide shortcuts"
 msgstr ""
 
 #: kallithea/templates/base/default_perms_box.html:14
-#, fuzzy
 msgid "Inherit defaults"
-msgstr "Значэнні па змаўчанні"
+msgstr "Ужываць значэнні па змоўчанні"
 
 #: kallithea/templates/base/default_perms_box.html:19
 #, python-format
@@ -4618,15 +4562,15 @@
 
 #: kallithea/templates/base/default_perms_box.html:33
 msgid "Select this option to allow repository creation for this user"
-msgstr "Опцыя дазваляе карыстачу ствараць рэпазітары"
+msgstr "Опцыя дазваляе карыстальніку ствараць рэпазітары"
 
 #: kallithea/templates/base/default_perms_box.html:40
 msgid "Create user groups"
-msgstr "Ствараць групы карыстачоў"
+msgstr "Ствараць групы карыстальнікаў"
 
 #: kallithea/templates/base/default_perms_box.html:45
 msgid "Select this option to allow user group creation for this user"
-msgstr "Опцыя дазваляе карыстачу ствараць групы карыстачоў"
+msgstr "Опцыя дазваляе карыстальніку ствараць групы карыстальнікаў"
 
 #: kallithea/templates/base/default_perms_box.html:52
 msgid "Fork repositories"
@@ -4635,7 +4579,7 @@
 #: kallithea/templates/base/default_perms_box.html:57
 msgid "Select this option to allow repository forking for this user"
 msgstr ""
-"Абярыце гэту опцыю каб дазволіць дадзенаму карыстачу ствараць fork'і "
+"Абярыце гэту опцыю каб дазволіць дадзенаму карыстальніку ствараць форкі "
 "рэпазітароў"
 
 #: kallithea/templates/base/perms_summary.html:13
@@ -4645,7 +4589,7 @@
 
 #: kallithea/templates/base/perms_summary.html:22
 msgid "No permissions defined yet"
-msgstr "Прывілеі яшчэ не прызначаны"
+msgstr "Прывілеі яшчэ не прызначаныя"
 
 #: kallithea/templates/base/perms_summary.html:30
 #: kallithea/templates/base/perms_summary.html:54
@@ -4699,20 +4643,16 @@
 msgstr "Няма супадзенняў"
 
 #: kallithea/templates/base/root.html:31
-#, fuzzy
-#| msgid "on pull request"
 msgid "Open New Pull Request from {0}"
-msgstr "Каментар у pull-запыце"
+msgstr "Стварыць новы pull-запыт з {0}"
 
 #: kallithea/templates/base/root.html:32
 msgid "Open New Pull Request for {0} &rarr; {1}"
-msgstr ""
+msgstr "Стварыць новы pull-запыт для {0} &rarr; {1}"
 
 #: kallithea/templates/base/root.html:33
-#, fuzzy
-#| msgid "Show Selected Changesets __S &rarr; __E"
 msgid "Show Selected Changesets {0} &rarr; {1}"
-msgstr "Паказаць абраныя наборы змен: __S &rarr; __E"
+msgstr "Паказаць выбраныя наборы змен: {0} &rarr; {1}"
 
 #: kallithea/templates/base/root.html:34
 msgid "Selection Link"
@@ -4720,12 +4660,13 @@
 
 #: kallithea/templates/base/root.html:35
 #: kallithea/templates/changeset/diff_block.html:8
+#: kallithea/templates/changeset/diff_block.html:21
 msgid "Collapse Diff"
 msgstr "Згарнуць параўнанне"
 
 #: kallithea/templates/base/root.html:36
 msgid "Expand Diff"
-msgstr "Расчыніць параўнанне"
+msgstr "Разгарнуць параўнанне"
 
 #: kallithea/templates/base/root.html:37
 msgid "Failed to revoke permission"
@@ -4745,7 +4686,7 @@
 
 #: kallithea/templates/base/root.html:42
 msgid "Specify changeset"
-msgstr "Абраць набор змен"
+msgstr "Выбраць набор змен"
 
 #: kallithea/templates/bookmarks/bookmarks.html:5
 #, python-format
@@ -4754,7 +4695,7 @@
 
 #: kallithea/templates/bookmarks/bookmarks.html:26
 msgid "Compare Bookmarks"
-msgstr ""
+msgstr "Параўнаць закладкі"
 
 #: kallithea/templates/bookmarks/bookmarks.html:53
 #: kallithea/templates/bookmarks/bookmarks_data.html:10
@@ -4782,11 +4723,11 @@
 #: kallithea/templates/branches/branches.html:5
 #, python-format
 msgid "%s Branches"
-msgstr "Галінкі %s"
+msgstr "%s Галіны"
 
 #: kallithea/templates/branches/branches.html:26
 msgid "Compare Branches"
-msgstr ""
+msgstr "Параўнаць галіны"
 
 #: kallithea/templates/changelog/changelog.html:6
 #, python-format
@@ -4823,28 +4764,28 @@
 #: kallithea/templates/changelog/changelog.html:66
 #: kallithea/templates/files/files.html:29
 msgid "Branch filter:"
-msgstr "Адфільтраваць галінку:"
+msgstr "Адфільтраваць галіну:"
 
 #: kallithea/templates/changelog/changelog.html:92
 #: kallithea/templates/changelog/changelog_summary_data.html:20
-#, fuzzy, python-format
+#, python-format
 msgid ""
 "Changeset status: %s\n"
 "Click to open associated pull request %s"
 msgstr ""
-"Статут набору змен: %s?\n"
-"Клікніце, каб перайсці да адпаведнага pull-request'у #%s"
+"Статус набору змен: %s?\n"
+"Клікніце, каб перайсці да адпаведнага pull-запыту %s"
 
 #: kallithea/templates/changelog/changelog.html:96
 #: kallithea/templates/compare/compare_cs.html:24
 #, python-format
 msgid "Changeset status: %s"
-msgstr "Статут набору змен: %s"
+msgstr "Статус набору змен: %s"
 
 #: kallithea/templates/changelog/changelog.html:115
 #: kallithea/templates/compare/compare_cs.html:63
 msgid "Expand commit message"
-msgstr ""
+msgstr "Разгарнуць паведамленне"
 
 #: kallithea/templates/changelog/changelog.html:124
 #: kallithea/templates/compare/compare_cs.html:30
@@ -4863,9 +4804,11 @@
 #: kallithea/templates/changelog/changelog_summary_data.html:60
 #: kallithea/templates/changeset/changeset.html:101
 #: kallithea/templates/changeset/changeset_range.html:98
+#: kallithea/templates/compare/compare_cs.html:69
+#: kallithea/templates/pullrequests/pullrequest_show.html:203
 #, python-format
 msgid "Tag %s"
-msgstr "Пазнака %s"
+msgstr "Тэг %s"
 
 #: kallithea/templates/changelog/changelog.html:145
 #: kallithea/templates/changelog/changelog_summary_data.html:65
@@ -4873,7 +4816,7 @@
 #: kallithea/templates/changeset/changeset_range.html:102
 #, python-format
 msgid "Branch %s"
-msgstr "Галінка %s"
+msgstr "Галіна %s"
 
 #: kallithea/templates/changelog/changelog.html:310
 msgid "There are no changes yet"
@@ -4891,7 +4834,7 @@
 
 #: kallithea/templates/changelog/changelog_details.html:6
 #: kallithea/templates/changeset/changeset.html:79
-#: kallithea/templates/changeset/diff_block.html:79
+#: kallithea/templates/changeset/diff_block.html:47
 msgid "Added"
 msgstr "Дададзена"
 
@@ -4932,7 +4875,6 @@
 msgstr "Дадаць новы файл"
 
 #: kallithea/templates/changelog/changelog_summary_data.html:90
-#, fuzzy
 msgid "Push new repository"
 msgstr "Адправіць новы рэпазітар"
 
@@ -4954,32 +4896,31 @@
 msgstr ""
 
 #: kallithea/templates/changeset/changeset.html:50
-#: kallithea/templates/changeset/changeset_file_comment.html:37
+#: kallithea/templates/changeset/changeset_file_comment.html:39
 #: kallithea/templates/changeset/changeset_range.html:48
 msgid "Changeset status"
-msgstr "Статут змен"
+msgstr "Статус змен"
 
 #: kallithea/templates/changeset/changeset.html:54
-#: kallithea/templates/changeset/diff_block.html:27
+#: kallithea/templates/changeset/diff_block.html:72
 #: kallithea/templates/files/diff_2way.html:49
 msgid "Raw diff"
 msgstr "Адлюстраваць у фармаце diff"
 
 #: kallithea/templates/changeset/changeset.html:57
 msgid "Patch diff"
-msgstr "Ужыць рознаснае выпраўленне (Patch diff)"
+msgstr "Patch diff"
 
 #: kallithea/templates/changeset/changeset.html:60
-#: kallithea/templates/changeset/diff_block.html:30
+#: kallithea/templates/changeset/diff_block.html:75
 #: kallithea/templates/files/diff_2way.html:52
 msgid "Download diff"
-msgstr "Запампаваць diff"
+msgstr "Спампаваць diff"
 
 #: kallithea/templates/changeset/changeset.html:89
 #: kallithea/templates/changeset/changeset_range.html:88
-#, fuzzy
 msgid "Merge"
-msgstr "звесці"
+msgstr ""
 
 #: kallithea/templates/changeset/changeset.html:123
 msgid "Grafted from:"
@@ -4990,135 +4931,136 @@
 msgstr ""
 
 #: kallithea/templates/changeset/changeset.html:135
-#, fuzzy
 msgid "Replaced by:"
-msgstr "Створана"
+msgstr "Заменена:"
 
 #: kallithea/templates/changeset/changeset.html:149
-#, fuzzy
 msgid "Preceded by:"
-msgstr "Створана"
+msgstr "Замяняе:"
 
 #: kallithea/templates/changeset/changeset.html:166
-#: kallithea/templates/compare/compare_diff.html:54
-#: kallithea/templates/pullrequests/pullrequest_show.html:318
+#: kallithea/templates/compare/compare_diff.html:60
+#: kallithea/templates/pullrequests/pullrequest_show.html:329
 #, python-format
 msgid "%s file changed"
 msgid_plural "%s files changed"
 msgstr[0] "%s файл зменены"
-msgstr[1] "%s файлаў зменена"
-msgstr[2] "%s файла зменена"
+msgstr[1] "%s файлы зменена"
+msgstr[2] "%s файлаў зменена"
 
 #: kallithea/templates/changeset/changeset.html:168
-#: kallithea/templates/compare/compare_diff.html:56
-#: kallithea/templates/pullrequests/pullrequest_show.html:320
+#: kallithea/templates/compare/compare_diff.html:62
+#: kallithea/templates/pullrequests/pullrequest_show.html:331
 #, python-format
 msgid "%s file changed with %s insertions and %s deletions"
 msgid_plural "%s files changed with %s insertions and %s deletions"
 msgstr[0] "%s файл зменены: %s даданне, %s выдаленне"
-msgstr[1] "%s файла зменена: %s даданні, %s выдаленні"
+msgstr[1] "%s файлы зменена: %s даданні, %s выдаленні"
 msgstr[2] "%s файлаў зменена: %s даданняў, %s выдаленняў"
 
 #: kallithea/templates/changeset/changeset.html:182
 #: kallithea/templates/changeset/changeset.html:195
-#: kallithea/templates/pullrequests/pullrequest_show.html:339
-#: kallithea/templates/pullrequests/pullrequest_show.html:363
+#: kallithea/templates/pullrequests/pullrequest_show.html:350
+#: kallithea/templates/pullrequests/pullrequest_show.html:372
 msgid "Show full diff anyway"
 msgstr "Паказаць поўны diff"
 
-#: kallithea/templates/changeset/changeset.html:247
-#: kallithea/templates/changeset/changeset.html:284
-#, fuzzy
+#: kallithea/templates/changeset/changeset.html:231
+#: kallithea/templates/changeset/changeset.html:268
 msgid "No revisions"
-msgstr "няма рэвізій"
+msgstr "Няма рэвізій"
 
 #: kallithea/templates/changeset/changeset_file_comment.html:21
-#, fuzzy
 msgid "on pull request"
-msgstr "Каментар у pull-запыце"
+msgstr "у pull-запыце"
 
 #: kallithea/templates/changeset/changeset_file_comment.html:22
 msgid "No title"
 msgstr "Няма загалоўка"
 
 #: kallithea/templates/changeset/changeset_file_comment.html:24
-#, fuzzy
 msgid "on this changeset"
-msgstr "Няма змен"
-
-#: kallithea/templates/changeset/changeset_file_comment.html:30
+msgstr "на змене"
+
+#: kallithea/templates/changeset/changeset_file_comment.html:31
 msgid "Delete comment?"
 msgstr "Выдаліць каментар?"
 
-#: kallithea/templates/changeset/changeset_file_comment.html:37
-#, fuzzy
+#: kallithea/templates/changeset/changeset_file_comment.html:39
 msgid "Status change"
-msgstr "Апошнія змены"
+msgstr "Змена статусу"
 
 #: kallithea/templates/changeset/changeset_file_comment.html:59
-msgid "Commenting on line {1}."
-msgstr "Каментар да радка {1}."
+msgid "Commenting on line."
+msgstr "Каментар да радка."
 
 #: kallithea/templates/changeset/changeset_file_comment.html:60
-#: kallithea/templates/changeset/changeset_file_comment.html:148
-#, python-format
-msgid "Comments parsed using %s syntax with %s support."
-msgstr "Парсінг каментароў выкананы з выкарыстаннем сінтаксісу %s з падтрымкай %s."
-
-#: kallithea/templates/changeset/changeset_file_comment.html:62
-msgid "Use @username inside this text to notify another user"
-msgstr ""
-"Выкарыстоўвайце @імя_карыстача ў тэксце, каб адправіць абвестку пэўнаму "
-"карыстачу"
-
-#: kallithea/templates/changeset/changeset_file_comment.html:72
-#: kallithea/templates/changeset/changeset_file_comment.html:184
-msgid "Comment preview"
-msgstr "Папярэдні прагляд каментара"
-
-#: kallithea/templates/changeset/changeset_file_comment.html:77
+#, fuzzy
+msgid ""
+"Comments are in plain text. Use @username inside this text to notify "
+"another user."
+msgstr ""
+"Выкарыстоўвайце @імя_карыстальніка ў тэксце, каб адправіць паведамленне пэўнаму "
+"карыстальніку"
+
+#: kallithea/templates/changeset/changeset_file_comment.html:67
+msgid "Set changeset status"
+msgstr "Змяніць статус рэвізіі"
+
+#: kallithea/templates/changeset/changeset_file_comment.html:69
+msgid "Vote for pull request status"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:75
+msgid "No change"
+msgstr "Без змен"
+
+#: kallithea/templates/changeset/changeset_file_comment.html:88
+#, fuzzy
+msgid "Finish pull request"
+msgstr "Каментар у pull-запыце"
+
+#: kallithea/templates/changeset/changeset_file_comment.html:91
+msgid "Close"
+msgstr "Закрыць"
+
+#: kallithea/templates/changeset/changeset_file_comment.html:103
 msgid "Submitting ..."
-msgstr "Ужыванне..."
-
-#: kallithea/templates/changeset/changeset_file_comment.html:80
-#: kallithea/templates/changeset/changeset_file_comment.html:190
+msgstr "Адпраўка..."
+
+#: kallithea/templates/changeset/changeset_file_comment.html:104
 msgid "Comment"
 msgstr "Каментаваць"
 
-#: kallithea/templates/changeset/changeset_file_comment.html:82
-#: kallithea/templates/changeset/changeset_file_comment.html:191
-msgid "Preview"
-msgstr "Прадпрагляд"
-
-#: kallithea/templates/changeset/changeset_file_comment.html:90
+#: kallithea/templates/changeset/changeset_file_comment.html:112
 msgid "You need to be logged in to comment."
 msgstr "Вам неабходна аўтарызавацца, каб пакідаць каментары."
 
-#: kallithea/templates/changeset/changeset_file_comment.html:90
+#: kallithea/templates/changeset/changeset_file_comment.html:112
 msgid "Login now"
-msgstr "Аўтарызавацца цяпер"
-
-#: kallithea/templates/changeset/changeset_file_comment.html:94
+msgstr "Аўтарызавацца зараз"
+
+#: kallithea/templates/changeset/changeset_file_comment.html:116
 msgid "Hide"
 msgstr "Схаваць"
 
-#: kallithea/templates/changeset/changeset_file_comment.html:106
+#: kallithea/templates/changeset/changeset_file_comment.html:128
 #, python-format
 msgid "%d comment"
 msgid_plural "%d comments"
 msgstr[0] "%d каментар"
-msgstr[1] "%d каментара"
+msgstr[1] "%d каментары"
 msgstr[2] "%d каментароў"
 
-#: kallithea/templates/changeset/changeset_file_comment.html:107
+#: kallithea/templates/changeset/changeset_file_comment.html:129
 #, python-format
 msgid "%d inline"
 msgid_plural "%d inline"
-msgstr[0] "%d да радка"
-msgstr[1] "%d да радкоў"
-msgstr[2] "%d да радкоў"
-
-#: kallithea/templates/changeset/changeset_file_comment.html:108
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:130
 #, python-format
 msgid "%d general"
 msgid_plural "%d general"
@@ -5126,29 +5068,6 @@
 msgstr[1] ""
 msgstr[2] ""
 
-#: kallithea/templates/changeset/changeset_file_comment.html:150
-#, fuzzy
-msgid "Use @username inside this text to notify another user."
-msgstr ""
-"Выкарыстоўвайце @імя_карыстача ў тэксце, каб адправіць абвестку пэўнаму "
-"карыстачу"
-
-#: kallithea/templates/changeset/changeset_file_comment.html:157
-msgid "Vote for pull request status"
-msgstr ""
-
-#: kallithea/templates/changeset/changeset_file_comment.html:159
-msgid "Set changeset status"
-msgstr "Змяніць статус рэвізіі"
-
-#: kallithea/templates/changeset/changeset_file_comment.html:163
-msgid "No change"
-msgstr "Без змен"
-
-#: kallithea/templates/changeset/changeset_file_comment.html:176
-msgid "Close"
-msgstr "Зачыніць"
-
 #: kallithea/templates/changeset/changeset_range.html:5
 #, python-format
 msgid "%s Changesets"
@@ -5158,29 +5077,28 @@
 msgid "Files affected"
 msgstr "Закранутыя файлы"
 
-#: kallithea/templates/changeset/diff_block.html:21
+#: kallithea/templates/changeset/diff_block.html:54
+msgid "Deleted"
+msgstr "Выдалены"
+
+#: kallithea/templates/changeset/diff_block.html:57
+msgid "Renamed"
+msgstr "Пераназваны"
+
+#: kallithea/templates/changeset/diff_block.html:66
 #: kallithea/templates/files/diff_2way.html:43
 msgid "Show full diff for this file"
 msgstr "Паказаць поўны diff для гэтага файла"
 
-#: kallithea/templates/changeset/diff_block.html:24
-#: kallithea/templates/changeset/diff_block.html:98
+#: kallithea/templates/changeset/diff_block.html:69
 #: kallithea/templates/files/diff_2way.html:46
 msgid "Show full side-by-side diff for this file"
 msgstr "Паказаць поўны diff для гэтага файла"
 
-#: kallithea/templates/changeset/diff_block.html:38
+#: kallithea/templates/changeset/diff_block.html:83
 msgid "Show inline comments"
 msgstr "Паказаць каментары ў радках"
 
-#: kallithea/templates/changeset/diff_block.html:86
-msgid "Deleted"
-msgstr "Выдалены"
-
-#: kallithea/templates/changeset/diff_block.html:89
-msgid "Renamed"
-msgstr "Пераназваны"
-
 #: kallithea/templates/compare/compare_cs.html:4
 msgid "No changesets"
 msgstr "Няма змен"
@@ -5201,29 +5119,29 @@
 msgid "Position in this list of changesets"
 msgstr ""
 
-#: kallithea/templates/compare/compare_cs.html:76
+#: kallithea/templates/compare/compare_cs.html:85
 msgid "Show merge diff"
 msgstr "Паказаць merge diff"
 
-#: kallithea/templates/compare/compare_cs.html:86
-#: kallithea/templates/pullrequests/pullrequest_show.html:310
+#: kallithea/templates/compare/compare_cs.html:95
+#: kallithea/templates/pullrequests/pullrequest_show.html:321
 msgid "Common ancestor"
 msgstr "Агульны продак"
 
-#: kallithea/templates/compare/compare_cs.html:90
+#: kallithea/templates/compare/compare_cs.html:99
 msgid "No common ancestor found - repositories are unrelated"
 msgstr ""
 
-#: kallithea/templates/compare/compare_cs.html:98
+#: kallithea/templates/compare/compare_cs.html:107
 msgid "is"
 msgstr "адстае на"
 
-#: kallithea/templates/compare/compare_cs.html:99
+#: kallithea/templates/compare/compare_cs.html:108
 #, python-format
 msgid "%s changesets"
 msgstr "%s змен"
 
-#: kallithea/templates/compare/compare_cs.html:100
+#: kallithea/templates/compare/compare_cs.html:109
 msgid "behind"
 msgstr "ад"
 
@@ -5234,29 +5152,29 @@
 msgstr "%s Параўнаць"
 
 #: kallithea/templates/compare/compare_diff.html:13
-#: kallithea/templates/compare/compare_diff.html:35
+#: kallithea/templates/compare/compare_diff.html:41
 msgid "Compare Revisions"
 msgstr "Параўнаць рэвізіі"
 
-#: kallithea/templates/compare/compare_diff.html:33
+#: kallithea/templates/compare/compare_diff.html:39
 msgid "Swap"
 msgstr ""
 
-#: kallithea/templates/compare/compare_diff.html:42
+#: kallithea/templates/compare/compare_diff.html:48
 msgid "Compare revisions, branches, bookmarks, or tags."
 msgstr ""
 
-#: kallithea/templates/compare/compare_diff.html:47
-#: kallithea/templates/pullrequests/pullrequest_show.html:305
+#: kallithea/templates/compare/compare_diff.html:53
+#: kallithea/templates/pullrequests/pullrequest_show.html:316
 #, python-format
 msgid "Showing %s commit"
 msgid_plural "Showing %s commits"
 msgstr[0] "Паказаць %s commit"
-msgstr[1] "Паказаць %s commit'а"
+msgstr[1] "Паказаць %s commit'ы"
 msgstr[2] "Паказаць %s commit'аў"
 
-#: kallithea/templates/compare/compare_diff.html:78
-#: kallithea/templates/compare/compare_diff.html:89
+#: kallithea/templates/compare/compare_diff.html:84
+#: kallithea/templates/compare/compare_diff.html:93
 msgid "Show full diff"
 msgstr "Паказаць поўны diff"
 
@@ -5300,7 +5218,7 @@
 
 #: kallithea/templates/email_templates/changeset_comment.html:12
 msgid "The changeset status was changed to"
-msgstr "Стан набору змен зменены на"
+msgstr "Статус набору змен зменены на"
 
 #: kallithea/templates/email_templates/main.html:6
 msgid "This is an automatic notification. Don't reply to this mail."
@@ -5312,22 +5230,26 @@
 msgstr "Добры дзень, %s"
 
 #: kallithea/templates/email_templates/password_reset.html:6
-#, fuzzy
-#| msgid "We received a request to create a new password for your account."
 msgid "We have received a request to reset the password for your account."
-msgstr "Мы атрымалі запыт на стварэнне новага пароля для вашага акаўнта."
-
-#: kallithea/templates/email_templates/password_reset.html:7
-msgid "To set a new password, click the following link"
+msgstr "Мы атрымалі запыт на скіданне пароля для вашага акаўнта."
+
+#: kallithea/templates/email_templates/password_reset.html:8
+msgid ""
+"This account is however managed outside this system and the password "
+"cannot be changed here."
 msgstr ""
 
 #: kallithea/templates/email_templates/password_reset.html:10
+msgid "To set a new password, click the following link"
+msgstr ""
+
+#: kallithea/templates/email_templates/password_reset.html:13
 msgid ""
 "Should you not be able to use the link above, please type the following "
 "code into the password reset form"
 msgstr ""
 
-#: kallithea/templates/email_templates/password_reset.html:12
+#: kallithea/templates/email_templates/password_reset.html:16
 msgid ""
 "If it weren't you who requested the password reset, just disregard this "
 "message."
@@ -5358,7 +5280,7 @@
 
 #: kallithea/templates/email_templates/registration.html:6
 msgid "View this user here"
-msgstr "Падрабязней пра карыстача"
+msgstr "Падрабязней пра карыстальніка"
 
 #: kallithea/templates/files/diff_2way.html:15
 #, python-format
@@ -5410,14 +5332,15 @@
 msgstr "Стварыць новы файл"
 
 #: kallithea/templates/files/files_add.html:53
-msgid "New file mode"
-msgstr "Рэжым новага файла"
+#, fuzzy
+msgid "New file type"
+msgstr "Тып новага файла"
 
 #: kallithea/templates/files/files_add.html:64
 #: kallithea/templates/files/files_delete.html:43
 #: kallithea/templates/files/files_edit.html:67
 msgid "Commit Changes"
-msgstr "Ужыць змены"
+msgstr "Захаваць змены"
 
 #: kallithea/templates/files/files_browser.html:33
 msgid "Previous revision"
@@ -5429,7 +5352,7 @@
 
 #: kallithea/templates/files/files_browser.html:41
 msgid "Follow current branch"
-msgstr "Адсочваць дадзеную галінку"
+msgstr "Адсочваць дадзеную галіну"
 
 #: kallithea/templates/files/files_browser.html:44
 msgid "Search File List"
@@ -5527,11 +5450,11 @@
 
 #: kallithea/templates/files/files_source.html:41
 msgid "Editing binary files not allowed"
-msgstr "Рэдагаванне бінарных файлаў забаронена"
+msgstr "Рэдагаванне бінарных файлаў забароненае"
 
 #: kallithea/templates/files/files_source.html:44
 msgid "Editing files allowed only when on branch head revision"
-msgstr "Рэдагаванне файлаў дазволена толькі ў HEAD-рэвізіі дадзенай галінкі"
+msgstr "Рэдагаванне файлаў дазволенае толькі ў HEAD-рэвізіі дадзенай галіны"
 
 #: kallithea/templates/files/files_source.html:45
 msgid "Deleting files allowed only when on branch head revision"
@@ -5542,9 +5465,20 @@
 msgid "Binary file (%s)"
 msgstr "Бінарны файл (%s)"
 
-#: kallithea/templates/files/files_source.html:73
-msgid "File is too big to display"
-msgstr "Файл занадта вялікай для адлюстравання"
+#: kallithea/templates/files/files_source.html:74
+#, fuzzy
+msgid "File is too big to display."
+msgstr "Файл занадта вялікі для адлюстравання"
+
+#: kallithea/templates/files/files_source.html:76
+#, fuzzy
+msgid "Show full annotation anyway."
+msgstr "Паказаць поўны diff"
+
+#: kallithea/templates/files/files_source.html:78
+#, fuzzy
+msgid "Show as raw."
+msgstr "Паказаць толькі тэкст"
 
 #: kallithea/templates/files/files_ypjax.html:5
 msgid "annotation"
@@ -5629,11 +5563,11 @@
 
 #: kallithea/templates/forks/forks_data.html:30
 msgid "There are no forks yet"
-msgstr "Форкі яшчэ не створаны"
+msgstr "Форкі яшчэ не створаныя"
 
 #: kallithea/templates/journal/journal.html:21
 msgid "ATOM journal feed"
-msgstr "Стужка часопіса ATOM"
+msgstr "Стужка часопіса Atom"
 
 #: kallithea/templates/journal/journal.html:22
 msgid "RSS journal feed"
@@ -5649,7 +5583,7 @@
 
 #: kallithea/templates/journal/public_journal.html:13
 msgid "ATOM public journal feed"
-msgstr "Агульная стужка часопіса ATOM"
+msgstr "Агульная стужка часопіса Atom"
 
 #: kallithea/templates/journal/public_journal.html:14
 msgid "RSS public journal feed"
@@ -5693,9 +5627,8 @@
 msgstr "Запісы отсуствуют"
 
 #: kallithea/templates/pullrequests/pullrequest_data.html:14
-#, fuzzy
 msgid "Vote"
-msgstr "адклікаць"
+msgstr "Галасаваць"
 
 #: kallithea/templates/pullrequests/pullrequest_data.html:18
 msgid "From"
@@ -5733,19 +5666,19 @@
 msgstr "Пацвердзіце выдаленне гэтага pull-request'а"
 
 #: kallithea/templates/pullrequests/pullrequest_data.html:70
-#, fuzzy, python-format
+#, python-format
 msgid "Confirm again to delete this pull request with %s comments"
-msgstr "Пацвердзіце выдаленне гэтага pull-request'а"
+msgstr "Пацвердзіце выдаленне гэтага pull-запыту з %s каментарамі"
 
 #: kallithea/templates/pullrequests/pullrequest_show.html:6
-#, fuzzy, python-format
+#, python-format
 msgid "%s Pull Request %s"
-msgstr "%s Pull-запыт #%s"
+msgstr "%s зull-запыт %s"
 
 #: kallithea/templates/pullrequests/pullrequest_show.html:10
-#, fuzzy, python-format
+#, python-format
 msgid "Pull request %s from %s#%s"
-msgstr "Pull-запыты №%s ад %s#%s"
+msgstr "Pull-запыт %s ад %s#%s"
 
 #: kallithea/templates/pullrequests/pullrequest_show.html:57
 msgid "Summarize the changes"
@@ -5769,7 +5702,7 @@
 msgid "%d reviewer"
 msgid_plural "%d reviewers"
 msgstr[0] "%d рэцэнзент"
-msgstr[1] "%d рэцэнзента"
+msgstr[1] "%d рэцэнзенты"
 msgstr[2] "%d рэцэнзентаў"
 
 #: kallithea/templates/pullrequests/pullrequest_show.html:99
@@ -5810,56 +5743,63 @@
 msgid "Current revision - no change"
 msgstr ""
 
-#: kallithea/templates/pullrequests/pullrequest_show.html:213
+#: kallithea/templates/pullrequests/pullrequest_show.html:215
+msgid ""
+"Pull requests do not change once created. Select a revision and save to "
+"replace this pull request with a new one."
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:224
 msgid "Pull Request Reviewers"
 msgstr "Рэцэнзенты pull-запытаў"
 
-#: kallithea/templates/pullrequests/pullrequest_show.html:238
+#: kallithea/templates/pullrequests/pullrequest_show.html:249
 msgid "Remove reviewer"
 msgstr "Выдаліць рэцэнзента"
 
-#: kallithea/templates/pullrequests/pullrequest_show.html:250
-msgid "Type name of reviewer to add"
-msgstr ""
-
-#: kallithea/templates/pullrequests/pullrequest_show.html:258
-msgid "Potential Reviewers"
-msgstr "Патэнцыйныя рэцэнзенты"
-
 #: kallithea/templates/pullrequests/pullrequest_show.html:261
+msgid "Type name of reviewer to add"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:269
+msgid "Potential Reviewers"
+msgstr "Патэнцыйныя рэцэнзенты"
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:272
 msgid "Click to add the repository owner as reviewer:"
 msgstr ""
 
-#: kallithea/templates/pullrequests/pullrequest_show.html:284
+#: kallithea/templates/pullrequests/pullrequest_show.html:295
 msgid "Save Changes"
 msgstr "Захаваць змены"
 
-#: kallithea/templates/pullrequests/pullrequest_show.html:285
-msgid "Save as New Pull Request"
-msgstr ""
-
-#: kallithea/templates/pullrequests/pullrequest_show.html:286
+#: kallithea/templates/pullrequests/pullrequest_show.html:296
+#, fuzzy
+msgid "Save Updates as New Pull Request"
+msgstr "Pull-запыт створаны паспяхова"
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:297
 msgid "Cancel Changes"
 msgstr "Адмяніць змены"
 
-#: kallithea/templates/pullrequests/pullrequest_show.html:296
+#: kallithea/templates/pullrequests/pullrequest_show.html:307
 msgid "Pull Request Content"
 msgstr ""
 
 #: kallithea/templates/pullrequests/pullrequest_show_all.html:6
 #, python-format
 msgid "%s Pull Requests"
-msgstr "%s Запыты на занясенне змен"
+msgstr "%s pull-запыты"
 
 #: kallithea/templates/pullrequests/pullrequest_show_all.html:11
 #, python-format
-msgid "Pull Requests from %s'"
-msgstr "Pull-запыты ад %s"
+msgid "Pull Requests from '%s'"
+msgstr "Pull-запыты ад '%s'"
 
 #: kallithea/templates/pullrequests/pullrequest_show_all.html:13
 #, python-format
 msgid "Pull Requests to '%s'"
-msgstr "Pull-запыты для %s"
+msgstr "Pull-запыты да '%s'"
 
 #: kallithea/templates/pullrequests/pullrequest_show_all.html:32
 msgid "Open New Pull Request"
@@ -5913,7 +5853,7 @@
 
 #: kallithea/templates/search/search.html:65
 msgid "File contents"
-msgstr "Змесціва файлаў"
+msgstr "Змест файлаў"
 
 #: kallithea/templates/search/search.html:66
 msgid "Commit messages"
@@ -5927,7 +5867,7 @@
 #: kallithea/templates/search/search_content.html:21
 #: kallithea/templates/search/search_path.html:15
 msgid "Permission denied"
-msgstr "Недастаткова мае рацыю"
+msgstr "Недастаткова правоў"
 
 #: kallithea/templates/summary/statistics.html:4
 #, python-format
@@ -5938,7 +5878,7 @@
 #: kallithea/templates/summary/summary.html:39
 #, python-format
 msgid "%s ATOM feed"
-msgstr "ATOM стужка рэпазітара %s"
+msgstr "Atom стужка рэпазітара %s"
 
 #: kallithea/templates/summary/statistics.html:17
 #: kallithea/templates/summary/summary.html:40
@@ -6022,15 +5962,15 @@
 
 #: kallithea/templates/summary/summary.html:72
 msgid "Clone URL"
-msgstr "Спасылка для кланавання"
+msgstr "URL для кланавання"
 
 #: kallithea/templates/summary/summary.html:78
 msgid "Show by Name"
-msgstr "Паказаць па імі"
+msgstr "Паводле імя"
 
 #: kallithea/templates/summary/summary.html:79
 msgid "Show by ID"
-msgstr "Паказаць па ID"
+msgstr "Паводле ID"
 
 #: kallithea/templates/summary/summary.html:92
 msgid "Trending files"
@@ -6038,7 +5978,7 @@
 
 #: kallithea/templates/summary/summary.html:108
 msgid "Download"
-msgstr "Запампаваць"
+msgstr "Спампаваць"
 
 #: kallithea/templates/summary/summary.html:112
 msgid "There are no downloads yet"
@@ -6046,20 +5986,19 @@
 
 #: kallithea/templates/summary/summary.html:114
 msgid "Downloads are disabled for this repository"
-msgstr "Спампоўка адключана ў гэтым рэпазітары"
+msgstr "Спампоўванне адключанае ў гэтым рэпазітары"
 
 #: kallithea/templates/summary/summary.html:120
 msgid "Download as zip"
-msgstr "Запампаваць у zip"
+msgstr "Спампаваць у zip"
 
 #: kallithea/templates/summary/summary.html:125
 msgid "Check this to download archive with subrepos"
 msgstr "Адзначце для спампоўкі архіва з даччынымі рэпазітарамі"
 
 #: kallithea/templates/summary/summary.html:125
-#, fuzzy
 msgid "With subrepos"
-msgstr "з даччынымі рэпазітарамі"
+msgstr "З даччынымі рэпазітарамі"
 
 #: kallithea/templates/summary/summary.html:156
 msgid "Repository Size"
@@ -6086,12 +6025,12 @@
 #: kallithea/templates/summary/summary.html:293
 #, python-format
 msgid "Download %s as %s"
-msgstr "Запампаваць %s як %s"
+msgstr "Спампаваць %s як %s"
 
 #: kallithea/templates/tags/tags.html:5
 #, python-format
 msgid "%s Tags"
-msgstr "%s Пазнак"
+msgstr "%s Тэгаў"
 
 #: kallithea/templates/tags/tags.html:26
 msgid "Compare Tags"
@@ -6122,19 +6061,16 @@
 #~ msgstr "Рэпазітар %s"
 
 #~ msgid "You can't edit this user"
-#~ msgstr "Вы не можаце рэдагаваць дадзенага карыстача"
+#~ msgstr "Вы не можаце рэдагаваць дадзенага карыстальніка"
 
 #~ msgid "No Files"
 #~ msgstr "Файлаў няма"
 
-#~ msgid ""
-#~ msgstr ""
-
 #~ msgid "Username \"%(username)s\" is forbidden"
 #~ msgstr "Імя \"%(username)s\" адхілена"
 
 #~ msgid "invalid user name"
-#~ msgstr "няслушнае імя карыстача"
+#~ msgstr "няслушнае імя карыстальніка"
 
 #~ msgid "Your account is disabled"
 #~ msgstr "Ваш акаўнт выключаны"
@@ -6185,10 +6121,10 @@
 #~ msgstr "Перазапісаць існыя налады"
 
 #~ msgid "Default IP Whitelist for All Users"
-#~ msgstr "Белы спіс IP для ўсіх карыстачоў"
+#~ msgstr "Белы спіс IP для ўсіх карыстальнікаў"
 
 #~ msgid "Default User Permissions Overview"
-#~ msgstr "Агляд мае рацыю карыстачоў па змаўчанні"
+#~ msgstr "Агляд мае рацыю карыстальнікаў па змаўчанні"
 
 #~ msgid "none"
 #~ msgstr "нічога"
@@ -6313,27 +6249,6 @@
 #~ msgid "reviewer"
 #~ msgstr "рэцэнзент"
 
-#~ msgid ""
-#~ "Your password reset was successful, new"
-#~ " password has been sent to your "
-#~ "email"
-#~ msgstr "Пароль скінуты паспяхова, новы пароль быў адпраўлены на ваш email"
-
-#~ msgid ""
-#~ "[Comment from %(comment_username)s] %(repo_name)s"
-#~ " changeset %(short_id)s on %(branch)s"
-#~ msgstr ""
-
-#~ msgid ""
-#~ "[Added by %(pr_username)s] %(repo_name)s pull"
-#~ " request %(pr_nice_id)s from %(ref)s"
-#~ msgstr ""
-
-#~ msgid ""
-#~ "[Comment from %(comment_username)s] %(repo_name)s"
-#~ " pull request %(pr_nice_id)s from %(ref)s"
-#~ msgstr ""
-
 #~ msgid "Your new password"
 #~ msgstr "Ваш новы пароль"
 
@@ -6351,10 +6266,31 @@
 
 #~ msgid "Please ignore this email if you did not request a new password ."
 #~ msgstr ""
-#~ "Калі ласка, праігнаруйце дадзенае "
-#~ "паведамленне, калі вы не запытвалі новы"
-#~ " пароль."
 
 #~ msgid "Created by"
 #~ msgstr "Створана"
 
+#~ msgid "This pull request can be updated with changes on %s:"
+#~ msgstr "Гэты pull-запыт можа быць абноўлены з %s:"
+
+#~ msgid "Confirm to invalidate repository cache."
+#~ msgstr "Пацвердзіце скід кэша."
+
+#~ msgid "Comments parsed using %s syntax with %s support."
+#~ msgstr ""
+
+#~ msgid "Use @username inside this text to notify another user"
+#~ msgstr ""
+
+#~ msgid "Comment preview"
+#~ msgstr "Папярэдні прагляд каментара"
+
+#~ msgid "Preview"
+#~ msgstr "Прадпрагляд"
+
+#~ msgid "New file mode"
+#~ msgstr "Рэжым новага файла"
+
+#~ msgid "Save as New Pull Request"
+#~ msgstr ""
+
--- a/kallithea/i18n/cs/LC_MESSAGES/kallithea.po	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/i18n/cs/LC_MESSAGES/kallithea.po	Mon Mar 14 00:36:08 2016 +0100
@@ -11,7 +11,7 @@
 "PO-Revision-Date: 2015-11-12 08:51+0000\n"
 "Last-Translator: Michal Čihař <michal@cihar.com>\n"
 "Language-Team: Czech "
-"<https://hosted.weblate.org/projects/kallithea/stable/cs/>\n"
+"<https://hosted.weblate.org/projects/kallithea/kallithea/cs/>\n"
 "Language: cs\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
--- a/kallithea/i18n/de/LC_MESSAGES/kallithea.po	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/i18n/de/LC_MESSAGES/kallithea.po	Mon Mar 14 00:36:08 2016 +0100
@@ -8,16 +8,16 @@
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
 "POT-Creation-Date: 2015-09-08 10:34+0200\n"
-"PO-Revision-Date: 2015-09-08 10:56+0200\n"
+"PO-Revision-Date: 2016-02-08 22:22+0000\n"
 "Last-Translator: Robert Rauch <mail@robertrauch.de>\n"
 "Language-Team: German "
-"<https://hosted.weblate.org/projects/kallithea/stable/de/>\n"
+"<https://hosted.weblate.org/projects/kallithea/kallithea/de/>\n"
 "Language: de\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 2.4-dev\n"
+"X-Generator: Weblate 2.5-dev\n"
 
 #: kallithea/controllers/changelog.py:86
 #: kallithea/controllers/pullrequests.py:241 kallithea/lib/base.py:512
@@ -309,15 +309,15 @@
 
 #: kallithea/controllers/login.py:201
 msgid "Your password reset link was sent"
-msgstr "Ihr Passwort Zurücksetzen link wurde versendet"
+msgstr "Ihr Link um das Passwort zurückzusetzen wurde versendet"
 
 #: kallithea/controllers/login.py:222
 msgid ""
 "Your password reset was successful, new password has been sent to your "
 "email"
 msgstr ""
-"Das Zurücksetzen des Passworted war erfolgreich, ein neues Passwort wurde"
-" an ihre EMail Addresse gesendet"
+"Das Zurücksetzen des Passwortes war erfolgreich, ein neues Passwort wurde an "
+"ihre EMail-Addresse gesendet"
 
 #: kallithea/controllers/pullrequests.py:124
 #, python-format
@@ -336,7 +336,7 @@
 
 #: kallithea/controllers/pullrequests.py:174
 msgid "Peer branches"
-msgstr ""
+msgstr "Branches anderer"
 
 #: kallithea/controllers/pullrequests.py:175 kallithea/model/scm.py:827
 #: kallithea/templates/switch_to_list.html:38
@@ -381,7 +381,7 @@
 
 #: kallithea/controllers/pullrequests.py:411
 msgid "Ancestor didn't change - show diff since previous version:"
-msgstr ""
+msgstr "Vorgänger unverändert - zeige Diff zu lezter Version:"
 
 #: kallithea/controllers/pullrequests.py:418
 #, python-format
@@ -445,7 +445,7 @@
 
 #: kallithea/controllers/pullrequests.py:710
 msgid "No permission to change pull request status"
-msgstr ""
+msgstr "Keine Berechtigung zum Ändern des Pull Request Status"
 
 #: kallithea/controllers/pullrequests.py:715
 msgid "Closing."
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kallithea/i18n/el/LC_MESSAGES/kallithea.po	Mon Mar 14 00:36:08 2016 +0100
@@ -0,0 +1,6017 @@
+# Greek translations for Kallithea.
+# Copyright (C) 2015 Various authors, licensing as GPLv3
+# This file is distributed under the same license as the Kallithea project.
+# Automatically generated, 2015.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Kallithea 0.3\n"
+"Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
+"POT-Creation-Date: 2016-02-22 19:35+0100\n"
+"PO-Revision-Date: 2016-02-10 12:01+0000\n"
+"Last-Translator: Asterios Dimitriou <steve@pci.gr>\n"
+"Language-Team: Greek "
+"<https://hosted.weblate.org/projects/kallithea/kallithea/el/>\n"
+"Language: el\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 2.5-dev\n"
+
+#: kallithea/controllers/changelog.py:85
+#: kallithea/controllers/pullrequests.py:237 kallithea/lib/base.py:515
+msgid "There are no changesets yet"
+msgstr "Δεν υπάρχουν σετ αλλαγών ακόμα"
+
+#: kallithea/controllers/changelog.py:164
+#: kallithea/controllers/admin/permissions.py:61
+#: kallithea/controllers/admin/permissions.py:65
+#: kallithea/controllers/admin/permissions.py:69
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:7
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:104
+#: kallithea/templates/admin/repos/repo_edit_permissions.html:8
+#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:7
+#: kallithea/templates/base/perms_summary.html:14
+msgid "None"
+msgstr "Χωρίς"
+
+#: kallithea/controllers/changelog.py:167 kallithea/controllers/files.py:197
+msgid "(closed)"
+msgstr "(κλειστό)"
+
+#: kallithea/controllers/changeset.py:88
+msgid "Show whitespace"
+msgstr "Εμφάνιση κενού"
+
+#: kallithea/controllers/changeset.py:95 kallithea/controllers/changeset.py:102
+#: kallithea/templates/files/diff_2way.html:55
+msgid "Ignore whitespace"
+msgstr "Αγνόηση κενού"
+
+#: kallithea/controllers/changeset.py:168
+#, python-format
+msgid "Increase diff context to %(num)s lines"
+msgstr "Αύξηση του diff πλαισίου σε %(num)s γραμμές"
+
+#: kallithea/controllers/changeset.py:233 kallithea/controllers/files.py:96
+#: kallithea/controllers/files.py:116 kallithea/controllers/files.py:743
+msgid "Such revision does not exist for this repository"
+msgstr "Δεν υπάρχει τέτοια αναθεώρηση για αυτό το αποθετήριο"
+
+#: kallithea/controllers/compare.py:161 kallithea/templates/base/root.html:41
+msgid "Select changeset"
+msgstr "Επιλογή σετ αλλαγών"
+
+#: kallithea/controllers/compare.py:261
+msgid "Cannot compare repositories without using common ancestor"
+msgstr ""
+"Δεν μπορεί να γίνει σύγκριση αποθετηρίων χωρίς να χρησιμοποιηθεί κοινός "
+"πρόγονος"
+
+#: kallithea/controllers/error.py:71
+msgid "No response"
+msgstr "Χωρίς απόκριση"
+
+#: kallithea/controllers/error.py:72
+msgid "Unknown error"
+msgstr "Άγνωστο λάθος"
+
+#: kallithea/controllers/error.py:100
+msgid "The request could not be understood by the server due to malformed syntax."
+msgstr ""
+"Η αίτηση δεν  μπόρεσε να ερμηνευτεί από τον εξυπηρετητή λόγω κακής "
+"διατύπωσης."
+
+#: kallithea/controllers/error.py:103
+msgid "Unauthorized access to resource"
+msgstr "Ανεξουσιοδοτημένη πρόσβαση στον πόρο"
+
+#: kallithea/controllers/error.py:105
+msgid "You don't have permission to view this page"
+msgstr "Δεν έχετε άδεια για να εμφανίσετε αυτή τη σελίδα"
+
+#: kallithea/controllers/error.py:107
+msgid "The resource could not be found"
+msgstr "Ο πόρος δεν μπορεί να βρεθεί"
+
+#: kallithea/controllers/error.py:109
+msgid ""
+"The server encountered an unexpected condition which prevented it from "
+"fulfilling the request."
+msgstr ""
+"Ο εξυπηρετητής συνάντησε μια απρόσμενη κατάσταση που τον απέτρεψαν να "
+"πραγματοποιήσει την αίτηση."
+
+#: kallithea/controllers/feed.py:55
+#, python-format
+msgid "Changes on %s repository"
+msgstr "Αλλαγές στο αποθετήριο %s"
+
+#: kallithea/controllers/feed.py:56
+#, python-format
+msgid "%s %s feed"
+msgstr "%s %s τροφοδοσία"
+
+#: kallithea/controllers/feed.py:87
+#: kallithea/templates/changeset/changeset.html:182
+#: kallithea/templates/changeset/changeset.html:195
+#: kallithea/templates/compare/compare_diff.html:84
+#: kallithea/templates/compare/compare_diff.html:93
+#: kallithea/templates/pullrequests/pullrequest_show.html:350
+#: kallithea/templates/pullrequests/pullrequest_show.html:372
+msgid "Changeset was too big and was cut off..."
+msgstr "Το σετ αλλαγών ήταν πολύ μεγάλο και περικόπηκε..."
+
+#: kallithea/controllers/feed.py:91
+#, python-format
+msgid "%s committed on %s"
+msgstr "%s συνέβαλε στο %s"
+
+#: kallithea/controllers/files.py:91
+msgid "Click here to add new file"
+msgstr "Κλικ εδώ για προθήκη νέου αρχείου"
+
+#: kallithea/controllers/files.py:92
+#, python-format
+msgid "There are no files yet. %s"
+msgstr "Δεν υπάρχουν αρχεία ακόμα. %s"
+
+#: kallithea/controllers/files.py:194
+#, python-format
+msgid "%s at %s"
+msgstr "%s την %s"
+
+#: kallithea/controllers/files.py:306 kallithea/controllers/files.py:366
+#: kallithea/controllers/files.py:433
+#, python-format
+msgid "This repository has been locked by %s on %s"
+msgstr "Το αποθετήριο κλειδώθηκε από %s την %s"
+
+#: kallithea/controllers/files.py:318
+#, fuzzy
+#| msgid "You can only delete files with revision being a valid branch "
+msgid "You can only delete files with revision being a valid branch"
+msgstr ""
+"Μπορείτε να διαγράψετε μόνο αρχεία σε αναθεώρηση που βρίσκονται σε έγκυρη"
+" διακλάδωση "
+
+#: kallithea/controllers/files.py:329
+#, python-format
+msgid "Deleted file %s via Kallithea"
+msgstr "Διαγραφή αρχείου %s μέσω του Kallithea"
+
+#: kallithea/controllers/files.py:351
+#, python-format
+msgid "Successfully deleted file %s"
+msgstr "Επιτυχής διαγραφή αρχείου %s"
+
+#: kallithea/controllers/files.py:355 kallithea/controllers/files.py:421
+#: kallithea/controllers/files.py:502
+msgid "Error occurred during commit"
+msgstr "Συνέβη λάθος κατά το commit"
+
+#: kallithea/controllers/files.py:378
+#, fuzzy
+#| msgid "You can only edit files with revision being a valid branch "
+msgid "You can only edit files with revision being a valid branch"
+msgstr ""
+"Μπορείτε να επεξεργαστείτε μόνο αρχεία σε αναθεώρηση που βρίσκονται σε "
+"έγκυρη διακλάδωση "
+
+#: kallithea/controllers/files.py:392
+#, python-format
+msgid "Edited file %s via Kallithea"
+msgstr "Επεξεργασία αρχείου %s μέσω του Kallithea"
+
+#: kallithea/controllers/files.py:408
+msgid "No changes"
+msgstr "Καμία αλλαγή"
+
+#: kallithea/controllers/files.py:417 kallithea/controllers/files.py:491
+#, python-format
+msgid "Successfully committed to %s"
+msgstr "Επιτυχής παράδοση σε %s"
+
+#: kallithea/controllers/files.py:444
+msgid "Added file via Kallithea"
+msgstr "Προσθήκη αρχείου μέσω Kallithea"
+
+#: kallithea/controllers/files.py:465
+msgid "No content"
+msgstr "Χωρίς περιεχόμενο"
+
+#: kallithea/controllers/files.py:469
+msgid "No filename"
+msgstr "Χωρίς όνομα αρχείου"
+
+#: kallithea/controllers/files.py:494
+msgid "Location must be relative path and must not contain .. in path"
+msgstr ""
+"Η τοποθεσία πρέπει να είναι σχετική διαδρομή και να μην περιέχει .. μέσα "
+"της"
+
+#: kallithea/controllers/files.py:527
+msgid "Downloads disabled"
+msgstr "Οι μεταφορτώσεις απενεργοποιήθηκαν"
+
+#: kallithea/controllers/files.py:538
+#, python-format
+msgid "Unknown revision %s"
+msgstr "Άγνωστη αναθεώρηση %s"
+
+#: kallithea/controllers/files.py:540
+msgid "Empty repository"
+msgstr "Άδειο αποθετήριο"
+
+#: kallithea/controllers/files.py:542
+msgid "Unknown archive type"
+msgstr "Άγνωστος τύπος αρχειοθέτησης"
+
+#: kallithea/controllers/files.py:772
+#: kallithea/templates/changeset/changeset_range.html:9
+#: kallithea/templates/email_templates/pull_request.html:15
+#: kallithea/templates/pullrequests/pullrequest.html:97
+msgid "Changesets"
+msgstr "Σετ αλλαγών"
+
+#: kallithea/controllers/files.py:773 kallithea/controllers/pullrequests.py:175
+#: kallithea/model/scm.py:820 kallithea/templates/switch_to_list.html:3
+#: kallithea/templates/branches/branches.html:10
+msgid "Branches"
+msgstr "Κλάδοι"
+
+#: kallithea/controllers/files.py:774 kallithea/controllers/pullrequests.py:176
+#: kallithea/model/scm.py:831 kallithea/templates/switch_to_list.html:25
+#: kallithea/templates/tags/tags.html:10
+msgid "Tags"
+msgstr "Ετικέτες"
+
+#: kallithea/controllers/forks.py:186
+#, python-format
+msgid "An error occurred during repository forking %s"
+msgstr "Συνέβει ένα λάθος κατά την διακλάδωση του αποθετηρίου %s"
+
+#: kallithea/controllers/home.py:84
+msgid "Groups"
+msgstr "Ομάδες"
+
+#: kallithea/controllers/home.py:89
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:106
+#: kallithea/templates/admin/repos/repo_add.html:12
+#: kallithea/templates/admin/repos/repo_add.html:16
+#: kallithea/templates/admin/repos/repos.html:9
+#: kallithea/templates/admin/users/user_edit_advanced.html:6
+#: kallithea/templates/base/base.html:60 kallithea/templates/base/base.html:77
+#: kallithea/templates/base/base.html:124
+#: kallithea/templates/base/base.html:479
+#: kallithea/templates/base/base.html:653
+msgid "Repositories"
+msgstr "Αποθετήρια"
+
+#: kallithea/controllers/home.py:130
+#: kallithea/templates/files/files_add.html:32
+#: kallithea/templates/files/files_delete.html:23
+#: kallithea/templates/files/files_edit.html:32
+msgid "Branch"
+msgstr "Κλάδος"
+
+#: kallithea/controllers/home.py:136 kallithea/templates/switch_to_list.html:16
+msgid "Closed Branches"
+msgstr ""
+
+#: kallithea/controllers/home.py:142
+msgid "Tag"
+msgstr "Ετικέτα"
+
+#: kallithea/controllers/home.py:148
+msgid "Bookmark"
+msgstr "Σελιδοδείκτης"
+
+#: kallithea/controllers/journal.py:111 kallithea/controllers/journal.py:153
+#: kallithea/templates/journal/public_journal.html:4
+#: kallithea/templates/journal/public_journal.html:21
+msgid "Public Journal"
+msgstr "Δημόσιο Ημερολόγιο"
+
+#: kallithea/controllers/journal.py:115 kallithea/controllers/journal.py:157
+#: kallithea/templates/base/base.html:306
+#: kallithea/templates/journal/journal.html:4
+#: kallithea/templates/journal/journal.html:12
+msgid "Journal"
+msgstr "Ημερολόγιο"
+
+#: kallithea/controllers/login.py:144 kallithea/controllers/login.py:190
+msgid "Bad captcha"
+msgstr "Λάθος captcha"
+
+#: kallithea/controllers/login.py:150
+msgid "You have successfully registered into Kallithea"
+msgstr "Εγγραφήκατε επιτυχώς στο Kallithea"
+
+#: kallithea/controllers/login.py:195
+msgid "A password reset confirmation code has been sent"
+msgstr "Στάλθηκε ένας κωδικός επιβεβαίωσης επαναφοράς του συνθηματικού"
+
+#: kallithea/controllers/login.py:244
+msgid "Invalid password reset token"
+msgstr "Άκυρο τεκμήριο (token) επαναφοράς του συνθηματικού"
+
+#: kallithea/controllers/login.py:249
+#: kallithea/controllers/admin/my_account.py:167
+msgid "Successfully updated password"
+msgstr "Το συνθηματικό ενημερώθηκε επιτυχώς"
+
+#: kallithea/controllers/pullrequests.py:123
+#, python-format
+msgid "%s (closed)"
+msgstr "%s (κλειστό)"
+
+#: kallithea/controllers/pullrequests.py:151
+#: kallithea/templates/changeset/changeset.html:12
+#: kallithea/templates/email_templates/changeset_comment.html:17
+msgid "Changeset"
+msgstr "Σετ αλλαγών"
+
+#: kallithea/controllers/pullrequests.py:172
+msgid "Special"
+msgstr "Ειδικός"
+
+#: kallithea/controllers/pullrequests.py:173
+msgid "Peer branches"
+msgstr "Ομότιμοι κλάδοι"
+
+#: kallithea/controllers/pullrequests.py:174 kallithea/model/scm.py:826
+#: kallithea/templates/switch_to_list.html:38
+#: kallithea/templates/bookmarks/bookmarks.html:10
+msgid "Bookmarks"
+msgstr "Σελιδοδείκτες"
+
+#: kallithea/controllers/pullrequests.py:309
+#, python-format
+msgid "Error creating pull request: %s"
+msgstr "Λάθος στη δημιουργία αιτήματος έλξης - pull request: %s"
+
+#: kallithea/controllers/pullrequests.py:355
+#: kallithea/controllers/pullrequests.py:502
+msgid "No description"
+msgstr "Χωρίς περιγραφή"
+
+#: kallithea/controllers/pullrequests.py:362
+msgid "Successfully opened new pull request"
+msgstr "Ένα νέο αίτημα έλξης (pull request) δημιουργήθηκε επιτυχώς"
+
+#: kallithea/controllers/pullrequests.py:365
+#: kallithea/controllers/pullrequests.py:452
+#: kallithea/controllers/pullrequests.py:509
+#, python-format
+msgid "Invalid reviewer \"%s\" specified"
+msgstr "Καθορίστηκε άκυρος σχολιαστής \"%s\""
+
+#: kallithea/controllers/pullrequests.py:368
+#: kallithea/controllers/pullrequests.py:455
+msgid "Error occurred while creating pull request"
+msgstr "Λάθος κατά τη δημιουργία αιτήματος έλξης (pull request)"
+
+#: kallithea/controllers/pullrequests.py:400
+msgid "Missing changesets since the previous pull request:"
+msgstr "Ελλιπή σετ αλλαγών από την προηγούμενη αίτηση έλξης:"
+
+#: kallithea/controllers/pullrequests.py:407
+#, python-format
+msgid "New changesets on %s %s since the previous pull request:"
+msgstr "Καινούρια σετ αλλαγών στα %s %s από την προηγούμενη αίτηση έλξης:"
+
+#: kallithea/controllers/pullrequests.py:414
+msgid "Ancestor didn't change - show diff since previous version:"
+msgstr "Το γονικό δεν άλλαξε - εμφάνισε τις διαφορές από την προηγούμενη έκδοση:"
+
+#: kallithea/controllers/pullrequests.py:421
+#, python-format
+msgid ""
+"This pull request is based on another %s revision and there is no simple "
+"diff."
+msgstr ""
+"Αυτή η αίτηση έλξης είναι βασισμένη σε μία άλλη %s αναθεώρηση και δεν "
+"υπάρχει ένα απλό diff."
+
+#: kallithea/controllers/pullrequests.py:423
+#, python-format
+msgid "No changes found on %s %s since previous version."
+msgstr "Δεν βρέθηκαν αλλαγές στο %s %s από την προηγούμενη έκδοση."
+
+#: kallithea/controllers/pullrequests.py:461
+#, python-format
+msgid "Closed, replaced by %s ."
+msgstr "Κλειστό, αντικαταστάθηκε από %s."
+
+#: kallithea/controllers/pullrequests.py:469
+msgid "Pull request update created"
+msgstr "Δημιουργήθηκε ενημέρωση αιτήματος έλξης"
+
+#: kallithea/controllers/pullrequests.py:513
+msgid "Pull request updated"
+msgstr "Ενημερώθηκε η αίτηση έλξης"
+
+#: kallithea/controllers/pullrequests.py:528
+msgid "Successfully deleted pull request"
+msgstr "Επιτυχής διαγραφή αιτήματος έλξης"
+
+#: kallithea/controllers/pullrequests.py:594
+#, python-format
+msgid "This pull request has already been merged to %s."
+msgstr "Το αίτημα έλξης έχει ήδη συγχωνευτεί με το %s."
+
+#: kallithea/controllers/pullrequests.py:596
+msgid "This pull request has been closed and can not be updated."
+msgstr "Αυτό το αίτημα έλξης έχει κλείσει και δεν μπορεί να ενημερωθεί."
+
+#: kallithea/controllers/pullrequests.py:614
+#, python-format
+msgid "The following changes are available on %s:"
+msgstr ""
+
+#: kallithea/controllers/pullrequests.py:618
+msgid "No changesets found for updating this pull request."
+msgstr "Δεν βρέθηκαν σετ αλλαγών για ενημέρωση αυτού του αιτήματος έλξης."
+
+#: kallithea/controllers/pullrequests.py:626
+#, python-format
+msgid "Note: Branch %s has another head: %s."
+msgstr "Σημείωση: Ο κλάδος %s έχει άλλη κεφαλή (head): %s."
+
+#: kallithea/controllers/pullrequests.py:632
+msgid "Git pull requests don't support updates yet."
+msgstr "Αιτήματα έλξης του git δεν υποστηρίζουν ακόμα ενημερώσεις."
+
+#: kallithea/controllers/pullrequests.py:724
+msgid "No permission to change pull request status"
+msgstr "Χωρίς δικαιώματα αλλαγής της κατάστασης του αιτήματος έλξης"
+
+#: kallithea/controllers/pullrequests.py:735
+#, fuzzy, python-format
+#| msgid "Successfully deleted pull request"
+msgid "Successfully deleted pull request %s"
+msgstr "Επιτυχής διαγραφή αιτήματος έλξης"
+
+#: kallithea/controllers/pullrequests.py:745
+msgid "Closing."
+msgstr "Κλείνει."
+
+#: kallithea/controllers/search.py:135
+msgid "Invalid search query. Try quoting it."
+msgstr "Άκυρο αίτημα αναζήτησης. Δοκιμάστε με εισαγωγικά."
+
+#: kallithea/controllers/search.py:140
+msgid "There is no index to search in. Please run whoosh indexer"
+msgstr ""
+"Δεν υπάρχει ευρετήριο για την αναζήτηση. Παρακαλώ τρέξτε τον whoosh για "
+"την δημιουργία του"
+
+#: kallithea/controllers/search.py:144
+msgid "An error occurred during search operation."
+msgstr "Ένα λάθος συνέβη κατά την διαδικασία αναζήτησης."
+
+#: kallithea/controllers/summary.py:181
+#: kallithea/templates/summary/summary.html:384
+msgid "No data ready yet"
+msgstr "Δεν υπάρχουν ακόμα έτοιμα δεδομένα"
+
+#: kallithea/controllers/summary.py:184
+#: kallithea/templates/summary/summary.html:98
+msgid "Statistics are disabled for this repository"
+msgstr "Τα στατιστικά είναι απενεργοποιημένα για αυτό το αποθετήριο"
+
+#: kallithea/controllers/admin/auth_settings.py:135
+msgid "Auth settings updated successfully"
+msgstr "Οι ρυθμίσεις εξουσιοδότησης ενημερώθηκαν επιτυχώς"
+
+#: kallithea/controllers/admin/auth_settings.py:146
+msgid "error occurred during update of auth settings"
+msgstr "παρουσιάστηκε βλάβη κατά την ενημέρωση των ρυθμίσεων εξουσιοδότησης"
+
+#: kallithea/controllers/admin/defaults.py:97
+msgid "Default settings updated successfully"
+msgstr "Οι προεπιλεγμένες ρυθμίσεις ενημερώθηκαν επιτυχώς"
+
+#: kallithea/controllers/admin/defaults.py:112
+msgid "Error occurred during update of defaults"
+msgstr "Συνέβη μία βλάβη κατά την ενημέρωση των προεπιλογών"
+
+#: kallithea/controllers/admin/gists.py:58
+#: kallithea/controllers/admin/my_account.py:243
+#: kallithea/controllers/admin/users.py:284
+msgid "Forever"
+msgstr "Πάντα"
+
+#: kallithea/controllers/admin/gists.py:59
+#: kallithea/controllers/admin/my_account.py:244
+#: kallithea/controllers/admin/users.py:285
+msgid "5 minutes"
+msgstr "5 λεπτά"
+
+#: kallithea/controllers/admin/gists.py:60
+#: kallithea/controllers/admin/my_account.py:245
+#: kallithea/controllers/admin/users.py:286
+msgid "1 hour"
+msgstr "1 ώρα"
+
+#: kallithea/controllers/admin/gists.py:61
+#: kallithea/controllers/admin/my_account.py:246
+#: kallithea/controllers/admin/users.py:287
+msgid "1 day"
+msgstr "1 ημέρα"
+
+#: kallithea/controllers/admin/gists.py:62
+#: kallithea/controllers/admin/my_account.py:247
+#: kallithea/controllers/admin/users.py:288
+msgid "1 month"
+msgstr "1 μήνας"
+
+#: kallithea/controllers/admin/gists.py:66
+#: kallithea/controllers/admin/my_account.py:249
+#: kallithea/controllers/admin/users.py:290
+msgid "Lifetime"
+msgstr "Διάρκεια ζωής"
+
+#: kallithea/controllers/admin/gists.py:145
+msgid "Error occurred during gist creation"
+msgstr "Συνέβη μία βλάβη κατά τη δημιουργία του gist"
+
+#: kallithea/controllers/admin/gists.py:183
+#, python-format
+msgid "Deleted gist %s"
+msgstr "Διαγράφηκε το gist %s"
+
+#: kallithea/controllers/admin/gists.py:232
+msgid "Unmodified"
+msgstr "Mη τροποποιημένo"
+
+#: kallithea/controllers/admin/gists.py:261
+msgid "Successfully updated gist content"
+msgstr "Το περιεχόμενο του gist ενημερώθηκε επιτυχώς"
+
+#: kallithea/controllers/admin/gists.py:266
+msgid "Successfully updated gist data"
+msgstr "Τα δεδομένα του gist ενημερώθηκαν επιτυχώς"
+
+#: kallithea/controllers/admin/gists.py:269
+#, python-format
+msgid "Error occurred during update of gist %s"
+msgstr "Σφάλμα συνέβη κατά την ενημέρωση του gist %s"
+
+#: kallithea/controllers/admin/my_account.py:70 kallithea/model/user.py:215
+#: kallithea/model/user.py:237
+msgid "You can't edit this user since it's crucial for entire application"
+msgstr ""
+"Δεν μπορείτε να επεξεργαστείτε αυτόν το χρήστη καθώς είναι κρίσιμος για "
+"όλη την εφαρμογή"
+
+#: kallithea/controllers/admin/my_account.py:129
+msgid "Your account was updated successfully"
+msgstr "Ο λογαριασμός σας ενημερώθηκε επιτυχώς"
+
+#: kallithea/controllers/admin/my_account.py:144
+#: kallithea/controllers/admin/users.py:201
+#, python-format
+msgid "Error occurred during update of user %s"
+msgstr "Συνέβη ένα σφάλμα κατά την ενημέρωση του χρήστη %s"
+
+#: kallithea/controllers/admin/my_account.py:178
+msgid "Error occurred during update of user password"
+msgstr "Συνέβη ένα σφάλμα κατά την ενημέρωση του κωδικού του χρήστη"
+
+#: kallithea/controllers/admin/my_account.py:220
+#: kallithea/controllers/admin/users.py:414
+#, python-format
+msgid "Added email %s to user"
+msgstr "Προστέθηκε το email %s στον χρήστη"
+
+#: kallithea/controllers/admin/my_account.py:226
+#: kallithea/controllers/admin/users.py:420
+msgid "An error occurred during email saving"
+msgstr "Συνέβη ένα σφάλμα κατά την αποθήκευση του email"
+
+#: kallithea/controllers/admin/my_account.py:235
+#: kallithea/controllers/admin/users.py:432
+msgid "Removed email from user"
+msgstr "Αφαιρέθηκε το email από τον χρήστη"
+
+#: kallithea/controllers/admin/my_account.py:259
+#: kallithea/controllers/admin/users.py:307
+msgid "API key successfully created"
+msgstr "Το API κλειδί δημιουργήθηκε επιτυχώς"
+
+#: kallithea/controllers/admin/my_account.py:271
+#: kallithea/controllers/admin/users.py:320
+msgid "API key successfully reset"
+msgstr "Το API κλειδί επαναφέρθηκε επιτυχώς"
+
+#: kallithea/controllers/admin/my_account.py:275
+#: kallithea/controllers/admin/users.py:324
+msgid "API key successfully deleted"
+msgstr "Το API κλειδί διαγράφηκε επιτυχώς"
+
+#: kallithea/controllers/admin/permissions.py:62
+#: kallithea/controllers/admin/permissions.py:66
+#: kallithea/controllers/admin/permissions.py:70
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:8
+#: kallithea/templates/admin/repos/repo_edit_permissions.html:9
+#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:8
+#: kallithea/templates/base/perms_summary.html:15
+msgid "Read"
+msgstr "Ανάγνωση"
+
+#: kallithea/controllers/admin/permissions.py:63
+#: kallithea/controllers/admin/permissions.py:67
+#: kallithea/controllers/admin/permissions.py:71
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:9
+#: kallithea/templates/admin/repos/repo_edit_permissions.html:10
+#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:9
+#: kallithea/templates/base/perms_summary.html:16
+msgid "Write"
+msgstr "Εγγραφή"
+
+#: kallithea/controllers/admin/permissions.py:64
+#: kallithea/controllers/admin/permissions.py:68
+#: kallithea/controllers/admin/permissions.py:72
+#: kallithea/templates/admin/auth/auth_settings.html:9
+#: kallithea/templates/admin/defaults/defaults.html:9
+#: kallithea/templates/admin/permissions/permissions.html:9
+#: kallithea/templates/admin/repo_groups/repo_group_add.html:9
+#: kallithea/templates/admin/repo_groups/repo_group_edit.html:9
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:10
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:47
+#: kallithea/templates/admin/repo_groups/repo_groups.html:10
+#: kallithea/templates/admin/repos/repo_add.html:10
+#: kallithea/templates/admin/repos/repo_add.html:14
+#: kallithea/templates/admin/repos/repo_edit_permissions.html:11
+#: kallithea/templates/admin/repos/repos.html:9
+#: kallithea/templates/admin/settings/settings.html:9
+#: kallithea/templates/admin/user_groups/user_group_add.html:8
+#: kallithea/templates/admin/user_groups/user_group_edit.html:9
+#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:10
+#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:47
+#: kallithea/templates/admin/user_groups/user_groups.html:10
+#: kallithea/templates/admin/users/user_add.html:8
+#: kallithea/templates/admin/users/user_edit.html:9
+#: kallithea/templates/admin/users/user_edit_profile.html:105
+#: kallithea/templates/admin/users/users.html:10
+#: kallithea/templates/admin/users/users.html:55
+#: kallithea/templates/base/base.html:336
+#: kallithea/templates/base/base.html:337
+#: kallithea/templates/base/base.html:343
+#: kallithea/templates/base/base.html:344
+#: kallithea/templates/base/perms_summary.html:17
+msgid "Admin"
+msgstr "Διαχειριστής"
+
+#: kallithea/controllers/admin/permissions.py:75
+#: kallithea/controllers/admin/permissions.py:86
+#: kallithea/controllers/admin/permissions.py:91
+#: kallithea/controllers/admin/permissions.py:94
+#: kallithea/controllers/admin/permissions.py:97
+#: kallithea/controllers/admin/permissions.py:100
+#: kallithea/templates/admin/auth/auth_settings.html:40
+msgid "Disabled"
+msgstr "Απενεργοποιημένο"
+
+#: kallithea/controllers/admin/permissions.py:77
+msgid "Allowed with manual account activation"
+msgstr "Επιτρέπεται με χειροποίητη ενεργοποίηση του λογαριασμού"
+
+#: kallithea/controllers/admin/permissions.py:79
+msgid "Allowed with automatic account activation"
+msgstr "Επιτρέπεται με αυτόματη ενεργοποίηση του λογαριασμού"
+
+#: kallithea/controllers/admin/permissions.py:82
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1439
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1485
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1542
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1543
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1564
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1603
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1655
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1682 kallithea/model/db.py:1707
+msgid "Manual activation of external account"
+msgstr "Χειροποίητη ενεργοποίηση εξωτερικού λογαριασμού"
+
+#: kallithea/controllers/admin/permissions.py:83
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1440
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1486
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1543
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1544
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1565
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1604
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1656
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1683 kallithea/model/db.py:1708
+msgid "Automatic activation of external account"
+msgstr "Αυτόματη ενεργοποίηση εξωτερικού λογαριασμού"
+
+#: kallithea/controllers/admin/permissions.py:87
+#: kallithea/controllers/admin/permissions.py:90
+#: kallithea/controllers/admin/permissions.py:95
+#: kallithea/controllers/admin/permissions.py:98
+#: kallithea/controllers/admin/permissions.py:101
+#: kallithea/templates/admin/auth/auth_settings.html:40
+msgid "Enabled"
+msgstr "Ενεργό"
+
+#: kallithea/controllers/admin/permissions.py:124
+msgid "Global permissions updated successfully"
+msgstr ""
+
+#: kallithea/controllers/admin/permissions.py:139
+msgid "Error occurred during update of permissions"
+msgstr ""
+
+#: kallithea/controllers/admin/repo_groups.py:187
+#, python-format
+msgid "Error occurred during creation of repository group %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repo_groups.py:192
+#, python-format
+msgid "Created repository group %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repo_groups.py:249
+#, python-format
+msgid "Updated repository group %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repo_groups.py:265
+#, python-format
+msgid "Error occurred during update of repository group %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repo_groups.py:283
+#, python-format
+msgid "This group contains %s repositories and cannot be deleted"
+msgstr ""
+
+#: kallithea/controllers/admin/repo_groups.py:290
+#, python-format
+msgid "This group contains %s subgroups and cannot be deleted"
+msgstr ""
+
+#: kallithea/controllers/admin/repo_groups.py:296
+#, python-format
+msgid "Removed repository group %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repo_groups.py:301
+#, python-format
+msgid "Error occurred during deletion of repository group %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repo_groups.py:404
+#: kallithea/controllers/admin/repo_groups.py:439
+#: kallithea/controllers/admin/user_groups.py:340
+msgid "Cannot revoke permission for yourself as admin"
+msgstr ""
+
+#: kallithea/controllers/admin/repo_groups.py:419
+msgid "Repository group permissions updated"
+msgstr ""
+
+#: kallithea/controllers/admin/repo_groups.py:456
+#: kallithea/controllers/admin/repos.py:397
+#: kallithea/controllers/admin/user_groups.py:352
+msgid "An error occurred during revoking of permission"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:151
+#, python-format
+msgid "Error creating repository %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:212
+#, python-format
+msgid "Created repository %s from %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:221
+#, python-format
+msgid "Forked repository %s as %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:224
+#, python-format
+msgid "Created repository %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:261
+#, python-format
+msgid "Repository %s updated successfully"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:282
+#, python-format
+msgid "Error occurred during update of repository %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:309
+#, python-format
+msgid "Detached %s forks"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:312
+#, python-format
+msgid "Deleted %s forks"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:317
+#, python-format
+msgid "Deleted repository %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:320
+#, python-format
+msgid "Cannot delete repository %s which still has forks"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:325
+#, python-format
+msgid "An error occurred during deletion of %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:373
+msgid "Repository permissions updated"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:429
+msgid "An error occurred during creation of field"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:443
+msgid "An error occurred during removal of field"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:459
+msgid "-- Not a fork --"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:490
+msgid "Updated repository visibility in public journal"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:494
+msgid "An error occurred during setting this repository in public journal"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:511
+msgid "Nothing"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:513
+#, python-format
+msgid "Marked repository %s as fork of %s"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:520
+msgid "An error occurred during this operation"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:536
+#: kallithea/controllers/admin/repos.py:563
+msgid "Repository has been locked"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:539
+#: kallithea/controllers/admin/repos.py:560
+msgid "Repository has been unlocked"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:542
+#: kallithea/controllers/admin/repos.py:567
+msgid "An error occurred during unlocking"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:581
+msgid "Cache invalidation successful"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:585
+msgid "An error occurred during cache invalidation"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:600
+msgid "Pulled from remote location"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:603
+msgid "An error occurred during pull from remote location"
+msgstr ""
+
+#: kallithea/controllers/admin/repos.py:636
+msgid "An error occurred during deletion of repository stats"
+msgstr ""
+
+#: kallithea/controllers/admin/settings.py:141
+msgid "Updated VCS settings"
+msgstr ""
+
+#: kallithea/controllers/admin/settings.py:145
+msgid ""
+"Unable to activate hgsubversion support. The \"hgsubversion\" library is "
+"missing"
+msgstr ""
+
+#: kallithea/controllers/admin/settings.py:151
+#: kallithea/controllers/admin/settings.py:248
+msgid "Error occurred while updating application settings"
+msgstr ""
+
+#: kallithea/controllers/admin/settings.py:187
+#, python-format
+msgid "Repositories successfully rescanned. Added: %s. Removed: %s."
+msgstr ""
+
+#: kallithea/controllers/admin/settings.py:244
+msgid "Updated application settings"
+msgstr ""
+
+#: kallithea/controllers/admin/settings.py:301
+msgid "Updated visualisation settings"
+msgstr ""
+
+#: kallithea/controllers/admin/settings.py:306
+msgid "Error occurred during updating visualisation settings"
+msgstr ""
+
+#: kallithea/controllers/admin/settings.py:332
+msgid "Please enter email address"
+msgstr ""
+
+#: kallithea/controllers/admin/settings.py:347
+msgid "Send email task created"
+msgstr ""
+
+#: kallithea/controllers/admin/settings.py:378
+msgid "Added new hook"
+msgstr ""
+
+#: kallithea/controllers/admin/settings.py:392
+msgid "Updated hooks"
+msgstr ""
+
+#: kallithea/controllers/admin/settings.py:396
+msgid "Error occurred during hook creation"
+msgstr ""
+
+#: kallithea/controllers/admin/settings.py:422
+msgid "Whoosh reindex task scheduled"
+msgstr ""
+
+#: kallithea/controllers/admin/user_groups.py:150
+#, python-format
+msgid "Created user group %s"
+msgstr ""
+
+#: kallithea/controllers/admin/user_groups.py:163
+#, python-format
+msgid "Error occurred during creation of user group %s"
+msgstr ""
+
+#: kallithea/controllers/admin/user_groups.py:201
+#, python-format
+msgid "Updated user group %s"
+msgstr ""
+
+#: kallithea/controllers/admin/user_groups.py:224
+#, python-format
+msgid "Error occurred during update of user group %s"
+msgstr ""
+
+#: kallithea/controllers/admin/user_groups.py:242
+msgid "Successfully deleted user group"
+msgstr ""
+
+#: kallithea/controllers/admin/user_groups.py:247
+msgid "An error occurred during deletion of user group"
+msgstr ""
+
+#: kallithea/controllers/admin/user_groups.py:314
+msgid "Target group cannot be the same"
+msgstr ""
+
+#: kallithea/controllers/admin/user_groups.py:320
+msgid "User group permissions updated"
+msgstr ""
+
+#: kallithea/controllers/admin/user_groups.py:440
+#: kallithea/controllers/admin/users.py:383
+msgid "Updated permissions"
+msgstr ""
+
+#: kallithea/controllers/admin/user_groups.py:444
+#: kallithea/controllers/admin/users.py:387
+msgid "An error occurred during permissions saving"
+msgstr ""
+
+#: kallithea/controllers/admin/users.py:133
+#, python-format
+msgid "Created user %s"
+msgstr ""
+
+#: kallithea/controllers/admin/users.py:148
+#, python-format
+msgid "Error occurred during creation of user %s"
+msgstr ""
+
+#: kallithea/controllers/admin/users.py:181
+msgid "User updated successfully"
+msgstr ""
+
+#: kallithea/controllers/admin/users.py:217
+msgid "Successfully deleted user"
+msgstr ""
+
+#: kallithea/controllers/admin/users.py:222
+msgid "An error occurred during deletion of user"
+msgstr ""
+
+#: kallithea/controllers/admin/users.py:235
+msgid "The default user cannot be edited"
+msgstr ""
+
+#: kallithea/controllers/admin/users.py:462
+#, python-format
+msgid "Added IP address %s to user whitelist"
+msgstr ""
+
+#: kallithea/controllers/admin/users.py:468
+msgid "An error occurred while adding IP address"
+msgstr ""
+
+#: kallithea/controllers/admin/users.py:482
+msgid "Removed IP address from user whitelist"
+msgstr ""
+
+#: kallithea/lib/auth.py:737
+#, python-format
+msgid "IP %s not allowed"
+msgstr ""
+
+#: kallithea/lib/auth.py:750
+msgid "Invalid API key"
+msgstr ""
+
+#: kallithea/lib/auth.py:768
+msgid "CSRF token leak has been detected - all form tokens have been expired"
+msgstr ""
+
+#: kallithea/lib/auth.py:813
+msgid "You need to be a registered user to perform this action"
+msgstr ""
+
+#: kallithea/lib/auth.py:843
+msgid "You need to be signed in to view this page"
+msgstr ""
+
+#: kallithea/lib/base.py:493
+msgid "Repository not found in the filesystem"
+msgstr ""
+
+#: kallithea/lib/base.py:519 kallithea/lib/helpers.py:618
+msgid "Changeset not found"
+msgstr ""
+
+#: kallithea/lib/diffs.py:66
+msgid "Binary file"
+msgstr ""
+
+#: kallithea/lib/diffs.py:82
+msgid "Changeset was too big and was cut off, use diff menu to display this diff"
+msgstr ""
+
+#: kallithea/lib/diffs.py:92
+msgid "No changes detected"
+msgstr ""
+
+#: kallithea/lib/helpers.py:605
+#, python-format
+msgid "Deleted branch: %s"
+msgstr ""
+
+#: kallithea/lib/helpers.py:607
+#, python-format
+msgid "Created tag: %s"
+msgstr ""
+
+#: kallithea/lib/helpers.py:667
+#, python-format
+msgid "Show all combined changesets %s->%s"
+msgstr ""
+
+#: kallithea/lib/helpers.py:673
+msgid "Compare view"
+msgstr ""
+
+#: kallithea/lib/helpers.py:692
+msgid "and"
+msgstr ""
+
+#: kallithea/lib/helpers.py:693
+#, python-format
+msgid "%s more"
+msgstr ""
+
+#: kallithea/lib/helpers.py:694 kallithea/templates/changelog/changelog.html:44
+msgid "revisions"
+msgstr ""
+
+#: kallithea/lib/helpers.py:718
+#, python-format
+msgid "Fork name %s"
+msgstr ""
+
+#: kallithea/lib/helpers.py:738
+#, python-format
+msgid "Pull request %s"
+msgstr ""
+
+#: kallithea/lib/helpers.py:748
+msgid "[deleted] repository"
+msgstr ""
+
+#: kallithea/lib/helpers.py:750 kallithea/lib/helpers.py:762
+msgid "[created] repository"
+msgstr ""
+
+#: kallithea/lib/helpers.py:752
+msgid "[created] repository as fork"
+msgstr ""
+
+#: kallithea/lib/helpers.py:754 kallithea/lib/helpers.py:764
+msgid "[forked] repository"
+msgstr ""
+
+#: kallithea/lib/helpers.py:756 kallithea/lib/helpers.py:766
+msgid "[updated] repository"
+msgstr ""
+
+#: kallithea/lib/helpers.py:758
+msgid "[downloaded] archive from repository"
+msgstr ""
+
+#: kallithea/lib/helpers.py:760
+msgid "[delete] repository"
+msgstr ""
+
+#: kallithea/lib/helpers.py:768
+msgid "[created] user"
+msgstr ""
+
+#: kallithea/lib/helpers.py:770
+msgid "[updated] user"
+msgstr ""
+
+#: kallithea/lib/helpers.py:772
+msgid "[created] user group"
+msgstr ""
+
+#: kallithea/lib/helpers.py:774
+msgid "[updated] user group"
+msgstr ""
+
+#: kallithea/lib/helpers.py:776
+msgid "[commented] on revision in repository"
+msgstr ""
+
+#: kallithea/lib/helpers.py:778
+msgid "[commented] on pull request for"
+msgstr ""
+
+#: kallithea/lib/helpers.py:780
+msgid "[closed] pull request for"
+msgstr ""
+
+#: kallithea/lib/helpers.py:782
+msgid "[pushed] into"
+msgstr ""
+
+#: kallithea/lib/helpers.py:784
+msgid "[committed via Kallithea] into repository"
+msgstr ""
+
+#: kallithea/lib/helpers.py:786
+msgid "[pulled from remote] into repository"
+msgstr ""
+
+#: kallithea/lib/helpers.py:788
+msgid "[pulled] from"
+msgstr ""
+
+#: kallithea/lib/helpers.py:790
+msgid "[started following] repository"
+msgstr ""
+
+#: kallithea/lib/helpers.py:792
+msgid "[stopped following] repository"
+msgstr ""
+
+#: kallithea/lib/helpers.py:1119
+#, python-format
+msgid " and %s more"
+msgstr ""
+
+#: kallithea/lib/helpers.py:1123
+#: kallithea/templates/compare/compare_diff.html:71
+#: kallithea/templates/pullrequests/pullrequest_show.html:337
+msgid "No files"
+msgstr ""
+
+#: kallithea/lib/helpers.py:1189
+msgid "new file"
+msgstr ""
+
+#: kallithea/lib/helpers.py:1192
+msgid "mod"
+msgstr ""
+
+#: kallithea/lib/helpers.py:1195
+msgid "del"
+msgstr ""
+
+#: kallithea/lib/helpers.py:1198
+msgid "rename"
+msgstr ""
+
+#: kallithea/lib/helpers.py:1203
+msgid "chmod"
+msgstr ""
+
+#: kallithea/lib/helpers.py:1464
+#, python-format
+msgid ""
+"%s repository is not mapped to db perhaps it was created or renamed from "
+"the filesystem please run the application again in order to rescan "
+"repositories"
+msgstr ""
+
+#: kallithea/lib/utils2.py:434
+#, python-format
+msgid "%d year"
+msgid_plural "%d years"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/lib/utils2.py:435
+#, python-format
+msgid "%d month"
+msgid_plural "%d months"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/lib/utils2.py:436
+#, python-format
+msgid "%d day"
+msgid_plural "%d days"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/lib/utils2.py:437
+#, python-format
+msgid "%d hour"
+msgid_plural "%d hours"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/lib/utils2.py:438
+#, python-format
+msgid "%d minute"
+msgid_plural "%d minutes"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/lib/utils2.py:439
+#, python-format
+msgid "%d second"
+msgid_plural "%d seconds"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/lib/utils2.py:455
+#, python-format
+msgid "in %s"
+msgstr ""
+
+#: kallithea/lib/utils2.py:457
+#, python-format
+msgid "%s ago"
+msgstr ""
+
+#: kallithea/lib/utils2.py:459
+#, python-format
+msgid "in %s and %s"
+msgstr ""
+
+#: kallithea/lib/utils2.py:462
+#, python-format
+msgid "%s and %s ago"
+msgstr ""
+
+#: kallithea/lib/utils2.py:465
+msgid "just now"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1163
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1182
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1303
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1388
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1408
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1454
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1511
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1512
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1533
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1572
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1622
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1649
+msgid "Repository no access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1164
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1183
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1304
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1389
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1409
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1455
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1512
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1513
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1534
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1573
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1623
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1650
+msgid "Repository read access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1165
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1184
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1305
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1390
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1410
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1456
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1513
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1514
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1535
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1574
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1624
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1651
+msgid "Repository write access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1166
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1185
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1306
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1391
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1411
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1457
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1514
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1515
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1536
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1575
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1625
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1652
+msgid "Repository admin access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1168
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1187
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1308
+msgid "Repository Group no access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1169
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1188
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1309
+msgid "Repository Group read access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1170
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1189
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1310
+msgid "Repository Group write access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1171
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1190
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1311
+msgid "Repository Group admin access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1173
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1192
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1313
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1398
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1406
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1452
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1509
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1510
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1531
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1570
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1620
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1647 kallithea/model/db.py:1671
+msgid "Kallithea Administrator"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1174
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1193
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1314
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1399
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1429
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1475
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1532
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1533
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1554
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1593
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1643
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1670
+msgid "Repository creation disabled"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1175
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1194
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1315
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1400
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1430
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1476
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1533
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1534
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1555
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1594
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1644
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1671
+msgid "Repository creation enabled"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1176
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1195
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1316
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1401
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1432
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1478
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1535
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1536
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1557
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1596
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1648
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1675
+msgid "Repository forking disabled"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1177
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1196
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1317
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1402
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1433
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1479
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1536
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1537
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1558
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1597
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1649
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1676
+msgid "Repository forking enabled"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1178
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1197
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1318
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1403
+msgid "Register disabled"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1179
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1198
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1319
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1404
+msgid "Register new user with Kallithea with manual activation"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1182
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1201
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1322
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1407
+msgid "Register new user with Kallithea with auto activation"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1623
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1650
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1763
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1838
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1934
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1980
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:2040
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:2041
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:2062
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:2101
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:2154
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:2200
+msgid "Not Reviewed"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1624
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1651
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1764
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1839
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1935
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1981
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:2041
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:2042
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:2063
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:2102
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:2155
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:2201 kallithea/model/db.py:2238
+msgid "Approved"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1625
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1652
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1765
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1840
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1936
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1982
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:2042
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:2043
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:2064
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:2103
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:2156
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:2202 kallithea/model/db.py:2239
+msgid "Rejected"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_4_0.py:1626
+#: kallithea/lib/dbmigrate/schema/db_1_5_0.py:1653
+#: kallithea/lib/dbmigrate/schema/db_1_5_2.py:1766
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1841
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1937
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1983
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:2043
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:2044
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:2065
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:2104
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:2157
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:2203
+msgid "Under Review"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1252
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1270
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1300
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1357
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1358
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1379
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1418
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1471
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1498 kallithea/model/db.py:1520
+msgid "top level"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1393
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1413
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1459
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1516
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1517
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1538
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1577
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1627
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1654
+msgid "Repository group no access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1394
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1414
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1460
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1517
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1518
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1539
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1578
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1628
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1655
+msgid "Repository group read access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1395
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1415
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1461
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1518
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1519
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1540
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1579
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1629
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1656
+msgid "Repository group write access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_6_0.py:1396
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1416
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1462
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1519
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1520
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1541
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1580
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1630
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1657
+msgid "Repository group admin access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1418
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1464
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1521
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1522
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1543
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1582
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1632
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1659
+msgid "User group no access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1419
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1465
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1522
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1523
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1544
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1583
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1633
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1660
+msgid "User group read access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1420
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1466
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1523
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1524
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1545
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1584
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1634
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1661
+msgid "User group write access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1421
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1467
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1524
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1525
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1546
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1585
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1635
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1662
+msgid "User group admin access"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1423
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1469
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1526
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1527
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1548
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1587
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1637
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1664
+msgid "Repository Group creation disabled"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1424
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1470
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1527
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1528
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1549
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1588
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1638
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1665
+msgid "Repository Group creation enabled"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1426
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1472
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1529
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1530
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1551
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1590
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1640
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1667
+msgid "User Group creation disabled"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1427
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1473
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1530
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1531
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1552
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1591
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1641
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1668
+msgid "User Group creation enabled"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1435
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1481
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1538
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1539
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1560
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1599
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1651
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1678 kallithea/model/db.py:1703
+msgid "Registration disabled"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1436
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1482
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1539
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1540
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1561
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1600
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1652
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1679
+msgid "User Registration with manual account activation"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_1_7_0.py:1437
+#: kallithea/lib/dbmigrate/schema/db_1_8_0.py:1483
+#: kallithea/lib/dbmigrate/schema/db_2_0_0.py:1540
+#: kallithea/lib/dbmigrate/schema/db_2_0_1.py:1541
+#: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1562
+#: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1601
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1653
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1680
+msgid "User Registration with automatic account activation"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1645
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1672 kallithea/model/db.py:1697
+msgid "Repository creation enabled with write permission to a repository group"
+msgstr ""
+
+#: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1646
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1673 kallithea/model/db.py:1698
+msgid "Repository creation disabled with write permission to a repository group"
+msgstr ""
+
+#: kallithea/model/comment.py:72
+#, python-format
+msgid "on line %s"
+msgstr ""
+
+#: kallithea/model/comment.py:217 kallithea/model/pull_request.py:170
+msgid "[Mention]"
+msgstr ""
+
+#: kallithea/model/db.py:1673
+msgid "Default user has no access to new repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1674
+msgid "Default user has read access to new repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1675
+msgid "Default user has write access to new repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1676
+msgid "Default user has admin access to new repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1678
+msgid "Default user has no access to new repository groups"
+msgstr ""
+
+#: kallithea/model/db.py:1679
+msgid "Default user has read access to new repository groups"
+msgstr ""
+
+#: kallithea/model/db.py:1680
+msgid "Default user has write access to new repository groups"
+msgstr ""
+
+#: kallithea/model/db.py:1681
+msgid "Default user has admin access to new repository groups"
+msgstr ""
+
+#: kallithea/model/db.py:1683
+msgid "Default user has no access to new user groups"
+msgstr ""
+
+#: kallithea/model/db.py:1684
+msgid "Default user has read access to new user groups"
+msgstr ""
+
+#: kallithea/model/db.py:1685
+msgid "Default user has write access to new user groups"
+msgstr ""
+
+#: kallithea/model/db.py:1686
+msgid "Default user has admin access to new user groups"
+msgstr ""
+
+#: kallithea/model/db.py:1688
+msgid "Only admins can create repository groups"
+msgstr ""
+
+#: kallithea/model/db.py:1689
+msgid "Non-admins can create repository groups"
+msgstr ""
+
+#: kallithea/model/db.py:1691
+msgid "Only admins can create user groups"
+msgstr ""
+
+#: kallithea/model/db.py:1692
+msgid "Non-admins can create user groups"
+msgstr ""
+
+#: kallithea/model/db.py:1694
+msgid "Only admins can create top level repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1695
+msgid "Non-admins can create top level repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1700
+msgid "Only admins can fork repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1701
+msgid "Non-admins can fork repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1704
+msgid "User registration with manual account activation"
+msgstr ""
+
+#: kallithea/model/db.py:1705
+msgid "User registration with automatic account activation"
+msgstr ""
+
+#: kallithea/model/db.py:2237
+msgid "Not reviewed"
+msgstr ""
+
+#: kallithea/model/db.py:2240
+msgid "Under review"
+msgstr ""
+
+#: kallithea/model/forms.py:57
+msgid "Please enter a login"
+msgstr ""
+
+#: kallithea/model/forms.py:58
+#, python-format
+msgid "Enter a value %(min)i characters long or more"
+msgstr ""
+
+#: kallithea/model/forms.py:66
+msgid "Please enter a password"
+msgstr ""
+
+#: kallithea/model/forms.py:67
+#, python-format
+msgid "Enter %(min)i characters or more"
+msgstr ""
+
+#: kallithea/model/forms.py:165
+msgid "Name must not contain only digits"
+msgstr ""
+
+#: kallithea/model/notification.py:254
+#, python-format
+msgid "%(user)s commented on changeset %(age)s"
+msgstr ""
+
+#: kallithea/model/notification.py:255
+#, python-format
+msgid "%(user)s sent message %(age)s"
+msgstr ""
+
+#: kallithea/model/notification.py:256
+#, python-format
+msgid "%(user)s mentioned you %(age)s"
+msgstr ""
+
+#: kallithea/model/notification.py:257
+#, python-format
+msgid "%(user)s registered in Kallithea %(age)s"
+msgstr ""
+
+#: kallithea/model/notification.py:258
+#, python-format
+msgid "%(user)s opened new pull request %(age)s"
+msgstr ""
+
+#: kallithea/model/notification.py:259
+#, python-format
+msgid "%(user)s commented on pull request %(age)s"
+msgstr ""
+
+#: kallithea/model/notification.py:266
+#, python-format
+msgid "%(user)s commented on changeset at %(when)s"
+msgstr ""
+
+#: kallithea/model/notification.py:267
+#, python-format
+msgid "%(user)s sent message at %(when)s"
+msgstr ""
+
+#: kallithea/model/notification.py:268
+#, python-format
+msgid "%(user)s mentioned you at %(when)s"
+msgstr ""
+
+#: kallithea/model/notification.py:269
+#, python-format
+msgid "%(user)s registered in Kallithea at %(when)s"
+msgstr ""
+
+#: kallithea/model/notification.py:270
+#, python-format
+msgid "%(user)s opened new pull request at %(when)s"
+msgstr ""
+
+#: kallithea/model/notification.py:271
+#, python-format
+msgid "%(user)s commented on pull request at %(when)s"
+msgstr ""
+
+#: kallithea/model/notification.py:302
+#, python-format
+msgid "[Comment] %(repo_name)s changeset %(short_id)s on %(branch)s"
+msgstr ""
+
+#: kallithea/model/notification.py:305
+#, python-format
+msgid "New user %(new_username)s registered"
+msgstr ""
+
+#: kallithea/model/notification.py:307
+#, python-format
+msgid "[Added] %(repo_name)s pull request %(pr_nice_id)s from %(ref)s"
+msgstr ""
+
+#: kallithea/model/notification.py:308
+#, python-format
+msgid "[Comment] %(repo_name)s pull request %(pr_nice_id)s from %(ref)s"
+msgstr ""
+
+#: kallithea/model/notification.py:321
+msgid "Closing"
+msgstr ""
+
+#: kallithea/model/pull_request.py:137
+#, python-format
+msgid "%(user)s wants you to review pull request %(pr_nice_id)s: %(pr_title)s"
+msgstr ""
+
+#: kallithea/model/scm.py:812
+msgid "latest tip"
+msgstr ""
+
+#: kallithea/model/user.py:192
+msgid "New user registration"
+msgstr ""
+
+#: kallithea/model/user.py:256
+msgid "You can't remove this user since it is crucial for the entire application"
+msgstr ""
+
+#: kallithea/model/user.py:261
+#, python-format
+msgid ""
+"User \"%s\" still owns %s repositories and cannot be removed. Switch "
+"owners or remove those repositories: %s"
+msgstr ""
+
+#: kallithea/model/user.py:266
+#, python-format
+msgid ""
+"User \"%s\" still owns %s repository groups and cannot be removed. Switch"
+" owners or remove those repository groups: %s"
+msgstr ""
+
+#: kallithea/model/user.py:273
+#, python-format
+msgid ""
+"User \"%s\" still owns %s user groups and cannot be removed. Switch "
+"owners or remove those user groups: %s"
+msgstr ""
+
+#: kallithea/model/user.py:368
+msgid "Password reset link"
+msgstr ""
+
+#: kallithea/model/user.py:418
+msgid "Password reset notification"
+msgstr ""
+
+#: kallithea/model/user.py:419
+#, python-format
+msgid ""
+"The password to your account %s has been changed using password reset "
+"form."
+msgstr ""
+
+#: kallithea/model/validators.py:77 kallithea/model/validators.py:78
+msgid "Value cannot be an empty list"
+msgstr ""
+
+#: kallithea/model/validators.py:96
+#, python-format
+msgid "Username \"%(username)s\" already exists"
+msgstr ""
+
+#: kallithea/model/validators.py:98
+#, python-format
+msgid "Username \"%(username)s\" cannot be used"
+msgstr ""
+
+#: kallithea/model/validators.py:100
+msgid ""
+"Username may only contain alphanumeric characters underscores, periods or"
+" dashes and must begin with an alphanumeric character or underscore"
+msgstr ""
+
+#: kallithea/model/validators.py:127
+msgid "The input is not valid"
+msgstr ""
+
+#: kallithea/model/validators.py:134
+#, python-format
+msgid "Username %(username)s is not valid"
+msgstr ""
+
+#: kallithea/model/validators.py:154
+msgid "Invalid user group name"
+msgstr ""
+
+#: kallithea/model/validators.py:155
+#, python-format
+msgid "User group \"%(usergroup)s\" already exists"
+msgstr ""
+
+#: kallithea/model/validators.py:157
+msgid ""
+"user group name may only contain alphanumeric characters underscores, "
+"periods or dashes and must begin with alphanumeric character"
+msgstr ""
+
+#: kallithea/model/validators.py:197
+msgid "Cannot assign this group as parent"
+msgstr ""
+
+#: kallithea/model/validators.py:198
+#, python-format
+msgid "Group \"%(group_name)s\" already exists"
+msgstr ""
+
+#: kallithea/model/validators.py:200
+#, python-format
+msgid "Repository with name \"%(group_name)s\" already exists"
+msgstr ""
+
+#: kallithea/model/validators.py:258
+msgid "Invalid characters (non-ascii) in password"
+msgstr ""
+
+#: kallithea/model/validators.py:273
+msgid "Invalid old password"
+msgstr ""
+
+#: kallithea/model/validators.py:289
+msgid "Passwords do not match"
+msgstr ""
+
+#: kallithea/model/validators.py:304
+msgid "Invalid username or password"
+msgstr ""
+
+#: kallithea/model/validators.py:335
+msgid "Token mismatch"
+msgstr ""
+
+#: kallithea/model/validators.py:351
+#, python-format
+msgid "Repository name %(repo)s is not allowed"
+msgstr ""
+
+#: kallithea/model/validators.py:353
+#, python-format
+msgid "Repository named %(repo)s already exists"
+msgstr ""
+
+#: kallithea/model/validators.py:354
+#, python-format
+msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
+msgstr ""
+
+#: kallithea/model/validators.py:356
+#, python-format
+msgid "Repository group with name \"%(repo)s\" already exists"
+msgstr ""
+
+#: kallithea/model/validators.py:470
+msgid "Invalid repository URL"
+msgstr ""
+
+#: kallithea/model/validators.py:471
+msgid ""
+"Invalid repository URL. It must be a valid http, https, ssh, svn+http or "
+"svn+https URL"
+msgstr ""
+
+#: kallithea/model/validators.py:496
+msgid "Fork has to be the same type as parent"
+msgstr ""
+
+#: kallithea/model/validators.py:511
+msgid "You don't have permissions to create repository in this group"
+msgstr ""
+
+#: kallithea/model/validators.py:513
+msgid "no permission to create repository in root location"
+msgstr ""
+
+#: kallithea/model/validators.py:563
+msgid "You don't have permissions to create a group in this location"
+msgstr ""
+
+#: kallithea/model/validators.py:604
+msgid "This username or user group name is not valid"
+msgstr ""
+
+#: kallithea/model/validators.py:697
+msgid "This is not a valid path"
+msgstr ""
+
+#: kallithea/model/validators.py:714
+msgid "This email address is already in use"
+msgstr ""
+
+#: kallithea/model/validators.py:734
+#, python-format
+msgid "Email address \"%(email)s\" not found"
+msgstr ""
+
+#: kallithea/model/validators.py:771
+msgid ""
+"The LDAP Login attribute of the CN must be specified - this is the name "
+"of the attribute that is equivalent to \"username\""
+msgstr ""
+
+#: kallithea/model/validators.py:783
+msgid "Please enter a valid IPv4 or IPv6 address"
+msgstr ""
+
+#: kallithea/model/validators.py:784
+#, python-format
+msgid "The network size (bits) must be within the range of 0-32 (not %(bits)r)"
+msgstr ""
+
+#: kallithea/model/validators.py:817
+msgid "Key name can only consist of letters, underscore, dash or numbers"
+msgstr ""
+
+#: kallithea/model/validators.py:831
+msgid "Filename cannot be inside a directory"
+msgstr ""
+
+#: kallithea/model/validators.py:847
+#, python-format
+msgid "Plugins %(loaded)s and %(next_to_load)s both export the same name"
+msgstr ""
+
+#: kallithea/templates/about.html:4 kallithea/templates/about.html:17
+msgid "About"
+msgstr ""
+
+#: kallithea/templates/index.html:5
+msgid "Dashboard"
+msgstr ""
+
+#: kallithea/templates/index_base.html:6
+#: kallithea/templates/admin/my_account/my_account_repos.html:3
+#: kallithea/templates/admin/my_account/my_account_watched.html:3
+#: kallithea/templates/admin/repo_groups/repo_groups.html:9
+#: kallithea/templates/admin/repos/repos.html:9
+#: kallithea/templates/admin/user_groups/user_groups.html:9
+#: kallithea/templates/admin/users/users.html:9
+#: kallithea/templates/bookmarks/bookmarks.html:9
+#: kallithea/templates/branches/branches.html:9
+#: kallithea/templates/journal/journal.html:9
+#: kallithea/templates/journal/journal.html:48
+#: kallithea/templates/journal/journal.html:49
+#: kallithea/templates/tags/tags.html:9
+msgid "quick filter..."
+msgstr ""
+
+#: kallithea/templates/index_base.html:6
+msgid "repositories"
+msgstr ""
+
+#: kallithea/templates/index_base.html:20
+#: kallithea/templates/index_base.html:25
+#: kallithea/templates/admin/repos/repo_add.html:5
+#: kallithea/templates/admin/repos/repo_add.html:19
+#: kallithea/templates/admin/repos/repos.html:22
+msgid "Add Repository"
+msgstr ""
+
+#: kallithea/templates/index_base.html:22
+#: kallithea/templates/index_base.html:27
+#: kallithea/templates/admin/repo_groups/repo_group_add.html:5
+#: kallithea/templates/admin/repo_groups/repo_group_add.html:13
+#: kallithea/templates/admin/repo_groups/repo_groups.html:26
+msgid "Add Repository Group"
+msgstr ""
+
+#: kallithea/templates/index_base.html:32
+msgid "You have admin right to this group, and can edit it"
+msgstr ""
+
+#: kallithea/templates/index_base.html:32
+msgid "Edit Repository Group"
+msgstr ""
+
+#: kallithea/templates/index_base.html:45
+msgid "Group Name"
+msgstr ""
+
+#: kallithea/templates/index_base.html:46
+#: kallithea/templates/index_base.html:127
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:64
+#: kallithea/templates/admin/repo_groups/repo_group_add.html:42
+#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:17
+#: kallithea/templates/admin/repo_groups/repo_groups.html:47
+#: kallithea/templates/admin/repos/repo_add_base.html:28
+#: kallithea/templates/admin/repos/repo_edit_settings.html:65
+#: kallithea/templates/admin/repos/repos.html:48
+#: kallithea/templates/admin/user_groups/user_group_add.html:40
+#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:15
+#: kallithea/templates/admin/user_groups/user_groups.html:47
+#: kallithea/templates/admin/users/user_edit_api_keys.html:64
+#: kallithea/templates/email_templates/changeset_comment.html:18
+#: kallithea/templates/email_templates/pull_request.html:12
+#: kallithea/templates/forks/fork.html:38
+#: kallithea/templates/pullrequests/pullrequest.html:40
+#: kallithea/templates/pullrequests/pullrequest_show.html:38
+#: kallithea/templates/pullrequests/pullrequest_show.html:63
+#: kallithea/templates/summary/summary.html:85
+msgid "Description"
+msgstr ""
+
+#: kallithea/templates/index_base.html:125
+#: kallithea/templates/admin/my_account/my_account_repos.html:46
+#: kallithea/templates/admin/my_account/my_account_watched.html:46
+#: kallithea/templates/admin/repo_groups/repo_groups.html:46
+#: kallithea/templates/admin/repos/repo_add_base.html:9
+#: kallithea/templates/admin/repos/repo_edit_settings.html:7
+#: kallithea/templates/admin/repos/repos.html:47
+#: kallithea/templates/admin/user_groups/user_groups.html:46
+#: kallithea/templates/base/perms_summary.html:53
+#: kallithea/templates/bookmarks/bookmarks.html:49
+#: kallithea/templates/bookmarks/bookmarks_data.html:7
+#: kallithea/templates/branches/branches.html:49
+#: kallithea/templates/branches/branches_data.html:7
+#: kallithea/templates/files/files_browser.html:60
+#: kallithea/templates/journal/journal.html:187
+#: kallithea/templates/journal/journal.html:278
+#: kallithea/templates/tags/tags.html:49
+#: kallithea/templates/tags/tags_data.html:7
+msgid "Name"
+msgstr ""
+
+#: kallithea/templates/index_base.html:128
+msgid "Last Change"
+msgstr ""
+
+#: kallithea/templates/index_base.html:130
+#: kallithea/templates/admin/my_account/my_account_repos.html:48
+#: kallithea/templates/admin/my_account/my_account_watched.html:48
+#: kallithea/templates/admin/repos/repos.html:49
+#: kallithea/templates/journal/journal.html:189
+#: kallithea/templates/journal/journal.html:280
+msgid "Tip"
+msgstr ""
+
+#: kallithea/templates/index_base.html:132
+#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:10
+#: kallithea/templates/admin/repo_groups/repo_groups.html:49
+#: kallithea/templates/admin/repos/repo_edit_settings.html:53
+#: kallithea/templates/admin/repos/repos.html:50
+#: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:8
+#: kallithea/templates/admin/user_groups/user_groups.html:50
+#: kallithea/templates/pullrequests/pullrequest_data.html:16
+#: kallithea/templates/pullrequests/pullrequest_show.html:156
+#: kallithea/templates/pullrequests/pullrequest_show.html:244
+#: kallithea/templates/summary/summary.html:134
+msgid "Owner"
+msgstr ""
+
+#: kallithea/templates/index_base.html:140
+#: kallithea/templates/admin/my_account/my_account_repos.html:57
+#: kallithea/templates/admin/my_account/my_account_watched.html:57
+#: kallithea/templates/base/root.html:43
+#: kallithea/templates/bookmarks/bookmarks.html:79
+#: kallithea/templates/branches/branches.html:79
+#: kallithea/templates/journal/journal.html:198
+#: kallithea/templates/journal/journal.html:289
+#: kallithea/templates/tags/tags.html:79
+msgid "Click to sort ascending"
+msgstr ""
+
+#: kallithea/templates/index_base.html:141
+#: kallithea/templates/admin/my_account/my_account_repos.html:58
+#: kallithea/templates/admin/my_account/my_account_watched.html:58
+#: kallithea/templates/base/root.html:44
+#: kallithea/templates/bookmarks/bookmarks.html:80
+#: kallithea/templates/branches/branches.html:80
+#: kallithea/templates/journal/journal.html:199
+#: kallithea/templates/journal/journal.html:290
+#: kallithea/templates/tags/tags.html:80
+msgid "Click to sort descending"
+msgstr ""
+
+#: kallithea/templates/index_base.html:142
+msgid "No repositories found."
+msgstr ""
+
+#: kallithea/templates/index_base.html:143
+#: kallithea/templates/admin/my_account/my_account_repos.html:60
+#: kallithea/templates/admin/my_account/my_account_watched.html:60
+#: kallithea/templates/base/root.html:46
+#: kallithea/templates/bookmarks/bookmarks.html:82
+#: kallithea/templates/branches/branches.html:82
+#: kallithea/templates/journal/journal.html:201
+#: kallithea/templates/journal/journal.html:292
+#: kallithea/templates/tags/tags.html:82
+msgid "Data error."
+msgstr ""
+
+#: kallithea/templates/index_base.html:144
+#: kallithea/templates/admin/my_account/my_account_repos.html:61
+#: kallithea/templates/admin/my_account/my_account_watched.html:61
+#: kallithea/templates/base/root.html:47
+#: kallithea/templates/bookmarks/bookmarks.html:83
+#: kallithea/templates/branches/branches.html:83
+#: kallithea/templates/journal/journal.html:202
+#: kallithea/templates/journal/journal.html:293
+#: kallithea/templates/tags/tags.html:83
+msgid "Loading..."
+msgstr ""
+
+#: kallithea/templates/login.html:5 kallithea/templates/login.html:15
+#: kallithea/templates/base/base.html:414
+msgid "Log In"
+msgstr ""
+
+#: kallithea/templates/login.html:13
+#, python-format
+msgid "Log In to %s"
+msgstr ""
+
+#: kallithea/templates/login.html:26 kallithea/templates/register.html:24
+#: kallithea/templates/admin/admin_log.html:5
+#: kallithea/templates/admin/my_account/my_account_profile.html:25
+#: kallithea/templates/admin/users/user_add.html:32
+#: kallithea/templates/admin/users/user_edit_profile.html:24
+#: kallithea/templates/admin/users/users.html:50
+#: kallithea/templates/base/base.html:390
+#: kallithea/templates/pullrequests/pullrequest_show.html:166
+msgid "Username"
+msgstr ""
+
+#: kallithea/templates/login.html:33 kallithea/templates/register.html:33
+#: kallithea/templates/admin/my_account/my_account.html:37
+#: kallithea/templates/admin/users/user_add.html:41
+#: kallithea/templates/base/base.html:399
+msgid "Password"
+msgstr ""
+
+#: kallithea/templates/login.html:44
+msgid "Remember me"
+msgstr ""
+
+#: kallithea/templates/login.html:53
+msgid "Forgot your password ?"
+msgstr ""
+
+#: kallithea/templates/login.html:56 kallithea/templates/base/base.html:410
+msgid "Don't have an account ?"
+msgstr ""
+
+#: kallithea/templates/login.html:59
+msgid "Sign In"
+msgstr ""
+
+#: kallithea/templates/password_reset.html:5
+msgid "Password Reset"
+msgstr ""
+
+#: kallithea/templates/password_reset.html:12
+#: kallithea/templates/password_reset_confirmation.html:12
+#, python-format
+msgid "Reset Your Password to %s"
+msgstr ""
+
+#: kallithea/templates/password_reset.html:14
+#: kallithea/templates/password_reset_confirmation.html:5
+#: kallithea/templates/password_reset_confirmation.html:14
+msgid "Reset Your Password"
+msgstr ""
+
+#: kallithea/templates/password_reset.html:25
+msgid "Email Address"
+msgstr ""
+
+#: kallithea/templates/password_reset.html:35
+#: kallithea/templates/register.html:79
+msgid "Captcha"
+msgstr ""
+
+#: kallithea/templates/password_reset.html:46
+msgid "Send Password Reset Email"
+msgstr ""
+
+#: kallithea/templates/password_reset.html:47
+msgid ""
+"A password reset link will be sent to the specified email address if it "
+"is registered in the system."
+msgstr ""
+
+#: kallithea/templates/password_reset_confirmation.html:19
+#, python-format
+msgid "You are about to set a new password for the email address %s."
+msgstr ""
+
+#: kallithea/templates/password_reset_confirmation.html:20
+msgid ""
+"Note that you must use the same browser session for this as the one used "
+"to request the password reset."
+msgstr ""
+
+#: kallithea/templates/password_reset_confirmation.html:30
+msgid "Code you received in the email"
+msgstr ""
+
+#: kallithea/templates/password_reset_confirmation.html:39
+msgid "New Password"
+msgstr ""
+
+#: kallithea/templates/password_reset_confirmation.html:48
+msgid "Confirm New Password"
+msgstr ""
+
+#: kallithea/templates/password_reset_confirmation.html:56
+msgid "Confirm"
+msgstr ""
+
+#: kallithea/templates/register.html:5 kallithea/templates/register.html:14
+#: kallithea/templates/register.html:90
+msgid "Sign Up"
+msgstr ""
+
+#: kallithea/templates/register.html:12
+#, python-format
+msgid "Sign Up to %s"
+msgstr ""
+
+#: kallithea/templates/register.html:42
+msgid "Re-enter password"
+msgstr ""
+
+#: kallithea/templates/register.html:51
+#: kallithea/templates/admin/my_account/my_account_profile.html:34
+#: kallithea/templates/admin/users/user_add.html:59
+#: kallithea/templates/admin/users/user_edit_profile.html:78
+#: kallithea/templates/admin/users/users.html:51
+msgid "First Name"
+msgstr ""
+
+#: kallithea/templates/register.html:60
+#: kallithea/templates/admin/my_account/my_account_profile.html:43
+#: kallithea/templates/admin/users/user_add.html:68
+#: kallithea/templates/admin/users/user_edit_profile.html:87
+#: kallithea/templates/admin/users/users.html:52
+msgid "Last Name"
+msgstr ""
+
+#: kallithea/templates/register.html:69
+#: kallithea/templates/admin/my_account/my_account_profile.html:52
+#: kallithea/templates/admin/settings/settings.html:31
+#: kallithea/templates/admin/users/user_add.html:77
+#: kallithea/templates/admin/users/user_edit_profile.html:33
+msgid "Email"
+msgstr ""
+
+#: kallithea/templates/register.html:92
+msgid "Registered accounts are ready to use and need no further action."
+msgstr ""
+
+#: kallithea/templates/register.html:94
+msgid "Please wait for an administrator to activate your account."
+msgstr ""
+
+#: kallithea/templates/switch_to_list.html:10
+#: kallithea/templates/branches/branches_data.html:69
+msgid "There are no branches yet"
+msgstr ""
+
+#: kallithea/templates/switch_to_list.html:32
+#: kallithea/templates/tags/tags_data.html:44
+msgid "There are no tags yet"
+msgstr ""
+
+#: kallithea/templates/switch_to_list.html:45
+#: kallithea/templates/bookmarks/bookmarks_data.html:43
+msgid "There are no bookmarks yet"
+msgstr ""
+
+#: kallithea/templates/admin/admin.html:5
+#: kallithea/templates/admin/admin.html:13
+#: kallithea/templates/base/base.html:59
+msgid "Admin Journal"
+msgstr ""
+
+#: kallithea/templates/admin/admin.html:10
+msgid "journal filter..."
+msgstr ""
+
+#: kallithea/templates/admin/admin.html:12
+#: kallithea/templates/journal/journal.html:11
+msgid "Filter"
+msgstr ""
+
+#: kallithea/templates/admin/admin.html:13
+#: kallithea/templates/journal/journal.html:12
+#, python-format
+msgid "%s Entry"
+msgid_plural "%s Entries"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/templates/admin/admin_log.html:6
+#: kallithea/templates/admin/my_account/my_account_repos.html:50
+#: kallithea/templates/admin/my_account/my_account_watched.html:50
+#: kallithea/templates/admin/repo_groups/repo_groups.html:50
+#: kallithea/templates/admin/repos/repo_edit_fields.html:8
+#: kallithea/templates/admin/repos/repos.html:52
+#: kallithea/templates/admin/user_groups/user_groups.html:51
+#: kallithea/templates/admin/users/users.html:57
+#: kallithea/templates/journal/journal.html:191
+#: kallithea/templates/journal/journal.html:282
+msgid "Action"
+msgstr ""
+
+#: kallithea/templates/admin/admin_log.html:7
+#: kallithea/templates/admin/permissions/permissions_globals.html:18
+msgid "Repository"
+msgstr ""
+
+#: kallithea/templates/admin/admin_log.html:8
+#: kallithea/templates/bookmarks/bookmarks.html:51
+#: kallithea/templates/bookmarks/bookmarks_data.html:9
+#: kallithea/templates/branches/branches.html:51
+#: kallithea/templates/branches/branches_data.html:9
+#: kallithea/templates/tags/tags.html:51
+#: kallithea/templates/tags/tags_data.html:9
+msgid "Date"
+msgstr ""
+
+#: kallithea/templates/admin/admin_log.html:9
+msgid "From IP"
+msgstr ""
+
+#: kallithea/templates/admin/admin_log.html:63
+msgid "No actions yet"
+msgstr ""
+
+#: kallithea/templates/admin/auth/auth_settings.html:5
+msgid "Authentication Settings"
+msgstr ""
+
+#: kallithea/templates/admin/auth/auth_settings.html:11
+#: kallithea/templates/base/base.html:65
+msgid "Authentication"
+msgstr ""
+
+#: kallithea/templates/admin/auth/auth_settings.html:28
+msgid "Authentication Plugins"
+msgstr ""
+
+#: kallithea/templates/admin/auth/auth_settings.html:31
+msgid "Enabled Plugins"
+msgstr ""
+
+#: kallithea/templates/admin/auth/auth_settings.html:33
+msgid ""
+"Comma-separated list of plugins; Kallithea will try user authentication "
+"in plugin order"
+msgstr ""
+
+#: kallithea/templates/admin/auth/auth_settings.html:34
+msgid "Available built-in plugins"
+msgstr ""
+
+#: kallithea/templates/admin/auth/auth_settings.html:51
+msgid "Plugin"
+msgstr ""
+
+#: kallithea/templates/admin/auth/auth_settings.html:101
+#: kallithea/templates/admin/defaults/defaults.html:82
+#: kallithea/templates/admin/my_account/my_account_password.html:36
+#: kallithea/templates/admin/my_account/my_account_profile.html:60
+#: kallithea/templates/admin/permissions/permissions_globals.html:112
+#: kallithea/templates/admin/repo_groups/repo_group_add.html:69
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:114
+#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:42
+#: kallithea/templates/admin/repos/repo_edit_permissions.html:101
+#: kallithea/templates/admin/repos/repo_edit_settings.html:127
+#: kallithea/templates/admin/settings/settings_hooks.html:53
+#: kallithea/templates/admin/user_groups/user_group_add.html:57
+#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:104
+#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:60
+#: kallithea/templates/admin/users/user_add.html:96
+#: kallithea/templates/admin/users/user_edit_profile.html:113
+#: kallithea/templates/base/default_perms_box.html:64
+msgid "Save"
+msgstr ""
+
+#: kallithea/templates/admin/defaults/defaults.html:5
+#: kallithea/templates/admin/defaults/defaults.html:11
+#: kallithea/templates/base/base.html:66
+msgid "Repository Defaults"
+msgstr ""
+
+#: kallithea/templates/admin/defaults/defaults.html:33
+#: kallithea/templates/admin/repos/repo_add_base.html:55
+#: kallithea/templates/admin/repos/repo_edit_fields.html:7
+msgid "Type"
+msgstr ""
+
+#: kallithea/templates/admin/defaults/defaults.html:42
+#: kallithea/templates/admin/repos/repo_add_base.html:73
+#: kallithea/templates/admin/repos/repo_edit_settings.html:75
+#: kallithea/templates/data_table/_dt_elements.html:72
+msgid "Private repository"
+msgstr ""
+
+#: kallithea/templates/admin/defaults/defaults.html:46
+#: kallithea/templates/admin/repos/repo_add_base.html:77
+#: kallithea/templates/admin/repos/repo_edit_settings.html:79
+#: kallithea/templates/forks/fork.html:72
+msgid ""
+"Private repositories are only visible to people explicitly added as "
+"collaborators."
+msgstr ""
+
+#: kallithea/templates/admin/defaults/defaults.html:53
+#: kallithea/templates/admin/repos/repo_edit_settings.html:84
+msgid "Enable statistics"
+msgstr ""
+
+#: kallithea/templates/admin/defaults/defaults.html:57
+#: kallithea/templates/admin/repos/repo_edit_settings.html:88
+msgid "Enable statistics window on summary page."
+msgstr ""
+
+#: kallithea/templates/admin/defaults/defaults.html:63
+#: kallithea/templates/admin/repos/repo_edit_settings.html:93
+msgid "Enable downloads"
+msgstr ""
+
+#: kallithea/templates/admin/defaults/defaults.html:67
+#: kallithea/templates/admin/repos/repo_edit_settings.html:97
+msgid "Enable download menu on summary page."
+msgstr ""
+
+#: kallithea/templates/admin/defaults/defaults.html:73
+#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:34
+#: kallithea/templates/admin/repos/repo_edit_settings.html:102
+msgid "Enable locking"
+msgstr ""
+
+#: kallithea/templates/admin/defaults/defaults.html:77
+#: kallithea/templates/admin/repos/repo_edit_settings.html:106
+msgid "Enable lock-by-pulling on repository."
+msgstr ""
+
+#: kallithea/templates/admin/gists/edit.html:5
+#: kallithea/templates/admin/gists/edit.html:18
+msgid "Edit Gist"
+msgstr ""
+
+#: kallithea/templates/admin/gists/edit.html:36
+#, python-format
+msgid ""
+"Gist was update since you started editing. Copy your changes and click "
+"%(here)s to reload new version."
+msgstr ""
+
+#: kallithea/templates/admin/gists/edit.html:55
+#: kallithea/templates/admin/gists/new.html:39
+msgid "Gist description ..."
+msgstr ""
+
+#: kallithea/templates/admin/gists/edit.html:57
+#: kallithea/templates/admin/gists/new.html:41
+msgid "Gist lifetime"
+msgstr ""
+
+#: kallithea/templates/admin/gists/edit.html:61
+#: kallithea/templates/admin/gists/edit.html:63
+#: kallithea/templates/admin/gists/index.html:57
+#: kallithea/templates/admin/gists/index.html:59
+#: kallithea/templates/admin/gists/show.html:47
+#: kallithea/templates/admin/gists/show.html:49
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:8
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:27
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:32
+#: kallithea/templates/admin/users/user_edit_api_keys.html:8
+#: kallithea/templates/admin/users/user_edit_api_keys.html:27
+#: kallithea/templates/admin/users/user_edit_api_keys.html:32
+msgid "Expires"
+msgstr ""
+
+#: kallithea/templates/admin/gists/edit.html:61
+#: kallithea/templates/admin/gists/index.html:57
+#: kallithea/templates/admin/gists/show.html:47
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:8
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:27
+#: kallithea/templates/admin/users/user_edit_api_keys.html:8
+#: kallithea/templates/admin/users/user_edit_api_keys.html:27
+msgid "Never"
+msgstr ""
+
+#: kallithea/templates/admin/gists/edit.html:146
+msgid "Update Gist"
+msgstr ""
+
+#: kallithea/templates/admin/gists/edit.html:147
+#: kallithea/templates/changeset/changeset_file_comment.html:105
+msgid "Cancel"
+msgstr ""
+
+#: kallithea/templates/admin/gists/index.html:6
+#: kallithea/templates/admin/gists/index.html:16
+#, python-format
+msgid "Private Gists for User %s"
+msgstr ""
+
+#: kallithea/templates/admin/gists/index.html:8
+#: kallithea/templates/admin/gists/index.html:18
+#, python-format
+msgid "Public Gists for User %s"
+msgstr ""
+
+#: kallithea/templates/admin/gists/index.html:10
+#: kallithea/templates/admin/gists/index.html:20
+msgid "Public Gists"
+msgstr ""
+
+#: kallithea/templates/admin/gists/index.html:37
+#: kallithea/templates/admin/gists/show.html:25
+#: kallithea/templates/base/base.html:321
+msgid "Create New Gist"
+msgstr ""
+
+#: kallithea/templates/admin/gists/index.html:54
+#: kallithea/templates/data_table/_dt_elements.html:141
+msgid "Created"
+msgstr ""
+
+#: kallithea/templates/admin/gists/index.html:74
+msgid "There are no gists yet"
+msgstr ""
+
+#: kallithea/templates/admin/gists/new.html:5
+#: kallithea/templates/admin/gists/new.html:18
+msgid "New Gist"
+msgstr ""
+
+#: kallithea/templates/admin/gists/new.html:47
+msgid "name this file..."
+msgstr ""
+
+#: kallithea/templates/admin/gists/new.html:56
+msgid "Create Private Gist"
+msgstr ""
+
+#: kallithea/templates/admin/gists/new.html:57
+msgid "Create Public Gist"
+msgstr ""
+
+#: kallithea/templates/admin/gists/new.html:58
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:15
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:70
+#: kallithea/templates/admin/my_account/my_account_emails.html:46
+#: kallithea/templates/admin/my_account/my_account_password.html:37
+#: kallithea/templates/admin/my_account/my_account_profile.html:61
+#: kallithea/templates/admin/permissions/permissions_globals.html:113
+#: kallithea/templates/admin/permissions/permissions_ips.html:39
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:115
+#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:43
+#: kallithea/templates/admin/repos/repo_edit_fields.html:59
+#: kallithea/templates/admin/repos/repo_edit_permissions.html:102
+#: kallithea/templates/admin/repos/repo_edit_settings.html:128
+#: kallithea/templates/admin/settings/settings_global.html:57
+#: kallithea/templates/admin/settings/settings_vcs.html:81
+#: kallithea/templates/admin/settings/settings_visual.html:117
+#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:105
+#: kallithea/templates/admin/users/user_edit_api_keys.html:15
+#: kallithea/templates/admin/users/user_edit_api_keys.html:70
+#: kallithea/templates/admin/users/user_edit_emails.html:46
+#: kallithea/templates/admin/users/user_edit_ips.html:50
+#: kallithea/templates/admin/users/user_edit_profile.html:114
+#: kallithea/templates/base/default_perms_box.html:65
+#: kallithea/templates/files/files_add.html:65
+#: kallithea/templates/files/files_delete.html:44
+#: kallithea/templates/files/files_edit.html:68
+#: kallithea/templates/pullrequests/pullrequest.html:89
+msgid "Reset"
+msgstr ""
+
+#: kallithea/templates/admin/gists/show.html:5
+#: kallithea/templates/admin/gists/show.html:9
+msgid "Gist"
+msgstr ""
+
+#: kallithea/templates/admin/gists/show.html:10
+#: kallithea/templates/email_templates/changeset_comment.html:15
+#: kallithea/templates/email_templates/pull_request.html:10
+#: kallithea/templates/email_templates/pull_request_comment.html:15
+msgid "URL"
+msgstr ""
+
+#: kallithea/templates/admin/gists/show.html:37
+msgid "Public Gist"
+msgstr ""
+
+#: kallithea/templates/admin/gists/show.html:39
+msgid "Private Gist"
+msgstr ""
+
+#: kallithea/templates/admin/gists/show.html:56
+#: kallithea/templates/admin/my_account/my_account_emails.html:19
+#: kallithea/templates/admin/permissions/permissions_ips.html:12
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:75
+#: kallithea/templates/admin/repos/repo_edit_fields.html:18
+#: kallithea/templates/admin/settings/settings_hooks.html:36
+#: kallithea/templates/admin/users/user_edit_emails.html:19
+#: kallithea/templates/admin/users/user_edit_ips.html:22
+#: kallithea/templates/changeset/changeset_file_comment.html:31
+#: kallithea/templates/changeset/changeset_file_comment.html:95
+#: kallithea/templates/data_table/_dt_elements.html:129
+#: kallithea/templates/data_table/_dt_elements.html:157
+#: kallithea/templates/data_table/_dt_elements.html:173
+#: kallithea/templates/data_table/_dt_elements.html:189
+#: kallithea/templates/files/files_source.html:39
+#: kallithea/templates/files/files_source.html:42
+#: kallithea/templates/files/files_source.html:45
+#: kallithea/templates/pullrequests/pullrequest_data.html:20
+msgid "Delete"
+msgstr ""
+
+#: kallithea/templates/admin/gists/show.html:56
+msgid "Confirm to delete this Gist"
+msgstr ""
+
+#: kallithea/templates/admin/gists/show.html:63
+#: kallithea/templates/base/perms_summary.html:43
+#: kallithea/templates/base/perms_summary.html:79
+#: kallithea/templates/base/perms_summary.html:81
+#: kallithea/templates/data_table/_dt_elements.html:122
+#: kallithea/templates/data_table/_dt_elements.html:123
+#: kallithea/templates/data_table/_dt_elements.html:150
+#: kallithea/templates/data_table/_dt_elements.html:151
+#: kallithea/templates/data_table/_dt_elements.html:165
+#: kallithea/templates/data_table/_dt_elements.html:167
+#: kallithea/templates/data_table/_dt_elements.html:181
+#: kallithea/templates/data_table/_dt_elements.html:183
+#: kallithea/templates/files/diff_2way.html:56
+#: kallithea/templates/files/files_source.html:41
+#: kallithea/templates/files/files_source.html:44
+#: kallithea/templates/pullrequests/pullrequest_show.html:41
+msgid "Edit"
+msgstr ""
+
+#: kallithea/templates/admin/gists/show.html:65
+#: kallithea/templates/files/files_edit.html:49
+#: kallithea/templates/files/files_source.html:34
+msgid "Show as Raw"
+msgstr ""
+
+#: kallithea/templates/admin/gists/show.html:73
+msgid "created"
+msgstr ""
+
+#: kallithea/templates/admin/gists/show.html:86
+msgid "Show as raw"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account.html:5
+#: kallithea/templates/admin/my_account/my_account.html:9
+#: kallithea/templates/base/base.html:431
+msgid "My Account"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account.html:35
+#: kallithea/templates/admin/users/user_edit.html:29
+msgid "Profile"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account.html:36
+msgid "Email Addresses"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account.html:38
+#: kallithea/templates/admin/users/user_edit.html:31
+msgid "API Keys"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account.html:39
+msgid "Owned Repositories"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account.html:40
+#: kallithea/templates/journal/journal.html:53
+msgid "Watched Repositories"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account.html:41
+#: kallithea/templates/admin/permissions/permissions.html:30
+#: kallithea/templates/admin/user_groups/user_group_edit.html:32
+#: kallithea/templates/admin/users/user_edit.html:34
+msgid "Show Permissions"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:6
+#: kallithea/templates/admin/users/user_edit_api_keys.html:6
+msgid "Built-in"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:14
+#: kallithea/templates/admin/users/user_edit_api_keys.html:14
+#, python-format
+msgid "Confirm to reset this API key: %s"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:30
+#: kallithea/templates/admin/users/user_edit_api_keys.html:30
+msgid "Expired"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:40
+#: kallithea/templates/admin/users/user_edit_api_keys.html:40
+#, python-format
+msgid "Confirm to remove this API key: %s"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:42
+#: kallithea/templates/admin/users/user_edit_api_keys.html:42
+msgid "Remove"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:49
+#: kallithea/templates/admin/users/user_edit_api_keys.html:49
+msgid "No additional API keys specified"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:61
+#: kallithea/templates/admin/users/user_edit_api_keys.html:61
+msgid "New API key"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_api_keys.html:69
+#: kallithea/templates/admin/my_account/my_account_emails.html:45
+#: kallithea/templates/admin/permissions/permissions_ips.html:38
+#: kallithea/templates/admin/repos/repo_add_base.html:81
+#: kallithea/templates/admin/repos/repo_edit_fields.html:58
+#: kallithea/templates/admin/users/user_edit_api_keys.html:69
+#: kallithea/templates/admin/users/user_edit_emails.html:45
+#: kallithea/templates/admin/users/user_edit_ips.html:49
+msgid "Add"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_emails.html:7
+#: kallithea/templates/admin/users/user_edit_emails.html:7
+msgid "Primary"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_emails.html:20
+#: kallithea/templates/admin/users/user_edit_emails.html:20
+#, python-format
+msgid "Confirm to delete this email: %s"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_emails.html:26
+#: kallithea/templates/admin/users/user_edit_emails.html:26
+msgid "No additional emails specified."
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_emails.html:38
+#: kallithea/templates/admin/users/user_edit_emails.html:38
+msgid "New email address"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_password.html:1
+msgid "Change Your Account Password"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_password.html:10
+msgid "Current password"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_password.html:19
+#: kallithea/templates/admin/users/user_edit_profile.html:60
+msgid "New password"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_password.html:28
+msgid "Confirm new password"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_password.html:45
+#, python-format
+msgid "This account is managed with %s and the password cannot be changed here"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_profile.html:11
+msgid "Change your avatar at"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_profile.html:12
+#: kallithea/templates/admin/users/user_edit_profile.html:9
+msgid "Using"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_profile.html:14
+#: kallithea/templates/admin/users/user_edit_profile.html:11
+msgid "Avatars are disabled"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_profile.html:15
+msgid "Missing email, please update your user email address."
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_profile.html:16
+#: kallithea/templates/admin/users/user_edit_profile.html:15
+msgid "Current IP"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_repos.html:1
+msgid "Repositories You Own"
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_repos.html:59
+#: kallithea/templates/admin/my_account/my_account_watched.html:59
+#: kallithea/templates/base/root.html:45
+#: kallithea/templates/bookmarks/bookmarks.html:81
+#: kallithea/templates/branches/branches.html:81
+#: kallithea/templates/journal/journal.html:200
+#: kallithea/templates/journal/journal.html:291
+#: kallithea/templates/tags/tags.html:81
+msgid "No records found."
+msgstr ""
+
+#: kallithea/templates/admin/my_account/my_account_watched.html:1
+msgid "Repositories You are Watching"
+msgstr ""
+
+#: kallithea/templates/admin/notifications/notifications.html:5
+#: kallithea/templates/admin/notifications/notifications.html:9
+msgid "My Notifications"
+msgstr ""
+
+#: kallithea/templates/admin/notifications/notifications.html:24
+msgid "All"
+msgstr ""
+
+#: kallithea/templates/admin/notifications/notifications.html:25
+msgid "Comments"
+msgstr ""
+
+#: kallithea/templates/admin/notifications/notifications.html:26
+#: kallithea/templates/base/base.html:180
+msgid "Pull Requests"
+msgstr ""
+
+#: kallithea/templates/admin/notifications/notifications.html:30
+msgid "Mark All Read"
+msgstr ""
+
+#: kallithea/templates/admin/notifications/notifications_data.html:40
+msgid "No notifications here yet"
+msgstr ""
+
+#: kallithea/templates/admin/notifications/show_notification.html:5
+#: kallithea/templates/admin/notifications/show_notification.html:11
+msgid "Show Notification"
+msgstr ""
+
+#: kallithea/templates/admin/notifications/show_notification.html:9
+#: kallithea/templates/base/base.html:430
+msgid "Notifications"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions.html:5
+#: kallithea/templates/admin/permissions/permissions.html:11
+#: kallithea/templates/base/base.html:64
+msgid "Default Permissions"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions.html:28
+#: kallithea/templates/admin/settings/settings.html:29
+msgid "Global"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions.html:29
+#: kallithea/templates/admin/users/user_edit.html:32
+msgid "IP Whitelist"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:7
+msgid "Anonymous access"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:13
+#, python-format
+msgid ""
+"Allow access to Kallithea without needing to log in. Anonymous users use "
+"%s user permissions."
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:25
+msgid ""
+"All default permissions on each repository will be reset to chosen "
+"permission, note that all custom default permission on repositories will "
+"be lost"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:26
+msgid "Apply to all existing repositories"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:27
+msgid "Permissions for the Default user on new repositories."
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:32
+#: kallithea/templates/admin/repos/repo_add_base.html:37
+#: kallithea/templates/admin/repos/repo_edit_settings.html:35
+#: kallithea/templates/data_table/_dt_elements.html:202
+#: kallithea/templates/forks/fork.html:48
+msgid "Repository group"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:39
+msgid ""
+"All default permissions on each repository group will be reset to chosen "
+"permission, note that all custom default permission on repository groups "
+"will be lost"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:40
+msgid "Apply to all existing repository groups"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:41
+msgid "Permissions for the Default user on new repository groups."
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:46
+#: kallithea/templates/data_table/_dt_elements.html:209
+msgid "User group"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:53
+msgid ""
+"All default permissions on each user group will be reset to chosen "
+"permission, note that all custom default permission on user groups will "
+"be lost"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:54
+msgid "Apply to all existing user groups"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:55
+msgid "Permissions for the Default user on new user groups."
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:60
+msgid "Top level repository creation"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:64
+msgid "Enable this to allow non-admins to create repositories at the top level."
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:65
+msgid ""
+"Note: This will also give all users API access to create repositories "
+"everywhere. That might change in future versions."
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:70
+msgid "Repository creation with group write access"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:74
+msgid ""
+"With this, write permission to a repository group allows creating "
+"repositories inside that group. Without this, group write permissions "
+"mean nothing."
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:79
+msgid "User group creation"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:83
+msgid "Enable this to allow non-admins to create user groups."
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:88
+msgid "Repository forking"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:92
+msgid "Enable this to allow non-admins to fork repositories."
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:97
+msgid "Registration"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_globals.html:105
+msgid "External auth account activation"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_ips.html:13
+#: kallithea/templates/admin/users/user_edit_ips.html:23
+#, python-format
+msgid "Confirm to delete this IP address: %s"
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_ips.html:19
+#: kallithea/templates/admin/users/user_edit_ips.html:30
+msgid "All IP addresses are allowed."
+msgstr ""
+
+#: kallithea/templates/admin/permissions/permissions_ips.html:30
+#: kallithea/templates/admin/users/user_edit_ips.html:42
+msgid "New IP address"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_add.html:11
+#: kallithea/templates/admin/repo_groups/repo_group_edit.html:11
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:105
+#: kallithea/templates/admin/repo_groups/repo_groups.html:10
+#: kallithea/templates/base/base.html:61 kallithea/templates/base/base.html:80
+msgid "Repository Groups"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_add.html:33
+#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:8
+#: kallithea/templates/admin/user_groups/user_group_add.html:32
+#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:7
+msgid "Group name"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_add.html:51
+#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:26
+msgid "Group parent"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_add.html:60
+#: kallithea/templates/admin/repos/repo_add_base.html:46
+msgid "Copy parent group permissions"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_add.html:64
+#: kallithea/templates/admin/repos/repo_add_base.html:50
+msgid "Copy permission set from parent repository group."
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit.html:5
+#, python-format
+msgid "%s Repository Group Settings"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit.html:21
+msgid "Add Child Group"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit.html:40
+#: kallithea/templates/admin/repos/repo_edit.html:12
+#: kallithea/templates/admin/repos/repo_edit.html:40
+#: kallithea/templates/admin/settings/settings.html:11
+#: kallithea/templates/admin/user_groups/user_group_edit.html:29
+#: kallithea/templates/base/base.html:67 kallithea/templates/base/base.html:148
+#: kallithea/templates/data_table/_dt_elements.html:45
+#: kallithea/templates/data_table/_dt_elements.html:49
+msgid "Settings"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit.html:41
+#: kallithea/templates/admin/repos/repo_edit.html:46
+#: kallithea/templates/admin/user_groups/user_group_edit.html:30
+#: kallithea/templates/admin/users/user_edit.html:33
+msgid "Advanced"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit.html:42
+#: kallithea/templates/admin/repos/repo_edit.html:43
+#: kallithea/templates/admin/user_groups/user_group_edit.html:31
+msgid "Permissions"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:1
+#, python-format
+msgid "Repository Group: %s"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:6
+msgid "Top level repositories"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:7
+msgid "Total repositories"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:8
+msgid "Children groups"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:9
+#: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:7
+#: kallithea/templates/admin/users/user_edit_advanced.html:8
+#: kallithea/templates/pullrequests/pullrequest_show.html:148
+msgid "Created on"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:21
+#: kallithea/templates/data_table/_dt_elements.html:190
+#, python-format
+msgid "Confirm to delete this group: %s with %s repository"
+msgid_plural "Confirm to delete this group: %s with %s repositories"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_advanced.html:25
+msgid "Delete this repository group"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:11
+#: kallithea/templates/admin/repos/repo_edit_permissions.html:12
+#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:11
+msgid "User/User Group"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:28
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:45
+#: kallithea/templates/admin/repos/repo_edit_permissions.html:24
+#: kallithea/templates/admin/repos/repo_edit_permissions.html:37
+#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:28
+#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:45
+msgid "Default"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:34
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:71
+#: kallithea/templates/admin/repos/repo_edit_permissions.html:43
+#: kallithea/templates/admin/repos/repo_edit_permissions.html:68
+#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:34
+#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:71
+msgid "Revoke"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:97
+#: kallithea/templates/admin/repos/repo_edit_permissions.html:94
+#: kallithea/templates/admin/user_groups/user_group_edit_perms.html:97
+msgid "Add new"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:103
+msgid "Apply to children"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:107
+msgid "Both"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_perms.html:108
+msgid ""
+"Set or revoke permission to all children of that group, including non-"
+"private repositories and other groups if selected."
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:38
+msgid ""
+"Enable lock-by-pulling on group. This option will be applied to all other"
+" groups and repositories inside"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:53
+msgid "Remove this group"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_edit_settings.html:53
+msgid "Confirm to delete this group"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_show.html:4
+#, python-format
+msgid "%s Repository group dashboard"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_show.html:9
+msgid "Home"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_group_show.html:13
+msgid "with"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_groups.html:5
+msgid "Repository Groups Administration"
+msgstr ""
+
+#: kallithea/templates/admin/repo_groups/repo_groups.html:48
+msgid "Number of Top-level Repositories"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_add_base.html:17
+msgid "Clone remote repository"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_add_base.html:22
+msgid ""
+"Optional: URL of a remote repository. If set, the repository will be "
+"created as a clone from this URL."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_add_base.html:32
+#: kallithea/templates/admin/repos/repo_edit_settings.html:69
+#: kallithea/templates/forks/fork.html:42
+msgid "Keep it short and to the point. Use a README file for longer descriptions."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_add_base.html:41
+#: kallithea/templates/admin/repos/repo_edit_settings.html:39
+#: kallithea/templates/forks/fork.html:52
+msgid "Optionally select a group to put this repository into."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_add_base.html:59
+msgid "Type of repository to create."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_add_base.html:64
+#: kallithea/templates/admin/repos/repo_edit_settings.html:44
+#: kallithea/templates/forks/fork.html:58
+msgid "Landing revision"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_add_base.html:68
+msgid ""
+"Default revision for files page, downloads, full text search index and "
+"readme generation"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_creating.html:9
+#, python-format
+msgid "%s Creating Repository"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_creating.html:13
+msgid "Creating repository"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_creating.html:27
+#, python-format
+msgid ""
+"Repository \"%(repo_name)s\" is being created, you will be redirected "
+"when this process is finished.repo_name"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_creating.html:39
+msgid ""
+"We're sorry but error occurred during this operation. Please check your "
+"Kallithea server logs, or contact administrator."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit.html:8
+#, python-format
+msgid "%s Repository Settings"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit.html:49
+msgid "Extra Fields"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit.html:52
+msgid "Caches"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit.html:55
+msgid "Remote"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit.html:58
+#: kallithea/templates/summary/statistics.html:8
+#: kallithea/templates/summary/summary.html:171
+#: kallithea/templates/summary/summary.html:172
+msgid "Statistics"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:1
+msgid "Parent"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:5
+#: kallithea/templates/admin/repos/repo_edit_fork.html:5
+msgid "Set"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:8
+#: kallithea/templates/admin/repos/repo_edit_fork.html:9
+msgid "Manually set this repository as a fork of another from the list."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:22
+msgid "Public Journal Visibility"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:29
+msgid "Remove from public journal"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:34
+msgid "Add to Public Journal"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:40
+msgid ""
+"All actions done in this repository will be visible to everyone in the "
+"public journal."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:46
+msgid "Change Locking"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:52
+msgid "Confirm to unlock repository."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:54
+msgid "Unlock Repository"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:56
+#, python-format
+msgid "Locked by %s on %s"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:60
+msgid "Confirm to lock repository."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:62
+msgid "Lock Repository"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:64
+msgid "Repository is not locked"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:68
+msgid ""
+"Force locking on the repository. Works only when anonymous access is "
+"disabled. Triggering a pull locks the repository.  The user who is "
+"pulling locks the repository; only the user who pulled and locked it can "
+"unlock it by doing a push."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:79
+#: kallithea/templates/data_table/_dt_elements.html:130
+#, python-format
+msgid "Confirm to delete this repository: %s"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:81
+msgid "Delete this Repository"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:84
+#, python-format
+msgid "This repository has %s fork"
+msgid_plural "This repository has %s forks"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:85
+msgid "Detach forks"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:86
+msgid "Delete forks"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:90
+msgid ""
+"The deleted repository will be moved away and hidden until the "
+"administrator expires it. The administrator can both permanently delete "
+"it or restore it."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_caches.html:4
+msgid "Invalidate Repository Cache"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_caches.html:7
+msgid ""
+"Manually invalidate cache for this repository. On first access, the "
+"repository will be cached again."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_caches.html:12
+msgid "List of Cached Values"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_caches.html:15
+msgid "Prefix"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_caches.html:16
+#: kallithea/templates/admin/repos/repo_edit_fields.html:6
+msgid "Key"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_caches.html:17
+#: kallithea/templates/admin/user_groups/user_group_add.html:49
+#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:24
+#: kallithea/templates/admin/user_groups/user_groups.html:49
+#: kallithea/templates/admin/users/user_add.html:86
+#: kallithea/templates/admin/users/user_edit_profile.html:96
+#: kallithea/templates/admin/users/users.html:54
+msgid "Active"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_fields.html:5
+msgid "Label"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_fields.html:19
+#, python-format
+msgid "Confirm to delete this field: %s"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_fields.html:33
+msgid "New field key"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_fields.html:41
+msgid "New field label"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_fields.html:44
+msgid "Enter short label"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_fields.html:50
+msgid "New field description"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_fields.html:53
+msgid "Enter description of a field"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_fields.html:66
+msgid "Extra fields are disabled."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_permissions.html:21
+msgid "Private Repository"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_remote.html:3
+msgid "Remote repository URL"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_remote.html:9
+msgid "Pull Changes from Remote Repository"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_remote.html:11
+msgid "Confirm to pull changes from remote repository."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_remote.html:17
+msgid "This repository does not have a remote repository URL."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_settings.html:11
+msgid "Permanent Repository ID"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_settings.html:11
+msgid "What is that?"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_settings.html:13
+msgid "URL by id"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_settings.html:14
+msgid ""
+"In case this repository is renamed or moved into another group the "
+"repository URL changes.\n"
+"                               Using the above permanent URL guarantees "
+"that this repository always will be accessible on that URL.\n"
+"                               This is useful for CI systems, or any "
+"other cases that you need to hardcode the URL into a 3rd party service."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_settings.html:21
+msgid "Remote repository"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_settings.html:25
+msgid "Repository URL"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_settings.html:29
+msgid ""
+"Optional: URL of a remote repository. If set, the repository can be "
+"pulled from this URL."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_settings.html:48
+msgid "Default revision for files page, downloads, whoosh and readme"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_settings.html:58
+msgid "Change owner of this repository."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_statistics.html:6
+msgid "Processed commits"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_statistics.html:7
+msgid "Processed progress"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_statistics.html:10
+msgid "Reset Statistics"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repo_edit_statistics.html:10
+msgid "Confirm to remove current statistics."
+msgstr ""
+
+#: kallithea/templates/admin/repos/repos.html:5
+msgid "Repositories Administration"
+msgstr ""
+
+#: kallithea/templates/admin/repos/repos.html:51
+msgid "State"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings.html:5
+msgid "Settings Administration"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings.html:27
+msgid "VCS"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings.html:28
+msgid "Remap and Rescan"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings.html:30
+msgid "Visual"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings.html:32
+#: kallithea/templates/admin/settings/settings_vcs.html:19
+msgid "Hooks"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings.html:33
+msgid "Full Text Search"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings.html:34
+msgid "System Info"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_email.html:7
+msgid "Send test email to"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_email.html:15
+msgid "Send"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_global.html:8
+msgid "Site branding"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_global.html:12
+msgid "Set a custom title for your Kallithea Service."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_global.html:18
+msgid "HTTP authentication realm"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_global.html:27
+msgid "Analytics HTML block"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_global.html:31
+msgid ""
+"HTML with JavaScript for web analytics systems like Google Analytics or "
+"Piwik. This will be added at the bottom of every page."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_global.html:37
+msgid "ReCaptcha public key"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_global.html:41
+msgid "Public key for reCaptcha system."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_global.html:47
+msgid "ReCaptcha private key"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_global.html:51
+msgid ""
+"Private key for reCaptcha system. Setting this value will enable captcha "
+"on registration."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_global.html:56
+#: kallithea/templates/admin/settings/settings_vcs.html:80
+#: kallithea/templates/admin/settings/settings_visual.html:116
+msgid "Save Settings"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_hooks.html:1
+msgid "Built-in Mercurial Hooks (Read-Only)"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_hooks.html:15
+msgid ""
+"Hooks can be used to trigger actions on certain events such as push / "
+"pull. They can trigger Python functions or external applications."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_hooks.html:19
+msgid "Custom Hooks"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_hooks.html:67
+msgid "Failed to remove hook"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_mapping.html:6
+msgid "Rescan option"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_mapping.html:11
+msgid "Delete records of missing repositories"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_mapping.html:13
+msgid ""
+"Check this option to remove all comments, pull requests and other records"
+" related to repositories that no longer exist in the filesystem."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_mapping.html:17
+msgid "Invalidate cache for all repositories"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_mapping.html:19
+msgid "Check this to reload data and clear cache keys for all repositories."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_mapping.html:23
+msgid "Install Git hooks"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_mapping.html:25
+msgid ""
+"Verify if Kallithea's Git hooks are installed for each repository. "
+"Current hooks will be updated to the latest version."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_mapping.html:28
+msgid "Overwrite existing Git hooks"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_mapping.html:30
+msgid ""
+"If installing Git hooks, overwrite any existing hooks, even if they do "
+"not seem to come from Kallithea. WARNING: This operation will destroy any"
+" custom git hooks you may have deployed by hand!"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_mapping.html:35
+msgid "Rescan Repositories"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_search.html:7
+msgid "Index build option"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_search.html:12
+msgid "Build from scratch"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_search.html:15
+msgid ""
+"This option completely reindexeses all of the repositories for proper "
+"fulltext search capabilities."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_search.html:21
+msgid "Reindex"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_system.html:4
+msgid "Kallithea version"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_system.html:4
+msgid "Check for updates"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_system.html:5
+msgid "Kallithea configuration file"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_system.html:6
+msgid "Python version"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_system.html:7
+msgid "Platform"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_system.html:8
+msgid "Git version"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_system.html:9
+msgid "Git path"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_system.html:10
+msgid "Upgrade info endpoint"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_system.html:10
+msgid "Note: please make sure this server can access this URL"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_system.html:15
+msgid "Checking for updates..."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_system.html:23
+msgid "Python Packages"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:6
+msgid "Web"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:11
+msgid "Require SSL for vcs operations"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:13
+msgid ""
+"Activate to require SSL both pushing and pulling. If SSL certificate is "
+"missing, it will return an HTTP Error 406: Not Acceptable."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:24
+msgid "Show repository size after push"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:28
+msgid "Log user push commands"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:32
+msgid "Log user pull commands"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:36
+msgid "Update repository after push (hg update)"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:42
+msgid "Mercurial extensions"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:47
+msgid "Enable largefiles extension"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:51
+msgid "Enable hgsubversion extension"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:53
+msgid ""
+"Requires hgsubversion library to be installed. Enables cloning of remote "
+"Subversion repositories while converting them to Mercurial."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:64
+msgid "Location of repositories"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:69
+msgid ""
+"Click to unlock. You must restart Kallithea in order to make this setting"
+" take effect."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_vcs.html:72
+msgid ""
+"Filesystem location where repositories are stored. After changing this "
+"value, a restart and rescan of the repository folder are both required."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:8
+msgid "General"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:13
+msgid "Use repository extra fields"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:15
+msgid "Allows storing additional customized fields per repository."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:18
+msgid "Show Kallithea version"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:20
+msgid "Shows or hides a version number of Kallithea displayed in the footer."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:24
+msgid "Use Gravatars in Kallithea"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:30
+msgid ""
+"Gravatar URL allows you to use another avatar server application.\n"
+"                                                        The following "
+"variables of the URL will be replaced accordingly.\n"
+"                                                        {scheme}    "
+"'http' or 'https' sent from running Kallithea server,\n"
+"                                                        {email}     user "
+"email,\n"
+"                                                        {md5email}  md5 "
+"hash of the user email (like at gravatar.com),\n"
+"                                                        {size}      size "
+"of the image that is expected from the server application,\n"
+"                                                        {netloc}    "
+"network location/server host of running Kallithea server"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:42
+msgid ""
+"Schema of clone URL construction eg. '{scheme}://{user}@{netloc}/{repo}'."
+"\n"
+"                                                        The following "
+"variables are available:\n"
+"                                                        {scheme} 'http' "
+"or 'https' sent from running Kallithea server,\n"
+"                                                        {user}   current "
+"user username,\n"
+"                                                        {netloc} network "
+"location/server host of running Kallithea server,\n"
+"                                                        {repo}   full "
+"repository name,\n"
+"                                                        {repoid} ID of "
+"repository, can be used to contruct clone-by-id"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:55
+msgid "Dashboard items"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:59
+msgid ""
+"Number of items displayed in the main page dashboard before pagination is"
+" shown."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:65
+msgid "Admin pages items"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:69
+msgid ""
+"Number of items displayed in the admin pages grids before pagination is "
+"shown."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:75
+msgid "Icons"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:80
+msgid "Show public repository icon on repositories"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:84
+msgid "Show private repository icon on repositories"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:86
+msgid "Show public/private icons next to repository names."
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:92
+msgid "Meta Tagging"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:97
+msgid "Stylify recognised meta tags:"
+msgstr ""
+
+#: kallithea/templates/admin/settings/settings_visual.html:111
+msgid ""
+"Parses meta tags from the repository description field and turns them "
+"into colored tags."
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_group_add.html:5
+msgid "Add user group"
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_group_add.html:10
+#: kallithea/templates/admin/user_groups/user_group_edit.html:11
+#: kallithea/templates/admin/user_groups/user_groups.html:10
+#: kallithea/templates/base/base.html:63 kallithea/templates/base/base.html:83
+msgid "User Groups"
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_group_add.html:12
+#: kallithea/templates/admin/user_groups/user_groups.html:25
+msgid "Add User Group"
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_group_add.html:44
+#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:19
+msgid "Short, optional description for this user group."
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_group_edit.html:5
+#, python-format
+msgid "%s user group settings"
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_group_edit.html:33
+msgid "Show Members"
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:1
+#, python-format
+msgid "User Group: %s"
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:6
+#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:32
+#: kallithea/templates/admin/user_groups/user_groups.html:48
+msgid "Members"
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:19
+#: kallithea/templates/data_table/_dt_elements.html:174
+#, python-format
+msgid "Confirm to delete this user group: %s"
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_group_edit_advanced.html:21
+msgid "Delete this user group"
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_group_edit_members.html:17
+msgid "No members yet"
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:40
+msgid "Chosen group members"
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_group_edit_settings.html:49
+msgid "Available members"
+msgstr ""
+
+#: kallithea/templates/admin/user_groups/user_groups.html:5
+msgid "User Groups Administration"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_add.html:5
+msgid "Add user"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_add.html:10
+#: kallithea/templates/admin/users/user_edit.html:11
+#: kallithea/templates/admin/users/users.html:10
+#: kallithea/templates/base/base.html:62
+msgid "Users"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_add.html:12
+#: kallithea/templates/admin/users/users.html:24
+msgid "Add User"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_add.html:50
+msgid "Password confirmation"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_edit.html:5
+#, python-format
+msgid "%s user settings"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_edit.html:30
+msgid "Emails"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_edit_advanced.html:1
+#, python-format
+msgid "User: %s"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_edit_advanced.html:7
+#: kallithea/templates/admin/users/user_edit_profile.html:42
+msgid "Source of Record"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_edit_advanced.html:9
+#: kallithea/templates/admin/users/users.html:53
+msgid "Last Login"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_edit_advanced.html:10
+msgid "Member of User Groups"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_edit_advanced.html:21
+#: kallithea/templates/data_table/_dt_elements.html:158
+#, python-format
+msgid "Confirm to delete this user: %s"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_edit_advanced.html:23
+msgid "Delete this user"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_edit_ips.html:8
+#, python-format
+msgid "Inherited from %s"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_edit_profile.html:8
+msgid "Change avatar at"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_edit_profile.html:12
+msgid "Missing email, please update this user email address."
+msgstr ""
+
+#: kallithea/templates/admin/users/user_edit_profile.html:51
+msgid "Name in Source of Record"
+msgstr ""
+
+#: kallithea/templates/admin/users/user_edit_profile.html:69
+msgid "New password confirmation"
+msgstr ""
+
+#: kallithea/templates/admin/users/users.html:5
+msgid "Users Administration"
+msgstr ""
+
+#: kallithea/templates/admin/users/users.html:56
+msgid "Auth Type"
+msgstr ""
+
+#: kallithea/templates/base/base.html:18
+#, python-format
+msgid "Server instance: %s"
+msgstr ""
+
+#: kallithea/templates/base/base.html:30
+msgid "Support"
+msgstr ""
+
+#: kallithea/templates/base/base.html:90
+msgid "Mercurial repository"
+msgstr ""
+
+#: kallithea/templates/base/base.html:93
+msgid "Git repository"
+msgstr ""
+
+#: kallithea/templates/base/base.html:119
+msgid "Create Fork"
+msgstr ""
+
+#: kallithea/templates/base/base.html:130
+#: kallithea/templates/data_table/_dt_elements.html:13
+#: kallithea/templates/data_table/_dt_elements.html:17
+#: kallithea/templates/summary/summary.html:8
+msgid "Summary"
+msgstr ""
+
+#: kallithea/templates/base/base.html:132
+#: kallithea/templates/base/base.html:134
+#: kallithea/templates/changelog/changelog.html:14
+#: kallithea/templates/data_table/_dt_elements.html:21
+#: kallithea/templates/data_table/_dt_elements.html:25
+msgid "Changelog"
+msgstr ""
+
+#: kallithea/templates/base/base.html:136
+#: kallithea/templates/data_table/_dt_elements.html:29
+#: kallithea/templates/data_table/_dt_elements.html:33
+#: kallithea/templates/files/files.html:11
+msgid "Files"
+msgstr ""
+
+#: kallithea/templates/base/base.html:142
+#: kallithea/templates/base/base.html:144
+msgid "Options"
+msgstr ""
+
+#: kallithea/templates/base/base.html:152
+#: kallithea/templates/forks/forks_data.html:21
+msgid "Compare Fork"
+msgstr ""
+
+#: kallithea/templates/base/base.html:154
+#: kallithea/templates/bookmarks/bookmarks.html:56
+#: kallithea/templates/bookmarks/bookmarks_data.html:13
+#: kallithea/templates/branches/branches.html:56
+#: kallithea/templates/branches/branches_data.html:13
+#: kallithea/templates/tags/tags.html:56
+#: kallithea/templates/tags/tags_data.html:13
+msgid "Compare"
+msgstr ""
+
+#: kallithea/templates/base/base.html:156
+#: kallithea/templates/base/base.html:331
+#: kallithea/templates/search/search.html:14
+#: kallithea/templates/search/search.html:54
+msgid "Search"
+msgstr ""
+
+#: kallithea/templates/base/base.html:160
+msgid "Unlock"
+msgstr ""
+
+#: kallithea/templates/base/base.html:162
+msgid "Lock"
+msgstr ""
+
+#: kallithea/templates/base/base.html:170
+msgid "Follow"
+msgstr ""
+
+#: kallithea/templates/base/base.html:171
+msgid "Unfollow"
+msgstr ""
+
+#: kallithea/templates/base/base.html:174
+#: kallithea/templates/data_table/_dt_elements.html:37
+#: kallithea/templates/data_table/_dt_elements.html:41
+#: kallithea/templates/forks/fork.html:9
+msgid "Fork"
+msgstr ""
+
+#: kallithea/templates/base/base.html:175
+#: kallithea/templates/pullrequests/pullrequest.html:88
+msgid "Create Pull Request"
+msgstr ""
+
+#: kallithea/templates/base/base.html:180
+#, python-format
+msgid "Show Pull Requests for %s"
+msgstr ""
+
+#: kallithea/templates/base/base.html:193
+msgid "Switch To"
+msgstr ""
+
+#: kallithea/templates/base/base.html:203
+#: kallithea/templates/base/base.html:485
+msgid "No matches found"
+msgstr ""
+
+#: kallithea/templates/base/base.html:305
+msgid "Show recent activity"
+msgstr ""
+
+#: kallithea/templates/base/base.html:311
+#: kallithea/templates/base/base.html:312
+msgid "Public journal"
+msgstr ""
+
+#: kallithea/templates/base/base.html:317
+msgid "Show public gists"
+msgstr ""
+
+#: kallithea/templates/base/base.html:318
+msgid "Gists"
+msgstr ""
+
+#: kallithea/templates/base/base.html:322
+msgid "All Public Gists"
+msgstr ""
+
+#: kallithea/templates/base/base.html:324
+msgid "My Public Gists"
+msgstr ""
+
+#: kallithea/templates/base/base.html:325
+msgid "My Private Gists"
+msgstr ""
+
+#: kallithea/templates/base/base.html:330
+msgid "Search in repositories"
+msgstr ""
+
+#: kallithea/templates/base/base.html:353
+#: kallithea/templates/base/base.html:354
+#: kallithea/templates/pullrequests/pullrequest_show_my.html:6
+#: kallithea/templates/pullrequests/pullrequest_show_my.html:10
+msgid "My Pull Requests"
+msgstr ""
+
+#: kallithea/templates/base/base.html:377
+msgid "Not Logged In"
+msgstr ""
+
+#: kallithea/templates/base/base.html:384
+msgid "Login to Your Account"
+msgstr ""
+
+#: kallithea/templates/base/base.html:407
+msgid "Forgot password ?"
+msgstr ""
+
+#: kallithea/templates/base/base.html:434
+msgid "Log Out"
+msgstr ""
+
+#: kallithea/templates/base/base.html:615
+msgid "Keyboard shortcuts"
+msgstr ""
+
+#: kallithea/templates/base/base.html:624
+msgid "Site-wide shortcuts"
+msgstr ""
+
+#: kallithea/templates/base/default_perms_box.html:14
+msgid "Inherit defaults"
+msgstr ""
+
+#: kallithea/templates/base/default_perms_box.html:19
+#, python-format
+msgid ""
+"Select to inherit global settings, IP whitelist and permissions from the "
+"%s."
+msgstr ""
+
+#: kallithea/templates/base/default_perms_box.html:28
+msgid "Create repositories"
+msgstr ""
+
+#: kallithea/templates/base/default_perms_box.html:33
+msgid "Select this option to allow repository creation for this user"
+msgstr ""
+
+#: kallithea/templates/base/default_perms_box.html:40
+msgid "Create user groups"
+msgstr ""
+
+#: kallithea/templates/base/default_perms_box.html:45
+msgid "Select this option to allow user group creation for this user"
+msgstr ""
+
+#: kallithea/templates/base/default_perms_box.html:52
+msgid "Fork repositories"
+msgstr ""
+
+#: kallithea/templates/base/default_perms_box.html:57
+msgid "Select this option to allow repository forking for this user"
+msgstr ""
+
+#: kallithea/templates/base/perms_summary.html:13
+#: kallithea/templates/changelog/changelog.html:42
+msgid "Show"
+msgstr ""
+
+#: kallithea/templates/base/perms_summary.html:22
+msgid "No permissions defined yet"
+msgstr ""
+
+#: kallithea/templates/base/perms_summary.html:30
+#: kallithea/templates/base/perms_summary.html:54
+msgid "Permission"
+msgstr ""
+
+#: kallithea/templates/base/perms_summary.html:32
+#: kallithea/templates/base/perms_summary.html:56
+msgid "Edit Permission"
+msgstr ""
+
+#: kallithea/templates/base/perms_summary.html:90
+msgid "No permission defined"
+msgstr ""
+
+#: kallithea/templates/base/root.html:22
+msgid "Add Another Comment"
+msgstr ""
+
+#: kallithea/templates/base/root.html:23
+#: kallithea/templates/data_table/_dt_elements.html:214
+msgid "Stop following this repository"
+msgstr ""
+
+#: kallithea/templates/base/root.html:24
+msgid "Start following this repository"
+msgstr ""
+
+#: kallithea/templates/base/root.html:25
+msgid "Group"
+msgstr ""
+
+#: kallithea/templates/base/root.html:26
+msgid "members"
+msgstr ""
+
+#: kallithea/templates/base/root.html:27
+msgid "Loading ..."
+msgstr ""
+
+#: kallithea/templates/base/root.html:28
+msgid "loading ..."
+msgstr ""
+
+#: kallithea/templates/base/root.html:29
+msgid "Search truncated"
+msgstr ""
+
+#: kallithea/templates/base/root.html:30
+msgid "No matching files"
+msgstr ""
+
+#: kallithea/templates/base/root.html:31
+msgid "Open New Pull Request from {0}"
+msgstr ""
+
+#: kallithea/templates/base/root.html:32
+msgid "Open New Pull Request for {0} &rarr; {1}"
+msgstr ""
+
+#: kallithea/templates/base/root.html:33
+msgid "Show Selected Changesets {0} &rarr; {1}"
+msgstr ""
+
+#: kallithea/templates/base/root.html:34
+msgid "Selection Link"
+msgstr ""
+
+#: kallithea/templates/base/root.html:35
+#: kallithea/templates/changeset/diff_block.html:8
+#: kallithea/templates/changeset/diff_block.html:21
+msgid "Collapse Diff"
+msgstr ""
+
+#: kallithea/templates/base/root.html:36
+msgid "Expand Diff"
+msgstr ""
+
+#: kallithea/templates/base/root.html:37
+msgid "Failed to revoke permission"
+msgstr ""
+
+#: kallithea/templates/base/root.html:38
+msgid "Confirm to revoke permission for {0}: {1} ?"
+msgstr ""
+
+#: kallithea/templates/base/root.html:39
+msgid "enabled"
+msgstr ""
+
+#: kallithea/templates/base/root.html:40
+msgid "disabled"
+msgstr ""
+
+#: kallithea/templates/base/root.html:42
+msgid "Specify changeset"
+msgstr ""
+
+#: kallithea/templates/bookmarks/bookmarks.html:5
+#, python-format
+msgid "%s Bookmarks"
+msgstr ""
+
+#: kallithea/templates/bookmarks/bookmarks.html:26
+msgid "Compare Bookmarks"
+msgstr ""
+
+#: kallithea/templates/bookmarks/bookmarks.html:53
+#: kallithea/templates/bookmarks/bookmarks_data.html:10
+#: kallithea/templates/branches/branches.html:53
+#: kallithea/templates/branches/branches_data.html:10
+#: kallithea/templates/changelog/changelog_summary_data.html:10
+#: kallithea/templates/tags/tags.html:53
+#: kallithea/templates/tags/tags_data.html:10
+msgid "Author"
+msgstr ""
+
+#: kallithea/templates/bookmarks/bookmarks.html:54
+#: kallithea/templates/bookmarks/bookmarks_data.html:12
+#: kallithea/templates/branches/branches.html:54
+#: kallithea/templates/branches/branches_data.html:12
+#: kallithea/templates/changelog/changelog_summary_data.html:7
+#: kallithea/templates/files/files_browser.html:32
+#: kallithea/templates/pullrequests/pullrequest.html:62
+#: kallithea/templates/pullrequests/pullrequest.html:78
+#: kallithea/templates/tags/tags.html:54
+#: kallithea/templates/tags/tags_data.html:12
+msgid "Revision"
+msgstr ""
+
+#: kallithea/templates/branches/branches.html:5
+#, python-format
+msgid "%s Branches"
+msgstr ""
+
+#: kallithea/templates/branches/branches.html:26
+msgid "Compare Branches"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:6
+#, python-format
+msgid "%s Changelog"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:21
+#, python-format
+msgid "showing %d out of %d revision"
+msgid_plural "showing %d out of %d revisions"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/templates/changelog/changelog.html:49
+msgid "Clear selection"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:55
+msgid "Go to tip of repository"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:60
+#: kallithea/templates/forks/forks_data.html:19
+#, python-format
+msgid "Compare fork with %s"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:62
+#, python-format
+msgid "Compare fork with parent repository (%s)"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:66
+#: kallithea/templates/files/files.html:29
+msgid "Branch filter:"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:92
+#: kallithea/templates/changelog/changelog_summary_data.html:20
+#, python-format
+msgid ""
+"Changeset status: %s\n"
+"Click to open associated pull request %s"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:96
+#: kallithea/templates/compare/compare_cs.html:24
+#, python-format
+msgid "Changeset status: %s"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:115
+#: kallithea/templates/compare/compare_cs.html:63
+msgid "Expand commit message"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:124
+#: kallithea/templates/compare/compare_cs.html:30
+msgid "Changeset has comments"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:134
+#: kallithea/templates/changelog/changelog_summary_data.html:54
+#: kallithea/templates/changeset/changeset.html:94
+#: kallithea/templates/changeset/changeset_range.html:92
+#, python-format
+msgid "Bookmark %s"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:140
+#: kallithea/templates/changelog/changelog_summary_data.html:60
+#: kallithea/templates/changeset/changeset.html:101
+#: kallithea/templates/changeset/changeset_range.html:98
+#: kallithea/templates/compare/compare_cs.html:69
+#: kallithea/templates/pullrequests/pullrequest_show.html:203
+#, python-format
+msgid "Tag %s"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:145
+#: kallithea/templates/changelog/changelog_summary_data.html:65
+#: kallithea/templates/changeset/changeset.html:106
+#: kallithea/templates/changeset/changeset_range.html:102
+#, python-format
+msgid "Branch %s"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog.html:310
+msgid "There are no changes yet"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog_details.html:4
+#: kallithea/templates/changeset/changeset.html:77
+msgid "Removed"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog_details.html:5
+#: kallithea/templates/changeset/changeset.html:78
+msgid "Changed"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog_details.html:6
+#: kallithea/templates/changeset/changeset.html:79
+#: kallithea/templates/changeset/diff_block.html:47
+msgid "Added"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog_details.html:8
+#: kallithea/templates/changelog/changelog_details.html:9
+#: kallithea/templates/changelog/changelog_details.html:10
+#: kallithea/templates/changeset/changeset.html:81
+#: kallithea/templates/changeset/changeset.html:82
+#: kallithea/templates/changeset/changeset.html:83
+#, python-format
+msgid "Affected %s files"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog_summary_data.html:8
+#: kallithea/templates/files/files_add.html:60
+#: kallithea/templates/files/files_delete.html:39
+#: kallithea/templates/files/files_edit.html:63
+msgid "Commit Message"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog_summary_data.html:9
+#: kallithea/templates/pullrequests/pullrequest_data.html:17
+msgid "Age"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog_summary_data.html:11
+msgid "Refs"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog_summary_data.html:81
+msgid "Add or upload files directly via Kallithea"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog_summary_data.html:84
+#: kallithea/templates/files/files_add.html:21
+#: kallithea/templates/files/files_ypjax.html:9
+msgid "Add New File"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog_summary_data.html:90
+msgid "Push new repository"
+msgstr ""
+
+#: kallithea/templates/changelog/changelog_summary_data.html:98
+msgid "Existing repository?"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:8
+#, python-format
+msgid "%s Changeset"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:36
+msgid "Parent rev."
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:42
+msgid "Child rev."
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:50
+#: kallithea/templates/changeset/changeset_file_comment.html:39
+#: kallithea/templates/changeset/changeset_range.html:48
+msgid "Changeset status"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:54
+#: kallithea/templates/changeset/diff_block.html:72
+#: kallithea/templates/files/diff_2way.html:49
+msgid "Raw diff"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:57
+msgid "Patch diff"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:60
+#: kallithea/templates/changeset/diff_block.html:75
+#: kallithea/templates/files/diff_2way.html:52
+msgid "Download diff"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:89
+#: kallithea/templates/changeset/changeset_range.html:88
+msgid "Merge"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:123
+msgid "Grafted from:"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:129
+msgid "Transplanted from:"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:135
+msgid "Replaced by:"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:149
+msgid "Preceded by:"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:166
+#: kallithea/templates/compare/compare_diff.html:60
+#: kallithea/templates/pullrequests/pullrequest_show.html:329
+#, python-format
+msgid "%s file changed"
+msgid_plural "%s files changed"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/templates/changeset/changeset.html:168
+#: kallithea/templates/compare/compare_diff.html:62
+#: kallithea/templates/pullrequests/pullrequest_show.html:331
+#, python-format
+msgid "%s file changed with %s insertions and %s deletions"
+msgid_plural "%s files changed with %s insertions and %s deletions"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/templates/changeset/changeset.html:182
+#: kallithea/templates/changeset/changeset.html:195
+#: kallithea/templates/pullrequests/pullrequest_show.html:350
+#: kallithea/templates/pullrequests/pullrequest_show.html:372
+msgid "Show full diff anyway"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset.html:231
+#: kallithea/templates/changeset/changeset.html:268
+msgid "No revisions"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:21
+msgid "on pull request"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:22
+msgid "No title"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:24
+msgid "on this changeset"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:31
+msgid "Delete comment?"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:39
+msgid "Status change"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:59
+msgid "Commenting on line."
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:60
+msgid ""
+"Comments are in plain text. Use @username inside this text to notify "
+"another user."
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:67
+msgid "Set changeset status"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:69
+msgid "Vote for pull request status"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:75
+msgid "No change"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:88
+#, fuzzy, python-format
+#| msgid "Error creating pull request: %s"
+msgid "Finish pull request"
+msgstr "Λάθος στη δημιουργία αιτήματος έλξης - pull request: %s"
+
+#: kallithea/templates/changeset/changeset_file_comment.html:91
+msgid "Close"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:103
+msgid "Submitting ..."
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:104
+msgid "Comment"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:112
+msgid "You need to be logged in to comment."
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:112
+msgid "Login now"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:116
+msgid "Hide"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:128
+#, python-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:129
+#, python-format
+msgid "%d inline"
+msgid_plural "%d inline"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:130
+#, python-format
+msgid "%d general"
+msgid_plural "%d general"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/templates/changeset/changeset_range.html:5
+#, python-format
+msgid "%s Changesets"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_range.html:56
+msgid "Files affected"
+msgstr ""
+
+#: kallithea/templates/changeset/diff_block.html:54
+msgid "Deleted"
+msgstr ""
+
+#: kallithea/templates/changeset/diff_block.html:57
+msgid "Renamed"
+msgstr ""
+
+#: kallithea/templates/changeset/diff_block.html:66
+#: kallithea/templates/files/diff_2way.html:43
+msgid "Show full diff for this file"
+msgstr ""
+
+#: kallithea/templates/changeset/diff_block.html:69
+#: kallithea/templates/files/diff_2way.html:46
+msgid "Show full side-by-side diff for this file"
+msgstr ""
+
+#: kallithea/templates/changeset/diff_block.html:83
+msgid "Show inline comments"
+msgstr ""
+
+#: kallithea/templates/compare/compare_cs.html:4
+msgid "No changesets"
+msgstr ""
+
+#: kallithea/templates/compare/compare_cs.html:8
+msgid "Ancestor"
+msgstr ""
+
+#: kallithea/templates/compare/compare_cs.html:44
+msgid "First (oldest) changeset in this list"
+msgstr ""
+
+#: kallithea/templates/compare/compare_cs.html:46
+msgid "Last (most recent) changeset in this list"
+msgstr ""
+
+#: kallithea/templates/compare/compare_cs.html:48
+msgid "Position in this list of changesets"
+msgstr ""
+
+#: kallithea/templates/compare/compare_cs.html:85
+msgid "Show merge diff"
+msgstr ""
+
+#: kallithea/templates/compare/compare_cs.html:95
+#: kallithea/templates/pullrequests/pullrequest_show.html:321
+msgid "Common ancestor"
+msgstr ""
+
+#: kallithea/templates/compare/compare_cs.html:99
+msgid "No common ancestor found - repositories are unrelated"
+msgstr ""
+
+#: kallithea/templates/compare/compare_cs.html:107
+msgid "is"
+msgstr ""
+
+#: kallithea/templates/compare/compare_cs.html:108
+#, python-format
+msgid "%s changesets"
+msgstr ""
+
+#: kallithea/templates/compare/compare_cs.html:109
+msgid "behind"
+msgstr ""
+
+#: kallithea/templates/compare/compare_diff.html:6
+#: kallithea/templates/compare/compare_diff.html:8
+#, python-format
+msgid "%s Compare"
+msgstr ""
+
+#: kallithea/templates/compare/compare_diff.html:13
+#: kallithea/templates/compare/compare_diff.html:41
+msgid "Compare Revisions"
+msgstr ""
+
+#: kallithea/templates/compare/compare_diff.html:39
+msgid "Swap"
+msgstr ""
+
+#: kallithea/templates/compare/compare_diff.html:48
+msgid "Compare revisions, branches, bookmarks, or tags."
+msgstr ""
+
+#: kallithea/templates/compare/compare_diff.html:53
+#: kallithea/templates/pullrequests/pullrequest_show.html:316
+#, python-format
+msgid "Showing %s commit"
+msgid_plural "Showing %s commits"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/templates/compare/compare_diff.html:84
+#: kallithea/templates/compare/compare_diff.html:93
+msgid "Show full diff"
+msgstr ""
+
+#: kallithea/templates/data_table/_dt_elements.html:74
+msgid "Public repository"
+msgstr ""
+
+#: kallithea/templates/data_table/_dt_elements.html:84
+msgid "Repository creation in progress..."
+msgstr ""
+
+#: kallithea/templates/data_table/_dt_elements.html:98
+msgid "No changesets yet"
+msgstr ""
+
+#: kallithea/templates/data_table/_dt_elements.html:105
+#: kallithea/templates/data_table/_dt_elements.html:107
+#, python-format
+msgid "Subscribe to %s rss feed"
+msgstr ""
+
+#: kallithea/templates/data_table/_dt_elements.html:113
+#: kallithea/templates/data_table/_dt_elements.html:115
+#, python-format
+msgid "Subscribe to %s atom feed"
+msgstr ""
+
+#: kallithea/templates/data_table/_dt_elements.html:139
+msgid "Creating"
+msgstr ""
+
+#: kallithea/templates/email_templates/changeset_comment.html:5
+#, python-format
+msgid "Comment from %s on %s changeset %s mentioned you"
+msgstr ""
+
+#: kallithea/templates/email_templates/changeset_comment.html:7
+#, python-format
+msgid "Comment from %s on %s changeset %s"
+msgstr ""
+
+#: kallithea/templates/email_templates/changeset_comment.html:12
+msgid "The changeset status was changed to"
+msgstr ""
+
+#: kallithea/templates/email_templates/main.html:6
+msgid "This is an automatic notification. Don't reply to this mail."
+msgstr ""
+
+#: kallithea/templates/email_templates/password_reset.html:4
+#, python-format
+msgid "Hello %s"
+msgstr ""
+
+#: kallithea/templates/email_templates/password_reset.html:6
+msgid "We have received a request to reset the password for your account."
+msgstr ""
+
+#: kallithea/templates/email_templates/password_reset.html:8
+msgid ""
+"This account is however managed outside this system and the password "
+"cannot be changed here."
+msgstr ""
+
+#: kallithea/templates/email_templates/password_reset.html:10
+msgid "To set a new password, click the following link"
+msgstr ""
+
+#: kallithea/templates/email_templates/password_reset.html:13
+msgid ""
+"Should you not be able to use the link above, please type the following "
+"code into the password reset form"
+msgstr ""
+
+#: kallithea/templates/email_templates/password_reset.html:16
+msgid ""
+"If it weren't you who requested the password reset, just disregard this "
+"message."
+msgstr ""
+
+#: kallithea/templates/email_templates/pull_request.html:5
+#, python-format
+msgid "%s mentioned you on %s pull request \"%s\""
+msgstr ""
+
+#: kallithea/templates/email_templates/pull_request.html:7
+#, python-format
+msgid "%s requested your review of %s pull request \"%s\""
+msgstr ""
+
+#: kallithea/templates/email_templates/pull_request_comment.html:4
+#, python-format
+msgid "Comment from %s on %s pull request \"%s\""
+msgstr ""
+
+#: kallithea/templates/email_templates/pull_request_comment.html:9
+msgid "The comment closed the pull request with status"
+msgstr ""
+
+#: kallithea/templates/email_templates/pull_request_comment.html:11
+msgid "The comment was made with status"
+msgstr ""
+
+#: kallithea/templates/email_templates/registration.html:6
+msgid "View this user here"
+msgstr ""
+
+#: kallithea/templates/files/diff_2way.html:15
+#, python-format
+msgid "%s File side-by-side diff"
+msgstr ""
+
+#: kallithea/templates/files/diff_2way.html:19
+#: kallithea/templates/files/file_diff.html:8
+msgid "File diff"
+msgstr ""
+
+#: kallithea/templates/files/file_diff.html:4
+#, python-format
+msgid "%s File Diff"
+msgstr ""
+
+#: kallithea/templates/files/files.html:4
+#: kallithea/templates/files/files.html:80
+#, python-format
+msgid "%s Files"
+msgstr ""
+
+#: kallithea/templates/files/files_add.html:4
+#, python-format
+msgid "%s Files Add"
+msgstr ""
+
+#: kallithea/templates/files/files_add.html:40
+#: kallithea/templates/files/files_edit.html:38
+#: kallithea/templates/files/files_ypjax.html:3
+msgid "Location"
+msgstr ""
+
+#: kallithea/templates/files/files_add.html:42
+msgid "Enter filename..."
+msgstr ""
+
+#: kallithea/templates/files/files_add.html:44
+#: kallithea/templates/files/files_add.html:48
+msgid "or"
+msgstr ""
+
+#: kallithea/templates/files/files_add.html:44
+msgid "Upload File"
+msgstr ""
+
+#: kallithea/templates/files/files_add.html:48
+msgid "Create New File"
+msgstr ""
+
+#: kallithea/templates/files/files_add.html:53
+#, fuzzy
+#| msgid "Unknown archive type"
+msgid "New file type"
+msgstr "Άγνωστος τύπος αρχειοθέτησης"
+
+#: kallithea/templates/files/files_add.html:64
+#: kallithea/templates/files/files_delete.html:43
+#: kallithea/templates/files/files_edit.html:67
+msgid "Commit Changes"
+msgstr ""
+
+#: kallithea/templates/files/files_browser.html:33
+msgid "Previous revision"
+msgstr ""
+
+#: kallithea/templates/files/files_browser.html:35
+msgid "Next revision"
+msgstr ""
+
+#: kallithea/templates/files/files_browser.html:41
+msgid "Follow current branch"
+msgstr ""
+
+#: kallithea/templates/files/files_browser.html:44
+msgid "Search File List"
+msgstr ""
+
+#: kallithea/templates/files/files_browser.html:48
+msgid "Loading file list..."
+msgstr ""
+
+#: kallithea/templates/files/files_browser.html:61
+msgid "Size"
+msgstr ""
+
+#: kallithea/templates/files/files_browser.html:62
+msgid "Last Revision"
+msgstr ""
+
+#: kallithea/templates/files/files_browser.html:63
+msgid "Last Modified"
+msgstr ""
+
+#: kallithea/templates/files/files_browser.html:64
+msgid "Last Committer"
+msgstr ""
+
+#: kallithea/templates/files/files_delete.html:4
+#, python-format
+msgid "%s Files Delete"
+msgstr ""
+
+#: kallithea/templates/files/files_delete.html:12
+#: kallithea/templates/files/files_delete.html:31
+msgid "Delete file"
+msgstr ""
+
+#: kallithea/templates/files/files_edit.html:4
+#, python-format
+msgid "%s File Edit"
+msgstr ""
+
+#: kallithea/templates/files/files_edit.html:21
+msgid "Edit file"
+msgstr ""
+
+#: kallithea/templates/files/files_edit.html:48
+#: kallithea/templates/files/files_source.html:32
+msgid "Show Annotation"
+msgstr ""
+
+#: kallithea/templates/files/files_edit.html:50
+#: kallithea/templates/files/files_source.html:35
+msgid "Download as Raw"
+msgstr ""
+
+#: kallithea/templates/files/files_edit.html:53
+msgid "Source"
+msgstr ""
+
+#: kallithea/templates/files/files_edit.html:58
+msgid "Editing file"
+msgstr ""
+
+#: kallithea/templates/files/files_history_box.html:2
+#, python-format
+msgid "%s author"
+msgid_plural "%s authors"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/templates/files/files_source.html:7
+msgid "Diff to Revision"
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:8
+msgid "Show at Revision"
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:10
+msgid "Show Full History"
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:11
+msgid "Show Authors"
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:30
+msgid "Show Source"
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:38
+#, python-format
+msgid "Edit on Branch:%s"
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:41
+msgid "Editing binary files not allowed"
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:44
+msgid "Editing files allowed only when on branch head revision"
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:45
+msgid "Deleting files allowed only when on branch head revision"
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:63
+#, python-format
+msgid "Binary file (%s)"
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:74
+msgid "File is too big to display."
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:76
+msgid "Show full annotation anyway."
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:78
+msgid "Show as raw."
+msgstr ""
+
+#: kallithea/templates/files/files_ypjax.html:5
+msgid "annotation"
+msgstr ""
+
+#: kallithea/templates/files/files_ypjax.html:23
+msgid "Go Back"
+msgstr ""
+
+#: kallithea/templates/files/files_ypjax.html:24
+msgid "No files at given path"
+msgstr ""
+
+#: kallithea/templates/followers/followers.html:5
+#, python-format
+msgid "%s Followers"
+msgstr ""
+
+#: kallithea/templates/followers/followers.html:9
+#: kallithea/templates/summary/summary.html:142
+#: kallithea/templates/summary/summary.html:143
+msgid "Followers"
+msgstr ""
+
+#: kallithea/templates/followers/followers_data.html:12
+msgid "Started following -"
+msgstr ""
+
+#: kallithea/templates/forks/fork.html:5
+#, python-format
+msgid "Fork repository %s"
+msgstr ""
+
+#: kallithea/templates/forks/fork.html:27
+msgid "Fork name"
+msgstr ""
+
+#: kallithea/templates/forks/fork.html:62
+msgid "Default revision for files page, downloads, whoosh, and readme."
+msgstr ""
+
+#: kallithea/templates/forks/fork.html:68
+msgid "Private"
+msgstr ""
+
+#: kallithea/templates/forks/fork.html:77
+msgid "Copy permissions"
+msgstr ""
+
+#: kallithea/templates/forks/fork.html:81
+msgid "Copy permissions from forked repository"
+msgstr ""
+
+#: kallithea/templates/forks/fork.html:87
+msgid "Update after clone"
+msgstr ""
+
+#: kallithea/templates/forks/fork.html:91
+msgid "Checkout source after making a clone"
+msgstr ""
+
+#: kallithea/templates/forks/fork.html:96
+msgid "Fork this Repository"
+msgstr ""
+
+#: kallithea/templates/forks/forks.html:5
+#, python-format
+msgid "%s Forks"
+msgstr ""
+
+#: kallithea/templates/forks/forks.html:9
+#: kallithea/templates/summary/summary.html:148
+#: kallithea/templates/summary/summary.html:149
+msgid "Forks"
+msgstr ""
+
+#: kallithea/templates/forks/forks_data.html:17
+msgid "Forked"
+msgstr ""
+
+#: kallithea/templates/forks/forks_data.html:30
+msgid "There are no forks yet"
+msgstr ""
+
+#: kallithea/templates/journal/journal.html:21
+msgid "ATOM journal feed"
+msgstr ""
+
+#: kallithea/templates/journal/journal.html:22
+msgid "RSS journal feed"
+msgstr ""
+
+#: kallithea/templates/journal/journal.html:56
+msgid "My Repositories"
+msgstr ""
+
+#: kallithea/templates/journal/journal_data.html:43
+msgid "No entries yet"
+msgstr ""
+
+#: kallithea/templates/journal/public_journal.html:13
+msgid "ATOM public journal feed"
+msgstr ""
+
+#: kallithea/templates/journal/public_journal.html:14
+msgid "RSS public journal feed"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest.html:4
+#: kallithea/templates/pullrequests/pullrequest.html:8
+msgid "New Pull Request"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest.html:31
+#: kallithea/templates/pullrequests/pullrequest_data.html:15
+#: kallithea/templates/pullrequests/pullrequest_show.html:29
+#: kallithea/templates/pullrequests/pullrequest_show.html:54
+msgid "Title"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest.html:34
+msgid "Summarize the changes - or leave empty"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest.html:43
+#: kallithea/templates/pullrequests/pullrequest_show.html:66
+msgid "Write a short description on this pull request"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest.html:49
+msgid "Changeset flow"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest.html:56
+msgid "Origin repository"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest.html:72
+msgid "Destination repository"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_data.html:6
+msgid "No entries"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_data.html:14
+msgid "Vote"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_data.html:18
+msgid "From"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_data.html:19
+msgid "To"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_data.html:28
+#, python-format
+msgid "You voted: %s"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_data.html:30
+msgid "You didn't vote"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_data.html:35
+msgid "(no title)"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_data.html:37
+#: kallithea/templates/pullrequests/pullrequest_show.html:31
+#: kallithea/templates/pullrequests/pullrequest_show.html:83
+msgid "Closed"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_data.html:67
+msgid "Delete Pull Request"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_data.html:68
+msgid "Confirm to delete this pull request"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_data.html:70
+#, python-format
+msgid "Confirm again to delete this pull request with %s comments"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:6
+#, python-format
+msgid "%s Pull Request %s"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:10
+#, python-format
+msgid "Pull request %s from %s#%s"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:57
+msgid "Summarize the changes"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:74
+msgid "Reviewer voting result"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:80
+#: kallithea/templates/pullrequests/pullrequest_show.html:81
+msgid "Pull request status calculated from votes"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:93
+msgid "Still not reviewed by"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:97
+#, python-format
+msgid "%d reviewer"
+msgid_plural "%d reviewers"
+msgstr[0] ""
+msgstr[1] ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:99
+msgid "Pull request was reviewed by all reviewers"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:101
+msgid "There are no reviewers"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:107
+msgid "Origin"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:113
+msgid "on"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:120
+msgid "Target"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:124
+msgid ""
+"This is just a range of changesets and doesn't have a target or a real "
+"merge ancestor."
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:133
+msgid "Pull changes"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:173
+msgid "Update"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:191
+msgid "Current revision - no change"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:215
+msgid ""
+"Pull requests do not change once created. Select a revision and save to "
+"replace this pull request with a new one."
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:224
+msgid "Pull Request Reviewers"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:249
+msgid "Remove reviewer"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:261
+msgid "Type name of reviewer to add"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:269
+msgid "Potential Reviewers"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:272
+msgid "Click to add the repository owner as reviewer:"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:295
+msgid "Save Changes"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:296
+#, fuzzy
+#| msgid "Successfully opened new pull request"
+msgid "Save Updates as New Pull Request"
+msgstr "Ένα νέο αίτημα έλξης (pull request) δημιουργήθηκε επιτυχώς"
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:297
+msgid "Cancel Changes"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:307
+msgid "Pull Request Content"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show_all.html:6
+#, python-format
+msgid "%s Pull Requests"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show_all.html:11
+#, python-format
+msgid "Pull Requests from '%s'"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show_all.html:13
+#, python-format
+msgid "Pull Requests to '%s'"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show_all.html:32
+msgid "Open New Pull Request"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show_all.html:37
+#, python-format
+msgid "Show Pull Requests to %s"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show_all.html:39
+#, python-format
+msgid "Show Pull Requests from '%s'"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show_all.html:49
+#: kallithea/templates/pullrequests/pullrequest_show_my.html:28
+msgid "Hide closed pull requests (only show open pull requests)"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show_all.html:51
+#: kallithea/templates/pullrequests/pullrequest_show_my.html:30
+msgid "Show closed pull requests (in addition to open pull requests)"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show_my.html:35
+msgid "Pull Requests Created by Me"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show_my.html:38
+msgid "Pull Requests I Participate In"
+msgstr ""
+
+#: kallithea/templates/search/search.html:6
+#, python-format
+msgid "%s Search"
+msgstr ""
+
+#: kallithea/templates/search/search.html:8
+#: kallithea/templates/search/search.html:16
+msgid "Search in All Repositories"
+msgstr ""
+
+#: kallithea/templates/search/search.html:50
+msgid "Search term"
+msgstr ""
+
+#: kallithea/templates/search/search.html:62
+msgid "Search in"
+msgstr ""
+
+#: kallithea/templates/search/search.html:65
+msgid "File contents"
+msgstr ""
+
+#: kallithea/templates/search/search.html:66
+msgid "Commit messages"
+msgstr ""
+
+#: kallithea/templates/search/search.html:67
+msgid "File names"
+msgstr ""
+
+#: kallithea/templates/search/search_commit.html:35
+#: kallithea/templates/search/search_content.html:21
+#: kallithea/templates/search/search_path.html:15
+msgid "Permission denied"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:4
+#, python-format
+msgid "%s Statistics"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:16
+#: kallithea/templates/summary/summary.html:39
+#, python-format
+msgid "%s ATOM feed"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:17
+#: kallithea/templates/summary/summary.html:40
+#, python-format
+msgid "%s RSS feed"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:36
+#: kallithea/templates/summary/summary.html:100
+#: kallithea/templates/summary/summary.html:116
+msgid "Enable"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:39
+msgid "Stats gathered: "
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:89
+#: kallithea/templates/summary/summary.html:349
+msgid "files"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:113
+#: kallithea/templates/summary/summary.html:373
+msgid "Show more"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:390
+msgid "commits"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:391
+msgid "files added"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:392
+msgid "files changed"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:393
+msgid "files removed"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:395
+msgid "commit"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:396
+msgid "file added"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:397
+msgid "file changed"
+msgstr ""
+
+#: kallithea/templates/summary/statistics.html:398
+msgid "file removed"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:4
+#, python-format
+msgid "%s Summary"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:13
+#, python-format
+msgid "Repository locked by %s"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:15
+msgid "Repository unlocked"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:22
+msgid "Fork of"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:29
+msgid "Clone from"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:72
+msgid "Clone URL"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:78
+msgid "Show by Name"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:79
+msgid "Show by ID"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:92
+msgid "Trending files"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:108
+msgid "Download"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:112
+msgid "There are no downloads yet"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:114
+msgid "Downloads are disabled for this repository"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:120
+msgid "Download as zip"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:125
+msgid "Check this to download archive with subrepos"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:125
+msgid "With subrepos"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:156
+msgid "Repository Size"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:163
+#: kallithea/templates/summary/summary.html:165
+msgid "Feed"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:186
+msgid "Latest Changes"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:188
+msgid "Quick Start"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:202
+#, python-format
+msgid "Readme file from revision %s:%s"
+msgstr ""
+
+#: kallithea/templates/summary/summary.html:293
+#, python-format
+msgid "Download %s as %s"
+msgstr ""
+
+#: kallithea/templates/tags/tags.html:5
+#, python-format
+msgid "%s Tags"
+msgstr ""
+
+#: kallithea/templates/tags/tags.html:26
+msgid "Compare Tags"
+msgstr ""
+
+#~ msgid ""
+#~ "Changing status on a changeset "
+#~ "associated with a closed pull request"
+#~ " is not allowed"
+#~ msgstr ""
+#~ "Η αλλαγή της κατάστασης ενός σετ "
+#~ "αλλαγών σχετιζόμενων με μια κλειστή "
+#~ "αίτηση έλξης(pull request) δεν επιτρέπεται"
+
+#~ msgid "This pull request can be updated with changes on %s:"
+#~ msgstr "Αυτό το αίτημα έλξης μπορεί να ενημερωθεί με αλλαγές στο %s:"
+
+#~ msgid "Non-admins can can fork repositories"
+#~ msgstr ""
+
+#~ msgid "Confirm to invalidate repository cache."
+#~ msgstr ""
+
+#~ msgid "Commenting on line {1}."
+#~ msgstr ""
+
+#~ msgid "Comments parsed using %s syntax with %s support."
+#~ msgstr ""
+
+#~ msgid "Use @username inside this text to notify another user"
+#~ msgstr ""
+
+#~ msgid "Comment preview"
+#~ msgstr ""
+
+#~ msgid "Preview"
+#~ msgstr ""
+
+#~ msgid "Use @username inside this text to notify another user."
+#~ msgstr ""
+
+#~ msgid "New file mode"
+#~ msgstr ""
+
+#~ msgid "File is too big to display"
+#~ msgstr ""
+
+#~ msgid "Save as New Pull Request"
+#~ msgstr ""
+
+#~ msgid "Pull Requests from %s'"
+#~ msgstr ""
+
--- a/kallithea/i18n/how_to	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/i18n/how_to	Mon Mar 14 00:36:08 2016 +0100
@@ -94,4 +94,4 @@
 
 Run Kallithea tests by executing::
 
-    nosetests
+    py.test
--- a/kallithea/i18n/ja/LC_MESSAGES/kallithea.po	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/i18n/ja/LC_MESSAGES/kallithea.po	Mon Mar 14 00:36:08 2016 +0100
@@ -16,7 +16,7 @@
 "PO-Revision-Date: 2016-01-07 01:53+0000\n"
 "Last-Translator: Takumi IINO <trot.thunder@gmail.com>\n"
 "Language-Team: Japanese "
-"<https://hosted.weblate.org/projects/kallithea/stable/ja/>\n"
+"<https://hosted.weblate.org/projects/kallithea/kallithea/ja/>\n"
 "Language: ja\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
--- a/kallithea/i18n/kallithea.pot	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/i18n/kallithea.pot	Mon Mar 14 00:36:08 2016 +0100
@@ -1,14 +1,14 @@
 # Translations template for Kallithea.
-# Copyright (C) 2015 Various authors, licensing as GPLv3
+# Copyright (C) 2016 Various authors, licensing as GPLv3
 # This file is distributed under the same license as the Kallithea project.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
 #
 #, fuzzy
 msgid ""
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2015-09-08 10:34+0200\n"
+"POT-Creation-Date: 2016-02-22 19:35+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,12 +16,12 @@
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: kallithea/controllers/changelog.py:86
-#: kallithea/controllers/pullrequests.py:238 kallithea/lib/base.py:512
+#: kallithea/controllers/changelog.py:85
+#: kallithea/controllers/pullrequests.py:237 kallithea/lib/base.py:515
 msgid "There are no changesets yet"
 msgstr ""
 
-#: kallithea/controllers/changelog.py:166
+#: kallithea/controllers/changelog.py:164
 #: kallithea/controllers/admin/permissions.py:61
 #: kallithea/controllers/admin/permissions.py:65
 #: kallithea/controllers/admin/permissions.py:69
@@ -33,33 +33,29 @@
 msgid "None"
 msgstr ""
 
-#: kallithea/controllers/changelog.py:169 kallithea/controllers/files.py:196
+#: kallithea/controllers/changelog.py:167 kallithea/controllers/files.py:197
 msgid "(closed)"
 msgstr ""
 
-#: kallithea/controllers/changeset.py:89
+#: kallithea/controllers/changeset.py:88
 msgid "Show whitespace"
 msgstr ""
 
-#: kallithea/controllers/changeset.py:96 kallithea/controllers/changeset.py:103
+#: kallithea/controllers/changeset.py:95 kallithea/controllers/changeset.py:102
 #: kallithea/templates/files/diff_2way.html:55
 msgid "Ignore whitespace"
 msgstr ""
 
-#: kallithea/controllers/changeset.py:169
+#: kallithea/controllers/changeset.py:168
 #, python-format
 msgid "Increase diff context to %(num)s lines"
 msgstr ""
 
-#: kallithea/controllers/changeset.py:212 kallithea/controllers/files.py:96
-#: kallithea/controllers/files.py:116 kallithea/controllers/files.py:742
+#: kallithea/controllers/changeset.py:233 kallithea/controllers/files.py:96
+#: kallithea/controllers/files.py:116 kallithea/controllers/files.py:743
 msgid "Such revision does not exist for this repository"
 msgstr ""
 
-#: kallithea/controllers/changeset.py:383
-msgid "Changing status on a changeset associated with a closed pull request is not allowed"
-msgstr ""
-
 #: kallithea/controllers/compare.py:161 kallithea/templates/base/root.html:41
 msgid "Select changeset"
 msgstr ""
@@ -109,10 +105,10 @@
 #: kallithea/controllers/feed.py:87
 #: kallithea/templates/changeset/changeset.html:182
 #: kallithea/templates/changeset/changeset.html:195
-#: kallithea/templates/compare/compare_diff.html:78
-#: kallithea/templates/compare/compare_diff.html:89
-#: kallithea/templates/pullrequests/pullrequest_show.html:339
-#: kallithea/templates/pullrequests/pullrequest_show.html:363
+#: kallithea/templates/compare/compare_diff.html:84
+#: kallithea/templates/compare/compare_diff.html:93
+#: kallithea/templates/pullrequests/pullrequest_show.html:350
+#: kallithea/templates/pullrequests/pullrequest_show.html:372
 msgid "Changeset was too big and was cut off..."
 msgstr ""
 
@@ -130,101 +126,101 @@
 msgid "There are no files yet. %s"
 msgstr ""
 
-#: kallithea/controllers/files.py:193
+#: kallithea/controllers/files.py:194
 #, python-format
 msgid "%s at %s"
 msgstr ""
 
-#: kallithea/controllers/files.py:305 kallithea/controllers/files.py:365
-#: kallithea/controllers/files.py:432
+#: kallithea/controllers/files.py:306 kallithea/controllers/files.py:366
+#: kallithea/controllers/files.py:433
 #, python-format
 msgid "This repository has been locked by %s on %s"
 msgstr ""
 
-#: kallithea/controllers/files.py:317
-msgid "You can only delete files with revision being a valid branch "
-msgstr ""
-
-#: kallithea/controllers/files.py:328
+#: kallithea/controllers/files.py:318
+msgid "You can only delete files with revision being a valid branch"
+msgstr ""
+
+#: kallithea/controllers/files.py:329
 #, python-format
 msgid "Deleted file %s via Kallithea"
 msgstr ""
 
-#: kallithea/controllers/files.py:350
+#: kallithea/controllers/files.py:351
 #, python-format
 msgid "Successfully deleted file %s"
 msgstr ""
 
-#: kallithea/controllers/files.py:354 kallithea/controllers/files.py:420
-#: kallithea/controllers/files.py:501
+#: kallithea/controllers/files.py:355 kallithea/controllers/files.py:421
+#: kallithea/controllers/files.py:502
 msgid "Error occurred during commit"
 msgstr ""
 
-#: kallithea/controllers/files.py:377
-msgid "You can only edit files with revision being a valid branch "
-msgstr ""
-
-#: kallithea/controllers/files.py:391
+#: kallithea/controllers/files.py:378
+msgid "You can only edit files with revision being a valid branch"
+msgstr ""
+
+#: kallithea/controllers/files.py:392
 #, python-format
 msgid "Edited file %s via Kallithea"
 msgstr ""
 
-#: kallithea/controllers/files.py:407
+#: kallithea/controllers/files.py:408
 msgid "No changes"
 msgstr ""
 
-#: kallithea/controllers/files.py:416 kallithea/controllers/files.py:490
+#: kallithea/controllers/files.py:417 kallithea/controllers/files.py:491
 #, python-format
 msgid "Successfully committed to %s"
 msgstr ""
 
-#: kallithea/controllers/files.py:443
+#: kallithea/controllers/files.py:444
 msgid "Added file via Kallithea"
 msgstr ""
 
-#: kallithea/controllers/files.py:464
+#: kallithea/controllers/files.py:465
 msgid "No content"
 msgstr ""
 
-#: kallithea/controllers/files.py:468
+#: kallithea/controllers/files.py:469
 msgid "No filename"
 msgstr ""
 
-#: kallithea/controllers/files.py:493
+#: kallithea/controllers/files.py:494
 msgid "Location must be relative path and must not contain .. in path"
 msgstr ""
 
-#: kallithea/controllers/files.py:526
+#: kallithea/controllers/files.py:527
 msgid "Downloads disabled"
 msgstr ""
 
-#: kallithea/controllers/files.py:537
+#: kallithea/controllers/files.py:538
 #, python-format
 msgid "Unknown revision %s"
 msgstr ""
 
-#: kallithea/controllers/files.py:539
+#: kallithea/controllers/files.py:540
 msgid "Empty repository"
 msgstr ""
 
-#: kallithea/controllers/files.py:541
+#: kallithea/controllers/files.py:542
 msgid "Unknown archive type"
 msgstr ""
 
-#: kallithea/controllers/files.py:771
+#: kallithea/controllers/files.py:772
 #: kallithea/templates/changeset/changeset_range.html:9
 #: kallithea/templates/email_templates/pull_request.html:15
 #: kallithea/templates/pullrequests/pullrequest.html:97
 msgid "Changesets"
 msgstr ""
 
-#: kallithea/controllers/files.py:772 kallithea/controllers/pullrequests.py:176
+#: kallithea/controllers/files.py:773 kallithea/controllers/pullrequests.py:175
 #: kallithea/model/scm.py:820 kallithea/templates/switch_to_list.html:3
 #: kallithea/templates/branches/branches.html:10
 msgid "Branches"
 msgstr ""
 
-#: kallithea/controllers/files.py:773 kallithea/controllers/pullrequests.py:177
+#: kallithea/controllers/files.py:774 kallithea/controllers/pullrequests.py:176
 #: kallithea/model/scm.py:831 kallithea/templates/switch_to_list.html:25
 #: kallithea/templates/tags/tags.html:10
 msgid "Tags"
@@ -247,8 +243,8 @@
 #: kallithea/templates/admin/users/user_edit_advanced.html:6
 #: kallithea/templates/base/base.html:60 kallithea/templates/base/base.html:77
 #: kallithea/templates/base/base.html:124
-#: kallithea/templates/base/base.html:390
-#: kallithea/templates/base/base.html:562
+#: kallithea/templates/base/base.html:479
+#: kallithea/templates/base/base.html:653
 msgid "Repositories"
 msgstr ""
 
@@ -259,11 +255,15 @@
 msgid "Branch"
 msgstr ""
 
-#: kallithea/controllers/home.py:136
-msgid "Tag"
+#: kallithea/controllers/home.py:136 kallithea/templates/switch_to_list.html:16
+msgid "Closed Branches"
 msgstr ""
 
 #: kallithea/controllers/home.py:142
+msgid "Tag"
+msgstr ""
+
+#: kallithea/controllers/home.py:148
 msgid "Bookmark"
 msgstr ""
 
@@ -274,113 +274,113 @@
 msgstr ""
 
 #: kallithea/controllers/journal.py:115 kallithea/controllers/journal.py:157
-#: kallithea/templates/base/base.html:222
+#: kallithea/templates/base/base.html:306
 #: kallithea/templates/journal/journal.html:4
 #: kallithea/templates/journal/journal.html:12
 msgid "Journal"
 msgstr ""
 
-#: kallithea/controllers/login.py:151 kallithea/controllers/login.py:197
+#: kallithea/controllers/login.py:144 kallithea/controllers/login.py:190
 msgid "Bad captcha"
 msgstr ""
 
-#: kallithea/controllers/login.py:157
+#: kallithea/controllers/login.py:150
 msgid "You have successfully registered into Kallithea"
 msgstr ""
 
-#: kallithea/controllers/login.py:202
+#: kallithea/controllers/login.py:195
 msgid "A password reset confirmation code has been sent"
 msgstr ""
 
-#: kallithea/controllers/login.py:251
+#: kallithea/controllers/login.py:244
 msgid "Invalid password reset token"
 msgstr ""
 
-#: kallithea/controllers/login.py:256
+#: kallithea/controllers/login.py:249
 #: kallithea/controllers/admin/my_account.py:167
 msgid "Successfully updated password"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:124
+#: kallithea/controllers/pullrequests.py:123
 #, python-format
 msgid "%s (closed)"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:152
+#: kallithea/controllers/pullrequests.py:151
 #: kallithea/templates/changeset/changeset.html:12
 #: kallithea/templates/email_templates/changeset_comment.html:17
 msgid "Changeset"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:173
+#: kallithea/controllers/pullrequests.py:172
 msgid "Special"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:174
+#: kallithea/controllers/pullrequests.py:173
 msgid "Peer branches"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:175 kallithea/model/scm.py:826
+#: kallithea/controllers/pullrequests.py:174 kallithea/model/scm.py:826
 #: kallithea/templates/switch_to_list.html:38
 #: kallithea/templates/bookmarks/bookmarks.html:10
 msgid "Bookmarks"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:310
+#: kallithea/controllers/pullrequests.py:309
 #, python-format
 msgid "Error creating pull request: %s"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:356
-#: kallithea/controllers/pullrequests.py:503
+#: kallithea/controllers/pullrequests.py:355
+#: kallithea/controllers/pullrequests.py:502
 msgid "No description"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:363
+#: kallithea/controllers/pullrequests.py:362
 msgid "Successfully opened new pull request"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:366
-#: kallithea/controllers/pullrequests.py:453
+#: kallithea/controllers/pullrequests.py:365
+#: kallithea/controllers/pullrequests.py:452
 #: kallithea/controllers/pullrequests.py:509
 #, python-format
 msgid "Invalid reviewer \"%s\" specified"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:369
-#: kallithea/controllers/pullrequests.py:456
+#: kallithea/controllers/pullrequests.py:368
+#: kallithea/controllers/pullrequests.py:455
 msgid "Error occurred while creating pull request"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:401
+#: kallithea/controllers/pullrequests.py:400
 msgid "Missing changesets since the previous pull request:"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:408
+#: kallithea/controllers/pullrequests.py:407
 #, python-format
 msgid "New changesets on %s %s since the previous pull request:"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:415
+#: kallithea/controllers/pullrequests.py:414
 msgid "Ancestor didn't change - show diff since previous version:"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:422
+#: kallithea/controllers/pullrequests.py:421
 #, python-format
 msgid "This pull request is based on another %s revision and there is no simple diff."
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:424
+#: kallithea/controllers/pullrequests.py:423
 #, python-format
 msgid "No changes found on %s %s since previous version."
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:462
+#: kallithea/controllers/pullrequests.py:461
 #, python-format
 msgid "Closed, replaced by %s ."
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:470
+#: kallithea/controllers/pullrequests.py:469
 msgid "Pull request update created"
 msgstr ""
 
@@ -403,27 +403,32 @@
 
 #: kallithea/controllers/pullrequests.py:614
 #, python-format
-msgid "This pull request can be updated with changes on %s:"
-msgstr ""
-
-#: kallithea/controllers/pullrequests.py:617
+msgid "The following changes are available on %s:"
+msgstr ""
+
+#: kallithea/controllers/pullrequests.py:618
 msgid "No changesets found for updating this pull request."
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:625
+#: kallithea/controllers/pullrequests.py:626
 #, python-format
 msgid "Note: Branch %s has another head: %s."
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:631
+#: kallithea/controllers/pullrequests.py:632
 msgid "Git pull requests don't support updates yet."
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:722
+#: kallithea/controllers/pullrequests.py:724
 msgid "No permission to change pull request status"
 msgstr ""
 
-#: kallithea/controllers/pullrequests.py:727
+#: kallithea/controllers/pullrequests.py:735
+#, python-format
+msgid "Successfully deleted pull request %s"
+msgstr ""
+
+#: kallithea/controllers/pullrequests.py:745
 msgid "Closing."
 msgstr ""
 
@@ -439,12 +444,12 @@
 msgid "An error occurred during search operation."
 msgstr ""
 
-#: kallithea/controllers/summary.py:180
+#: kallithea/controllers/summary.py:181
 #: kallithea/templates/summary/summary.html:384
 msgid "No data ready yet"
 msgstr ""
 
-#: kallithea/controllers/summary.py:183
+#: kallithea/controllers/summary.py:184
 #: kallithea/templates/summary/summary.html:98
 msgid "Statistics are disabled for this repository"
 msgstr ""
@@ -465,64 +470,64 @@
 msgid "Error occurred during update of defaults"
 msgstr ""
 
-#: kallithea/controllers/admin/gists.py:59
+#: kallithea/controllers/admin/gists.py:58
 #: kallithea/controllers/admin/my_account.py:243
+#: kallithea/controllers/admin/users.py:284
+msgid "Forever"
+msgstr ""
+
+#: kallithea/controllers/admin/gists.py:59
+#: kallithea/controllers/admin/my_account.py:244
 #: kallithea/controllers/admin/users.py:285
-msgid "Forever"
+msgid "5 minutes"
 msgstr ""
 
 #: kallithea/controllers/admin/gists.py:60
-#: kallithea/controllers/admin/my_account.py:244
+#: kallithea/controllers/admin/my_account.py:245
 #: kallithea/controllers/admin/users.py:286
-msgid "5 minutes"
+msgid "1 hour"
 msgstr ""
 
 #: kallithea/controllers/admin/gists.py:61
-#: kallithea/controllers/admin/my_account.py:245
+#: kallithea/controllers/admin/my_account.py:246
 #: kallithea/controllers/admin/users.py:287
-msgid "1 hour"
+msgid "1 day"
 msgstr ""
 
 #: kallithea/controllers/admin/gists.py:62
-#: kallithea/controllers/admin/my_account.py:246
+#: kallithea/controllers/admin/my_account.py:247
 #: kallithea/controllers/admin/users.py:288
-msgid "1 day"
-msgstr ""
-
-#: kallithea/controllers/admin/gists.py:63
-#: kallithea/controllers/admin/my_account.py:247
-#: kallithea/controllers/admin/users.py:289
 msgid "1 month"
 msgstr ""
 
-#: kallithea/controllers/admin/gists.py:67
+#: kallithea/controllers/admin/gists.py:66
 #: kallithea/controllers/admin/my_account.py:249
-#: kallithea/controllers/admin/users.py:291
+#: kallithea/controllers/admin/users.py:290
 msgid "Lifetime"
 msgstr ""
 
-#: kallithea/controllers/admin/gists.py:146
+#: kallithea/controllers/admin/gists.py:145
 msgid "Error occurred during gist creation"
 msgstr ""
 
-#: kallithea/controllers/admin/gists.py:184
+#: kallithea/controllers/admin/gists.py:183
 #, python-format
 msgid "Deleted gist %s"
 msgstr ""
 
-#: kallithea/controllers/admin/gists.py:233
+#: kallithea/controllers/admin/gists.py:232
 msgid "Unmodified"
 msgstr ""
 
-#: kallithea/controllers/admin/gists.py:262
+#: kallithea/controllers/admin/gists.py:261
 msgid "Successfully updated gist content"
 msgstr ""
 
-#: kallithea/controllers/admin/gists.py:267
+#: kallithea/controllers/admin/gists.py:266
 msgid "Successfully updated gist data"
 msgstr ""
 
-#: kallithea/controllers/admin/gists.py:270
+#: kallithea/controllers/admin/gists.py:269
 #, python-format
 msgid "Error occurred during update of gist %s"
 msgstr ""
@@ -537,7 +542,7 @@
 msgstr ""
 
 #: kallithea/controllers/admin/my_account.py:144
-#: kallithea/controllers/admin/users.py:202
+#: kallithea/controllers/admin/users.py:201
 #, python-format
 msgid "Error occurred during update of user %s"
 msgstr ""
@@ -547,33 +552,33 @@
 msgstr ""
 
 #: kallithea/controllers/admin/my_account.py:220
-#: kallithea/controllers/admin/users.py:415
+#: kallithea/controllers/admin/users.py:414
 #, python-format
 msgid "Added email %s to user"
 msgstr ""
 
 #: kallithea/controllers/admin/my_account.py:226
-#: kallithea/controllers/admin/users.py:421
+#: kallithea/controllers/admin/users.py:420
 msgid "An error occurred during email saving"
 msgstr ""
 
 #: kallithea/controllers/admin/my_account.py:235
-#: kallithea/controllers/admin/users.py:433
+#: kallithea/controllers/admin/users.py:432
 msgid "Removed email from user"
 msgstr ""
 
 #: kallithea/controllers/admin/my_account.py:259
-#: kallithea/controllers/admin/users.py:308
+#: kallithea/controllers/admin/users.py:307
 msgid "API key successfully created"
 msgstr ""
 
 #: kallithea/controllers/admin/my_account.py:271
-#: kallithea/controllers/admin/users.py:321
+#: kallithea/controllers/admin/users.py:320
 msgid "API key successfully reset"
 msgstr ""
 
 #: kallithea/controllers/admin/my_account.py:275
-#: kallithea/controllers/admin/users.py:325
+#: kallithea/controllers/admin/users.py:324
 msgid "API key successfully deleted"
 msgstr ""
 
@@ -623,10 +628,10 @@
 #: kallithea/templates/admin/users/user_edit_profile.html:105
 #: kallithea/templates/admin/users/users.html:10
 #: kallithea/templates/admin/users/users.html:55
-#: kallithea/templates/base/base.html:252
-#: kallithea/templates/base/base.html:253
-#: kallithea/templates/base/base.html:259
-#: kallithea/templates/base/base.html:260
+#: kallithea/templates/base/base.html:336
+#: kallithea/templates/base/base.html:337
+#: kallithea/templates/base/base.html:343
+#: kallithea/templates/base/base.html:344
 #: kallithea/templates/base/perms_summary.html:17
 msgid "Admin"
 msgstr ""
@@ -657,7 +662,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1564
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1603
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1655
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1682 kallithea/model/db.py:1701
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1682 kallithea/model/db.py:1707
 msgid "Manual activation of external account"
 msgstr ""
 
@@ -669,7 +674,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1565
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1604
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1656
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1683 kallithea/model/db.py:1702
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1683 kallithea/model/db.py:1708
 msgid "Automatic activation of external account"
 msgstr ""
 
@@ -690,240 +695,240 @@
 msgid "Error occurred during update of permissions"
 msgstr ""
 
-#: kallithea/controllers/admin/repo_groups.py:188
+#: kallithea/controllers/admin/repo_groups.py:187
 #, python-format
 msgid "Error occurred during creation of repository group %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repo_groups.py:193
+#: kallithea/controllers/admin/repo_groups.py:192
 #, python-format
 msgid "Created repository group %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repo_groups.py:250
+#: kallithea/controllers/admin/repo_groups.py:249
 #, python-format
 msgid "Updated repository group %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repo_groups.py:266
+#: kallithea/controllers/admin/repo_groups.py:265
 #, python-format
 msgid "Error occurred during update of repository group %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repo_groups.py:284
+#: kallithea/controllers/admin/repo_groups.py:283
 #, python-format
 msgid "This group contains %s repositories and cannot be deleted"
 msgstr ""
 
-#: kallithea/controllers/admin/repo_groups.py:291
+#: kallithea/controllers/admin/repo_groups.py:290
 #, python-format
 msgid "This group contains %s subgroups and cannot be deleted"
 msgstr ""
 
-#: kallithea/controllers/admin/repo_groups.py:297
+#: kallithea/controllers/admin/repo_groups.py:296
 #, python-format
 msgid "Removed repository group %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repo_groups.py:302
+#: kallithea/controllers/admin/repo_groups.py:301
 #, python-format
 msgid "Error occurred during deletion of repository group %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repo_groups.py:405
-#: kallithea/controllers/admin/repo_groups.py:440
+#: kallithea/controllers/admin/repo_groups.py:404
+#: kallithea/controllers/admin/repo_groups.py:439
 #: kallithea/controllers/admin/user_groups.py:340
 msgid "Cannot revoke permission for yourself as admin"
 msgstr ""
 
-#: kallithea/controllers/admin/repo_groups.py:420
+#: kallithea/controllers/admin/repo_groups.py:419
 msgid "Repository group permissions updated"
 msgstr ""
 
-#: kallithea/controllers/admin/repo_groups.py:457
-#: kallithea/controllers/admin/repos.py:398
+#: kallithea/controllers/admin/repo_groups.py:456
+#: kallithea/controllers/admin/repos.py:397
 #: kallithea/controllers/admin/user_groups.py:352
 msgid "An error occurred during revoking of permission"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:152
+#: kallithea/controllers/admin/repos.py:151
 #, python-format
 msgid "Error creating repository %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:213
+#: kallithea/controllers/admin/repos.py:212
 #, python-format
 msgid "Created repository %s from %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:222
+#: kallithea/controllers/admin/repos.py:221
 #, python-format
 msgid "Forked repository %s as %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:225
+#: kallithea/controllers/admin/repos.py:224
 #, python-format
 msgid "Created repository %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:262
+#: kallithea/controllers/admin/repos.py:261
 #, python-format
 msgid "Repository %s updated successfully"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:283
+#: kallithea/controllers/admin/repos.py:282
 #, python-format
 msgid "Error occurred during update of repository %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:310
+#: kallithea/controllers/admin/repos.py:309
 #, python-format
 msgid "Detached %s forks"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:313
+#: kallithea/controllers/admin/repos.py:312
 #, python-format
 msgid "Deleted %s forks"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:318
+#: kallithea/controllers/admin/repos.py:317
 #, python-format
 msgid "Deleted repository %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:321
+#: kallithea/controllers/admin/repos.py:320
 #, python-format
 msgid "Cannot delete repository %s which still has forks"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:326
+#: kallithea/controllers/admin/repos.py:325
 #, python-format
 msgid "An error occurred during deletion of %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:374
+#: kallithea/controllers/admin/repos.py:373
 msgid "Repository permissions updated"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:430
+#: kallithea/controllers/admin/repos.py:429
 msgid "An error occurred during creation of field"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:444
+#: kallithea/controllers/admin/repos.py:443
 msgid "An error occurred during removal of field"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:460
+#: kallithea/controllers/admin/repos.py:459
 msgid "-- Not a fork --"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:491
+#: kallithea/controllers/admin/repos.py:490
 msgid "Updated repository visibility in public journal"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:495
+#: kallithea/controllers/admin/repos.py:494
 msgid "An error occurred during setting this repository in public journal"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:512
+#: kallithea/controllers/admin/repos.py:511
 msgid "Nothing"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:514
+#: kallithea/controllers/admin/repos.py:513
 #, python-format
 msgid "Marked repository %s as fork of %s"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:521
+#: kallithea/controllers/admin/repos.py:520
 msgid "An error occurred during this operation"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:537
-#: kallithea/controllers/admin/repos.py:564
+#: kallithea/controllers/admin/repos.py:536
+#: kallithea/controllers/admin/repos.py:563
 msgid "Repository has been locked"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:540
-#: kallithea/controllers/admin/repos.py:561
+#: kallithea/controllers/admin/repos.py:539
+#: kallithea/controllers/admin/repos.py:560
 msgid "Repository has been unlocked"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:543
-#: kallithea/controllers/admin/repos.py:568
+#: kallithea/controllers/admin/repos.py:542
+#: kallithea/controllers/admin/repos.py:567
 msgid "An error occurred during unlocking"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:582
+#: kallithea/controllers/admin/repos.py:581
 msgid "Cache invalidation successful"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:586
+#: kallithea/controllers/admin/repos.py:585
 msgid "An error occurred during cache invalidation"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:601
+#: kallithea/controllers/admin/repos.py:600
 msgid "Pulled from remote location"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:604
+#: kallithea/controllers/admin/repos.py:603
 msgid "An error occurred during pull from remote location"
 msgstr ""
 
-#: kallithea/controllers/admin/repos.py:637
+#: kallithea/controllers/admin/repos.py:636
 msgid "An error occurred during deletion of repository stats"
 msgstr ""
 
-#: kallithea/controllers/admin/settings.py:170
+#: kallithea/controllers/admin/settings.py:141
 msgid "Updated VCS settings"
 msgstr ""
 
-#: kallithea/controllers/admin/settings.py:174
+#: kallithea/controllers/admin/settings.py:145
 msgid "Unable to activate hgsubversion support. The \"hgsubversion\" library is missing"
 msgstr ""
 
-#: kallithea/controllers/admin/settings.py:180
-#: kallithea/controllers/admin/settings.py:277
+#: kallithea/controllers/admin/settings.py:151
+#: kallithea/controllers/admin/settings.py:248
 msgid "Error occurred while updating application settings"
 msgstr ""
 
-#: kallithea/controllers/admin/settings.py:216
+#: kallithea/controllers/admin/settings.py:187
 #, python-format
 msgid "Repositories successfully rescanned. Added: %s. Removed: %s."
 msgstr ""
 
-#: kallithea/controllers/admin/settings.py:273
+#: kallithea/controllers/admin/settings.py:244
 msgid "Updated application settings"
 msgstr ""
 
-#: kallithea/controllers/admin/settings.py:330
+#: kallithea/controllers/admin/settings.py:301
 msgid "Updated visualisation settings"
 msgstr ""
 
-#: kallithea/controllers/admin/settings.py:335
+#: kallithea/controllers/admin/settings.py:306
 msgid "Error occurred during updating visualisation settings"
 msgstr ""
 
-#: kallithea/controllers/admin/settings.py:361
+#: kallithea/controllers/admin/settings.py:332
 msgid "Please enter email address"
 msgstr ""
 
-#: kallithea/controllers/admin/settings.py:376
+#: kallithea/controllers/admin/settings.py:347
 msgid "Send email task created"
 msgstr ""
 
-#: kallithea/controllers/admin/settings.py:407
+#: kallithea/controllers/admin/settings.py:378
 msgid "Added new hook"
 msgstr ""
 
-#: kallithea/controllers/admin/settings.py:421
+#: kallithea/controllers/admin/settings.py:392
 msgid "Updated hooks"
 msgstr ""
 
-#: kallithea/controllers/admin/settings.py:425
+#: kallithea/controllers/admin/settings.py:396
 msgid "Error occurred during hook creation"
 msgstr ""
 
-#: kallithea/controllers/admin/settings.py:451
+#: kallithea/controllers/admin/settings.py:422
 msgid "Whoosh reindex task scheduled"
 msgstr ""
 
@@ -964,76 +969,80 @@
 msgstr ""
 
 #: kallithea/controllers/admin/user_groups.py:440
-#: kallithea/controllers/admin/users.py:384
+#: kallithea/controllers/admin/users.py:383
 msgid "Updated permissions"
 msgstr ""
 
 #: kallithea/controllers/admin/user_groups.py:444
-#: kallithea/controllers/admin/users.py:388
+#: kallithea/controllers/admin/users.py:387
 msgid "An error occurred during permissions saving"
 msgstr ""
 
-#: kallithea/controllers/admin/users.py:134
+#: kallithea/controllers/admin/users.py:133
 #, python-format
 msgid "Created user %s"
 msgstr ""
 
-#: kallithea/controllers/admin/users.py:149
+#: kallithea/controllers/admin/users.py:148
 #, python-format
 msgid "Error occurred during creation of user %s"
 msgstr ""
 
-#: kallithea/controllers/admin/users.py:182
+#: kallithea/controllers/admin/users.py:181
 msgid "User updated successfully"
 msgstr ""
 
-#: kallithea/controllers/admin/users.py:218
+#: kallithea/controllers/admin/users.py:217
 msgid "Successfully deleted user"
 msgstr ""
 
-#: kallithea/controllers/admin/users.py:223
+#: kallithea/controllers/admin/users.py:222
 msgid "An error occurred during deletion of user"
 msgstr ""
 
-#: kallithea/controllers/admin/users.py:236
+#: kallithea/controllers/admin/users.py:235
 msgid "The default user cannot be edited"
 msgstr ""
 
-#: kallithea/controllers/admin/users.py:463
+#: kallithea/controllers/admin/users.py:462
 #, python-format
 msgid "Added IP address %s to user whitelist"
 msgstr ""
 
-#: kallithea/controllers/admin/users.py:469
+#: kallithea/controllers/admin/users.py:468
 msgid "An error occurred while adding IP address"
 msgstr ""
 
-#: kallithea/controllers/admin/users.py:483
+#: kallithea/controllers/admin/users.py:482
 msgid "Removed IP address from user whitelist"
 msgstr ""
 
-#: kallithea/lib/auth.py:743
+#: kallithea/lib/auth.py:737
 #, python-format
 msgid "IP %s not allowed"
 msgstr ""
 
-#: kallithea/lib/auth.py:756
+#: kallithea/lib/auth.py:750
 msgid "Invalid API key"
 msgstr ""
 
-#: kallithea/lib/auth.py:812
+#: kallithea/lib/auth.py:768
+msgid "CSRF token leak has been detected - all form tokens have been expired"
+msgstr ""
+
+#: kallithea/lib/auth.py:813
 msgid "You need to be a registered user to perform this action"
 msgstr ""
 
-#: kallithea/lib/auth.py:844
+#: kallithea/lib/auth.py:843
 msgid "You need to be signed in to view this page"
 msgstr ""
 
-#: kallithea/lib/base.py:490
+#: kallithea/lib/base.py:493
 msgid "Repository not found in the filesystem"
 msgstr ""
 
-#: kallithea/lib/base.py:516 kallithea/lib/helpers.py:622
+#: kallithea/lib/base.py:519 kallithea/lib/helpers.py:618
 msgid "Changeset not found"
 msgstr ""
 
@@ -1049,227 +1058,227 @@
 msgid "No changes detected"
 msgstr ""
 
-#: kallithea/lib/helpers.py:609
+#: kallithea/lib/helpers.py:605
 #, python-format
 msgid "Deleted branch: %s"
 msgstr ""
 
-#: kallithea/lib/helpers.py:611
+#: kallithea/lib/helpers.py:607
 #, python-format
 msgid "Created tag: %s"
 msgstr ""
 
-#: kallithea/lib/helpers.py:671
+#: kallithea/lib/helpers.py:667
 #, python-format
 msgid "Show all combined changesets %s->%s"
 msgstr ""
 
-#: kallithea/lib/helpers.py:677
+#: kallithea/lib/helpers.py:673
 msgid "Compare view"
 msgstr ""
 
-#: kallithea/lib/helpers.py:696
+#: kallithea/lib/helpers.py:692
 msgid "and"
 msgstr ""
 
-#: kallithea/lib/helpers.py:697
+#: kallithea/lib/helpers.py:693
 #, python-format
 msgid "%s more"
 msgstr ""
 
-#: kallithea/lib/helpers.py:698 kallithea/templates/changelog/changelog.html:44
+#: kallithea/lib/helpers.py:694 kallithea/templates/changelog/changelog.html:44
 msgid "revisions"
 msgstr ""
 
-#: kallithea/lib/helpers.py:722
+#: kallithea/lib/helpers.py:718
 #, python-format
 msgid "Fork name %s"
 msgstr ""
 
-#: kallithea/lib/helpers.py:742
+#: kallithea/lib/helpers.py:738
 #, python-format
 msgid "Pull request %s"
 msgstr ""
 
+#: kallithea/lib/helpers.py:748
+msgid "[deleted] repository"
+msgstr ""
+
+#: kallithea/lib/helpers.py:750 kallithea/lib/helpers.py:762
+msgid "[created] repository"
+msgstr ""
+
 #: kallithea/lib/helpers.py:752
-msgid "[deleted] repository"
-msgstr ""
-
-#: kallithea/lib/helpers.py:754 kallithea/lib/helpers.py:766
-msgid "[created] repository"
-msgstr ""
-
-#: kallithea/lib/helpers.py:756
 msgid "[created] repository as fork"
 msgstr ""
 
-#: kallithea/lib/helpers.py:758 kallithea/lib/helpers.py:768
+#: kallithea/lib/helpers.py:754 kallithea/lib/helpers.py:764
 msgid "[forked] repository"
 msgstr ""
 
-#: kallithea/lib/helpers.py:760 kallithea/lib/helpers.py:770
+#: kallithea/lib/helpers.py:756 kallithea/lib/helpers.py:766
 msgid "[updated] repository"
 msgstr ""
 
-#: kallithea/lib/helpers.py:762
+#: kallithea/lib/helpers.py:758
 msgid "[downloaded] archive from repository"
 msgstr ""
 
-#: kallithea/lib/helpers.py:764
+#: kallithea/lib/helpers.py:760
 msgid "[delete] repository"
 msgstr ""
 
+#: kallithea/lib/helpers.py:768
+msgid "[created] user"
+msgstr ""
+
+#: kallithea/lib/helpers.py:770
+msgid "[updated] user"
+msgstr ""
+
 #: kallithea/lib/helpers.py:772
-msgid "[created] user"
+msgid "[created] user group"
 msgstr ""
 
 #: kallithea/lib/helpers.py:774
-msgid "[updated] user"
+msgid "[updated] user group"
 msgstr ""
 
 #: kallithea/lib/helpers.py:776
-msgid "[created] user group"
+msgid "[commented] on revision in repository"
 msgstr ""
 
 #: kallithea/lib/helpers.py:778
-msgid "[updated] user group"
+msgid "[commented] on pull request for"
 msgstr ""
 
 #: kallithea/lib/helpers.py:780
-msgid "[commented] on revision in repository"
+msgid "[closed] pull request for"
 msgstr ""
 
 #: kallithea/lib/helpers.py:782
-msgid "[commented] on pull request for"
+msgid "[pushed] into"
 msgstr ""
 
 #: kallithea/lib/helpers.py:784
-msgid "[closed] pull request for"
+msgid "[committed via Kallithea] into repository"
 msgstr ""
 
 #: kallithea/lib/helpers.py:786
-msgid "[pushed] into"
+msgid "[pulled from remote] into repository"
 msgstr ""
 
 #: kallithea/lib/helpers.py:788
-msgid "[committed via Kallithea] into repository"
+msgid "[pulled] from"
 msgstr ""
 
 #: kallithea/lib/helpers.py:790
-msgid "[pulled from remote] into repository"
+msgid "[started following] repository"
 msgstr ""
 
 #: kallithea/lib/helpers.py:792
-msgid "[pulled] from"
-msgstr ""
-
-#: kallithea/lib/helpers.py:794
-msgid "[started following] repository"
-msgstr ""
-
-#: kallithea/lib/helpers.py:796
 msgid "[stopped following] repository"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1124
+#: kallithea/lib/helpers.py:1119
 #, python-format
 msgid " and %s more"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1128
-#: kallithea/templates/compare/compare_diff.html:65
-#: kallithea/templates/pullrequests/pullrequest_show.html:326
+#: kallithea/lib/helpers.py:1123
+#: kallithea/templates/compare/compare_diff.html:71
+#: kallithea/templates/pullrequests/pullrequest_show.html:337
 msgid "No files"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1194
+#: kallithea/lib/helpers.py:1189
 msgid "new file"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1197
+#: kallithea/lib/helpers.py:1192
 msgid "mod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1200
+#: kallithea/lib/helpers.py:1195
 msgid "del"
 msgstr ""
 
+#: kallithea/lib/helpers.py:1198
+msgid "rename"
+msgstr ""
+
 #: kallithea/lib/helpers.py:1203
-msgid "rename"
-msgstr ""
-
-#: kallithea/lib/helpers.py:1208
 msgid "chmod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1444
+#: kallithea/lib/helpers.py:1464
 #, python-format
 msgid "%s repository is not mapped to db perhaps it was created or renamed from the filesystem please run the application again in order to rescan repositories"
 msgstr ""
 
-#: kallithea/lib/utils2.py:415
+#: kallithea/lib/utils2.py:434
 #, python-format
 msgid "%d year"
 msgid_plural "%d years"
 msgstr[0] ""
 msgstr[1] ""
 
-#: kallithea/lib/utils2.py:416
+#: kallithea/lib/utils2.py:435
 #, python-format
 msgid "%d month"
 msgid_plural "%d months"
 msgstr[0] ""
 msgstr[1] ""
 
-#: kallithea/lib/utils2.py:417
+#: kallithea/lib/utils2.py:436
 #, python-format
 msgid "%d day"
 msgid_plural "%d days"
 msgstr[0] ""
 msgstr[1] ""
 
-#: kallithea/lib/utils2.py:418
+#: kallithea/lib/utils2.py:437
 #, python-format
 msgid "%d hour"
 msgid_plural "%d hours"
 msgstr[0] ""
 msgstr[1] ""
 
-#: kallithea/lib/utils2.py:419
+#: kallithea/lib/utils2.py:438
 #, python-format
 msgid "%d minute"
 msgid_plural "%d minutes"
 msgstr[0] ""
 msgstr[1] ""
 
-#: kallithea/lib/utils2.py:420
+#: kallithea/lib/utils2.py:439
 #, python-format
 msgid "%d second"
 msgid_plural "%d seconds"
 msgstr[0] ""
 msgstr[1] ""
 
-#: kallithea/lib/utils2.py:436
+#: kallithea/lib/utils2.py:455
 #, python-format
 msgid "in %s"
 msgstr ""
 
-#: kallithea/lib/utils2.py:438
+#: kallithea/lib/utils2.py:457
 #, python-format
 msgid "%s ago"
 msgstr ""
 
-#: kallithea/lib/utils2.py:440
+#: kallithea/lib/utils2.py:459
 #, python-format
 msgid "in %s and %s"
 msgstr ""
 
-#: kallithea/lib/utils2.py:443
+#: kallithea/lib/utils2.py:462
 #, python-format
 msgid "%s and %s ago"
 msgstr ""
 
-#: kallithea/lib/utils2.py:446
+#: kallithea/lib/utils2.py:465
 msgid "just now"
 msgstr ""
 
@@ -1368,7 +1377,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1531
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1570
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1620
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1647 kallithea/model/db.py:1665
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1647 kallithea/model/db.py:1671
 msgid "Kallithea Administrator"
 msgstr ""
 
@@ -1479,7 +1488,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:2063
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:2102
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:2155
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:2201 kallithea/model/db.py:2229
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:2201 kallithea/model/db.py:2238
 msgid "Approved"
 msgstr ""
 
@@ -1494,7 +1503,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:2064
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:2103
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:2156
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:2202 kallithea/model/db.py:2230
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:2202 kallithea/model/db.py:2239
 msgid "Rejected"
 msgstr ""
 
@@ -1521,7 +1530,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1379
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1418
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1471
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1498 kallithea/model/db.py:1514
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1498 kallithea/model/db.py:1520
 msgid "top level"
 msgstr ""
 
@@ -1668,7 +1677,7 @@
 #: kallithea/lib/dbmigrate/schema/db_2_0_2.py:1560
 #: kallithea/lib/dbmigrate/schema/db_2_1_0.py:1599
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1651
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1678 kallithea/model/db.py:1697
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1678 kallithea/model/db.py:1703
 msgid "Registration disabled"
 msgstr ""
 
@@ -1695,12 +1704,12 @@
 msgstr ""
 
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1645
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1672 kallithea/model/db.py:1691
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1672 kallithea/model/db.py:1697
 msgid "Repository creation enabled with write permission to a repository group"
 msgstr ""
 
 #: kallithea/lib/dbmigrate/schema/db_2_2_0.py:1646
-#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1673 kallithea/model/db.py:1692
+#: kallithea/lib/dbmigrate/schema/db_2_2_3.py:1673 kallithea/model/db.py:1698
 msgid "Repository creation disabled with write permission to a repository group"
 msgstr ""
 
@@ -1709,103 +1718,103 @@
 msgid "on line %s"
 msgstr ""
 
-#: kallithea/model/comment.py:217 kallithea/model/pull_request.py:169
+#: kallithea/model/comment.py:217 kallithea/model/pull_request.py:170
 msgid "[Mention]"
 msgstr ""
 
-#: kallithea/model/db.py:1667
-msgid "Default user has no access to new repositories"
-msgstr ""
-
-#: kallithea/model/db.py:1668
-msgid "Default user has read access to new repositories"
-msgstr ""
-
-#: kallithea/model/db.py:1669
-msgid "Default user has write access to new repositories"
-msgstr ""
-
-#: kallithea/model/db.py:1670
-msgid "Default user has admin access to new repositories"
-msgstr ""
-
-#: kallithea/model/db.py:1672
-msgid "Default user has no access to new repository groups"
-msgstr ""
-
 #: kallithea/model/db.py:1673
-msgid "Default user has read access to new repository groups"
+msgid "Default user has no access to new repositories"
 msgstr ""
 
 #: kallithea/model/db.py:1674
-msgid "Default user has write access to new repository groups"
+msgid "Default user has read access to new repositories"
 msgstr ""
 
 #: kallithea/model/db.py:1675
-msgid "Default user has admin access to new repository groups"
-msgstr ""
-
-#: kallithea/model/db.py:1677
-msgid "Default user has no access to new user groups"
+msgid "Default user has write access to new repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1676
+msgid "Default user has admin access to new repositories"
 msgstr ""
 
 #: kallithea/model/db.py:1678
-msgid "Default user has read access to new user groups"
+msgid "Default user has no access to new repository groups"
 msgstr ""
 
 #: kallithea/model/db.py:1679
-msgid "Default user has write access to new user groups"
+msgid "Default user has read access to new repository groups"
 msgstr ""
 
 #: kallithea/model/db.py:1680
-msgid "Default user has admin access to new user groups"
-msgstr ""
-
-#: kallithea/model/db.py:1682
-msgid "Only admins can create repository groups"
+msgid "Default user has write access to new repository groups"
+msgstr ""
+
+#: kallithea/model/db.py:1681
+msgid "Default user has admin access to new repository groups"
 msgstr ""
 
 #: kallithea/model/db.py:1683
-msgid "Non-admins can create repository groups"
+msgid "Default user has no access to new user groups"
+msgstr ""
+
+#: kallithea/model/db.py:1684
+msgid "Default user has read access to new user groups"
 msgstr ""
 
 #: kallithea/model/db.py:1685
-msgid "Only admins can create user groups"
+msgid "Default user has write access to new user groups"
 msgstr ""
 
 #: kallithea/model/db.py:1686
-msgid "Non-admins can create user groups"
+msgid "Default user has admin access to new user groups"
 msgstr ""
 
 #: kallithea/model/db.py:1688
-msgid "Only admins can create top level repositories"
+msgid "Only admins can create repository groups"
 msgstr ""
 
 #: kallithea/model/db.py:1689
-msgid "Non-admins can create top level repositories"
+msgid "Non-admins can create repository groups"
+msgstr ""
+
+#: kallithea/model/db.py:1691
+msgid "Only admins can create user groups"
+msgstr ""
+
+#: kallithea/model/db.py:1692
+msgid "Non-admins can create user groups"
 msgstr ""
 
 #: kallithea/model/db.py:1694
-msgid "Only admins can fork repositories"
+msgid "Only admins can create top level repositories"
 msgstr ""
 
 #: kallithea/model/db.py:1695
-msgid "Non-admins can can fork repositories"
-msgstr ""
-
-#: kallithea/model/db.py:1698
+msgid "Non-admins can create top level repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1700
+msgid "Only admins can fork repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1701
+msgid "Non-admins can fork repositories"
+msgstr ""
+
+#: kallithea/model/db.py:1704
 msgid "User registration with manual account activation"
 msgstr ""
 
-#: kallithea/model/db.py:1699
+#: kallithea/model/db.py:1705
 msgid "User registration with automatic account activation"
 msgstr ""
 
-#: kallithea/model/db.py:2228
+#: kallithea/model/db.py:2237
 msgid "Not reviewed"
 msgstr ""
 
-#: kallithea/model/db.py:2231
+#: kallithea/model/db.py:2240
 msgid "Under review"
 msgstr ""
 
@@ -1827,7 +1836,7 @@
 msgid "Enter %(min)i characters or more"
 msgstr ""
 
-#: kallithea/model/forms.py:160
+#: kallithea/model/forms.py:165
 msgid "Name must not contain only digits"
 msgstr ""
 
@@ -1947,15 +1956,15 @@
 msgid "User \"%s\" still owns %s user groups and cannot be removed. Switch owners or remove those user groups: %s"
 msgstr ""
 
-#: kallithea/model/user.py:360
+#: kallithea/model/user.py:368
 msgid "Password reset link"
 msgstr ""
 
-#: kallithea/model/user.py:408
+#: kallithea/model/user.py:418
 msgid "Password reset notification"
 msgstr ""
 
-#: kallithea/model/user.py:409
+#: kallithea/model/user.py:419
 #, python-format
 msgid "The password to your account %s has been changed using password reset form."
 msgstr ""
@@ -1964,159 +1973,159 @@
 msgid "Value cannot be an empty list"
 msgstr ""
 
-#: kallithea/model/validators.py:95
+#: kallithea/model/validators.py:96
 #, python-format
 msgid "Username \"%(username)s\" already exists"
 msgstr ""
 
-#: kallithea/model/validators.py:97
+#: kallithea/model/validators.py:98
 #, python-format
 msgid "Username \"%(username)s\" cannot be used"
 msgstr ""
 
-#: kallithea/model/validators.py:99
+#: kallithea/model/validators.py:100
 msgid "Username may only contain alphanumeric characters underscores, periods or dashes and must begin with an alphanumeric character or underscore"
 msgstr ""
 
-#: kallithea/model/validators.py:126
+#: kallithea/model/validators.py:127
 msgid "The input is not valid"
 msgstr ""
 
-#: kallithea/model/validators.py:133
+#: kallithea/model/validators.py:134
 #, python-format
 msgid "Username %(username)s is not valid"
 msgstr ""
 
-#: kallithea/model/validators.py:152
+#: kallithea/model/validators.py:154
 msgid "Invalid user group name"
 msgstr ""
 
-#: kallithea/model/validators.py:153
-#, python-format
-msgid "User group \"%(usergroup)s\" already exists"
-msgstr ""
-
 #: kallithea/model/validators.py:155
+#, python-format
+msgid "User group \"%(usergroup)s\" already exists"
+msgstr ""
+
+#: kallithea/model/validators.py:157
 msgid "user group name may only contain alphanumeric characters underscores, periods or dashes and must begin with alphanumeric character"
 msgstr ""
 
-#: kallithea/model/validators.py:193
+#: kallithea/model/validators.py:197
 msgid "Cannot assign this group as parent"
 msgstr ""
 
-#: kallithea/model/validators.py:194
+#: kallithea/model/validators.py:198
 #, python-format
 msgid "Group \"%(group_name)s\" already exists"
 msgstr ""
 
-#: kallithea/model/validators.py:196
+#: kallithea/model/validators.py:200
 #, python-format
 msgid "Repository with name \"%(group_name)s\" already exists"
 msgstr ""
 
-#: kallithea/model/validators.py:254
+#: kallithea/model/validators.py:258
 msgid "Invalid characters (non-ascii) in password"
 msgstr ""
 
-#: kallithea/model/validators.py:269
+#: kallithea/model/validators.py:273
 msgid "Invalid old password"
 msgstr ""
 
-#: kallithea/model/validators.py:285
+#: kallithea/model/validators.py:289
 msgid "Passwords do not match"
 msgstr ""
 
-#: kallithea/model/validators.py:300
+#: kallithea/model/validators.py:304
 msgid "Invalid username or password"
 msgstr ""
 
-#: kallithea/model/validators.py:331
+#: kallithea/model/validators.py:335
 msgid "Token mismatch"
 msgstr ""
 
-#: kallithea/model/validators.py:345
+#: kallithea/model/validators.py:351
 #, python-format
 msgid "Repository name %(repo)s is not allowed"
 msgstr ""
 
-#: kallithea/model/validators.py:347
+#: kallithea/model/validators.py:353
 #, python-format
 msgid "Repository named %(repo)s already exists"
 msgstr ""
 
-#: kallithea/model/validators.py:348
+#: kallithea/model/validators.py:354
 #, python-format
 msgid "Repository \"%(repo)s\" already exists in group \"%(group)s\""
 msgstr ""
 
-#: kallithea/model/validators.py:350
+#: kallithea/model/validators.py:356
 #, python-format
 msgid "Repository group with name \"%(repo)s\" already exists"
 msgstr ""
 
-#: kallithea/model/validators.py:465
+#: kallithea/model/validators.py:470
 msgid "Invalid repository URL"
 msgstr ""
 
-#: kallithea/model/validators.py:466
+#: kallithea/model/validators.py:471
 msgid "Invalid repository URL. It must be a valid http, https, ssh, svn+http or svn+https URL"
 msgstr ""
 
-#: kallithea/model/validators.py:489
+#: kallithea/model/validators.py:496
 msgid "Fork has to be the same type as parent"
 msgstr ""
 
-#: kallithea/model/validators.py:504
+#: kallithea/model/validators.py:511
 msgid "You don't have permissions to create repository in this group"
 msgstr ""
 
-#: kallithea/model/validators.py:506
+#: kallithea/model/validators.py:513
 msgid "no permission to create repository in root location"
 msgstr ""
 
-#: kallithea/model/validators.py:556
+#: kallithea/model/validators.py:563
 msgid "You don't have permissions to create a group in this location"
 msgstr ""
 
-#: kallithea/model/validators.py:597
+#: kallithea/model/validators.py:604
 msgid "This username or user group name is not valid"
 msgstr ""
 
-#: kallithea/model/validators.py:690
+#: kallithea/model/validators.py:697
 msgid "This is not a valid path"
 msgstr ""
 
-#: kallithea/model/validators.py:705
+#: kallithea/model/validators.py:714
 msgid "This email address is already in use"
 msgstr ""
 
-#: kallithea/model/validators.py:725
+#: kallithea/model/validators.py:734
 #, python-format
 msgid "Email address \"%(email)s\" not found"
 msgstr ""
 
-#: kallithea/model/validators.py:762
+#: kallithea/model/validators.py:771
 msgid "The LDAP Login attribute of the CN must be specified - this is the name of the attribute that is equivalent to \"username\""
 msgstr ""
 
-#: kallithea/model/validators.py:774
+#: kallithea/model/validators.py:783
 msgid "Please enter a valid IPv4 or IPv6 address"
 msgstr ""
 
-#: kallithea/model/validators.py:775
+#: kallithea/model/validators.py:784
 #, python-format
 msgid "The network size (bits) must be within the range of 0-32 (not %(bits)r)"
 msgstr ""
 
-#: kallithea/model/validators.py:808
+#: kallithea/model/validators.py:817
 msgid "Key name can only consist of letters, underscore, dash or numbers"
 msgstr ""
 
-#: kallithea/model/validators.py:822
+#: kallithea/model/validators.py:831
 msgid "Filename cannot be inside a directory"
 msgstr ""
 
-#: kallithea/model/validators.py:838
+#: kallithea/model/validators.py:847
 #, python-format
 msgid "Plugins %(loaded)s and %(next_to_load)s both export the same name"
 msgstr ""
@@ -2243,7 +2252,7 @@
 #: kallithea/templates/admin/user_groups/user_groups.html:50
 #: kallithea/templates/pullrequests/pullrequest_data.html:16
 #: kallithea/templates/pullrequests/pullrequest_show.html:156
-#: kallithea/templates/pullrequests/pullrequest_show.html:233
+#: kallithea/templates/pullrequests/pullrequest_show.html:244
 #: kallithea/templates/summary/summary.html:134
 msgid "Owner"
 msgstr ""
@@ -2291,7 +2300,7 @@
 #: kallithea/templates/index_base.html:144
 #: kallithea/templates/admin/my_account/my_account_repos.html:61
 #: kallithea/templates/admin/my_account/my_account_watched.html:61
-#: kallithea/templates/base/base.html:140 kallithea/templates/base/root.html:47
+#: kallithea/templates/base/root.html:47
 #: kallithea/templates/bookmarks/bookmarks.html:83
 #: kallithea/templates/branches/branches.html:83
 #: kallithea/templates/journal/journal.html:202
@@ -2301,7 +2310,7 @@
 msgstr ""
 
 #: kallithea/templates/login.html:5 kallithea/templates/login.html:15
-#: kallithea/templates/base/base.html:326
+#: kallithea/templates/base/base.html:414
 msgid "Log In"
 msgstr ""
 
@@ -2316,7 +2325,7 @@
 #: kallithea/templates/admin/users/user_add.html:32
 #: kallithea/templates/admin/users/user_edit_profile.html:24
 #: kallithea/templates/admin/users/users.html:50
-#: kallithea/templates/base/base.html:302
+#: kallithea/templates/base/base.html:390
 #: kallithea/templates/pullrequests/pullrequest_show.html:166
 msgid "Username"
 msgstr ""
@@ -2324,7 +2333,7 @@
 #: kallithea/templates/login.html:33 kallithea/templates/register.html:33
 #: kallithea/templates/admin/my_account/my_account.html:37
 #: kallithea/templates/admin/users/user_add.html:41
-#: kallithea/templates/base/base.html:311
+#: kallithea/templates/base/base.html:399
 msgid "Password"
 msgstr ""
 
@@ -2336,7 +2345,7 @@
 msgid "Forgot your password ?"
 msgstr ""
 
-#: kallithea/templates/login.html:56 kallithea/templates/base/base.html:322
+#: kallithea/templates/login.html:56 kallithea/templates/base/base.html:410
 msgid "Don't have an account ?"
 msgstr ""
 
@@ -2453,10 +2462,6 @@
 msgid "There are no branches yet"
 msgstr ""
 
-#: kallithea/templates/switch_to_list.html:16
-msgid "Closed Branches"
-msgstr ""
-
 #: kallithea/templates/switch_to_list.html:32
 #: kallithea/templates/tags/tags_data.html:44
 msgid "There are no tags yet"
@@ -2677,12 +2682,12 @@
 msgid "Never"
 msgstr ""
 
-#: kallithea/templates/admin/gists/edit.html:145
+#: kallithea/templates/admin/gists/edit.html:146
 msgid "Update Gist"
 msgstr ""
 
-#: kallithea/templates/admin/gists/edit.html:146
-#: kallithea/templates/changeset/changeset_file_comment.html:81
+#: kallithea/templates/admin/gists/edit.html:147
+#: kallithea/templates/changeset/changeset_file_comment.html:105
 msgid "Cancel"
 msgstr ""
 
@@ -2705,7 +2710,7 @@
 
 #: kallithea/templates/admin/gists/index.html:37
 #: kallithea/templates/admin/gists/show.html:25
-#: kallithea/templates/base/base.html:237
+#: kallithea/templates/base/base.html:321
 msgid "Create New Gist"
 msgstr ""
 
@@ -2793,7 +2798,8 @@
 #: kallithea/templates/admin/settings/settings_hooks.html:36
 #: kallithea/templates/admin/users/user_edit_emails.html:19
 #: kallithea/templates/admin/users/user_edit_ips.html:22
-#: kallithea/templates/changeset/changeset_file_comment.html:30
+#: kallithea/templates/changeset/changeset_file_comment.html:31
+#: kallithea/templates/changeset/changeset_file_comment.html:95
 #: kallithea/templates/data_table/_dt_elements.html:129
 #: kallithea/templates/data_table/_dt_elements.html:157
 #: kallithea/templates/data_table/_dt_elements.html:173
@@ -2813,8 +2819,6 @@
 #: kallithea/templates/base/perms_summary.html:43
 #: kallithea/templates/base/perms_summary.html:79
 #: kallithea/templates/base/perms_summary.html:81
-#: kallithea/templates/changeset/changeset_file_comment.html:83
-#: kallithea/templates/changeset/changeset_file_comment.html:192
 #: kallithea/templates/data_table/_dt_elements.html:122
 #: kallithea/templates/data_table/_dt_elements.html:123
 #: kallithea/templates/data_table/_dt_elements.html:150
@@ -2841,13 +2845,12 @@
 msgstr ""
 
 #: kallithea/templates/admin/gists/show.html:86
-#: kallithea/templates/files/files_source.html:73
 msgid "Show as raw"
 msgstr ""
 
 #: kallithea/templates/admin/my_account/my_account.html:5
 #: kallithea/templates/admin/my_account/my_account.html:9
-#: kallithea/templates/base/base.html:343
+#: kallithea/templates/base/base.html:431
 msgid "My Account"
 msgstr ""
 
@@ -3028,7 +3031,7 @@
 msgstr ""
 
 #: kallithea/templates/admin/notifications/notifications.html:26
-#: kallithea/templates/base/base.html:183
+#: kallithea/templates/base/base.html:180
 msgid "Pull Requests"
 msgstr ""
 
@@ -3046,7 +3049,7 @@
 msgstr ""
 
 #: kallithea/templates/admin/notifications/show_notification.html:9
-#: kallithea/templates/base/base.html:342
+#: kallithea/templates/base/base.html:430
 msgid "Notifications"
 msgstr ""
 
@@ -3228,7 +3231,7 @@
 #: kallithea/templates/admin/repos/repo_edit.html:40
 #: kallithea/templates/admin/settings/settings.html:11
 #: kallithea/templates/admin/user_groups/user_group_edit.html:29
-#: kallithea/templates/base/base.html:67 kallithea/templates/base/base.html:151
+#: kallithea/templates/base/base.html:67 kallithea/templates/base/base.html:148
 #: kallithea/templates/data_table/_dt_elements.html:45
 #: kallithea/templates/data_table/_dt_elements.html:49
 msgid "Settings"
@@ -3476,6 +3479,11 @@
 msgid "Unlock Repository"
 msgstr ""
 
+#: kallithea/templates/admin/repos/repo_edit_advanced.html:56
+#, python-format
+msgid "Locked by %s on %s"
+msgstr ""
+
 #: kallithea/templates/admin/repos/repo_edit_advanced.html:60
 msgid "Confirm to lock repository."
 msgstr ""
@@ -3525,10 +3533,6 @@
 msgid "Invalidate Repository Cache"
 msgstr ""
 
-#: kallithea/templates/admin/repos/repo_edit_caches.html:4
-msgid "Confirm to invalidate repository cache."
-msgstr ""
-
 #: kallithea/templates/admin/repos/repo_edit_caches.html:7
 msgid "Manually invalidate cache for this repository. On first access, the repository will be cached again."
 msgstr ""
@@ -4210,21 +4214,17 @@
 msgid "Files"
 msgstr ""
 
-#: kallithea/templates/base/base.html:138
-msgid "Switch To"
-msgstr ""
-
-#: kallithea/templates/base/base.html:145
-#: kallithea/templates/base/base.html:147
+#: kallithea/templates/base/base.html:142
+#: kallithea/templates/base/base.html:144
 msgid "Options"
 msgstr ""
 
-#: kallithea/templates/base/base.html:155
+#: kallithea/templates/base/base.html:152
 #: kallithea/templates/forks/forks_data.html:21
 msgid "Compare Fork"
 msgstr ""
 
-#: kallithea/templates/base/base.html:157
+#: kallithea/templates/base/base.html:154
 #: kallithea/templates/bookmarks/bookmarks.html:56
 #: kallithea/templates/bookmarks/bookmarks_data.html:13
 #: kallithea/templates/branches/branches.html:56
@@ -4234,111 +4234,116 @@
 msgid "Compare"
 msgstr ""
 
-#: kallithea/templates/base/base.html:159
-#: kallithea/templates/base/base.html:247
+#: kallithea/templates/base/base.html:156
+#: kallithea/templates/base/base.html:331
 #: kallithea/templates/search/search.html:14
 #: kallithea/templates/search/search.html:54
 msgid "Search"
 msgstr ""
 
-#: kallithea/templates/base/base.html:163
+#: kallithea/templates/base/base.html:160
 msgid "Unlock"
 msgstr ""
 
-#: kallithea/templates/base/base.html:165
+#: kallithea/templates/base/base.html:162
 msgid "Lock"
 msgstr ""
 
-#: kallithea/templates/base/base.html:173
+#: kallithea/templates/base/base.html:170
 msgid "Follow"
 msgstr ""
 
+#: kallithea/templates/base/base.html:171
+msgid "Unfollow"
+msgstr ""
+
 #: kallithea/templates/base/base.html:174
-msgid "Unfollow"
-msgstr ""
-
-#: kallithea/templates/base/base.html:177
 #: kallithea/templates/data_table/_dt_elements.html:37
 #: kallithea/templates/data_table/_dt_elements.html:41
 #: kallithea/templates/forks/fork.html:9
 msgid "Fork"
 msgstr ""
 
-#: kallithea/templates/base/base.html:178
+#: kallithea/templates/base/base.html:175
 #: kallithea/templates/pullrequests/pullrequest.html:88
 msgid "Create Pull Request"
 msgstr ""
 
-#: kallithea/templates/base/base.html:183
+#: kallithea/templates/base/base.html:180
 #, python-format
 msgid "Show Pull Requests for %s"
 msgstr ""
 
-#: kallithea/templates/base/base.html:221
+#: kallithea/templates/base/base.html:193
+msgid "Switch To"
+msgstr ""
+
+#: kallithea/templates/base/base.html:203
+#: kallithea/templates/base/base.html:485
+msgid "No matches found"
+msgstr ""
+
+#: kallithea/templates/base/base.html:305
 msgid "Show recent activity"
 msgstr ""
 
-#: kallithea/templates/base/base.html:227
-#: kallithea/templates/base/base.html:228
+#: kallithea/templates/base/base.html:311
+#: kallithea/templates/base/base.html:312
 msgid "Public journal"
 msgstr ""
 
-#: kallithea/templates/base/base.html:233
+#: kallithea/templates/base/base.html:317
 msgid "Show public gists"
 msgstr ""
 
-#: kallithea/templates/base/base.html:234
+#: kallithea/templates/base/base.html:318
 msgid "Gists"
 msgstr ""
 
-#: kallithea/templates/base/base.html:238
+#: kallithea/templates/base/base.html:322
 msgid "All Public Gists"
 msgstr ""
 
-#: kallithea/templates/base/base.html:240
+#: kallithea/templates/base/base.html:324
 msgid "My Public Gists"
 msgstr ""
 
-#: kallithea/templates/base/base.html:241
+#: kallithea/templates/base/base.html:325
 msgid "My Private Gists"
 msgstr ""
 
-#: kallithea/templates/base/base.html:246
+#: kallithea/templates/base/base.html:330
 msgid "Search in repositories"
 msgstr ""
 
-#: kallithea/templates/base/base.html:269
-#: kallithea/templates/base/base.html:270
+#: kallithea/templates/base/base.html:353
+#: kallithea/templates/base/base.html:354
 #: kallithea/templates/pullrequests/pullrequest_show_my.html:6
 #: kallithea/templates/pullrequests/pullrequest_show_my.html:10
 msgid "My Pull Requests"
 msgstr ""
 
-#: kallithea/templates/base/base.html:289
+#: kallithea/templates/base/base.html:377
 msgid "Not Logged In"
 msgstr ""
 
-#: kallithea/templates/base/base.html:296
+#: kallithea/templates/base/base.html:384
 msgid "Login to Your Account"
 msgstr ""
 
-#: kallithea/templates/base/base.html:319
+#: kallithea/templates/base/base.html:407
 msgid "Forgot password ?"
 msgstr ""
 
-#: kallithea/templates/base/base.html:346
+#: kallithea/templates/base/base.html:434
 msgid "Log Out"
 msgstr ""
 
-#: kallithea/templates/base/base.html:395
-msgid "No matches found"
-msgstr ""
-
-#: kallithea/templates/base/base.html:524
+#: kallithea/templates/base/base.html:615
 msgid "Keyboard shortcuts"
 msgstr ""
 
-#: kallithea/templates/base/base.html:533
+#: kallithea/templates/base/base.html:624
 msgid "Site-wide shortcuts"
 msgstr ""
 
@@ -4453,6 +4458,7 @@
 
 #: kallithea/templates/base/root.html:35
 #: kallithea/templates/changeset/diff_block.html:8
+#: kallithea/templates/changeset/diff_block.html:21
 msgid "Collapse Diff"
 msgstr ""
 
@@ -4593,6 +4599,8 @@
 #: kallithea/templates/changelog/changelog_summary_data.html:60
 #: kallithea/templates/changeset/changeset.html:101
 #: kallithea/templates/changeset/changeset_range.html:98
+#: kallithea/templates/compare/compare_cs.html:69
+#: kallithea/templates/pullrequests/pullrequest_show.html:203
 #, python-format
 msgid "Tag %s"
 msgstr ""
@@ -4621,7 +4629,7 @@
 
 #: kallithea/templates/changelog/changelog_details.html:6
 #: kallithea/templates/changeset/changeset.html:79
-#: kallithea/templates/changeset/diff_block.html:79
+#: kallithea/templates/changeset/diff_block.html:47
 msgid "Added"
 msgstr ""
 
@@ -4683,13 +4691,13 @@
 msgstr ""
 
 #: kallithea/templates/changeset/changeset.html:50
-#: kallithea/templates/changeset/changeset_file_comment.html:37
+#: kallithea/templates/changeset/changeset_file_comment.html:39
 #: kallithea/templates/changeset/changeset_range.html:48
 msgid "Changeset status"
 msgstr ""
 
 #: kallithea/templates/changeset/changeset.html:54
-#: kallithea/templates/changeset/diff_block.html:27
+#: kallithea/templates/changeset/diff_block.html:72
 #: kallithea/templates/files/diff_2way.html:49
 msgid "Raw diff"
 msgstr ""
@@ -4699,7 +4707,7 @@
 msgstr ""
 
 #: kallithea/templates/changeset/changeset.html:60
-#: kallithea/templates/changeset/diff_block.html:30
+#: kallithea/templates/changeset/diff_block.html:75
 #: kallithea/templates/files/diff_2way.html:52
 msgid "Download diff"
 msgstr ""
@@ -4726,8 +4734,8 @@
 msgstr ""
 
 #: kallithea/templates/changeset/changeset.html:166
-#: kallithea/templates/compare/compare_diff.html:54
-#: kallithea/templates/pullrequests/pullrequest_show.html:318
+#: kallithea/templates/compare/compare_diff.html:60
+#: kallithea/templates/pullrequests/pullrequest_show.html:329
 #, python-format
 msgid "%s file changed"
 msgid_plural "%s files changed"
@@ -4735,8 +4743,8 @@
 msgstr[1] ""
 
 #: kallithea/templates/changeset/changeset.html:168
-#: kallithea/templates/compare/compare_diff.html:56
-#: kallithea/templates/pullrequests/pullrequest_show.html:320
+#: kallithea/templates/compare/compare_diff.html:62
+#: kallithea/templates/pullrequests/pullrequest_show.html:331
 #, python-format
 msgid "%s file changed with %s insertions and %s deletions"
 msgid_plural "%s files changed with %s insertions and %s deletions"
@@ -4745,13 +4753,13 @@
 
 #: kallithea/templates/changeset/changeset.html:182
 #: kallithea/templates/changeset/changeset.html:195
-#: kallithea/templates/pullrequests/pullrequest_show.html:339
-#: kallithea/templates/pullrequests/pullrequest_show.html:363
+#: kallithea/templates/pullrequests/pullrequest_show.html:350
+#: kallithea/templates/pullrequests/pullrequest_show.html:372
 msgid "Show full diff anyway"
 msgstr ""
 
-#: kallithea/templates/changeset/changeset.html:247
-#: kallithea/templates/changeset/changeset.html:284
+#: kallithea/templates/changeset/changeset.html:231
+#: kallithea/templates/changeset/changeset.html:268
 msgid "No revisions"
 msgstr ""
 
@@ -4767,100 +4775,83 @@
 msgid "on this changeset"
 msgstr ""
 
-#: kallithea/templates/changeset/changeset_file_comment.html:30
+#: kallithea/templates/changeset/changeset_file_comment.html:31
 msgid "Delete comment?"
 msgstr ""
 
-#: kallithea/templates/changeset/changeset_file_comment.html:37
+#: kallithea/templates/changeset/changeset_file_comment.html:39
 msgid "Status change"
 msgstr ""
 
 #: kallithea/templates/changeset/changeset_file_comment.html:59
-msgid "Commenting on line {1}."
+msgid "Commenting on line."
 msgstr ""
 
 #: kallithea/templates/changeset/changeset_file_comment.html:60
-#: kallithea/templates/changeset/changeset_file_comment.html:148
-#, python-format
-msgid "Comments parsed using %s syntax with %s support."
-msgstr ""
-
-#: kallithea/templates/changeset/changeset_file_comment.html:62
-msgid "Use @username inside this text to notify another user"
-msgstr ""
-
-#: kallithea/templates/changeset/changeset_file_comment.html:72
-#: kallithea/templates/changeset/changeset_file_comment.html:184
-msgid "Comment preview"
-msgstr ""
-
-#: kallithea/templates/changeset/changeset_file_comment.html:77
+msgid "Comments are in plain text. Use @username inside this text to notify another user."
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:67
+msgid "Set changeset status"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:69
+msgid "Vote for pull request status"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:75
+msgid "No change"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:88
+msgid "Finish pull request"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:91
+msgid "Close"
+msgstr ""
+
+#: kallithea/templates/changeset/changeset_file_comment.html:103
 msgid "Submitting ..."
 msgstr ""
 
-#: kallithea/templates/changeset/changeset_file_comment.html:80
-#: kallithea/templates/changeset/changeset_file_comment.html:190
+#: kallithea/templates/changeset/changeset_file_comment.html:104
 msgid "Comment"
 msgstr ""
 
-#: kallithea/templates/changeset/changeset_file_comment.html:82
-#: kallithea/templates/changeset/changeset_file_comment.html:191
-msgid "Preview"
-msgstr ""
-
-#: kallithea/templates/changeset/changeset_file_comment.html:90
+#: kallithea/templates/changeset/changeset_file_comment.html:112
 msgid "You need to be logged in to comment."
 msgstr ""
 
-#: kallithea/templates/changeset/changeset_file_comment.html:90
+#: kallithea/templates/changeset/changeset_file_comment.html:112
 msgid "Login now"
 msgstr ""
 
-#: kallithea/templates/changeset/changeset_file_comment.html:94
+#: kallithea/templates/changeset/changeset_file_comment.html:116
 msgid "Hide"
 msgstr ""
 
-#: kallithea/templates/changeset/changeset_file_comment.html:106
+#: kallithea/templates/changeset/changeset_file_comment.html:128
 #, python-format
 msgid "%d comment"
 msgid_plural "%d comments"
 msgstr[0] ""
 msgstr[1] ""
 
-#: kallithea/templates/changeset/changeset_file_comment.html:107
+#: kallithea/templates/changeset/changeset_file_comment.html:129
 #, python-format
 msgid "%d inline"
 msgid_plural "%d inline"
 msgstr[0] ""
 msgstr[1] ""
 
-#: kallithea/templates/changeset/changeset_file_comment.html:108
+#: kallithea/templates/changeset/changeset_file_comment.html:130
 #, python-format
 msgid "%d general"
 msgid_plural "%d general"
 msgstr[0] ""
 msgstr[1] ""
 
-#: kallithea/templates/changeset/changeset_file_comment.html:150
-msgid "Use @username inside this text to notify another user."
-msgstr ""
-
-#: kallithea/templates/changeset/changeset_file_comment.html:157
-msgid "Vote for pull request status"
-msgstr ""
-
-#: kallithea/templates/changeset/changeset_file_comment.html:159
-msgid "Set changeset status"
-msgstr ""
-
-#: kallithea/templates/changeset/changeset_file_comment.html:163
-msgid "No change"
-msgstr ""
-
-#: kallithea/templates/changeset/changeset_file_comment.html:176
-msgid "Close"
-msgstr ""
-
 #: kallithea/templates/changeset/changeset_range.html:5
 #, python-format
 msgid "%s Changesets"
@@ -4870,29 +4861,28 @@
 msgid "Files affected"
 msgstr ""
 
-#: kallithea/templates/changeset/diff_block.html:21
+#: kallithea/templates/changeset/diff_block.html:54
+msgid "Deleted"
+msgstr ""
+
+#: kallithea/templates/changeset/diff_block.html:57
+msgid "Renamed"
+msgstr ""
+
+#: kallithea/templates/changeset/diff_block.html:66
 #: kallithea/templates/files/diff_2way.html:43
 msgid "Show full diff for this file"
 msgstr ""
 
-#: kallithea/templates/changeset/diff_block.html:24
-#: kallithea/templates/changeset/diff_block.html:98
+#: kallithea/templates/changeset/diff_block.html:69
 #: kallithea/templates/files/diff_2way.html:46
 msgid "Show full side-by-side diff for this file"
 msgstr ""
 
-#: kallithea/templates/changeset/diff_block.html:38
+#: kallithea/templates/changeset/diff_block.html:83
 msgid "Show inline comments"
 msgstr ""
 
-#: kallithea/templates/changeset/diff_block.html:86
-msgid "Deleted"
-msgstr ""
-
-#: kallithea/templates/changeset/diff_block.html:89
-msgid "Renamed"
-msgstr ""
-
 #: kallithea/templates/compare/compare_cs.html:4
 msgid "No changesets"
 msgstr ""
@@ -4913,29 +4903,29 @@
 msgid "Position in this list of changesets"
 msgstr ""
 
-#: kallithea/templates/compare/compare_cs.html:76
+#: kallithea/templates/compare/compare_cs.html:85
 msgid "Show merge diff"
 msgstr ""
 
-#: kallithea/templates/compare/compare_cs.html:86
-#: kallithea/templates/pullrequests/pullrequest_show.html:310
+#: kallithea/templates/compare/compare_cs.html:95
+#: kallithea/templates/pullrequests/pullrequest_show.html:321
 msgid "Common ancestor"
 msgstr ""
 
-#: kallithea/templates/compare/compare_cs.html:90
-msgid "No common ancestor found - repositories are unrelated"
-msgstr ""
-
-#: kallithea/templates/compare/compare_cs.html:98
-msgid "is"
-msgstr ""
-
 #: kallithea/templates/compare/compare_cs.html:99
+msgid "No common ancestor found - repositories are unrelated"
+msgstr ""
+
+#: kallithea/templates/compare/compare_cs.html:107
+msgid "is"
+msgstr ""
+
+#: kallithea/templates/compare/compare_cs.html:108
 #, python-format
 msgid "%s changesets"
 msgstr ""
 
-#: kallithea/templates/compare/compare_cs.html:100
+#: kallithea/templates/compare/compare_cs.html:109
 msgid "behind"
 msgstr ""
 
@@ -4946,28 +4936,28 @@
 msgstr ""
 
 #: kallithea/templates/compare/compare_diff.html:13
-#: kallithea/templates/compare/compare_diff.html:35
+#: kallithea/templates/compare/compare_diff.html:41
 msgid "Compare Revisions"
 msgstr ""
 
-#: kallithea/templates/compare/compare_diff.html:33
+#: kallithea/templates/compare/compare_diff.html:39
 msgid "Swap"
 msgstr ""
 
-#: kallithea/templates/compare/compare_diff.html:42
+#: kallithea/templates/compare/compare_diff.html:48
 msgid "Compare revisions, branches, bookmarks, or tags."
 msgstr ""
 
-#: kallithea/templates/compare/compare_diff.html:47
-#: kallithea/templates/pullrequests/pullrequest_show.html:305
+#: kallithea/templates/compare/compare_diff.html:53
+#: kallithea/templates/pullrequests/pullrequest_show.html:316
 #, python-format
 msgid "Showing %s commit"
 msgid_plural "Showing %s commits"
 msgstr[0] ""
 msgstr[1] ""
 
-#: kallithea/templates/compare/compare_diff.html:78
-#: kallithea/templates/compare/compare_diff.html:89
+#: kallithea/templates/compare/compare_diff.html:84
+#: kallithea/templates/compare/compare_diff.html:93
 msgid "Show full diff"
 msgstr ""
 
@@ -5026,15 +5016,19 @@
 msgid "We have received a request to reset the password for your account."
 msgstr ""
 
-#: kallithea/templates/email_templates/password_reset.html:7
-msgid "To set a new password, click the following link"
+#: kallithea/templates/email_templates/password_reset.html:8
+msgid "This account is however managed outside this system and the password cannot be changed here."
 msgstr ""
 
 #: kallithea/templates/email_templates/password_reset.html:10
+msgid "To set a new password, click the following link"
+msgstr ""
+
+#: kallithea/templates/email_templates/password_reset.html:13
 msgid "Should you not be able to use the link above, please type the following code into the password reset form"
 msgstr ""
 
-#: kallithea/templates/email_templates/password_reset.html:12
+#: kallithea/templates/email_templates/password_reset.html:16
 msgid "If it weren't you who requested the password reset, just disregard this message."
 msgstr ""
 
@@ -5115,7 +5109,7 @@
 msgstr ""
 
 #: kallithea/templates/files/files_add.html:53
-msgid "New file mode"
+msgid "New file type"
 msgstr ""
 
 #: kallithea/templates/files/files_add.html:64
@@ -5246,8 +5240,16 @@
 msgid "Binary file (%s)"
 msgstr ""
 
-#: kallithea/templates/files/files_source.html:73
-msgid "File is too big to display"
+#: kallithea/templates/files/files_source.html:74
+msgid "File is too big to display."
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:76
+msgid "Show full annotation anyway."
+msgstr ""
+
+#: kallithea/templates/files/files_source.html:78
+msgid "Show as raw."
 msgstr ""
 
 #: kallithea/templates/files/files_ypjax.html:5
@@ -5508,39 +5510,43 @@
 msgid "Current revision - no change"
 msgstr ""
 
-#: kallithea/templates/pullrequests/pullrequest_show.html:213
+#: kallithea/templates/pullrequests/pullrequest_show.html:215
+msgid "Pull requests do not change once created. Select a revision and save to replace this pull request with a new one."
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:224
 msgid "Pull Request Reviewers"
 msgstr ""
 
-#: kallithea/templates/pullrequests/pullrequest_show.html:238
+#: kallithea/templates/pullrequests/pullrequest_show.html:249
 msgid "Remove reviewer"
 msgstr ""
 
-#: kallithea/templates/pullrequests/pullrequest_show.html:250
-msgid "Type name of reviewer to add"
-msgstr ""
-
-#: kallithea/templates/pullrequests/pullrequest_show.html:258
-msgid "Potential Reviewers"
-msgstr ""
-
 #: kallithea/templates/pullrequests/pullrequest_show.html:261
+msgid "Type name of reviewer to add"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:269
+msgid "Potential Reviewers"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:272
 msgid "Click to add the repository owner as reviewer:"
 msgstr ""
 
-#: kallithea/templates/pullrequests/pullrequest_show.html:284
+#: kallithea/templates/pullrequests/pullrequest_show.html:295
 msgid "Save Changes"
 msgstr ""
 
-#: kallithea/templates/pullrequests/pullrequest_show.html:285
-msgid "Save as New Pull Request"
-msgstr ""
-
-#: kallithea/templates/pullrequests/pullrequest_show.html:286
-msgid "Cancel Changes"
-msgstr ""
-
 #: kallithea/templates/pullrequests/pullrequest_show.html:296
+msgid "Save Updates as New Pull Request"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:297
+msgid "Cancel Changes"
+msgstr ""
+
+#: kallithea/templates/pullrequests/pullrequest_show.html:307
 msgid "Pull Request Content"
 msgstr ""
 
@@ -5551,7 +5557,7 @@
 
 #: kallithea/templates/pullrequests/pullrequest_show_all.html:11
 #, python-format
-msgid "Pull Requests from %s'"
+msgid "Pull Requests from '%s'"
 msgstr ""
 
 #: kallithea/templates/pullrequests/pullrequest_show_all.html:13
--- a/kallithea/lib/annotate.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/annotate.py	Mon Mar 14 00:36:08 2016 +0100
@@ -68,7 +68,7 @@
         following function as ``annotate_from_changeset_func``::
 
             def changeset_to_anchor(changeset):
-                return '<a href="/changesets/%s/">%s</a>\n' %\
+                return '<a href="/changesets/%s/">%s</a>\n' % \
                        (changeset.id, changeset.id)
 
         :param annotate_from_changeset_func: see above
--- a/kallithea/lib/auth.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/auth.py	Mon Mar 14 00:36:08 2016 +0100
@@ -35,12 +35,12 @@
 from decorator import decorator
 
 from pylons import url, request, session
-from pylons.controllers.util import abort, redirect
 from pylons.i18n.translation import _
 from webhelpers.pylonslib import secure_form
 from sqlalchemy import or_
 from sqlalchemy.orm.exc import ObjectDeletedError
 from sqlalchemy.orm import joinedload
+from webob.exc import HTTPFound, HTTPBadRequest, HTTPForbidden, HTTPMethodNotAllowed
 
 from kallithea import __platform__, is_windows, is_unix
 from kallithea.lib.vcs.utils.lazy import LazyProperty
@@ -204,8 +204,8 @@
     #==================================================================
 
     # default global permissions taken from the default user
-    default_global_perms = UserToPerm.query()\
-        .filter(UserToPerm.user_id == default_user_id)\
+    default_global_perms = UserToPerm.query() \
+        .filter(UserToPerm.user_id == default_user_id) \
         .options(joinedload(UserToPerm.permission))
 
     for perm in default_global_perms:
@@ -251,15 +251,15 @@
 
     # USER GROUPS comes first
     # user group global permissions
-    user_perms_from_users_groups = Session().query(UserGroupToPerm)\
-        .options(joinedload(UserGroupToPerm.permission))\
+    user_perms_from_users_groups = Session().query(UserGroupToPerm) \
+        .options(joinedload(UserGroupToPerm.permission)) \
         .join((UserGroupMember, UserGroupToPerm.users_group_id ==
-               UserGroupMember.users_group_id))\
-        .filter(UserGroupMember.user_id == user_id)\
+               UserGroupMember.users_group_id)) \
+        .filter(UserGroupMember.user_id == user_id) \
         .join((UserGroup, UserGroupMember.users_group_id ==
-               UserGroup.users_group_id))\
-        .filter(UserGroup.users_group_active == True)\
-        .order_by(UserGroupToPerm.users_group_id)\
+               UserGroup.users_group_id)) \
+        .filter(UserGroup.users_group_active == True) \
+        .order_by(UserGroupToPerm.users_group_id) \
         .all()
     # need to group here by groups since user can be in more than
     # one group
@@ -273,20 +273,20 @@
         if not gr.inherit_default_permissions:
             # NEED TO IGNORE all configurable permissions and
             # replace them with explicitly set
-            permissions[GLOBAL] = permissions[GLOBAL]\
+            permissions[GLOBAL] = permissions[GLOBAL] \
                                             .difference(_configurable)
         for perm in perms:
             permissions[GLOBAL].add(perm.permission.permission_name)
 
     # user specific global permissions
-    user_perms = Session().query(UserToPerm)\
-            .options(joinedload(UserToPerm.permission))\
+    user_perms = Session().query(UserToPerm) \
+            .options(joinedload(UserToPerm.permission)) \
             .filter(UserToPerm.user_id == user_id).all()
 
     if not user_inherit_default_permissions:
         # NEED TO IGNORE all configurable permissions and
         # replace them with explicitly set
-        permissions[GLOBAL] = permissions[GLOBAL]\
+        permissions[GLOBAL] = permissions[GLOBAL] \
                                         .difference(_configurable)
 
         for perm in user_perms:
@@ -304,17 +304,17 @@
 
     # user group for repositories permissions
     user_repo_perms_from_users_groups = \
-     Session().query(UserGroupRepoToPerm, Permission, Repository,)\
+     Session().query(UserGroupRepoToPerm, Permission, Repository,) \
         .join((Repository, UserGroupRepoToPerm.repository_id ==
-               Repository.repo_id))\
+               Repository.repo_id)) \
         .join((Permission, UserGroupRepoToPerm.permission_id ==
-               Permission.permission_id))\
+               Permission.permission_id)) \
         .join((UserGroup, UserGroupRepoToPerm.users_group_id ==
-               UserGroup.users_group_id))\
-        .filter(UserGroup.users_group_active == True)\
+               UserGroup.users_group_id)) \
+        .filter(UserGroup.users_group_active == True) \
         .join((UserGroupMember, UserGroupRepoToPerm.users_group_id ==
-               UserGroupMember.users_group_id))\
-        .filter(UserGroupMember.user_id == user_id)\
+               UserGroupMember.users_group_id)) \
+        .filter(UserGroupMember.user_id == user_id) \
         .all()
 
     multiple_counter = collections.defaultdict(int)
@@ -357,16 +357,16 @@
     #======================================================================
     # user group for repo groups permissions
     user_repo_group_perms_from_users_groups = \
-     Session().query(UserGroupRepoGroupToPerm, Permission, RepoGroup)\
-     .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
+     Session().query(UserGroupRepoGroupToPerm, Permission, RepoGroup) \
+     .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id)) \
      .join((Permission, UserGroupRepoGroupToPerm.permission_id
-            == Permission.permission_id))\
+            == Permission.permission_id)) \
      .join((UserGroup, UserGroupRepoGroupToPerm.users_group_id ==
-            UserGroup.users_group_id))\
-     .filter(UserGroup.users_group_active == True)\
+            UserGroup.users_group_id)) \
+     .filter(UserGroup.users_group_active == True) \
      .join((UserGroupMember, UserGroupRepoGroupToPerm.users_group_id
-            == UserGroupMember.users_group_id))\
-     .filter(UserGroupMember.user_id == user_id)\
+            == UserGroupMember.users_group_id)) \
+     .filter(UserGroupMember.user_id == user_id) \
      .all()
 
     multiple_counter = collections.defaultdict(int)
@@ -394,17 +394,17 @@
     #======================================================================
     # user group for user group permissions
     user_group_user_groups_perms = \
-     Session().query(UserGroupUserGroupToPerm, Permission, UserGroup)\
+     Session().query(UserGroupUserGroupToPerm, Permission, UserGroup) \
      .join((UserGroup, UserGroupUserGroupToPerm.target_user_group_id
-            == UserGroup.users_group_id))\
+            == UserGroup.users_group_id)) \
      .join((Permission, UserGroupUserGroupToPerm.permission_id
-            == Permission.permission_id))\
+            == Permission.permission_id)) \
      .join((UserGroupMember, UserGroupUserGroupToPerm.user_group_id
-            == UserGroupMember.users_group_id))\
-     .filter(UserGroupMember.user_id == user_id)\
+            == UserGroupMember.users_group_id)) \
+     .filter(UserGroupMember.user_id == user_id) \
      .join((UserGroup, UserGroupMember.users_group_id ==
-            UserGroup.users_group_id), aliased=True, from_joinpoint=True)\
-     .filter(UserGroup.users_group_active == True)\
+            UserGroup.users_group_id), aliased=True, from_joinpoint=True) \
+     .filter(UserGroup.users_group_active == True) \
      .all()
 
     multiple_counter = collections.defaultdict(int)
@@ -465,8 +465,7 @@
     access to Kallithea is enabled, the default user is loaded instead.
 
     `AuthUser` does not by itself authenticate users and the constructor
-    sets the `is_authenticated` field to False, except when falling back
-    to the default anonymous user (if enabled). It's up to other parts
+    sets the `is_authenticated` field to False. It's up to other parts
     of the code to check e.g. if a supplied password is correct, and if
     so, set `is_authenticated` to True.
 
@@ -508,9 +507,7 @@
         if not is_user_loaded:
             is_user_loaded =  self._fill_data(self.anonymous_user)
 
-        # The anonymous user is always "logged in".
-        if self.user_id == self.anonymous_user.user_id:
-            self.is_authenticated = True
+        self.is_default_user = (self.user_id == self.anonymous_user.user_id)
 
         if not self.username:
             self.username = 'None'
@@ -569,8 +566,8 @@
 
     def _get_api_keys(self):
         api_keys = [self.api_key]
-        for api_key in UserApiKeys.query()\
-                .filter(UserApiKeys.user_id == self.user_id)\
+        for api_key in UserApiKeys.query() \
+                .filter(UserApiKeys.user_id == self.user_id) \
                 .filter(or_(UserApiKeys.expires == -1,
                             UserApiKeys.expires >= time.time())).all():
             api_keys.append(api_key.api_key)
@@ -622,18 +619,13 @@
             return False
 
     def __repr__(self):
-        return "<AuthUser('id:%s[%s] auth:%s')>"\
-            % (self.user_id, self.username, self.is_authenticated)
-
-    def set_authenticated(self, authenticated=True):
-        if self.user_id != self.anonymous_user.user_id:
-            self.is_authenticated = authenticated
+        return "<AuthUser('id:%s[%s] auth:%s')>" \
+            % (self.user_id, self.username, (self.is_authenticated or self.is_default_user))
 
     def to_cookie(self):
         """ Serializes this login session to a cookie `dict`. """
         return {
             'user_id': self.user_id,
-            'is_authenticated': self.is_authenticated,
             'is_external_auth': self.is_external_auth,
         }
 
@@ -647,9 +639,7 @@
             user_id=cookie.get('user_id'),
             is_external_auth=cookie.get('is_external_auth', False),
         )
-        if not au.is_authenticated and au.user_id is not None:
-            # user is not authenticated and not empty
-            au.set_authenticated(cookie.get('is_authenticated'))
+        au.is_authenticated = True
         return au
 
     @classmethod
@@ -710,13 +700,16 @@
 # CHECK DECORATORS
 #==============================================================================
 
-def redirect_to_login(message=None):
+def _redirect_to_login(message=None):
+    """Return an exception that must be raised. It will redirect to the login
+    page which will redirect back to the current URL after authentication.
+    The optional message will be shown in a flash message."""
     from kallithea.lib import helpers as h
-    p = request.path_qs
     if message:
         h.flash(h.literal(message), category='warning')
+    p = request.path_qs
     log.debug('Redirecting to login page, origin: %s', p)
-    return redirect(url('login_home', came_from=p))
+    return HTTPFound(location=url('login_home', came_from=p))
 
 
 class LoginRequired(object):
@@ -741,7 +734,7 @@
         log.debug('Checking access for user %s @ %s', user, loc)
 
         if not AuthUser.check_ip_allowed(user, controller.ip_addr):
-            return redirect_to_login(_('IP %s not allowed') % controller.ip_addr)
+            raise _redirect_to_login(_('IP %s not allowed') % controller.ip_addr)
 
         # check if we used an API key and it's a valid one
         api_key = request.GET.get('api_key')
@@ -754,17 +747,17 @@
                     return func(*fargs, **fkwargs)
                 else:
                     log.warning('API key ****%s is NOT valid', api_key[-4:])
-                    return redirect_to_login(_('Invalid API key'))
+                    raise _redirect_to_login(_('Invalid API key'))
             else:
                 # controller does not allow API access
                 log.warning('API access to %s is not allowed', loc)
-                return abort(403)
+                raise HTTPForbidden()
 
         # Only allow the following HTTP request methods. (We sometimes use POST
         # requests with a '_method' set to 'PUT' or 'DELETE'; but that is only
         # used for the route lookup, and does not affect request.method.)
         if request.method not in ['GET', 'HEAD', 'POST', 'PUT']:
-            return abort(405)
+            raise HTTPMethodNotAllowed()
 
         # Make sure CSRF token never appears in the URL. If so, invalidate it.
         if secure_form.token_key in request.GET:
@@ -785,22 +778,22 @@
             token = request.POST.get(secure_form.token_key)
             if not token or token != secure_form.authentication_token():
                 log.error('CSRF check failed')
-                return abort(403)
+                raise HTTPForbidden()
 
         # WebOb already ignores request payload parameters for anything other
         # than POST/PUT, but double-check since other Kallithea code relies on
         # this assumption.
         if request.method not in ['POST', 'PUT'] and request.POST:
             log.error('%r request with payload parameters; WebOb should have stopped this', request.method)
-            return abort(400)
+            raise HTTPBadRequest()
 
         # regular user authentication
-        if user.is_authenticated:
+        if user.is_authenticated or user.is_default_user:
             log.info('user %s authenticated with regular auth @ %s', user, loc)
             return func(*fargs, **fkwargs)
         else:
             log.warning('user %s NOT authenticated with regular auth @ %s', user, loc)
-            return redirect_to_login()
+            raise _redirect_to_login()
 
 class NotAnonymous(object):
     """
@@ -816,11 +809,9 @@
 
         log.debug('Checking if user is not anonymous @%s', cls)
 
-        anonymous = self.user.username == User.DEFAULT_USER
-
-        if anonymous:
-            return redirect_to_login(_('You need to be a registered user to '
-                    'perform this action'))
+        if self.user.is_default_user:
+            raise _redirect_to_login(_('You need to be a registered user to '
+                                       'perform this action'))
         else:
             return func(*fargs, **fkwargs)
 
@@ -848,13 +839,10 @@
 
         else:
             log.debug('Permission denied for %s %s', cls, self.user)
-            anonymous = self.user.username == User.DEFAULT_USER
-
-            if anonymous:
-                return redirect_to_login(_('You need to be signed in to view this page'))
+            if self.user.is_default_user:
+                raise _redirect_to_login(_('You need to be signed in to view this page'))
             else:
-                # redirect with forbidden ret code
-                return abort(403)
+                raise HTTPForbidden()
 
     def check_permissions(self):
         """Dummy function for overriding"""
--- a/kallithea/lib/auth_modules/__init__.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/auth_modules/__init__.py	Mon Mar 14 00:36:08 2016 +0100
@@ -139,8 +139,8 @@
         log.debug('Trying to fetch user `%s` from Kallithea database',
                   username)
         if username:
-            user = User.get_by_username(username)
-            if not user:
+            user = User.get_by_username_or_email(username)
+            if user is None:
                 log.debug('Fallback to fetch user in case insensitive mode')
                 user = User.get_by_username(username, case_insensitive=True)
         else:
@@ -395,8 +395,15 @@
         else:
             log.debug('Plugin %s accepted user `%s` for authentication',
                       module, user)
+            # The user might have tried to authenticate using their email address,
+            # then the username variable wouldn't contain a valid username.
+            # But as the plugin has accepted the user, .username field should
+            # have a valid username, so use it for authentication purposes.
+            if user is not None:
+                username = user.username
 
         log.info('Authenticating user using %s plugin', plugin.__module__)
+
         # _authenticate is a wrapper for .auth() method of plugin.
         # it checks if .auth() sends proper data. For KallitheaExternalAuthPlugin
         # it also maps users to Database and maps the attributes returned
--- a/kallithea/lib/auth_modules/auth_container.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/auth_modules/auth_container.py	Mon Mar 14 00:36:08 2016 +0100
@@ -27,9 +27,9 @@
 
 import logging
 from kallithea.lib import auth_modules
-from kallithea.lib.utils2 import str2bool, safe_unicode
+from kallithea.lib.utils2 import str2bool, safe_unicode, safe_str
 from kallithea.lib.compat import hybrid_property
-from kallithea.model.db import User
+from kallithea.model.db import User, Setting
 
 log = logging.getLogger(__name__)
 
@@ -53,15 +53,39 @@
                 "name": "header",
                 "validator": self.validators.UnicodeString(strip=True, not_empty=True),
                 "type": "string",
-                "description": "Header to extract the user from",
+                "description": "Request header to extract the username from",
                 "default": "REMOTE_USER",
-                "formname": "Header"
+                "formname": "Username header"
+            },
+            {
+                "name": "email_header",
+                "validator": self.validators.UnicodeString(strip=True),
+                "type": "string",
+                "description": "Optional request header to extract the email from",
+                "default": "",
+                "formname": "Email header"
+            },
+            {
+                "name": "firstname_header",
+                "validator": self.validators.UnicodeString(strip=True),
+                "type": "string",
+                "description": "Optional request header to extract the first name from",
+                "default": "",
+                "formname": "Firstname header"
+            },
+            {
+                "name": "lastname_header",
+                "validator": self.validators.UnicodeString(strip=True),
+                "type": "string",
+                "description": "Optional request header to extract the last name from",
+                "default": "",
+                "formname": "Lastname header"
             },
             {
                 "name": "fallback_header",
                 "validator": self.validators.UnicodeString(strip=True),
                 "type": "string",
-                "description": "Header to extract the user from when main one fails",
+                "description": "Request header to extract the user from when main one fails",
                 "default": "HTTP_X_FORWARDED_USER",
                 "formname": "Fallback header"
             },
@@ -158,7 +182,7 @@
         # only way to log in is using environ
         username = None
         if userobj:
-            username = getattr(userobj, 'username')
+            username = safe_str(getattr(userobj, 'username'))
 
         if not username:
             # we don't have any objects in DB, user doesn't exist, extract
@@ -172,9 +196,9 @@
         # old attrs fetched from Kallithea database
         admin = getattr(userobj, 'admin', False)
         active = getattr(userobj, 'active', True)
-        email = getattr(userobj, 'email', '')
-        firstname = getattr(userobj, 'firstname', '')
-        lastname = getattr(userobj, 'lastname', '')
+        email = environ.get(settings.get('email_header'), getattr(userobj, 'email', ''))
+        firstname = environ.get(settings.get('firstname_header'), getattr(userobj, 'firstname', ''))
+        lastname = environ.get(settings.get('lastname_header'), getattr(userobj, 'lastname', ''))
 
         user_data = {
             'username': username,
@@ -192,4 +216,11 @@
         return user_data
 
     def get_managed_fields(self):
-        return ['username', 'password']
+        fields = ['username', 'password']
+        if(Setting.get_by_name('auth_container_email_header').app_settings_value):
+            fields.append('email')
+        if(Setting.get_by_name('auth_container_firstname_header').app_settings_value):
+            fields.append('firstname')
+        if(Setting.get_by_name('auth_container_lastname_header').app_settings_value):
+            fields.append('lastname')
+        return fields
--- a/kallithea/lib/auth_modules/auth_ldap.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/auth_modules/auth_ldap.py	Mon Mar 14 00:36:08 2016 +0100
@@ -356,7 +356,7 @@
         except (LdapUsernameError, LdapPasswordError, LdapImportError):
             log.error(traceback.format_exc())
             return None
-        except (Exception,):
+        except Exception:
             log.error(traceback.format_exc())
             return None
 
--- a/kallithea/lib/base.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/base.py	Mon Mar 14 00:36:08 2016 +0100
@@ -40,13 +40,12 @@
 
 from pylons import config, tmpl_context as c, request, session, url
 from pylons.controllers import WSGIController
-from pylons.controllers.util import redirect
 from pylons.templating import render_mako as render  # don't remove this import
 from pylons.i18n.translation import _
 
 from kallithea import __version__, BACKENDS
 
-from kallithea.lib.utils2 import str2bool, safe_unicode, AttributeDict,\
+from kallithea.lib.utils2 import str2bool, safe_unicode, AttributeDict, \
     safe_str, safe_int
 from kallithea.lib import auth_modules
 from kallithea.lib.auth import AuthUser, HasPermissionAnyMiddleware
@@ -117,7 +116,9 @@
 
     auth_user = AuthUser(dbuser=user,
                          is_external_auth=is_external_auth)
-    auth_user.set_authenticated()
+    # It should not be possible to explicitly log in as the default user.
+    assert not auth_user.is_default_user
+    auth_user.is_authenticated = True
 
     # Start new session to prevent session fixation attacks.
     session.invalidate()
@@ -254,7 +255,7 @@
         and required True otherwise
         """
         #check if we have SSL required  ! if not it's a bad request !
-        if str2bool(Ui.get_by_key('push_ssl').ui_value):
+        if str2bool(Ui.get_by_key('web', 'push_ssl').ui_value):
             org_proto = environ.get('wsgi._org_proto', environ['wsgi.url_scheme'])
             if org_proto != 'https':
                 log.debug('proto is %s and SSL is required BAD REQUEST !',
@@ -366,7 +367,7 @@
 
         c.repo_name = get_repo_slug(request)  # can be empty
         c.backends = BACKENDS.keys()
-        c.unread_notifications = NotificationModel()\
+        c.unread_notifications = NotificationModel() \
                         .get_unread_cnt_for_user(c.authuser.user_id)
 
         self.cut_off_limit = safe_int(config.get('cut_off_limit'))
@@ -392,7 +393,9 @@
         # Authenticate by session cookie
         # In ancient login sessions, 'authuser' may not be a dict.
         # In that case, the user will have to log in again.
-        if isinstance(session_authuser, dict):
+        # v0.3 and earlier included an 'is_authenticated' key; if present,
+        # this must be True.
+        if isinstance(session_authuser, dict) and session_authuser.get('is_authenticated', True):
             try:
                 return AuthUser.from_cookie(session_authuser)
             except UserCreationError as e:
@@ -479,7 +482,7 @@
                 if route in ['repo_creating_home']:
                     return
                 check_url = url('repo_creating_home', repo_name=c.repo_name)
-                return redirect(check_url)
+                raise webob.exc.HTTPFound(location=check_url)
 
             dbr = c.db_repo = _dbr
             c.db_repo_scm_instance = c.db_repo.scm_instance
--- a/kallithea/lib/celerylib/tasks.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/celerylib/tasks.py	Mon Mar 14 00:36:08 2016 +0100
@@ -26,7 +26,7 @@
 :license: GPLv3, see LICENSE.md for more details.
 """
 
-from celery.decorators import task
+from celery.task import task
 
 import os
 import traceback
@@ -76,7 +76,7 @@
 
     index_location = config['index_dir']
     WhooshIndexingDaemon(index_location=index_location,
-                         repo_location=repo_location, sa=DBS)\
+                         repo_location=repo_location, sa=DBS) \
                          .run(full_index=full_index)
 
 
@@ -116,9 +116,9 @@
         last_cs = None
         timegetter = itemgetter('time')
 
-        dbrepo = DBS.query(Repository)\
+        dbrepo = DBS.query(Repository) \
             .filter(Repository.repo_name == repo_name).scalar()
-        cur_stats = DBS.query(Statistics)\
+        cur_stats = DBS.query(Statistics) \
             .filter(Statistics.repository == dbrepo).scalar()
 
         if cur_stats is not None:
@@ -176,7 +176,7 @@
                                     "changed": len(cs.changed),
                                     "removed": len(cs.removed),
                                    }
-                        co_day_auth_aggr[akc(cs.author)]['data']\
+                        co_day_auth_aggr[akc(cs.author)]['data'] \
                             .append(datadict)
 
             else:
--- a/kallithea/lib/colored_formatter.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/colored_formatter.py	Mon Mar 14 00:36:08 2016 +0100
@@ -42,19 +42,19 @@
 def format_sql(sql):
     sql = sql.replace('\n', '')
     sql = one_space_trim(sql)
-    sql = sql\
-        .replace(',', ',\n\t')\
-        .replace('SELECT', '\n\tSELECT \n\t')\
-        .replace('UPDATE', '\n\tUPDATE \n\t')\
-        .replace('DELETE', '\n\tDELETE \n\t')\
-        .replace('FROM', '\n\tFROM')\
-        .replace('ORDER BY', '\n\tORDER BY')\
-        .replace('LIMIT', '\n\tLIMIT')\
-        .replace('WHERE', '\n\tWHERE')\
-        .replace('AND', '\n\tAND')\
-        .replace('LEFT', '\n\tLEFT')\
-        .replace('INNER', '\n\tINNER')\
-        .replace('INSERT', '\n\tINSERT')\
+    sql = sql \
+        .replace(',', ',\n\t') \
+        .replace('SELECT', '\n\tSELECT \n\t') \
+        .replace('UPDATE', '\n\tUPDATE \n\t') \
+        .replace('DELETE', '\n\tDELETE \n\t') \
+        .replace('FROM', '\n\tFROM') \
+        .replace('ORDER BY', '\n\tORDER BY') \
+        .replace('LIMIT', '\n\tLIMIT') \
+        .replace('WHERE', '\n\tWHERE') \
+        .replace('AND', '\n\tAND') \
+        .replace('LEFT', '\n\tLEFT') \
+        .replace('INNER', '\n\tINNER') \
+        .replace('INSERT', '\n\tINSERT') \
         .replace('DELETE', '\n\tDELETE')
     return sql
 
--- a/kallithea/lib/compat.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/compat.py	Mon Mar 14 00:36:08 2016 +0100
@@ -540,7 +540,8 @@
                 return cmp(type(self), type(other))
             return cmp(list(self), list(other))
 
-        def __repr__(self, _track=[]):
+        def __repr__(self, _track=None):
+            _track = _track or []
             if id(self) in _track:
                 return '...'
             _track.append(id(self))
@@ -560,8 +561,9 @@
         def __copy__(self):
             return self.__class__(self)
 
-        def __deepcopy__(self, memo={}):
+        def __deepcopy__(self, memo=None):
             from copy import deepcopy
+            memo = memo or {}
             result = self.__class__()
             memo[id(self)] = result
             result.__init__(deepcopy(tuple(self), memo))
--- a/kallithea/lib/db_manage.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/db_manage.py	Mon Mar 14 00:36:08 2016 +0100
@@ -61,14 +61,14 @@
 
 
 class DbManage(object):
-    def __init__(self, log_sql, dbconf, root, tests=False, SESSION=None, cli_args={}):
+    def __init__(self, log_sql, dbconf, root, tests=False, SESSION=None, cli_args=None):
         self.dbname = dbconf.split('/')[-1]
         self.tests = tests
         self.root = root
         self.dburi = dbconf
         self.log_sql = log_sql
         self.db_exists = False
-        self.cli_args = cli_args
+        self.cli_args = cli_args or {}
         self.init_db(SESSION=SESSION)
 
         force_ask = self.cli_args.get('force_ask')
@@ -184,8 +184,8 @@
         Fixes a old kallithea version path into new one without a '*'
         """
 
-        paths = self.sa.query(Ui)\
-                .filter(Ui.ui_key == '/')\
+        paths = self.sa.query(Ui) \
+                .filter(Ui.ui_key == '/') \
                 .scalar()
 
         paths.ui_value = paths.ui_value.replace('*', '')
@@ -198,8 +198,8 @@
         Fixes a old default user with some 'nicer' default values,
         used mostly for anonymous access
         """
-        def_user = self.sa.query(User)\
-                .filter(User.username == User.DEFAULT_USER)\
+        def_user = self.sa.query(User) \
+                .filter(User.username == User.DEFAULT_USER) \
                 .one()
 
         def_user.name = 'Anonymous'
@@ -278,7 +278,7 @@
 
         #HOOKS
         hooks1_key = Ui.HOOK_UPDATE
-        hooks1_ = self.sa.query(Ui)\
+        hooks1_ = self.sa.query(Ui) \
             .filter(Ui.ui_key == hooks1_key).scalar()
 
         hooks1 = Ui() if hooks1_ is None else hooks1_
@@ -289,7 +289,7 @@
         self.sa.add(hooks1)
 
         hooks2_key = Ui.HOOK_REPO_SIZE
-        hooks2_ = self.sa.query(Ui)\
+        hooks2_ = self.sa.query(Ui) \
             .filter(Ui.ui_key == hooks2_key).scalar()
         hooks2 = Ui() if hooks2_ is None else hooks2_
         hooks2.ui_section = 'hooks'
@@ -390,9 +390,9 @@
             g.group_name = g.get_new_name(g.name)
             self.sa.add(g)
             # get default perm
-            default = UserRepoGroupToPerm.query()\
-                .filter(UserRepoGroupToPerm.group == g)\
-                .filter(UserRepoGroupToPerm.user == def_usr)\
+            default = UserRepoGroupToPerm.query() \
+                .filter(UserRepoGroupToPerm.group == g) \
+                .filter(UserRepoGroupToPerm.user == def_usr) \
                 .scalar()
 
             if default is None:
@@ -411,7 +411,7 @@
         if not default_user:
             return
 
-        u2p = UserToPerm.query()\
+        u2p = UserToPerm.query() \
             .filter(UserToPerm.user == default_user).all()
         fixed = False
         if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
@@ -522,7 +522,7 @@
     def create_user(self, username, password, email='', admin=False):
         log.info('creating user %s', username)
         UserModel().create_or_update(username, password, email,
-                                     firstname='Kallithea', lastname='Admin',
+                                     firstname=u'Kallithea', lastname=u'Admin',
                                      active=True, admin=admin,
                                      extern_type=EXTERN_TYPE_INTERNAL)
 
@@ -532,8 +532,8 @@
         user = UserModel().create_or_update(username=User.DEFAULT_USER,
                                             password=str(uuid.uuid1())[:20],
                                             email='anonymous@kallithea-scm.org',
-                                            firstname='Anonymous',
-                                            lastname='User')
+                                            firstname=u'Anonymous',
+                                            lastname=u'User')
         # based on configuration options activate/deactivate this user which
         # controls anonymous access
         if self.cli_args.get('public_access') is False:
--- a/kallithea/lib/dbmigrate/migrate/changeset/constraint.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/migrate/changeset/constraint.py	Mon Mar 14 00:36:08 2016 +0100
@@ -146,7 +146,7 @@
     Migrate's additional parameters:
 
     :param sqltext: Plain SQL text to check condition
-    :param columns: If not name is applied, you must supply this kw\
+    :param columns: If not name is applied, you must supply this kw \
     to autoname constraint
     :param table: If columns are passed as strings, this kw is required
     :type table: Table instance
--- a/kallithea/lib/dbmigrate/migrate/changeset/schema.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/migrate/changeset/schema.py	Mon Mar 14 00:36:08 2016 +0100
@@ -180,7 +180,7 @@
         * **current_col_name, \*p, \*\*kw**
             Table kw must specified.
 
-        :param table: Table at which current Column should be bound to.\
+        :param table: Table at which current Column should be bound to. \
         If table name is given, reflection will be used.
         :type table: string or Table instance
 
@@ -499,9 +499,9 @@
 
         :param table: Table instance to create on.
         :param index_name: Creates :class:`ChangesetIndex` on this column.
-        :param unique_name: Creates :class:\
+        :param unique_name: Creates :class: \
 `~migrate.changeset.constraint.UniqueConstraint` on this column.
-        :param primary_key_name: Creates :class:\
+        :param primary_key_name: Creates :class: \
 `~migrate.changeset.constraint.PrimaryKeyConstraint` on this column.
         :param populate_default: If True, created column will be \
 populated with defaults
--- a/kallithea/lib/dbmigrate/migrate/versioning/api.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/migrate/versioning/api.py	Mon Mar 14 00:36:08 2016 +0100
@@ -181,7 +181,7 @@
     You may preview the Python or SQL code to be executed, rather than
     actually executing it, using the appropriate 'preview' option.
     """
-    err = "Cannot upgrade a database of version %s to version %s. "\
+    err = "Cannot upgrade a database of version %s to version %s. " \
         "Try 'downgrade' instead."
     return _migrate(url, repository, version, upgrade=True, err=err, **opts)
 
@@ -197,7 +197,7 @@
     You may preview the Python or SQL code to be executed, rather than
     actually executing it, using the appropriate 'preview' option.
     """
-    err = "Cannot downgrade a database of version %s to version %s. "\
+    err = "Cannot downgrade a database of version %s to version %s. " \
         "Try 'upgrade' instead."
     return _migrate(url, repository, version, upgrade=False, err=err, **opts)
 
--- a/kallithea/lib/dbmigrate/migrate/versioning/repository.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/migrate/versioning/repository.py	Mon Mar 14 00:36:08 2016 +0100
@@ -238,7 +238,7 @@
         :param file_: Destination file to be written
         :param opts: Options that are passed to :func:`migrate.versioning.shell.main`
         """
-        mng_file = Template(opts.pop('templates_path', None))\
+        mng_file = Template(opts.pop('templates_path', None)) \
             .get_manage(theme=opts.pop('templates_theme', None))
 
         tmpl = open(mng_file).read()
--- a/kallithea/lib/dbmigrate/migrate/versioning/schema.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/migrate/versioning/schema.py	Mon Mar 14 00:36:08 2016 +0100
@@ -67,7 +67,7 @@
         else:
             try:
                 self.table.drop()
-            except (sa_exceptions.SQLError):
+            except sa_exceptions.SQLError:
                 raise exceptions.DatabaseNotControlledError(str(self.table))
 
     def changeset(self, version=None):
--- a/kallithea/lib/dbmigrate/schema/db_1_2_0.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_1_2_0.py	Mon Mar 14 00:36:08 2016 +0100
@@ -182,7 +182,7 @@
 
     @classmethod
     def get_by_name(cls, ldap_key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == ldap_key).scalar()
 
     @classmethod
@@ -204,7 +204,7 @@
 
     @classmethod
     def get_ldap_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('ldap_')).all()
         fd = {}
         for row in ret:
@@ -380,10 +380,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.users_group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.users_group_name == group_name)
         if cache:
             gr = gr.options(FromCache("sql_cache_short",
@@ -445,7 +445,7 @@
         try:
 
             # check if this group is not assigned to repo
-            assigned_groups = UserGroupRepoToPerm.query()\
+            assigned_groups = UserGroupRepoToPerm.query() \
                 .filter(UserGroupRepoToPerm.users_group_id ==
                         users_group_id).all()
 
@@ -526,8 +526,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session.query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.one()
 
@@ -616,7 +616,7 @@
         baseui._tcfg = config.config()
 
 
-        ret = Ui.query()\
+        ret = Ui.query() \
             .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
 
         hg_ui = ret
@@ -758,10 +758,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache("sql_cache_short",
@@ -879,7 +879,7 @@
         if not isinstance(perm, Permission):
             raise Exception('perm needs to be an instance of Permission class')
 
-        return cls.query().filter(cls.user_id == user_id)\
+        return cls.query().filter(cls.user_id == user_id) \
             .filter(cls.permission == perm).scalar() is not None
 
     @classmethod
@@ -941,8 +941,8 @@
             raise Exception('perm needs to be an instance of Permission class')
 
         return cls.query().filter(cls.users_group_id ==
-                                         users_group_id)\
-                                         .filter(cls.permission == perm)\
+                                         users_group_id) \
+                                         .filter(cls.permission == perm) \
                                          .scalar() is not None
 
     @classmethod
@@ -1047,9 +1047,9 @@
 
         :param key:
         """
-        return cls.query()\
-                .filter(CacheInvalidation.cache_key == key)\
-                .filter(CacheInvalidation.cache_active == False)\
+        return cls.query() \
+                .filter(CacheInvalidation.cache_key == key) \
+                .filter(CacheInvalidation.cache_active == False) \
                 .scalar()
 
     @classmethod
@@ -1061,7 +1061,7 @@
         """
 
         log.debug('marking %s for invalidation', key)
-        inv_obj = Session.query(cls)\
+        inv_obj = Session.query(cls) \
             .filter(cls.cache_key == key).scalar()
         if inv_obj:
             inv_obj.cache_active = False
@@ -1083,7 +1083,7 @@
 
         :param key:
         """
-        inv_obj = Session.query(CacheInvalidation)\
+        inv_obj = Session.query(CacheInvalidation) \
             .filter(CacheInvalidation.cache_key == key).scalar()
         inv_obj.cache_active = True
         Session.add(inv_obj)
--- a/kallithea/lib/dbmigrate/schema/db_1_3_0.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_1_3_0.py	Mon Mar 14 00:36:08 2016 +0100
@@ -201,7 +201,7 @@
 
     @classmethod
     def get_by_name(cls, ldap_key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == ldap_key).scalar()
 
     @classmethod
@@ -223,7 +223,7 @@
 
     @classmethod
     def get_ldap_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('ldap_')).all()
         fd = {}
         for row in ret:
@@ -533,8 +533,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session.query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -549,7 +549,7 @@
 
         :param cls:
         """
-        q = Session.query(Ui)\
+        q = Session.query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -622,7 +622,7 @@
         baseui._ucfg = config.config()
         baseui._tcfg = config.config()
 
-        ret = Ui.query()\
+        ret = Ui.query() \
             .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
 
         hg_ui = ret
@@ -671,7 +671,7 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions:
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
@@ -783,10 +783,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -838,8 +838,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -887,18 +887,18 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session.query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session.query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session.query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -1204,8 +1204,8 @@
         :param cls:
         :param revision:
         """
-        return Session.query(User)\
-                .filter(cls.revision == revision)\
+        return Session.query(User) \
+                .filter(cls.revision == revision) \
                 .join(ChangesetComment.author).all()
 
 
@@ -1234,7 +1234,7 @@
 
     @property
     def recipients(self):
-        return [x.user for x in UserNotification.query()\
+        return [x.user for x in UserNotification.query() \
                 .filter(UserNotification.notification == self).all()]
 
     @classmethod
--- a/kallithea/lib/dbmigrate/schema/db_1_4_0.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_1_4_0.py	Mon Mar 14 00:36:08 2016 +0100
@@ -191,7 +191,7 @@
 
     @classmethod
     def get_by_name(cls, key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == key).scalar()
 
     @classmethod
@@ -220,7 +220,7 @@
 
     @classmethod
     def get_ldap_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('ldap_')).all()
         fd = {}
         for row in ret:
@@ -672,8 +672,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session().query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -693,7 +693,7 @@
 
         :param cls:
         """
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -762,9 +762,9 @@
         """
         Returns associated cache keys for that repo
         """
-        return CacheInvalidation.query()\
-            .filter(CacheInvalidation.cache_args == self.repo_name)\
-            .order_by(CacheInvalidation.cache_key)\
+        return CacheInvalidation.query() \
+            .filter(CacheInvalidation.cache_args == self.repo_name) \
+            .order_by(CacheInvalidation.cache_key) \
             .all()
 
     def get_new_name(self, repo_name):
@@ -886,7 +886,7 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions:
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
@@ -903,8 +903,8 @@
         :type revisions: list
         """
 
-        statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == self)\
+        statuses = ChangesetStatus.query() \
+            .filter(ChangesetStatus.repo == self) \
             .filter(ChangesetStatus.version == 0)
         if revisions:
             statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
@@ -1051,10 +1051,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -1106,8 +1106,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -1216,18 +1216,18 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session().query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -1596,7 +1596,7 @@
         :param cls:
         :param revision:
         """
-        q = Session().query(User)\
+        q = Session().query(User) \
                 .join(ChangesetComment.author)
         if revision:
             q = q.filter(cls.revision == revision)
@@ -1753,8 +1753,8 @@
 
     @property
     def recipients(self):
-        return [x.user for x in UserNotification.query()\
-                .filter(UserNotification.notification == self)\
+        return [x.user for x in UserNotification.query() \
+                .filter(UserNotification.notification == self) \
                 .order_by(UserNotification.user_id.asc()).all()]
 
     @classmethod
--- a/kallithea/lib/dbmigrate/schema/db_1_5_0.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_1_5_0.py	Mon Mar 14 00:36:08 2016 +0100
@@ -194,7 +194,7 @@
 
     @classmethod
     def get_by_name(cls, key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == key).scalar()
 
     @classmethod
@@ -223,7 +223,7 @@
 
     @classmethod
     def get_ldap_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('ldap_')).all()
         fd = {}
         for row in ret:
@@ -233,7 +233,7 @@
 
     @classmethod
     def get_default_repo_settings(cls, cache=False, strip_prefix=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('default_')).all()
         fd = {}
         for row in ret:
@@ -691,8 +691,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session().query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -712,7 +712,7 @@
 
         :param cls:
         """
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -781,9 +781,9 @@
         """
         Returns associated cache keys for that repo
         """
-        return CacheInvalidation.query()\
-            .filter(CacheInvalidation.cache_args == self.repo_name)\
-            .order_by(CacheInvalidation.cache_key)\
+        return CacheInvalidation.query() \
+            .filter(CacheInvalidation.cache_args == self.repo_name) \
+            .order_by(CacheInvalidation.cache_key) \
             .all()
 
     def get_new_name(self, repo_name):
@@ -905,7 +905,7 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions:
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
@@ -922,8 +922,8 @@
         :type revisions: list
         """
 
-        statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == self)\
+        statuses = ChangesetStatus.query() \
+            .filter(ChangesetStatus.repo == self) \
             .filter(ChangesetStatus.version == 0)
         if revisions:
             statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
@@ -1070,10 +1070,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -1125,8 +1125,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -1243,18 +1243,18 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session().query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -1623,7 +1623,7 @@
         :param cls:
         :param revision:
         """
-        q = Session().query(User)\
+        q = Session().query(User) \
                 .join(ChangesetComment.author)
         if revision:
             q = q.filter(cls.revision == revision)
@@ -1780,8 +1780,8 @@
 
     @property
     def recipients(self):
-        return [x.user for x in UserNotification.query()\
-                .filter(UserNotification.notification == self)\
+        return [x.user for x in UserNotification.query() \
+                .filter(UserNotification.notification == self) \
                 .order_by(UserNotification.user_id.asc()).all()]
 
     @classmethod
--- a/kallithea/lib/dbmigrate/schema/db_1_5_2.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_1_5_2.py	Mon Mar 14 00:36:08 2016 +0100
@@ -196,7 +196,7 @@
 
     @classmethod
     def get_by_name(cls, key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == key).scalar()
 
     @classmethod
@@ -225,7 +225,7 @@
 
     @classmethod
     def get_ldap_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('ldap_')).all()
         fd = {}
         for row in ret:
@@ -235,7 +235,7 @@
 
     @classmethod
     def get_default_repo_settings(cls, cache=False, strip_prefix=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('default_')).all()
         fd = {}
         for row in ret:
@@ -757,8 +757,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session().query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -779,7 +779,7 @@
 
         :param cls:
         """
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -848,9 +848,9 @@
         """
         Returns associated cache keys for that repo
         """
-        return CacheInvalidation.query()\
-            .filter(CacheInvalidation.cache_args == self.repo_name)\
-            .order_by(CacheInvalidation.cache_key)\
+        return CacheInvalidation.query() \
+            .filter(CacheInvalidation.cache_args == self.repo_name) \
+            .order_by(CacheInvalidation.cache_key) \
             .all()
 
     def get_new_name(self, repo_name):
@@ -1023,7 +1023,7 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions:
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
@@ -1040,8 +1040,8 @@
         :type revisions: list
         """
 
-        statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == self)\
+        statuses = ChangesetStatus.query() \
+            .filter(ChangesetStatus.repo == self) \
             .filter(ChangesetStatus.version == 0)
         if revisions:
             statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
@@ -1191,10 +1191,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -1246,8 +1246,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -1356,18 +1356,18 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session().query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -1736,7 +1736,7 @@
         :param cls:
         :param revision:
         """
-        q = Session().query(User)\
+        q = Session().query(User) \
                 .join(ChangesetComment.author)
         if revision:
             q = q.filter(cls.revision == revision)
@@ -1901,8 +1901,8 @@
 
     @property
     def recipients(self):
-        return [x.user for x in UserNotification.query()\
-                .filter(UserNotification.notification == self)\
+        return [x.user for x in UserNotification.query() \
+                .filter(UserNotification.notification == self) \
                 .order_by(UserNotification.user_id.asc()).all()]
 
     @classmethod
--- a/kallithea/lib/dbmigrate/schema/db_1_6_0.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_1_6_0.py	Mon Mar 14 00:36:08 2016 +0100
@@ -196,7 +196,7 @@
 
     @classmethod
     def get_by_name(cls, key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == key).scalar()
 
     @classmethod
@@ -225,7 +225,7 @@
 
     @classmethod
     def get_ldap_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('ldap_')).all()
         fd = {}
         for row in ret:
@@ -235,7 +235,7 @@
 
     @classmethod
     def get_default_repo_settings(cls, cache=False, strip_prefix=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('default_')).all()
         fd = {}
         for row in ret:
@@ -713,8 +713,8 @@
 
     @classmethod
     def get_by_key_name(cls, key, repo):
-        row = cls.query()\
-                .filter(cls.repository == repo)\
+        row = cls.query() \
+                .filter(cls.repository == repo) \
                 .filter(cls.field_key == key).scalar()
         return row
 
@@ -826,8 +826,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session().query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -848,7 +848,7 @@
 
         :param cls:
         """
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -917,9 +917,9 @@
         """
         Returns associated cache keys for that repo
         """
-        return CacheInvalidation.query()\
-            .filter(CacheInvalidation.cache_args == self.repo_name)\
-            .order_by(CacheInvalidation.cache_key)\
+        return CacheInvalidation.query() \
+            .filter(CacheInvalidation.cache_args == self.repo_name) \
+            .order_by(CacheInvalidation.cache_key) \
             .all()
 
     def get_new_name(self, repo_name):
@@ -1093,7 +1093,7 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions:
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
@@ -1110,8 +1110,8 @@
         :type revisions: list
         """
 
-        statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == self)\
+        statuses = ChangesetStatus.query() \
+            .filter(ChangesetStatus.repo == self) \
             .filter(ChangesetStatus.version == 0)
         if revisions:
             statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
@@ -1266,10 +1266,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -1321,8 +1321,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -1441,18 +1441,18 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session().query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -1811,7 +1811,7 @@
         :param cls:
         :param revision:
         """
-        q = Session().query(User)\
+        q = Session().query(User) \
                 .join(ChangesetComment.author)
         if revision:
             q = q.filter(cls.revision == revision)
@@ -1980,8 +1980,8 @@
 
     @property
     def recipients(self):
-        return [x.user for x in UserNotification.query()\
-                .filter(UserNotification.notification == self)\
+        return [x.user for x in UserNotification.query() \
+                .filter(UserNotification.notification == self) \
                 .order_by(UserNotification.user_id.asc()).all()]
 
     @classmethod
--- a/kallithea/lib/dbmigrate/schema/db_1_7_0.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_1_7_0.py	Mon Mar 14 00:36:08 2016 +0100
@@ -201,7 +201,7 @@
 
     @classmethod
     def get_by_name(cls, key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == key).scalar()
 
     @classmethod
@@ -230,7 +230,7 @@
 
     @classmethod
     def get_ldap_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('ldap_')).all()
         fd = {}
         for row in ret:
@@ -240,7 +240,7 @@
 
     @classmethod
     def get_default_repo_settings(cls, cache=False, strip_prefix=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('default_')).all()
         fd = {}
         for row in ret:
@@ -742,8 +742,8 @@
 
     @classmethod
     def get_by_key_name(cls, key, repo):
-        row = cls.query()\
-                .filter(cls.repository == repo)\
+        row = cls.query() \
+                .filter(cls.repository == repo) \
                 .filter(cls.field_key == key).scalar()
         return row
 
@@ -855,8 +855,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session().query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -877,7 +877,7 @@
 
         :param cls:
         """
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -946,9 +946,9 @@
         """
         Returns associated cache keys for that repo
         """
-        return CacheInvalidation.query()\
-            .filter(CacheInvalidation.cache_args == self.repo_name)\
-            .order_by(CacheInvalidation.cache_key)\
+        return CacheInvalidation.query() \
+            .filter(CacheInvalidation.cache_args == self.repo_name) \
+            .order_by(CacheInvalidation.cache_key) \
             .all()
 
     def get_new_name(self, repo_name):
@@ -1124,7 +1124,7 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions:
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
@@ -1140,8 +1140,8 @@
         :param revisions: list of revisions to get statuses for
         """
 
-        statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == self)\
+        statuses = ChangesetStatus.query() \
+            .filter(ChangesetStatus.repo == self) \
             .filter(ChangesetStatus.version == 0)
         if revisions:
             statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
@@ -1284,10 +1284,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -1339,8 +1339,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -1497,27 +1497,27 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session().query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_user_group_perms(cls, default_user_id):
-        q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
-         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
-         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserUserGroupToPerm, UserGroup, cls) \
+         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id)) \
+         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserUserGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -1907,7 +1907,7 @@
         :param cls:
         :param revision:
         """
-        q = Session().query(User)\
+        q = Session().query(User) \
                 .join(ChangesetComment.author)
         if revision:
             q = q.filter(cls.revision == revision)
@@ -2076,8 +2076,8 @@
 
     @property
     def recipients(self):
-        return [x.user for x in UserNotification.query()\
-                .filter(UserNotification.notification == self)\
+        return [x.user for x in UserNotification.query() \
+                .filter(UserNotification.notification == self) \
                 .order_by(UserNotification.user_id.asc()).all()]
 
     @classmethod
@@ -2176,7 +2176,7 @@
         :param cls:
         """
         from kallithea.model.gist import GIST_STORE_LOC
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == URL_SEP)
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return os.path.join(q.one().ui_value, GIST_STORE_LOC)
--- a/kallithea/lib/dbmigrate/schema/db_1_8_0.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_1_8_0.py	Mon Mar 14 00:36:08 2016 +0100
@@ -203,7 +203,7 @@
 
     @classmethod
     def get_by_name(cls, key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == key).scalar()
 
     @classmethod
@@ -232,7 +232,7 @@
 
     @classmethod
     def get_ldap_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('ldap_')).all()
         fd = {}
         for row in ret:
@@ -242,7 +242,7 @@
 
     @classmethod
     def get_default_repo_settings(cls, cache=False, strip_prefix=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('default_')).all()
         fd = {}
         for row in ret:
@@ -772,8 +772,8 @@
 
     @classmethod
     def get_by_key_name(cls, key, repo):
-        row = cls.query()\
-                .filter(cls.repository == repo)\
+        row = cls.query() \
+                .filter(cls.repository == repo) \
                 .filter(cls.field_key == key).scalar()
         return row
 
@@ -885,8 +885,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session().query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -907,7 +907,7 @@
 
         :param cls:
         """
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -976,9 +976,9 @@
         """
         Returns associated cache keys for that repo
         """
-        return CacheInvalidation.query()\
-            .filter(CacheInvalidation.cache_args == self.repo_name)\
-            .order_by(CacheInvalidation.cache_key)\
+        return CacheInvalidation.query() \
+            .filter(CacheInvalidation.cache_args == self.repo_name) \
+            .order_by(CacheInvalidation.cache_key) \
             .all()
 
     def get_new_name(self, repo_name):
@@ -1154,7 +1154,7 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions:
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
@@ -1170,8 +1170,8 @@
         :param revisions: list of revisions to get statuses for
         """
 
-        statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == self)\
+        statuses = ChangesetStatus.query() \
+            .filter(ChangesetStatus.repo == self) \
             .filter(ChangesetStatus.version == 0)
         if revisions:
             statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
@@ -1314,10 +1314,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -1369,8 +1369,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -1543,27 +1543,27 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session().query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_user_group_perms(cls, default_user_id):
-        q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
-         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
-         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserUserGroupToPerm, UserGroup, cls) \
+         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id)) \
+         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserUserGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -1953,7 +1953,7 @@
         :param cls:
         :param revision:
         """
-        q = Session().query(User)\
+        q = Session().query(User) \
                 .join(ChangesetComment.author)
         if revision:
             q = q.filter(cls.revision == revision)
@@ -2123,8 +2123,8 @@
 
     @property
     def recipients(self):
-        return [x.user for x in UserNotification.query()\
-                .filter(UserNotification.notification == self)\
+        return [x.user for x in UserNotification.query() \
+                .filter(UserNotification.notification == self) \
                 .order_by(UserNotification.user_id.asc()).all()]
 
     @classmethod
@@ -2223,7 +2223,7 @@
         :param cls:
         """
         from kallithea.model.gist import GIST_STORE_LOC
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == URL_SEP)
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return os.path.join(q.one().ui_value, GIST_STORE_LOC)
--- a/kallithea/lib/dbmigrate/schema/db_2_0_0.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_2_0_0.py	Mon Mar 14 00:36:08 2016 +0100
@@ -218,7 +218,7 @@
 
     @classmethod
     def get_by_name(cls, key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == key).scalar()
 
     @classmethod
@@ -278,7 +278,7 @@
 
     @classmethod
     def get_auth_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('auth_')).all()
         fd = {}
         for row in ret:
@@ -288,7 +288,7 @@
 
     @classmethod
     def get_default_repo_settings(cls, cache=False, strip_prefix=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('default_')).all()
         fd = {}
         for row in ret:
@@ -829,8 +829,8 @@
 
     @classmethod
     def get_by_key_name(cls, key, repo):
-        row = cls.query()\
-                .filter(cls.repository == repo)\
+        row = cls.query() \
+                .filter(cls.repository == repo) \
                 .filter(cls.field_key == key).scalar()
         return row
 
@@ -942,8 +942,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session().query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -964,7 +964,7 @@
 
         :param cls:
         """
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -1033,9 +1033,9 @@
         """
         Returns associated cache keys for that repo
         """
-        return CacheInvalidation.query()\
-            .filter(CacheInvalidation.cache_args == self.repo_name)\
-            .order_by(CacheInvalidation.cache_key)\
+        return CacheInvalidation.query() \
+            .filter(CacheInvalidation.cache_args == self.repo_name) \
+            .order_by(CacheInvalidation.cache_key) \
             .all()
 
     def get_new_name(self, repo_name):
@@ -1211,7 +1211,7 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions:
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
@@ -1227,8 +1227,8 @@
         :param revisions: list of revisions to get statuses for
         """
 
-        statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == self)\
+        statuses = ChangesetStatus.query() \
+            .filter(ChangesetStatus.repo == self) \
             .filter(ChangesetStatus.version == 0)
         if revisions:
             statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
@@ -1371,10 +1371,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -1426,8 +1426,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -1600,27 +1600,27 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session().query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_user_group_perms(cls, default_user_id):
-        q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
-         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
-         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserUserGroupToPerm, UserGroup, cls) \
+         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id)) \
+         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserUserGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -2013,7 +2013,7 @@
         :param cls:
         :param revision:
         """
-        q = Session().query(User)\
+        q = Session().query(User) \
                 .join(ChangesetComment.author)
         if revision:
             q = q.filter(cls.revision == revision)
@@ -2183,8 +2183,8 @@
 
     @property
     def recipients(self):
-        return [x.user for x in UserNotification.query()\
-                .filter(UserNotification.notification == self)\
+        return [x.user for x in UserNotification.query() \
+                .filter(UserNotification.notification == self) \
                 .order_by(UserNotification.user_id.asc()).all()]
 
     @classmethod
@@ -2283,7 +2283,7 @@
         :param cls:
         """
         from kallithea.model.gist import GIST_STORE_LOC
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == URL_SEP)
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return os.path.join(q.one().ui_value, GIST_STORE_LOC)
--- a/kallithea/lib/dbmigrate/schema/db_2_0_1.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_2_0_1.py	Mon Mar 14 00:36:08 2016 +0100
@@ -221,7 +221,7 @@
 
     @classmethod
     def get_by_name(cls, key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == key).scalar()
 
     @classmethod
@@ -281,7 +281,7 @@
 
     @classmethod
     def get_auth_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('auth_')).all()
         fd = {}
         for row in ret:
@@ -291,7 +291,7 @@
 
     @classmethod
     def get_default_repo_settings(cls, cache=False, strip_prefix=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('default_')).all()
         fd = {}
         for row in ret:
@@ -828,8 +828,8 @@
 
     @classmethod
     def get_by_key_name(cls, key, repo):
-        row = cls.query()\
-                .filter(cls.repository == repo)\
+        row = cls.query() \
+                .filter(cls.repository == repo) \
                 .filter(cls.field_key == key).scalar()
         return row
 
@@ -941,8 +941,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session().query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -963,7 +963,7 @@
 
         :param cls:
         """
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -1032,9 +1032,9 @@
         """
         Returns associated cache keys for that repo
         """
-        return CacheInvalidation.query()\
-            .filter(CacheInvalidation.cache_args == self.repo_name)\
-            .order_by(CacheInvalidation.cache_key)\
+        return CacheInvalidation.query() \
+            .filter(CacheInvalidation.cache_args == self.repo_name) \
+            .order_by(CacheInvalidation.cache_key) \
             .all()
 
     def get_new_name(self, repo_name):
@@ -1210,7 +1210,7 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions:
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
@@ -1226,8 +1226,8 @@
         :param revisions: list of revisions to get statuses for
         """
 
-        statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == self)\
+        statuses = ChangesetStatus.query() \
+            .filter(ChangesetStatus.repo == self) \
             .filter(ChangesetStatus.version == 0)
         if revisions:
             statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
@@ -1372,10 +1372,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -1427,8 +1427,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -1601,27 +1601,27 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session().query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_user_group_perms(cls, default_user_id):
-        q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
-         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
-         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserUserGroupToPerm, UserGroup, cls) \
+         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id)) \
+         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserUserGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -2014,7 +2014,7 @@
         :param cls:
         :param revision:
         """
-        q = Session().query(User)\
+        q = Session().query(User) \
                 .join(ChangesetComment.author)
         if revision:
             q = q.filter(cls.revision == revision)
@@ -2184,8 +2184,8 @@
 
     @property
     def recipients(self):
-        return [x.user for x in UserNotification.query()\
-                .filter(UserNotification.notification == self)\
+        return [x.user for x in UserNotification.query() \
+                .filter(UserNotification.notification == self) \
                 .order_by(UserNotification.user_id.asc()).all()]
 
     @classmethod
@@ -2284,7 +2284,7 @@
         :param cls:
         """
         from kallithea.model.gist import GIST_STORE_LOC
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == URL_SEP)
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return os.path.join(q.one().ui_value, GIST_STORE_LOC)
--- a/kallithea/lib/dbmigrate/schema/db_2_0_2.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_2_0_2.py	Mon Mar 14 00:36:08 2016 +0100
@@ -221,7 +221,7 @@
 
     @classmethod
     def get_by_name(cls, key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == key).scalar()
 
     @classmethod
@@ -281,7 +281,7 @@
 
     @classmethod
     def get_auth_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('auth_')).all()
         fd = {}
         for row in ret:
@@ -291,7 +291,7 @@
 
     @classmethod
     def get_default_repo_settings(cls, cache=False, strip_prefix=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('default_')).all()
         fd = {}
         for row in ret:
@@ -828,8 +828,8 @@
 
     @classmethod
     def get_by_key_name(cls, key, repo):
-        row = cls.query()\
-                .filter(cls.repository == repo)\
+        row = cls.query() \
+                .filter(cls.repository == repo) \
                 .filter(cls.field_key == key).scalar()
         return row
 
@@ -958,8 +958,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session().query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -980,7 +980,7 @@
 
         :param cls:
         """
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -1049,9 +1049,9 @@
         """
         Returns associated cache keys for that repo
         """
-        return CacheInvalidation.query()\
-            .filter(CacheInvalidation.cache_args == self.repo_name)\
-            .order_by(CacheInvalidation.cache_key)\
+        return CacheInvalidation.query() \
+            .filter(CacheInvalidation.cache_args == self.repo_name) \
+            .order_by(CacheInvalidation.cache_key) \
             .all()
 
     def get_new_name(self, repo_name):
@@ -1230,7 +1230,7 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions:
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
@@ -1246,8 +1246,8 @@
         :param revisions: list of revisions to get statuses for
         """
 
-        statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == self)\
+        statuses = ChangesetStatus.query() \
+            .filter(ChangesetStatus.repo == self) \
             .filter(ChangesetStatus.version == 0)
         if revisions:
             statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
@@ -1393,10 +1393,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -1448,8 +1448,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -1622,27 +1622,27 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session().query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_user_group_perms(cls, default_user_id):
-        q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
-         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
-         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserUserGroupToPerm, UserGroup, cls) \
+         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id)) \
+         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserUserGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -2035,7 +2035,7 @@
         :param cls:
         :param revision:
         """
-        q = Session().query(User)\
+        q = Session().query(User) \
                 .join(ChangesetComment.author)
         if revision:
             q = q.filter(cls.revision == revision)
@@ -2205,8 +2205,8 @@
 
     @property
     def recipients(self):
-        return [x.user for x in UserNotification.query()\
-                .filter(UserNotification.notification == self)\
+        return [x.user for x in UserNotification.query() \
+                .filter(UserNotification.notification == self) \
                 .order_by(UserNotification.user_id.asc()).all()]
 
     @classmethod
@@ -2305,7 +2305,7 @@
         :param cls:
         """
         from kallithea.model.gist import GIST_STORE_LOC
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == URL_SEP)
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return os.path.join(q.one().ui_value, GIST_STORE_LOC)
--- a/kallithea/lib/dbmigrate/schema/db_2_1_0.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_2_1_0.py	Mon Mar 14 00:36:08 2016 +0100
@@ -224,7 +224,7 @@
 
     @classmethod
     def get_by_name(cls, key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == key).scalar()
 
     @classmethod
@@ -284,7 +284,7 @@
 
     @classmethod
     def get_auth_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('auth_')).all()
         fd = {}
         for row in ret:
@@ -294,7 +294,7 @@
 
     @classmethod
     def get_default_repo_settings(cls, cache=False, strip_prefix=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('default_')).all()
         fd = {}
         for row in ret:
@@ -523,10 +523,10 @@
 
         if fallback and not res:
             #fallback to additional keys
-            _res = UserApiKeys.query()\
-                .filter(UserApiKeys.api_key == api_key)\
+            _res = UserApiKeys.query() \
+                .filter(UserApiKeys.api_key == api_key) \
                 .filter(or_(UserApiKeys.expires == -1,
-                            UserApiKeys.expires >= time.time()))\
+                            UserApiKeys.expires >= time.time())) \
                 .first()
             if _res:
                 res = _res.user
@@ -866,8 +866,8 @@
 
     @classmethod
     def get_by_key_name(cls, key, repo):
-        row = cls.query()\
-                .filter(cls.repository == repo)\
+        row = cls.query() \
+                .filter(cls.repository == repo) \
                 .filter(cls.field_key == key).scalar()
         return row
 
@@ -997,8 +997,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session().query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -1019,7 +1019,7 @@
 
         :param cls:
         """
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -1088,9 +1088,9 @@
         """
         Returns associated cache keys for that repo
         """
-        return CacheInvalidation.query()\
-            .filter(CacheInvalidation.cache_args == self.repo_name)\
-            .order_by(CacheInvalidation.cache_key)\
+        return CacheInvalidation.query() \
+            .filter(CacheInvalidation.cache_args == self.repo_name) \
+            .order_by(CacheInvalidation.cache_key) \
             .all()
 
     def get_new_name(self, repo_name):
@@ -1269,7 +1269,7 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions:
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
@@ -1285,8 +1285,8 @@
         :param revisions: list of revisions to get statuses for
         """
 
-        statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == self)\
+        statuses = ChangesetStatus.query() \
+            .filter(ChangesetStatus.repo == self) \
             .filter(ChangesetStatus.version == 0)
         if revisions:
             statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
@@ -1432,10 +1432,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -1487,8 +1487,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -1661,27 +1661,27 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session().query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_user_group_perms(cls, default_user_id):
-        q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
-         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
-         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserUserGroupToPerm, UserGroup, cls) \
+         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id)) \
+         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserUserGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -2074,7 +2074,7 @@
         :param cls:
         :param revision:
         """
-        q = Session().query(User)\
+        q = Session().query(User) \
                 .join(ChangesetComment.author)
         if revision:
             q = q.filter(cls.revision == revision)
@@ -2244,8 +2244,8 @@
 
     @property
     def recipients(self):
-        return [x.user for x in UserNotification.query()\
-                .filter(UserNotification.notification == self)\
+        return [x.user for x in UserNotification.query() \
+                .filter(UserNotification.notification == self) \
                 .order_by(UserNotification.user_id.asc()).all()]
 
     @classmethod
@@ -2344,7 +2344,7 @@
         :param cls:
         """
         from kallithea.model.gist import GIST_STORE_LOC
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == URL_SEP)
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return os.path.join(q.one().ui_value, GIST_STORE_LOC)
--- a/kallithea/lib/dbmigrate/schema/db_2_2_0.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_2_2_0.py	Mon Mar 14 00:36:08 2016 +0100
@@ -225,7 +225,7 @@
 
     @classmethod
     def get_by_name(cls, key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == key).scalar()
 
     @classmethod
@@ -285,7 +285,7 @@
 
     @classmethod
     def get_auth_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('auth_')).all()
         fd = {}
         for row in ret:
@@ -295,7 +295,7 @@
 
     @classmethod
     def get_default_repo_settings(cls, cache=False, strip_prefix=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('default_')).all()
         fd = {}
         for row in ret:
@@ -550,10 +550,10 @@
 
         if fallback and not res:
             #fallback to additional keys
-            _res = UserApiKeys.query()\
-                .filter(UserApiKeys.api_key == api_key)\
+            _res = UserApiKeys.query() \
+                .filter(UserApiKeys.api_key == api_key) \
                 .filter(or_(UserApiKeys.expires == -1,
-                            UserApiKeys.expires >= time.time()))\
+                            UserApiKeys.expires >= time.time())) \
                 .first()
             if _res:
                 res = _res.user
@@ -901,8 +901,8 @@
 
     @classmethod
     def get_by_key_name(cls, key, repo):
-        row = cls.query()\
-                .filter(cls.repository == repo)\
+        row = cls.query() \
+                .filter(cls.repository == repo) \
                 .filter(cls.field_key == key).scalar()
         return row
 
@@ -1037,8 +1037,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session().query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -1059,7 +1059,7 @@
 
         :param cls:
         """
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -1128,9 +1128,9 @@
         """
         Returns associated cache keys for that repo
         """
-        return CacheInvalidation.query()\
-            .filter(CacheInvalidation.cache_args == self.repo_name)\
-            .order_by(CacheInvalidation.cache_key)\
+        return CacheInvalidation.query() \
+            .filter(CacheInvalidation.cache_args == self.repo_name) \
+            .order_by(CacheInvalidation.cache_key) \
             .all()
 
     def get_new_name(self, repo_name):
@@ -1315,7 +1315,7 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions:
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
@@ -1331,8 +1331,8 @@
         :param revisions: list of revisions to get statuses for
         """
 
-        statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == self)\
+        statuses = ChangesetStatus.query() \
+            .filter(ChangesetStatus.repo == self) \
             .filter(ChangesetStatus.version == 0)
         if revisions:
             statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
@@ -1482,10 +1482,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -1537,8 +1537,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -1714,27 +1714,27 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session().query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_user_group_perms(cls, default_user_id):
-        q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
-         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
-         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserUserGroupToPerm, UserGroup, cls) \
+         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id)) \
+         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserUserGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -2127,7 +2127,7 @@
         :param cls:
         :param revision:
         """
-        q = Session().query(User)\
+        q = Session().query(User) \
                 .join(ChangesetComment.author)
         if revision:
             q = q.filter(cls.revision == revision)
@@ -2297,8 +2297,8 @@
 
     @property
     def recipients(self):
-        return [x.user for x in UserNotification.query()\
-                .filter(UserNotification.notification == self)\
+        return [x.user for x in UserNotification.query() \
+                .filter(UserNotification.notification == self) \
                 .order_by(UserNotification.user_id.asc()).all()]
 
     @classmethod
@@ -2401,7 +2401,7 @@
         :param cls:
         """
         from kallithea.model.gist import GIST_STORE_LOC
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == URL_SEP)
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return os.path.join(q.one().ui_value, GIST_STORE_LOC)
--- a/kallithea/lib/dbmigrate/schema/db_2_2_3.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/schema/db_2_2_3.py	Mon Mar 14 00:36:08 2016 +0100
@@ -225,7 +225,7 @@
 
     @classmethod
     def get_by_name(cls, key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == key).scalar()
 
     @classmethod
@@ -285,7 +285,7 @@
 
     @classmethod
     def get_auth_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('auth_')).all()
         fd = {}
         for row in ret:
@@ -295,7 +295,7 @@
 
     @classmethod
     def get_default_repo_settings(cls, cache=False, strip_prefix=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('default_')).all()
         fd = {}
         for row in ret:
@@ -550,10 +550,10 @@
 
         if fallback and not res:
             #fallback to additional keys
-            _res = UserApiKeys.query()\
-                .filter(UserApiKeys.api_key == api_key)\
+            _res = UserApiKeys.query() \
+                .filter(UserApiKeys.api_key == api_key) \
                 .filter(or_(UserApiKeys.expires == -1,
-                            UserApiKeys.expires >= time.time()))\
+                            UserApiKeys.expires >= time.time())) \
                 .first()
             if _res:
                 res = _res.user
@@ -919,8 +919,8 @@
 
     @classmethod
     def get_by_key_name(cls, key, repo):
-        row = cls.query()\
-                .filter(cls.repository == repo)\
+        row = cls.query() \
+                .filter(cls.repository == repo) \
                 .filter(cls.field_key == key).scalar()
         return row
 
@@ -1057,8 +1057,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session().query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -1079,7 +1079,7 @@
 
         :param cls:
         """
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -1148,9 +1148,9 @@
         """
         Returns associated cache keys for that repo
         """
-        return CacheInvalidation.query()\
-            .filter(CacheInvalidation.cache_args == self.repo_name)\
-            .order_by(CacheInvalidation.cache_key)\
+        return CacheInvalidation.query() \
+            .filter(CacheInvalidation.cache_args == self.repo_name) \
+            .order_by(CacheInvalidation.cache_key) \
             .all()
 
     def get_new_name(self, repo_name):
@@ -1342,7 +1342,7 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions:
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
@@ -1358,8 +1358,8 @@
         :param revisions: list of revisions to get statuses for
         """
 
-        statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == self)\
+        statuses = ChangesetStatus.query() \
+            .filter(ChangesetStatus.repo == self) \
             .filter(ChangesetStatus.version == 0)
         if revisions:
             statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
@@ -1509,10 +1509,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name.ilike(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -1564,8 +1564,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -1741,27 +1741,27 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session().query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_user_group_perms(cls, default_user_id):
-        q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
-         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
-         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserUserGroupToPerm, UserGroup, cls) \
+         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id)) \
+         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserUserGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -2173,7 +2173,7 @@
         :param cls:
         :param revision:
         """
-        q = Session().query(User)\
+        q = Session().query(User) \
                 .join(ChangesetComment.author)
         if revision:
             q = q.filter(cls.revision == revision)
@@ -2343,8 +2343,8 @@
 
     @property
     def recipients(self):
-        return [x.user for x in UserNotification.query()\
-                .filter(UserNotification.notification == self)\
+        return [x.user for x in UserNotification.query() \
+                .filter(UserNotification.notification == self) \
                 .order_by(UserNotification.user_id.asc()).all()]
 
     @classmethod
@@ -2447,7 +2447,7 @@
         :param cls:
         """
         from kallithea.model.gist import GIST_STORE_LOC
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == URL_SEP)
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return os.path.join(q.one().ui_value, GIST_STORE_LOC)
--- a/kallithea/lib/dbmigrate/versions/001_initial_release.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/versions/001_initial_release.py	Mon Mar 14 00:36:08 2016 +0100
@@ -77,7 +77,7 @@
             session.add(self)
             session.commit()
             log.debug('updated user %s lastlogin', self.username)
-        except (DatabaseError,):
+        except DatabaseError:
             session.rollback()
 
 
--- a/kallithea/lib/dbmigrate/versions/008_version_1_5_0.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/dbmigrate/versions/008_version_1_5_0.py	Mon Mar 14 00:36:08 2016 +0100
@@ -32,8 +32,8 @@
 
     _Session = meta.Session()
     ## after adding that column fix all usernames
-    users_log = _Session.query(db_1_5_0.UserLog)\
-            .options(joinedload(db_1_5_0.UserLog.user))\
+    users_log = _Session.query(db_1_5_0.UserLog) \
+            .options(joinedload(db_1_5_0.UserLog.user)) \
             .options(joinedload(db_1_5_0.UserLog.repository)).all()
 
     for entry in users_log:
--- a/kallithea/lib/diffs.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/diffs.py	Mon Mar 14 00:36:08 2016 +0100
@@ -364,7 +364,7 @@
         groups = match.groupdict()
         rest = diff_chunk[match.end():]
         if rest and not rest.startswith('@') and not rest.startswith('literal ') and not rest.startswith('delta '):
-            raise Exception('cannot parse diff header: %r followed by %r' % (diff_chunk[:match.end()], rest[:1000]))
+            raise Exception('cannot parse %s diff header: %r followed by %r' % (self.vcs, diff_chunk[:match.end()], rest[:1000]))
         difflines = imap(self._escaper, re.findall(r'.*\n|.+$', rest)) # don't split on \r as str.splitlines do
         return groups, difflines
 
--- a/kallithea/lib/graphmod.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/graphmod.py	Mon Mar 14 00:36:08 2016 +0100
@@ -21,9 +21,14 @@
 
 def _first_known_ancestors(parentrev_func, minrev, knownrevs, head):
     """
-    Walk DAG defined by parentrev_func.
-    Return most immediate ancestors of head that are in knownrevs.
-    minrev is lower bound on knownrevs.
+    Return the apparent parents of the head revision in a filtered DAG.
+    This is like calling plain parentrev_func, except that if the parent has
+    been filtered (isn't in knownrevs), recurse on its parents.
+    For ancestors that are unknown in knownrevs, the revision number is negated.
+    (This means that a Mercurial revision can have more than 2 "parents".)
+
+    parentrev_func defines the full DAG.
+    knownrevs contains the subset we care about. minrev is lower bound on knownrevs.
     """
     pending = set([head])
     seen = set()
@@ -57,6 +62,7 @@
     return list(_colored(repo, dag))
 
 def _dagwalker(repo, revs):
+    """Iterate over revs, yielding revs (highest first) and parents to show in the graph."""
     if not revs:
         return
 
@@ -66,7 +72,7 @@
         def parentrev_func(rev):
             return [x.revision for x in repo[rev].parents]
 
-    minrev = revs[-1] # assuming sorted reverse
+    minrev = revs[-1] # assuming sorted with highest revision numbers first
     knownrevs = set(revs)
     acache = {}
     for rev in revs:
@@ -97,14 +103,17 @@
     """
     branch_cache = {}
     def branch(rev):
+        """Return branch for rev, using cache for efficiency.
+        For Mercurial, always return the named branch name (which may be 'default').
+        For Git, return a branch name for branch heads, otherwise None."""
         if rev not in branch_cache:
             branch_cache[rev] = repo[rev].branch
         return branch_cache[rev]
 
-    row = []
-    colors = {}
-    obs = {}
-    newcolor = 1
+    row = []  # the ancestor revision that each column is tracking
+    colors = {}  # color number for revisions - set by descendants
+    obs = {}  # obsolete flag for revisions - set by descendants
+    newcolor = 1  # the next available color
 
     for (rev, dagparents) in dag:
 
@@ -121,24 +130,26 @@
         # Add unknown parents to nextrow
         tmprow = row[:]
         tmprow[col:col + 1] = reversed(addparents) # highest revs first (to the right), dead ends last (to the left)
-        # Stop looking for non-existing ancestors
+        # Filter old fake parents but keep new ones
         nextrow = []
         for r in tmprow:
-            if r > nullrev or r in dagparents:
+            if r >= 0 or r in dagparents:
                 nextrow.append(r)
             else:
                 colors.pop(r)
                 obs.pop(r)
 
-        # Set colors for the parents
+        # Let color and obs for this rev be "inherited" by the first "parent"
         color = colors.pop(rev)
         if addparents:
             b = branch(rev)
+            searching = True
             for p in reversed(addparents):
                 obs[p] = int(repo[p].obsolete)
-                if b and branch(abs(p)) == b:
+                if searching and branch(abs(p)) in [b, None]:
+                    # This is the first parent on the same branch - inherit the color
                     colors[p] = color
-                    b = None
+                    searching = False # make sure we don't give the color away twice
                 else:
                     colors[p] = newcolor
                     newcolor += 1
--- a/kallithea/lib/helpers.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/helpers.py	Mon Mar 14 00:36:08 2016 +0100
@@ -54,8 +54,8 @@
 from kallithea.lib.annotate import annotate_highlight
 from kallithea.lib.utils import repo_name_slug, get_custom_lexer
 from kallithea.lib.utils2 import str2bool, safe_unicode, safe_str, \
-    get_changeset_safe, datetime_to_time, time_to_datetime, AttributeDict,\
-    safe_int
+    get_changeset_safe, datetime_to_time, time_to_datetime, AttributeDict, \
+    safe_int, MENTIONS_REGEX
 from kallithea.lib.markup_renderer import MarkupRenderer, url_re
 from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError
 from kallithea.lib.vcs.backends.base import BaseChangeset, EmptyChangeset
@@ -480,14 +480,9 @@
 def user_or_none(author):
     email = author_email(author)
     if email:
-        user = User.get_by_email(email, case_insensitive=True, cache=True)
+        user = User.get_by_email(email, cache=True)
         if user is not None:
             return user
-
-    user = User.get_by_username(author_name(author), case_insensitive=True, cache=True)
-    if user is not None:
-        return user
-
     return None
 
 def email_or_none(author):
@@ -802,8 +797,8 @@
     if feed:
         action = action_str[0].replace('[', '').replace(']', '')
     else:
-        action = action_str[0]\
-            .replace('[', '<span class="journal_highlight">')\
+        action = action_str[0] \
+            .replace('[', '<span class="journal_highlight">') \
             .replace(']', '</span>')
 
     action_params_func = lambda: ""
@@ -879,10 +874,10 @@
 
         tmpl = _gravatar_url
         parsed_url = urlparse.urlparse(url.current(qualified=True))
-        tmpl = tmpl.replace('{email}', email_address)\
+        tmpl = tmpl.replace('{email}', email_address) \
                    .replace('{md5email}', _md5(safe_str(email_address).lower())) \
-                   .replace('{netloc}', parsed_url.netloc)\
-                   .replace('{scheme}', parsed_url.scheme)\
+                   .replace('{netloc}', parsed_url.netloc) \
+                   .replace('{scheme}', parsed_url.scheme) \
                    .replace('{size}', safe_str(size))
         return tmpl
 
@@ -979,18 +974,17 @@
         show_if_single_page=False, separator=' ', onclick=None,
         symbol_first='<<', symbol_last='>>',
         symbol_previous='<', symbol_next='>',
-        link_attr={'class': 'pager_link', 'rel': 'prerender'},
-        curpage_attr={'class': 'pager_curpage'},
-        dotdot_attr={'class': 'pager_dotdot'}, **kwargs):
-
-        self.curpage_attr = curpage_attr
+        link_attr=None,
+        curpage_attr=None,
+        dotdot_attr=None, **kwargs):
+        self.curpage_attr = curpage_attr or {'class': 'pager_curpage'}
         self.separator = separator
         self.pager_kwargs = kwargs
         self.page_param = page_param
         self.partial_param = partial_param
         self.onclick = onclick
-        self.link_attr = link_attr
-        self.dotdot_attr = dotdot_attr
+        self.link_attr = link_attr or {'class': 'pager_link', 'rel': 'prerender'}
+        self.dotdot_attr = dotdot_attr or {'class': 'pager_dotdot'}
 
         # Don't show navigator if there is no more than one page
         if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page):
@@ -1241,14 +1235,16 @@
     return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
 
 
+def _urlify_text_replace(match_obj):
+    url_full = match_obj.group(1)
+    return '<a href="%(url)s">%(url)s</a>' % {'url': url_full}
+
+
 def _urlify_text(s):
     """
     Extract urls from text and make html links out of them
     """
-    def url_func(match_obj):
-        url_full = match_obj.group(1)
-        return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
-    return url_re.sub(url_func, s)
+    return url_re.sub(_urlify_text_replace, s)
 
 def urlify_text(s, truncate=None, stylize=False, truncatef=truncate):
     """
@@ -1262,6 +1258,20 @@
     s = _urlify_text(s)
     return literal(s)
 
+
+def _urlify_changeset_replace_f(repository):
+    from pylons import url  # doh, we need to re-import url to mock it later
+    def urlify_changeset_replace(match_obj):
+        rev = match_obj.group(0)
+        return '<a class="revision-link" href="%(url)s">%(rev)s</a>' % {
+         'url': url('changeset_home', repo_name=repository, revision=rev),
+         'rev': rev,
+        }
+    return urlify_changeset_replace
+
+
+urilify_changeset_re = r'(?:^|(?<=[\s(),]))([0-9a-fA-F]{12,40})(?=$|\s|[.,:()])'
+
 def urlify_changesets(text_, repository):
     """
     Extract revision ids from changeset and make link from them
@@ -1269,18 +1279,12 @@
     :param text_:
     :param repository: repo name to build the URL with
     """
-    from pylons import url  # doh, we need to re-import url to mock it later
+    urlify_changeset_replace = _urlify_changeset_replace_f(repository)
+    return re.sub(urilify_changeset_re, urlify_changeset_replace, text_)
 
-    def url_func(match_obj):
-        rev = match_obj.group(0)
-        return '<a class="revision-link" href="%(url)s">%(rev)s</a>' % {
-         'url': url('changeset_home', repo_name=repository, revision=rev),
-         'rev': rev,
-        }
-
-    return re.sub(r'(?:^|(?<=[\s(),]))([0-9a-fA-F]{12,40})(?=$|\s|[.,:()])', url_func, text_)
 
 def linkify_others(t, l):
+    # attempt at fixing double quoting?
     urls = re.compile(r'(\<a.*?\<\/a\>)',)
     links = []
     for e in urls.split(t):
@@ -1313,6 +1317,35 @@
 
     return literal(newtext)
 
+
+def _urlify_issues_replace_f(repository, ISSUE_SERVER_LNK, ISSUE_PREFIX):
+    def urlify_issues_replace(match_obj):
+        pref = ''
+        if match_obj.group().startswith(' '):
+            pref = ' '
+
+        issue_id = ''.join(match_obj.groups())
+        issue_url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
+        if repository:
+            issue_url = issue_url.replace('{repo}', repository)
+            repo_name = repository.split(URL_SEP)[-1]
+            issue_url = issue_url.replace('{repo_name}', repo_name)
+
+        return (
+            '%(pref)s<a class="%(cls)s" href="%(url)s">'
+            '%(issue-prefix)s%(id-repr)s'
+            '</a>'
+            ) % {
+             'pref': pref,
+             'cls': 'issue-tracker-link',
+             'url': issue_url,
+             'id-repr': issue_id,
+             'issue-prefix': ISSUE_PREFIX,
+             'serv': ISSUE_SERVER_LNK,
+            }
+    return urlify_issues_replace
+
+
 def urlify_issues(newtext, repository, link_=None):
     from kallithea import CONFIG as conf
 
@@ -1335,35 +1368,12 @@
 
         log.debug('pattern suffix `%s` PAT:%s SERVER_LINK:%s PREFIX:%s',
                   pattern_index, ISSUE_PATTERN, ISSUE_SERVER_LNK,
-                     ISSUE_PREFIX)
+                  ISSUE_PREFIX)
 
         URL_PAT = re.compile(ISSUE_PATTERN)
 
-        def url_func(match_obj):
-            pref = ''
-            if match_obj.group().startswith(' '):
-                pref = ' '
-
-            issue_id = ''.join(match_obj.groups())
-            issue_url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
-            if repository:
-                issue_url = issue_url.replace('{repo}', repository)
-                repo_name = repository.split(URL_SEP)[-1]
-                issue_url = issue_url.replace('{repo_name}', repo_name)
-
-            return (
-                '%(pref)s<a class="%(cls)s" href="%(url)s">'
-                '%(issue-prefix)s%(id-repr)s'
-                '</a>'
-                ) % {
-                 'pref': pref,
-                 'cls': 'issue-tracker-link',
-                 'url': issue_url,
-                 'id-repr': issue_id,
-                 'issue-prefix': ISSUE_PREFIX,
-                 'serv': ISSUE_SERVER_LNK,
-                }
-        newtext = URL_PAT.sub(url_func, newtext)
+        urlify_issues_replace = _urlify_issues_replace_f(repository, ISSUE_SERVER_LNK, ISSUE_PREFIX)
+        newtext = URL_PAT.sub(urlify_issues_replace, newtext)
         log.debug('processed prefix:`%s` => %s', pattern_index, newtext)
 
     # if we actually did something above
@@ -1373,19 +1383,28 @@
     return newtext
 
 
-def rst(source):
-    return literal('<div class="rst-block">%s</div>' %
-                   MarkupRenderer.rst(source))
+def _mentions_replace(match_obj):
+    return '<b>@%s</b>' % match_obj.group(1)
 
 
-def rst_w_mentions(source):
+def render_w_mentions(source, repo_name=None):
+    """
+    Render plain text with revision hashes and issue references urlified
+    and with @mention highlighting.
     """
-    Wrapped rst renderer with @mention highlighting
+    s = source.rstrip()
+    s = safe_unicode(s)
+    s = '\n'.join(s.splitlines())
+    s = html_escape(s)
+    # this sequence of html-ifications seems to be safe and non-conflicting
+    # if the issues regexp is sane
+    s = _urlify_text(s)
+    if repo_name is not None:
+        s = urlify_changesets(s, repo_name)
+    s = urlify_issues(s, repo_name)
+    s = MENTIONS_REGEX.sub(_mentions_replace, s)
+    return literal('<code style="white-space:pre-wrap">%s</code>' % s)
 
-    :param source:
-    """
-    return literal('<div class="rst-block">%s</div>' %
-                   MarkupRenderer.rst_with_mentions(source))
 
 def short_ref(ref_type, ref_name):
     if ref_type == 'rev':
--- a/kallithea/lib/indexers/daemon.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/indexers/daemon.py	Mon Mar 14 00:36:08 2016 +0100
@@ -41,7 +41,7 @@
 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
 sys.path.append(project_path)
 
-from kallithea.config.conf import INDEX_EXTENSIONS
+from kallithea.config.conf import INDEX_EXTENSIONS, INDEX_FILENAMES
 from kallithea.model.scm import ScmModel
 from kallithea.model.db import Repository
 from kallithea.lib.utils2 import safe_unicode, safe_str
@@ -162,6 +162,13 @@
         node = cs.get_node(node_path)
         return node
 
+    def is_indexable_node(self, node):
+        """
+        Just index the content of chosen files, skipping binary files
+        """
+        return (node.extension in INDEX_EXTENSIONS or node.name in INDEX_FILENAMES) and \
+               not node.is_binary
+
     def get_node_mtime(self, node):
         return mktime(node.last_changeset.date.timetuple())
 
@@ -177,8 +184,7 @@
             return 0, 0
 
         indexed = indexed_w_content = 0
-        # we just index the content of chosen files, and skip binary files
-        if node.extension in INDEX_EXTENSIONS and not node.is_binary:
+        if self.is_indexable_node(node):
             u_content = node.content
             if not isinstance(u_content, unicode):
                 log.warning('  >> %s Could not get this content as unicode '
--- a/kallithea/lib/middleware/simplegit.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/middleware/simplegit.py	Mon Mar 14 00:36:08 2016 +0100
@@ -38,7 +38,7 @@
     HTTPNotAcceptable
 from kallithea.model.db import User, Ui
 
-from kallithea.lib.utils2 import safe_str, safe_unicode, fix_PATH, get_server_url,\
+from kallithea.lib.utils2 import safe_str, safe_unicode, fix_PATH, get_server_url, \
     _set_extras
 from kallithea.lib.base import BaseVCSController, WSGIResultCloseCallback
 from kallithea.lib.utils import make_ui, is_valid_repo
@@ -99,7 +99,7 @@
         # CHECK ANONYMOUS PERMISSION
         #======================================================================
         if action in ['pull', 'push']:
-            anonymous_user = self.__get_user('default')
+            anonymous_user = User.get_default_user(cache=True)
             username = anonymous_user.username
             if anonymous_user.active:
                 # ONLY check permissions if the user is activated
@@ -146,7 +146,7 @@
                 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
                 #==============================================================
                 try:
-                    user = self.__get_user(username)
+                    user = User.get_by_username_or_email(username)
                     if user is None or not user.active:
                         return HTTPForbidden()(environ, start_response)
                     username = user.username
@@ -248,9 +248,6 @@
 
         return repo_name
 
-    def __get_user(self, username):
-        return User.get_by_username(username)
-
     def __get_action(self, environ):
         """
         Maps git request commands into a pull or push command.
@@ -295,12 +292,11 @@
         if action == 'pull' and _hooks.get(Ui.HOOK_PULL):
             log_pull_action(ui=baseui, repo=_repo._repo)
 
-    def __inject_extras(self, repo_path, baseui, extras={}):
+    def __inject_extras(self, repo_path, baseui, extras=None):
         """
         Injects some extra params into baseui instance
 
         :param baseui: baseui instance
         :param extras: dict with extra params to put into baseui
         """
-
-        _set_extras(extras)
+        _set_extras(extras or {})
--- a/kallithea/lib/middleware/simplehg.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/middleware/simplehg.py	Mon Mar 14 00:36:08 2016 +0100
@@ -37,7 +37,7 @@
     HTTPNotAcceptable
 from kallithea.model.db import User
 
-from kallithea.lib.utils2 import safe_str, safe_unicode, fix_PATH, get_server_url,\
+from kallithea.lib.utils2 import safe_str, safe_unicode, fix_PATH, get_server_url, \
     _set_extras
 from kallithea.lib.base import BaseVCSController, WSGIResultCloseCallback
 from kallithea.lib.utils import make_ui, is_valid_repo, ui_sections
@@ -105,7 +105,7 @@
         # CHECK ANONYMOUS PERMISSION
         #======================================================================
         if action in ['pull', 'push']:
-            anonymous_user = self.__get_user('default')
+            anonymous_user = User.get_default_user(cache=True)
             username = anonymous_user.username
             if anonymous_user.active:
                 # ONLY check permissions if the user is activated
@@ -152,7 +152,7 @@
                 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
                 #==============================================================
                 try:
-                    user = self.__get_user(username)
+                    user = User.get_by_username_or_email(username)
                     if user is None or not user.active:
                         return HTTPForbidden()(environ, start_response)
                     username = user.username
@@ -248,9 +248,6 @@
 
         return repo_name
 
-    def __get_user(self, username):
-        return User.get_by_username(username)
-
     def __get_action(self, environ):
         """
         Maps mercurial request commands into a clone,pull or push command.
@@ -275,7 +272,7 @@
         raise Exception('Unable to detect pull/push action !!'
                         'Are you using non standard command or client ?')
 
-    def __inject_extras(self, repo_path, baseui, extras={}):
+    def __inject_extras(self, repo_path, baseui, extras=None):
         """
         Injects some extra params into baseui instance
 
@@ -294,4 +291,4 @@
             for section in ui_sections:
                 for k, v in repoui.configitems(section):
                     baseui.setconfig(section, k, v)
-        _set_extras(extras)
+        _set_extras(extras or {})
--- a/kallithea/lib/paster_commands/cache_keys.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/paster_commands/cache_keys.py	Mon Mar 14 00:36:08 2016 +0100
@@ -29,7 +29,6 @@
 
 import os
 import sys
-import logging
 
 from kallithea.model.meta import Session
 from kallithea.lib.utils import BasePasterCommand
@@ -40,8 +39,6 @@
 rc_path = dn(dn(dn(os.path.realpath(__file__))))
 sys.path.append(rc_path)
 
-log = logging.getLogger(__name__)
-
 
 class Command(BasePasterCommand):
 
@@ -57,6 +54,7 @@
     def command(self):
         #get SqlAlchemy session
         self._init_session()
+
         _caches = CacheInvalidation.query().order_by(CacheInvalidation.cache_key).all()
         if self.options.show:
             for c_obj in _caches:
@@ -64,11 +62,10 @@
         elif self.options.cleanup:
             for c_obj in _caches:
                 Session().delete(c_obj)
-                print 'removing key:%s' % (c_obj.cache_key)
+                print 'Removing key: %s' % (c_obj.cache_key)
                 Session().commit()
         else:
-            print 'nothing done exiting...'
-        sys.exit(0)
+            print 'Nothing done, exiting...'
 
     def update_parser(self):
         self.parser.add_option(
--- a/kallithea/lib/paster_commands/cleanup.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/paster_commands/cleanup.py	Mon Mar 14 00:36:08 2016 +0100
@@ -22,7 +22,7 @@
 Original author and date, and relevant copyright and licensing information is below:
 :created_on: Jul 14, 2012
 :author: marcink
-:copyright: (c) 2013 RhodeCode GmbH.
+:copyright: (c) 2013 RhodeCode GmbH, and others.
 :license: GPLv3, see LICENSE.md for more details.
 """
 
@@ -31,7 +31,6 @@
 import sys
 import re
 import shutil
-import logging
 import datetime
 
 from kallithea.lib.utils import BasePasterCommand, ask_ok, REMOVED_REPO_PAT
@@ -43,8 +42,6 @@
 rc_path = dn(dn(dn(os.path.realpath(__file__))))
 sys.path.append(rc_path)
 
-log = logging.getLogger(__name__)
-
 
 class Command(BasePasterCommand):
 
@@ -110,27 +107,25 @@
                     to_remove_filtered.append([name, date_])
 
             to_remove = to_remove_filtered
-            print >> sys.stdout, 'removing %s deleted repos older than %s (%s)' \
+            print 'Removing %s deleted repos older than %s (%s)' \
                 % (len(to_remove), older_than, older_than_date)
         else:
-            print >> sys.stdout, 'removing all [%s] deleted repos' \
-                % len(to_remove)
+            print 'Removing all %s deleted repos' % len(to_remove)
         if self.options.dont_ask or not to_remove:
             # don't ask just remove !
             remove = True
         else:
             remove = ask_ok('the following repositories will be deleted completely:\n%s\n'
                             'are you sure you want to remove them [y/n]?'
-                            % ', \n'.join(['%s removed on %s'
-                    % (safe_str(x[0]), safe_str(x[1])) for x in to_remove]))
+                            % '\n'.join(['%s removed on %s' % (safe_str(x[0]), safe_str(x[1]))
+                                         for x in to_remove]))
 
         if remove:
             for path, date_ in to_remove:
-                print >> sys.stdout, 'removing repository %s' % path
+                print 'Removing repository %s' % path
                 shutil.rmtree(path)
         else:
-            print 'nothing done exiting...'
-            sys.exit(0)
+            print 'Nothing done, exiting...'
 
     def update_parser(self):
         self.parser.add_option(
--- a/kallithea/lib/paster_commands/install_iis.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/paster_commands/install_iis.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,3 +1,24 @@
+# -*- coding: utf-8 -*-
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+kallithea.lib.paster_commands.install_iis
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+IIS installation tools for Kallithea
+"""
+
+
 import os
 import sys
 from paste.script.appinstall import AbstractInstallCommand
@@ -37,7 +58,9 @@
         except ImportError:
             raise BadCommand('missing requirement: isapi-wsgi not installed')
 
-        file = '''import sys
+        file = '''\
+# Created by Kallithea install_iis
+import sys
 
 if hasattr(sys, "isapidllhandle"):
     import win32traceutil
@@ -78,8 +101,8 @@
 
         dispatchfile = os.path.join(os.getcwd(), 'dispatch.py')
         self.ensure_file(dispatchfile, outdata, False)
-        print 'generating', dispatchfile
+        print 'Generating %s' % (dispatchfile,)
 
-        print ('run \'python "%s" install\' with administrative privileges '
+        print ('Run \'python "%s" install\' with administrative privileges '
             'to generate the _dispatch.dll file and install it into the '
             'default web site') % (dispatchfile,)
--- a/kallithea/lib/paster_commands/ishell.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/paster_commands/ishell.py	Mon Mar 14 00:36:08 2016 +0100
@@ -28,7 +28,6 @@
 
 import os
 import sys
-import logging
 
 from kallithea.lib.utils import BasePasterCommand
 
@@ -37,8 +36,6 @@
 rc_path = dn(dn(dn(os.path.realpath(__file__))))
 sys.path.append(rc_path)
 
-log = logging.getLogger(__name__)
-
 
 class Command(BasePasterCommand):
 
@@ -55,7 +52,7 @@
         #get SqlAlchemy session
         self._init_session()
 
-        # imports, used in ipython shell
+        # imports, used in IPython shell
         import os
         import sys
         import time
@@ -70,7 +67,7 @@
             cfg.InteractiveShellEmbed.confirm_exit = False
             embed(config=cfg, banner1="Kallithea IShell.")
         except ImportError:
-            print 'ipython installation required for ishell'
+            print 'IPython installation is required for ishell'
             sys.exit(-1)
 
     def update_parser(self):
--- a/kallithea/lib/paster_commands/make_index.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/paster_commands/make_index.py	Mon Mar 14 00:36:08 2016 +0100
@@ -23,13 +23,11 @@
 :author: marcink
 :copyright: (c) 2013 RhodeCode GmbH, and others.
 :license: GPLv3, see LICENSE.md for more details.
-
 """
 
 
 import os
 import sys
-import logging
 
 from string import strip
 from kallithea.model.repo import RepoModel
@@ -53,7 +51,6 @@
     summary = "Creates or updates full text search index"
 
     def command(self):
-        logging.config.fileConfig(self.path_to_ini_file)
         #get SqlAlchemy session
         self._init_session()
         from pylons import config
@@ -79,7 +76,7 @@
             WhooshIndexingDaemon(index_location=index_location,
                                  repo_location=repo_location,
                                  repo_list=repo_list,
-                                 repo_update_list=repo_update_list)\
+                                 repo_update_list=repo_update_list) \
                 .run(full_index=self.options.full_index)
             l.release()
         except LockHeld:
--- a/kallithea/lib/paster_commands/make_rcextensions.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/paster_commands/make_rcextensions.py	Mon Mar 14 00:36:08 2016 +0100
@@ -23,7 +23,6 @@
 :author: marcink
 :copyright: (c) 2013 RhodeCode GmbH, and others.
 :license: GPLv3, see LICENSE.md for more details.
-
 """
 
 
@@ -67,7 +66,7 @@
             msg = ('Extension file already exists, do you want '
                    'to overwrite it ? [y/n]')
             if not ask_ok(msg):
-                print 'Nothing done...'
+                print 'Nothing done, exiting...'
                 return
 
         dirname = os.path.dirname(ext_file)
--- a/kallithea/lib/paster_commands/repo_scan.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/paster_commands/repo_scan.py	Mon Mar 14 00:36:08 2016 +0100
@@ -28,7 +28,6 @@
 
 import os
 import sys
-import logging
 
 from kallithea.model.scm import ScmModel
 from kallithea.lib.utils import BasePasterCommand, repo2db_mapper
@@ -38,8 +37,6 @@
 rc_path = dn(dn(dn(os.path.realpath(__file__))))
 sys.path.append(rc_path)
 
-log = logging.getLogger(__name__)
-
 
 class Command(BasePasterCommand):
 
--- a/kallithea/lib/paster_commands/setup_db.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/paster_commands/setup_db.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,3 +1,24 @@
+# -*- coding: utf-8 -*-
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+kallithea.lib.paster_commands.setup_db
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Databaset setup paster command for Kallithea
+"""
+
+
 import os
 import sys
 from paste.script.appinstall import AbstractInstallCommand
@@ -20,11 +41,10 @@
     group_name = "Kallithea"
 
     description = """\
-
-    Setup Kallithea according to its configuration file.  This is
-    the second part of a two-phase web application installation
-    process (the first phase is prepare-app).  The setup process
-    consist of things like setting up databases, creating super user
+        Setup Kallithea according to its configuration file.  This is
+        the second part of a two-phase web application installation
+        process (the first phase is prepare-app).  The setup process
+        consist of things like setting up databases, creating super user
     """
 
     parser = AbstractInstallCommand.standard_parser(
@@ -74,6 +94,7 @@
                        dest='public_access',
                        default=None,
                        help='Disable public access on this installation ')
+
     def command(self):
         config_spec = self.args[0]
         section = self.options.section_name
@@ -100,7 +121,7 @@
         dist = conf.context.distribution
         if dist is None:
             raise BadCommand(
-                "The section %r is not the application (probably a filter).  "
+                "The section %r is not the application (probably a filter). "
                 "You should add #section_name, where section_name is the "
                 "section that configures your application" % plain_section)
         installer = self.get_installer(dist, ep_group, ep_name)
--- a/kallithea/lib/paster_commands/update_repoinfo.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/paster_commands/update_repoinfo.py	Mon Mar 14 00:36:08 2016 +0100
@@ -28,7 +28,6 @@
 
 import os
 import sys
-import logging
 import string
 
 from kallithea.lib.utils import BasePasterCommand
@@ -41,8 +40,6 @@
 rc_path = dn(dn(dn(os.path.realpath(__file__))))
 sys.path.append(rc_path)
 
-log = logging.getLogger(__name__)
-
 
 class Command(BasePasterCommand):
 
@@ -64,7 +61,7 @@
                                if self.options.repo_update_list else None
 
         if repo_update_list is not None:
-            repo_list = list(Repository.query()\
+            repo_list = list(Repository.query() \
                 .filter(Repository.repo_name.in_(repo_update_list)))
         else:
             repo_list = Repository.getAll()
--- a/kallithea/lib/rcmail/smtp_mailer.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/rcmail/smtp_mailer.py	Mon Mar 14 00:36:08 2016 +0100
@@ -60,9 +60,9 @@
         self.debug = debug
         self.auth = smtp_auth
 
-    def send(self, recipients=[], subject='', body='', html='',
+    def send(self, recipients=None, subject='', body='', html='',
              attachment_files=None, headers=None):
-
+        recipients = recipients or []
         if isinstance(recipients, basestring):
             recipients = [recipients]
         if headers is None:
--- a/kallithea/lib/utils.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/utils.py	Mon Mar 14 00:36:08 2016 +0100
@@ -186,7 +186,7 @@
         repo_obj = Repository.get_by_repo_name(repo_name)
     else:
         repo_obj = None
-        repo_name = ''
+        repo_name = u''
 
     user_log = UserLog()
     user_log.user_id = user_obj.user_id
@@ -418,6 +418,21 @@
                                                         'utf8'), sep=',')
 
 
+def set_indexer_config(config):
+    """
+    Update Whoosh index mapping
+
+    :param config: kallithea.CONFIG
+    """
+    from kallithea.config import conf
+
+    log.debug('adding extra into INDEX_EXTENSIONS')
+    conf.INDEX_EXTENSIONS.extend(re.split('\s+', config.get('index.extensions', '')))
+
+    log.debug('adding extra into INDEX_FILENAMES')
+    conf.INDEX_FILENAMES.extend(re.split('\s+', config.get('index.filenames', '')))
+
+
 def map_groups(path):
     """
     Given a full path to a repository, create all nested groups that this
@@ -436,7 +451,7 @@
     rgm = RepoGroupModel(sa)
     owner = User.get_first_admin()
     for lvl, group_name in enumerate(groups):
-        group_name = '/'.join(groups[:lvl] + [group_name])
+        group_name = u'/'.join(groups[:lvl] + [group_name])
         group = RepoGroup.get_by_group_name(group_name)
         desc = '%s group' % group_name
 
@@ -528,8 +543,9 @@
 
     removed = []
     # remove from database those repositories that are not in the filesystem
+    unicode_initial_repo_list = set(safe_unicode(name) for name in initial_repo_list)
     for repo in sa.query(Repository).all():
-        if repo.repo_name not in initial_repo_list.keys():
+        if repo.repo_name not in unicode_initial_repo_list:
             if remove_obsolete:
                 log.debug("Removing non-existing repository found in db `%s`",
                           repo.repo_name)
@@ -639,7 +655,7 @@
     try:
         l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
         WhooshIndexingDaemon(index_location=index_location,
-                             repo_location=repo_location)\
+                             repo_location=repo_location) \
             .run(full_index=full_index)
         l.release()
     except LockHeld:
@@ -722,17 +738,6 @@
     takes_config_file = 1
     requires_config_file = True
 
-    def notify_msg(self, msg, log=False):
-        """Make a notification to user, additionally if logger is passed
-        it logs this action using given logger
-
-        :param msg: message that will be printed to user
-        :param log: logging instance, to use to additionally log this message
-
-        """
-        if log and isinstance(log, logging):
-            log(msg)
-
     def run(self, args):
         """
         Overrides Command.run
@@ -775,11 +780,10 @@
         Inits SqlAlchemy Session
         """
         logging.config.fileConfig(self.path_to_ini_file)
+
         from pylons import config
         from kallithea.model import init_model
         from kallithea.lib.utils2 import engine_from_config
-
-        #get to remove repos !!
         add_cache(config)
         engine = engine_from_config(config, 'sqlalchemy.db1.')
         init_model(engine)
@@ -853,7 +857,7 @@
             return data
 
         # denpending from condition the compute is wrapped in cache or not
-        compute = conditional_cache('short_term', 'cache_desc', codnition=True, func=func)
+        compute = conditional_cache('short_term', 'cache_desc', condition=True, func=func)
         return compute(arg)
 
     :param region: name of cache region
--- a/kallithea/lib/utils2.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/utils2.py	Mon Mar 14 00:36:08 2016 +0100
@@ -78,6 +78,25 @@
     return dict(d)
 
 
+def __get_index_filenames():
+    """
+    Get list of known indexable filenames from pygment lexer internals
+    """
+    from pygments import lexers
+    from itertools import ifilter
+
+    filenames = []
+
+    def likely_filename(s):
+        return s.find('*') == -1 and s.find('[') == -1
+
+    for lx, t in sorted(lexers.LEXERS.items()):
+        for f in ifilter(likely_filename, t[-2]):
+            filenames.append(f)
+
+    return filenames
+
+
 def str2bool(_str):
     """
     returs True/False value from given string, it tries to translate the
--- a/kallithea/lib/vcs/backends/git/inmemory.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/vcs/backends/git/inmemory.py	Mon Mar 14 00:36:08 2016 +0100
@@ -40,7 +40,7 @@
         ENCODING = "UTF-8"
 
         # Create tree and populates it with blobs
-        commit_tree = self.parents[0] and repo[self.parents[0]._commit.tree] or\
+        commit_tree = self.parents[0] and repo[self.parents[0]._commit.tree] or \
             objects.Tree()
         for node in self.added + self.changed:
             # Compute subdirs if needed
--- a/kallithea/lib/vcs/backends/hg/inmemory.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/vcs/backends/hg/inmemory.py	Mon Mar 14 00:36:08 2016 +0100
@@ -83,7 +83,7 @@
                 parents[i] = parent._ctx.node()
 
         if date and isinstance(date, datetime.datetime):
-            date = date.ctime()
+            date = date.strftime('%a, %d %b %Y %H:%M:%S')
 
         commit_ctx = memctx(repo=self.repository._repo,
             parents=parents,
--- a/kallithea/lib/vcs/backends/hg/repository.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/vcs/backends/hg/repository.py	Mon Mar 14 00:36:08 2016 +0100
@@ -172,7 +172,7 @@
                 changeset.short_id)
 
         if date is None:
-            date = datetime.datetime.now().ctime()
+            date = datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S')
 
         try:
             self._repo.tag(name, changeset._ctx.node(), message, local, user,
@@ -202,7 +202,7 @@
         if message is None:
             message = "Removed tag %s" % name
         if date is None:
-            date = datetime.datetime.now().ctime()
+            date = datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S')
         local = False
 
         try:
@@ -361,10 +361,10 @@
             return localrepository(self.baseui, self.path, create=create)
         except (Abort, RepoError) as err:
             if create:
-                msg = "Cannot create repository at %s. Original error was %s"\
+                msg = "Cannot create repository at %s. Original error was %s" \
                     % (self.path, err)
             else:
-                msg = "Not valid repository at %s. Original error was %s"\
+                msg = "Not valid repository at %s. Original error was %s" \
                     % (self.path, err)
             raise RepositoryError(msg)
 
--- a/kallithea/lib/vcs/subprocessio.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/vcs/subprocessio.py	Mon Mar 14 00:36:08 2016 +0100
@@ -156,8 +156,8 @@
     """
 
     def __init__(self, source, buffer_size=65536, chunk_size=4096,
-                 starting_values=[], bottomless=False):
-
+                 starting_values=None, bottomless=False):
+        starting_values = starting_values or []
         if bottomless:
             maxlen = int(buffer_size / chunk_size)
         else:
@@ -326,7 +326,7 @@
     """
 
     def __init__(self, cmd, inputstream=None, buffer_size=65536,
-                 chunk_size=4096, starting_values=[], **kwargs):
+                 chunk_size=4096, starting_values=None, **kwargs):
         """
         Initializes SubprocessIOChunker
 
@@ -336,7 +336,7 @@
         :param chunk_size: (Default: 4096) A max size of a chunk. Actual chunk may be smaller.
         :param starting_values: (Default: []) An array of strings to put in front of output que.
         """
-
+        starting_values = starting_values or []
         if inputstream:
             input_streamer = StreamFeeder(inputstream)
             input_streamer.start()
--- a/kallithea/lib/vcs/utils/__init__.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/vcs/utils/__init__.py	Mon Mar 14 00:36:08 2016 +0100
@@ -5,6 +5,7 @@
 
 import time
 import datetime
+import re
 
 
 def makedate():
@@ -150,30 +151,33 @@
         return unicode_.encode(to_encoding[0], 'replace')
 
 
+# Regex taken from http://www.regular-expressions.info/email.html
+email_re = re.compile(
+    r"""[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@"""
+    r"""(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?""",
+    re.IGNORECASE)
+
 def author_email(author):
     """
-    returns email address of given author.
-    If any of <,> sign are found, it fallbacks to regex findall()
-    and returns first found result or empty string
+    Returns email address of given author string.
+    If author contains <> brackets, only look inside that.
+    If any RFC valid email address is found, return that.
+    Else, return empty string.
 
-    Regex taken from http://www.regular-expressions.info/email.html
     """
     if not author:
         return ''
-    import re
-    r = author.find('>')
-    l = author.find('<')
 
-    if l == -1 or r == -1:
-        # fallback to regex match of email out of a string
-        email_re = re.compile(r"""[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!"""
-                              r"""#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z"""
-                              r"""0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]"""
-                              r"""*[a-z0-9])?""", re.IGNORECASE)
-        m = re.findall(email_re, author)
-        return m[0] if m else ''
+    l = author.find('<') + 1
+    if l != 0:
+        r = author.find('>', l)
+        if r != -1:
+            author = author[l:r]
 
-    return author[l + 1:r].strip()
+    m = email_re.search(author)
+    if m is None:
+        return ''
+    return safe_str(m.group(0))
 
 
 def author_name(author):
@@ -186,5 +190,5 @@
         return ''
     if not '@' in author:
         return author
-    return author.replace(author_email(author), '').replace('<', '')\
+    return author.replace(author_email(author), '').replace('<', '') \
         .replace('>', '').strip()
--- a/kallithea/lib/vcs/utils/annotate.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/vcs/utils/annotate.py	Mon Mar 14 00:36:08 2016 +0100
@@ -41,7 +41,7 @@
         following function as ``annotate_from_changeset_func``::
 
             def changeset_to_anchor(changeset):
-                return '<a href="/changesets/%s/">%s</a>\n' %\
+                return '<a href="/changesets/%s/">%s</a>\n' % \
                        (changeset.id, changeset.id)
 
         :param annotate_from_changeset_func: see above
--- a/kallithea/lib/vcs/utils/compat.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/vcs/utils/compat.py	Mon Mar 14 00:36:08 2016 +0100
@@ -120,7 +120,8 @@
                 return cmp(type(self), type(other))
             return cmp(list(self), list(other))
 
-        def __repr__(self, _track=[]):
+        def __repr__(self, _track=None):
+            _track = _track or []
             if id(self) in _track:
                 return '...'
             _track.append(id(self))
@@ -140,8 +141,9 @@
         def __copy__(self):
             return self.__class__(self)
 
-        def __deepcopy__(self, memo={}):
+        def __deepcopy__(self, memo=None):
             from copy import deepcopy
+            memo = memo or {}
             result = self.__class__()
             memo[id(self)] = result
             result.__init__(deepcopy(tuple(self), memo))
--- a/kallithea/lib/vcs/utils/diffs.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/vcs/utils/diffs.py	Mon Mar 14 00:36:08 2016 +0100
@@ -389,15 +389,15 @@
         diff_lines = self.prepare()
         _html_empty = True
         _html = []
-        _html.append('''<table class="%(table_class)s">\n''' \
-                                            % {'table_class': table_class})
+        _html.append('''<table class="%(table_class)s">\n'''
+                     % {'table_class': table_class})
         for diff in diff_lines:
             for line in diff['chunks']:
                 _html_empty = False
                 for change in line:
-                    _html.append('''<tr class="%(line_class)s %(action)s">\n''' \
-                        % {'line_class': line_class,
-                           'action': change['action']})
+                    _html.append('''<tr class="%(line_class)s %(action)s">\n'''
+                                 % {'line_class': line_class,
+                                    'action': change['action']})
                     anchor_old_id = ''
                     anchor_new_id = ''
                     anchor_old = "%(filename)s_o%(oldline_no)s" % \
@@ -417,35 +417,32 @@
                     ###########################################################
                     # OLD LINE NUMBER
                     ###########################################################
-                    _html.append('''\t<td %(a_id)s class="%(old_lineno_cls)s">''' \
-                                    % {'a_id': anchor_old_id,
-                                       'old_lineno_cls': old_lineno_class})
+                    _html.append('''\t<td %(a_id)s class="%(old_lineno_cls)s">'''
+                                  % {'a_id': anchor_old_id,
+                                     'old_lineno_cls': old_lineno_class})
 
-                    _html.append('''<pre>%(link)s</pre>''' \
-                        % {'link':
-                        _link_to_if(cond_old, change['old_lineno'], '#%s' \
-                                                                % anchor_old)})
+                    _html.append('''<pre>%(link)s</pre>'''
+                                 % {'link':
+                                    _link_to_if(cond_old, change['old_lineno'], '#%s' % anchor_old)})
                     _html.append('''</td>\n''')
                     ###########################################################
                     # NEW LINE NUMBER
                     ###########################################################
 
-                    _html.append('''\t<td %(a_id)s class="%(new_lineno_cls)s">''' \
-                                    % {'a_id': anchor_new_id,
-                                       'new_lineno_cls': new_lineno_class})
+                    _html.append('''\t<td %(a_id)s class="%(new_lineno_cls)s">'''
+                                  % {'a_id': anchor_new_id,
+                                     'new_lineno_cls': new_lineno_class})
 
-                    _html.append('''<pre>%(link)s</pre>''' \
-                        % {'link':
-                        _link_to_if(cond_new, change['new_lineno'], '#%s' \
-                                                                % anchor_new)})
+                    _html.append('''<pre>%(link)s</pre>'''
+                                 % {'link': _link_to_if(cond_new, change['new_lineno'], '#%s' % anchor_new)})
                     _html.append('''</td>\n''')
                     ###########################################################
                     # CODE
                     ###########################################################
-                    _html.append('''\t<td class="%(code_class)s">''' \
-                                                % {'code_class': code_class})
-                    _html.append('''\n\t\t<pre>%(code)s</pre>\n''' \
-                                                % {'code': change['line']})
+                    _html.append('''\t<td class="%(code_class)s">'''
+                                 % {'code_class': code_class})
+                    _html.append('''\n\t\t<pre>%(code)s</pre>\n'''
+                                 % {'code': change['line']})
                     _html.append('''\t</td>''')
                     _html.append('''\n</tr>\n''')
         _html.append('''</table>''')
--- a/kallithea/lib/vcs/utils/imports.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/lib/vcs/utils/imports.py	Mon Mar 14 00:36:08 2016 +0100
@@ -19,7 +19,7 @@
     try:
         class_mod = __import__(mod_path, {}, {}, [class_name])
     except ImportError as err:
-        msg = "There was problem while trying to import backend class. "\
+        msg = "There was problem while trying to import backend class. " \
             "Original error was:\n%s" % err
         raise VCSError(msg)
     cls = getattr(class_mod, class_name)
--- a/kallithea/model/api_key.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/model/api_key.py	Mon Mar 14 00:36:08 2016 +0100
@@ -73,10 +73,10 @@
 
     def get_api_keys(self, user, show_expired=True):
         user = self._get_user(user)
-        user_api_keys = UserApiKeys.query()\
+        user_api_keys = UserApiKeys.query() \
             .filter(UserApiKeys.user_id == user.user_id)
         if not show_expired:
-            user_api_keys = user_api_keys\
+            user_api_keys = user_api_keys \
                 .filter(or_(UserApiKeys.expires == -1,
                             UserApiKeys.expires >= time.time()))
         return user_api_keys
--- a/kallithea/model/changeset_status.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/model/changeset_status.py	Mon Mar 14 00:36:08 2016 +0100
@@ -49,7 +49,7 @@
                           with_revisions=False):
         repo = self._get_repo(repo)
 
-        q = ChangesetStatus.query()\
+        q = ChangesetStatus.query() \
             .filter(ChangesetStatus.repo == repo)
         if not with_revisions:
             q = q.filter(ChangesetStatus.version == 0)
--- a/kallithea/model/comment.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/model/comment.py	Mon Mar 14 00:36:08 2016 +0100
@@ -210,7 +210,7 @@
                 email_kwargs=email_kwargs,
             )
 
-            mention_recipients = set(self._extract_mentions(body))\
+            mention_recipients = set(self._extract_mentions(body)) \
                                     .difference(recipients)
             if mention_recipients:
                 email_kwargs['is_mention'] = True
@@ -260,16 +260,16 @@
         q = Session().query(ChangesetComment)
 
         if inline:
-            q = q.filter(ChangesetComment.line_no != None)\
+            q = q.filter(ChangesetComment.line_no != None) \
                 .filter(ChangesetComment.f_path != None)
         else:
-            q = q.filter(ChangesetComment.line_no == None)\
+            q = q.filter(ChangesetComment.line_no == None) \
                 .filter(ChangesetComment.f_path == None)
 
-        if revision:
-            q = q.filter(ChangesetComment.revision == revision)\
+        if revision is not None:
+            q = q.filter(ChangesetComment.revision == revision) \
                 .filter(ChangesetComment.repo_id == repo_id)
-        elif pull_request:
+        elif pull_request is not None:
             pull_request = self.__get_pull_request(pull_request)
             q = q.filter(ChangesetComment.pull_request == pull_request)
         else:
--- a/kallithea/model/db.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/model/db.py	Mon Mar 14 00:36:08 2016 +0100
@@ -158,13 +158,16 @@
         return '<DB:%s>' % (self.__class__.__name__)
 
 
+_table_args_default_dict = {'extend_existing': True,
+                            'mysql_engine': 'InnoDB',
+                            'mysql_charset': 'utf8',
+                            'sqlite_autoincrement': True,
+                           }
+
 class Setting(Base, BaseModel):
     __tablename__ = DB_PREFIX + 'settings'
-
     __table_args__ = (
-        UniqueConstraint('app_settings_name'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
 
     SETTINGS_TYPES = {
@@ -176,10 +179,10 @@
     }
     DEFAULT_UPDATE_URL = ''
 
-    app_settings_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    app_settings_name = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    _app_settings_value = Column("app_settings_value", String(4096, convert_unicode=False), nullable=True, unique=None, default=None)
-    _app_settings_type = Column("app_settings_type", String(255, convert_unicode=False), nullable=True, unique=None, default=None)
+    app_settings_id = Column(Integer(), unique=True, primary_key=True)
+    app_settings_name = Column(String(255), nullable=False, unique=True)
+    _app_settings_value = Column("app_settings_value", Unicode(4096), nullable=False)
+    _app_settings_type = Column("app_settings_type", String(255), nullable=True) # FIXME: not nullable?
 
     def __init__(self, key='', val='', type='unicode'):
         self.app_settings_name = key
@@ -226,7 +229,7 @@
 
     @classmethod
     def get_by_name(cls, key):
-        return cls.query()\
+        return cls.query() \
             .filter(cls.app_settings_name == key).scalar()
 
     @classmethod
@@ -286,7 +289,7 @@
 
     @classmethod
     def get_auth_settings(cls, cache=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('auth_')).all()
         fd = {}
         for row in ret:
@@ -295,7 +298,7 @@
 
     @classmethod
     def get_default_repo_settings(cls, cache=False, strip_prefix=False):
-        ret = cls.query()\
+        ret = cls.query() \
                 .filter(cls.app_settings_name.startswith('default_')).all()
         fd = {}
         for row in ret:
@@ -327,9 +330,11 @@
 class Ui(Base, BaseModel):
     __tablename__ = DB_PREFIX + 'ui'
     __table_args__ = (
+        # FIXME: ui_key as key is wrong and should be removed when the corresponding
+        # Ui.get_by_key has been replaced by the composite key
         UniqueConstraint('ui_key'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        UniqueConstraint('ui_section', 'ui_key'),
+        _table_args_default_dict,
     )
 
     HOOK_UPDATE = 'changegroup.update'
@@ -339,20 +344,25 @@
     HOOK_PULL = 'outgoing.pull_logger'
     HOOK_PRE_PULL = 'preoutgoing.pre_pull'
 
-    ui_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    ui_section = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    ui_key = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    ui_value = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    ui_active = Column(Boolean(), nullable=True, unique=None, default=True)
-
-    # def __init__(self, section='', key='', value=''):
-    #     self.ui_section = section
-    #     self.ui_key = key
-    #     self.ui_value = value
+    ui_id = Column(Integer(), unique=True, primary_key=True)
+    ui_section = Column(String(255), nullable=False)
+    ui_key = Column(String(255), nullable=False)
+    ui_value = Column(String(255), nullable=True) # FIXME: not nullable?
+    ui_active = Column(Boolean(), nullable=False, default=True)
 
     @classmethod
-    def get_by_key(cls, key):
-        return cls.query().filter(cls.ui_key == key).scalar()
+    def get_by_key(cls, section, key):
+        """ Return specified Ui object, or None if not found. """
+        return cls.query().filter_by(ui_section=section, ui_key=key).scalar()
+
+    @classmethod
+    def get_or_create(cls, section, key):
+        """ Return specified Ui object, creating it if necessary. """
+        setting = cls.get_by_key(section, key)
+        if setting is None:
+            setting = cls(ui_section=section, ui_key=key)
+            Session().add(setting)
+        return setting
 
     @classmethod
     def get_builtin_hooks(cls):
@@ -360,6 +370,7 @@
         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()
 
     @classmethod
@@ -373,18 +384,14 @@
 
     @classmethod
     def get_repos_location(cls):
-        return cls.get_by_key('/').ui_value
+        return cls.get_by_key('paths', '/').ui_value
 
     @classmethod
     def create_or_update_hook(cls, key, val):
-        new_ui = cls.get_by_key(key) or cls()
-        new_ui.ui_section = 'hooks'
+        new_ui = cls.get_or_create('hooks', key)
         new_ui.ui_active = True
-        new_ui.ui_key = key
         new_ui.ui_value = val
 
-        Session().add(new_ui)
-
     def __repr__(self):
         return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.ui_section,
                                     self.ui_key, self.ui_value)
@@ -393,30 +400,29 @@
 class User(Base, BaseModel):
     __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', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
+
     DEFAULT_USER = 'default'
     DEFAULT_GRAVATAR_URL = 'https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}'
 
-    user_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    username = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    password = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    active = Column(Boolean(), nullable=True, unique=None, default=True)
-    admin = Column(Boolean(), nullable=True, unique=None, default=False)
-    name = Column("firstname", String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    lastname = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    _email = Column("email", String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    last_login = Column(DateTime(timezone=False), nullable=True, unique=None, default=None)
-    extern_type = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    extern_name = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    api_key = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    inherit_default_permissions = Column(Boolean(), nullable=False, unique=None, default=True)
+    user_id = Column(Integer(), unique=True, primary_key=True)
+    username = Column(String(255), nullable=False, unique=True)
+    password = Column(String(255), nullable=False)
+    active = Column(Boolean(), nullable=False, default=True)
+    admin = Column(Boolean(), nullable=False, default=False)
+    name = Column("firstname", Unicode(255), nullable=False)
+    lastname = Column(Unicode(255), nullable=False)
+    _email = Column("email", String(255), nullable=True, unique=True) # FIXME: not nullable?
+    last_login = Column(DateTime(timezone=False), nullable=True)
+    extern_type = Column(String(255), nullable=True) # FIXME: not nullable?
+    extern_name = Column(String(255), nullable=True) # FIXME: not nullable?
+    api_key = Column(String(255), nullable=False)
+    inherit_default_permissions = Column(Boolean(), nullable=False, default=True)
     created_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
-    _user_data = Column("user_data", LargeBinary(), nullable=True)  # JSON data
+    _user_data = Column("user_data", LargeBinary(), nullable=True)  # JSON data # FIXME: not nullable?
 
     user_log = relationship('UserLog')
     user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
@@ -547,9 +553,23 @@
         return user
 
     @classmethod
+    def get_by_username_or_email(cls, username_or_email, case_insensitive=False, cache=False):
+        """
+        For anything that looks like an email address, look up by the email address (matching
+        case insensitively).
+        For anything else, try to look up by the user name.
+
+        This assumes no normal username can have '@' symbol.
+        """
+        if '@' in username_or_email:
+            return User.get_by_email(username_or_email, cache=cache)
+        else:
+            return User.get_by_username(username_or_email, case_insensitive=case_insensitive, cache=cache)
+
+    @classmethod
     def get_by_username(cls, username, case_insensitive=False, cache=False):
         if case_insensitive:
-            q = cls.query().filter(cls.username.ilike(username))
+            q = cls.query().filter(func.lower(cls.username) == func.lower(username))
         else:
             q = cls.query().filter(cls.username == username)
 
@@ -575,21 +595,18 @@
 
         if fallback and not res:
             #fallback to additional keys
-            _res = UserApiKeys.query()\
-                .filter(UserApiKeys.api_key == api_key)\
+            _res = UserApiKeys.query() \
+                .filter(UserApiKeys.api_key == api_key) \
                 .filter(or_(UserApiKeys.expires == -1,
-                            UserApiKeys.expires >= time.time()))\
+                            UserApiKeys.expires >= time.time())) \
                 .first()
             if _res:
                 res = _res.user
         return res
 
     @classmethod
-    def get_by_email(cls, email, case_insensitive=False, cache=False):
-        if case_insensitive:
-            q = cls.query().filter(cls.email.ilike(email))
-        else:
-            q = cls.query().filter(cls.email == email)
+    def get_by_email(cls, email, cache=False):
+        q = cls.query().filter(func.lower(cls.email) == func.lower(email))
 
         if cache:
             q = q.options(FromCache("sql_cache_short",
@@ -599,10 +616,7 @@
         if ret is None:
             q = UserEmailMap.query()
             # try fetching in alternate email map
-            if case_insensitive:
-                q = q.filter(UserEmailMap.email.ilike(email))
-            else:
-                q = q.filter(UserEmailMap.email == email)
+            q = q.filter(func.lower(UserEmailMap.email) == func.lower(email))
             q = q.options(joinedload(UserEmailMap.user))
             if cache:
                 q = q.options(FromCache("sql_cache_short",
@@ -622,7 +636,7 @@
         # Valid email in the attribute passed, see if they're in the system
         _email = email(author)
         if _email:
-            user = cls.get_by_email(_email, case_insensitive=True)
+            user = cls.get_by_email(_email)
             if user is not None:
                 return user
         # Maybe we can match by username?
@@ -693,16 +707,14 @@
     __table_args__ = (
         Index('uak_api_key_idx', 'api_key'),
         Index('uak_api_key_expires_idx', 'api_key', 'expires'),
-        UniqueConstraint('api_key'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
     __mapper_args__ = {}
 
-    user_api_key_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
-    api_key = Column(String(255, convert_unicode=False), nullable=False, unique=True)
-    description = Column(UnicodeText(1024))
+    user_api_key_id = Column(Integer(), unique=True, primary_key=True)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
+    api_key = Column(String(255), nullable=False, unique=True)
+    description = Column(UnicodeText(1024), nullable=False)
     expires = Column(Float(53), nullable=False)
     created_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
 
@@ -719,15 +731,13 @@
     __tablename__ = 'user_email_map'
     __table_args__ = (
         Index('uem_email_idx', 'email'),
-        UniqueConstraint('email'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
     __mapper_args__ = {}
 
-    email_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
-    _email = Column("email", String(255, convert_unicode=False), nullable=True, unique=False, default=None)
+    email_id = Column(Integer(), unique=True, primary_key=True)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
+    _email = Column("email", String(255), nullable=False, unique=True)
     user = relationship('User')
 
     @validates('_email')
@@ -751,15 +761,14 @@
     __tablename__ = 'user_ip_map'
     __table_args__ = (
         UniqueConstraint('user_id', 'ip_addr'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
     __mapper_args__ = {}
 
-    ip_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
-    ip_addr = Column(String(255, convert_unicode=False), nullable=True, unique=False, default=None)
-    active = Column(Boolean(), nullable=True, unique=None, default=True)
+    ip_id = Column(Integer(), unique=True, primary_key=True)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
+    ip_addr = Column(String(255), nullable=False)
+    active = Column(Boolean(), nullable=False, default=True)
     user = relationship('User')
 
     @classmethod
@@ -781,17 +790,17 @@
 class UserLog(Base, BaseModel):
     __tablename__ = 'user_logs'
     __table_args__ = (
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
+        _table_args_default_dict,
     )
-    user_log_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
-    username = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
+
+    user_log_id = Column(Integer(), unique=True, primary_key=True)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=True)
+    username = Column(String(255), nullable=False)
     repository_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=True)
-    repository_name = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    user_ip = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    action = Column(UnicodeText(1200000, convert_unicode=False), nullable=True, unique=None, default=None)
-    action_date = Column(DateTime(timezone=False), nullable=True, unique=None, default=None)
+    repository_name = Column(Unicode(255), nullable=False)
+    user_ip = Column(String(255), nullable=True)
+    action = Column(UnicodeText(1200000), nullable=False)
+    action_date = Column(DateTime(timezone=False), nullable=False)
 
     def __unicode__(self):
         return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
@@ -809,18 +818,17 @@
 class UserGroup(Base, BaseModel):
     __tablename__ = 'users_groups'
     __table_args__ = (
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
+        _table_args_default_dict,
     )
 
-    users_group_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    users_group_name = Column(String(255, convert_unicode=False), nullable=False, unique=True, default=None)
-    user_group_description = Column(String(10000, convert_unicode=False), nullable=True, unique=None, default=None)
-    users_group_active = Column(Boolean(), nullable=True, unique=None, default=None)
-    inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
+    users_group_id = Column(Integer(), unique=True, primary_key=True)
+    users_group_name = Column(Unicode(255), nullable=False, unique=True)
+    user_group_description = Column(Unicode(10000), nullable=True) # FIXME: not nullable?
+    users_group_active = Column(Boolean(), nullable=False)
+    inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, default=True)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
     created_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
-    _group_data = Column("group_data", LargeBinary(), nullable=True)  # JSON data
+    _group_data = Column("group_data", LargeBinary(), nullable=True)  # JSON data # FIXME: not nullable?
 
     members = relationship('UserGroupMember', cascade="all, delete-orphan")
     users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
@@ -857,7 +865,7 @@
     def get_by_group_name(cls, group_name, cache=False,
                           case_insensitive=False):
         if case_insensitive:
-            q = cls.query().filter(cls.users_group_name.ilike(group_name))
+            q = cls.query().filter(func.lower(cls.users_group_name) == func.lower(group_name))
         else:
             q = cls.query().filter(cls.users_group_name == group_name)
         if cache:
@@ -899,13 +907,12 @@
 class UserGroupMember(Base, BaseModel):
     __tablename__ = 'users_groups_members'
     __table_args__ = (
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
+        _table_args_default_dict,
     )
 
-    users_group_member_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    users_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
+    users_group_member_id = Column(Integer(), unique=True, primary_key=True)
+    users_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
 
     user = relationship('User')
     users_group = relationship('UserGroup')
@@ -919,18 +926,18 @@
     __tablename__ = 'repositories_fields'
     __table_args__ = (
         UniqueConstraint('repository_id', 'field_key'),  # no-multi field
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
+        _table_args_default_dict,
     )
+
     PREFIX = 'ex_'  # prefix used in form to not conflict with already existing fields
 
-    repo_field_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    repository_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
-    field_key = Column(String(250, convert_unicode=False))
-    field_label = Column(String(1024, convert_unicode=False), nullable=False)
-    field_value = Column(String(10000, convert_unicode=False), nullable=False)
-    field_desc = Column(String(1024, convert_unicode=False), nullable=False)
-    field_type = Column(String(255), nullable=False, unique=None)
+    repo_field_id = Column(Integer(), unique=True, primary_key=True)
+    repository_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=False)
+    field_key = Column(String(250), nullable=False)
+    field_label = Column(String(1024), nullable=False)
+    field_value = Column(String(10000), nullable=False)
+    field_desc = Column(String(1024), nullable=False)
+    field_type = Column(String(255), nullable=False)
     created_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
 
     repository = relationship('Repository')
@@ -947,8 +954,8 @@
 
     @classmethod
     def get_by_key_name(cls, key, repo):
-        row = cls.query()\
-                .filter(cls.repository == repo)\
+        row = cls.query() \
+                .filter(cls.repository == repo) \
                 .filter(cls.field_key == key).scalar()
         return row
 
@@ -956,11 +963,10 @@
 class Repository(Base, BaseModel):
     __tablename__ = 'repositories'
     __table_args__ = (
-        UniqueConstraint('repo_name'),
         Index('r_repo_name_idx', 'repo_name'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
+        _table_args_default_dict,
     )
+
     DEFAULT_CLONE_URI = '{scheme}://{user}@{netloc}/{repo}'
     DEFAULT_CLONE_URI_ID = '{scheme}://{user}@{netloc}/_{repoid}'
 
@@ -968,26 +974,26 @@
     STATE_PENDING = 'repo_state_pending'
     STATE_ERROR = 'repo_state_error'
 
-    repo_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    repo_name = Column(String(255, convert_unicode=False), nullable=False, unique=True, default=None)
-    repo_state = Column(String(255), nullable=True)
-
-    clone_uri = Column(String(255, convert_unicode=False), nullable=True, unique=False, default=None)
-    repo_type = Column(String(255, convert_unicode=False), nullable=False, unique=False, default=None)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
-    private = Column(Boolean(), nullable=True, unique=None, default=None)
-    enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
-    enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
-    description = Column(String(10000, convert_unicode=False), nullable=True, unique=None, default=None)
-    created_on = Column(DateTime(timezone=False), nullable=False, unique=None, default=datetime.datetime.now)
-    updated_on = Column(DateTime(timezone=False), nullable=False, unique=None, default=datetime.datetime.now)
-    _landing_revision = Column("landing_revision", String(255, convert_unicode=False), nullable=False, unique=False, default=None)
-    enable_locking = Column(Boolean(), nullable=False, unique=None, default=False)
-    _locked = Column("locked", String(255, convert_unicode=False), nullable=True, unique=False, default=None)
-    _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
-
-    fork_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
-    group_id = Column(Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
+    repo_id = Column(Integer(), unique=True, primary_key=True)
+    repo_name = Column(Unicode(255), nullable=False, unique=True)
+    repo_state = Column(String(255), nullable=False)
+
+    clone_uri = Column(String(255), nullable=True) # FIXME: not nullable?
+    repo_type = Column(String(255), nullable=False)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
+    private = Column(Boolean(), nullable=False)
+    enable_statistics = Column("statistics", Boolean(), nullable=False, default=True)
+    enable_downloads = Column("downloads", Boolean(), nullable=False, default=True)
+    description = Column(Unicode(10000), nullable=False)
+    created_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
+    updated_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
+    _landing_revision = Column("landing_revision", String(255), nullable=False)
+    enable_locking = Column(Boolean(), nullable=False, default=False)
+    _locked = Column("locked", String(255), nullable=True) # FIXME: not nullable?
+    _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) # JSON data # FIXME: not nullable?
+
+    fork_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=True)
+    group_id = Column(Integer(), ForeignKey('groups.group_id'), nullable=True)
 
     user = relationship('User')
     fork = relationship('Repository', remote_side=repo_id)
@@ -1083,8 +1089,8 @@
     @classmethod
     def get_by_repo_name(cls, repo_name):
         q = Session().query(cls).filter(cls.repo_name == repo_name)
-        q = q.options(joinedload(Repository.fork))\
-                .options(joinedload(Repository.user))\
+        q = q.options(joinedload(Repository.fork)) \
+                .options(joinedload(Repository.user)) \
                 .options(joinedload(Repository.group))
         return q.scalar()
 
@@ -1105,7 +1111,7 @@
 
         :param cls:
         """
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == cls.url_sep())
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
@@ -1174,9 +1180,9 @@
         """
         Returns associated cache keys for that repo
         """
-        return CacheInvalidation.query()\
-            .filter(CacheInvalidation.cache_args == self.repo_name)\
-            .order_by(CacheInvalidation.cache_key)\
+        return CacheInvalidation.query() \
+            .filter(CacheInvalidation.cache_args == self.repo_name) \
+            .order_by(CacheInvalidation.cache_key) \
             .all()
 
     def get_new_name(self, repo_name):
@@ -1378,11 +1384,11 @@
 
         :param revisions: filter query by revisions only
         """
-        cmts = ChangesetComment.query()\
+        cmts = ChangesetComment.query() \
             .filter(ChangesetComment.repo == self)
         if revisions is not None:
             if not revisions:
-                return [] # don't use sql 'in' on empty set
+                return {} # don't use sql 'in' on empty set
             cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
         grouped = collections.defaultdict(list)
         for cmt in cmts.all():
@@ -1399,9 +1405,9 @@
         if not revisions:
             return {}
 
-        statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == self)\
-            .filter(ChangesetStatus.version == 0)\
+        statuses = ChangesetStatus.query() \
+            .filter(ChangesetStatus.repo == self) \
+            .filter(ChangesetStatus.version == 0) \
             .filter(ChangesetStatus.revision.in_(revisions))
 
         grouped = {}
@@ -1412,7 +1418,8 @@
                 pr_nice_id = PullRequest.make_nice_id(pr_id)
                 pr_repo = stat.pull_request.other_repo.repo_name
             grouped[stat.revision] = [str(stat.status), stat.status_lbl,
-                                      pr_id, pr_repo, pr_nice_id]
+                                      pr_id, pr_repo, pr_nice_id,
+                                      stat.author]
         return grouped
 
     def _repo_size(self):
@@ -1443,7 +1450,8 @@
 
     def scm_instance_cached(self, valid_cache_keys=None):
         @cache_region('long_term')
-        def _c(repo_name):
+        def _c(repo_name): # repo_name is just for the cache key
+            log.debug('Creating new %s scm_instance and populating cache', repo_name)
             return self.__get_instance()
         rn = self.repo_name
 
@@ -1452,19 +1460,18 @@
             log.debug('Cache for %s invalidated, getting new object', rn)
             region_invalidate(_c, None, rn)
         else:
-            log.debug('Getting scm_instance of %s from cache', rn)
+            log.debug('Trying to get scm_instance of %s from cache', rn)
         return _c(rn)
 
     def __get_instance(self):
-        repo_full_path = self.repo_full_path
-
-        alias = get_scm(safe_str(repo_full_path))[0]
+        repo_full_path = safe_str(self.repo_full_path)
+        alias = get_scm(repo_full_path)[0]
         log.debug('Creating instance of %s repository from %s',
-                  alias, repo_full_path)
+                  alias, self.repo_full_path)
         backend = get_backend(alias)
 
         if alias == 'hg':
-            repo = backend(safe_str(repo_full_path), create=False,
+            repo = backend(repo_full_path, create=False,
                            baseui=self._ui)
         else:
             repo = backend(repo_full_path, create=False)
@@ -1479,19 +1486,18 @@
     __table_args__ = (
         UniqueConstraint('group_name', 'group_parent_id'),
         CheckConstraint('group_id != group_parent_id'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
+        _table_args_default_dict,
     )
     __mapper_args__ = {'order_by': 'group_name'}
 
     SEP = ' &raquo; '
 
-    group_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    group_name = Column(String(255, convert_unicode=False), nullable=False, unique=True, default=None)
-    group_parent_id = Column(Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
-    group_description = Column(String(10000, convert_unicode=False), nullable=True, unique=None, default=None)
-    enable_locking = Column(Boolean(), nullable=False, unique=None, default=False)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
+    group_id = Column(Integer(), unique=True, primary_key=True)
+    group_name = Column(Unicode(255), nullable=False, unique=True)
+    group_parent_id = Column(Integer(), ForeignKey('groups.group_id'), nullable=True)
+    group_description = Column(Unicode(10000), nullable=False)
+    enable_locking = Column(Boolean(), nullable=False, default=False)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
     created_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
 
     repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
@@ -1528,10 +1534,10 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = cls.query()\
-                .filter(cls.group_name.ilike(group_name))
+            gr = cls.query() \
+                .filter(func.lower(cls.group_name) == func.lower(group_name))
         else:
-            gr = cls.query()\
+            gr = cls.query() \
                 .filter(cls.group_name == group_name)
         if cache:
             gr = gr.options(FromCache(
@@ -1583,8 +1589,8 @@
 
     @property
     def repositories(self):
-        return Repository.query()\
-                .filter(Repository.group == self)\
+        return Repository.query() \
+                .filter(Repository.group == self) \
                 .order_by(Repository.repo_name)
 
     @property
@@ -1659,9 +1665,9 @@
     __tablename__ = 'permissions'
     __table_args__ = (
         Index('p_perm_name_idx', 'permission_name'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
+        _table_args_default_dict,
     )
+
     PERMS = [
         ('hg.admin', _('Kallithea Administrator')),
 
@@ -1747,9 +1753,8 @@
         'hg.create.repository': 1
     }
 
-    permission_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    permission_name = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
-    permission_longname = Column(String(255, convert_unicode=False), nullable=True, unique=None, default=None)
+    permission_id = Column(Integer(), unique=True, primary_key=True)
+    permission_name = Column(String(255), nullable=False)
 
     def __unicode__(self):
         return u"<%s('%s:%s')>" % (
@@ -1762,27 +1767,27 @@
 
     @classmethod
     def get_default_perms(cls, default_user_id):
-        q = Session().query(UserRepoToPerm, Repository, cls)\
-         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
-         .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoToPerm, Repository, cls) \
+         .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id)) \
+         .join((cls, UserRepoToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_group_perms(cls, default_user_id):
-        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
-         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
-         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserRepoGroupToPerm, RepoGroup, cls) \
+         .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id)) \
+         .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserRepoGroupToPerm.user_id == default_user_id)
 
         return q.all()
 
     @classmethod
     def get_default_user_group_perms(cls, default_user_id):
-        q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
-         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
-         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
+        q = Session().query(UserUserGroupToPerm, UserGroup, cls) \
+         .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id)) \
+         .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id)) \
          .filter(UserUserGroupToPerm.user_id == default_user_id)
 
         return q.all()
@@ -1792,13 +1797,13 @@
     __tablename__ = 'repo_to_perm'
     __table_args__ = (
         UniqueConstraint('user_id', 'repository_id', 'permission_id'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
-    repo_to_perm_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
-    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
-    repository_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
+
+    repo_to_perm_id = Column(Integer(), unique=True, primary_key=True)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
+    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False)
+    repository_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=False)
 
     user = relationship('User')
     repository = relationship('Repository')
@@ -1821,13 +1826,13 @@
     __tablename__ = 'user_user_group_to_perm'
     __table_args__ = (
         UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
-    user_user_group_to_perm_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
-    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
-    user_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
+
+    user_user_group_to_perm_id = Column(Integer(), unique=True, primary_key=True)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
+    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False)
+    user_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False)
 
     user = relationship('User')
     user_group = relationship('UserGroup')
@@ -1850,12 +1855,12 @@
     __tablename__ = 'user_to_perm'
     __table_args__ = (
         UniqueConstraint('user_id', 'permission_id'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
-    user_to_perm_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
-    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
+
+    user_to_perm_id = Column(Integer(), unique=True, primary_key=True)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
+    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False)
 
     user = relationship('User')
     permission = relationship('Permission')
@@ -1868,13 +1873,13 @@
     __tablename__ = 'users_group_repo_to_perm'
     __table_args__ = (
         UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
-    users_group_to_perm_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    users_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
-    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
-    repository_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
+
+    users_group_to_perm_id = Column(Integer(), unique=True, primary_key=True)
+    users_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False)
+    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False)
+    repository_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=False)
 
     users_group = relationship('UserGroup')
     permission = relationship('Permission')
@@ -1898,13 +1903,13 @@
     __table_args__ = (
         UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
         CheckConstraint('target_user_group_id != user_group_id'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
-    user_group_user_group_to_perm_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    target_user_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
-    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
-    user_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
+
+    user_group_user_group_to_perm_id = Column(Integer(), unique=True, primary_key=True)
+    target_user_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False)
+    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False)
+    user_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False)
 
     target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
     user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
@@ -1927,12 +1932,12 @@
     __tablename__ = 'users_group_to_perm'
     __table_args__ = (
         UniqueConstraint('users_group_id', 'permission_id',),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
-    users_group_to_perm_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    users_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
-    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
+
+    users_group_to_perm_id = Column(Integer(), unique=True, primary_key=True)
+    users_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False)
+    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False)
 
     users_group = relationship('UserGroup')
     permission = relationship('Permission')
@@ -1942,14 +1947,13 @@
     __tablename__ = 'user_repo_group_to_perm'
     __table_args__ = (
         UniqueConstraint('user_id', 'group_id', 'permission_id'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
 
-    group_to_perm_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
-    group_id = Column(Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
-    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
+    group_to_perm_id = Column(Integer(), unique=True, primary_key=True)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
+    group_id = Column(Integer(), ForeignKey('groups.group_id'), nullable=False)
+    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False)
 
     user = relationship('User')
     group = relationship('RepoGroup')
@@ -1969,14 +1973,13 @@
     __tablename__ = 'users_group_repo_group_to_perm'
     __table_args__ = (
         UniqueConstraint('users_group_id', 'group_id'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
 
-    users_group_repo_group_to_perm_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    users_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
-    group_id = Column(Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
-    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
+    users_group_repo_group_to_perm_id = Column(Integer(), unique=True, primary_key=True)
+    users_group_id = Column(Integer(), ForeignKey('users_groups.users_group_id'), nullable=False)
+    group_id = Column(Integer(), ForeignKey('groups.group_id'), nullable=False)
+    permission_id = Column(Integer(), ForeignKey('permissions.permission_id'), nullable=False)
 
     users_group = relationship('UserGroup')
     permission = relationship('Permission')
@@ -1995,12 +1998,11 @@
 class Statistics(Base, BaseModel):
     __tablename__ = 'statistics'
     __table_args__ = (
-         UniqueConstraint('repository_id'),
-         {'extend_existing': True, 'mysql_engine': 'InnoDB',
-          'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+         _table_args_default_dict,
     )
-    stat_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    repository_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
+
+    stat_id = Column(Integer(), unique=True, primary_key=True)
+    repository_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True)
     stat_on_revision = Column(Integer(), nullable=False)
     commit_activity = Column(LargeBinary(1000000), nullable=False)#JSON data
     commit_activity_combined = Column(LargeBinary(), nullable=False)#JSON data
@@ -2014,15 +2016,14 @@
     __table_args__ = (
         UniqueConstraint('user_id', 'follows_repository_id'),
         UniqueConstraint('user_id', 'follows_user_id'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
 
-    user_following_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
-    follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
-    follows_user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
-    follows_from = Column(DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
+    user_following_id = Column(Integer(), unique=True, primary_key=True)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
+    follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
+    follows_user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=True)
+    follows_from = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
 
     user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
 
@@ -2037,20 +2038,19 @@
 class CacheInvalidation(Base, BaseModel):
     __tablename__ = 'cache_invalidation'
     __table_args__ = (
-        UniqueConstraint('cache_key'),
         Index('key_idx', 'cache_key'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
+        _table_args_default_dict,
     )
+
     # cache_id, not used
-    cache_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
+    cache_id = Column(Integer(), unique=True, primary_key=True)
     # cache_key as created by _get_cache_key
-    cache_key = Column(String(255, convert_unicode=False))
+    cache_key = Column(Unicode(255), nullable=False, unique=True)
     # cache_args is a repo_name
-    cache_args = Column(String(255, convert_unicode=False))
+    cache_args = Column(Unicode(255), nullable=False)
     # instance sets cache_active True when it is caching, other instances set
     # cache_active to False to indicate that this cache is invalid
-    cache_active = Column(Boolean(), nullable=True, unique=None, default=False)
+    cache_active = Column(Boolean(), nullable=False, default=False)
 
     def __init__(self, cache_key, repo_name=''):
         self.cache_key = cache_key
@@ -2104,7 +2104,7 @@
         return "%s%s" % (prefix, key)
 
     @classmethod
-    def set_invalidate(cls, repo_name, delete=False):
+    def set_invalidate(cls, repo_name):
         """
         Mark all caches of a repo as invalid in the database.
         """
@@ -2115,11 +2115,7 @@
         for inv_obj in inv_objs:
             log.debug('marking %s key for invalidation based on repo_name=%s',
                       inv_obj, safe_str(repo_name))
-            if delete:
-                Session().delete(inv_obj)
-            else:
-                inv_obj.cache_active = False
-                Session().add(inv_obj)
+            Session().delete(inv_obj)
         Session().commit()
 
     @classmethod
@@ -2138,7 +2134,7 @@
 
         inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
         if inv_obj is None:
-            inv_obj = CacheInvalidation(cache_key, repo_name)
+            inv_obj = cls(cache_key, repo_name)
         elif inv_obj.cache_active:
             return True
         inv_obj.cache_active = True
@@ -2169,15 +2165,15 @@
     __table_args__ = (
         Index('cc_revision_idx', 'revision'),
         Index('cc_pull_request_id_idx', 'pull_request_id'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
+        _table_args_default_dict,
     )
-    comment_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
+
+    comment_id = Column(Integer(), unique=True, primary_key=True)
     repo_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=False)
-    revision = Column(String(40))
-    pull_request_id = Column(Integer(), ForeignKey('pull_requests.pull_request_id'))
-    line_no = Column(Unicode(10))
-    f_path = Column(Unicode(1000))
+    revision = Column(String(40), nullable=True)
+    pull_request_id = Column(Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
+    line_no = Column(Unicode(10), nullable=True)
+    f_path = Column(Unicode(1000), nullable=True)
     user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
     text = Column(UnicodeText(25000), nullable=False)
     created_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
@@ -2201,7 +2197,7 @@
         :param cls:
         :param revision:
         """
-        q = Session().query(User)\
+        q = Session().query(User) \
                 .join(ChangesetComment.author)
         if revision is not None:
             q = q.filter(cls.revision == revision)
@@ -2217,6 +2213,10 @@
         elif self.pull_request_id is not None:
             return self.pull_request.url(anchor=anchor)
 
+    def deletable(self):
+        return self.created_on > datetime.datetime.now() - datetime.timedelta(minutes=5)
+
+
 class ChangesetStatus(Base, BaseModel):
     __tablename__ = 'changeset_statuses'
     __table_args__ = (
@@ -2226,9 +2226,9 @@
         Index('cs_changeset_comment_id_idx', 'changeset_comment_id'),
         Index('cs_pull_request_id_user_id_version_idx', 'pull_request_id', 'user_id', 'version'),
         UniqueConstraint('repo_id', 'revision', 'version'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
+
     STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
     STATUS_APPROVED = 'approved'
     STATUS_REJECTED = 'rejected'
@@ -2241,10 +2241,10 @@
         (STATUS_UNDER_REVIEW, _("Under review")),
     ]
 
-    changeset_status_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
+    changeset_status_id = Column(Integer(), unique=True, primary_key=True)
     repo_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=False)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
-    revision = Column(String(40), nullable=False)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
+    revision = Column(String(40), nullable=True)
     status = Column(String(128), nullable=False, default=DEFAULT)
     changeset_comment_id = Column(Integer(), ForeignKey('changeset_comments.comment_id'), nullable=False)
     modified_at = Column(DateTime(), nullable=False, default=datetime.datetime.now)
@@ -2276,22 +2276,21 @@
     __table_args__ = (
         Index('pr_org_repo_id_idx', 'org_repo_id'),
         Index('pr_other_repo_id_idx', 'other_repo_id'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
+        _table_args_default_dict,
     )
 
     # values for .status
     STATUS_NEW = u'new'
     STATUS_CLOSED = u'closed'
 
-    pull_request_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    title = Column(Unicode(255), nullable=True)
-    description = Column(UnicodeText(10240))
+    pull_request_id = Column(Integer(), unique=True, primary_key=True)
+    title = Column(Unicode(255), nullable=False)
+    description = Column(UnicodeText(10240), nullable=False)
     status = Column(Unicode(255), nullable=False, default=STATUS_NEW) # only for closedness, not approve/reject/etc
     created_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
     updated_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
-    _revisions = Column('revisions', UnicodeText(20500))  # 500 revisions max
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
+    _revisions = Column('revisions', UnicodeText(20500), nullable=False)  # 500 revisions max
     org_repo_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=False)
     org_ref = Column(Unicode(255), nullable=False)
     other_repo_id = Column(Integer(), ForeignKey('repositories.repo_id'), nullable=False)
@@ -2328,10 +2327,10 @@
     def user_review_status(self, user_id):
         """Return the user's latest status votes on PR"""
         # note: no filtering on repo - that would be redundant
-        status = ChangesetStatus.query()\
-            .filter(ChangesetStatus.pull_request == self)\
-            .filter(ChangesetStatus.user_id == user_id)\
-            .order_by(ChangesetStatus.version)\
+        status = ChangesetStatus.query() \
+            .filter(ChangesetStatus.pull_request == self) \
+            .filter(ChangesetStatus.user_id == user_id) \
+            .order_by(ChangesetStatus.version) \
             .first()
         return str(status.status) if status else ''
 
@@ -2367,17 +2366,17 @@
 class PullRequestReviewers(Base, BaseModel):
     __tablename__ = 'pull_request_reviewers'
     __table_args__ = (
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
+        Index('pull_request_reviewers_user_id_idx', 'user_id'),
+        _table_args_default_dict,
     )
 
     def __init__(self, user=None, pull_request=None):
         self.user = user
         self.pull_request = pull_request
 
-    pull_requests_reviewers_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
+    pull_requests_reviewers_id = Column(Integer(), unique=True, primary_key=True)
     pull_request_id = Column(Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
-    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=True)
+    user_id = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
 
     user = relationship('User')
     pull_request = relationship('PullRequest')
@@ -2387,8 +2386,7 @@
     __tablename__ = 'notifications'
     __table_args__ = (
         Index('notification_type_idx', 'type'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
+        _table_args_default_dict,
     )
 
     TYPE_CHANGESET_COMMENT = u'cs_comment'
@@ -2398,12 +2396,12 @@
     TYPE_PULL_REQUEST = u'pull_request'
     TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
 
-    notification_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    subject = Column(Unicode(512), nullable=True)
-    body = Column(UnicodeText(50000), nullable=True)
-    created_by = Column(Integer(), ForeignKey('users.user_id'), nullable=True)
+    notification_id = Column(Integer(), unique=True, primary_key=True)
+    subject = Column(Unicode(512), nullable=False)
+    body = Column(UnicodeText(50000), nullable=False)
+    created_by = Column(Integer(), ForeignKey('users.user_id'), nullable=False)
     created_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
-    type_ = Column('type', Unicode(255))
+    type_ = Column('type', Unicode(255), nullable=False)
 
     created_by_user = relationship('User')
     notifications_to_users = relationship('UserNotification', cascade="all, delete-orphan")
@@ -2426,11 +2424,15 @@
         notification.type_ = type_
         notification.created_on = datetime.datetime.now()
 
-        for u in recipients:
-            assoc = UserNotification()
-            assoc.notification = notification
-            assoc.user_id = u.user_id
-            Session().add(assoc)
+        for recipient in recipients:
+            un = UserNotification()
+            un.notification = notification
+            un.user_id = recipient.user_id
+            # Mark notifications to self "pre-read" - should perhaps just be skipped
+            if recipient == created_by:
+                un.read = True
+            Session().add(un)
+
         Session().add(notification)
         Session().flush() # assign notificaiton.notification_id
         return notification
@@ -2445,13 +2447,13 @@
     __tablename__ = 'user_to_notification'
     __table_args__ = (
         UniqueConstraint('user_id', 'notification_id'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
+
     user_id = Column(Integer(), ForeignKey('users.user_id'), primary_key=True)
     notification_id = Column(Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
-    read = Column(Boolean, default=False)
-    sent_on = Column(DateTime(timezone=False), nullable=True, unique=None)
+    read = Column(Boolean, nullable=False, default=False)
+    sent_on = Column(DateTime(timezone=False), nullable=True) # FIXME: not nullable?
 
     user = relationship('User')
     notification = relationship('Notification')
@@ -2466,17 +2468,17 @@
     __table_args__ = (
         Index('g_gist_access_id_idx', 'gist_access_id'),
         Index('g_created_on_idx', 'created_on'),
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
+        _table_args_default_dict,
     )
+
     GIST_PUBLIC = u'public'
     GIST_PRIVATE = u'private'
     DEFAULT_FILENAME = u'gistfile1.txt'
 
-    gist_id = Column(Integer(), nullable=False, unique=True, primary_key=True)
-    gist_access_id = Column(Unicode(250))
-    gist_description = Column(UnicodeText(1024))
-    gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
+    gist_id = Column(Integer(), unique=True, primary_key=True)
+    gist_access_id = Column(Unicode(250), nullable=False)
+    gist_description = Column(UnicodeText(1024), nullable=False)
+    gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
     gist_expires = Column(Float(53), nullable=False)
     gist_type = Column(Unicode(128), nullable=False)
     created_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
@@ -2515,7 +2517,7 @@
         :param cls:
         """
         from kallithea.model.gist import GIST_STORE_LOC
-        q = Session().query(Ui)\
+        q = Session().query(Ui) \
             .filter(Ui.ui_key == URL_SEP)
         q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return os.path.join(q.one().ui_value, GIST_STORE_LOC)
@@ -2554,9 +2556,9 @@
 class DbMigrateVersion(Base, BaseModel):
     __tablename__ = 'db_migrate_version'
     __table_args__ = (
-        {'extend_existing': True, 'mysql_engine': 'InnoDB',
-         'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
+        _table_args_default_dict,
     )
-    repository_id = Column(String(250), nullable=False, unique=True, primary_key=True)
-    repository_path = Column(Text)
-    version = Column(Integer)
+
+    repository_id = Column(String(250), unique=True, primary_key=True)
+    repository_path = Column(Text, nullable=False)
+    version = Column(Integer, nullable=False)
--- a/kallithea/model/forms.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/model/forms.py	Mon Mar 14 00:36:08 2016 +0100
@@ -86,7 +86,8 @@
     return _PasswordChangeForm
 
 
-def UserForm(edit=False, old_data={}):
+def UserForm(edit=False, old_data=None):
+    old_data = old_data or {}
     class _UserForm(formencode.Schema):
         allow_extra_fields = True
         filter_extra_fields = True
@@ -125,7 +126,9 @@
     return _UserForm
 
 
-def UserGroupForm(edit=False, old_data={}, available_members=[]):
+def UserGroupForm(edit=False, old_data=None, available_members=None):
+    old_data = old_data or {}
+    available_members = available_members or []
     class _UserGroupForm(formencode.Schema):
         allow_extra_fields = True
         filter_extra_fields = True
@@ -148,8 +151,10 @@
     return _UserGroupForm
 
 
-def RepoGroupForm(edit=False, old_data={}, repo_groups=[],
+def RepoGroupForm(edit=False, old_data=None, repo_groups=None,
                    can_create_in_root=False):
+    old_data = old_data or {}
+    repo_groups = repo_groups or []
     repo_group_ids = [rg[0] for rg in repo_groups]
     class _RepoGroupForm(formencode.Schema):
         allow_extra_fields = True
@@ -178,7 +183,7 @@
     return _RepoGroupForm
 
 
-def RegisterForm(edit=False, old_data={}):
+def RegisterForm(edit=False, old_data=None):
     class _RegisterForm(formencode.Schema):
         allow_extra_fields = True
         filter_extra_fields = True
@@ -227,8 +232,11 @@
                                                     'password_confirm')]
     return _PasswordResetConfirmationForm
 
-def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
-             repo_groups=[], landing_revs=[]):
+def RepoForm(edit=False, old_data=None, supported_backends=BACKENDS.keys(),
+             repo_groups=None, landing_revs=None):
+    old_data = old_data or {}
+    repo_groups = repo_groups or []
+    landing_revs = landing_revs or []
     repo_group_ids = [rg[0] for rg in repo_groups]
     class _RepoForm(formencode.Schema):
         allow_extra_fields = True
@@ -302,8 +310,11 @@
     return _RepoFieldForm
 
 
-def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
-                 repo_groups=[], landing_revs=[]):
+def RepoForkForm(edit=False, old_data=None, supported_backends=BACKENDS.keys(),
+                 repo_groups=None, landing_revs=None):
+    old_data = old_data or {}
+    repo_groups = repo_groups or []
+    landing_revs = landing_revs or []
     repo_group_ids = [rg[0] for rg in repo_groups]
     class _RepoForkForm(formencode.Schema):
         allow_extra_fields = True
@@ -421,7 +432,7 @@
     return _CustomDefaultPermissionsForm
 
 
-def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
+def DefaultsForm(edit=False, old_data=None, supported_backends=BACKENDS.keys()):
     class _DefaultsForm(formencode.Schema):
         allow_extra_fields = True
         filter_extra_fields = True
--- a/kallithea/model/notification.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/model/notification.py	Mon Mar 14 00:36:08 2016 +0100
@@ -59,7 +59,7 @@
 
     def create(self, created_by, subject, body, recipients=None,
                type_=Notification.TYPE_MESSAGE, with_email=True,
-               email_kwargs={}):
+               email_kwargs=None, repo_name=None):
         """
 
         Creates notification of given type
@@ -75,7 +75,7 @@
         :param email_kwargs: additional dict to pass as args to email template
         """
         from kallithea.lib.celerylib import tasks, run_task
-
+        email_kwargs = email_kwargs or {}
         if recipients and not getattr(recipients, '__iter__', False):
             raise Exception('recipients must be a list or iterable')
 
@@ -123,7 +123,7 @@
             ## this is passed into template
             html_kwargs = {
                       'subject': subject,
-                      'body': h.rst_w_mentions(body),
+                      'body': h.render_w_mentions(body, repo_name),
                       'when': h.fmt_date(notif.created_on),
                       'user': notif.created_by_user.username,
                       }
@@ -137,11 +137,11 @@
 
             html_kwargs.update(email_kwargs)
             txt_kwargs.update(email_kwargs)
-            email_subject = EmailNotificationModel()\
+            email_subject = EmailNotificationModel() \
                                 .get_email_description(type_, **txt_kwargs)
-            email_txt_body = EmailNotificationModel()\
+            email_txt_body = EmailNotificationModel() \
                                 .get_email_tmpl(type_, 'txt', **txt_kwargs)
-            email_html_body = EmailNotificationModel()\
+            email_html_body = EmailNotificationModel() \
                                 .get_email_tmpl(type_, 'html', **html_kwargs)
 
             run_task(tasks.send_email, [rec.email], email_subject, email_txt_body,
@@ -155,10 +155,10 @@
             notification = self.__get_notification(notification)
             user = self._get_user(user)
             if notification and user:
-                obj = UserNotification.query()\
-                        .filter(UserNotification.user == user)\
+                obj = UserNotification.query() \
+                        .filter(UserNotification.user == user) \
                         .filter(UserNotification.notification
-                                == notification)\
+                                == notification) \
                         .one()
                 Session().delete(obj)
                 return True
@@ -175,12 +175,12 @@
         """
         user = self._get_user(user)
 
-        q = UserNotification.query()\
-            .filter(UserNotification.user == user)\
+        q = UserNotification.query() \
+            .filter(UserNotification.user == user) \
             .join((Notification, UserNotification.notification_id ==
-                                 Notification.notification_id))\
-            .options(joinedload('notification'))\
-            .options(subqueryload('notification.created_by_user'))\
+                                 Notification.notification_id)) \
+            .options(joinedload('notification')) \
+            .options(subqueryload('notification.created_by_user')) \
             .order_by(Notification.created_on.desc())
 
         if filter_:
@@ -193,10 +193,10 @@
             notification = self.__get_notification(notification)
             user = self._get_user(user)
             if notification and user:
-                obj = UserNotification.query()\
-                        .filter(UserNotification.user == user)\
+                obj = UserNotification.query() \
+                        .filter(UserNotification.user == user) \
                         .filter(UserNotification.notification
-                                == notification)\
+                                == notification) \
                         .one()
                 obj.read = True
                 Session().add(obj)
@@ -207,9 +207,9 @@
 
     def mark_all_read_for_user(self, user, filter_=None):
         user = self._get_user(user)
-        q = UserNotification.query()\
-            .filter(UserNotification.user == user)\
-            .filter(UserNotification.read == False)\
+        q = UserNotification.query() \
+            .filter(UserNotification.user == user) \
+            .filter(UserNotification.read == False) \
             .join((Notification, UserNotification.notification_id ==
                                  Notification.notification_id))
         if filter_:
@@ -223,22 +223,22 @@
 
     def get_unread_cnt_for_user(self, user):
         user = self._get_user(user)
-        return UserNotification.query()\
-                .filter(UserNotification.read == False)\
+        return UserNotification.query() \
+                .filter(UserNotification.read == False) \
                 .filter(UserNotification.user == user).count()
 
     def get_unread_for_user(self, user):
         user = self._get_user(user)
-        return [x.notification for x in UserNotification.query()\
-                .filter(UserNotification.read == False)\
+        return [x.notification for x in UserNotification.query() \
+                .filter(UserNotification.read == False) \
                 .filter(UserNotification.user == user).all()]
 
     def get_user_notification(self, user, notification):
         user = self._get_user(user)
         notification = self.__get_notification(notification)
 
-        return UserNotification.query()\
-            .filter(UserNotification.notification == notification)\
+        return UserNotification.query() \
+            .filter(UserNotification.notification == notification) \
             .filter(UserNotification.user == user).scalar()
 
     def make_description(self, notification, show_age=True):
--- a/kallithea/model/permission.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/model/permission.py	Mon Mar 14 00:36:08 2016 +0100
@@ -32,7 +32,7 @@
 from sqlalchemy.exc import DatabaseError
 
 from kallithea.model import BaseModel
-from kallithea.model.db import User, Permission, UserToPerm, UserRepoToPerm,\
+from kallithea.model.db import User, Permission, UserToPerm, UserRepoToPerm, \
     UserRepoGroupToPerm, UserUserGroupToPerm
 from kallithea.lib.utils2 import str2bool
 
@@ -54,7 +54,6 @@
             if not Permission.get_by_key(p[0]):
                 new_perm = Permission()
                 new_perm.permission_name = p[0]
-                new_perm.permission_longname = p[0]  #translation err with p[1]
                 self.sa.add(new_perm)
 
     def create_default_permissions(self, user, force=False):
@@ -116,8 +115,8 @@
             # clear current entries, to make this function idempotent
             # it will fix even if we define more permissions or permissions
             # are somehow missing
-            u2p = self.sa.query(UserToPerm)\
-                .filter(UserToPerm.user == perm_user)\
+            u2p = self.sa.query(UserToPerm) \
+                .filter(UserToPerm.user == perm_user) \
                 .all()
             for p in u2p:
                 self.sa.delete(p)
@@ -140,8 +139,8 @@
                 _def_name = form_result['default_repo_perm'].split('repository.')[-1]
                 _def = Permission.get_by_key('repository.' + _def_name)
                 # repos
-                for r2p in self.sa.query(UserRepoToPerm)\
-                               .filter(UserRepoToPerm.user == perm_user)\
+                for r2p in self.sa.query(UserRepoToPerm) \
+                               .filter(UserRepoToPerm.user == perm_user) \
                                .all():
 
                     #don't reset PRIVATE repositories
@@ -153,8 +152,8 @@
                 _def_name = form_result['default_group_perm'].split('group.')[-1]
                 # groups
                 _def = Permission.get_by_key('group.' + _def_name)
-                for g2p in self.sa.query(UserRepoGroupToPerm)\
-                               .filter(UserRepoGroupToPerm.user == perm_user)\
+                for g2p in self.sa.query(UserRepoGroupToPerm) \
+                               .filter(UserRepoGroupToPerm.user == perm_user) \
                                .all():
                     g2p.permission = _def
                     self.sa.add(g2p)
@@ -163,8 +162,8 @@
                 _def_name = form_result['default_user_group_perm'].split('usergroup.')[-1]
                 # groups
                 _def = Permission.get_by_key('usergroup.' + _def_name)
-                for g2p in self.sa.query(UserUserGroupToPerm)\
-                               .filter(UserUserGroupToPerm.user == perm_user)\
+                for g2p in self.sa.query(UserUserGroupToPerm) \
+                               .filter(UserUserGroupToPerm.user == perm_user) \
                                .all():
                     g2p.permission = _def
                     self.sa.add(g2p)
--- a/kallithea/model/pull_request.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/model/pull_request.py	Mon Mar 14 00:36:08 2016 +0100
@@ -34,7 +34,7 @@
 from kallithea.lib import helpers as h
 from kallithea.lib.exceptions import UserInvalidException
 from kallithea.model import BaseModel
-from kallithea.model.db import PullRequest, PullRequestReviewers, Notification,\
+from kallithea.model.db import PullRequest, PullRequestReviewers, Notification, \
     ChangesetStatus, User
 from kallithea.model.notification import NotificationModel
 from kallithea.lib.utils2 import extract_mentioned_users, safe_unicode
@@ -51,10 +51,10 @@
         return self._get_instance(PullRequest, pull_request)
 
     def get_pullrequest_cnt_for_user(self, user):
-        return PullRequest.query()\
-                                .join(PullRequestReviewers)\
-                                .filter(PullRequestReviewers.user_id == user)\
-                                .filter(PullRequest.status != PullRequest.STATUS_CLOSED)\
+        return PullRequest.query() \
+                                .join(PullRequestReviewers) \
+                                .filter(PullRequestReviewers.user_id == user) \
+                                .filter(PullRequest.status != PullRequest.STATUS_CLOSED) \
                                 .count()
 
     def get_all(self, repo_name, from_=False, closed=False):
@@ -149,6 +149,7 @@
             'pr_url': pr_url,
             'pr_revisions': revision_data,
             'repo_name': pr.other_repo.repo_name,
+            'org_repo_name': pr.org_repo.repo_name,
             'pr_nice_id': pr.nice_id(),
             'ref': org_ref_name,
             'pr_username': user.username,
@@ -184,9 +185,9 @@
     def update_reviewers(self, user, pull_request, reviewers_ids):
         reviewers_ids = set(reviewers_ids)
         pull_request = self.__get_pull_request(pull_request)
-        current_reviewers = PullRequestReviewers.query()\
+        current_reviewers = PullRequestReviewers.query() \
                             .filter(PullRequestReviewers.pull_request==
-                                   pull_request)\
+                                   pull_request) \
                             .all()
         current_reviewers_ids = set([x.user.user_id for x in current_reviewers])
 
@@ -198,9 +199,9 @@
 
         log.debug("Removing %s reviewers", to_remove)
         for uid in to_remove:
-            reviewer = PullRequestReviewers.query()\
+            reviewer = PullRequestReviewers.query() \
                     .filter(PullRequestReviewers.user_id==uid,
-                            PullRequestReviewers.pull_request==pull_request)\
+                            PullRequestReviewers.pull_request==pull_request) \
                     .scalar()
             if reviewer:
                 Session().delete(reviewer)
--- a/kallithea/model/repo_group.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/model/repo_group.py	Mon Mar 14 00:36:08 2016 +0100
@@ -59,7 +59,7 @@
         Gets the repositories root path from database
         """
 
-        q = Ui.get_by_key('/')
+        q = Ui.get_by_key('paths', '/')
         return q.ui_value
 
     def _create_default_perms(self, new_group):
@@ -455,9 +455,9 @@
         permission = self._get_perm(perm)
 
         # check if we have that permission already
-        obj = self.sa.query(UserRepoGroupToPerm)\
-            .filter(UserRepoGroupToPerm.user == user)\
-            .filter(UserRepoGroupToPerm.group == repo_group)\
+        obj = self.sa.query(UserRepoGroupToPerm) \
+            .filter(UserRepoGroupToPerm.user == user) \
+            .filter(UserRepoGroupToPerm.group == repo_group) \
             .scalar()
         if obj is None:
             # create new !
@@ -481,9 +481,9 @@
         repo_group = self._get_repo_group(repo_group)
         user = self._get_user(user)
 
-        obj = self.sa.query(UserRepoGroupToPerm)\
-            .filter(UserRepoGroupToPerm.user == user)\
-            .filter(UserRepoGroupToPerm.group == repo_group)\
+        obj = self.sa.query(UserRepoGroupToPerm) \
+            .filter(UserRepoGroupToPerm.user == user) \
+            .filter(UserRepoGroupToPerm.group == repo_group) \
             .scalar()
         if obj is not None:
             self.sa.delete(obj)
@@ -505,9 +505,9 @@
         permission = self._get_perm(perm)
 
         # check if we have that permission already
-        obj = self.sa.query(UserGroupRepoGroupToPerm)\
-            .filter(UserGroupRepoGroupToPerm.group == repo_group)\
-            .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
+        obj = self.sa.query(UserGroupRepoGroupToPerm) \
+            .filter(UserGroupRepoGroupToPerm.group == repo_group) \
+            .filter(UserGroupRepoGroupToPerm.users_group == group_name) \
             .scalar()
 
         if obj is None:
@@ -533,9 +533,9 @@
         repo_group = self._get_repo_group(repo_group)
         group_name = self._get_user_group(group_name)
 
-        obj = self.sa.query(UserGroupRepoGroupToPerm)\
-            .filter(UserGroupRepoGroupToPerm.group == repo_group)\
-            .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
+        obj = self.sa.query(UserGroupRepoGroupToPerm) \
+            .filter(UserGroupRepoGroupToPerm.group == repo_group) \
+            .filter(UserGroupRepoGroupToPerm.users_group == group_name) \
             .scalar()
         if obj is not None:
             self.sa.delete(obj)
--- a/kallithea/model/scm.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/model/scm.py	Mon Mar 14 00:36:08 2016 +0100
@@ -26,6 +26,7 @@
 """
 
 import os
+import posixpath
 import re
 import time
 import traceback
@@ -46,9 +47,9 @@
 
 from kallithea import BACKENDS
 from kallithea.lib import helpers as h
-from kallithea.lib.utils2 import safe_str, safe_unicode, get_server_url,\
+from kallithea.lib.utils2 import safe_str, safe_unicode, get_server_url, \
     _set_extras
-from kallithea.lib.auth import HasRepoPermissionAny, HasRepoGroupPermissionAny,\
+from kallithea.lib.auth import HasRepoPermissionAny, HasRepoGroupPermissionAny, \
     HasUserGroupPermissionAny, HasPermissionAny, HasPermissionAll
 from kallithea.lib.utils import get_filesystem_repos, make_ui, \
     action_logger
@@ -311,8 +312,8 @@
         :param simple: use SimpleCachedList - one without the SCM info
         """
         if all_repos is None:
-            all_repos = self.sa.query(Repository)\
-                        .filter(Repository.group_id == None)\
+            all_repos = self.sa.query(Repository) \
+                        .filter(Repository.group_id == None) \
                         .order_by(func.lower(Repository.repo_name)).all()
         if simple:
             repo_iter = SimpleCachedRepoList(all_repos,
@@ -327,25 +328,25 @@
 
     def get_repo_groups(self, all_groups=None):
         if all_groups is None:
-            all_groups = RepoGroup.query()\
+            all_groups = RepoGroup.query() \
                 .filter(RepoGroup.group_parent_id == None).all()
         return [x for x in RepoGroupList(all_groups)]
 
-    def mark_for_invalidation(self, repo_name, delete=False):
+    def mark_for_invalidation(self, repo_name):
         """
         Mark caches of this repo invalid in the database.
 
         :param repo_name: the repo for which caches should be marked invalid
         """
-        CacheInvalidation.set_invalidate(repo_name, delete=delete)
+        CacheInvalidation.set_invalidate(repo_name)
         repo = Repository.get_by_repo_name(repo_name)
         if repo is not None:
             repo.update_changeset_cache()
 
     def toggle_following_repo(self, follow_repo_id, user_id):
 
-        f = self.sa.query(UserFollowing)\
-            .filter(UserFollowing.follows_repo_id == follow_repo_id)\
+        f = self.sa.query(UserFollowing) \
+            .filter(UserFollowing.follows_repo_id == follow_repo_id) \
             .filter(UserFollowing.user_id == user_id).scalar()
 
         if f is not None:
@@ -373,8 +374,8 @@
             raise
 
     def toggle_following_user(self, follow_user_id, user_id):
-        f = self.sa.query(UserFollowing)\
-            .filter(UserFollowing.follows_user_id == follow_user_id)\
+        f = self.sa.query(UserFollowing) \
+            .filter(UserFollowing.follows_user_id == follow_user_id) \
             .filter(UserFollowing.user_id == user_id).scalar()
 
         if f is not None:
@@ -395,11 +396,11 @@
             raise
 
     def is_following_repo(self, repo_name, user_id, cache=False):
-        r = self.sa.query(Repository)\
+        r = self.sa.query(Repository) \
             .filter(Repository.repo_name == repo_name).scalar()
 
-        f = self.sa.query(UserFollowing)\
-            .filter(UserFollowing.follows_repository == r)\
+        f = self.sa.query(UserFollowing) \
+            .filter(UserFollowing.follows_repository == r) \
             .filter(UserFollowing.user_id == user_id).scalar()
 
         return f is not None
@@ -407,8 +408,8 @@
     def is_following_user(self, username, user_id, cache=False):
         u = User.get_by_username(username)
 
-        f = self.sa.query(UserFollowing)\
-            .filter(UserFollowing.follows_user == u)\
+        f = self.sa.query(UserFollowing) \
+            .filter(UserFollowing.follows_user == u) \
             .filter(UserFollowing.user_id == user_id).scalar()
 
         return f is not None
@@ -416,18 +417,18 @@
     def get_followers(self, repo):
         repo = self._get_repo(repo)
 
-        return self.sa.query(UserFollowing)\
+        return self.sa.query(UserFollowing) \
                 .filter(UserFollowing.follows_repository == repo).count()
 
     def get_forks(self, repo):
         repo = self._get_repo(repo)
-        return self.sa.query(Repository)\
+        return self.sa.query(Repository) \
                 .filter(Repository.fork == repo).count()
 
     def get_pull_requests(self, repo):
         repo = self._get_repo(repo)
-        return self.sa.query(PullRequest)\
-                .filter(PullRequest.other_repo == repo)\
+        return self.sa.query(PullRequest) \
+                .filter(PullRequest.other_repo == repo) \
                 .filter(PullRequest.status != PullRequest.STATUS_CLOSED).count()
 
     def mark_as_fork(self, repo, fork, user):
@@ -575,7 +576,7 @@
         if f_path.startswith('/') or f_path.startswith('.') or '../' in f_path:
             raise NonRelativePathError('%s is not an relative path' % f_path)
         if f_path:
-            f_path = os.path.normpath(f_path)
+            f_path = posixpath.normpath(f_path)
         return f_path
 
     def get_nodes(self, repo_name, revision, root_path='/', flat=True):
@@ -628,8 +629,8 @@
 
         processed_nodes = []
         for f_path in nodes:
+            content = nodes[f_path]['content']
             f_path = self._sanitize_path(f_path)
-            content = nodes[f_path]['content']
             f_path = safe_str(f_path)
             # decoding here will force that we have proper encoded values
             # in any other case this will throw exceptions and deny commit
--- a/kallithea/model/user.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/model/user.py	Mon Mar 14 00:36:08 2016 +0100
@@ -38,7 +38,7 @@
 from sqlalchemy.exc import DatabaseError
 
 from kallithea import EXTERN_TYPE_INTERNAL
-from kallithea.lib.utils2 import safe_unicode, generate_api_key, get_current_authuser
+from kallithea.lib.utils2 import safe_str, generate_api_key, get_current_authuser
 from kallithea.lib.caching_query import FromCache
 from kallithea.model import BaseModel
 from kallithea.model.db import User, UserToPerm, Notification, \
@@ -100,8 +100,8 @@
         log_create_user(new_user.get_dict(), cur_user)
         return new_user
 
-    def create_or_update(self, username, password, email, firstname='',
-                         lastname='', active=True, admin=False,
+    def create_or_update(self, username, password, email, firstname=u'',
+                         lastname=u'', active=True, admin=False,
                          extern_type=None, extern_name=None, cur_user=None):
         """
         Creates a new instance if not found, or updates current one
@@ -148,9 +148,9 @@
             new_user.admin = admin
             new_user.email = email
             new_user.active = active
-            new_user.extern_name = safe_unicode(extern_name) \
+            new_user.extern_name = safe_str(extern_name) \
                 if extern_name else None
-            new_user.extern_type = safe_unicode(extern_type) \
+            new_user.extern_type = safe_str(extern_type) \
                 if extern_type else None
             new_user.name = firstname
             new_user.lastname = lastname
@@ -206,9 +206,9 @@
                                    type_=Notification.TYPE_REGISTRATION,
                                    email_kwargs=email_kwargs)
 
-    def update(self, user_id, form_data, skip_attrs=[]):
+    def update(self, user_id, form_data, skip_attrs=None):
         from kallithea.lib.auth import get_crypt_password
-
+        skip_attrs = skip_attrs or []
         user = self.get(user_id, cache=False)
         if user.username == User.DEFAULT_USER:
             raise DefaultUserException(
@@ -278,6 +278,11 @@
         from kallithea.lib.hooks import log_delete_user
         log_delete_user(user.get_dict(), cur_user)
 
+    def can_change_password(self, user):
+        from kallithea.lib import auth_modules
+        managed_fields = auth_modules.get_managed_fields(user)
+        return 'password' not in managed_fields
+
     def get_reset_password_token(self, user, timestamp, session_id):
         """
         The token is a 40-digit hexstring, calculated as a HMAC-SHA1.
@@ -332,18 +337,21 @@
         user = User.get_by_email(user_email)
         timestamp = int(time.time())
         if user is not None:
-            log.debug('password reset user %s found', user)
-            token = self.get_reset_password_token(user,
-                                                  timestamp,
-                                                  h.authentication_token())
-            # URL must be fully qualified; but since the token is locked to
-            # the current browser session, we must provide a URL with the
-            # current scheme and hostname, rather than the canonical_url.
-            link = h.url('reset_password_confirmation', qualified=True,
-                         email=user_email,
-                         timestamp=timestamp,
-                         token=token)
-
+            if self.can_change_password(user):
+                log.debug('password reset user %s found', user)
+                token = self.get_reset_password_token(user,
+                                                      timestamp,
+                                                      h.authentication_token())
+                # URL must be fully qualified; but since the token is locked to
+                # the current browser session, we must provide a URL with the
+                # current scheme and hostname, rather than the canonical_url.
+                link = h.url('reset_password_confirmation', qualified=True,
+                             email=user_email,
+                             timestamp=timestamp,
+                             token=token)
+            else:
+                log.debug('password reset user %s found but was managed', user)
+                token = link = None
             reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
             body = EmailNotificationModel().get_email_tmpl(
                 reg_type, 'txt',
@@ -397,6 +405,8 @@
         from kallithea.lib import auth
         user = User.get_by_email(user_email)
         if user is not None:
+            if not self.can_change_password(user):
+                raise Exception('trying to change password for external user')
             user.password = auth.get_crypt_password(new_passwd)
             Session().add(user)
             Session().commit()
@@ -415,7 +425,7 @@
         perm = self._get_perm(perm)
         user = self._get_user(user)
 
-        return UserToPerm.query().filter(UserToPerm.user == user)\
+        return UserToPerm.query().filter(UserToPerm.user == user) \
             .filter(UserToPerm.permission == perm).scalar() is not None
 
     def grant_perm(self, user, perm):
@@ -428,9 +438,9 @@
         user = self._get_user(user)
         perm = self._get_perm(perm)
         # if this permission is already granted skip it
-        _perm = UserToPerm.query()\
-            .filter(UserToPerm.user == user)\
-            .filter(UserToPerm.permission == perm)\
+        _perm = UserToPerm.query() \
+            .filter(UserToPerm.user == user) \
+            .filter(UserToPerm.permission == perm) \
             .scalar()
         if _perm:
             return
--- a/kallithea/model/user_group.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/model/user_group.py	Mon Mar 14 00:36:08 2016 +0100
@@ -28,10 +28,10 @@
 import traceback
 
 from kallithea.model import BaseModel
-from kallithea.model.db import UserGroupMember, UserGroup,\
-    UserGroupRepoToPerm, Permission, UserGroupToPerm, User, UserUserGroupToPerm,\
+from kallithea.model.db import UserGroupMember, UserGroup, \
+    UserGroupRepoToPerm, Permission, UserGroupToPerm, User, UserUserGroupToPerm, \
     UserGroupUserGroupToPerm
-from kallithea.lib.exceptions import UserGroupsAssignedException,\
+from kallithea.lib.exceptions import UserGroupsAssignedException, \
     RepoGroupAssignmentError
 
 log = logging.getLogger(__name__)
@@ -162,7 +162,7 @@
         user_group = self._get_user_group(user_group)
         try:
             # check if this group is not assigned to repo
-            assigned_groups = UserGroupRepoToPerm.query()\
+            assigned_groups = UserGroupRepoToPerm.query() \
                 .filter(UserGroupRepoToPerm.users_group == user_group).all()
             assigned_groups = [x.repository.repo_name for x in assigned_groups]
 
@@ -224,8 +224,8 @@
         user_group = self._get_user_group(user_group)
         perm = self._get_perm(perm)
 
-        return UserGroupToPerm.query()\
-            .filter(UserGroupToPerm.users_group == user_group)\
+        return UserGroupToPerm.query() \
+            .filter(UserGroupToPerm.users_group == user_group) \
             .filter(UserGroupToPerm.permission == perm).scalar() is not None
 
     def grant_perm(self, user_group, perm):
@@ -233,9 +233,9 @@
         perm = self._get_perm(perm)
 
         # if this permission is already granted skip it
-        _perm = UserGroupToPerm.query()\
-            .filter(UserGroupToPerm.users_group == user_group)\
-            .filter(UserGroupToPerm.permission == perm)\
+        _perm = UserGroupToPerm.query() \
+            .filter(UserGroupToPerm.users_group == user_group) \
+            .filter(UserGroupToPerm.permission == perm) \
             .scalar()
         if _perm:
             return
@@ -250,8 +250,8 @@
         user_group = self._get_user_group(user_group)
         perm = self._get_perm(perm)
 
-        obj = UserGroupToPerm.query()\
-            .filter(UserGroupToPerm.users_group == user_group)\
+        obj = UserGroupToPerm.query() \
+            .filter(UserGroupToPerm.users_group == user_group) \
             .filter(UserGroupToPerm.permission == perm).scalar()
         if obj is not None:
             self.sa.delete(obj)
@@ -272,9 +272,9 @@
         permission = self._get_perm(perm)
 
         # check if we have that permission already
-        obj = self.sa.query(UserUserGroupToPerm)\
-            .filter(UserUserGroupToPerm.user == user)\
-            .filter(UserUserGroupToPerm.user_group == user_group)\
+        obj = self.sa.query(UserUserGroupToPerm) \
+            .filter(UserUserGroupToPerm.user == user) \
+            .filter(UserUserGroupToPerm.user_group == user_group) \
             .scalar()
         if obj is None:
             # create new !
@@ -298,9 +298,9 @@
         user_group = self._get_user_group(user_group)
         user = self._get_user(user)
 
-        obj = self.sa.query(UserUserGroupToPerm)\
-            .filter(UserUserGroupToPerm.user == user)\
-            .filter(UserUserGroupToPerm.user_group == user_group)\
+        obj = self.sa.query(UserUserGroupToPerm) \
+            .filter(UserUserGroupToPerm.user == user) \
+            .filter(UserUserGroupToPerm.user_group == user_group) \
             .scalar()
         if obj is not None:
             self.sa.delete(obj)
@@ -323,9 +323,9 @@
                                            'assigned to itself' % target_user_group)
 
         # check if we have that permission already
-        obj = self.sa.query(UserGroupUserGroupToPerm)\
-            .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
-            .filter(UserGroupUserGroupToPerm.user_group == user_group)\
+        obj = self.sa.query(UserGroupUserGroupToPerm) \
+            .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group) \
+            .filter(UserGroupUserGroupToPerm.user_group == user_group) \
             .scalar()
         if obj is None:
             # create new !
@@ -347,9 +347,9 @@
         target_user_group = self._get_user_group(target_user_group)
         user_group = self._get_user_group(user_group)
 
-        obj = self.sa.query(UserGroupUserGroupToPerm)\
-            .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
-            .filter(UserGroupUserGroupToPerm.user_group == user_group)\
+        obj = self.sa.query(UserGroupUserGroupToPerm) \
+            .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group) \
+            .filter(UserGroupUserGroupToPerm.user_group == user_group) \
             .scalar()
         if obj is not None:
             self.sa.delete(obj)
@@ -375,7 +375,7 @@
         for gr in set(groups):
             existing_group = UserGroup.get_by_group_name(gr)
             if not existing_group:
-                desc = 'Automatically created from plugin:%s' % extern_type
+                desc = u'Automatically created from plugin:%s' % extern_type
                 # we use first admin account to set the owner of the group
                 existing_group = UserGroupModel().create(gr, desc, owner,
                                         group_data={'extern_type': extern_type})
--- a/kallithea/model/validators.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/model/validators.py	Mon Mar 14 00:36:08 2016 +0100
@@ -89,7 +89,8 @@
     return _UniqueListFromString
 
 
-def ValidUsername(edit=False, old_data={}):
+def ValidUsername(edit=False, old_data=None):
+    old_data = old_data or {}
     class _validator(formencode.validators.FancyValidator):
         messages = {
             'username_exists': _('Username "%(username)s" already exists'),
@@ -135,7 +136,7 @@
 
         def validate_python(self, value, state):
             try:
-                User.query().filter(User.active == True)\
+                User.query().filter(User.active == True) \
                     .filter(User.username == value).one()
             except sqlalchemy.exc.InvalidRequestError: # NoResultFound/MultipleResultsFound
                 msg = M(self, 'invalid_username', state, username=value)
@@ -146,7 +147,8 @@
     return _validator
 
 
-def ValidUserGroup(edit=False, old_data={}):
+def ValidUserGroup(edit=False, old_data=None):
+    old_data = old_data or {}
     class _validator(formencode.validators.FancyValidator):
         messages = {
             'invalid_group': _('Invalid user group name'),
@@ -187,7 +189,9 @@
     return _validator
 
 
-def ValidRepoGroup(edit=False, old_data={}):
+def ValidRepoGroup(edit=False, old_data=None):
+    old_data = old_data or {}
+
     class _validator(formencode.validators.FancyValidator):
         messages = {
             'group_parent_id': _('Cannot assign this group as parent'),
@@ -222,9 +226,9 @@
             if old_gname != group_name or not edit:
 
                 # check group
-                gr = RepoGroup.query()\
-                      .filter(RepoGroup.group_name == slug)\
-                      .filter(RepoGroup.group_parent_id == group_parent_id)\
+                gr = RepoGroup.query() \
+                      .filter(RepoGroup.group_name == slug) \
+                      .filter(RepoGroup.group_parent_id == group_parent_id) \
                       .scalar()
 
                 if gr is not None:
@@ -234,8 +238,8 @@
                     )
 
                 # check for same repo
-                repo = Repository.query()\
-                      .filter(Repository.repo_name == slug)\
+                repo = Repository.query() \
+                      .filter(Repository.repo_name == slug) \
                       .scalar()
 
                 if repo is not None:
@@ -309,7 +313,7 @@
             # authenticate returns unused dict but has called
             # plugin._authenticate which has create_or_update'ed the username user in db
             if auth_modules.authenticate(username, password) is None:
-                user = User.get_by_username(username)
+                user = User.get_by_username_or_email(username)
                 if user and not user.active:
                     log.warning('user %s is disabled', username)
                     msg = M(self, 'invalid_auth', state)
@@ -338,7 +342,9 @@
     return _validator
 
 
-def ValidRepoName(edit=False, old_data={}):
+def ValidRepoName(edit=False, old_data=None):
+    old_data = old_data or {}
+
     class _validator(formencode.validators.FancyValidator):
         messages = {
             'invalid_repo_name':
@@ -373,7 +379,6 @@
             return value
 
         def validate_python(self, value, state):
-
             repo_name = value.get('repo_name')
             repo_name_full = value.get('repo_name_full')
             group_path = value.get('group_path')
@@ -483,7 +488,9 @@
     return _validator
 
 
-def ValidForkType(old_data={}):
+def ValidForkType(old_data=None):
+    old_data = old_data or {}
+
     class _validator(formencode.validators.FancyValidator):
         messages = {
             'invalid_fork_type': _('Fork has to be the same type as parent')
@@ -644,12 +651,12 @@
             for k, v, t in perms_new:
                 try:
                     if t is 'user':
-                        self.user_db = User.query()\
-                            .filter(User.active == True)\
+                        self.user_db = User.query() \
+                            .filter(User.active == True) \
                             .filter(User.username == k).one()
                     if t is 'users_group':
-                        self.user_db = UserGroup.query()\
-                            .filter(UserGroup.users_group_active == True)\
+                        self.user_db = UserGroup.query() \
+                            .filter(UserGroup.users_group_active == True) \
                             .filter(UserGroup.users_group_name == k).one()
 
                 except Exception:
@@ -699,7 +706,9 @@
     return _validator
 
 
-def UniqSystemEmail(old_data={}):
+def UniqSystemEmail(old_data=None):
+    old_data = old_data or {}
+
     class _validator(formencode.validators.FancyValidator):
         messages = {
             'email_taken': _('This email address is already in use')
@@ -710,7 +719,7 @@
 
         def validate_python(self, value, state):
             if (old_data.get('email') or '').lower() != value:
-                user = User.get_by_email(value, case_insensitive=True)
+                user = User.get_by_email(value)
                 if user is not None:
                     msg = M(self, 'email_taken', state)
                     raise formencode.Invalid(msg, value, state,
@@ -729,7 +738,7 @@
             return value.lower()
 
         def validate_python(self, value, state):
-            user = User.get_by_email(value, case_insensitive=True)
+            user = User.get_by_email(value)
             if user is None:
                 msg = M(self, 'non_existing_email', state, email=value)
                 raise formencode.Invalid(msg, value, state,
--- a/kallithea/public/css/contextbar.css	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/public/css/contextbar.css	Mon Mar 14 00:36:08 2016 +0100
@@ -71,6 +71,10 @@
     vertical-align: text-bottom;
 }
 
+#content #context-bar li span {
+    margin: 0;
+}
+
 ul.horizontal-list {
     display: block;
 }
--- a/kallithea/public/css/style.css	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/public/css/style.css	Mon Mar 14 00:36:08 2016 +0100
@@ -208,6 +208,21 @@
     color: #B9B9B9;
 }
 
+.inline-comments-general.show-general-status .hidden.general-only {
+    display: block !important;
+}
+
+/* Bootstrap compatible */
+.show {
+    display: block !important;
+}
+.hidden {
+    display: none !important;
+}
+.invisible {
+    visibility: hidden;
+}
+
 .truncate {
        white-space: nowrap;
        overflow: hidden;
@@ -522,10 +537,6 @@
     margin-bottom: -2px;
 }
 
-td.quick_repo_menu .menu_items.hidden {
-    display: none;
-}
-
 .dt_repo {
     white-space: nowrap;
     color: #577632;
@@ -946,7 +957,7 @@
 }
 
 #content div.box div.message {
-    clear: both;
+    float: left;
     overflow: hidden;
     margin: 0;
     padding: 5px 0;
@@ -3440,10 +3451,6 @@
     color: #aaa;
 }
 
-.btn.blue.hidden {
-    display: none;
-}
-
 .btn.active {
     font-weight: bold;
 }
@@ -4131,14 +4138,11 @@
 
 .comments .comment .text {
     background-color: #FAFAFA;
-}
-.comment .text div.rst-block p {
-    margin: 0.5em 0px !important;
+    margin: 6px;
 }
 
 .comments-number {
-    margin: 5px;
-    padding: 0px 0px 10px 0px;
+    padding: 0px 20px 10px;
     font-weight: bold;
     color: #666;
     font-size: 16px;
@@ -4190,19 +4194,6 @@
     padding: 5px 5px 5px 5px;
     color: #666;
 }
-.comment-form .comment-help .preview-btn,
-.comment-form .comment-help .edit-btn {
-    float: right;
-    margin: -6px 0px 0px 0px;
-}
-
-.comment-form .preview-box.unloaded,
-.comment-inline-form .preview-box.unloaded {
-    height: 50px;
-    text-align: center;
-    padding: 20px;
-    background-color: #fafafa;
-}
 
 .comment-form .comment-button {
     padding-top: 5px;
@@ -4219,7 +4210,6 @@
     margin: -1px 0px 0px 0px;
 }
 
-
 .show-inline-comments {
     position: relative;
     top: 1px
@@ -4279,12 +4269,6 @@
     color: #666;
 }
 
-.comment-inline-form .comment-help .preview-btn,
-.comment-inline-form .comment-help .edit-btn {
-    float: right;
-    margin: -6px 0px 0px 0px;
-}
-
 .comment-inline-form .comment-button {
     padding-top: 5px;
 }
@@ -4294,13 +4278,6 @@
     padding: 0 20px;
 }
 
-.inline-comments div.rst-block {
-    clear: both;
-    overflow: hidden;
-    margin: 0;
-    padding: 0 20px 0px;
-}
-
 .inline-comments .comment .comment-wrapp {
     max-width: 978px;
     border: 1px solid #ddd;
@@ -4338,6 +4315,7 @@
 
 .inline-comments .comment .text {
     background-color: #FAFAFA;
+    margin: 6px;
 }
 
 .inline-comments .comments-number {
@@ -4346,9 +4324,6 @@
     color: #666;
     font-size: 16px;
 }
-.inline-comments-button .add-comment {
-    margin: 2px 0px 8px 5px !important
-}
 
 input.status_change_radio {
     margin: 2px 0 5px 15px;
@@ -4552,9 +4527,17 @@
 
 }
 
-.diff-container.hidden {
-    display: none;
-    overflow: hidden;
+.compare-revision-selector {
+    font-weight: bold;
+    font-size: 14px;
+}
+.compare-revision-selector > div {
+    display: inline-block;
+    margin-left: 8px;
+    vertical-align: middle;
+}
+.compare-revision-selector .btn {
+    margin-bottom: 0;
 }
 
 
@@ -4572,6 +4555,7 @@
 div.diffblock.margined {
     margin: 0px 20px 0px 20px;
 }
+.compare-revision-selector,
 div.diffblock .code-header {
     border-bottom: 1px solid #CCCCCC;
     background: #EEEEEE;
--- a/kallithea/public/js/base.js	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/public/js/base.js	Mon Mar 14 00:36:08 2016 +0100
@@ -377,15 +377,16 @@
         ;
 };
 
-var ajaxGET = function(url,success) {
+var ajaxGET = function(url, success, failure) {
+    if(failure === undefined) {
+        failure = function(jqXHR, textStatus, errorThrown) {
+                if (textStatus != "abort")
+                    alert("Ajax GET error: " + textStatus);
+            };
+    }
     return $.ajax({url: url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
         .done(success)
-        .fail(function(jqXHR, textStatus, errorThrown) {
-                if (textStatus == "abort")
-                    return;
-                alert("Ajax GET error: " + textStatus);
-        })
-        ;
+        .fail(failure);
 };
 
 var ajaxPOST = function(url, postData, success, failure) {
@@ -621,9 +622,13 @@
         var line_no = $anchorcomment.data('line_no');
         if ($comment_div[0]) {
             $comment_div.append($anchorcomment.children());
-            _comment_div_append_add($comment_div, f_path, line_no);
+            if (f_path && line_no) {
+                _comment_div_append_add($comment_div, f_path, line_no);
+            } else {
+                _comment_div_append_form($comment_div, f_path, line_no);
+            }
         } else {
-           $anchorcomment.before("Comment to {0} line {1} which is outside the diff context:".format(f_path || '?', line_no || '?'));
+            $anchorcomment.before("Comment to {0} line {1} which is outside the diff context:".format(f_path || '?', line_no || '?'));
         }
     });
     linkInlineComments($('.firstlink'), $('.comment:first-child'));
@@ -652,20 +657,19 @@
     return $comments_box;
 }
 
-// set $comment_div state - showing or not showing form and Add button
-function comment_div_state($comment_div, f_path, line_no, show_form) {
+// Set $comment_div state - showing or not showing form and Add button.
+// An Add button is shown on non-empty forms when no form is shown.
+// The form is controlled by show_form - if undefined, form is only shown for general comments.
+function comment_div_state($comment_div, f_path, line_no, show_form_opt) {
+    var show_form = show_form_opt !== undefined ? show_form_opt : !f_path && !line_no;
     var $forms = $comment_div.children('.comment-inline-form');
     var $buttonrow = $comment_div.children('.add-button-row');
     var $comments = $comment_div.children('.comment');
+    $forms.remove();
+    $buttonrow.remove();
     if (show_form) {
-        if (!$forms.length) {
-            _comment_div_append_form($comment_div, f_path, line_no);
-        }
-    } else {
-        $forms.remove();
-    }
-    $buttonrow.remove();
-    if ($comments.length && !show_form) {
+        _comment_div_append_form($comment_div, f_path, line_no);
+    } else if ($comments.length) {
         _comment_div_append_add($comment_div, f_path, line_no);
     }
 }
@@ -682,72 +686,79 @@
 
 // append a comment form to $comment_div
 function _comment_div_append_form($comment_div, f_path, line_no) {
-    var $form_div = $($('#comment-inline-form-template').html().format(f_path, line_no))
+    var $form_div = $('#comment-inline-form-template').children()
+        .clone()
         .addClass('comment-inline-form');
     $comment_div.append($form_div);
     var $form = $comment_div.find("form");
+    var $textarea = $form.find('textarea');
+    var $mentions_container = $form.find('div.mentions-container');
 
     $form.submit(function(e) {
         e.preventDefault();
 
-        var text = $('#text_'+line_no).val();
-        if (!text){
-            return;
+        var text = $textarea.val();
+        var review_status = $form.find('input:radio[name=changeset_status]:checked').val();
+        var pr_close = $form.find('input:checkbox[name=save_close]:checked').length ? 'on' : '';
+        var pr_delete = $form.find('input:checkbox[name=save_delete]:checked').length ? 'delete' : '';
+
+        if (!text && !review_status && !pr_close && !pr_delete) {
+            alert("Please provide a comment");
+            return false;
+        }
+
+        if (pr_delete) {
+            if (text || review_status || pr_close) {
+                alert('Cannot delete pull request while making other changes');
+                return false;
+            }
+            if (!confirm('Confirm to delete this pull request')) {
+                return false;
+            }
+            var comments = $('.comment').size();
+            if (comments > 0 &&
+                !confirm('Confirm again to delete this pull request with {0} comments'.format(comments))) {
+                return false;
+            }
         }
 
         $form.find('.submitting-overlay').show();
 
-        var success = function(json_data) {
-            $comment_div.append(json_data['rendered_text']);
-            comment_div_state($comment_div, f_path, line_no, false);
-            linkInlineComments($('.firstlink'), $('.comment:first-child'));
-        };
         var postData = {
             'text': text,
             'f_path': f_path,
-            'line': line_no
+            'line': line_no,
+            'changeset_status': review_status,
+            'save_close': pr_close,
+            'save_delete': pr_delete
+        };
+        var success = function(json_data) {
+            if (pr_delete) {
+                location = json_data['location'];
+            } else {
+                $comment_div.append(json_data['rendered_text']);
+                comment_div_state($comment_div, f_path, line_no);
+                linkInlineComments($('.firstlink'), $('.comment:first-child'));
+                if ((review_status || pr_close) && !f_path && !line_no) {
+                    // Page changed a lot - reload it after closing the submitted form
+                    comment_div_state($comment_div, f_path, line_no, false);
+                    location.reload(true);
+                }
+            }
         };
         ajaxPOST(AJAX_COMMENT_URL, postData, success);
     });
 
-    $('#preview-btn_'+line_no).click(function(e){
-        var text = $('#text_'+line_no).val();
-        if(!text){
-            return
-        }
-        $('#preview-box_'+line_no).addClass('unloaded');
-        $('#preview-box_'+line_no).html(_TM['Loading ...']);
-        $('#edit-container_'+line_no).hide();
-        $('#edit-btn_'+line_no).show();
-        $('#preview-container_'+line_no).show();
-        $('#preview-btn_'+line_no).hide();
-
-        var url = pyroutes.url('changeset_comment_preview', {'repo_name': REPO_NAME});
-        var post_data = {'text': text};
-        ajaxPOST(url, post_data, function(html) {
-            $('#preview-box_'+line_no).html(html);
-            $('#preview-box_'+line_no).removeClass('unloaded');
-        })
-    })
-    $('#edit-btn_'+line_no).click(function(e) {
-        $('#edit-container_'+line_no).show();
-        $('#edit-btn_'+line_no).hide();
-        $('#preview-container_'+line_no).hide();
-        $('#preview-btn_'+line_no).show();
-    })
-
     // create event for hide button
     $form.find('.hide-inline-form').click(function(e) {
-        comment_div_state($comment_div, f_path, line_no, false);
+        comment_div_state($comment_div, f_path, line_no);
     });
 
-    setTimeout(function() {
-        // callbacks
-        tooltip_activate();
-        MentionsAutoComplete($('#text_'+line_no), $('#mentions_container_'+line_no),
-                             _USERS_AC_DATA);
-        $('#text_'+line_no).focus();
-    }, 10);
+    tooltip_activate();
+    MentionsAutoComplete($textarea, $mentions_container, _USERS_AC_DATA);
+    if (f_path) {
+        $textarea.focus();
+    }
 }
 
 
@@ -913,14 +924,14 @@
             $('#editor_container').show();
             $('#upload_file_container').hide();
             $('#filename_container').show();
-            $('#set_mode_header').show();
+            $('#mimetype_header').show();
         });
 
     $('#upload_file_enable').click(function(){
             $('#editor_container').hide();
             $('#upload_file_container').show();
             $('#filename_container').hide();
-            $('#set_mode_header').hide();
+            $('#mimetype_header').hide();
         });
 
     return myCodeMirror
@@ -1859,8 +1870,8 @@
             }
 
             // Put prefix matches before matches in the line
-            var aPos = a.text.indexOf(query.term),
-                bPos = b.text.indexOf(query.term);
+            var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
+                bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
             if (aPos === 0 && bPos !== 0) {
                 return -1;
             }
@@ -1881,19 +1892,33 @@
     return results;
 };
 
-// global hooks after DOM is loaded
+var prefixFirstSort = function(results, container, query) {
+    if (query.term) {
+        return results.sort(function (a, b) {
+            // if parent node, no sorting
+            if (a.children != undefined || b.children != undefined) {
+                return 0;
+            }
 
-$(document).ready(function(){
-    $('.diff-collapse-button').click(function(e) {
-        var $button = $(e.currentTarget);
-        var $target = $('#' + $button.attr('target'));
-        if($target.hasClass('hidden')){
-            $target.removeClass('hidden');
-            $button.html("&uarr; {0} &uarr;".format(_TM['Collapse Diff']));
-        }
-        else if(!$target.hasClass('hidden')){
-            $target.addClass('hidden');
-            $button.html("&darr; {0} &darr;".format(_TM['Expand Diff']));
-        }
-    });
-});
+            // Put prefix matches before matches in the line
+            var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
+                bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
+            if (aPos === 0 && bPos !== 0) {
+                return -1;
+            }
+            if (bPos === 0 && aPos !== 0) {
+                return 1;
+            }
+
+            // Default sorting
+            if (a.text > b.text) {
+                return 1;
+            }
+            if (a.text < b.text) {
+                return -1;
+            }
+            return 0;
+        });
+    }
+    return results;
+};
--- a/kallithea/templates/about.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/about.html	Mon Mar 14 00:36:08 2016 +0100
@@ -30,14 +30,20 @@
   <li>Copyright &copy; 2012&ndash;2016, Mads Kiilerich</li>
   <li>Copyright &copy; 2012&ndash;2016, Takumi IINO</li>
   <li>Copyright &copy; 2012&ndash;2016, Unity Technologies</li>
+  <li>Copyright &copy; 2012, 2014&ndash;2016, Andrew Shadura</li>
+  <li>Copyright &copy; 2014&ndash;2016, Thomas De Schampheleire</li>
+  <li>Copyright &copy; 2015&ndash;2016, Jan Heylen</li>
+  <li>Copyright &copy; 2015&ndash;2016, Robert Rauch</li>
+  <li>Copyright &copy; 2015&ndash;2016, Søren Løvborg</li>
+  <li>Copyright &copy; 2016, Angel Ezquerra</li>
+  <li>Copyright &copy; 2016, Asterios Dimitriou</li>
   <li>Copyright &copy; 2016, Robert James Dennington</li>
-  <li>Copyright &copy; 2012, 2014&ndash;2015, Andrew Shadura</li>
   <li>Copyright &copy; 2012&ndash;2013, 2015, Aras Pranckevičius</li>
+  <li>Copyright &copy; 2012, 2014&ndash;2015, Dominik Ruf</li>
   <li>Copyright &copy; 2014&ndash;2015, Christian Oyarzun</li>
   <li>Copyright &copy; 2014&ndash;2015, Joseph Rivera</li>
   <li>Copyright &copy; 2014&ndash;2015, Michal Čihař</li>
   <li>Copyright &copy; 2014&ndash;2015, Sean Farley</li>
-  <li>Copyright &copy; 2014&ndash;2015, Thomas De Schampheleire</li>
   <li>Copyright &copy; 2015, Anatoly Bubenkov</li>
   <li>Copyright &copy; 2015, Andrew Bartlett</li>
   <li>Copyright &copy; 2015, Balázs Úr</li>
@@ -50,7 +56,7 @@
   <li>Copyright &copy; 2015, EriCSN Chang</li>
   <li>Copyright &copy; 2015, Étienne Gilli</li>
   <li>Copyright &copy; 2015, Grzegorz Krason</li>
-  <li>Copyright &copy; 2015, Jan Heylen</li>
+  <li>Copyright &copy; 2015, Jiří Suchan</li>
   <li>Copyright &copy; 2015, Kazunari Kobayashi</li>
   <li>Copyright &copy; 2015, Kevin Bullock</li>
   <li>Copyright &copy; 2015, kobanari</li>
@@ -64,13 +70,11 @@
   <li>Copyright &copy; 2015, Niemand Jedermann</li>
   <li>Copyright &copy; 2015, Peter Vitt</li>
   <li>Copyright &copy; 2015, Robert Martinez</li>
-  <li>Copyright &copy; 2015, Robert Rauch</li>
   <li>Copyright &copy; 2015, Ronny Pfannschmidt</li>
   <li>Copyright &copy; 2015, Sam Jaques</li>
-  <li>Copyright &copy; 2015, Søren Løvborg</li>
   <li>Copyright &copy; 2015, Tuux</li>
   <li>Copyright &copy; 2015, Viktar Palstsiuk</li>
-  <li>Copyright &copy; 2012, 2014, Dominik Ruf</li>
+  <li>Copyright &copy; 2014, Ante Ilic</li>
   <li>Copyright &copy; 2014, Bradley M. Kuhn</li>
   <li>Copyright &copy; 2014, Calinou</li>
   <li>Copyright &copy; 2014, Daniel Anderson</li>
--- a/kallithea/templates/admin/gists/edit.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/admin/gists/edit.html	Mon Mar 14 00:36:08 2016 +0100
@@ -81,62 +81,63 @@
 
                 ## dynamic edit box.
                 <script type="text/javascript">
-                var myCodeMirror = initCodeMirror("editor_${h.FID('f',file.path)}", "${request.script_name}", '');
+                    $(document).ready(function(){
+                        var myCodeMirror = initCodeMirror("editor_${h.FID('f',file.path)}", "${request.script_name}", '');
+
+                        //inject new modes
+                        var $mimetype_select = $('#mimetype_${h.FID('f',file.path)}');
+                        $mimetype_select.each(function(){
+                            var modes_select = this;
+                            var index = 1;
+                            for(var i=0;i<CodeMirror.modeInfo.length;i++) {
+                                var m = CodeMirror.modeInfo[i];
+                                var opt = new Option(m.name, m.mime);
+                                $(opt).attr('mode', m.mode);
+                                if (m.mime == 'text/plain') {
+                                    // default plain text
+                                    $(opt).prop('selected', true);
+                                    modes_select.options[0] = opt;
+                                } else {
+                                    modes_select.options[index++] = opt;
+                                }
+                            }
+                        });
 
-                //inject new modes
-                var $modes_select = $('#mimetype_${h.FID('f',file.path)}');
-                $modes_select.each(function(){
-                    var modes_select = this;
-                    var index = 1;
-                    for(var i=0;i<CodeMirror.modeInfo.length;i++) {
-                        var m = CodeMirror.modeInfo[i];
-                        var opt = new Option(m.name, m.mime);
-                        $(opt).attr('mode', m.mode);
-                        if (m.mime == 'text/plain') {
-                            // default plain text
-                            $(opt).prop('selected', true);
-                            modes_select.options[0] = opt;
-                        } else {
-                            modes_select.options[index++] = opt;
-                        }
-                    }
-                });
+                        var $filename_input = $('#filename_${h.FID('f',file.path)}');
+                        // on select change set new mode
+                        $mimetype_select.change(function(e){
+                            var selected = e.currentTarget;
+                            var node = selected.options[selected.selectedIndex];
+                            var detected_mode = CodeMirror.findModeByMIME(node.value);
+                            setCodeMirrorMode(myCodeMirror, detected_mode);
+
+                            var proposed_ext = CodeMirror.findExtensionByMode(detected_mode);
+                            var file_data = CodeMirror.getFilenameAndExt($filename_input.val());
+                            var filename = file_data['filename'] || 'filename1';
+                            $filename_input.val(filename + '.' + proposed_ext);
+                        });
 
-                var $filename = $('#filename_${h.FID('f',file.path)}');
-                // on select change set new mode
-                $modes_select.change(function(e){
-                    var selected = e.currentTarget;
-                    var node = selected.options[selected.selectedIndex];
-                    var detected_mode = CodeMirror.findModeByMIME(node.value);
-                    setCodeMirrorMode(myCodeMirror, detected_mode);
+                        // on type the new filename set mode
+                        $filename_input.keyup(function(e){
+                            var file_data = CodeMirror.getFilenameAndExt(this.value);
+                            if(file_data['ext'] != null){
+                                var detected_mode = CodeMirror.findModeByExtension(file_data['ext']) || CodeMirror.findModeByMIME('text/plain');
 
-                    var proposed_ext = CodeMirror.findExtensionByMode(detected_mode);
-                    var file_data = CodeMirror.getFilenameAndExt($filename.val());
-                    var filename = file_data['filename'] || 'filename1';
-                    $filename.val(filename + '.' + proposed_ext);
-                });
+                                if (detected_mode){
+                                    setCodeMirrorMode(myCodeMirror, detected_mode);
+                                    $mimetype_select.val(detected_mode.mime);
+                                }
+                            }
+                        });
 
-                // on type the new filename set mode
-                $filename.keyup(function(e){
-                    var file_data = CodeMirror.getFilenameAndExt(this.value);
-                    if(file_data['ext'] != null){
-                        var detected_mode = CodeMirror.findModeByExtension(file_data['ext']) || CodeMirror.findModeByMIME('text/plain');
+                        // set mode on page load
+                        var detected_mode = CodeMirror.findModeByExtension("${file.extension}");
 
                         if (detected_mode){
                             setCodeMirrorMode(myCodeMirror, detected_mode);
-                            $modes_select.val(detected_mode.mime);
+                            $mimetype_select.val(detected_mode.mime);
                         }
-                    }
-                });
-
-                // set mode on page load
-                var detected_mode = CodeMirror.findModeByExtension("${file.extension}");
-
-                if (detected_mode){
-                    setCodeMirrorMode(myCodeMirror, detected_mode);
-                    $modes_select.val(detected_mode.mime);
-                }
-
+                    });
                 </script>
 
             %endfor
--- a/kallithea/templates/admin/gists/new.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/admin/gists/new.html	Mon Mar 14 00:36:08 2016 +0100
@@ -59,51 +59,53 @@
             </div>
           ${h.end_form()}
           <script type="text/javascript">
-            var myCodeMirror = initCodeMirror('editor', "${request.script_name}", '');
+            $(document).ready(function(){
+                var myCodeMirror = initCodeMirror('editor', "${request.script_name}", '');
 
-            //inject new modes
-            var $modes_select = $('#mimetype');
-            $modes_select.each(function(){
-                var modes_select = this;
-                var index = 1;
-                for(var i=0;i<CodeMirror.modeInfo.length;i++) {
-                    var m = CodeMirror.modeInfo[i];
-                    var opt = new Option(m.name, m.mime);
-                    $(opt).attr('mode', m.mode);
-                    if (m.mime == 'text/plain') {
-                        // default plain text
-                        $(opt).prop('selected', true);
-                        modes_select.options[0] = opt;
-                    } else {
-                        modes_select.options[index++] = opt;
+                //inject new modes
+                var $mimetype_select = $('#mimetype');
+                $mimetype_select.each(function(){
+                    var modes_select = this;
+                    var index = 1;
+                    for(var i=0;i<CodeMirror.modeInfo.length;i++) {
+                        var m = CodeMirror.modeInfo[i];
+                        var opt = new Option(m.name, m.mime);
+                        $(opt).attr('mode', m.mode);
+                        if (m.mime == 'text/plain') {
+                            // default plain text
+                            $(opt).prop('selected', true);
+                            modes_select.options[0] = opt;
+                        } else {
+                            modes_select.options[index++] = opt;
+                        }
                     }
-                }
-            });
+                });
 
-            var $filename = $('#filename');
-            // on select change set new mode
-            $modes_select.change(function(e){
-                var selected = e.currentTarget;
-                var node = selected.options[selected.selectedIndex];
-                var detected_mode = CodeMirror.findModeByMIME(node.value);
-                setCodeMirrorMode(myCodeMirror, detected_mode);
+                var $filename_input = $('#filename');
+                // on select change set new mode
+                $mimetype_select.change(function(e){
+                    var selected = e.currentTarget;
+                    var node = selected.options[selected.selectedIndex];
+                    var detected_mode = CodeMirror.findModeByMIME(node.value);
+                    setCodeMirrorMode(myCodeMirror, detected_mode);
 
-                var proposed_ext = CodeMirror.findExtensionByMode(detected_mode);
-                var file_data = CodeMirror.getFilenameAndExt($filename.val());
-                var filename = file_data['filename'] || 'filename1';
-                $filename.val(filename + '.' + proposed_ext);
-            });
+                    var proposed_ext = CodeMirror.findExtensionByMode(detected_mode);
+                    var file_data = CodeMirror.getFilenameAndExt($filename_input.val());
+                    var filename = file_data['filename'] || 'filename1';
+                    $filename_input.val(filename + '.' + proposed_ext);
+                });
 
-            // on type the new filename set mode
-            $filename.keyup(function(e){
-                var file_data = CodeMirror.getFilenameAndExt(this.value);
-                if(file_data['ext'] != null){
-                    var detected_mode = CodeMirror.findModeByExtension(file_data['ext']) || CodeMirror.findModeByMIME('text/plain');
-                    if (detected_mode){
-                        setCodeMirrorMode(myCodeMirror, detected_mode);
-                        $modes_select.val(detected_mode.mime);
+                // on type the new filename set mode
+                $filename_input.keyup(function(e){
+                    var file_data = CodeMirror.getFilenameAndExt(this.value);
+                    if(file_data['ext'] != null){
+                        var detected_mode = CodeMirror.findModeByExtension(file_data['ext']) || CodeMirror.findModeByMIME('text/plain');
+                        if (detected_mode){
+                            setCodeMirrorMode(myCodeMirror, detected_mode);
+                            $mimetype_select.val(detected_mode.mime);
+                        }
                     }
-                }
+                });
             });
           </script>
         </div>
--- a/kallithea/templates/admin/notifications/show_notification.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/admin/notifications/show_notification.html	Mon Mar 14 00:36:08 2016 +0100
@@ -37,7 +37,7 @@
         <div class="notification-body">
         <div class="notification-subject">${h.literal(c.notification.subject)}</div>
         %if c.notification.body:
-            ${h.rst_w_mentions(c.notification.body)}
+            ${h.render_w_mentions(c.notification.body)}
         %endif
         </div>
       </div>
--- a/kallithea/templates/admin/repo_groups/repo_group_edit_perms.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/admin/repo_groups/repo_group_edit_perms.html	Mon Mar 14 00:36:08 2016 +0100
@@ -75,7 +75,7 @@
                 %endfor
 
                 <%
-                _tmpl = h.literal("""' \
+                _tmpl = h.literal("""'\
                     <td><input type="radio" value="group.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
                     <td><input type="radio" value="group.read" checked="checked" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
                     <td><input type="radio" value="group.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
--- a/kallithea/templates/admin/repos/repo_edit_caches.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/admin/repos/repo_edit_caches.html	Mon Mar 14 00:36:08 2016 +0100
@@ -1,7 +1,7 @@
 ${h.form(url('edit_repo_caches', repo_name=c.repo_name), method='put')}
 <div class="form">
    <div class="fields">
-       ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate Repository Cache'),class_="btn btn-small",onclick="return confirm('"+_('Confirm to invalidate repository cache.')+"');")}
+       ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate Repository Cache'),class_="btn btn-small")}
       <div class="field" style="border:none;color:#888">
       <ul>
           <li>${_('Manually invalidate cache for this repository. On first access, the repository will be cached again.')}
--- a/kallithea/templates/admin/repos/repo_edit_permissions.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/admin/repos/repo_edit_permissions.html	Mon Mar 14 00:36:08 2016 +0100
@@ -72,7 +72,7 @@
                 %endfor
 
                 <%
-                _tmpl = h.literal("""' \
+                _tmpl = h.literal("""'\
                     <td><input type="radio" value="repository.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
                     <td><input type="radio" value="repository.read" checked="checked" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
                     <td><input type="radio" value="repository.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
--- a/kallithea/templates/admin/user_groups/user_group_edit_perms.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/admin/user_groups/user_group_edit_perms.html	Mon Mar 14 00:36:08 2016 +0100
@@ -75,7 +75,7 @@
                 %endfor
 
                 <%
-                _tmpl = h.literal("""' \
+                _tmpl = h.literal("""'\
                     <td><input type="radio" value="usergroup.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
                     <td><input type="radio" value="usergroup.read" checked="checked" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
                     <td><input type="radio" value="usergroup.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
--- a/kallithea/templates/base/base.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/base/base.html	Mon Mar 14 00:36:08 2016 +0100
@@ -127,20 +127,17 @@
       </div>
       -->
       <ul id="context-pages" class="horizontal-list">
-        <li ${is_current('summary')}><a href="${h.url('summary_home', repo_name=c.repo_name)}"><i class="icon-doc-text"></i> ${_('Summary')}</a></li>
+        <li ${is_current('summary')} data-context="summary"><a href="${h.url('summary_home', repo_name=c.repo_name)}"><i class="icon-doc-text"></i> ${_('Summary')}</a></li>
         %if rev:
-        <li ${is_current('changelog')}><a href="${h.url('changelog_file_home', repo_name=c.repo_name, revision=rev, f_path='')}"><i class="icon-clock"></i> ${_('Changelog')}</a></li>
+        <li ${is_current('changelog')} data-context="changelog"><a href="${h.url('changelog_file_home', repo_name=c.repo_name, revision=rev, f_path='')}"><i class="icon-clock"></i> ${_('Changelog')}</a></li>
         %else:
-        <li ${is_current('changelog')}><a href="${h.url('changelog_home', repo_name=c.repo_name)}"><i class="icon-clock"></i> ${_('Changelog')}</a></li>
+        <li ${is_current('changelog')} data-context="changelog"><a href="${h.url('changelog_home', repo_name=c.repo_name)}"><i class="icon-clock"></i> ${_('Changelog')}</a></li>
         %endif
-        <li ${is_current('files')}><a href="${h.url('files_home', repo_name=c.repo_name, revision=rev or 'tip')}"><i class="icon-doc-inv"></i> ${_('Files')}</a></li>
-        <li ${is_current('switch-to')}>
-          <a href="#" id="branch_tag_switcher_2" class="dropdown"><i class="icon-exchange"></i> ${_('Switch To')}</a>
-          <ul id="switch_to_list_2" class="switch_to submenu">
-            <li><a href="#">${_('Loading...')}</a></li>
-          </ul>
+        <li ${is_current('files')} data-context="files"><a href="${h.url('files_home', repo_name=c.repo_name, revision=rev or 'tip')}"><i class="icon-doc-inv"></i> ${_('Files')}</a></li>
+        <li ${is_current('switch-to')} data-context="switch-to">
+          <input id="branch_switcher" name="branch_switcher" type="hidden">
         </li>
-        <li ${is_current('options')}>
+        <li ${is_current('options')} data-context="options">
              %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
                <a href="${h.url('edit_repo',repo_name=c.repo_name)}" class="dropdown"><i class="icon-wrench"></i> ${_('Options')}</a>
              %else:
@@ -179,7 +176,7 @@
               %endif
              </ul>
         </li>
-        <li ${is_current('showpullrequest')}>
+        <li ${is_current('showpullrequest')} data-context="showpullrequest">
           <a href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}"> <i class="icon-git-pull-request"></i> ${_('Pull Requests')}
             %if c.repository_pull_requests:
               <span>${c.repository_pull_requests}</span>
@@ -189,15 +186,102 @@
       </ul>
   </div>
   <script type="text/javascript">
-      YUE.on('branch_tag_switcher_2','mouseover',function(){
-         var $branch_tag_switcher_2 = $('#branch_tag_switcher_2');
-         var loaded = $branch_tag_switcher_2.hasClass('loaded');
-         if(!loaded){
-             $branch_tag_switcher_2.addClass('loaded');
-             asynchtml("${h.url('branch_tag_switcher',repo_name=c.repo_name)}", $('#switch_to_list_2'));
-         }
-         return false;
+    $(document).ready(function() {
+      var bcache = {};
+
+      $("#branch_switcher").select2({
+          placeholder: '<i class="icon-exchange"></i> ${_('Switch To')}',
+          dropdownAutoWidth: true,
+          sortResults: prefixFirstSort,
+          formatResult: function(obj) {
+              return obj.text;
+          },
+          formatSelection: function(obj) {
+              return obj.text;
+          },
+          formatNoMatches: function(term) {
+              return "${_('No matches found')}";
+          },
+          escapeMarkup: function(m) {
+              // don't escape our custom placeholder
+              if (m.substr(0, 29) == '<i class="icon-exchange"></i>') {
+                  return m;
+              }
+
+              return Select2.util.escapeMarkup(m);
+          },
+          containerCssClass: "repo-switcher",
+          dropdownCssClass: "repo-switcher-dropdown",
+          query: function(query) {
+              var key = 'cache';
+              var cached = bcache[key];
+              if (cached) {
+                  var data = {
+                      results: []
+                  };
+                  // filter results
+                  $.each(cached.results, function() {
+                      var section = this.text;
+                      var children = [];
+                      $.each(this.children, function() {
+                          if (query.term.length === 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
+                              children.push({
+                                  'id': this.id,
+                                  'text': this.text,
+                                  'type': this.type,
+                                  'obj': this.obj
+                              });
+                          }
+                      });
+                      if (children.length !== 0) {
+                          data.results.push({
+                              'text': section,
+                              'children': children
+                          });
+                      }
+
+                  });
+                  query.callback(data);
+              } else {
+                  $.ajax({
+                      url: pyroutes.url('repo_refs_data', {
+                          'repo_name': '${c.repo_name}'
+                      }),
+                      data: {},
+                      dataType: 'json',
+                      type: 'GET',
+                      success: function(data) {
+                          bcache[key] = data;
+                          query.callback(data);
+                      }
+                  });
+              }
+          }
       });
+
+      $("#branch_switcher").on('select2-selecting', function(e) {
+          e.preventDefault();
+          var context = $('#context-bar .current').data('context');
+          if (context == 'files') {
+              window.location = pyroutes.url('files_home', {
+                  'repo_name': REPO_NAME,
+                  'revision': e.choice.id,
+                  'f_path': '',
+                  'at': e.choice.text
+              });
+          } else if (context == 'changelog') {
+              if (e.choice.type == 'tag' || e.choice.type == 'book') {
+                  $("#branch_filter").append($('<option/>').val(e.choice.text));
+              }
+              $("#branch_filter").val(e.choice.text).change();
+          } else {
+              window.location = pyroutes.url('changelog_home', {
+                  'repo_name': '${c.repo_name}',
+                  'branch': e.choice.text
+              });
+          }
+      });
+    });
   </script>
   <!--- END CONTEXT BAR -->
 </%def>
@@ -276,7 +360,11 @@
 
     ## USER MENU
     <li>
-      <a class="menu_link childs" id="quick_login_link">
+      <a class="menu_link childs" id="quick_login_link"
+        %if c.authuser.username != 'default':
+          href="${h.url('notifications')}"
+        %endif
+      >
           <span class="icon">
             ${h.gravatar(c.authuser.email, size=20)}
           </span>
@@ -353,95 +441,98 @@
     </li>
 
     <script type="text/javascript">
-        var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
-        var cache = {}
-        /*format the look of items in the list*/
-        var format = function(state){
-            if (!state.id){
-              return state.text; // optgroup
-            }
-            var obj_dict = state.obj;
-            var tmpl = '';
-
-            if(obj_dict && state.type == 'repo'){
-                tmpl += '<span class="repo-icons">';
-                if(obj_dict['repo_type'] === 'hg'){
-                    tmpl += '<span class="repotag">hg</span> ';
+        $(document).ready(function(){
+            var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
+            var cache = {}
+            /*format the look of items in the list*/
+            var format = function(state){
+                if (!state.id){
+                  return state.text; // optgroup
                 }
-                else if(obj_dict['repo_type'] === 'git'){
-                    tmpl += '<span class="repotag">git</span> ';
-                }
-                if(obj_dict['private']){
-                    tmpl += '<i class="icon-keyhole-circled"></i> ';
-                }
-                else if(visual_show_public_icon){
-                    tmpl += '<i class="icon-globe"></i> ';
-                }
-                tmpl += '</span>';
-            }
-            if(obj_dict && state.type == 'group'){
-                    tmpl += '<i class="icon-folder"></i> ';
-            }
-            tmpl += state.text;
-            return tmpl;
-        }
+                var obj_dict = state.obj;
+                var tmpl = '';
 
-        $("#repo_switcher").select2({
-            placeholder: '<i class="icon-database"></i> ${_('Repositories')}',
-            dropdownAutoWidth: true,
-            formatResult: format,
-            formatSelection: format,
-            formatNoMatches: function(term){
-                return "${_('No matches found')}";
-            },
-            containerCssClass: "repo-switcher",
-            dropdownCssClass: "repo-switcher-dropdown",
-            escapeMarkup: function(m){
-                // don't escape our custom placeholder
-                if(m.substr(0,29) == '<i class="icon-database"></i>'){
-                    return m;
+                if(obj_dict && state.type == 'repo'){
+                    tmpl += '<span class="repo-icons">';
+                    if(obj_dict['repo_type'] === 'hg'){
+                        tmpl += '<span class="repotag">hg</span> ';
+                    }
+                    else if(obj_dict['repo_type'] === 'git'){
+                        tmpl += '<span class="repotag">git</span> ';
+                    }
+                    if(obj_dict['private']){
+                        tmpl += '<i class="icon-keyhole-circled"></i> ';
+                    }
+                    else if(visual_show_public_icon){
+                        tmpl += '<i class="icon-globe"></i> ';
+                    }
+                    tmpl += '</span>';
                 }
+                if(obj_dict && state.type == 'group'){
+                        tmpl += '<i class="icon-folder"></i> ';
+                }
+                tmpl += state.text;
+                return tmpl;
+            }
 
-                return Select2.util.escapeMarkup(m);
-            },
-            query: function(query){
-              var key = 'cache';
-              var cached = cache[key] ;
-              if(cached) {
-                var data = {results: []};
-                //filter results
-                $.each(cached.results, function(){
-                    var section = this.text;
-                    var children = [];
-                    $.each(this.children, function(){
-                        if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){
-                            children.push({'id': this.id, 'text': this.text, 'type': this.type, 'obj': this.obj});
-                        }
-                    });
-                    if(children.length !== 0){
-                        data.results.push({'text': section, 'children': children});
+            $("#repo_switcher").select2({
+                placeholder: '<i class="icon-database"></i> ${_('Repositories')}',
+                dropdownAutoWidth: true,
+                sortResults: prefixFirstSort,
+                formatResult: format,
+                formatSelection: format,
+                formatNoMatches: function(term){
+                    return "${_('No matches found')}";
+                },
+                containerCssClass: "repo-switcher",
+                dropdownCssClass: "repo-switcher-dropdown",
+                escapeMarkup: function(m){
+                    // don't escape our custom placeholder
+                    if(m.substr(0,29) == '<i class="icon-database"></i>'){
+                        return m;
                     }
 
-                });
-                query.callback(data);
-              }else{
-                  $.ajax({
-                    url: "${h.url('repo_switcher_data')}",
-                    data: {},
-                    dataType: 'json',
-                    type: 'GET',
-                    success: function(data) {
-                      cache[key] = data;
-                      query.callback({results: data.results});
-                    }
-                  });
-              }
-            }
-        });
+                    return Select2.util.escapeMarkup(m);
+                },
+                query: function(query){
+                  var key = 'cache';
+                  var cached = cache[key] ;
+                  if(cached) {
+                    var data = {results: []};
+                    //filter results
+                    $.each(cached.results, function(){
+                        var section = this.text;
+                        var children = [];
+                        $.each(this.children, function(){
+                            if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){
+                                children.push({'id': this.id, 'text': this.text, 'type': this.type, 'obj': this.obj});
+                            }
+                        });
+                        if(children.length !== 0){
+                            data.results.push({'text': section, 'children': children});
+                        }
 
-        $("#repo_switcher").on('select2-selecting', function(e){
-            e.preventDefault();
-            window.location = pyroutes.url('summary_home', {'repo_name': e.val});
+                    });
+                    query.callback(data);
+                  }else{
+                      $.ajax({
+                        url: "${h.url('repo_switcher_data')}",
+                        data: {},
+                        dataType: 'json',
+                        type: 'GET',
+                        success: function(data) {
+                          cache[key] = data;
+                          query.callback({results: data.results});
+                        }
+                      });
+                  }
+                }
+            });
+
+            $("#repo_switcher").on('select2-selecting', function(e){
+                e.preventDefault();
+                window.location = pyroutes.url('summary_home', {'repo_name': e.val});
+            });
         });
 
         ## Global mouse bindings ##
--- a/kallithea/templates/base/root.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/base/root.html	Mon Mar 14 00:36:08 2016 +0100
@@ -97,7 +97,6 @@
               pyroutes.register('toggle_following', "${h.url('toggle_following')}");
               pyroutes.register('changeset_info', "${h.url('changeset_info', repo_name='%(repo_name)s', revision='%(revision)s')}", ['repo_name', 'revision']);
               pyroutes.register('repo_size', "${h.url('repo_size', repo_name='%(repo_name)s')}", ['repo_name']);
-              pyroutes.register('changeset_comment_preview', "${h.url('changeset_comment_preview', repo_name='%(repo_name)s')}", ['repo_name']);
               pyroutes.register('repo_refs_data', "${h.url('repo_refs_data', repo_name='%(repo_name)s')}", ['repo_name']);
              });
         </script>
--- a/kallithea/templates/changelog/changelog.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/changelog/changelog.html	Mon Mar 14 00:36:08 2016 +0100
@@ -89,11 +89,12 @@
                           %if c.statuses.get(cs.raw_id):
                             <div class="changeset-status-ico">
                             %if c.statuses.get(cs.raw_id)[2]:
-                              <a class="tooltip" title="${_('Changeset status: %s\nClick to open associated pull request %s') % (c.statuses.get(cs.raw_id)[1], c.statuses.get(cs.raw_id)[4])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}">
+                              <a class="tooltip" title="${_('Changeset status: %s by %s\nClick to open associated pull request %s') % (c.statuses.get(cs.raw_id)[1], c.statuses.get(cs.raw_id)[5].username, c.statuses.get(cs.raw_id)[4])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}">
                                 <i class="icon-circle changeset-status-${c.statuses.get(cs.raw_id)[0]}"></i>
                               </a>
                             %else:
-                              <a class="tooltip" title="${_('Changeset status: %s') % c.statuses.get(cs.raw_id)[1]}" href="${c.comments[cs.raw_id][0].url()}">
+                              <a class="tooltip" title="${_('Changeset status: %s by %s') % (c.statuses.get(cs.raw_id)[1], c.statuses.get(cs.raw_id)[5].username)}"
+                                 href="${c.comments[cs.raw_id][0].url()}">
                                   <i class="icon-circle changeset-status-${c.statuses.get(cs.raw_id)[0]}"></i>
                               </a>
                             %endif
--- a/kallithea/templates/changelog/changelog_summary_data.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/changelog/changelog_summary_data.html	Mon Mar 14 00:36:08 2016 +0100
@@ -17,11 +17,14 @@
               %if c.statuses.get(cs.raw_id):
                 <span class="changeset-status-ico shortlog">
                 %if c.statuses.get(cs.raw_id)[2]:
-                  <a class="tooltip" title="${_('Changeset status: %s\nClick to open associated pull request %s') % (c.statuses.get(cs.raw_id)[1], c.statuses.get(cs.raw_id)[4])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}">
+                  <a class="tooltip" title="${_('Changeset status: %s by %s\nClick to open associated pull request %s') % (c.statuses.get(cs.raw_id)[1], c.statuses.get(cs.raw_id)[5].username, c.statuses.get(cs.raw_id)[4])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}">
                     <i class="icon-circle changeset-status-${c.statuses.get(cs.raw_id)[0]}"></i>
                   </a>
                 %else:
-                  <i class="icon-circle changeset-status-${c.statuses.get(cs.raw_id)[0]}"></i>
+                  <a class="tooltip" title="${_('Changeset status: %s by %s') % (c.statuses.get(cs.raw_id)[1], c.statuses.get(cs.raw_id)[5].username)}"
+                     href="${c.comments[cs.raw_id][0].url()}">
+                    <i class="icon-circle changeset-status-${c.statuses.get(cs.raw_id)[0]}"></i>
+                  </a>
                 %endif
                 </span>
               %endif
--- a/kallithea/templates/changeset/changeset.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/changeset/changeset.html	Mon Mar 14 00:36:08 2016 +0100
@@ -203,27 +203,11 @@
     ${comment.generate_comments()}
 
     ## main comment form and it status
-    ${comment.comments(h.url('changeset_comment', repo_name=c.repo_name, revision=c.changeset.raw_id),
-                       h.changeset_status(c.db_repo, c.changeset.raw_id))}
+    ${comment.comments()}
 
     ## FORM FOR MAKING JS ACTION AS CHANGESET COMMENTS
     <script type="text/javascript">
       $(document).ready(function(){
-          $('.show-inline-comments').change(function(e){
-              var target = e.currentTarget;
-              if(target == null){
-                  target = this;
-              }
-              var boxid = $(target).attr('id_for');
-              if(target.checked){
-                  $('#{0} .inline-comments'.format(boxid)).show();
-                  $('#{0} .inline-comments-button'.format(boxid)).show();
-              }else{
-                  $('#{0} .inline-comments'.format(boxid)).hide();
-                  $('#{0} .inline-comments-button'.format(boxid)).hide();
-              }
-          });
-
           $('.code-difftable').on('click', '.add-bubble', function(e){
               show_comment_form($(this));
           });
--- a/kallithea/templates/changeset/changeset_file_comment.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/changeset/changeset_file_comment.html	Mon Mar 14 00:36:08 2016 +0100
@@ -26,13 +26,15 @@
               <a class="permalink" href="${co.url()}">&para;</a>
           </span>
 
-          %if h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or co.author.user_id == c.authuser.user_id:
-            <div onClick="confirm('${_("Delete comment?")}') && deleteComment(${co.comment_id})" class="buttons delete-comment btn btn-mini">${_('Delete')}</div>
+          %if co.author.user_id == c.authuser.user_id or h.HasRepoPermissionAny('repository.admin')(c.repo_name):
+            %if co.deletable():
+              <div onClick="confirm('${_("Delete comment?")}') && deleteComment(${co.comment_id})" class="buttons delete-comment btn btn-mini" style="margin:0 5px">${_('Delete')}</div>
+            %endif
           %endif
       </div>
       <div class="text">
         %if co.status_change:
-           <div class="rst-block automatic-comment">
+           <div class="automatic-comment">
              <p>
                <span title="${_('Changeset status')}" class="changeset-status-lbl">${_("Status change")}: ${co.status_change[0].status_lbl}</span>
                <span class="changeset-status-ico"><i class="icon-circle changeset-status-${co.status_change[0].status}"></i></span>
@@ -40,7 +42,7 @@
            </div>
         %endif
         %if co.text:
-          ${h.rst_w_mentions(co.text)|n}
+          ${h.render_w_mentions(co.text, c.repo_name)|n}
         %endif
       </div>
     </div>
@@ -48,39 +50,59 @@
 </%def>
 
 
-## expanded with .format(f_path, line_no)
-## TODO: don't assume line_no is globally unique ...
 <%def name="comment_inline_form()">
 <div id='comment-inline-form-template' style="display:none">
   <div class="ac">
   %if c.authuser.username != 'default':
     ${h.form('#', class_='inline-form')}
-      <div id="edit-container_{1}" class="clearfix">
-        <div class="comment-help">${_('Commenting on line {1}.')}
-          ${(_('Comments parsed using %s syntax with %s support.') % (
-                 ('<a href="%s">RST</a>' % h.url('rst_help')),
-                   ('<span style="color:#577632" class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to notify another user'))
-               )
-            )|n
-           }
+      <div class="clearfix">
+        <div class="comment-help">${_('Commenting on line.')}
+          <span style="color:#577632" class="tooltip">${_('Comments are in plain text. Use @username inside this text to notify another user.')|n}</span>
         </div>
-        <div class="mentions-container" id="mentions_container_{1}"></div>
-        <textarea id="text_{1}" name="text" class="comment-block-ta yui-ac-input"></textarea>
-      </div>
-      <div id="preview-container_{1}" class="clearfix" style="display:none">
-        <div class="comment-help">
-            ${_('Comment preview')}
+        <div class="mentions-container"></div>
+        <textarea name="text" class="comment-block-ta yui-ac-input"></textarea>
+
+        <div id="status_block_container" class="status-block general-only hidden">
+                %if c.pull_request is None:
+                  ${_('Set changeset status')}:
+                %else:
+                  ${_('Vote for pull request status')}:
+                %endif
+                <span class="general-only cs-only">
+                </span>
+                <input type="radio" class="status_change_radio" name="changeset_status" id="changeset_status_unchanged" value="" checked="checked" />
+                <label for="changeset_status_unchanged">
+                  ${_('No change')}
+                </label>
+                %for status, lbl in c.changeset_statuses:
+                    <label>
+                      <input type="radio" class="status_change_radio" name="changeset_status" id="${status}" value="${status}">
+                      ${lbl}<i class="icon-circle changeset-status-${status}" /></i>
+                    </label>
+                %endfor
+
+                %if c.pull_request is not None and ( \
+                    h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) \
+                    or c.pull_request.owner.user_id == c.authuser.user_id):
+                <div>
+                  ${_('Finish pull request')}:
+                  <label>
+                    <input id="save_close" type="checkbox" name="save_close">
+                    ${_("Close")}
+                  </label>
+                  <label>
+                    <input id="save_delete" type="checkbox" name="save_delete" value="delete">
+                    ${_("Delete")}
+                  </label>
+                </div>
+                %endif
         </div>
-        <div id="preview-box_{1}" class="preview-box"></div>
+
       </div>
       <div class="comment-button">
         <div class="submitting-overlay">${_('Submitting ...')}</div>
-        <input type="hidden" name="f_path" value="{0}">
-        <input type="hidden" name="line" value="{1}">
         ${h.submit('save', _('Comment'), class_='btn btn-small save-inline-form')}
         ${h.reset('hide-inline-form', _('Cancel'), class_='btn btn-small hide-inline-form')}
-        <div id="preview-btn_{1}" class="preview-btn btn btn-small">${_('Preview')}</div>
-        <div id="edit-btn_{1}" class="edit-btn btn btn-small" style="display:none">${_('Edit')}</div>
       </div>
     ${h.end_form()}
   %else:
@@ -113,7 +135,8 @@
 
 ## generate inline comments and the main ones
 <%def name="generate_comments()">
-<div class="comments">
+## original location of comments ... but the ones outside diff context remains here
+<div class="comments inline-comments">
   %for f_path, lines in c.inline_comments:
     %for line_no, comments in lines.iteritems():
       <div class="comments-list-chunk" data-f_path="${f_path}" data-line_no="${line_no}" data-target-id="${h.safeid(h.safe_unicode(f_path))}_${line_no}">
@@ -124,126 +147,44 @@
     %endfor
   %endfor
 
-  <div class="comments-number">
-    ${comment_count(c.inline_cnt, len(c.comments))}
-  </div>
-
-      <div class="comments-list-general">
+      <div class="comments-list-chunk" data-f_path="" data-line_no="" data-target-id="general-comments">
         %for co in c.comments:
             ${comment_block(co)}
         %endfor
       </div>
 </div>
+<div class="comments-number">
+    ${comment_count(c.inline_cnt, len(c.comments))}
+</div>
 </%def>
 
 ## MAIN COMMENT FORM
-<%def name="comments(post_url, cur_status, is_pr=False, change_status=True)">
+<%def name="comments(change_status=True)">
+
+## global, shared for all edit boxes
+<div class="mentions-container" id="mentions_container"></div>
 
-<div class="comments">
-    %if c.authuser.username != 'default':
-    <div class="comment-form ac">
-      ${h.form(post_url, id="main_form")}
-        <div id="edit-container" class="clearfix">
-            <div class="comment-help">
-                ${(_('Comments parsed using %s syntax with %s support.') % (('<a href="%s">RST</a>' % h.url('rst_help')),
-                  '<span style="color:#577632" class="tooltip" title="%s">@mention</span>' %
-                  _('Use @username inside this text to notify another user.')))|n}
-            </div>
-            <div class="mentions-container" id="mentions_container"></div>
-            ${h.textarea('text', class_="comment-block-ta")}
-            %if change_status:
-              <div id="status_block_container" class="status-block">
-                %if is_pr:
-                  ${_('Vote for pull request status')}:
-                %else:
-                  ${_('Set changeset status')}:
-                %endif
-                <input type="radio" class="status_change_radio" name="changeset_status" id="changeset_status_unchanged" value="" checked="checked" />
-                <label for="changeset_status_unchanged">
-                  ${_('No change')}
-                </label>
-                %for status,lbl in c.changeset_statuses:
-                    <span>
-                        <input type="radio" class="status_change_radio" name="changeset_status" id="${status}" value="${status}">
-                        <label for="${status}"><i class="icon-circle changeset-status-${status}" /></i>${lbl}</label>
-                    </span>
-                %endfor
-
-                %if is_pr and ( \
-                    h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) \
-                    or c.pull_request.owner.user_id == c.authuser.user_id):
-                  <input id="save_close" type="checkbox" name="save_close">
-                  <label id="save_close_label" for="save_close">${_("Close")}</label>
-                %endif
-              </div>
-            %endif
-        </div>
-
-        <div id="preview-container" class="clearfix" style="display:none">
-            <div class="comment-help">
-                ${_('Comment preview')}
-            </div>
-            <div id="preview-box" class="preview-box"></div>
-        </div>
-
-        <div class="comment-button">
-            ${h.submit('save', _('Comment'), class_="btn")}
-            <div id="preview-btn" class="preview-btn btn">${_('Preview')}</div>
-            <div id="edit-btn" class="edit-btn btn" style="display:none">${_('Edit')}</div>
-        </div>
-      ${h.end_form()}
-    </div>
-    %endif
+<div class="inline-comments inline-comments-general
+            ${'show-general-status' if change_status else ''}">
+  <div id="comments-general-comments" class="">
+  ## comment_div for general comments
+  </div>
 </div>
 
 <script>
 
 $(document).ready(function () {
-   MentionsAutoComplete($('#text'), $('#mentions_container'), _USERS_AC_DATA, _GROUPS_AC_DATA);
 
    $(window).on('beforeunload', function(){
-      if($('.comment-inline-form textarea[name=text]').size() ||
-         $('textarea#text').val()) {
+      var $textareas = $('.comment-inline-form textarea[name=text]');
+      if($textareas.size() > 1 ||
+         $textareas.val()) {
          // this message will not be displayed on all browsers
          // (e.g. some versions of Firefox), but the user will still be warned
          return 'There are uncommitted comments.';
       }
    });
 
-   $('form#main_form').submit(function(){
-      // if no open inline forms, disable the beforeunload check - it would
-      // fail in the check for the textarea we are about to submit
-      if(!$('.form-open').size()){
-          $(window).off('beforeunload');
-      }
-   });
-
-   $('#preview-btn').click(function(){
-       var _text = $('#text').val();
-       if(!_text){
-           return;
-       }
-       var post_data = {'text': _text};
-       $('#preview-box').addClass('unloaded');
-       $('#preview-box').html(_TM['Loading ...']);
-       $('#edit-container').hide();
-       $('#edit-btn').show();
-       $('#preview-container').show();
-       $('#preview-btn').hide();
-
-       var url = pyroutes.url('changeset_comment_preview', {'repo_name': '${c.repo_name}'});
-       ajaxPOST(url,post_data,function(html){
-           $('#preview-box').html(html);
-           $('#preview-box').removeClass('unloaded');
-       });
-   });
-   $('#edit-btn').click(function(){
-       $('#edit-container').show();
-       $('#edit-btn').hide();
-       $('#preview-container').hide();
-       $('#preview-btn').show();
-   });
-
 });
 </script>
 </%def>
--- a/kallithea/templates/changeset/diff_block.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/changeset/diff_block.html	Mon Mar 14 00:36:08 2016 +0100
@@ -8,122 +8,104 @@
     <span target="${'diff-container-%s' % (id(change))}" class="diff-collapse-button">&uarr; ${_('Collapse Diff')} &uarr;</span>
 </div>
 <div class="diff-container" id="${'diff-container-%s' % (id(change))}">
-%for FID,(cs1, cs2, change, path, diff, stats) in change.iteritems():
-    <div id="${FID}_target" style="clear:both;margin-top:25px"></div>
-    <div id="${FID}" class="diffblock  margined comm">
+%for id_fid, (a_rev, cs_rev, op, filename, diff, stats) in change.iteritems():
+    ${diff_block_diffblock(id_fid, h.FID(cs_rev, filename), op, filename, diff,
+        c.repo_name, a_rev, 'rev', a_rev,
+        c.repo_name, cs_rev, 'rev', cs_rev)}
+%endfor
+</div>
+</%def>
+
+<%def name="diff_block_simple(files, changes)">
+<div class="diff-collapse">
+    <span target="${'diff-container-%s' % (id(changes))}" class="diff-collapse-button">&uarr; ${_('Collapse Diff')} &uarr;</span>
+</div>
+<div class="diff-container" id="${'diff-container-%s' % (id(changes))}">
+  %for fid, ch, f, stat in files:
+    <%
+    op, filename, diff = changes[fid]
+    %>
+    ${diff_block_diffblock(h.FID('', filename), None, op, filename, diff,
+        c.a_repo.repo_name, c.a_rev, c.a_ref_type, c.a_ref_name,
+        c.cs_repo.repo_name, c.cs_rev, c.cs_ref_type, c.cs_ref_name)}
+  %endfor
+</div>
+</%def>
+
+<%def name="diff_block_diffblock(id_fid, url_fid, op, filename, diff,
+    a_repo_name, a_rev, a_ref_type, a_ref_name,
+    cs_repo_name, cs_rev, cs_ref_type, cs_ref_name)"
+>
+    <div id="${id_fid}_target" style="clear:both;margin-top:25px"></div>
+    <div id="${id_fid}" class="diffblock margined comm">
         <div class="code-header">
             <div class="changeset_header">
                 <div class="changeset_file">
-                    ${h.link_to_if(change!='D',h.safe_unicode(path),h.url('files_home',repo_name=c.repo_name,
-                    revision=cs2,f_path=h.safe_unicode(path)))}
+                    ${h.safe_unicode(filename)} |
+                    ## TODO: link to ancestor and head of other instead of exactly other
+                    %if op == 'A':
+                      ${_('Added')}
+                      <a class="spantag" href="${h.url('files_home', repo_name=cs_repo_name, f_path=filename, revision=cs_rev)}">${h.short_ref(cs_ref_type, cs_ref_name)}</a>
+                    %elif op == 'M':
+                      <a class="spantag" href="${h.url('files_home', repo_name=a_repo_name, f_path=filename, revision=a_rev)}">${h.short_ref(a_ref_type, a_ref_name)}</a>
+                      <i class="icon-right"></i>
+                      <a class="spantag" href="${h.url('files_home', repo_name=cs_repo_name, f_path=filename, revision=cs_rev)}">${h.short_ref(cs_ref_type, cs_ref_name)}</a>
+                    %elif op == 'D':
+                      ${_('Deleted')}
+                      <a class="spantag" href="${h.url('files_home', repo_name=a_repo_name, f_path=filename, revision=a_rev)}">${h.short_ref(a_ref_type, a_ref_name)}</a>
+                    %elif op == 'R':
+                      ${_('Renamed')}
+                      <a class="spantag" href="${h.url('files_home', repo_name=a_repo_name, f_path=filename, revision=a_rev)}">${h.short_ref(a_ref_type, a_ref_name)}</a>
+                      <i class="icon-right"></i>
+                      <a class="spantag" href="${h.url('files_home', repo_name=cs_repo_name, f_path=filename, revision=cs_rev)}">${h.short_ref(cs_ref_type, cs_ref_name)}</a>
+                    %else:
+                      ${op}???
+                    %endif
                 </div>
                 <div class="diff-actions">
-                  <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(path),diff2=cs2,diff1=cs1,diff='diff',fulldiff=1)}" class="tooltip" title="${_('Show full diff for this file')}">
+                  <a href="${h.url('files_diff_home',repo_name=cs_repo_name,f_path=h.safe_unicode(filename),diff2=cs_rev,diff1=a_rev,diff='diff',fulldiff=1)}" class="tooltip" title="${_('Show full diff for this file')}">
                       <i class="icon-file-code"></i>
                   </a>
-                  <a href="${h.url('files_diff_2way_home',repo_name=c.repo_name,f_path=h.safe_unicode(path),diff2=cs2,diff1=cs1,diff='diff',fulldiff=1)}" class="tooltip" title="${_('Show full side-by-side diff for this file')}">
+                  <a href="${h.url('files_diff_2way_home',repo_name=cs_repo_name,f_path=h.safe_unicode(filename),diff2=cs_rev,diff1=a_rev,diff='diff',fulldiff=1)}" class="tooltip" title="${_('Show full side-by-side diff for this file')}">
                       <i class="icon-docs"></i>
                   </a>
-                  <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(path),diff2=cs2,diff1=cs1,diff='raw')}" class="tooltip" title="${_('Raw diff')}">
+                  <a href="${h.url('files_diff_home',repo_name=cs_repo_name,f_path=h.safe_unicode(filename),diff2=cs_rev,diff1=a_rev,diff='raw')}" class="tooltip" title="${_('Raw diff')}">
                       <i class="icon-diff"></i>
                   </a>
-                  <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(path),diff2=cs2,diff1=cs1,diff='download')}" class="tooltip" title="${_('Download diff')}">
+                  <a href="${h.url('files_diff_home',repo_name=cs_repo_name,f_path=h.safe_unicode(filename),diff2=cs_rev,diff1=a_rev,diff='download')}" class="tooltip" title="${_('Download diff')}">
                       <i class="icon-floppy"></i>
                   </a>
-                  ${c.ignorews_url(request.GET, h.FID(cs2,path))}
-                  ${c.context_url(request.GET, h.FID(cs2,path))}
+                  ${c.ignorews_url(request.GET, url_fid)}
+                  ${c.context_url(request.GET, url_fid)}
                 </div>
                 <span style="float:right;margin-top:-3px">
-                  <label>
-                  ${_('Show inline comments')}
-                  ${h.checkbox('',checked="checked",class_="show-inline-comments",id_for=h.FID(cs2,path))}
-                  </label>
+                    <label>
+                        ${_('Show inline comments')}
+                        ${h.checkbox('',checked="checked",class_="show-inline-comments",id_for=url_fid)}
+                    </label>
                 </span>
             </div>
         </div>
-        <div class="code-body full_f_path" data-f_path="${h.safe_unicode(path)}">
+        <div class="code-body full_f_path" data-f_path="${h.safe_unicode(filename)}">
             ${diff|n}
-            %if path.rsplit('.')[-1] in ['png', 'gif', 'jpg', 'bmp']:
+            %if filename.rsplit('.')[-1] in ['png', 'gif', 'jpg', 'bmp']:
               <div class="btn btn-image-diff-show">Show images</div>
-              %if change =='M':
-                <div id="${FID}_image-diff" class="btn btn-image-diff-swap" style="display:none">Press to swap images</div>
+              %if op == 'M':
+                <div id="${id_fid}_image-diff" class="btn btn-image-diff-swap" style="display:none">Press to swap images</div>
               %endif
               <div style="font-size: 0">
-                %if change == 'M':
-                  <img id="${FID}_image-diff-img-a" class="img-diff img-diff-swapable" style="display:none"
-                      realsrc="${h.url('files_raw_home',repo_name=c.repo_name,revision=cs1,f_path=path)}" />
+                %if op in 'DM':
+                  <img id="${id_fid}_image-diff-img-a" class="img-diff img-diff-swapable" style="display:none"
+                      realsrc="${h.url('files_raw_home',repo_name=a_repo_name,revision=a_rev,f_path=filename)}" />
                 %endif
-                %if change in 'AM':
-                  <img id="${FID}_image-diff-img-b" class="img-diff img-diff-swapable" style="display:none"
-                      realsrc="${h.url('files_raw_home',repo_name=c.repo_name,revision=cs2,f_path=path)}" />
+                %if op in 'AM':
+                  <img id="${id_fid}_image-diff-img-b" class="img-diff img-diff-swapable" style="display:none"
+                      realsrc="${h.url('files_raw_home',repo_name=cs_repo_name,revision=cs_rev,f_path=filename)}" />
                 %endif
               </div>
             %endif
         </div>
     </div>
-%endfor
-</div>
-</%def>
-
-<%def name="diff_block_simple(change)">
-
-  %for op,filenode_path,diff in change:
-    <div id="${h.FID('',filenode_path)}_target" style="clear:both;margin-top:25px"></div>
-    <div id="${h.FID('',filenode_path)}" class="diffblock  margined comm">
-      <div class="code-header">
-          <div class="changeset_header">
-              <div class="changeset_file">
-                  ${h.safe_unicode(filenode_path)} |
-                  ## TODO: link to ancestor and head of other instead of exactly other
-                  %if op == 'A':
-                    ${_('Added')}
-                    <a class="spantag" href="${h.url('files_home', repo_name=c.cs_repo.repo_name, f_path=filenode_path, revision=c.cs_rev)}">${h.short_id(c.cs_ref_name) if c.cs_ref_type=='rev' else c.cs_ref_name}</a>
-                  %elif op == 'M':
-                    <a class="spantag" href="${h.url('files_home', repo_name=c.a_repo.repo_name, f_path=filenode_path, revision=c.a_rev)}">${h.short_id(c.a_ref_name) if c.a_ref_type=='rev' else c.a_ref_name}</a>
-                    <i class="icon-right"></i>
-                    <a class="spantag" href="${h.url('files_home', repo_name=c.cs_repo.repo_name, f_path=filenode_path, revision=c.cs_rev)}">${h.short_id(c.cs_ref_name) if c.cs_ref_type=='rev' else c.cs_ref_name}</a>
-                  %elif op == 'D':
-                    ${_('Deleted')}
-                    <a class="spantag" href="${h.url('files_home', repo_name=c.a_repo.repo_name, f_path=filenode_path, revision=c.a_rev)}">${h.short_id(c.a_ref_name) if c.a_ref_type=='rev' else c.a_ref_name}</a>
-                  %elif op == 'R':
-                    ${_('Renamed')}
-                    <a class="spantag" href="${h.url('files_home', repo_name=c.a_repo.repo_name, f_path=filenode_path, revision=c.a_rev)}">${h.short_id(c.a_ref_name) if c.a_ref_type=='rev' else c.a_ref_name}</a>
-                    <i class="icon-right"></i>
-                    <a class="spantag" href="${h.url('files_home', repo_name=c.cs_repo.repo_name, f_path=filenode_path, revision=c.cs_rev)}">${h.short_id(c.cs_ref_name) if c.cs_ref_type=='rev' else c.cs_ref_name}</a>
-                  %else:
-                    ${op}???
-                  %endif
-              </div>
-              <div class="diff-actions">
-                <a href="${h.url('files_diff_2way_home',repo_name=c.cs_repo.repo_name,f_path=h.safe_unicode(filenode_path),diff1=c.a_rev,diff2=c.cs_rev,diff='diff',fulldiff=1)}" class="tooltip" title="${_('Show full side-by-side diff for this file')}">
-                  <i class="icon-docs"></i>
-                </a>
-                ${c.ignorews_url(request.GET)}
-                ${c.context_url(request.GET)}
-              </div>
-          </div>
-      </div>
-        <div class="code-body full_f_path" data-f_path="${h.safe_unicode(filenode_path)}">
-            ${diff|n}
-            %if filenode_path.rsplit('.')[-1] in ['png', 'gif', 'jpg', 'bmp']:
-              <div class="btn btn-image-diff-show">Show images</div>
-              %if op == 'M':
-                <div id="${h.FID('',filenode_path)}_image-diff" class="btn btn-image-diff-swap" style="display:none">Press to swap images</div>
-              %endif
-              <div style="font-size: 0">
-                %if op == 'M':
-                  <img id="${h.FID('',filenode_path)}_image-diff-img-a" class="img-diff img-diff-swapable" style="display:none"
-                      realsrc="${h.url('files_raw_home',repo_name=c.a_repo.repo_name,revision=c.a_rev,f_path=filenode_path) if op in 'DM' else ''}" />
-                %endif
-                %if op in 'AM':
-                  <img id="${h.FID('',filenode_path)}_image-diff-img-b" class="img-diff img-diff-swapable" style="display:none"
-                      realsrc="${h.url('files_raw_home',repo_name=c.cs_repo.repo_name,revision=c.cs_rev,f_path=filenode_path) if op in 'AM' else ''}" />
-                %endif
-              </div>
-            %endif
-        </div>
-    </div>
-  %endfor
 </%def>
 
 <%def name="diff_block_js()">
@@ -149,6 +131,31 @@
     };
     $('.btn-image-diff-swap').mouseup(reset);
     $('.btn-image-diff-swap').mouseleave(reset);
+
+    $('.diff-collapse-button').click(function(e) {
+        var $button = $(e.currentTarget);
+        var $target = $('#' + $button.attr('target'));
+        if($target.hasClass('hidden')){
+            $target.removeClass('hidden');
+            $button.html("&uarr; {0} &uarr;".format(_TM['Collapse Diff']));
+        }
+        else if(!$target.hasClass('hidden')){
+            $target.addClass('hidden');
+            $button.html("&darr; {0} &darr;".format(_TM['Expand Diff']));
+        }
+    });
+    $('.show-inline-comments').change(function(e){
+        var target = e.currentTarget;
+        if(target == null){
+            target = this;
+        }
+        var boxid = $(target).attr('id_for');
+        if(target.checked){
+            $('#{0} .inline-comments'.format(boxid)).show();
+        }else{
+            $('#{0} .inline-comments'.format(boxid)).hide();
+        }
+    });
 });
 </script>
 </%def>
--- a/kallithea/templates/compare/compare_cs.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/compare/compare_cs.html	Mon Mar 14 00:36:08 2016 +0100
@@ -63,7 +63,16 @@
         <td class="expand_commit" commit_id="${cs.raw_id}" title="${_('Expand commit message')}">
             <i class="icon-align-left" style="color:#999"></i>
         </td>
-        <td><div id="C-${cs.raw_id}" class="message">${h.urlify_commit(cs.message, c.repo_name)}</div></td>
+        <td>
+            <div style="float: right; margin-top: -4px;">
+                %for tag in cs.tags:
+                    <div class="tagtag" title="${_('Tag %s') % tag}">
+                        ${h.link_to(tag,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
+                    </div>
+                %endfor
+            </div>
+            <div id="C-${cs.raw_id}" class="message">${h.urlify_commit(cs.message, c.repo_name)}</div>
+        </td>
         </tr>
     %endfor
     </table>
--- a/kallithea/templates/compare/compare_diff.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/compare/compare_diff.html	Mon Mar 14 00:36:08 2016 +0100
@@ -26,9 +26,15 @@
     </div>
     <div class="table">
         <div id="body" class="diffblock">
-            <div class="code-header">
+            <div class="compare-revision-selector">
+                ## divs are "inline-block" and cannot have whitespace between them.
                 <div>
-                    ${h.hidden('compare_org')} <i class="icon-right"></i> ${h.hidden('compare_other')}
+                    ${h.hidden('compare_org')}
+                </div><div>
+                    <i class="icon-right"></i>
+                </div><div>
+                    ${h.hidden('compare_other')}
+                </div><div>
                     %if not c.compare_home:
                         <a class="btn btn-small" href="${c.swap_url}"><i class="icon-arrows-cw"></i> ${_('Swap')}</a>
                     %endif
@@ -82,9 +88,7 @@
         ## diff block
         <%namespace name="diff_block" file="/changeset/diff_block.html"/>
         ${diff_block.diff_block_js()}
-        %for fid, change, f, stat in c.files:
-          ${diff_block.diff_block_simple([c.changes[fid]])}
-        %endfor
+        ${diff_block.diff_block_simple(c.files, c.changes)}
         % if c.limited_diff:
           <h4>${_('Changeset was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}">${_('Show full diff')}</a></h4>
         % endif
--- a/kallithea/templates/email_templates/password_reset.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/email_templates/password_reset.html	Mon Mar 14 00:36:08 2016 +0100
@@ -4,9 +4,13 @@
 <h4>${_('Hello %s') % user}</h4>
 
 <p>${_('We have received a request to reset the password for your account.')}</p>
+%if reset_token is None:
+<p>${_('This account is however managed outside this system and the password cannot be changed here.')}</p>
+%else:
 <p>${_('To set a new password, click the following link')}:</p>
 <p><a href="${reset_url}">${reset_url}</a></p>
 
 <p>${_("Should you not be able to use the link above, please type the following code into the password reset form")}: <code>${reset_token}</code></p>
+%endif
 
 <p>${_("If it weren't you who requested the password reset, just disregard this message.")}</p>
--- a/kallithea/templates/email_templates/password_reset.txt	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/email_templates/password_reset.txt	Mon Mar 14 00:36:08 2016 +0100
@@ -3,11 +3,15 @@
 
 ${_('Hello %s') % user|n,unicode}
 
-${_('We have received a request to reset the password for your account..')|n,unicode}
+${_('We have received a request to reset the password for your account.')|n,unicode}
+%if reset_token is None:
+${_('This account is however managed outside this system and the password cannot be changed here.')|n,unicode}
+%else:
 ${_('To set a new password, click the following link')|n,unicode}:
 
 ${reset_url|n,unicode}
 
 ${_("Should you not be able to use the link above, please type the following code into the password reset form")|n,unicode}: ${reset_token|n,unicode}
+%endif
 
 ${_("If it weren't you who requested the password reset, just disregard this message.")|n,unicode}
--- a/kallithea/templates/email_templates/pull_request.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/email_templates/pull_request.html	Mon Mar 14 00:36:08 2016 +0100
@@ -15,7 +15,7 @@
 <p>${_('Changesets')}:</p>
 <p style="white-space: pre-wrap;">
 %for r,r_msg in pr_revisions:
-<i>${h.short_id(r)}</i>:
+<i><a href="${h.canonical_url('changeset_home', repo_name=org_repo_name, revision=r)}">${h.short_id(r)}</a></i>:
 ${h.shorter(r_msg, 256)}
 
 %endfor
--- a/kallithea/templates/email_templates/pull_request.txt	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/email_templates/pull_request.txt	Mon Mar 14 00:36:08 2016 +0100
@@ -14,7 +14,7 @@
 
 ${_('Changesets')|n,unicode}:
 %for r,r_msg in pr_revisions:
-${h.short_id(r)|n,unicode}:
+${h.short_id(r)|n,unicode}: ${h.canonical_url('changeset_home', repo_name=org_repo_name, revision=r)}
 ${h.shorter(r_msg, 256)|n,unicode}
 
 %endfor
--- a/kallithea/templates/files/files_add.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/files/files_add.html	Mon Mar 14 00:36:08 2016 +0100
@@ -44,14 +44,14 @@
                   ${_('or')} <div class="btn btn-small" id="upload_file_enable">${_('Upload File')}</div>
               </span>
               <span id="upload_file_container" class="reviewer_ac" style="display:none">
-                  <input type="file"  size="20" name="upload_file" id="upload_file">
+                  <input type="file" size="20" name="upload_file" id="upload_file">
                   ${_('or')} <div class="btn btn-small" id="file_enable">${_('Create New File')}</div>
               </span>
           </h3>
             <div id="body" class="codeblock">
-            <div class="code-header" id="set_mode_header">
-                <label class="commit" for="set_mode">${_('New file mode')}</label>
-                <select id="set_mode" name="set_mode"/>
+            <div class="code-header" id="mimetype_header">
+                <label class="commit" for="mimetype">${_('New file type')}</label>
+                <select id="mimetype" name="mimetype"/>
             </div>
                 <div id="editor_container">
                     <pre id="editor_pre"></pre>
@@ -66,50 +66,53 @@
             </div>
             ${h.end_form()}
             <script type="text/javascript">
-            var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path)}";
-            var myCodeMirror = initCodeMirror('editor', "${request.script_name}", reset_url);
+                $(document).ready(function(){
+                    var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path)}";
+                    var myCodeMirror = initCodeMirror('editor', "${request.script_name}", reset_url);
 
-            //inject new modes, based on codeMirrors modeInfo object
-            $('#set_mode').each(function(){
-                var modes_select = this;
-                var index = 1;
-                for(var i=0;i<CodeMirror.modeInfo.length;i++){
-                    var m = CodeMirror.modeInfo[i];
-                    var opt = new Option(m.name, m.mime);
-                    $(opt).attr('mode', m.mode);
-                    if (m.mime == 'text/plain') {
-                        // default plain text
-                        $(opt).prop('selected', true);
-                        modes_select.options[0] = opt;
-                    } else {
-                        modes_select.options[index++] = opt;
-                    }
-                }
-            });
-            $('#set_mode').change(function(e){
-                var selected = e.currentTarget;
-                var node = selected.options[selected.selectedIndex];
-                var detected_mode = CodeMirror.findModeByMIME(node.value);
-                setCodeMirrorMode(myCodeMirror, detected_mode);
+                    //inject new modes, based on codeMirrors modeInfo object
+                    var $mimetype_select = $('#mimetype');
+                    $mimetype_select.each(function(){
+                        var modes_select = this;
+                        var index = 1;
+                        for(var i=0;i<CodeMirror.modeInfo.length;i++){
+                            var m = CodeMirror.modeInfo[i];
+                            var opt = new Option(m.name, m.mime);
+                            $(opt).attr('mode', m.mode);
+                            if (m.mime == 'text/plain') {
+                                // default plain text
+                                $(opt).prop('selected', true);
+                                modes_select.options[0] = opt;
+                            } else {
+                                modes_select.options[index++] = opt;
+                            }
+                        }
+                    });
+                    var $filename_input = $('#filename');
+                    $mimetype_select.change(function(e){
+                        var selected = e.currentTarget;
+                        var node = selected.options[selected.selectedIndex];
+                        var detected_mode = CodeMirror.findModeByMIME(node.value);
+                        setCodeMirrorMode(myCodeMirror, detected_mode);
 
-                var filenameInput = $('#filename');
-                var proposed_ext = CodeMirror.findExtensionByMode(detected_mode);
-                var file_data = CodeMirror.getFilenameAndExt(filenameInput.val());
-                var filename = file_data['filename'] || 'filename1';
-                filenameInput.val(filename + '.' + proposed_ext);
-            });
+                        var proposed_ext = CodeMirror.findExtensionByMode(detected_mode);
+                        var file_data = CodeMirror.getFilenameAndExt($filename_input.val());
+                        var filename = file_data['filename'] || 'filename1';
+                        $filename_input.val(filename + '.' + proposed_ext);
+                    });
 
-            // on type the new filename set mode
-            $('#filename').keyup(function(e){
-                var file_data = CodeMirror.getFilenameAndExt(this.value);
-                if(file_data['ext'] != null){
-                    var detected_mode = CodeMirror.findModeByExtension(file_data['ext']) || CodeMirror.findModeByMIME('text/plain');
-                    if (detected_mode){
-                        setCodeMirrorMode(myCodeMirror, detected_mode);
-                        $('#set_mode').val(detected_mode.mime);
-                    }
-                }
-            });
+                    // on type the new filename set mode
+                    $filename_input.keyup(function(e){
+                        var file_data = CodeMirror.getFilenameAndExt(this.value);
+                        if(file_data['ext'] != null){
+                            var detected_mode = CodeMirror.findModeByExtension(file_data['ext']) || CodeMirror.findModeByMIME('text/plain');
+                            if (detected_mode){
+                                setCodeMirrorMode(myCodeMirror, detected_mode);
+                                $mimetype_select.val(detected_mode.mime);
+                            }
+                        }
+                    });
+                });
             </script>
         </div>
     </div>
--- a/kallithea/templates/files/files_edit.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/files/files_edit.html	Mon Mar 14 00:36:08 2016 +0100
@@ -55,8 +55,8 @@
                       % endif
                     </div>
                 </div>
-                <label class="commit" for="set_mode">${_('Editing file')}: ${c.file.unicode_path}</label>
-                <select id="set_mode" name="set_mode"/>
+                <label class="commit" for="mimetype">${_('Editing file')}: ${c.file.unicode_path}</label>
+                <select id="mimetype" name="mimetype"/>
             </div>
                 <pre id="editor_pre"></pre>
                 <textarea id="editor" name="content" style="display:none">${h.escape(c.file.content)|n}</textarea>
@@ -73,40 +73,41 @@
 </div>
 
 <script type="text/javascript">
-$(document).ready(function(){
-    var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.file.path)}";
-    var myCodeMirror = initCodeMirror('editor', "${request.script_name}", reset_url);
+    $(document).ready(function(){
+        var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.file.path)}";
+        var myCodeMirror = initCodeMirror('editor', "${request.script_name}", reset_url);
 
-   //inject new modes, based on codeMirrors modeInfo object
-    $('#set_mode').each(function(){
-        var modes_select = this;
-        var index = 1;
-        for(var i=0;i<CodeMirror.modeInfo.length;i++){
-            var m = CodeMirror.modeInfo[i];
-            var opt = new Option(m.name, m.mime);
-            $(opt).attr('mode', m.mode);
-            if (m.mime == 'text/plain') {
-                // default plain text
-                $(opt).prop('selected', true);
-                modes_select.options[0] = opt;
-            } else {
-                modes_select.options[index++] = opt;
+       //inject new modes, based on codeMirrors modeInfo object
+        var $mimetype_select = $('#mimetype');
+        $mimetype_select.each(function(){
+            var modes_select = this;
+            var index = 1;
+            for(var i=0;i<CodeMirror.modeInfo.length;i++){
+                var m = CodeMirror.modeInfo[i];
+                var opt = new Option(m.name, m.mime);
+                $(opt).attr('mode', m.mode);
+                if (m.mime == 'text/plain') {
+                    // default plain text
+                    $(opt).prop('selected', true);
+                    modes_select.options[0] = opt;
+                } else {
+                    modes_select.options[index++] = opt;
+                }
             }
+        });
+        // try to detect the mode based on the file we edit
+        var detected_mode = CodeMirror.findModeByExtension("${c.file.extension}");
+        if(detected_mode){
+            setCodeMirrorMode(myCodeMirror, detected_mode);
+            $($mimetype_select.find('option[value="'+detected_mode.mime+'"]')[0]).prop('selected', true);
         }
+
+        $mimetype_select.on('change', function(e){
+            var selected = e.currentTarget;
+            var node = selected.options[selected.selectedIndex];
+            var detected_mode = CodeMirror.findModeByMIME(node.value);
+            setCodeMirrorMode(myCodeMirror, detected_mode);
+        });
     });
-    // try to detect the mode based on the file we edit
-    var detected_mode = CodeMirror.findModeByExtension("${c.file.extension}");
-    if(detected_mode){
-        setCodeMirrorMode(myCodeMirror, detected_mode);
-        $($('#set_mode option[value="'+detected_mode.mime+'"]')[0]).prop('selected', true);
-    }
-
-    $('#set_mode').on('change', function(e){
-        var selected = e.currentTarget;
-        var node = selected.options[selected.selectedIndex];
-        var detected_mode = CodeMirror.findModeByMIME(node.value);
-        setCodeMirrorMode(myCodeMirror, detected_mode);
-    });
-});
 </script>
 </%def>
--- a/kallithea/templates/files/files_source.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/files/files_source.html	Mon Mar 14 00:36:08 2016 +0100
@@ -63,15 +63,21 @@
           ${_('Binary file (%s)') % c.file.mimetype}
         </div>
       %else:
-        %if c.file.size < c.cut_off_limit:
+        %if c.file.size < c.cut_off_limit or c.fulldiff:
             %if c.annotate:
               ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
             %else:
               ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
             %endif
         %else:
-            ${_('File is too big to display')} ${h.link_to(_('Show as raw'),
-            h.url('files_raw_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path))}
+            <h4>
+            ${_('File is too big to display.')}
+            %if c.annotate:
+              ${h.link_to(_('Show full annotation anyway.'), h.url.current(fulldiff=1, **request.GET.mixed()))}
+            %else:
+              ${h.link_to(_('Show as raw.'), h.url('files_raw_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path))}
+            %endif
+            </h4>
         %endif
       %endif
     </div>
--- a/kallithea/templates/pullrequests/pullrequest_show.html	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/templates/pullrequests/pullrequest_show.html	Mon Mar 14 00:36:08 2016 +0100
@@ -43,7 +43,7 @@
             %endif
           </div>
           <div class="input">
-            <div style="white-space:pre-wrap; line-height: 14px">${h.urlify_commit(c.pull_request.description, c.pull_request.org_repo.repo_name)}</div>
+            <code style="white-space:pre-wrap; line-height: 14px">${h.urlify_commit(c.pull_request.description, c.pull_request.org_repo.repo_name)}</code>
           </div>
         </div>
 
@@ -185,23 +185,34 @@
                       %if cs.revision == c.cs_ranges[-1].revision:
                         <td>
                           %if editable:
-                            ${h.radio(name='updaterev', value='')}
+                            ${h.radio(name='updaterev', value='', checked=True)}
                           %endif
                         </td>
-                        <td colspan="2">${_("Current revision - no change")}</td>
+                        <td colspan="4">${_("Current revision - no change")}</td>
                       %else:
                         <td>
                           %if editable and cs.revision in c.avail_revs:
                             ${h.radio(name='updaterev', value=cs.raw_id)}
                           %endif
                         </td>
+                        <td style="width: 120px"><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
                         <td>${h.link_to(h.show_id(cs),h.url('changeset_home',repo_name=c.cs_repo.repo_name,revision=cs.raw_id))}</td>
-                        <td><div class="message" style="white-space:normal; height:1.1em; max-width: 500px; padding:0">${h.urlify_commit(cs.message, c.repo_name)}</div></td>
+                        <td>
+                          <div style="float: right; margin-top: -4px;">
+                            %for tag in cs.tags:
+                              <div class="tagtag" title="${_('Tag %s') % tag}">
+                                ${h.link_to(tag,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
+                              </div>
+                            %endfor
+                          </div>
+                          <div class="message" style="white-space:normal; height:1.1em; max-width: 500px; padding:0">${h.urlify_commit(cs.message, c.repo_name)}</div>
+                        </td>
                       %endif
                     </tr>
                   %endfor
                 </table>
               </div>
+              <div class="msg-div">${_("Pull requests do not change once created. Select a revision and save to replace this pull request with a new one.")}</div>
               %endif
               <div class="msg-div">${c.update_msg_other}</div>
             </div>
@@ -282,7 +293,7 @@
         %if editable:
           <div class="buttons">
             ${h.submit('pr-form-save',_('Save Changes'),class_="btn btn-small")}
-            ${h.submit('pr-form-clone',_('Save as New Pull Request'),class_="btn btn-small",disabled='disabled')}
+            ${h.submit('pr-form-clone',_('Save Updates as New Pull Request'),class_="btn btn-small",disabled='disabled')}
             ${h.reset('pr-form-reset',_('Cancel Changes'),class_="btn btn-small")}
           </div>
         %endif
@@ -356,9 +367,7 @@
     <div class="commentable-diff">
     <%namespace name="diff_block" file="/changeset/diff_block.html"/>
     ${diff_block.diff_block_js()}
-    %for fid, change, f, stat in c.files:
-      ${diff_block.diff_block_simple([c.changes[fid]])}
-    %endfor
+    ${diff_block.diff_block_simple(c.files, c.changes)}
     % if c.limited_diff:
       <h4>${_('Changeset was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}">${_('Show full diff anyway')}</a></h4>
     % endif
@@ -371,10 +380,7 @@
     ${comment.generate_comments()}
 
     ## main comment form and it status
-    ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
-                              pull_request_id=c.pull_request.pull_request_id),
-                       c.current_voting_result,
-                       is_pr=True, change_status=c.allowed_to_change_status)}
+    ${comment.comments(change_status=c.allowed_to_change_status)}
 
     <script type="text/javascript">
       $(document).ready(function(){
--- a/kallithea/tests/__init__.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/__init__.py	Mon Mar 14 00:36:08 2016 +0100
@@ -15,22 +15,12 @@
 """
 Pylons application test package
 
-This package assumes the Pylons environment is already loaded, such as
-when this script is imported from the `nosetests --with-pylons=test.ini`
-command.
+This package assumes the Pylons environment is already loaded.
 
 This module initializes the application via ``websetup`` (`paster
 setup-app`) and provides the base testing objects.
 
-nosetests -x - fail on first error
-nosetests kallithea.tests.functional.test_admin_settings:TestSettingsController.test_my_account
-nosetests --pdb --pdb-failures
-nosetests --with-coverage --cover-package=kallithea.model.validators kallithea.tests.test_validators
-
-optional FLAGS:
-    KALLITHEA_WHOOSH_TEST_DISABLE=1 - skip whoosh index building and tests
-    KALLITHEA_NO_TMP_PATH=1 - disable new temp path for tests, used mostly for test_vcs_operations
-
+Refer to docs/contributing.rst for details on running the test suite.
 """
 import os
 import re
@@ -51,7 +41,7 @@
 
 from routes.util import URLGenerator
 from webtest import TestApp
-from nose.plugins.skip import SkipTest
+import pytest
 
 from kallithea.lib.compat import unittest
 from kallithea import is_windows
@@ -67,9 +57,12 @@
 
 log = logging.getLogger(__name__)
 
+skipif = pytest.mark.skipif
+parametrize = pytest.mark.parametrize
+
 __all__ = [
-    'parameterized', 'environ', 'url', 'TestController',
-    'SkipTest', 'ldap_lib_installed', 'pam_lib_installed', 'BaseTestCase', 'init_stack',
+    'skipif', 'parametrize', 'parameterized', 'environ', 'url', 'TestController', 'TestControllerPytest',
+    'ldap_lib_installed', 'pam_lib_installed', 'BaseTestCase', 'init_stack',
     'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO',
     'HG_FORK', 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS',
     'TEST_USER_ADMIN_EMAIL', 'TEST_USER_REGULAR_LOGIN', 'TEST_USER_REGULAR_PASS',
@@ -77,7 +70,7 @@
     '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',
+    'GIT_REMOTE_REPO', 'SCM_TESTS', 'remove_all_notifications',
 ]
 
 # Invoke websetup with the current config file
@@ -87,7 +80,7 @@
 
 #SOME GLOBALS FOR TESTS
 
-TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
+TESTS_TMP_PATH = jn(tempfile.gettempdir(), 'rc_test_%s' % _RandomNameSequence().next())
 TEST_USER_ADMIN_LOGIN = 'test_admin'
 TEST_USER_ADMIN_PASS = 'test12'
 TEST_USER_ADMIN_EMAIL = 'test_admin@example.com'
@@ -100,14 +93,14 @@
 TEST_USER_REGULAR2_PASS = 'test12'
 TEST_USER_REGULAR2_EMAIL = 'test_regular2@example.com'
 
-HG_REPO = 'vcs_test_hg'
-GIT_REPO = 'vcs_test_git'
+HG_REPO = u'vcs_test_hg'
+GIT_REPO = u'vcs_test_git'
 
-NEW_HG_REPO = 'vcs_test_hg_new'
-NEW_GIT_REPO = 'vcs_test_git_new'
+NEW_HG_REPO = u'vcs_test_hg_new'
+NEW_GIT_REPO = u'vcs_test_git_new'
 
-HG_FORK = 'vcs_test_hg_fork'
-GIT_FORK = 'vcs_test_git_fork'
+HG_FORK = u'vcs_test_hg_fork'
+GIT_FORK = u'vcs_test_git_fork'
 
 ## VCS
 SCM_TESTS = ['hg', 'git']
@@ -151,8 +144,6 @@
 except ImportError:
     pam_lib_installed = False
 
-import logging
-
 class NullHandler(logging.Handler):
     def emit(self, record):
         pass
@@ -170,27 +161,29 @@
     h = NullHandler()
     logging.getLogger("kallithea").addHandler(h)
 
+def remove_all_notifications():
+    Notification.query().delete()
+
+    # Because query().delete() does not (by default) trigger cascades.
+    # http://docs.sqlalchemy.org/en/rel_0_7/orm/collections.html#passive-deletes
+    UserNotification.query().delete()
+
+    Session().commit()
 
 class BaseTestCase(unittest.TestCase):
+    """Unittest-style base test case. Deprecated in favor of pytest style."""
+
     def __init__(self, *args, **kwargs):
         self.wsgiapp = pylons.test.pylonsapp
         init_stack(self.wsgiapp.config)
         unittest.TestCase.__init__(self, *args, **kwargs)
 
-    def remove_all_notifications(self):
-        Notification.query().delete()
-
-        # Because query().delete() does not (by default) trigger cascades.
-        # http://docs.sqlalchemy.org/en/rel_0_7/orm/collections.html#passive-deletes
-        UserNotification.query().delete()
+class BaseTestController(object):
+    """Base test controller used by pytest and unittest tests controllers."""
 
-        Session().commit()
-
+    # Note: pytest base classes cannot have an __init__ method
 
-class TestController(BaseTestCase):
-
-    def __init__(self, *args, **kwargs):
-        BaseTestCase.__init__(self, *args, **kwargs)
+    def init(self):
         self.app = TestApp(self.wsgiapp)
         self.maxDiff = None
         self.index_location = config['app_conf']['index_dir']
@@ -203,7 +196,7 @@
                                   'password': password})
 
         if 'Invalid username or password' in response.body:
-            self.fail('could not login using %s %s' % (username, password))
+            pytest.fail('could not login using %s %s' % (username, password))
 
         self.assertEqual(response.status, '302 Found')
         self.assert_authenticated_user(response, username)
@@ -220,23 +213,62 @@
         user = user and User.get(user)
         user = user and user.username
         self.assertEqual(user, expected_username)
-        self.assertEqual(cookie.get('is_authenticated'), True)
 
     def authentication_token(self):
         return self.app.get(url('authentication_token')).body
 
     def checkSessionFlash(self, response, msg=None, skip=0, _matcher=lambda msg, m: msg in m):
         if 'flash' not in response.session:
-            self.fail(safe_str(u'msg `%s` not found - session has no flash:\n%s' % (msg, response)))
+            pytest.fail(safe_str(u'msg `%s` not found - session has no flash:\n%s' % (msg, response)))
         try:
             level, m = response.session['flash'][-1 - skip]
             if _matcher(msg, m):
                 return
         except IndexError:
             pass
-        self.fail(safe_str(u'msg `%s` not found in session flash (skipping %s): %s' %
+        pytest.fail(safe_str(u'msg `%s` not found in session flash (skipping %s): %s' %
                            (msg, skip,
                             ', '.join('`%s`' % m for level, m in response.session['flash']))))
 
     def checkSessionFlashRegex(self, response, regex, skip=0):
         self.checkSessionFlash(response, regex, skip=skip, _matcher=re.search)
+
+class TestController(BaseTestCase, BaseTestController):
+    """Deprecated unittest-style test controller"""
+
+    def __init__(self, *args, **kwargs):
+        super(TestController, self).__init__(*args, **kwargs)
+        self.init()
+
+class TestControllerPytest(BaseTestController):
+    """Pytest-style test controller"""
+
+    # Note: pytest base classes cannot have an __init__ method
+
+    @pytest.fixture(autouse=True)
+    def app_fixture(self):
+        self.wsgiapp = pylons.test.pylonsapp
+        init_stack(self.wsgiapp.config)
+        self.init()
+        return self.app
+
+    # transitional implementations of unittest.TestCase asserts
+    # Users of these should be converted to pytest's single 'assert' call
+    def assertEqual(self, first, second, msg=None):
+        assert first == second
+    def assertNotEqual(self, first, second, msg=None):
+        assert first != second
+    def assertTrue(self, expr, msg=None):
+        assert bool(expr) is True
+    def assertFalse(self, expr, msg=None):
+        assert bool(expr) is False
+    def assertIn(self, first, second, msg=None):
+        assert first in second
+    def assertNotIn(self, first, second, msg=None):
+        assert first not in second
+    def assertSetEqual(self, first, second, msg=None):
+        assert first == second
+    def assertListEqual(self, first, second, msg=None):
+        assert first == second
+    def assertDictEqual(self, first, second, msg=None):
+        assert first == second
--- a/kallithea/tests/api/api_base.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/api/api_base.py	Mon Mar 14 00:36:08 2016 +0100
@@ -33,13 +33,13 @@
 from kallithea.model.meta import Session
 from kallithea.model.scm import ScmModel
 from kallithea.model.gist import GistModel
-from kallithea.model.db import Repository, User, Setting
+from kallithea.model.db import Repository, User, Setting, Ui
 from kallithea.lib.utils2 import time_to_datetime
 
 
 API_URL = '/_admin/api'
-TEST_USER_GROUP = 'test_user_group'
-TEST_REPO_GROUP = 'test_repo_group'
+TEST_USER_GROUP = u'test_user_group'
+TEST_REPO_GROUP = u'test_repo_group'
 
 fixture = Fixture()
 
@@ -99,8 +99,8 @@
             username='test-api',
             password='test',
             email='test@example.com',
-            firstname='first',
-            lastname='last'
+            firstname=u'first',
+            lastname=u'last'
         )
         Session().commit()
         cls.TEST_USER_LOGIN = cls.test_user.username
@@ -278,9 +278,9 @@
         self._compare_error(id_, expected, given=response.body)
 
     def test_api_pull(self):
-        repo_name = 'test_pull'
+        repo_name = u'test_pull'
         r = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
-        r.clone_uri = os.path.join(TESTS_TMP_PATH, self.REPO)
+        r.clone_uri = os.path.join(Ui.get_by_key('paths', '/').ui_value, self.REPO)
         Session.add(r)
         Session.commit()
 
@@ -368,7 +368,7 @@
         self._compare_ok(id_, expected, given=response.body)
 
     def test_api_lock_repo_lock_aquire_by_non_admin(self):
-        repo_name = 'api_delete_me'
+        repo_name = u'api_delete_me'
         fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
                             cur_user=self.TEST_USER_LOGIN)
         try:
@@ -390,7 +390,7 @@
             fixture.destroy_repo(repo_name)
 
     def test_api_lock_repo_lock_aquire_non_admin_with_userid(self):
-        repo_name = 'api_delete_me'
+        repo_name = u'api_delete_me'
         fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
                             cur_user=self.TEST_USER_LOGIN)
         try:
@@ -465,7 +465,7 @@
         self._compare_ok(id_, expected, given=response.body)
 
     def test_api_lock_repo_lock_optional_not_locked(self):
-        repo_name = 'api_not_locked'
+        repo_name = u'api_not_locked'
         repo = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
                             cur_user=self.TEST_USER_LOGIN)
         self.assertEqual(repo.locked, [None, None])
@@ -516,7 +516,7 @@
         self._compare_ok(id_, expected, given=response.body)
 
     def test_api_get_locks_with_one_locked_repo(self):
-        repo_name = 'api_delete_me'
+        repo_name = u'api_delete_me'
         repo = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
                                    cur_user=self.TEST_USER_LOGIN)
         Repository.lock(repo, User.get_by_username(self.TEST_USER_LOGIN).user_id)
@@ -529,7 +529,7 @@
             fixture.destroy_repo(repo_name)
 
     def test_api_get_locks_with_one_locked_repo_for_specific_user(self):
-        repo_name = 'api_delete_me'
+        repo_name = u'api_delete_me'
         repo = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
                                    cur_user=self.TEST_USER_LOGIN)
         Repository.lock(repo, User.get_by_username(self.TEST_USER_LOGIN).user_id)
@@ -766,7 +766,7 @@
         self._compare_error(id_, expected, given=response.body)
 
     def test_api_get_repo(self):
-        new_group = 'some_new_group'
+        new_group = u'some_new_group'
         make_user_group(new_group)
         RepoModel().grant_user_group_permission(repo=self.REPO,
                                                 group_name=new_group,
@@ -975,7 +975,7 @@
             RepoModel().revoke_user_permission(self.REPO, self.TEST_USER_LOGIN)
 
     def test_api_create_repo(self):
-        repo_name = 'api-repo'
+        repo_name = u'api-repo'
         id_, params = _build_data(self.apikey, 'create_repo',
                                   repo_name=repo_name,
                                   owner=TEST_USER_ADMIN_LOGIN,
@@ -995,7 +995,7 @@
         fixture.destroy_repo(repo_name)
 
     def test_api_create_repo_and_repo_group(self):
-        repo_name = 'my_gr/api-repo'
+        repo_name = u'my_gr/api-repo'
         id_, params = _build_data(self.apikey, 'create_repo',
                                   repo_name=repo_name,
                                   owner=TEST_USER_ADMIN_LOGIN,
@@ -1012,11 +1012,11 @@
         expected = ret
         self._compare_ok(id_, expected, given=response.body)
         fixture.destroy_repo(repo_name)
-        fixture.destroy_repo_group('my_gr')
+        fixture.destroy_repo_group(u'my_gr')
 
     def test_api_create_repo_in_repo_group_without_permission(self):
-        repo_group_name = '%s/api-repo-repo' % TEST_REPO_GROUP
-        repo_name = '%s/api-repo' % repo_group_name
+        repo_group_name = u'%s/api-repo-repo' % TEST_REPO_GROUP
+        repo_name = u'%s/api-repo' % repo_group_name
 
         rg = fixture.create_repo_group(repo_group_name)
         Session().commit()
@@ -1048,7 +1048,7 @@
         fixture.destroy_repo_group(repo_group_name)
 
     def test_api_create_repo_unknown_owner(self):
-        repo_name = 'api-repo'
+        repo_name = u'api-repo'
         owner = 'i-dont-exist'
         id_, params = _build_data(self.apikey, 'create_repo',
                                   repo_name=repo_name,
@@ -1060,7 +1060,7 @@
         self._compare_error(id_, expected, given=response.body)
 
     def test_api_create_repo_dont_specify_owner(self):
-        repo_name = 'api-repo'
+        repo_name = u'api-repo'
         owner = 'i-dont-exist'
         id_, params = _build_data(self.apikey, 'create_repo',
                                   repo_name=repo_name,
@@ -1080,7 +1080,7 @@
         fixture.destroy_repo(repo_name)
 
     def test_api_create_repo_by_non_admin(self):
-        repo_name = 'api-repo'
+        repo_name = u'api-repo'
         owner = 'i-dont-exist'
         id_, params = _build_data(self.apikey_regular, 'create_repo',
                                   repo_name=repo_name,
@@ -1100,7 +1100,7 @@
         fixture.destroy_repo(repo_name)
 
     def test_api_create_repo_by_non_admin_specify_owner(self):
-        repo_name = 'api-repo'
+        repo_name = u'api-repo'
         owner = 'i-dont-exist'
         id_, params = _build_data(self.apikey_regular, 'create_repo',
                                   repo_name=repo_name,
@@ -1124,7 +1124,7 @@
 
     @mock.patch.object(RepoModel, 'create', crash)
     def test_api_create_repo_exception_occurred(self):
-        repo_name = 'api-repo'
+        repo_name = u'api-repo'
         id_, params = _build_data(self.apikey, 'create_repo',
                                   repo_name=repo_name,
                                   owner=TEST_USER_ADMIN_LOGIN,
@@ -1135,7 +1135,7 @@
 
     @parameterized.expand([
         ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
-        ('description', {'description': 'new description'}),
+        ('description', {'description': u'new description'}),
         ('active', {'active': True}),
         ('active', {'active': False}),
         ('clone_uri', {'clone_uri': 'http://example.com/repo'}),
@@ -1144,11 +1144,11 @@
         ('enable_statistics', {'enable_statistics': True}),
         ('enable_locking', {'enable_locking': True}),
         ('enable_downloads', {'enable_downloads': True}),
-        ('name', {'name': 'new_repo_name'}),
-        ('repo_group', {'group': 'test_group_for_update'}),
+        ('name', {'name': u'new_repo_name'}),
+        ('repo_group', {'group': u'test_group_for_update'}),
     ])
     def test_api_update_repo(self, changing_attr, updates):
-        repo_name = 'api_update_me'
+        repo_name = u'api_update_me'
         repo = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
         if changing_attr == 'repo_group':
             fixture.create_repo_group(updates['group'])
@@ -1159,7 +1159,7 @@
         if changing_attr == 'name':
             repo_name = updates['name']
         if changing_attr == 'repo_group':
-            repo_name = '/'.join([updates['group'], repo_name])
+            repo_name = u'/'.join([updates['group'], repo_name])
         try:
             expected = {
                 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo_name),
@@ -1172,7 +1172,7 @@
                 fixture.destroy_repo_group(updates['group'])
 
     def test_api_update_repo_repo_group_does_not_exist(self):
-        repo_name = 'admin_owned'
+        repo_name = u'admin_owned'
         fixture.create_repo(repo_name)
         updates = {'group': 'test_group_for_update'}
         id_, params = _build_data(self.apikey, 'update_repo',
@@ -1185,7 +1185,7 @@
             fixture.destroy_repo(repo_name)
 
     def test_api_update_repo_regular_user_not_allowed(self):
-        repo_name = 'admin_owned'
+        repo_name = u'admin_owned'
         fixture.create_repo(repo_name)
         updates = {'active': False}
         id_, params = _build_data(self.apikey_regular, 'update_repo',
@@ -1199,7 +1199,7 @@
 
     @mock.patch.object(RepoModel, 'update', crash)
     def test_api_update_repo_exception_occurred(self):
-        repo_name = 'api_update_me'
+        repo_name = u'api_update_me'
         fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
         id_, params = _build_data(self.apikey, 'update_repo',
                                   repoid=repo_name, owner=TEST_USER_ADMIN_LOGIN,)
@@ -1211,8 +1211,8 @@
             fixture.destroy_repo(repo_name)
 
     def test_api_update_repo_regular_user_change_repo_name(self):
-        repo_name = 'admin_owned'
-        new_repo_name = 'new_repo_name'
+        repo_name = u'admin_owned'
+        new_repo_name = u'new_repo_name'
         fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
         RepoModel().grant_user_permission(repo=repo_name,
                                           user=self.TEST_USER_LOGIN,
@@ -1231,8 +1231,8 @@
             fixture.destroy_repo(new_repo_name)
 
     def test_api_update_repo_regular_user_change_repo_name_allowed(self):
-        repo_name = 'admin_owned'
-        new_repo_name = 'new_repo_name'
+        repo_name = u'admin_owned'
+        new_repo_name = u'new_repo_name'
         repo = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
         RepoModel().grant_user_permission(repo=repo_name,
                                           user=self.TEST_USER_LOGIN,
@@ -1254,7 +1254,7 @@
             fixture.destroy_repo(new_repo_name)
 
     def test_api_update_repo_regular_user_change_owner(self):
-        repo_name = 'admin_owned'
+        repo_name = u'admin_owned'
         fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
         RepoModel().grant_user_permission(repo=repo_name,
                                           user=self.TEST_USER_LOGIN,
@@ -1270,7 +1270,7 @@
             fixture.destroy_repo(repo_name)
 
     def test_api_delete_repo(self):
-        repo_name = 'api_delete_me'
+        repo_name = u'api_delete_me'
         fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
 
         id_, params = _build_data(self.apikey, 'delete_repo',
@@ -1288,7 +1288,7 @@
             fixture.destroy_repo(repo_name)
 
     def test_api_delete_repo_by_non_admin(self):
-        repo_name = 'api_delete_me'
+        repo_name = u'api_delete_me'
         fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
                             cur_user=self.TEST_USER_LOGIN)
         id_, params = _build_data(self.apikey_regular, 'delete_repo',
@@ -1306,7 +1306,7 @@
             fixture.destroy_repo(repo_name)
 
     def test_api_delete_repo_by_non_admin_no_permission(self):
-        repo_name = 'api_delete_me'
+        repo_name = u'api_delete_me'
         fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
         try:
             id_, params = _build_data(self.apikey_regular, 'delete_repo',
@@ -1318,7 +1318,7 @@
             fixture.destroy_repo(repo_name)
 
     def test_api_delete_repo_exception_occurred(self):
-        repo_name = 'api_delete_me'
+        repo_name = u'api_delete_me'
         fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
         try:
             with mock.patch.object(RepoModel, 'delete', crash):
@@ -1332,7 +1332,7 @@
             fixture.destroy_repo(repo_name)
 
     def test_api_fork_repo(self):
-        fork_name = 'api-repo-fork'
+        fork_name = u'api-repo-fork'
         id_, params = _build_data(self.apikey, 'fork_repo',
                                   repoid=self.REPO,
                                   fork_name=fork_name,
@@ -1351,7 +1351,7 @@
         fixture.destroy_repo(fork_name)
 
     def test_api_fork_repo_non_admin(self):
-        fork_name = 'api-repo-fork'
+        fork_name = u'api-repo-fork'
         id_, params = _build_data(self.apikey_regular, 'fork_repo',
                                   repoid=self.REPO,
                                   fork_name=fork_name,
@@ -1369,7 +1369,7 @@
         fixture.destroy_repo(fork_name)
 
     def test_api_fork_repo_non_admin_specify_owner(self):
-        fork_name = 'api-repo-fork'
+        fork_name = u'api-repo-fork'
         id_, params = _build_data(self.apikey_regular, 'fork_repo',
                                   repoid=self.REPO,
                                   fork_name=fork_name,
@@ -1384,7 +1384,7 @@
         RepoModel().grant_user_permission(repo=self.REPO,
                                           user=self.TEST_USER_LOGIN,
                                           perm='repository.none')
-        fork_name = 'api-repo-fork'
+        fork_name = u'api-repo-fork'
         id_, params = _build_data(self.apikey_regular, 'fork_repo',
                                   repoid=self.REPO,
                                   fork_name=fork_name,
@@ -1398,7 +1398,7 @@
                            ('write', 'repository.write'),
                            ('admin', 'repository.admin')])
     def test_api_fork_repo_non_admin_no_create_repo_permission(self, name, perm):
-        fork_name = 'api-repo-fork'
+        fork_name = u'api-repo-fork'
         # regardless of base repository permission, forking is disallowed
         # when repository creation is disabled
         RepoModel().grant_user_permission(repo=self.REPO,
@@ -1416,7 +1416,7 @@
         fixture.destroy_repo(fork_name)
 
     def test_api_fork_repo_unknown_owner(self):
-        fork_name = 'api-repo-fork'
+        fork_name = u'api-repo-fork'
         owner = 'i-dont-exist'
         id_, params = _build_data(self.apikey, 'fork_repo',
                                   repoid=self.REPO,
@@ -1428,11 +1428,11 @@
         self._compare_error(id_, expected, given=response.body)
 
     def test_api_fork_repo_fork_exists(self):
-        fork_name = 'api-repo-fork'
+        fork_name = u'api-repo-fork'
         fixture.create_fork(self.REPO, fork_name)
 
         try:
-            fork_name = 'api-repo-fork'
+            fork_name = u'api-repo-fork'
 
             id_, params = _build_data(self.apikey, 'fork_repo',
                                       repoid=self.REPO,
@@ -1461,7 +1461,7 @@
 
     @mock.patch.object(RepoModel, 'create_fork', crash)
     def test_api_fork_repo_exception_occurred(self):
-        fork_name = 'api-repo-fork'
+        fork_name = u'api-repo-fork'
         id_, params = _build_data(self.apikey, 'fork_repo',
                                   repoid=self.REPO,
                                   fork_name=fork_name,
@@ -1490,7 +1490,7 @@
         self._compare_ok(id_, expected, given=response.body)
 
     def test_api_get_user_groups(self):
-        gr_name = 'test_user_group2'
+        gr_name = u'test_user_group2'
         make_user_group(gr_name)
 
         id_, params = _build_data(self.apikey, 'get_user_groups', )
@@ -1498,7 +1498,7 @@
 
         try:
             expected = []
-            for gr_name in [TEST_USER_GROUP, 'test_user_group2']:
+            for gr_name in [TEST_USER_GROUP, u'test_user_group2']:
                 user_group = UserGroupModel().get_group(gr_name)
                 ret = user_group.get_api_data()
                 expected.append(ret)
@@ -1507,7 +1507,7 @@
             fixture.destroy_user_group(gr_name)
 
     def test_api_create_user_group(self):
-        group_name = 'some_new_group'
+        group_name = u'some_new_group'
         id_, params = _build_data(self.apikey, 'create_user_group',
                                   group_name=group_name)
         response = api_call(self, params)
@@ -1533,7 +1533,7 @@
 
     @mock.patch.object(UserGroupModel, 'create', crash)
     def test_api_get_user_group_exception_occurred(self):
-        group_name = 'exception_happens'
+        group_name = u'exception_happens'
         id_, params = _build_data(self.apikey, 'create_user_group',
                                   group_name=group_name)
         response = api_call(self, params)
@@ -1541,13 +1541,13 @@
         expected = 'failed to create group `%s`' % group_name
         self._compare_error(id_, expected, given=response.body)
 
-    @parameterized.expand([('group_name', {'group_name': 'new_group_name'}),
-                           ('group_name', {'group_name': 'test_group_for_update'}),
+    @parameterized.expand([('group_name', {'group_name': u'new_group_name'}),
+                           ('group_name', {'group_name': u'test_group_for_update'}),
                            ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
                            ('active', {'active': False}),
                            ('active', {'active': True})])
     def test_api_update_user_group(self, changing_attr, updates):
-        gr_name = 'test_group_for_update'
+        gr_name = u'test_group_for_update'
         user_group = fixture.create_user_group(gr_name)
         id_, params = _build_data(self.apikey, 'update_user_group',
                                   usergroupid=gr_name, **updates)
@@ -1567,7 +1567,7 @@
 
     @mock.patch.object(UserGroupModel, 'update', crash)
     def test_api_update_user_group_exception_occurred(self):
-        gr_name = 'test_group'
+        gr_name = u'test_group'
         fixture.create_user_group(gr_name)
         id_, params = _build_data(self.apikey, 'update_user_group',
                                   usergroupid=gr_name)
@@ -1579,7 +1579,7 @@
             fixture.destroy_user_group(gr_name)
 
     def test_api_add_user_to_user_group(self):
-        gr_name = 'test_group'
+        gr_name = u'test_group'
         fixture.create_user_group(gr_name)
         id_, params = _build_data(self.apikey, 'add_user_to_user_group',
                                   usergroupid=gr_name,
@@ -1606,7 +1606,7 @@
 
     @mock.patch.object(UserGroupModel, 'add_user_to_group', crash)
     def test_api_add_user_to_user_group_exception_occurred(self):
-        gr_name = 'test_group'
+        gr_name = u'test_group'
         fixture.create_user_group(gr_name)
         id_, params = _build_data(self.apikey, 'add_user_to_user_group',
                                   usergroupid=gr_name,
@@ -1620,7 +1620,7 @@
             fixture.destroy_user_group(gr_name)
 
     def test_api_remove_user_from_user_group(self):
-        gr_name = 'test_group_3'
+        gr_name = u'test_group_3'
         gr = fixture.create_user_group(gr_name)
         UserGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
         Session().commit()
@@ -1641,7 +1641,7 @@
 
     @mock.patch.object(UserGroupModel, 'remove_user_from_group', crash)
     def test_api_remove_user_from_user_group_exception_occurred(self):
-        gr_name = 'test_group_3'
+        gr_name = u'test_group_3'
         gr = fixture.create_user_group(gr_name)
         UserGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
         Session().commit()
@@ -1656,7 +1656,7 @@
             fixture.destroy_user_group(gr_name)
 
     def test_api_delete_user_group(self):
-        gr_name = 'test_group'
+        gr_name = u'test_group'
         ugroup = fixture.create_user_group(gr_name)
         gr_id = ugroup.users_group_id
         id_, params = _build_data(self.apikey, 'delete_user_group',
@@ -1675,7 +1675,7 @@
                 fixture.destroy_user_group(gr_name)
 
     def test_api_delete_user_group_that_is_assigned(self):
-        gr_name = 'test_group'
+        gr_name = u'test_group'
         ugroup = fixture.create_user_group(gr_name)
         gr_id = ugroup.users_group_id
 
@@ -1695,7 +1695,7 @@
                 fixture.destroy_user_group(gr_name)
 
     def test_api_delete_user_group_exception_occurred(self):
-        gr_name = 'test_group'
+        gr_name = u'test_group'
         ugroup = fixture.create_user_group(gr_name)
         gr_id = ugroup.users_group_id
         id_, params = _build_data(self.apikey, 'delete_user_group',
--- a/kallithea/tests/fixture.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/fixture.py	Mon Mar 14 00:36:08 2016 +0100
@@ -76,7 +76,7 @@
             repo_type='hg',
             clone_uri='',
             repo_group=u'-1',
-            repo_description='DESC',
+            repo_description=u'DESC',
             repo_private=False,
             repo_landing_rev='rev:tip',
             repo_copy_permissions=False,
@@ -95,7 +95,7 @@
     def _get_group_create_params(self, **custom):
         defs = dict(
             group_name=None,
-            group_description='DESC',
+            group_description=u'DESC',
             group_parent_id=None,
             perms_updates=[],
             perms_new=[],
@@ -111,8 +111,8 @@
             username=name,
             password='qweqwe',
             email='%s+test@example.com' % name,
-            firstname='TestUser',
-            lastname='Test',
+            firstname=u'TestUser',
+            lastname=u'Test',
             active=True,
             admin=False,
             extern_type='internal',
@@ -125,7 +125,7 @@
     def _get_user_group_create_params(self, name, **custom):
         defs = dict(
             users_group_name=name,
-            user_group_description='DESC',
+            user_group_description=u'DESC',
             users_group_active=True,
             user_group_data={},
         )
@@ -259,7 +259,7 @@
         Session().commit()
 
     def load_resource(self, resource_name, strip=True):
-        with open(os.path.join(FIXTURES, resource_name)) as f:
+        with open(os.path.join(FIXTURES, resource_name), 'rb') as f:
             source = f.read()
             if strip:
                 source = source.strip()
--- a/kallithea/tests/functional/test_admin_auth_settings.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_admin_auth_settings.py	Mon Mar 14 00:36:08 2016 +0100
@@ -2,7 +2,7 @@
 from kallithea.model.db import Setting
 
 
-class TestAuthSettingsController(TestController):
+class TestAuthSettingsController(TestControllerPytest):
     def _enable_plugins(self, plugins_list):
         test_url = url(controller='admin/auth_settings',
                        action='auth_settings')
@@ -21,10 +21,9 @@
                                     action='index'))
         response.mustcontain('Authentication Plugins')
 
+    @skipif(not ldap_lib_installed, reason='skipping due to missing ldap lib')
     def test_ldap_save_settings(self):
         self.log_user()
-        if not ldap_lib_installed:
-            raise SkipTest('skipping due to missing ldap lib')
 
         params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_ldap')
         params.update({'auth_ldap_host': u'dc.example.com',
@@ -51,10 +50,9 @@
         self.assertEqual(new_settings['auth_ldap_host'], u'dc.example.com',
                          'fail db write compare')
 
+    @skipif(not ldap_lib_installed, reason='skipping due to missing ldap lib')
     def test_ldap_error_form_wrong_port_number(self):
         self.log_user()
-        if not ldap_lib_installed:
-            raise SkipTest('skipping due to missing ldap lib')
 
         params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_ldap')
         params.update({'auth_ldap_host': '',
@@ -78,10 +76,9 @@
         response.mustcontain("""<span class="error-message">"""
                              """Please enter a number</span>""")
 
+    @skipif(not ldap_lib_installed, reason='skipping due to missing ldap lib')
     def test_ldap_error_form(self):
         self.log_user()
-        if not ldap_lib_installed:
-            raise SkipTest('skipping due to missing ldap lib')
 
         params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_ldap')
         params.update({'auth_ldap_host': 'Host',
@@ -135,6 +132,9 @@
     def test_container_auth_login_header(self):
         self._container_auth_setup(
             auth_container_header='THE_USER_NAME',
+            auth_container_email_header='',
+            auth_container_firstname_header='',
+            auth_container_lastname_header='',
             auth_container_fallback_header='',
             auth_container_clean_username='False',
         )
@@ -143,9 +143,34 @@
             resulting_username='john@example.org',
         )
 
+    def test_container_auth_login_header_attr(self):
+        self._container_auth_setup(
+            auth_container_header='THE_USER_NAME',
+            auth_container_email_header='THE_USER_EMAIL',
+            auth_container_firstname_header='THE_USER_FIRSTNAME',
+            auth_container_lastname_header='THE_USER_LASTNAME',
+            auth_container_fallback_header='',
+            auth_container_clean_username='False',
+        )
+        response = self.app.get(
+            url=url(controller='admin/my_account', action='my_account'),
+            extra_environ={'THE_USER_NAME': 'johnd',
+                           'THE_USER_EMAIL': 'john@example.org',
+                           'THE_USER_FIRSTNAME': 'John',
+                           'THE_USER_LASTNAME': 'Doe',
+                           }
+        )
+        self.assertEqual(response.form['email'].value, 'john@example.org')
+        self.assertEqual(response.form['firstname'].value, 'John')
+        self.assertEqual(response.form['lastname'].value, 'Doe')
+
+
     def test_container_auth_login_fallback_header(self):
         self._container_auth_setup(
             auth_container_header='THE_USER_NAME',
+            auth_container_email_header='',
+            auth_container_firstname_header='',
+            auth_container_lastname_header='',
             auth_container_fallback_header='HTTP_X_YZZY',
             auth_container_clean_username='False',
         )
@@ -157,6 +182,9 @@
     def test_container_auth_clean_username_at(self):
         self._container_auth_setup(
             auth_container_header='REMOTE_USER',
+            auth_container_email_header='',
+            auth_container_firstname_header='',
+            auth_container_lastname_header='',
             auth_container_fallback_header='',
             auth_container_clean_username='True',
         )
@@ -168,6 +196,9 @@
     def test_container_auth_clean_username_backslash(self):
         self._container_auth_setup(
             auth_container_header='REMOTE_USER',
+            auth_container_email_header='',
+            auth_container_firstname_header='',
+            auth_container_lastname_header='',
             auth_container_fallback_header='',
             auth_container_clean_username='True',
         )
@@ -179,6 +210,9 @@
     def test_container_auth_no_logout(self):
         self._container_auth_setup(
             auth_container_header='REMOTE_USER',
+            auth_container_email_header='',
+            auth_container_firstname_header='',
+            auth_container_lastname_header='',
             auth_container_fallback_header='',
             auth_container_clean_username='True',
         )
@@ -208,12 +242,10 @@
         self.assertEqual(new_settings['auth_crowd_host'], u'hostname',
                          'fail db write compare')
 
+    @skipif(not pam_lib_installed, reason='skipping due to missing pam lib')
     def test_pam_save_settings(self):
         self.log_user()
 
-        if not pam_lib_installed:
-            raise SkipTest('skipping due to missing pam lib')
-
         params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_pam')
         params.update({'auth_pam_service': 'kallithea',
                        'auth_pam_gecos': '^foo-.*'})
--- a/kallithea/tests/functional/test_admin_defaults.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_admin_defaults.py	Mon Mar 14 00:36:08 2016 +0100
@@ -2,7 +2,7 @@
 from kallithea.model.db import Setting
 
 
-class TestDefaultsController(TestController):
+class TestDefaultsController(TestControllerPytest):
 
     def test_index(self):
         self.log_user()
--- a/kallithea/tests/functional/test_admin_gists.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_admin_gists.py	Mon Mar 14 00:36:08 2016 +0100
@@ -129,9 +129,9 @@
         self.log_user()
         response = self.app.get(url('new_gist'))
 
-    def test_update(self):
-        self.skipTest('not implemented')
-        response = self.app.put(url('gist', gist_id=1))
+    # Functionality currently not implemented
+    #def test_update(self):
+    #    response = self.app.put(url('gist', gist_id=1))
 
     def test_delete(self):
         self.log_user()
--- a/kallithea/tests/functional/test_admin_notifications.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_admin_notifications.py	Mon Mar 14 00:36:08 2016 +0100
@@ -9,14 +9,14 @@
 
 class TestNotificationsController(TestController):
     def setUp(self):
-        self.remove_all_notifications()
+        remove_all_notifications()
 
     def test_index(self):
         self.log_user()
 
         u1 = UserModel().create_or_update(username='u1', password='qweqwe',
                                           email='u1@example.com',
-                                          firstname='u1', lastname='u1')
+                                          firstname=u'u1', lastname=u'u1')
         u1 = u1.user_id
 
         response = self.app.get(url('notifications'))
@@ -35,10 +35,10 @@
 
         u1 = UserModel().create_or_update(username='u1', password='qweqwe',
                                                email='u1@example.com',
-                                               firstname='u1', lastname='u1')
+                                               firstname=u'u1', lastname=u'u1')
         u2 = UserModel().create_or_update(username='u2', password='qweqwe',
                                                email='u2@example.com',
-                                               firstname='u2', lastname='u2')
+                                               firstname=u'u2', lastname=u'u2')
 
         # make notifications
         notification = NotificationModel().create(created_by=cur_user,
@@ -69,10 +69,10 @@
         cur_user = self._get_logged_user()
         u1 = UserModel().create_or_update(username='u1', password='qweqwe',
                                           email='u1@example.com',
-                                          firstname='u1', lastname='u1')
+                                          firstname=u'u1', lastname=u'u1')
         u2 = UserModel().create_or_update(username='u2', password='qweqwe',
                                           email='u2@example.com',
-                                          firstname='u2', lastname='u2')
+                                          firstname=u'u2', lastname=u'u2')
 
         subject = u'test'
         notif_body = u'hi there'
--- a/kallithea/tests/functional/test_admin_permissions.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_admin_permissions.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,7 +1,9 @@
+import time
+
 from kallithea.model.db import User, UserIpMap
 from kallithea.tests import *
 
-class TestAdminPermissionsController(TestController):
+class TestAdminPermissionsController(TestControllerPytest):
 
     def test_index(self):
         self.log_user()
@@ -21,7 +23,13 @@
                                  params=dict(new_ip='127.0.0.0/24',
                                  _authentication_token=self.authentication_token()))
 
-        response = self.app.get(url('admin_permissions_ips'))
+        # sleep more than beaker.cache.sql_cache_short.expire to expire user cache
+        time.sleep(1.5)
+        self.app.get(url('admin_permissions_ips'), status=302)
+
+        # REMOTE_ADDR must match 127.0.0.0/24
+        response = self.app.get(url('admin_permissions_ips'),
+                                extra_environ={'REMOTE_ADDR': '127.0.0.1'})
         response.mustcontain('127.0.0.0/24')
         response.mustcontain('127.0.0.0 - 127.0.0.255')
 
@@ -33,7 +41,11 @@
         response = self.app.post(url('edit_user_ips', id=default_user_id),
                                  params=dict(_method='delete',
                                              del_ip_id=del_ip_id,
-                                             _authentication_token=self.authentication_token()))
+                                             _authentication_token=self.authentication_token()),
+                                 extra_environ={'REMOTE_ADDR': '127.0.0.1'})
+
+        # sleep more than beaker.cache.sql_cache_short.expire to expire user cache
+        time.sleep(1.5)
 
         response = self.app.get(url('admin_permissions_ips'))
         response.mustcontain('All IP addresses are allowed')
--- a/kallithea/tests/functional/test_admin_repo_groups.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_admin_repo_groups.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,4 +1,4 @@
 from kallithea.tests import *
 
-class TestRepoGroupsController(TestController):
+class TestRepoGroupsController(TestControllerPytest):
     pass
--- a/kallithea/tests/functional/test_admin_repos.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_admin_repos.py	Mon Mar 14 00:36:08 2016 +0100
@@ -5,8 +5,9 @@
 import urllib
 
 from kallithea.lib import vcs
-from kallithea.model.db import Repository, RepoGroup, UserRepoToPerm, User,\
-    Permission
+from kallithea.lib.utils2 import safe_str, safe_unicode
+from kallithea.model.db import Repository, RepoGroup, UserRepoToPerm, User, \
+    Permission, Ui
 from kallithea.model.user import UserModel
 from kallithea.tests import *
 from kallithea.model.repo_group import RepoGroupModel
@@ -18,10 +19,10 @@
 
 
 def _get_permission_for_user(user, repo):
-    perm = UserRepoToPerm.query()\
+    perm = UserRepoToPerm.query() \
                 .filter(UserRepoToPerm.repository ==
-                        Repository.get_by_repo_name(repo))\
-                .filter(UserRepoToPerm.user == User.get_by_username(user))\
+                        Repository.get_by_repo_name(repo)) \
+                .filter(UserRepoToPerm.user == User.get_by_username(user)) \
                 .all()
     return perm
 
@@ -51,7 +52,7 @@
     def test_create(self):
         self.log_user()
         repo_name = self.NEW_REPO
-        description = 'description for newly created repo'
+        description = u'description for newly created repo'
         response = self.app.post(url('repos'),
                         fixture._get_repo_create_params(repo_private=False,
                                                 repo_name=repo_name,
@@ -66,7 +67,7 @@
                                % (repo_name, repo_name))
 
         # test if the repo was created in the database
-        new_repo = Session().query(Repository)\
+        new_repo = Session().query(Repository) \
             .filter(Repository.repo_name == repo_name).one()
 
         self.assertEqual(new_repo.repo_name, repo_name)
@@ -79,63 +80,26 @@
 
         # test if the repository was created on filesystem
         try:
-            vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
+            vcs.get_repo(safe_str(os.path.join(Ui.get_by_key('paths', '/').ui_value, repo_name)))
         except vcs.exceptions.VCSError:
-            self.fail('no repo %s in filesystem' % repo_name)
+            pytest.fail('no repo %s in filesystem' % repo_name)
 
         RepoModel().delete(repo_name)
         Session().commit()
 
-    def test_create_non_ascii(self):
-        self.log_user()
-        non_ascii = "ąęł"
-        repo_name = "%s%s" % (self.NEW_REPO, non_ascii)
-        repo_name_unicode = repo_name.decode('utf8')
-        description = 'description for newly created repo' + non_ascii
-        description_unicode = description.decode('utf8')
-        response = self.app.post(url('repos'),
-                        fixture._get_repo_create_params(repo_private=False,
-                                                repo_name=repo_name,
-                                                repo_type=self.REPO_TYPE,
-                                                repo_description=description,
-                                                _authentication_token=self.authentication_token()))
-        ## run the check page that triggers the flash message
-        response = self.app.get(url('repo_check_home', repo_name=repo_name))
-        self.assertEqual(response.json, {u'result': True})
-        self.checkSessionFlash(response,
-                               u'Created repository <a href="/%s">%s</a>'
-                               % (urllib.quote(repo_name), repo_name_unicode))
-        # test if the repo was created in the database
-        new_repo = Session().query(Repository)\
-            .filter(Repository.repo_name == repo_name_unicode).one()
-
-        self.assertEqual(new_repo.repo_name, repo_name_unicode)
-        self.assertEqual(new_repo.description, description_unicode)
-
-        # test if the repository is visible in the list ?
-        response = self.app.get(url('summary_home', repo_name=repo_name))
-        response.mustcontain(repo_name)
-        response.mustcontain(self.REPO_TYPE)
-
-        # test if the repository was created on filesystem
-        try:
-            vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
-        except vcs.exceptions.VCSError:
-            self.fail('no repo %s in filesystem' % repo_name)
-
     def test_create_in_group(self):
         self.log_user()
 
         ## create GROUP
-        group_name = 'sometest_%s' % self.REPO_TYPE
+        group_name = u'sometest_%s' % self.REPO_TYPE
         gr = RepoGroupModel().create(group_name=group_name,
-                                     group_description='test',
+                                     group_description=u'test',
                                      owner=TEST_USER_ADMIN_LOGIN)
         Session().commit()
 
-        repo_name = 'ingroup'
+        repo_name = u'ingroup'
         repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
-        description = 'description for newly created repo'
+        description = u'description for newly created repo'
         response = self.app.post(url('repos'),
                         fixture._get_repo_create_params(repo_private=False,
                                                 repo_name=repo_name,
@@ -150,7 +114,7 @@
                                'Created repository <a href="/%s">%s</a>'
                                % (repo_name_full, repo_name_full))
         # test if the repo was created in the database
-        new_repo = Session().query(Repository)\
+        new_repo = Session().query(Repository) \
             .filter(Repository.repo_name == repo_name_full).one()
         new_repo_id = new_repo.repo_id
 
@@ -162,17 +126,17 @@
         response.mustcontain(repo_name_full)
         response.mustcontain(self.REPO_TYPE)
 
-        inherited_perms = UserRepoToPerm.query()\
+        inherited_perms = UserRepoToPerm.query() \
             .filter(UserRepoToPerm.repository_id == new_repo_id).all()
         self.assertEqual(len(inherited_perms), 1)
 
         # test if the repository was created on filesystem
         try:
-            vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name_full))
+            vcs.get_repo(safe_str(os.path.join(Ui.get_by_key('paths', '/').ui_value, repo_name_full)))
         except vcs.exceptions.VCSError:
             RepoGroupModel().delete(group_name)
             Session().commit()
-            self.fail('no repo %s in filesystem' % repo_name)
+            pytest.fail('no repo %s in filesystem' % repo_name)
 
         RepoModel().delete(repo_name_full)
         RepoGroupModel().delete(group_name)
@@ -198,21 +162,21 @@
         Session().commit()
 
         ## create GROUP
-        group_name = 'reg_sometest_%s' % self.REPO_TYPE
+        group_name = u'reg_sometest_%s' % self.REPO_TYPE
         gr = RepoGroupModel().create(group_name=group_name,
-                                     group_description='test',
+                                     group_description=u'test',
                                      owner=TEST_USER_ADMIN_LOGIN)
         Session().commit()
 
-        group_name_allowed = 'reg_sometest_allowed_%s' % self.REPO_TYPE
+        group_name_allowed = u'reg_sometest_allowed_%s' % self.REPO_TYPE
         gr_allowed = RepoGroupModel().create(group_name=group_name_allowed,
-                                     group_description='test',
+                                     group_description=u'test',
                                      owner=TEST_USER_REGULAR_LOGIN)
         Session().commit()
 
-        repo_name = 'ingroup'
+        repo_name = u'ingroup'
         repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
-        description = 'description for newly created repo'
+        description = u'description for newly created repo'
         response = self.app.post(url('repos'),
                         fixture._get_repo_create_params(repo_private=False,
                                                 repo_name=repo_name,
@@ -224,9 +188,9 @@
         response.mustcontain('Invalid value')
 
         # user is allowed to create in this group
-        repo_name = 'ingroup'
+        repo_name = u'ingroup'
         repo_name_full = RepoGroup.url_sep().join([group_name_allowed, repo_name])
-        description = 'description for newly created repo'
+        description = u'description for newly created repo'
         response = self.app.post(url('repos'),
                         fixture._get_repo_create_params(repo_private=False,
                                                 repo_name=repo_name,
@@ -242,7 +206,7 @@
                                'Created repository <a href="/%s">%s</a>'
                                % (repo_name_full, repo_name_full))
         # test if the repo was created in the database
-        new_repo = Session().query(Repository)\
+        new_repo = Session().query(Repository) \
             .filter(Repository.repo_name == repo_name_full).one()
         new_repo_id = new_repo.repo_id
 
@@ -254,17 +218,17 @@
         response.mustcontain(repo_name_full)
         response.mustcontain(self.REPO_TYPE)
 
-        inherited_perms = UserRepoToPerm.query()\
+        inherited_perms = UserRepoToPerm.query() \
             .filter(UserRepoToPerm.repository_id == new_repo_id).all()
         self.assertEqual(len(inherited_perms), 1)
 
         # test if the repository was created on filesystem
         try:
-            vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name_full))
+            vcs.get_repo(safe_str(os.path.join(Ui.get_by_key('paths', '/').ui_value, repo_name_full)))
         except vcs.exceptions.VCSError:
             RepoGroupModel().delete(group_name)
             Session().commit()
-            self.fail('no repo %s in filesystem' % repo_name)
+            pytest.fail('no repo %s in filesystem' % repo_name)
 
         RepoModel().delete(repo_name_full)
         RepoGroupModel().delete(group_name)
@@ -275,9 +239,9 @@
         self.log_user()
 
         ## create GROUP
-        group_name = 'sometest_%s' % self.REPO_TYPE
+        group_name = u'sometest_%s' % self.REPO_TYPE
         gr = RepoGroupModel().create(group_name=group_name,
-                                     group_description='test',
+                                     group_description=u'test',
                                      owner=TEST_USER_ADMIN_LOGIN)
         perm = Permission.get_by_key('repository.write')
         RepoGroupModel().grant_user_permission(gr, TEST_USER_REGULAR_LOGIN, perm)
@@ -285,9 +249,9 @@
         ## add repo permissions
         Session().commit()
 
-        repo_name = 'ingroup_inherited_%s' % self.REPO_TYPE
+        repo_name = u'ingroup_inherited_%s' % self.REPO_TYPE
         repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
-        description = 'description for newly created repo'
+        description = u'description for newly created repo'
         response = self.app.post(url('repos'),
                         fixture._get_repo_create_params(repo_private=False,
                                                 repo_name=repo_name,
@@ -303,7 +267,7 @@
                                'Created repository <a href="/%s">%s</a>'
                                % (repo_name_full, repo_name_full))
         # test if the repo was created in the database
-        new_repo = Session().query(Repository)\
+        new_repo = Session().query(Repository) \
             .filter(Repository.repo_name == repo_name_full).one()
         new_repo_id = new_repo.repo_id
 
@@ -317,14 +281,14 @@
 
         # test if the repository was created on filesystem
         try:
-            vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name_full))
+            vcs.get_repo(safe_str(os.path.join(Ui.get_by_key('paths', '/').ui_value, repo_name_full)))
         except vcs.exceptions.VCSError:
             RepoGroupModel().delete(group_name)
             Session().commit()
-            self.fail('no repo %s in filesystem' % repo_name)
+            pytest.fail('no repo %s in filesystem' % repo_name)
 
         #check if inherited permissiona are applied
-        inherited_perms = UserRepoToPerm.query()\
+        inherited_perms = UserRepoToPerm.query() \
             .filter(UserRepoToPerm.repository_id == new_repo_id).all()
         self.assertEqual(len(inherited_perms), 2)
 
@@ -340,7 +304,7 @@
     def test_create_remote_repo_wrong_clone_uri(self):
         self.log_user()
         repo_name = self.NEW_REPO
-        description = 'description for newly created repo'
+        description = u'description for newly created repo'
         response = self.app.post(url('repos'),
                         fixture._get_repo_create_params(repo_private=False,
                                                 repo_name=repo_name,
@@ -354,7 +318,7 @@
     def test_create_remote_repo_wrong_clone_uri_hg_svn(self):
         self.log_user()
         repo_name = self.NEW_REPO
-        description = 'description for newly created repo'
+        description = u'description for newly created repo'
         response = self.app.post(url('repos'),
                         fixture._get_repo_create_params(repo_private=False,
                                                 repo_name=repo_name,
@@ -367,8 +331,8 @@
 
     def test_delete(self):
         self.log_user()
-        repo_name = 'vcs_test_new_to_delete_%s' % self.REPO_TYPE
-        description = 'description for newly created repo'
+        repo_name = u'vcs_test_new_to_delete_%s' % self.REPO_TYPE
+        description = u'description for newly created repo'
         response = self.app.post(url('repos'),
                         fixture._get_repo_create_params(repo_private=False,
                                                 repo_type=self.REPO_TYPE,
@@ -381,7 +345,7 @@
                                'Created repository <a href="/%s">%s</a>'
                                % (repo_name, repo_name))
         # test if the repo was created in the database
-        new_repo = Session().query(Repository)\
+        new_repo = Session().query(Repository) \
             .filter(Repository.repo_name == repo_name).one()
 
         self.assertEqual(new_repo.repo_name, repo_name)
@@ -394,9 +358,9 @@
 
         # test if the repository was created on filesystem
         try:
-            vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
+            vcs.get_repo(safe_str(os.path.join(Ui.get_by_key('paths', '/').ui_value, repo_name)))
         except vcs.exceptions.VCSError:
-            self.fail('no repo %s in filesystem' % repo_name)
+            pytest.fail('no repo %s in filesystem' % repo_name)
 
         response = self.app.post(url('delete_repo', repo_name=repo_name),
             params={'_method': 'delete', '_authentication_token': self.authentication_token()})
@@ -406,21 +370,21 @@
         response.follow()
 
         #check if repo was deleted from db
-        deleted_repo = Session().query(Repository)\
+        deleted_repo = Session().query(Repository) \
             .filter(Repository.repo_name == repo_name).scalar()
 
         self.assertEqual(deleted_repo, None)
 
-        self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
+        self.assertEqual(os.path.isdir(os.path.join(Ui.get_by_key('paths', '/').ui_value, repo_name)),
                                   False)
 
     def test_delete_non_ascii(self):
         self.log_user()
         non_ascii = "ąęł"
-        repo_name = "%s%s" % (self.NEW_REPO, non_ascii)
-        repo_name_unicode = repo_name.decode('utf8')
+        repo_name = "%s%s" % (safe_str(self.NEW_REPO), non_ascii)
+        repo_name_unicode = safe_unicode(repo_name)
         description = 'description for newly created repo' + non_ascii
-        description_unicode = description.decode('utf8')
+        description_unicode = safe_unicode(description)
         response = self.app.post(url('repos'),
                         fixture._get_repo_create_params(repo_private=False,
                                                 repo_name=repo_name,
@@ -434,7 +398,7 @@
                                u'Created repository <a href="/%s">%s</a>'
                                % (urllib.quote(repo_name), repo_name_unicode))
         # test if the repo was created in the database
-        new_repo = Session().query(Repository)\
+        new_repo = Session().query(Repository) \
             .filter(Repository.repo_name == repo_name_unicode).one()
 
         self.assertEqual(new_repo.repo_name, repo_name_unicode)
@@ -447,9 +411,9 @@
 
         # test if the repository was created on filesystem
         try:
-            vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
+            vcs.get_repo(safe_str(os.path.join(Ui.get_by_key('paths', '/').ui_value, repo_name_unicode)))
         except vcs.exceptions.VCSError:
-            self.fail('no repo %s in filesystem' % repo_name)
+            pytest.fail('no repo %s in filesystem' % repo_name)
 
         response = self.app.post(url('delete_repo', repo_name=repo_name),
             params={'_method': 'delete', '_authentication_token': self.authentication_token()})
@@ -457,12 +421,12 @@
         response.follow()
 
         #check if repo was deleted from db
-        deleted_repo = Session().query(Repository)\
+        deleted_repo = Session().query(Repository) \
             .filter(Repository.repo_name == repo_name_unicode).scalar()
 
         self.assertEqual(deleted_repo, None)
 
-        self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
+        self.assertEqual(os.path.isdir(os.path.join(Ui.get_by_key('paths', '/').ui_value, repo_name_unicode)),
                                   False)
 
     def test_delete_repo_with_group(self):
@@ -532,7 +496,7 @@
 
     def test_set_fork_of_other_repo(self):
         self.log_user()
-        other_repo = 'other_%s' % self.REPO_TYPE
+        other_repo = u'other_%s' % self.REPO_TYPE
         fixture.create_repo(other_repo, repo_type=self.REPO_TYPE)
         repo = Repository.get_by_repo_name(self.REPO)
         repo2 = Repository.get_by_repo_name(other_repo)
@@ -604,7 +568,7 @@
 
         user = User.get(usr['user_id'])
 
-        repo_name = self.NEW_REPO+'no_perms'
+        repo_name = self.NEW_REPO + u'no_perms'
         description = 'description for newly created repo'
         response = self.app.post(url('repos'),
                         fixture._get_repo_create_params(repo_private=False,
@@ -638,7 +602,7 @@
         self.assertEqual(repo, None)
 
         # repo must not be in filesystem !
-        self.assertFalse(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)))
+        self.assertFalse(os.path.isdir(os.path.join(Ui.get_by_key('paths', '/').ui_value, repo_name)))
 
 class TestAdminReposControllerGIT(TestController, _BaseTest):
     REPO = GIT_REPO
--- a/kallithea/tests/functional/test_admin_settings.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_admin_settings.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,5 +1,7 @@
 # -*- coding: utf-8 -*-
 
+import tempfile
+
 from kallithea.model.db import Setting, Ui
 from kallithea.tests import *
 from kallithea.tests.fixture import Fixture
@@ -7,7 +9,7 @@
 fixture = Fixture()
 
 
-class TestAdminSettingsController(TestController):
+class TestAdminSettingsController(TestControllerPytest):
 
     def test_index_main(self):
         self.log_user()
@@ -37,31 +39,31 @@
         self.log_user()
         response = self.app.post(url('admin_settings_hooks'),
                                 params=dict(new_hook_ui_key='test_hooks_1',
-                                            new_hook_ui_value='cd /tmp',
+                                            new_hook_ui_value='cd %s' % tempfile.gettempdir(),
                                             _authentication_token=self.authentication_token()))
 
         response = response.follow()
         response.mustcontain('test_hooks_1')
-        response.mustcontain('cd /tmp')
+        response.mustcontain('cd %s' % tempfile.gettempdir())
 
     def test_create_custom_hook_delete(self):
         self.log_user()
         response = self.app.post(url('admin_settings_hooks'),
                                 params=dict(new_hook_ui_key='test_hooks_2',
-                                            new_hook_ui_value='cd /tmp2',
+                                            new_hook_ui_value='cd %s2' % tempfile.gettempdir(),
                                             _authentication_token=self.authentication_token()))
 
         response = response.follow()
         response.mustcontain('test_hooks_2')
-        response.mustcontain('cd /tmp2')
+        response.mustcontain('cd %s2' % tempfile.gettempdir())
 
-        hook_id = Ui.get_by_key('test_hooks_2').ui_id
+        hook_id = Ui.get_by_key('hooks', 'test_hooks_2').ui_id
         ## delete
         self.app.post(url('admin_settings_hooks'),
                         params=dict(hook_id=hook_id, _authentication_token=self.authentication_token()))
         response = self.app.get(url('admin_settings_hooks'))
         response.mustcontain(no=['test_hooks_2'])
-        response.mustcontain(no=['cd /tmp2'])
+        response.mustcontain(no=['cd %s2' % tempfile.gettempdir()])
 
     def test_index_search(self):
         self.log_user()
--- a/kallithea/tests/functional/test_admin_user_groups.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_admin_user_groups.py	Mon Mar 14 00:36:08 2016 +0100
@@ -3,10 +3,10 @@
 from kallithea.model.db import UserGroup, UserGroupToPerm, Permission
 from kallithea.model.meta import Session
 
-TEST_USER_GROUP = 'admins_test'
+TEST_USER_GROUP = u'admins_test'
 
 
-class TestAdminUsersGroupsController(TestController):
+class TestAdminUsersGroupsController(TestControllerPytest):
 
     def test_index(self):
         self.log_user()
@@ -18,7 +18,7 @@
         users_group_name = TEST_USER_GROUP
         response = self.app.post(url('users_groups'),
                                  {'users_group_name': users_group_name,
-                                  'user_group_description': 'DESC',
+                                  'user_group_description': u'DESC',
                                   'active': True,
                                   '_authentication_token': self.authentication_token()})
         response.follow()
@@ -43,7 +43,7 @@
         users_group_name = TEST_USER_GROUP + 'another'
         response = self.app.post(url('users_groups'),
                                  {'users_group_name':users_group_name,
-                                  'user_group_description': 'DESC',
+                                  'user_group_description': u'DESC',
                                   'active': True,
                                   '_authentication_token': self.authentication_token()})
         response.follow()
@@ -51,13 +51,13 @@
         self.checkSessionFlash(response,
                                'Created user group ')
 
-        gr = Session().query(UserGroup)\
+        gr = Session().query(UserGroup) \
             .filter(UserGroup.users_group_name == users_group_name).one()
 
         response = self.app.post(url('users_group', id=gr.users_group_id),
             params={'_method': 'delete', '_authentication_token': self.authentication_token()})
 
-        gr = Session().query(UserGroup)\
+        gr = Session().query(UserGroup) \
             .filter(UserGroup.users_group_name == users_group_name).scalar()
 
         self.assertEqual(gr, None)
@@ -67,7 +67,7 @@
         users_group_name = TEST_USER_GROUP + 'another2'
         response = self.app.post(url('users_groups'),
                                  {'users_group_name': users_group_name,
-                                  'user_group_description': 'DESC',
+                                  'user_group_description': u'DESC',
                                   'active': True,
                                   '_authentication_token': self.authentication_token()})
         response.follow()
@@ -87,7 +87,7 @@
         p3 = Permission.get_by_key('hg.fork.none')
         # check if user has this perms, they should be here since
         # defaults are on
-        perms = UserGroupToPerm.query()\
+        perms = UserGroupToPerm.query() \
             .filter(UserGroupToPerm.users_group == ug).all()
 
         self.assertEqual(
@@ -109,7 +109,7 @@
 
         # check if user has this perms, they should be here since
         # defaults are on
-        perms = UserGroupToPerm.query()\
+        perms = UserGroupToPerm.query() \
             .filter(UserGroupToPerm.users_group == ug).all()
 
         self.assertEqual(
@@ -124,12 +124,12 @@
         response = self.app.post(url('users_group', id=ug.users_group_id),
             params={'_method': 'delete', '_authentication_token': self.authentication_token()})
         response = response.follow()
-        gr = Session().query(UserGroup)\
+        gr = Session().query(UserGroup) \
             .filter(UserGroup.users_group_name == users_group_name).scalar()
 
         self.assertEqual(gr, None)
         p = Permission.get_by_key('hg.create.repository')
-        perms = UserGroupToPerm.query()\
+        perms = UserGroupToPerm.query() \
             .filter(UserGroupToPerm.users_group_id == ugid).all()
         perms = [[x.users_group_id,
                   x.permission_id, ] for x in perms]
@@ -140,7 +140,7 @@
         users_group_name = TEST_USER_GROUP + 'another2'
         response = self.app.post(url('users_groups'),
                                  {'users_group_name': users_group_name,
-                                  'user_group_description': 'DESC',
+                                  'user_group_description': u'DESC',
                                   'active': True,
                                   '_authentication_token': self.authentication_token()})
         response.follow()
@@ -160,7 +160,7 @@
         p3 = Permission.get_by_key('hg.fork.repository')
         # check if user has this perms, they should be here since
         # defaults are on
-        perms = UserGroupToPerm.query()\
+        perms = UserGroupToPerm.query() \
             .filter(UserGroupToPerm.users_group == ug).all()
 
         self.assertEqual(
@@ -180,7 +180,7 @@
         p3 = Permission.get_by_key('hg.fork.none')
         # check if user has this perms, they should be here since
         # defaults are on
-        perms = UserGroupToPerm.query()\
+        perms = UserGroupToPerm.query() \
             .filter(UserGroupToPerm.users_group == ug).all()
 
         self.assertEqual(
@@ -195,13 +195,13 @@
         response = self.app.post(url('users_group', id=ug.users_group_id),
             params={'_method': 'delete', '_authentication_token': self.authentication_token()})
         response = response.follow()
-        gr = Session().query(UserGroup)\
+        gr = Session().query(UserGroup) \
                            .filter(UserGroup.users_group_name ==
                                    users_group_name).scalar()
 
         self.assertEqual(gr, None)
         p = Permission.get_by_key('hg.fork.repository')
-        perms = UserGroupToPerm.query()\
+        perms = UserGroupToPerm.query() \
             .filter(UserGroupToPerm.users_group_id == ugid).all()
         perms = [[x.users_group_id,
                   x.permission_id, ] for x in perms]
--- a/kallithea/tests/functional/test_admin_users.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_admin_users.py	Mon Mar 14 00:36:08 2016 +0100
@@ -45,8 +45,8 @@
         username = 'newtestuser'
         password = 'test12'
         password_confirmation = password
-        name = 'name'
-        lastname = 'lastname'
+        name = u'name'
+        lastname = u'lastname'
         email = 'mail@example.com'
 
         response = self.app.post(url('users'),
@@ -64,7 +64,7 @@
         self.checkSessionFlash(response, '''Created user <a href="/_admin/users/''')
         self.checkSessionFlash(response, '''/edit">%s</a>''' % (username))
 
-        new_user = Session().query(User).\
+        new_user = Session().query(User). \
             filter(User.username == username).one()
 
         self.assertEqual(new_user.username, username)
@@ -81,8 +81,8 @@
         self.log_user()
         username = 'new_user'
         password = ''
-        name = 'name'
-        lastname = 'lastname'
+        name = u'name'
+        lastname = u'lastname'
         email = 'errmail.example.com'
 
         response = self.app.post(url('users'), {'username': username,
@@ -165,7 +165,7 @@
 
         fixture.create_user(name=username)
 
-        new_user = Session().query(User)\
+        new_user = Session().query(User) \
             .filter(User.username == username).one()
         response = self.app.post(url('user', id=new_user.user_id),
             params={'_method': 'delete', '_authentication_token': self.authentication_token()})
@@ -175,12 +175,12 @@
     def test_delete_repo_err(self):
         self.log_user()
         username = 'repoerr'
-        reponame = 'repoerr_fail'
+        reponame = u'repoerr_fail'
 
         fixture.create_user(name=username)
         fixture.create_repo(name=reponame, cur_user=username)
 
-        new_user = Session().query(User)\
+        new_user = Session().query(User) \
             .filter(User.username == username).one()
         response = self.app.post(url('user', id=new_user.user_id),
             params={'_method': 'delete', '_authentication_token': self.authentication_token()})
@@ -200,12 +200,12 @@
     def test_delete_repo_group_err(self):
         self.log_user()
         username = 'repogrouperr'
-        groupname = 'repogroup_fail'
+        groupname = u'repogroup_fail'
 
         fixture.create_user(name=username)
         fixture.create_repo_group(name=groupname, cur_user=username)
 
-        new_user = Session().query(User)\
+        new_user = Session().query(User) \
             .filter(User.username == username).one()
         response = self.app.post(url('user', id=new_user.user_id),
             params={'_method': 'delete', '_authentication_token': self.authentication_token()})
@@ -229,12 +229,12 @@
     def test_delete_user_group_err(self):
         self.log_user()
         username = 'usergrouperr'
-        groupname = 'usergroup_fail'
+        groupname = u'usergroup_fail'
 
         fixture.create_user(name=username)
         ug = fixture.create_user_group(name=groupname, cur_user=username)
 
-        new_user = Session().query(User)\
+        new_user = Session().query(User) \
             .filter(User.username == username).one()
         response = self.app.post(url('user', id=new_user.user_id),
             params={'_method': 'delete', '_authentication_token': self.authentication_token()})
@@ -267,8 +267,8 @@
         perm_create = Permission.get_by_key('hg.create.repository')
 
         user = UserModel().create_or_update(username='dummy', password='qwe',
-                                            email='dummy', firstname='a',
-                                            lastname='b')
+                                            email='dummy', firstname=u'a',
+                                            lastname=u'b')
         Session().commit()
         uid = user.user_id
 
@@ -298,8 +298,8 @@
         perm_create = Permission.get_by_key('hg.create.repository')
 
         user = UserModel().create_or_update(username='dummy', password='qwe',
-                                            email='dummy', firstname='a',
-                                            lastname='b')
+                                            email='dummy', firstname=u'a',
+                                            lastname=u'b')
         Session().commit()
         uid = user.user_id
 
@@ -327,8 +327,8 @@
         perm_fork = Permission.get_by_key('hg.fork.repository')
 
         user = UserModel().create_or_update(username='dummy', password='qwe',
-                                            email='dummy', firstname='a',
-                                            lastname='b')
+                                            email='dummy', firstname=u'a',
+                                            lastname=u'b')
         Session().commit()
         uid = user.user_id
 
@@ -358,8 +358,8 @@
         perm_fork = Permission.get_by_key('hg.fork.repository')
 
         user = UserModel().create_or_update(username='dummy', password='qwe',
-                                            email='dummy', firstname='a',
-                                            lastname='b')
+                                            email='dummy', firstname=u'a',
+                                            lastname=u'b')
         Session().commit()
         uid = user.user_id
 
@@ -532,7 +532,7 @@
 #            u._get_user_or_raise_if_default(User.get_default_user().user_id)
 
 
-class TestAdminUsersControllerForDefaultUser(TestController):
+class TestAdminUsersControllerForDefaultUser(TestControllerPytest):
     """
     Edit actions on the default user are not allowed.
     Validate that they throw a 404 exception.
--- a/kallithea/tests/functional/test_branches.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_branches.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,7 +1,7 @@
 from kallithea.tests import *
 
 
-class TestBranchesController(TestController):
+class TestBranchesController(TestControllerPytest):
 
     def test_index_hg(self):
         self.log_user()
--- a/kallithea/tests/functional/test_changelog.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_changelog.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,7 +1,7 @@
 from kallithea.tests import *
 
 
-class TestChangelogController(TestController):
+class TestChangelogController(TestControllerPytest):
 
     def test_index_hg(self):
         self.log_user()
@@ -21,6 +21,8 @@
         )
         response.mustcontain("""code garden""")
 
+        response.mustcontain("""var jsdata = [[[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 1, 2, 0], [0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0], [1, 1, 2, 0]], 0, 0], [[0, 1], [[0, 0, 2, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 1, 3, 0], [0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 3, 0]], 0, 0], [[1, 3], [[0, 0, 2, 0], [1, 1, 3, 0]], 0, 0], [[1, 3], [[0, 0, 2, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 1, 4, 0], [0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 1, 5, 0], [0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 5, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 5, 0]], 0, 0], [[1, 5], [[0, 0, 2, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 1, 6, 0], [0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 6, 0]], 0, 0], [[1, 6], [[0, 0, 2, 0], [1, 1, 6, 0]], 0, 0], [[1, 6], [[0, 0, 2, 0], [1, 1, 6, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 6, 0]], 0, 0], [[1, 6], [[0, 0, 2, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 1, 7, 0], [0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 7, 0]], 0, 0], [[1, 7], [[0, 0, 2, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 1, 8, 0], [0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 8, 0]], 0, 0], [[1, 8], [[0, 0, 2, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 1, 9, 0], [0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 1, 10, 0], [0, 0, 2, 0], [1, 2, 9, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 10, 0], [2, 2, 9, 0]], 0, 0], [[2, 9], [[0, 0, 2, 0], [1, 1, 10, 0], [2, 1, 10, 0]], 0, 0], [[1, 10], [[0, 0, 2, 0], [1, 1, 10, 0]], 0, 0], [[1, 10], [[0, 0, 2, 0], [1, 1, 10, 0]], 0, 0], [[1, 10], [[0, 0, 2, 0], [1, 1, 10, 0]], 0, 0], [[1, 10], [[0, 0, 2, 0], [1, 1, 10, 0]], 0, 0], [[1, 10], [[0, 0, 2, 0], [1, 1, 10, 0]], 0, 0], [[1, 10], [[0, 0, 2, 0], [1, 1, 10, 0]], 0, 0], [[1, 10], [[0, 0, 2, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[1, 11], [[0, 0, 2, 0], [1, 1, 11, 0]], 1, 0], [[2, 12], [[0, 0, 2, 0], [2, 1, 12, 0]], 1, 0], [[0, 2], [[0, 1, 13, 0], [0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 13, 0]], 0, 0], [[1, 13], [[0, 0, 2, 0], [1, 1, 13, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 13, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 13, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 13, 0]], 0, 0], [[1, 13], [[0, 0, 2, 0], [1, 1, 13, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 1, 14, 0], [0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0]], 0, 0]];""")
+
     def test_index_pagination_hg(self):
         self.log_user()
         #pagination
@@ -68,6 +70,8 @@
 
         response.mustcontain("""fixing stupid typo in context for mercurial""")
 
+        response.mustcontain("""var jsdata = [[[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 1, 2, 0], [0, 0, 1, 0]], 0, 0], [[0, 1], [[0, 0, 1, 0], [1, 1, 2, 0]], 0, 0], [[0, 1], [[0, 0, 2, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 1, 3, 0], [0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 3, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 3, 0]], 0, 0], [[1, 3], [[0, 0, 2, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 1, 4, 0], [0, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0], [1, 0, 2, 0]], 0, 0], [[0, 2], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[1, 4], [[0, 0, 2, 0], [1, 1, 4, 0]], 0, 0], [[0, 2], [[0, 0, 4, 0], [1, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 1, 5, 0], [0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0], [1, 1, 5, 0]], 0, 0], [[1, 5], [[0, 0, 4, 0], [1, 1, 5, 0]], 0, 0], [[1, 5], [[0, 0, 4, 0], [1, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 1, 6, 0], [0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0], [1, 1, 6, 0]], 0, 0], [[1, 6], [[0, 0, 4, 0], [1, 1, 6, 0]], 0, 0], [[1, 6], [[0, 0, 4, 0], [1, 1, 6, 0], [1, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0], [1, 1, 6, 0]], 0, 0], [[1, 6], [[0, 0, 4, 0], [1, 0, 4, 0]], 0, 0], [[0, 4], [[0, 1, 7, 0], [0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0], [1, 1, 7, 0]], 0, 0], [[1, 7], [[0, 0, 4, 0], [1, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 1, 8, 0], [0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0], [1, 1, 8, 0]], 0, 0], [[1, 8], [[0, 0, 4, 0], [1, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 1, 9, 0], [0, 0, 4, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0], [1, 1, 9, 0]], 0, 0], [[1, 9], [[0, 0, 4, 0], [1, 0, 4, 0], [1, 1, 9, 0]], 0, 0], [[1, 9], [[0, 0, 4, 0], [1, 1, 9, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0], [1, 1, 9, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0], [1, 1, 9, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0], [1, 1, 9, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0], [1, 1, 9, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0], [1, 1, 9, 0]], 0, 0], [[0, 4], [[0, 0, 4, 0], [1, 1, 9, 0]], 0, 0], [[0, 4], [[0, 0, 9, 0], [1, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 1, 10, 0], [0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 1, 11, 0], [0, 0, 9, 0], [1, 2, 10, 0]], 0, 0], [[2, 10], [[0, 0, 9, 0], [1, 1, 11, 0], [2, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0], [1, 1, 11, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0], [1, 1, 11, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0], [1, 1, 11, 0]], 0, 0], [[1, 11], [[0, 0, 9, 0], [1, 0, 9, 0], [1, 1, 11, 0]], 0, 0], [[1, 11], [[0, 0, 9, 0], [1, 1, 11, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0], [1, 1, 11, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0], [1, 1, 11, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0], [1, 1, 11, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0], [1, 1, 11, 0]], 0, 0], [[0, 9], [[0, 1, 11, 0], [0, 0, 9, 0], [1, 1, 11, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0], [1, 1, 11, 0]], 0, 0], [[1, 11], [[0, 0, 9, 0], [1, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0], [[0, 9], [[0, 0, 9, 0]], 0, 0]];""")
+
 #        response.mustcontain(
 #            """<div id="changed_total_5e204e7583b9c8e7b93a020bd036564b1e731dae" """
 #            """style="float:right;" class="changed_total tooltip" """
--- a/kallithea/tests/functional/test_changeset.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_changeset.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,6 +1,6 @@
 from kallithea.tests import *
 
-class TestChangesetController(TestController):
+class TestChangesetController(TestControllerPytest):
 
     def test_index(self):
         response = self.app.get(url(controller='changeset', action='index',
--- a/kallithea/tests/functional/test_changeset_comments.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_changeset_comments.py	Mon Mar 14 00:36:08 2016 +0100
@@ -11,7 +11,7 @@
             Session().delete(x)
         Session().commit()
 
-        self.remove_all_notifications()
+        remove_all_notifications()
 
     def test_create(self):
         self.log_user()
@@ -21,10 +21,9 @@
         params = {'text': text, '_authentication_token': self.authentication_token()}
         response = self.app.post(url(controller='changeset', action='comment',
                                      repo_name=HG_REPO, revision=rev),
-                                     params=params)
+                                     params=params, extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
         # Test response...
-        self.assertEqual(response.status, '302 Found')
-        response.follow()
+        self.assertEqual(response.status, '200 OK')
 
         response = self.app.get(url(controller='changeset', action='index',
                                 repo_name=HG_REPO, revision=rev))
@@ -58,10 +57,9 @@
         params = {'text': text, 'f_path': f_path, 'line': line, '_authentication_token': self.authentication_token()}
         response = self.app.post(url(controller='changeset', action='comment',
                                      repo_name=HG_REPO, revision=rev),
-                                     params=params)
+                                     params=params, extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
         # Test response...
-        self.assertEqual(response.status, '302 Found')
-        response.follow()
+        self.assertEqual(response.status, '200 OK')
 
         response = self.app.get(url(controller='changeset', action='index',
                                 repo_name=HG_REPO, revision=rev))
@@ -98,10 +96,9 @@
         params = {'text': text, '_authentication_token': self.authentication_token()}
         response = self.app.post(url(controller='changeset', action='comment',
                                      repo_name=HG_REPO, revision=rev),
-                                     params=params)
+                                     params=params, extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
         # Test response...
-        self.assertEqual(response.status, '302 Found')
-        response.follow()
+        self.assertEqual(response.status, '200 OK')
 
         response = self.app.get(url(controller='changeset', action='index',
                                 repo_name=HG_REPO, revision=rev))
@@ -126,7 +123,7 @@
         params = {'text': text, '_authentication_token': self.authentication_token()}
         response = self.app.post(url(controller='changeset', action='comment',
                                      repo_name=HG_REPO, revision=rev),
-                                     params=params)
+                                     params=params, extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
 
         comments = ChangesetComment.query().all()
         self.assertEqual(len(comments), 1)
--- a/kallithea/tests/functional/test_compare.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_compare.py	Mon Mar 14 00:36:08 2016 +0100
@@ -26,7 +26,7 @@
 
     def test_compare_forks_on_branch_extra_commits_hg(self):
         self.log_user()
-        repo1 = fixture.create_repo('one', repo_type='hg',
+        repo1 = fixture.create_repo(u'one', repo_type='hg',
                                     repo_description='diff-test',
                                     cur_user=TEST_USER_ADMIN_LOGIN)
         self.r1_id = repo1.repo_id
@@ -36,7 +36,7 @@
                 parent=None, newfile=True)
 
         #fork this repo
-        repo2 = fixture.create_fork('one', 'one-fork')
+        repo2 = fixture.create_fork(u'one', u'one-fork')
         self.r2_id = repo2.repo_id
 
         #add two extra commit into fork
@@ -77,7 +77,7 @@
 
     def test_compare_forks_on_branch_extra_commits_git(self):
         self.log_user()
-        repo1 = fixture.create_repo('one-git', repo_type='git',
+        repo1 = fixture.create_repo(u'one-git', repo_type='git',
                                     repo_description='diff-test',
                                     cur_user=TEST_USER_ADMIN_LOGIN)
         self.r1_id = repo1.repo_id
@@ -87,7 +87,7 @@
                 parent=None, newfile=True)
 
         #fork this repo
-        repo2 = fixture.create_fork('one-git', 'one-git-fork')
+        repo2 = fixture.create_fork(u'one-git', u'one-git-fork')
         self.r2_id = repo2.repo_id
 
         #add two extra commit into fork
@@ -129,7 +129,7 @@
     def test_compare_forks_on_branch_extra_commits_origin_has_incomming_hg(self):
         self.log_user()
 
-        repo1 = fixture.create_repo('one', repo_type='hg',
+        repo1 = fixture.create_repo(u'one', repo_type='hg',
                                     repo_description='diff-test',
                                     cur_user=TEST_USER_ADMIN_LOGIN)
 
@@ -141,7 +141,7 @@
                 parent=None, newfile=True)
 
         #fork this repo
-        repo2 = fixture.create_fork('one', 'one-fork')
+        repo2 = fixture.create_fork(u'one', u'one-fork')
         self.r2_id = repo2.repo_id
 
         #now commit something to origin repo
@@ -188,7 +188,7 @@
     def test_compare_forks_on_branch_extra_commits_origin_has_incomming_git(self):
         self.log_user()
 
-        repo1 = fixture.create_repo('one-git', repo_type='git',
+        repo1 = fixture.create_repo(u'one-git', repo_type='git',
                                     repo_description='diff-test',
                                     cur_user=TEST_USER_ADMIN_LOGIN)
 
@@ -200,7 +200,7 @@
                 parent=None, newfile=True)
 
         #fork this repo
-        repo2 = fixture.create_fork('one-git', 'one-git-fork')
+        repo2 = fixture.create_fork(u'one-git', u'one-git-fork')
         self.r2_id = repo2.repo_id
 
         #now commit something to origin repo
@@ -259,7 +259,7 @@
         #make repo1, and cs1+cs2
         self.log_user()
 
-        repo1 = fixture.create_repo('repo1', repo_type='hg',
+        repo1 = fixture.create_repo(u'repo1', repo_type='hg',
                                     repo_description='diff-test',
                                     cur_user=TEST_USER_ADMIN_LOGIN)
         self.r1_id = repo1.repo_id
@@ -272,7 +272,7 @@
                 content='line1\nline2\n', message='commit2', vcs_type='hg',
                 parent=cs0)
         #fork this repo
-        repo2 = fixture.create_fork('repo1', 'repo1-fork')
+        repo2 = fixture.create_fork(u'repo1', u'repo1-fork')
         self.r2_id = repo2.repo_id
         #now make cs3-6
         cs2 = fixture.commit_change(repo1.repo_name, filename='file1',
@@ -326,7 +326,7 @@
 #
         #make repo1, and cs1+cs2
         self.log_user()
-        repo1 = fixture.create_repo('repo1', repo_type='hg',
+        repo1 = fixture.create_repo(u'repo1', repo_type='hg',
                                     repo_description='diff-test',
                                     cur_user=TEST_USER_ADMIN_LOGIN)
         self.r1_id = repo1.repo_id
@@ -339,7 +339,7 @@
                 content='line1\nline2\n', message='commit2', vcs_type='hg',
                 parent=cs0)
         #fork this repo
-        repo2 = fixture.create_fork('repo1', 'repo1-fork')
+        repo2 = fixture.create_fork(u'repo1', u'repo1-fork')
         self.r2_id = repo2.repo_id
         #now make cs3-6
         cs2 = fixture.commit_change(repo1.repo_name, filename='file1',
@@ -445,7 +445,7 @@
     def test_org_repo_new_commits_after_forking_simple_diff_hg(self):
         self.log_user()
 
-        repo1 = fixture.create_repo('one', repo_type='hg',
+        repo1 = fixture.create_repo(u'one', repo_type='hg',
                                     repo_description='diff-test',
                                     cur_user=TEST_USER_ADMIN_LOGIN)
 
@@ -457,7 +457,7 @@
         Session().commit()
         self.assertEqual(repo1.scm_instance.revisions, [cs0.raw_id])
         #fork the repo1
-        repo2 = fixture.create_repo('one-fork', repo_type='hg',
+        repo2 = fixture.create_repo(u'one-fork', repo_type='hg',
                                     repo_description='diff-test',
                                     cur_user=TEST_USER_ADMIN_LOGIN,
                                     clone_uri=repo1.repo_full_path,
@@ -524,7 +524,7 @@
     def test_org_repo_new_commits_after_forking_simple_diff_git(self):
         self.log_user()
 
-        repo1 = fixture.create_repo('one-git', repo_type='git',
+        repo1 = fixture.create_repo(u'one-git', repo_type='git',
                                     repo_description='diff-test',
                                     cur_user=TEST_USER_ADMIN_LOGIN)
 
@@ -537,7 +537,7 @@
         Session().commit()
         self.assertEqual(repo1.scm_instance.revisions, [cs0.raw_id])
         #fork the repo1
-        repo2 = fixture.create_repo('one-git-fork', repo_type='git',
+        repo2 = fixture.create_repo(u'one-git-fork', repo_type='git',
                                     repo_description='diff-test',
                                     cur_user=TEST_USER_ADMIN_LOGIN,
                                     clone_uri=repo1.repo_full_path,
--- a/kallithea/tests/functional/test_compare_local.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_compare_local.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 from kallithea.tests import *
 
-class TestCompareController(TestController):
+class TestCompareController(TestControllerPytest):
 
     def test_compare_tag_hg(self):
         self.log_user()
--- a/kallithea/tests/functional/test_feed.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_feed.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,6 +1,6 @@
 from kallithea.tests import *
 
-class TestFeedController(TestController):
+class TestFeedController(TestControllerPytest):
 
     def test_rss(self):
         self.log_user()
--- a/kallithea/tests/functional/test_files.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_files.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 import os
+import posixpath
 from kallithea.tests import *
 from kallithea.model.db import Repository
 from kallithea.model.meta import Session
@@ -24,7 +25,7 @@
     Session().commit()
 
 
-class TestFilesController(TestController):
+class TestFilesController(TestControllerPytest):
 
     def test_index(self):
         self.log_user()
@@ -347,7 +348,7 @@
 
         self.checkSessionFlash(response, 'No filename')
 
-    @parameterized.expand([
+    @parametrize('location,filename', [
         ('/abs', 'foo'),
         ('../rel', 'foo'),
         ('file/../foo', 'foo'),
@@ -367,14 +368,14 @@
 
         self.checkSessionFlash(response, 'Location must be relative path and must not contain .. in path')
 
-    @parameterized.expand([
+    @parametrize('cnt,location,filename', [
         (1, '', 'foo.txt'),
         (2, 'dir', 'foo.rst'),
         (3, 'rel/dir', 'foo.bar'),
     ])
     def test_add_file_into_hg(self, cnt, location, filename):
         self.log_user()
-        repo = fixture.create_repo('commit-test-%s' % cnt, repo_type='hg')
+        repo = fixture.create_repo(u'commit-test-%s' % cnt, repo_type='hg')
         response = self.app.post(url('files_add_home',
                                       repo_name=repo.repo_name,
                                       revision='tip', f_path='/'),
@@ -387,7 +388,7 @@
                                  status=302)
         try:
             self.checkSessionFlash(response, 'Successfully committed to %s'
-                                   % os.path.join(location, filename))
+                                   % posixpath.join(location, filename))
         finally:
             fixture.destroy_repo(repo.repo_name)
 
@@ -423,7 +424,7 @@
 
         self.checkSessionFlash(response, 'No filename')
 
-    @parameterized.expand([
+    @parametrize('location,filename', [
         ('/abs', 'foo'),
         ('../rel', 'foo'),
         ('file/../foo', 'foo'),
@@ -443,14 +444,14 @@
 
         self.checkSessionFlash(response, 'Location must be relative path and must not contain .. in path')
 
-    @parameterized.expand([
+    @parametrize('cnt,location,filename', [
         (1, '', 'foo.txt'),
         (2, 'dir', 'foo.rst'),
         (3, 'rel/dir', 'foo.bar'),
     ])
     def test_add_file_into_git(self, cnt, location, filename):
         self.log_user()
-        repo = fixture.create_repo('commit-test-%s' % cnt, repo_type='git')
+        repo = fixture.create_repo(u'commit-test-%s' % cnt, repo_type='git')
         response = self.app.post(url('files_add_home',
                                       repo_name=repo.repo_name,
                                       revision='tip', f_path='/'),
@@ -463,7 +464,7 @@
                                  status=302)
         try:
             self.checkSessionFlash(response, 'Successfully committed to %s'
-                                   % os.path.join(location, filename))
+                                   % posixpath.join(location, filename))
         finally:
             fixture.destroy_repo(repo.repo_name)
 
@@ -476,7 +477,7 @@
 
     def test_edit_file_view_not_on_branch_hg(self):
         self.log_user()
-        repo = fixture.create_repo('test-edit-repo', repo_type='hg')
+        repo = fixture.create_repo(u'test-edit-repo', repo_type='hg')
 
         ## add file
         location = 'vcs'
@@ -494,10 +495,10 @@
         response.follow()
         try:
             self.checkSessionFlash(response, 'Successfully committed to %s'
-                                   % os.path.join(location, filename))
+                                   % posixpath.join(location, filename))
             response = self.app.get(url('files_edit_home',
                                           repo_name=repo.repo_name,
-                                          revision='tip', f_path='vcs/nodes.py'),
+                                          revision='tip', f_path=posixpath.join(location, filename)),
                                     status=302)
             self.checkSessionFlash(response,
                 'You can only edit files with revision being a valid branch')
@@ -506,7 +507,7 @@
 
     def test_edit_file_view_commit_changes_hg(self):
         self.log_user()
-        repo = fixture.create_repo('test-edit-repo', repo_type='hg')
+        repo = fixture.create_repo(u'test-edit-repo', repo_type='hg')
 
         ## add file
         location = 'vcs'
@@ -525,19 +526,19 @@
         response.follow()
         try:
             self.checkSessionFlash(response, 'Successfully committed to %s'
-                                   % os.path.join(location, filename))
+                                   % posixpath.join(location, filename))
             response = self.app.post(url('files_edit_home',
                                           repo_name=repo.repo_name,
                                           revision=repo.scm_instance.DEFAULT_BRANCH_NAME,
-                                          f_path='vcs/nodes.py'),
+                                          f_path=posixpath.join(location, filename)),
                                      params={
                                         'content': "def py():\n print 'hello world'\n",
                                         'message': 'i commited',
                                         '_authentication_token': self.authentication_token(),
                                      },
                                     status=302)
-            self.checkSessionFlash(response,
-                                   'Successfully committed to vcs/nodes.py')
+            self.checkSessionFlash(response, 'Successfully committed to %s'
+                                   % posixpath.join(location, filename))
         finally:
             fixture.destroy_repo(repo.repo_name)
 
@@ -550,7 +551,7 @@
 
     def test_edit_file_view_not_on_branch_git(self):
         self.log_user()
-        repo = fixture.create_repo('test-edit-repo', repo_type='git')
+        repo = fixture.create_repo(u'test-edit-repo', repo_type='git')
 
         ## add file
         location = 'vcs'
@@ -568,10 +569,10 @@
         response.follow()
         try:
             self.checkSessionFlash(response, 'Successfully committed to %s'
-                                   % os.path.join(location, filename))
+                                   % posixpath.join(location, filename))
             response = self.app.get(url('files_edit_home',
                                           repo_name=repo.repo_name,
-                                          revision='tip', f_path='vcs/nodes.py'),
+                                          revision='tip', f_path=posixpath.join(location, filename)),
                                     status=302)
             self.checkSessionFlash(response,
                 'You can only edit files with revision being a valid branch')
@@ -580,7 +581,7 @@
 
     def test_edit_file_view_commit_changes_git(self):
         self.log_user()
-        repo = fixture.create_repo('test-edit-repo', repo_type='git')
+        repo = fixture.create_repo(u'test-edit-repo', repo_type='git')
 
         ## add file
         location = 'vcs'
@@ -599,19 +600,19 @@
         response.follow()
         try:
             self.checkSessionFlash(response, 'Successfully committed to %s'
-                                   % os.path.join(location, filename))
+                                   % posixpath.join(location, filename))
             response = self.app.post(url('files_edit_home',
                                           repo_name=repo.repo_name,
                                           revision=repo.scm_instance.DEFAULT_BRANCH_NAME,
-                                          f_path='vcs/nodes.py'),
+                                          f_path=posixpath.join(location, filename)),
                                      params={
                                         'content': "def py():\n print 'hello world'\n",
                                         'message': 'i commited',
                                         '_authentication_token': self.authentication_token(),
                                      },
                                     status=302)
-            self.checkSessionFlash(response,
-                                   'Successfully committed to vcs/nodes.py')
+            self.checkSessionFlash(response, 'Successfully committed to %s'
+                                   % posixpath.join(location, filename))
         finally:
             fixture.destroy_repo(repo.repo_name)
 
@@ -624,7 +625,7 @@
 
     def test_delete_file_view_not_on_branch_hg(self):
         self.log_user()
-        repo = fixture.create_repo('test-delete-repo', repo_type='hg')
+        repo = fixture.create_repo(u'test-delete-repo', repo_type='hg')
 
         ## add file
         location = 'vcs'
@@ -642,10 +643,10 @@
         response.follow()
         try:
             self.checkSessionFlash(response, 'Successfully committed to %s'
-                                   % os.path.join(location, filename))
+                                   % posixpath.join(location, filename))
             response = self.app.get(url('files_delete_home',
                                           repo_name=repo.repo_name,
-                                          revision='tip', f_path='vcs/nodes.py'),
+                                          revision='tip', f_path=posixpath.join(location, filename)),
                                     status=302)
             self.checkSessionFlash(response,
                 'You can only delete files with revision being a valid branch')
@@ -654,7 +655,7 @@
 
     def test_delete_file_view_commit_changes_hg(self):
         self.log_user()
-        repo = fixture.create_repo('test-delete-repo', repo_type='hg')
+        repo = fixture.create_repo(u'test-delete-repo', repo_type='hg')
 
         ## add file
         location = 'vcs'
@@ -673,18 +674,18 @@
         response.follow()
         try:
             self.checkSessionFlash(response, 'Successfully committed to %s'
-                                   % os.path.join(location, filename))
+                                   % posixpath.join(location, filename))
             response = self.app.post(url('files_delete_home',
                                           repo_name=repo.repo_name,
                                           revision=repo.scm_instance.DEFAULT_BRANCH_NAME,
-                                          f_path='vcs/nodes.py'),
+                                          f_path=posixpath.join(location, filename)),
                                      params={
                                         'message': 'i commited',
                                         '_authentication_token': self.authentication_token(),
                                      },
                                     status=302)
             self.checkSessionFlash(response,
-                                   'Successfully deleted file vcs/nodes.py')
+                                   'Successfully deleted file %s' % posixpath.join(location, filename))
         finally:
             fixture.destroy_repo(repo.repo_name)
 
@@ -697,7 +698,7 @@
 
     def test_delete_file_view_not_on_branch_git(self):
         self.log_user()
-        repo = fixture.create_repo('test-delete-repo', repo_type='git')
+        repo = fixture.create_repo(u'test-delete-repo', repo_type='git')
 
         ## add file
         location = 'vcs'
@@ -715,10 +716,10 @@
         response.follow()
         try:
             self.checkSessionFlash(response, 'Successfully committed to %s'
-                                   % os.path.join(location, filename))
+                                   % posixpath.join(location, filename))
             response = self.app.get(url('files_delete_home',
                                           repo_name=repo.repo_name,
-                                          revision='tip', f_path='vcs/nodes.py'),
+                                          revision='tip', f_path=posixpath.join(location, filename)),
                                     status=302)
             self.checkSessionFlash(response,
                 'You can only delete files with revision being a valid branch')
@@ -727,7 +728,7 @@
 
     def test_delete_file_view_commit_changes_git(self):
         self.log_user()
-        repo = fixture.create_repo('test-delete-repo', repo_type='git')
+        repo = fixture.create_repo(u'test-delete-repo', repo_type='git')
 
         ## add file
         location = 'vcs'
@@ -746,17 +747,17 @@
         response.follow()
         try:
             self.checkSessionFlash(response, 'Successfully committed to %s'
-                                   % os.path.join(location, filename))
+                                   % posixpath.join(location, filename))
             response = self.app.post(url('files_delete_home',
                                           repo_name=repo.repo_name,
                                           revision=repo.scm_instance.DEFAULT_BRANCH_NAME,
-                                          f_path='vcs/nodes.py'),
+                                          f_path=posixpath.join(location, filename)),
                                      params={
                                         'message': 'i commited',
                                         '_authentication_token': self.authentication_token(),
                                      },
                                     status=302)
             self.checkSessionFlash(response,
-                                   'Successfully deleted file vcs/nodes.py')
+                                   'Successfully deleted file %s' % posixpath.join(location, filename))
         finally:
             fixture.destroy_repo(repo.repo_name)
--- a/kallithea/tests/functional/test_followers.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_followers.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,7 +1,7 @@
 from kallithea.tests import *
 
 
-class TestFollowersController(TestController):
+class TestFollowersController(TestControllerPytest):
 
     def test_index_hg(self):
         self.log_user()
--- a/kallithea/tests/functional/test_forks.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_forks.py	Mon Mar 14 00:36:08 2016 +0100
@@ -101,7 +101,7 @@
 
     def test_fork_create_into_group(self):
         self.log_user()
-        group = fixture.create_repo_group('vc')
+        group = fixture.create_repo_group(u'vc')
         group_id = group.group_id
         fork_name = self.REPO_FORK
         fork_name_full = 'vc/%s' % fork_name
@@ -130,7 +130,7 @@
                 % (repo_name, fork_name_full, fork_name_full))
 
         #test if the fork was created in the database
-        fork_repo = Session().query(Repository)\
+        fork_repo = Session().query(Repository) \
             .filter(Repository.repo_name == fork_name_full).one()
 
         self.assertEqual(fork_repo.repo_name, fork_name_full)
@@ -173,7 +173,7 @@
                 % (repo_name, fork_name, fork_name))
 
         #test if the fork was created in the database
-        fork_repo = Session().query(Repository)\
+        fork_repo = Session().query(Repository) \
             .filter(Repository.repo_name == fork_name).one()
 
         self.assertEqual(fork_repo.repo_name, fork_name)
@@ -189,8 +189,8 @@
         usr = self.log_user(self.username, self.password)['user_id']
         repo_name = self.REPO
 
-        forks = Repository.query()\
-            .filter(Repository.repo_type == self.REPO_TYPE)\
+        forks = Repository.query() \
+            .filter(Repository.repo_type == self.REPO_TYPE) \
             .filter(Repository.fork_id != None).all()
         self.assertEqual(1, len(forks))
 
@@ -209,8 +209,8 @@
         usr = self.log_user(self.username, self.password)['user_id']
         repo_name = self.REPO
 
-        forks = Repository.query()\
-            .filter(Repository.repo_type == self.REPO_TYPE)\
+        forks = Repository.query() \
+            .filter(Repository.repo_type == self.REPO_TYPE) \
             .filter(Repository.fork_id != None).all()
         self.assertEqual(1, len(forks))
 
--- a/kallithea/tests/functional/test_home.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_home.py	Mon Mar 14 00:36:08 2016 +0100
@@ -9,7 +9,7 @@
 fixture = Fixture()
 
 
-class TestHomeController(TestController):
+class TestHomeController(TestControllerPytest):
 
     def test_index(self):
         self.log_user()
@@ -44,13 +44,13 @@
 
     def test_index_page_on_groups(self):
         self.log_user()
-        gr = fixture.create_repo_group('gr1')
-        fixture.create_repo(name='gr1/repo_in_group', repo_group=gr)
-        response = self.app.get(url('repos_group_home', group_name='gr1'))
+        gr = fixture.create_repo_group(u'gr1')
+        fixture.create_repo(name=u'gr1/repo_in_group', repo_group=gr)
+        response = self.app.get(url('repos_group_home', group_name=u'gr1'))
 
         try:
-            response.mustcontain("gr1/repo_in_group")
+            response.mustcontain(u"gr1/repo_in_group")
         finally:
-            RepoModel().delete('gr1/repo_in_group')
-            RepoGroupModel().delete(repo_group='gr1', force_delete=True)
+            RepoModel().delete(u'gr1/repo_in_group')
+            RepoGroupModel().delete(repo_group=u'gr1', force_delete=True)
             Session().commit()
--- a/kallithea/tests/functional/test_journal.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_journal.py	Mon Mar 14 00:36:08 2016 +0100
@@ -2,7 +2,7 @@
 import datetime
 
 
-class TestJournalController(TestController):
+class TestJournalController(TestControllerPytest):
 
     def test_index(self):
         self.log_user()
@@ -15,8 +15,8 @@
 #        usr = Session().query(User).filter(User.username == TEST_USER_ADMIN_LOGIN).one()
 #        repo = Session().query(Repository).filter(Repository.repo_name == HG_REPO).one()
 #
-#        followings = Session().query(UserFollowing)\
-#            .filter(UserFollowing.user == usr)\
+#        followings = Session().query(UserFollowing) \
+#            .filter(UserFollowing.user == usr) \
 #            .filter(UserFollowing.follows_repository == repo).all()
 #
 #        assert len(followings) == 1, 'Not following any repository'
--- a/kallithea/tests/functional/test_login.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_login.py	Mon Mar 14 00:36:08 2016 +0100
@@ -21,7 +21,7 @@
 
 class TestLoginController(TestController):
     def setUp(self):
-        self.remove_all_notifications()
+        remove_all_notifications()
         self.assertEqual(Notification.query().all(), [])
 
     def test_index(self):
@@ -50,6 +50,17 @@
         response = response.follow()
         response.mustcontain('/%s' % HG_REPO)
 
+    def test_login_regular_email_ok(self):
+        response = self.app.post(url(controller='login', action='index'),
+                                 {'username': TEST_USER_REGULAR_EMAIL,
+                                  'password': TEST_USER_REGULAR_PASS})
+
+        self.assertEqual(response.status, '302 Found')
+        self.assert_authenticated_user(response, TEST_USER_REGULAR_LOGIN)
+
+        response = response.follow()
+        response.mustcontain('/%s' % HG_REPO)
+
     def test_login_ok_came_from(self):
         test_came_from = '/_admin/users'
         response = self.app.post(url(controller='login', action='index',
@@ -350,8 +361,8 @@
         username = 'test_password_reset_1'
         password = 'qweqwe'
         email = 'username@example.com'
-        name = 'passwd'
-        lastname = 'reset'
+        name = u'passwd'
+        lastname = u'reset'
         timestamp = int(time.time())
 
         new = User()
--- a/kallithea/tests/functional/test_my_account.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_my_account.py	Mon Mar 14 00:36:08 2016 +0100
@@ -72,8 +72,8 @@
         response = self.app.get(url('my_account_emails'))
 
         from kallithea.model.db import UserEmailMap
-        email_id = UserEmailMap.query()\
-            .filter(UserEmailMap.user == User.get_by_username(TEST_USER_ADMIN_LOGIN))\
+        email_id = UserEmailMap.query() \
+            .filter(UserEmailMap.user == User.get_by_username(TEST_USER_ADMIN_LOGIN)) \
             .filter(UserEmailMap.email == 'barz@example.com').one().email_id
 
         response.mustcontain('barz@example.com')
@@ -156,8 +156,8 @@
                                     username=TEST_USER_ADMIN_LOGIN,
                                     new_password=TEST_USER_ADMIN_PASS,
                                     password_confirmation='test122',
-                                    firstname='NewName',
-                                    lastname='NewLastname',
+                                    firstname=u'NewName',
+                                    lastname=u'NewLastname',
                                     email=new_email,
                                     _authentication_token=self.authentication_token())
                                 )
@@ -173,14 +173,14 @@
                                             username=TEST_USER_ADMIN_LOGIN,
                                             new_password=TEST_USER_ADMIN_PASS,
                                             password_confirmation='test122',
-                                            firstname='NewName',
-                                            lastname='NewLastname',
+                                            firstname=u'NewName',
+                                            lastname=u'NewLastname',
                                             email=new_email,
                                             _authentication_token=self.authentication_token()))
 
         response.mustcontain('An email address must contain a single @')
         from kallithea.model import validators
-        msg = validators.ValidUsername(edit=False, old_data={})\
+        msg = validators.ValidUsername(edit=False, old_data={}) \
                 ._messages['username_exists']
         msg = h.html_escape(msg % {'username': TEST_USER_ADMIN_LOGIN})
         response.mustcontain(msg)
--- a/kallithea/tests/functional/test_pullrequests.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_pullrequests.py	Mon Mar 14 00:36:08 2016 +0100
@@ -8,7 +8,7 @@
 
 fixture = Fixture()
 
-class TestPullrequestsController(TestController):
+class TestPullrequestsController(TestControllerPytest):
 
     def test_index(self):
         self.log_user()
@@ -146,13 +146,13 @@
 class TestPullrequestsGetRepoRefs(TestController):
 
     def setUp(self):
-        self.main = fixture.create_repo('main', repo_type='hg')
+        self.main = fixture.create_repo(u'main', repo_type='hg')
         Session.add(self.main)
         Session.commit()
         self.c = PullrequestsController()
 
     def tearDown(self):
-        fixture.destroy_repo('main')
+        fixture.destroy_repo(u'main')
         Session.commit()
         Session.remove()
 
--- a/kallithea/tests/functional/test_repo_groups.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_repo_groups.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,7 +1,7 @@
 from kallithea.tests import *
 
 
-class TestRepoGroupsController(TestController):
+class TestRepoGroupsController(TestControllerPytest):
 
     def test_index(self):
         self.log_user()
--- a/kallithea/tests/functional/test_search.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_search.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,8 +1,9 @@
+import mock
 import os
 from kallithea.tests import *
 
 
-class TestSearchController(TestController):
+class TestSearchController(TestControllerPytest):
 
     def test_index(self):
         self.log_user()
@@ -11,11 +12,16 @@
         response.mustcontain('class="small" id="q" name="q" type="text"')
         # Test response...
 
-    def test_empty_search(self):
-        if os.path.isdir(self.index_location):
-            raise SkipTest('skipped due to existing index')
-        else:
-            self.log_user()
+    def test_empty_search(self, tmpdir):
+        self.log_user()
+
+        config_mock = {
+            'app_conf': {
+                # can be any existing dir that does not contain an actual index
+                'index_dir': str(tmpdir),
+            }
+        }
+        with mock.patch('kallithea.controllers.search.config', config_mock):
             response = self.app.get(url(controller='search', action='index'),
                                     {'q': HG_REPO})
             response.mustcontain('There is no index to search in. '
--- a/kallithea/tests/functional/test_summary.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_summary.py	Mon Mar 14 00:36:08 2016 +0100
@@ -22,7 +22,7 @@
 fixture = Fixture()
 
 
-class TestSummaryController(TestController):
+class TestSummaryController(TestControllerPytest):
 
     def test_index(self):
         self.log_user()
@@ -82,7 +82,7 @@
 
     def test_index_by_repo_having_id_path_in_name_hg(self):
         self.log_user()
-        fixture.create_repo(name='repo_1')
+        fixture.create_repo(name=u'repo_1')
         response = self.app.get(url(controller='summary',
                                     action='index',
                                     repo_name='repo_1'))
@@ -90,7 +90,7 @@
         try:
             response.mustcontain("repo_1")
         finally:
-            RepoModel().delete(Repository.get_by_repo_name('repo_1'))
+            RepoModel().delete(Repository.get_by_repo_name(u'repo_1'))
             Session().commit()
 
     def test_index_by_id_git(self):
@@ -121,6 +121,9 @@
         self._enable_stats(HG_REPO)
 
         ScmModel().mark_for_invalidation(HG_REPO)
+        # generate statistics first
+        response = self.app.get(url(controller='summary', action='statistics',
+                                    repo_name=HG_REPO))
         response = self.app.get(url(controller='summary', action='index',
                                     repo_name=HG_REPO))
         response.mustcontain(
@@ -151,6 +154,9 @@
         self._enable_stats(GIT_REPO)
 
         ScmModel().mark_for_invalidation(GIT_REPO)
+        # generate statistics first
+        response = self.app.get(url(controller='summary', action='statistics',
+                                    repo_name=GIT_REPO))
         response = self.app.get(url(controller='summary', action='index',
                                     repo_name=GIT_REPO))
         response.mustcontain(
--- a/kallithea/tests/functional/test_tags.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/functional/test_tags.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,7 +1,7 @@
 from kallithea.tests import *
 
 
-class TestTagsController(TestController):
+class TestTagsController(TestControllerPytest):
 
     def test_index_hg(self):
         self.log_user()
--- a/kallithea/tests/models/common.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/models/common.py	Mon Mar 14 00:36:08 2016 +0100
@@ -15,7 +15,7 @@
 
 def _destroy_project_tree(test_u1_id):
     Session.remove()
-    repo_group = RepoGroup.get_by_group_name(group_name='g0')
+    repo_group = RepoGroup.get_by_group_name(group_name=u'g0')
     for el in reversed(repo_group.recursive_groups_and_repos()):
         if isinstance(el, Repository):
             RepoModel().delete(el)
@@ -56,18 +56,18 @@
         username=u'test_u1', password=u'qweqwe',
         email=u'test_u1@example.com', firstname=u'test_u1', lastname=u'test_u1'
     )
-    g0 = fixture.create_repo_group('g0')
-    g0_1 = fixture.create_repo_group('g0_1', group_parent_id=g0)
-    g0_1_1 = fixture.create_repo_group('g0_1_1', group_parent_id=g0_1)
-    g0_1_1_r1 = fixture.create_repo('g0/g0_1/g0_1_1/g0_1_1_r1', repo_group=g0_1_1)
-    g0_1_1_r2 = fixture.create_repo('g0/g0_1/g0_1_1/g0_1_1_r2', repo_group=g0_1_1)
-    g0_1_r1 = fixture.create_repo('g0/g0_1/g0_1_r1', repo_group=g0_1)
-    g0_2 = fixture.create_repo_group('g0_2', group_parent_id=g0)
-    g0_2_r1 = fixture.create_repo('g0/g0_2/g0_2_r1', repo_group=g0_2)
-    g0_2_r2 = fixture.create_repo('g0/g0_2/g0_2_r2', repo_group=g0_2)
-    g0_3 = fixture.create_repo_group('g0_3', group_parent_id=g0)
-    g0_3_r1 = fixture.create_repo('g0/g0_3/g0_3_r1', repo_group=g0_3)
-    g0_3_r2_private = fixture.create_repo('g0/g0_3/g0_3_r1_private',
+    g0 = fixture.create_repo_group(u'g0')
+    g0_1 = fixture.create_repo_group(u'g0_1', group_parent_id=g0)
+    g0_1_1 = fixture.create_repo_group(u'g0_1_1', group_parent_id=g0_1)
+    g0_1_1_r1 = fixture.create_repo(u'g0/g0_1/g0_1_1/g0_1_1_r1', repo_group=g0_1_1)
+    g0_1_1_r2 = fixture.create_repo(u'g0/g0_1/g0_1_1/g0_1_1_r2', repo_group=g0_1_1)
+    g0_1_r1 = fixture.create_repo(u'g0/g0_1/g0_1_r1', repo_group=g0_1)
+    g0_2 = fixture.create_repo_group(u'g0_2', group_parent_id=g0)
+    g0_2_r1 = fixture.create_repo(u'g0/g0_2/g0_2_r1', repo_group=g0_2)
+    g0_2_r2 = fixture.create_repo(u'g0/g0_2/g0_2_r2', repo_group=g0_2)
+    g0_3 = fixture.create_repo_group(u'g0_3', group_parent_id=g0)
+    g0_3_r1 = fixture.create_repo(u'g0/g0_3/g0_3_r1', repo_group=g0_3)
+    g0_3_r2_private = fixture.create_repo(u'g0/g0_3/g0_3_r1_private',
                                           repo_group=g0_3, repo_private=True)
     return test_u1
 
--- a/kallithea/tests/models/test_diff_parsers.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/models/test_diff_parsers.py	Mon Mar 14 00:36:08 2016 +0100
@@ -276,7 +276,10 @@
     @parameterized.expand([(x,) for x in DIFF_FIXTURES])
     def test_diff(self, diff_fixture):
         diff = fixture.load_resource(diff_fixture, strip=False)
-        diff_proc = DiffProcessor(diff)
+        vcs = 'hg'
+        if diff_fixture.startswith('git_'):
+            vcs = 'git'
+        diff_proc = DiffProcessor(diff, vcs=vcs)
         diff_proc_d = diff_proc.prepare()
         data = [(x['filename'], x['operation'], x['stats']) for x in diff_proc_d]
         expected_data = DIFF_FIXTURES[diff_fixture]
--- a/kallithea/tests/models/test_notifications.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/models/test_notifications.py	Mon Mar 14 00:36:08 2016 +0100
@@ -35,7 +35,7 @@
         super(TestNotifications, self).__init__(methodName=methodName)
 
     def setUp(self):
-        self.remove_all_notifications()
+        remove_all_notifications()
         self.assertEqual([], Notification.query().all())
         self.assertEqual([], UserNotification.query().all())
 
@@ -55,7 +55,7 @@
         self.assertEqual(notification.notification_id,
                          notifications[0].notification_id)
 
-        unotification = UserNotification.query()\
+        unotification = UserNotification.query() \
             .filter(UserNotification.notification == notification).all()
 
         self.assertEqual(len(unotification), len(usrs))
@@ -100,10 +100,10 @@
                                     recipients=[self.u3, self.u1, self.u2])
         Session().commit()
 
-        unotification = UserNotification.query()\
+        unotification = UserNotification.query() \
                             .filter(UserNotification.notification ==
-                                    notification)\
-                            .filter(UserNotification.user_id == self.u3)\
+                                    notification) \
+                            .filter(UserNotification.user_id == self.u3) \
                             .scalar()
 
         self.assertEqual(unotification.user_id, self.u3)
@@ -112,10 +112,10 @@
                                    notification.notification_id)
         Session().commit()
 
-        u3notification = UserNotification.query()\
+        u3notification = UserNotification.query() \
                             .filter(UserNotification.notification ==
-                                    notification)\
-                            .filter(UserNotification.user_id == self.u3)\
+                                    notification) \
+                            .filter(UserNotification.user_id == self.u3) \
                             .scalar()
 
         self.assertEqual(u3notification, None)
@@ -124,16 +124,16 @@
         self.assertEqual(Notification.query().all(), [notification])
 
         #u1 and u2 still have assignments
-        u1notification = UserNotification.query()\
+        u1notification = UserNotification.query() \
                             .filter(UserNotification.notification ==
-                                    notification)\
-                            .filter(UserNotification.user_id == self.u1)\
+                                    notification) \
+                            .filter(UserNotification.user_id == self.u1) \
                             .scalar()
         self.assertNotEqual(u1notification, None)
-        u2notification = UserNotification.query()\
+        u2notification = UserNotification.query() \
                             .filter(UserNotification.notification ==
-                                    notification)\
-                            .filter(UserNotification.user_id == self.u2)\
+                                    notification) \
+                            .filter(UserNotification.user_id == self.u2) \
                             .scalar()
         self.assertNotEqual(u2notification, None)
 
@@ -144,7 +144,7 @@
         Session().commit()
 
         self.assertEqual(NotificationModel()
-                         .get_unread_cnt_for_user(self.u1), 1)
+                         .get_unread_cnt_for_user(self.u1), 0)
         self.assertEqual(NotificationModel()
                          .get_unread_cnt_for_user(self.u2), 0)
         self.assertEqual(NotificationModel()
@@ -156,7 +156,7 @@
         Session().commit()
 
         self.assertEqual(NotificationModel()
-                         .get_unread_cnt_for_user(self.u1), 2)
+                         .get_unread_cnt_for_user(self.u1), 0)
         self.assertEqual(NotificationModel()
                          .get_unread_cnt_for_user(self.u2), 1)
         self.assertEqual(NotificationModel()
--- a/kallithea/tests/models/test_permissions.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/models/test_permissions.py	Mon Mar 14 00:36:08 2016 +0100
@@ -2,7 +2,7 @@
 from kallithea.tests.fixture import Fixture
 from kallithea.model.repo_group import RepoGroupModel
 from kallithea.model.repo import RepoModel
-from kallithea.model.db import RepoGroup, User, UserGroupRepoGroupToPerm,\
+from kallithea.model.db import RepoGroup, User, UserGroupRepoGroupToPerm, \
     Permission, UserToPerm
 from kallithea.model.user import UserModel
 
@@ -70,9 +70,9 @@
         u1_auth = AuthUser(user_id=self.u1.user_id)
         perms = {
             'repositories_groups': {},
-            'global': set([u'hg.create.repository', u'repository.read',
-                           u'hg.register.manual_activate']),
-            'repositories': {HG_REPO: u'repository.read'}
+            'global': set(['hg.create.repository', 'repository.read',
+                           'hg.register.manual_activate']),
+            'repositories': {HG_REPO: 'repository.read'}
         }
         self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
                          perms['repositories'][HG_REPO])
@@ -89,8 +89,8 @@
         a1_auth = AuthUser(user_id=self.a1.user_id)
         perms = {
             'repositories_groups': {},
-            'global': set([u'hg.admin', 'hg.create.write_on_repogroup.true']),
-            'repositories': {HG_REPO: u'repository.admin'}
+            'global': set(['hg.admin', 'hg.create.write_on_repogroup.true']),
+            'repositories': {HG_REPO: 'repository.admin'}
         }
         self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
                          perms['repositories'][HG_REPO])
@@ -105,13 +105,13 @@
                          perms['repositories'][HG_REPO])
 
     def test_default_group_perms(self):
-        self.g1 = fixture.create_repo_group('test1', skip_if_exists=True)
-        self.g2 = fixture.create_repo_group('test2', skip_if_exists=True)
+        self.g1 = fixture.create_repo_group(u'test1', skip_if_exists=True)
+        self.g2 = fixture.create_repo_group(u'test2', skip_if_exists=True)
         u1_auth = AuthUser(user_id=self.u1.user_id)
         perms = {
             'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
             'global': set(Permission.DEFAULT_USER_PERMISSIONS),
-            'repositories': {HG_REPO: u'repository.read'}
+            'repositories': {HG_REPO: 'repository.read'}
         }
         self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
                          perms['repositories'][HG_REPO])
@@ -121,8 +121,8 @@
                          perms['global'])
 
     def test_default_admin_group_perms(self):
-        self.g1 = fixture.create_repo_group('test1', skip_if_exists=True)
-        self.g2 = fixture.create_repo_group('test2', skip_if_exists=True)
+        self.g1 = fixture.create_repo_group(u'test1', skip_if_exists=True)
+        self.g2 = fixture.create_repo_group(u'test2', skip_if_exists=True)
         a1_auth = AuthUser(user_id=self.a1.user_id)
         perms = {
             'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
@@ -137,7 +137,7 @@
 
     def test_propagated_permission_from_users_group_by_explicit_perms_exist(self):
         # make group
-        self.ug1 = fixture.create_user_group('G1')
+        self.ug1 = fixture.create_user_group(u'G1')
         UserGroupModel().add_user_to_group(self.ug1, self.u1)
 
         # set permission to lower
@@ -158,9 +158,9 @@
         u1_auth = AuthUser(user_id=self.u1.user_id)
         perms = {
             'repositories_groups': {},
-            'global': set([u'hg.create.repository', u'repository.read',
-                           u'hg.register.manual_activate']),
-            'repositories': {HG_REPO: u'repository.read'}
+            'global': set(['hg.create.repository', 'repository.read',
+                           'hg.register.manual_activate']),
+            'repositories': {HG_REPO: 'repository.read'}
         }
         self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
                          new_perm)
@@ -169,7 +169,7 @@
 
     def test_propagated_permission_from_users_group(self):
         # make group
-        self.ug1 = fixture.create_user_group('G1')
+        self.ug1 = fixture.create_user_group(u'G1')
         UserGroupModel().add_user_to_group(self.ug1, self.u3)
 
         # grant perm for group this should override default permission from user
@@ -181,9 +181,9 @@
         u3_auth = AuthUser(user_id=self.u3.user_id)
         perms = {
             'repositories_groups': {},
-            'global': set([u'hg.create.repository', u'repository.read',
-                           u'hg.register.manual_activate']),
-            'repositories': {HG_REPO: u'repository.read'}
+            'global': set(['hg.create.repository', 'repository.read',
+                           'hg.register.manual_activate']),
+            'repositories': {HG_REPO: 'repository.read'}
         }
         self.assertEqual(u3_auth.permissions['repositories'][HG_REPO],
                          new_perm_gr)
@@ -192,7 +192,7 @@
 
     def test_propagated_permission_from_users_group_lower_weight(self):
         # make group
-        self.ug1 = fixture.create_user_group('G1')
+        self.ug1 = fixture.create_user_group(u'G1')
         # add user to group
         UserGroupModel().add_user_to_group(self.ug1, self.u1)
 
@@ -215,9 +215,9 @@
         u1_auth = AuthUser(user_id=self.u1.user_id)
         perms = {
             'repositories_groups': {},
-            'global': set([u'hg.create.repository', u'repository.read',
-                           u'hg.register.manual_activate']),
-            'repositories': {HG_REPO: u'repository.write'}
+            'global': set(['hg.create.repository', 'repository.read',
+                           'hg.register.manual_activate']),
+            'repositories': {HG_REPO: 'repository.write'}
         }
         self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
                          new_perm_h)
@@ -225,8 +225,8 @@
                          perms['repositories_groups'])
 
     def test_repo_in_group_permissions(self):
-        self.g1 = fixture.create_repo_group('group1', skip_if_exists=True)
-        self.g2 = fixture.create_repo_group('group2', skip_if_exists=True)
+        self.g1 = fixture.create_repo_group(u'group1', skip_if_exists=True)
+        self.g2 = fixture.create_repo_group(u'group2', skip_if_exists=True)
         # both perms should be read !
         u1_auth = AuthUser(user_id=self.u1.user_id)
         self.assertEqual(u1_auth.permissions['repositories_groups'],
@@ -289,7 +289,7 @@
 
     def test_repo_group_user_as_user_group_member(self):
         # create Group1
-        self.g1 = fixture.create_repo_group('group1', skip_if_exists=True)
+        self.g1 = fixture.create_repo_group(u'group1', skip_if_exists=True)
         a1_auth = AuthUser(user_id=self.anon.user_id)
 
         self.assertEqual(a1_auth.permissions['repositories_groups'],
@@ -300,7 +300,7 @@
                                                user=self.anon,
                                                perm='group.none')
         # make group
-        self.ug1 = fixture.create_user_group('G1')
+        self.ug1 = fixture.create_user_group(u'G1')
         # add user to group
         UserGroupModel().add_user_to_group(self.ug1, self.u1)
         Session().commit()
@@ -325,9 +325,9 @@
                                                       perm='group.read')
         Session().commit()
         # check if the
-        obj = Session().query(UserGroupRepoGroupToPerm)\
-            .filter(UserGroupRepoGroupToPerm.group == self.g1)\
-            .filter(UserGroupRepoGroupToPerm.users_group == self.ug1)\
+        obj = Session().query(UserGroupRepoGroupToPerm) \
+            .filter(UserGroupRepoGroupToPerm.group == self.g1) \
+            .filter(UserGroupRepoGroupToPerm.users_group == self.ug1) \
             .scalar()
         self.assertEqual(obj.permission.permission_name, 'group.read')
 
@@ -441,7 +441,7 @@
         # Add user to inactive user group, set specific permissions on user
         # group and disable inherit-from-default. User permissions should still
         # inherit from default.
-        self.ug1 = fixture.create_user_group('G1')
+        self.ug1 = fixture.create_user_group(u'G1')
         self.ug1.inherit_default_permissions = False
         user_group_model = UserGroupModel()
         user_group_model.add_user_to_group(self.ug1, self.u1)
@@ -477,7 +477,7 @@
         # Add user to inactive user group, set specific permissions on user
         # group and disable inherit-from-default. User permissions should still
         # inherit from default.
-        self.ug1 = fixture.create_user_group('G1')
+        self.ug1 = fixture.create_user_group(u'G1')
         self.ug1.inherit_default_permissions = False
         user_group_model = UserGroupModel()
         user_group_model.add_user_to_group(self.ug1, self.u1)
@@ -509,7 +509,7 @@
                               'hg.create.write_on_repogroup.true']))
 
     def test_inactive_user_group_does_not_affect_repo_permissions(self):
-        self.ug1 = fixture.create_user_group('G1')
+        self.ug1 = fixture.create_user_group(u'G1')
         self.ug1.inherit_default_permissions = False
         user_group_model = UserGroupModel()
         user_group_model.add_user_to_group(self.ug1, self.u1)
@@ -517,7 +517,7 @@
 
         # note: make u2 repo owner rather than u1, because the owner always has
         # admin permissions
-        self.test_repo = fixture.create_repo(name='myownrepo',
+        self.test_repo = fixture.create_repo(name=u'myownrepo',
                                              repo_type='hg',
                                              cur_user=self.u2)
 
@@ -535,7 +535,7 @@
                          'repository.write')
 
     def test_inactive_user_group_does_not_affect_repo_permissions_inverse(self):
-        self.ug1 = fixture.create_user_group('G1')
+        self.ug1 = fixture.create_user_group(u'G1')
         self.ug1.inherit_default_permissions = False
         user_group_model = UserGroupModel()
         user_group_model.add_user_to_group(self.ug1, self.u1)
@@ -543,7 +543,7 @@
 
         # note: make u2 repo owner rather than u1, because the owner always has
         # admin permissions
-        self.test_repo = fixture.create_repo(name='myownrepo',
+        self.test_repo = fixture.create_repo(name=u'myownrepo',
                                              repo_type='hg',
                                              cur_user=self.u2)
 
@@ -561,13 +561,13 @@
                          'repository.admin')
 
     def test_inactive_user_group_does_not_affect_repo_group_permissions(self):
-        self.ug1 = fixture.create_user_group('G1')
+        self.ug1 = fixture.create_user_group(u'G1')
         self.ug1.inherit_default_permissions = False
         user_group_model = UserGroupModel()
         user_group_model.add_user_to_group(self.ug1, self.u1)
         user_group_model.update(self.ug1, {'users_group_active': False})
 
-        self.g1 = fixture.create_repo_group('group1', skip_if_exists=True)
+        self.g1 = fixture.create_repo_group(u'group1', skip_if_exists=True)
 
         # enable admin access for user group on repo group
         RepoGroupModel().grant_user_group_permission(self.g1,
@@ -583,13 +583,13 @@
                          {u'group1': u'group.write'})
 
     def test_inactive_user_group_does_not_affect_repo_group_permissions_inverse(self):
-        self.ug1 = fixture.create_user_group('G1')
+        self.ug1 = fixture.create_user_group(u'G1')
         self.ug1.inherit_default_permissions = False
         user_group_model = UserGroupModel()
         user_group_model.add_user_to_group(self.ug1, self.u1)
         user_group_model.update(self.ug1, {'users_group_active': False})
 
-        self.g1 = fixture.create_repo_group('group1', skip_if_exists=True)
+        self.g1 = fixture.create_repo_group(u'group1', skip_if_exists=True)
 
         # enable only write access for user group on repo group
         RepoGroupModel().grant_user_group_permission(self.g1,
@@ -605,13 +605,13 @@
                          {u'group1': u'group.admin'})
 
     def test_inactive_user_group_does_not_affect_user_group_permissions(self):
-        self.ug1 = fixture.create_user_group('G1')
+        self.ug1 = fixture.create_user_group(u'G1')
         self.ug1.inherit_default_permissions = False
         user_group_model = UserGroupModel()
         user_group_model.add_user_to_group(self.ug1, self.u1)
         user_group_model.update(self.ug1, {'users_group_active': False})
 
-        self.ug2 = fixture.create_user_group('G2')
+        self.ug2 = fixture.create_user_group(u'G2')
 
         # enable admin access for user group on user group
         UserGroupModel().grant_user_group_permission(self.ug2,
@@ -627,13 +627,13 @@
         self.assertEqual(u1_auth.permissions['user_groups'][u'G2'], u'usergroup.write')
 
     def test_inactive_user_group_does_not_affect_user_group_permissions_inverse(self):
-        self.ug1 = fixture.create_user_group('G1')
+        self.ug1 = fixture.create_user_group(u'G1')
         self.ug1.inherit_default_permissions = False
         user_group_model = UserGroupModel()
         user_group_model.add_user_to_group(self.ug1, self.u1)
         user_group_model.update(self.ug1, {'users_group_active': False})
 
-        self.ug2 = fixture.create_user_group('G2')
+        self.ug2 = fixture.create_user_group(u'G2')
 
         # enable only write access for user group on user group
         UserGroupModel().grant_user_group_permission(self.ug2,
@@ -650,7 +650,7 @@
 
     def test_owner_permissions_doesnot_get_overwritten_by_group(self):
         #create repo as USER,
-        self.test_repo = fixture.create_repo(name='myownrepo',
+        self.test_repo = fixture.create_repo(name=u'myownrepo',
                                              repo_type='hg',
                                              cur_user=self.u1)
 
@@ -659,7 +659,7 @@
         self.assertEqual(u1_auth.permissions['repositories']['myownrepo'],
                          'repository.admin')
         #set his permission as user group, he should still be admin
-        self.ug1 = fixture.create_user_group('G1')
+        self.ug1 = fixture.create_user_group(u'G1')
         UserGroupModel().add_user_to_group(self.ug1, self.u1)
         RepoModel().grant_user_group_permission(self.test_repo,
                                                  group_name=self.ug1,
@@ -672,7 +672,7 @@
 
     def test_owner_permissions_doesnot_get_overwritten_by_others(self):
         #create repo as USER,
-        self.test_repo = fixture.create_repo(name='myownrepo',
+        self.test_repo = fixture.create_repo(name=u'myownrepo',
                                              repo_type='hg',
                                              cur_user=self.u1)
 
@@ -689,8 +689,8 @@
                          'repository.admin')
 
     def _test_def_perm_equal(self, user, change_factor=0):
-        perms = UserToPerm.query()\
-                .filter(UserToPerm.user == user)\
+        perms = UserToPerm.query() \
+                .filter(UserToPerm.user == user) \
                 .all()
         self.assertEqual(len(perms),
                          len(Permission.DEFAULT_USER_PERMISSIONS,)+change_factor,
@@ -704,8 +704,8 @@
         PermissionModel().create_default_permissions(user=self.u1)
         self._test_def_perm_equal(user=self.u1)
         #now we delete one, it should be re-created after another call
-        perms = UserToPerm.query()\
-                .filter(UserToPerm.user == self.u1)\
+        perms = UserToPerm.query() \
+                .filter(UserToPerm.user == self.u1) \
                 .all()
         Session().delete(perms[0])
         Session().commit()
@@ -734,9 +734,9 @@
         self.assertNotEqual(new, None)
 
         #now modify permissions
-        p = UserToPerm.query()\
-                .filter(UserToPerm.user == self.u1)\
-                .filter(UserToPerm.permission == old)\
+        p = UserToPerm.query() \
+                .filter(UserToPerm.user == self.u1) \
+                .filter(UserToPerm.permission == old) \
                 .one()
         p.permission = new
         Session().add(p)
--- a/kallithea/tests/models/test_repo_groups.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/models/test_repo_groups.py	Mon Mar 14 00:36:08 2016 +0100
@@ -13,7 +13,7 @@
 fixture = Fixture()
 
 
-def _update_group(id_, group_name, desc='desc', parent_id=None):
+def _update_group(id_, group_name, desc=u'desc', parent_id=None):
     form_data = fixture._get_group_create_params(group_name=group_name,
                                                  group_desc=desc,
                                                  group_parent_id=parent_id)
@@ -36,9 +36,9 @@
 class TestRepoGroups(BaseTestCase):
 
     def setUp(self):
-        self.g1 = fixture.create_repo_group('test1', skip_if_exists=True)
-        self.g2 = fixture.create_repo_group('test2', skip_if_exists=True)
-        self.g3 = fixture.create_repo_group('test3', skip_if_exists=True)
+        self.g1 = fixture.create_repo_group(u'test1', skip_if_exists=True)
+        self.g2 = fixture.create_repo_group(u'test2', skip_if_exists=True)
+        self.g3 = fixture.create_repo_group(u'test3', skip_if_exists=True)
 
     def tearDown(self):
         Session.remove()
@@ -58,78 +58,78 @@
         RepoGroupModel().delete(id_)
 
     def test_create_group(self):
-        g = fixture.create_repo_group('newGroup')
+        g = fixture.create_repo_group(u'newGroup')
         Session().commit()
         self.assertEqual(g.full_path, 'newGroup')
 
         self.assertTrue(self.__check_path('newGroup'))
 
     def test_create_same_name_group(self):
-        self.assertRaises(IntegrityError, lambda: fixture.create_repo_group('newGroup'))
+        self.assertRaises(IntegrityError, lambda: fixture.create_repo_group(u'newGroup'))
         Session().rollback()
 
     def test_same_subgroup(self):
-        sg1 = fixture.create_repo_group('sub1', group_parent_id=self.g1.group_id)
+        sg1 = fixture.create_repo_group(u'sub1', group_parent_id=self.g1.group_id)
         self.assertEqual(sg1.parent_group, self.g1)
         self.assertEqual(sg1.full_path, 'test1/sub1')
         self.assertTrue(self.__check_path('test1', 'sub1'))
 
-        ssg1 = fixture.create_repo_group('subsub1', group_parent_id=sg1.group_id)
+        ssg1 = fixture.create_repo_group(u'subsub1', group_parent_id=sg1.group_id)
         self.assertEqual(ssg1.parent_group, sg1)
         self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
         self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
 
     def test_remove_group(self):
-        sg1 = fixture.create_repo_group('deleteme')
+        sg1 = fixture.create_repo_group(u'deleteme')
         self.__delete_group(sg1.group_id)
 
         self.assertEqual(RepoGroup.get(sg1.group_id), None)
         self.assertFalse(self.__check_path('deteteme'))
 
-        sg1 = fixture.create_repo_group('deleteme', group_parent_id=self.g1.group_id)
+        sg1 = fixture.create_repo_group(u'deleteme', group_parent_id=self.g1.group_id)
         self.__delete_group(sg1.group_id)
 
         self.assertEqual(RepoGroup.get(sg1.group_id), None)
         self.assertFalse(self.__check_path('test1', 'deteteme'))
 
     def test_rename_single_group(self):
-        sg1 = fixture.create_repo_group('initial')
+        sg1 = fixture.create_repo_group(u'initial')
 
-        new_sg1 = _update_group(sg1.group_id, 'after')
+        new_sg1 = _update_group(sg1.group_id, u'after')
         self.assertTrue(self.__check_path('after'))
-        self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
+        self.assertEqual(RepoGroup.get_by_group_name(u'initial'), None)
 
     def test_update_group_parent(self):
 
-        sg1 = fixture.create_repo_group('initial', group_parent_id=self.g1.group_id)
+        sg1 = fixture.create_repo_group(u'initial', group_parent_id=self.g1.group_id)
 
-        new_sg1 = _update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
+        new_sg1 = _update_group(sg1.group_id, u'after', parent_id=self.g1.group_id)
         self.assertTrue(self.__check_path('test1', 'after'))
-        self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
+        self.assertEqual(RepoGroup.get_by_group_name(u'test1/initial'), None)
 
-        new_sg1 = _update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
+        new_sg1 = _update_group(sg1.group_id, u'after', parent_id=self.g3.group_id)
         self.assertTrue(self.__check_path('test3', 'after'))
-        self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
+        self.assertEqual(RepoGroup.get_by_group_name(u'test3/initial'), None)
 
-        new_sg1 = _update_group(sg1.group_id, 'hello')
+        new_sg1 = _update_group(sg1.group_id, u'hello')
         self.assertTrue(self.__check_path('hello'))
 
-        self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
+        self.assertEqual(RepoGroup.get_by_group_name(u'hello'), new_sg1)
 
     def test_subgrouping_with_repo(self):
 
-        g1 = fixture.create_repo_group('g1')
-        g2 = fixture.create_repo_group('g2')
+        g1 = fixture.create_repo_group(u'g1')
+        g2 = fixture.create_repo_group(u'g2')
         # create new repo
-        r = fixture.create_repo('john')
+        r = fixture.create_repo(u'john')
 
         self.assertEqual(r.repo_name, 'john')
         # put repo into group
-        r = _update_repo('john', repo_group=g1.group_id)
+        r = _update_repo(u'john', repo_group=g1.group_id)
         Session().commit()
         self.assertEqual(r.repo_name, 'g1/john')
 
-        _update_group(g1.group_id, 'g1', parent_id=g2.group_id)
+        _update_group(g1.group_id, u'g1', parent_id=g2.group_id)
         self.assertTrue(self.__check_path('g2', 'g1'))
 
         # test repo
@@ -137,13 +137,13 @@
                                                                 r.just_name]))
 
     def test_move_to_root(self):
-        g1 = fixture.create_repo_group('t11')
-        g2 = fixture.create_repo_group('t22', group_parent_id=g1.group_id)
+        g1 = fixture.create_repo_group(u't11')
+        g2 = fixture.create_repo_group(u't22', group_parent_id=g1.group_id)
 
         self.assertEqual(g2.full_path, 't11/t22')
         self.assertTrue(self.__check_path('t11', 't22'))
 
-        g2 = _update_group(g2.group_id, 'g22', parent_id=None)
+        g2 = _update_group(g2.group_id, u'g22', parent_id=None)
         Session().commit()
 
         self.assertEqual(g2.group_name, 'g22')
@@ -153,14 +153,14 @@
         self.assertTrue(self.__check_path('g22'))
 
     def test_rename_top_level_group_in_nested_setup(self):
-        g1 = fixture.create_repo_group('L1')
-        g2 = fixture.create_repo_group('L2', group_parent_id=g1.group_id)
-        g3 = fixture.create_repo_group('L3', group_parent_id=g2.group_id)
+        g1 = fixture.create_repo_group(u'L1')
+        g2 = fixture.create_repo_group(u'L2', group_parent_id=g1.group_id)
+        g3 = fixture.create_repo_group(u'L3', group_parent_id=g2.group_id)
 
-        r = fixture.create_repo('L1/L2/L3/L3_REPO', repo_group=g3.group_id)
+        r = fixture.create_repo(u'L1/L2/L3/L3_REPO', repo_group=g3.group_id)
 
         ##rename L1 all groups should be now changed
-        _update_group(g1.group_id, 'L1_NEW')
+        _update_group(g1.group_id, u'L1_NEW')
         Session().commit()
         self.assertEqual(g1.full_path, 'L1_NEW')
         self.assertEqual(g2.full_path, 'L1_NEW/L2')
@@ -168,14 +168,14 @@
         self.assertEqual(r.repo_name,  'L1_NEW/L2/L3/L3_REPO')
 
     def test_change_parent_of_top_level_group_in_nested_setup(self):
-        g1 = fixture.create_repo_group('R1')
-        g2 = fixture.create_repo_group('R2', group_parent_id=g1.group_id)
-        g3 = fixture.create_repo_group('R3', group_parent_id=g2.group_id)
-        g4 = fixture.create_repo_group('R1_NEW')
+        g1 = fixture.create_repo_group(u'R1')
+        g2 = fixture.create_repo_group(u'R2', group_parent_id=g1.group_id)
+        g3 = fixture.create_repo_group(u'R3', group_parent_id=g2.group_id)
+        g4 = fixture.create_repo_group(u'R1_NEW')
 
-        r = fixture.create_repo('R1/R2/R3/R3_REPO', repo_group=g3.group_id)
+        r = fixture.create_repo(u'R1/R2/R3/R3_REPO', repo_group=g3.group_id)
         ##rename L1 all groups should be now changed
-        _update_group(g1.group_id, 'R1', parent_id=g4.group_id)
+        _update_group(g1.group_id, u'R1', parent_id=g4.group_id)
         Session().commit()
         self.assertEqual(g1.full_path, 'R1_NEW/R1')
         self.assertEqual(g2.full_path, 'R1_NEW/R1/R2')
@@ -183,15 +183,15 @@
         self.assertEqual(r.repo_name,  'R1_NEW/R1/R2/R3/R3_REPO')
 
     def test_change_parent_of_top_level_group_in_nested_setup_with_rename(self):
-        g1 = fixture.create_repo_group('X1')
-        g2 = fixture.create_repo_group('X2', group_parent_id=g1.group_id)
-        g3 = fixture.create_repo_group('X3', group_parent_id=g2.group_id)
-        g4 = fixture.create_repo_group('X1_NEW')
+        g1 = fixture.create_repo_group(u'X1')
+        g2 = fixture.create_repo_group(u'X2', group_parent_id=g1.group_id)
+        g3 = fixture.create_repo_group(u'X3', group_parent_id=g2.group_id)
+        g4 = fixture.create_repo_group(u'X1_NEW')
 
-        r = fixture.create_repo('X1/X2/X3/X3_REPO', repo_group=g3.group_id)
+        r = fixture.create_repo(u'X1/X2/X3/X3_REPO', repo_group=g3.group_id)
 
         ##rename L1 all groups should be now changed
-        _update_group(g1.group_id, 'X1_PRIM', parent_id=g4.group_id)
+        _update_group(g1.group_id, u'X1_PRIM', parent_id=g4.group_id)
         Session().commit()
         self.assertEqual(g1.full_path, 'X1_NEW/X1_PRIM')
         self.assertEqual(g2.full_path, 'X1_NEW/X1_PRIM/X2')
--- a/kallithea/tests/models/test_repos.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/models/test_repos.py	Mon Mar 14 00:36:08 2016 +0100
@@ -18,60 +18,60 @@
         Session.remove()
 
     def test_remove_repo(self):
-        repo = fixture.create_repo(name='test-repo-1')
+        repo = fixture.create_repo(name=u'test-repo-1')
         Session().commit()
 
         RepoModel().delete(repo=repo)
         Session().commit()
 
-        self.assertEqual(None, Repository.get_by_repo_name(repo_name='test-repo-1'))
+        self.assertEqual(None, Repository.get_by_repo_name(repo_name=u'test-repo-1'))
 
     def test_remove_repo_repo_raises_exc_when_attached_forks(self):
-        repo = fixture.create_repo(name='test-repo-1')
+        repo = fixture.create_repo(name=u'test-repo-1')
         Session().commit()
 
-        fixture.create_fork(repo.repo_name, 'test-repo-fork-1')
+        fixture.create_fork(repo.repo_name, u'test-repo-fork-1')
         Session().commit()
 
         self.assertRaises(AttachedForksError, lambda: RepoModel().delete(repo=repo))
 
     def test_remove_repo_delete_forks(self):
-        repo = fixture.create_repo(name='test-repo-1')
+        repo = fixture.create_repo(name=u'test-repo-1')
         Session().commit()
 
-        fork = fixture.create_fork(repo.repo_name, 'test-repo-fork-1')
+        fork = fixture.create_fork(repo.repo_name, u'test-repo-fork-1')
         Session().commit()
 
         #fork of fork
-        fixture.create_fork(fork.repo_name, 'test-repo-fork-fork-1')
+        fixture.create_fork(fork.repo_name, u'test-repo-fork-fork-1')
         Session().commit()
 
         RepoModel().delete(repo=repo, forks='delete')
         Session().commit()
 
-        self.assertEqual(None, Repository.get_by_repo_name(repo_name='test-repo-1'))
-        self.assertEqual(None, Repository.get_by_repo_name(repo_name='test-repo-fork-1'))
-        self.assertEqual(None, Repository.get_by_repo_name(repo_name='test-repo-fork-fork-1'))
+        self.assertEqual(None, Repository.get_by_repo_name(repo_name=u'test-repo-1'))
+        self.assertEqual(None, Repository.get_by_repo_name(repo_name=u'test-repo-fork-1'))
+        self.assertEqual(None, Repository.get_by_repo_name(repo_name=u'test-repo-fork-fork-1'))
 
     def test_remove_repo_detach_forks(self):
-        repo = fixture.create_repo(name='test-repo-1')
+        repo = fixture.create_repo(name=u'test-repo-1')
         Session().commit()
 
-        fork = fixture.create_fork(repo.repo_name, 'test-repo-fork-1')
+        fork = fixture.create_fork(repo.repo_name, u'test-repo-fork-1')
         Session().commit()
 
         #fork of fork
-        fixture.create_fork(fork.repo_name, 'test-repo-fork-fork-1')
+        fixture.create_fork(fork.repo_name, u'test-repo-fork-fork-1')
         Session().commit()
 
         RepoModel().delete(repo=repo, forks='detach')
         Session().commit()
 
         try:
-            self.assertEqual(None, Repository.get_by_repo_name(repo_name='test-repo-1'))
-            self.assertNotEqual(None, Repository.get_by_repo_name(repo_name='test-repo-fork-1'))
-            self.assertNotEqual(None, Repository.get_by_repo_name(repo_name='test-repo-fork-fork-1'))
+            self.assertEqual(None, Repository.get_by_repo_name(repo_name=u'test-repo-1'))
+            self.assertNotEqual(None, Repository.get_by_repo_name(repo_name=u'test-repo-fork-1'))
+            self.assertNotEqual(None, Repository.get_by_repo_name(repo_name=u'test-repo-fork-fork-1'))
         finally:
-            RepoModel().delete(repo='test-repo-fork-fork-1')
-            RepoModel().delete(repo='test-repo-fork-1')
+            RepoModel().delete(repo=u'test-repo-fork-fork-1')
+            RepoModel().delete(repo=u'test-repo-fork-1')
             Session().commit()
--- a/kallithea/tests/models/test_user_group_permissions_on_repo_groups.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/models/test_user_group_permissions_on_repo_groups.py	Mon Mar 14 00:36:08 2016 +0100
@@ -17,7 +17,7 @@
 _get_group_perms = None
 
 
-def permissions_setup_func(group_name='g0', perm='group.read', recursive='all'):
+def permissions_setup_func(group_name=u'g0', perm='group.read', recursive='all'):
     """
     Resets all permissions to perm attribute
     """
@@ -44,7 +44,7 @@
     Session().commit()
     test_u2_id = test_u2.user_id
 
-    gr1 = fixture.create_user_group('perms_group_1')
+    gr1 = fixture.create_user_group(u'perms_group_1')
     Session().commit()
     test_u2_gr_id = gr1.users_group_id
     UserGroupModel().add_user_to_group(gr1, user=test_u2_id)
@@ -58,13 +58,13 @@
 
 def teardown_module():
     _destroy_project_tree(test_u2_id)
-    fixture.destroy_user_group('perms_group_1')
+    fixture.destroy_user_group(u'perms_group_1')
 
 
 def test_user_permissions_on_group_without_recursive_mode():
     # set permission to g0 non-recursive mode
     recursive = 'none'
-    group = 'g0'
+    group = u'g0'
     permissions_setup_func(group, 'group.write', recursive=recursive)
 
     items = [x for x in _get_repo_perms(group, recursive)]
@@ -83,7 +83,7 @@
 def test_user_permissions_on_group_without_recursive_mode_subgroup():
     # set permission to g0 non-recursive mode
     recursive = 'none'
-    group = 'g0/g0_1'
+    group = u'g0/g0_1'
     permissions_setup_func(group, 'group.write', recursive=recursive)
 
     items = [x for x in _get_repo_perms(group, recursive)]
@@ -104,7 +104,7 @@
     # set permission to g0 recursive mode, all children including
     # other repos and groups should have this permission now set !
     recursive = 'all'
-    group = 'g0'
+    group = u'g0'
     permissions_setup_func(group, 'group.write', recursive=recursive)
 
     repo_items = [x for x in _get_repo_perms(group, recursive)]
@@ -121,7 +121,7 @@
 def test_user_permissions_on_group_with_recursive_mode_inner_group():
     ## set permission to g0_3 group to none
     recursive = 'all'
-    group = 'g0/g0_3'
+    group = u'g0/g0_3'
     permissions_setup_func(group, 'group.none', recursive=recursive)
 
     repo_items = [x for x in _get_repo_perms(group, recursive)]
@@ -138,7 +138,7 @@
 def test_user_permissions_on_group_with_recursive_mode_deepest():
     ## set permission to g0_3 group to none
     recursive = 'all'
-    group = 'g0/g0_1/g0_1_1'
+    group = u'g0/g0_1/g0_1_1'
     permissions_setup_func(group, 'group.write', recursive=recursive)
 
     repo_items = [x for x in _get_repo_perms(group, recursive)]
@@ -155,7 +155,7 @@
 def test_user_permissions_on_group_with_recursive_mode_only_with_repos():
     ## set permission to g0_3 group to none
     recursive = 'all'
-    group = 'g0/g0_2'
+    group = u'g0/g0_2'
     permissions_setup_func(group, 'group.admin', recursive=recursive)
 
     repo_items = [x for x in _get_repo_perms(group, recursive)]
@@ -172,7 +172,7 @@
 def test_user_permissions_on_group_with_recursive_mode_on_repos():
     # set permission to g0/g0_1 with recursive mode on just repositories
     recursive = 'repos'
-    group = 'g0/g0_1'
+    group = u'g0/g0_1'
     perm = 'group.write'
     permissions_setup_func(group, perm, recursive=recursive)
 
@@ -196,7 +196,7 @@
 def test_user_permissions_on_group_with_recursive_mode_on_repo_groups():
     # set permission to g0/g0_1 with recursive mode on just repository groups
     recursive = 'groups'
-    group = 'g0/g0_1'
+    group = u'g0/g0_1'
     perm = 'group.none'
     permissions_setup_func(group, perm, recursive=recursive)
 
--- a/kallithea/tests/models/test_user_groups.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/models/test_user_groups.py	Mon Mar 14 00:36:08 2016 +0100
@@ -20,12 +20,12 @@
 
     @parameterized.expand([
         ([], [], [], [], []),
-        ([], ['regular'], [], [], ['regular']),  # no changes of regular
-        (['some_other'], [], [], ['some_other'], []),   # not added to regular group
-        ([], ['regular'], ['container'], ['container'], ['regular', 'container']),
-        ([], ['regular'], [], ['container', 'container2'], ['regular', 'container', 'container2']),
-        ([], ['regular'], ['other'], [], ['regular']),  # remove not used
-        (['some_other'], ['regular'], ['other', 'container'], ['container', 'container2'], ['regular', 'container', 'container2']),
+        ([], [u'regular'], [], [], [u'regular']),  # no changes of regular
+        ([u'some_other'], [], [], [u'some_other'], []),   # not added to regular group
+        ([], [u'regular'], [u'container'], [u'container'], [u'regular', u'container']),
+        ([], [u'regular'], [], [u'container', u'container2'], [u'regular', u'container', u'container2']),
+        ([], [u'regular'], [u'other'], [], [u'regular']),  # remove not used
+        ([u'some_other'], [u'regular'], [u'other', u'container'], [u'container', u'container2'], [u'regular', u'container', u'container2']),
     ])
     def test_enforce_groups(self, pre_existing, regular_should_be,
                             external_should_be, groups, expected):
--- a/kallithea/tests/models/test_user_permissions_on_repo_groups.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/models/test_user_permissions_on_repo_groups.py	Mon Mar 14 00:36:08 2016 +0100
@@ -13,7 +13,7 @@
 _get_group_perms = None
 
 
-def permissions_setup_func(group_name='g0', perm='group.read', recursive='all',
+def permissions_setup_func(group_name=u'g0', perm='group.read', recursive='all',
                            user_id=None):
     """
     Resets all permissions to perm attribute
@@ -58,7 +58,7 @@
 def test_user_permissions_on_group_without_recursive_mode():
     # set permission to g0 non-recursive mode
     recursive = 'none'
-    group = 'g0'
+    group = u'g0'
     permissions_setup_func(group, 'group.write', recursive=recursive)
 
     items = [x for x in _get_repo_perms(group, recursive)]
@@ -77,7 +77,7 @@
 def test_user_permissions_on_group_without_recursive_mode_subgroup():
     # set permission to g0 non-recursive mode
     recursive = 'none'
-    group = 'g0/g0_1'
+    group = u'g0/g0_1'
     permissions_setup_func(group, 'group.write', recursive=recursive)
 
     items = [x for x in _get_repo_perms(group, recursive)]
@@ -98,7 +98,7 @@
     # set permission to g0 recursive mode, all children including
     # other repos and groups should have this permission now set !
     recursive = 'all'
-    group = 'g0'
+    group = u'g0'
     permissions_setup_func(group, 'group.write', recursive=recursive)
 
     repo_items = [x for x in _get_repo_perms(group, recursive)]
@@ -117,7 +117,7 @@
     # set permission to g0 recursive mode, all children including
     # other repos and groups should have this permission now set !
     recursive = 'all'
-    group = 'g0'
+    group = u'g0'
     default_user_id = User.get_default_user().user_id
     permissions_setup_func(group, 'group.write', recursive=recursive,
                            user_id=default_user_id)
@@ -142,7 +142,7 @@
 def test_user_permissions_on_group_with_recursive_mode_inner_group():
     ## set permission to g0_3 group to none
     recursive = 'all'
-    group = 'g0/g0_3'
+    group = u'g0/g0_3'
     permissions_setup_func(group, 'group.none', recursive=recursive)
 
     repo_items = [x for x in _get_repo_perms(group, recursive)]
@@ -159,7 +159,7 @@
 def test_user_permissions_on_group_with_recursive_mode_deepest():
     ## set permission to g0_3 group to none
     recursive = 'all'
-    group = 'g0/g0_1/g0_1_1'
+    group = u'g0/g0_1/g0_1_1'
     permissions_setup_func(group, 'group.write', recursive=recursive)
 
     repo_items = [x for x in _get_repo_perms(group, recursive)]
@@ -176,7 +176,7 @@
 def test_user_permissions_on_group_with_recursive_mode_only_with_repos():
     ## set permission to g0_3 group to none
     recursive = 'all'
-    group = 'g0/g0_2'
+    group = u'g0/g0_2'
     permissions_setup_func(group, 'group.admin', recursive=recursive)
 
     repo_items = [x for x in _get_repo_perms(group, recursive)]
@@ -194,7 +194,7 @@
     # set permission to g0/g0_1 recursive repos only mode, all children including
     # other repos should have this permission now set, inner groups are excluded!
     recursive = 'repos'
-    group = 'g0/g0_1'
+    group = u'g0/g0_1'
     perm = 'group.none'
     default_user_id = User.get_default_user().user_id
 
@@ -227,7 +227,7 @@
 def test_user_permissions_on_group_with_recursive_repo_mode_inner_group():
     ## set permission to g0_3 group to none, with recursive repos only
     recursive = 'repos'
-    group = 'g0/g0_3'
+    group = u'g0/g0_3'
     perm = 'group.none'
     permissions_setup_func(group, perm, recursive=recursive)
 
@@ -253,7 +253,7 @@
     # other groups should have this permission now set. repositories should
     # remain intact as we use groups only mode !
     recursive = 'groups'
-    group = 'g0/g0_1'
+    group = u'g0/g0_1'
     default_user_id = User.get_default_user().user_id
     permissions_setup_func(group, 'group.write', recursive=recursive,
                            user_id=default_user_id)
@@ -278,7 +278,7 @@
 def test_user_permissions_on_group_with_recursive_group_mode_inner_group():
     ## set permission to g0_3 group to none, with recursive mode for groups only
     recursive = 'groups'
-    group = 'g0/g0_3'
+    group = u'g0/g0_3'
     permissions_setup_func(group, 'group.none', recursive=recursive)
 
     repo_items = [x for x in _get_repo_perms(group, recursive)]
--- a/kallithea/tests/models/test_users.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/models/test_users.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,6 +1,6 @@
 from kallithea.tests import *
 
-from kallithea.model.db import User, UserGroup, UserGroupMember, UserEmailMap,\
+from kallithea.model.db import User, UserGroup, UserGroupMember, UserEmailMap, \
     Permission
 from kallithea.model.user import UserModel
 
@@ -26,9 +26,11 @@
                                            firstname=u'u1', lastname=u'u1')
         Session().commit()
         self.assertEqual(User.get_by_username(u'test_user'), usr)
+        self.assertEqual(User.get_by_username(u'test_USER', case_insensitive=True), usr)
+        self.assertEqual(User.get_by_username(u'test_USER', case_insensitive=False), None)
 
         # make user group
-        user_group = fixture.create_user_group('some_example_group')
+        user_group = fixture.create_user_group(u'some_example_group')
         Session().commit()
 
         UserGroupModel().add_user_to_group(user_group, usr)
@@ -72,6 +74,10 @@
         Session().add(m)
         Session().commit()
 
+        u = User.get_by_email(email='MAIN_email@example.com')
+        self.assertEqual(usr.user_id, u.user_id)
+        self.assertEqual(usr.username, u.username)
+
         u = User.get_by_email(email='main_email@example.com')
         self.assertEqual(usr.user_id, u.user_id)
         self.assertEqual(usr.username, u.username)
@@ -82,6 +88,12 @@
         u = User.get_by_email(email='main_email3@example.com')
         self.assertEqual(None, u)
 
+        u = User.get_by_email(email='main_e%ail@example.com')
+        self.assertEqual(None, u)
+        u = User.get_by_email(email='main_emai_@example.com')
+        self.assertEqual(None, u)
+
+
         UserModel().delete(usr.user_id)
         Session().commit()
 
--- a/kallithea/tests/other/manual_test_vcs_operations.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/other/manual_test_vcs_operations.py	Mon Mar 14 00:36:08 2016 +0100
@@ -188,7 +188,7 @@
 
     def test_clone_hg_repo_by_admin(self):
         clone_url = _construct_url(HG_REPO)
-        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
 
         assert 'requesting all changes' in stdout
         assert 'adding changesets' in stdout
@@ -199,45 +199,45 @@
 
     def test_clone_git_repo_by_admin(self):
         clone_url = _construct_url(GIT_REPO)
-        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
 
         assert 'Cloning into' in stdout + stderr
         assert stderr == '' or stdout == ''
 
     def test_clone_wrong_credentials_hg(self):
         clone_url = _construct_url(HG_REPO, passwd='bad!')
-        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
         assert 'abort: authorization failed' in stderr
 
     def test_clone_wrong_credentials_git(self):
         clone_url = _construct_url(GIT_REPO, passwd='bad!')
-        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
         assert 'fatal: Authentication failed' in stderr
 
     def test_clone_git_dir_as_hg(self):
         clone_url = _construct_url(GIT_REPO)
-        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
         assert 'HTTP Error 404: Not Found' in stderr
 
     def test_clone_hg_repo_as_git(self):
         clone_url = _construct_url(HG_REPO)
-        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
         assert 'not found' in stderr
 
     def test_clone_non_existing_path_hg(self):
         clone_url = _construct_url('trololo')
-        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
         assert 'HTTP Error 404: Not Found' in stderr
 
     def test_clone_non_existing_path_git(self):
         clone_url = _construct_url('trololo')
-        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
         assert 'not found' in stderr
 
     def test_push_new_file_hg(self):
         DEST = _get_tmp_dir()
         clone_url = _construct_url(HG_REPO, dest=DEST)
-        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
 
         stdout, stderr = _add_files_and_push('hg', DEST)
 
@@ -248,7 +248,7 @@
     def test_push_new_file_git(self):
         DEST = _get_tmp_dir()
         clone_url = _construct_url(GIT_REPO, dest=DEST)
-        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
 
         # commit some stuff into this repo
         stdout, stderr = _add_files_and_push('git', DEST)
@@ -268,13 +268,13 @@
 
         DEST = _get_tmp_dir()
         clone_url = _construct_url(HG_REPO, dest=DEST)
-        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
 
         stdout, stderr = _add_files_and_push('hg', DEST, files_no=1)
 
         key = CacheInvalidation.query().filter(CacheInvalidation.cache_key
-                                               ==HG_REPO).one()
-        self.assertEqual(key.cache_active, False)
+                                               ==HG_REPO).all()
+        self.assertEqual(key, [])
 
     def test_push_invalidates_cache_git(self):
         key = CacheInvalidation.query().filter(CacheInvalidation.cache_key
@@ -288,21 +288,20 @@
 
         DEST = _get_tmp_dir()
         clone_url = _construct_url(GIT_REPO, dest=DEST)
-        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
 
         # commit some stuff into this repo
         stdout, stderr = _add_files_and_push('git', DEST, files_no=1)
         _check_proper_git_push(stdout, stderr)
 
         key = CacheInvalidation.query().filter(CacheInvalidation.cache_key
-                                               ==GIT_REPO).one()
-        print CacheInvalidation.get_all()
-        self.assertEqual(key.cache_active, False)
+                                               ==GIT_REPO).all()
+        self.assertEqual(key, [])
 
     def test_push_wrong_credentials_hg(self):
         DEST = _get_tmp_dir()
         clone_url = _construct_url(HG_REPO, dest=DEST)
-        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
 
         stdout, stderr = _add_files_and_push('hg', DEST, user='bad',
                                              passwd='name')
@@ -312,7 +311,7 @@
     def test_push_wrong_credentials_git(self):
         DEST = _get_tmp_dir()
         clone_url = _construct_url(GIT_REPO, dest=DEST)
-        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
 
         stdout, stderr = _add_files_and_push('git', DEST, user='bad',
                                              passwd='name')
@@ -322,7 +321,7 @@
     def test_push_back_to_wrong_url_hg(self):
         DEST = _get_tmp_dir()
         clone_url = _construct_url(HG_REPO, dest=DEST)
-        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
 
         stdout, stderr = _add_files_and_push('hg', DEST,
                                     clone_url='http://%s/tmp' % HOST)
@@ -332,7 +331,7 @@
     def test_push_back_to_wrong_url_git(self):
         DEST = _get_tmp_dir()
         clone_url = _construct_url(GIT_REPO, dest=DEST)
-        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
 
         stdout, stderr = _add_files_and_push('git', DEST,
                                     clone_url='http://%s/tmp' % HOST)
@@ -347,7 +346,7 @@
         Session().commit()
         # clone
         clone_url = _construct_url(HG_REPO)
-        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
 
         #check if lock was made
         r = Repository.get_by_repo_name(HG_REPO)
@@ -361,7 +360,7 @@
         Session().commit()
         # clone
         clone_url = _construct_url(GIT_REPO)
-        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
 
         #check if lock was made
         r = Repository.get_by_repo_name(GIT_REPO)
@@ -373,7 +372,7 @@
         Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
         #pull fails since repo is locked
         clone_url = _construct_url(HG_REPO)
-        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
         msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`"""
                 % (HG_REPO, TEST_USER_ADMIN_LOGIN))
         assert msg in stderr
@@ -384,7 +383,7 @@
         Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
         #pull fails since repo is locked
         clone_url = _construct_url(GIT_REPO)
-        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
         msg = ("""The requested URL returned error: 423""")
         assert msg in stderr
 
@@ -392,7 +391,7 @@
         #clone some temp
         DEST = _get_tmp_dir()
         clone_url = _construct_url(HG_REPO, dest=DEST)
-        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
 
         #lock repo
         r = Repository.get_by_repo_name(HG_REPO)
@@ -414,7 +413,7 @@
         #clone some temp
         DEST = _get_tmp_dir()
         clone_url = _construct_url(GIT_REPO, dest=DEST)
-        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
 
         #lock repo
         r = Repository.get_by_repo_name(GIT_REPO)
@@ -448,7 +447,7 @@
         #clone some temp
         DEST = _get_tmp_dir()
         clone_url = _construct_url(HG_REPO, dest=DEST)
-        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
 
         #check for lock repo after clone
         r = Repository.get_by_repo_name(HG_REPO)
@@ -473,7 +472,7 @@
         #clone some temp
         DEST = _get_tmp_dir()
         clone_url = _construct_url(GIT_REPO, dest=DEST)
-        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
 
         #check for lock repo after clone
         r = Repository.get_by_repo_name(GIT_REPO)
@@ -495,7 +494,7 @@
             user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32')
             Session().commit()
             clone_url = _construct_url(HG_REPO)
-            stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+            stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
             assert 'abort: HTTP Error 403: Forbidden' in stderr
         finally:
             #release IP restrictions
@@ -505,7 +504,7 @@
 
         time.sleep(2)
         clone_url = _construct_url(HG_REPO)
-        stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('hg clone', clone_url)
 
         assert 'requesting all changes' in stdout
         assert 'adding changesets' in stdout
@@ -520,7 +519,7 @@
             user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32')
             Session().commit()
             clone_url = _construct_url(GIT_REPO)
-            stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+            stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
             # The message apparently changed in Git 1.8.3, so match it loosely.
             assert re.search(r'\b403\b', stderr)
         finally:
@@ -531,7 +530,7 @@
 
         time.sleep(2)
         clone_url = _construct_url(GIT_REPO)
-        stdout, stderr = Command('/tmp').execute('git clone', clone_url)
+        stdout, stderr = Command(tempfile.gettempdir()).execute('git clone', clone_url)
 
         assert 'Cloning into' in stdout + stderr
         assert stderr == '' or stdout == ''
--- a/kallithea/tests/other/test_mail.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/other/test_mail.py	Mon Mar 14 00:36:08 2016 +0100
@@ -145,7 +145,7 @@
         subject = 'subject'
         body = 'body'
         html_body = 'html_body'
-        author = User(name='foo', lastname='(fubar) "baz"')
+        author = User(name='foo', lastname=u'(fubar) "baz"')
         headers = {'extra': 'yes'}
 
         config_mock = {
--- a/kallithea/tests/other/test_validators.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/other/test_validators.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 import formencode
+import tempfile
 
 from kallithea.tests import *
 
@@ -51,21 +52,21 @@
 
     def test_ValidUserGroup(self):
         validator = v.ValidUserGroup()
-        self.assertRaises(formencode.Invalid, validator.to_python, 'default')
-        self.assertRaises(formencode.Invalid, validator.to_python, '.,')
+        self.assertRaises(formencode.Invalid, validator.to_python, u'default')
+        self.assertRaises(formencode.Invalid, validator.to_python, u'.,')
 
-        gr = fixture.create_user_group('test')
-        gr2 = fixture.create_user_group('tes2')
+        gr = fixture.create_user_group(u'test')
+        gr2 = fixture.create_user_group(u'tes2')
         Session().commit()
-        self.assertRaises(formencode.Invalid, validator.to_python, 'test')
+        self.assertRaises(formencode.Invalid, validator.to_python, u'test')
         assert gr.users_group_id is not None
         validator = v.ValidUserGroup(edit=True,
                                     old_data={'users_group_id':
                                               gr2.users_group_id})
 
-        self.assertRaises(formencode.Invalid, validator.to_python, 'test')
-        self.assertRaises(formencode.Invalid, validator.to_python, 'TesT')
-        self.assertRaises(formencode.Invalid, validator.to_python, 'TEST')
+        self.assertRaises(formencode.Invalid, validator.to_python, u'test')
+        self.assertRaises(formencode.Invalid, validator.to_python, u'TesT')
+        self.assertRaises(formencode.Invalid, validator.to_python, u'TEST')
         UserGroupModel().delete(gr)
         UserGroupModel().delete(gr2)
         Session().commit()
@@ -75,7 +76,7 @@
         model = RepoGroupModel()
         self.assertRaises(formencode.Invalid, validator.to_python,
                           {'group_name': HG_REPO, })
-        gr = model.create(group_name='test_gr', group_description='desc',
+        gr = model.create(group_name=u'test_gr', group_description=u'desc',
                           parent=None,
                           just_db=True,
                           owner=TEST_USER_ADMIN_LOGIN)
@@ -147,8 +148,8 @@
         self.assertRaises(formencode.Invalid,
                           validator.to_python, {'repo_name': HG_REPO})
 
-        gr = RepoGroupModel().create(group_name='group_test',
-                                      group_description='desc',
+        gr = RepoGroupModel().create(group_name=u'group_test',
+                                      group_description=u'desc',
                                       parent=None,
                                       owner=TEST_USER_ADMIN_LOGIN)
         self.assertRaises(formencode.Invalid,
@@ -197,8 +198,8 @@
 
     def test_ValidPath(self):
             validator = v.ValidPath()
-            self.assertEqual(TESTS_TMP_PATH,
-                             validator.to_python(TESTS_TMP_PATH))
+            self.assertEqual(tempfile.gettempdir(),
+                             validator.to_python(tempfile.gettempdir()))
             self.assertRaises(formencode.Invalid, validator.to_python,
                               '/no_such_dir')
 
--- a/kallithea/tests/parameterized.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/parameterized.py	Mon Mar 14 00:36:08 2016 +0100
@@ -10,13 +10,6 @@
 
 def skip_test(func):
     try:
-        from nose.tools import nottest
-    except ImportError:
-        pass
-    else:
-        func = nottest(func)
-
-    try:
         import pytest
     except ImportError:
         pass
--- a/kallithea/tests/scripts/manual_test_concurrency.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/scripts/manual_test_concurrency.py	Mon Mar 14 00:36:08 2016 +0100
@@ -42,10 +42,10 @@
 from kallithea.lib.utils import add_cache
 from kallithea.model import init_model
 from kallithea.model import meta
-from kallithea.model.db import User, Repository
+from kallithea.model.db import User, Repository, Ui
 from kallithea.lib.auth import get_crypt_password
 
-from kallithea.tests import TESTS_TMP_PATH, HG_REPO
+from kallithea.tests import HG_REPO
 from kallithea.config.environment import load_environment
 
 rel_path = dn(dn(dn(dn(os.path.abspath(__file__)))))
@@ -161,7 +161,7 @@
 #==============================================================================
 def test_clone_with_credentials(no_errors=False, repo=HG_REPO, method=METHOD,
                                 seq=None, backend='hg'):
-    cwd = path = jn(TESTS_TMP_PATH, repo)
+    cwd = path = jn(Ui.get_by_key('paths', '/').ui_value, repo)
 
     if seq is None:
         seq = _RandomNameSequence().next()
--- a/kallithea/tests/scripts/manual_test_crawler.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/scripts/manual_test_crawler.py	Mon Mar 14 00:36:08 2016 +0100
@@ -37,6 +37,7 @@
 import time
 import os
 import sys
+import tempfile
 from os.path import join as jn
 from os.path import dirname as dn
 
@@ -69,7 +70,7 @@
 ]
 
 
-cj = cookielib.FileCookieJar('/tmp/rc_test_cookie.txt')
+cj = cookielib.FileCookieJar(jn(tempfile.gettempdir(), 'rc_test_cookie.txt'))
 o = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
 o.addheaders = [
     ('User-agent', 'kallithea-crawler'),
--- a/kallithea/tests/test.ini	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/test.ini	Mon Mar 14 00:36:08 2016 +0100
@@ -163,6 +163,7 @@
 
 ## COMMON ##
 host = 127.0.0.1
+#port = 5000
 port = 4999
 
 ## middleware for hosting the WSGI application under a URL prefix
@@ -224,6 +225,7 @@
 
 ## options for showing and identifying changesets
 show_sha_length = 12
+#show_revision_number = false
 show_revision_number = true
 
 ## gist URL alias, used to create nicer urls for gist. This should be an
@@ -280,12 +282,6 @@
 #issue_server_link_wiki = https://wiki.example.com/{id}
 #issue_prefix_wiki = WIKI-
 
-## instance-id prefix
-## a prefix key for this instance used for cache invalidation when running
-## multiple instances of kallithea, make sure it's globally unique for
-## all running kallithea instances. Leave empty if you don't use it
-instance_id =
-
 ## alternative return HTTP header for failed authentication. Default HTTP
 ## response is 401 HTTPUnauthorized. Currently Mercurial clients have trouble with
 ## handling that. Set this variable to 403 to return HTTPForbidden
@@ -301,6 +297,18 @@
 ## allows to setup custom hooks in settings page
 allow_custom_hooks_settings = True
 
+## extra extensions for indexing, space separated and without the leading '.'.
+# index.extensions =
+#    gemfile
+#    lock
+
+## extra filenames for indexing, space separated
+# index.filenames =
+#    .dockerignore
+#    .editorconfig
+#    INSTALL
+#    CHANGELOG
+
 ####################################
 ###        CELERY CONFIG        ####
 ####################################
@@ -347,6 +355,7 @@
 beaker.cache.long_term.key_length = 256
 
 beaker.cache.sql_cache_short.type = memory
+#beaker.cache.sql_cache_short.expire = 10
 beaker.cache.sql_cache_short.expire = 1
 beaker.cache.sql_cache_short.key_length = 256
 
@@ -486,13 +495,12 @@
 #sqlalchemy.db1.url = postgresql://user:pass@localhost/kallithea
 
 # MySQL
-#sqlalchemy.db1.url = mysql://user:pass@localhost/kallithea
+#sqlalchemy.db1.url = mysql://user:pass@localhost/kallithea?charset=utf8
 
 # see sqlalchemy docs for others
 
 sqlalchemy.db1.echo = false
 sqlalchemy.db1.pool_recycle = 3600
-sqlalchemy.db1.convert_unicode = true
 
 ################################
 ### LOGGING CONFIGURATION   ####
@@ -543,8 +551,8 @@
 
 [logger_sqlalchemy]
 #level = INFO
+level = ERROR
 #handlers = console_sql
-level = ERROR
 handlers = console
 qualname = sqlalchemy.engine
 propagate = 0
--- a/kallithea/tests/vcs/conf.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/vcs/conf.py	Mon Mar 14 00:36:08 2016 +0100
@@ -22,7 +22,7 @@
 
 GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
 
-TEST_TMP_PATH = os.environ.get('VCS_TEST_ROOT', '/tmp')
+TEST_TMP_PATH = os.environ.get('VCS_TEST_ROOT', tempfile.gettempdir())
 TEST_GIT_REPO = os.environ.get('VCS_TEST_GIT_REPO',
                               jn(TEST_TMP_PATH, 'vcs-git'))
 TEST_GIT_REPO_CLONE = os.environ.get('VCS_TEST_GIT_REPO_CLONE',
--- a/kallithea/tests/vcs/test_archives.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/vcs/test_archives.py	Mon Mar 14 00:36:08 2016 +0100
@@ -74,12 +74,12 @@
 
     def test_archive_default_stream(self):
         tmppath = tempfile.mkstemp()[1]
-        with open(tmppath, 'w') as stream:
+        with open(tmppath, 'wb') as stream:
             self.tip.fill_archive(stream=stream)
         mystream = StringIO.StringIO()
         self.tip.fill_archive(stream=mystream)
         mystream.seek(0)
-        with open(tmppath, 'r') as f:
+        with open(tmppath, 'rb') as f:
             self.assertEqual(f.read(), mystream.read())
 
     def test_archive_wrong_kind(self):
--- a/kallithea/tests/vcs/test_git.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/vcs/test_git.py	Mon Mar 14 00:36:08 2016 +0100
@@ -4,6 +4,10 @@
 import mock
 import datetime
 import urllib2
+import tempfile
+
+import pytest
+
 from kallithea.lib.vcs.backends.git import GitRepository, GitChangeset
 from kallithea.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
 from kallithea.lib.vcs.nodes import NodeKind, FileNode, DirNode, NodeState
@@ -17,7 +21,7 @@
 
     def __check_for_existing_repo(self):
         if os.path.exists(TEST_GIT_REPO_CLONE):
-            self.fail('Cannot test git clone repo as location %s already '
+            pytest.fail('Cannot test git clone repo as location %s already '
                       'exists. You should manually remove it first.'
                       % TEST_GIT_REPO_CLONE)
 
@@ -25,7 +29,7 @@
         self.repo = GitRepository(TEST_GIT_REPO)
 
     def test_wrong_repo_path(self):
-        wrong_repo_path = '/tmp/errorrepo'
+        wrong_repo_path = os.path.join(tempfile.gettempdir(), 'errorrepo')
         self.assertRaises(RepositoryError, GitRepository, wrong_repo_path)
 
     def test_git_cmd_injection(self):
@@ -303,8 +307,8 @@
         self.assertTrue(api is chset.get_node('docs/api'))
         index = api.get_node('index.rst')
         self.assertTrue(index is chset.get_node('docs/api/index.rst'))
-        self.assertTrue(index is chset.get_node('docs')\
-            .get_node('api')\
+        self.assertTrue(index is chset.get_node('docs') \
+            .get_node('api') \
             .get_node('index.rst'))
 
     def test_branch_and_tags(self):
@@ -339,7 +343,7 @@
             idx += 1
             rev_id = self.repo.revisions[rev]
             if idx > limit:
-                self.fail("Exceeded limit already (getting revision %s, "
+                pytest.fail("Exceeded limit already (getting revision %s, "
                     "there are %s total revisions, offset=%s, limit=%s)"
                     % (rev_id, count, offset, limit))
             self.assertEqual(changeset, self.repo.get_changeset(rev_id))
@@ -347,7 +351,7 @@
         start = offset
         end = limit and offset + limit or None
         sliced = list(self.repo[start:end])
-        self.failUnlessEqual(result, sliced,
+        pytest.failUnlessEqual(result, sliced,
             msg="Comparison failed for limit=%s, offset=%s"
             "(get_changeset returned: %s and sliced: %s"
             % (limit, offset, result, sliced))
@@ -535,8 +539,8 @@
         """
         Tests state of FileNodes.
         """
-        node = self.repo\
-            .get_changeset('e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0')\
+        node = self.repo \
+            .get_changeset('e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0') \
             .get_node('vcs/utils/diffs.py')
         self.assertTrue(node.state, NodeState.ADDED)
         self.assertTrue(node.added)
@@ -544,8 +548,8 @@
         self.assertFalse(node.not_changed)
         self.assertFalse(node.removed)
 
-        node = self.repo\
-            .get_changeset('33fa3223355104431402a888fa77a4e9956feb3e')\
+        node = self.repo \
+            .get_changeset('33fa3223355104431402a888fa77a4e9956feb3e') \
             .get_node('.hgignore')
         self.assertTrue(node.state, NodeState.CHANGED)
         self.assertFalse(node.added)
@@ -553,8 +557,8 @@
         self.assertFalse(node.not_changed)
         self.assertFalse(node.removed)
 
-        node = self.repo\
-            .get_changeset('e29b67bd158580fc90fc5e9111240b90e6e86064')\
+        node = self.repo \
+            .get_changeset('e29b67bd158580fc90fc5e9111240b90e6e86064') \
             .get_node('setup.py')
         self.assertTrue(node.state, NodeState.NOT_CHANGED)
         self.assertFalse(node.added)
@@ -599,24 +603,24 @@
 
     def test_author_email(self):
         self.assertEqual('marcin@python-blog.com',
-          self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3')\
+          self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3') \
           .author_email)
         self.assertEqual('lukasz.balcerzak@python-center.pl',
-          self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b')\
+          self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b') \
           .author_email)
-        self.assertEqual('none@none',
-          self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992')\
+        self.assertEqual('',
+          self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992') \
           .author_email)
 
     def test_author_username(self):
         self.assertEqual('Marcin Kuzminski',
-          self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3')\
+          self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3') \
           .author_name)
         self.assertEqual('Lukasz Balcerzak',
-          self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b')\
+          self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b') \
           .author_name)
-        self.assertEqual('marcink',
-          self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992')\
+        self.assertEqual('marcink none@none',
+          self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992') \
           .author_name)
 
 
--- a/kallithea/tests/vcs/test_hg.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/vcs/test_hg.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,5 +1,10 @@
 
 import os
+
+import pytest
+
+import tempfile
+from kallithea.lib.utils2 import safe_str
 from kallithea.lib.vcs.backends.hg import MercurialRepository, MercurialChangeset
 from kallithea.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
 from kallithea.lib.vcs.nodes import NodeKind, NodeState
@@ -19,15 +24,15 @@
 
     def __check_for_existing_repo(self):
         if os.path.exists(TEST_HG_REPO_CLONE):
-            self.fail('Cannot test mercurial clone repo as location %s already '
+            pytest.fail('Cannot test mercurial clone repo as location %s already '
                       'exists. You should manually remove it first.'
                       % TEST_HG_REPO_CLONE)
 
     def setUp(self):
-        self.repo = MercurialRepository(TEST_HG_REPO)
+        self.repo = MercurialRepository(safe_str(TEST_HG_REPO))
 
     def test_wrong_repo_path(self):
-        wrong_repo_path = '/tmp/errorrepo'
+        wrong_repo_path = os.path.join(tempfile.gettempdir(), 'errorrepo')
         self.assertRaises(RepositoryError, MercurialRepository, wrong_repo_path)
 
     def test_unicode_path_repo(self):
@@ -35,7 +40,7 @@
 
     def test_repo_clone(self):
         self.__check_for_existing_repo()
-        repo = MercurialRepository(TEST_HG_REPO)
+        repo = MercurialRepository(safe_str(TEST_HG_REPO))
         repo_clone = MercurialRepository(TEST_HG_REPO_CLONE,
             src_url=TEST_HG_REPO, update_after_clone=True)
         self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
@@ -45,7 +50,7 @@
             self.assertEqual(raw_id, repo_clone.get_changeset(raw_id).raw_id)
 
     def test_repo_clone_with_update(self):
-        repo = MercurialRepository(TEST_HG_REPO)
+        repo = MercurialRepository(safe_str(TEST_HG_REPO))
         repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_w_update',
             src_url=TEST_HG_REPO, update_after_clone=True)
         self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
@@ -56,7 +61,7 @@
                                                     'MANIFEST.in')), True,)
 
     def test_repo_clone_without_update(self):
-        repo = MercurialRepository(TEST_HG_REPO)
+        repo = MercurialRepository(safe_str(TEST_HG_REPO))
         repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_wo_update',
             src_url=TEST_HG_REPO, update_after_clone=False)
         self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
@@ -66,7 +71,7 @@
 
     def test_pull(self):
         if os.path.exists(TEST_HG_REPO_PULL):
-            self.fail('Cannot test mercurial pull command as location %s '
+            pytest.fail('Cannot test mercurial pull command as location %s '
                       'already exists. You should manually remove it first'
                       % TEST_HG_REPO_PULL)
         repo_new = MercurialRepository(TEST_HG_REPO_PULL, create=True)
@@ -242,7 +247,7 @@
 class MercurialChangesetTest(unittest.TestCase):
 
     def setUp(self):
-        self.repo = MercurialRepository(TEST_HG_REPO)
+        self.repo = MercurialRepository(safe_str(TEST_HG_REPO))
 
     def _test_equality(self, changeset):
         revision = changeset.revision
@@ -302,8 +307,8 @@
         self.assertTrue(api is chset.get_node('docs/api'))
         index = api.get_node('index.rst')
         self.assertTrue(index is chset.get_node('docs/api/index.rst'))
-        self.assertTrue(index is chset.get_node('docs')\
-            .get_node('api')\
+        self.assertTrue(index is chset.get_node('docs') \
+            .get_node('api') \
             .get_node('index.rst'))
 
     def test_branch_and_tags(self):
--- a/kallithea/tests/vcs/test_nodes.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/vcs/test_nodes.py	Mon Mar 14 00:36:08 2016 +0100
@@ -76,7 +76,7 @@
     '''
     def _test_trailing_slash(self, path):
         if not path.endswith('/'):
-            self.fail("Trailing slash tests needs paths to end with slash")
+            pytest.fail("Trailing slash tests needs paths to end with slash")
         for kind in NodeKind.FILE, NodeKind.DIR:
             self.assertRaises(NodeError, Node, path=path, kind=kind)
 
--- a/kallithea/tests/vcs/test_utils.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/vcs/test_utils.py	Mon Mar 14 00:36:08 2016 +0100
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 
 import os
 import mock
@@ -204,6 +205,8 @@
                    ('justname', '')),
                   ('Mr Double Name withemail@example.com ',
                    ('Mr Double Name', 'withemail@example.com')),
+                  (u'John Doe <джондо à éẋàṁṗłê.ç°ḿ>',
+                   (u'John Doe <\u0434\u0436\u043e\u043d\u0434\u043e \xe0 \xe9\u1e8b\xe0\u1e41\u1e57\u0142\xea.\xe7\xb0\u1e3f>', '')),
                   ]
 
     def test_author_email(self):
--- a/kallithea/tests/vcs/test_vcs.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/kallithea/tests/vcs/test_vcs.py	Mon Mar 14 00:36:08 2016 +0100
@@ -2,6 +2,7 @@
 import os
 import shutil
 
+from kallithea.lib.utils2 import safe_str
 from kallithea.lib.vcs import VCSError, get_repo, get_backend
 from kallithea.lib.vcs.backends.hg import MercurialRepository
 from kallithea.lib.vcs.utils.compat import unittest
@@ -22,14 +23,14 @@
         alias = 'hg'
         path = TEST_HG_REPO
         backend = get_backend(alias)
-        repo = backend(path)
+        repo = backend(safe_str(path))
         self.assertEqual('hg',repo.alias)
 
     def test_alias_detect_git(self):
         alias = 'git'
         path = TEST_GIT_REPO
         backend = get_backend(alias)
-        repo = backend(path)
+        repo = backend(safe_str(path))
         self.assertEqual('git',repo.alias)
 
     def test_wrong_alias(self):
@@ -40,28 +41,28 @@
         alias = 'hg'
         path = TEST_HG_REPO
         backend = get_backend(alias)
-        repo = backend(path)
+        repo = backend(safe_str(path))
 
-        self.assertEqual(repo.__class__, get_repo(path, alias).__class__)
-        self.assertEqual(repo.path, get_repo(path, alias).path)
+        self.assertEqual(repo.__class__, get_repo(safe_str(path), alias).__class__)
+        self.assertEqual(repo.path, get_repo(safe_str(path), alias).path)
 
     def test_get_repo_autoalias_hg(self):
         alias = 'hg'
         path = TEST_HG_REPO
         backend = get_backend(alias)
-        repo = backend(path)
+        repo = backend(safe_str(path))
 
-        self.assertEqual(repo.__class__, get_repo(path).__class__)
-        self.assertEqual(repo.path, get_repo(path).path)
+        self.assertEqual(repo.__class__, get_repo(safe_str(path)).__class__)
+        self.assertEqual(repo.path, get_repo(safe_str(path)).path)
 
     def test_get_repo_autoalias_git(self):
         alias = 'git'
         path = TEST_GIT_REPO
         backend = get_backend(alias)
-        repo = backend(path)
+        repo = backend(safe_str(path))
 
-        self.assertEqual(repo.__class__, get_repo(path).__class__)
-        self.assertEqual(repo.path, get_repo(path).path)
+        self.assertEqual(repo.__class__, get_repo(safe_str(path)).__class__)
+        self.assertEqual(repo.path, get_repo(safe_str(path)).path)
 
 
     def test_get_repo_err(self):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pytest.ini	Mon Mar 14 00:36:08 2016 +0100
@@ -0,0 +1,9 @@
+[pytest]
+# only look for tests in kallithea/tests
+python_files = kallithea/tests/**/test_*.py
+addopts =
+    # --verbose
+    # show extra test summary info as specified by chars (f)ailed, (E)error, (s)skipped, (x)failed, (X)passed, (w)warnings.
+    -rfEsxXw
+    # Shorter scrollbacks; less stuff to scroll through
+    --tb=short
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/docs-headings.py	Mon Mar 14 00:36:08 2016 +0100
@@ -0,0 +1,79 @@
+#!/usr/bin/env python2
+
+"""
+Consistent formatting of rst section titles
+"""
+
+import re
+import subprocess
+
+spaces = [
+    (0, 1), # we assume this is a over-and-underlined header
+    (2, 1),
+    (1, 1),
+    (1, 0),
+    (1, 0),
+    ]
+
+# http://sphinx-doc.org/rest.html :
+#   for the Python documentation, this convention is used which you may follow:
+#   # with overline, for parts
+#   * with overline, for chapters
+#   =, for sections
+#   -, for subsections
+#   ^, for subsubsections
+#   ", for paragraphs
+pystyles = ['#', '*', '=', '-', '^', '"']
+
+# match on a header line underlined with one of the valid characters
+headermatch = re.compile(r'''\n*(.+)\n([][!"#$%&'()*+,./:;<=>?@\\^_`{|}~-])\2{2,}\n+''', flags=re.MULTILINE)
+
+
+def main():
+    for fn in subprocess.check_output(['hg', 'loc', 'set:**.rst+kallithea/i18n/how_to']).splitlines():
+        print 'processing %s:' % fn
+        s = file(fn).read()
+
+        # find levels and their styles
+        lastpos = 0
+        styles = []
+        for markup in headermatch.findall(s):
+            style = markup[1]
+            if style in styles:
+                stylepos = styles.index(style)
+                if stylepos > lastpos + 1:
+                    print 'bad style %r with level %s - was at %s' % (style, stylepos, lastpos)
+            else:
+                stylepos = len(styles)
+                if stylepos > lastpos + 1:
+                    print 'bad new style %r - expected %r' % (style, styles[lastpos + 1])
+                else:
+                    styles.append(style)
+            lastpos = stylepos
+
+        # remove superfluous spacing (may however be restored by header spacing)
+        s = re.sub(r'''(\n\n)\n*''', r'\1', s, flags=re.MULTILINE)
+
+        if styles:
+            newstyles = pystyles[pystyles.index(styles[0]):]
+
+            def subf(m):
+                title, style = m.groups()
+                level = styles.index(style)
+                before, after = spaces[level]
+                newstyle = newstyles[level]
+                return '\n' * (before + 1) + title + '\n' + newstyle * len(title) + '\n' * (after + 1)
+            s = headermatch.sub(subf, s)
+
+        # remove superfluous spacing when headers are adjacent
+        s = re.sub(r'''(\n.+\n([][!"#$%&'()*+,./:;<=>?@\\^_`{|}~-])\2{2,}\n\n\n)\n*''', r'\1', s, flags=re.MULTILINE)
+        # fix trailing space and spacing before link sections
+        s = s.strip() + '\n'
+        s = re.sub(r'''\n+((?:\.\. _[^\n]*\n)+)$''', r'\n\n\n\1', s)
+
+        file(fn, 'w').write(s)
+        print subprocess.check_output(['hg', 'diff', fn])
+        print
+
+if __name__ == '__main__':
+    main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/generate-ini.py	Mon Mar 14 00:36:08 2016 +0100
@@ -0,0 +1,173 @@
+#!/usr/bin/env python2
+"""
+Based on kallithea/bin/template.ini.mako, generate
+  kallithea/config/deployment.ini_tmpl
+  development.ini
+  kallithea/tests/test.ini
+"""
+
+import re
+
+makofile = 'kallithea/bin/template.ini.mako'
+
+# the mako conditionals used in all other ini files and templates
+selected_mako_conditionals = set([
+    "database_engine == 'sqlite'",
+    "http_server == 'waitress'",
+    "error_aggregation_service == 'errormator'",
+    "error_aggregation_service == 'sentry'",
+])
+
+# the mako variables used in all other ini files and templates
+mako_variable_values = {
+    'host': '127.0.0.1',
+    'port': '5000',
+    'here': '%(here)s',
+    'uuid()': '${app_instance_uuid}',
+}
+
+# files to be generated from the mako template
+ini_files = [
+    ('kallithea/config/deployment.ini_tmpl',
+        '''
+        Kallithea - Example config
+
+        The %(here)s variable will be replaced with the parent directory of this file
+        ''',
+        {}, # exactly the same settings as template.ini.mako
+    ),
+    ('kallithea/tests/test.ini',
+        '''
+        Kallithea - config for tests:
+        initial_repo_scan = true
+        vcs_full_cache = false
+        sqlalchemy and kallithea_test.sqlite
+        custom logging
+
+        The %(here)s variable will be replaced with the parent directory of this file
+        ''',
+        {
+            '[server:main]': {
+                'port': '4999',
+            },
+            '[app:main]': {
+                'initial_repo_scan': 'true',
+                'app_instance_uuid': 'test',
+                'vcs_full_cache': 'false',
+                'show_revision_number': 'true',
+                'beaker.cache.sql_cache_short.expire': '1',
+                'beaker.session.secret': '{74e0cd75-b339-478b-b129-07dd221def1f}',
+                'sqlalchemy.db1.url': 'sqlite:///%(here)s/kallithea_test.sqlite',
+            },
+            '[logger_root]': {
+                'level': 'DEBUG',
+            },
+            '[logger_sqlalchemy]': {
+                'level': 'ERROR',
+                'handlers': 'console',
+            },
+            '[handler_console]': {
+                'level': 'NOTSET',
+            },
+        },
+    ),
+    ('development.ini',
+        '''
+        Kallithea - Development config:
+        listening on *:5000
+        sqlite and kallithea.db
+        initial_repo_scan = true
+        set debug = true
+        verbose and colorful logging
+
+        The %(here)s variable will be replaced with the parent directory of this file
+        ''',
+        {
+            '[server:main]': {
+                'host': '0.0.0.0',
+            },
+            '[app:main]': {
+                'initial_repo_scan': 'true',
+                'set debug': 'true',
+                'app_instance_uuid': 'development-not-secret',
+                'beaker.session.secret': 'development-not-secret',
+            },
+            '[handler_console]': {
+                'level': 'DEBUG',
+                'formatter': 'color_formatter',
+            },
+            '[handler_console_sql]': {
+                'level': 'DEBUG',
+                'formatter': 'color_formatter_sql',
+            },
+        },
+    ),
+]
+
+
+def main():
+    # make sure all mako lines starting with '#' (the '##' comments) are marked up as <text>
+    print 'reading:', makofile
+    mako_org = file(makofile).read()
+    mako_no_text_markup = re.sub(r'</?%text>', '', mako_org)
+    mako_marked_up = re.sub(r'\n(##.*)', r'\n<%text>\1</%text>', mako_no_text_markup, flags=re.MULTILINE)
+    if mako_marked_up != mako_org:
+        print 'writing:', makofile
+        file(makofile, 'w').write(mako_marked_up)
+
+    # select the right mako conditionals for the other less sophisticated formats
+    def sub_conditionals(m):
+        """given a %if...%endif match, replace with just the selected
+        conditional sections enabled and the rest as comments
+        """
+        conditional_lines = m.group(1)
+        def sub_conditional(m):
+            """given a conditional and the corresponding lines, return them raw
+            or commented out, based on whether conditional is selected
+            """
+            criteria, lines = m.groups()
+            if criteria not in selected_mako_conditionals:
+                lines = '\n'.join((l if not l or l.startswith('#') else '#' + l) for l in lines.split('\n'))
+            return lines
+        conditional_lines = re.sub(r'^%(?:el)?if (.*):\n((?:^[^%\n].*\n|\n)*)',
+            sub_conditional, conditional_lines, flags=re.MULTILINE)
+        return conditional_lines
+    mako_no_conditionals = re.sub(r'^(%if .*\n(?:[^%\n].*\n|%elif .*\n|\n)*)%endif\n',
+        sub_conditionals, mako_no_text_markup, flags=re.MULTILINE)
+
+    # expand mako variables
+    def pyrepl(m):
+        return mako_variable_values.get(m.group(1), m.group(0))
+    mako_no_variables = re.sub(r'\${([^}]*)}', pyrepl, mako_no_conditionals)
+
+    # remove utf-8 coding header
+    base_ini = re.sub(r'^## -\*- coding: utf-8 -\*-\n', '', mako_no_variables)
+
+    # create ini files
+    for fn, desc, settings in ini_files:
+        print 'updating:', fn
+        ini_lines = re.sub(
+            '# Kallithea - config file generated with kallithea-config *#\n',
+            ''.join('# %-77s#\n' % l.strip() for l in desc.strip().split('\n')),
+            base_ini)
+        def process_section(m):
+            """process a ini section, replacing values as necessary"""
+            sectionname, lines = m.groups()
+            if sectionname in settings:
+                section_settings = settings[sectionname]
+                def process_line(m):
+                    """process a section line and update value if necessary"""
+                    setting, value = m.groups()
+                    line = m.group(0)
+                    if setting in section_settings:
+                        line = '%s = %s' % (setting, section_settings[setting])
+                        if '$' not in value:
+                            line = '#%s = %s\n%s' % (setting, value, line)
+                    return line.rstrip()
+                lines = re.sub(r'^([^#\n].*) = ?(.*)', process_line, lines, flags=re.MULTILINE)
+            return sectionname + '\n' + lines
+        ini_lines = re.sub(r'^(\[.*\])\n((?:(?:[^[\n].*)?\n)*)', process_section, ini_lines, flags=re.MULTILINE)
+        file(fn, 'w').write(ini_lines)
+
+if __name__ == '__main__':
+    main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/logformat.py	Mon Mar 14 00:36:08 2016 +0100
@@ -0,0 +1,39 @@
+#!/usr/bin/env python2
+
+import re
+import sys
+
+if len(sys.argv) < 2:
+    print 'Cleanup of superfluous % formatting of log statements.'
+    print 'Usage:'
+    print '''  hg revert `hg loc '*.py'|grep -v logformat.py` && scripts/logformat.py `hg loc '*.py'` && hg diff'''
+    raise SystemExit(1)
+
+
+logre = r'''
+(log\.(?:error|info|warning|debug)
+[(][ \n]*
+)
+%s
+(
+[ \n]*[)]
+)
+'''
+res = [
+    # handle % () - keeping spaces around the old %
+    (re.compile(logre % r'''("[^"]*"|'[^']*')   ([\n ]*) %  ([\n ]*) \( ( (?:[^()]|\n)* (?: \( (?:[^()]|\n)* \) (?:[^()]|\n)* )* ) \) ''', flags=re.MULTILINE|re.VERBOSE), r'\1\2,\3\4\5\6'),
+    # handle % without () - keeping spaces around the old %
+    (re.compile(logre % r'''("[^"]*"|'[^']*')   ([\n ]*) %  ([\n ]*)    ( (?:[^()]|\n)* (?: \( (?:[^()]|\n)* \) (?:[^()]|\n)* )* )    ''', flags=re.MULTILINE|re.VERBOSE), r'\1\2,\3\4\5\6'),
+    # remove extra space if it is on next line
+    (re.compile(logre % r'''("[^"]*"|'[^']*') , (\n [ ]) ([ ][\n ]*)    ( (?:[^()]|\n)* (?: \( (?:[^()]|\n)* \) (?:[^()]|\n)* )* )    ''', flags=re.MULTILINE|re.VERBOSE), r'\1\2,\3\4\5\6'),
+    # remove extra space if it is on same line
+    (re.compile(logre % r'''("[^"]*"|'[^']*') , [ ]+  () (   [\n ]+)    ( (?:[^()]|\n)* (?: \( (?:[^()]|\n)* \) (?:[^()]|\n)* )* )    ''', flags=re.MULTILINE|re.VERBOSE), r'\1\2,\3\4\5\6'),
+    # remove trailing , and space
+    (re.compile(logre % r'''("[^"]*"|'[^']*') ,       () (   [\n ]*)    ( (?:[^()]|\n)* (?: \( (?:[^()]|\n)* \) (?:[^()]|\n)* )* [^(), \n] ) [ ,]*''', flags=re.MULTILINE|re.VERBOSE), r'\1\2,\3\4\5\6'),
+    ]
+
+for f in sys.argv[1:]:
+    s = file(f).read()
+    for r, t in res:
+        s = r.sub(t, s)
+    file(f, 'w').write(s)
--- a/scripts/whitespacecleanup.sh	Sun Mar 13 15:18:08 2016 +0100
+++ b/scripts/whitespacecleanup.sh	Mon Mar 14 00:36:08 2016 +0100
@@ -6,6 +6,7 @@
 
 sed -i -e "s,`printf '\t'`,    ,g" $files
 sed -i -e "s,  *$,,g" $files
+sed -i -e 's,\([^ ]\)\\$,\1 \\,g' -e 's,\(["'"'"']["'"'"']["'"'"']\) \\$,\1\\,g' $files
 # ensure one trailing newline - remove empty last line and make last line include trailing newline:
 sed -i -e '$,${/^$/d}' -e '$a\' $files
 
--- a/setup.cfg	Sun Mar 13 15:18:08 2016 +0100
+++ b/setup.cfg	Mon Mar 14 00:36:08 2016 +0100
@@ -3,22 +3,8 @@
 tag_svn_revision = 0
 tag_date = 0
 
-[nosetests]
-verbose = True
-verbosity = 2
-with-pylons = kallithea/tests/test.ini
-detailed-errors = 1
-nologcapture = 1
-
-[pytest]
-# only look for tests in kallithea/tests
-python_files = kallithea/tests/**/test_*.py
-addopts =
-    # --verbose
-    # show extra test summary info as specified by chars (f)ailed, (E)error, (s)skipped, (x)failed, (X)passed, (w)warnings.
-    -rfEsxXw
-    # Shorter scrollbacks; less stuff to scroll through
-    --tb=short
+[aliases]
+test = pytest
 
 [compile_catalog]
 domain = kallithea
--- a/setup.py	Sun Mar 13 15:18:08 2016 +0100
+++ b/setup.py	Mon Mar 14 00:36:08 2016 +0100
@@ -54,6 +54,7 @@
     "mock",
     "URLObject==2.3.4",
     "Routes==1.13",
+    "pytest>=2.7.0,<3.0",
     "dulwich>=0.9.9,<=0.9.9",
     "mercurial>=2.9,<3.8",
 ]
@@ -147,11 +148,12 @@
     url=__url__,
     install_requires=requirements,
     classifiers=classifiers,
-    setup_requires=["PasteScript>=1.6.3"],
+    setup_requires=['PasteScript>=1.6.3',
+                    'pytest-runner'],
+    tests_require=['pytest'],
     data_files=data_files,
     packages=packages,
     include_package_data=True,
-    test_suite='nose.collector',
     package_data=package_data,
     message_extractors={'kallithea': [
             ('**.py', 'python', None),
@@ -184,8 +186,5 @@
     upgrade-db=kallithea.lib.dbmigrate:UpgradeDb
     celeryd=kallithea.lib.celerypylons.commands:CeleryDaemonCommand
     install-iis=kallithea.lib.paster_commands.install_iis:Command
-
-    [nose.plugins]
-    pylons = pylons.test:PylonsPlugin
     """,
 )
--- a/tox.ini	Sun Mar 13 15:18:08 2016 +0100
+++ b/tox.ini	Mon Mar 14 00:36:08 2016 +0100
@@ -1,13 +1,11 @@
 [tox]
 minversion = 1.8
-envlist = py{26,27}-{pytest,nose}
+envlist = py{26,27}-pytest
 
 [testenv]
 setenv =
     PYTHONHASHSEED = 0
 deps =
-    nose: nose
     pytest: pytest
 commands =
-    nose: nosetests {posargs}
     pytest: py.test {posargs}