changeset 8386:a67945aec3df i18n

Let i18n branch track stable branch
author Mads Kiilerich <mads@kiilerich.com>
date Mon, 04 May 2020 19:12:40 +0200
parents 32ca332042cd (current diff) 4c7eca54cd55 (diff)
children 482e163caccd
files docs/setup.rst kallithea/i18n/be/LC_MESSAGES/kallithea.po kallithea/i18n/bg/LC_MESSAGES/kallithea.po kallithea/i18n/cs/LC_MESSAGES/kallithea.po kallithea/i18n/da/LC_MESSAGES/kallithea.po kallithea/i18n/de/LC_MESSAGES/kallithea.po kallithea/i18n/el/LC_MESSAGES/kallithea.po kallithea/i18n/es/LC_MESSAGES/kallithea.po kallithea/i18n/fr/LC_MESSAGES/kallithea.po kallithea/i18n/hu/LC_MESSAGES/kallithea.po kallithea/i18n/ja/LC_MESSAGES/kallithea.po kallithea/i18n/kallithea.pot kallithea/i18n/lb/LC_MESSAGES/kallithea.po kallithea/i18n/nb_NO/LC_MESSAGES/kallithea.po kallithea/i18n/nl_BE/LC_MESSAGES/kallithea.po kallithea/i18n/pl/LC_MESSAGES/kallithea.po kallithea/i18n/pt_BR/LC_MESSAGES/kallithea.po kallithea/i18n/ru/LC_MESSAGES/kallithea.po kallithea/i18n/sk/LC_MESSAGES/kallithea.po kallithea/i18n/tr/LC_MESSAGES/kallithea.po kallithea/i18n/uk/LC_MESSAGES/kallithea.po kallithea/i18n/zh_CN/LC_MESSAGES/kallithea.po kallithea/i18n/zh_TW/LC_MESSAGES/kallithea.po
diffstat 36 files changed, 456 insertions(+), 319 deletions(-) [+]
line wrap: on
line diff
--- a/docs/contributing.rst	Mon May 04 19:08:30 2020 +0200
+++ b/docs/contributing.rst	Mon May 04 19:12:40 2020 +0200
@@ -269,8 +269,8 @@
 Debugging
 ^^^^^^^^^
 
-A good way to trace what Kallithea is doing is to keep an eye on the output of
-stdout/stderr from the server process. Perhaps change ``my.ini`` to log at
+A good way to trace what Kallithea is doing is to keep an eye on the output on
+stdout/stderr of the server process. Perhaps change ``my.ini`` to log at
 ``DEBUG`` or ``INFO`` level, especially ``[logger_kallithea]``, but perhaps
 also other loggers. It is often easier to add additional ``log`` or ``print``
 statements than to use a Python debugger.
--- a/docs/setup.rst	Mon May 04 19:08:30 2020 +0200
+++ b/docs/setup.rst	Mon May 04 19:12:40 2020 +0200
@@ -153,6 +153,16 @@
     process, the server process will raise an exception each time it attempts to
     write the ``authorized_keys`` file.
 
+.. note:: It is possible to configure the SSH server to look for authorized
+   keys in multiple files, for example reserving ``ssh/authorized_keys`` to be
+   used for normal SSH and with Kallithea using
+   ``.ssh/authorized_keys_kallithea``. In ``/etc/ssh/sshd_config`` set
+   ``AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys_kallithea``
+   and restart sshd, and in ``my.ini`` set ``ssh_authorized_keys =
+   /home/kallithea/.ssh/authorized_keys_kallithea``. Note that this new
+   location will apply to all system users, and that multiple entries for the
+   same SSH key will shadow each other.
+
 .. warning:: The handling of SSH access is steered directly by the command
     specified in the ``authorized_keys`` file. There is no interaction with the
     web UI.  Once SSH access is correctly configured and enabled, it will work
--- a/kallithea/controllers/error.py	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/controllers/error.py	Mon May 04 19:12:40 2020 +0200
@@ -41,11 +41,8 @@
 class ErrorController(BaseController):
     """Generates error documents as and when they are required.
 
-    The ErrorDocuments middleware forwards to ErrorController when error
+    The errorpage middleware renders /error/document when error
     related status codes are returned from the application.
-
-    This behavior can be altered by changing the parameters to the
-    ErrorDocuments middleware in your config/middleware.py file.
     """
 
     def _before(self, *args, **kwargs):
--- a/kallithea/controllers/root.py	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/controllers/root.py	Mon May 04 19:12:40 2020 +0200
@@ -31,5 +31,5 @@
     def __init__(self):
         self.mapper = make_map(config)
 
-        # the following assignment hooks in error handling
+        # The URL '/error/document' (the default TG errorpage.path) should be handled by ErrorController.document
         self.error = ErrorController()
--- a/kallithea/i18n/be/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/be/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -5,7 +5,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2017-08-20 10:44+0000\n"
 "Last-Translator: Viktar Vauchkevich <victorenator@gmail.com>\n"
 "Language-Team: Belarusian <https://hosted.weblate.org/projects/kallithea/"
@@ -95,32 +95,32 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr "Немагчыма параўноўваць рэпазітары без агульнага продка"
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "Няма адказу"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "Невядомая памылка"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr "Запыт не распазнаны серверам з-за няправільнага сінтаксісу."
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "Несанкцыянаваны доступ да рэсурсу"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "У вас няма правоў для прагляду гэтай старонкі"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "Рэсурс не знойдзены"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1259,7 +1259,7 @@
 msgid "chmod"
 msgstr "chmod"
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/bg/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/bg/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -7,7 +7,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.4.99\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -92,32 +92,32 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr ""
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr ""
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr ""
 
+#: kallithea/controllers/error.py:81
+msgid ""
+"The request could not be understood by the server due to malformed syntax."
+msgstr ""
+
 #: kallithea/controllers/error.py:84
