diff --git a/assets/go-licenses.json b/assets/go-licenses.json
index ee1e503f2a..159f335a17 100644
--- a/assets/go-licenses.json
+++ b/assets/go-licenses.json
@@ -89,11 +89,6 @@
     "path": "github.com/DataDog/zstd/LICENSE",
     "licenseText": "Simplified BSD License\n\nCopyright (c) 2016, Datadog \u003cinfo@datadoghq.com\u003e\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n    * Redistributions in binary form must reproduce the above copyright notice,\n      this list of conditions and the following disclaimer in the documentation\n      and/or other materials provided with the distribution.\n    * Neither the name of the copyright holder nor the names of its contributors\n      may be used to endorse or promote products derived from this software\n      without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
   },
-  {
-    "name": "github.com/NYTimes/gziphandler",
-    "path": "github.com/NYTimes/gziphandler/LICENSE",
-    "licenseText": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2016-2017 The New York Times Company\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
-  },
   {
     "name": "github.com/ProtonMail/go-crypto",
     "path": "github.com/ProtonMail/go-crypto/LICENSE",
@@ -669,6 +664,11 @@
     "path": "github.com/klauspost/compress/LICENSE",
     "licenseText": "Copyright (c) 2012 The Go Authors. All rights reserved.\nCopyright (c) 2019 Klaus Post. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n------------------\n\nFiles: gzhttp/*\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2016-2017 The New York Times Company\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n------------------\n\nFiles: s2/cmd/internal/readahead/*\n\nThe MIT License (MIT)\n\nCopyright (c) 2015 Klaus Post\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n---------------------\nFiles: snappy/*\nFiles: internal/snapref/*\n\nCopyright (c) 2011 The Snappy-Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n-----------------\n\nFiles: s2/cmd/internal/filepathx/*\n\nCopyright 2016 The filepathx Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
   },
