changeset 8446:881d2bd97973 i18n

Merge stable
author Mads Kiilerich <mads@kiilerich.com>
date Thu, 18 Jun 2020 16:33:55 +0200
parents 5ed9471793d1 (current diff) 0bca9e828db2 (diff)
children 949e3280200b
files docs/setup.rst kallithea/i18n/kallithea.pot
diffstat 33 files changed, 630 insertions(+), 556 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Wed May 06 14:47:06 2020 +0200
+++ b/.hgtags	Thu Jun 18 16:33:55 2020 +0200
@@ -76,3 +76,5 @@
 bfa0b0a814644f0af3f492d17a9ed169cc3b89fe 0.5.0
 d01a8e92936dbd62c76505432f60efba432e9397 0.5.1
 aa0a637fa6f635a5e024fa56b19ed2a2dacca857 0.5.2
+9f5ca9088067618d79129d224c35c818bd2d2f12 0.6.0
+a22edac2be58eaf68d1940d4dfeb88fadbabb43a 0.6.1
--- a/CONTRIBUTORS	Wed May 06 14:47:06 2020 +0200
+++ b/CONTRIBUTORS	Thu Jun 18 16:33:55 2020 +0200
@@ -2,7 +2,13 @@
 
     Thomas De Schampheleire <thomas.de_schampheleire@nokia.com> 2014-2020
     Mads Kiilerich <mads@kiilerich.com> 2016-2020
+    Asterios Dimitriou <steve@pci.gr> 2016-2017 2020
+    Private <adamantine.sword@gmail.com> 2019-2020
     Dennis Fink <dennis.fink@c3l.lu> 2020
+    Étienne Gilli <etienne@gilli.io> 2020
+    J. Lavoie <j.lavoie@net-c.ca> 2020
+    robertus <robertuss12@gmail.com> 2020
+    Ross Thomas <ross@lns-nevasoft.com> 2020
     Andrej Shadura <andrew@shadura.me> 2012 2014-2017 2019
     Étienne Gilli <etienne.gilli@gmail.com> 2015-2017 2019
     Allan Nordhøy <epost@anotheragency.no> 2017-2019
@@ -17,7 +23,6 @@
     Mateusz Mendel <mendelm9@gmail.com> 2019
     Nathan <bonnemainsnathan@gmail.com> 2019
     Oleksandr Shtalinberg <o.shtalinberg@gmail.com> 2019
-    Private <adamantine.sword@gmail.com> 2019
     THANOS SIOURDAKIS <siourdakisthanos@gmail.com> 2019
     Wolfgang Scherer <wolfgang.scherer@gmx.de> 2019
     Христо Станев <hstanev@gmail.com> 2019
@@ -34,7 +39,6 @@
     Unity Technologies 2012-2017
     Søren Løvborg <sorenl@unity3d.com> 2015-2017
     Sam Jaques <sam.jaques@me.com> 2015 2017
-    Asterios Dimitriou <steve@pci.gr> 2016-2017
     Alessandro Molina <alessandro.molina@axant.it> 2017
     Anton Schur <tonich.sh@gmail.com> 2017
     Ching-Chen Mao <mao@lins.fju.edu.tw> 2017
--- a/MANIFEST.in	Wed May 06 14:47:06 2020 +0200
+++ b/MANIFEST.in	Thu Jun 18 16:33:55 2020 +0200
@@ -1,4 +1,5 @@
 include           .coveragerc
+include           .eslintrc.js
 include           Apache-License-2.0.txt
 include           CONTRIBUTORS
 include           COPYING
--- a/development.ini	Wed May 06 14:47:06 2020 +0200
+++ b/development.ini	Thu Jun 18 16:33:55 2020 +0200
@@ -1,8 +1,9 @@
 ###################################################################################
 ###################################################################################
-## Kallithea config file generated with kallithea-config                         ##
+## Kallithea config file generated with kallithea-cli                            ##
 ##                                                                               ##
-## The %(here)s variable will be replaced with the parent directory of this file ##
+## The %(here)s variable will generally be replaced with the parent directory of ##
+## this file. Other use of % must be escaped as %% .                             ##
 ###################################################################################
 ###################################################################################
 
@@ -228,7 +229,7 @@
 #    CHANGELOG
 
 ####################################
-###           SSH CONFIG        ####
+##            SSH CONFIG          ##
 ####################################
 
 ## SSH is disabled by default, until an Administrator decides to enable it.
@@ -247,7 +248,7 @@
 #ssh_locale = C.UTF-8
 
 ####################################
-###        CELERY CONFIG        ####
+##         CELERY CONFIG          ##
 ####################################
 
 ## Note: Celery doesn't support Windows.
@@ -269,7 +270,7 @@
 celery.task_always_eager = false
 
 ####################################
-###         BEAKER CACHE        ####
+##          BEAKER CACHE          ##
 ####################################
 
 beaker.cache.data_dir = %(here)s/data/cache/data
@@ -286,7 +287,7 @@
 beaker.cache.long_term_file.key_length = 256
 
 ####################################
-###       BEAKER SESSION        ####
+##        BEAKER SESSION          ##
 ####################################
 
 ## Name of session cookie. Should be unique for a given host and path, even when running
@@ -319,7 +320,7 @@
 #session.table_name = db_session
 
 ####################################
-###       ERROR HANDLING        ####
+##        ERROR HANDLING          ##
 ####################################
 
 ## Show a nice error page for application HTTP errors and exceptions (default true)
@@ -347,16 +348,16 @@
 
 
 ##################################
-###       LOGVIEW CONFIG       ###
+##        LOGVIEW CONFIG        ##
 ##################################
 
 logview.sqlalchemy = #faa
 logview.pylons.templating = #bfb
 logview.pylons.util = #eee
 
-#########################################################
-### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG    ###
-#########################################################
+#########################
+##      DB CONFIG      ##
+#########################
 
 ## SQLITE [default]
 sqlalchemy.url = sqlite:///%(here)s/kallithea.db?timeout=60
@@ -366,14 +367,14 @@
 sqlalchemy.pool_recycle = 3600
 
 ################################
-### ALEMBIC CONFIGURATION   ####
+##   ALEMBIC CONFIGURATION    ##
 ################################
 
 [alembic]
 script_location = kallithea:alembic
 
 ################################
-### LOGGING CONFIGURATION   ####
+##   LOGGING CONFIGURATION    ##
 ################################
 
 [loggers]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/dev/i18n.rst	Thu Jun 18 16:33:55 2020 +0200