-msgid ""
-"The request could not be understood by the server due to malformed syntax."
-msgstr ""
-
-#: kallithea/controllers/error.py:87
 msgid "Unauthorized access to resource"
 msgstr ""
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr ""
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr ""
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1222,7 +1222,7 @@
 msgid "chmod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/cs/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/cs/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -5,7 +5,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "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/"
@@ -94,33 +94,33 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr ""
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 #, fuzzy
 msgid "No response"
 msgstr "Neznámá revize %s"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr ""
 
+#: kallithea/controllers/error.py:81
+msgid ""
+"The request could not be understood by the server due to malformed syntax."
+msgstr ""
+
 #: kallithea/controllers/error.py:84
-msgid ""
-"The request could not be understood by the server due to malformed syntax."
-msgstr ""
-
-#: kallithea/controllers/error.py:87
 msgid "Unauthorized access to resource"
 msgstr ""
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "Nemáte oprávnění k zobrazení této stránky"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr ""
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1231,7 +1231,7 @@
 msgid "chmod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/da/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/da/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -4,7 +4,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3.99\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2019-03-14 01:03+0000\n"
 "Last-Translator: Allan Nordhøy <epost@anotheragency.no>\n"
 "Language-Team: Danish <https://hosted.weblate.org/projects/kallithea/"
@@ -93,34 +93,34 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr "Kan ikke sammenligne repositories uden en fælles forfader"
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "Intet svar"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "Ukendt fejl"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr ""
 "Forespørgslen kunne ikke forstås af serveren på grund af fejlformet "
 "syntaks."
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "Uautoriseret adgang til ressource"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "Du har ikke tilladelse til at se denne side"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "Kunne ikke finde ressourcen"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1256,7 +1256,7 @@
 msgid "chmod"
 msgstr "chmod"
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/de/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/de/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -4,7 +4,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2019-05-29 22:52+0000\n"
 "Last-Translator: ssantos <ssantos@web.de>\n"
 "Language-Team: German <https://hosted.weblate.org/projects/kallithea/"
@@ -100,34 +100,34 @@
 "Ohne einen gemeinsamen Vorfahren ist ein Vergleich der Repositories nicht "
 "möglich"
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "Keine Rückmeldung"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "Unbekannter Fehler"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr ""
 "Die Anfrage konnte wegen ungültiger Syntax vom Server nicht ausgewertet "
 "werden."
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "Unauthorisierter Zugang zur Ressource"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "Du hast keine Rechte, um diese Seite zu betrachten"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "Die Ressource konnte nicht gefunden werden"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1290,7 +1290,7 @@
 msgid "chmod"
 msgstr "chmod"
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/el/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/el/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -4,7 +4,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-05-04 18:55+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2020-04-22 16:11+0000\n"
 "Last-Translator: Asterios Dimitriou <steve@pci.gr>\n"
 "Language-Team: Greek <https://hosted.weblate.org/projects/kallithea/"
@@ -93,34 +93,34 @@
 "Δεν μπορεί να γίνει σύγκριση αποθετηρίων χωρίς να χρησιμοποιηθεί κοινός "
 "πρόγονος"
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "Χωρίς απόκριση"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "Άγνωστο σφάλμα"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr ""
 "Η αίτηση δεν  μπόρεσε να ερμηνευτεί από τον εξυπηρετητή λόγω κακής "
 "διατύπωσης."
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "Ανεξουσιοδοτημένη πρόσβαση στον πόρο"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "Δεν έχετε άδεια για να εμφανίσετε αυτή τη σελίδα"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "Ο πόρος δεν μπορεί να βρεθεί"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1258,7 +1258,7 @@
 msgid "chmod"
 msgstr "αλλ δικαιωμ"
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/es/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/es/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -4,7 +4,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2018-04-18 11:43+0000\n"
 "Last-Translator: Jesús Sánchez <jsanchezfdz95@gmail.com>\n"
 "Language-Team: Spanish <https://hosted.weblate.org/projects/kallithea/"
@@ -93,34 +93,34 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr "No se pueden comparar repositorios sin usar un ancestro común"
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "No hay respuesta"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "Error desconocido"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr ""
 "La petición no ha podido ser atendida por el servidor debido un error de "
 "sintaxis."
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "Acceso no autorizado al recurso"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "No tiene permiso para ver esta página"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "No se ha encontrado el recurso"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1247,7 +1247,7 @@
 msgid "chmod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/fr/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/fr/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -4,7 +4,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-05-04 18:55+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2020-04-15 07:11+0000\n"
 "Last-Translator: Étienne Gilli <etienne@gilli.io>\n"
 "Language-Team: French <https://hosted.weblate.org/projects/kallithea/"
@@ -91,34 +91,34 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr "Impossible de comparer des dépôts sans utiliser un ancêtre commun"
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "Pas de réponse"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "Erreur inconnue"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr ""
 "Le serveur n’a pas pu interpréter la requête à cause d’une erreur de "
 "syntaxe."
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "Accès interdit à cette ressource"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "Vous n’avez pas la permission de voir cette page"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "Ressource introuvable"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1270,7 +1270,7 @@
 msgid "chmod"
 msgstr "chmod"
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/how_to	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/how_to	Mon May 04 19:12:40 2020 +0200
@@ -117,4 +117,139 @@
     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/hu/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/hu/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -5,7 +5,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2015-04-11 00:59+0200\n"
 "Last-Translator: Balázs Úr <urbalazs@gmail.com>\n"
 "Language-Team: Hungarian <https://hosted.weblate.org/projects/kallithea/"
@@ -92,32 +92,32 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr ""
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr ""
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr ""
 
+#: kallithea/controllers/error.py:81
+msgid ""
+"The request could not be understood by the server due to malformed syntax."
+msgstr ""
+
 #: kallithea/controllers/error.py:84
-msgid ""
-"The request could not be understood by the server due to malformed syntax."
-msgstr ""
-
-#: kallithea/controllers/error.py:87
 msgid "Unauthorized access to resource"
 msgstr ""
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr ""
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr ""
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1223,7 +1223,7 @@
 msgid "chmod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/ja/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/ja/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -4,7 +4,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2019-08-27 07:23+0000\n"
 "Last-Translator: leela <53352@protonmail.com>\n"
 "Language-Team: Japanese <https://hosted.weblate.org/projects/kallithea/"