+  {
+    "name": "github.com/klauspost/compress/gzhttp",
+    "path": "github.com/klauspost/compress/gzhttp/LICENSE",
+    "licenseText": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2016-2017 The New York Times Company\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
+  },
   {
     "name": "github.com/klauspost/compress/internal/snapref",
     "path": "github.com/klauspost/compress/internal/snapref/LICENSE",
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 4c0812a13d..308c4f925c 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -409,6 +409,10 @@ USER = root
 ;;
 ;; Whether execute database models migrations automatically
 ;AUTO_MIGRATION = true
+;;
+;; Threshold value (in seconds) beyond which query execution time is logged as a warning in the xorm logger
+;;
+;SLOW_QUERY_TRESHOLD = 5s
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -808,6 +812,11 @@ LEVEL = Info
 ;; Every new user will have restricted permissions depending on this setting
 ;DEFAULT_USER_IS_RESTRICTED = false
 ;;
+;; Users will be able to use dots when choosing their username. Disabling this is
+;; helpful if your usersare having issues with e.g. RSS feeds or advanced third-party
+;; extensions that use strange regex patterns.
+; ALLOW_DOTS_IN_USERNAMES = true
+;;
 ;; Either "public", "limited" or "private", default is "public"
 ;; Limited is for users visible only to signed users
 ;; Private is for users visible only to members of their organizations
@@ -1453,6 +1462,8 @@ LEVEL = Info
 ;;
 ;; Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled
 ;DEFAULT_EMAIL_NOTIFICATIONS = enabled
+;; Send an email to all admins when a new user signs up to inform the admins about this act. Options: true, false
+;SEND_NOTIFICATION_EMAIL_ON_NEW_USER = false
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1767,9 +1778,6 @@ LEVEL = Info
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;
-;AVATAR_UPLOAD_PATH = data/avatars
-;REPOSITORY_AVATAR_UPLOAD_PATH = data/repo-avatars
-;;
 ;; How Gitea deals with missing repository avatars
 ;; none = no avatar will be displayed; random = random avatar will be displayed; image = default image will be used
 ;REPOSITORY_AVATAR_FALLBACK = none
diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index e373713fff..ec3500272b 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -454,6 +454,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
 - `MAX_IDLE_CONNS` **2**: Max idle database connections on connection pool, default is 2 - this will be capped to `MAX_OPEN_CONNS`.
 - `CONN_MAX_LIFETIME` **0 or 3s**: Sets the maximum amount of time a DB connection may be reused - default is 0, meaning there is no limit (except on MySQL where it is 3s - see #6804 & #7071).
 - `AUTO_MIGRATION` **true**: Whether execute database models migrations automatically.
+- `SLOW_QUERY_TRESHOLD` **5s**: Threshold value in seconds beyond which query execution time is logged as a warning in the xorm logger.
 
 [^1]: It may be necessary to specify a hostport even when listening on a unix socket, as the port is part of the socket name. see [#24552](https://github.com/go-gitea/gitea/issues/24552#issuecomment-1681649367) for additional details.
 
@@ -513,6 +514,7 @@ And the following unique queues:
 
 - `DEFAULT_EMAIL_NOTIFICATIONS`: **enabled**: Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled
 - `DISABLE_REGULAR_ORG_CREATION`: **false**: Disallow regular (non-admin) users from creating organizations.
+- `SEND_NOTIFICATION_EMAIL_ON_NEW_USER`: **false**: Send an email to all admins when a new user signs up to inform the admins about this act.
 
 ## Security (`security`)
 
diff --git a/go.mod b/go.mod
index a3b4656f76..00a589d31a 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,6 @@ require (
 	gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
 	github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
 	github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
-	github.com/NYTimes/gziphandler v1.1.1
 	github.com/PuerkitoBio/goquery v1.8.1
 	github.com/alecthomas/chroma/v2 v2.10.0
 	github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
@@ -36,7 +35,7 @@ require (
 	github.com/ethantkoenig/rupture v1.0.1
 	github.com/felixge/fgprof v0.9.3
 	github.com/fsnotify/fsnotify v1.6.0
-	github.com/gliderlabs/ssh v0.3.5
+	github.com/gliderlabs/ssh v0.3.6-0.20230927171611-ece6c7995e46
 	github.com/go-ap/activitypub v0.0.0-20231003111253-1fba3772399b
 	github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
 	github.com/go-chi/chi/v5 v5.0.10
@@ -78,7 +77,6 @@ require (
 	github.com/mholt/archiver/v3 v3.5.1
 	github.com/microcosm-cc/bluemonday v1.0.26
 	github.com/minio/minio-go/v7 v7.0.63
-	github.com/minio/sha256-simd v1.0.1
 	github.com/msteinert/pam v1.2.0
 	github.com/nektos/act v0.2.52
 	github.com/niklasfasching/go-org v1.7.0
@@ -101,7 +99,6 @@ require (
 	github.com/ulikunitz/xz v0.5.11
 	github.com/urfave/cli/v2 v2.25.7
 	github.com/xanzy/go-gitlab v0.93.1
-	github.com/xeipuuv/gojsonschema v1.2.0
 	github.com/yohcop/openid-go v1.0.1
 	github.com/yuin/goldmark v1.5.6
 	github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
@@ -232,6 +229,7 @@ require (
 	github.com/mholt/acmez v1.2.0 // indirect
 	github.com/miekg/dns v1.1.56 // indirect
 	github.com/minio/md5-simd v1.1.2 // indirect
+	github.com/minio/sha256-simd v1.0.1 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/mitchellh/reflectwalk v1.0.2 // indirect
@@ -277,8 +275,6 @@ require (
 	github.com/valyala/fastjson v1.6.4 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
 	github.com/xanzy/ssh-agent v0.3.3 // indirect
-	github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
-	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
 	github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
 	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
 	github.com/zeebo/blake3 v0.2.3 // indirect
diff --git a/go.sum b/go.sum
index 0e57ec5368..5f34af5b44 100644
--- a/go.sum
+++ b/go.sum
@@ -101,8 +101,6 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa
 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
 github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
 github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
-github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
-github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
 github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE=
 github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
 github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
@@ -329,8 +327,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4
 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
 github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
-github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
-github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
+github.com/gliderlabs/ssh v0.3.6-0.20230927171611-ece6c7995e46 h1:fYiA820jw7wmAvdXrHwMItxjJkra7dT9y8yiXhtzb94=
+github.com/gliderlabs/ssh v0.3.6-0.20230927171611-ece6c7995e46/go.mod h1:i/TCLcdiX9Up/vs+Rp8c3yMbqp2Y4Y7Nh9uzGFCa5pM=
 github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
 github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
 github.com/go-ap/activitypub v0.0.0-20231003111253-1fba3772399b h1:VLD6IPBDkqEsOZ+EfLO6MayuHycZ0cv4BStTlRoZduo=
@@ -1047,13 +1045,6 @@ github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3k
 github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
 github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
 github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
-github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
-github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
-github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
-github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
-github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
-github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
-github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
 github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
@@ -1237,7 +1228,6 @@ golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
 golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
@@ -1337,9 +1327,7 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1353,7 +1341,6 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
diff --git a/models/asymkey/ssh_key_authorized_keys.go b/models/asymkey/ssh_key_authorized_keys.go
index f0a3a77eaf..4c9cc1deab 100644
--- a/models/asymkey/ssh_key_authorized_keys.go
+++ b/models/asymkey/ssh_key_authorized_keys.go
@@ -169,7 +169,12 @@ func RewriteAllPublicKeys(ctx context.Context) error {
 		return err
 	}
 
-	t.Close()
+	if err := t.Sync(); err != nil {
+		return err
+	}
+	if err := t.Close(); err != nil {
+		return err
+	}
 	return util.Rename(tmpPath, fPath)
 }
 
diff --git a/models/asymkey/ssh_key_authorized_principals.go b/models/asymkey/ssh_key_authorized_principals.go
index 592196c255..79915df7b5 100644
--- a/models/asymkey/ssh_key_authorized_principals.go
+++ b/models/asymkey/ssh_key_authorized_principals.go
@@ -92,7 +92,12 @@ func RewriteAllPrincipalKeys(ctx context.Context) error {
 		return err
 	}
 
-	t.Close()
+	if err := t.Sync(); err != nil {
+		return err
+	}
+	if err := t.Close(); err != nil {
+		return err
+	}
 	return util.Rename(tmpPath, fPath)
 }
 
diff --git a/models/auth/auth_token.go b/models/auth/auth_token.go
new file mode 100644
index 0000000000..2c3ca90734
--- /dev/null
+++ b/models/auth/auth_token.go
@@ -0,0 +1,96 @@
+// Copyright 2023 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package auth
+
+import (
+	"context"
+	"crypto/sha256"
+	"encoding/hex"
+	"fmt"
+	"time"
+
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/modules/timeutil"
+	"code.gitea.io/gitea/modules/util"
+)
+
+// AuthorizationToken represents a authorization token to a user.
+type AuthorizationToken struct {
+	ID              int64  `xorm:"pk autoincr"`
+	UID             int64  `xorm:"INDEX"`
+	LookupKey       string `xorm:"INDEX UNIQUE"`
+	HashedValidator string
+	Expiry          timeutil.TimeStamp
+}
+
+// TableName provides the real table name.
+func (AuthorizationToken) TableName() string {
+	return "forgejo_auth_token"
+}
+
+func init() {
+	db.RegisterModel(new(AuthorizationToken))
+}
+
+// IsExpired returns if the authorization token is expired.
+func (authToken *AuthorizationToken) IsExpired() bool {
+	return authToken.Expiry.AsLocalTime().Before(time.Now())
+}
+
+// GenerateAuthToken generates a new authentication token for the given user.
+// It returns the lookup key and validator values that should be passed to the
+// user via a long-term cookie.
+func GenerateAuthToken(ctx context.Context, userID int64, expiry timeutil.TimeStamp) (lookupKey, validator string, err error) {
+	// Request 64 random bytes. The first 32 bytes will be used for the lookupKey
+	// and the other 32 bytes will be used for the validator.
+	rBytes, err := util.CryptoRandomBytes(64)
+	if err != nil {
+		return "", "", err
+	}
+	hexEncoded := hex.EncodeToString(rBytes)
+	validator, lookupKey = hexEncoded[64:], hexEncoded[:64]
+
+	_, err = db.GetEngine(ctx).Insert(&AuthorizationToken{
+		UID:             userID,
+		Expiry:          expiry,
+		LookupKey:       lookupKey,
+		HashedValidator: HashValidator(rBytes[32:]),
+	})
+	return lookupKey, validator, err
+}
+
+// FindAuthToken will find a authorization token via the lookup key.
+func FindAuthToken(ctx context.Context, lookupKey string) (*AuthorizationToken, error) {
+	var authToken AuthorizationToken
+	has, err := db.GetEngine(ctx).Where("lookup_key = ?", lookupKey).Get(&authToken)
+	if err != nil {
+		return nil, err
+	} else if !has {
+		return nil, fmt.Errorf("lookup key %q: %w", lookupKey, util.ErrNotExist)
+	}
+	return &authToken, nil
+}
+
+// DeleteAuthToken will delete the authorization token.
+func DeleteAuthToken(ctx context.Context, authToken *AuthorizationToken) error {
+	_, err := db.DeleteByBean(ctx, authToken)
+	return err
+}
+
+// DeleteAuthTokenByUser will delete all authorization tokens for the user.
+func DeleteAuthTokenByUser(ctx context.Context, userID int64) error {
+	if userID == 0 {
+		return nil
+	}
+
+	_, err := db.DeleteByBean(ctx, &AuthorizationToken{UID: userID})
+	return err
+}
+
+// HashValidator will return a hexified hashed version of the validator.
+func HashValidator(validator []byte) string {
+	h := sha256.New()
+	h.Write(validator)
+	return hex.EncodeToString(h.Sum(nil))
+}
diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go
index 9c419eff69..7a0ebe3d23 100644
--- a/models/auth/oauth2.go
+++ b/models/auth/oauth2.go
@@ -5,6 +5,7 @@ package auth
 
 import (
 	"context"
+	"crypto/sha256"
 	"encoding/base32"
 	"encoding/base64"
 	"fmt"
@@ -19,7 +20,6 @@ import (
 	"code.gitea.io/gitea/modules/util"
 
 	uuid "github.com/google/uuid"
-	"github.com/minio/sha256-simd"
 	"golang.org/x/crypto/bcrypt"
 	"xorm.io/builder"
 	"xorm.io/xorm"
diff --git a/models/auth/token_scope.go b/models/auth/token_scope.go
index fe57276700..003ca5c9ab 100644
--- a/models/auth/token_scope.go
+++ b/models/auth/token_scope.go
@@ -250,7 +250,7 @@ func (s AccessTokenScope) parse() (accessTokenScopeBitmap, error) {
 			remainingScopes = remainingScopes[i+1:]
 		}
 		singleScope := AccessTokenScope(v)
-		if singleScope == "" {
+		if singleScope == "" || singleScope == "sudo" {
 			continue
 		}
 		if singleScope == AccessTokenScopeAll {
diff --git a/models/auth/token_scope_test.go b/models/auth/token_scope_test.go
index a6097e45d7..d11c5e6a3d 100644
--- a/models/auth/token_scope_test.go
+++ b/models/auth/token_scope_test.go
@@ -20,7 +20,7 @@ func TestAccessTokenScope_Normalize(t *testing.T) {
 	tests := []scopeTestNormalize{
 		{"", "", nil},
 		{"write:misc,write:notification,read:package,write:notification,public-only", "public-only,write:misc,write:notification,read:package", nil},
-		{"all", "all", nil},
+		{"all,sudo", "all", nil},
 		{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user", "all", nil},
 		{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,public-only", "public-only,all", nil},
 	}
diff --git a/models/auth/twofactor.go b/models/auth/twofactor.go
index 51061e5205..d0c341a192 100644
--- a/models/auth/twofactor.go
+++ b/models/auth/twofactor.go
@@ -6,6 +6,7 @@ package auth
 import (
 	"context"
 	"crypto/md5"
+	"crypto/sha256"
 	"crypto/subtle"
 	"encoding/base32"
 	"encoding/base64"
@@ -18,7 +19,6 @@ import (
 	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/util"
 
-	"github.com/minio/sha256-simd"
 	"github.com/pquerna/otp/totp"
 	"golang.org/x/crypto/pbkdf2"
 )
diff --git a/models/db/engine.go b/models/db/engine.go
index b5a41f93e3..0fcd4849bf 100755
--- a/models/db/engine.go
+++ b/models/db/engine.go
@@ -11,10 +11,13 @@ import (
 	"io"
 	"reflect"
 	"strings"
+	"time"
 
+	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 
 	"xorm.io/xorm"
+	"xorm.io/xorm/contexts"
 	"xorm.io/xorm/names"
 	"xorm.io/xorm/schemas"
 
@@ -147,6 +150,13 @@ func InitEngine(ctx context.Context) error {
 	xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
 	xormEngine.SetDefaultContext(ctx)
 
+	if setting.Database.SlowQueryTreshold > 0 {
+		xormEngine.AddHook(&SlowQueryHook{
+			Treshold: setting.Database.SlowQueryTreshold,
+			Logger:   log.GetLogger("xorm"),
+		})
+	}
+
 	SetDefaultEngine(ctx, xormEngine)
 	return nil
 }
@@ -300,3 +310,21 @@ func SetLogSQL(ctx context.Context, on bool) {
 		sess.Engine().ShowSQL(on)
 	}
 }
+
+type SlowQueryHook struct {
+	Treshold time.Duration
+	Logger   log.Logger
+}
+
+var _ contexts.Hook = &SlowQueryHook{}
+
+func (SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
+	return c.Ctx, nil
+}
+
+func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error {
+	if c.ExecuteTime >= h.Treshold {
+		h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
+	}
+	return nil
+}
diff --git a/models/db/engine_test.go b/models/db/engine_test.go
index c9ae5f1542..ba922821b0 100644
--- a/models/db/engine_test.go
+++ b/models/db/engine_test.go
@@ -6,15 +6,19 @@ package db_test
 import (
 	"path/filepath"
 	"testing"
+	"time"
 
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
 	"code.gitea.io/gitea/models/unittest"
+	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/test"
 
 	_ "code.gitea.io/gitea/cmd" // for TestPrimaryKeys
 
 	"github.com/stretchr/testify/assert"
+	"xorm.io/xorm"
 )
 
 func TestDumpDatabase(t *testing.T) {
@@ -85,3 +89,37 @@ func TestPrimaryKeys(t *testing.T) {
 		}
 	}
 }
+
+func TestSlowQuery(t *testing.T) {
+	lc, cleanup := test.NewLogChecker("slow-query")
+	lc.StopMark("[Slow SQL Query]")
+	defer cleanup()
+
+	e := db.GetEngine(db.DefaultContext)
+	engine, ok := e.(*xorm.Engine)
+	assert.True(t, ok)
+
+	// It's not possible to clean this up with XORM, but it's luckily not harmful
+	// to leave around.
+	engine.AddHook(&db.SlowQueryHook{
+		Treshold: time.Second * 10,
+		Logger:   log.GetLogger("slow-query"),
+	})
+
+	// NOOP query.
+	e.Exec("SELECT 1 WHERE false;")
+
+	_, stopped := lc.Check(100 * time.Millisecond)
+	assert.False(t, stopped)
+
+	engine.AddHook(&db.SlowQueryHook{
+		Treshold: 0, // Every query should be logged.
+		Logger:   log.GetLogger("slow-query"),
+	})
+
+	// NOOP query.
+	e.Exec("SELECT 1 WHERE false;")
+
+	_, stopped = lc.Check(100 * time.Millisecond)
+	assert.True(t, stopped)
+}
diff --git a/models/fixtures/release.yml b/models/fixtures/release.yml
index 372a79509f..938f2cd7b7 100644
--- a/models/fixtures/release.yml
+++ b/models/fixtures/release.yml
@@ -150,3 +150,17 @@
   is_prerelease: false
   is_tag: false
   created_unix: 946684803
+
+- id: 12
+  repo_id: 59
+  publisher_id: 2
+  tag_name: "v1.0"
+  lower_tag_name: "v1.0"
+  target: "main"
+  title: "v1.0"
+  sha1: "d8f53dfb33f6ccf4169c34970b5e747511c18beb"
+  num_commits: 1
+  is_draft: false
+  is_prerelease: false
+  is_tag: false
+  created_unix: 946684803
diff --git a/models/fixtures/repo_unit.yml b/models/fixtures/repo_unit.yml
index c22eb8c2a2..6afef2a432 100644
--- a/models/fixtures/repo_unit.yml
+++ b/models/fixtures/repo_unit.yml
@@ -608,6 +608,38 @@
   type: 1
   created_unix: 946684810
 
+# BEGIN Forgejo [GITEA] Improve HTML title on repositories
+-
+  id: 1093
+  repo_id: 59
+  type: 1
+  created_unix: 946684810
+
+-
+  id: 1094
+  repo_id: 59
+  type: 2
+  created_unix: 946684810
+
+-
+  id: 1095
+  repo_id: 59
+  type: 3
+  created_unix: 946684810
+
+-
+  id: 1096
+  repo_id: 59
+  type: 4
+  created_unix: 946684810
+
+-
+  id: 1097
+  repo_id: 59
+  type: 5
+  created_unix: 946684810
+# END Forgejo [GITEA] Improve HTML title on repositories
+
 -
   id: 91
   repo_id: 58
diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml
index a127b6bdf9..e86dac8228 100644
--- a/models/fixtures/repository.yml
+++ b/models/fixtures/repository.yml
@@ -1467,6 +1467,7 @@
   owner_name: user27
   lower_name: repo49
   name: repo49
+  description: A wonderful repository with more than just a README.md
   default_branch: master
   num_watches: 0
   num_stars: 0
@@ -1693,3 +1694,16 @@
   size: 0
   is_fsck_enabled: true
   close_issues_via_commit_in_any_branch: false
+
+-
+  id: 59
+  owner_id: 2
+  owner_name: user2
+  lower_name: repo59
+  name: repo59
+  default_branch: master
+  is_empty: false
+  is_archived: false
+  is_private: false
+  status: 0
+  num_issues: 0
diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml
index fd51379816..79fbb981f6 100644
--- a/models/fixtures/user.yml
+++ b/models/fixtures/user.yml
@@ -66,7 +66,7 @@
   num_followers: 2
   num_following: 1
   num_stars: 2
-  num_repos: 14
+  num_repos: 15
   num_teams: 0
   num_members: 0
   visibility: 0
diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go
index 2becf1b713..58f158bd17 100644
--- a/models/forgejo_migrations/migrate.go
+++ b/models/forgejo_migrations/migrate.go
@@ -41,6 +41,8 @@ var migrations = []*Migration{
 	NewMigration("Add Forgejo Blocked Users table", forgejo_v1_20.AddForgejoBlockedUser),
 	// v1 -> v2
 	NewMigration("create the forgejo_sem_ver table", forgejo_v1_20.CreateSemVerTable),
+	// v2 -> v3
+	NewMigration("create the forgejo_auth_token table", forgejo_v1_20.CreateAuthorizationTokenTable),
 }
 
 // GetCurrentDBVersion returns the current Forgejo database version.
diff --git a/models/forgejo_migrations/v1_20/v3.go b/models/forgejo_migrations/v1_20/v3.go
new file mode 100644
index 0000000000..38c29bed03
--- /dev/null
+++ b/models/forgejo_migrations/v1_20/v3.go
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: MIT
+
+package forgejo_v1_20 //nolint:revive
+
+import (
+	"code.gitea.io/gitea/modules/timeutil"
+
+	"xorm.io/xorm"
+)
+
+type AuthorizationToken struct {
+	ID              int64  `xorm:"pk autoincr"`
+	UID             int64  `xorm:"INDEX"`
+	LookupKey       string `xorm:"INDEX UNIQUE"`
+	HashedValidator string
+	Expiry          timeutil.TimeStamp
+}
+
+func (AuthorizationToken) TableName() string {
+	return "forgejo_auth_token"
+}
+
+func CreateAuthorizationTokenTable(x *xorm.Engine) error {
+	return x.Sync(new(AuthorizationToken))
+}
diff --git a/models/issues/comment.go b/models/issues/comment.go
index e700da97de..a661233e77 100644
--- a/models/issues/comment.go
+++ b/models/issues/comment.go
@@ -342,7 +342,7 @@ func (c *Comment) AfterLoad(session *xorm.Session) {
 
 // LoadPoster loads comment poster
 func (c *Comment) LoadPoster(ctx context.Context) (err error) {
-	if c.PosterID <= 0 || c.Poster != nil {
+	if c.Poster != nil {
 		return nil
 	}
 
diff --git a/models/migrations/base/hash.go b/models/migrations/base/hash.go
index 0debec272b..00fd1efd4a 100644
--- a/models/migrations/base/hash.go
+++ b/models/migrations/base/hash.go
@@ -4,9 +4,9 @@
 package base
 
 import (
+	"crypto/sha256"
 	"encoding/hex"
 
-	"github.com/minio/sha256-simd"
 	"golang.org/x/crypto/pbkdf2"
 )
 
diff --git a/models/migrations/v1_14/v166.go b/models/migrations/v1_14/v166.go
index 78f33e8f9b..e5731582fd 100644
--- a/models/migrations/v1_14/v166.go
+++ b/models/migrations/v1_14/v166.go
@@ -4,9 +4,9 @@
 package v1_14 //nolint
 
 import (
+	"crypto/sha256"
 	"encoding/hex"
 
-	"github.com/minio/sha256-simd"
 	"golang.org/x/crypto/argon2"
 	"golang.org/x/crypto/bcrypt"
 	"golang.org/x/crypto/pbkdf2"
diff --git a/models/repo/repo.go b/models/repo/repo.go
index 5ebc7bfc24..ddcc32ddf9 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -576,9 +576,9 @@ func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML {
 	}, repo.Description)
 	if err != nil {
 		log.Error("Failed to render description for %s (ID: %d): %v", repo.Name, repo.ID, err)
-		return template.HTML(markup.Sanitize(repo.Description))
+		return template.HTML(markup.SanitizeDescription(repo.Description))
 	}
-	return template.HTML(markup.Sanitize(desc))
+	return template.HTML(markup.SanitizeDescription(desc))
 }
 
 // CloneLink represents different types of clone URLs of repository.
diff --git a/models/repo/repo_list_test.go b/models/repo/repo_list_test.go
index 8a1799aac0..a8b958109c 100644
--- a/models/repo/repo_list_test.go
+++ b/models/repo/repo_list_test.go
@@ -138,12 +138,12 @@ func getTestCases() []struct {
 		{
 			name:  "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
 			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse},
-			count: 31,
+			count: 32,
 		},
 		{
 			name:  "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
 			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse},
-			count: 36,
+			count: 37,
 		},
 		{
 			name:  "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
@@ -158,7 +158,7 @@ func getTestCases() []struct {
 		{
 			name:  "AllPublic/PublicRepositoriesOfOrganization",
 			opts:  &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse},
-			count: 31,
+			count: 32,
 		},
 		{
 			name:  "AllTemplates",
diff --git a/models/user/user.go b/models/user/user.go
index 63b95816ce..39e758a043 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -223,6 +223,12 @@ func GetAllUsers(ctx context.Context) ([]*User, error) {
 	return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).Find(&users)
 }
 
+// GetAllAdmins returns a slice of all adminusers found in DB.
+func GetAllAdmins(ctx context.Context) ([]*User, error) {
+	users := make([]*User, 0)
+	return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).And("is_admin = ?", true).Find(&users)
+}
+
 // IsLocal returns true if user login type is LoginPlain.
 func (u *User) IsLocal() bool {
 	return u.LoginType <= auth.Plain
@@ -380,6 +386,11 @@ func (u *User) SetPassword(passwd string) (err error) {
 		return nil
 	}
 
+	// Invalidate all authentication tokens for this user.
+	if err := auth.DeleteAuthTokenByUser(db.DefaultContext, u.ID); err != nil {
+		return err
+	}
+
 	if u.Salt, err = GetUserSalt(); err != nil {
 		return err
 	}
diff --git a/models/user/user_system.go b/models/user/user_system.go
index f54f4e3ffb..ecf235abc3 100644
--- a/models/user/user_system.go
+++ b/models/user/user_system.go
@@ -9,10 +9,12 @@ import (
 	"code.gitea.io/gitea/modules/structs"
 )
 
+const GhostUserID = -1
+
 // NewGhostUser creates and returns a fake user for someone has deleted their account.
 func NewGhostUser() *User {
 	return &User{
-		ID:        -1,
+		ID:        GhostUserID,
 		Name:      "Ghost",
 		LowerName: "ghost",
 	}
diff --git a/models/user/user_test.go b/models/user/user_test.go
index c0082f8927..24be6b55e7 100644
--- a/models/user/user_test.go
+++ b/models/user/user_test.go
@@ -550,3 +550,13 @@ func Test_ValidateUser(t *testing.T) {
 		assert.EqualValues(t, expected, err == nil, fmt.Sprintf("case: %+v", kase))
 	}
 }
+
+func TestGetAllAdmins(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	admins, err := user_model.GetAllAdmins(db.DefaultContext)
+	assert.NoError(t, err)
+
+	assert.Len(t, admins, 1)
+	assert.Equal(t, int64(1), admins[0].ID)
+}
diff --git a/modules/auth/password/hash/pbkdf2.go b/modules/auth/password/hash/pbkdf2.go
index 9ff6d162fc..27382fedb8 100644
--- a/modules/auth/password/hash/pbkdf2.go
+++ b/modules/auth/password/hash/pbkdf2.go
@@ -4,12 +4,12 @@
 package hash
 
 import (
+	"crypto/sha256"
 	"encoding/hex"
 	"strings"
 
 	"code.gitea.io/gitea/modules/log"
 
-	"github.com/minio/sha256-simd"
 	"golang.org/x/crypto/pbkdf2"
 )
 
diff --git a/modules/avatar/hash.go b/modules/avatar/hash.go
index 4fc28a7739..50db9c1943 100644
--- a/modules/avatar/hash.go
+++ b/modules/avatar/hash.go
@@ -4,10 +4,9 @@
 package avatar
 
 import (
+	"crypto/sha256"
 	"encoding/hex"
 	"strconv"
-
-	"github.com/minio/sha256-simd"
 )
 
 // HashAvatar will generate a unique string, which ensures that when there's a
diff --git a/modules/avatar/identicon/identicon.go b/modules/avatar/identicon/identicon.go
index 9b7a2faf05..63926d5f19 100644
--- a/modules/avatar/identicon/identicon.go
+++ b/modules/avatar/identicon/identicon.go
@@ -7,11 +7,10 @@
 package identicon
 
 import (
+	"crypto/sha256"
 	"fmt"
 	"image"
 	"image/color"
-
-	"github.com/minio/sha256-simd"
 )
 
 const minImageSize = 16
diff --git a/modules/base/tool.go b/modules/base/tool.go
index 71dcb83fb4..038138d647 100644
--- a/modules/base/tool.go
+++ b/modules/base/tool.go
@@ -6,6 +6,7 @@ package base
 import (
 	"crypto/md5"
 	"crypto/sha1"
+	"crypto/sha256"
 	"encoding/base64"
 	"encoding/hex"
 	"errors"
@@ -24,7 +25,6 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 
 	"github.com/dustin/go-humanize"
-	"github.com/minio/sha256-simd"
 )
 
 // EncodeMD5 encodes string to md5 hex value.
diff --git a/modules/context/context_cookie.go b/modules/context/context_cookie.go
index 9ce67a5298..39e3218d1b 100644
--- a/modules/context/context_cookie.go
+++ b/modules/context/context_cookie.go
@@ -4,16 +4,14 @@
 package context
 
 import (
-	"encoding/hex"
 	"net/http"
 	"strings"
 
+	auth_model "code.gitea.io/gitea/models/auth"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/setting"
-	"code.gitea.io/gitea/modules/util"
+	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/web/middleware"
-
-	"github.com/minio/sha256-simd"
-	"golang.org/x/crypto/pbkdf2"
 )
 
 const CookieNameFlash = "gitea_flash"
@@ -46,41 +44,13 @@ func (ctx *Context) GetSiteCookie(name string) string {
 	return middleware.GetSiteCookie(ctx.Req, name)
 }
 
-// GetSuperSecureCookie returns given cookie value from request header with secret string.
-func (ctx *Context) GetSuperSecureCookie(secret, name string) (string, bool) {
-	val := ctx.GetSiteCookie(name)
-	return ctx.CookieDecrypt(secret, val)
-}
-
-// CookieDecrypt returns given value from with secret string.
-func (ctx *Context) CookieDecrypt(secret, val string) (string, bool) {
-	if val == "" {
-		return "", false
-	}
-
-	text, err := hex.DecodeString(val)
+// SetLTACookie will generate a LTA token and add it as an cookie.
+func (ctx *Context) SetLTACookie(u *user_model.User) error {
+	days := 86400 * setting.LogInRememberDays
+	lookup, validator, err := auth_model.GenerateAuthToken(ctx, u.ID, timeutil.TimeStampNow().Add(int64(days)))
 	if err != nil {
-		return "", false
+		return err
 	}
-
-	key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
-	text, err = util.AESGCMDecrypt(key, text)
-	return string(text), err == nil
-}
-
-// SetSuperSecureCookie sets given cookie value to response header with secret string.
-func (ctx *Context) SetSuperSecureCookie(secret, name, value string, maxAge int) {
-	text := ctx.CookieEncrypt(secret, value)
-	ctx.SetSiteCookie(name, text, maxAge)
-}
-
-// CookieEncrypt encrypts a given value using the provided secret
-func (ctx *Context) CookieEncrypt(secret, value string) string {
-	key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
-	text, err := util.AESGCMEncrypt(key, []byte(value))
-	if err != nil {
-		panic("error encrypting cookie: " + err.Error())
-	}
-
-	return hex.EncodeToString(text)
+	ctx.SetSiteCookie(setting.CookieRememberName, lookup+":"+validator, days)
+	return nil
 }
diff --git a/modules/git/commit.go b/modules/git/commit.go
index b09be25ba0..bc22d52b45 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -509,6 +509,62 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi
 	return fileStatus, nil
 }
 
+func parseCommitRenames(renames *[][2]string, stdout io.Reader) {
+	rd := bufio.NewReader(stdout)
+	for {
+		// Skip (R || three digits || NULL byte)
+		_, err := rd.Discard(5)
+		if err != nil {
+			if err != io.EOF {
+				log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
+			}
+			return
+		}
+		oldFileName, err := rd.ReadString('\x00')
+		if err != nil {
+			if err != io.EOF {
+				log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
+			}
+			return
+		}
+		newFileName, err := rd.ReadString('\x00')
+		if err != nil {
+			if err != io.EOF {
+				log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
+			}
+			return
+		}
+		oldFileName = strings.TrimSuffix(oldFileName, "\x00")
+		newFileName = strings.TrimSuffix(newFileName, "\x00")
+		*renames = append(*renames, [2]string{oldFileName, newFileName})
+	}
+}
+
+// GetCommitFileRenames returns the renames that the commit contains.
+func GetCommitFileRenames(ctx context.Context, repoPath, commitID string) ([][2]string, error) {
+	renames := [][2]string{}
+	stdout, w := io.Pipe()
+	done := make(chan struct{})
+	go func() {
+		parseCommitRenames(&renames, stdout)
+		close(done)
+	}()
+
+	stderr := new(bytes.Buffer)
+	err := NewCommand(ctx, "show", "--name-status", "--pretty=format:", "-z", "--diff-filter=R").AddDynamicArguments(commitID).Run(&RunOpts{
+		Dir:    repoPath,
+		Stdout: w,
+		Stderr: stderr,
+	})
+	w.Close() // Close writer to exit parsing goroutine
+	if err != nil {
+		return nil, ConcatenateError(err, stderr.String())
+	}
+
+	<-done
+	return renames, nil
+}
+
 // GetFullCommitID returns full length (40) of commit ID by given short SHA in a repository.
 func GetFullCommitID(ctx context.Context, repoPath, shortID string) (string, error) {
 	commitID, _, err := NewCommand(ctx, "rev-parse").AddDynamicArguments(shortID).RunStdString(&RunOpts{Dir: repoPath})
diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go
index ac586fdf09..a095ac9844 100644
--- a/modules/git/commit_test.go
+++ b/modules/git/commit_test.go
@@ -278,3 +278,30 @@ func TestGetCommitFileStatusMerges(t *testing.T) {
 	assert.Equal(t, commitFileStatus.Removed, expected.Removed)
 	assert.Equal(t, commitFileStatus.Modified, expected.Modified)
 }
+
+func TestParseCommitRenames(t *testing.T) {
+	testcases := []struct {
+		output  string
+		renames [][2]string
+	}{
+		{
+			output:  "R090\x00renamed.txt\x00history.txt\x00",
+			renames: [][2]string{{"renamed.txt", "history.txt"}},
+		},
+		{
+			output:  "R090\x00renamed.txt\x00history.txt\x00R000\x00corruptedstdouthere",
+			renames: [][2]string{{"renamed.txt", "history.txt"}},
+		},
+		{
+			output:  "R100\x00renamed.txt\x00history.txt\x00R001\x00readme.md\x00README.md\x00",
+			renames: [][2]string{{"renamed.txt", "history.txt"}, {"readme.md", "README.md"}},
+		},
+	}
+
+	for _, testcase := range testcases {
+		renames := [][2]string{}
+		parseCommitRenames(&renames, strings.NewReader(testcase.output))
+
+		assert.Equal(t, testcase.renames, renames)
+	}
+}
diff --git a/modules/git/last_commit_cache.go b/modules/git/last_commit_cache.go
index 20bc796085..6fb910fcae 100644
--- a/modules/git/last_commit_cache.go
+++ b/modules/git/last_commit_cache.go
@@ -4,12 +4,11 @@
 package git
 
 import (
+	"crypto/sha256"
 	"fmt"
 
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
-
-	"github.com/minio/sha256-simd"
 )
 
 // Cache represents a caching interface
diff --git a/modules/lfs/content_store.go b/modules/lfs/content_store.go
index daf8c6cfdd..0d9c0c98ac 100644
--- a/modules/lfs/content_store.go
+++ b/modules/lfs/content_store.go
@@ -4,6 +4,7 @@
 package lfs
 
 import (
+	"crypto/sha256"
 	"encoding/hex"
 	"errors"
 	"hash"
@@ -12,8 +13,6 @@ import (
 
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/storage"
-
-	"github.com/minio/sha256-simd"
 )
 
 var (
diff --git a/modules/lfs/pointer.go b/modules/lfs/pointer.go
index 3e5bb8f91d..ebde20f826 100644
--- a/modules/lfs/pointer.go
+++ b/modules/lfs/pointer.go
@@ -4,6 +4,7 @@
 package lfs
 
 import (
+	"crypto/sha256"
 	"encoding/hex"
 	"errors"
 	"fmt"
@@ -12,8 +13,6 @@ import (
 	"regexp"
 	"strconv"
 	"strings"
-
-	"github.com/minio/sha256-simd"
 )
 
 const (
diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go
index 48c08831f1..992e85b989 100644
--- a/modules/markup/sanitizer.go
+++ b/modules/markup/sanitizer.go
@@ -18,9 +18,10 @@ import (
 // Sanitizer is a protection wrapper of *bluemonday.Policy which does not allow
 // any modification to the underlying policies once it's been created.
 type Sanitizer struct {
-	defaultPolicy    *bluemonday.Policy
-	rendererPolicies map[string]*bluemonday.Policy
-	init             sync.Once
+	defaultPolicy     *bluemonday.Policy
+	descriptionPolicy *bluemonday.Policy
+	rendererPolicies  map[string]*bluemonday.Policy
+	init              sync.Once
 }
 
 var (
@@ -41,6 +42,7 @@ func NewSanitizer() {
 func InitializeSanitizer() {
 	sanitizer.rendererPolicies = map[string]*bluemonday.Policy{}
 	sanitizer.defaultPolicy = createDefaultPolicy()
+	sanitizer.descriptionPolicy = createRepoDescriptionPolicy()
 
 	for name, renderer := range renderers {
 		sanitizerRules := renderer.SanitizerRules()
@@ -161,6 +163,27 @@ func createDefaultPolicy() *bluemonday.Policy {
 	return policy
 }
 
+// createRepoDescriptionPolicy returns a minimal more strict policy that is used for
+// repository descriptions.
+func createRepoDescriptionPolicy() *bluemonday.Policy {
+	policy := bluemonday.NewPolicy()
+
+	// Allow italics and bold.
+	policy.AllowElements("i", "b", "em", "strong")
+
+	// Allow code.
+	policy.AllowElements("code")
+
+	// Allow links
+	policy.AllowAttrs("href", "target", "rel").OnElements("a")
+
+	// Allow classes for emojis
+	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img", "span")
+	policy.AllowAttrs("aria-label").OnElements("span")
+
+	return policy
+}
+
 func addSanitizerRules(policy *bluemonday.Policy, rules []setting.MarkupSanitizerRule) {
 	for _, rule := range rules {
 		if rule.AllowDataURIImages {
@@ -176,6 +199,12 @@ func addSanitizerRules(policy *bluemonday.Policy, rules []setting.MarkupSanitize
 	}
 }
 
+// SanitizeDescription sanitizes the HTML generated for a repository description.
+func SanitizeDescription(s string) string {
+	NewSanitizer()
+	return sanitizer.descriptionPolicy.Sanitize(s)
+}
+
 // Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
 func Sanitize(s string) string {
 	NewSanitizer()
diff --git a/modules/markup/sanitizer_test.go b/modules/markup/sanitizer_test.go
index 0bc63ff0a7..b7b8792bd7 100644
--- a/modules/markup/sanitizer_test.go
+++ b/modules/markup/sanitizer_test.go
@@ -73,6 +73,28 @@ func Test_Sanitizer(t *testing.T) {
 	}
 }
 
+func TestDescriptionSanitizer(t *testing.T) {
+	NewSanitizer()
+
+	testCases := []string{
+		`<h1>Title</h1>`, `Title`,
+		`<img src='img.png' alt='image'>`, ``,
+		`<span class="emoji" aria-label="thumbs up">THUMBS UP</span>`, `<span class="emoji" aria-label="thumbs up">THUMBS UP</span>`,
+		`<span style="color: red">Hello World</span>`, `<span>Hello World</span>`,
+		`<br>`, ``,
+		`<a href="https://example.com" target="_blank" rel="noopener noreferrer">https://example.com</a>`, `<a href="https://example.com" target="_blank" rel="noopener noreferrer">https://example.com</a>`,
+		`<mark>Important!</mark>`, `Important!`,
+		`<details>Click me! <summary>Nothing to see here.</summary></details>`, `Click me! Nothing to see here.`,
+		`<input type="hidden">`, ``,
+		`<b>I</b> have a <i>strong</i> <strong>opinion</strong> about <em>this</em>.`, `<b>I</b> have a <i>strong</i> <strong>opinion</strong> about <em>this</em>.`,
+		`Provides alternative <code>wg(8)</code> tool`, `Provides alternative <code>wg(8)</code> tool`,
+	}
+
+	for i := 0; i < len(testCases); i += 2 {
+		assert.Equal(t, testCases[i+1], SanitizeDescription(testCases[i]))
+	}
+}
+
 func TestSanitizeNonEscape(t *testing.T) {
 	descStr := "<scrÄ°pt>&lt;script&gt;alert(document.domain)&lt;/script&gt;</scrÄ°pt>"
 
diff --git a/modules/repository/create.go b/modules/repository/create.go
index 2dac35224e..153686089c 100644
--- a/modules/repository/create.go
+++ b/modules/repository/create.go
@@ -167,7 +167,11 @@ func getDirectorySize(path string) (int64, error) {
 			}
 			return err
 		}
-		if info.IsDir() {
+
+		fileName := info.Name()
+		// Ignore temporary Git files as they will like be missing once info.Info is
+		// called and cause a disrupt to the whole operation.
+		if info.IsDir() || strings.HasSuffix(fileName, ".lock") || strings.HasPrefix(filepath.Base(fileName), "tmp_graph") {
 			return nil
 		}
 		f, err := info.Info()
diff --git a/modules/secret/secret.go b/modules/secret/secret.go
index 9c2ecd181d..e70ae1839c 100644
--- a/modules/secret/secret.go
+++ b/modules/secret/secret.go
@@ -7,13 +7,12 @@ import (
 	"crypto/aes"
 	"crypto/cipher"
 	"crypto/rand"
+	"crypto/sha256"
 	"encoding/base64"
 	"encoding/hex"
 	"errors"
 	"fmt"
 	"io"
-
-	"github.com/minio/sha256-simd"
 )
 
 // AesEncrypt encrypts text and given key with AES.
diff --git a/modules/setting/admin.go b/modules/setting/admin.go
index 2d2dd26de9..d7f0ee827d 100644
--- a/modules/setting/admin.go
+++ b/modules/setting/admin.go
@@ -5,8 +5,9 @@ package setting
 
 // Admin settings
 var Admin struct {
-	DisableRegularOrgCreation bool
-	DefaultEmailNotification  string
+	DisableRegularOrgCreation      bool
+	DefaultEmailNotification       string
+	SendNotificationEmailOnNewUser bool
 }
 
 func loadAdminFrom(rootCfg ConfigProvider) {
diff --git a/modules/setting/database.go b/modules/setting/database.go
index aa42f506bc..6ece1e7c06 100644
--- a/modules/setting/database.go
+++ b/modules/setting/database.go
@@ -45,6 +45,7 @@ var (
 		ConnMaxLifetime   time.Duration
 		IterateBufferSize int
 		AutoMigration     bool
+		SlowQueryTreshold time.Duration
 	}{
 		Timeout:           500,
 		IterateBufferSize: 50,
@@ -87,6 +88,7 @@ func loadDBSetting(rootCfg ConfigProvider) {
 	Database.DBConnectRetries = sec.Key("DB_RETRIES").MustInt(10)
 	Database.DBConnectBackoff = sec.Key("DB_RETRY_BACKOFF").MustDuration(3 * time.Second)
 	Database.AutoMigration = sec.Key("AUTO_MIGRATION").MustBool(true)
+	Database.SlowQueryTreshold = sec.Key("SLOW_QUERY_TRESHOLD").MustDuration(5 * time.Second)
 }
 
 // DBConnStr returns database connection string
diff --git a/modules/setting/security.go b/modules/setting/security.go
index 90f614d4cd..92caa05fad 100644
--- a/modules/setting/security.go
+++ b/modules/setting/security.go
@@ -19,7 +19,6 @@ var (
 	SecretKey                          string
 	InternalToken                      string // internal access token
 	LogInRememberDays                  int
-	CookieUserName                     string
 	CookieRememberName                 string
 	ReverseProxyAuthUser               string
 	ReverseProxyAuthEmail              string
@@ -104,7 +103,6 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
 	sec := rootCfg.Section("security")
 	InstallLock = HasInstallLock(rootCfg)
 	LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
-	CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
 	SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY")
 	if SecretKey == "" {
 		// FIXME: https://github.com/go-gitea/gitea/issues/16832
diff --git a/modules/setting/service.go b/modules/setting/service.go
index befb94b61b..afaee18101 100644
--- a/modules/setting/service.go
+++ b/modules/setting/service.go
@@ -68,6 +68,7 @@ var Service = struct {
 	DefaultKeepEmailPrivate                 bool
 	DefaultAllowCreateOrganization          bool
 	DefaultUserIsRestricted                 bool
+	AllowDotsInUsernames                    bool
 	EnableTimetracking                      bool
 	DefaultEnableTimetracking               bool
 	DefaultEnableDependencies               bool
@@ -180,6 +181,7 @@ func loadServiceFrom(rootCfg ConfigProvider) {
 	Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool()
 	Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true)
 	Service.DefaultUserIsRestricted = sec.Key("DEFAULT_USER_IS_RESTRICTED").MustBool(false)
+	Service.AllowDotsInUsernames = sec.Key("ALLOW_DOTS_IN_USERNAMES").MustBool(true)
 	Service.EnableTimetracking = sec.Key("ENABLE_TIMETRACKING").MustBool(true)
 	if Service.EnableTimetracking {
 		Service.DefaultEnableTimetracking = sec.Key("DEFAULT_ENABLE_TIMETRACKING").MustBool(true)
diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go
index 37624ab679..f8e4f569b8 100644
--- a/modules/ssh/ssh.go
+++ b/modules/ssh/ssh.go
@@ -17,7 +17,6 @@ import (
 	"os"
 	"os/exec"
 	"path/filepath"
-	"reflect"
 	"strconv"
 	"strings"
 	"sync"
@@ -165,10 +164,6 @@ func sessionHandler(session ssh.Session) {
 }
 
 func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
-	// FIXME: the "ssh.Context" is not thread-safe, so db operations should use the immutable parent "Context"
-	// TODO: Remove after https://github.com/gliderlabs/ssh/pull/211
-	parentCtx := reflect.ValueOf(ctx).Elem().FieldByName("Context").Interface().(context.Context)
-
 	if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary
 		log.Debug("Handle Public Key: Fingerprint: %s from %s", gossh.FingerprintSHA256(key), ctx.RemoteAddr())
 	}
@@ -200,7 +195,7 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
 		// look for the exact principal
 	principalLoop:
 		for _, principal := range cert.ValidPrincipals {
-			pkey, err := asymkey_model.SearchPublicKeyByContentExact(parentCtx, principal)
+			pkey, err := asymkey_model.SearchPublicKeyByContentExact(ctx, principal)
 			if err != nil {
 				if asymkey_model.IsErrKeyNotExist(err) {
 					log.Debug("Principal Rejected: %s Unknown Principal: %s", ctx.RemoteAddr(), principal)
@@ -257,7 +252,7 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
 		log.Debug("Handle Public Key: %s Fingerprint: %s is not a certificate", ctx.RemoteAddr(), gossh.FingerprintSHA256(key))
 	}
 
-	pkey, err := asymkey_model.SearchPublicKeyByContent(parentCtx, strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key))))
+	pkey, err := asymkey_model.SearchPublicKeyByContent(ctx, strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key))))
 	if err != nil {
 		if asymkey_model.IsErrKeyNotExist(err) {
 			log.Warn("Unknown public key: %s from %s", gossh.FingerprintSHA256(key), ctx.RemoteAddr())
diff --git a/modules/util/keypair.go b/modules/util/keypair.go
index 97f2d9ebca..8b86c142af 100644
--- a/modules/util/keypair.go
+++ b/modules/util/keypair.go
@@ -7,10 +7,9 @@ import (
 	"crypto"
 	"crypto/rand"
 	"crypto/rsa"
+	"crypto/sha256"
 	"crypto/x509"
 	"encoding/pem"
-
-	"github.com/minio/sha256-simd"
 )
 
 // GenerateKeyPair generates a public and private keypair
diff --git a/modules/util/keypair_test.go b/modules/util/keypair_test.go
index c9925f7988..c6f68c845a 100644
--- a/modules/util/keypair_test.go
+++ b/modules/util/keypair_test.go
@@ -7,12 +7,12 @@ import (
 	"crypto"
 	"crypto/rand"
 	"crypto/rsa"
+	"crypto/sha256"
 	"crypto/x509"
 	"encoding/pem"
 	"regexp"
 	"testing"
 
-	"github.com/minio/sha256-simd"
 	"github.com/stretchr/testify/assert"
 )
 
diff --git a/modules/util/legacy.go b/modules/util/legacy.go
index 2ea293a2be..2d4de01949 100644
--- a/modules/util/legacy.go
+++ b/modules/util/legacy.go
@@ -4,10 +4,6 @@
 package util
 
 import (
-	"crypto/aes"
-	"crypto/cipher"
-	"crypto/rand"
-	"errors"
 	"io"
 	"os"
 )
@@ -40,52 +36,3 @@ func CopyFile(src, dest string) error {
 	}
 	return os.Chmod(dest, si.Mode())
 }
-
-// AESGCMEncrypt (from legacy package): encrypts plaintext with the given key using AES in GCM mode. should be replaced.
-func AESGCMEncrypt(key, plaintext []byte) ([]byte, error) {
-	block, err := aes.NewCipher(key)
-	if err != nil {
-		return nil, err
-	}
-
-	gcm, err := cipher.NewGCM(block)
-	if err != nil {
-		return nil, err
-	}
-
-	nonce := make([]byte, gcm.NonceSize())
-	if _, err := rand.Read(nonce); err != nil {
-		return nil, err
-	}
-
-	ciphertext := gcm.Seal(nil, nonce, plaintext, nil)
-	return append(nonce, ciphertext...), nil
-}
-
-// AESGCMDecrypt (from legacy package): decrypts ciphertext with the given key using AES in GCM mode. should be replaced.
-func AESGCMDecrypt(key, ciphertext []byte) ([]byte, error) {
-	block, err := aes.NewCipher(key)
-	if err != nil {
-		return nil, err
-	}
-
-	gcm, err := cipher.NewGCM(block)
-	if err != nil {
-		return nil, err
-	}
-
-	size := gcm.NonceSize()
-	if len(ciphertext)-size <= 0 {
-		return nil, errors.New("ciphertext is empty")
-	}
-
-	nonce := ciphertext[:size]
-	ciphertext = ciphertext[size:]
-
-	plainText, err := gcm.Open(nil, nonce, ciphertext, nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return plainText, nil
-}
diff --git a/modules/util/legacy_test.go b/modules/util/legacy_test.go
index e732094c29..b7991bd365 100644
--- a/modules/util/legacy_test.go
+++ b/modules/util/legacy_test.go
@@ -4,8 +4,6 @@
 package util
 
 import (
-	"crypto/aes"
-	"crypto/rand"
 	"fmt"
 	"os"
 	"testing"
@@ -37,21 +35,3 @@ func TestCopyFile(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, testContent, dstContent)
 }
-
-func TestAESGCM(t *testing.T) {
-	t.Parallel()
-
-	key := make([]byte, aes.BlockSize)
-	_, err := rand.Read(key)
-	assert.NoError(t, err)
-
-	plaintext := []byte("this will be encrypted")
-
-	ciphertext, err := AESGCMEncrypt(key, plaintext)
-	assert.NoError(t, err)
-
-	decrypted, err := AESGCMDecrypt(key, ciphertext)
-	assert.NoError(t, err)
-
-	assert.Equal(t, plaintext, decrypted)
-}
diff --git a/modules/validation/helpers.go b/modules/validation/helpers.go
index f6e00f3887..567ad867fe 100644
--- a/modules/validation/helpers.go
+++ b/modules/validation/helpers.go
@@ -117,13 +117,20 @@ func IsValidExternalTrackerURLFormat(uri string) bool {
 }
 
 var (
-	validUsernamePattern   = regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`)
-	invalidUsernamePattern = regexp.MustCompile(`[-._]{2,}|[-._]$`) // No consecutive or trailing non-alphanumeric chars
+	validUsernamePatternWithDots    = regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`)
+	validUsernamePatternWithoutDots = regexp.MustCompile(`^[\da-zA-Z][-\w]*$`)
+
+	// No consecutive or trailing non-alphanumeric chars, catches both cases
+	invalidUsernamePattern = regexp.MustCompile(`[-._]{2,}|[-._]$`)
 )
 
 // IsValidUsername checks if username is valid
 func IsValidUsername(name string) bool {
 	// It is difficult to find a single pattern that is both readable and effective,
 	// but it's easier to use positive and negative checks.
-	return validUsernamePattern.MatchString(name) && !invalidUsernamePattern.MatchString(name)
+	if setting.Service.AllowDotsInUsernames {
+		return validUsernamePatternWithDots.MatchString(name) && !invalidUsernamePattern.MatchString(name)
+	}
+
+	return validUsernamePatternWithoutDots.MatchString(name) && !invalidUsernamePattern.MatchString(name)
 }
diff --git a/modules/validation/helpers_test.go b/modules/validation/helpers_test.go
index 52f383f698..a1bdf2a29c 100644
--- a/modules/validation/helpers_test.go
+++ b/modules/validation/helpers_test.go
@@ -155,7 +155,8 @@ func Test_IsValidExternalTrackerURLFormat(t *testing.T) {
 	}
 }
 
-func TestIsValidUsername(t *testing.T) {
+func TestIsValidUsernameAllowDots(t *testing.T) {
+	setting.Service.AllowDotsInUsernames = true
 	tests := []struct {
 		arg  string
 		want bool
@@ -185,3 +186,31 @@ func TestIsValidUsername(t *testing.T) {
 		})
 	}
 }
+
+func TestIsValidUsernameBanDots(t *testing.T) {
+	setting.Service.AllowDotsInUsernames = false
+	defer func() {
+		setting.Service.AllowDotsInUsernames = true
+	}()
+
+	tests := []struct {
+		arg  string
+		want bool
+	}{
+		{arg: "a", want: true},
+		{arg: "abc", want: true},
+		{arg: "0.b-c", want: false},
+		{arg: "a.b-c_d", want: false},
+		{arg: ".abc", want: false},
+		{arg: "abc.", want: false},
+		{arg: "a..bc", want: false},
+		{arg: "a...bc", want: false},
+		{arg: "a.-bc", want: false},
+		{arg: "a._bc", want: false},
+	}
+	for _, tt := range tests {
+		t.Run(tt.arg, func(t *testing.T) {
+			assert.Equalf(t, tt.want, IsValidUsername(tt.arg), "IsValidUsername[AllowDotsInUsernames=false](%v)", tt.arg)
+		})
+	}
+}
diff --git a/modules/web/handler.go b/modules/web/handler.go
index 26b7428016..728cc5a160 100644
--- a/modules/web/handler.go
+++ b/modules/web/handler.go
@@ -147,6 +147,16 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
 		}
 	}
 
+	if hp, ok := handler.(func(next http.Handler) http.HandlerFunc); ok {
+		return func(next http.Handler) http.Handler {
+			h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
+			return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
+				routing.UpdateFuncInfo(req.Context(), funcInfo)
+				h.ServeHTTP(resp, req)
+			})
+		}
+	}
+
 	provider := func(next http.Handler) http.Handler {
 		return http.HandlerFunc(func(respOrig http.ResponseWriter, req *http.Request) {
 			// wrap the response writer to check whether the response has been written
diff --git a/modules/web/middleware/binding.go b/modules/web/middleware/binding.go
index d9bcdf3b2a..4e7fca80e2 100644
--- a/modules/web/middleware/binding.go
+++ b/modules/web/middleware/binding.go
@@ -8,6 +8,7 @@ import (
 	"reflect"
 	"strings"
 
+	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/translation"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/validation"
@@ -135,7 +136,11 @@ func Validate(errs binding.Errors, data map[string]any, f Form, l translation.Lo
 			case validation.ErrRegexPattern:
 				data["ErrorMsg"] = trName + l.Tr("form.regex_pattern_error", errs[0].Message)
 			case validation.ErrUsername:
-				data["ErrorMsg"] = trName + l.Tr("form.username_error")
+				if setting.Service.AllowDotsInUsernames {
+					data["ErrorMsg"] = trName + l.Tr("form.username_error")
+				} else {
+					data["ErrorMsg"] = trName + l.Tr("form.username_error_no_dots")
+				}
 			case validation.ErrInvalidGroupTeamMap:
 				data["ErrorMsg"] = trName + l.Tr("form.invalid_group_team_map_error", errs[0].Message)
 			default:
diff --git a/options/locales/gitea_en-US.ini b/options/locales/gitea_en-US.ini
index 4aaf9cc2c3..bbba16f2e7 100644
--- a/options/locales/gitea_en-US.ini
+++ b/options/locales/gitea_en-US.ini
@@ -295,6 +295,7 @@ default_allow_create_organization = Allow Creation of Organizations by Default
 default_allow_create_organization_popup = Allow new user accounts to create organizations by default.
 default_enable_timetracking = Enable Time Tracking by Default
 default_enable_timetracking_popup = Enable time tracking for new repositories by default.
+allow_dots_in_usernames = Allow users to use dots in their usernames. Doesn't affect existing accounts.
 no_reply_address = Hidden Email Domain
 no_reply_address_helper = Domain name for users with a hidden email address. For example, the username 'joe' will be logged in Git as 'joe@noreply.example.org' if the hidden email domain is set to 'noreply.example.org'.
 password_algorithm = Password Hash Algorithm
@@ -439,6 +440,10 @@ activate_email = Verify your email address
 activate_email.title = %s, please verify your email address
 activate_email.text = Please click the following link to verify your email address within <b>%s</b>:
 
+admin.new_user.subject = New user %s just signed up
+admin.new_user.user_info = User Information
+admin.new_user.text = Please <a href="%s">click here</a> to manage the user from the admin panel.
+
 register_notify = Welcome to Gitea
 register_notify.title = %[1]s, welcome to %[2]s
 register_notify.text_1 = this is your registration confirmation email for %s!
@@ -533,6 +538,7 @@ include_error = ` must contain substring "%s".`
 glob_pattern_error = ` glob pattern is invalid: %s.`
 regex_pattern_error = ` regex pattern is invalid: %s.`
 username_error = ` can only contain alphanumeric chars ('0-9','a-z','A-Z'), dash ('-'), underscore ('_') and dot ('.'). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.`
+username_error_no_dots = ` can only contain alphanumeric chars ('0-9','a-z','A-Z'), dash ('-') and underscore ('_'). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.`
 invalid_group_team_map_error = ` mapping is invalid: %s`
 unknown_error = Unknown error:
 captcha_incorrect = The CAPTCHA code is incorrect.
@@ -1286,6 +1292,8 @@ commits.find = Search
 commits.search_all = All Branches
 commits.author = Author
 commits.message = Message
+commits.browse_further = Browse further
+commits.renamed_from = Renamed from %s
 commits.date = Date
 commits.older = Older
 commits.newer = Newer
diff --git a/routers/api/packages/chef/auth.go b/routers/api/packages/chef/auth.go
index 3aef8281a4..a790e9a363 100644
--- a/routers/api/packages/chef/auth.go
+++ b/routers/api/packages/chef/auth.go
@@ -8,6 +8,7 @@ import (
 	"crypto"
 	"crypto/rsa"
 	"crypto/sha1"
+	"crypto/sha256"
 	"crypto/x509"
 	"encoding/base64"
 	"encoding/pem"
@@ -26,8 +27,6 @@ import (
 	chef_module "code.gitea.io/gitea/modules/packages/chef"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/services/auth"
-
-	"github.com/minio/sha256-simd"
 )
 
 const (
diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go
index 52e31e8033..6cd5804645 100644
--- a/routers/api/packages/maven/maven.go
+++ b/routers/api/packages/maven/maven.go
@@ -6,6 +6,7 @@ package maven
 import (
 	"crypto/md5"
 	"crypto/sha1"
+	"crypto/sha256"
 	"crypto/sha512"
 	"encoding/hex"
 	"encoding/xml"
@@ -26,8 +27,6 @@ import (
 	maven_module "code.gitea.io/gitea/modules/packages/maven"
 	"code.gitea.io/gitea/routers/api/packages/helper"
 	packages_service "code.gitea.io/gitea/services/packages"
-
-	"github.com/minio/sha256-simd"
 )
 
 const (
diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go
index 47b95eed1b..67651062c7 100644
--- a/routers/api/v1/user/user.go
+++ b/routers/api/v1/user/user.go
@@ -55,19 +55,33 @@ func Search(ctx *context.APIContext) {
 
 	listOptions := utils.GetListOptions(ctx)
 
-	users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
-		Actor:       ctx.Doer,
-		Keyword:     ctx.FormTrim("q"),
-		UID:         ctx.FormInt64("uid"),
-		Type:        user_model.UserTypeIndividual,
-		ListOptions: listOptions,
-	})
-	if err != nil {
-		ctx.JSON(http.StatusInternalServerError, map[string]any{
-			"ok":    false,
-			"error": err.Error(),
+	uid := ctx.FormInt64("uid")
+	var users []*user_model.User
+	var maxResults int64
+	var err error
+
+	switch uid {
+	case user_model.GhostUserID:
+		maxResults = 1
+		users = []*user_model.User{user_model.NewGhostUser()}
+	case user_model.ActionsUserID:
+		maxResults = 1
+		users = []*user_model.User{user_model.NewActionsUser()}
+	default:
+		users, maxResults, err = user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
+			Actor:       ctx.Doer,
+			Keyword:     ctx.FormTrim("q"),
+			UID:         uid,
+			Type:        user_model.UserTypeIndividual,
+			ListOptions: listOptions,
 		})
-		return
+		if err != nil {
+			ctx.JSON(http.StatusInternalServerError, map[string]any{
+				"ok":    false,
+				"error": err.Error(),
+			})
+			return
+		}
 	}
 
 	ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
diff --git a/routers/install/install.go b/routers/install/install.go
index 185e4bf6bf..cb7818bd33 100644
--- a/routers/install/install.go
+++ b/routers/install/install.go
@@ -358,6 +358,12 @@ func SubmitInstall(ctx *context.Context) {
 			ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplInstall, form)
 			return
 		}
+		if len(form.AdminPasswd) < setting.MinPasswordLength {
+			ctx.Data["Err_Admin"] = true
+			ctx.Data["Err_AdminPasswd"] = true
+			ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplInstall, form)
+			return
+		}
 	}
 
 	// Init the engine with migration
@@ -547,18 +553,13 @@ func SubmitInstall(ctx *context.Context) {
 			u, _ = user_model.GetUserByName(ctx, u.Name)
 		}
 
-		days := 86400 * setting.LogInRememberDays
-		ctx.SetSiteCookie(setting.CookieUserName, u.Name, days)
-
-		ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd),
-			setting.CookieRememberName, u.Name, days)
-
-		// Auto-login for admin
-		if err = ctx.Session.Set("uid", u.ID); err != nil {
+		if err := ctx.SetLTACookie(u); err != nil {
 			ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form)
 			return
 		}
-		if err = ctx.Session.Set("uname", u.Name); err != nil {
+
+		// Auto-login for admin
+		if err = ctx.Session.Set("uid", u.ID); err != nil {
 			ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form)
 			return
 		}
diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go
index 8017602d99..884c6fe266 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -5,6 +5,8 @@
 package auth
 
 import (
+	"crypto/subtle"
+	"encoding/hex"
 	"errors"
 	"fmt"
 	"net/http"
@@ -30,6 +32,7 @@ import (
 	"code.gitea.io/gitea/services/externalaccount"
 	"code.gitea.io/gitea/services/forms"
 	"code.gitea.io/gitea/services/mailer"
+	notify_service "code.gitea.io/gitea/services/notify"
 
 	"github.com/markbates/goth"
 )
@@ -49,21 +52,47 @@ func AutoSignIn(ctx *context.Context) (bool, error) {
 		return false, nil
 	}
 
-	uname := ctx.GetSiteCookie(setting.CookieUserName)
-	if len(uname) == 0 {
+	authCookie := ctx.GetSiteCookie(setting.CookieRememberName)
+	if len(authCookie) == 0 {
 		return false, nil
 	}
 
 	isSucceed := false
 	defer func() {
 		if !isSucceed {
-			log.Trace("auto-login cookie cleared: %s", uname)
-			ctx.DeleteSiteCookie(setting.CookieUserName)
+			log.Trace("Auto login cookie is cleared: %s", authCookie)
 			ctx.DeleteSiteCookie(setting.CookieRememberName)
 		}
 	}()
 
-	u, err := user_model.GetUserByName(ctx, uname)
+	lookupKey, validator, found := strings.Cut(authCookie, ":")
+	if !found {
+		return false, nil
+	}
+
+	authToken, err := auth.FindAuthToken(ctx, lookupKey)
+	if err != nil {
+		if errors.Is(err, util.ErrNotExist) {
+			return false, nil
+		}
+		return false, err
+	}
+
+	if authToken.IsExpired() {
+		err = auth.DeleteAuthToken(ctx, authToken)
+		return false, err
+	}
+
+	rawValidator, err := hex.DecodeString(validator)
+	if err != nil {
+		return false, err
+	}
+
+	if subtle.ConstantTimeCompare([]byte(authToken.HashedValidator), []byte(auth.HashValidator(rawValidator))) == 0 {
+		return false, nil
+	}
+
+	u, err := user_model.GetUserByID(ctx, authToken.UID)
 	if err != nil {
 		if !user_model.IsErrUserNotExist(err) {
 			return false, fmt.Errorf("GetUserByName: %w", err)
@@ -71,17 +100,11 @@ func AutoSignIn(ctx *context.Context) (bool, error) {
 		return false, nil
 	}
 
-	if val, ok := ctx.GetSuperSecureCookie(
-		base.EncodeMD5(u.Rands+u.Passwd), setting.CookieRememberName); !ok || val != u.Name {
-		return false, nil
-	}
-
 	isSucceed = true
 
 	if err := updateSession(ctx, nil, map[string]any{
 		// Set session IDs
-		"uid":   u.ID,
-		"uname": u.Name,
+		"uid": authToken.UID,
 	}); err != nil {
 		return false, fmt.Errorf("unable to updateSession: %w", err)
 	}
@@ -290,10 +313,10 @@ func handleSignIn(ctx *context.Context, u *user_model.User, remember bool) {
 
 func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRedirect bool) string {
 	if remember {
-		days := 86400 * setting.LogInRememberDays
-		ctx.SetSiteCookie(setting.CookieUserName, u.Name, days)
-		ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd),
-			setting.CookieRememberName, u.Name, days)
+		if err := ctx.SetLTACookie(u); err != nil {
+			ctx.ServerError("GenerateAuthToken", err)
+			return setting.AppSubURL + "/"
+		}
 	}
 
 	if err := updateSession(ctx, []string{
@@ -306,8 +329,7 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
 		"twofaRemember",
 		"linkAccount",
 	}, map[string]any{
-		"uid":   u.ID,
-		"uname": u.Name,
+		"uid": u.ID,
 	}); err != nil {
 		ctx.ServerError("RegenerateSession", err)
 		return setting.AppSubURL + "/"
@@ -368,7 +390,6 @@ func getUserName(gothUser *goth.User) string {
 func HandleSignOut(ctx *context.Context) {
 	_ = ctx.Session.Flush()
 	_ = ctx.Session.Destroy(ctx.Resp, ctx.Req)
-	ctx.DeleteSiteCookie(setting.CookieUserName)
 	ctx.DeleteSiteCookie(setting.CookieRememberName)
 	ctx.Csrf.DeleteCookie(ctx)
 	middleware.DeleteRedirectToCookie(ctx.Resp)
@@ -586,6 +607,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
 		}
 	}
 
+	notify_service.NewUserSignUp(ctx, u)
 	// update external user information
 	if gothUser != nil {
 		if err := externalaccount.UpdateExternalUser(u, *gothUser); err != nil {
@@ -609,7 +631,6 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
 		ctx.Data["Email"] = u.Email
 		ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
 		ctx.HTML(http.StatusOK, TplActivate)
-
 		if setting.CacheService.Enabled {
 			if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
 				log.Error("Set cache(MailResendLimit) fail: %v", err)
@@ -731,8 +752,7 @@ func handleAccountActivation(ctx *context.Context, user *user_model.User) {
 	log.Trace("User activated: %s", user.Name)
 
 	if err := updateSession(ctx, nil, map[string]any{
-		"uid":   user.ID,
-		"uname": user.Name,
+		"uid": user.ID,
 	}); err != nil {
 		log.Error("Unable to regenerate session for user: %-v with email: %s: %v", user, user.Email, err)
 		ctx.ServerError("ActivateUserEmail", err)
diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go
index 79f4711c26..9de56f6bd9 100644
--- a/routers/web/auth/oauth.go
+++ b/routers/web/auth/oauth.go
@@ -1118,8 +1118,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model
 	// we can't sign the user in just yet. Instead, redirect them to the 2FA authentication page.
 	if !needs2FA {
 		if err := updateSession(ctx, nil, map[string]any{
-			"uid":   u.ID,
-			"uname": u.Name,
+			"uid": u.ID,
 		}); err != nil {
 			ctx.ServerError("updateSession", err)
 			return
diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go
index 04078955bb..4fdbea52ca 100644
--- a/routers/web/feed/convert.go
+++ b/routers/web/feed/convert.go
@@ -21,6 +21,7 @@ import (
 	"code.gitea.io/gitea/modules/util"
 
 	"github.com/gorilla/feeds"
+	"github.com/jaytaylor/html2text"
 )
 
 func toBranchLink(ctx *context.Context, act *activities_model.Action) string {
@@ -239,8 +240,15 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
 			content = desc
 		}
 
+		// It's a common practice for feed generators to use plain text titles.
+		// See https://codeberg.org/forgejo/forgejo/pulls/1595
+		plainTitle, err := html2text.FromString(title, html2text.Options{OmitLinks: true})
+		if err != nil {
+			return nil, err
+		}
+
 		items = append(items, &feeds.Item{
-			Title:       title,
+			Title:       plainTitle,
 			Link:        link,
 			Description: desc,
 			Author: &feeds.Author{
diff --git a/routers/web/feed/render.go b/routers/web/feed/render.go
index 8931dae8cc..41f9af1c8c 100644
--- a/routers/web/feed/render.go
+++ b/routers/web/feed/render.go
@@ -8,11 +8,12 @@ import (
 )
 
 // RenderBranchFeed render format for branch or file
-func RenderBranchFeed(ctx *context.Context) {
-	_, _, showFeedType := GetFeedType(ctx.Params(":reponame"), ctx.Req)
-	if ctx.Repo.TreePath == "" {
-		ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
-	} else {
-		ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
+func RenderBranchFeed(feedType string) func(ctx *context.Context) {
+	return func(ctx *context.Context) {
+		if ctx.Repo.TreePath == "" {
+			ShowBranchFeed(ctx, ctx.Repo.Repository, feedType)
+		} else {
+			ShowFileFeed(ctx, ctx.Repo.Repository, feedType)
+		}
 	}
 }
diff --git a/routers/web/home.go b/routers/web/home.go
index ab3fbde2c9..4bcc4adcbf 100644
--- a/routers/web/home.go
+++ b/routers/web/home.go
@@ -54,8 +54,7 @@ func Home(ctx *context.Context) {
 	}
 
 	// Check auto-login.
-	uname := ctx.GetSiteCookie(setting.CookieUserName)
-	if len(uname) != 0 {
+	if len(ctx.GetSiteCookie(setting.CookieRememberName)) != 0 {
 		ctx.Redirect(setting.AppSubURL + "/user/login")
 		return
 	}
diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index a6eb7efeb0..fd47aa5ba5 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -239,6 +239,22 @@ func FileHistory(ctx *context.Context) {
 		ctx.ServerError("CommitsByFileAndRange", err)
 		return
 	}
+	oldestCommit := commits[len(commits)-1]
+
+	renamedFiles, err := git.GetCommitFileRenames(ctx, ctx.Repo.GitRepo.Path, oldestCommit.ID.String())
+	if err != nil {
+		ctx.ServerError("GetCommitFileRenames", err)
+		return
+	}
+
+	for _, renames := range renamedFiles {
+		if renames[1] == fileName {
+			ctx.Data["OldFilename"] = renames[0]
+			ctx.Data["OldFilenameHistory"] = fmt.Sprintf("%s/commits/commit/%s/%s", ctx.Repo.RepoLink, oldestCommit.ID.String(), renames[0])
+			break
+		}
+	}
+
 	ctx.Data["Commits"] = git_model.ConvertFromGitCommit(ctx, commits, ctx.Repo.Repository)
 
 	ctx.Data["Username"] = ctx.Repo.Owner.Name
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index 91ade32ccc..a67b94c6cc 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -387,7 +387,9 @@ func NewReleasePost(ctx *context.Context) {
 		return
 	}
 
-	if !ctx.Repo.GitRepo.IsBranchExist(form.Target) {
+	// form.Target can be a branch name or a full commitID.
+	if !ctx.Repo.GitRepo.IsBranchExist(form.Target) &&
+		len(form.Target) == git.SHAFullLength && !ctx.Repo.GitRepo.IsCommitExist(form.Target) {
 		ctx.RenderWithErr(ctx.Tr("form.target_branch_not_exist"), tplReleaseNew, &form)
 		return
 	}
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 3ea11943d6..5734a4e5fc 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -165,7 +165,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
 
 	if ctx.Repo.TreePath != "" {
 		ctx.Data["HideRepoInfo"] = true
-		ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
+		ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+ctx.Repo.TreePath, ctx.Repo.RefName)
 	}
 
 	subfolder, readmeFile, err := findReadmeFileInEntries(ctx, entries, true)
@@ -344,7 +344,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
 	}
 	defer dataRc.Close()
 
-	ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
+	ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+ctx.Repo.TreePath, ctx.Repo.RefName)
 	ctx.Data["FileIsSymlink"] = entry.IsLink()
 	ctx.Data["FileName"] = blob.Name()
 	ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
@@ -726,12 +726,19 @@ func Home(ctx *context.Context) {
 	if setting.Other.EnableFeed {
 		isFeed, _, showFeedType := feed.GetFeedType(ctx.Params(":reponame"), ctx.Req)
 		if isFeed {
-			switch {
-			case ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType):
+			if ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType) {
 				feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
-			case ctx.Repo.TreePath == "":
+				return
+			}
+
+			if ctx.Repo.Repository.IsEmpty {
+				ctx.NotFound("MustBeNotEmpty", nil)
+				return
+			}
+
+			if ctx.Repo.TreePath == "" {
 				feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
-			case ctx.Repo.TreePath != "":
+			} else {
 				feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
 			}
 			return
diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go
index 5c14f3ad4b..f50c19a923 100644
--- a/routers/web/user/setting/account.go
+++ b/routers/web/user/setting/account.go
@@ -78,6 +78,15 @@ func AccountPost(ctx *context.Context) {
 			ctx.ServerError("UpdateUser", err)
 			return
 		}
+
+		// Re-generate LTA cookie.
+		if len(ctx.GetSiteCookie(setting.CookieRememberName)) != 0 {
+			if err := ctx.SetLTACookie(ctx.Doer); err != nil {
+				ctx.ServerError("SetLTACookie", err)
+				return
+			}
+		}
+
 		log.Trace("User password updated: %s", ctx.Doer.Name)
 		ctx.Flash.Success(ctx.Tr("settings.change_password_success"))
 	}
diff --git a/routers/web/web.go b/routers/web/web.go
index a3ae5f4758..6419094f30 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -48,17 +48,12 @@ import (
 	_ "code.gitea.io/gitea/modules/session" // to registers all internal adapters
 
 	"gitea.com/go-chi/captcha"
-	"github.com/NYTimes/gziphandler"
 	chi_middleware "github.com/go-chi/chi/v5/middleware"
 	"github.com/go-chi/cors"
+	"github.com/klauspost/compress/gzhttp"
 	"github.com/prometheus/client_golang/prometheus"
 )
 
-const (
-	// GzipMinSize represents min size to compress for the body size of response
-	GzipMinSize = 1400
-)
-
 // CorsHandler return a http handler who set CORS options if enabled by config
 func CorsHandler() func(next http.Handler) http.Handler {
 	if setting.CORSConfig.Enabled {
@@ -186,7 +181,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont
 
 		// Redirect to log in page if auto-signin info is provided and has not signed in.
 		if !options.SignOutRequired && !ctx.IsSigned &&
-			len(ctx.GetSiteCookie(setting.CookieUserName)) > 0 {
+			len(ctx.GetSiteCookie(setting.CookieRememberName)) > 0 {
 			if ctx.Req.URL.Path != "/user/events" {
 				middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI())
 			}
@@ -229,11 +224,11 @@ func Routes() *web.Route {
 	var mid []any
 
 	if setting.EnableGzip {
-		h, err := gziphandler.GzipHandlerWithOpts(gziphandler.MinSize(GzipMinSize))
+		wrapper, err := gzhttp.NewWrapper(gzhttp.RandomJitter(32, 0, false))
 		if err != nil {
-			log.Fatal("GzipHandlerWithOpts failed: %v", err)
+			log.Fatal("gzhttp.NewWrapper failed: %v", err)
 		}
-		mid = append(mid, h)
+		mid = append(mid, wrapper)
 	}
 
 	if setting.Service.EnableCaptcha {
@@ -1477,8 +1472,8 @@ func registerRoutes(m *web.Route) {
 			m.Get("/cherry-pick/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.CherryPick)
 		}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
 
-		m.Get("/rss/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
-		m.Get("/atom/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
+		m.Get("/rss/branch/*", repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed("rss"))
+		m.Get("/atom/branch/*", repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed("atom"))
 
 		m.Group("/src", func() {
 			m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
diff --git a/services/auth/auth.go b/services/auth/auth.go
index 713463a3d4..4adf549204 100644
--- a/services/auth/auth.go
+++ b/services/auth/auth.go
@@ -76,10 +76,6 @@ func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore
 	if err != nil {
 		log.Error(fmt.Sprintf("Error setting session: %v", err))
 	}
-	err = sess.Set("uname", user.Name)
-	if err != nil {
-		log.Error(fmt.Sprintf("Error setting session: %v", err))
-	}
 
 	// Language setting of the user overwrites the one previously set
 	// If the user does not have a locale set, we save the current one.
diff --git a/services/forms/user_form.go b/services/forms/user_form.go
index c0eb03f554..cbab274238 100644
--- a/services/forms/user_form.go
+++ b/services/forms/user_form.go
@@ -365,7 +365,7 @@ func (f *EditVariableForm) Validate(req *http.Request, errs binding.Errors) bind
 
 // NewAccessTokenForm form for creating access token
 type NewAccessTokenForm struct {
-	Name  string `binding:"Required;MaxSize(255)"`
+	Name  string `binding:"Required;MaxSize(255)" locale:"settings.token_name"`
 	Scope []string
 }
 
diff --git a/services/lfs/server.go b/services/lfs/server.go
index 58b4663345..68e3cf98a0 100644
--- a/services/lfs/server.go
+++ b/services/lfs/server.go
@@ -5,6 +5,7 @@ package lfs
 
 import (
 	stdCtx "context"
+	"crypto/sha256"
 	"encoding/base64"
 	"encoding/hex"
 	"errors"
@@ -33,7 +34,6 @@ import (
 	"code.gitea.io/gitea/modules/storage"
 
 	"github.com/golang-jwt/jwt/v5"
-	"github.com/minio/sha256-simd"
 )
 
 // requestContext contain variables from the HTTP request.
diff --git a/services/mailer/mail_admin_new_user.go b/services/mailer/mail_admin_new_user.go
new file mode 100644
index 0000000000..b5c7fd8fed
--- /dev/null
+++ b/services/mailer/mail_admin_new_user.go
@@ -0,0 +1,80 @@
+// Copyright 2023 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+package mailer
+
+import (
+	"bytes"
+	"context"
+	"strconv"
+
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/base"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/templates"
+	"code.gitea.io/gitea/modules/translation"
+)
+
+const (
+	tplNewUserMail base.TplName = "notify/admin_new_user"
+)
+
+var sa = SendAsync
+
+// MailNewUser sends notification emails on new user registrations to all admins
+func MailNewUser(ctx context.Context, u *user_model.User) {
+	if !setting.Admin.SendNotificationEmailOnNewUser {
+		return
+	}
+
+	if setting.MailService == nil {
+		// No mail service configured
+		return
+	}
+
+	recipients, err := user_model.GetAllAdmins(ctx)
+	if err != nil {
+		log.Error("user_model.GetAllAdmins: %v", err)
+		return
+	}
+
+	langMap := make(map[string][]string)
+	for _, r := range recipients {
+		langMap[r.Language] = append(langMap[r.Language], r.Email)
+	}
+
+	for lang, tos := range langMap {
+		mailNewUser(ctx, u, lang, tos)
+	}
+}
+
+func mailNewUser(ctx context.Context, u *user_model.User, lang string, tos []string) {
+	locale := translation.NewLocale(lang)
+
+	subject := locale.Tr("mail.admin.new_user.subject", u.Name)
+	manageUserURL := setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10)
+	body := locale.Tr("mail.admin.new_user.text", manageUserURL)
+	mailMeta := map[string]any{
+		"NewUser":  u,
+		"Subject":  subject,
+		"Body":     body,
+		"Language": locale.Language(),
+		"locale":   locale,
+		"Str2html": templates.Str2html,
+	}
+
+	var mailBody bytes.Buffer
+
+	if err := bodyTemplates.ExecuteTemplate(&mailBody, string(tplNewUserMail), mailMeta); err != nil {
+		log.Error("ExecuteTemplate [%s]: %v", string(tplNewUserMail)+"/body", err)
+		return
+	}
+
+	msgs := make([]*Message, 0, len(tos))
+	for _, to := range tos {
+		msg := NewMessage(to, subject, mailBody.String())
+		msg.Info = subject
+		msgs = append(msgs, msg)
+	}
+	sa(msgs...)
+}
diff --git a/services/mailer/mail_admin_new_user_test.go b/services/mailer/mail_admin_new_user_test.go
new file mode 100644
index 0000000000..e6149a6a53
--- /dev/null
+++ b/services/mailer/mail_admin_new_user_test.go
@@ -0,0 +1,88 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package mailer
+
+import (
+	"context"
+	"strconv"
+	"strings"
+	"testing"
+
+	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/setting"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func getTestUsers() []*user_model.User {
+	admin := new(user_model.User)
+	admin.Name = "admin"
+	admin.IsAdmin = true
+	admin.Language = "en_US"
+	admin.Email = "admin@example.com"
+
+	newUser := new(user_model.User)
+	newUser.Name = "new_user"
+	newUser.Language = "en_US"
+	newUser.IsAdmin = false
+	newUser.Email = "new_user@example.com"
+	newUser.LastLoginUnix = 1693648327
+	newUser.CreatedUnix = 1693648027
+
+	user_model.CreateUser(db.DefaultContext, admin)
+	user_model.CreateUser(db.DefaultContext, newUser)
+
+	users := make([]*user_model.User, 0)
+	users = append(users, admin)
+	users = append(users, newUser)
+
+	return users
+}
+
+func cleanUpUsers(ctx context.Context, users []*user_model.User) {
+	for _, u := range users {
+		db.DeleteByID(ctx, u.ID, new(user_model.User))
+	}
+}
+
+func TestAdminNotificationMail_test(t *testing.T) {
+	mailService := setting.Mailer{
+		From:     "test@example.com",
+		Protocol: "dummy",
+	}
+
+	setting.MailService = &mailService
+	setting.Domain = "localhost"
+	setting.AppSubURL = "http://localhost"
+
+	// test with SEND_NOTIFICATION_EMAIL_ON_NEW_USER enabled
+	setting.Admin.SendNotificationEmailOnNewUser = true
+
+	ctx := context.Background()
+	NewContext(ctx)
+
+	users := getTestUsers()
+	oldSendAsync := sa
+	defer func() {
+		sa = oldSendAsync
+		cleanUpUsers(ctx, users)
+	}()
+
+	sa = func(msgs ...*Message) {
+		assert.Equal(t, len(msgs), 1, "Test provides only one admin user, so only one email must be sent")
+		assert.Equal(t, msgs[0].To, users[0].Email, "checks if the recipient is the admin of the instance")
+		manageUserURL := "/admin/users/" + strconv.FormatInt(users[1].ID, 10)
+		assert.True(t, strings.ContainsAny(msgs[0].Body, manageUserURL), "checks if the message contains the link to manage the newly created user from the admin panel")
+	}
+	MailNewUser(ctx, users[1])
+
+	// test with SEND_NOTIFICATION_EMAIL_ON_NEW_USER disabled; emails shouldn't be sent
+	setting.Admin.SendNotificationEmailOnNewUser = false
+	sa = func(msgs ...*Message) {
+		assert.Equal(t, 1, 0, "this shouldn't execute. MailNewUser must exit early since SEND_NOTIFICATION_EMAIL_ON_NEW_USER is disabled")
+	}
+
+	MailNewUser(ctx, users[1])
+}
diff --git a/services/mailer/notify.go b/services/mailer/notify.go
index 9eaf268d0a..1577e824a5 100644
--- a/services/mailer/notify.go
+++ b/services/mailer/notify.go
@@ -202,3 +202,7 @@ func (m *mailNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner *
 		log.Error("SendRepoTransferNotifyMail: %v", err)
 	}
 }
+
+func (m *mailNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.User) {
+	MailNewUser(ctx, newUser)
+}
diff --git a/services/mailer/token/token.go b/services/mailer/token/token.go
index aa7b567188..8a5a762d6b 100644
--- a/services/mailer/token/token.go
+++ b/services/mailer/token/token.go
@@ -6,14 +6,13 @@ package token
 import (
 	"context"
 	crypto_hmac "crypto/hmac"
+	"crypto/sha256"
 	"encoding/base32"
 	"fmt"
 	"time"
 
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/util"
-
-	"github.com/minio/sha256-simd"
 )
 
 // A token is a verifiable container describing an action.
diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go
index 9e4adc55be..5e4a096222 100644
--- a/services/migrations/gitea_uploader.go
+++ b/services/migrations/gitea_uploader.go
@@ -859,6 +859,11 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
 		}
 
 		for _, comment := range review.Comments {
+			// Skip code comment if it doesn't have a diff it is commeting on.
+			if comment.DiffHunk == "" {
+				continue
+			}
+
 			line := comment.Line
 			if line != 0 {
 				comment.Position = 1
diff --git a/services/notify/notifier.go b/services/notify/notifier.go
index ed053a812a..3230a5e5f5 100644
--- a/services/notify/notifier.go
+++ b/services/notify/notifier.go
@@ -59,6 +59,8 @@ type Notifier interface {
 	EditWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page, comment string)
 	DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page string)
 
+	NewUserSignUp(ctx context.Context, newUser *user_model.User)
+
 	NewRelease(ctx context.Context, rel *repo_model.Release)
 	UpdateRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release)
 	DeleteRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release)
diff --git a/services/notify/notify.go b/services/notify/notify.go
index 16fbb6325d..9cb329d302 100644
--- a/services/notify/notify.go
+++ b/services/notify/notify.go
@@ -347,6 +347,13 @@ func RepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, r
 	}
 }
 
+// NewUserSignUp notifies about a newly signed up user to notifiers
+func NewUserSignUp(ctx context.Context, newUser *user_model.User) {
+	for _, notifier := range notifiers {
+		notifier.NewUserSignUp(ctx, newUser)
+	}
+}
+
 // PackageCreate notifies creation of a package to notifiers
 func PackageCreate(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor) {
 	for _, notifier := range notifiers {
diff --git a/services/notify/null.go b/services/notify/null.go
index dddd421bef..894d118eac 100644
--- a/services/notify/null.go
+++ b/services/notify/null.go
@@ -197,6 +197,9 @@ func (*NullNotifier) SyncDeleteRef(ctx context.Context, doer *user_model.User, r
 func (*NullNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) {
 }
 
+func (*NullNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.User) {
+}
+
 // PackageCreate places a place holder function
 func (*NullNotifier) PackageCreate(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor) {
 }
diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go
index 176ba83e67..8d68686656 100644
--- a/services/webhook/deliver.go
+++ b/services/webhook/deliver.go
@@ -7,6 +7,7 @@ import (
 	"context"
 	"crypto/hmac"
 	"crypto/sha1"
+	"crypto/sha256"
 	"crypto/tls"
 	"encoding/hex"
 	"fmt"
@@ -29,7 +30,6 @@ import (
 	webhook_module "code.gitea.io/gitea/modules/webhook"
 
 	"github.com/gobwas/glob"
-	"github.com/minio/sha256-simd"
 )
 
 // Deliver deliver hook task
diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl
index 7eb9d086e6..d40af66bc4 100644
--- a/templates/admin/config.tmpl
+++ b/templates/admin/config.tmpl
@@ -159,6 +159,8 @@
 				<dd>{{if .Service.DefaultKeepEmailPrivate}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
 				<dt>{{ctx.Locale.Tr "admin.config.default_allow_create_organization"}}</dt>
 				<dd>{{if .Service.DefaultAllowCreateOrganization}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
+				<dt>{{ctx.Locale.Tr "admin.config.allow_dots_in_usernames"}}</dt>
+				<dd>{{if .Service.AllowDotsInUsernames}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
 				<dt>{{ctx.Locale.Tr "admin.config.enable_timetracking"}}</dt>
 				<dd>{{if .Service.EnableTimetracking}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
 				{{if .Service.EnableTimetracking}}
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl
index c3645209cd..08c68752e2 100644
--- a/templates/base/head.tmpl
+++ b/templates/base/head.tmpl
@@ -2,7 +2,8 @@
 <html lang="{{ctx.Locale.Lang}}" class="theme-{{if .SignedUser.Theme}}{{.SignedUser.Theme}}{{else}}{{DefaultTheme}}{{end}}">
 <head>
 	<meta name="viewport" content="width=device-width, initial-scale=1">
-	<title>{{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}}{{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}</title>
+	{{/* Display `- .Repsository.FullName` only if `.Title` does not already start with that. */}}
+	<title>{{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}}{{if and (.Repository.Name) (not (StringUtils.HasPrefix .Title .Repository.FullName))}}{{.Repository.FullName}} - {{end}}{{AppName}}</title>
 	{{if .ManifestData}}<link rel="manifest" href="data:{{.ManifestData}}">{{end}}
 	<meta name="author" content="{{if .Repository}}{{.Owner.Name}}{{else}}{{MetaAuthor}}{{end}}">
 	<meta name="description" content="{{if .Repository}}{{.Repository.Name}}{{if .Repository.Description}} - {{.Repository.Description}}{{end}}{{else}}{{MetaDescription}}{{end}}">
diff --git a/templates/mail/notify/admin_new_user.tmpl b/templates/mail/notify/admin_new_user.tmpl
new file mode 100644
index 0000000000..03ef4abe21
--- /dev/null
+++ b/templates/mail/notify/admin_new_user.tmpl
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+	<title>{{.Subject}}</title>
+
+	<style>
+		blockquote { padding-left: 1em; margin: 1em 0; border-left: 1px solid grey; color: #777}
+		.footer { font-size:small; color:#666;}
+	</style>
+
+</head>
+
+<body>
+	<ul>
+		<h3>{{ctx.Locale.Tr "mail.admin.new_user.user_info"}}</h3>
+		<li>{{ctx.Locale.Tr "admin.users.created"}}: {{DateTime "full" .NewUser.LastLoginUnix}}</li>
+		<li>{{ctx.Locale.Tr "admin.users.last_login"}}: {{DateTime "full" .NewUser.CreatedUnix}}</li>
+	</ul>
+	<p> {{.Body | Str2html}} </p>
+</body>
+</html>
diff --git a/templates/package/content/maven.tmpl b/templates/package/content/maven.tmpl
index b2cd567e16..100c12e180 100644
--- a/templates/package/content/maven.tmpl
+++ b/templates/package/content/maven.tmpl
@@ -7,7 +7,7 @@
 				<div class="markup"><pre class="code-block"><code>&lt;repositories&gt;
 	&lt;repository&gt;
 		&lt;id&gt;gitea&lt;/id&gt;
-			&lt;url&gt;<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></gitea-origin-url>&lt;/url&gt;
+		&lt;url&gt;<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></gitea-origin-url>&lt;/url&gt;
 	&lt;/repository&gt;
 &lt;/repositories&gt;
 
diff --git a/templates/repo/commits.tmpl b/templates/repo/commits.tmpl
index 42004c2610..7b3b27af1d 100644
--- a/templates/repo/commits.tmpl
+++ b/templates/repo/commits.tmpl
@@ -13,6 +13,11 @@
 			</div>
 		</div>
 		{{template "repo/commits_table" .}}
+		{{if .OldFilename}}
+			<div class="ui bottom attached header">
+				<span>{{ctx.Locale.Tr "repo.commits.renamed_from" .OldFilename}} (<a href="{{.OldFilenameHistory}}">{{ctx.Locale.Tr "repo.commits.browse_further"}}</a>)</span>
+			</div>
+		{{end}}
 	</div>
 </div>
 {{template "base/footer" .}}
diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl
index 05b7dbaabc..8e46b2da0e 100644
--- a/templates/repo/issue/card.tmpl
+++ b/templates/repo/issue/card.tmpl
@@ -33,7 +33,7 @@
 		</div>
 		{{if .MilestoneID}}
 		<div class="meta gt-my-2">
-			<a class="milestone" href="{{$.Page.RepoLink}}/milestone/{{.MilestoneID}}">
+			<a class="milestone" href="{{.Repo.Link}}/milestone/{{.MilestoneID}}">
 				{{svg "octicon-milestone" 16 "gt-mr-2 gt-vm"}}
 				<span class="gt-vm">{{.Milestone.Name}}</span>
 			</a>
@@ -42,7 +42,7 @@
 		{{if $.Page.LinkedPRs}}
 		{{range index $.Page.LinkedPRs .ID}}
 		<div class="meta gt-my-2">
-			<a href="{{$.Page.RepoLink}}/pulls/{{.Index}}">
+			<a href="{{$.Issue.Repo.Link}}/pulls/{{.Index}}">
 				<span class="gt-m-0 text {{if .PullRequest.HasMerged}}purple{{else if .IsClosed}}red{{else}}green{{end}}">{{svg "octicon-git-merge" 16 "gt-mr-2 gt-vm"}}</span>
 				<span class="gt-vm">{{.Title}} <span class="text light grey">#{{.Index}}</span></span>
 			</a>
@@ -54,7 +54,7 @@
 	{{if or .Labels .Assignees}}
 	<div class="extra content labels-list gt-p-0 gt-pt-2">
 		{{range .Labels}}
-			<a target="_blank" href="{{$.Page.RepoLink}}/issues?labels={{.ID}}">{{RenderLabel ctx .}}</a>
+			<a target="_blank" href="{{$.Issue.Repo.Link}}/issues?labels={{.ID}}">{{RenderLabel ctx .}}</a>
 		{{end}}
 		<div class="right floated">
 			{{range .Assignees}}
diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl
index 5265c09b50..0e16d49b95 100644
--- a/templates/repo/issue/view_content/comments.tmpl
+++ b/templates/repo/issue/view_content/comments.tmpl
@@ -363,7 +363,7 @@
 				{{end}}
 			</div>
 		{{else if eq .Type 22}}
-			<div class="timeline-item-group">
+			<div class="timeline-item-group" id="{{.HashTag}}">
 				<div class="timeline-item event">
 					{{if .OriginalAuthor}}
 					{{else}}
@@ -402,7 +402,7 @@
 					</span>
 				</div>
 				{{if or .Content .Attachments}}
-				<div class="timeline-item comment" id="{{.HashTag}}">
+				<div class="timeline-item comment">
 					<div class="content comment-container">
 						<div class="ui top attached header comment-header gt-df gt-ac gt-sb">
 							<div class="comment-header-left gt-df gt-ac">
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 2a989d3bda..ad46953ae5 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -976,20 +976,23 @@
 				{{end}}
 			</div>
 			<div class="content">
-				<p>
+				<div class="ui warning message">
 					{{if .Repository.IsArchived}}
 						{{ctx.Locale.Tr "repo.settings.unarchive.text"}}
 					{{else}}
 						{{ctx.Locale.Tr "repo.settings.archive.text"}}
 					{{end}}
-				</p>
-			</div>
-			<form action="{{.Link}}" method="post">
-				{{.CsrfTokenHtml}}
-				<input type="hidden" name="action" value="{{if .Repository.IsArchived}}unarchive{{else}}archive{{end}}">
-				<input type="hidden" name="repo_id" value="{{.Repository.ID}}">
-				{{template "base/modal_actions_confirm" .}}
+				</div>
+				<form action="{{.Link}}" method="post">
+					{{.CsrfTokenHtml}}
+					<input type="hidden" name="action" value="{{if .Repository.IsArchived}}unarchive{{else}}archive{{end}}">
+					<input type="hidden" name="repo_id" value="{{.Repository.ID}}">
+					<div class="text right actions">
+						<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
+						<button class="ui red button">{{ctx.Locale.Tr "repo.settings.archive.button"}}</button>
+					</div>
 			</form>
+			</div>
 		</div>
 	{{end}}
 {{end}}
diff --git a/tests/gitea-repositories-meta/user2/repo59.git/HEAD b/tests/gitea-repositories-meta/user2/repo59.git/HEAD
new file mode 100644
index 0000000000..cb089cd89a
--- /dev/null
+++ b/tests/gitea-repositories-meta/user2/repo59.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/gitea-repositories-meta/user2/repo59.git/config b/tests/gitea-repositories-meta/user2/repo59.git/config
new file mode 100644
index 0000000000..07d359d07c
--- /dev/null
+++ b/tests/gitea-repositories-meta/user2/repo59.git/config
@@ -0,0 +1,4 @@
+[core]
+	repositoryformatversion = 0
+	filemode = true
+	bare = true
diff --git a/tests/gitea-repositories-meta/user2/repo59.git/description b/tests/gitea-repositories-meta/user2/repo59.git/description
new file mode 100644
index 0000000000..498b267a8c
--- /dev/null
+++ b/tests/gitea-repositories-meta/user2/repo59.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/gitea-repositories-meta/user2/repo59.git/info/exclude b/tests/gitea-repositories-meta/user2/repo59.git/info/exclude
new file mode 100644
index 0000000000..a5196d1be8
--- /dev/null
+++ b/tests/gitea-repositories-meta/user2/repo59.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/gitea-repositories-meta/user2/repo59.git/objects/info/commit-graph b/tests/gitea-repositories-meta/user2/repo59.git/objects/info/commit-graph
new file mode 100644
index 0000000000..d151dc87e6
Binary files /dev/null and b/tests/gitea-repositories-meta/user2/repo59.git/objects/info/commit-graph differ
diff --git a/tests/gitea-repositories-meta/user2/repo59.git/objects/info/packs b/tests/gitea-repositories-meta/user2/repo59.git/objects/info/packs
new file mode 100644
index 0000000000..0374746b5e
--- /dev/null
+++ b/tests/gitea-repositories-meta/user2/repo59.git/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.pack
+
diff --git a/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.idx b/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.idx
new file mode 100644
index 0000000000..aaa9981cf5
Binary files /dev/null and b/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.idx differ
diff --git a/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.pack b/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.pack
new file mode 100644
index 0000000000..ddb8c16caf
Binary files /dev/null and b/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.pack differ
diff --git a/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.rev b/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.rev
new file mode 100644
index 0000000000..81554dba74
Binary files /dev/null and b/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.rev differ
diff --git a/tests/gitea-repositories-meta/user2/repo59.git/packed-refs b/tests/gitea-repositories-meta/user2/repo59.git/packed-refs
new file mode 100644
index 0000000000..77fedbf67d
--- /dev/null
+++ b/tests/gitea-repositories-meta/user2/repo59.git/packed-refs
@@ -0,0 +1,4 @@
+# pack-refs with: peeled fully-peeled sorted 
+d8f53dfb33f6ccf4169c34970b5e747511c18beb refs/heads/cake-recipe
+80b83c5c8220c3aa3906e081f202a2a7563ec879 refs/heads/master
+d8f53dfb33f6ccf4169c34970b5e747511c18beb refs/tags/v1.0
diff --git a/tests/integration/api_comment_test.go b/tests/integration/api_comment_test.go
index 339ffdbe0e..eed496a9bf 100644
--- a/tests/integration/api_comment_test.go
+++ b/tests/integration/api_comment_test.go
@@ -189,6 +189,43 @@ func TestAPIGetComment(t *testing.T) {
 	assert.Equal(t, expect.Created.Unix(), apiComment.Created.Unix())
 }
 
+func TestAPIGetSystemUserComment(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{})
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
+	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+
+	for _, systemUser := range []*user_model.User{
+		user_model.NewGhostUser(),
+		user_model.NewActionsUser(),
+	} {
+		body := fmt.Sprintf("Hello %s", systemUser.Name)
+		comment, err := issues_model.CreateComment(db.DefaultContext, &issues_model.CreateCommentOptions{
+			Type:    issues_model.CommentTypeComment,
+			Doer:    systemUser,
+			Repo:    repo,
+			Issue:   issue,
+			Content: body,
+		})
+		assert.NoError(t, err)
+
+		req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d", repoOwner.Name, repo.Name, comment.ID)
+		resp := MakeRequest(t, req, http.StatusOK)
+
+		var apiComment api.Comment
+		DecodeJSON(t, resp, &apiComment)
+
+		if assert.NotNil(t, apiComment.Poster) {
+			if assert.Equal(t, systemUser.ID, apiComment.Poster.ID) {
+				assert.NoError(t, comment.LoadPoster(db.DefaultContext))
+				assert.Equal(t, systemUser.Name, apiComment.Poster.UserName)
+			}
+		}
+		assert.Equal(t, body, apiComment.Body)
+	}
+}
+
 func TestAPIEditComment(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 	const newCommentBody = "This is the new comment body"
diff --git a/tests/integration/api_feed_plain_text_titles_test.go b/tests/integration/api_feed_plain_text_titles_test.go
new file mode 100644
index 0000000000..a058b7321c
--- /dev/null
+++ b/tests/integration/api_feed_plain_text_titles_test.go
@@ -0,0 +1,40 @@
+// Copyright 2023 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+	"net/http"
+	"testing"
+
+	"code.gitea.io/gitea/tests"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestFeedPlainTextTitles(t *testing.T) {
+	// This test verifies that items' titles in feeds are generated as plain text.
+	// See https://codeberg.org/forgejo/forgejo/pulls/1595
+
+	t.Run("Feed plain text titles", func(t *testing.T) {
+		t.Run("Atom", func(t *testing.T) {
+			defer tests.PrepareTestEnv(t)()
+
+			req := NewRequest(t, "GET", "/user2/repo1.atom")
+			resp := MakeRequest(t, req, http.StatusOK)
+
+			data := resp.Body.String()
+			assert.Contains(t, data, "<title>the_1-user.with.all.allowedChars closed issue user2/repo1#4</title>")
+		})
+
+		t.Run("RSS", func(t *testing.T) {
+			defer tests.PrepareTestEnv(t)()
+
+			req := NewRequest(t, "GET", "/user2/repo1.rss")
+			resp := MakeRequest(t, req, http.StatusOK)
+
+			data := resp.Body.String()
+			assert.Contains(t, data, "<title>the_1-user.with.all.allowedChars closed issue user2/repo1#4</title>")
+		})
+	})
+}
diff --git a/tests/integration/api_feed_user_test.go b/tests/integration/api_feed_user_test.go
index c44f9a1951..608f7608ae 100644
--- a/tests/integration/api_feed_user_test.go
+++ b/tests/integration/api_feed_user_test.go
@@ -7,15 +7,19 @@ import (
 	"net/http"
 	"testing"
 
+	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/tests"
 
 	"github.com/stretchr/testify/assert"
 )
 
 func TestFeed(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
 	t.Run("User", func(t *testing.T) {
 		t.Run("Atom", func(t *testing.T) {
-			defer tests.PrepareTestEnv(t)()
+			defer tests.PrintCurrentTest(t)()
 
 			req := NewRequest(t, "GET", "/user2.atom")
 			resp := MakeRequest(t, req, http.StatusOK)
@@ -25,7 +29,7 @@ func TestFeed(t *testing.T) {
 		})
 
 		t.Run("RSS", func(t *testing.T) {
-			defer tests.PrepareTestEnv(t)()
+			defer tests.PrintCurrentTest(t)()
 
 			req := NewRequest(t, "GET", "/user2.rss")
 			resp := MakeRequest(t, req, http.StatusOK)
@@ -34,4 +38,51 @@ func TestFeed(t *testing.T) {
 			assert.Contains(t, data, `<rss version="2.0"`)
 		})
 	})
+
+	t.Run("Repo", func(t *testing.T) {
+		t.Run("Normal", func(t *testing.T) {
+			t.Run("Atom", func(t *testing.T) {
+				defer tests.PrintCurrentTest(t)()
+
+				req := NewRequest(t, "GET", "/user2/repo1/atom/branch/master")
+				resp := MakeRequest(t, req, http.StatusOK)
+
+				data := resp.Body.String()
+				assert.Contains(t, data, `<feed xmlns="http://www.w3.org/2005/Atom"`)
+			})
+			t.Run("RSS", func(t *testing.T) {
+				defer tests.PrintCurrentTest(t)()
+
+				req := NewRequest(t, "GET", "/user2/repo1/rss/branch/master")
+				resp := MakeRequest(t, req, http.StatusOK)
+
+				data := resp.Body.String()
+				assert.Contains(t, data, `<rss version="2.0"`)
+			})
+		})
+		t.Run("Empty", func(t *testing.T) {
+			err := user_model.UpdateUserCols(db.DefaultContext, &user_model.User{ID: 30, ProhibitLogin: false}, "prohibit_login")
+			assert.NoError(t, err)
+
+			session := loginUser(t, "user30")
+			t.Run("Atom", func(t *testing.T) {
+				defer tests.PrintCurrentTest(t)()
+
+				req := NewRequest(t, "GET", "/user30/empty/atom/branch/master")
+				session.MakeRequest(t, req, http.StatusNotFound)
+
+				req = NewRequest(t, "GET", "/user30/empty.atom/src/branch/master")
+				session.MakeRequest(t, req, http.StatusNotFound)
+			})
+			t.Run("RSS", func(t *testing.T) {
+				defer tests.PrintCurrentTest(t)()
+
+				req := NewRequest(t, "GET", "/user30/empty/rss/branch/master")
+				session.MakeRequest(t, req, http.StatusNotFound)
+
+				req = NewRequest(t, "GET", "/user30/empty.rss/src/branch/master")
+				session.MakeRequest(t, req, http.StatusNotFound)
+			})
+		})
+	})
 }
diff --git a/tests/integration/api_packages_chef_test.go b/tests/integration/api_packages_chef_test.go
index 4a1fe2607e..7a9a8f30b8 100644
--- a/tests/integration/api_packages_chef_test.go
+++ b/tests/integration/api_packages_chef_test.go
@@ -11,6 +11,7 @@ import (
 	"crypto/rand"
 	"crypto/rsa"
 	"crypto/sha1"
+	"crypto/sha256"
 	"crypto/x509"
 	"encoding/base64"
 	"encoding/pem"
@@ -33,7 +34,6 @@ import (
 	chef_router "code.gitea.io/gitea/routers/api/packages/chef"
 	"code.gitea.io/gitea/tests"
 
-	"github.com/minio/sha256-simd"
 	"github.com/stretchr/testify/assert"
 )
 
diff --git a/tests/integration/api_packages_container_test.go b/tests/integration/api_packages_container_test.go
index 01002a4413..7d2215e7f8 100644
--- a/tests/integration/api_packages_container_test.go
+++ b/tests/integration/api_packages_container_test.go
@@ -5,6 +5,7 @@ package integration
 
 import (
 	"bytes"
+	"crypto/sha256"
 	"encoding/base64"
 	"fmt"
 	"net/http"
@@ -23,7 +24,6 @@ import (
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/tests"
 
-	"github.com/minio/sha256-simd"
 	oci "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/stretchr/testify/assert"
 )
diff --git a/tests/integration/api_packages_test.go b/tests/integration/api_packages_test.go
index e530b2c1ad..59d8dcb88a 100644
--- a/tests/integration/api_packages_test.go
+++ b/tests/integration/api_packages_test.go
@@ -5,6 +5,7 @@ package integration
 
 import (
 	"bytes"
+	"crypto/sha256"
 	"fmt"
 	"net/http"
 	"strings"
@@ -24,7 +25,6 @@ import (
 	packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup"
 	"code.gitea.io/gitea/tests"
 
-	"github.com/minio/sha256-simd"
 	"github.com/stretchr/testify/assert"
 )
 
diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go
index a6d32a89ea..edab964475 100644
--- a/tests/integration/api_repo_test.go
+++ b/tests/integration/api_repo_test.go
@@ -93,9 +93,9 @@ func TestAPISearchRepo(t *testing.T) {
 	}{
 		{
 			name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50&private=false", expectedResults: expectedResults{
-				nil:   {count: 33},
-				user:  {count: 33},
-				user2: {count: 33},
+				nil:   {count: 34},
+				user:  {count: 34},
+				user2: {count: 34},
 			},
 		},
 		{
diff --git a/tests/integration/api_user_search_test.go b/tests/integration/api_user_search_test.go
index c5b202b319..ddfeb25234 100644
--- a/tests/integration/api_user_search_test.go
+++ b/tests/integration/api_user_search_test.go
@@ -56,6 +56,28 @@ func TestAPIUserSearchNotLoggedIn(t *testing.T) {
 	}
 }
 
+func TestAPIUserSearchSystemUsers(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+	for _, systemUser := range []*user_model.User{
+		user_model.NewGhostUser(),
+		user_model.NewActionsUser(),
+	} {
+		t.Run(systemUser.Name, func(t *testing.T) {
+			req := NewRequestf(t, "GET", "/api/v1/users/search?uid=%d", systemUser.ID)
+			resp := MakeRequest(t, req, http.StatusOK)
+
+			var results SearchResults
+			DecodeJSON(t, resp, &results)
+			assert.NotEmpty(t, results.Data)
+			if assert.EqualValues(t, 1, len(results.Data)) {
+				user := results.Data[0]
+				assert.EqualValues(t, user.UserName, systemUser.Name)
+				assert.EqualValues(t, user.ID, systemUser.ID)
+			}
+		})
+	}
+}
+
 func TestAPIUserSearchAdminLoggedInUserHidden(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 	adminUsername := "user1"
diff --git a/tests/integration/auth_token_test.go b/tests/integration/auth_token_test.go
new file mode 100644
index 0000000000..24c66ee261
--- /dev/null
+++ b/tests/integration/auth_token_test.go
@@ -0,0 +1,163 @@
+// Copyright 2023 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+	"encoding/hex"
+	"net/http"
+	"net/url"
+	"strings"
+	"testing"
+
+	"code.gitea.io/gitea/models/auth"
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/timeutil"
+	"code.gitea.io/gitea/tests"
+
+	"github.com/stretchr/testify/assert"
+)
+
+// GetSessionForLTACookie returns a new session with only the LTA cookie being set.
+func GetSessionForLTACookie(t *testing.T, ltaCookie *http.Cookie) *TestSession {
+	t.Helper()
+
+	ch := http.Header{}
+	ch.Add("Cookie", ltaCookie.String())
+	cr := http.Request{Header: ch}
+
+	session := emptyTestSession(t)
+	baseURL, err := url.Parse(setting.AppURL)
+	assert.NoError(t, err)
+	session.jar.SetCookies(baseURL, cr.Cookies())
+
+	return session
+}
+
+// GetLTACookieValue returns the value of the LTA cookie.
+func GetLTACookieValue(t *testing.T, sess *TestSession) string {
+	t.Helper()
+
+	rememberCookie := sess.GetCookie(setting.CookieRememberName)
+	assert.NotNil(t, rememberCookie)
+
+	cookieValue, err := url.QueryUnescape(rememberCookie.Value)
+	assert.NoError(t, err)
+
+	return cookieValue
+}
+
+// TestSessionCookie checks if the session cookie provides authentication.
+func TestSessionCookie(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	sess := loginUser(t, "user1")
+	assert.NotNil(t, sess.GetCookie(setting.SessionConfig.CookieName))
+
+	req := NewRequest(t, "GET", "/user/settings")
+	sess.MakeRequest(t, req, http.StatusOK)
+}
+
+// TestLTACookie checks if the LTA cookie that's returned is valid, exists in the database
+// and provides authentication of no session cookie is present.
+func TestLTACookie(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+	sess := emptyTestSession(t)
+
+	req := NewRequestWithValues(t, "POST", "/user/login", map[string]string{
+		"_csrf":     GetCSRF(t, sess, "/user/login"),
+		"user_name": user.Name,
+		"password":  userPassword,
+		"remember":  "true",
+	})
+	sess.MakeRequest(t, req, http.StatusSeeOther)
+
+	// Checks if the database entry exist for the user.
+	ltaCookieValue := GetLTACookieValue(t, sess)
+	lookupKey, validator, found := strings.Cut(ltaCookieValue, ":")
+	assert.True(t, found)
+	rawValidator, err := hex.DecodeString(validator)
+	assert.NoError(t, err)
+	unittest.AssertExistsAndLoadBean(t, &auth.AuthorizationToken{LookupKey: lookupKey, HashedValidator: auth.HashValidator(rawValidator), UID: user.ID})
+
+	// Check if the LTA cookie it provides authentication.
+	// If LTA cookie provides authentication /user/login shouldn't return status 200.
+	session := GetSessionForLTACookie(t, sess.GetCookie(setting.CookieRememberName))
+	req = NewRequest(t, "GET", "/user/login")
+	session.MakeRequest(t, req, http.StatusSeeOther)
+}
+
+// TestLTAPasswordChange checks that LTA doesn't provide authentication when a
+// password change has happened and that the new LTA does provide authentication.
+func TestLTAPasswordChange(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+
+	sess := loginUserWithPasswordRemember(t, user.Name, userPassword, true)
+	oldRememberCookie := sess.GetCookie(setting.CookieRememberName)
+	assert.NotNil(t, oldRememberCookie)
+
+	// Make a simple password change.
+	req := NewRequestWithValues(t, "POST", "/user/settings/account", map[string]string{
+		"_csrf":        GetCSRF(t, sess, "/user/settings/account"),
+		"old_password": userPassword,
+		"password":     "password2",
+		"retype":       "password2",
+	})
+	sess.MakeRequest(t, req, http.StatusSeeOther)
+	rememberCookie := sess.GetCookie(setting.CookieRememberName)
+	assert.NotNil(t, rememberCookie)
+
+	// Check if the password really changed.
+	assert.NotEqualValues(t, unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).Passwd, user.Passwd)
+
+	// /user/settings/account should provide with a new LTA cookie, so check for that.
+	// If LTA cookie provides authentication /user/login shouldn't return status 200.
+	session := GetSessionForLTACookie(t, rememberCookie)
+	req = NewRequest(t, "GET", "/user/login")
+	session.MakeRequest(t, req, http.StatusSeeOther)
+
+	// Check if the old LTA token is invalidated.
+	session = GetSessionForLTACookie(t, oldRememberCookie)
+	req = NewRequest(t, "GET", "/user/login")
+	session.MakeRequest(t, req, http.StatusOK)
+}
+
+// TestLTAExpiry tests that the LTA expiry works.
+func TestLTAExpiry(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+
+	sess := loginUserWithPasswordRemember(t, user.Name, userPassword, true)
+
+	ltaCookieValie := GetLTACookieValue(t, sess)
+	lookupKey, _, found := strings.Cut(ltaCookieValie, ":")
+	assert.True(t, found)
+
+	// Ensure it's not expired.
+	lta := unittest.AssertExistsAndLoadBean(t, &auth.AuthorizationToken{UID: user.ID, LookupKey: lookupKey})
+	assert.False(t, lta.IsExpired())
+
+	// Manually stub LTA's expiry.
+	_, err := db.GetEngine(db.DefaultContext).ID(lta.ID).Table("forgejo_auth_token").Cols("expiry").Update(&auth.AuthorizationToken{Expiry: timeutil.TimeStampNow()})
+	assert.NoError(t, err)
+
+	// Ensure it's expired.
+	lta = unittest.AssertExistsAndLoadBean(t, &auth.AuthorizationToken{UID: user.ID, LookupKey: lookupKey})
+	assert.True(t, lta.IsExpired())
+
+	// Should return 200 OK, because LTA doesn't provide authorization anymore.
+	session := GetSessionForLTACookie(t, sess.GetCookie(setting.CookieRememberName))
+	req := NewRequest(t, "GET", "/user/login")
+	session.MakeRequest(t, req, http.StatusOK)
+
+	// Ensure it's deleted.
+	unittest.AssertNotExistsBean(t, &auth.AuthorizationToken{UID: user.ID, LookupKey: lookupKey})
+}
diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go
index 49a714c343..cb6269cea9 100644
--- a/tests/integration/integration_test.go
+++ b/tests/integration/integration_test.go
@@ -17,6 +17,7 @@ import (
 	"net/url"
 	"os"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"sync/atomic"
 	"testing"
@@ -42,8 +43,8 @@ import (
 	"github.com/markbates/goth"
 	"github.com/markbates/goth/gothic"
 	goth_gitlab "github.com/markbates/goth/providers/gitlab"
+	"github.com/santhosh-tekuri/jsonschema/v5"
 	"github.com/stretchr/testify/assert"
-	"github.com/xeipuuv/gojsonschema"
 )
 
 var testWebRoutes *web.Route
@@ -299,6 +300,12 @@ func loginUser(t testing.TB, userName string) *TestSession {
 
 func loginUserWithPassword(t testing.TB, userName, password string) *TestSession {
 	t.Helper()
+
+	return loginUserWithPasswordRemember(t, userName, password, false)
+}
+
+func loginUserWithPasswordRemember(t testing.TB, userName, password string, rememberMe bool) *TestSession {
+	t.Helper()
 	req := NewRequest(t, "GET", "/user/login")
 	resp := MakeRequest(t, req, http.StatusOK)
 
@@ -307,6 +314,7 @@ func loginUserWithPassword(t testing.TB, userName, password string) *TestSession
 		"_csrf":     doc.GetCSRF(),
 		"user_name": userName,
 		"password":  password,
+		"remember":  strconv.FormatBool(rememberMe),
 	})
 	resp = MakeRequest(t, req, http.StatusSeeOther)
 
@@ -512,16 +520,15 @@ func VerifyJSONSchema(t testing.TB, resp *httptest.ResponseRecorder, schemaFile
 	_, schemaFileErr := os.Stat(schemaFilePath)
 	assert.Nil(t, schemaFileErr)
 
-	schema, schemaFileReadErr := os.ReadFile(schemaFilePath)
-	assert.Nil(t, schemaFileReadErr)
-	assert.True(t, len(schema) > 0)
+	schema, err := jsonschema.Compile(schemaFilePath)
+	assert.NoError(t, err)
 
-	nodeinfoSchema := gojsonschema.NewStringLoader(string(schema))
-	nodeinfoString := gojsonschema.NewStringLoader(resp.Body.String())
-	result, schemaValidationErr := gojsonschema.Validate(nodeinfoSchema, nodeinfoString)
-	assert.Nil(t, schemaValidationErr)
-	assert.Empty(t, result.Errors())
-	assert.True(t, result.Valid())
+	var data interface{}
+	err = json.Unmarshal(resp.Body.Bytes(), &data)
+	assert.NoError(t, err)
+
+	schemaValidation := schema.Validate(data)
+	assert.Nil(t, schemaValidation)
 }
 
 func GetCSRF(t testing.TB, session *TestSession, urlStr string) string {
@@ -531,3 +538,18 @@ func GetCSRF(t testing.TB, session *TestSession, urlStr string) string {
 	doc := NewHTMLParser(t, resp.Body)
 	return doc.GetCSRF()
 }
+
+func GetHTMLTitle(t testing.TB, session *TestSession, urlStr string) string {
+	t.Helper()
+
+	req := NewRequest(t, "GET", urlStr)
+	var resp *httptest.ResponseRecorder
+	if session == nil {
+		resp = MakeRequest(t, req, http.StatusOK)
+	} else {
+		resp = session.MakeRequest(t, req, http.StatusOK)
+	}
+
+	doc := NewHTMLParser(t, resp.Body)
+	return doc.Find("head title").Text()
+}
diff --git a/tests/integration/lfs_getobject_test.go b/tests/integration/lfs_getobject_test.go
index fe070c62d5..c36e0ef06c 100644
--- a/tests/integration/lfs_getobject_test.go
+++ b/tests/integration/lfs_getobject_test.go
@@ -18,9 +18,9 @@ import (
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/lfs"
 	"code.gitea.io/gitea/modules/setting"
-	"code.gitea.io/gitea/routers/web"
 	"code.gitea.io/gitea/tests"
 
+	"github.com/klauspost/compress/gzhttp"
 	gzipp "github.com/klauspost/compress/gzip"
 	"github.com/stretchr/testify/assert"
 )
@@ -132,7 +132,7 @@ func TestGetLFSSmallTokenFail(t *testing.T) {
 
 func TestGetLFSLarge(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
-	content := make([]byte, web.GzipMinSize*10)
+	content := make([]byte, gzhttp.DefaultMinSize*10)
 	for i := range content {
 		content[i] = byte(i % 256)
 	}
@@ -143,7 +143,7 @@ func TestGetLFSLarge(t *testing.T) {
 
 func TestGetLFSGzip(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
-	b := make([]byte, web.GzipMinSize*10)
+	b := make([]byte, gzhttp.DefaultMinSize*10)
 	for i := range b {
 		b[i] = byte(i % 256)
 	}
@@ -159,7 +159,7 @@ func TestGetLFSGzip(t *testing.T) {
 
 func TestGetLFSZip(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
-	b := make([]byte, web.GzipMinSize*10)
+	b := make([]byte, gzhttp.DefaultMinSize*10)
 	for i := range b {
 		b[i] = byte(i % 256)
 	}
diff --git a/tests/integration/release_test.go b/tests/integration/release_test.go
index 42d0d00e78..439e315347 100644
--- a/tests/integration/release_test.go
+++ b/tests/integration/release_test.go
@@ -21,6 +21,10 @@ import (
 )
 
 func createNewRelease(t *testing.T, session *TestSession, repoURL, tag, title string, preRelease, draft bool) {
+	createNewReleaseTarget(t, session, repoURL, tag, title, "master", preRelease, draft)
+}
+
+func createNewReleaseTarget(t *testing.T, session *TestSession, repoURL, tag, title, target string, preRelease, draft bool) {
 	req := NewRequest(t, "GET", repoURL+"/releases/new")
 	resp := session.MakeRequest(t, req, http.StatusOK)
 	htmlDoc := NewHTMLParser(t, resp.Body)
@@ -31,7 +35,7 @@ func createNewRelease(t *testing.T, session *TestSession, repoURL, tag, title st
 	postData := map[string]string{
 		"_csrf":      htmlDoc.GetCSRF(),
 		"tag_name":   tag,
-		"tag_target": "master",
+		"tag_target": target,
 		"title":      title,
 		"content":    "",
 	}
@@ -217,6 +221,15 @@ func TestViewReleaseListLogin(t *testing.T) {
 	}, links)
 }
 
+func TestReleaseOnCommit(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	session := loginUser(t, "user2")
+	createNewReleaseTarget(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", "65f1bf27bc3bf70f64657658635e66094edbcb4d", false, false)
+
+	checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", translation.NewLocale("en-US").Tr("repo.release.stable"), 4)
+}
+
 func TestViewTagsList(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 
diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go
index d6de72553c..2ac1632188 100644
--- a/tests/integration/repo_test.go
+++ b/tests/integration/repo_test.go
@@ -203,6 +203,110 @@ func TestViewAsRepoAdmin(t *testing.T) {
 	}
 }
 
+func TestRepoHTMLTitle(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	t.Run("Repository homepage", func(t *testing.T) {
+		t.Run("Without description", func(t *testing.T) {
+			defer tests.PrintCurrentTest(t)()
+
+			htmlTitle := GetHTMLTitle(t, nil, "/user2/repo1")
+			assert.EqualValues(t, "user2/repo1 - Gitea: Git with a cup of tea", htmlTitle)
+		})
+		t.Run("With description", func(t *testing.T) {
+			defer tests.PrintCurrentTest(t)()
+
+			htmlTitle := GetHTMLTitle(t, nil, "/user27/repo49")
+			assert.EqualValues(t, "user27/repo49: A wonderful repository with more than just a README.md - Gitea: Git with a cup of tea", htmlTitle)
+		})
+	})
+
+	t.Run("Code view", func(t *testing.T) {
+		t.Run("Directory", func(t *testing.T) {
+			t.Run("Default branch", func(t *testing.T) {
+				defer tests.PrintCurrentTest(t)()
+
+				htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/branch/master/deep/nesting")
+				assert.EqualValues(t, "repo59/deep/nesting at master - user2/repo59 - Gitea: Git with a cup of tea", htmlTitle)
+			})
+			t.Run("Non-default branch", func(t *testing.T) {
+				defer tests.PrintCurrentTest(t)()
+
+				htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/branch/cake-recipe/deep/nesting")
+				assert.EqualValues(t, "repo59/deep/nesting at cake-recipe - user2/repo59 - Gitea: Git with a cup of tea", htmlTitle)
+			})
+			t.Run("Commit", func(t *testing.T) {
+				defer tests.PrintCurrentTest(t)()
+
+				htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/commit/d8f53dfb33f6ccf4169c34970b5e747511c18beb/deep/nesting/")
+				assert.EqualValues(t, "repo59/deep/nesting at d8f53dfb33f6ccf4169c34970b5e747511c18beb - user2/repo59 - Gitea: Git with a cup of tea", htmlTitle)
+			})
+			t.Run("Tag", func(t *testing.T) {
+				defer tests.PrintCurrentTest(t)()
+
+				htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/tag/v1.0/deep/nesting/")
+				assert.EqualValues(t, "repo59/deep/nesting at v1.0 - user2/repo59 - Gitea: Git with a cup of tea", htmlTitle)
+			})
+		})
+		t.Run("File", func(t *testing.T) {
+			t.Run("Default branch", func(t *testing.T) {
+				defer tests.PrintCurrentTest(t)()
+
+				htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/branch/master/deep/nesting/folder/secret_sauce_recipe.txt")
+				assert.EqualValues(t, "repo59/deep/nesting/folder/secret_sauce_recipe.txt at master - user2/repo59 - Gitea: Git with a cup of tea", htmlTitle)
+			})
+			t.Run("Non-default branch", func(t *testing.T) {
+				defer tests.PrintCurrentTest(t)()
+
+				htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/branch/cake-recipe/deep/nesting/folder/secret_sauce_recipe.txt")
+				assert.EqualValues(t, "repo59/deep/nesting/folder/secret_sauce_recipe.txt at cake-recipe - user2/repo59 - Gitea: Git with a cup of tea", htmlTitle)
+			})
+			t.Run("Commit", func(t *testing.T) {
+				defer tests.PrintCurrentTest(t)()
+
+				htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/commit/d8f53dfb33f6ccf4169c34970b5e747511c18beb/deep/nesting/folder/secret_sauce_recipe.txt")
+				assert.EqualValues(t, "repo59/deep/nesting/folder/secret_sauce_recipe.txt at d8f53dfb33f6ccf4169c34970b5e747511c18beb - user2/repo59 - Gitea: Git with a cup of tea", htmlTitle)
+			})
+			t.Run("Tag", func(t *testing.T) {
+				defer tests.PrintCurrentTest(t)()
+
+				htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/tag/v1.0/deep/nesting/folder/secret_sauce_recipe.txt")
+				assert.EqualValues(t, "repo59/deep/nesting/folder/secret_sauce_recipe.txt at v1.0 - user2/repo59 - Gitea: Git with a cup of tea", htmlTitle)
+			})
+		})
+	})
+
+	t.Run("Issues view", func(t *testing.T) {
+		t.Run("Overview page", func(t *testing.T) {
+			defer tests.PrintCurrentTest(t)()
+
+			htmlTitle := GetHTMLTitle(t, nil, "/user2/repo1/issues")
+			assert.EqualValues(t, "Issues - user2/repo1 - Gitea: Git with a cup of tea", htmlTitle)
+		})
+		t.Run("View issue page", func(t *testing.T) {
+			defer tests.PrintCurrentTest(t)()
+
+			htmlTitle := GetHTMLTitle(t, nil, "/user2/repo1/issues/1")
+			assert.EqualValues(t, "#1 - issue1 - user2/repo1 - Gitea: Git with a cup of tea", htmlTitle)
+		})
+	})
+
+	t.Run("Pull requests view", func(t *testing.T) {
+		t.Run("Overview page", func(t *testing.T) {
+			defer tests.PrintCurrentTest(t)()
+
+			htmlTitle := GetHTMLTitle(t, nil, "/user2/repo1/pulls")
+			assert.EqualValues(t, "Pull Requests - user2/repo1 - Gitea: Git with a cup of tea", htmlTitle)
+		})
+		t.Run("View pull request", func(t *testing.T) {
+			defer tests.PrintCurrentTest(t)()
+
+			htmlTitle := GetHTMLTitle(t, nil, "/user2/repo1/pulls/2")
+			assert.EqualValues(t, "#2 - issue2 - user2/repo1 - Gitea: Git with a cup of tea", htmlTitle)
+		})
+	})
+}
+
 // TestViewFileInRepo repo description, topics and summary should not be displayed when viewing a file
 func TestViewFileInRepo(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
@@ -586,3 +690,33 @@ func TestDangerZoneConfirmation(t *testing.T) {
 		})
 	})
 }
+
+func TestRenamedFileHistory(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	t.Run("Renamed file", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
+
+		req := NewRequest(t, "GET", "/user2/repo59/commits/branch/master/license")
+		resp := MakeRequest(t, req, http.StatusOK)
+
+		htmlDoc := NewHTMLParser(t, resp.Body)
+
+		renameNotice := htmlDoc.doc.Find(".ui.bottom.attached.header")
+		assert.Equal(t, 1, renameNotice.Length())
+		assert.Contains(t, renameNotice.Text(), "Renamed from licnse (Browse further)")
+
+		oldFileHistoryLink, ok := renameNotice.Find("a").Attr("href")
+		assert.True(t, ok)
+		assert.Equal(t, "/user2/repo59/commits/commit/80b83c5c8220c3aa3906e081f202a2a7563ec879/licnse", oldFileHistoryLink)
+	})
+
+	t.Run("Non renamed file", func(t *testing.T) {
+		req := NewRequest(t, "GET", "/user2/repo59/commits/branch/master/README.md")
+		resp := MakeRequest(t, req, http.StatusOK)
+
+		htmlDoc := NewHTMLParser(t, resp.Body)
+
+		htmlDoc.AssertElement(t, ".ui.bottom.attached.header", false)
+	})
+}
diff --git a/web_src/css/repo/issue-list.css b/web_src/css/repo/issue-list.css
index d2f8e429f5..15ea70bbc9 100644
--- a/web_src/css/repo/issue-list.css
+++ b/web_src/css/repo/issue-list.css
@@ -17,10 +17,6 @@
 }
 
 @media (max-width: 767.98px) {
-  .issue-list-toolbar-right .dropdown .menu {
-    left: auto !important;
-    right: auto !important;
-  }
   .issue-list-navbar {
     order: 0;
   }
@@ -31,6 +27,37 @@
   .issue-list-search {
     order: 2 !important;
   }
+  /* Don't use flex wrap on mobile as it takes too much vertical space.
+   * Only set overflow properties on mobile screens, because while the
+   * CSS trick to pop out from overflowing works on desktop screen, it
+   * has a massive flaw that it cannot inherited any max width from it's 'real'
+   * parent and therefor ends up taking more vertical space than is desired.
+   **/
+  .issue-list-toolbar-right .filter.menu {
+    flex-wrap: nowrap;
+    overflow-x: auto;
+    overflow-y: hidden;
+  }
+
+  /* The following few CSS was created with care and built with the information
+   * from CSS-Tricks: https://css-tricks.com/popping-hidden-overflow/
+  */
+
+  /* It's important that every element up to .issue-list-toolbar-right doesn't
+   * have a position set, such that element that wants to pop out will use
+   * .issue-list-toolbar-right as 'clip parent' and thereby avoids the
+   * overflow-y: hidden.
+  */
+  .issue-list-toolbar-right .filter.menu > .dropdown.item {
+    position: initial;
+  }
+  /* It's important that this element and not an child has `position` set.
+   * Set width so that overflow-x knows where to stop overflowing.
+  */
+  .issue-list-toolbar-right {
+    position: relative;
+    width: 100%;
+  }
 }
 
 #issue-list .flex-item-title .labels-list {