@@ -0,0 +1,257 @@
+.. _translations:
+
+============
+Translations
+============
+
+Translations are available on Hosted Weblate at the following URL:
+
+    https://hosted.weblate.org/projects/kallithea/kallithea/
+
+Registered users may contribute to the existing languages, or request a new
+language translation.
+
+
+Translating using Weblate
+-------------------------
+
+Weblate_ offers a simple and easy to use interface featuring glossary, machine
+translation, suggestions based on similar translations in other projects,
+automatic checks etc. Weblate imports the source code tree directly from
+the version control system, and commits edits back from time to time.
+
+When registering at Weblate, make sure you use the name and email address you
+prefer to be used when your changes are committed. We can and probably will
+amend changesets coming from Weblate, but having things right from the beginning
+makes things easier.
+
+Weblate performs sanity checks all the time and tries to prevent you from ignoring
+them. Most common mistakes are inconsistent punctuation, whitespace, missing or extra
+format parameters, untranslated strings copied into the translation. Please perform
+necessary corrections when they're needed, or override the false positives.
+
+
+Merging translations from Weblate (admin-only)
+----------------------------------------------
+
+Weblate rebases its changes every time it pulls from our repository. Pulls are triggered
+by a web hook from Our Own Kallithea every time it receives new commits. Usually merging
+the new translations is a straightforward process consisting of a pull from the Weblate-hosted
+repository which is available under the Data Exports tab in the Weblate interface.
+
+Weblate tries to minimise the number of commits, but that doesn't always work, especially
+when two translators work with different languages at more or less the same time.
+It makes sense sometimes to re-order or fold commits by the same author when they touch
+just the same language translation. That, however, may confuse Weblate sometimes, in
+which case it should be manually convinced it has to discard the commits it created by
+using its administrative interface.
+
+
+Regenerating translations after source code changes (admin-only)
+----------------------------------------------------------------
+
+When the Kallithea source code changes, both the location as the content of
+translation strings can change. It is therefore necessary to regularly
+regenerate the `kallithea.pot` file containing these strings, as well as aligning
+the translation files (`*.po`).
+
+First update the translation strings::
+
+    python3 setup.py extract_messages
+
+Then regenerate the translation files. This could either be done with `python3
+setup.py update_catalog` or with `msgmerge` from the `gettext` package. As
+Weblate is also touching these translation files, it is preferred to use the
+same tools (`msgmerge`) and settings as Weblate to minimize the diff::
+
+    find kallithea/i18n -name kallithea.po | xargs -I '{}' \
+        msgmerge --width=76 --backup=none --previous --update '{}' \
+        kallithea/i18n/kallithea.pot
+
+
+Manual creation of a new language translation
+---------------------------------------------
+
+In the prepared development environment, run the following to ensure
+all translation strings are extracted and up-to-date::
+
+    python3 setup.py extract_messages
+
+Create new language by executing following command::
+
+    python3 setup.py init_catalog -l <new_language_code>
+
+This creates a new translation under directory `kallithea/i18n/<new_language_code>`
+based on the translation template file, `kallithea/i18n/kallithea.pot`.
+
+Edit the new PO file located in `LC_MESSAGES` directory with poedit or your
+favorite PO files editor. After you finished with the translations, check the
+translation file for errors by executing::
+
+    msgfmt -f -c kallithea/i18n/<new_language_code>/LC_MESSAGES/<updated_file.po>
+
+Finally, compile the translations::
+
+    python3 setup.py compile_catalog -l <new_language_code>
+
+
+Manually updating translations
+------------------------------
+
+Extract the latest versions of strings for translation by running::
+
+    python3 setup.py extract_messages
+
+Update the PO file by doing::
+
+    python3 setup.py update_catalog -l <new_language_code>
+
+Edit the newly updated translation file. Repeat all steps after the
+`init_catalog` step from the 'new translation' instructions above.
+
+
+Testing translations
+--------------------
+
+Edit `kallithea/tests/conftest.py` and set `i18n.lang` to `<new_language_code>`
+and run Kallithea tests by executing::
+
+    py.test
+
+
+Managing translations with scripts/i18n tooling
+-----------------------------------------------
+
+The general idea with the ``scripts/i18n`` tooling is to keep changes in the
+main repository focussed on actual and reviewable changes with minimal noise.
+Noisy generated or redundant localization changes (that are useful when
+translations) are contained in the ``kallithea-i18n`` repo on the ``i18n``
+branch. The translation files in the main repository have no line numbers, no
+untranslated entries, no fuzzy entries, no unused entries, and no constantly
+changing records of "latest" this and that (name, date, version, etc).
+
+The branches in the main repo (``default`` and ``stable``) will thus only have
+stripped ``.pot`` and ``.po`` files: an (almost) empty
+``kallithea/i18n/kallithea.pot`` file, and minimal ``.po`` files. There are no
+binary ``.mo`` files in any repo - these are only generated when packaging for
+release (or locally if installing from source).
+
+Generally, ``kallithea/i18n/`` should not be changed on the ``default`` and
+``stable`` branches at all. The ``i18n`` branch should *only* change
+``kallithea/i18n/`` . If there are changesets with exceptions from that, these
+changesets should probably be grafted/redone in the "right" place.
+
+The basic flow is thus:
+
+0. All weblate translation is done on the ``i18n`` branch which generally is
+   based on the ``stable`` branch.
+1. Graft the essential part of all new changes on the ``i18n`` branch to
+   ``stable`` (while normalizing to current stripped state of stable).
+2. Merge from ``stable`` to ``i18n`` (while normalizing to the resulting
+   unstripped and fully ``msgmerge``'d state and ``.pot``-updating state).
+3. Verify that the content of the ``i18n`` branch will give exactly the content
+   of the ``stable`` branch after stripping. If there is a diff, something has
+   to be fixed in one way or the other ... and the whole process should
+   probably be redone.
+
+Translate
+^^^^^^^^^
+
+First land full translation changes in the ``kallithea-i18n`` repo on the
+``i18n`` branch. That can be done in pretty much any way you want. If changes
+for some reason have to be grafted or merged, there might be odd conflicts due
+to all the noise. Conflicts on the full ``i18n`` branch can perhaps be resolved
+more easily using non-stripping normalization before merging::
+
+  python3 setup.py extract_messages && cp kallithea/i18n/kallithea.pot full.pot && hg revert kallithea/i18n/kallithea.pot -r .
+  hg resolve kallithea/i18n/ --tool X --config merge-tools.X.executable=python3 --config merge-tools.X.args='scripts/i18n normalized-merge --merge-pot-file full.pot $local $base $other $output'
+
+Land in main repository - stripped
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When the full i18n changes have landed on the ``i18n`` branch, prepare to land
+them on ``stable``::
+
+  hg up -cr stable
+  python3 setup.py extract_messages && cp kallithea/i18n/kallithea.pot full.pot && hg revert kallithea/i18n/kallithea.pot
+
+Consider all new ``i18n`` changes since last merge from ``stable``::
+
+  hg log -G --style compact -r 'only("i18n", children(::stable))'
+
+Graft them one by one (or in collapsed chunks) while normalizing.
+
+If the graft has conflicts, use the ``scripts/i18n`` normalization tool to
+apply ``msgmerge`` and strip before doing 3-way merge and resolving conflicts::
+
+  hg resolve kallithea/i18n/ --tool X --config merge-tools.X.executable=python3 --config merge-tools.X.args='scripts/i18n normalized-merge --merge-pot-file full.pot --strip $local $base $other $output'
+
+When all conflicts have been resolved, continue the graft::
+
+  hg graft --continue
+
+Then make sure any non-conflicting files are normalized and stripped too::
+
+  scripts/i18n normalize-po-files --strip --merge-pot-file full.pot kallithea/i18n/*/LC_MESSAGES/kallithea.po
+  hg ci --amend --config ui.editor=true
+
+When things have been grafted to the ``stable`` branch, clean up history if
+necessary: clean up the author and commit message when necessary, and perhaps
+merge multiple changesets from same contributor.
+
+Merge back to ``i18n``
+^^^^^^^^^^^^^^^^^^^^^^
+
+For any i18n changes that for some reason have been done on the ``stable``
+branch, apply them manually on the ``i18n`` branch too - perhaps by grafting
+and editing manually. The merge done in this step will `not` take care of it.
+If the verification step done a bit later points out that something has been
+missed, strip and go back to this point.
+
+Then merge back to the ``i18n`` branch using normalization while keeping the
+full ``.po`` files, and updating the full ``.pot`` and ``.po`` to current
+state::
+
+  hg up -cr i18n
+  hg merge stable --tool internal:fail
+  hg revert kallithea/i18n/*/LC_MESSAGES/*.po -r .
+  hg resolve -m kallithea/i18n/*/LC_MESSAGES/*.po
+  hg resolve -l  # verify all conflicts have been resolved
+  python3 setup.py extract_messages && cp kallithea/i18n/kallithea.pot full.pot
+  scripts/i18n normalize-po-files --merge-pot-file full.pot kallithea/i18n/*/LC_MESSAGES/kallithea.po
+  hg commit  # "Merge from stable"
+
+Note: ``normalize-po-files`` can also pretty much be done manually with::
+
+  for po in kallithea/i18n/*/LC_MESSAGES/kallithea.po; do msgmerge --width=76 --backup=none --previous --update $po full.pot ; done
+
+Note: Additional merges from ``stable`` to ``i18n`` can be done any time.
+
+Verify
+^^^^^^
+
+Verify things are in sync between the full ``i18n`` branch and the stripped
+``stable`` branch::
+
+  hg up -cr stable
+  hg revert -a -r i18n
+  python3 setup.py extract_messages && cp kallithea/i18n/kallithea.pot full.pot && hg revert kallithea/i18n/kallithea.pot
+  scripts/i18n normalize-po-files --strip --merge-pot-file full.pot kallithea/i18n/*/LC_MESSAGES/kallithea.po
+  hg diff
+
+If there is a diff, figure out where it came from, go back and fix the root
+cause, and redo the graft/merge.
+
+Push
+^^^^
+
+The changes on the ``stable`` branch should now be ready for pushing - verify
+the actual changes with a thorough review of::
+
+  hg out -pvr stable
+
+When ``stable`` changes have been pushed, also push the ``i18n`` branch to the
+``kallithea-i18n`` repo so Weblate can see it.
+
+
+.. _Weblate: http://weblate.org/
--- a/docs/dev/translation.rst	Wed May 06 14:47:06 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-.. _translations:
-.. include:: ./../../kallithea/i18n/how_to
--- a/docs/index.rst	Wed May 06 14:47:06 2020 +0200
+++ b/docs/index.rst	Thu Jun 18 16:33:55 2020 +0200
@@ -74,7 +74,7 @@
    :maxdepth: 1
 
    contributing
-   dev/translation
+   dev/i18n
    dev/dbmigrations
 
 
--- a/docs/overview.rst	Wed May 06 14:47:06 2020 +0200
+++ b/docs/overview.rst	Thu Jun 18 16:33:55 2020 +0200
@@ -7,6 +7,42 @@
 Some overview and some details that can help understanding the options when
 installing Kallithea.
 
+1. **Prepare environment and external dependencies.**
+    Kallithea needs:
+
+    * A filesystem where the Mercurial and Git repositories can be stored.
+    * A database where meta data can be stored.
+    * A Python environment where the Kallithea application and its dependencies
+      can be installed.
+    * A web server that can host the Kallithea web application using the WSGI
+      API.
+
+2. **Install Kallithea software.**
+    This makes the ``kallithea-cli`` command line tool available.
+
+3. **Create low level configuration file.**
+    Use ``kallithea-cli config-create`` to create a ``.ini`` file with database
+    connection info, mail server information, configuration for the specified
+    web server, etc.
+
+4. **Populate the database.**
+    Use ``kallithea-cli db-create`` with the ``.ini`` file to create the
+    database schema and insert the most basic information: the location of the
+    repository store and an initial local admin user.
+
+5. **Configure the web server.**
+    The web server must invoke the WSGI entrypoint for the Kallithea software
+    using the ``.ini`` file (and thus the database). This makes the web
+    application available so the local admin user can log in and tweak the
+    configuration further.
+
+6. **Configure users.**
+    The initial admin user can create additional local users, or configure how
+    users can be created and authenticated from other user directories.
+
+See the subsequent sections, the separate OS-specific instructions, and
+:ref:`setup` for details on these steps.
+
 
 Python environment
 ------------------
@@ -105,9 +141,11 @@
   but build the Kallithea package itself locally instead of downloading it.)
 
 .. note::
-   Kallithea includes front-end code that needs to be processed first.
-   The tool npm_ is used to download external dependencies and orchestrate the
-   processing. The ``npm`` binary must thus be available.
+   Kallithea includes front-end code that needs to be processed to prepare
+   static files that can be served at run time and used on the client side. The
+   tool npm_ is used to download external dependencies and orchestrate the
+   processing. The ``npm`` binary must thus be available at install time but is
+   not used at run time.
 
 
 Web server
@@ -130,19 +168,24 @@
   Actual use in production might have different requirements and need extra
   work to make it manageable as a scalable system service.
 
-  Gearbox comes with its own built-in web server but Kallithea defaults to use
-  Waitress_. Gunicorn_ is also an option. These web servers have different
-  limited feature sets.
+  Gearbox comes with its own built-in web server for development but Kallithea
+  defaults to using Waitress_. Gunicorn_ and Gevent_ are also options. These
+  web servers have different limited feature sets.
 
-  The web server used by ``gearbox`` is configured in the ``.ini`` file passed
-  to it. The entry point for the WSGI application is configured
-  in ``setup.py`` as ``kallithea.config.middleware:make_app``.
+  The web server used by ``gearbox serve`` is configured in the ``.ini`` file.
+  Create it with ``config-create`` using for example ``http_server=waitress``
+  to get a configuration starting point for your choice of web server.
+
+  (Gearbox will do like ``paste`` and use the WSGI application entry point
+  ``kallithea.config.middleware:make_app`` as specified in ``setup.py``.)
 
 - `Apache httpd`_ can serve WSGI applications directly using mod_wsgi_ and a
   simple Python file with the necessary configuration. This is a good option if
   Apache is an option.
 
-- uWSGI_ is also a full web server with built-in WSGI module.
+- uWSGI_ is also a full web server with built-in WSGI module. Use
+  ``config-create`` with ``http_server=uwsgi`` to get a ``.ini`` file with
+  uWSGI configuration.
 
 - IIS_ can also server WSGI applications directly using isapi-wsgi_.
 
@@ -161,9 +204,18 @@
 also often used inside organizations with a limited amount of users and thus no
 continuous hammering from the internet.
 
+.. note::
+   Kallithea, the libraries it uses, and Python itself do in several places use
+   simple caching in memory. Caches and memory are not always released in a way
+   that is suitable for long-running processes. They might appear to be leaking
+   memory. The worker processes should thus regularly be restarted - for
+   example after 1000 requests and/or one hour. This can usually be done by the
+   web server or the tool used for running it as a system service.
+
 
 .. _Python: http://www.python.org/
 .. _Gunicorn: http://gunicorn.org/