@@ -96,33 +96,33 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr "共通の祖先を持たないのでリポジトリを比較できません"
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "応答がありません"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "不明なエラー"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr ""
 "形式が間違っているため、サーバーはリクエストを処理できませんでした。"
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "リソースにアクセスする権限がありません"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "このページを閲覧する権限がありません"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "リソースが見つかりません"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1261,7 +1261,7 @@
 msgid "chmod"
 msgstr "chmod"
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/kallithea.pot	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/kallithea.pot	Mon May 04 19:12:40 2020 +0200
@@ -8,14 +8,14 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.5.99\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+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.8.0\n"
+"Generated-By: Babel 2.7.0\n"
 
 #: kallithea/controllers/changelog.py:67
 #: kallithea/controllers/pullrequests.py:247 kallithea/lib/base.py:602
@@ -91,31 +91,31 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr ""
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr ""
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr ""
 
+#: kallithea/controllers/error.py:81
+msgid "The request could not be understood by the server due to malformed syntax."
+msgstr ""
+
 #: kallithea/controllers/error.py:84
-msgid "The request could not be understood by the server due to malformed syntax."
-msgstr ""
-
-#: kallithea/controllers/error.py:87
 msgid "Unauthorized access to resource"
 msgstr ""
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr ""
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr ""
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1215,7 +1215,7 @@
 msgid "chmod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/lb/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/lb/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -3,7 +3,7 @@
 msgid ""
 msgstr ""
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-05-04 18:57+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2020-04-13 19:42+0000\n"
 "Last-Translator: Dennis Fink <dennis.fink@c3l.lu>\n"
 "Language: lb\n"
@@ -88,32 +88,32 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr ""
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "Keng Äntwert"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "Onbekannten Feeler"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr ""
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr ""
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr ""
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr ""
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1218,7 +1218,7 @@
 msgid "chmod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/nb_NO/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/nb_NO/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -4,7 +4,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3.99\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2019-04-30 22:25+0000\n"
 "Last-Translator: Allan Nordhøy <epost@anotheragency.no>\n"
 "Language-Team: Norwegian Bokmål <https://hosted.weblate.org/projects/"
@@ -94,34 +94,34 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr "Kan ikke sammenligne pakkebrønner uten bruk av felles opphav"
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "Ingen respons"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "Ukjent feil"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr ""
 "Forespørselen kunne ikke forstås av tjeneren som følge av feilaktig "
 "syntaks."
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "Uautorisert tilgang til ressurs"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "Du har ikke tilgang til å se denne siden"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "Kunne ikke finne ressursen"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1263,7 +1263,7 @@
 msgid "chmod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/nl_BE/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/nl_BE/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -5,7 +5,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2019-10-05 19:28+0000\n"
 "Last-Translator: Thomas De Schampheleire <patrickdepinguin@gmail.com>\n"
 "Language-Team: Flemish <https://hosted.weblate.org/projects/kallithea/"
@@ -95,34 +95,34 @@
 "Kan geen repositories vergelijken zonder een gemeenschappelijke voorouder "
 "te gebruiken"
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "Geen antwoord"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "Ongekende fout"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr ""
 "De aanvraag kon niet door de server begrepen worden wegens incorrecte "
 "syntax."
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "Ongeautoriseerde toegang tot resource"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "U hebt geen permissie om deze pagina te bekijken"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "De resource kon niet gevonden worden"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1230,7 +1230,7 @@
 msgid "chmod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/pl/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/pl/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -4,7 +4,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2020-01-27 15:21+0000\n"
 "Last-Translator: robertus <robertuss12@gmail.com>\n"
 "Language-Team: Polish <https://hosted.weblate.org/projects/kallithea/"
@@ -94,34 +94,34 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr ""
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "Brak odpowiedzi"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "Nieznany błąd"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr ""
 "Żądanie nie może być rozumiane przez serwer z powodu zniekształconej "
 "składni."
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "Nieautoryzowany dostęp do zasobów"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "Nie masz uprawnień do przeglądania tej strony"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "Zasób nie został znaleziony"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1248,7 +1248,7 @@
 msgid "chmod"
 msgstr "chmod"
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/pt_BR/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/pt_BR/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -4,7 +4,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2014-02-13 14:34+0000\n"
 "Last-Translator: Marcin Kuźmiński <marcin@python-works.com>\n"
 "Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/"
@@ -92,35 +92,35 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr ""
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 #, fuzzy
 msgid "No response"
 msgstr "revisões"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr ""
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr ""
 "A requisição não pôde ser compreendida pelo servidor devido à sintaxe mal "
 "formada."
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "Acesso não autorizado ao recurso"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "Você não tem permissão para ver esta página"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "O recurso não pôde ser encontrado"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1260,7 +1260,7 @@
 msgid "chmod"
 msgstr "chmod"
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/ru/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/ru/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -4,7 +4,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2020-02-13 13:50+0000\n"
 "Last-Translator: Private <adamantine.sword@gmail.com>\n"
 "Language-Team: Russian <https://hosted.weblate.org/projects/kallithea/"
@@ -92,32 +92,32 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr "Невозможно сравнивать репозитории без общего предка"
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "Нет ответа"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "Неизвестная ошибка"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr "Запрос не распознан сервером из-за неправильного синтаксиса."
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "Несанкционированный доступ к ресурсу"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "У вас нет прав для просмотра этой страницы"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "Ресурс не найден"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1243,7 +1243,7 @@
 msgid "chmod"
 msgstr "chmod"
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/sk/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/sk/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -5,7 +5,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2015-04-01 12:59+0200\n"
 "Last-Translator: Andrej Shadura <andrew@shadura.me>\n"
 "Language-Team: Slovak <https://hosted.weblate.org/projects/kallithea/"
