Merge "Make sure to set the right rootDir for app/api"
diff --git a/Documentation/BUILD b/Documentation/BUILD
index 11d3efa..af355ca 100644
--- a/Documentation/BUILD
+++ b/Documentation/BUILD
@@ -53,6 +53,14 @@
 )
 
 license_map(
+    name = "backend_licenses",
+    opts = ["--asciidoctor"],
+    targets = [
+        "//java/com/google/gerrit/pgm",
+    ],
+)
+
+license_map(
     name = "js_licenses",
     json_maps = [
         "//polygerrit-ui/app/node_modules_licenses:polygerrit-licenses.json",
@@ -66,6 +74,8 @@
     name = "check_licenses",
     srcs = ["check_licenses_test.sh"],
     data = [
+        "backend_licenses.gen.txt",
+        "backend_licenses.txt",
         "js_licenses.gen.txt",
         "js_licenses.txt",
         "licenses.gen.txt",
diff --git a/Documentation/backend_licenses.txt b/Documentation/backend_licenses.txt
new file mode 100755
index 0000000..3113682
--- /dev/null
+++ b/Documentation/backend_licenses.txt
@@ -0,0 +1,3176 @@
+= Gerrit Code Review - Licenses
+
+// DO NOT EDIT - GENERATED AUTOMATICALLY.
+
+Gerrit open source software is licensed under the <<Apache2_0,Apache
+License 2.0>>.  Executable distributions also include other software
+components that are provided under additional licenses.
+
+[[cryptography]]
+== Cryptography Notice
+
+This distribution includes cryptographic software.  The country
+in which you currently reside may have restrictions on the import,
+possession, use, and/or re-export to another country, of encryption
+software.  BEFORE using any encryption software, please check
+your country's laws, regulations and policies concerning the
+import, possession, or use, and re-export of encryption software,
+to see if this is permitted.  See the
+link:http://www.wassenaar.org/[Wassenaar Arrangement]
+for more information.
+
+The U.S. Government Department of Commerce, Bureau of Industry
+and Security (BIS), has classified this software as Export
+Commodity Control Number (ECCN) 5D002.C.1, which includes
+information security software using or performing cryptographic
+functions with asymmetric algorithms.  The form and manner of
+this distribution makes it eligible for export under the License
+Exception ENC Technology Software Unrestricted (TSU) exception
+(see the BIS Export Administration Regulations, Section 740.13)
+for both object code and source code.
+
+Gerrit includes an SSH daemon (Apache SSHD), to support authenticated
+uploads of changes directly from `git push` command line clients.
+
+Gerrit includes an SSH client (JSch), to support authenticated
+replication of changes to remote systems, such as for automatic
+updates of mirror servers, or realtime backups.
+
+== Licenses
+
+
+[[Apache2_0]]
+Apache2.0
+
+* auto:auto-value
+* auto:auto-value-annotations
+* auto:auto-value-gson
+* commons:codec
+* commons:compress
+* commons:dbcp
+* commons:lang
+* commons:lang3
+* commons:net
+* commons:pool
+* commons:validator
+* dropwizard:dropwizard-core
+* errorprone:annotations
+* flogger:api
+* guice:guice
+* guice:guice-assistedinject
+* guice:guice-library
+* guice:guice-servlet
+* guice:javax_inject
+* httpcomponents:httpasyncclient
+* httpcomponents:httpclient
+* httpcomponents:httpcore
+* httpcomponents:httpcore-nio
+* jackson:jackson-core
+* jetty:http
+* jetty:io
+* jetty:jmx
+* jetty:security
+* jetty:server
+* jetty:servlet
+* jetty:util
+* jetty:util-ajax
+* log:log4j
+* lucene:lucene-analyzers-common
+* lucene:lucene-core-and-backward-codecs-merged
+* lucene:lucene-misc
+* lucene:lucene-queryparser
+* mime4j:core
+* mime4j:dom
+* mina:core
+* mina:sshd
+* mina:sshd-sftp
+* openid:consumer
+* openid:nekohtml
+* openid:xerces
+* blame-cache
+* caffeine
+* caffeine-guava
+* gson
+* guava
+* guava-failureaccess
+* guava-retrying
+* html-types
+* j2objc
+* javaewah
+* jsr305
+* mime-util
+* servlet-api
+* servlet-api-without-neverlink
+* soy
+
+[[Apache2_0_license]]
+----
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+----
+
+
+[[CC0-1_0]]
+CC0-1.0
+
+* mina:eddsa
+
+[[CC0-1_0_license]]
+----
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
+
+For more information, please see https://creativecommons.org/publicdomain/zero/1.0/
+
+----
+
+
+[[MPL1_1]]
+MPL1.1
+
+* juniversalchardet
+
+[[MPL1_1_license]]
+----
+                          MOZILLA PUBLIC LICENSE
+                                Version 1.1
+
+                              ---------------
+
+1. Definitions.
+
+     1.0.1. "Commercial Use" means distribution or otherwise making the
+     Covered Code available to a third party.
+
+     1.1. "Contributor" means each entity that creates or contributes to
+     the creation of Modifications.
+
+     1.2. "Contributor Version" means the combination of the Original
+     Code, prior Modifications used by a Contributor, and the Modifications
+     made by that particular Contributor.
+
+     1.3. "Covered Code" means the Original Code or Modifications or the
+     combination of the Original Code and Modifications, in each case
+     including portions thereof.
+
+     1.4. "Electronic Distribution Mechanism" means a mechanism generally
+     accepted in the software development community for the electronic
+     transfer of data.
+
+     1.5. "Executable" means Covered Code in any form other than Source
+     Code.
+
+     1.6. "Initial Developer" means the individual or entity identified
+     as the Initial Developer in the Source Code notice required by Exhibit
+     A.
+
+     1.7. "Larger Work" means a work which combines Covered Code or
+     portions thereof with code not governed by the terms of this License.
+
+     1.8. "License" means this document.
+
+     1.8.1. "Licensable" means having the right to grant, to the maximum
+     extent possible, whether at the time of the initial grant or
+     subsequently acquired, any and all of the rights conveyed herein.
+
+     1.9. "Modifications" means any addition to or deletion from the
+     substance or structure of either the Original Code or any previous
+     Modifications. When Covered Code is released as a series of files, a
+     Modification is:
+          A. Any addition to or deletion from the contents of a file
+          containing Original Code or previous Modifications.
+
+          B. Any new file that contains any part of the Original Code or
+          previous Modifications.
+
+     1.10. "Original Code" means Source Code of computer software code
+     which is described in the Source Code notice required by Exhibit A as
+     Original Code, and which, at the time of its release under this
+     License is not already Covered Code governed by this License.
+
+     1.10.1. "Patent Claims" means any patent claim(s), now owned or
+     hereafter acquired, including without limitation,  method, process,
+     and apparatus claims, in any patent Licensable by grantor.
+
+     1.11. "Source Code" means the preferred form of the Covered Code for
+     making modifications to it, including all modules it contains, plus
+     any associated interface definition files, scripts used to control
+     compilation and installation of an Executable, or source code
+     differential comparisons against either the Original Code or another
+     well known, available Covered Code of the Contributor's choice. The
+     Source Code can be in a compressed or archival form, provided the
+     appropriate decompression or de-archiving software is widely available
+     for no charge.
+
+     1.12. "You" (or "Your")  means an individual or a legal entity
+     exercising rights under, and complying with all of the terms of, this
+     License or a future version of this License issued under Section 6.1.
+     For legal entities, "You" includes any entity which controls, is
+     controlled by, or is under common control with You. For purposes of
+     this definition, "control" means (a) the power, direct or indirect,
+     to cause the direction or management of such entity, whether by
+     contract or otherwise, or (b) ownership of more than fifty percent
+     (50%) of the outstanding shares or beneficial ownership of such
+     entity.
+
+2. Source Code License.
+
+     2.1. The Initial Developer Grant.
+     The Initial Developer hereby grants You a world-wide, royalty-free,
+     non-exclusive license, subject to third party intellectual property
+     claims:
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Initial Developer to use, reproduce,
+          modify, display, perform, sublicense and distribute the Original
+          Code (or portions thereof) with or without Modifications, and/or
+          as part of a Larger Work; and
+
+          (b) under Patents Claims infringed by the making, using or
+          selling of Original Code, to make, have made, use, practice,
+          sell, and offer for sale, and/or otherwise dispose of the
+          Original Code (or portions thereof).
+
+          (c) the licenses granted in this Section 2.1(a) and (b) are
+          effective on the date Initial Developer first distributes
+          Original Code under the terms of this License.
+
+          (d) Notwithstanding Section 2.1(b) above, no patent license is
+          granted: 1) for code that You delete from the Original Code; 2)
+          separate from the Original Code;  or 3) for infringements caused
+          by: i) the modification of the Original Code or ii) the
+          combination of the Original Code with other software or devices.
+
+     2.2. Contributor Grant.
+     Subject to third party intellectual property claims, each Contributor
+     hereby grants You a world-wide, royalty-free, non-exclusive license
+
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Contributor, to use, reproduce, modify,
+          display, perform, sublicense and distribute the Modifications
+          created by such Contributor (or portions thereof) either on an
+          unmodified basis, with other Modifications, as Covered Code
+          and/or as part of a Larger Work; and
+
+          (b) under Patent Claims infringed by the making, using, or
+          selling of  Modifications made by that Contributor either alone
+          and/or in combination with its Contributor Version (or portions
+          of such combination), to make, use, sell, offer for sale, have
+          made, and/or otherwise dispose of: 1) Modifications made by that
+          Contributor (or portions thereof); and 2) the combination of
+          Modifications made by that Contributor with its Contributor
+          Version (or portions of such combination).
+
+          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
+          effective on the date Contributor first makes Commercial Use of
+          the Covered Code.
+
+          (d)    Notwithstanding Section 2.2(b) above, no patent license is
+          granted: 1) for any code that Contributor has deleted from the
+          Contributor Version; 2)  separate from the Contributor Version;
+          3)  for infringements caused by: i) third party modifications of
+          Contributor Version or ii)  the combination of Modifications made
+          by that Contributor with other software  (except as part of the
+          Contributor Version) or other devices; or 4) under Patent Claims
+          infringed by Covered Code in the absence of Modifications made by
+          that Contributor.
+
+3. Distribution Obligations.
+
+     3.1. Application of License.
+     The Modifications which You create or to which You contribute are
+     governed by the terms of this License, including without limitation
+     Section 2.2. The Source Code version of Covered Code may be
+     distributed only under the terms of this License or a future version
+     of this License released under Section 6.1, and You must include a
+     copy of this License with every copy of the Source Code You
+     distribute. You may not offer or impose any terms on any Source Code
+     version that alters or restricts the applicable version of this
+     License or the recipients' rights hereunder. However, You may include
+     an additional document offering the additional rights described in
+     Section 3.5.
+
+     3.2. Availability of Source Code.
+     Any Modification which You create or to which You contribute must be
+     made available in Source Code form under the terms of this License
+     either on the same media as an Executable version or via an accepted
+     Electronic Distribution Mechanism to anyone to whom you made an
+     Executable version available; and if made available via Electronic
+     Distribution Mechanism, must remain available for at least twelve (12)
+     months after the date it initially became available, or at least six
+     (6) months after a subsequent version of that particular Modification
+     has been made available to such recipients. You are responsible for
+     ensuring that the Source Code version remains available even if the
+     Electronic Distribution Mechanism is maintained by a third party.
+
+     3.3. Description of Modifications.
+     You must cause all Covered Code to which You contribute to contain a
+     file documenting the changes You made to create that Covered Code and
+     the date of any change. You must include a prominent statement that
+     the Modification is derived, directly or indirectly, from Original
+     Code provided by the Initial Developer and including the name of the
+     Initial Developer in (a) the Source Code, and (b) in any notice in an
+     Executable version or related documentation in which You describe the
+     origin or ownership of the Covered Code.
+
+     3.4. Intellectual Property Matters
+          (a) Third Party Claims.
+          If Contributor has knowledge that a license under a third party's
+          intellectual property rights is required to exercise the rights
+          granted by such Contributor under Sections 2.1 or 2.2,
+          Contributor must include a text file with the Source Code
+          distribution titled "LEGAL" which describes the claim and the
+          party making the claim in sufficient detail that a recipient will
+          know whom to contact. If Contributor obtains such knowledge after
+          the Modification is made available as described in Section 3.2,
+          Contributor shall promptly modify the LEGAL file in all copies
+          Contributor makes available thereafter and shall take other steps
+          (such as notifying appropriate mailing lists or newsgroups)
+          reasonably calculated to inform those who received the Covered
+          Code that new knowledge has been obtained.
+
+          (b) Contributor APIs.
+          If Contributor's Modifications include an application programming
+          interface and Contributor has knowledge of patent licenses which
+          are reasonably necessary to implement that API, Contributor must
+          also include this information in the LEGAL file.
+
+               (c)    Representations.
+          Contributor represents that, except as disclosed pursuant to
+          Section 3.4(a) above, Contributor believes that Contributor's
+          Modifications are Contributor's original creation(s) and/or
+          Contributor has sufficient rights to grant the rights conveyed by
+          this License.
+
+     3.5. Required Notices.
+     You must duplicate the notice in Exhibit A in each file of the Source
+     Code.  If it is not possible to put such notice in a particular Source
+     Code file due to its structure, then You must include such notice in a
+     location (such as a relevant directory) where a user would be likely
+     to look for such a notice.  If You created one or more Modification(s)
+     You may add your name as a Contributor to the notice described in
+     Exhibit A.  You must also duplicate this License in any documentation
+     for the Source Code where You describe recipients' rights or ownership
+     rights relating to Covered Code.  You may choose to offer, and to
+     charge a fee for, warranty, support, indemnity or liability
+     obligations to one or more recipients of Covered Code. However, You
+     may do so only on Your own behalf, and not on behalf of the Initial
+     Developer or any Contributor. You must make it absolutely clear than
+     any such warranty, support, indemnity or liability obligation is
+     offered by You alone, and You hereby agree to indemnify the Initial
+     Developer and every Contributor for any liability incurred by the
+     Initial Developer or such Contributor as a result of warranty,
+     support, indemnity or liability terms You offer.
+
+     3.6. Distribution of Executable Versions.
+     You may distribute Covered Code in Executable form only if the
+     requirements of Section 3.1-3.5 have been met for that Covered Code,
+     and if You include a notice stating that the Source Code version of
+     the Covered Code is available under the terms of this License,
+     including a description of how and where You have fulfilled the
+     obligations of Section 3.2. The notice must be conspicuously included
+     in any notice in an Executable version, related documentation or
+     collateral in which You describe recipients' rights relating to the
+     Covered Code. You may distribute the Executable version of Covered
+     Code or ownership rights under a license of Your choice, which may
+     contain terms different from this License, provided that You are in
+     compliance with the terms of this License and that the license for the
+     Executable version does not attempt to limit or alter the recipient's
+     rights in the Source Code version from the rights set forth in this
+     License. If You distribute the Executable version under a different
+     license You must make it absolutely clear that any terms which differ
+     from this License are offered by You alone, not by the Initial
+     Developer or any Contributor. You hereby agree to indemnify the
+     Initial Developer and every Contributor for any liability incurred by
+     the Initial Developer or such Contributor as a result of any such
+     terms You offer.
+
+     3.7. Larger Works.
+     You may create a Larger Work by combining Covered Code with other code
+     not governed by the terms of this License and distribute the Larger
+     Work as a single product. In such a case, You must make sure the
+     requirements of this License are fulfilled for the Covered Code.
+
+4. Inability to Comply Due to Statute or Regulation.
+
+     If it is impossible for You to comply with any of the terms of this
+     License with respect to some or all of the Covered Code due to
+     statute, judicial order, or regulation then You must: (a) comply with
+     the terms of this License to the maximum extent possible; and (b)
+     describe the limitations and the code they affect. Such description
+     must be included in the LEGAL file described in Section 3.4 and must
+     be included with all distributions of the Source Code. Except to the
+     extent prohibited by statute or regulation, such description must be
+     sufficiently detailed for a recipient of ordinary skill to be able to
+     understand it.
+
+5. Application of this License.
+
+     This License applies to code to which the Initial Developer has
+     attached the notice in Exhibit A and to related Covered Code.
+
+6. Versions of the License.
+
+     6.1. New Versions.
+     Netscape Communications Corporation ("Netscape") may publish revised
+     and/or new versions of the License from time to time. Each version
+     will be given a distinguishing version number.
+
+     6.2. Effect of New Versions.
+     Once Covered Code has been published under a particular version of the
+     License, You may always continue to use it under the terms of that
+     version. You may also choose to use such Covered Code under the terms
+     of any subsequent version of the License published by Netscape. No one
+     other than Netscape has the right to modify the terms applicable to
+     Covered Code created under this License.
+
+     6.3. Derivative Works.
+     If You create or use a modified version of this License (which you may
+     only do in order to apply it to code which is not already Covered Code
+     governed by this License), You must (a) rename Your license so that
+     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
+     "MPL", "NPL" or any confusingly similar phrase do not appear in your
+     license (except to note that your license differs from this License)
+     and (b) otherwise make it clear that Your version of the license
+     contains terms which differ from the Mozilla Public License and
+     Netscape Public License. (Filling in the name of the Initial
+     Developer, Original Code or Contributor in the notice described in
+     Exhibit A shall not of themselves be deemed to be modifications of
+     this License.)
+
+7. DISCLAIMER OF WARRANTY.
+
+     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
+     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
+     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
+     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
+     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
+     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+8. TERMINATION.
+
+     8.1.  This License and the rights granted hereunder will terminate
+     automatically if You fail to comply with terms herein and fail to cure
+     such breach within 30 days of becoming aware of the breach. All
+     sublicenses to the Covered Code which are properly granted shall
+     survive any termination of this License. Provisions which, by their
+     nature, must remain in effect beyond the termination of this License
+     shall survive.
+
+     8.2.  If You initiate litigation by asserting a patent infringement
+     claim (excluding declatory judgment actions) against Initial Developer
+     or a Contributor (the Initial Developer or Contributor against whom
+     You file such action is referred to as "Participant")  alleging that:
+
+     (a)  such Participant's Contributor Version directly or indirectly
+     infringes any patent, then any and all rights granted by such
+     Participant to You under Sections 2.1 and/or 2.2 of this License
+     shall, upon 60 days notice from Participant terminate prospectively,
+     unless if within 60 days after receipt of notice You either: (i)
+     agree in writing to pay Participant a mutually agreeable reasonable
+     royalty for Your past and future use of Modifications made by such
+     Participant, or (ii) withdraw Your litigation claim with respect to
+     the Contributor Version against such Participant.  If within 60 days
+     of notice, a reasonable royalty and payment arrangement are not
+     mutually agreed upon in writing by the parties or the litigation claim
+     is not withdrawn, the rights granted by Participant to You under
+     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
+     the 60 day notice period specified above.
+
+     (b)  any software, hardware, or device, other than such Participant's
+     Contributor Version, directly or indirectly infringes any patent, then
+     any rights granted to You by such Participant under Sections 2.1(b)
+     and 2.2(b) are revoked effective as of the date You first made, used,
+     sold, distributed, or had made, Modifications made by that
+     Participant.
+
+     8.3.  If You assert a patent infringement claim against Participant
+     alleging that such Participant's Contributor Version directly or
+     indirectly infringes any patent where such claim is resolved (such as
+     by license or settlement) prior to the initiation of patent
+     infringement litigation, then the reasonable value of the licenses
+     granted by such Participant under Sections 2.1 or 2.2 shall be taken
+     into account in determining the amount or value of any payment or
+     license.
+
+     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
+     all end user license agreements (excluding distributors and resellers)
+     which have been validly granted by You or any distributor hereunder
+     prior to termination shall survive termination.
+
+9. LIMITATION OF LIABILITY.
+
+     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
+     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
+     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
+     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
+     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
+     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
+     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+10. U.S. GOVERNMENT END USERS.
+
+     The Covered Code is a "commercial item," as that term is defined in
+     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+     software" and "commercial computer software documentation," as such
+     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
+     all U.S. Government End Users acquire Covered Code with only those
+     rights set forth herein.
+
+11. MISCELLANEOUS.
+
+     This License represents the complete agreement concerning subject
+     matter hereof. If any provision of this License is held to be
+     unenforceable, such provision shall be reformed only to the extent
+     necessary to make it enforceable. This License shall be governed by
+     California law provisions (except to the extent applicable law, if
+     any, provides otherwise), excluding its conflict-of-law provisions.
+     With respect to disputes in which at least one party is a citizen of,
+     or an entity chartered or registered to do business in the United
+     States of America, any litigation relating to this License shall be
+     subject to the jurisdiction of the Federal Courts of the Northern
+     District of California, with venue lying in Santa Clara County,
+     California, with the losing party responsible for costs, including
+     without limitation, court costs and reasonable attorneys' fees and
+     expenses. The application of the United Nations Convention on
+     Contracts for the International Sale of Goods is expressly excluded.
+     Any law or regulation which provides that the language of a contract
+     shall be construed against the drafter shall not apply to this
+     License.
+
+12. RESPONSIBILITY FOR CLAIMS.
+
+     As between Initial Developer and the Contributors, each party is
+     responsible for claims and damages arising, directly or indirectly,
+     out of its utilization of rights under this License and You agree to
+     work with Initial Developer and Contributors to distribute such
+     responsibility on an equitable basis. Nothing herein is intended or
+     shall be deemed to constitute any admission of liability.
+
+13. MULTIPLE-LICENSED CODE.
+
+     Initial Developer may designate portions of the Covered Code as
+     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
+     Developer permits you to utilize portions of the Covered Code under
+     Your choice of the NPL or the alternative licenses, if any, specified
+     by the Initial Developer in the file described in Exhibit A.
+
+EXHIBIT A -Mozilla Public License.
+
+     ``The contents of this file are subject to the Mozilla Public License
+     Version 1.1 (the "License"); you may not use this file except in
+     compliance with the License. You may obtain a copy of the License at
+     http://www.mozilla.org/MPL/
+
+     Software distributed under the License is distributed on an "AS IS"
+     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+     License for the specific language governing rights and limitations
+     under the License.
+
+     The Original Code is ______________________________________.
+
+     The Initial Developer of the Original Code is ________________________.
+     Portions created by ______________________ are Copyright (C) ______
+     _______________________. All Rights Reserved.
+
+     Contributor(s): ______________________________________.
+
+     Alternatively, the contents of this file may be used under the terms
+     of the _____ license (the  "[___] License"), in which case the
+     provisions of [______] License are applicable instead of those
+     above.  If you wish to allow use of your version of this file only
+     under the terms of the [____] License and not to allow others to use
+     your version of this file under the MPL, indicate your decision by
+     deleting  the provisions above and replace  them with the notice and
+     other provisions required by the [___] License.  If you do not delete
+     the provisions above, a recipient may use your version of this file
+     under either the MPL or the [___] License."
+
+     [NOTE: The text of this Exhibit A may differ slightly from the text of
+     the notices in the Source Code files of the Original Code. You should
+     use the text of this Exhibit A rather than the text found in the
+     Original Code Source Code for Your Modifications.]
+
+----
+
+
+[[PublicDomain]]
+PublicDomain
+
+* guice:aopalliance
+
+[[PublicDomain_license]]
+----
+This software has been placed in the public domain by its author(s).
+
+----
+
+
+[[antlr]]
+antlr
+
+* antlr:antlr27
+* antlr:java-runtime
+* antlr:stringtemplate
+* antlr:tool
+
+[[antlr_license]]
+----
+Copyright (c) 2003-2008, Terence Parr
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of the author nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+----
+
+
+[[args4j]]
+args4j
+
+* args4j
+
+[[args4j_license]]
+----
+Copyright (c) 2013 Kohsuke Kawaguchi and other contributors
+
+Permission 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:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE 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.
+
+----
+
+
+[[autolink]]
+autolink
+
+* autolink
+
+[[autolink_license]]
+----
+The MIT License (MIT)
+
+Copyright (c) 2015 Robin Stocker
+
+Permission 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:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE 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.
+
+----
+
+
+[[automaton]]
+automaton
+
+* automaton
+
+[[automaton_license]]
+----
+Copyright (c) 2001-2011 Anders Moeller
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of the JSR305 expert group nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+----
+
+
+[[bouncycastle]]
+bouncycastle
+
+* bouncycastle:bcpg-neverlink
+* bouncycastle:bcpkix-neverlink
+* bouncycastle:bcprov-neverlink
+
+[[bouncycastle_license]]
+----
+Copyright (c) 2000 - 2015 The Legion of the Bouncy Castle Inc.
+(http://www.bouncycastle.org)
+
+Permission 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:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE 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.
+
+----
+
+
+[[elasticsearch]]
+elasticsearch
+
+* elasticsearch-rest-client:elasticsearch-rest-client
+
+[[elasticsearch_license]]
+----
+Elasticsearch
+Copyright 2009-2015 Elasticsearch
+
+This product includes software developed by The Apache Software
+Foundation (http://www.apache.org/).
+
+----
+
+
+[[flexmark]]
+flexmark
+
+* flexmark
+* flexmark-ext-abbreviation
+* flexmark-ext-anchorlink
+* flexmark-ext-autolink
+* flexmark-ext-definition
+* flexmark-ext-emoji
+* flexmark-ext-escaped-character
+* flexmark-ext-footnotes
+* flexmark-ext-gfm-issues
+* flexmark-ext-gfm-strikethrough
+* flexmark-ext-gfm-tables
+* flexmark-ext-gfm-tasklist
+* flexmark-ext-gfm-users
+* flexmark-ext-ins
+* flexmark-ext-jekyll-front-matter
+* flexmark-ext-superscript
+* flexmark-ext-tables
+* flexmark-ext-toc
+* flexmark-ext-typographic
+* flexmark-ext-wikilink
+* flexmark-ext-yaml-front-matter
+* flexmark-formatter
+* flexmark-html-parser
+* flexmark-profile-pegdown
+* flexmark-util
+
+[[flexmark_license]]
+----
+Copyright (c) 2015-2016, Atlassian Pty Ltd
+All rights reserved.
+
+Copyright (c) 2016, Vladimir Schneider,
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----
+
+
+[[h2]]
+h2
+
+* h2
+
+[[h2_license]]
+----
+H2 is dual licensed and available under a modified version of the
+MPL 1.1 (Mozilla Public License) or under the (unmodified) EPL 1.0.
+----
+
+link:http://www.h2database.com/html/license.html[H2 License]
+
+----
+H2 License - Version 1.0
+1. Definitions
+
+1.0.1. "Commercial Use" means distribution or otherwise making the
+       Covered Code available to a third party.
+
+1.1. "Contributor" means each entity that creates or contributes
+     to the creation of Modifications.
+
+1.2. "Contributor Version" means the combination of the Original
+     Code, prior Modifications used by a Contributor, and the
+     Modifications made by that particular Contributor.
+
+1.3. "Covered Code" means the Original Code or Modifications or
+     the combination of the Original Code and Modifications, in each
+     case including portions thereof.
+
+1.4. "Electronic Distribution Mechanism" means a mechanism generally
+     accepted in the software development community for the electronic
+     transfer of data.
+
+1.5. "Executable" means Covered Code in any form other than Source Code.
+
+1.6. "Initial Developer" means the individual or entity identified
+     as the Initial Developer in the Source Code notice required
+     by Exhibit A.
+
+1.7. "Larger Work" means a work which combines Covered Code or
+     portions thereof with code not governed by the terms of this
+     License.
+
+1.8. "License" means this document.
+
+1.8.1. "Licensable" means having the right to grant, to the maximum
+       extent possible, whether at the time of the initial grant
+       or subsequently acquired, any and all of the rights conveyed
+       herein.
+
+1.9. "Modifications" means any addition to or deletion from the
+     substance or structure of either the Original Code or any
+     previous Modifications. When Covered Code is released as a
+     series of files, a Modification is:
+
+1.9.a. Any addition to or deletion from the contents of a file
+       containing Original Code or previous Modifications.
+
+1.9.b. Any new file that contains any part of the Original Code or
+       previous Modifications.
+
+1.10. "Original Code" means Source Code of computer software
+      code which is described in the Source Code notice required
+      by Exhibit A as Original Code, and which, at the time of
+      its release under this License is not already Covered Code
+      governed by this License.
+
+1.10.1. "Patent Claims" means any patent claim(s), now owned or
+        hereafter acquired, including without limitation, method,
+        process, and apparatus claims, in any patent Licensable
+        by grantor.
+
+1.11. "Source Code" means the preferred form of the Covered Code
+      for making modifications to it, including all modules it
+      contains, plus any associated interface definition files,
+      scripts used to control compilation and installation of an
+      Executable, or source code differential comparisons against
+      either the Original Code or another well known, available
+      Covered Code of the Contributor's choice. The Source Code can
+      be in a compressed or archival form, provided the appropriate
+      decompression or de-archiving software is widely available
+      for no charge.
+
+1.12. "You" (or "Your") means an individual or a legal entity
+      exercising rights under, and complying with all of the terms
+      of, this License or a future version of this License issued
+      under Section 6.1. For legal entities, "You" includes any
+      entity which controls, is controlled by, or is under common
+      control with You. For purposes of this definition, "control"
+      means (a) the power, direct or indirect, to cause the direction
+      or management of such entity, whether by contract or otherwise,
+      or (b) ownership of more than fifty percent (50%) of the
+      outstanding shares or beneficial ownership of such entity.
+
+2. Source Code License
+
+2.1. The Initial Developer Grant
+
+The Initial Developer hereby grants You a world-wide, royalty-free,
+non-exclusive license, subject to third party intellectual property
+claims:
+
+2.1.a. under intellectual property rights (other than patent
+       or trademark) Licensable by Initial Developer to use,
+       reproduce, modify, display, perform, sublicense and distribute
+       the Original Code (or portions thereof) with or without
+       Modifications, and/or as part of a Larger Work; and
+
+2.1.b. under Patents Claims infringed by the making, using or selling
+       of Original Code, to make, have made, use, practice, sell,
+       and offer for sale, and/or otherwise dispose of the Original
+       Code (or portions thereof).
+
+2.1.c. the licenses granted in this Section 2.1 (a) and (b) are
+       effective on the date Initial Developer first distributes
+       Original Code under the terms of this License.
+
+2.1.d. Notwithstanding Section 2.1 (b) above, no patent license is
+       granted: 1) for code that You delete from the Original Code;
+       2) separate from the Original Code; or 3) for infringements
+       caused by: i) the modification of the Original Code or ii)
+       the combination of the Original Code with other software
+       or devices.
+
+2.2. Contributor Grant
+
+Subject to third party intellectual property claims, each Contributor
+hereby grants You a world-wide, royalty-free, non-exclusive license
+
+2.2.a. under intellectual property rights (other than patent or
+       trademark) Licensable by Contributor, to use, reproduce,
+       modify, display, perform, sublicense and distribute the
+       Modifications created by such Contributor (or portions
+       thereof) either on an unmodified basis, with other
+       Modifications, as Covered Code and/or as part of a Larger
+       Work; and
+
+2.2.b. under Patent Claims infringed by the making, using, or selling
+       of Modifications made by that Contributor either alone and/or
+       in combination with its Contributor Version (or portions
+       of such combination), to make, use, sell, offer for sale,
+       have made, and/or otherwise dispose of: 1) Modifications
+       made by that Contributor (or portions thereof); and 2) the
+       combination of Modifications made by that Contributor with
+       its Contributor Version (or portions of such combination).
+
+2.2.c. the licenses granted in Sections 2.2 (a) and 2.2 (b) are
+       effective on the date Contributor first makes Commercial
+       Use of the Covered Code.
+
+2.2.c. Notwithstanding Section 2.2 (b) above, no patent license is
+       granted: 1) for any code that Contributor has deleted from
+       the Contributor Version; 2) separate from the Contributor
+       Version; 3) for infringements caused by: i) third party
+       modifications of Contributor Version or ii) the combination
+       of Modifications made by that Contributor with other software
+       (except as part of the Contributor Version) or other devices;
+       or 4) under Patent Claims infringed by Covered Code in the
+       absence of Modifications made by that Contributor.
+
+3. Distribution Obligations
+
+3.1. Application of License
+
+The Modifications which You create or to which You contribute
+are governed by the terms of this License, including without
+limitation Section 2.2. The Source Code version of Covered Code may
+be distributed only under the terms of this License or a future
+version of this License released under Section 6.1, and You must
+include a copy of this License with every copy of the Source Code
+You distribute. You may not offer or impose any terms on any Source
+Code version that alters or restricts the applicable version of
+this License or the recipients' rights hereunder. However, You
+may include an additional document offering the additional rights
+described in Section 3.5.
+
+3.2. Availability of Source Code
+
+Any Modification which You create or to which You contribute must
+be made available in Source Code form under the terms of this
+License either on the same media as an Executable version or via
+an accepted Electronic Distribution Mechanism to anyone to whom
+you made an Executable version available; and if made available
+via Electronic Distribution Mechanism, must remain available for
+at least twelve (12) months after the date it initially became
+available, or at least six (6) months after a subsequent version
+of that particular Modification has been made available to such
+recipients. You are responsible for ensuring that the Source Code
+version remains available even if the Electronic Distribution
+Mechanism is maintained by a third party.
+
+3.3. Description of Modifications
+
+You must cause all Covered Code to which You contribute to contain
+a file documenting the changes You made to create that Covered
+Code and the date of any change. You must include a prominent
+statement that the Modification is derived, directly or indirectly,
+from Original Code provided by the Initial Developer and including
+the name of the Initial Developer in (a) the Source Code, and (b)
+in any notice in an Executable version or related documentation in
+which You describe the origin or ownership of the Covered Code.
+
+3.4. Intellectual Property Matters
+
+3.4.a. Third Party Claims: If Contributor has knowledge that
+       a license under a third party's intellectual property
+       rights is required to exercise the rights granted by such
+       Contributor under Sections 2.1 or 2.2, Contributor must
+       include a text file with the Source Code distribution titled
+       "LEGAL" which describes the claim and the party making the
+       claim in sufficient detail that a recipient will know whom
+       to contact. If Contributor obtains such knowledge after the
+       Modification is made available as described in Section 3.2,
+       Contributor shall promptly modify the LEGAL file in all
+       copies Contributor makes available thereafter and shall take
+       other steps (such as notifying appropriate mailing lists or
+       newsgroups) reasonably calculated to inform those who received
+       the Covered Code that new knowledge has been obtained.
+
+3.4.b. Contributor APIs: If Contributor's Modifications include
+       an application programming interface and Contributor has
+       knowledge of patent licenses which are reasonably necessary
+       to implement that API, Contributor must also include this
+       information in the legal file.
+
+3.4.c. Representations: Contributor represents that, except as
+       disclosed pursuant to Section 3.4 (a) above, Contributor
+       believes that Contributor's Modifications are Contributor's
+       original creation(s) and/or Contributor has sufficient rights
+       to grant the rights conveyed by this License.
+
+3.5. Required Notices
+
+You must duplicate the notice in Exhibit A in each file of
+the Source Code. If it is not possible to put such notice in a
+particular Source Code file due to its structure, then You must
+include such notice in a location (such as a relevant directory)
+where a user would be likely to look for such a notice. If You
+created one or more Modification(s) You may add your name as a
+Contributor to the notice described in Exhibit A. You must also
+duplicate this License in any documentation for the Source Code
+where You describe recipients' rights or ownership rights relating
+to Covered Code. You may choose to offer, and to charge a fee for,
+warranty, support, indemnity or liability obligations to one or
+more recipients of Covered Code. However, You may do so only on
+Your own behalf, and not on behalf of the Initial Developer or
+any Contributor. You must make it absolutely clear than any such
+warranty, support, indemnity or liability obligation is offered by
+You alone, and You hereby agree to indemnify the Initial Developer
+and every Contributor for any liability incurred by the Initial
+Developer or such Contributor as a result of warranty, support,
+indemnity or liability terms You offer.
+
+3.6. Distribution of Executable Versions
+
+You may distribute Covered Code in Executable form only if the
+requirements of Sections 3.1, 3.2, 3.3, 3.4 and 3.5 have been met
+for that Covered Code, and if You include a notice stating that
+the Source Code version of the Covered Code is available under the
+terms of this License, including a description of how and where
+You have fulfilled the obligations of Section 3.2. The notice
+must be conspicuously included in any notice in an Executable
+version, related documentation or collateral in which You describe
+recipients' rights relating to the Covered Code. You may distribute
+the Executable version of Covered Code or ownership rights under
+a license of Your choice, which may contain terms different from
+this License, provided that You are in compliance with the terms
+of this License and that the license for the Executable version
+does not attempt to limit or alter the recipient's rights in the
+Source Code version from the rights set forth in this License. If
+You distribute the Executable version under a different license You
+must make it absolutely clear that any terms which differ from this
+License are offered by You alone, not by the Initial Developer or any
+Contributor. You hereby agree to indemnify the Initial Developer and
+every Contributor for any liability incurred by the Initial Developer
+or such Contributor as a result of any such terms You offer.
+
+3.7. Larger Works
+
+You may create a Larger Work by combining Covered Code with other
+code not governed by the terms of this License and distribute the
+Larger Work as a single product. In such a case, You must make sure
+the requirements of this License are fulfilled for the Covered Code.
+
+4. Inability to Comply Due to Statute or Regulation.
+
+If it is impossible for You to comply with any of the terms of
+this License with respect to some or all of the Covered Code due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description
+must be included in the legal file described in Section 3.4 and
+must be included with all distributions of the Source Code. Except
+to the extent prohibited by statute or regulation, such description
+must be sufficiently detailed for a recipient of ordinary skill to
+be able to understand it.
+
+5. Application of this License.
+
+This License applies to code to which the Initial Developer has
+attached the notice in Exhibit A and to related Covered Code.
+
+6. Versions of the License.
+
+6.1. New Versions
+
+The H2 Group may publish revised and/or new versions of the License
+from time to time. Each version will be given a distinguishing
+version number.
+
+6.2. Effect of New Versions
+
+Once Covered Code has been published under a particular version of
+the License, You may always continue to use it under the terms of
+that version. You may also choose to use such Covered Code under the
+terms of any subsequent version of the License published by the H2
+Group. No one other than the H2 Group has the right to modify the
+terms applicable to Covered Code created under this License.
+
+6.3. Derivative Works
+
+If You create or use a modified version of this License (which you
+may only do in order to apply it to code which is not already Covered
+Code governed by this License), You must (a) rename Your license so
+that the phrases "H2 Group", "H2" or any confusingly similar phrase
+do not appear in your license (except to note that your license
+differs from this License) and (b) otherwise make it clear that
+Your version of the license contains terms which differ from the
+H2 License. (Filling in the name of the Initial Developer, Original
+Code or Contributor in the notice described in Exhibit A shall not
+of themselves be deemed to be modifications of this License.)
+
+7. Disclaimer of Warranty
+
+Covered code is provided under this license on an "as is" basis,
+without warranty of any kind, either expressed or implied,
+including, without limitation, warranties that the covered code
+is free of defects, merchantable, fit for a particular purpose or
+non-infringing. The entire risk as to the quality and performance
+of the covered code is with you. Should any covered code prove
+defective in any respect, you (not the initial developer or any
+other contributor) assume the cost of any necessary servicing,
+repair or correction. This disclaimer of warranty constitutes
+an essential part of this license. No use of any covered code is
+authorized hereunder except under this disclaimer.
+
+8. Termination
+
+8.1. This License and the rights granted hereunder will terminate
+     automatically if You fail to comply with terms herein and
+     fail to cure such breach within 30 days of becoming aware
+     of the breach. All sublicenses to the Covered Code which
+     are properly granted shall survive any termination of this
+     License. Provisions which, by their nature, must remain in
+     effect beyond the termination of this License shall survive.
+
+8.2. If You initiate litigation by asserting a patent infringement
+     claim (excluding declaratory judgment actions) against
+     Initial Developer or a Contributor (the Initial Developer or
+     Contributor against whom You file such action is referred to as
+     "Participant") alleging that:
+
+8.2.a. such Participant's Contributor Version directly or indirectly
+       infringes any patent, then any and all rights granted by
+       such Participant to You under Sections 2.1 and/or 2.2 of this
+       License shall, upon 60 days notice from Participant terminate
+       prospectively, unless if within 60 days after receipt of
+       notice You either: (i) agree in writing to pay Participant
+       a mutually agreeable reasonable royalty for Your past and
+       future use of Modifications made by such Participant, or (ii)
+       withdraw Your litigation claim with respect to the Contributor
+       Version against such Participant. If within 60 days of notice,
+       a reasonable royalty and payment arrangement are not mutually
+       agreed upon in writing by the parties or the litigation claim
+       is not withdrawn, the rights granted by Participant to You
+       under Sections 2.1 and/or 2.2 automatically terminate at
+       the expiration of the 60 day notice period specified above.
+
+8.2.b. any software, hardware, or device, other than such
+       Participant's Contributor Version, directly or indirectly
+       infringes any patent, then any rights granted to You by
+       such Participant under Sections 2.1(b) and 2.2(b) are
+       revoked effective as of the date You first made, used,
+       sold, distributed, or had made, Modifications made by that
+       Participant.
+
+8.3. If You assert a patent infringement claim against Participant
+     alleging that such Participant's Contributor Version directly
+     or indirectly infringes any patent where such claim is resolved
+     (such as by license or settlement) prior to the initiation of
+     patent infringement litigation, then the reasonable value of
+     the licenses granted by such Participant under Sections 2.1
+     or 2.2 shall be taken into account in determining the amount
+     or value of any payment or license.
+
+8.4. In the event of termination under Sections 8.1 or 8.2 above,
+     all end user license agreements (excluding distributors and
+     resellers) which have been validly granted by You or any
+     distributor hereunder prior to termination shall survive
+     termination.
+
+9. Limitation of Liability
+
+Under no circumstances and under no legal theory, whether tort
+(including negligence), contract, or otherwise, shall you, the
+initial developer, any other contributor, or any distributor of
+covered code, or any supplier of any of such parties, be liable to
+any person for any indirect, special, incidental, or consequential
+damages of any character including, without limitation, damages for
+loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses, even if such party
+shall have been informed of the possibility of such damages. This
+limitation of liability shall not apply to liability for death or
+personal injury resulting from such party's negligence to the extent
+applicable law prohibits such limitation. Some jurisdictions do not
+allow the exclusion or limitation of incidental or consequential
+damages, so this exclusion and limitation may not apply to you.
+
+10. United States Government End Users
+
+The Covered Code is a "commercial item", as that term is defined in
+48 C.F.R. 2.101 (October 1995), consisting of "commercial computer
+software" and "commercial computer software documentation", as such
+terms are used in 48 C.F.R. 12.212 (September 1995). Consistent
+with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4
+(June 1995), all U.S. Government End Users acquire Covered Code
+with only those rights set forth herein.
+
+11. Miscellaneous
+
+This License represents the complete agreement concerning subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. This License shall be governed
+by California law provisions (except to the extent applicable
+law, if any, provides otherwise), excluding its conflict-of-law
+provisions. With respect to disputes in which at least one party is
+a citizen of, or an entity chartered or registered to do business in
+United States of America, any litigation relating to this License
+shall be subject to the jurisdiction of the Federal Courts of the
+Northern District of California, with venue lying in Santa Clara
+County, California, with the losing party responsible for costs,
+including without limitation, court costs and reasonable attorneys'
+fees and expenses. The application of the United Nations Convention
+on Contracts for the International Sale of Goods is expressly
+excluded. Any law or regulation which provides that the language of
+a contract shall be construed against the drafter shall not apply
+to this License.
+
+12. Responsibility for Claims
+
+As between Initial Developer and the Contributors, each party is
+responsible for claims and damages arising, directly or indirectly,
+out of its utilization of rights under this License and You agree
+to work with Initial Developer and Contributors to distribute such
+responsibility on an equitable basis. Nothing herein is intended
+or shall be deemed to constitute any admission of liability.
+
+13. Multiple-Licensed Code
+
+Initial Developer may designate portions of the Covered Code as
+"Multiple-Licensed". "Multiple-Licensed" means that the Initial
+Developer permits you to utilize portions of the Covered Code under
+Your choice of this or the alternative licenses, if any, specified
+by the Initial Developer in the file described in Exhibit A.
+
+Exhibit A
+
+Multiple-Licensed under the H2 License, Version 1.0,
+and under the Eclipse Public License, Version 1.0
+(http://h2database.com/html/license.html).
+Initial Developer: H2 Group
+----
+
+----
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
+OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and
+   documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from
+and are distributed by that particular Contributor. A Contribution
+'originates' from a Contributor if it was added to the Program
+by such Contributor itself or anyone acting on such Contributor's
+behalf. Contributions do not include additions to the Program which:
+(i) are separate modules of software distributed in conjunction
+with the Program under their own license agreement, and (ii) are
+not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor
+which are necessarily infringed by the use or sale of its
+Contribution alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with
+this Agreement.
+
+"Recipient" means anyone who receives the Program under this
+Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby
+   grants Recipient a non-exclusive, worldwide, royalty-free copyright
+   license to reproduce, prepare derivative works of, publicly display,
+   publicly perform, distribute and sublicense the Contribution of such
+   Contributor, if any, and such derivative works, in source code and
+   object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby
+   grants Recipient a non-exclusive, worldwide, royalty-free patent
+   license under Licensed Patents to make, use, sell, offer to sell,
+   import and otherwise transfer the Contribution of such Contributor,
+   if any, in source code and object code form. This patent license
+   shall apply to the combination of the Contribution and the Program
+   if, at the time the Contribution is added by the Contributor, such
+   addition of the Contribution causes such combination to be covered
+   by the Licensed Patents. The patent license shall not apply to any
+   other combinations which include the Contribution. No hardware per
+   se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the
+   licenses to its Contributions set forth herein, no assurances are
+   provided by any Contributor that the Program does not infringe
+   the patent or other intellectual property rights of any other
+   entity. Each Contributor disclaims any liability to Recipient
+   for claims brought by any other entity based on infringement
+   of intellectual property rights or otherwise. As a condition to
+   exercising the rights and licenses granted hereunder, each Recipient
+   hereby assumes sole responsibility to secure any other intellectual
+   property rights needed, if any. For example, if a third party patent
+   license is required to allow Recipient to distribute the Program,
+   it is Recipient's responsibility to acquire that license before
+   distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has
+   sufficient copyright rights in its Contribution, if any, to grant
+   the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code
+  form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties
+   and conditions, express and implied, including warranties or
+   conditions of title and non-infringement, and implied warranties or
+   conditions of merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability
+    for damages, including direct, indirect, special, incidental and
+    consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement
+     are offered by that Contributor alone and not by any other
+     party; and
+
+iv) states that source code for the Program is available from such
+    Contributor, and informs licensees how to obtain it in a reasonable
+    manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained
+within the Program.
+
+Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial
+use of the Program, the Contributor who includes the Program in a
+commercial product offering should do so in a manner which does not
+create potential liability for other Contributors. Therefore, if a
+Contributor includes the Program in a commercial product offering,
+such Contributor ("Commercial Contributor") hereby agrees to defend
+and indemnify every other Contributor ("Indemnified Contributor")
+against any losses, damages and costs (collectively "Losses") arising
+from claims, lawsuits and other legal actions brought by a third
+party against the Indemnified Contributor to the extent caused by
+the acts or omissions of such Commercial Contributor in connection
+with its distribution of the Program in a commercial product
+offering. The obligations in this section do not apply to any claims
+or Losses relating to any actual or alleged intellectual property
+infringement. In order to qualify, an Indemnified Contributor must:
+a) promptly notify the Commercial Contributor in writing of such
+claim, and b) allow the Commercial Contributor to control, and
+cooperate with the Commercial Contributor in, the defense and any
+related settlement negotiations. The Indemnified Contributor may
+participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a
+commercial product offering, Product X. That Contributor is then a
+Commercial Contributor. If that Commercial Contributor then makes
+performance claims, or offers warranties related to Product X, those
+performance claims and warranties are such Commercial Contributor's
+responsibility alone. Under this section, the Commercial Contributor
+would have to defend claims against the other Contributors related
+to those performance claims and warranties, and if a court requires
+any other Contributor to pay any damages as a result, the Commercial
+Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with
+its exercise of rights under this Agreement , including but not
+limited to the risks and costs of program errors, compliance with
+applicable laws, damage to or loss of data, programs or equipment,
+and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
+NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY
+RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed
+to the minimum extent necessary to make such provision valid and
+enforceable.
+
+If Recipient institutes patent litigation against any entity
+(including a cross-claim or counterclaim in a lawsuit) alleging
+that the Program itself (excluding combinations of the Program with
+other software or hardware) infringes such Recipient's patent(s),
+then such Recipient's rights granted under Section 2(b) shall
+terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if
+it fails to comply with any of the material terms or conditions
+of this Agreement and does not cure such failure in a reasonable
+period of time after becoming aware of such noncompliance. If all
+Recipient's rights under this Agreement terminate, Recipient agrees
+to cease use and distribution of the Program as soon as reasonably
+practicable. However, Recipient's obligations under this Agreement
+and any licenses granted by Recipient relating to the Program shall
+continue and survive.
+
+Everyone is permitted to copy and distribute copies of this
+Agreement, but in order to avoid inconsistency the Agreement is
+copyrighted and may only be modified in the following manner. The
+Agreement Steward reserves the right to publish new versions
+(including revisions) of this Agreement from time to time. No
+one other than the Agreement Steward has the right to modify
+this Agreement. The Eclipse Foundation is the initial Agreement
+Steward. The Eclipse Foundation may assign the responsibility to
+serve as the Agreement Steward to a suitable separate entity. Each
+new version of the Agreement will be given a distinguishing
+version number. The Program (including Contributions) may always be
+distributed subject to the version of the Agreement under which it
+was received. In addition, after a new version of the Agreement is
+published, Contributor may elect to distribute the Program (including
+its Contributions) under the new version. Except as expressly stated
+in Sections 2(a) and 2(b) above, Recipient receives no rights or
+licenses to the intellectual property of any Contributor under
+this Agreement, whether expressly, by implication, estoppel or
+otherwise. All rights in the Program not expressly granted under
+this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No
+party to this Agreement will bring a legal action under this
+Agreement more than one year after the cause of action arose. Each
+party waives its rights to a jury trial in any resulting litigation.
+----
+
+----
+Export Control Classification Number (ECCN)
+
+As far as we know, the U.S. Export Control Classification Number
+(ECCN) for this software is 5D002. However, for legal reasons, we
+can make no warranty that this information is correct. For details,
+see also the Apache Software Foundation Export Classifications page.
+
+----
+
+
+[[icu4j]]
+icu4j
+
+* icu4j
+
+[[icu4j_license]]
+----
+COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later)
+
+Copyright © 1991-2016 Unicode, Inc. All rights reserved.
+Distributed under the Terms of Use in http://www.unicode.org/copyright.html
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation
+(the "Data Files") or Unicode software and any associated documentation
+(the "Software") to deal in the Data Files or Software
+without restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, and/or sell copies of
+the Data Files or Software, and to permit persons to whom the Data Files
+or Software are furnished to do so, provided that either
+(a) this copyright and permission notice appear with all copies
+of the Data Files or Software, or
+(b) this copyright and permission notice appear in associated
+Documentation.
+
+THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in these Data Files or Software without prior
+written authorization of the copyright holder.
+
+---------------------
+
+Third-Party Software Licenses
+
+This section contains third-party software notices and/or additional
+terms for licensed third-party software components included within ICU
+libraries.
+
+1. ICU License - ICU 1.8.1 to ICU 57.1
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1995-2016 International Business Machines Corporation and others
+All rights reserved.
+
+Permission 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, and/or sell copies of the Software, and to permit persons
+to whom the Software is furnished to do so, provided that the above
+copyright notice(s) and this permission notice appear in all copies of
+the Software and that both the above copyright notice(s) and this
+permission notice appear in supporting documentation.
+
+THE 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
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
+SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+All trademarks and registered trademarks mentioned herein are the
+property of their respective owners.
+
+2. Chinese/Japanese Word Break Dictionary Data (cjdict.txt)
+
+ #     The Google Chrome software developed by Google is licensed under
+ # the BSD license. Other software included in this distribution is
+ # provided under other licenses, as set forth below.
+ #
+ #  The BSD License
+ #  http://opensource.org/licenses/bsd-license.php
+ #  Copyright (C) 2006-2008, Google Inc.
+ #
+ #  All rights reserved.
+ #
+ #  Redistribution and use in source and binary forms, with or without
+ # modification, are permitted provided that the following conditions are met:
+ #
+ #  Redistributions of source code must retain the above copyright notice,
+ # this list of conditions and the following disclaimer.
+ #  Redistributions in binary form must reproduce the above
+ # copyright notice, this list of conditions and the following
+ # disclaimer in the documentation and/or other materials provided with
+ # the distribution.
+ #  Neither the name of  Google Inc. nor the names of its
+ # contributors may be used to endorse or promote products derived from
+ # this software without specific prior written permission.
+ #
+ #
+ #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ #
+ #
+ #  The word list in cjdict.txt are generated by combining three word lists
+ # listed below with further processing for compound word breaking. The
+ # frequency is generated with an iterative training against Google web
+ # corpora.
+ #
+ #  * Libtabe (Chinese)
+ #    - https://sourceforge.net/project/?group_id=1519
+ #    - Its license terms and conditions are shown below.
+ #
+ #  * IPADIC (Japanese)
+ #    - http://chasen.aist-nara.ac.jp/chasen/distribution.html
+ #    - Its license terms and conditions are shown below.
+ #
+ #  ---------COPYING.libtabe ---- BEGIN--------------------
+ #
+ #  /*
+ #   * Copyrighy (c) 1999 TaBE Project.
+ #   * Copyright (c) 1999 Pai-Hsiang Hsiao.
+ #   * All rights reserved.
+ #   *
+ #   * Redistribution and use in source and binary forms, with or without
+ #   * modification, are permitted provided that the following conditions
+ #   * are met:
+ #   *
+ #   * . Redistributions of source code must retain the above copyright
+ #   *   notice, this list of conditions and the following disclaimer.
+ #   * . Redistributions in binary form must reproduce the above copyright
+ #   *   notice, this list of conditions and the following disclaimer in
+ #   *   the documentation and/or other materials provided with the
+ #   *   distribution.
+ #   * . Neither the name of the TaBE Project nor the names of its
+ #   *   contributors may be used to endorse or promote products derived
+ #   *   from this software without specific prior written permission.
+ #   *
+ #   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ #   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ #   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ #   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ #   * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ #   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ #   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ #   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ #   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ #   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ #   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ #   * OF THE POSSIBILITY OF SUCH DAMAGE.
+ #   */
+ #
+ #  /*
+ #   * Copyright (c) 1999 Computer Systems and Communication Lab,
+ #   *                    Institute of Information Science, Academia
+ #       *                    Sinica. All rights reserved.
+ #   *
+ #   * Redistribution and use in source and binary forms, with or without
+ #   * modification, are permitted provided that the following conditions
+ #   * are met:
+ #   *
+ #   * . Redistributions of source code must retain the above copyright
+ #   *   notice, this list of conditions and the following disclaimer.
+ #   * . Redistributions in binary form must reproduce the above copyright
+ #   *   notice, this list of conditions and the following disclaimer in
+ #   *   the documentation and/or other materials provided with the
+ #   *   distribution.
+ #   * . Neither the name of the Computer Systems and Communication Lab
+ #   *   nor the names of its contributors may be used to endorse or
+ #   *   promote products derived from this software without specific
+ #   *   prior written permission.
+ #   *
+ #   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ #   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ #   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ #   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ #   * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ #   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ #   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ #   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ #   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ #   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ #   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ #   * OF THE POSSIBILITY OF SUCH DAMAGE.
+ #   */
+ #
+ #  Copyright 1996 Chih-Hao Tsai @ Beckman Institute,
+ #      University of Illinois
+ #  c-tsai4@uiuc.edu  http://casper.beckman.uiuc.edu/~c-tsai4
+ #
+ #  ---------------COPYING.libtabe-----END--------------------------------
+ #
+ #
+ #  ---------------COPYING.ipadic-----BEGIN-------------------------------
+ #
+ #  Copyright 2000, 2001, 2002, 2003 Nara Institute of Science
+ #  and Technology.  All Rights Reserved.
+ #
+ #  Use, reproduction, and distribution of this software is permitted.
+ #  Any copy of this software, whether in its original form or modified,
+ #  must include both the above copyright notice and the following
+ #  paragraphs.
+ #
+ #  Nara Institute of Science and Technology (NAIST),
+ #  the copyright holders, disclaims all warranties with regard to this
+ #  software, including all implied warranties of merchantability and
+ #  fitness, in no event shall NAIST be liable for
+ #  any special, indirect or consequential damages or any damages
+ #  whatsoever resulting from loss of use, data or profits, whether in an
+ #  action of contract, negligence or other tortuous action, arising out
+ #  of or in connection with the use or performance of this software.
+ #
+ #  A large portion of the dictionary entries
+ #  originate from ICOT Free Software.  The following conditions for ICOT
+ #  Free Software applies to the current dictionary as well.
+ #
+ #  Each User may also freely distribute the Program, whether in its
+ #  original form or modified, to any third party or parties, PROVIDED
+ #  that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear
+ #  on, or be attached to, the Program, which is distributed substantially
+ #  in the same form as set out herein and that such intended
+ #  distribution, if actually made, will neither violate or otherwise
+ #  contravene any of the laws and regulations of the countries having
+ #  jurisdiction over the User or the intended distribution itself.
+ #
+ #  NO WARRANTY
+ #
+ #  The program was produced on an experimental basis in the course of the
+ #  research and development conducted during the project and is provided
+ #  to users as so produced on an experimental basis.  Accordingly, the
+ #  program is provided without any warranty whatsoever, whether express,
+ #  implied, statutory or otherwise.  The term "warranty" used herein
+ #  includes, but is not limited to, any warranty of the quality,
+ #  performance, merchantability and fitness for a particular purpose of
+ #  the program and the nonexistence of any infringement or violation of
+ #  any right of any third party.
+ #
+ #  Each user of the program will agree and understand, and be deemed to
+ #  have agreed and understood, that there is no warranty whatsoever for
+ #  the program and, accordingly, the entire risk arising from or
+ #  otherwise connected with the program is assumed by the user.
+ #
+ #  Therefore, neither ICOT, the copyright holder, or any other
+ #  organization that participated in or was otherwise related to the
+ #  development of the program and their respective officials, directors,
+ #  officers and other employees shall be held liable for any and all
+ #  damages, including, without limitation, general, special, incidental
+ #  and consequential damages, arising out of or otherwise in connection
+ #  with the use or inability to use the program or any product, material
+ #  or result produced or otherwise obtained by using the program,
+ #  regardless of whether they have been advised of, or otherwise had
+ #  knowledge of, the possibility of such damages at any time during the
+ #  project or thereafter.  Each user will be deemed to have agreed to the
+ #  foregoing by his or her commencement of use of the program.  The term
+ #  "use" as used herein includes, but is not limited to, the use,
+ #  modification, copying and distribution of the program and the
+ #  production of secondary products from the program.
+ #
+ #  In the case where the program, whether in its original form or
+ #  modified, was distributed or delivered to or received by a user from
+ #  any person, organization or entity other than ICOT, unless it makes or
+ #  grants independently of ICOT any specific warranty to the user in
+ #  writing, such person, organization or entity, will also be exempted
+ #  from and not be held liable to the user for any such damages as noted
+ #  above as far as the program is concerned.
+ #
+ #  ---------------COPYING.ipadic-----END----------------------------------
+
+3. Lao Word Break Dictionary Data (laodict.txt)
+
+ #  Copyright (c) 2013 International Business Machines Corporation
+ #  and others. All Rights Reserved.
+ #
+ # Project: http://code.google.com/p/lao-dictionary/
+ # Dictionary: http://lao-dictionary.googlecode.com/git/Lao-Dictionary.txt
+ # License: http://lao-dictionary.googlecode.com/git/Lao-Dictionary-LICENSE.txt
+ #              (copied below)
+ #
+ #  This file is derived from the above dictionary, with slight
+ #  modifications.
+ #  ----------------------------------------------------------------------
+ #  Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell.
+ #  All rights reserved.
+ #
+ #  Redistribution and use in source and binary forms, with or without
+ #  modification,
+ #  are permitted provided that the following conditions are met:
+ #
+ #
+ # Redistributions of source code must retain the above copyright notice, this
+ #  list of conditions and the following disclaimer. Redistributions in
+ #  binary form must reproduce the above copyright notice, this list of
+ #  conditions and the following disclaimer in the documentation and/or
+ #  other materials provided with the distribution.
+ #
+ #
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ # OF THE POSSIBILITY OF SUCH DAMAGE.
+ #  --------------------------------------------------------------------------
+
+4. Burmese Word Break Dictionary Data (burmesedict.txt)
+
+ #  Copyright (c) 2014 International Business Machines Corporation
+ #  and others. All Rights Reserved.
+ #
+ #  This list is part of a project hosted at:
+ #    github.com/kanyawtech/myanmar-karen-word-lists
+ #
+ #  --------------------------------------------------------------------------
+ #  Copyright (c) 2013, LeRoy Benjamin Sharon
+ #  All rights reserved.
+ #
+ #  Redistribution and use in source and binary forms, with or without
+ #  modification, are permitted provided that the following conditions
+ #  are met: Redistributions of source code must retain the above
+ #  copyright notice, this list of conditions and the following
+ #  disclaimer.  Redistributions in binary form must reproduce the
+ #  above copyright notice, this list of conditions and the following
+ #  disclaimer in the documentation and/or other materials provided
+ #  with the distribution.
+ #
+ #    Neither the name Myanmar Karen Word Lists, nor the names of its
+ #    contributors may be used to endorse or promote products derived
+ #    from this software without specific prior written permission.
+ #
+ #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ #  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ #  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ #  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ #  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ #  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ #  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ #  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ #  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ #  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ #  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ #  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ #  SUCH DAMAGE.
+ #  --------------------------------------------------------------------------
+
+5. Time Zone Database
+
+  ICU uses the public domain data and code derived from Time Zone
+Database for its time zone support. The ownership of the TZ database
+is explained in BCP 175: Procedure for Maintaining the Time Zone
+Database section 7.
+
+ # 7.  Database Ownership
+ #
+ #    The TZ database itself is not an IETF Contribution or an IETF
+ #    document.  Rather it is a pre-existing and regularly updated work
+ #    that is in the public domain, and is intended to remain in the
+ #    public domain.  Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do
+ #    not apply to the TZ Database or contributions that individuals make
+ #    to it.  Should any claims be made and substantiated against the TZ
+ #    Database, the organization that is providing the IANA
+ #    Considerations defined in this RFC, under the memorandum of
+ #    understanding with the IETF, currently ICANN, may act in accordance
+ #    with all competent court orders.  No ownership claims will be made
+ #    by ICANN or the IETF Trust on the database or the code.  Any person
+ #    making a contribution to the database or code waives all rights to
+ #    future claims in that contribution or in the TZ Database.
+
+----
+
+
+[[jgit]]
+jgit
+
+* jgit
+* jgit-archive
+* jgit-servlet
+* jgit-ssh-apache
+
+[[jgit_license]]
+----
+This program and the accompanying materials are made available
+under the terms of the Eclipse Distribution License v1.0 which
+accompanies this distribution, is reproduced below, and is
+available at http://www.eclipse.org/org/documents/edl-v10.php
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+- Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the following
+  disclaimer in the documentation and/or other materials provided
+  with the distribution.
+
+- Neither the name of the Eclipse Foundation, Inc. nor the
+  names of its contributors may be used to endorse or promote
+  products derived from this software without specific prior
+  written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----
+
+
+[[jsch]]
+jsch
+
+* jsch
+
+[[jsch_license]]
+----
+Copyright (c) 2002-2012 Atsuhiko Yamanaka, JCraft,Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the distribution.
+
+  3. The names of the authors may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----
+
+
+[[jsoup]]
+jsoup
+
+* jsoup:jsoup
+
+[[jsoup_license]]
+----
+The MIT License
+
+© 2009-2016, Jonathan Hedley <jonathan@hedley.net>
+
+Permission 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:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE 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.
+
+----
+
+
+[[ow2]]
+ow2
+
+* ow2:ow2-asm
+* ow2:ow2-asm-analysis
+* ow2:ow2-asm-commons
+* ow2:ow2-asm-tree
+* ow2:ow2-asm-util
+
+[[ow2_license]]
+----
+Copyright (c) 2000-2011 INRIA, France Telecom
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holders nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+
+----
+
+
+[[prologcafe]]
+prologcafe
+
+* prolog:cafeteria
+* prolog:compiler
+* prolog:io
+* prolog:runtime
+
+[[prologcafe_license]]
+----
+Prolog Cafe (A Prolog to Java Translator System)
+Copyright (C) 1997-2009 by Mutsunori Banbara and Naoyuki Tamura
+
+Prolog Cafe is free software; you can redistribute it and/or modify
+it under the terms of either:
+
+  * the GNU General Public License as published by the Free Software
+    Foundation; either version 2 of the License, or (at your option)
+    any later version, or
+
+  * the Eclipse Public License
+----
+
+In the context of Gerrit Code Review, Prolog Cafe is consumed under
+the <<prologcafe_EPL,EPL>>. Gerrit Code Review uses a fork derived
+from the 1.2.5 release and offers the corresponding source code at
+link:https://gerrit.googlesource.com/prolog-cafe[].
+
+----
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
+----
+
+[[prologcafe_EPL]]
+----
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
+OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and
+   documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from
+and are distributed by that particular Contributor. A Contribution
+'originates' from a Contributor if it was added to the Program
+by such Contributor itself or anyone acting on such Contributor's
+behalf. Contributions do not include additions to the Program which:
+(i) are separate modules of software distributed in conjunction
+with the Program under their own license agreement, and (ii) are
+not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor
+which are necessarily infringed by the use or sale of its
+Contribution alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with
+this Agreement.
+
+"Recipient" means anyone who receives the Program under this
+Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby
+   grants Recipient a non-exclusive, worldwide, royalty-free copyright
+   license to reproduce, prepare derivative works of, publicly display,
+   publicly perform, distribute and sublicense the Contribution of such
+   Contributor, if any, and such derivative works, in source code and
+   object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby
+   grants Recipient a non-exclusive, worldwide, royalty-free patent
+   license under Licensed Patents to make, use, sell, offer to sell,
+   import and otherwise transfer the Contribution of such Contributor,
+   if any, in source code and object code form. This patent license
+   shall apply to the combination of the Contribution and the Program
+   if, at the time the Contribution is added by the Contributor, such
+   addition of the Contribution causes such combination to be covered
+   by the Licensed Patents. The patent license shall not apply to any
+   other combinations which include the Contribution. No hardware per
+   se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the
+   licenses to its Contributions set forth herein, no assurances are
+   provided by any Contributor that the Program does not infringe
+   the patent or other intellectual property rights of any other
+   entity. Each Contributor disclaims any liability to Recipient
+   for claims brought by any other entity based on infringement
+   of intellectual property rights or otherwise. As a condition to
+   exercising the rights and licenses granted hereunder, each Recipient
+   hereby assumes sole responsibility to secure any other intellectual
+   property rights needed, if any. For example, if a third party patent
+   license is required to allow Recipient to distribute the Program,
+   it is Recipient's responsibility to acquire that license before
+   distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has
+   sufficient copyright rights in its Contribution, if any, to grant
+   the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code
+  form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties
+   and conditions, express and implied, including warranties or
+   conditions of title and non-infringement, and implied warranties or
+   conditions of merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability
+    for damages, including direct, indirect, special, incidental and
+    consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement
+     are offered by that Contributor alone and not by any other
+     party; and
+
+iv) states that source code for the Program is available from such
+    Contributor, and informs licensees how to obtain it in a reasonable
+    manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained
+within the Program.
+
+Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial
+use of the Program, the Contributor who includes the Program in a
+commercial product offering should do so in a manner which does not
+create potential liability for other Contributors. Therefore, if a
+Contributor includes the Program in a commercial product offering,
+such Contributor ("Commercial Contributor") hereby agrees to defend
+and indemnify every other Contributor ("Indemnified Contributor")
+against any losses, damages and costs (collectively "Losses") arising
+from claims, lawsuits and other legal actions brought by a third
+party against the Indemnified Contributor to the extent caused by
+the acts or omissions of such Commercial Contributor in connection
+with its distribution of the Program in a commercial product
+offering. The obligations in this section do not apply to any claims
+or Losses relating to any actual or alleged intellectual property
+infringement. In order to qualify, an Indemnified Contributor must:
+a) promptly notify the Commercial Contributor in writing of such
+claim, and b) allow the Commercial Contributor to control, and
+cooperate with the Commercial Contributor in, the defense and any
+related settlement negotiations. The Indemnified Contributor may
+participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a
+commercial product offering, Product X. That Contributor is then a
+Commercial Contributor. If that Commercial Contributor then makes
+performance claims, or offers warranties related to Product X, those
+performance claims and warranties are such Commercial Contributor's
+responsibility alone. Under this section, the Commercial Contributor
+would have to defend claims against the other Contributors related
+to those performance claims and warranties, and if a court requires
+any other Contributor to pay any damages as a result, the Commercial
+Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with
+its exercise of rights under this Agreement , including but not
+limited to the risks and costs of program errors, compliance with
+applicable laws, damage to or loss of data, programs or equipment,
+and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
+NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY
+RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed
+to the minimum extent necessary to make such provision valid and
+enforceable.
+
+If Recipient institutes patent litigation against any entity
+(including a cross-claim or counterclaim in a lawsuit) alleging
+that the Program itself (excluding combinations of the Program with
+other software or hardware) infringes such Recipient's patent(s),
+then such Recipient's rights granted under Section 2(b) shall
+terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if
+it fails to comply with any of the material terms or conditions
+of this Agreement and does not cure such failure in a reasonable
+period of time after becoming aware of such noncompliance. If all
+Recipient's rights under this Agreement terminate, Recipient agrees
+to cease use and distribution of the Program as soon as reasonably
+practicable. However, Recipient's obligations under this Agreement
+and any licenses granted by Recipient relating to the Program shall
+continue and survive.
+
+Everyone is permitted to copy and distribute copies of this
+Agreement, but in order to avoid inconsistency the Agreement is
+copyrighted and may only be modified in the following manner. The
+Agreement Steward reserves the right to publish new versions
+(including revisions) of this Agreement from time to time. No
+one other than the Agreement Steward has the right to modify
+this Agreement. The Eclipse Foundation is the initial Agreement
+Steward. The Eclipse Foundation may assign the responsibility to
+serve as the Agreement Steward to a suitable separate entity. Each
+new version of the Agreement will be given a distinguishing
+version number. The Program (including Contributions) may always be
+distributed subject to the version of the Agreement under which it
+was received. In addition, after a new version of the Agreement is
+published, Contributor may elect to distribute the Program (including
+its Contributions) under the new version. Except as expressly stated
+in Sections 2(a) and 2(b) above, Recipient receives no rights or
+licenses to the intellectual property of any Contributor under
+this Agreement, whether expressly, by implication, estoppel or
+otherwise. All rights in the Program not expressly granted under
+this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No
+party to this Agreement will bring a legal action under this
+Agreement more than one year after the cause of action arose. Each
+party waives its rights to a jury trial in any resulting litigation.
+
+----
+
+
+[[protobuf]]
+protobuf
+
+* protobuf
+
+[[protobuf_license]]
+----
+Copyright 2008, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+    * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Code generated by the Protocol Buffer compiler is owned by the owner
+of the input file used when generating it.  This code is not
+standalone and requires a support library to be linked with it.  This
+support library is itself covered by the above license.
+
+----
+
+
+[[slf4j]]
+slf4j
+
+* log:api
+* log:jcl-over-slf4j
+
+[[slf4j_license]]
+----
+Copyright (c) 2004-2008 QOS.ch
+All rights reserved.
+
+Permission 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:
+
+The  above  copyright  notice  and  this permission  notice  shall  be
+included in all copies or substantial portions of the Software.
+
+THE  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.
+
+----
+
+
+[[xz]]
+xz
+
+* tukaani-xz
+
+[[xz_license]]
+----
+All the files in this package have been written by Lasse Collin
+and/or Igor Pavlov. All these files have been put into the
+public domain. You can do whatever you want with these files.
+This software is provided "as is", without any warranty.
+
+----
+
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 84e68ed..acf65a5 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -4343,7 +4343,7 @@
 before it is focefully cancelled.
 +
 The receive timeout cannot be overriden by setting a higher