+.. _Gevent: http://www.gevent.org/
 .. _Waitress: http://waitress.readthedocs.org/en/latest/
 .. _Gearbox: http://turbogears.readthedocs.io/en/latest/turbogears/gearbox.html
 .. _PyPI: https://pypi.python.org/pypi
--- a/docs/setup.rst	Wed May 06 14:47:06 2020 +0200
+++ b/docs/setup.rst	Thu Jun 18 16:33:55 2020 +0200
@@ -24,7 +24,7 @@
 use PostgreSQL or SQLite (default). If you choose a database other than the
 default, ensure you properly adjust the database URL in your ``my.ini``
 configuration file to use this other database. Kallithea currently supports
-PostgreSQL, SQLite and MySQL databases. Create the database by running
+PostgreSQL, SQLite and MariaDB/MySQL databases. Create the database by running
 the following command::
 
     kallithea-cli db-create -c my.ini
@@ -54,7 +54,9 @@
           but when trying to do a push it will fail with permission
           denied errors unless it has write access.
 
-Finally, prepare the front-end by running::
+Finally, the front-end files must be prepared. This requires ``npm`` version 6
+or later, which needs ``node.js`` (version 12 or later). Prepare the front-end
+by running::
 
     kallithea-cli front-end-build
 
--- a/docs/theme/nature/layout.html	Wed May 06 14:47:06 2020 +0200
+++ b/docs/theme/nature/layout.html	Thu Jun 18 16:33:55 2020 +0200
@@ -17,4 +17,4 @@
      <img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /></a>
     </div>
 </div>
-{% endblock %}}
+{% endblock %}
--- a/docs/upgrade.rst	Wed May 06 14:47:06 2020 +0200
+++ b/docs/upgrade.rst	Thu Jun 18 16:33:55 2020 +0200
@@ -51,7 +51,7 @@
 If using PostgreSQL, please consult the documentation for the ``pg_dump``
 utility.
 
-If using MySQL, please consult the documentation for the ``mysqldump``
+If using MariaDB/MySQL, please consult the documentation for the ``mysqldump``
 utility.
 
 Look for ``sqlalchemy.url`` in your configuration file to determine
--- a/docs/usage/performance.rst	Wed May 06 14:47:06 2020 +0200
+++ b/docs/usage/performance.rst	Thu Jun 18 16:33:55 2020 +0200
@@ -40,7 +40,7 @@
 locking issues with SQLite, it is not recommended to use it for larger
 deployments.
 
-Switching to MySQL or PostgreSQL will result in an immediate performance
+Switching to PostgreSQL or MariaDB/MySQL will result in an immediate performance
 increase. A tool like SQLAlchemyGrate_ can be used for migrating to another
 database platform.
 
--- a/kallithea/__init__.py	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/__init__.py	Thu Jun 18 16:33:55 2020 +0200
@@ -34,7 +34,7 @@
 if sys.version_info < (3, 6):
     raise Exception('Kallithea requires python 3.6 or later')
 