@@ -94,33 +94,33 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr ""
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 #, fuzzy
 msgid "No response"
 msgstr "Neznáma revízia %s"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr ""
 
+#: kallithea/controllers/error.py:81
+msgid ""
+"The request could not be understood by the server due to malformed syntax."
+msgstr ""
+
 #: kallithea/controllers/error.py:84
-msgid ""
-"The request could not be understood by the server due to malformed syntax."
-msgstr ""
-
-#: kallithea/controllers/error.py:87
 msgid "Unauthorized access to resource"
 msgstr ""
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "Nemáte oprávnenie na zobrazenie tejto stránky"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr ""
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1233,7 +1233,7 @@
 msgid "chmod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/tr/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/tr/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -7,7 +7,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.4.99\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2019-11-05 08:03+0000\n"
 "Last-Translator: Hüseyin Tunç <huseyin.tunc@bulutfon.com>\n"
 "Language-Team: Turkish <https://hosted.weblate.org/projects/kallithea/"
@@ -95,32 +95,32 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr ""
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr ""
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr ""
 
+#: kallithea/controllers/error.py:81
+msgid ""
+"The request could not be understood by the server due to malformed syntax."
+msgstr ""
+
 #: kallithea/controllers/error.py:84
-msgid ""
-"The request could not be understood by the server due to malformed syntax."
-msgstr ""
-
-#: kallithea/controllers/error.py:87
 msgid "Unauthorized access to resource"
 msgstr ""
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr ""
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr ""
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1225,7 +1225,7 @@
 msgid "chmod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/uk/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/uk/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -4,7 +4,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3.2\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2019-11-13 10:04+0000\n"
 "Last-Translator: Oleksandr Shtalinberg <o.shtalinberg@gmail.com>\n"
 "Language-Team: Ukrainian <https://hosted.weblate.org/projects/kallithea/"
@@ -92,32 +92,32 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr "Не вдається порівняти репозиторії без використання спільного предка"
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "Немає відповіді"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "Невідома помилка"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr "Запит не може бути зрозумілий сервером через синтаксичні помилки."
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "Несанкціонований доступ до ресурсів"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "Ви не маєте дозволу на перегляд цієї сторінки"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "Ресурс не може бути знайдений"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1238,7 +1238,7 @@
 msgid "chmod"
 msgstr "chmod"
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/zh_CN/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/zh_CN/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -4,7 +4,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2019-08-14 19:00+0000\n"
 "Last-Translator: Elizabeth Sherrock <lizzyd710@gmail.com>\n"
 "Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/"
@@ -94,32 +94,32 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr ""
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "无响应"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr "未知错误"
 
-#: kallithea/controllers/error.py:84
+#: kallithea/controllers/error.py:81
 msgid ""
 "The request could not be understood by the server due to malformed syntax."
 msgstr "由于错误的语法,服务器无法对请求进行响应。"
 
-#: kallithea/controllers/error.py:87
+#: kallithea/controllers/error.py:84
 msgid "Unauthorized access to resource"
 msgstr "未授权的资源访问"
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "无权访问该页面"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "资源未找到"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1243,7 +1243,7 @@
 msgid "chmod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/i18n/zh_TW/LC_MESSAGES/kallithea.po	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/i18n/zh_TW/LC_MESSAGES/kallithea.po	Mon May 04 19:12:40 2020 +0200
@@ -4,7 +4,7 @@
 msgstr ""
 "Project-Id-Version: Kallithea 0.3\n"
 "Report-Msgid-Bugs-To: translations@kallithea-scm.org\n"
-"POT-Creation-Date: 2020-04-27 13:26+0200\n"
+"POT-Creation-Date: 2020-05-04 19:12+0200\n"
 "PO-Revision-Date: 2017-03-10 18:26+0000\n"
 "Last-Translator: mao <mao@lins.fju.edu.tw>\n"
 "Language-Team: Chinese (Traditional) <https://hosted.weblate.org/projects/"
@@ -94,32 +94,32 @@
 msgid "Cannot compare repositories without using common ancestor"
 msgstr ""
 
-#: kallithea/controllers/error.py:70
+#: kallithea/controllers/error.py:67
 msgid "No response"
 msgstr "未回應"
 
-#: kallithea/controllers/error.py:71
+#: kallithea/controllers/error.py:68
 msgid "Unknown error"
 msgstr ""
 
+#: kallithea/controllers/error.py:81
+msgid ""
+"The request could not be understood by the server due to malformed syntax."
+msgstr ""
+
 #: kallithea/controllers/error.py:84
-msgid ""
-"The request could not be understood by the server due to malformed syntax."
-msgstr ""
-
-#: kallithea/controllers/error.py:87
 msgid "Unauthorized access to resource"
 msgstr ""
 
-#: kallithea/controllers/error.py:89
+#: kallithea/controllers/error.py:86
 msgid "You don't have permission to view this page"
 msgstr "您沒有權限瀏覽這個頁面"
 
-#: kallithea/controllers/error.py:91
+#: kallithea/controllers/error.py:88
 msgid "The resource could not be found"
 msgstr "找不到這個資源"
 
-#: kallithea/controllers/error.py:93
+#: kallithea/controllers/error.py:90
 msgid ""
 "The server encountered an unexpected condition which prevented it from "
 "fulfilling the request."
@@ -1231,7 +1231,7 @@
 msgid "chmod"
 msgstr ""
 
-#: kallithea/lib/helpers.py:1314
+#: kallithea/lib/helpers.py:1323
 #, python-format
 msgid ""
 "%s repository is not mapped to db perhaps it was created or renamed from "
--- a/kallithea/lib/helpers.py	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/lib/helpers.py	Mon May 04 19:12:40 2020 +0200
@@ -1191,18 +1191,27 @@
             issue_pat = CONFIG.get(k)
             issue_server_link = CONFIG.get('issue_server_link%s' % suffix)
             issue_sub = CONFIG.get('issue_sub%s' % suffix)