-link:user-upload#deadline[deadline] on the git push request.
+link:user-upload.html#deadline[deadline] on the git push request.
 +
 Default is 4 minutes. If no unit is specified, milliseconds
 is assumed.
diff --git a/Documentation/user-request-cancellation-and-deadlines.txt b/Documentation/user-request-cancellation-and-deadlines.txt
index b368d6a..7c39f16 100644
--- a/Documentation/user-request-cancellation-and-deadlines.txt
+++ b/Documentation/user-request-cancellation-and-deadlines.txt
@@ -24,16 +24,16 @@
 [[server-side-deadlines]]
 == Server-side deadlines
 
-To limit the maximal execution time for requests, administrators can [configure
-server-side deadlines](config-gerrit.html#deadline.id). If a server-side
-deadline is exceeded by a matching request, the request is automatically
-aborted. In this case the client gets a proper error message informing the user
-about the exceeded deadline.
+To limit the maximal execution time for requests, administrators can
+link:config-gerrit.html#deadline.id[configure server-side deadlines]. If a
+server-side deadline is exceeded by a matching request, the request is
+automatically aborted. In this case the client gets a proper error message
+informing the user about the exceeded deadline.
 
 Clients may override server-side deadlines by setting a
-[deadline](#client-provided-deadline) on the request. This means, if a request
-fails due to an exceeded server-side deadline, the client may repeat the request
-with a higher deadline or no deadline (deadline = 0) to get unblocked.
+link:#client-provided-deadlines[deadline] on the request. This means, if a
+request fails due to an exceeded server-side deadline, the client may repeat the
+request with a higher deadline or no deadline (deadline = 0) to get unblocked.
 
 Server-side deadlines are meant to protect the Gerrit service against resource
 exhaustion due to performence issues with a particular request. E.g. imagine a
@@ -49,15 +49,15 @@
 
 Finally server-side deadlines can help ops engineers to detect performance
 issues more reliably and more quicky. For this alerts may be setup that are
-based on the [cancellation metrics](metrics.html#cancellations).
+based on the link:metrics.html#cancellations[cancellation metrics].
 
 [[receive-timeout]]
 === Receive Timeout
 
-For git pushes it is possible to configure a [hard
-timeout](config-gerrit.html#receive.timeout). In contrast to server-side
-deadlines, this timeout is not overridable by [client-provided
-deadlines](#client-provided-deadlines).
+For git pushes it is possible to configure a
+link:config-gerrit.html#receive.timeout[hard timeout]. In contrast to
+server-side deadlines, this timeout is not overridable by
+link:#client-provided-deadlines[client-provided deadlines].
 
 [[client-provided-deadlines]]
 == Client-provided deadlines
@@ -72,30 +72,32 @@
 [options="header",cols="1,6"]
 |=======================
 |Request Type   |How to set a deadline?
-|REST over HTTP |Set the [X-Gerrit-Deadline header](rest-api.html#deadline).
-|SSH command    |Set the [deadline option](cmd-index.html#deadline).
-|git push       |Set the [deadline push option](user-upload.html#deadline).
+|REST over HTTP |Set the link:rest-api.html#deadline[X-Gerrit-Deadline header].
+|SSH command    |Set the link:cmd-index.html#deadline[deadline option].
+|git push       |Set the link:user-upload.html#deadline[deadline push option].
 |git clone/fetch|Not supported.
 |=======================
 
 [[override-server-side-deadline]]
 === Override server-side deadline
 
-By setting a deadline on a request it is possible to override any [server-side
-deadline](#server-side-deadline), e.g. in order to increase it. Setting the
-deadline to `0` disables any server-side deadline. This allows clients to get
-unblocked if a request has previously failed due to an exceeded deadline.
+By setting a deadline on a request it is possible to override any
+link:#server-side-deadlines[server-side deadline], e.g. in order to increase it.
+Setting the deadline to `0` disables any server-side deadline. This allows
+clients to get unblocked if a request has previously failed due to an exceeded
+deadline.
 
 [NOTE]
-It is stronly discouraged for clients to permanently override [server-side
-deadlines](#server-side-deadlines] with a higher deadline or to permanently
-disable them by always setting the deadline to `0`. If this becomes necessary
-the caller should get in touch with the Gerrit administrators to increase the
-server-side deadlines or resolve the performance issue in another way.
+It is stronly discouraged for clients to permanently override
+link:#server-side-deadlines[server-side deadlines] with a higher deadline or to
+permanently disable them by always setting the deadline to `0`. If this becomes
+necessary the caller should get in touch with the Gerrit administrators to
+increase the server-side deadlines or resolve the performance issue in another
+way.
 
 [NOTE]
-It's not possible for clients to override the [receive
-timeout](#receive-timeout) that is enforced on git push.
+It's not possible for clients to override the link:#receive-timeout[receive
+timeout] that is enforced on git push.
 
 [[faqs]]
 == FAQs
@@ -105,7 +107,7 @@
 
 To get unblocked, you may repeat the request with deadlines disabled. To do this
 set the deadline to `0` on the request as explained
-[above](#override-server-side-deadline).
+link:#override-server-side-deadline[above].
 
 If doing this becomes required frequently, please get in touch with the Gerrit
 administrators in order to investigate the performance issue and increase the
@@ -119,7 +121,7 @@
 [[push-fails-due-to-exceeded-deadline-but-cannot-be-overridden]]
 === My git push fails due to an exceeded deadline and I cannot override the deadline, what can I do?
 
-As explained [above](#receive-timeout) a configured receive timeout cannot be
+As explained link:#receive-timeout[above] a configured receive timeout cannot be
 overridden by clients. If pushes fail due to this timeout, get in touch with the
 Gerrit administrators in order to investigate the performance issue and increase
 the receive timeout if necessary.
@@ -138,7 +140,7 @@
 Technically the check whether a request should be aborted is done whenever the
 execution time of an operation or sub-step is captured, either by a timer
 metric or a `TraceTimer` ('TraceTimer` is the class that logs the execution time
-when the request is being [traced](user-request-tracing.html)).
+when the request is being link:user-request-tracing.html[traced]).
 
 [[how-are-requests-aborted]]
 === How does Gerrit abort requests?
@@ -165,8 +167,8 @@
 request has been aborted.
 
 Errors due to aborted requests are usually not counted as internal server errors,
-but the [cancellation metrics](metrics.html#cancellations) may be used to setup
-alerting for performance issues.
+but the link:metrics.html#cancellations[cancellation metrics] may be used to
+setup alerting for performance issues.
 
 [NOTE]
 During a request, cancellations can occur at any time. This means for non-atomic
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 8064482..a3b5e8d 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -59,6 +59,7 @@
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.EmailHeader;
+import com.google.gerrit.entities.EmailHeader.StringEmailHeader;
 import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.InternalGroup;
@@ -202,7 +203,6 @@
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.SystemReader;
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
@@ -365,7 +365,7 @@
     }
   }
 
-  @AfterClass
+  @ConfigSuite.AfterConfig
   public static void stopCommonServer() throws Exception {
     if (commonServer != null) {
       try {
@@ -1247,7 +1247,7 @@
 
   protected void assertMailReplyTo(Message message, String email) throws Exception {
     assertThat(message.headers()).containsKey("Reply-To");
-    EmailHeader.String replyTo = (EmailHeader.String) message.headers().get("Reply-To");
+    StringEmailHeader replyTo = (StringEmailHeader) message.headers().get("Reply-To");
     assertThat(replyTo.getString()).contains(email);
   }
 
diff --git a/java/com/google/gerrit/acceptance/AbstractNotificationTest.java b/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
index 452df67..6c9a2b1 100644
--- a/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
@@ -31,6 +31,7 @@
 import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.EmailHeader;
 import com.google.gerrit.entities.EmailHeader.AddressList;
+import com.google.gerrit.entities.EmailHeader.StringEmailHeader;
 import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -135,13 +136,13 @@
             fact("expected to have message sent with", "X-Gerrit-MessageType header"));
       }
       EmailHeader header = message.headers().get("X-Gerrit-MessageType");
-      if (!header.equals(new EmailHeader.String(messageType))) {
+      if (!header.equals(new StringEmailHeader(messageType))) {
         failWithoutActual(
             fact("expected message of type", messageType),
             fact(
                 "actual",
-                header instanceof EmailHeader.String
-                    ? ((EmailHeader.String) header).getString()
+                header instanceof StringEmailHeader
+                    ? ((StringEmailHeader) header).getString()
                     : header));
       }
 
diff --git a/java/com/google/gerrit/acceptance/AbstractPluginLogFileTest.java b/java/com/google/gerrit/acceptance/AbstractPluginLogFileTest.java
index 60def29..f22ddea 100644
--- a/java/com/google/gerrit/acceptance/AbstractPluginLogFileTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractPluginLogFileTest.java
@@ -34,7 +34,7 @@
 import org.kohsuke.args4j.Option;
 
 public class AbstractPluginLogFileTest extends AbstractDaemonTest {
-  protected static class Module extends AbstractModule {
+  protected static class TestModule extends AbstractModule {
     @Override
     public void configure() {
       bind(com.google.gerrit.server.DynamicOptions.DynamicBean.class)
diff --git a/java/com/google/gerrit/acceptance/FakeGroupAuditService.java b/java/com/google/gerrit/acceptance/FakeGroupAuditService.java
index 48dc408..a1c28b9 100644
--- a/java/com/google/gerrit/acceptance/FakeGroupAuditService.java
+++ b/java/com/google/gerrit/acceptance/FakeGroupAuditService.java
@@ -39,7 +39,7 @@
 
 @Singleton
 public class FakeGroupAuditService extends AuditService {
-  public static class Module extends AbstractModule {
+  public static class FakeGroupAuditServiceModule extends AbstractModule {
     @Override
     public void configure() {
       DynamicSet.setOf(binder(), GroupAuditListener.class);
diff --git a/java/com/google/gerrit/acceptance/FakeSubmitRule.java b/java/com/google/gerrit/acceptance/FakeSubmitRule.java
index d53456f..1fbb9cf 100644
--- a/java/com/google/gerrit/acceptance/FakeSubmitRule.java
+++ b/java/com/google/gerrit/acceptance/FakeSubmitRule.java
@@ -16,23 +16,14 @@
 
 import com.google.gerrit.entities.SubmitRecord;
 import com.google.gerrit.entities.SubmitRecord.Status;
-import com.google.gerrit.extensions.annotations.Exports;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.rules.SubmitRule;
-import com.google.inject.AbstractModule;
 import com.google.inject.Singleton;
 import java.util.Optional;
 
 /** Fake submit rule that returns OK if the change contains one or more hashtags. */
 @Singleton
 public class FakeSubmitRule implements SubmitRule {
-  public static class Module extends AbstractModule {
-    @Override
-    public void configure() {
-      bind(SubmitRule.class).annotatedWith(Exports.named("Fake")).to(FakeSubmitRule.class);
-    }
-  }
-
   @Override
   public Optional<SubmitRecord> evaluate(ChangeData cd) {
     SubmitRecord record = new SubmitRecord();
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index 4d87f4d..33abc68 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -23,6 +23,9 @@
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.FakeGroupAuditService.FakeGroupAuditServiceModule;
+import com.google.gerrit.acceptance.ReindexGroupsAtStartup.ReindexGroupsAtStartupModule;
+import com.google.gerrit.acceptance.ReindexProjectsAtStartup.ReindexProjectsAtStartupModule;
 import com.google.gerrit.acceptance.config.ConfigAnnotationParser;
 import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.config.GerritConfigs;
@@ -53,15 +56,15 @@
 import com.google.gerrit.server.config.GerritRuntime;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePath;
-import com.google.gerrit.server.experiments.ConfigExperimentFeatures;
-import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
+import com.google.gerrit.server.experiments.ConfigExperimentFeatures.ConfigExperimentFeaturesModule;
+import com.google.gerrit.server.git.receive.AsyncReceiveCommits.AsyncReceiveCommitsModule;
 import com.google.gerrit.server.git.validators.CommitValidationListener;
 import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
 import com.google.gerrit.server.ssh.NoSshModule;
 import com.google.gerrit.server.util.ReplicaUtil;
 import com.google.gerrit.server.util.SocketUtil;
 import com.google.gerrit.server.util.SystemLog;
-import com.google.gerrit.testing.FakeEmailSender;
+import com.google.gerrit.testing.FakeEmailSender.FakeEmailSenderModule;
 import com.google.gerrit.testing.InMemoryRepositoryManager;
 import com.google.gerrit.testing.SshMode;
 import com.google.gerrit.testing.TestLoggingActivator;
@@ -407,9 +410,9 @@
               }
             },
             site);
-    daemon.setEmailModuleForTesting(new FakeEmailSender.Module());
+    daemon.setEmailModuleForTesting(new FakeEmailSenderModule());
     daemon.setAuditEventModuleForTesting(
-        MoreObjects.firstNonNull(testAuditModule, new FakeGroupAuditService.Module()));
+        MoreObjects.firstNonNull(testAuditModule, new FakeGroupAuditServiceModule()));
     if (testSysModule != null) {
       daemon.addAdditionalSysModuleForTesting(testSysModule);
     }
@@ -486,9 +489,9 @@
                 bind(GerritRuntime.class).toInstance(GerritRuntime.DAEMON);
               }
             },
-            new ConfigExperimentFeatures.Module()));
+            new ConfigExperimentFeaturesModule()));
     daemon.addAdditionalSysModuleForTesting(
-        new ReindexProjectsAtStartup.Module(), new ReindexGroupsAtStartup.Module());
+        new ReindexProjectsAtStartupModule(), new ReindexGroupsAtStartupModule());
     daemon.start();
     return new GerritServer(desc, null, createTestInjector(daemon), daemon, null);
   }
@@ -502,7 +505,7 @@
       throws Exception {
     requireNonNull(site);
     daemon.addAdditionalSysModuleForTesting(
-        new ReindexProjectsAtStartup.Module(), new ReindexGroupsAtStartup.Module());
+        new ReindexProjectsAtStartupModule(), new ReindexGroupsAtStartupModule());
     ExecutorService daemonService = Executors.newSingleThreadExecutor();
     String[] args =
         Stream.concat(
@@ -581,7 +584,7 @@
             factory(PushOneCommit.Factory.class);
             install(InProcessProtocol.module());
             install(new NoSshModule());
-            install(new AsyncReceiveCommits.Module());
+            install(new AsyncReceiveCommitsModule());
             factory(ProjectResetter.Builder.Factory.class);
           }
 
diff --git a/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
index 0b2282e..ba1c3cd 100644
--- a/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
+++ b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
@@ -34,7 +34,7 @@
   private final Groups groups;
   private final Config cfg;
 
-  public static class Module extends LifecycleModule {
+  public static class ReindexGroupsAtStartupModule extends LifecycleModule {
     @Override
     protected void configure() {
       listener().to(ReindexGroupsAtStartup.class).in(Scopes.SINGLETON);
diff --git a/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java b/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java
index 2f0ffcb..991136f 100644
--- a/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java
+++ b/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java
@@ -26,7 +26,7 @@
   private final ProjectIndexer projectIndexer;
   private final GitRepositoryManager repoMgr;
 
-  public static class Module extends LifecycleModule {
+  public static class ReindexProjectsAtStartupModule extends LifecycleModule {
     @Override
     protected void configure() {
       listener().to(ReindexProjectsAtStartup.class).in(Scopes.SINGLETON);
diff --git a/java/com/google/gerrit/acceptance/TestMetricMaker.java b/java/com/google/gerrit/acceptance/TestMetricMaker.java
index d60ef1a..2620f99 100644
--- a/java/com/google/gerrit/acceptance/TestMetricMaker.java
+++ b/java/com/google/gerrit/acceptance/TestMetricMaker.java
@@ -34,11 +34,11 @@
  *
  * <pre>
  * public class MyTest extends AbstractDaemonTest {
- *   @Inject private TestMetricMaker testMetricMaker;
+ *   {@literal @}Inject private TestMetricMaker testMetricMaker;
  *
  *   ...
  *
- *   @Test
+ *   {@literal @}Test
  *   public void testFoo() throws Exception {
  *     testMetricMaker.reset();
  *     doSomething();
diff --git a/java/com/google/gerrit/common/data/GarbageCollectionResult.java b/java/com/google/gerrit/common/data/GarbageCollectionResult.java
index 5e3601e..b995979 100644
--- a/java/com/google/gerrit/common/data/GarbageCollectionResult.java
+++ b/java/com/google/gerrit/common/data/GarbageCollectionResult.java
@@ -20,17 +20,17 @@
 
 /** A list of errors occurred during GC. */
 public class GarbageCollectionResult {
-  protected List<Error> errors;
+  protected List<GcError> errors;
 
   public GarbageCollectionResult() {
     errors = new ArrayList<>();
   }
 
-  public void addError(Error e) {
+  public void addError(GcError e) {
     errors.add(e);
   }
 
-  public List<Error> getErrors() {
+  public List<GcError> getErrors() {
     return errors;
   }
 
@@ -38,7 +38,7 @@
     return !errors.isEmpty();
   }
 
-  public static class Error {
+  public static class GcError {
     public enum Type {
       /** Git garbage collection was already scheduled for this project */
       GC_ALREADY_SCHEDULED,
@@ -53,9 +53,9 @@
     protected Type type;
     protected Project.NameKey projectName;
 
-    protected Error() {}
+    protected GcError() {}
 
-    public Error(Type type, Project.NameKey projectName) {
+    public GcError(Type type, Project.NameKey projectName) {
       this.type = type;
       this.projectName = projectName;
     }
diff --git a/java/com/google/gerrit/entities/EmailHeader.java b/java/com/google/gerrit/entities/EmailHeader.java
index 71414c7..bf5a644 100644
--- a/java/com/google/gerrit/entities/EmailHeader.java
+++ b/java/com/google/gerrit/entities/EmailHeader.java
@@ -31,14 +31,14 @@
 
   public abstract void write(Writer w) throws IOException;
 
-  public static class String extends EmailHeader {
-    private final java.lang.String value;
+  public static class StringEmailHeader extends EmailHeader {
+    private final String value;
 
-    public String(java.lang.String v) {
+    public StringEmailHeader(String v) {
       value = v;
     }
 
-    public java.lang.String getString() {
+    public String getString() {
       return value;
     }
 
@@ -63,16 +63,17 @@
 
     @Override
     public boolean equals(Object o) {
-      return (o instanceof String) && Objects.equals(value, ((String) o).value);
+      return (o instanceof StringEmailHeader)
+          && Objects.equals(value, ((StringEmailHeader) o).value);
     }
 
     @Override
-    public java.lang.String toString() {
+    public String toString() {
       return MoreObjects.toStringHelper(this).addValue(value).toString();
     }
   }
 
-  public static boolean needsQuotedPrintable(java.lang.String value) {
+  public static boolean needsQuotedPrintable(String value) {
     for (int i = 0; i < value.length(); i++) {
       if (value.charAt(i) < ' ' || '~' < value.charAt(i)) {
         return true;
@@ -99,7 +100,7 @@
     }
   }
 
-  public static java.lang.String quotedPrintable(java.lang.String value) {
+  public static String quotedPrintable(String value) {
     final StringBuilder r = new StringBuilder();
 
     r.append("=?UTF-8?Q?");
@@ -109,7 +110,7 @@
         r.append('_');
 
       } else if (needsQuotedPrintableWithinPhrase(cp)) {
-        byte[] buf = new java.lang.String(Character.toChars(cp)).getBytes(UTF_8);
+        byte[] buf = new String(Character.toChars(cp)).getBytes(UTF_8);
         for (byte b : buf) {
           r.append('=');
           r.append(Integer.toHexString((b >>> 4) & 0x0f).toUpperCase());
@@ -160,7 +161,7 @@
     }
 
     @Override
-    public java.lang.String toString() {
+    public String toString() {
       return MoreObjects.toStringHelper(this).addValue(value).toString();
     }
   }
@@ -182,7 +183,7 @@
       list.add(addr);
     }
 
-    public void remove(java.lang.String email) {
+    public void remove(String email) {
       list.removeIf(address -> address.email().equals(email));
     }
 
@@ -197,7 +198,7 @@
       boolean firstAddress = true;
       boolean needComma = false;
       for (Address addr : list) {
-        java.lang.String s = addr.toHeaderString();
+        String s = addr.toHeaderString();
         if (firstAddress) {
           firstAddress = false;
         } else if (72 < len + s.length()) {
@@ -226,7 +227,7 @@
     }
 
     @Override
-    public java.lang.String toString() {
+    public String toString() {
       return MoreObjects.toStringHelper(this).addValue(list).toString();
     }
   }
diff --git a/java/com/google/gerrit/httpd/GetUserFilter.java b/java/com/google/gerrit/httpd/GetUserFilter.java
index 2199411..68db98a 100644
--- a/java/com/google/gerrit/httpd/GetUserFilter.java
+++ b/java/com/google/gerrit/httpd/GetUserFilter.java
@@ -40,13 +40,13 @@
 
   public static final String USER_ATTR_KEY = "User";
 
-  public static class Module extends ServletModule {
+  public static class GetUserFilterModule extends ServletModule {
 
     private final boolean reqEnabled;
     private final boolean resEnabled;
 
     @Inject
-    Module(@GerritServerConfig Config cfg) {
+    GetUserFilterModule(@GerritServerConfig Config cfg) {
       reqEnabled = cfg.getBoolean("http", "addUserAsRequestAttribute", true);
       resEnabled = cfg.getBoolean("http", "addUserAsResponseHeader", false);
     }
diff --git a/java/com/google/gerrit/httpd/GitOverHttpServlet.java b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index 9736652..dcdbc13 100644
--- a/java/com/google/gerrit/httpd/GitOverHttpServlet.java
+++ b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -111,11 +111,11 @@
     URL_REGEX = url.toString();
   }
 
-  static class Module extends AbstractModule {
+  static class GitOverHttpServletModule extends AbstractModule {
 
     private final boolean enableReceive;
 
-    Module(boolean enableReceive) {
+    GitOverHttpServletModule(boolean enableReceive) {
       this.enableReceive = enableReceive;
     }
 
diff --git a/java/com/google/gerrit/httpd/RequireSslFilter.java b/java/com/google/gerrit/httpd/RequireSslFilter.java
index d8e6f84..a4a87e2 100644
--- a/java/com/google/gerrit/httpd/RequireSslFilter.java
+++ b/java/com/google/gerrit/httpd/RequireSslFilter.java
@@ -33,11 +33,11 @@
 /** Requires the connection to use SSL, redirects if not. */
 @Singleton
 public class RequireSslFilter implements Filter {
-  public static class Module extends ServletModule {
+  public static class RequireSslFilterModule extends ServletModule {
     private final boolean wantSsl;
 
     @Inject
-    Module(@Nullable @CanonicalWebUrl String canonicalUrl) {
+    RequireSslFilterModule(@Nullable @CanonicalWebUrl String canonicalUrl) {
       this.wantSsl = canonicalUrl != null && canonicalUrl.startsWith("https:");
     }
 
diff --git a/java/com/google/gerrit/httpd/RunAsFilter.java b/java/com/google/gerrit/httpd/RunAsFilter.java
index 135de42..b93f7ed 100644
--- a/java/com/google/gerrit/httpd/RunAsFilter.java
+++ b/java/com/google/gerrit/httpd/RunAsFilter.java
@@ -49,7 +49,7 @@
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
   private static final String RUN_AS = "X-Gerrit-RunAs";
 
-  static class Module extends ServletModule {
+  static class RunAsFilterModule extends ServletModule {
     @Override
     protected void configureServlets() {
       filter("/*").through(RunAsFilter.class);
diff --git a/java/com/google/gerrit/httpd/UrlModule.java b/java/com/google/gerrit/httpd/UrlModule.java
index ac73d22..029efba 100644
--- a/java/com/google/gerrit/httpd/UrlModule.java
+++ b/java/com/google/gerrit/httpd/UrlModule.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.client.AuthType;
+import com.google.gerrit.httpd.RunAsFilter.RunAsFilterModule;
 import com.google.gerrit.httpd.raw.AuthorizationCheckServlet;
 import com.google.gerrit.httpd.raw.CatServlet;
 import com.google.gerrit.httpd.raw.SshInfoServlet;
@@ -79,7 +80,7 @@
 
     // Must be after RequireIdentifiedUserFilter so auth happens before checking
     // for RunAs capability.
-    install(new RunAsFilter.Module());
+    install(new RunAsFilterModule());
 
     serveRegex("^/(?:a/)?tools/(.*)$").with(ToolServlet.class);
 
diff --git a/java/com/google/gerrit/httpd/WebModule.java b/java/com/google/gerrit/httpd/WebModule.java
index 0645aac..da485cc 100644
--- a/java/com/google/gerrit/httpd/WebModule.java
+++ b/java/com/google/gerrit/httpd/WebModule.java
@@ -16,6 +16,7 @@
 
 import static com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes.registerInParentInjectors;
 
+import com.google.gerrit.httpd.GitOverHttpServlet.GitOverHttpServletModule;
 import com.google.gerrit.httpd.auth.become.BecomeAnyAccountModule;
 import com.google.gerrit.httpd.auth.container.HttpAuthModule;
 import com.google.gerrit.httpd.auth.container.HttpsClientSslCertModule;
@@ -27,7 +28,7 @@
 import com.google.gerrit.server.config.GerritOptions;
 import com.google.gerrit.server.config.GerritRequestModule;
 import com.google.gerrit.server.config.GitwebCgiConfig;
-import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
+import com.google.gerrit.server.git.receive.AsyncReceiveCommits.AsyncReceiveCommitsModule;
 import com.google.gerrit.server.util.GuiceRequestScopePropagator;
 import com.google.gerrit.server.util.RequestScopePropagator;
 import com.google.inject.Inject;
@@ -57,13 +58,13 @@
       install(new UrlModule(authConfig));
     }
     install(new GerritRequestModule());
-    install(new GitOverHttpServlet.Module(options.enableMasterFeatures()));
+    install(new GitOverHttpServletModule(options.enableMasterFeatures()));
 
     if (gitwebCgiConfig.getGitwebCgi() != null) {
       install(new GitwebModule());
     }
 
-    install(new AsyncReceiveCommits.Module());
+    install(new AsyncReceiveCommitsModule());
 
     bind(SocketAddress.class)
         .annotatedWith(RemotePeer.class)
diff --git a/java/com/google/gerrit/httpd/init/WebAppInitializer.java b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
index 91d032e..13df520 100644
--- a/java/com/google/gerrit/httpd/init/WebAppInitializer.java
+++ b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
@@ -32,7 +32,7 @@
 import com.google.gerrit.httpd.RequestCleanupFilter;
 import com.google.gerrit.httpd.RequestContextFilter;
 import com.google.gerrit.httpd.RequestMetricsFilter;
-import com.google.gerrit.httpd.RequireSslFilter;
+import com.google.gerrit.httpd.RequireSslFilter.RequireSslFilterModule;
 import com.google.gerrit.httpd.SetThreadNameFilter;
 import com.google.gerrit.httpd.WebModule;
 import com.google.gerrit.httpd.WebSshGlueModule;
@@ -46,23 +46,23 @@
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.lucene.LuceneIndexModule;
 import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
-import com.google.gerrit.pgm.util.LogFileCompressor;
+import com.google.gerrit.pgm.util.LogFileCompressor.LogFileCompressorModule;
 import com.google.gerrit.server.LibModuleLoader;
 import com.google.gerrit.server.LibModuleType;
 import com.google.gerrit.server.ModuleOverloader;
-import com.google.gerrit.server.StartupChecks;
-import com.google.gerrit.server.account.AccountDeactivator;
-import com.google.gerrit.server.account.InternalAccountDirectory;
+import com.google.gerrit.server.StartupChecks.StartupChecksModule;
+import com.google.gerrit.server.account.AccountDeactivator.AccountDeactivatorModule;
+import com.google.gerrit.server.account.InternalAccountDirectory.InternalAccountDirectoryModule;
 import com.google.gerrit.server.api.GerritApiModule;
 import com.google.gerrit.server.api.PluginApiModule;
 import com.google.gerrit.server.audit.AuditModule;
 import com.google.gerrit.server.cache.h2.H2CacheModule;
 import com.google.gerrit.server.cache.mem.DefaultMemoryCacheModule;
-import com.google.gerrit.server.change.ChangeCleanupRunner;
+import com.google.gerrit.server.change.ChangeCleanupRunner.ChangeCleanupRunnerModule;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.config.AuthConfigModule;
 import com.google.gerrit.server.config.CanonicalWebUrlModule;
-import com.google.gerrit.server.config.DefaultUrlFormatter;
+import com.google.gerrit.server.config.DefaultUrlFormatter.DefaultUrlFormatterModule;
 import com.google.gerrit.server.config.DownloadConfig;
 import com.google.gerrit.server.config.GerritGlobalModule;
 import com.google.gerrit.server.config.GerritInstanceNameModule;
@@ -72,35 +72,35 @@
 import com.google.gerrit.server.config.GerritServerConfigModule;
 import com.google.gerrit.server.config.SitePath;
 import com.google.gerrit.server.config.SysExecutorModule;
-import com.google.gerrit.server.events.EventBroker;
-import com.google.gerrit.server.events.StreamEventsApiListener;
+import com.google.gerrit.server.events.EventBroker.EventBrokerModule;
+import com.google.gerrit.server.events.StreamEventsApiListener.StreamEventsApiListenerModule;
 import com.google.gerrit.server.git.GarbageCollectionModule;
 import com.google.gerrit.server.git.GitRepositoryManagerModule;
-import com.google.gerrit.server.git.SearchingChangeCacheImpl;
+import com.google.gerrit.server.git.SearchingChangeCacheImpl.SearchingChangeCacheImplModule;
 import com.google.gerrit.server.git.SystemReaderInstaller;
-import com.google.gerrit.server.git.WorkQueue;
+import com.google.gerrit.server.git.WorkQueue.WorkQueueModule;
 import com.google.gerrit.server.index.IndexModule;
-import com.google.gerrit.server.index.OnlineUpgrader;
+import com.google.gerrit.server.index.OnlineUpgrader.OnlineUpgraderModule;
 import com.google.gerrit.server.index.VersionManager;
-import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
-import com.google.gerrit.server.mail.receive.MailReceiver;
-import com.google.gerrit.server.mail.send.SmtpEmailSender;
+import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier.SignedTokenEmailTokenVerifierModule;
+import com.google.gerrit.server.mail.receive.MailReceiver.MailReceiverModule;
+import com.google.gerrit.server.mail.send.SmtpEmailSender.SmtpEmailSenderModule;
 import com.google.gerrit.server.mime.MimeUtil2Module;
 import com.google.gerrit.server.patch.DiffExecutorModule;
 import com.google.gerrit.server.permissions.DefaultPermissionBackendModule;
 import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
 import com.google.gerrit.server.plugins.PluginModule;
-import com.google.gerrit.server.project.DefaultProjectNameLockManager;
+import com.google.gerrit.server.project.DefaultProjectNameLockManager.DefaultProjectNameLockManagerModule;
 import com.google.gerrit.server.restapi.RestApiModule;
-import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
+import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore.JdbcAccountPatchReviewStoreModule;
 import com.google.gerrit.server.schema.NoteDbSchemaVersionCheck;
 import com.google.gerrit.server.schema.SchemaModule;
 import com.google.gerrit.server.securestore.SecureStoreClassName;
 import com.google.gerrit.server.ssh.NoSshModule;
 import com.google.gerrit.server.ssh.SshAddressesModule;
-import com.google.gerrit.server.submit.LocalMergeSuperSetComputation;
-import com.google.gerrit.server.submit.SubscriptionGraph;
-import com.google.gerrit.server.update.SuperprojectUpdateSubmissionListener;
+import com.google.gerrit.server.submit.LocalMergeSuperSetComputation.LocalMergeSuperSetComputationModule;
+import com.google.gerrit.server.submit.SubscriptionGraph.SubscriptionGraphModule;
+import com.google.gerrit.server.update.SuperprojectUpdateSubmissionListener.SuperprojectUpdateSubmissionListenerModule;
 import com.google.gerrit.sshd.SshHostKeyModule;
 import com.google.gerrit.sshd.SshKeyCacheImpl;
 import com.google.gerrit.sshd.SshModule;
@@ -108,7 +108,7 @@
 import com.google.gerrit.sshd.commands.DefaultCommandModule;
 import com.google.gerrit.sshd.commands.IndexCommandsModule;
 import com.google.gerrit.sshd.commands.SequenceCommandsModule;
-import com.google.gerrit.sshd.plugin.LfsPluginAuthCommand;
+import com.google.gerrit.sshd.plugin.LfsPluginAuthCommand.LfsPluginAuthCommandModule;
 import com.google.inject.AbstractModule;
 import com.google.inject.CreationException;
 import com.google.inject.Guice;
@@ -296,29 +296,29 @@
   private Injector createSysInjector() {
     final List<Module> modules = new ArrayList<>();
     modules.add(new DropWizardMetricMaker.RestModule());
-    modules.add(new LogFileCompressor.Module());
-    modules.add(new EventBroker.Module());
-    modules.add(new JdbcAccountPatchReviewStore.Module(config));
+    modules.add(new LogFileCompressorModule());
+    modules.add(new EventBrokerModule());
+    modules.add(new JdbcAccountPatchReviewStoreModule(config));
     modules.add(cfgInjector.getInstance(GitRepositoryManagerModule.class));
-    modules.add(new StreamEventsApiListener.Module());
+    modules.add(new StreamEventsApiListenerModule());
     modules.add(new SysExecutorModule());
     modules.add(new DiffExecutorModule());
     modules.add(new MimeUtil2Module());
     modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
     modules.add(new GerritApiModule());
     modules.add(new PluginApiModule());
-    modules.add(new SearchingChangeCacheImpl.Module());
-    modules.add(new InternalAccountDirectory.Module());
+    modules.add(new SearchingChangeCacheImplModule());
+    modules.add(new InternalAccountDirectoryModule());
     modules.add(new DefaultPermissionBackendModule());
     modules.add(new DefaultMemoryCacheModule());
     modules.add(new H2CacheModule());
-    modules.add(cfgInjector.getInstance(MailReceiver.Module.class));
-    modules.add(new SmtpEmailSender.Module());
-    modules.add(new SignedTokenEmailTokenVerifier.Module());
-    modules.add(new LocalMergeSuperSetComputation.Module());
+    modules.add(cfgInjector.getInstance(MailReceiverModule.class));
+    modules.add(new SmtpEmailSenderModule());
+    modules.add(new SignedTokenEmailTokenVerifierModule());
+    modules.add(new LocalMergeSuperSetComputationModule());
     modules.add(new AuditModule());
     modules.add(new GpgModule(config));
-    modules.add(new StartupChecks.Module());
+    modules.add(new StartupChecksModule());
 
     // Index module shutdown must happen before work queue shutdown, otherwise
     // work queue can get stuck waiting on index futures that will never return.
@@ -326,13 +326,13 @@
 
     modules.add(new PluginModule());
     if (VersionManager.getOnlineUpgrade(config)) {
-      modules.add(new OnlineUpgrader.Module());
+      modules.add(new OnlineUpgraderModule());
     }
     modules.add(new OAuthRestModule());
     modules.add(new RestApiModule());
-    modules.add(new SubscriptionGraph.Module());
-    modules.add(new SuperprojectUpdateSubmissionListener.Module());
-    modules.add(new WorkQueue.Module());
+    modules.add(new SubscriptionGraphModule());
+    modules.add(new SuperprojectUpdateSubmissionListenerModule());
+    modules.add(new WorkQueueModule());
     modules.add(new GerritInstanceNameModule());
     modules.add(
         new CanonicalWebUrlModule() {
@@ -341,7 +341,7 @@
             return HttpCanonicalWebUrlProvider.class;
           }
         });
-    modules.add(new DefaultUrlFormatter.Module());
+    modules.add(new DefaultUrlFormatterModule());
 
     SshSessionFactoryInitializer.init(config);
     modules.add(SshKeyCacheImpl.module());
@@ -354,9 +354,9 @@
           }
         });
     modules.add(new GarbageCollectionModule());
-    modules.add(new ChangeCleanupRunner.Module());
-    modules.add(new AccountDeactivator.Module());
-    modules.add(new DefaultProjectNameLockManager.Module());
+    modules.add(new ChangeCleanupRunnerModule());
+    modules.add(new AccountDeactivatorModule());
+    modules.add(new DefaultProjectNameLockManagerModule());
     return dbInjector.createChildInjector(
         ModuleOverloader.override(
             modules, LibModuleLoader.loadModules(cfgInjector, LibModuleType.SYS_MODULE)));
@@ -397,7 +397,7 @@
         new DefaultCommandModule(
             false,
             sysInjector.getInstance(DownloadConfig.class),
-            sysInjector.getInstance(LfsPluginAuthCommand.Module.class)));
+            sysInjector.getInstance(LfsPluginAuthCommandModule.class)));
     modules.add(new IndexCommandsModule(sysInjector));
     modules.add(new SequenceCommandsModule());
     return sysInjector.createChildInjector(modules);
@@ -414,7 +414,7 @@
     modules.add(SetThreadNameFilter.module());
     modules.add(AllRequestFilter.module());
     modules.add(sysInjector.getInstance(WebModule.class));
-    modules.add(sysInjector.getInstance(RequireSslFilter.Module.class));
+    modules.add(sysInjector.getInstance(RequireSslFilterModule.class));
     if (sshInjector != null) {
       modules.add(sshInjector.getInstance(WebSshGlueModule.class));
     } else {
@@ -431,7 +431,7 @@
     }
     modules.add(new AuthModule(authConfig));
 
-    modules.add(sysInjector.getInstance(GetUserFilter.Module.class));
+    modules.add(sysInjector.getInstance(GetUserFilter.GetUserFilterModule.class));
 
     // StaticModule contains a "/*" wildcard, place it last.
     GerritOptions opts = sysInjector.getInstance(GerritOptions.class);
diff --git a/java/com/google/gerrit/lucene/WrappableSearcherManager.java b/java/com/google/gerrit/lucene/WrappableSearcherManager.java
index 4044b90..c164b29 100644
--- a/java/com/google/gerrit/lucene/WrappableSearcherManager.java
+++ b/java/com/google/gerrit/lucene/WrappableSearcherManager.java
@@ -53,7 +53,6 @@
  * {@link #maybeRefresh}. Finally, be sure to call {@link #close} once you are done.
  *
  * @see SearcherFactory
- * @lucene.experimental
  */
 // This file was copied from:
 // https://github.com/apache/lucene-solr/blob/lucene_solr_5_0/lucene/core/src/java/org/apache/lucene/search/SearcherManager.java
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index 0a9b4d8..f3691ed 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -28,7 +28,7 @@
 import com.google.gerrit.gpg.GpgModule;
 import com.google.gerrit.httpd.AllRequestFilter;
 import com.google.gerrit.httpd.GerritAuthModule;
-import com.google.gerrit.httpd.GetUserFilter;
+import com.google.gerrit.httpd.GetUserFilter.GetUserFilterModule;
 import com.google.gerrit.httpd.GitOverHttpModule;
 import com.google.gerrit.httpd.H2CacheBasedWebSession;
 import com.google.gerrit.httpd.HttpCanonicalWebUrlProvider;
@@ -36,7 +36,7 @@
 import com.google.gerrit.httpd.RequestCleanupFilter;
 import com.google.gerrit.httpd.RequestContextFilter;
 import com.google.gerrit.httpd.RequestMetricsFilter;
-import com.google.gerrit.httpd.RequireSslFilter;
+import com.google.gerrit.httpd.RequireSslFilter.RequireSslFilterModule;
 import com.google.gerrit.httpd.SetThreadNameFilter;
 import com.google.gerrit.httpd.WebModule;
 import com.google.gerrit.httpd.WebSshGlueModule;
@@ -51,28 +51,28 @@
 import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
 import com.google.gerrit.pgm.http.jetty.JettyEnv;
 import com.google.gerrit.pgm.http.jetty.JettyModule;
-import com.google.gerrit.pgm.http.jetty.ProjectQoSFilter;
+import com.google.gerrit.pgm.http.jetty.ProjectQoSFilter.ProjectQoSFilterModule;
 import com.google.gerrit.pgm.util.ErrorLogFile;
-import com.google.gerrit.pgm.util.LogFileCompressor;
+import com.google.gerrit.pgm.util.LogFileCompressor.LogFileCompressorModule;
 import com.google.gerrit.pgm.util.RuntimeShutdown;
 import com.google.gerrit.pgm.util.SiteProgram;
 import com.google.gerrit.server.LibModuleLoader;
 import com.google.gerrit.server.LibModuleType;
 import com.google.gerrit.server.ModuleOverloader;
-import com.google.gerrit.server.StartupChecks;
-import com.google.gerrit.server.account.AccountDeactivator;
-import com.google.gerrit.server.account.InternalAccountDirectory;
+import com.google.gerrit.server.StartupChecks.StartupChecksModule;
+import com.google.gerrit.server.account.AccountDeactivator.AccountDeactivatorModule;
+import com.google.gerrit.server.account.InternalAccountDirectory.InternalAccountDirectoryModule;
 import com.google.gerrit.server.api.GerritApiModule;
 import com.google.gerrit.server.api.PluginApiModule;
 import com.google.gerrit.server.audit.AuditModule;
 import com.google.gerrit.server.cache.h2.H2CacheModule;
 import com.google.gerrit.server.cache.mem.DefaultMemoryCacheModule;
-import com.google.gerrit.server.change.ChangeCleanupRunner;
+import com.google.gerrit.server.change.ChangeCleanupRunner.ChangeCleanupRunnerModule;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.config.AuthConfigModule;
 import com.google.gerrit.server.config.CanonicalWebUrlModule;
 import com.google.gerrit.server.config.CanonicalWebUrlProvider;
-import com.google.gerrit.server.config.DefaultUrlFormatter;
+import com.google.gerrit.server.config.DefaultUrlFormatter.DefaultUrlFormatterModule;
 import com.google.gerrit.server.config.DownloadConfig;
 import com.google.gerrit.server.config.GerritGlobalModule;
 import com.google.gerrit.server.config.GerritInstanceIdModule;
@@ -81,27 +81,27 @@
 import com.google.gerrit.server.config.GerritRuntime;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SysExecutorModule;
-import com.google.gerrit.server.events.EventBroker;
-import com.google.gerrit.server.events.StreamEventsApiListener;
+import com.google.gerrit.server.events.EventBroker.EventBrokerModule;
+import com.google.gerrit.server.events.StreamEventsApiListener.StreamEventsApiListenerModule;
 import com.google.gerrit.server.git.GarbageCollectionModule;
-import com.google.gerrit.server.git.SearchingChangeCacheImpl;
-import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.group.PeriodicGroupIndexer;
+import com.google.gerrit.server.git.SearchingChangeCacheImpl.SearchingChangeCacheImplModule;
+import com.google.gerrit.server.git.WorkQueue.WorkQueueModule;
+import com.google.gerrit.server.group.PeriodicGroupIndexer.PeriodicGroupIndexerModule;
 import com.google.gerrit.server.index.AbstractIndexModule;
 import com.google.gerrit.server.index.IndexModule;
-import com.google.gerrit.server.index.OnlineUpgrader;
+import com.google.gerrit.server.index.OnlineUpgrader.OnlineUpgraderModule;
 import com.google.gerrit.server.index.VersionManager;
-import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
-import com.google.gerrit.server.mail.receive.MailReceiver;
-import com.google.gerrit.server.mail.send.SmtpEmailSender;
+import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier.SignedTokenEmailTokenVerifierModule;
+import com.google.gerrit.server.mail.receive.MailReceiver.MailReceiverModule;
+import com.google.gerrit.server.mail.send.SmtpEmailSender.SmtpEmailSenderModule;
 import com.google.gerrit.server.mime.MimeUtil2Module;
 import com.google.gerrit.server.patch.DiffExecutorModule;
 import com.google.gerrit.server.permissions.DefaultPermissionBackendModule;
 import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
 import com.google.gerrit.server.plugins.PluginModule;
-import com.google.gerrit.server.project.DefaultProjectNameLockManager;
+import com.google.gerrit.server.project.DefaultProjectNameLockManager.DefaultProjectNameLockManagerModule;
 import com.google.gerrit.server.restapi.RestApiModule;
-import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
+import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore.JdbcAccountPatchReviewStoreModule;
 import com.google.gerrit.server.schema.NoteDbSchemaVersionCheck;
 import com.google.gerrit.server.securestore.DefaultSecureStore;
 import com.google.gerrit.server.securestore.SecureStore;
@@ -110,9 +110,9 @@
 import com.google.gerrit.server.ssh.NoSshKeyCache;
 import com.google.gerrit.server.ssh.NoSshModule;
 import com.google.gerrit.server.ssh.SshAddressesModule;
-import com.google.gerrit.server.submit.LocalMergeSuperSetComputation;
-import com.google.gerrit.server.submit.SubscriptionGraph;
-import com.google.gerrit.server.update.SuperprojectUpdateSubmissionListener;
+import com.google.gerrit.server.submit.LocalMergeSuperSetComputation.LocalMergeSuperSetComputationModule;
+import com.google.gerrit.server.submit.SubscriptionGraph.SubscriptionGraphModule;
+import com.google.gerrit.server.update.SuperprojectUpdateSubmissionListener.SuperprojectUpdateSubmissionListenerModule;
 import com.google.gerrit.sshd.SshHostKeyModule;
 import com.google.gerrit.sshd.SshKeyCacheImpl;
 import com.google.gerrit.sshd.SshModule;
@@ -120,7 +120,7 @@
 import com.google.gerrit.sshd.commands.DefaultCommandModule;
 import com.google.gerrit.sshd.commands.IndexCommandsModule;
 import com.google.gerrit.sshd.commands.SequenceCommandsModule;
-import com.google.gerrit.sshd.plugin.LfsPluginAuthCommand;
+import com.google.gerrit.sshd.plugin.LfsPluginAuthCommand.LfsPluginAuthCommandModule;
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
@@ -427,18 +427,18 @@
     final List<Module> modules = new ArrayList<>();
     modules.add(NoteDbSchemaVersionCheck.module());
     modules.add(new DropWizardMetricMaker.RestModule());
-    modules.add(new LogFileCompressor.Module());
+    modules.add(new LogFileCompressorModule());
 
     // Index module shutdown must happen before work queue shutdown, otherwise
     // work queue can get stuck waiting on index futures that will never return.
     modules.add(createIndexModule());
 
-    modules.add(new SubscriptionGraph.Module());
-    modules.add(new SuperprojectUpdateSubmissionListener.Module());
-    modules.add(new WorkQueue.Module());
-    modules.add(new StreamEventsApiListener.Module());
-    modules.add(new EventBroker.Module());
-    modules.add(new JdbcAccountPatchReviewStore.Module(config));
+    modules.add(new SubscriptionGraphModule());
+    modules.add(new SuperprojectUpdateSubmissionListenerModule());
+    modules.add(new WorkQueueModule());
+    modules.add(new StreamEventsApiListenerModule());
+    modules.add(new EventBrokerModule());
+    modules.add(new JdbcAccountPatchReviewStoreModule(config));
     modules.add(new SysExecutorModule());
     modules.add(new DiffExecutorModule());
     modules.add(new MimeUtil2Module());
@@ -446,31 +446,31 @@
     modules.add(new GerritApiModule());
     modules.add(new PluginApiModule());
 
-    modules.add(new SearchingChangeCacheImpl.Module(replica));
-    modules.add(new InternalAccountDirectory.Module());
+    modules.add(new SearchingChangeCacheImplModule(replica));
+    modules.add(new InternalAccountDirectoryModule());
     modules.add(new DefaultPermissionBackendModule());
     modules.add(new DefaultMemoryCacheModule());
     modules.add(new H2CacheModule());
-    modules.add(cfgInjector.getInstance(MailReceiver.Module.class));
+    modules.add(cfgInjector.getInstance(MailReceiverModule.class));
     if (emailModule != null) {
       modules.add(emailModule);
     } else {
-      modules.add(new SmtpEmailSender.Module());
+      modules.add(new SmtpEmailSenderModule());
     }
     if (auditEventModule != null) {
       modules.add(auditEventModule);
     } else {
       modules.add(new AuditModule());
     }
-    modules.add(new SignedTokenEmailTokenVerifier.Module());
+    modules.add(new SignedTokenEmailTokenVerifierModule());
     modules.add(new PluginModule());
     if (VersionManager.getOnlineUpgrade(config)) {
-      modules.add(new OnlineUpgrader.Module());
+      modules.add(new OnlineUpgraderModule());
     }
     modules.add(new OAuthRestModule());
     modules.add(new RestApiModule());
     modules.add(new GpgModule(config));
-    modules.add(new StartupChecks.Module());
+    modules.add(new StartupChecksModule());
     modules.add(new GerritInstanceNameModule());
     modules.add(new GerritInstanceIdModule());
     if (MoreObjects.firstNonNull(httpd, true)) {
@@ -490,7 +490,7 @@
             }
           });
     }
-    modules.add(new DefaultUrlFormatter.Module());
+    modules.add(new DefaultUrlFormatterModule());
     SshSessionFactoryInitializer.init(config);
     if (sshd) {
       modules.add(SshKeyCacheImpl.module());
@@ -512,13 +512,13 @@
         });
     modules.add(new GarbageCollectionModule());
     if (replica) {
-      modules.add(new PeriodicGroupIndexer.Module());
+      modules.add(new PeriodicGroupIndexerModule());
     } else {
-      modules.add(new AccountDeactivator.Module());
-      modules.add(new ChangeCleanupRunner.Module());
+      modules.add(new AccountDeactivatorModule());
+      modules.add(new ChangeCleanupRunnerModule());
     }
-    modules.add(new LocalMergeSuperSetComputation.Module());
-    modules.add(new DefaultProjectNameLockManager.Module());
+    modules.add(new LocalMergeSuperSetComputationModule());
+    modules.add(new DefaultProjectNameLockManagerModule());
 
     List<Module> libModules = LibModuleLoader.loadModules(cfgInjector, LibModuleType.SYS_MODULE);
     libModules.addAll(testSysModules);
@@ -572,7 +572,7 @@
         new DefaultCommandModule(
             replica,
             sysInjector.getInstance(DownloadConfig.class),
-            sysInjector.getInstance(LfsPluginAuthCommand.Module.class)));
+            sysInjector.getInstance(LfsPluginAuthCommandModule.class)));
 
     modules.addAll(testSshModules);
     if (!replica) {
@@ -604,13 +604,13 @@
     modules.add(sysInjector.getInstance(GitOverHttpModule.class));
     modules.add(sysInjector.getInstance(HttpdModule.class));
     if (sshd) {
-      modules.add(new ProjectQoSFilter.Module());
+      modules.add(new ProjectQoSFilterModule());
     }
     modules.add(RequestCleanupFilter.module());
     modules.add(AllRequestFilter.module());
     modules.add(SetThreadNameFilter.module());
     modules.add(sysInjector.getInstance(WebModule.class));
-    modules.add(sysInjector.getInstance(RequireSslFilter.Module.class));
+    modules.add(sysInjector.getInstance(RequireSslFilterModule.class));
     modules.add(new HttpPluginModule());
     if (sshd) {
       modules.add(sshInjector.getInstance(WebSshGlueModule.class));
@@ -626,7 +626,7 @@
       modules.add(new OAuthModule());
     }
 
-    modules.add(sysInjector.getInstance(GetUserFilter.Module.class));
+    modules.add(sysInjector.getInstance(GetUserFilterModule.class));
 
     // StaticModule contains a "/*" wildcard, place it last.
     GerritOptions opts = sysInjector.getInstance(GerritOptions.class);
diff --git a/java/com/google/gerrit/pgm/http/jetty/ProjectQoSFilter.java b/java/com/google/gerrit/pgm/http/jetty/ProjectQoSFilter.java
index 1cca789..1a5b4c4 100644
--- a/java/com/google/gerrit/pgm/http/jetty/ProjectQoSFilter.java
+++ b/java/com/google/gerrit/pgm/http/jetty/ProjectQoSFilter.java
@@ -71,7 +71,7 @@
   private static final String FILTER_RE = "^/(.*)/(git-upload-pack|git-receive-pack)$";
   private static final Pattern URI_PATTERN = Pattern.compile(FILTER_RE);
 
-  public static class Module extends ServletModule {
+  public static class ProjectQoSFilterModule extends ServletModule {
     @Override
     protected void configureServlets() {
       bind(QueueProvider.class).to(CommandExecutorQueueProvider.class);
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 188f30b..b24fdad 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -53,7 +53,7 @@
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.CanonicalWebUrlProvider;
 import com.google.gerrit.server.config.DefaultPreferencesCacheImpl;
-import com.google.gerrit.server.config.DefaultUrlFormatter;
+import com.google.gerrit.server.config.DefaultUrlFormatter.DefaultUrlFormatterModule;
 import com.google.gerrit.server.config.EnablePeerIPInReflogRecord;
 import com.google.gerrit.server.config.EnablePeerIPInReflogRecordProvider;
 import com.google.gerrit.server.config.GitReceivePackGroups;
@@ -87,8 +87,8 @@
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
 import com.google.gerrit.server.query.change.ConflictsCacheImpl;
 import com.google.gerrit.server.restapi.group.GroupModule;
-import com.google.gerrit.server.rules.DefaultSubmitRule;
-import com.google.gerrit.server.rules.IgnoreSelfApprovalRule;
+import com.google.gerrit.server.rules.DefaultSubmitRule.DefaultSubmitRuleModule;
+import com.google.gerrit.server.rules.IgnoreSelfApprovalRule.IgnoreSelfApprovalRuleModule;
 import com.google.gerrit.server.rules.PrologModule;
 import com.google.gerrit.server.rules.SubmitRule;
 import com.google.gerrit.server.update.BatchUpdate;
@@ -118,7 +118,7 @@
     modules.add(new SysExecutorModule());
     modules.add(BatchUpdate.module());
     modules.add(PatchListCacheImpl.module());
-    modules.add(new DefaultUrlFormatter.Module());
+    modules.add(new DefaultUrlFormatterModule());
     modules.add(DiffOperationsImpl.module());
 
     // There is the concept of LifecycleModule, in Gerrit's own extension to Guice, which has these:
@@ -203,8 +203,8 @@
     DynamicSet.setOf(binder(), SubmitRule.class);
     factory(SubmitRuleEvaluator.Factory.class);
     modules.add(new PrologModule());
-    modules.add(new DefaultSubmitRule.Module());
-    modules.add(new IgnoreSelfApprovalRule.Module());
+    modules.add(new DefaultSubmitRuleModule());
+    modules.add(new IgnoreSelfApprovalRuleModule());
 
     bind(ChangeJson.Factory.class).toProvider(Providers.of(null));
     bind(EventUtil.class).toProvider(Providers.of(null));
diff --git a/java/com/google/gerrit/pgm/util/LogFileCompressor.java b/java/com/google/gerrit/pgm/util/LogFileCompressor.java
index 413e0fa..5e49312 100644
--- a/java/com/google/gerrit/pgm/util/LogFileCompressor.java
+++ b/java/com/google/gerrit/pgm/util/LogFileCompressor.java
@@ -42,7 +42,7 @@
 public class LogFileCompressor implements Runnable {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  public static class Module extends LifecycleModule {
+  public static class LogFileCompressorModule extends LifecycleModule {
     @Override
     protected void configure() {
       listener().to(Lifecycle.class);
diff --git a/java/com/google/gerrit/pgm/util/SiteProgram.java b/java/com/google/gerrit/pgm/util/SiteProgram.java
index c1ba896..ed6bce6 100644
--- a/java/com/google/gerrit/pgm/util/SiteProgram.java
+++ b/java/com/google/gerrit/pgm/util/SiteProgram.java
@@ -28,7 +28,7 @@
 import com.google.gerrit.server.config.GerritRuntime;
 import com.google.gerrit.server.config.GerritServerConfigModule;
 import com.google.gerrit.server.config.SitePath;
-import com.google.gerrit.server.experiments.ConfigExperimentFeatures;
+import com.google.gerrit.server.experiments.ConfigExperimentFeatures.ConfigExperimentFeaturesModule;
 import com.google.gerrit.server.git.GitRepositoryManagerModule;
 import com.google.gerrit.server.git.SystemReaderInstaller;
 import com.google.gerrit.server.schema.SchemaModule;
@@ -131,7 +131,7 @@
     modules.add(cfgInjector.getInstance(GitRepositoryManagerModule.class));
     // The only implementation of experiments is available in all programs that can use
     // gerrit.config
-    modules.add(new ConfigExperimentFeatures.Module());
+    modules.add(new ConfigExperimentFeaturesModule());
 
     try {
       return Guice.createInjector(
diff --git a/java/com/google/gerrit/server/StartupChecks.java b/java/com/google/gerrit/server/StartupChecks.java
index 9bf94ae..3b9892b 100644
--- a/java/com/google/gerrit/server/StartupChecks.java
+++ b/java/com/google/gerrit/server/StartupChecks.java
@@ -25,7 +25,7 @@
 
 @Singleton
 public class StartupChecks implements LifecycleListener {
-  public static class Module extends LifecycleModule {
+  public static class StartupChecksModule extends LifecycleModule {
     @Override
     protected void configure() {
       DynamicSet.setOf(binder(), StartupCheck.class);
diff --git a/java/com/google/gerrit/server/account/AccountDeactivator.java b/java/com/google/gerrit/server/account/AccountDeactivator.java
index b12e585..c7f6496 100644
--- a/java/com/google/gerrit/server/account/AccountDeactivator.java
+++ b/java/com/google/gerrit/server/account/AccountDeactivator.java
@@ -33,7 +33,7 @@
 public class AccountDeactivator implements Runnable {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  public static class Module extends LifecycleModule {
+  public static class AccountDeactivatorModule extends LifecycleModule {
     @Override
     protected void configure() {
       listener().to(Lifecycle.class);
diff --git a/java/com/google/gerrit/server/account/InternalAccountDirectory.java b/java/com/google/gerrit/server/account/InternalAccountDirectory.java
index 9d684ac..130fa44 100644
--- a/java/com/google/gerrit/server/account/InternalAccountDirectory.java
+++ b/java/com/google/gerrit/server/account/InternalAccountDirectory.java
@@ -53,7 +53,7 @@
 public class InternalAccountDirectory extends AccountDirectory {
   static final Set<FillOptions> ID_ONLY = Collections.unmodifiableSet(EnumSet.of(FillOptions.ID));
 
-  public static class Module extends AbstractModule {
+  public static class InternalAccountDirectoryModule extends AbstractModule {
     @Override
     protected void configure() {
       bind(AccountDirectory.class).to(InternalAccountDirectory.class);
diff --git a/java/com/google/gerrit/server/api/GerritApiModule.java b/java/com/google/gerrit/server/api/GerritApiModule.java
index 9e60107..37fde47 100644
--- a/java/com/google/gerrit/server/api/GerritApiModule.java
+++ b/java/com/google/gerrit/server/api/GerritApiModule.java
@@ -16,16 +16,21 @@
 
 import com.google.gerrit.extensions.api.GerritApi;
 import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.server.api.accounts.AccountsModule;
+import com.google.gerrit.server.api.changes.ChangesModule;
+import com.google.gerrit.server.api.config.ConfigModule;
+import com.google.gerrit.server.api.groups.GroupsModule;
+import com.google.gerrit.server.api.projects.ProjectsModule;
 
 public class GerritApiModule extends FactoryModule {
   @Override
   protected void configure() {
     bind(GerritApi.class).to(GerritApiImpl.class);
 
-    install(new com.google.gerrit.server.api.accounts.Module());
-    install(new com.google.gerrit.server.api.changes.Module());
-    install(new com.google.gerrit.server.api.config.Module());
-    install(new com.google.gerrit.server.api.groups.Module());
-    install(new com.google.gerrit.server.api.projects.Module());
+    install(new AccountsModule());
+    install(new ChangesModule());
+    install(new ConfigModule());
+    install(new GroupsModule());
+    install(new ProjectsModule());
   }
 }
diff --git a/java/com/google/gerrit/server/api/accounts/Module.java b/java/com/google/gerrit/server/api/accounts/AccountsModule.java
similarity index 94%
rename from java/com/google/gerrit/server/api/accounts/Module.java
rename to java/com/google/gerrit/server/api/accounts/AccountsModule.java
index 15c6ddb..70513b6 100644
--- a/java/com/google/gerrit/server/api/accounts/Module.java
+++ b/java/com/google/gerrit/server/api/accounts/AccountsModule.java
@@ -17,7 +17,7 @@
 import com.google.gerrit.extensions.api.accounts.Accounts;
 import com.google.gerrit.extensions.config.FactoryModule;
 
-public class Module extends FactoryModule {
+public class AccountsModule extends FactoryModule {
   @Override
   protected void configure() {
     bind(Accounts.class).to(AccountsImpl.class);
diff --git a/java/com/google/gerrit/server/api/changes/Module.java b/java/com/google/gerrit/server/api/changes/ChangesModule.java
similarity index 96%
rename from java/com/google/gerrit/server/api/changes/Module.java
rename to java/com/google/gerrit/server/api/changes/ChangesModule.java
index f54d1fe..729396b 100644
--- a/java/com/google/gerrit/server/api/changes/Module.java
+++ b/java/com/google/gerrit/server/api/changes/ChangesModule.java
@@ -17,7 +17,7 @@
 import com.google.gerrit.extensions.api.changes.Changes;
 import com.google.gerrit.extensions.config.FactoryModule;
 
-public class Module extends FactoryModule {
+public class ChangesModule extends FactoryModule {
   @Override
   protected void configure() {
     bind(Changes.class).to(ChangesImpl.class);
diff --git a/java/com/google/gerrit/server/api/config/Module.java b/java/com/google/gerrit/server/api/config/ConfigModule.java
similarity index 94%
rename from java/com/google/gerrit/server/api/config/Module.java
rename to java/com/google/gerrit/server/api/config/ConfigModule.java
index b37aa1c..9340ae1 100644
--- a/java/com/google/gerrit/server/api/config/Module.java
+++ b/java/com/google/gerrit/server/api/config/ConfigModule.java
@@ -18,7 +18,7 @@
 import com.google.gerrit.extensions.api.config.Server;
 import com.google.gerrit.extensions.config.FactoryModule;
 
-public class Module extends FactoryModule {
+public class ConfigModule extends FactoryModule {
   @Override
   protected void configure() {
     bind(Config.class).to(ConfigImpl.class);
diff --git a/java/com/google/gerrit/server/api/groups/Module.java b/java/com/google/gerrit/server/api/groups/GroupsModule.java
similarity index 94%
rename from java/com/google/gerrit/server/api/groups/Module.java
rename to java/com/google/gerrit/server/api/groups/GroupsModule.java
index 7d7af4e..58925e6 100644
--- a/java/com/google/gerrit/server/api/groups/Module.java
+++ b/java/com/google/gerrit/server/api/groups/GroupsModule.java
@@ -17,7 +17,7 @@
 import com.google.gerrit.extensions.api.groups.Groups;
 import com.google.gerrit.extensions.config.FactoryModule;
 
-public class Module extends FactoryModule {
+public class GroupsModule extends FactoryModule {
   @Override
   protected void configure() {
     bind(Groups.class).to(GroupsImpl.class);
diff --git a/java/com/google/gerrit/server/api/projects/Module.java b/java/com/google/gerrit/server/api/projects/ProjectsModule.java
similarity index 95%
rename from java/com/google/gerrit/server/api/projects/Module.java
rename to java/com/google/gerrit/server/api/projects/ProjectsModule.java
index 8df5495..987c71f 100644
--- a/java/com/google/gerrit/server/api/projects/Module.java
+++ b/java/com/google/gerrit/server/api/projects/ProjectsModule.java
@@ -17,7 +17,7 @@
 import com.google.gerrit.extensions.api.projects.Projects;
 import com.google.gerrit.extensions.config.FactoryModule;
 
-public class Module extends FactoryModule {
+public class ProjectsModule extends FactoryModule {
   @Override
   protected void configure() {
     bind(Projects.class).to(ProjectsImpl.class);
diff --git a/java/com/google/gerrit/server/change/ChangeCleanupRunner.java b/java/com/google/gerrit/server/change/ChangeCleanupRunner.java
index 6cf7a8f..a080d15 100644
--- a/java/com/google/gerrit/server/change/ChangeCleanupRunner.java
+++ b/java/com/google/gerrit/server/change/ChangeCleanupRunner.java
@@ -30,7 +30,7 @@
 public class ChangeCleanupRunner implements Runnable {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  public static class Module extends LifecycleModule {
+  public static class ChangeCleanupRunnerModule extends LifecycleModule {
     @Override
     protected void configure() {
       listener().to(Lifecycle.class);
diff --git a/java/com/google/gerrit/server/change/PatchSetInserter.java b/java/com/google/gerrit/server/change/PatchSetInserter.java
index f093958..209901d 100644
--- a/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -104,6 +104,7 @@
   private boolean allowClosed;
   private boolean sendEmail = true;
   private String topic;
+  private boolean storeCopiedVotes = true;
 
   // Fields set during some phase of BatchUpdate.Op.
   private Change change;
@@ -203,6 +204,17 @@
     return this;
   }
 
+  /**
+   * We always want to store copied votes except when the change is getting submitted and a new
+   * patch-set is created on submit (using submit strategies such as "REBASE_ALWAYS"). In such
+   * cases, we already store the votes of the new patch-sets in SubmitStrategyOp#saveApprovals. We
+   * should not also store the copied votes.
+   */
+  public PatchSetInserter setStoreCopiedVotes(boolean storeCopiedVotes) {
+    this.storeCopiedVotes = storeCopiedVotes;
+    return this;
+  }
+
   public Change getChange() {
     checkState(change != null, "getChange() only valid after executing update");
     return change;
@@ -288,7 +300,9 @@
 
     // Approvals that are being set in the new patch-set during this operation are not available yet
     // outside of the scope of this method. Only copied approvals are set here.
-    approvalsUtil.byPatchSet(ctx.getNotes(), patchSet).forEach(a -> update.putCopiedApproval(a));
+    if (storeCopiedVotes) {
+      approvalsUtil.byPatchSet(ctx.getNotes(), patchSet).forEach(a -> update.putCopiedApproval(a));
+    }
 
     return true;
   }
diff --git a/java/com/google/gerrit/server/change/RebaseChangeOp.java b/java/com/google/gerrit/server/change/RebaseChangeOp.java
index 4952464..3e67cca 100644
--- a/java/com/google/gerrit/server/change/RebaseChangeOp.java
+++ b/java/com/google/gerrit/server/change/RebaseChangeOp.java
@@ -92,6 +92,7 @@
   private boolean detailedCommitMessage;
   private boolean postMessage = true;
   private boolean sendEmail = true;
+  private boolean storeCopiedVotes = true;
   private boolean matchAuthorToCommitterDate = false;
 
   private CodeReviewCommit rebasedCommit;
@@ -169,6 +170,17 @@
     return this;
   }
 
+  /**
+   * We always want to store copied votes except when the change is getting submitted and a new
+   * patch-set is created on submit (using submit strategies such as "REBASE_ALWAYS"). In such
+   * cases, we already store the votes of the new patch-sets in SubmitStrategyOp#saveApprovals. We
+   * should not also store the copied votes.
+   */
+  public RebaseChangeOp setStoreCopiedVotes(boolean storeCopiedVotes) {
+    this.storeCopiedVotes = storeCopiedVotes;
+    return this;
+  }
+
   public RebaseChangeOp setSendEmail(boolean sendEmail) {
     this.sendEmail = sendEmail;
     return this;
@@ -219,7 +231,10 @@
             .setFireRevisionCreated(fireRevisionCreated)
             .setCheckAddPatchSetPermission(checkAddPatchSetPermission)
             .setValidate(validate)
-            .setSendEmail(sendEmail);
+            .setSendEmail(sendEmail)
+            // The votes are automatically copied and they don't count as copied votes. See
+            // method's javadoc.
+            .setStoreCopiedVotes(storeCopiedVotes);
 
     if (!rebasedCommit.getFilesWithGitConflicts().isEmpty()
         && !notes.getChange().isWorkInProgress()) {
diff --git a/java/com/google/gerrit/server/config/DefaultUrlFormatter.java b/java/com/google/gerrit/server/config/DefaultUrlFormatter.java
index 060ee3f..095fa3e 100644
--- a/java/com/google/gerrit/server/config/DefaultUrlFormatter.java
+++ b/java/com/google/gerrit/server/config/DefaultUrlFormatter.java
@@ -25,7 +25,7 @@
 public class DefaultUrlFormatter implements UrlFormatter {
   private final Provider<String> canonicalWebUrlProvider;
 
-  public static class Module extends AbstractModule {
+  public static class DefaultUrlFormatterModule extends AbstractModule {
     @Override
     protected void configure() {
       DynamicItem.itemOf(binder(), UrlFormatter.class);
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 35b16b4..3a7f2b2 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -198,8 +198,8 @@
 import com.google.gerrit.server.restapi.change.OnPostReview;
 import com.google.gerrit.server.restapi.change.SuggestReviewers;
 import com.google.gerrit.server.restapi.group.GroupModule;
-import com.google.gerrit.server.rules.DefaultSubmitRule;
-import com.google.gerrit.server.rules.IgnoreSelfApprovalRule;
+import com.google.gerrit.server.rules.DefaultSubmitRule.DefaultSubmitRuleModule;
+import com.google.gerrit.server.rules.IgnoreSelfApprovalRule.IgnoreSelfApprovalRuleModule;
 import com.google.gerrit.server.rules.PrologModule;
 import com.google.gerrit.server.rules.RulesCache;
 import com.google.gerrit.server.rules.SubmitRule;
@@ -279,8 +279,8 @@
     install(new GroupModule());
     install(new NoteDbModule());
     install(new PrologModule());
-    install(new DefaultSubmitRule.Module());
-    install(new IgnoreSelfApprovalRule.Module());
+    install(new DefaultSubmitRuleModule());
+    install(new IgnoreSelfApprovalRuleModule());
     install(new ReceiveCommitsModule());
     install(new SshAddressesModule());
     install(new FileInfoJsonModule());
diff --git a/java/com/google/gerrit/server/events/EventBroker.java b/java/com/google/gerrit/server/events/EventBroker.java
index 0fcb64e..4001a48 100644
--- a/java/com/google/gerrit/server/events/EventBroker.java
+++ b/java/com/google/gerrit/server/events/EventBroker.java
@@ -47,7 +47,7 @@
 public class EventBroker implements EventDispatcher {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  public static class Module extends LifecycleModule {
+  public static class EventBrokerModule extends LifecycleModule {
     @Override
     protected void configure() {
       DynamicItem.itemOf(binder(), EventDispatcher.class);
diff --git a/java/com/google/gerrit/server/events/StreamEventsApiListener.java b/java/com/google/gerrit/server/events/StreamEventsApiListener.java
index edd1928..abacb85 100644
--- a/java/com/google/gerrit/server/events/StreamEventsApiListener.java
+++ b/java/com/google/gerrit/server/events/StreamEventsApiListener.java
@@ -89,7 +89,7 @@
         VoteDeletedListener {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  public static class Module extends AbstractModule {
+  public static class StreamEventsApiListenerModule extends AbstractModule {
     @Override
     protected void configure() {
       DynamicSet.bind(binder(), AssigneeChangedListener.class).to(StreamEventsApiListener.class);
diff --git a/java/com/google/gerrit/server/experiments/ConfigExperimentFeatures.java b/java/com/google/gerrit/server/experiments/ConfigExperimentFeatures.java
index f526935..227deb5 100644
--- a/java/com/google/gerrit/server/experiments/ConfigExperimentFeatures.java
+++ b/java/com/google/gerrit/server/experiments/ConfigExperimentFeatures.java
@@ -31,7 +31,7 @@
 @Singleton
 public class ConfigExperimentFeatures implements ExperimentFeatures {
 
-  public static class Module extends AbstractModule {
+  public static class ConfigExperimentFeaturesModule extends AbstractModule {
     @Override
     protected void configure() {
       bind(ExperimentFeatures.class).to(ConfigExperimentFeatures.class);
diff --git a/java/com/google/gerrit/server/git/GarbageCollection.java b/java/com/google/gerrit/server/git/GarbageCollection.java
index 9b52f48..a2942fe 100644
--- a/java/com/google/gerrit/server/git/GarbageCollection.java
+++ b/java/com/google/gerrit/server/git/GarbageCollection.java
@@ -18,6 +18,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.GarbageCollectionResult;
+import com.google.gerrit.common.data.GarbageCollectionResult.GcError;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.events.GarbageCollectorListener;
 import com.google.gerrit.server.config.GcConfig;
@@ -78,9 +79,7 @@
     Set<Project.NameKey> projectsToGc = gcQueue.addAll(projectNames);
     for (Project.NameKey projectName :
         Sets.difference(Sets.newHashSet(projectNames), projectsToGc)) {
-      result.addError(
-          new GarbageCollectionResult.Error(
-              GarbageCollectionResult.Error.Type.GC_ALREADY_SCHEDULED, projectName));
+      result.addError(new GcError(GcError.Type.GC_ALREADY_SCHEDULED, projectName));
     }
     for (Project.NameKey p : projectsToGc) {
       try (Repository repo = repoManager.openRepository(p)) {
@@ -97,13 +96,10 @@
         fire(p, statistics);
       } catch (RepositoryNotFoundException e) {
         logGcError(writer, p, e);
-        result.addError(
-            new GarbageCollectionResult.Error(
-                GarbageCollectionResult.Error.Type.REPOSITORY_NOT_FOUND, p));
+        result.addError(new GcError(GcError.Type.REPOSITORY_NOT_FOUND, p));
       } catch (Exception e) {
         logGcError(writer, p, e);
-        result.addError(
-            new GarbageCollectionResult.Error(GarbageCollectionResult.Error.Type.GC_FAILED, p));
+        result.addError(new GcError(GcError.Type.GC_FAILED, p));
       } finally {
         gcQueue.gcFinished(p);
       }
diff --git a/java/com/google/gerrit/server/git/GitRepositoryManagerModule.java b/java/com/google/gerrit/server/git/GitRepositoryManagerModule.java
index 354b69f..6266925 100644
--- a/java/com/google/gerrit/server/git/GitRepositoryManagerModule.java
+++ b/java/com/google/gerrit/server/git/GitRepositoryManagerModule.java
@@ -16,6 +16,8 @@
 
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.server.config.RepositoryConfig;
+import com.google.gerrit.server.git.LocalDiskRepositoryManager.LocalDiskRepositoryManagerModule;
+import com.google.gerrit.server.git.MultiBaseLocalDiskRepositoryManager.MultiBaseLocalDiskRepositoryManagerModule;
 import com.google.inject.Inject;
 
 /**
@@ -34,9 +36,9 @@
   @Override
   protected void configure() {
     if (repoConfig.getAllBasePaths().isEmpty()) {
-      install(new LocalDiskRepositoryManager.Module());
+      install(new LocalDiskRepositoryManagerModule());
     } else {
-      install(new MultiBaseLocalDiskRepositoryManager.Module());
+      install(new MultiBaseLocalDiskRepositoryManagerModule());
     }
   }
 }
diff --git a/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java b/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
index 1dc5c16..8527ff8 100644
--- a/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
+++ b/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
@@ -53,7 +53,7 @@
 public class LocalDiskRepositoryManager implements GitRepositoryManager {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  public static class Module extends LifecycleModule {
+  public static class LocalDiskRepositoryManagerModule extends LifecycleModule {
     @Override
     protected void configure() {
       listener().to(LocalDiskRepositoryManager.Lifecycle.class);
diff --git a/java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java b/java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java
index fd6506a..72e2df4 100644
--- a/java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java
+++ b/java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java
@@ -35,7 +35,7 @@
 @Singleton
 public class MultiBaseLocalDiskRepositoryManager extends LocalDiskRepositoryManager {
 
-  public static class Module extends LifecycleModule {
+  public static class MultiBaseLocalDiskRepositoryManagerModule extends LifecycleModule {
     @Override
     protected void configure() {
       bind(GitRepositoryManager.class).to(MultiBaseLocalDiskRepositoryManager.class);
diff --git a/java/com/google/gerrit/server/git/MultiProgressMonitor.java b/java/com/google/gerrit/server/git/MultiProgressMonitor.java
index a4b1033..3de8ff3 100644
--- a/java/com/google/gerrit/server/git/MultiProgressMonitor.java
+++ b/java/com/google/gerrit/server/git/MultiProgressMonitor.java
@@ -343,9 +343,9 @@
     }
 
     // The loop exits as soon as the worker calls end(), but we give it another
-    // maxIntervalNanos to finish up and return.
+    // 2 x maxIntervalNanos to finish up and return.
     try {
-      return workerFuture.get(maxIntervalNanos, NANOSECONDS);
+      return workerFuture.get(2 * maxIntervalNanos, NANOSECONDS);
     } catch (InterruptedException | CancellationException e) {
       logger.atWarning().withCause(e).log(
           "unable to finish processing (task=%s(%s))", taskKind, taskName);
diff --git a/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java b/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
index fed6541..ff5bcc2 100644
--- a/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
+++ b/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
@@ -59,14 +59,14 @@
 
   static final String ID_CACHE = "changes";
 
-  public static class Module extends CacheModule {
+  public static class SearchingChangeCacheImplModule extends CacheModule {
     private final boolean slave;
 
-    public Module() {
+    public SearchingChangeCacheImplModule() {
       this(false);
     }
 
-    public Module(boolean slave) {
+    public SearchingChangeCacheImplModule(boolean slave) {
       this.slave = slave;
     }
 
diff --git a/java/com/google/gerrit/server/git/WorkQueue.java b/java/com/google/gerrit/server/git/WorkQueue.java
index 4b08040..8b59474 100644
--- a/java/com/google/gerrit/server/git/WorkQueue.java
+++ b/java/com/google/gerrit/server/git/WorkQueue.java
@@ -76,7 +76,7 @@
     }
   }
 
-  public static class Module extends LifecycleModule {
+  public static class WorkQueueModule extends LifecycleModule {
     @Override
     protected void configure() {
       bind(WorkQueue.class);
diff --git a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
index 488b008..85d7db0 100644
--- a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
@@ -105,7 +105,7 @@
         @Nullable MessageSender messageSender);
   }
 
-  public static class Module extends PrivateModule {
+  public static class AsyncReceiveCommitsModule extends PrivateModule {
     @Override
     public void configure() {
       install(new FactoryModuleBuilder().build(LazyPostReceiveHookChain.Factory.class));
@@ -355,10 +355,6 @@
       try {
         result = preReceive(commands);
       } catch (TimeoutException e) {
-        metrics.timeouts.increment();
-        logger.atWarning().withCause(e).log(
-            "Timeout in ReceiveCommits while processing changes for project %s",
-            projectState.getName());
         receivePack.sendError("timeout while processing changes");
         rejectCommandsNotAttempted(commands);
         return;
@@ -414,6 +410,12 @@
         throw new IllegalStateException("unable to get receive commits result");
       }
       return runnable.get();
+    } catch (TimeoutException e) {
+      metrics.timeouts.increment();
+      logger.atWarning().withCause(e).log(
+          "Timeout in ReceiveCommits while processing changes for project %s",
+          projectState.getName());
+      throw e;
     } catch (InterruptedException | ExecutionException e) {
       throw new UncheckedExecutionException(e);
     }
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index c1cd30c..66e7d80 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -709,10 +709,10 @@
       sendErrorMessages();
 
       commandProgress.end();
-      progress.end();
       loggingTags = traceContext.getTags();
       logger.atFine().log("Processing commands done.");
     }
+    progress.end();
     return result.build();
   }
 
diff --git a/java/com/google/gerrit/server/group/PeriodicGroupIndexer.java b/java/com/google/gerrit/server/group/PeriodicGroupIndexer.java
index 2823548..72e15ee 100644
--- a/java/com/google/gerrit/server/group/PeriodicGroupIndexer.java
+++ b/java/com/google/gerrit/server/group/PeriodicGroupIndexer.java
@@ -59,7 +59,7 @@
 public class PeriodicGroupIndexer implements Runnable {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  public static class Module extends LifecycleModule {
+  public static class PeriodicGroupIndexerModule extends LifecycleModule {
     @Override
     protected void configure() {
       listener().to(Lifecycle.class);
diff --git a/java/com/google/gerrit/server/group/db/GroupNameNotes.java b/java/com/google/gerrit/server/group/db/GroupNameNotes.java
index cdba81f..7c4fb16f9 100644
--- a/java/com/google/gerrit/server/group/db/GroupNameNotes.java
+++ b/java/com/google/gerrit/server/group/db/GroupNameNotes.java
@@ -35,6 +35,9 @@
 import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.git.ObjectIds;
 import com.google.gerrit.server.git.meta.VersionedMetaData;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Map;
@@ -210,7 +213,11 @@
     if (ref == null) {
       return ImmutableList.of();
     }
-    try (RevWalk revWalk = new RevWalk(repository);
+    try (TraceTimer ignored =
+            TraceContext.newTimer(
+                "Loading all groups",
+                Metadata.builder().noteDbRefName(RefNames.REFS_GROUPNAMES).build());
+        RevWalk revWalk = new RevWalk(repository);
         ObjectReader reader = revWalk.getObjectReader()) {
       RevCommit notesCommit = revWalk.parseCommit(ref.getObjectId());
       NoteMap noteMap = NoteMap.read(reader, notesCommit);
diff --git a/java/com/google/gerrit/server/index/OnlineUpgrader.java b/java/com/google/gerrit/server/index/OnlineUpgrader.java
index bfcf55f..a7e3dbc 100644
--- a/java/com/google/gerrit/server/index/OnlineUpgrader.java
+++ b/java/com/google/gerrit/server/index/OnlineUpgrader.java
@@ -20,7 +20,7 @@
 
 /** Listener to handle upgrading index schema versions at startup. */
 public class OnlineUpgrader implements LifecycleListener {
-  public static class Module extends LifecycleModule {
+  public static class OnlineUpgraderModule extends LifecycleModule {
     @Override
     protected void configure() {
       listener().to(OnlineUpgrader.class);
diff --git a/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java b/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
index bdfaf6d..36e801b 100644
--- a/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
+++ b/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
@@ -34,7 +34,7 @@
   private final SignedToken emailRegistrationToken;
   private final AuthRequest.Factory authRequestFactory;
 
-  public static class Module extends AbstractModule {
+  public static class SignedTokenEmailTokenVerifierModule extends AbstractModule {
     @Override
     protected void configure() {
       bind(EmailTokenVerifier.class).to(SignedTokenEmailTokenVerifier.class);
diff --git a/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index e98647d..0710784 100644
--- a/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -57,6 +57,7 @@
 import com.google.gerrit.server.extensions.events.CommentAdded;
 import com.google.gerrit.server.mail.MailFilter;
 import com.google.gerrit.server.mail.send.InboundEmailRejectionSender;
+import com.google.gerrit.server.mail.send.InboundEmailRejectionSender.InboundEmailError;
 import com.google.gerrit.server.mail.send.MessageIdGenerator;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
@@ -184,7 +185,7 @@
       logger.atSevere().log(
           "Message %s is missing required metadata, have %s. Will delete message.",
           message.id(), metadata);
-      sendRejectionEmail(message, InboundEmailRejectionSender.Error.PARSING_ERROR);
+      sendRejectionEmail(message, InboundEmailError.PARSING_ERROR);
       return;
     }
 
@@ -198,7 +199,7 @@
 
       // We don't want to send an email if no accounts are linked to it.
       if (accountIds.size() > 1) {
-        sendRejectionEmail(message, InboundEmailRejectionSender.Error.UNKNOWN_ACCOUNT);
+        sendRejectionEmail(message, InboundEmailError.UNKNOWN_ACCOUNT);
       }
       return;
     }
@@ -210,14 +211,14 @@
     }
     if (!accountState.get().account().isActive()) {
       logger.atWarning().log("Mail: Account %s is inactive. Will delete message.", accountId);
-      sendRejectionEmail(message, InboundEmailRejectionSender.Error.INACTIVE_ACCOUNT);
+      sendRejectionEmail(message, InboundEmailError.INACTIVE_ACCOUNT);
       return;
     }
 
     persistComments(buf, message, metadata, accountId);
   }
 
-  private void sendRejectionEmail(MailMessage message, InboundEmailRejectionSender.Error reason) {
+  private void sendRejectionEmail(MailMessage message, InboundEmailError reason) {
     try {
       InboundEmailRejectionSender emailSender =
           emailRejectionSender.create(message.from(), message.id(), reason);
@@ -238,7 +239,7 @@
               .enforceVisibility(true)
               .byLegacyChangeId(Change.id(metadata.changeNumber));
       if (changeDataList.isEmpty()) {
-        sendRejectionEmail(message, InboundEmailRejectionSender.Error.CHANGE_NOT_FOUND);
+        sendRejectionEmail(message, InboundEmailError.CHANGE_NOT_FOUND);
         return;
       }
       if (changeDataList.size() != 1) {
@@ -248,7 +249,7 @@
                 + " Will delete message.",
             message.id(), metadata.changeNumber, changeDataList.size());
 
-        sendRejectionEmail(message, InboundEmailRejectionSender.Error.INTERNAL_EXCEPTION);
+        sendRejectionEmail(message, InboundEmailError.INTERNAL_EXCEPTION);
         return;
       }
       ChangeData cd = Iterables.getOnlyElement(changeDataList);
@@ -284,7 +285,7 @@
       if (parsedComments.isEmpty()) {
         logger.atWarning().log(
             "Could not parse any comments from %s. Will delete message.", message.id());
-        sendRejectionEmail(message, InboundEmailRejectionSender.Error.PARSING_ERROR);
+        sendRejectionEmail(message, InboundEmailError.PARSING_ERROR);
         return;
       }
 
@@ -305,7 +306,7 @@
           PublishCommentUtil.findInvalidComments(
               commentValidationCtx, commentValidators, parsedCommentsForValidation);
       if (!commentValidationFailures.isEmpty()) {
-        sendRejectionEmail(message, InboundEmailRejectionSender.Error.COMMENT_REJECTED);
+        sendRejectionEmail(message, InboundEmailError.COMMENT_REJECTED);
         return;
       }
 
diff --git a/java/com/google/gerrit/server/mail/receive/MailReceiver.java b/java/com/google/gerrit/server/mail/receive/MailReceiver.java
index e383207..23e1cc3 100644
--- a/java/com/google/gerrit/server/mail/receive/MailReceiver.java
+++ b/java/com/google/gerrit/server/mail/receive/MailReceiver.java
@@ -43,11 +43,11 @@
   private WorkQueue workQueue;
   private Timer timer;
 
-  public static class Module extends LifecycleModule {
+  public static class MailReceiverModule extends LifecycleModule {
     private final EmailSettings mailSettings;
 
     @Inject
-    Module(EmailSettings mailSettings) {
+    MailReceiverModule(EmailSettings mailSettings) {
       this.mailSettings = mailSettings;
     }
 
diff --git a/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java b/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
index acdeb5a..2e0eeb3 100644
--- a/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
+++ b/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
@@ -28,7 +28,7 @@
 public class InboundEmailRejectionSender extends OutgoingEmail {
 
   /** Used by the templating system to determine what error message should be sent */
-  public enum Error {
+  public enum InboundEmailError {
     PARSING_ERROR,
     INACTIVE_ACCOUNT,
     UNKNOWN_ACCOUNT,
@@ -38,11 +38,11 @@
   }
 
   public interface Factory {
-    InboundEmailRejectionSender create(Address to, String threadId, Error reason);
+    InboundEmailRejectionSender create(Address to, String threadId, InboundEmailError reason);
   }
 
   private final Address to;
-  private final Error reason;
+  private final InboundEmailError reason;
   private final String threadId;
 
   @Inject
@@ -50,7 +50,7 @@
       EmailArguments args,
       @Assisted Address to,
       @Assisted String threadId,
-      @Assisted Error reason) {
+      @Assisted InboundEmailError reason) {
     super(args, "error");
     this.to = requireNonNull(to);
     this.threadId = requireNonNull(threadId);
diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index 8824cbd..effeaea 100644
--- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.EmailHeader;
 import com.google.gerrit.entities.EmailHeader.AddressList;
+import com.google.gerrit.entities.EmailHeader.StringEmailHeader;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
@@ -301,7 +302,7 @@
     if (messageId != null) {
       String message = "<" + messageId.id() + suffix + "@" + getGerritHost() + ">";
       message = message.replaceAll("\\s", "");
-      va.headers.put(FieldName.MESSAGE_ID, new EmailHeader.String(message));
+      va.headers.put(FieldName.MESSAGE_ID, new StringEmailHeader(message));
     }
   }
 
@@ -383,7 +384,7 @@
 
   /** Set a header in the outgoing message. */
   protected void setHeader(String name, String value) {
-    headers.put(name, new EmailHeader.String(value));
+    headers.put(name, new StringEmailHeader(value));
   }
 
   /** Remove a header from the outgoing message. */
diff --git a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
index 1ad94be..d32e6fb 100644
--- a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.common.Version;
 import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.EmailHeader;
+import com.google.gerrit.entities.EmailHeader.StringEmailHeader;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.config.GerritServerConfig;
@@ -59,7 +60,7 @@
 
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  public static class Module extends AbstractModule {
+  public static class SmtpEmailSenderModule extends AbstractModule {
     @Override
     protected void configure() {
       bind(EmailSender.class).to(SmtpEmailSender.class);
@@ -386,7 +387,7 @@
 
   private static void setMissingHeader(Map<String, EmailHeader> hdrs, String name, String value) {
     if (!hdrs.containsKey(name) || hdrs.get(name).isEmpty()) {
-      hdrs.put(name, new EmailHeader.String(value));
+      hdrs.put(name, new StringEmailHeader(value));
     }
   }
 
diff --git a/java/com/google/gerrit/server/notedb/CommitRewriter.java b/java/com/google/gerrit/server/notedb/CommitRewriter.java
index e940b1e..eabee65 100644
--- a/java/com/google/gerrit/server/notedb/CommitRewriter.java
+++ b/java/com/google/gerrit/server/notedb/CommitRewriter.java
@@ -30,6 +30,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.UsedAt;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
@@ -117,6 +118,16 @@
     public boolean verifyCommits = true;
     /** Whether to compute and output the diff of the commit history for the backfilled refs. */
     public boolean outputDiff = true;
+
+    /** Max number of refs to update in a single {@link BatchRefUpdate}. */
+    public int maxRefsInBatch = 10000;
+    /**
+     * Max number of refs to fix by a single {@link RefsUpdate#backfillProject} run. Since second
+     * run on the same set of refs is a no-op, running with this option in a loop will eventually
+     * fix all refs. Number of executed {@link BatchRefUpdate} depends on {@link #maxRefsInBatch}
+     * option.
+     */
+    public int maxRefsToUpdate = 50000;
   }
 
   /** Result of the backfill run for a project. */
@@ -239,15 +250,24 @@
    */
   public BackfillResult backfillProject(
       Project.NameKey project, Repository repo, RunOptions options) {
+
+    checkState(
+        options.maxRefsInBatch > 0 && options.maxRefsToUpdate > 0,
+        "Expected maxRefsInBatch>0 && <= maxRefsToUpdate>0");
+    checkState(
+        options.maxRefsInBatch <= options.maxRefsToUpdate,
+        "Expected maxRefsInBatch(%s) <= maxRefsToUpdate(%s)",
+        options.maxRefsInBatch,
+        options.maxRefsToUpdate);
     BackfillResult result = new BackfillResult();
     result.ok = true;
-    try (RevWalk revWalk = new RevWalk(repo);
-        ObjectInserter ins = newPackInserter(repo)) {
-      BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
-      bru.setForceRefLog(true);
-      bru.setRefLogMessage(CommitRewriter.class.getName(), false);
-      bru.setAllowNonFastForwards(true);
+    int refsInUpdate = 0;
+    RefsUpdate refsUpdate = null;
+    try {
       for (Ref ref : repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_CHANGES)) {
+        if (result.fixedRefDiff.size() >= options.maxRefsToUpdate) {
+          return result;
+        }
         Change.Id changeId = Change.Id.fromRef(ref.getName());
         if (changeId == null || !ref.getName().equals(RefNames.changeMetaRef(changeId))) {
           continue;
@@ -262,14 +282,26 @@
               logger.atWarning().withCause(e).log("Failed to run verification on ref %s", ref);
             }
           }
+          if (refsUpdate == null) {
+            refsUpdate = RefsUpdate.create(repo);
+          }
           ChangeFixProgress changeFixProgress =
-              backfillChange(revWalk, ins, ref, accountsInChange, options);
+              backfillChange(refsUpdate, ref, accountsInChange, options);
           if (changeFixProgress.anyFixesApplied) {
-            bru.addCommand(
-                new ReceiveCommand(ref.getObjectId(), changeFixProgress.newTipId, ref.getName()));
+            refsInUpdate++;
+            refsUpdate
+                .batchRefUpdate()
+                .addCommand(
+                    new ReceiveCommand(
+                        ref.getObjectId(), changeFixProgress.newTipId, ref.getName()));
             result.fixedRefDiff.put(ref.getName(), changeFixProgress.commitDiffs);
           }
-
+          if (refsInUpdate >= options.maxRefsInBatch
+              || result.fixedRefDiff.size() >= options.maxRefsToUpdate) {
+            processUpdate(options, refsUpdate);
+            refsUpdate = null;
+            refsInUpdate = 0;
+          }
           if (!changeFixProgress.isValidAfterFix) {
             result.refsStillInvalidAfterFix.add(ref.getName());
           }
@@ -278,21 +310,34 @@
           result.refsFailedToFix.add(ref.getName());
         }
       }
-
-      if (!bru.getCommands().isEmpty()) {
-        if (!options.dryRun) {
-          ins.flush();
-          RefUpdateUtil.executeChecked(bru, revWalk);
-        }
-      }
+      processUpdate(options, refsUpdate);
     } catch (IOException e) {
-      logger.atWarning().withCause(e).log("Failed to fix project %s", project.get());
+      logger.atWarning().log("Failed to fix project %s. Reason: %s", project.get(), e.getMessage());
       result.ok = false;
+    } finally {
+      if (refsUpdate != null) {
+        refsUpdate.close();
+      }
     }
 
     return result;
   }
 
+  /** Executes a single {@link RefsUpdate#batchRefUpdate}. */
+  private void processUpdate(RunOptions options, @Nullable RefsUpdate refsUpdate)
+      throws IOException {
+    if (refsUpdate == null) {
+      return;
+    }
+    if (!refsUpdate.batchRefUpdate().getCommands().isEmpty()) {
+      if (!options.dryRun) {
+        refsUpdate.inserter().flush();
+        RefUpdateUtil.executeChecked(refsUpdate.batchRefUpdate(), refsUpdate.revWalk());
+      }
+    }
+    refsUpdate.close();
+  }
+
   /**
    * Retrieves accounts, that are associated with a change (e.g. reviewers, commenters, etc.). These
    * accounts are used to verify that commits do not contain user data. See {@link #verifyCommit}
@@ -376,8 +421,7 @@
    * ChangeFixProgress#newTipId}.
    */
   public ChangeFixProgress backfillChange(
-      RevWalk revWalk,
-      ObjectInserter inserter,
+      RefsUpdate refsUpdate,
       Ref ref,
       ImmutableSet<AccountState> accountsInChange,
       RunOptions options)
@@ -385,17 +429,17 @@
 
     ObjectId oldTip = ref.getObjectId();
     // Walk from the first commit of the branch.
-    revWalk.reset();
-    revWalk.markStart(revWalk.parseCommit(oldTip));
-    revWalk.sort(RevSort.TOPO);
+    refsUpdate.revWalk().reset();
+    refsUpdate.revWalk().markStart(refsUpdate.revWalk().parseCommit(oldTip));
+    refsUpdate.revWalk().sort(RevSort.TOPO);
 
-    revWalk.sort(RevSort.REVERSE);
+    refsUpdate.revWalk().sort(RevSort.REVERSE);
 
     RevCommit originalCommit;
 
     boolean rewriteStarted = false;
     ChangeFixProgress changeFixProgress = new ChangeFixProgress(ref.getName());
-    while ((originalCommit = revWalk.next()) != null) {
+    while ((originalCommit = refsUpdate.revWalk().next()) != null) {
 
       changeFixProgress.updateAuthorId =
           parseIdent(changeFixProgress, originalCommit.getAuthorIdent());
@@ -453,7 +497,8 @@
       cb.setEncoding(originalCommit.getEncoding());
       byte[] newCommitContent = cb.build();
       checkCommitModification(originalCommit, newCommitContent);
-      changeFixProgress.newTipId = inserter.insert(Constants.OBJ_COMMIT, newCommitContent);
+      changeFixProgress.newTipId =
+          refsUpdate.inserter().insert(Constants.OBJ_COMMIT, newCommitContent);
       // Only compute diff if the content of the commit was actually changed.
       if (options.outputDiff && needsFix) {
         String diff = computeDiff(originalCommit.getRawBuffer(), newCommitContent);
@@ -1283,4 +1328,33 @@
 
     abstract Optional<String> email();
   }
+
+  /**
+   * Objects, needed to fix Refs in a single {@link BatchRefUpdate}. Number of changes in a batch
+   * are limited by {@link RunOptions#maxRefsInBatch}.
+   */
+  @AutoValue
+  abstract static class RefsUpdate implements AutoCloseable {
+    static RefsUpdate create(Repository repo) {
+      RevWalk revWalk = new RevWalk(repo);
+      ObjectInserter inserter = newPackInserter(repo);
+      BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
+      bru.setForceRefLog(true);
+      bru.setRefLogMessage(CommitRewriter.class.getName(), false);
+      bru.setAllowNonFastForwards(true);
+      return new AutoValue_CommitRewriter_RefsUpdate(bru, revWalk, inserter);
+    }
+
+    @Override
+    public void close() {
+      inserter().close();
+      revWalk().close();
+    }
+
+    abstract BatchRefUpdate batchRefUpdate();
+
+    abstract RevWalk revWalk();
+
+    abstract ObjectInserter inserter();
+  }
 }
diff --git a/java/com/google/gerrit/server/project/DefaultProjectNameLockManager.java b/java/com/google/gerrit/server/project/DefaultProjectNameLockManager.java
index ab347e5..762e244 100644
--- a/java/com/google/gerrit/server/project/DefaultProjectNameLockManager.java
+++ b/java/com/google/gerrit/server/project/DefaultProjectNameLockManager.java
@@ -25,7 +25,7 @@
 @Singleton
 public class DefaultProjectNameLockManager implements ProjectNameLockManager {
 
-  public static class Module extends AbstractModule {
+  public static class DefaultProjectNameLockManagerModule extends AbstractModule {
     @Override
     protected void configure() {
       DynamicItem.bind(binder(), ProjectNameLockManager.class)
diff --git a/java/com/google/gerrit/server/restapi/RestApiModule.java b/java/com/google/gerrit/server/restapi/RestApiModule.java
index 909c1f4..dffcf44 100644
--- a/java/com/google/gerrit/server/restapi/RestApiModule.java
+++ b/java/com/google/gerrit/server/restapi/RestApiModule.java
@@ -15,20 +15,26 @@
 package com.google.gerrit.server.restapi;
 
 import com.google.gerrit.server.plugins.PluginRestApiModule;
+import com.google.gerrit.server.restapi.access.AccessRestApiModule;
+import com.google.gerrit.server.restapi.account.AccountRestApiModule;
+import com.google.gerrit.server.restapi.change.ChangeRestApiModule;
+import com.google.gerrit.server.restapi.config.ConfigRestApiModule;
 import com.google.gerrit.server.restapi.config.RestCacheAdminModule;
+import com.google.gerrit.server.restapi.group.GroupRestApiModule;
+import com.google.gerrit.server.restapi.project.ProjectRestApiModule;
 import com.google.inject.AbstractModule;
 
 public class RestApiModule extends AbstractModule {
   @Override
   protected void configure() {
-    install(new com.google.gerrit.server.restapi.access.Module());
-    install(new com.google.gerrit.server.restapi.account.Module());
-    install(new com.google.gerrit.server.restapi.change.Module());
-    install(new com.google.gerrit.server.restapi.config.Module());
+    install(new AccessRestApiModule());
+    install(new AccountRestApiModule());
+    install(new ChangeRestApiModule());
+    install(new ConfigRestApiModule());
     install(new RestCacheAdminModule());
-    install(new com.google.gerrit.server.restapi.group.Module());
+    install(new GroupRestApiModule());
     install(new PluginRestApiModule());
-    install(new com.google.gerrit.server.restapi.project.Module());
-    install(new com.google.gerrit.server.restapi.project.Module.BatchModule());
+    install(new ProjectRestApiModule());
+    install(new ProjectRestApiModule.BatchModule());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/access/Module.java b/java/com/google/gerrit/server/restapi/access/AccessRestApiModule.java
similarity index 94%
rename from java/com/google/gerrit/server/restapi/access/Module.java
rename to java/com/google/gerrit/server/restapi/access/AccessRestApiModule.java
index 3a4955d..71bb696 100644
--- a/java/com/google/gerrit/server/restapi/access/Module.java
+++ b/java/com/google/gerrit/server/restapi/access/AccessRestApiModule.java
@@ -20,7 +20,7 @@
 import com.google.gerrit.extensions.restapi.RestApiModule;
 
 /** Guice module that binds all REST endpoints for {@code /access/}. */
-public class Module extends RestApiModule {
+public class AccessRestApiModule extends RestApiModule {
   @Override
   protected void configure() {
     bind(AccessCollection.class);
diff --git a/java/com/google/gerrit/server/restapi/account/Module.java b/java/com/google/gerrit/server/restapi/account/AccountRestApiModule.java
similarity index 98%
rename from java/com/google/gerrit/server/restapi/account/Module.java
rename to java/com/google/gerrit/server/restapi/account/AccountRestApiModule.java
index dd38ccf..2a8f55f 100644
--- a/java/com/google/gerrit/server/restapi/account/Module.java
+++ b/java/com/google/gerrit/server/restapi/account/AccountRestApiModule.java
@@ -30,7 +30,7 @@
 import com.google.gerrit.server.account.externalids.ExternalIdNotes;
 import com.google.inject.Provides;
 
-public class Module extends RestApiModule {
+public class AccountRestApiModule extends RestApiModule {
   @Override
   protected void configure() {
     bind(AccountsCollection.class);
diff --git a/java/com/google/gerrit/server/restapi/change/Module.java b/java/com/google/gerrit/server/restapi/change/ChangeRestApiModule.java
similarity index 99%
rename from java/com/google/gerrit/server/restapi/change/Module.java
rename to java/com/google/gerrit/server/restapi/change/ChangeRestApiModule.java
index 4d955fb..02b4c13 100644
--- a/java/com/google/gerrit/server/restapi/change/Module.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeRestApiModule.java
@@ -53,7 +53,7 @@
 import com.google.gerrit.server.restapi.change.Reviewed.PutReviewed;
 import com.google.gerrit.server.util.AttentionSetEmail;
 
-public class Module extends RestApiModule {
+public class ChangeRestApiModule extends RestApiModule {
   @Override
   protected void configure() {
     bind(ChangesCollection.class);
diff --git a/java/com/google/gerrit/server/restapi/config/Module.java b/java/com/google/gerrit/server/restapi/config/ConfigRestApiModule.java
similarity index 97%
rename from java/com/google/gerrit/server/restapi/config/Module.java
rename to java/com/google/gerrit/server/restapi/config/ConfigRestApiModule.java
index 78a7f48..427ff84 100644
--- a/java/com/google/gerrit/server/restapi/config/Module.java
+++ b/java/com/google/gerrit/server/restapi/config/ConfigRestApiModule.java
@@ -22,7 +22,7 @@
 import com.google.gerrit.server.config.CapabilityResource;
 import com.google.gerrit.server.config.TopMenuResource;
 
-public class Module extends RestApiModule {
+public class ConfigRestApiModule extends RestApiModule {
   @Override
   protected void configure() {
     DynamicMap.mapOf(binder(), CapabilityResource.CAPABILITY_KIND);
diff --git a/java/com/google/gerrit/server/restapi/group/Module.java b/java/com/google/gerrit/server/restapi/group/GroupRestApiModule.java
similarity index 98%
rename from java/com/google/gerrit/server/restapi/group/Module.java
rename to java/com/google/gerrit/server/restapi/group/GroupRestApiModule.java
index 45ac411..8024862 100644
--- a/java/com/google/gerrit/server/restapi/group/Module.java
+++ b/java/com/google/gerrit/server/restapi/group/GroupRestApiModule.java
@@ -32,7 +32,7 @@
 import com.google.gerrit.server.restapi.group.DeleteSubgroups.DeleteSubgroup;
 import com.google.inject.Provides;
 
-public class Module extends RestApiModule {
+public class GroupRestApiModule extends RestApiModule {
 
   @Override
   protected void configure() {
diff --git a/java/com/google/gerrit/server/restapi/project/GarbageCollect.java b/java/com/google/gerrit/server/restapi/project/GarbageCollect.java
index abd47e9..7457eb7 100644
--- a/java/com/google/gerrit/server/restapi/project/GarbageCollect.java
+++ b/java/com/google/gerrit/server/restapi/project/GarbageCollect.java
@@ -18,6 +18,7 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.gerrit.common.data.GarbageCollectionResult;
+import com.google.gerrit.common.data.GarbageCollectionResult.GcError;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
@@ -125,7 +126,7 @@
           GarbageCollectionResult result = runGC(project, input, progressWriter);
           String msg = "Garbage collection completed successfully.";
           if (result.hasErrors()) {
-            for (GarbageCollectionResult.Error e : result.getErrors()) {
+            for (GcError e : result.getErrors()) {
               switch (e.getType()) {
                 case REPOSITORY_NOT_FOUND:
                   msg = "Error: project \"" + e.getProjectName() + "\" not found.";
diff --git a/java/com/google/gerrit/server/restapi/project/Module.java b/java/com/google/gerrit/server/restapi/project/ProjectRestApiModule.java
similarity index 98%
rename from java/com/google/gerrit/server/restapi/project/Module.java
rename to java/com/google/gerrit/server/restapi/project/ProjectRestApiModule.java
index 9217077..410a88c8 100644
--- a/java/com/google/gerrit/server/restapi/project/Module.java
+++ b/java/com/google/gerrit/server/restapi/project/ProjectRestApiModule.java
@@ -31,7 +31,7 @@
 import com.google.gerrit.server.restapi.change.CherryPickCommit;
 import com.google.gerrit.server.validators.ProjectCreationValidationListener;
 
-public class Module extends RestApiModule {
+public class ProjectRestApiModule extends RestApiModule {
 
   @Override
   protected void configure() {
diff --git a/java/com/google/gerrit/server/rules/DefaultSubmitRule.java b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
index fe429dd..9f64863 100644
--- a/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
+++ b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
@@ -39,7 +39,7 @@
  */
 @Singleton
 public final class DefaultSubmitRule implements SubmitRule {
-  public static class Module extends FactoryModule {
+  public static class DefaultSubmitRuleModule extends FactoryModule {
     @Override
     public void configure() {
       bind(SubmitRule.class)
diff --git a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
index 08335df..216405d 100644
--- a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
+++ b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
@@ -38,7 +38,7 @@
  */
 @Singleton
 public class IgnoreSelfApprovalRule implements SubmitRule {
-  public static class Module extends AbstractModule {
+  public static class IgnoreSelfApprovalRuleModule extends AbstractModule {
     @Override
     public void configure() {
       bind(SubmitRule.class)
diff --git a/java/com/google/gerrit/server/rules/PrologModule.java b/java/com/google/gerrit/server/rules/PrologModule.java
index 37dbba6..5cf4220 100644
--- a/java/com/google/gerrit/server/rules/PrologModule.java
+++ b/java/com/google/gerrit/server/rules/PrologModule.java
@@ -17,12 +17,13 @@
 import com.google.gerrit.extensions.annotations.Exports;
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.server.rules.RulesCache.RulesCacheModule;
 
 public class PrologModule extends FactoryModule {
   @Override
   protected void configure() {
     install(new EnvironmentModule());
-    install(new RulesCache.Module());
+    install(new RulesCacheModule());
     bind(PrologEnvironment.Args.class);
     factory(PrologRuleEvaluator.Factory.class);
 
diff --git a/java/com/google/gerrit/server/rules/RulesCache.java b/java/com/google/gerrit/server/rules/RulesCache.java
index 8b72714..706804a 100644
--- a/java/com/google/gerrit/server/rules/RulesCache.java
+++ b/java/com/google/gerrit/server/rules/RulesCache.java
@@ -72,7 +72,7 @@
  */
 @Singleton
 public class RulesCache {
-  public static class Module extends CacheModule {
+  public static class RulesCacheModule extends CacheModule {
     @Override
     protected void configure() {
       cache(RulesCache.CACHE_NAME, ObjectId.class, PrologMachineCopy.class)
diff --git a/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
index 49dcc46..189d448 100644
--- a/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
@@ -66,10 +66,10 @@
   private static final String POSTGRESQL = "postgresql";
   private static final String URL = "url";
 
-  public static class Module extends LifecycleModule {
+  public static class JdbcAccountPatchReviewStoreModule extends LifecycleModule {
     private final Config cfg;
 
-    public Module(Config cfg) {
+    public JdbcAccountPatchReviewStoreModule(Config cfg) {
       this.cfg = cfg;
     }
 
diff --git a/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java b/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java
index 547c946..89ba1fa 100644
--- a/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java
+++ b/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java
@@ -59,7 +59,7 @@
 public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  public static class Module extends AbstractModule {
+  public static class LocalMergeSuperSetComputationModule extends AbstractModule {
     @Override
     protected void configure() {
       DynamicItem.bind(binder(), MergeSuperSetComputation.class)
diff --git a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
index cc3b75d..355d25f 100644
--- a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
+++ b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
@@ -211,7 +211,10 @@
                 .setPostMessage(false)
                 .setSendEmail(false)
                 .setMatchAuthorToCommitterDate(
-                    args.project.is(BooleanProjectConfig.MATCH_AUTHOR_TO_COMMITTER_DATE));
+                    args.project.is(BooleanProjectConfig.MATCH_AUTHOR_TO_COMMITTER_DATE))
+                // The votes are automatically copied and they don't count as copied votes. See
+                // method's javadoc.
+                .setStoreCopiedVotes(/* storeCopiedVotes = */ false);
         try {
           rebaseOp.updateRepo(ctx);
         } catch (MergeConflictException | NoSuchChangeException e) {
diff --git a/java/com/google/gerrit/server/submit/SubscriptionGraph.java b/java/com/google/gerrit/server/submit/SubscriptionGraph.java
index dfbbf81..d434890 100644
--- a/java/com/google/gerrit/server/submit/SubscriptionGraph.java
+++ b/java/com/google/gerrit/server/submit/SubscriptionGraph.java
@@ -158,7 +158,7 @@
         throws SubmoduleConflictException;
   }
 
-  public static class Module extends AbstractModule {
+  public static class SubscriptionGraphModule extends AbstractModule {
     @Override
     protected void configure() {
       bind(Factory.class).annotatedWith(VanillaSubscriptionGraph.class).to(DefaultFactory.class);
diff --git a/java/com/google/gerrit/server/update/SuperprojectUpdateSubmissionListener.java b/java/com/google/gerrit/server/update/SuperprojectUpdateSubmissionListener.java
index 4c65c80..8a216cd 100644
--- a/java/com/google/gerrit/server/update/SuperprojectUpdateSubmissionListener.java
+++ b/java/com/google/gerrit/server/update/SuperprojectUpdateSubmissionListener.java
@@ -39,7 +39,7 @@
   private ImmutableList<BatchUpdate> batchUpdates = ImmutableList.of();
   private boolean dryrun;
 
-  public static class Module extends AbstractModule {
+  public static class SuperprojectUpdateSubmissionListenerModule extends AbstractModule {
     @Provides
     @SuperprojectUpdateOnSubmission
     ImmutableList<SubmissionListener> provideSubmissionListeners(
diff --git a/java/com/google/gerrit/server/util/CommitMessageUtil.java b/java/com/google/gerrit/server/util/CommitMessageUtil.java
index 55e3951..dc9c2d9 100644
--- a/java/com/google/gerrit/server/util/CommitMessageUtil.java
+++ b/java/com/google/gerrit/server/util/CommitMessageUtil.java
@@ -51,7 +51,7 @@
    * the commit message.
    *
    * @throws BadRequestException if the commit message is null or empty
-   * @returns the trimmed message with a trailing newline character
+   * @return the trimmed message with a trailing newline character
    */
   public static String checkAndSanitizeCommitMessage(@Nullable String commitMessage)
       throws BadRequestException {
diff --git a/java/com/google/gerrit/sshd/SshModule.java b/java/com/google/gerrit/sshd/SshModule.java
index 9301f8a..ca452c1 100644
--- a/java/com/google/gerrit/sshd/SshModule.java
+++ b/java/com/google/gerrit/sshd/SshModule.java
@@ -29,7 +29,7 @@
 import com.google.gerrit.server.config.GerritRequestModule;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.QueueProvider;
-import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
+import com.google.gerrit.server.git.receive.AsyncReceiveCommits.AsyncReceiveCommitsModule;
 import com.google.gerrit.server.plugins.ModuleGenerator;
 import com.google.gerrit.server.plugins.ReloadPluginListener;
 import com.google.gerrit.server.plugins.StartPluginListener;
@@ -67,7 +67,7 @@
     bind(SshScope.class).in(SINGLETON);
 
     configureRequestScope();
-    install(new AsyncReceiveCommits.Module());
+    install(new AsyncReceiveCommitsModule());
     configureAliases();
 
     bind(SshLog.class);
diff --git a/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java b/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
index 8ee6a0d..e7fe22f 100644
--- a/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
+++ b/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
@@ -21,15 +21,15 @@
 import com.google.gerrit.sshd.Commands;
 import com.google.gerrit.sshd.DispatchCommandProvider;
 import com.google.gerrit.sshd.SuExec;
-import com.google.gerrit.sshd.plugin.LfsPluginAuthCommand;
+import com.google.gerrit.sshd.plugin.LfsPluginAuthCommand.LfsPluginAuthCommandModule;
 
 /** Register the commands a Gerrit server supports. */
 public class DefaultCommandModule extends CommandModule {
   private final DownloadConfig downloadConfig;
-  private final LfsPluginAuthCommand.Module lfsPluginAuthModule;
+  private final LfsPluginAuthCommandModule lfsPluginAuthModule;
 
   public DefaultCommandModule(
-      boolean slave, DownloadConfig downloadCfg, LfsPluginAuthCommand.Module module) {
+      boolean slave, DownloadConfig downloadCfg, LfsPluginAuthCommandModule module) {
     slaveMode = slave;
     downloadConfig = downloadCfg;
     lfsPluginAuthModule = module;
diff --git a/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java b/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
index 28a7804..63a01d00 100644
--- a/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
+++ b/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
@@ -21,6 +21,7 @@
 
 import com.google.common.collect.Lists;
 import com.google.gerrit.common.data.GarbageCollectionResult;
+import com.google.gerrit.common.data.GarbageCollectionResult.GcError;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
 import com.google.gerrit.server.git.GarbageCollection;
@@ -89,7 +90,7 @@
             .create()
             .run(projectNames, aggressive, showProgress ? stdout : null);
     if (result.hasErrors()) {
-      for (GarbageCollectionResult.Error e : result.getErrors()) {
+      for (GcError e : result.getErrors()) {
         String msg;
         switch (e.getType()) {
           case REPOSITORY_NOT_FOUND:
diff --git a/java/com/google/gerrit/sshd/plugin/LfsPluginAuthCommand.java b/java/com/google/gerrit/sshd/plugin/LfsPluginAuthCommand.java
index 8fb2461..8ba673a 100644
--- a/java/com/google/gerrit/sshd/plugin/LfsPluginAuthCommand.java
+++ b/java/com/google/gerrit/sshd/plugin/LfsPluginAuthCommand.java
@@ -36,11 +36,11 @@
     String authenticate(CurrentUser user, List<String> args) throws UnloggedFailure, Failure;
   }
 
-  public static class Module extends CommandModule {
+  public static class LfsPluginAuthCommandModule extends CommandModule {
     private final boolean pluginProvided;
 
     @Inject
-    Module(@GerritServerConfig Config cfg) {
+    LfsPluginAuthCommandModule(@GerritServerConfig Config cfg) {
       pluginProvided = cfg.getString("lfs", null, "plugin") != null;
     }
 
diff --git a/java/com/google/gerrit/testing/ConfigSuite.java b/java/com/google/gerrit/testing/ConfigSuite.java
index d7ae397..3101c48 100644
--- a/java/com/google/gerrit/testing/ConfigSuite.java
+++ b/java/com/google/gerrit/testing/ConfigSuite.java
@@ -25,7 +25,9 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
@@ -35,11 +37,15 @@
 import java.lang.reflect.Type;
 import java.util.List;
 import java.util.Map;
+import org.junit.internal.runners.statements.RunAfters;
+import org.junit.internal.runners.statements.RunBefores;
+import org.junit.rules.TestRule;
 import org.junit.runner.Runner;
 import org.junit.runners.BlockJUnit4ClassRunner;
 import org.junit.runners.Suite;
 import org.junit.runners.model.FrameworkMethod;
 import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
 
 /**
  * Suite to run tests with different {@code gerrit.config} values.
@@ -47,6 +53,9 @@
  * <p>For each {@link Config} method in the class and base classes, a new group of tests is created
  * with the {@link Parameter} field set to the config.
  *
+ * <p>Additional actions can be executed before or after each group of tests using
+ * {@literal @}BeforeConfig, {@literal @}AfterConfig or {@literal @}ConfigRule annotations.
+ *
  * <pre>
  * {@literal @}RunWith(ConfigSuite.class)
  * public abstract class MyAbstractTest {
@@ -128,6 +137,38 @@
   @Retention(RUNTIME)
   public static @interface Name {}
 
+  /**
+   * Annotation for methods which should be run after executing group of tests with a new
+   * configuration.
+   *
+   * <p>Works similar to {@link org.junit.AfterClass}, but a method can be executed multiple times
+   * if a test class provides multiple configs.
+   */
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target(ElementType.METHOD)
+  public @interface AfterConfig {}
+
+  /**
+   * Annotation for methods which should be run before executing group of tests with a new
+   * configuration.
+   *
+   * <p>Works similar to {@link org.junit.BeforeClass}, but a method can be executed multiple times
+   * if a test class provides multiple configs.
+   */
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target(ElementType.METHOD)
+  public @interface BeforeConfig {}
+
+  /**
+   * Annotation for fields or methods which wraps all tests with the same config
+   *
+   * <p>Works similar to {@link org.junit.ClassRule}, but Statement evaluates multiple time - ones
+   * for each config provided by a test class.
+   */
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target({ElementType.FIELD, ElementType.METHOD})
+  public @interface ConfigRule {}
+
   private static class ConfigRunner extends BlockJUnit4ClassRunner {
     private final org.eclipse.jgit.lib.Config cfg;
     private final Field parameterField;
@@ -168,6 +209,26 @@
       String n = method.getName();
       return name == null ? n : n + "[" + name + "]";
     }
+
+    @Override
+    protected Statement withBeforeClasses(Statement statement) {
+      List<FrameworkMethod> befores = getTestClass().getAnnotatedMethods(BeforeConfig.class);
+      return befores.isEmpty() ? statement : new RunBefores(statement, befores, null);
+    }
+
+    @Override
+    protected Statement withAfterClasses(Statement statement) {
+      List<FrameworkMethod> afters = getTestClass().getAnnotatedMethods(AfterConfig.class);
+      return afters.isEmpty() ? statement : new RunAfters(statement, afters, null);
+    }
+
+    @Override
+    protected List<TestRule> classRules() {
+      List<TestRule> result =
+          getTestClass().getAnnotatedMethodValues(null, ConfigRule.class, TestRule.class);
+      result.addAll(getTestClass().getAnnotatedFieldValues(null, ConfigRule.class, TestRule.class));
+      return result;
+    }
   }
 
   private static List<Runner> runnersFor(Class<?> clazz) {
diff --git a/java/com/google/gerrit/testing/FakeEmailSender.java b/java/com/google/gerrit/testing/FakeEmailSender.java
index fec9b27..918a622 100644
--- a/java/com/google/gerrit/testing/FakeEmailSender.java
+++ b/java/com/google/gerrit/testing/FakeEmailSender.java
@@ -49,7 +49,7 @@
 public class FakeEmailSender implements EmailSender {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  public static class Module extends AbstractModule {
+  public static class FakeEmailSenderModule extends AbstractModule {
     @Override
     public void configure() {
       bind(EmailSender.class).to(FakeEmailSender.class);
diff --git a/java/com/google/gerrit/testing/InMemoryModule.java b/java/com/google/gerrit/testing/InMemoryModule.java
index 3949de0..a45e906 100644
--- a/java/com/google/gerrit/testing/InMemoryModule.java
+++ b/java/com/google/gerrit/testing/InMemoryModule.java
@@ -54,7 +54,7 @@
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.config.CanonicalWebUrlModule;
 import com.google.gerrit.server.config.CanonicalWebUrlProvider;
-import com.google.gerrit.server.config.DefaultUrlFormatter;
+import com.google.gerrit.server.config.DefaultUrlFormatter.DefaultUrlFormatterModule;
 import com.google.gerrit.server.config.FileBasedAllProjectsConfigProvider;
 import com.google.gerrit.server.config.FileBasedGlobalPluginConfigProvider;
 import com.google.gerrit.server.config.GerritGlobalModule;
@@ -70,11 +70,11 @@
 import com.google.gerrit.server.config.SitePath;
 import com.google.gerrit.server.config.TrackingFooters;
 import com.google.gerrit.server.config.TrackingFootersProvider;
-import com.google.gerrit.server.experiments.ConfigExperimentFeatures;
+import com.google.gerrit.server.experiments.ConfigExperimentFeatures.ConfigExperimentFeaturesModule;
 import com.google.gerrit.server.git.GarbageCollection;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.PerThreadRequestScope;
-import com.google.gerrit.server.git.SearchingChangeCacheImpl;
+import com.google.gerrit.server.git.SearchingChangeCacheImpl.SearchingChangeCacheImplModule;
 import com.google.gerrit.server.git.WorkQueue;
 import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
 import com.google.gerrit.server.index.account.AllAccountsIndexer;
@@ -83,11 +83,11 @@
 import com.google.gerrit.server.index.group.AllGroupsIndexer;
 import com.google.gerrit.server.index.group.GroupIndexCollection;
 import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
-import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
+import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier.SignedTokenEmailTokenVerifierModule;
 import com.google.gerrit.server.patch.DiffExecutor;
 import com.google.gerrit.server.permissions.DefaultPermissionBackendModule;
 import com.google.gerrit.server.plugins.ServerInformationImpl;
-import com.google.gerrit.server.project.DefaultProjectNameLockManager;
+import com.google.gerrit.server.project.DefaultProjectNameLockManager.DefaultProjectNameLockManagerModule;
 import com.google.gerrit.server.restapi.RestApiModule;
 import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
 import com.google.gerrit.server.schema.SchemaCreator;
@@ -95,10 +95,11 @@
 import com.google.gerrit.server.securestore.DefaultSecureStore;
 import com.google.gerrit.server.securestore.SecureStore;
 import com.google.gerrit.server.ssh.NoSshKeyCache;
-import com.google.gerrit.server.submit.LocalMergeSuperSetComputation;
-import com.google.gerrit.server.submit.SubscriptionGraph;
-import com.google.gerrit.server.update.SuperprojectUpdateSubmissionListener;
+import com.google.gerrit.server.submit.LocalMergeSuperSetComputation.LocalMergeSuperSetComputationModule;
+import com.google.gerrit.server.submit.SubscriptionGraph.SubscriptionGraphModule;
+import com.google.gerrit.server.update.SuperprojectUpdateSubmissionListener.SuperprojectUpdateSubmissionListenerModule;
 import com.google.gerrit.server.util.ReplicaUtil;
+import com.google.gerrit.testing.FakeEmailSender.FakeEmailSenderModule;
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
@@ -185,11 +186,11 @@
     factory(PluginUser.Factory.class);
     install(new PluginApiModule());
     install(new DefaultPermissionBackendModule());
-    install(new SearchingChangeCacheImpl.Module());
+    install(new SearchingChangeCacheImplModule());
     factory(GarbageCollection.Factory.class);
     install(new AuditModule());
-    install(new SubscriptionGraph.Module());
-    install(new SuperprojectUpdateSubmissionListener.Module());
+    install(new SubscriptionGraphModule());
+    install(new SuperprojectUpdateSubmissionListenerModule());
 
     bindScope(RequestScoped.class, PerThreadRequestScope.REQUEST);
 
@@ -217,7 +218,7 @@
             return CanonicalWebUrlProvider.class;
           }
         });
-    install(new DefaultUrlFormatter.Module());
+    install(new DefaultUrlFormatterModule());
     // Replacement of DiffExecutorModule to not use thread pool in the tests
     install(
         new AbstractModule() {
@@ -233,10 +234,10 @@
         });
     install(new DefaultMemoryCacheModule());
     install(new H2CacheModule());
-    install(new FakeEmailSender.Module());
-    install(new SignedTokenEmailTokenVerifier.Module());
+    install(new FakeEmailSenderModule());
+    install(new SignedTokenEmailTokenVerifierModule());
     install(new GpgModule(cfg));
-    install(new LocalMergeSuperSetComputation.Module());
+    install(new LocalMergeSuperSetComputationModule());
 
     bind(AllAccountsIndexer.class).toProvider(Providers.of(null));
     bind(AllChangesIndexer.class).toProvider(Providers.of(null));
@@ -256,9 +257,9 @@
     bind(ServerInformation.class).to(ServerInformationImpl.class);
     install(new RestApiModule());
     install(new OAuthRestModule());
-    install(new DefaultProjectNameLockManager.Module());
+    install(new DefaultProjectNameLockManagerModule());
     install(new FileInfoJsonModule());
-    install(new ConfigExperimentFeatures.Module());
+    install(new ConfigExperimentFeaturesModule());
 
     bind(ProjectOperations.class).to(ProjectOperationsImpl.class);
   }
diff --git a/java/com/google/gerrit/testing/SystemPropertiesTestRule.java b/java/com/google/gerrit/testing/SystemPropertiesTestRule.java
new file mode 100644
index 0000000..c7a3542
--- /dev/null
+++ b/java/com/google/gerrit/testing/SystemPropertiesTestRule.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.testing;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.common.Nullable;
+import java.util.Map;
+import java.util.Optional;
+import org.junit.rules.ExternalResource;
+
+/** Setup system properties before tests and return previous value after tests are finished */
+public class SystemPropertiesTestRule extends ExternalResource {
+  ImmutableMap<String, Optional<String>> properties;
+  @Nullable ImmutableMap<String, Optional<String>> previousValues;
+
+  public SystemPropertiesTestRule(String key, String value) {
+    this(ImmutableMap.of(key, Optional.of(value)));
+  }
+
+  public SystemPropertiesTestRule(Map<String, Optional<String>> properties) {
+    this.properties = ImmutableMap.copyOf(properties);
+  }
+
+  @Override
+  protected void before() throws Throwable {
+    super.before();
+    checkState(
+        previousValues == null,
+        "after() wasn't called after the previous call to the before() method");
+    ImmutableMap.Builder<String, Optional<String>> previousValuesBuilder = ImmutableMap.builder();
+    for (String key : properties.keySet()) {
+      previousValuesBuilder.put(key, Optional.ofNullable(System.getProperty(key)));
+    }
+    previousValues = previousValuesBuilder.build();
+    properties.entrySet().stream().forEach(this::setSystemProperty);
+  }
+
+  @Override
+  protected void after() {
+    checkState(previousValues != null, "before() wasn't called");
+    previousValues.entrySet().stream().forEach(this::setSystemProperty);
+    previousValues = null;
+    super.after();
+  }
+
+  private void setSystemProperty(Map.Entry<String, Optional<String>> keyValue) {
+    if (keyValue.getValue().isPresent()) {
+      System.setProperty(keyValue.getKey(), keyValue.getValue().get());
+    } else {
+      System.clearProperty(keyValue.getKey());
+    }
+  }
+}
diff --git a/javatests/com/google/gerrit/acceptance/OutgoingEmailIT.java b/javatests/com/google/gerrit/acceptance/OutgoingEmailIT.java
index 5e85fae..7758be6 100644
--- a/javatests/com/google/gerrit/acceptance/OutgoingEmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/OutgoingEmailIT.java
@@ -19,7 +19,7 @@
 import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.entities.Address;
-import com.google.gerrit.entities.EmailHeader;
+import com.google.gerrit.entities.EmailHeader.StringEmailHeader;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.api.accounts.EmailInput;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -125,7 +125,7 @@
   }
 
   private static String getMessageId(FakeEmailSender sender) {
-    return ((EmailHeader.String)
+    return ((StringEmailHeader)
             (Iterables.getOnlyElement(sender.getMessages()).headers().get("Message-ID")))
         .getString();
   }
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountListenersIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountListenersIT.java
index 80eff96..3ead608 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountListenersIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountListenersIT.java
@@ -41,11 +41,11 @@
  */
 @TestPlugin(
     name = "account-listener-it-plugin",
-    sysModule = "com.google.gerrit.acceptance.api.accounts.AccountListenersIT$Module")
+    sysModule = "com.google.gerrit.acceptance.api.accounts.AccountListenersIT$TestModule")
 public class AccountListenersIT extends LightweightPluginDaemonTest {
   @Inject private AccountOperations accountOperations;
 
-  public static class Module extends AbstractModule {
+  public static class TestModule extends AbstractModule {
     @Override
     protected void configure() {
       DynamicSet.bind(binder(), AccountActivationValidationListener.class).to(Validator.class);
diff --git a/javatests/com/google/gerrit/acceptance/api/config/TopMenusIT.java b/javatests/com/google/gerrit/acceptance/api/config/TopMenusIT.java
index b6d2712..6ca3a37 100644
--- a/javatests/com/google/gerrit/acceptance/api/config/TopMenusIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/config/TopMenusIT.java
@@ -30,13 +30,13 @@
 
 @TestPlugin(
     name = "test-topmenus",
-    sysModule = "com.google.gerrit.acceptance.api.config.TopMenusIT$Module")
+    sysModule = "com.google.gerrit.acceptance.api.config.TopMenusIT$TestModule")
 public class TopMenusIT extends LightweightPluginDaemonTest {
 
   static final TopMenu.MenuEntry TEST_MENU_ENTRY =
       new TopMenu.MenuEntry("MyMenu", Collections.emptyList());
 
-  public static class Module extends AbstractModule {
+  public static class TestModule extends AbstractModule {
 
     @Override
     protected void configure() {
diff --git a/javatests/com/google/gerrit/acceptance/config/InstanceIdFromPluginIT.java b/javatests/com/google/gerrit/acceptance/config/InstanceIdFromPluginIT.java
index ac10e96..4b47668 100644
--- a/javatests/com/google/gerrit/acceptance/config/InstanceIdFromPluginIT.java
+++ b/javatests/com/google/gerrit/acceptance/config/InstanceIdFromPluginIT.java
@@ -36,10 +36,10 @@
 
 @TestPlugin(
     name = "instance-id-from-plugin",
-    sysModule = "com.google.gerrit.acceptance.config.InstanceIdFromPluginIT$Module")
+    sysModule = "com.google.gerrit.acceptance.config.InstanceIdFromPluginIT$TestModule")
 public class InstanceIdFromPluginIT extends LightweightPluginDaemonTest {
 
-  public static class Module extends AbstractModule {
+  public static class TestModule extends AbstractModule {
 
     @Override
     protected void configure() {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
index fff3cb6..b3592e3 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
@@ -14,9 +14,11 @@
 
 package com.google.gerrit.acceptance.rest.change;
 
+import static com.google.common.collect.ImmutableList.toImmutableList;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
 import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
+import static java.util.Comparator.comparing;
 
 import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.ExtensionRegistry;
@@ -26,6 +28,10 @@
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.common.FooterConstants;
 import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project.NameKey;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.SubmitInput;
 import com.google.gerrit.extensions.client.ChangeStatus;
 import com.google.gerrit.extensions.client.InheritableBoolean;
@@ -403,4 +409,54 @@
 
     assertSubmittable(change2Result.getChangeId());
   }
+
+  @Test
+  public void stickyVoteStoredOnSubmitOnNewPatchset_withoutCopyCondition() throws Exception {
+    try (ProjectConfigUpdate u = updateProject(NameKey.parse("All-Projects"))) {
+      u.getConfig()
+          .updateLabelType(
+              LabelId.CODE_REVIEW, b -> b.setCopyAllScoresIfListOfFilesDidNotChange(true));
+      u.save();
+    }
+    stickyVoteStoredOnSubmitOnNewPatchset();
+  }
+
+  @Test
+  public void stickyVoteStoredOnSubmitOnNewPatchset_withCopyCondition() throws Exception {
+    // Code-Review will be sticky.
+    try (ProjectConfigUpdate u = updateProject(NameKey.parse("All-Projects"))) {
+      u.getConfig()
+          .updateLabelType(LabelId.CODE_REVIEW, b -> b.setCopyCondition("has:unchanged-files"));
+      u.save();
+    }
+    stickyVoteStoredOnSubmitOnNewPatchset();
+  }
+
+  private void stickyVoteStoredOnSubmitOnNewPatchset() throws Exception {
+    PushOneCommit.Result r = createChange();
+
+    // Add a new vote.
+    ReviewInput input = new ReviewInput().label(LabelId.CODE_REVIEW, 2);
+    gApi.changes().id(r.getChangeId()).current().review(input);
+
+    // Submit, also keeping the Code-Review +2 vote.
+    gApi.changes().id(r.getChangeId()).current().submit();
+
+    // The last approval is stored on the submitted patch-set which was created by cherry-pick
+    // during submit.
+    PatchSetApproval patchSetApprovals =
+        Iterables.getLast(
+            r.getChange().notes().getApprovalsWithCopied().values().stream()
+                .filter(a -> a.labelId().equals(LabelId.create(LabelId.CODE_REVIEW)))
+                .sorted(comparing(a -> a.patchSetId().get()))
+                .collect(toImmutableList()));
+
+    assertThat(patchSetApprovals.patchSetId().get()).isEqualTo(2);
+    assertThat(patchSetApprovals.label()).isEqualTo(LabelId.CODE_REVIEW);
+    assertThat(patchSetApprovals.value()).isEqualTo((short) 2);
+
+    // The approval is not copied since we don't need to persist copied votes on submit, only
+    // persist votes normally.
+    assertThat(patchSetApprovals.copied()).isFalse();
+  }
 }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
index eeeac2a..aa93815 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
@@ -14,20 +14,27 @@
 
 package com.google.gerrit.acceptance.rest.change;
 
+import static com.google.common.collect.ImmutableList.toImmutableList;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static java.util.Comparator.comparing;
 
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.ExtensionRegistry;
 import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.TestProjectInput;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.common.FooterConstants;
+import com.google.gerrit.entities.LabelId;
 import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project.NameKey;
 import com.google.gerrit.exceptions.InternalServerWithUserMessageException;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -159,6 +166,56 @@
     }
   }
 
+  @Test
+  public void stickyVoteStoredOnSubmitOnNewPatchset_withoutCopyCondition() throws Exception {
+    try (ProjectConfigUpdate u = updateProject(NameKey.parse("All-Projects"))) {
+      u.getConfig()
+          .updateLabelType(
+              LabelId.CODE_REVIEW, b -> b.setCopyAllScoresIfListOfFilesDidNotChange(true));
+      u.save();
+    }
+    stickyVoteStoredOnSubmitOnNewPatchset();
+  }
+
+  @Test
+  public void stickyVoteStoredOnSubmitOnNewPatchset_withCopyCondition() throws Exception {
+    // Code-Review will be sticky.
+    try (ProjectConfigUpdate u = updateProject(NameKey.parse("All-Projects"))) {
+      u.getConfig()
+          .updateLabelType(LabelId.CODE_REVIEW, b -> b.setCopyCondition("has:unchanged-files"));
+      u.save();
+    }
+    stickyVoteStoredOnSubmitOnNewPatchset();
+  }
+
+  private void stickyVoteStoredOnSubmitOnNewPatchset() throws Exception {
+    PushOneCommit.Result r = createChange();
+
+    // Add a new vote.
+    ReviewInput input = new ReviewInput().label(LabelId.CODE_REVIEW, 2);
+    gApi.changes().id(r.getChangeId()).current().review(input);
+
+    // Submit, also keeping the Code-Review +2 vote.
+    gApi.changes().id(r.getChangeId()).current().submit();
+
+    // The last approval is stored on the submitted patch-set which was created by rebase during
+    // submit.
+    PatchSetApproval patchSetApprovals =
+        Iterables.getLast(
+            r.getChange().notes().getApprovalsWithCopied().values().stream()
+                .filter(a -> a.labelId().equals(LabelId.create(LabelId.CODE_REVIEW)))
+                .sorted(comparing(a -> a.patchSetId().get()))
+                .collect(toImmutableList()));
+
+    assertThat(patchSetApprovals.patchSetId().get()).isEqualTo(2);
+    assertThat(patchSetApprovals.label()).isEqualTo(LabelId.CODE_REVIEW);
+    assertThat(patchSetApprovals.value()).isEqualTo((short) 2);
+
+    // The approval is not copied since we don't need to persist copied votes on submit, only
+    // persist votes normally.
+    assertThat(patchSetApprovals.copied()).isFalse();
+  }
+
   private void assertLatestRevisionHasFooters(PushOneCommit.Result change) throws Throwable {
     RevCommit c = getCurrentCommit(change);
     assertThat(c.getFooterLines(FooterConstants.CHANGE_ID)).isNotEmpty();
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
index 6dd2f32..d6fcccc 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.acceptance.UseTimezone;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.entities.EmailHeader;
+import com.google.gerrit.entities.EmailHeader.StringEmailHeader;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.common.ChangeMessageInfo;
 import com.google.gerrit.mail.MailProcessingUtil;
@@ -114,8 +115,7 @@
     for (Map.Entry<String, Object> entry : want.entrySet()) {
       if (entry.getValue() instanceof String) {
         assertThat(have)
-            .containsEntry(
-                "X-" + entry.getKey(), new EmailHeader.String((String) entry.getValue()));
+            .containsEntry("X-" + entry.getKey(), new StringEmailHeader((String) entry.getValue()));
       } else if (entry.getValue() instanceof Date) {
         assertThat(have)
             .containsEntry("X-" + entry.getKey(), new EmailHeader.Date((Date) entry.getValue()));
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
index 06d9349..d4000b3 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
@@ -32,7 +32,7 @@
 import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
-import com.google.gerrit.entities.EmailHeader;
+import com.google.gerrit.entities.EmailHeader.StringEmailHeader;
 import com.google.gerrit.entities.Permission;
 import com.google.gerrit.extensions.annotations.Exports;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -445,7 +445,7 @@
     assertThat(message.body()).contains("rejected one or more comments");
 
     // ensure the message header contains a valid message id.
-    assertThat(((EmailHeader.String) (message.headers().get("Message-ID"))).getString())
+    assertThat(((StringEmailHeader) (message.headers().get("Message-ID"))).getString())
         .containsMatch("<someid-REJECTION-HTML@" + new URL(canonicalWebUrl.get()).getHost() + ">");
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
index 1c916a3..45a471b 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
@@ -18,6 +18,7 @@
 
 import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.entities.EmailHeader;
+import com.google.gerrit.entities.EmailHeader.StringEmailHeader;
 import java.net.URI;
 import java.util.Map;
 import org.junit.Test;
@@ -64,7 +65,7 @@
 
   private String headerString(Map<String, EmailHeader> headers, String name) {
     EmailHeader header = headers.get(name);
-    assertThat(header).isInstanceOf(EmailHeader.String.class);
-    return ((EmailHeader.String) header).getString();
+    assertThat(header).isInstanceOf(StringEmailHeader.class);
+    return ((StringEmailHeader) header).getString();
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/ExternalIdNotesUpsertPreprocessorIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/ExternalIdNotesUpsertPreprocessorIT.java
index 277c0e6..1b164eb 100644
--- a/javatests/com/google/gerrit/acceptance/server/notedb/ExternalIdNotesUpsertPreprocessorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/notedb/ExternalIdNotesUpsertPreprocessorIT.java
@@ -45,14 +45,14 @@
 @TestPlugin(
     name = "external-id-update-preprocessor",
     sysModule =
-        "com.google.gerrit.acceptance.server.notedb.ExternalIdNotesUpsertPreprocessorIT$Module")
+        "com.google.gerrit.acceptance.server.notedb.ExternalIdNotesUpsertPreprocessorIT$TestModule")
 public class ExternalIdNotesUpsertPreprocessorIT extends LightweightPluginDaemonTest {
   @Inject private Sequences sequences;
   @Inject private @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider;
   @Inject private ExternalIdNotes.Factory extIdNotesFactory;
   @Inject private ExternalIdFactory extIdFactory;
 
-  public static class Module extends AbstractModule {
+  public static class TestModule extends AbstractModule {
     @Override
     protected void configure() {
       bind(ExternalIdUpsertPreprocessor.class)
diff --git a/javatests/com/google/gerrit/acceptance/server/util/PluginLogFileIT.java b/javatests/com/google/gerrit/acceptance/server/util/PluginLogFileIT.java
index 06bf1ae..6486fbe 100644
--- a/javatests/com/google/gerrit/acceptance/server/util/PluginLogFileIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/util/PluginLogFileIT.java
@@ -34,7 +34,7 @@
 
   @Test
   public void testMultiThreadedPluginLogFile() throws Exception {
-    try (AutoCloseable ignored = installPlugin("my-plugin", Module.class)) {
+    try (AutoCloseable ignored = installPlugin("my-plugin", TestModule.class)) {
       ExecutorService service = Executors.newFixedThreadPool(NUMBER_OF_THREADS);
       CountDownLatch latch = new CountDownLatch(NUMBER_OF_THREADS);
       createChange();
diff --git a/javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java b/javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
index 9dcfa3f..10c81a1 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.acceptance.UseSsh;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.common.data.GarbageCollectionResult;
+import com.google.gerrit.common.data.GarbageCollectionResult.GcError;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.git.GarbageCollection;
 import com.google.gerrit.server.git.GarbageCollectionQueue;
@@ -91,8 +92,8 @@
             .run(Arrays.asList(allProjects, project, project2, project3));
     assertThat(result.hasErrors()).isTrue();
     assertThat(result.getErrors()).hasSize(1);
-    GarbageCollectionResult.Error error = result.getErrors().get(0);
-    assertThat(error.getType()).isEqualTo(GarbageCollectionResult.Error.Type.GC_ALREADY_SCHEDULED);
+    GcError error = result.getErrors().get(0);
+    assertThat(error.getType()).isEqualTo(GcError.Type.GC_ALREADY_SCHEDULED);
     assertThat(error.getProjectName()).isEqualTo(project);
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/index/DefaultIndexBindingIT.java b/javatests/com/google/gerrit/acceptance/testsuite/index/DefaultIndexBindingIT.java
index f5e4e09..8e0d4bb 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/index/DefaultIndexBindingIT.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/index/DefaultIndexBindingIT.java
@@ -20,29 +20,19 @@
 import com.google.gerrit.index.IndexType;
 import com.google.gerrit.index.testing.AbstractFakeIndex;
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
+import com.google.gerrit.testing.SystemPropertiesTestRule;
 import javax.inject.Inject;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import org.junit.ClassRule;
 import org.junit.Test;
 
 /** Test to check that the expected index backend was bound depending on sys/env properties. */
 public class DefaultIndexBindingIT extends AbstractDaemonTest {
+  @ClassRule
+  public static SystemPropertiesTestRule systemProperties =
+      new SystemPropertiesTestRule(IndexType.SYS_PROP, "");
 
   @Inject private ChangeIndexCollection changeIndex;
 
-  private static String propertyBeforeTest;
-
-  @BeforeClass
-  public static void setup() {
-    propertyBeforeTest = System.getProperty(IndexType.SYS_PROP);
-    System.setProperty(IndexType.SYS_PROP, "");
-  }
-
-  @AfterClass
-  public static void teardown() {
-    System.setProperty(IndexType.SYS_PROP, propertyBeforeTest);
-  }
-
   @Test
   public void fakeIsBoundByDefault() throws Exception {
     assertThat(System.getProperty(IndexType.SYS_PROP)).isEmpty();
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/index/FakeIndexBindingIT.java b/javatests/com/google/gerrit/acceptance/testsuite/index/FakeIndexBindingIT.java
index 4122426..acb2e5a 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/index/FakeIndexBindingIT.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/index/FakeIndexBindingIT.java
@@ -20,29 +20,19 @@
 import com.google.gerrit.index.IndexType;
 import com.google.gerrit.index.testing.AbstractFakeIndex;
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
+import com.google.gerrit.testing.SystemPropertiesTestRule;
 import javax.inject.Inject;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import org.junit.ClassRule;
 import org.junit.Test;
 
 /** Test to check that the expected index backend was bound depending on sys/env properties. */
 public class FakeIndexBindingIT extends AbstractDaemonTest {
+  @ClassRule
+  public static SystemPropertiesTestRule systemProperties =
+      new SystemPropertiesTestRule(IndexType.SYS_PROP, "fake");
 
   @Inject private ChangeIndexCollection changeIndex;
 
-  private static String propertyBeforeTest;
-
-  @BeforeClass
-  public static void setup() {
-    propertyBeforeTest = System.getProperty(IndexType.SYS_PROP);
-    System.setProperty(IndexType.SYS_PROP, "fake");
-  }
-
-  @AfterClass
-  public static void teardown() {
-    System.setProperty(IndexType.SYS_PROP, propertyBeforeTest);
-  }
-
   @Test
   public void fakeIsBoundWhenConfigured() throws Exception {
     assertThat(System.getProperty(IndexType.SYS_PROP)).isEqualTo("fake");
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/index/LuceneIndexBindingIT.java b/javatests/com/google/gerrit/acceptance/testsuite/index/LuceneIndexBindingIT.java
index 31e31fd..5dd6f01 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/index/LuceneIndexBindingIT.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/index/LuceneIndexBindingIT.java
@@ -20,29 +20,19 @@
 import com.google.gerrit.index.IndexType;
 import com.google.gerrit.lucene.LuceneChangeIndex;
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
+import com.google.gerrit.testing.SystemPropertiesTestRule;
 import javax.inject.Inject;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import org.junit.ClassRule;
 import org.junit.Test;
 
 /** Test to check that the expected index backend was bound depending on sys/env properties. */
 public class LuceneIndexBindingIT extends AbstractDaemonTest {
+  @ClassRule
+  public static SystemPropertiesTestRule systemProperties =
+      new SystemPropertiesTestRule(IndexType.SYS_PROP, "lucene");
 
   @Inject private ChangeIndexCollection changeIndex;
 
-  private static String propertyBeforeTest;
-
-  @BeforeClass
-  public static void setup() {
-    propertyBeforeTest = System.getProperty(IndexType.SYS_PROP);
-    System.setProperty(IndexType.SYS_PROP, "lucene");
-  }
-
-  @AfterClass
-  public static void teardown() {
-    System.setProperty(IndexType.SYS_PROP, propertyBeforeTest);
-  }
-
   @Test
   public void luceneIsBoundWhenConfigured() throws Exception {
     assertThat(System.getProperty(IndexType.SYS_PROP)).isEqualTo("lucene");
diff --git a/javatests/com/google/gerrit/server/cache/PersistentCacheFactoryIT.java b/javatests/com/google/gerrit/server/cache/PersistentCacheFactoryIT.java
index d8c6fe2..2264612 100644
--- a/javatests/com/google/gerrit/server/cache/PersistentCacheFactoryIT.java
+++ b/javatests/com/google/gerrit/server/cache/PersistentCacheFactoryIT.java
@@ -29,7 +29,7 @@
   @Inject PersistentCacheFactory persistentCacheFactory;
 
   @ModuleImpl(name = CacheModule.PERSISTENT_MODULE)
-  public static class Module extends AbstractModule {
+  public static class TestModule extends AbstractModule {
 
     @Override
     protected void configure() {
@@ -39,7 +39,7 @@
 
   @Override
   public com.google.inject.Module createModule() {
-    return new Module();
+    return new TestModule();
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
index c8ad84c..ba514fd 100644
--- a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
@@ -44,7 +44,7 @@
 import com.google.gerrit.server.config.AnonymousCowardName;
 import com.google.gerrit.server.config.AnonymousCowardNameProvider;
 import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.DefaultUrlFormatter;
+import com.google.gerrit.server.config.DefaultUrlFormatter.DefaultUrlFormatterModule;
 import com.google.gerrit.server.config.EnablePeerIPInReflogRecord;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.GerritServerId;
@@ -140,7 +140,7 @@
               public void configure() {
                 install(new GitModule());
 
-                install(new DefaultUrlFormatter.Module());
+                install(new DefaultUrlFormatterModule());
                 install(NoteDbModule.forTest());
                 bind(AllUsersName.class).toProvider(AllUsersNameProvider.class);
                 bind(String.class).annotatedWith(GerritServerId.class).toInstance("gerrit");
diff --git a/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java b/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
index 056c7dc..98721fd 100644
--- a/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
+++ b/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
@@ -24,6 +24,7 @@
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AttentionSetUpdate;
 import com.google.gerrit.entities.AttentionSetUpdate.Operation;
@@ -33,6 +34,7 @@
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.entities.SubmitRecord;
+import com.google.gerrit.git.RefUpdateUtil;
 import com.google.gerrit.json.OutputFormat;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
@@ -48,7 +50,9 @@
 import java.sql.Timestamp;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.IntStream;
+import org.eclipse.jgit.lib.BatchRefUpdate;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
@@ -56,6 +60,8 @@
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevSort;
 import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -70,6 +76,21 @@
   @Before
   public void setUp() throws Exception {}
 
+  @After
+  public void cleanUp() throws Exception {
+    BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
+    bru.setAllowNonFastForwards(true);
+    for (Ref ref : repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_CHANGES)) {
+      Change.Id changeId = Change.Id.fromRef(ref.getName());
+      if (changeId == null || !ref.getName().equals(RefNames.changeMetaRef(changeId))) {
+        continue;
+      }
+      bru.addCommand(new ReceiveCommand(ref.getObjectId(), ObjectId.zeroId(), ref.getName()));
+    }
+
+    RefUpdateUtil.executeChecked(bru, repo);
+  }
+
   @Test
   public void validHistoryNoOp() throws Exception {
     String tag = "jenkins";
@@ -157,6 +178,138 @@
   }
 
   @Test
+  public void numRefs_greater_maxRefsToUpdate_allFixed() throws Exception {
+    int numberOfChanges = 12;
+    ImmutableMap.Builder<String, Ref> refsToOldMetaBuilder = new ImmutableMap.Builder<>();
+    for (int i = 0; i < numberOfChanges; i++) {
+      Change c = newChange();
+      ChangeUpdate update = newUpdate(c, changeOwner);
+      update.setChangeMessage("Change has been successfully merged by " + changeOwner.getName());
+      update.commit();
+      ChangeUpdate updateWithSubject = newUpdate(c, changeOwner);
+      updateWithSubject.setSubjectForCommit("Update with subject");
+      updateWithSubject.commit();
+      String refName = RefNames.changeMetaRef(c.getId());
+      Ref metaRefBeforeRewrite = repo.exactRef(refName);
+      refsToOldMetaBuilder.put(refName, metaRefBeforeRewrite);
+    }
+    ImmutableMap<String, Ref> refsToOldMeta = refsToOldMetaBuilder.build();
+
+    RunOptions options = new RunOptions();
+    options.dryRun = false;
+    options.outputDiff = false;
+    options.verifyCommits = false;
+    options.maxRefsInBatch = 10;
+    options.maxRefsToUpdate = 12;
+    BackfillResult backfillResult = rewriter.backfillProject(project, repo, options);
+    assertThat(backfillResult.fixedRefDiff.keySet()).isEqualTo(refsToOldMeta.keySet());
+    for (Map.Entry<String, Ref> refEntry : refsToOldMeta.entrySet()) {
+      Ref metaRefAfterRewrite = repo.exactRef(refEntry.getKey());
+      assertThat(refEntry.getValue()).isNotEqualTo(metaRefAfterRewrite);
+    }
+  }
+
+  @Test
+  public void maxRefsToUpdate_coversAllInvalid_inMultipleBatches() throws Exception {
+    testMaxRefsToUpdate(
+        /*numberOfInvalidChanges=*/ 11,
+        /*numberOfValidChanges=*/ 9,
+        /*maxRefsToUpdate=*/ 12,
+        /*maxRefsInBatch=*/ 2);
+  }
+
+  @Test
+  public void maxRefsToUpdate_coversAllInvalid_inSingleBatch() throws Exception {
+    testMaxRefsToUpdate(
+        /*numberOfInvalidChanges=*/ 11,
+        /*numberOfValidChanges=*/ 9,
+        /*maxRefsToUpdate=*/ 12,
+        /*maxRefsInBatch=*/ 12);
+  }
+
+  @Test
+  public void moreInvalidRefs_thenMaxRefsToUpdate_inMultipleBatches() throws Exception {
+    testMaxRefsToUpdate(
+        /*numberOfInvalidChanges=*/ 11,
+        /*numberOfValidChanges=*/ 9,
+        /*maxRefsToUpdate=*/ 10,
+        /*maxRefsInBatch=*/ 2);
+  }
+
+  @Test
+  public void moreInvalidRefs_thenMaxRefsToUpdate_inSingleBatch() throws Exception {
+    testMaxRefsToUpdate(
+        /*numberOfInvalidChanges=*/ 11,
+        /*numberOfValidChanges=*/ 9,
+        /*maxRefsToUpdate=*/ 10,
+        /*maxRefsInBatch=*/ 10);
+  }
+
+  private void testMaxRefsToUpdate(
+      int numberOfInvalidChanges, int numberOfValidChanges, int maxRefsToUpdate, int maxRefsInBatch)
+      throws Exception {
+    ImmutableMap.Builder<String, ObjectId> expectedFixedRefsToOldMetaBuilder =
+        new ImmutableMap.Builder<>();
+    ImmutableMap.Builder<String, ObjectId> expectedSkippedRefsToOldMetaBuilder =
+        new ImmutableMap.Builder<>();
+    for (int i = 0; i < numberOfValidChanges; i++) {
+      Change c = newChange();
+      ChangeUpdate updateWithSubject = newUpdate(c, changeOwner);
+      updateWithSubject.setSubjectForCommit("Update with subject");
+      updateWithSubject.commit();
+      String refName = RefNames.changeMetaRef(c.getId());
+      Ref metaRefBeforeRewrite = repo.exactRef(refName);
+      expectedSkippedRefsToOldMetaBuilder.put(refName, metaRefBeforeRewrite.getObjectId());
+    }
+    for (int i = 0; i < numberOfInvalidChanges; i++) {
+      Change c = newChange();
+      ChangeUpdate update = newUpdate(c, changeOwner);
+      update.setChangeMessage("Change has been successfully merged by " + changeOwner.getName());
+      update.commit();
+      ChangeUpdate updateWithSubject = newUpdate(c, changeOwner);
+      updateWithSubject.setSubjectForCommit("Update with subject");
+      updateWithSubject.commit();
+      String refName = RefNames.changeMetaRef(c.getId());
+      Ref metaRefBeforeRewrite = repo.exactRef(refName);
+      if (i < maxRefsToUpdate) {
+        expectedFixedRefsToOldMetaBuilder.put(refName, metaRefBeforeRewrite.getObjectId());
+      } else {
+        expectedSkippedRefsToOldMetaBuilder.put(refName, metaRefBeforeRewrite.getObjectId());
+      }
+    }
+    ImmutableMap<String, ObjectId> expectedFixedRefsToOldMeta =
+        expectedFixedRefsToOldMetaBuilder.build();
+    ImmutableMap<String, ObjectId> expectedSkippedRefsToOldMeta =
+        expectedSkippedRefsToOldMetaBuilder.build();
+    RunOptions options = new RunOptions();
+    options.dryRun = false;
+    options.outputDiff = false;
+    options.verifyCommits = false;
+    options.maxRefsInBatch = maxRefsInBatch;
+    options.maxRefsToUpdate = maxRefsToUpdate;
+    BackfillResult backfillResult = rewriter.backfillProject(project, repo, options);
+    assertThat(backfillResult.fixedRefDiff.keySet()).isEqualTo(expectedFixedRefsToOldMeta.keySet());
+    for (Map.Entry<String, ObjectId> refEntry : expectedFixedRefsToOldMeta.entrySet()) {
+      Ref metaRefAfterRewrite = repo.exactRef(refEntry.getKey());
+      assertThat(refEntry.getValue()).isNotEqualTo(metaRefAfterRewrite.getObjectId());
+    }
+    for (Map.Entry<String, ObjectId> refEntry : expectedSkippedRefsToOldMeta.entrySet()) {
+      Ref metaRefAfterRewrite = repo.exactRef(refEntry.getKey());
+      assertThat(refEntry.getValue()).isEqualTo(metaRefAfterRewrite.getObjectId());
+    }
+    RunOptions secondRunOptions = new RunOptions();
+    secondRunOptions.dryRun = false;
+    secondRunOptions.outputDiff = false;
+    secondRunOptions.verifyCommits = false;
+    secondRunOptions.maxRefsInBatch = maxRefsInBatch;
+    secondRunOptions.maxRefsToUpdate = numberOfInvalidChanges + numberOfValidChanges;
+    BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options);
+    int expectedSecondRunResult =
+        numberOfInvalidChanges > maxRefsToUpdate ? numberOfInvalidChanges - maxRefsToUpdate : 0;
+    assertThat(secondRunResult.fixedRefDiff.keySet().size()).isEqualTo(expectedSecondRunResult);
+  }
+
+  @Test
   public void fixAuthorIdent() throws Exception {
     Change c = newChange();
     Timestamp when = TimeUtil.nowTs();
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 9ebee9c..65b725b 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -1726,6 +1726,8 @@
       assertQuery(predicate + "\"2009-10-01 21:02:00\"", change1);
       assertQuery(predicate + "2009-10-01", change1);
       assertQuery(predicate + "2009-10-03", change2, change1);
+      assertQuery(predicate + "\"2009-09-30 21:00:00 -0000\"", change1);
+      assertQuery(predicate + "\"2009-10-02 03:00:00 -0000\"", change2, change1);
     }
 
     // Same test as above, but using filter code path.
@@ -1742,6 +1744,12 @@
       assertQuery(makeIndexedPredicateFilterQuery(predicate + "\"2009-10-01 21:02:00\""), change1);
       assertQuery(makeIndexedPredicateFilterQuery(predicate + "2009-10-01"), change1);
       assertQuery(makeIndexedPredicateFilterQuery(predicate + "2009-10-03"), change2, change1);
+      assertQuery(
+          makeIndexedPredicateFilterQuery(predicate + "\"2009-09-30 21:00:00 -0000\""), change1);
+      assertQuery(
+          makeIndexedPredicateFilterQuery(predicate + "\"2009-10-02 03:00:00 -0000\""),
+          change2,
+          change1);
     }
   }
 
@@ -1763,6 +1771,8 @@
       assertQuery(predicate + "\"2009-10-01 20:59:59 -0000\"", change2);
       assertQuery(predicate + "2009-10-01", change2);
       assertQuery(predicate + "2009-09-30", change2, change1);
+      assertQuery(predicate + "\"2009-09-30 21:00:00 -0000\"", change2, change1);
+      assertQuery(predicate + "\"2009-10-02 03:00:00 -0000\"", change2);
     }
 
     // Same test as above, but using filter code path.
@@ -1774,6 +1784,12 @@
           makeIndexedPredicateFilterQuery(predicate + "\"2009-10-01 20:59:59 -0000\""), change2);
       assertQuery(makeIndexedPredicateFilterQuery(predicate + "2009-10-01"), change2);
       assertQuery(makeIndexedPredicateFilterQuery(predicate + "2009-09-30"), change2, change1);
+      assertQuery(
+          makeIndexedPredicateFilterQuery(predicate + "\"2009-09-30 21:00:00 -0000\""),
+          change2,
+          change1);
+      assertQuery(
+          makeIndexedPredicateFilterQuery(predicate + "\"2009-10-02 03:00:00 -0000\""), change2);
     }
   }
 
diff --git a/javatests/com/google/gerrit/testing/BUILD b/javatests/com/google/gerrit/testing/BUILD
index 5774707..9443b0d 100644
--- a/javatests/com/google/gerrit/testing/BUILD
+++ b/javatests/com/google/gerrit/testing/BUILD
@@ -7,6 +7,7 @@
     deps = [
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/testing:gerrit-test-util",
+        "//lib:jgit",
         "//lib/truth",
     ],
 )
diff --git a/javatests/com/google/gerrit/testing/ConfigSuiteTest.java b/javatests/com/google/gerrit/testing/ConfigSuiteTest.java
new file mode 100644
index 0000000..1ec30da
--- /dev/null
+++ b/javatests/com/google/gerrit/testing/ConfigSuiteTest.java
@@ -0,0 +1,266 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.testing;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+
+import com.google.gerrit.testing.ConfigSuite.AfterConfig;
+import com.google.gerrit.testing.ConfigSuite.BeforeConfig;
+import com.google.gerrit.testing.ConfigSuite.ConfigRule;
+import org.eclipse.jgit.lib.Config;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.RunWith;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.model.Statement;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ConfigSuiteTest {
+  @Mock private ConfigBasedTestListener configBasedTestListener;
+  private RunNotifier notifier;
+
+  @Before
+  public void setUp() {
+    notifier = new RunNotifier();
+    ConfigBasedTest.setConfigBasedTestListener(configBasedTestListener);
+  }
+
+  @After
+  public void tearDown() {
+    ConfigBasedTest.setConfigBasedTestListener(null);
+  }
+
+  @Test
+  public void methodsExecuteInCorrectOrder() throws Exception {
+    InOrder inOrder = Mockito.inOrder(configBasedTestListener);
+    new ConfigSuite(ConfigBasedTest.class).run(notifier);
+    inOrder.verify(configBasedTestListener, Mockito.times(1)).classRuleExecuted();
+    inOrder.verify(configBasedTestListener, Mockito.times(1)).beforeClassExecuted();
+    // default config
+    inOrder.verify(configBasedTestListener, Mockito.times(1)).configRuleExectued();
+    inOrder.verify(configBasedTestListener, Mockito.times(1)).beforeConfigExecuted();
+    inOrder.verify(configBasedTestListener, Mockito.times(2)).testExecuted(any(), any(), any());
+    inOrder.verify(configBasedTestListener, Mockito.times(1)).afterConfigExecuted();
+    // first config
+    inOrder.verify(configBasedTestListener, Mockito.times(1)).configRuleExectued();
+    inOrder.verify(configBasedTestListener, Mockito.times(1)).beforeConfigExecuted();
+    inOrder.verify(configBasedTestListener, Mockito.times(2)).testExecuted(any(), any(), any());
+    inOrder.verify(configBasedTestListener, Mockito.times(1)).afterConfigExecuted();
+    // second config
+    inOrder.verify(configBasedTestListener, Mockito.times(1)).configRuleExectued();
+    inOrder.verify(configBasedTestListener, Mockito.times(1)).beforeConfigExecuted();
+    inOrder.verify(configBasedTestListener, Mockito.times(2)).testExecuted(any(), any(), any());
+    inOrder.verify(configBasedTestListener, Mockito.times(1)).afterConfigExecuted();
+
+    inOrder.verify(configBasedTestListener, Mockito.times(1)).afterClassExecuted();
+  }
+
+  @Test
+  public void beforeClassExecutedOnce() throws Exception {
+    new ConfigSuite(ConfigBasedTest.class).run(notifier);
+    verify(configBasedTestListener, Mockito.times(1)).beforeClassExecuted();
+  }
+
+  @Test
+  public void afterClassExecutedOnce() throws Exception {
+    new ConfigSuite(ConfigBasedTest.class).run(notifier);
+    verify(configBasedTestListener, Mockito.times(1)).afterClassExecuted();
+  }
+
+  @Test
+  public void classRuleExecutedOnlyOnce() throws Exception {
+    new ConfigSuite(ConfigBasedTest.class).run(notifier);
+    verify(configBasedTestListener, Mockito.times(1)).classRuleExecuted();
+  }
+
+  @Test
+  public void beforeConfigExecutedForEachConfig() throws Exception {
+    new ConfigSuite(ConfigBasedTest.class).run(notifier);
+    // default, firstConfig, secondConfig
+    verify(configBasedTestListener, Mockito.times(3)).beforeConfigExecuted();
+  }
+
+  @Test
+  public void afterConfigExecutedForEachConfig() throws Exception {
+    new ConfigSuite(ConfigBasedTest.class).run(notifier);
+    // default, firstConfig, secondConfig
+    verify(configBasedTestListener, Mockito.times(3)).afterConfigExecuted();
+  }
+
+  @Test
+  public void configRuleExecutedForEachConfig() throws Exception {
+    new ConfigSuite(ConfigBasedTest.class).run(notifier);
+    // default, firstConfig, secondConfig
+    verify(configBasedTestListener, Mockito.times(3)).afterConfigExecuted();
+  }
+
+  @Test
+  public void testsExecuteWithCorrectConfigAndName() throws Exception {
+    new ConfigSuite(ConfigBasedTest.class).run(notifier);
+    verify(configBasedTestListener, Mockito.times(6)).testExecuted(any(), any(), any());
+
+    verify(configBasedTestListener, Mockito.times(1)).testExecuted("test1", "default", null);
+    verify(configBasedTestListener, Mockito.times(1)).testExecuted("test2", "default", null);
+    verify(configBasedTestListener, Mockito.times(1))
+        .testExecuted("test1", "firstValue", "firstConfig");
+    verify(configBasedTestListener, Mockito.times(1))
+        .testExecuted("test2", "firstValue", "firstConfig");
+    verify(configBasedTestListener, Mockito.times(1))
+        .testExecuted("test1", "secondValue", "secondConfig");
+    verify(configBasedTestListener, Mockito.times(1))
+        .testExecuted("test2", "secondValue", "secondConfig");
+  }
+
+  @Test
+  public void testResultWasSuccessful() throws Exception {
+    Result result = new Result();
+    notifier.addListener(result.createListener());
+    new ConfigSuite(ConfigBasedTest.class).run(notifier);
+    assertThat(result.wasSuccessful()).isTrue();
+  }
+
+  @Test
+  public void failedTestResultNotMissed() throws Exception {
+    Result result = new Result();
+    notifier.addListener(result.createListener());
+    new ConfigSuite(FailedConfigBasedTest.class).run(notifier);
+    // 3 fails with 3 different configs
+    assertThat(result.wasSuccessful()).isFalse();
+    assertThat(result.getFailureCount()).isEqualTo(3);
+  }
+
+  interface ConfigBasedTestListener {
+    void beforeClassExecuted();
+
+    void afterClassExecuted();
+
+    void classRuleExecuted();
+
+    void configRuleExectued();
+
+    void testExecuted(String testName, String testValue, String configName);
+
+    void beforeConfigExecuted();
+
+    void afterConfigExecuted();
+  }
+
+  public static class ConfigBasedTest {
+    private static ConfigBasedTestListener listener;
+
+    public static void setConfigBasedTestListener(ConfigBasedTestListener listener) {
+      ConfigBasedTest.listener = listener;
+    }
+
+    @BeforeClass
+    public static void beforeClass() {
+      listener.beforeClassExecuted();
+    }
+
+    @AfterClass
+    public static void afterClass() {
+      listener.afterClassExecuted();
+    }
+
+    @ClassRule
+    public static TestRule classRule =
+        new TestRule() {
+          @Override
+          public Statement apply(Statement statement, Description description) {
+            return new Statement() {
+              @Override
+              public void evaluate() throws Throwable {
+                listener.classRuleExecuted();
+                statement.evaluate();
+              }
+            };
+          }
+        };
+
+    @ConfigRule
+    public static TestRule configRule =
+        new TestRule() {
+          @Override
+          public Statement apply(Statement statement, Description description) {
+            return new Statement() {
+              @Override
+              public void evaluate() throws Throwable {
+                listener.configRuleExectued();
+                statement.evaluate();
+              }
+            };
+          }
+        };
+
+    @BeforeConfig
+    public static void beforeConfig() {
+      listener.beforeConfigExecuted();
+    }
+
+    @AfterConfig
+    public static void afterConfig() {
+      listener.afterConfigExecuted();
+    }
+
+    @ConfigSuite.Config
+    public static Config firstConfig() {
+      Config cfg = new Config();
+      cfg.setString("gerrit", null, "testValue", "firstValue");
+      return cfg;
+    }
+
+    @ConfigSuite.Config
+    public static Config secondConfig() {
+      Config cfg = new Config();
+      cfg.setString("gerrit", null, "testValue", "secondValue");
+      return cfg;
+    }
+
+    @ConfigSuite.Parameter public Config config;
+    @ConfigSuite.Name public String name;
+
+    @Test
+    public void test1() {
+      String testValue = config.getString("gerrit", null, "testValue");
+      listener.testExecuted("test1", testValue != null ? testValue : "default", name);
+    }
+
+    @Test
+    public void test2() {
+      String testValue = config.getString("gerrit", null, "testValue");
+      listener.testExecuted("test2", testValue != null ? testValue : "default", name);
+    }
+  }
+
+  public static class FailedConfigBasedTest extends ConfigBasedTest {
+    @Test
+    public void failedTest() {
+      assertThat(true).isFalse();
+    }
+  }
+}
diff --git a/plugins/delete-project b/plugins/delete-project
index 8fe544a..5dcb1a6 160000
--- a/plugins/delete-project
+++ b/plugins/delete-project
@@ -1 +1 @@
-Subproject commit 8fe544ac569efa357ee054257143d8e1d4aa6afd
+Subproject commit 5dcb1a64e779def0309354dd0e9886189845b020
diff --git a/plugins/download-commands b/plugins/download-commands
index c99bc84..d2f0cae 160000
--- a/plugins/download-commands
+++ b/plugins/download-commands
@@ -1 +1 @@
-Subproject commit c99bc8457910ec19315c1384e20267288b019592
+Subproject commit d2f0cae51a269ca660f221172cf010e2d528b661
diff --git a/plugins/gitiles b/plugins/gitiles
index 6e78bae..8e01636 160000
--- a/plugins/gitiles
+++ b/plugins/gitiles
@@ -1 +1 @@
-Subproject commit 6e78bae6502f693c509fa30c0c94ef2f1b1404c2
+Subproject commit 8e016364bfbaa30f957e14250f73d1c25bb006b4
diff --git a/plugins/hooks b/plugins/hooks
index ad4f877..4e07d16 160000
--- a/plugins/hooks
+++ b/plugins/hooks
@@ -1 +1 @@
-Subproject commit ad4f877749928b69ef94b62176c5797f6648887d
+Subproject commit 4e07d16a644ea823f6538a176621acee466d865b
diff --git a/plugins/plugin-manager b/plugins/plugin-manager
index 44808dc..e0664f6 160000
--- a/plugins/plugin-manager
+++ b/plugins/plugin-manager
@@ -1 +1 @@
-Subproject commit 44808dcad3c978ed12bb2cb454c6ad320912aa8a
+Subproject commit e0664f668ab5bac96a1e105b80d886de66743b1b
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
index 09623b9..3239ce3 160000
--- a/plugins/singleusergroup
+++ b/plugins/singleusergroup
@@ -1 +1 @@
-Subproject commit 09623b9432d360060f88ae48fb3386e374ca29c0
+Subproject commit 3239ce3a471f5aa9edd8f6f702bee655ea81f77d
diff --git a/plugins/webhooks b/plugins/webhooks
index 73f9dc7..9e4cd70 160000
--- a/plugins/webhooks
+++ b/plugins/webhooks
@@ -1 +1 @@
-Subproject commit 73f9dc72bd52f5d64853db31e711717a995f0a46
+Subproject commit 9e4cd708b96e4fab79b4101eaf3f3e6a8b872dca
diff --git a/polygerrit-ui/app/api/change-reply.ts b/polygerrit-ui/app/api/change-reply.ts
index bcbdc61..0b00b10 100644
--- a/polygerrit-ui/app/api/change-reply.ts
+++ b/polygerrit-ui/app/api/change-reply.ts
@@ -14,6 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import {ChangeInfo} from './rest-api';
+
 export declare interface LabelsChangedDetail {
   name: string;
   value: string;
@@ -21,8 +23,11 @@
 export declare interface ValueChangedDetail {
   value: string;
 }
-export type ReplyChangedCallback = (text: string) => void;
-export type LabelsChangedCallback = (detail: LabelsChangedDetail) => void;
+export type ReplyChangedCallback = (text: string, change?: ChangeInfo) => void;
+export type LabelsChangedCallback = (
+  detail: LabelsChangedDetail,
+  change?: ChangeInfo
+) => void;
 
 export declare interface ChangeReplyPluginApi {
   getLabelValue(label: string): string;
diff --git a/polygerrit-ui/app/api/hook.ts b/polygerrit-ui/app/api/hook.ts
index 8cbb9d0..e75d83a 100644
--- a/polygerrit-ui/app/api/hook.ts
+++ b/polygerrit-ui/app/api/hook.ts
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 import {ChangeInfo, ConfigInfo, RevisionInfo} from './rest-api';
+import {PluginApi} from './plugin';
 
 export declare interface GerritElementExtensions {
   content?: HTMLElement & {hidden?: boolean};
@@ -23,6 +24,7 @@
   token?: string;
   repoName?: string;
   config?: ConfigInfo;
+  plugin?: PluginApi;
 }
 
 export type PluginElement = HTMLElement & GerritElementExtensions;
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
index ac44908..a3db699 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
@@ -407,7 +407,7 @@
   }
 
   _nextChange(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -419,7 +419,7 @@
   }
 
   _prevChange(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -431,7 +431,7 @@
   }
 
   _openChange(e: IronKeyboardEvent) {
-    if (this.modifierPressed(e)) return;
+    if (this.shortcuts.modifierPressed(e)) return;
     this.openChange(e.detail.keyboardEvent);
   }
 
@@ -445,7 +445,7 @@
   _nextPage(e: IronKeyboardEvent) {
     if (
       this.shortcuts.shouldSuppress(e) ||
-      (this.modifierPressed(e) && !isShiftPressed(e))
+      (this.shortcuts.modifierPressed(e) && !isShiftPressed(e))
     ) {
       return;
     }
@@ -457,7 +457,7 @@
   _prevPage(e: IronKeyboardEvent) {
     if (
       this.shortcuts.shouldSuppress(e) ||
-      (this.modifierPressed(e) && !isShiftPressed(e))
+      (this.shortcuts.modifierPressed(e) && !isShiftPressed(e))
     ) {
       return;
     }
@@ -472,7 +472,7 @@
   }
 
   _toggleChangeReviewed(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -500,7 +500,7 @@
   }
 
   _toggleChangeStar(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
diff --git a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.ts b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.ts
index 8161592..6991c03 100644
--- a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.ts
@@ -154,6 +154,7 @@
           mutable="[[mutable]]"
           label="[[item.labelName]]"
           label-info="[[item.labelInfo]]"
+          showAlwaysOldUI
         ></gr-label-info>
       </div>
     </section>
@@ -204,6 +205,7 @@
           mutable="[[mutable]]"
           label="[[item.labelName]]"
           label-info="[[item.labelInfo]]"
+          showAlwaysOldUI
         ></gr-label-info>
       </div>
     </section>
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
index fc2cbe5..c19be58e 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
@@ -301,15 +301,6 @@
   @property({type: Boolean})
   disableEdit = false;
 
-  @property({type: Boolean})
-  disableDiffPrefs = false;
-
-  @property({
-    type: Boolean,
-    computed: '_computeDiffPrefsDisabled(disableDiffPrefs, _loggedIn)',
-  })
-  _diffPrefsDisabled?: boolean;
-
   @property({type: Array})
   _commentThreads?: CommentThread[];
 
@@ -752,7 +743,7 @@
   }
 
   _handleToggleDiffMode(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -1496,7 +1487,7 @@
   }
 
   _handleOpenReplyDialog(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
     this._getLoggedIn().then(isLoggedIn => {
@@ -1511,7 +1502,7 @@
   }
 
   _handleOpenDownloadDialogShortcut(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -1520,7 +1511,7 @@
   }
 
   _handleEditTopic(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -1672,7 +1663,7 @@
   }
 
   _handleToggleChangeStar(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
     e.preventDefault();
@@ -1680,7 +1671,7 @@
   }
 
   _handleUpToDashboard(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -1689,7 +1680,7 @@
   }
 
   _handleExpandAllMessages(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -1700,7 +1691,7 @@
   }
 
   _handleCollapseAllMessages(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -1711,14 +1702,10 @@
   }
 
   _handleOpenDiffPrefsShortcut(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
-
-    if (this._diffPrefsDisabled) {
-      return;
-    }
-
+    if (!this._loggedIn) return;
     e.preventDefault();
     this.$.fileList.openDiffPrefs();
   }
@@ -2602,10 +2589,6 @@
     return currentRevision && revisions && revisions[currentRevision];
   }
 
-  _computeDiffPrefsDisabled(disableDiffPrefs: boolean, loggedIn: boolean) {
-    return disableDiffPrefs || !loggedIn;
-  }
-
   /**
    * Wrapper for using in the element template and computed properties
    */
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
index d57aca8..0b77bc7 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
@@ -537,7 +537,7 @@
           patch-num="{{_patchRange.patchNum}}"
           base-patch-num="{{_patchRange.basePatchNum}}"
           files-expanded="[[_filesExpanded]]"
-          diff-prefs-disabled="[[_diffPrefsDisabled]]"
+          diff-prefs-disabled="[[!_loggedIn]]"
           on-open-diff-prefs="_handleOpenDiffPrefs"
           on-open-download-dialog="_handleOpenDownloadDialog"
           on-expand-diffs="_expandAllDiffs"
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
index a82fceb..e508c63 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
@@ -806,16 +806,11 @@
         'open'
       );
       element._loggedIn = false;
-      element.disableDiffPrefs = true;
       pressAndReleaseKeyOn(element, 188, null, ',');
       assert.isFalse(stub.called);
 
       element._loggedIn = true;
       pressAndReleaseKeyOn(element, 188, null, ',');
-      assert.isFalse(stub.called);
-
-      element.disableDiffPrefs = false;
-      pressAndReleaseKeyOn(element, 188, null, ',');
       assert.isTrue(stub.called);
     });
 
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.ts b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.ts
index 8aef3c0..50bb665 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.ts
@@ -126,9 +126,6 @@
   @property({type: Object})
   diffPrefs?: DiffPreferencesInfo;
 
-  @property({type: Boolean})
-  diffPrefsDisabled?: boolean;
-
   @property({type: String, notify: true})
   diffViewMode?: DiffViewMode;
 
@@ -175,11 +172,8 @@
     return classes.join(' ');
   }
 
-  _computePrefsButtonHidden(
-    prefs: DiffPreferencesInfo,
-    diffPrefsDisabled: boolean
-  ) {
-    return diffPrefsDisabled || !prefs;
+  _computePrefsButtonHidden(prefs: DiffPreferencesInfo, loggedIn: boolean) {
+    return !loggedIn || !prefs;
   }
 
   _fileListActionsVisible(
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts
index 5972393..73d0819 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts
@@ -170,12 +170,12 @@
         <gr-diff-mode-selector
           id="modeSelect"
           mode="{{diffViewMode}}"
-          save-on-change="[[!diffPrefsDisabled]]"
+          save-on-change="[[loggedIn]]"
         ></gr-diff-mode-selector>
         <span
           id="diffPrefsContainer"
           class="hideOnEdit"
-          hidden$="[[_computePrefsButtonHidden(diffPrefs, diffPrefsDisabled)]]"
+          hidden$="[[_computePrefsButtonHidden(diffPrefs, loggedIn)]]"
           hidden=""
         >
           <gr-tooltip-content has-tooltip title="Diff preferences">
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.js b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.js
index c90cfcc..479a9a1 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.js
@@ -38,21 +38,11 @@
     await flush();
   });
 
-  test('Diff preferences hidden when no prefs or diffPrefsDisabled', () => {
-    element.diffPrefsDisabled = true;
-    flush();
+  test('Diff preferences hidden when no prefs', () => {
     assert.isTrue(element.$.diffPrefsContainer.hidden);
 
-    element.diffPrefsDisabled = false;
-    flush();
-    assert.isTrue(element.$.diffPrefsContainer.hidden);
-
-    element.diffPrefsDisabled = true;
     element.diffPrefs = {font_size: '12'};
-    flush();
-    assert.isTrue(element.$.diffPrefsContainer.hidden);
-
-    element.diffPrefsDisabled = false;
+    element.loggedIn = true;
     flush();
     assert.isFalse(element.$.diffPrefsContainer.hidden);
   });
@@ -168,7 +158,7 @@
 
   suite('editMode behavior', () => {
     setup(() => {
-      element.diffPrefsDisabled = false;
+      element.loggedIn = true;
       element.diffPrefs = {};
     });
 
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
index b78c78f..74c079b 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
@@ -902,7 +902,7 @@
   _handleToggleInlineDiff(e: IronKeyboardEvent) {
     if (
       this.shortcuts.shouldSuppress(e) ||
-      this.modifierPressed(e) ||
+      this.shortcuts.modifierPressed(e) ||
       e.detail?.keyboardEvent?.repeat ||
       this.fileCursor.index === -1
     ) {
@@ -923,7 +923,7 @@
   }
 
   _handleToggleHideAllCommentThreads(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -932,7 +932,7 @@
   }
 
   _handleCursorNext(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -952,7 +952,7 @@
   }
 
   _handleCursorPrev(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -972,7 +972,7 @@
   }
 
   _handleNewComment(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
     e.preventDefault();
@@ -1001,7 +1001,7 @@
   }
 
   _handleOpenFile(e: IronKeyboardEvent) {
-    if (this.modifierPressed(e)) return;
+    if (this.shortcuts.modifierPressed(e)) return;
     this.handleOpenFile(e.detail.keyboardEvent);
   }
 
@@ -1022,7 +1022,7 @@
   _handleNextChunk(e: IronKeyboardEvent) {
     if (
       this.shortcuts.shouldSuppress(e) ||
-      (this.modifierPressed(e) && !isShiftPressed(e)) ||
+      (this.shortcuts.modifierPressed(e) && !isShiftPressed(e)) ||
       this._noDiffsExpanded()
     ) {
       return;
@@ -1039,7 +1039,7 @@
   _handlePrevChunk(e: IronKeyboardEvent) {
     if (
       this.shortcuts.shouldSuppress(e) ||
-      (this.modifierPressed(e) && !isShiftPressed(e)) ||
+      (this.shortcuts.modifierPressed(e) && !isShiftPressed(e)) ||
       this._noDiffsExpanded()
     ) {
       return;
@@ -1054,7 +1054,7 @@
   }
 
   _handleToggleFileReviewed(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -1543,7 +1543,7 @@
   }
 
   _handleEscKey(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e) || this.modifierPressed(e)) {
+    if (this.shortcuts.shouldSuppress(e) || this.shortcuts.modifierPressed(e)) {
       return;
     }
     e.preventDefault();
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
index f4064da..062d6a2 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
@@ -597,7 +597,6 @@
         let interact;
 
         setup(() => {
-          sinon.stub(element, 'modifierPressed').returns(false);
           const openCursorStub = sinon.stub(element, '_openCursorFile');
           const openSelectedStub = sinon.stub(element, '_openSelectedFile');
           const expandStub = sinon.stub(element, '_toggleFileExpanded');
@@ -1708,12 +1707,15 @@
     });
 
     test('_displayLine', () => {
-      sinon.stub(element, 'modifierPressed')
-          .callsFake(() => false);
       element.filesExpanded = FilesExpandedState.ALL;
       const mockEvent = {
         preventDefault() {},
         composedPath() { return []; },
+        detail: {
+          keyboardEvent: {
+            composedPath() { return []; },
+          },
+        },
       };
 
       element._displayLine = false;
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.ts b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.ts
index 0beb91c..45a55c1 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.ts
@@ -329,6 +329,7 @@
           on-labels-changed="_handleLabelsChanged"
           permitted-labels="[[permittedLabels]]"
         ></gr-label-scores>
+        <gr-endpoint-param name="change" value="[[change]]"></gr-endpoint-param>
       </gr-endpoint-decorator>
       <div id="pluginMessage">[[_pluginMessage]]</div>
     </section>
@@ -348,6 +349,8 @@
             on-bind-value-changed="_handleHeightChanged"
           >
           </gr-textarea>
+          <gr-endpoint-param name="change" value="[[change]]">
+          </gr-endpoint-param>
         </gr-endpoint-decorator>
         <div class="labelContainer">
           <label>
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.ts b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.ts
index e3edb5e..de11b16 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.ts
@@ -21,8 +21,6 @@
 import {dom, EventApi} from '@polymer/polymer/lib/legacy/polymer.dom';
 import {PolymerElement} from '@polymer/polymer/polymer-element';
 import {htmlTemplate} from './gr-reviewer-list_html';
-import {isSelf, isServiceUser} from '../../../utils/account-util';
-import {hasAttention} from '../../../utils/attention-set-util';
 import {customElement, property, computed, observe} from '@polymer/decorators';
 import {
   ChangeInfo,
@@ -44,6 +42,7 @@
 import {appContext} from '../../../services/app-context';
 import {fireAlert} from '../../../utils/event-util';
 import {getApprovalInfo, getCodeReviewLabel} from '../../../utils/label-util';
+import {sortReviewers} from '../../../utils/attention-set-util';
 
 @customElement('gr-reviewer-list')
 export class GrReviewerList extends PolymerElement {
@@ -237,22 +236,7 @@
     }
     this._reviewers = result
       .filter(reviewer => reviewer._account_id !== owner._account_id)
-      // Sort order:
-      // 1. The user themselves
-      // 2. Human users in the attention set.
-      // 3. Other human users.
-      // 4. Service users.
-      .sort((r1, r2) => {
-        if (this.account) {
-          if (isSelf(r1, this.account)) return -1;
-          if (isSelf(r2, this.account)) return 1;
-        }
-        const a1 = hasAttention(r1, this.change!) ? 1 : 0;
-        const a2 = hasAttention(r2, this.change!) ? 1 : 0;
-        const s1 = isServiceUser(r1) ? -2 : 0;
-        const s2 = isServiceUser(r2) ? -2 : 0;
-        return a2 - a1 + s2 - s1;
-      });
+      .sort((r1, r2) => sortReviewers(r1, r2, this.change, this.account));
 
     if (this._reviewers.length > 8) {
       this._displayedReviewers = this._reviewers.slice(0, 6);
diff --git a/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard.ts b/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard.ts
index 5feb1ae..002ac56 100644
--- a/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard.ts
+++ b/polygerrit-ui/app/elements/change/gr-submit-requirement-hovercard/gr-submit-requirement-hovercard.ts
@@ -119,9 +119,6 @@
           margin-top: var(--spacing-m);
           padding: var(--spacing-m) var(--spacing-xl) 0;
         }
-        .status-placeholder {
-          visibility: hidden;
-        }
       `,
     ];
   }
@@ -161,8 +158,6 @@
     return html` <div class="section">
       <div class="sectionIcon"></div>
       <div class="row">
-        <!-- Hidden placeholder to be aligned as Status line above -->
-        <div class="title status-placeholder">Status</div>
         <div>${labels.map(l => this.renderLabel(l, showLabelName))}</div>
       </div>
     </div>`;
diff --git a/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts b/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
index 40d80bd..e302b43 100644
--- a/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
+++ b/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
@@ -81,7 +81,7 @@
           color: var(--success-foreground);
         }
         iron-icon.close {
-          color: var(--warning-foreground);
+          color: var(--error-foreground);
         }
         .requirements,
         section.trigger-votes {
diff --git a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.ts b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.ts
index eb55177..ac3d5f4 100644
--- a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.ts
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.ts
@@ -141,14 +141,14 @@
     }
 
     const links: DropdownLink[] = [];
-    links.push({name: 'Settings', url: '/settings/'});
+    links.push({name: 'Settings', id: 'settings', url: '/settings/'});
     links.push({name: 'Keyboard Shortcuts', id: 'shortcuts'});
     if (switchAccountUrl) {
       const replacements = {path};
       const url = this._interpolateUrl(switchAccountUrl, replacements);
       links.push({name: 'Switch account', url, external: true});
     }
-    links.push({name: 'Sign out', url: '/logout'});
+    links.push({name: 'Sign out', id: 'signout', url: '/logout'});
     return links;
   }
 
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
index aaeb924..dd1ebd1 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
@@ -400,7 +400,7 @@
     const keyboardEvent = e.detail.keyboardEvent;
     if (
       this.shortcuts.shouldSuppress(e) ||
-      (this.modifierPressed(e) && !keyboardEvent.shiftKey)
+      (this.shortcuts.modifierPressed(e) && !keyboardEvent.shiftKey)
     ) {
       return;
     }
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
index c7a462e..084f9f6 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
@@ -174,15 +174,6 @@
   @property({type: Object, notify: true, observer: '_changeViewStateChanged'})
   changeViewState: Partial<ChangeViewState> = {};
 
-  @property({type: Boolean})
-  disableDiffPrefs = false;
-
-  @property({
-    type: Boolean,
-    computed: '_computeDiffPrefsDisabled(disableDiffPrefs, _loggedIn)',
-  })
-  _diffPrefsDisabled?: boolean;
-
   @property({type: Object})
   _patchRange?: PatchRange;
 
@@ -534,7 +525,7 @@
 
   _handleToggleFileReviewed(e: IronKeyboardEvent) {
     if (this.shortcuts.shouldSuppress(e)) return;
-    if (this.modifierPressed(e)) return;
+    if (this.shortcuts.modifierPressed(e)) return;
 
     e.preventDefault();
     this._setReviewed(!this.$.reviewed.checked);
@@ -542,7 +533,7 @@
 
   _handleEscKey(e: IronKeyboardEvent) {
     if (this.shortcuts.shouldSuppress(e)) return;
-    if (this.modifierPressed(e)) return;
+    if (this.shortcuts.modifierPressed(e)) return;
 
     e.preventDefault();
     this.$.diffHost.displayLine = false;
@@ -573,7 +564,7 @@
       this._moveToPreviousFileWithComment();
       return;
     }
-    if (this.modifierPressed(e)) {
+    if (this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -604,7 +595,7 @@
       this._moveToNextFileWithComment();
       return;
     }
-    if (this.modifierPressed(e)) {
+    if (this.shortcuts.modifierPressed(e)) {
       return;
     }
 
@@ -654,7 +645,7 @@
 
   _handleNewComment(ike: IronKeyboardEvent) {
     if (this.shortcuts.shouldSuppress(ike)) return;
-    if (this.modifierPressed(ike)) return;
+    if (this.shortcuts.modifierPressed(ike)) return;
 
     ike.preventDefault();
     this.classList.remove('hideComments');
@@ -695,7 +686,7 @@
         this._navigateToNextFileWithCommentThread();
       }
     } else {
-      if (this.modifierPressed(e)) return;
+      if (this.shortcuts.modifierPressed(e)) return;
       const result = this.cursor.moveToNextChunk();
       // navigate to next file if key is not being held down
       if (
@@ -753,7 +744,7 @@
     if (e.detail.keyboardEvent?.shiftKey) {
       this.cursor.moveToPreviousCommentThread();
     } else {
-      if (this.modifierPressed(e)) return;
+      if (this.shortcuts.modifierPressed(e)) return;
       this.cursor.moveToPreviousChunk();
       if (!e.detail.keyboardEvent?.repeat && this.cursor.isAtStart()) {
         this.showToastAndNavigateFile('previous', 'p');
@@ -764,7 +755,7 @@
   // Similar to gr-change-view._handleOpenReplyDialog
   _handleOpenReplyDialog(e: IronKeyboardEvent) {
     if (this.shortcuts.shouldSuppress(e)) return;
-    if (this.modifierPressed(e)) return;
+    if (this.shortcuts.modifierPressed(e)) return;
     this._getLoggedIn().then(isLoggedIn => {
       if (!isLoggedIn) {
         fireEvent(this, 'show-auth-required');
@@ -787,7 +778,7 @@
 
   _handleOpenDownloadDialog(e: IronKeyboardEvent) {
     if (this.shortcuts.shouldSuppress(e)) return;
-    if (this.modifierPressed(e)) return;
+    if (this.shortcuts.modifierPressed(e)) return;
 
     this.set('changeViewState.showDownloadDialog', true);
     e.preventDefault();
@@ -796,7 +787,7 @@
 
   _handleUpToChange(e: IronKeyboardEvent) {
     if (this.shortcuts.shouldSuppress(e)) return;
-    if (this.modifierPressed(e)) return;
+    if (this.shortcuts.modifierPressed(e)) return;
 
     e.preventDefault();
     this._navToChangeView();
@@ -804,8 +795,8 @@
 
   _handleCommaKey(e: IronKeyboardEvent) {
     if (this.shortcuts.shouldSuppress(e)) return;
-    if (this.modifierPressed(e)) return;
-    if (this._diffPrefsDisabled) return;
+    if (this.shortcuts.modifierPressed(e)) return;
+    if (!this._loggedIn) return;
 
     e.preventDefault();
     this.$.diffPreferencesDialog.open();
@@ -813,7 +804,7 @@
 
   _handleToggleDiffMode(e: IronKeyboardEvent) {
     if (this.shortcuts.shouldSuppress(e)) return;
-    if (this.modifierPressed(e)) return;
+    if (this.shortcuts.modifierPressed(e)) return;
 
     e.preventDefault();
     if (this._getDiffViewMode() === DiffViewMode.SIDE_BY_SIDE) {
@@ -1409,11 +1400,8 @@
     return dropdownContent;
   }
 
-  _computePrefsButtonHidden(
-    prefs?: DiffPreferencesInfo,
-    prefsDisabled?: boolean
-  ) {
-    return prefsDisabled || !prefs;
+  _computePrefsButtonHidden(prefs?: DiffPreferencesInfo, loggedIn?: boolean) {
+    return !loggedIn || !prefs;
   }
 
   _handleFileChange(e: CustomEvent) {
@@ -1703,21 +1691,21 @@
 
   _handleToggleBlame(e: IronKeyboardEvent) {
     if (this.shortcuts.shouldSuppress(e)) return;
-    if (this.modifierPressed(e)) return;
+    if (this.shortcuts.modifierPressed(e)) return;
 
     this._toggleBlame();
   }
 
   _handleToggleHideAllCommentThreads(e: IronKeyboardEvent) {
     if (this.shortcuts.shouldSuppress(e)) return;
-    if (this.modifierPressed(e)) return;
+    if (this.shortcuts.modifierPressed(e)) return;
 
     toggleClass(this, 'hideComments');
   }
 
   _handleOpenFileList(e: IronKeyboardEvent) {
     if (this.shortcuts.shouldSuppress(e)) return;
-    if (this.modifierPressed(e)) return;
+    if (this.shortcuts.modifierPressed(e)) return;
     this.$.dropdown.open();
   }
 
@@ -1842,10 +1830,6 @@
     this.$.diffHost.toggleAllContext();
   }
 
-  _computeDiffPrefsDisabled(disableDiffPrefs?: boolean, loggedIn?: boolean) {
-    return disableDiffPrefs || !loggedIn;
-  }
-
   _handleNextUnreviewedFile(e: IronKeyboardEvent) {
     if (this.shortcuts.shouldSuppress(e)) return;
     this._setReviewed(true);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
index b25be5a8..308c353 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
@@ -338,14 +338,14 @@
           <span>Diff view:</span>
           <gr-diff-mode-selector
             id="modeSelect"
-            save-on-change="[[!_diffPrefsDisabled]]"
+            save-on-change="[[_loggedIn]]"
             mode="{{changeViewState.diffMode}}"
             show-tooltip-below=""
           ></gr-diff-mode-selector>
         </div>
         <span
           id="diffPrefsContainer"
-          hidden$="[[_computePrefsButtonHidden(_prefs, _diffPrefsDisabled)]]"
+          hidden$="[[_computePrefsButtonHidden(_prefs, _loggedIn)]]"
           hidden=""
         >
           <span class="preferences desktop">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
index 0c7abc8..cc35c3c 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
@@ -489,10 +489,6 @@
       MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
       assert(showPrefsStub.calledOnce);
 
-      element.disableDiffPrefs = true;
-      MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
-      assert(showPrefsStub.calledOnce);
-
       let scrollStub = sinon.stub(element.cursor, 'moveToNextChunk');
       MockInteractions.pressAndReleaseKeyOn(element, 78, null, 'n');
       assert(scrollStub.calledOnce);
@@ -988,8 +984,7 @@
     });
 
     suite('diff prefs hidden', () => {
-      test('when no prefs or logged out', () => {
-        element.disableDiffPrefs = false;
+      test('whenlogged out', () => {
         element._loggedIn = false;
         flush();
         assert.isTrue(element.$.diffPrefsContainer.hidden);
@@ -1004,21 +999,9 @@
         assert.isTrue(element.$.diffPrefsContainer.hidden);
 
         element._loggedIn = true;
-        flush();
-        assert.isFalse(element.$.diffPrefsContainer.hidden);
-      });
-
-      test('when disableDiffPrefs is set', () => {
-        element._loggedIn = true;
         element._prefs = {font_size: '12'};
-        element.disableDiffPrefs = false;
         flush();
-
         assert.isFalse(element.$.diffPrefsContainer.hidden);
-        element.disableDiffPrefs = true;
-        flush();
-
-        assert.isTrue(element.$.diffPrefsContainer.hidden);
       });
     });
 
diff --git a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.ts b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.ts
index ab2ce6a..c3d9e4d 100644
--- a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.ts
+++ b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.ts
@@ -97,7 +97,8 @@
   }
 
   /**
-   * Sets value and dispatches event to force notify.
+   * Sets value of property (not attribute!) and dispatches event to force
+   * notify.
    */
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   set(name: string, value: any) {
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.ts b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.ts
index 35f8243..00fa77d 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.ts
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.ts
@@ -71,7 +71,7 @@
     plugin: PluginApi,
     slot?: string
   ): Promise<HTMLElement> {
-    const el = document.createElement(name);
+    const el = document.createElement(name) as PluginElement;
     return this._initProperties(
       el,
       plugin,
@@ -113,33 +113,31 @@
   }
 
   _initProperties(
-    htmlEl: HTMLElement,
+    el: PluginElement,
     plugin: PluginApi,
     content?: Element | null
   ) {
-    const el = htmlEl as HTMLElement & {
-      plugin?: PluginApi;
-      content?: Element;
-    };
     el.plugin = plugin;
     // The content is (only?) used in ChangeReplyPluginApi.
     // Maybe it would be better for the consumer side to figure out the content
     // with something like el.getRootNode().host, etc.
+    // Also note that the content element could easily end up being an instance
+    // of <gr-endpoint-param>.
     if (content) {
-      el.content = content;
+      el.content = content as HTMLElement;
     }
     const expectProperties = this._getEndpointParams().map(paramEl => {
       const helper = plugin.attributeHelper(paramEl);
       // TODO: this should be replaced by accessing the property directly
       const paramName = paramEl.getAttribute('name');
       if (!paramName) throw Error('plugin endpoint parameter missing a name');
-      return helper
-        .get('value')
-        .then(() =>
-          helper.bind('value', value =>
-            plugin.attributeHelper(el).set(paramName, value)
-          )
-        );
+      return helper.get('value').then(() =>
+        helper.bind('value', value =>
+          // Note that despite the naming this sets the property, not the
+          // attribute. :-)
+          plugin.attributeHelper(el).set(paramName, value)
+        )
+      );
     });
     let timeoutId: number;
     const timeout = new Promise(
@@ -197,17 +195,16 @@
 
   override ready() {
     super.ready();
+    if (!this.name) return;
     this._endpointCallBack = (info: ModuleInfo) => this._initModule(info);
     getPluginEndpoints().onNewEndpoint(this.name, this._endpointCallBack);
-    if (this.name) {
-      getPluginLoader()
-        .awaitPluginsLoaded()
-        .then(() =>
-          getPluginEndpoints()
-            .getDetails(this.name)
-            .forEach(this._initModule, this)
-        );
-    }
+    getPluginLoader()
+      .awaitPluginsLoaded()
+      .then(() =>
+        getPluginEndpoints()
+          .getDetails(this.name)
+          .forEach(this._initModule, this)
+      );
   }
 }
 
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
index f1b74ac..e7417f4 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
@@ -21,17 +21,16 @@
 import '../gr-copy-clipboard/gr-copy-clipboard';
 import {PolymerElement} from '@polymer/polymer/polymer-element';
 import {htmlTemplate} from './gr-comment-thread_html';
-import {KeyboardShortcutMixin} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
 import {
   computeDiffFromContext,
   computeId,
+  DraftInfo,
   isDraft,
   isRobot,
   sortComments,
   UIComment,
   UIDraft,
   UIRobot,
-  DraftInfo,
 } from '../../../utils/comment-util';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation';
 import {appContext} from '../../../services/app-context';
@@ -54,15 +53,14 @@
 } from '../../../types/common';
 import {GrComment} from '../gr-comment/gr-comment';
 import {PolymerDeepPropertyChange} from '@polymer/polymer/interfaces';
-import {IronKeyboardEvent} from '../../../types/events';
-import {LineNumber, FILE} from '../../diff/gr-diff/gr-diff-line';
+import {FILE, LineNumber} from '../../diff/gr-diff/gr-diff-line';
 import {GrButton} from '../gr-button/gr-button';
 import {KnownExperimentId} from '../../../services/flags/flags';
 import {DiffInfo, DiffPreferencesInfo} from '../../../types/diff';
 import {DiffLayer, RenderPreferences} from '../../../api/diff';
 import {
-  check,
   assertIsDefined,
+  check,
   queryAndAssert,
 } from '../../../utils/common-util';
 import {fireAlert, waitForEventOnce} from '../../../utils/event-util';
@@ -72,6 +70,7 @@
 import {anyLineTooLong} from '../../diff/gr-diff/gr-diff-utils';
 import {getUserName} from '../../../utils/display-name-util';
 import {generateAbsoluteUrl} from '../../../utils/url-util';
+import {addShortcut} from '../../../utils/dom-util';
 
 const UNRESOLVED_EXPAND_COUNT = 5;
 const NEWLINE_PATTERN = /\n/g;
@@ -83,11 +82,8 @@
   };
 }
 
-// This avoids JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC closure compiler error.
-const base = KeyboardShortcutMixin(PolymerElement);
-
 @customElement('gr-comment-thread')
-export class GrCommentThread extends base {
+export class GrCommentThread extends PolymerElement {
   // KeyboardShortcutMixin Not used in this element rather other elements tests
 
   static get template() {
@@ -123,9 +119,6 @@
   @property({type: Object, reflectToAttribute: true})
   range?: CommentRange;
 
-  @property({type: Object})
-  keyEventTarget: HTMLElement = document.body;
-
   @property({type: String, reflectToAttribute: true})
   diffSide?: Side;
 
@@ -210,11 +203,8 @@
   @property({type: Array})
   layers: DiffLayer[] = [];
 
-  get keyBindings() {
-    return {
-      'e shift+e': '_handleEKey',
-    };
-  }
+  /** Called in disconnectedCallback. */
+  private cleanups: (() => void)[] = [];
 
   private readonly reporting = appContext.reportingService;
 
@@ -240,8 +230,20 @@
     });
   }
 
+  override disconnectedCallback() {
+    super.disconnectedCallback();
+    for (const cleanup of this.cleanups) cleanup();
+    this.cleanups = [];
+  }
+
   override connectedCallback() {
     super.connectedCallback();
+    this.cleanups.push(
+      addShortcut({key: 'e'}, e => this.handleExpandShortcut(e))
+    );
+    this.cleanups.push(
+      addShortcut({key: 'E'}, e => this.handleCollapseShortcut(e))
+    );
     this._getLoggedIn().then(loggedIn => {
       this._showActions = loggedIn;
     });
@@ -499,21 +501,14 @@
     return this._orderedComments[this._orderedComments.length - 1] || {};
   }
 
-  _handleEKey(e: IronKeyboardEvent) {
-    if (this.shortcuts.shouldSuppress(e)) {
-      return;
-    }
+  private handleExpandShortcut(e: KeyboardEvent) {
+    if (this.shortcuts.shouldSuppress(e)) return;
+    this._expandCollapseComments(false);
+  }
 
-    // Don’t preventDefault in this case because it will render the event
-    // useless for other handlers (other gr-comment-thread elements).
-    if (e.detail.keyboardEvent?.shiftKey) {
-      this._expandCollapseComments(true);
-    } else {
-      if (this.modifierPressed(e)) {
-        return;
-      }
-      this._expandCollapseComments(false);
-    }
+  private handleCollapseShortcut(e: KeyboardEvent) {
+    if (this.shortcuts.shouldSuppress(e)) return;
+    this._expandCollapseComments(true);
   }
 
   _expandCollapseComments(actionIsCollapse: boolean) {
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts
index 06d25b3..595de34 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts
@@ -673,7 +673,7 @@
       pressAndReleaseKeyOn(element, 69, null, 'e');
       assert.isTrue(expandCollapseStub.lastCall.calledWith(false));
 
-      pressAndReleaseKeyOn(element, 69, 'shift', 'e');
+      pressAndReleaseKeyOn(element, 69, 'shift', 'E');
       assert.isTrue(expandCollapseStub.lastCall.calledWith(true));
     });
 
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api.ts b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api.ts
index 3eff401..aa86de4 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api.ts
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api.ts
@@ -59,46 +59,39 @@
 
   addReplyTextChangedCallback(handler: ReplyChangedCallback) {
     this.reporting.trackApi(this.plugin, 'reply', 'addReplyTextChangedCb');
-    const hookApi = this.plugin.hook('reply-text') as HookApi<PluginElement>;
-    const registeredHandler = (e: Event) => {
+    const hookApi = this.plugin.hook<PluginElement>('reply-text');
+    const wrappedHandler = (el: PluginElement, e: Event) => {
       const ce = e as CustomEvent<ValueChangedDetail>;
-      handler(ce.detail.value);
+      handler(ce.detail.value, el.change);
     };
-    hookApi.onAttached(el => {
-      if (!el.content) {
-        return;
-      }
-      el.content.addEventListener('value-changed', registeredHandler);
-    });
-    hookApi.onDetached(el => {
-      if (!el.content) {
-        return;
-      }
-      el.content.removeEventListener('value-changed', registeredHandler);
-    });
+    this.addCallbackAsHookListener(hookApi, 'value-changed', wrappedHandler);
   }
 
   addLabelValuesChangedCallback(handler: LabelsChangedCallback) {
     this.reporting.trackApi(this.plugin, 'reply', 'addLabelValuesChangedCb');
-    const hookApi = this.plugin.hook(
-      'reply-label-scores'
-    ) as HookApi<PluginElement>;
-    const registeredHandler = (e: Event) => {
+    const hookApi = this.plugin.hook<PluginElement>('reply-label-scores');
+    const wrappedHandler = (el: PluginElement, e: Event) => {
       const ce = e as CustomEvent<LabelsChangedDetail>;
-      handler(ce.detail);
+      handler(ce.detail, el.change);
     };
-    hookApi.onAttached(el => {
-      if (!el.content) {
-        return;
-      }
-      el.content.addEventListener('labels-changed', registeredHandler);
-    });
+    this.addCallbackAsHookListener(hookApi, 'labels-changed', wrappedHandler);
+  }
 
+  private addCallbackAsHookListener(
+    hookApi: HookApi<PluginElement>,
+    eventName: string,
+    handler: (el: PluginElement, e: Event) => void
+  ) {
+    let registeredHandler: ((e: Event) => void) | undefined;
+    hookApi.onAttached(el => {
+      registeredHandler = (e: Event) => handler(el, e);
+      el.content?.addEventListener(eventName, registeredHandler);
+    });
     hookApi.onDetached(el => {
-      if (!el.content) {
-        return;
+      if (registeredHandler) {
+        el.content?.removeEventListener(eventName, registeredHandler);
+        registeredHandler = undefined;
       }
-      el.content.removeEventListener('labels-changed', registeredHandler);
     });
   }
 
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
index 947ef3e..a65bb75 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
@@ -44,6 +44,7 @@
   getVotingRangeOrDefault,
   hasNeutralStatus,
   hasVoted,
+  valueString,
 } from '../../../utils/label-util';
 import {appContext} from '../../../services/app-context';
 import {ParsedChangeInfo} from '../../../types/types';
@@ -53,6 +54,7 @@
 import {ifDefined} from 'lit/directives/if-defined';
 import {fireReload} from '../../../utils/event-util';
 import {KnownExperimentId} from '../../../services/flags/flags';
+import {sortReviewers} from '../../../utils/attention-set-util';
 
 declare global {
   interface HTMLElementTagNameMap {
@@ -102,6 +104,10 @@
   @property({type: Boolean})
   showAllReviewers = true;
 
+  /** temporary until submit requirements are finished */
+  @property({type: Boolean})
+  showAlwaysOldUI = false;
+
   private readonly restApiService = appContext.restApiService;
 
   private readonly reporting = appContext.reportingService;
@@ -196,6 +202,11 @@
           color: var(--deemphasized-text-color);
           margin-left: var(--spacing-xs);
         }
+        gr-vote-chip {
+          --gr-vote-chip-width: 14px;
+          --gr-vote-chip-height: 14px;
+          margin-right: var(--spacing-s);
+        }
       `,
     ];
   }
@@ -203,7 +214,10 @@
   private readonly flagsService = appContext.flagsService;
 
   override render() {
-    if (this.flagsService.isEnabled(KnownExperimentId.SUBMIT_REQUIREMENTS_UI)) {
+    if (
+      this.flagsService.isEnabled(KnownExperimentId.SUBMIT_REQUIREMENTS_UI) &&
+      !this.showAlwaysOldUI
+    ) {
       return this.renderNewSubmitRequirements();
     } else {
       return this.renderOldSubmitRequirements();
@@ -213,11 +227,13 @@
   private renderNewSubmitRequirements() {
     const labelInfo = this.labelInfo;
     if (!labelInfo) return;
-    const reviewers = (this.change?.reviewers['REVIEWER'] ?? []).filter(
-      reviewer =>
-        (this.showAllReviewers && canVote(labelInfo, reviewer)) ||
-        (!this.showAllReviewers && hasVoted(labelInfo, reviewer))
-    );
+    const reviewers = (this.change?.reviewers['REVIEWER'] ?? [])
+      .filter(
+        reviewer =>
+          (this.showAllReviewers && canVote(labelInfo, reviewer)) ||
+          (!this.showAllReviewers && hasVoted(labelInfo, reviewer))
+      )
+      .sort((r1, r2) => sortReviewers(r1, r2, this.change, this.account));
     return html`<div>
       ${reviewers.map(reviewer => this.renderReviewerVote(reviewer))}
     </div>`;
@@ -255,7 +271,7 @@
         ></gr-vote-chip
       ></gr-account-chip>
       ${noVoteYet
-        ? html`<span class="no-votes">No votes</span>`
+        ? this.renderVoteAbility(reviewer)
         : html`${this.renderRemoveVote(reviewer)}`}
     </div>`;
   }
@@ -283,6 +299,19 @@
     </tr>`;
   }
 
+  private renderVoteAbility(reviewer: AccountInfo) {
+    if (this.labelInfo && isDetailedLabelInfo(this.labelInfo)) {
+      const approvalInfo = getApprovalInfo(this.labelInfo, reviewer);
+      if (approvalInfo?.permitted_voting_range) {
+        const {min, max} = approvalInfo?.permitted_voting_range;
+        return html`<span class="no-votes"
+          >Can vote ${valueString(min)}/${valueString(max)}</span
+        >`;
+      }
+    }
+    return html`<span class="no-votes">No votes</span>`;
+  }
+
   private renderRemoveVote(reviewer: AccountInfo) {
     return html`<gr-tooltip-content has-tooltip title="Remove vote">
       <gr-button
diff --git a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts
index 3d1e120..02cfa9f 100644
--- a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts
+++ b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts
@@ -19,7 +19,6 @@
 import {property} from '@polymer/decorators';
 import {PolymerElement} from '@polymer/polymer';
 import {check, Constructor} from '../../utils/common-util';
-import {isModifierPressed} from '../../utils/dom-util';
 import {IronKeyboardEvent} from '../../types/events';
 import {appContext} from '../../services/app-context';
 import {
@@ -28,8 +27,9 @@
   SPECIAL_SHORTCUT,
 } from '../../services/shortcuts/shortcuts-config';
 import {
-  ShortcutListener,
+  ComboKey,
   SectionView,
+  ShortcutListener,
 } from '../../services/shortcuts/shortcuts-service';
 
 export {
@@ -40,12 +40,6 @@
   SectionView,
 };
 
-// The maximum age of a keydown event to be used in a jump navigation. This
-// is only for cases when the keyup event is lost.
-const GO_KEY_TIMEOUT_MS = 1000;
-
-const V_KEY_TIMEOUT_MS = 1000;
-
 interface IronA11yKeysMixinConstructor {
   // Note: this is needed to have same interface as other mixins
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -65,12 +59,6 @@
    * @mixinClass
    */
   class Mixin extends superClass {
-    @property({type: Number})
-    _shortcut_go_key_last_pressed: number | null = null;
-
-    @property({type: Number})
-    _shortcut_v_key_last_pressed: number | null = null;
-
     @property({type: Object})
     _shortcut_go_table: Map<string, string> = new Map<string, string>();
 
@@ -100,16 +88,6 @@
     /** Are shortcuts currently enabled? True only when element is visible. */
     private bindingsEnabled = false;
 
-    modifierPressed(e: IronKeyboardEvent) {
-      /* We are checking for g/v as modifiers pressed. There are cases such as
-       * pressing v and then /, where we want the handler for / to be triggered.
-       * TODO(dhruvsri): find a way to support that keyboard combination
-       */
-      return (
-        isModifierPressed(e) || !!this._inGoKeyMode() || !!this.inVKeyMode()
-      );
-    }
-
     _addOwnKeyBindings(shortcut: Shortcut, handler: string) {
       const bindings = this.shortcuts.getBindingsForShortcut(shortcut);
       if (!bindings) {
@@ -188,11 +166,6 @@
         this._addOwnKeyBindings(key as Shortcut, value);
       }
 
-      // each component that uses this behaviour must be aware if go key is
-      // pressed or not, since it needs to check it as a modifier
-      this.addOwnKeyBinding('g:keydown', '_handleGoKeyDown');
-      this.addOwnKeyBinding('g:keyup', '_handleGoKeyUp');
-
       // If any of the shortcuts utilized GO_KEY, then they are handled
       // directly by this behavior.
       if (this._shortcut_go_table.size > 0) {
@@ -201,8 +174,6 @@
         });
       }
 
-      this.addOwnKeyBinding('v:keydown', '_handleVKeyDown');
-      this.addOwnKeyBinding('v:keyup', '_handleVKeyUp');
       if (this._shortcut_v_table.size > 0) {
         this._shortcut_v_table.forEach((_, key) => {
           this.addOwnKeyBinding(key, '_handleVAction');
@@ -231,27 +202,9 @@
       return {};
     }
 
-    _handleVKeyDown(e: IronKeyboardEvent) {
-      if (this.shortcuts.shouldSuppress(e)) return;
-      this._shortcut_v_key_last_pressed = Date.now();
-    }
-
-    _handleVKeyUp() {
-      setTimeout(() => {
-        this._shortcut_v_key_last_pressed = null;
-      }, V_KEY_TIMEOUT_MS);
-    }
-
-    private inVKeyMode() {
-      return !!(
-        this._shortcut_v_key_last_pressed &&
-        Date.now() - this._shortcut_v_key_last_pressed <= V_KEY_TIMEOUT_MS
-      );
-    }
-
     _handleVAction(e: IronKeyboardEvent) {
       if (
-        !this.inVKeyMode() ||
+        !this.shortcuts.isInSpecificComboKeyMode(ComboKey.V) ||
         !this._shortcut_v_table.has(e.detail.key) ||
         this.shortcuts.shouldSuppress(e)
       ) {
@@ -266,29 +219,9 @@
       }
     }
 
-    _handleGoKeyDown(e: IronKeyboardEvent) {
-      if (this.shortcuts.shouldSuppress(e)) return;
-      this._shortcut_go_key_last_pressed = Date.now();
-    }
-
-    _handleGoKeyUp() {
-      // Set go_key_last_pressed to null `GO_KEY_TIMEOUT_MS` after keyup event
-      // so that users can trigger `g + i` by pressing g and i quickly.
-      setTimeout(() => {
-        this._shortcut_go_key_last_pressed = null;
-      }, GO_KEY_TIMEOUT_MS);
-    }
-
-    _inGoKeyMode() {
-      return !!(
-        this._shortcut_go_key_last_pressed &&
-        Date.now() - this._shortcut_go_key_last_pressed <= GO_KEY_TIMEOUT_MS
-      );
-    }
-
     _handleGoAction(e: IronKeyboardEvent) {
       if (
-        !this._inGoKeyMode() ||
+        !this.shortcuts.isInSpecificComboKeyMode(ComboKey.G) ||
         !this._shortcut_go_table.has(e.detail.key) ||
         this.shortcuts.shouldSuppress(e)
       ) {
@@ -304,10 +237,7 @@
     }
   }
 
-  return Mixin as T &
-    Constructor<
-      KeyboardShortcutMixinInterface & KeyboardShortcutMixinInterfaceTesting
-    >;
+  return Mixin as T & Constructor<KeyboardShortcutMixinInterface>;
 };
 
 // The following doesn't work (IronA11yKeysBehavior crashes):
@@ -320,10 +250,7 @@
 // This is a workaround
 export const KeyboardShortcutMixin = <T extends Constructor<PolymerElement>>(
   superClass: T
-): T &
-  Constructor<
-    KeyboardShortcutMixinInterface & KeyboardShortcutMixinInterfaceTesting
-  > =>
+): T & Constructor<KeyboardShortcutMixinInterface> =>
   InternalKeyboardShortcutMixin(
     // TODO(TS): mixinBehaviors in some lib is returning: `new () => T` instead
     // which will fail the type check due to missing IronA11yKeysBehavior interface
@@ -334,13 +261,4 @@
 /** The interface corresponding to KeyboardShortcutMixin */
 export interface KeyboardShortcutMixinInterface {
   keyboardShortcuts(): {[key: string]: string | null};
-  modifierPressed(event: IronKeyboardEvent): boolean;
-}
-
-export interface KeyboardShortcutMixinInterfaceTesting {
-  _shortcut_go_key_last_pressed: number | null;
-  _shortcut_v_key_last_pressed: number | null;
-  _shortcut_go_table: Map<string, string>;
-  _shortcut_v_table: Map<string, string>;
-  _handleGoAction: (e: IronKeyboardEvent) => void;
 }
diff --git a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin_test.ts b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin_test.ts
deleted file mode 100644
index 01ad6cc..0000000
--- a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin_test.ts
+++ /dev/null
@@ -1,139 +0,0 @@
-/**
- * @license
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import '../../test/common-test-setup-karma';
-import {KeyboardShortcutMixin} from './keyboard-shortcut-mixin';
-import {PolymerElement} from '@polymer/polymer/polymer-element';
-import '../../elements/shared/gr-overlay/gr-overlay';
-import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions';
-import {IronKeyboardEvent} from '../../types/events';
-
-class GrKeyboardShortcutMixinTestElement extends KeyboardShortcutMixin(
-  PolymerElement
-) {
-  static get is() {
-    return 'keyboard-shortcut-mixin-test-element';
-  }
-
-  get keyBindings() {
-    return {
-      k: '_handleKey',
-      enter: '_handleKey',
-    };
-  }
-
-  _handleKey(_: any) {}
-
-  _handleA(_: any) {}
-}
-
-declare global {
-  interface HTMLElementTagNameMap {
-    'keyboard-shortcut-mixin-test-element': GrKeyboardShortcutMixinTestElement;
-  }
-}
-
-customElements.define(
-  GrKeyboardShortcutMixinTestElement.is,
-  GrKeyboardShortcutMixinTestElement
-);
-
-const basicFixture = fixtureFromElement('keyboard-shortcut-mixin-test-element');
-
-suite('keyboard-shortcut-mixin tests', () => {
-  let element: GrKeyboardShortcutMixinTestElement;
-
-  setup(async () => {
-    element = basicFixture.instantiate();
-    await flush();
-  });
-
-  test('modifierPressed returns accurate values', () => {
-    const spy = sinon.spy(element, 'modifierPressed');
-    element._handleKey = e => {
-      element.modifierPressed(e);
-    };
-    MockInteractions.keyDownOn(element, 75, 'shift', 'k');
-    assert.isTrue(spy.lastCall.returnValue);
-    MockInteractions.keyDownOn(element, 75, null, 'k');
-    assert.isFalse(spy.lastCall.returnValue);
-    MockInteractions.keyDownOn(element, 75, 'ctrl', 'k');
-    assert.isTrue(spy.lastCall.returnValue);
-    MockInteractions.keyDownOn(element, 75, null, 'k');
-    assert.isFalse(spy.lastCall.returnValue);
-    MockInteractions.keyDownOn(element, 75, 'meta', 'k');
-    assert.isTrue(spy.lastCall.returnValue);
-    MockInteractions.keyDownOn(element, 75, null, 'k');
-    assert.isFalse(spy.lastCall.returnValue);
-    MockInteractions.keyDownOn(element, 75, 'alt', 'k');
-    assert.isTrue(spy.lastCall.returnValue);
-  });
-
-  suite('GO_KEY timing', () => {
-    let handlerStub: sinon.SinonStub;
-
-    setup(() => {
-      element._shortcut_go_table.set('a', '_handleA');
-      handlerStub = element._handleA = sinon.stub();
-      sinon.stub(Date, 'now').returns(10000);
-    });
-
-    test('success', () => {
-      const e = {
-        detail: {key: 'a'},
-        preventDefault: () => {},
-        composedPath: () => [],
-      } as unknown as IronKeyboardEvent;
-      element._shortcut_go_key_last_pressed = 9000;
-      element._handleGoAction(e);
-      assert.isTrue(handlerStub.calledOnce);
-      assert.strictEqual(handlerStub.lastCall.args[0], e);
-    });
-
-    test('go key not pressed', () => {
-      const e = {
-        detail: {key: 'a'},
-        preventDefault: () => {},
-        composedPath: () => [],
-      } as unknown as IronKeyboardEvent;
-      element._shortcut_go_key_last_pressed = null;
-      element._handleGoAction(e);
-      assert.isFalse(handlerStub.called);
-    });
-
-    test('go key pressed too long ago', () => {
-      const e = {
-        detail: {key: 'a'},
-        preventDefault: () => {},
-        composedPath: () => [],
-      } as unknown as IronKeyboardEvent;
-      element._shortcut_go_key_last_pressed = 3000;
-      element._handleGoAction(e);
-      assert.isFalse(handlerStub.called);
-    });
-
-    test('unrecognized key', () => {
-      const e = {
-        detail: {key: 'f'},
-        preventDefault: () => {},
-        composedPath: () => [],
-      } as unknown as IronKeyboardEvent;
-      element._shortcut_go_key_last_pressed = 9000;
-      element._handleGoAction(e);
-      assert.isFalse(handlerStub.called);
-    });
-  });
-});
diff --git a/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts b/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts
index d0e2d49..edc31a4 100644
--- a/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts
+++ b/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts
@@ -23,7 +23,7 @@
 } from './shortcuts-config';
 import {disableShortcuts$} from '../user/user-model';
 import {IronKeyboardEvent, isIronKeyboardEvent} from '../../types/events';
-import {isElementTarget} from '../../utils/dom-util';
+import {isElementTarget, isModifierPressed} from '../../utils/dom-util';
 import {ReportingService} from '../gr-reporting/gr-reporting';
 
 export type SectionView = Array<{binding: string[][]; text: string}>;
@@ -35,7 +35,16 @@
   viewMap?: Map<ShortcutSection, SectionView>
 ) => void;
 
-const COMBO_KEYS = ['g', 'v'];
+export enum ComboKey {
+  G = 'g',
+  V = 'v',
+}
+
+function isComboKey(key: string): key is ComboKey {
+  return Object.values(ComboKey).includes(key as ComboKey);
+}
+
+export const COMBO_TIMEOUT_MS = 1000;
 
 /**
  * Shortcuts service, holds all hosts, bindings and listeners.
@@ -54,12 +63,12 @@
   private readonly listeners = new Set<ShortcutListener>();
 
   /**
-   * Maps keys (e.g. 'g') to the timestamp when they have last been pressed.
+   * Stores the timestamp of the last combo key being pressed.
    * This enabled key combinations like 'g+o' where we can check whether 'g' was
    * pressed recently when 'o' is processed. Keys of this map must be items of
    * COMBO_KEYS. Values are Date timestamps in milliseconds.
    */
-  private readonly keyLastPressed = new Map<string, number>();
+  private comboKeyLastPressed: {key?: ComboKey; timestampMs?: number} = {};
 
   /** Keeps track of the corresponding user preference. */
   private shortcutsDisabled = false;
@@ -73,9 +82,9 @@
     }
     disableShortcuts$.subscribe(x => (this.shortcutsDisabled = x));
     document.addEventListener('keydown', (e: KeyboardEvent) => {
-      if (!COMBO_KEYS.includes(e.key)) return;
+      if (!isComboKey(e.key)) return;
       if (this.shouldSuppress(e)) return;
-      this.keyLastPressed.set(e.key, Date.now());
+      this.comboKeyLastPressed = {key: e.key, timestampMs: Date.now()};
     });
   }
 
@@ -83,6 +92,25 @@
     return this.activeHosts.size === 0 && this.listeners.size === 0;
   }
 
+  isInComboKeyMode() {
+    return Object.values(ComboKey).some(key =>
+      this.isInSpecificComboKeyMode(key)
+    );
+  }
+
+  isInSpecificComboKeyMode(comboKey: ComboKey) {
+    const {key, timestampMs} = this.comboKeyLastPressed;
+    return (
+      key === comboKey &&
+      timestampMs &&
+      Date.now() - timestampMs < COMBO_TIMEOUT_MS
+    );
+  }
+
+  modifierPressed(e: IronKeyboardEvent) {
+    return isModifierPressed(e) || this.isInComboKeyMode();
+  }
+
   shouldSuppress(event: IronKeyboardEvent | KeyboardEvent) {
     if (this.shortcutsDisabled) return true;
     const e = isIronKeyboardEvent(event) ? event.detail.keyboardEvent : event;
@@ -116,9 +144,8 @@
     }
     // eg: {key: "k:keydown", ..., from: "gr-diff-view"}
     let key = `${e.key}:${e.type}`;
-    // TODO(brohlfs): Re-enable reporting of g- and v-keys.
-    // if (this._inGoKeyMode()) key = 'g+' + key;
-    // if (this.inVKeyMode()) key = 'v+' + key;
+    if (this.isInSpecificComboKeyMode(ComboKey.G)) key = 'g+' + key;
+    if (this.isInSpecificComboKeyMode(ComboKey.V)) key = 'v+' + key;
     if (e.shiftKey) key = 'shift+' + key;
     if (e.ctrlKey) key = 'ctrl+' + key;
     if (e.metaKey) key = 'meta+' + key;
diff --git a/polygerrit-ui/app/services/shortcuts/shortcuts-service_test.ts b/polygerrit-ui/app/services/shortcuts/shortcuts-service_test.ts
index 0998a4c..d8aa11e 100644
--- a/polygerrit-ui/app/services/shortcuts/shortcuts-service_test.ts
+++ b/polygerrit-ui/app/services/shortcuts/shortcuts-service_test.ts
@@ -15,9 +15,13 @@
  * limitations under the License.
  */
 import '../../test/common-test-setup-karma';
-import {ShortcutsService} from '../../services/shortcuts/shortcuts-service';
+import {
+  ShortcutsService,
+  COMBO_TIMEOUT_MS,
+} from '../../services/shortcuts/shortcuts-service';
 import {Shortcut, ShortcutSection} from './shortcuts-config';
 import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions';
+import {SinonFakeTimers} from 'sinon';
 
 async function keyEventOn(
   el: HTMLElement,
@@ -290,4 +294,48 @@
       });
     });
   });
+
+  suite('combo keys', () => {
+    let clock: SinonFakeTimers;
+    setup(() => {
+      clock = sinon.useFakeTimers();
+      clock.tick(1000);
+    });
+
+    teardown(() => {
+      clock.restore();
+    });
+
+    test('not in combo key mode initially', () => {
+      assert.isFalse(service.isInComboKeyMode());
+    });
+
+    test('pressing f does not switch into combo key mode', () => {
+      const event = new KeyboardEvent('keydown', {key: 'f'});
+      document.dispatchEvent(event);
+      assert.isFalse(service.isInComboKeyMode());
+    });
+
+    test('pressing g switches into combo key mode', () => {
+      const event = new KeyboardEvent('keydown', {key: 'g'});
+      document.dispatchEvent(event);
+      assert.isTrue(service.isInComboKeyMode());
+    });
+
+    test('pressing v switches into combo key mode', () => {
+      const event = new KeyboardEvent('keydown', {key: 'v'});
+      document.dispatchEvent(event);
+      assert.isTrue(service.isInComboKeyMode());
+    });
+
+    test('combo key mode timeout', () => {
+      const event = new KeyboardEvent('keydown', {key: 'g'});
+      document.dispatchEvent(event);
+      assert.isTrue(service.isInComboKeyMode());
+      clock.tick(COMBO_TIMEOUT_MS / 2);
+      assert.isTrue(service.isInComboKeyMode());
+      clock.tick(COMBO_TIMEOUT_MS);
+      assert.isFalse(service.isInComboKeyMode());
+    });
+  });
 });
diff --git a/polygerrit-ui/app/utils/attention-set-util.ts b/polygerrit-ui/app/utils/attention-set-util.ts
index dcd2863..b0b7ef8 100644
--- a/polygerrit-ui/app/utils/attention-set-util.ts
+++ b/polygerrit-ui/app/utils/attention-set-util.ts
@@ -19,6 +19,7 @@
 import {ParsedChangeInfo} from '../types/types';
 import {
   getAccountTemplate,
+  isSelf,
   isServiceUser,
   replaceTemplates,
 } from './account-util';
@@ -92,3 +93,27 @@
   const entry = change!.attention_set![account!._account_id!];
   return entry?.last_update ? entry.last_update : '';
 }
+
+/**
+ *  Sort order:
+ * 1. The user themselves
+ * 2. Human users in the attention set.
+ * 3. Other human users.
+ * 4. Service users.
+ */
+export function sortReviewers(
+  r1: AccountInfo,
+  r2: AccountInfo,
+  change?: ChangeInfo | ParsedChangeInfo,
+  selfAccount?: AccountInfo
+) {
+  if (selfAccount) {
+    if (isSelf(r1, selfAccount)) return -1;
+    if (isSelf(r2, selfAccount)) return 1;
+  }
+  const a1 = hasAttention(r1, change) ? 1 : 0;
+  const a2 = hasAttention(r2, change) ? 1 : 0;
+  const s1 = isServiceUser(r1) ? -2 : 0;
+  const s2 = isServiceUser(r2) ? -2 : 0;
+  return a2 - a1 + s2 - s1;
+}
diff --git a/polygerrit-ui/app/utils/dom-util.ts b/polygerrit-ui/app/utils/dom-util.ts
index ead47bb..e3e342e 100644
--- a/polygerrit-ui/app/utils/dom-util.ts
+++ b/polygerrit-ui/app/utils/dom-util.ts
@@ -304,6 +304,54 @@
   }
 }
 
+/**
+ * For matching the `key` property of KeyboardEvents. These are known to work
+ * with Firefox, Safari and Chrome.
+ */
+export enum Key {
+  ENTER = 'Enter',
+  ESC = 'Escape',
+  TAB = 'Tab',
+  SPACE = ' ',
+  LEFT = 'ArrowLeft',
+  RIGHT = 'ArrowRight',
+  UP = 'ArrowUp',
+  DOWN = 'ArrowDown',
+}
+
+export enum Modifier {
+  ALT_KEY,
+  CTRL_KEY,
+  META_KEY,
+  SHIFT_KEY,
+}
+
+export interface Shortcut {
+  key: string;
+  modifiers?: Modifier[];
+}
+
+export function addShortcut(
+  shortcut: Shortcut,
+  listener: (e: KeyboardEvent) => void
+) {
+  const wrappedListener = (e: KeyboardEvent) => {
+    if (e.key !== shortcut.key) return;
+    const modifiers = shortcut.modifiers ?? [];
+    if (e.ctrlKey !== modifiers.includes(Modifier.CTRL_KEY)) return;
+    if (e.metaKey !== modifiers.includes(Modifier.META_KEY)) return;
+    // TODO(brohlfs): Refine the matching of modifiers. For "normal" keys we
+    // don't want to check ALT and SHIFT, because we don't know what the
+    // keyboard layout looks like. The user may have to use ALT and/or SHIFT for
+    // certain keys. Comparing the `key` string is sufficient in that case.
+    // if (e.altKey !== modifiers.includes(Modifier.ALT_KEY)) return;
+    // if (e.shiftKey !== modifiers.includes(Modifier.SHIFT_KEY)) return;
+    listener(e);
+  };
+  document.addEventListener('keydown', wrappedListener);
+  return () => document.removeEventListener('keydown', wrappedListener);
+}
+
 export function modifierPressed(e: KeyboardEvent) {
   return e.altKey || e.ctrlKey || e.metaKey || e.shiftKey;
 }
diff --git a/polygerrit-ui/app/utils/label-util.ts b/polygerrit-ui/app/utils/label-util.ts
index a8a2719..918d2ab 100644
--- a/polygerrit-ui/app/utils/label-util.ts
+++ b/polygerrit-ui/app/utils/label-util.ts
@@ -94,7 +94,7 @@
   label: DetailedLabelInfo,
   approvalInfo?: ApprovalInfo
 ) {
-  if (!approvalInfo) return true;
+  if (approvalInfo?.value === undefined) return true;
   return getLabelStatus(label, approvalInfo.value) === LabelStatus.NEUTRAL;
 }
 
diff --git a/polygerrit-ui/app/utils/label-util_test.ts b/polygerrit-ui/app/utils/label-util_test.ts
index 1004aac..9360688 100644
--- a/polygerrit-ui/app/utils/label-util_test.ts
+++ b/polygerrit-ui/app/utils/label-util_test.ts
@@ -24,6 +24,7 @@
   getRepresentativeValue,
   getVotingRange,
   getVotingRangeOrDefault,
+  hasNeutralStatus,
   labelCompare,
   LabelStatus,
 } from './label-util';
@@ -193,6 +194,15 @@
     assert.equal(getLabelStatus(labelInfo), LabelStatus.NEUTRAL);
   });
 
+  test('hasNeutralStatus', () => {
+    const labelInfo: DetailedLabelInfo = {all: [], values: VALUES_2};
+    assert.isTrue(hasNeutralStatus(labelInfo));
+    assert.isTrue(hasNeutralStatus(labelInfo, {}));
+    assert.isTrue(hasNeutralStatus(labelInfo, {value: 0}));
+    assert.isFalse(hasNeutralStatus(labelInfo, {value: -1}));
+    assert.isFalse(hasNeutralStatus(labelInfo, {value: 1}));
+  });
+
   test('getRepresentativeValue', () => {
     let labelInfo: DetailedLabelInfo = {all: []};
     assert.equal(getRepresentativeValue(labelInfo), 0);
diff --git a/resources/com/google/gerrit/server/mime/mime-types.properties b/resources/com/google/gerrit/server/mime/mime-types.properties
index 6ab682c..fd2280c 100644
--- a/resources/com/google/gerrit/server/mime/mime-types.properties
+++ b/resources/com/google/gerrit/server/mime/mime-types.properties
@@ -116,6 +116,7 @@
 jsx = text/jsx
 jsp = application/x-jsp
 kt = text/x-kotlin
+kts = text/x-kotlin
 less = text/x-less
 lhs = text/x-literate-haskell
 lisp = text/x-common-lisp
diff --git a/tools/BUILD b/tools/BUILD
index e44ae78..47a2a2e 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -240,8 +240,8 @@
         "-Xep:InstanceOfAndCastMatchWrongType:ERROR",
         "-Xep:InstantTemporalUnit:ERROR",
         "-Xep:IntLongMath:ERROR",
-        # "-Xep:InvalidBlockTag:WARN",
-        "-Xep:InvalidInlineTag:WARN",
+        "-Xep:InvalidBlockTag:ERROR",
+        "-Xep:InvalidInlineTag:ERROR",
         "-Xep:InvalidJavaTimeConstant:ERROR",
         "-Xep:InvalidLink:ERROR",
         # "-Xep:InvalidParam:WARN",
@@ -270,7 +270,7 @@
         "-Xep:JavaDurationWithNanos:ERROR",
         "-Xep:JavaDurationWithSeconds:ERROR",
         "-Xep:JavaInstantGetSecondsGetNano:ERROR",
-        # "-Xep:JavaLangClash:WARN",
+        "-Xep:JavaLangClash:ERROR",
         "-Xep:JavaLocalDateTimeGetNano:ERROR",
         "-Xep:JavaLocalTimeGetNano:ERROR",
         "-Xep:JavaPeriodGetDays:ERROR",
diff --git a/tools/bzl/license-map.py b/tools/bzl/license-map.py
index 3e4fc92..43b172c 100644
--- a/tools/bzl/license-map.py
+++ b/tools/bzl/license-map.py
@@ -132,7 +132,7 @@
 
 def main():
     xml_data = load_xmls(args.xmls)
-    json_map_data = load_jsons(args.json_maps)
+    json_map_data = load_jsons(args.json_maps) if args.json_maps else []
 
     if args.asciidoctor:
         # We don't want any blank line before "= Gerrit Code Review - Licenses"