-VERSION = (0, 5, 99)
+VERSION = (0, 6, 1)
 BACKENDS = {
     'hg': 'Mercurial repository',
     'git': 'Git repository',
--- a/kallithea/bin/kallithea_cli_config.py	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/bin/kallithea_cli_config.py	Thu Jun 18 16:33:55 2020 +0200
@@ -62,6 +62,7 @@
     """
 
     mako_variable_values = {
+        'version': kallithea.__version__,
         'git_hook_interpreter': sys.executable,
         'user_home_path': os.path.expanduser('~'),
         'kallithea_cli_path': cli_base.kallithea_cli_path,
--- a/kallithea/i18n/how_to	Wed May 06 14:47:06 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,255 +0,0 @@
-============
-Translations
-============
-
-Translations are available on Hosted Weblate at the following URL:
-
-    https://hosted.weblate.org/projects/kallithea/kallithea/
-
-Registered users may contribute to the existing languages, or request a new
-language translation.
-
-
-Translating using Weblate
--------------------------
-
-Weblate_ offers a simple and easy to use interface featuring glossary, machine
-translation, suggestions based on similar translations in other projects,
-automatic checks etc. Weblate imports the source code tree directly from
-the version control system, and commits edits back from time to time.
-
-When registering at Weblate, make sure you use the name and email address you
-prefer to be used when your changes are committed. We can and probably will
-amend changesets coming from Weblate, but having things right from the beginning
-makes things easier.
-
-Weblate performs sanity checks all the time and tries to prevent you from ignoring
-them. Most common mistakes are inconsistent punctuation, whitespace, missing or extra
-format parameters, untranslated strings copied into the translation. Please perform
-necessary corrections when they're needed, or override the false positives.
-
-
-Merging translations from Weblate (admin-only)
-----------------------------------------------
-
-Weblate rebases its changes every time it pulls from our repository. Pulls are triggered
-by a web hook from Our Own Kallithea every time it receives new commits. Usually merging
-the new translations is a straightforward process consisting of a pull from the Weblate-hosted
-repository which is available under the Data Exports tab in the Weblate interface.
-
-Weblate tries to minimise the number of commits, but that doesn't always work, especially
-when two translators work with different languages at more or less the same time.
-It makes sense sometimes to re-order or fold commits by the same author when they touch
-just the same language translation. That, however, may confuse Weblate sometimes, in
-which case it should be manually convinced it has to discard the commits it created by
-using its administrative interface.
-
-
-Regenerating translations after source code changes (admin-only)
-----------------------------------------------------------------
-
-When the Kallithea source code changes, both the location as the content of
-translation strings can change. It is therefore necessary to regularly
-regenerate the `kallithea.pot` file containing these strings, as well as aligning
-the translation files (`*.po`).
-
-First update the translation strings::
-
-    python3 setup.py extract_messages
-
-Then regenerate the translation files. This could either be done with `python3
-setup.py update_catalog` or with `msgmerge` from the `gettext` package. As
-Weblate is also touching these translation files, it is preferred to use the
-same tools (`msgmerge`) and settings as Weblate to minimize the diff::
-
-    find kallithea/i18n -name kallithea.po | xargs -I '{}' \
-        msgmerge --width=76 --backup=none --previous --update '{}' \
-        kallithea/i18n/kallithea.pot
-
-
-Manual creation of a new language translation
----------------------------------------------
-
-In the prepared development environment, run the following to ensure
-all translation strings are extracted and up-to-date::
-
-    python3 setup.py extract_messages
-
-Create new language by executing following command::
-
-    python3 setup.py init_catalog -l <new_language_code>
-
-This creates a new translation under directory `kallithea/i18n/<new_language_code>`
-based on the translation template file, `kallithea/i18n/kallithea.pot`.
-
-Edit the new PO file located in `LC_MESSAGES` directory with poedit or your
-favorite PO files editor. After you finished with the translations, check the
-translation file for errors by executing::
-
-    msgfmt -f -c kallithea/i18n/<new_language_code>/LC_MESSAGES/<updated_file.po>
-
-Finally, compile the translations::
-
-    python3 setup.py compile_catalog -l <new_language_code>
-
-
-Manually updating translations
-------------------------------
-
-Extract the latest versions of strings for translation by running::
-
-    python3 setup.py extract_messages
-
-Update the PO file by doing::
-
-    python3 setup.py update_catalog -l <new_language_code>
-
-Edit the newly updated translation file. Repeat all steps after the
-`init_catalog` step from the 'new translation' instructions above.
-
-
-Testing translations
---------------------
-
-Edit `kallithea/tests/conftest.py` and set `i18n.lang` to `<new_language_code>`
-and run Kallithea tests by executing::
-
-    py.test
-
-
-Managing translations with scripts/i18n tooling
------------------------------------------------
-
-The general idea with the ``scripts/i18n`` tooling is to keep changes in the
-main repository focussed on actual and reviewable changes with minimal noise.
-Noisy generated or redundant localization changes (that are useful when
-translations) are contained in the ``kallithea-i18n`` repo on the ``i18n``
-branch. The translation files in the main repository have no line numbers, no
-untranslated entries, no fuzzy entries, no unused entries, and no constantly
-changing records of "latest" this and that (name, date, version, etc).
-
-The branches in the main repo (``default`` and ``stable``) will thus only have
-stripped ``.pot`` and ``.po`` files: an (almost) empty
-``kallithea/i18n/kallithea.pot`` file, and minimal ``.po`` files. There are no
-binary ``.mo`` files in any repo - these are only generated when packaging for
-release (or locally if installing from source).
-
-Generally, ``kallithea/i18n/`` should not be changed on the ``default`` and
-``stable`` branches at all. The ``i18n`` branch should *only* change
-``kallithea/i18n/`` . If there are changesets with exceptions from that, these
-changesets should probably be grafted/redone in the "right" place.
-
-The basic flow is thus:
-
-0. All weblate translation is done on the ``i18n`` branch which generally is
-   based on the ``stable`` branch.
-1. Graft the essential part of all new changes on the ``i18n`` branch to
-   ``stable`` (while normalizing to current stripped state of stable).
-2. Merge from ``stable`` to ``i18n`` (while normalizing to the resulting
-   unstripped and fully ``msgmerge``'d state and ``.pot``-updating state).
-3. Verify that the content of the ``i18n`` branch will give exactly the content
-   of the ``stable`` branch after stripping. If there is a diff, something has
-   to be fixed in one way or the other ... and the whole process should
-   probably be redone.
-
-Translate
-^^^^^^^^^
-
-First land full translation changes in the ``kallithea-i18n`` repo on the
-``i18n`` branch. That can be done in pretty much any way you want. If changes
-for some reason have to be grafted or merged, there might be odd conflicts due
-to all the noise. Conflicts on the full ``i18n`` branch can perhaps be resolved
-more easily using non-stripping normalization before merging::
-
-  python3 setup.py extract_messages && cp kallithea/i18n/kallithea.pot full.pot && hg revert kallithea/i18n/kallithea.pot -r .
-  hg resolve kallithea/i18n/ --tool X --config merge-tools.X.executable=python3 --config merge-tools.X.args='scripts/i18n normalized-merge --merge-pot-file full.pot $local $base $other $output'
-
-Land in main repository - stripped
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-When the full i18n changes have landed on the ``i18n`` branch, prepare to land
-them on ``stable``::
-
-  hg up -cr stable
-  python3 setup.py extract_messages && cp kallithea/i18n/kallithea.pot full.pot && hg revert kallithea/i18n/kallithea.pot
-
-Consider all new ``i18n`` changes since last merge from ``stable``::
-
-  hg log -G --style compact -r 'only("i18n", children(::stable))'
-
-Graft them one by one (or in collapsed chunks) while normalizing.
-
-If the graft has conflicts, use the ``scripts/i18n`` normalization tool to
-apply ``msgmerge`` and strip before doing 3-way merge and resolving conflicts::
-
-  hg resolve kallithea/i18n/ --tool X --config merge-tools.X.executable=python3 --config merge-tools.X.args='scripts/i18n normalized-merge --merge-pot-file full.pot --strip $local $base $other $output'
-
-When all conflicts have been resolved, continue the graft::
-
-  hg graft --continue
-
-Then make sure any non-conflicting files are normalized and stripped too::
-
-  scripts/i18n normalize-po-files --strip --merge-pot-file full.pot kallithea/i18n/*/LC_MESSAGES/kallithea.po
-  hg ci --amend --config ui.editor=true
-
-When things have been grafted to the ``stable`` branch, clean up history if
-necessary: clean up the author and commit message when necessary, and perhaps
-merge multiple changesets from same contributor.
-
-Merge back to ``i18n``
-^^^^^^^^^^^^^^^^^^^^^^
-
-For any i18n changes that for some reason have been done on the ``stable``
-branch, apply them manually on the ``i18n`` branch too - perhaps by grafting
-and editing manually. The merge done in this step will `not` take care of it.
-If the verification step done a bit later points out that something has been
-missed, strip and go back to this point.
-
-Then merge back to the ``i18n`` branch using normalization while keeping the
-full ``.po`` files, and updating the full ``.pot`` and ``.po`` to current
-state::
-
-  hg up -cr i18n
-  hg merge stable --tool internal:fail
-  hg revert kallithea/i18n/*/LC_MESSAGES/*.po -r .
-  hg resolve -m kallithea/i18n/*/LC_MESSAGES/*.po
-  hg resolve -l  # verify all conflicts have been resolved
-  python3 setup.py extract_messages && cp kallithea/i18n/kallithea.pot full.pot
-  scripts/i18n normalize-po-files --merge-pot-file full.pot kallithea/i18n/*/LC_MESSAGES/kallithea.po
-  hg commit  # "Merge from stable"
-
-Note: ``normalize-po-files`` can also pretty much be done manually with::
-
-  for po in kallithea/i18n/*/LC_MESSAGES/kallithea.po; do msgmerge --width=76 --backup=none --previous --update $po full.pot ; done
-
-Note: Additional merges from ``stable`` to ``i18n`` can be done any time.
-
-Verify
-^^^^^^
-
-Verify things are in sync between the full ``i18n`` branch and the stripped
-``stable`` branch::
-
-  hg up -cr stable
-  hg revert -a -r i18n
-  python3 setup.py extract_messages && cp kallithea/i18n/kallithea.pot full.pot && hg revert kallithea/i18n/kallithea.pot
-  scripts/i18n normalize-po-files --strip --merge-pot-file full.pot kallithea/i18n/*/LC_MESSAGES/kallithea.po
-  hg diff
-
-If there is a diff, figure out where it came from, go back and fix the root
-cause, and redo the graft/merge.
-
-Push
-^^^^
-
-The changes on the ``stable`` branch should now be ready for pushing - verify
-the actual changes with a thorough review of::
-
-  hg out -pvr stable
-
-When ``stable`` changes have been pushed, also push the ``i18n`` branch to the
-``kallithea-i18n`` repo so Weblate can see it.
-
-
-.. _Weblate: http://weblate.org/
--- a/kallithea/i18n/kallithea.pot	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/i18n/kallithea.pot	Thu Jun 18 16:33:55 2020 +0200
@@ -6,15 +6,16 @@
 #, fuzzy
 msgid ""
 msgstr ""
-"Project-Id-Version: Kallithea 0.5.99\n"
+"Project-Id-Version: Kallithea 0.6.1\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
+"POT-Creation-Date: 2020-06-18 16:35+0200\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"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.7.0\n"
+"Generated-By: Babel 2.8.0\n"
 
 #: kallithea/controllers/changelog.py:67
 #: kallithea/controllers/pullrequests.py:247 kallithea/lib/base.py:602
--- a/kallithea/lib/auth_modules/auth_crowd.py	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/lib/auth_modules/auth_crowd.py	Thu Jun 18 16:33:55 2020 +0200
@@ -78,42 +78,26 @@
         handler = urllib.request.HTTPBasicAuthHandler(mgr)
         self.opener = urllib.request.build_opener(handler)
 
-    def _request(self, url, body=None, headers=None,
-                 method=None, noformat=False,
-                 empty_response_ok=False):
+    def _request(self, url, body=None):
         _headers = {"Content-type": "application/json",
                     "Accept": "application/json"}
         if self.user and self.passwd:
             authstring = ascii_str(base64.b64encode(safe_bytes("%s:%s" % (self.user, self.passwd))))
             _headers["Authorization"] = "Basic %s" % authstring
-        if headers:
-            _headers.update(headers)
         log.debug("Sent to crowd at %s:\nHeaders: %s\nBody:\n%s", url, _headers, body)
         req = urllib.request.Request(url, body, _headers)
-        if method:
-            req.get_method = lambda: method
 
         global msg
-        msg = ""
+        msg = None
         try:
             rdoc = self.opener.open(req)
-            msg = "".join(rdoc.readlines())
-            if not msg and empty_response_ok:
-                rval = {}
-                rval["status"] = True
-                rval["error"] = "Response body was empty"
-            elif not noformat:
-                rval = ext_json.loads(msg)
-                rval["status"] = True
-            else:
-                rval = "".join(rdoc.readlines())
+            msg = rdoc.read()
+            rval = ext_json.loads(msg)
+            rval["status"] = True
         except Exception as e:
-            if not noformat:
-                rval = {"status": False,
-                        "body": body,
-                        "error": str(e) + "\n" + msg}
-            else:
-                rval = None
+            rval = {"status": False,
+                    "body": body,
+                    "error": "%s\n%r" % (e, msg)}
         return rval
 
     def user_auth(self, username, password):
--- a/kallithea/lib/inifile.py	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/lib/inifile.py	Thu Jun 18 16:33:55 2020 +0200
@@ -40,6 +40,7 @@
     'host': '127.0.0.1',
     'port': '5000',
     'uuid': lambda: 'VERY-SECRET',
+    'version': '',
 }
 
 variable_options = {
--- a/kallithea/lib/paster_commands/template.ini.mako	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/lib/paster_commands/template.ini.mako	Thu Jun 18 16:33:55 2020 +0200
@@ -1,59 +1,60 @@
 ## -*- coding: utf-8 -*-
-<%text>###################################################################################</%text>
-<%text>###################################################################################</%text>
-<%text>## Kallithea config file generated with kallithea-config                         ##</%text>
-<%text>##                                                                               ##</%text>
-<%text>## The %(here)s variable will be replaced with the parent directory of this file ##</%text>
-<%text>###################################################################################</%text>
-<%text>###################################################################################</%text>
+<%text>##</%text>#################################################################################
+<%text>##</%text>#################################################################################
+<%text>##</%text> Kallithea config file generated with kallithea-cli ${'%-27s' % version       }##
+<%text>##</%text>                                                                               ##
+<%text>##</%text> The %(here)s variable will generally be replaced with the parent directory of ##
+<%text>##</%text> this file. Other use of % must be escaped as %% .                             ##
+<%text>##</%text>#################################################################################
+<%text>##</%text>#################################################################################
 
 [DEFAULT]
 
-<%text>################################################################################</%text>
-<%text>## Email settings                                                             ##</%text>
-<%text>##                                                                            ##</%text>
-<%text>## Refer to the documentation ("Email settings") for more details.            ##</%text>
-<%text>##                                                                            ##</%text>
-<%text>## It is recommended to use a valid sender address that passes access         ##</%text>
-<%text>## validation and spam filtering in mail servers.                             ##</%text>
-<%text>################################################################################</%text>
+<%text>##</%text>##############################################################################
+<%text>##</%text> Email settings                                                             ##
+<%text>##</%text>                                                                            ##
+<%text>##</%text> Refer to the documentation ("Email settings") for more details.            ##
+<%text>##</%text>                                                                            ##
+<%text>##</%text> It is recommended to use a valid sender address that passes access         ##
+<%text>##</%text> validation and spam filtering in mail servers.                             ##
+<%text>##</%text>##############################################################################
 
-<%text>## 'From' header for application emails. You can optionally add a name.</%text>
-<%text>## Default:</%text>
+<%text>##</%text> 'From' header for application emails. You can optionally add a name.
+<%text>##</%text> Default:
 #app_email_from = Kallithea
-<%text>## Examples:</%text>
+<%text>##</%text> Examples:
 #app_email_from = Kallithea <kallithea-noreply@example.com>
 #app_email_from = kallithea-noreply@example.com
 
-<%text>## Subject prefix for application emails.</%text>
-<%text>## A space between this prefix and the real subject is automatically added.</%text>
-<%text>## Default:</%text>
+<%text>##</%text> Subject prefix for application emails.
+<%text>##</%text> A space between this prefix and the real subject is automatically added.
+<%text>##</%text> Default:
 #email_prefix =
-<%text>## Example:</%text>
+<%text>##</%text> Example:
 #email_prefix = [Kallithea]
 
-<%text>## Recipients for error emails and fallback recipients of application mails.</%text>
-<%text>## Multiple addresses can be specified, comma-separated.</%text>
-<%text>## Only addresses are allowed, do not add any name part.</%text>
-<%text>## Default:</%text>
+<%text>##</%text> Recipients for error emails and fallback recipients of application mails.
+<%text>##</%text> Multiple addresses can be specified, comma-separated.
+<%text>##</%text> Only addresses are allowed, do not add any name part.
+<%text>##</%text> Default:
 #email_to =
-<%text>## Examples:</%text>
+<%text>##</%text> Examples:
 #email_to = admin@example.com
 #email_to = admin@example.com,another_admin@example.com
 email_to =
 
-<%text>## 'From' header for error emails. You can optionally add a name.</%text>
-<%text>## Default: (none)</%text>
-<%text>## Examples:</%text>
+<%text>##</%text> 'From' header for error emails. You can optionally add a name.
+<%text>##</%text> Default: (none)
+<%text>##</%text> Examples:
 #error_email_from = Kallithea Errors <kallithea-noreply@example.com>
 #error_email_from = kallithea_errors@example.com
 error_email_from =
 
-<%text>## SMTP server settings</%text>
-<%text>## If specifying credentials, make sure to use secure connections.</%text>
-<%text>## Default: Send unencrypted unauthenticated mails to the specified smtp_server.</%text>
-<%text>## For "SSL", use smtp_use_ssl = true and smtp_port = 465.</%text>
-<%text>## For "STARTTLS", use smtp_use_tls = true and smtp_port = 587.</%text>
+<%text>##</%text> SMTP server settings
+<%text>##</%text> If specifying credentials, make sure to use secure connections.
+<%text>##</%text> Default: Send unencrypted unauthenticated mails to the specified smtp_server.
+<%text>##</%text> For "SSL", use smtp_use_ssl = true and smtp_port = 465.
+<%text>##</%text> For "STARTTLS", use smtp_use_tls = true and smtp_port = 587.
 smtp_server =
 smtp_username =
 smtp_password =
@@ -62,67 +63,67 @@
 smtp_use_tls = false
 
 %if http_server != 'uwsgi':
-<%text>## Entry point for 'gearbox serve'</%text>
+<%text>##</%text> Entry point for 'gearbox serve'
 [server:main]
 host = ${host}
 port = ${port}
 
 %if http_server == 'gearbox':
-<%text>## Gearbox default web server ##</%text>
+<%text>##</%text> Gearbox default web server ##
 use = egg:gearbox#wsgiref
-<%text>## nr of worker threads to spawn</%text>
+<%text>##</%text> nr of worker threads to spawn
 threadpool_workers = 1
-<%text>## max request before thread respawn</%text>
+<%text>##</%text> max request before thread respawn
 threadpool_max_requests = 100
-<%text>## option to use threads of process</%text>
+<%text>##</%text> option to use threads of process
 use_threadpool = true
 
 %elif http_server == 'gevent':
-<%text>## Gearbox gevent web server ##</%text>
+<%text>##</%text> Gearbox gevent web server ##
 use = egg:gearbox#gevent
 
 %elif http_server == 'waitress':
-<%text>## WAITRESS ##</%text>
+<%text>##</%text> WAITRESS ##
 use = egg:waitress#main
-<%text>## number of worker threads</%text>
+<%text>##</%text> number of worker threads
 threads = 1
-<%text>## MAX BODY SIZE 100GB</%text>
+<%text>##</%text> MAX BODY SIZE 100GB
 max_request_body_size = 107374182400
-<%text>## use poll instead of select, fixes fd limits, may not work on old</%text>
-<%text>## windows systems.</%text>
+<%text>##</%text> use poll instead of select, fixes fd limits, may not work on old
+<%text>##</%text> windows systems.
 #asyncore_use_poll = True
 
 %elif http_server == 'gunicorn':
-<%text>## GUNICORN ##</%text>
+<%text>##</%text> GUNICORN ##
 use = egg:gunicorn#main
-<%text>## number of process workers. You must set `instance_id = *` when this option</%text>
-<%text>## is set to more than one worker</%text>
+<%text>##</%text> number of process workers. You must set `instance_id = *` when this option
+<%text>##</%text> is set to more than one worker
 workers = 4
-<%text>## process name</%text>
+<%text>##</%text> process name
 proc_name = kallithea
-<%text>## type of worker class, one of sync, eventlet, gevent, tornado</%text>
-<%text>## recommended for bigger setup is using of of other than sync one</%text>
+<%text>##</%text> type of worker class, one of sync, eventlet, gevent, tornado
+<%text>##</%text> recommended for bigger setup is using of of other than sync one
 worker_class = sync
 max_requests = 1000
-<%text>## amount of time a worker can handle request before it gets killed and</%text>
-<%text>## restarted</%text>
+<%text>##</%text> amount of time a worker can handle request before it gets killed and
+<%text>##</%text> restarted
 timeout = 3600
 
 %endif
 %else:
-<%text>## UWSGI ##</%text>
+<%text>##</%text> UWSGI ##
 [uwsgi]
-<%text>## Note: this section is parsed by the uWSGI .ini parser when run as:</%text>
-<%text>## uwsgi --venv /srv/kallithea/venv --ini-paste-logged my.ini</%text>
-<%text>## Note: in uWSGI 2.0.18 or older, pastescript needs to be installed to</%text>
-<%text>## get correct application logging. In later versions this is not necessary.</%text>
-<%text>## pip install pastescript</%text>
+<%text>##</%text> Note: this section is parsed by the uWSGI .ini parser when run as:
+<%text>##</%text> uwsgi --venv /srv/kallithea/venv --ini-paste-logged my.ini
+<%text>##</%text> Note: in uWSGI 2.0.18 or older, pastescript needs to be installed to
+<%text>##</%text> get correct application logging. In later versions this is not necessary.
+<%text>##</%text> pip install pastescript
 
-<%text>## HTTP Basics:</%text>
+<%text>##</%text> HTTP Basics:
 http-socket = ${host}:${port}
 buffer-size = 65535                    ; Mercurial will use huge GET headers for discovery
 
-<%text>## Scaling:</%text>
+<%text>##</%text> Scaling:
 master = true                          ; Use separate master and worker processes
 auto-procname = true                   ; Name worker processes accordingly
 lazy = true                            ; App *must* be loaded in workers - db connections can't be shared
@@ -130,7 +131,7 @@
 cheaper = 1                            ; Initial and on demand scaling down to this many worker processes
 max-requests = 1000                    ; Graceful reload of worker processes to avoid leaks
 
-<%text>## Tweak defaults:</%text>
+<%text>##</%text> Tweak defaults:
 strict = true                          ; Fail on unknown config directives
 enable-threads = true                  ; Enable Python threads (not threaded workers)
 vacuum = true                          ; Delete sockets during shutdown
@@ -140,197 +141,197 @@
 reload-on-exception = true             ; Don't assume that the application worker can process more requests after a severe error
 
 %endif
-<%text>## middleware for hosting the WSGI application under a URL prefix</%text>
+<%text>##</%text> middleware for hosting the WSGI application under a URL prefix
 #[filter:proxy-prefix]
 #use = egg:PasteDeploy#prefix
 #prefix = /<your-prefix>
 
 [app:main]
 use = egg:kallithea
-<%text>## enable proxy prefix middleware</%text>
+<%text>##</%text> enable proxy prefix middleware
 #filter-with = proxy-prefix
 
 full_stack = true
 static_files = true
 
-<%text>## Internationalization (see setup documentation for details)</%text>
-<%text>## By default, the languages requested by the browser are used if available, with English as default.</%text>
-<%text>## Set i18n.enabled=false to disable automatic language choice.</%text>
+<%text>##</%text> Internationalization (see setup documentation for details)
+<%text>##</%text> By default, the languages requested by the browser are used if available, with English as default.
+<%text>##</%text> Set i18n.enabled=false to disable automatic language choice.
 #i18n.enabled = true
-<%text>## To Force a language, set i18n.enabled=false and specify the language in i18n.lang.</%text>
-<%text>## Valid values are the names of subdirectories in kallithea/i18n with a LC_MESSAGES/kallithea.mo</%text>
+<%text>##</%text> To Force a language, set i18n.enabled=false and specify the language in i18n.lang.
+<%text>##</%text> Valid values are the names of subdirectories in kallithea/i18n with a LC_MESSAGES/kallithea.mo
 #i18n.lang = en
 
 cache_dir = %(here)s/data
 index_dir = %(here)s/data/index
 
-<%text>## uncomment and set this path to use archive download cache</%text>
+<%text>##</%text> uncomment and set this path to use archive download cache
 archive_cache_dir = %(here)s/tarballcache
 
-<%text>## change this to unique ID for security</%text>
+<%text>##</%text> change this to unique ID for security
 app_instance_uuid = ${uuid()}
 
-<%text>## cut off limit for large diffs (size in bytes)</%text>
+<%text>##</%text> cut off limit for large diffs (size in bytes)
 cut_off_limit = 256000
 
-<%text>## force https in Kallithea, fixes https redirects, assumes it's always https</%text>
+<%text>##</%text> force https in Kallithea, fixes https redirects, assumes it's always https
 force_https = false
 
-<%text>## use Strict-Transport-Security headers</%text>
+<%text>##</%text> use Strict-Transport-Security headers
 use_htsts = false
 
-<%text>## number of commits stats will parse on each iteration</%text>
+<%text>##</%text> number of commits stats will parse on each iteration
 commit_parse_limit = 25
 
-<%text>## Path to Python executable to be used for git hooks.</%text>
-<%text>## This value will be written inside the git hook scripts as the text</%text>
-<%text>## after '#!' (shebang). When empty or not defined, the value of</%text>
-<%text>## 'sys.executable' at the time of installation of the git hooks is</%text>
-<%text>## used, which is correct in many cases but for example not when using uwsgi.</%text>
-<%text>## If you change this setting, you should reinstall the Git hooks via</%text>
-<%text>## Admin > Settings > Remap and Rescan.</%text>
+<%text>##</%text> Path to Python executable to be used for git hooks.
+<%text>##</%text> This value will be written inside the git hook scripts as the text
+<%text>##</%text> after '#!' (shebang). When empty or not defined, the value of
+<%text>##</%text> 'sys.executable' at the time of installation of the git hooks is
+<%text>##</%text> used, which is correct in many cases but for example not when using uwsgi.
+<%text>##</%text> If you change this setting, you should reinstall the Git hooks via
+<%text>##</%text> Admin > Settings > Remap and Rescan.
 #git_hook_interpreter = /srv/kallithea/venv/bin/python3
 %if git_hook_interpreter:
 git_hook_interpreter = ${git_hook_interpreter}
 %endif
 
-<%text>## path to git executable</%text>
+<%text>##</%text> path to git executable
 git_path = git
 
-<%text>## git rev filter option, --all is the default filter, if you need to</%text>
-<%text>## hide all refs in changelog switch this to --branches --tags</%text>
+<%text>##</%text> git rev filter option, --all is the default filter, if you need to
+<%text>##</%text> hide all refs in changelog switch this to --branches --tags
 #git_rev_filter = --branches --tags
 
-<%text>## RSS feed options</%text>
+<%text>##</%text> RSS feed options
 rss_cut_off_limit = 256000
 rss_items_per_page = 10
 rss_include_diff = false
 
-<%text>## options for showing and identifying changesets</%text>
+<%text>##</%text> options for showing and identifying changesets
 show_sha_length = 12
 show_revision_number = false
 
-<%text>## Canonical URL to use when creating full URLs in UI and texts.</%text>
-<%text>## Useful when the site is available under different names or protocols.</%text>
-<%text>## Defaults to what is provided in the WSGI environment.</%text>
+<%text>##</%text> Canonical URL to use when creating full URLs in UI and texts.
+<%text>##</%text> Useful when the site is available under different names or protocols.
+<%text>##</%text> Defaults to what is provided in the WSGI environment.
 #canonical_url = https://kallithea.example.com/repos
 
-<%text>## gist URL alias, used to create nicer urls for gist. This should be an</%text>
-<%text>## url that does rewrites to _admin/gists/<gistid>.</%text>
-<%text>## example: http://gist.example.com/{gistid}. Empty means use the internal</%text>
-<%text>## Kallithea url, ie. http[s]://kallithea.example.com/_admin/gists/<gistid></%text>
+<%text>##</%text> gist URL alias, used to create nicer urls for gist. This should be an
+<%text>##</%text> url that does rewrites to _admin/gists/<gistid>.
+<%text>##</%text> example: http://gist.example.com/{gistid}. Empty means use the internal
+<%text>##</%text> Kallithea url, ie. http[s]://kallithea.example.com/_admin/gists/<gistid>
 gist_alias_url =
 
-<%text>## default encoding used to convert from and to unicode</%text>
-<%text>## can be also a comma separated list of encoding in case of mixed encodings</%text>
+<%text>##</%text> default encoding used to convert from and to unicode
+<%text>##</%text> can be also a comma separated list of encoding in case of mixed encodings
 default_encoding = utf-8
 
-<%text>## Set Mercurial encoding, similar to setting HGENCODING before launching Kallithea</%text>
+<%text>##</%text> Set Mercurial encoding, similar to setting HGENCODING before launching Kallithea
 hgencoding = utf-8
 
-<%text>## issue tracker for Kallithea (leave blank to disable, absent for default)</%text>
+<%text>##</%text> issue tracker for Kallithea (leave blank to disable, absent for default)
 #bugtracker = https://bitbucket.org/conservancy/kallithea/issues
 
-<%text>## issue tracking mapping for commit messages, comments, PR descriptions, ...</%text>
-<%text>## Refer to the documentation ("Integration with issue trackers") for more details.</%text>
+<%text>##</%text> issue tracking mapping for commit messages, comments, PR descriptions, ...
+<%text>##</%text> Refer to the documentation ("Integration with issue trackers") for more details.
 
-<%text>## regular expression to match issue references</%text>
-<%text>## This pattern may/should contain parenthesized groups, that can</%text>
-<%text>## be referred to in issue_server_link or issue_sub using Python backreferences</%text>
-<%text>## (e.g. \1, \2, ...). You can also create named groups with '(?P<groupname>)'.</%text>
-<%text>## To require mandatory whitespace before the issue pattern, use:</%text>
-<%text>## (?:^|(?<=\s)) before the actual pattern, and for mandatory whitespace</%text>
-<%text>## behind the issue pattern, use (?:$|(?=\s)) after the actual pattern.</%text>
+<%text>##</%text> regular expression to match issue references
+<%text>##</%text> This pattern may/should contain parenthesized groups, that can
+<%text>##</%text> be referred to in issue_server_link or issue_sub using Python backreferences
+<%text>##</%text> (e.g. \1, \2, ...). You can also create named groups with '(?P<groupname>)'.
+<%text>##</%text> To require mandatory whitespace before the issue pattern, use:
+<%text>##</%text> (?:^|(?<=\s)) before the actual pattern, and for mandatory whitespace
+<%text>##</%text> behind the issue pattern, use (?:$|(?=\s)) after the actual pattern.
 
 issue_pat = #(\d+)
 
-<%text>## server url to the issue</%text>
-<%text>## This pattern may/should contain backreferences to parenthesized groups in issue_pat.</%text>
-<%text>## A backreference can be \1, \2, ... or \g<groupname> if you specified a named group</%text>
-<%text>## called 'groupname' in issue_pat.</%text>
-<%text>## The special token {repo} is replaced with the full repository name</%text>
-<%text>## including repository groups, while {repo_name} is replaced with just</%text>
-<%text>## the name of the repository.</%text>
+<%text>##</%text> server url to the issue
+<%text>##</%text> This pattern may/should contain backreferences to parenthesized groups in issue_pat.
+<%text>##</%text> A backreference can be \1, \2, ... or \g<groupname> if you specified a named group
+<%text>##</%text> called 'groupname' in issue_pat.
+<%text>##</%text> The special token {repo} is replaced with the full repository name
+<%text>##</%text> including repository groups, while {repo_name} is replaced with just
+<%text>##</%text> the name of the repository.
 
 issue_server_link = https://issues.example.com/{repo}/issue/\1
 
-<%text>## substitution pattern to use as the link text</%text>
-<%text>## If issue_sub is empty, the text matched by issue_pat is retained verbatim</%text>
-<%text>## for the link text. Otherwise, the link text is that of issue_sub, with any</%text>
-<%text>## backreferences to groups in issue_pat replaced.</%text>
+<%text>##</%text> substitution pattern to use as the link text
+<%text>##</%text> If issue_sub is empty, the text matched by issue_pat is retained verbatim
+<%text>##</%text> for the link text. Otherwise, the link text is that of issue_sub, with any
+<%text>##</%text> backreferences to groups in issue_pat replaced.
 
 issue_sub =
 
-<%text>## issue_pat, issue_server_link and issue_sub can have suffixes to specify</%text>
-<%text>## multiple patterns, to other issues server, wiki or others</%text>
-<%text>## below an example how to create a wiki pattern</%text>
-<%text>## wiki-some-id -> https://wiki.example.com/some-id</%text>
+<%text>##</%text> issue_pat, issue_server_link and issue_sub can have suffixes to specify
+<%text>##</%text> multiple patterns, to other issues server, wiki or others
+<%text>##</%text> below an example how to create a wiki pattern
+<%text>##</%text> wiki-some-id -> https://wiki.example.com/some-id
 
 #issue_pat_wiki = wiki-(\S+)
 #issue_server_link_wiki = https://wiki.example.com/\1
 #issue_sub_wiki = WIKI-\1
 
-<%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>
+<%text>##</%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
 auth_ret_code =
 
-<%text>## allows to change the repository location in settings page</%text>
+<%text>##</%text> allows to change the repository location in settings page
 allow_repo_location_change = True
 
-<%text>## allows to setup custom hooks in settings page</%text>
+<%text>##</%text> allows to setup custom hooks in settings page
 allow_custom_hooks_settings = True
 
-<%text>## extra extensions for indexing, space separated and without the leading '.'.</%text>
+<%text>##</%text> extra extensions for indexing, space separated and without the leading '.'.
 #index.extensions =
 #    gemfile
 #    lock
 
-<%text>## extra filenames for indexing, space separated</%text>
+<%text>##</%text> extra filenames for indexing, space separated
 #index.filenames =
 #    .dockerignore
 #    .editorconfig
 #    INSTALL
 #    CHANGELOG
 
-<%text>####################################</%text>
-<%text>###           SSH CONFIG        ####</%text>
-<%text>####################################</%text>
+<%text>##</%text>##################################
+<%text>##</%text>            SSH CONFIG          ##
+<%text>##</%text>##################################
 
-<%text>## SSH is disabled by default, until an Administrator decides to enable it.</%text>
+<%text>##</%text> SSH is disabled by default, until an Administrator decides to enable it.
 ssh_enabled = false
 
-<%text>## File where users' SSH keys will be stored *if* ssh_enabled is true.</%text>
+<%text>##</%text> File where users' SSH keys will be stored *if* ssh_enabled is true.
 #ssh_authorized_keys = /home/kallithea/.ssh/authorized_keys
 %if user_home_path:
 ssh_authorized_keys = ${user_home_path}/.ssh/authorized_keys
 %endif
 
-<%text>## Path to be used in ssh_authorized_keys file to invoke kallithea-cli with ssh-serve.</%text>
+<%text>##</%text> Path to be used in ssh_authorized_keys file to invoke kallithea-cli with ssh-serve.
 #kallithea_cli_path = /srv/kallithea/venv/bin/kallithea-cli
 %if kallithea_cli_path:
 kallithea_cli_path = ${kallithea_cli_path}
 %endif
 
-<%text>## Locale to be used in the ssh-serve command.</%text>
-<%text>## This is needed because an SSH client may try to use its own locale</%text>
-<%text>## settings, which may not be available on the server.</%text>
-<%text>## See `locale -a` for valid values on this system.</%text>
+<%text>##</%text> Locale to be used in the ssh-serve command.
+<%text>##</%text> This is needed because an SSH client may try to use its own locale
+<%text>##</%text> settings, which may not be available on the server.
+<%text>##</%text> See `locale -a` for valid values on this system.
 #ssh_locale = C.UTF-8
 %if ssh_locale:
 ssh_locale = ${ssh_locale}
 %endif
 
-<%text>####################################</%text>
-<%text>###        CELERY CONFIG        ####</%text>
-<%text>####################################</%text>
+<%text>##</%text>##################################
+<%text>##</%text>         CELERY CONFIG          ##
+<%text>##</%text>##################################
 
-<%text>## Note: Celery doesn't support Windows.</%text>
+<%text>##</%text> Note: Celery doesn't support Windows.
 use_celery = false
 
-<%text>## Celery config settings from https://docs.celeryproject.org/en/4.4.0/userguide/configuration.html prefixed with 'celery.'.</%text>
+<%text>##</%text> Celery config settings from https://docs.celeryproject.org/en/4.4.0/userguide/configuration.html prefixed with 'celery.'.
 
-<%text>## Example: use the message queue on the local virtual host 'kallitheavhost' as the RabbitMQ user 'kallithea':</%text>
+<%text>##</%text> Example: use the message queue on the local virtual host 'kallitheavhost' as the RabbitMQ user 'kallithea':
 celery.broker_url = amqp://kallithea:thepassword@localhost:5672/kallitheavhost
 
 celery.result.backend = db+sqlite:///celery-results.db
@@ -340,12 +341,12 @@
 celery.worker_concurrency = 2
 celery.worker_max_tasks_per_child = 1
 
-<%text>## If true, tasks will never be sent to the queue, but executed locally instead.</%text>
+<%text>##</%text> If true, tasks will never be sent to the queue, but executed locally instead.
 celery.task_always_eager = false
 
-<%text>####################################</%text>
-<%text>###         BEAKER CACHE        ####</%text>
-<%text>####################################</%text>
+<%text>##</%text>##################################
+<%text>##</%text>          BEAKER CACHE          ##
+<%text>##</%text>##################################
 
 beaker.cache.data_dir = %(here)s/data/cache/data
 beaker.cache.lock_dir = %(here)s/data/cache/lock
@@ -360,56 +361,56 @@
 beaker.cache.long_term_file.expire = 604800
 beaker.cache.long_term_file.key_length = 256
 
-<%text>####################################</%text>
-<%text>###       BEAKER SESSION        ####</%text>
-<%text>####################################</%text>
+<%text>##</%text>##################################
+<%text>##</%text>        BEAKER SESSION          ##
+<%text>##</%text>##################################
 
-<%text>## Name of session cookie. Should be unique for a given host and path, even when running</%text>
-<%text>## on different ports. Otherwise, cookie sessions will be shared and messed up.</%text>
+<%text>##</%text> Name of session cookie. Should be unique for a given host and path, even when running
+<%text>##</%text> on different ports. Otherwise, cookie sessions will be shared and messed up.
 session.key = kallithea
-<%text>## Sessions should always only be accessible by the browser, not directly by JavaScript.</%text>
+<%text>##</%text> Sessions should always only be accessible by the browser, not directly by JavaScript.
 session.httponly = true
-<%text>## Session lifetime. 2592000 seconds is 30 days.</%text>
+<%text>##</%text> Session lifetime. 2592000 seconds is 30 days.
 session.timeout = 2592000
 
-<%text>## Server secret used with HMAC to ensure integrity of cookies.</%text>
+<%text>##</%text> Server secret used with HMAC to ensure integrity of cookies.
 session.secret = ${uuid()}
-<%text>## Further, encrypt the data with AES.</%text>
+<%text>##</%text> Further, encrypt the data with AES.
 #session.encrypt_key = <key_for_encryption>
 #session.validate_key = <validation_key>
 
-<%text>## Type of storage used for the session, current types are</%text>
-<%text>## dbm, file, memcached, database, and memory.</%text>
+<%text>##</%text> Type of storage used for the session, current types are
+<%text>##</%text> dbm, file, memcached, database, and memory.
 
-<%text>## File system storage of session data. (default)</%text>
+<%text>##</%text> File system storage of session data. (default)
 #session.type = file
 
-<%text>## Cookie only, store all session data inside the cookie. Requires secure secrets.</%text>
+<%text>##</%text> Cookie only, store all session data inside the cookie. Requires secure secrets.
 #session.type = cookie
 
-<%text>## Database storage of session data.</%text>
+<%text>##</%text> Database storage of session data.
 #session.type = ext:database
 #session.sa.url = postgresql://postgres:qwe@localhost/kallithea
 #session.table_name = db_session
 
-<%text>####################################</%text>
-<%text>###       ERROR HANDLING        ####</%text>
-<%text>####################################</%text>
+<%text>##</%text>##################################
+<%text>##</%text>        ERROR HANDLING          ##
+<%text>##</%text>##################################
 
-<%text>## Show a nice error page for application HTTP errors and exceptions (default true)</%text>
+<%text>##</%text> Show a nice error page for application HTTP errors and exceptions (default true)
 #errorpage.enabled = true
 
-<%text>## Enable Backlash client-side interactive debugger (default false)</%text>
-<%text>## WARNING: *THIS MUST BE false IN PRODUCTION ENVIRONMENTS!!!*</%text>
-<%text>## This debug mode will allow all visitors to execute malicious code.</%text>
+<%text>##</%text> Enable Backlash client-side interactive debugger (default false)
+<%text>##</%text> WARNING: *THIS MUST BE false IN PRODUCTION ENVIRONMENTS!!!*
+<%text>##</%text> This debug mode will allow all visitors to execute malicious code.
 #debug = false
 
-<%text>## Enable Backlash server-side error reporting (unless debug mode handles it client-side) (default true)</%text>
+<%text>##</%text> Enable Backlash server-side error reporting (unless debug mode handles it client-side) (default true)
 #trace_errors.enable = true
-<%text>## Errors will be reported by mail if trace_errors.error_email is set.</%text>
+<%text>##</%text> Errors will be reported by mail if trace_errors.error_email is set.
 
-<%text>## Propagate email settings to ErrorReporter of TurboGears2</%text>
-<%text>## You do not normally need to change these lines</%text>
+<%text>##</%text> Propagate email settings to ErrorReporter of TurboGears2
+<%text>##</%text> You do not normally need to change these lines
 get trace_errors.smtp_server = smtp_server
 get trace_errors.smtp_port = smtp_port
 get trace_errors.from_address = error_email_from
@@ -419,12 +420,12 @@
 get trace_errors.smtp_use_tls = smtp_use_tls
 
 %if error_aggregation_service == 'sentry':
-<%text>################</%text>
-<%text>### [sentry] ###</%text>
-<%text>################</%text>
+<%text>##</%text>##############
+<%text>##</%text>  [sentry]  ##
+<%text>##</%text>##############
 
-<%text>## sentry is a alternative open source error aggregator</%text>
-<%text>## you must install python packages `sentry` and `raven` to enable</%text>
+<%text>##</%text> sentry is a alternative open source error aggregator
+<%text>##</%text> you must install python packages `sentry` and `raven` to enable
 
 sentry.dsn = YOUR_DNS
 sentry.servers =
@@ -439,45 +440,46 @@
 
 %endif
 
-<%text>##################################</%text>
-<%text>###       LOGVIEW CONFIG       ###</%text>
-<%text>##################################</%text>
+<%text>##</%text>################################
+<%text>##</%text>        LOGVIEW CONFIG        ##
+<%text>##</%text>################################
 
 logview.sqlalchemy = #faa
 logview.pylons.templating = #bfb
 logview.pylons.util = #eee
 
-<%text>#########################################################</%text>
-<%text>### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG    ###</%text>
-<%text>#########################################################</%text>
+<%text>##</%text>#######################
+<%text>##</%text>      DB CONFIG      ##
+<%text>##</%text>#######################
 
 %if database_engine == 'sqlite':
-<%text>## SQLITE [default]</%text>
+<%text>##</%text> SQLITE [default]
 sqlalchemy.url = sqlite:///%(here)s/kallithea.db?timeout=60
 
 %elif database_engine == 'postgres':
-<%text>## POSTGRESQL</%text>
+<%text>##</%text> POSTGRESQL
 sqlalchemy.url = postgresql://user:pass@localhost/kallithea
 
 %elif database_engine == 'mysql':
-<%text>## MySQL</%text>
+<%text>##</%text> MySQL
 sqlalchemy.url = mysql://user:pass@localhost/kallithea?charset=utf8
+<%text>##</%text> Note: the mysql:// prefix should also be used for MariaDB
 
 %endif
-<%text>## see sqlalchemy docs for other backends</%text>
+<%text>##</%text> see sqlalchemy docs for other backends
 
 sqlalchemy.pool_recycle = 3600
 
-<%text>################################</%text>
-<%text>### ALEMBIC CONFIGURATION   ####</%text>
-<%text>################################</%text>
+<%text>##</%text>##############################
+<%text>##</%text>   ALEMBIC CONFIGURATION    ##
+<%text>##</%text>##############################
 
 [alembic]
 script_location = kallithea:alembic
 
-<%text>################################</%text>
-<%text>### LOGGING CONFIGURATION   ####</%text>
-<%text>################################</%text>
+<%text>##</%text>##############################
+<%text>##</%text>   LOGGING CONFIGURATION    ##
+<%text>##</%text>##############################
 
 [loggers]
 keys = root, routes, kallithea, sqlalchemy, tg, gearbox, beaker, templates, whoosh_indexer, werkzeug, backlash
@@ -488,21 +490,21 @@
 [formatters]
 keys = generic, color_formatter, color_formatter_sql
 
-<%text>#############</%text>
-<%text>## LOGGERS ##</%text>
-<%text>#############</%text>
+<%text>##</%text>###########
+<%text>##</%text> LOGGERS ##
+<%text>##</%text>###########
 
 [logger_root]
 level = NOTSET
 handlers = console
-<%text>## For coloring based on log level:</%text>
+<%text>##</%text> For coloring based on log level:
 #handlers = console_color
 
 [logger_routes]
 level = WARN
 handlers =
 qualname = routes.middleware
-<%text>## "level = DEBUG" logs the route matched and routing variables.</%text>
+<%text>##</%text> "level = DEBUG" logs the route matched and routing variables.
 
 [logger_beaker]
 level = WARN
@@ -533,7 +535,7 @@
 level = WARN
 handlers =
 qualname = sqlalchemy.engine
-<%text>## For coloring based on log level and pretty printing of SQL:</%text>
+<%text>##</%text> For coloring based on log level and pretty printing of SQL:
 #level = INFO
 #handlers = console_color_sql
 #propagate = 0
@@ -553,9 +555,9 @@
 handlers =
 qualname = backlash
 
-<%text>##############</%text>
-<%text>## HANDLERS ##</%text>
-<%text>##############</%text>
+<%text>##</%text>############
+<%text>##</%text> HANDLERS ##
+<%text>##</%text>############
 
 [handler_console]
 class = StreamHandler
@@ -563,13 +565,13 @@
 formatter = generic
 
 [handler_console_color]
-<%text>## ANSI color coding based on log level</%text>
+<%text>##</%text> ANSI color coding based on log level
 class = StreamHandler
 args = (sys.stderr,)
 formatter = color_formatter
 
 [handler_console_color_sql]
-<%text>## ANSI color coding and pretty printing of SQL statements</%text>
+<%text>##</%text> ANSI color coding and pretty printing of SQL statements
 class = StreamHandler
 args = (sys.stderr,)
 formatter = color_formatter_sql
@@ -578,9 +580,9 @@
 class = NullHandler
 args = ()
 
-<%text>################</%text>
-<%text>## FORMATTERS ##</%text>
-<%text>################</%text>
+<%text>##</%text>##############
+<%text>##</%text> FORMATTERS ##
+<%text>##</%text>##############
 
 [formatter_generic]
 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
@@ -596,20 +598,20 @@
 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
 datefmt = %Y-%m-%d %H:%M:%S
 
-<%text>#################</%text>
-<%text>## SSH LOGGING ##</%text>
-<%text>#################</%text>
+<%text>##</%text>###############
+<%text>##</%text> SSH LOGGING ##
+<%text>##</%text>###############
 
-<%text>## The default loggers use 'handler_console' that uses StreamHandler with</%text>
-<%text>## destination 'sys.stderr'. In the context of the SSH server process, these log</%text>
-<%text>## messages would be sent to the client, which is normally not what you want.</%text>
-<%text>## By default, when running ssh-serve, just use NullHandler and disable logging</%text>
-<%text>## completely. For other logging options, see:</%text>
-<%text>## https://docs.python.org/2/library/logging.handlers.html</%text>
+<%text>##</%text> The default loggers use 'handler_console' that uses StreamHandler with
+<%text>##</%text> destination 'sys.stderr'. In the context of the SSH server process, these log
+<%text>##</%text> messages would be sent to the client, which is normally not what you want.
+<%text>##</%text> By default, when running ssh-serve, just use NullHandler and disable logging
+<%text>##</%text> completely. For other logging options, see:
+<%text>##</%text> https://docs.python.org/2/library/logging.handlers.html
 
 [ssh_serve:logger_root]
 level = CRITICAL
 handlers = null
 
-<%text>## Note: If logging is configured with other handlers, they might need similar</%text>
-<%text>## muting for ssh-serve too.</%text>
+<%text>##</%text> Note: If logging is configured with other handlers, they might need similar
+<%text>##</%text> muting for ssh-serve too.
--- a/kallithea/lib/vcs/backends/git/changeset.py	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/lib/vcs/backends/git/changeset.py	Thu Jun 18 16:33:55 2020 +0200
@@ -231,9 +231,9 @@
         # Only used to feed diffstat
         rev1 = self.parents[0] if self.parents else self.repository.EMPTY_CHANGESET
         rev2 = self
-        return b''.join(self.repository.get_diff(rev1, rev2,
+        return self.repository.get_diff(rev1, rev2,
                                     ignore_whitespace=ignore_whitespace,
-                                    context=context))
+                                    context=context)
 
     def get_file_mode(self, path):
         """
--- a/kallithea/lib/vcs/backends/hg/changeset.py	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/lib/vcs/backends/hg/changeset.py	Thu Jun 18 16:33:55 2020 +0200
@@ -329,7 +329,7 @@
             #vals = url,rev,type
             loc = vals[0]
             cs = vals[1]
-            dirnodes.append(SubModuleNode(k, url=loc, changeset=cs,
+            dirnodes.append(SubModuleNode(safe_str(k), url=safe_str(loc), changeset=cs,
                                           alias=als))
         nodes = dirnodes + filenodes
         for node in nodes:
--- a/kallithea/lib/vcs/backends/hg/repository.py	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/lib/vcs/backends/hg/repository.py	Thu Jun 18 16:33:55 2020 +0200
@@ -230,7 +230,7 @@
             return {}
 
         return OrderedDict(sorted(
-            ((safe_str(n), ascii_str(h)) for n, h in self._repo._bookmarks.items()),
+            ((safe_str(n), ascii_str(mercurial.node.hex(h))) for n, h in self._repo._bookmarks.items()),
             reverse=True,
             key=lambda x: x[0],  # sort by name
         ))
--- a/kallithea/lib/vcs/nodes.py	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/lib/vcs/nodes.py	Thu Jun 18 16:33:55 2020 +0200
@@ -603,4 +603,4 @@
         then only last part is returned.
         """
         org = self.path.rstrip('/').rsplit('/', 1)[-1]
-        return '%s @ %s' % (org, self.changeset.short_id)
+        return '%s @ %s' % (org, safe_str(self.changeset.short_id))
--- a/kallithea/model/db.py	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/model/db.py	Thu Jun 18 16:33:55 2020 +0200
@@ -2295,7 +2295,6 @@
     __tablename__ = 'user_ssh_keys'
     __table_args__ = (
         Index('usk_fingerprint_idx', 'fingerprint'),
-        UniqueConstraint('fingerprint'),
         _table_args_default_dict
     )
     __mapper_args__ = {}
--- a/kallithea/templates/about.html	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/templates/about.html	Thu Jun 18 16:33:55 2020 +0200
@@ -26,9 +26,14 @@
 
   <li>Copyright &copy; 2012&ndash;2020, Mads Kiilerich</li>
   <li>Copyright &copy; 2014&ndash;2020, Thomas De Schampheleire</li>
+  <li>Copyright &copy; 2015&ndash;2017, 2019&ndash;2020, Étienne Gilli</li>
+  <li>Copyright &copy; 2016&ndash;2017, 2020, Asterios Dimitriou</li>
+  <li>Copyright &copy; 2019&ndash;2020, Private</li>
   <li>Copyright &copy; 2020, Dennis Fink</li>
+  <li>Copyright &copy; 2020, J. Lavoie</li>
+  <li>Copyright &copy; 2020, robertus</li>
+  <li>Copyright &copy; 2020, Ross Thomas</li>
   <li>Copyright &copy; 2012, 2014&ndash;2017, 2019, Andrej Shadura</li>
-  <li>Copyright &copy; 2015&ndash;2017, 2019, Étienne Gilli</li>
   <li>Copyright &copy; 2017&ndash;2019, Allan Nordhøy</li>
   <li>Copyright &copy; 2018&ndash;2019, ssantos</li>
   <li>Copyright &copy; 2019, Adi Kriegisch</li>
@@ -41,7 +46,6 @@
   <li>Copyright &copy; 2019, Mateusz Mendel</li>
   <li>Copyright &copy; 2019, Nathan</li>
   <li>Copyright &copy; 2019, Oleksandr Shtalinberg</li>
-  <li>Copyright &copy; 2019, Private</li>
   <li>Copyright &copy; 2019, THANOS SIOURDAKIS</li>
   <li>Copyright &copy; 2019, Wolfgang Scherer</li>
   <li>Copyright &copy; 2019, Христо Станев</li>
@@ -57,7 +61,6 @@
   <li>Copyright &copy; 2012&ndash;2017, Unity Technologies</li>
   <li>Copyright &copy; 2015&ndash;2017, Søren Løvborg</li>
   <li>Copyright &copy; 2015, 2017, Sam Jaques</li>
-  <li>Copyright &copy; 2016&ndash;2017, Asterios Dimitriou</li>
   <li>Copyright &copy; 2017, Alessandro Molina</li>
   <li>Copyright &copy; 2017, Anton Schur</li>
   <li>Copyright &copy; 2017, Ching-Chen Mao</li>
--- a/kallithea/templates/admin/gists/edit.html	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/templates/admin/gists/edit.html	Thu Jun 18 16:33:55 2020 +0200
@@ -73,7 +73,7 @@
                     </div>
                     <div class="panel-body no-padding">
                         <div id="editor_container">
-                            <textarea id="editor_${h.FID('f',file.path)}" name="contents" style="display:none">${safe_str(file.content)}</textarea>
+                            <textarea id="editor_${h.FID('f',file.path)}" name="contents" style="display:none">${h.safe_str(file.content)}</textarea>
                         </div>
                     </div>
                 </div>
--- a/kallithea/templates/compare/compare_diff.html	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/templates/compare/compare_diff.html	Thu Jun 18 16:33:55 2020 +0200
@@ -82,8 +82,8 @@
                   %endif
                 </div>
         </div>
+    %endif
       </div>
-    %endif
 
     %if not c.compare_home:
         ## diff block
--- a/kallithea/tests/functional/test_admin_gists.py	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/tests/functional/test_admin_gists.py	Thu Jun 18 16:33:55 2020 +0200
@@ -168,4 +168,11 @@
         assert response.body == b'GIST BODY'
 
     def test_edit(self):
-        response = self.app.get(base.url('edit_gist', gist_id=1))
+        gist = _create_gist('gist-edit')
+        response = self.app.get(base.url('edit_gist', gist_id=gist.gist_access_id), status=302)
+        assert 'login' in response.location
+
+        self.log_user(base.TEST_USER_REGULAR_LOGIN, base.TEST_USER_REGULAR_PASS)
+        response = self.app.get(base.url('edit_gist', gist_id=gist.gist_access_id))
+
+        # FIXME actually test editing the gist
--- a/kallithea/tests/functional/test_feed.py	Wed May 06 14:47:06 2020 +0200
+++ b/kallithea/tests/functional/test_feed.py	Thu Jun 18 16:33:55 2020 +0200
@@ -3,18 +3,26 @@
 
 class TestFeedController(base.TestController):
 
-    def test_rss(self):
+    @base.parametrize('repo', [
+        base.HG_REPO,
+        base.GIT_REPO,
+    ])
+    def test_rss(self, repo):
         self.log_user()
         response = self.app.get(base.url(controller='feed', action='rss',
-                                    repo_name=base.HG_REPO))
+                                    repo_name=repo))
 
         assert response.content_type == "application/rss+xml"
         assert """<rss version="2.0">""" in response
 
-    def test_atom(self):
+    @base.parametrize('repo', [
+        base.HG_REPO,
+        base.GIT_REPO,
+    ])
+    def test_atom(self, repo):
         self.log_user()
         response = self.app.get(base.url(controller='feed', action='atom',
-                                    repo_name=base.HG_REPO))
+                                    repo_name=repo))
 
         assert response.content_type == """application/atom+xml"""
         assert """<?xml version="1.0" encoding="utf-8"?>""" in response
--- a/scripts/contributor_data.py	Wed May 06 14:47:06 2020 +0200
+++ b/scripts/contributor_data.py	Thu Jun 18 16:33:55 2020 +0200
@@ -40,6 +40,7 @@
 name_fixes['Weblate'] = "<>"
 name_fixes['xpol'] = "xpol <xpolife@gmail.com>"
 name_fixes['Lars <devel@sumpfralle.de>'] = "Lars Kruse <devel@sumpfralle.de>"
+name_fixes['Jeannette L'] = "J. Lavoie <j.lavoie@net-c.ca>"
 
 # Some committer email address domains that indicate that another entity might
 # hold some copyright too:
--- a/scripts/generate-ini.py	Wed May 06 14:47:06 2020 +0200
+++ b/scripts/generate-ini.py	Thu Jun 18 16:33:55 2020 +0200
@@ -55,7 +55,7 @@
     print('reading:', makofile)
     mako_org = open(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)
+    mako_marked_up = re.sub(r'\n##(.*)', r'\n<%text>##</%text>\1', mako_no_text_markup, flags=re.MULTILINE)
     if mako_marked_up != mako_org:
         print('writing:', makofile)
         open(makofile, 'w').write(mako_marked_up)
--- a/scripts/validate-commits	Wed May 06 14:47:06 2020 +0200
+++ b/scripts/validate-commits	Thu Jun 18 16:33:55 2020 +0200
@@ -11,6 +11,12 @@
     exit 1
 fi
 
+revset=$1
+if [ -z "$revset" ]; then
+    echo "Warning: no revisions specified, checking draft changes up to the current one."
+    revset='draft() and ancestors(.)'
+fi
+
 venv=$(mktemp -d kallithea-validatecommits-env-XXXXXX)
 resultfile=$(mktemp kallithea-validatecommits-result-XXXXXX)
 echo > "$resultfile"
@@ -29,7 +35,7 @@
 }
 trap finish EXIT
 
-for rev in $(hg log -r "$1" -T '{node}\n'); do
+for rev in $(hg log -r "$revset" -T '{node}\n'); do
     hg log -r "$rev"
     hg update "$rev"
 
--- a/setup.py	Wed May 06 14:47:06 2020 +0200
+++ b/setup.py	Thu Jun 18 16:33:55 2020 +0200
@@ -55,7 +55,7 @@
     "Mako >= 0.9.1, < 1.2",
     "Pygments >= 2.2.0, < 2.6",
     "Whoosh >= 2.7.1, < 2.8",
-    "celery >= 4.3, < 4.5",
+    "celery >= 4.3, < 4.5, != 4.4.4", # 4.4.4 is broken due to unexpressed dependency on 'future', see https://github.com/celery/celery/pull/6146
     "Babel >= 1.3, < 2.9",
     "python-dateutil >= 2.1.0, < 2.9",
     "Markdown >= 2.2.1, < 3.2",
@@ -153,7 +153,6 @@
     [console_scripts]
     kallithea-api =    kallithea.bin.kallithea_api:main
     kallithea-gist =   kallithea.bin.kallithea_gist:main
-    kallithea-config = kallithea.bin.kallithea_config:main
     kallithea-cli =    kallithea.bin.kallithea_cli:cli
 
     [paste.app_factory]