-            if not issue_pat or not issue_server_link or issue_sub is None: # issue_sub can be empty but should be present
-                log.error('skipping incomplete issue pattern %r: %r -> %r %r', suffix, issue_pat, issue_server_link, issue_sub)
+            issue_prefix = CONFIG.get('issue_prefix%s' % suffix)
+            if issue_prefix:
+                log.error('found unsupported issue_prefix%s = %r - use issue_sub%s instead', suffix, issue_prefix, suffix)
+            if not issue_pat:
+                log.error('skipping incomplete issue pattern %r: it needs a regexp', k)
+                continue
+            if not issue_server_link:
+                log.error('skipping incomplete issue pattern %r: it needs issue_server_link%s', k, suffix)
+                continue
+            if issue_sub is None: # issue_sub can be empty but should be present
+                log.error('skipping incomplete issue pattern %r: it needs (a potentially empty) issue_sub%s', k, suffix)
                 continue
 
             # Wrap tmp_urlify_issues_f with substitution of this pattern, while making sure all loop variables (and compiled regexpes) are bound
             try:
                 issue_re = re.compile(issue_pat)
             except re.error as e:
-                log.error('skipping invalid issue pattern %r: %r -> %r %r. Error: %s', suffix, issue_pat, issue_server_link, issue_sub, str(e))
+                log.error('skipping invalid issue pattern %r: %r -> %r %r. Error: %s', k, issue_pat, issue_server_link, issue_sub, str(e))
                 continue
 
-            log.debug('issue pattern %r: %r -> %r %r', suffix, issue_pat, issue_server_link, issue_sub)
+            log.debug('issue pattern %r: %r -> %r %r', k, issue_pat, issue_server_link, issue_sub)
 
             def issues_replace(match_obj,
                                issue_server_link=issue_server_link, issue_sub=issue_sub):
--- a/kallithea/lib/hooks.py	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/lib/hooks.py	Mon May 04 19:12:40 2020 +0200
@@ -66,7 +66,10 @@
 
 
 def repo_size(ui, repo, hooktype=None, **kwargs):
-    """Show size of Mercurial repository, to be called after push."""
+    """Show size of Mercurial repository.
+
+    Called as Mercurial hook changegroup.repo_size after push.
+    """
     size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', safe_str(repo.root))
 
     last_cs = repo[len(repo) - 1]
@@ -103,12 +106,13 @@
 
 def log_push_action(ui, repo, node, node_last, **kwargs):
     """
-    Entry point for Mercurial hook changegroup.push_logger.
+    Register that changes have been added to the repo - log the action *and* invalidate caches.
+    Note: This hook is not only logging, but also the side effect invalidating
+    caches! The function should perhaps be renamed.
+
+    Called as Mercurial hook changegroup.kallithea_log_push_action .
 
     The pushed changesets is given by the revset 'node:node_last'.
-
-    Note: This hook is not only logging, but also the side effect invalidating
-    cahes! The function should perhaps be renamed.
     """
     revs = [ascii_str(repo[r].hex()) for r in mercurial.scmutil.revrange(repo, [b'%s:%s' % (node, node_last)])]
     process_pushed_raw_ids(revs)
@@ -119,7 +123,7 @@
     """
     Register that changes have been added to the repo - log the action *and* invalidate caches.
 
-    Called from  Mercurial changegroup.push_logger calling hook log_push_action,
+    Called from Mercurial changegroup.kallithea_log_push_action calling hook log_push_action,
     or from the Git post-receive hook calling handle_git_post_receive ...
     or from scm _handle_push.
     """
--- a/kallithea/lib/inifile.py	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/lib/inifile.py	Mon May 04 19:12:40 2020 +0200
@@ -77,12 +77,13 @@
     ... #variable6 = 6.1
     ... #variable7 = 7.0
     ... variable7 = 7.1
+    ... variable8 = 8.0
     ... '''
     >>> mako_variable_values = {'mako_variable': 'VALUE', 'mako_function': (lambda: 'FUNCTION RESULT'),
     ...                         'conditional_options': 'option-a', 'http_server': 'nc'}
     >>> settings = { # only partially used
-    ...     '[first-section]': {'variable2': 'VAL2', 'first_extra': 'EXTRA'},
-    ...     '[comment-section]': {'variable3': '3.0', 'variable4': '4.1', 'variable5': '5.2', 'variable6': '6.2', 'variable7': '7.0'},
+    ...     '[first-section]': {'variable2': 'VAL2', 'first_extra': 'EXTRA', 'spacey': ' '},
+    ...     '[comment-section]': {'variable3': '3.0', 'variable4': '4.1', 'variable5': '5.2', 'variable6': '6.2', 'variable7': '7.0', 'variable8': None, 'variable9': None},
     ...     '[third-section]': {'third_extra': ' 3'},
     ...     '[fourth-section]': {'fourth_extra': '4', 'fourth': '"four"'},
     ... }
@@ -96,6 +97,7 @@
     variable2 = VAL2
     <BLANKLINE>
     first_extra = EXTRA
+    spacey =
     <BLANKLINE>
     <BLANKLINE>
     # FUNCTION RESULT
@@ -114,6 +116,10 @@
     variable6 = 6.2
     variable7 = 7.0
     #variable7 = 7.1
+    #variable8 = 8.0
+    <BLANKLINE>
+    variable8 = None
+    variable9 = None
     <BLANKLINE>
     [fourth-section]
     fourth = "four"
@@ -160,6 +166,8 @@
 
             lines = re.sub(r'^(#)?([^#\n\s]*)[ \t]*=[ \t]*(.*)$', comment_out, lines, flags=re.MULTILINE)
 
+            # 2nd pass:
+            # find the best comment line and un-comment or add after
             def add_after_comment(m):
                 """process a section comment line and add new value"""
                 line = m.group(0)
@@ -178,11 +186,12 @@
 
             lines = re.sub(r'^#([^#\n\s]*)[ \t]*=[ \t]*(.*)$', add_after_comment, lines, flags=re.MULTILINE)
 
-            # add unused section settings
+            # 3rd pass:
+            # settings that haven't been consumed yet at is appended to section
             if section_settings:
                 lines += '\n' + ''.join('%s = %s\n' % (key, value) for key, value in sorted(section_settings.items()))
 
-        return sectionname + '\n' + lines
+        return sectionname + '\n' + re.sub('[ \t]+\n', '\n', lines)
 
     # process sections until comments before next section or end
     ini_lines = re.sub(r'''^
--- a/kallithea/tests/api/api_base.py	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/tests/api/api_base.py	Mon May 04 19:12:40 2020 +0200
@@ -638,32 +638,32 @@
         response = api_call(self, params)
 
         repo = RepoModel().get_by_repo_name(self.REPO)
-        ret = repo.get_api_data()
+        assert len(repo.repo_to_perm) >= 2  # make sure we actually are testing something - probably the default 2 permissions, possibly more
+
+        expected = repo.get_api_data()
 
         members = []
-        followers = []
-        assert 2 == len(repo.repo_to_perm)
         for user in repo.repo_to_perm:
             perm = user.permission.permission_name
             user_obj = user.user
             user_data = {'name': user_obj.username, 'type': "user",
                          'permission': perm}
             members.append(user_data)
-
         for user_group in repo.users_group_to_perm:
             perm = user_group.permission.permission_name
             user_group_obj = user_group.users_group
             user_group_data = {'name': user_group_obj.users_group_name,
                                'type': "user_group", 'permission': perm}
             members.append(user_group_data)
+        expected['members'] = members
+
+        followers = []
 
         for user in repo.followers:
             followers.append(user.user.get_api_data())
 
-        ret['members'] = members
-        ret['followers'] = followers
+        expected['followers'] = followers
 
-        expected = ret
         try:
             self._compare_ok(id_, expected, given=response.body)
         finally:
--- a/kallithea/tests/functional/test_admin_auth_settings.py	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/tests/functional/test_admin_auth_settings.py	Mon May 04 19:12:40 2020 +0200
@@ -155,12 +155,12 @@
         response = self.app.get(
             url=base.url(controller='admin/my_account', action='my_account'),
             extra_environ={'THE_USER_NAME': 'johnd',
-                           'THE_USER_EMAIL': 'john@example.org',
+                           'THE_USER_EMAIL': 'john2@example.org',
                            'THE_USER_FIRSTNAME': 'John',
                            'THE_USER_LASTNAME': 'Doe',
                            }
         )
-        assert response.form['email'].value == 'john@example.org'
+        assert response.form['email'].value == 'john2@example.org'
         assert response.form['firstname'].value == 'John'
         assert response.form['lastname'].value == 'Doe'
 
--- a/kallithea/tests/functional/test_admin_settings.py	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/tests/functional/test_admin_settings.py	Mon May 04 19:12:40 2020 +0200
@@ -46,8 +46,7 @@
         response.mustcontain('test_hooks_1')
         response.mustcontain('cd %s' % base.TESTS_TMP_PATH)
 
-    def test_edit_custom_hook(self):
-        self.log_user()
+        # test_edit_custom_hook
         response = self.app.post(base.url('admin_settings_hooks'),
                                 params=dict(hook_ui_key='test_hooks_1',
                                             hook_ui_value='old_value_of_hook_1',
@@ -58,8 +57,7 @@
         response.mustcontain('test_hooks_1')
         response.mustcontain('new_value_of_hook_1')
 
-    def test_add_existing_custom_hook(self):
-        self.log_user()
+        # test_add_existing_custom_hook
         response = self.app.post(base.url('admin_settings_hooks'),
                                 params=dict(new_hook_ui_key='test_hooks_1',
                                             new_hook_ui_value='attempted_new_value',
--- a/kallithea/tests/models/test_permissions.py	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/tests/models/test_permissions.py	Mon May 04 19:12:40 2020 +0200
@@ -68,13 +68,7 @@
 
     def test_default_perms_set(self):
         u1_auth = AuthUser(user_id=self.u1.user_id)
-        perms = {
-            'repositories_groups': {},
-            'global': set(['hg.create.repository', 'repository.read',
-                           'hg.register.manual_activate']),
-            'repositories': {base.HG_REPO: 'repository.read'}
-        }
-        assert u1_auth.permissions['repositories'][base.HG_REPO] == perms['repositories'][base.HG_REPO]
+        assert u1_auth.permissions['repositories'][base.HG_REPO] == 'repository.read'
         new_perm = 'repository.write'
         RepoModel().grant_user_permission(repo=base.HG_REPO, user=self.u1,
                                           perm=new_perm)
@@ -85,12 +79,7 @@
 
     def test_default_admin_perms_set(self):
         a1_auth = AuthUser(user_id=self.a1.user_id)
-        perms = {
-            'repositories_groups': {},
-            'global': set(['hg.admin', 'hg.create.write_on_repogroup.true']),
-            'repositories': {base.HG_REPO: 'repository.admin'}
-        }
-        assert a1_auth.permissions['repositories'][base.HG_REPO] == perms['repositories'][base.HG_REPO]
+        assert a1_auth.permissions['repositories'][base.HG_REPO] == 'repository.admin'
         new_perm = 'repository.write'
         RepoModel().grant_user_permission(repo=base.HG_REPO, user=self.a1,
                                           perm=new_perm)
@@ -98,33 +87,24 @@
         # cannot really downgrade admins permissions !? they still gets set as
         # admin !
         u1_auth = AuthUser(user_id=self.a1.user_id)
-        assert u1_auth.permissions['repositories'][base.HG_REPO] == perms['repositories'][base.HG_REPO]
+        assert u1_auth.permissions['repositories'][base.HG_REPO] == 'repository.admin'
 
     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)
         u1_auth = AuthUser(user_id=self.u1.user_id)
-        perms = {
-            'repositories_groups': {'test1': 'group.read', 'test2': 'group.read'},
-            'global': set(Permission.DEFAULT_USER_PERMISSIONS),
-            'repositories': {base.HG_REPO: 'repository.read'}
-        }
-        assert u1_auth.permissions['repositories'][base.HG_REPO] == perms['repositories'][base.HG_REPO]
-        assert u1_auth.permissions['repositories_groups'] == perms['repositories_groups']
-        assert u1_auth.permissions['global'] == perms['global']
+        assert u1_auth.permissions['repositories'][base.HG_REPO] == 'repository.read'
+        assert u1_auth.permissions['repositories_groups'].get('test1') == 'group.read'
+        assert u1_auth.permissions['repositories_groups'].get('test2') == 'group.read'
+        assert u1_auth.permissions['global'] == set(Permission.DEFAULT_USER_PERMISSIONS)
 
     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)
         a1_auth = AuthUser(user_id=self.a1.user_id)
-        perms = {
-            'repositories_groups': {'test1': 'group.admin', 'test2': 'group.admin'},
-            'global': set(['hg.admin', 'hg.create.write_on_repogroup.true']),
-            'repositories': {base.HG_REPO: 'repository.admin'}
-        }
-
-        assert a1_auth.permissions['repositories'][base.HG_REPO] == perms['repositories'][base.HG_REPO]
-        assert a1_auth.permissions['repositories_groups'] == perms['repositories_groups']
+        assert a1_auth.permissions['repositories'][base.HG_REPO] == 'repository.admin'
+        assert a1_auth.permissions['repositories_groups'].get('test1') == 'group.admin'
+        assert a1_auth.permissions['repositories_groups'].get('test2') == 'group.admin'
 
     def test_propagated_permission_from_users_group_by_explicit_perms_exist(self):
         # make group
@@ -158,14 +138,7 @@
                                                  perm=new_perm_gr)
         # check perms
         u3_auth = AuthUser(user_id=self.u3.user_id)
-        perms = {
-            'repositories_groups': {},
-            'global': set(['hg.create.repository', 'repository.read',
-                           'hg.register.manual_activate']),
-            'repositories': {base.HG_REPO: 'repository.read'}
-        }
         assert u3_auth.permissions['repositories'][base.HG_REPO] == new_perm_gr
-        assert u3_auth.permissions['repositories_groups'] == perms['repositories_groups']
 
     def test_propagated_permission_from_users_group_lower_weight(self):
         # make group
@@ -189,24 +162,19 @@
                                                  perm=new_perm_l)
         # check perms
         u1_auth = AuthUser(user_id=self.u1.user_id)
-        perms = {
-            'repositories_groups': {},
-            'global': set(['hg.create.repository', 'repository.read',
-                           'hg.register.manual_activate']),
-            'repositories': {base.HG_REPO: 'repository.write'}
-        }
         assert u1_auth.permissions['repositories'][base.HG_REPO] == new_perm_h
-        assert u1_auth.permissions['repositories_groups'] == 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)
         # both perms should be read !
         u1_auth = AuthUser(user_id=self.u1.user_id)
-        assert u1_auth.permissions['repositories_groups'] == {'group1': 'group.read', 'group2': 'group.read'}
+        assert u1_auth.permissions['repositories_groups'].get('group1') == 'group.read'
+        assert u1_auth.permissions['repositories_groups'].get('group2') == 'group.read'
 
         a1_auth = AuthUser(user_id=self.anon.user_id)
-        assert a1_auth.permissions['repositories_groups'] == {'group1': 'group.read', 'group2': 'group.read'}
+        assert a1_auth.permissions['repositories_groups'].get('group1') == 'group.read'
+        assert a1_auth.permissions['repositories_groups'].get('group2') == 'group.read'
 
         # Change perms to none for both groups
         RepoGroupModel().grant_user_permission(repo_group=self.g1,
@@ -217,10 +185,12 @@
                                                perm='group.none')
 
         u1_auth = AuthUser(user_id=self.u1.user_id)
-        assert u1_auth.permissions['repositories_groups'] == {'group1': 'group.none', 'group2': 'group.none'}
+        assert u1_auth.permissions['repositories_groups'].get('group1') == 'group.none'
+        assert u1_auth.permissions['repositories_groups'].get('group2') == 'group.none'
 
         a1_auth = AuthUser(user_id=self.anon.user_id)
-        assert a1_auth.permissions['repositories_groups'] == {'group1': 'group.none', 'group2': 'group.none'}
+        assert a1_auth.permissions['repositories_groups'].get('group1') == 'group.none'
+        assert a1_auth.permissions['repositories_groups'].get('group2') == 'group.none'
 
         # add repo to group
         name = db.URL_SEP.join([self.g1.group_name, 'test_perm'])
@@ -230,10 +200,12 @@
                                              cur_user=self.u1,)
 
         u1_auth = AuthUser(user_id=self.u1.user_id)
-        assert u1_auth.permissions['repositories_groups'] == {'group1': 'group.none', 'group2': 'group.none'}
+        assert u1_auth.permissions['repositories_groups'].get('group1') == 'group.none'
+        assert u1_auth.permissions['repositories_groups'].get('group2') == 'group.none'
 
         a1_auth = AuthUser(user_id=self.anon.user_id)
-        assert a1_auth.permissions['repositories_groups'] == {'group1': 'group.none', 'group2': 'group.none'}
+        assert a1_auth.permissions['repositories_groups'].get('group1') == 'group.none'
+        assert a1_auth.permissions['repositories_groups'].get('group2') == 'group.none'
 
         # grant permission for u2 !
         RepoGroupModel().grant_user_permission(repo_group=self.g1, user=self.u2,
@@ -244,20 +216,23 @@
         assert self.u1 != self.u2
         # u1 and anon should have not change perms while u2 should !
         u1_auth = AuthUser(user_id=self.u1.user_id)
-        assert u1_auth.permissions['repositories_groups'] == {'group1': 'group.none', 'group2': 'group.none'}
+        assert u1_auth.permissions['repositories_groups'].get('group1') == 'group.none'
+        assert u1_auth.permissions['repositories_groups'].get('group2') == 'group.none'
 
         u2_auth = AuthUser(user_id=self.u2.user_id)
-        assert u2_auth.permissions['repositories_groups'] == {'group1': 'group.read', 'group2': 'group.read'}
+        assert u2_auth.permissions['repositories_groups'].get('group1') == 'group.read'
+        assert u2_auth.permissions['repositories_groups'].get('group2') == 'group.read'
 
         a1_auth = AuthUser(user_id=self.anon.user_id)
-        assert a1_auth.permissions['repositories_groups'] == {'group1': 'group.none', 'group2': 'group.none'}
+        assert a1_auth.permissions['repositories_groups'].get('group1') == 'group.none'
+        assert a1_auth.permissions['repositories_groups'].get('group2') == 'group.none'
 
     def test_repo_group_user_as_user_group_member(self):
         # create Group1
         self.g1 = fixture.create_repo_group('group1', skip_if_exists=True)
         a1_auth = AuthUser(user_id=self.anon.user_id)
 
-        assert a1_auth.permissions['repositories_groups'] == {'group1': 'group.read'}
+        assert a1_auth.permissions['repositories_groups'].get('group1') == 'group.read'
 
         # set default permission to none
         RepoGroupModel().grant_user_permission(repo_group=self.g1,
@@ -276,10 +251,10 @@
 
         # check his permissions
         a1_auth = AuthUser(user_id=self.anon.user_id)
-        assert a1_auth.permissions['repositories_groups'] == {'group1': 'group.none'}
+        assert a1_auth.permissions['repositories_groups'].get('group1') == 'group.none'
 
         u1_auth = AuthUser(user_id=self.u1.user_id)
-        assert u1_auth.permissions['repositories_groups'] == {'group1': 'group.none'}
+        assert u1_auth.permissions['repositories_groups'].get('group1') == 'group.none'
 
         # grant ug1 read permissions for
         RepoGroupModel().grant_user_group_permission(repo_group=self.g1,
@@ -295,10 +270,10 @@
 
         a1_auth = AuthUser(user_id=self.anon.user_id)
 
-        assert a1_auth.permissions['repositories_groups'] == {'group1': 'group.none'}
+        assert a1_auth.permissions['repositories_groups'].get('group1') == 'group.none'
 
         u1_auth = AuthUser(user_id=self.u1.user_id)
-        assert u1_auth.permissions['repositories_groups'] == {'group1': 'group.read'}
+        assert u1_auth.permissions['repositories_groups'].get('group1') == 'group.read'
 
     def test_inherit_nice_permissions_from_default_user(self):
         user_model = UserModel()
@@ -516,7 +491,7 @@
                                                perm='group.write')
         Session().commit()
         u1_auth = AuthUser(user_id=self.u1.user_id)
-        assert u1_auth.permissions['repositories_groups'] == {'group1': 'group.write'}
+        assert u1_auth.permissions['repositories_groups'].get('group1') == 'group.write'
 
     def test_inactive_user_group_does_not_affect_repo_group_permissions_inverse(self):
         self.ug1 = fixture.create_user_group('G1')
@@ -536,7 +511,7 @@
                                                perm='group.admin')
         Session().commit()
         u1_auth = AuthUser(user_id=self.u1.user_id)
-        assert u1_auth.permissions['repositories_groups'] == {'group1': 'group.admin'}
+        assert u1_auth.permissions['repositories_groups'].get('group1') == 'group.admin'
 
     def test_inactive_user_group_does_not_affect_user_group_permissions(self):
         self.ug1 = fixture.create_user_group('G1')
--- a/kallithea/tests/models/test_repo_groups.py	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/tests/models/test_repo_groups.py	Mon May 04 19:12:40 2020 +0200
@@ -63,7 +63,7 @@
 
         assert self.__check_path('newGroup')
 
-    def test_create_same_name_group(self):
+        # test_create_same_name_group
         with pytest.raises(IntegrityError):
             fixture.create_repo_group('newGroup')
         Session().rollback()
--- a/kallithea/tests/other/test_auth_ldap.py	Mon May 04 19:08:30 2020 +0200
+++ b/kallithea/tests/other/test_auth_ldap.py	Mon May 04 19:12:40 2020 +0200
@@ -24,7 +24,7 @@
     def authenticate_ldap(self, username, password):
         return 'spam dn', dict(test_ldap_firstname=['spam ldap first name'],
                                test_ldap_lastname=['spam ldap last name'],
-                               test_ldap_email=['spam ldap email'])
+                               test_ldap_email=['%s ldap email' % username])
 
 
 def test_update_user_attributes_from_ldap(monkeypatch, create_test_user,
@@ -56,13 +56,13 @@
     assert user_data is not None
     assert user_data.get('firstname') == 'spam ldap first name'
     assert user_data.get('lastname') == 'spam ldap last name'
-    assert user_data.get('email') == 'spam ldap email'
+    assert user_data.get('email') == '%s ldap email' % username
 
     # Verify that authentication overwrote user attributes with the ones
     # retrieved from LDAP.
     assert user.firstname == 'spam ldap first name'
     assert user.lastname == 'spam ldap last name'
-    assert user.email == 'spam ldap email'
+    assert user.email == '%s ldap email' % username
 
 
 def test_init_user_attributes_from_ldap(monkeypatch, arrange_ldap_auth):
@@ -85,7 +85,7 @@
     assert user_data is not None
     assert user_data.get('firstname') == 'spam ldap first name'
     assert user_data.get('lastname') == 'spam ldap last name'
-    assert user_data.get('email') == 'spam ldap email'
+    assert user_data.get('email') == '%s ldap email' % username
 
     # Verify that authentication created new user with attributes
     # retrieved from LDAP.
@@ -93,7 +93,7 @@
     assert new_user is not None
     assert new_user.firstname == 'spam ldap first name'
     assert new_user.lastname == 'spam ldap last name'
-    assert new_user.email == 'spam ldap email'
+    assert new_user.email == '%s ldap email' % username
 
 
 class _AuthLdapNoEmailMock():