Merge "Revert "Add topic to polygerrit index paths""
diff --git a/.bazelignore b/.bazelignore
index 69c04b1..aac80af 100644
--- a/.bazelignore
+++ b/.bazelignore
@@ -1,2 +1,5 @@
 eclipse-out
 node_modules
+polygerrit-ui/node_modules
+plugins/node_modules
+tools/node_tools/node_modules
diff --git a/.bazelproject b/.bazelproject
index a7f5450..ad7b022 100644
--- a/.bazelproject
+++ b/.bazelproject
@@ -16,7 +16,7 @@
 targets:
   //...:all
 
-java_language_level: 8
+java_language_level: 11
 
 workspace_type: java
 
diff --git a/.bazelrc b/.bazelrc
index 6ccd56a..407b005 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -2,7 +2,30 @@
 build --repository_cache=~/.gerritcodereview/bazel-cache/repository
 build --action_env=PATH
 build --disk_cache=~/.gerritcodereview/bazel-cache/cas
-build --java_toolchain=//tools:error_prone_warnings_toolchain_java11
+
+# Builds using remotejdk_11, executes using remotejdk_11 or local_jdk
+build --java_language_version=11
+build --java_runtime_version=remotejdk_11
+build --tool_java_language_version=11
+build --tool_java_runtime_version=remotejdk_11
+
+# Builds using remotejdk_17, executes using remotejdk_17 or local_jdk
+build:java17 --java_language_version=17
+build:java17 --java_runtime_version=remotejdk_17
+build:java17 --tool_java_language_version=17
+build:java17 --tool_java_runtime_version=remotejdk_17
+
+# Builds and executes on RBE using remotejdk_11
+build:remote --java_language_version=11
+build:remote --java_runtime_version=remotejdk_11
+build:remote --tool_java_language_version=11
+build:remote --tool_java_runtime_version=remotejdk_11
+
+# Builds and executes on RBE using remotejdk_17
+build:remote17 --java_language_version=17
+build:remote17 --java_runtime_version=remotejdk_17
+build:remote17 --tool_java_language_version=17
+build:remote17 --tool_java_runtime_version=remotejdk_17
 
 # Enable strict_action_env flag to. For more information on this feature see
 # https://groups.google.com/forum/#!topic/bazel-discuss/_VmRfMyyHBk.
@@ -13,12 +36,7 @@
 
 build --announce_rc
 
-# Workaround Bazel worker crash (remove after upgrading to 4.1.0)
-# https://github.com/bazelbuild/bazel/issues/13333
-build --experimental_worker_multiplex=false
-
 test --build_tests_only
-test --test_output=errors
-test --java_toolchain=//tools:error_prone_warnings_toolchain_java11
+test --test_output=all
 
 import %workspace%/tools/remote-bazelrc
diff --git a/.bazelversion b/.bazelversion
index fcdb2e1..c7cb131 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-4.0.0
+5.3.1
diff --git a/.gitignore b/.gitignore
index 95f94ba..0bbcaba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 # Keep following lines sorted according to `LC_COLLATE=C sort`
+*.code-workspace
 *.eml
 *.iml
 *.log
@@ -30,8 +31,13 @@
 /infer-out
 /local.properties
 /node_modules/
+/polygerrit-ui/node_modules/
+/polygerrit-ui/app/node_modules/
 /package-lock.json
 /plugins/*
+/polygerrit-ui/coverage/
+/polygerrit-ui/app/plugins/*
+/polygerrit-ui/screenshots/Chrome/failed/
 !/plugins/.eslintignore
 !/plugins/.eslintrc.js
 !/plugins/.prettierrc.js
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index 8efbd11..09ce63b 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -10,9 +10,9 @@
 org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
 org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.compliance=11
 org.eclipse.jdt.core.compiler.debug.lineNumber=generate
 org.eclipse.jdt.core.compiler.debug.localVariable=generate
 org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -29,6 +29,7 @@
 org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
@@ -87,6 +88,7 @@
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
 org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
 org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
 org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
@@ -126,4 +128,5 @@
 org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
 org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
 org.eclipse.jdt.core.compiler.processAnnotations=enabled
-org.eclipse.jdt.core.compiler.source=1.8
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=11
diff --git a/BUILD b/BUILD
index 084d383..984fd955 100644
--- a/BUILD
+++ b/BUILD
@@ -4,24 +4,17 @@
 package(default_visibility = ["//visibility:public"])
 
 config_setting(
-    name = "java11",
+    name = "java17",
     values = {
-        "java_toolchain": "@bazel_tools//tools/jdk:toolchain_java11",
-    },
-)
-
-config_setting(
-    name = "java_next",
-    values = {
-        "java_toolchain": "//tools:toolchain_vanilla",
+        "java_language_version": "17",
     },
 )
 
 genrule(
     name = "gen_version",
     outs = ["version.txt"],
-    cmd = ("cat bazel-out/volatile-status.txt bazel-out/stable-status.txt | " +
-           "grep STABLE_BUILD_GERRIT_LABEL | cut -d ' ' -f 2 > $@"),
+    cmd = ("(cat bazel-out/volatile-status.txt bazel-out/stable-status.txt | " +
+           "grep STABLE_BUILD_GERRIT_LABEL | cut -d ' ' -f 2) > $@ || echo 'UNKNOWN' > $@"),
     stamp = 1,
 )
 
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 3da69df..185fa07 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -800,6 +800,22 @@
 Users without this access right who are able to upload changes can
 still do the revert locally and upload the revert commit as a new change.
 
+[[category_remove_label]]
+=== Remove Label (Remove Vote)
+
+For every configured label `My-Name` in the project, there is a
+corresponding permission `removeLabel-My-Name` with a range corresponding to
+the defined values. For these values, the users are permitted to remove
+other users' votes from a change.
+
+Change owners can always remove zero or positive votes (even without
+having the `Remove Vote` access right assigned).
+
+Project owners and site administrators can always remove any vote (even
+without having the `Remove Vote` access right assigned).
+
+Users without this access right can still remove their own votes.
+
 [[category_remove_reviewer]]
 === Remove Reviewer
 
@@ -890,6 +906,9 @@
 the Work In Progress bit of the change (even without having the
 `Toggle Work In Progress state` access right assigned).
 
+Must be assigned on the target branch ref (i.e. on 'refs/heads/*', not on
+'refs/for/*').
+
 [[category_delete_own_changes]]
 === Delete Own Changes
 
@@ -1351,10 +1370,11 @@
 [[capability_createProject]]
 === Create Project
 
-Allow project creation.  This capability allows the granted group to
-either link:cmd-create-project.html[create new git projects via ssh]
-or via the web UI.
+Allow project creation.
 
+This capability allows the granted group to create projects via the web UI, via
+link:rest-api-projects.html#create-project][REST] and via
+link:cmd-create-project.html[SSH].
 
 [[capability_emailReviewers]]
 === Email Reviewers
diff --git a/Documentation/backend_licenses.txt b/Documentation/backend_licenses.txt
index 3113682..586406f 100755
--- a/Documentation/backend_licenses.txt
+++ b/Documentation/backend_licenses.txt
@@ -61,11 +61,8 @@
 * 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
@@ -1113,22 +1110,6 @@
 ----
 
 
-[[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
 
diff --git a/Documentation/backup.txt b/Documentation/backup.txt
index dd47035..9139e71 100644
--- a/Documentation/backup.txt
+++ b/Documentation/backup.txt
@@ -45,10 +45,6 @@
 It can be recomputed from primary data in the git repositories but
 reindexing may take a long time hence backing up the index makes sense
 for production installations.
-+
-If you have chosen to use _Elastic Search_ for indexing,
-refer to its
-link:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html[backup documentation,role=external,window=_blank].
 
 [#optional-backup-cache]
 Caches::
diff --git a/Documentation/cmd-check-project-access.txt b/Documentation/cmd-check-project-access.txt
new file mode 100644
index 0000000..885993a
--- /dev/null
+++ b/Documentation/cmd-check-project-access.txt
@@ -0,0 +1,74 @@
+= gerrit check-project-access
+
+== NAME
+gerrit check-project-access - Check project readability of all users in a
+matching the given link:rest-api-accounts.html#account-id[account identifier]
+
+== SYNOPSIS
+[verse]
+--
+_ssh_ -p <port> <host> _gerrit check-project-access_
+  [--project <PROJECT> | -p <PROJECT>]
+  [--user <USER> | -u <USER>]
+--
+
+== DESCRIPTION
+Allow users to check if user has access to a project’s changes, comments, code
+differences, and Git access over SSH or HTTP.
+
+It returns all users in given input String, where it includes: username, email
+address and full name.
+
+== ACCESS
+Users who have view access and administrate server capability.
+
+== EXAMPLES
+Check if users can read all references in the repository called "test_project",
+in given input String TestUser.
+
+Given that there are
+a user with username "test_user1", email "one@email.com", and Full name as
+TestUser,
+a user with username "test_user2", email "two@email.com", and Full name as
+TestUser,
+a user with username "test_user3", email "TestUser@email.com", and Full name
+as John Doe
+
+----
+$ ssh -p @SSH_PORT@ @SSH_HOST@ gerrit check-project-access
+  -p test_project
+  -u TestUser
+
+Username: 'test_user1', Email: 'one@example.com',  Full Name: 'TestUser'
+, Result: TRUE
+Username: 'test_user3', Email: 'TestUser@example.com',  Full Name: 'John Doe'
+, Result: FALSE
+Username: 'test_user2', Email: 'two@example.com',  Full Name: 'TestUser'
+, Result: FALSE
+----
+
+----
+$ ssh -p @SSH_PORT@ @SSH_HOST@ gerrit check-project-access
+  -p test_project_doesnt_exist
+  -u TestUser
+
+fatal: project 'test_project_doesnt_exist' is unavailable
+----
+
+----
+$ ssh -p @SSH_PORT@ @SSH_HOST@ gerrit check-project-access
+  -p test_project
+  -u test_user_doesnt_exist
+
+fatal: No accounts found for your query: "test_user_doesnt_exist"
+Tip: Try double-escaping spaces, for example: "--user Last,\\ First"
+----
+
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
+
diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt
index 0575eb9..87f3851 100644
--- a/Documentation/cmd-create-project.txt
+++ b/Documentation/cmd-create-project.txt
@@ -108,6 +108,7 @@
 	Action used by Gerrit to submit an approved change to its
 	destination branch.  Supported options are:
 +
+* INHERIT: inherits the submit-type from the parent project.
 * FAST_FORWARD_ONLY: produces a strictly linear history.
 * MERGE_IF_NECESSARY: create a merge commit when required.
 * REBASE_IF_NECESSARY: rebase the commit when required.
@@ -116,7 +117,7 @@
 * CHERRY_PICK: always cherry-pick the commit.
 
 +
-Defaults to MERGE_IF_NECESSARY unless
+Defaults to INHERIT unless
 link:config-gerrit.html#repository.name.defaultSubmitType[
 repository.<name>.defaultSubmitType] is set to a different value.
 For more details see link:config-project-config.html#submit-type[
@@ -136,13 +137,13 @@
 	project.  Disabled by default.
 
 --use-signed-off-by::
---so:
+--so::
 	If enabled, each change must contain a Signed-off-by line
 	from either the author or the uploader in the commit message.
 	Disabled by default.
 
 --create-new-change-for-all-not-in-target::
---ncfa:
+--ncfa::
 	If enabled, a new change is created for every commit that is not in
 	the target branch. If the pushed commit is a merge commit, this flag is
 	ignored for that push. To avoid accidental creation of a large number
diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt
index ab341e8..c959a07 100644
--- a/Documentation/cmd-index.txt
+++ b/Documentation/cmd-index.txt
@@ -58,6 +58,12 @@
 link:cmd-ban-commit.html[gerrit ban-commit]::
 	Bans a commit from a project's repository.
 
+link:cmd-copy-approvals.html[gerrit copy-approvals]::
+	Copy all inferred approvals labels to the latest patch-set.
+
+link:cmd-check-project-access.html[gerrit check-project-access]::
+	Check if user(s) can read non-config refs of a project
+
 link:cmd-create-branch.html[gerrit create-branch]::
 	Create a new project branch.
 
@@ -157,6 +163,9 @@
 link:cmd-ls-user-refs.html[gerrit ls-user-refs]::
 	Lists refs visible for a specified user.
 
+link:cmd-migrate-externalids-to-insensitive.html[gerrit migrate-externalids-to-insensitive]::
+	Migrate external-ids to case insensitive.
+
 link:cmd-plugin-install.html[gerrit plugin add]::
 	Alias for 'gerrit plugin install'.
 
diff --git a/Documentation/cmd-ls-groups.txt b/Documentation/cmd-ls-groups.txt
index 8a4845c..0a06c28 100644
--- a/Documentation/cmd-ls-groups.txt
+++ b/Documentation/cmd-ls-groups.txt
@@ -11,7 +11,7 @@
   [--user <NAME> | -u <NAME>]
   [--owned]
   [--visible-to-all]
-  [-q <GROUP>]
+  [-g <GROUP>]
   [--verbose | -v]
 --
 
@@ -67,8 +67,8 @@
 	(groups that are explicitly marked as visible to all registered
 	users).
 
--q::
-	Group that should be inspected. The `-q` option can be specified
+-g::
+	Group that should be inspected. The `-g` option can be specified
 	multiple times to define several groups to be inspected. If
 	specified the listed groups will only contain groups that were
 	specified to be inspected. This is e.g. useful in combination with
diff --git a/Documentation/cmd-migrate-externalids-to-insensitive.txt b/Documentation/cmd-migrate-externalids-to-insensitive.txt
new file mode 100644
index 0000000..b023089
--- /dev/null
+++ b/Documentation/cmd-migrate-externalids-to-insensitive.txt
@@ -0,0 +1,44 @@
+= gerrit migrate-externalids-to-insensitive
+
+== NAME
+gerrit migrate-externalids-to-insensitive - Migrate external-ids to case insensitive.
+
+== SYNOPSIS
+[verse]
+--
+_ssh_ -p <port> <host> _gerrit migrate-externalids-to-insensitive_
+--
+
+== DESCRIPTION
+This command allows to trigger online conversion of `username` and
+`gerrit` external IDs to be handled case insensitively. This is done by
+recomputing the name of the note from the sha1 sum of the all lowercase
+external ID key, instead of preserving the key capitalization.
+
+The command requires link:#auth.userNameCaseInsensitive[auth.userNameCaseInsensitive] and
+link:#auth.userNameCaseInsensitiveMigrationMode[auth.userNameCaseInsensitiveMigrationMode] to
+be set to true to perform the migration.
+
+After the successful migration
+link:#auth.userNameCaseInsensitiveMigrationMode[auth.userNameCaseInsensitiveMigrationMode] is
+set to false.
+
+== ACCESS
+Caller must be a member of the privileged 'Administrators' group.
+
+== SCRIPTING
+This command is intended to be used in scripts.
+
+== EXAMPLES
+Start the online external ids migration:
+
+----
+$ ssh -p 29418 review.example.com gerrit migrate-externalids-to-insensitive
+----
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/cmd-stream-events.txt b/Documentation/cmd-stream-events.txt
index f0ad460..5fd0bfc 100644
--- a/Documentation/cmd-stream-events.txt
+++ b/Documentation/cmd-stream-events.txt
@@ -324,6 +324,19 @@
 
 comment:: Review comment cover message.
 
+=== Project Head Updated
+
+Sent when project's head is updated.
+
+type:: "project-head-updated"
+
+oldHead:: The old project head name
+
+newHead:: The new project head name
+
+eventCreatedOn:: Time in seconds since the UNIX epoch when this event was
+created.
+
 == SEE ALSO
 
 * link:json.html[JSON Data Formats]
diff --git a/Documentation/concept-changes.txt b/Documentation/concept-changes.txt
index 0444fab..6e76a8a 100644
--- a/Documentation/concept-changes.txt
+++ b/Documentation/concept-changes.txt
@@ -34,6 +34,11 @@
 |Owner
 |The contributor who created the change.
 
+|Uploader
+|The user that uploaded the current patch set (e.g. the user that executed the
+`git push` command, or the user that triggered the patch set creation through
+an action in the UI).
+
 |Assignee
 |The contributor responsible for the change. Often used when a change has
 mulitple reviewers to identify the individual responsible for final approval.
diff --git a/Documentation/concept-patch-sets.txt b/Documentation/concept-patch-sets.txt
index 274fbb0..e39d091 100644
--- a/Documentation/concept-patch-sets.txt
+++ b/Documentation/concept-patch-sets.txt
@@ -88,8 +88,8 @@
 evolves, such as "Added more unit tests." Unlike the change description, a patch
 set description does not become a part of the project's history.
 
-To add a patch set description, click *Add a patch set description*, located in
-the file list, or provide it link:user-upload.html#patch_set_description[on upload].
+To add a patch set description provide it
+link:user-upload.html#patch_set_description[on upload].
 
 GERRIT
 ------
diff --git a/Documentation/config-accounts.txt b/Documentation/config-accounts.txt
index 7a7cef2..716fa2f 100644
--- a/Documentation/config-accounts.txt
+++ b/Documentation/config-accounts.txt
@@ -111,8 +111,8 @@
 Accessing the account data in Git is not fast enough for account
 queries, since it requires accessing all user branches and parsing
 all files in each of them. To overcome this Gerrit has a secondary
-index for accounts. The account index is either based on
-link:config-gerrit.html#index.type[Lucene or Elasticsearch].
+index for accounts. The account index is based on
+link:config-gerrit.html#index.type[Lucene].
 
 Via the link:rest-api-accounts.html#query-account[Query Account] REST
 endpoint link:user-search-accounts.html[generic account queries] are
@@ -383,8 +383,8 @@
 [[starred-changes]]
 == Starred Changes
 
-link:dev-stars.html[Starred changes] allow users to mark changes as
-favorites and receive email notifications for them.
+Starred changes allow users to mark changes as favorites and receive email
+notifications for them.
 
 Each starred change is a tuple of an account ID, a change ID and a
 label.
@@ -402,8 +402,7 @@
 when the prefix ends with '/', this ref format is optimized to find
 starred changes by change ID. Finding starred changes by change ID is
 e.g. needed when a change is updated so that all users that have
-the link:dev-stars.html#default-star[default star] on the change can be
-notified by email.
+the star on the change can be notified by email.
 
 Gerrit also needs an efficient way to find all changes that were
 starred by an account, e.g. to provide results for the
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 3109ec7..8d30db2 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -256,8 +256,8 @@
 if link:#auth.gitBasicAuthPolicy[`auth.gitBasicAuthPolicy`] is set to `HTTP_LDAP`,
 the password in the request is first checked against the HTTP password and, if
 it does not match, it is then validated against the LDAP password.
-Service users that only exist in the Gerrit database are authenticated by their
-HTTP passwords.
+Service users that are link:cmd-create-account.html[internal-only] are
+authenticated by their HTTP passwords.
 
 * `LDAP_BIND`
 +
@@ -561,7 +561,8 @@
 +
 To enable the actual usage of contributor agreement the project
 specific config option in the `project.config` must be set:
-link:config-project-config.html[receive.requireContributorAgreement].
+link:config-project-config.html#receive.requireContributorAgreement[
+receive.requireContributorAgreement].
 
 [[auth.trustContainerAuth]]auth.trustContainerAuth::
 +
@@ -611,7 +612,7 @@
 single call would trigger a full LDAP authentication and groups resolution
 which could introduce a noticeable latency on the overall execution
 and produce unwanted load to the LDAP server.
-+
+
 [[auth.gitOAuthProvider]]auth.gitOAuthProvider::
 +
 Selects the OAuth 2 provider to authenticate git over HTTP traffic with.
@@ -666,8 +667,9 @@
 the link:config-accounts.html#external-ids[External ID documentation].
 +
 Gerrit provides the
-link:pgm-ChangeExternalIdCaseSensitivity.html[ChangeExternalIdCaseSensitivity tool]
-to migrate existing accounts to match the new scheme.
+link:pgm-ChangeExternalIdCaseSensitivity.html[offline]
+and the online link:externalid-case-insensitivity.html#online-migration[online]
+tools to migrate existing accounts to match the new scheme.
 +
 Naturally, if there were two accounts only different in capitalization,
 e.g. `johndoe` and `JohnDoe`, the account `JohnDoe` will not be able
@@ -676,6 +678,16 @@
 note name would be identical and thus conflict. These duplicates thus
 have to be deleted manually by deleting the respective external ID.
 +
+For newly initialized sites this option defaults to true.
++
+Default is false.
+
+[[auth.userNameCaseInsensitiveMigrationMode]]auth.userNameCaseInsensitiveMigrationMode::
++
+Setting migration mode to true allows to fallback to case sensitive
+behaviour if the migrated external ID cannot be found. This allows to
+trigger the migration while Gerrit process is running.
++
 Default is false.
 
 [[auth.enableRunAs]]auth.enableRunAs::
@@ -703,8 +715,10 @@
 [[auth.autoUpdateAccountActiveStatus]]auth.autoUpdateAccountActiveStatus::
 +
 Whether to allow automatic synchronization of an account's inactive flag upon login.
++
 If set to true, upon login, if the authentication back-end reports the account as active,
-the account's inactive flag in the internal Gerrit database will be updated to be active.
+the account's inactive flag in NoteDb will be updated to be active.
++
 If the authentication back-end reports the account as inactive, the account's flag will be
 updated to be inactive and the login attempt will be blocked. Users enabling this feature
 should ensure that their authentication back-end is supported. Currently, only
@@ -832,18 +846,21 @@
 entry is relatively the same, memoryLimit is currently defined to be
 the number of entries held by the cache (each entry costs 1).
 +
-For caches where the size of an entry can vary significantly between
-individual entries (notably `"diff"`, `"diff_intraline"`), memoryLimit
-is an approximation of the total number of bytes stored by the cache.
-Larger entries that represent bigger patch sets or longer source files
-will consume a bigger portion of the memoryLimit. For these caches the
-memoryLimit should be set to roughly the amount of RAM (in bytes) the
-administrator can dedicate to the cache.
+For caches where the size of an entry can vary significantly between individual
+entries (notably `"git_modified_files"`, `"modified_files"`, `"git_file_diff"`,
+`"gerrit_file_diff"`, `"diff_intraline"`), memoryLimit is an approximation of
+the total number of bytes stored by the cache.  Larger entries that represent
+bigger patch sets or longer source files will consume a bigger portion of the
+memoryLimit. For these caches the memoryLimit should be set to roughly the
+amount of RAM (in bytes) the administrator can dedicate to the cache.
 +
 Default is 1024 for most caches, except:
 +
 * `"adv_bases"`: default is `4096`
-* `"diff"`: default is `10m` (10 MiB of memory)
+* `"git_modified_files"`: default is `10m` (10 MiB of memory)
+* `"modified_files"`: default is `10m` (10 MiB of memory)
+* `"git_file_diff"`: default is `10m` (10 MiB of memory)
+* `"gerrit_file_diff"`: default is `10m` (10 MiB of memory)
 * `"diff_intraline"`: default is `10m` (10 MiB of memory)
 * `"diff_summary"`: default is `10m` (10 MiB of memory)
 * `"external_ids_map"`: default is `2` and should not be changed
@@ -854,8 +871,14 @@
 * `"plugin_resources"`: default is 2m (2 MiB of memory)
 
 +
-If set to 0 the cache is disabled. Entries are removed immediately
-after being stored by the cache. This is primarily useful for testing.
+If set to 0 the cache is disabled; entries are loaded but not stored
+in-memory.
++
+**NOTE**: When the cache is disabled, there is no locking when accessing
+the same key/value, and therefore multiple threads may
+load the same value concurrently with a higher memory footprint.
+To keep a minimum caching and avoid concurrent loading of the same
+key/value, set `memoryLimit` to `1` and `maxAge` to `1`.
 
 [[cache.name.expireFromMemoryAfterAccess]]cache.<name>.expireFromMemoryAfterAccess::
 +
@@ -940,12 +963,6 @@
 +
 If direct updates are made to `All-Users`, this cache should be flushed.
 
-cache `"approvals"`::
-+
-Cache entries contain approvals for a given patch set. This includes
-approvals granted on this patch set as well as approvals copied from
-earlier patch sets.
-
 cache `"adv_bases"`::
 +
 Used only for push over smart HTTP when branch level access controls
@@ -971,19 +988,41 @@
 this cache should be disabled in a cluster setup using multiple primary
 or multiple replica nodes.
 +
-The cache should be flushed whenever the database changes table is modified
-outside of Gerrit.
+The cache should be flushed whenever NoteDb change metadata in a repository is
+modified outside of Gerrit.
 
-cache `"diff"`::
+cache `"git_modified_files"`::
 +
-Each item caches the differences between two commits, at both the
-directory and file levels.  Gerrit uses this cache to accelerate
-the display of affected file names, as well as file contents.
+Each item caches the list of git modified files between two git trees
+corresponding to two different commits. This cache does not read the actual
+file contents nor does it include the edits (modified regions) of the files.
+
+cache `"modified_files"`::
++
+Each item caches the list of modified files between two commits. This cache
+is similar to the `git_modified_files` cache but performs extra logic including
+filtering out files that are untouched by both commits because they were purely
+modified between the parent commits.
+
+cache `"git_file_diff"`::
++
+Each item caches the pure git diff between two git trees for a specific file
+path. The diff includes all the file attributes (old/new paths, change/patch
+types) as well as the list of edits corresponding to the modified regions in
+the file.
+
+cache `"gerrit_file_diff"`::
++
+Each item caches the diff between two git commits for a specific file path.
+This cache is similar to the `git_file_diff` cache but performs extra logic
+including identifying the edits that are due to rebase. The diff for the
+"commit message" and "merge list" can also be requested from this cache.
 +
 Entries in this cache are relatively large, so memoryLimit is an
 estimate in bytes of memory used. Administrators should try to target
 cache.diff.memoryLimit to fit all changes users will view in a 1 or 2
-day span.
+day span. The same applies for other diff caches: `"git_modified_files"`,
+`"modified_files"` and `"git_file_diff"`.
 
 cache `"diff_intraline"`::
 +
@@ -1017,12 +1056,6 @@
 away from the defaults. The cache may be persisted by setting
 `diskLimit`, which is only recommended if cold start performance is
 problematic.
-+
-`external_ids_map` supports computing the new cache value based on a
-previously cached state. This applies modifications based on the Git
-diff and is almost always faster.
-`cache.external_ids_map.enablePartialReloads` turns this behavior on
-or off. The default is `true`.
 
 cache `"git_tags"`::
 +
@@ -1162,7 +1195,7 @@
 cache `"prolog_rules"`::
 +
 Caches parsed `rules.pl` contents for each project. This cache uses the same
-size as the `projects` cache, and cannot be configured independently.
+size as the `projects` cache when `cache.prolog_rules.memoryLimit` is not set.
 
 cache `"pure_revert"`::
 +
@@ -1206,9 +1239,9 @@
 
 ==== [[cache_options]]Cache Options
 
-[[cache.diff.timeout]]cache.diff.timeout::
+[[cache.git_file_diff.timeout]]cache.git_file_diff.timeout::
 +
-Maximum number of milliseconds to wait for diff data before giving up and
+Maximum number of milliseconds to wait for git diff data before giving up and
 falling back on a simpler diff algorithm that will not be able to break down
 modified regions into smaller ones. This is a work around for an infinite loop
 bug in the default difference algorithm implementation.
@@ -1283,6 +1316,21 @@
 +
 Default is the number of CPUs.
 
+[[cache.project_list.interval]]cache.project_list.interval::
++
+The link:#schedule-configuration-interval[interval] for running
+the project_list cache warmer.
+
+By default, if `cache.project_list.maxAge` is set, `interval` will be set to
+half its value. If `cache.project_list.maxAge` is not set or `interval` is set
+to `-1`, it is disabled.
+
+[[cache.project_list.startTime]]cache.project_list.startTime::
++
+The link:#schedule-configuration-startTime[start time] for running
+the project_list cache warmer.
+
+Default is 00:00 if the project_list cache warmer is enabled.
 
 [[capability]]
 === Section capability
@@ -1314,7 +1362,7 @@
 Whether the first user that logs in to the Gerrit server should
 automatically be added to the administrator group and hence get the
 `administrateServer` capability assigned. This is useful to bootstrap
-the authentication database.
+the link:config-accounts.html[account data].
 +
 Default is true.
 
@@ -1330,15 +1378,16 @@
 
 [[change.cacheAutomerge]]change.cacheAutomerge::
 +
-When reviewing merge commits, the left-hand side shows the output of the
-result of JGit's automatic merge algorithm. This option controls whether
-this output is cached in the change repository, or if only the diff is
-cached in the persistent `diff` cache.
+When reviewing merge commits, the left-hand side shows the output of the result
+of JGit's automatic merge algorithm. This option controls whether this output is
+cached in the change repository, or if only the diff is cached in the persistent
+diff caches (`"git_modified_files"`, `modified_files`, `"git_file_diff"`,
+`"file_diff"`).
 +
 If true, automerge results are stored in the repository under
 `refs/cache-automerge/*`; the results of diffing the change against its
-automerge base are stored in the diff cache. If false, no extra data is
-stored in the repository, only the diff cache. This can result in slight
+automerge base are stored in the diff caches. If false, no extra data is
+stored in the repository, only the diff caches. This can result in slight
 performance improvements by reducing the number of refs in the repo.
 +
 Default is true.
@@ -1399,7 +1448,7 @@
 Maximum number of patch sets allowed per change. If this is insufficient,
 recreate the change with a new Change-Id, then abandon the old change.
 +
-By default 1,500.
+By default 1000.
 
 [[change.maxUpdates]]change.maxUpdates::
 +
@@ -1465,6 +1514,13 @@
 
 Default is true.
 
+[[change.maxSubmittableAtOnce]]change.maxSubmittableAtOnce::
++
+Maximum number of changes that can be chained together in the same repository
+to be submitted at once.
++
+Default is 32767.
+
 [[change.move]]change.move::
 +
 Whether the link:rest-api-changes.html#move-change[Move Change] REST
@@ -1672,9 +1728,7 @@
 will match typical Gerrit Change-Id values and create a hyperlink
 to changes which reference it.  The second configuration 'bugzilla'
 will hyperlink terms such as 'bug 42' to an external bug tracker,
-supplying the argument record number '42' for display.  The third
-configuration 'tracker' uses raw HTML to more precisely control
-how the replacement is displayed to the user.
+supplying the argument record number '42' for display.
 
 commentlinks supports link:#reloadConfig[configuration reloads]. Though a
 link:cmd-flush-caches.html[flush-caches] of "projects" is needed for the
@@ -1686,18 +1740,15 @@
   link = "#/q/$1"
 
 [commentlink "bugzilla"]
-  match = "(bug\\s+#?)(\\d+)"
-  link = http://bugs.example.com/show_bug.cgi?id=$2
-
-[commentlink "tracker"]
-  match = ([Bb]ug:\\s+)(\\d+)
-  html = $1<a href=\"http://trak.example.com/$2\">$2</a>
+  match = "(^|\\s)(bug\\s+#?)(\\d+)($|\\s)"
+  link = http://bugs.example.com/show_bug.cgi?id=$3
+  prefix = $1
+  suffix = $4
+  text = $2$3
 ----
 
 Comment links can also be specified in `project.config` and sections in
-children override those in parents. The only restriction is that to
-avoid injecting arbitrary user-supplied HTML in the page, comment links
-defined in `project.config` may only supply `link`, not `html`.
+children override those in parents.
 
 [[commentlink.name.match]]commentlink.<name>.match::
 +
@@ -1722,22 +1773,32 @@
 be updated to match text formats.
 +
 A common pattern to match is `bug\\s+(\\d+)`.
++
+In order to better control the visual presentation of the link `prefix`,
+`suffix` and `text` is used. With the generated link html looking like:
+`prefix<a ...>text</a>suffix`.
 
 [[commentlink.name.link]]commentlink.<name>.link::
 +
 The URL to direct the user to whenever the regular expression is
 matched.  Groups in the match expression may be accessed as `$'n'`.
-+
-The link property is used only when the html property is not present.
 
-[[commentlink.name.html]]commentlink.<name>.html::
+[[commentlink.name.prefix]]commentlink.<name>.prefix::
 +
-HTML to replace the entire matched string with.  If present,
-this property overrides the link property above.  Groups in the
-match expression may be accessed as `$'n'`.
+The text inserted before the link. Groups in the match expression may be
+accessed as `$'n'`.
+
+[[commentlink.name.suffix]]commentlink.<name>.suffix::
 +
-The configuration file eats double quotes, so escaping them as
-`\"` is necessary to protect them from the parser.
+The text inserted after the link. Groups in the match expression may be
+accessed as `$'n'`.
+
+[[commentlink.name.text]]commentlink.<name>.text::
++
+The text content of the link. Groups in the match expression may be
+accessed as `$'n'`.
++
+If not specified defaults to `$&` (the matched text).
 
 [[commentlink.name.enabled]]commentlink.<name>.enabled::
 +
@@ -1745,11 +1806,6 @@
 section in a parent or the site-wide config that is disabled by
 specifying `enabled = true`.
 +
-Disabling sections in `gerrit.config` can be used by site administrators
-to create a library of comment links with `html` set that are not
-user-supplied and thus can be verified to be XSS-free, but are only
-enabled for a subset of projects.
-+
 By default, true.
 +
 Note that the names and contents of disabled sections are visible even
@@ -1997,6 +2053,14 @@
 +
 Default is 1 hour.
 
+[[dashboard]]
+=== Section dashboard
+
+[[dashboard.submitRequirementColumns]]dashboard.submitRequirementColumns::
++
+The list of submit requirement names that should be displayed as separate
+columns in the dashboard.
+
 [[download]]
 === Section download
 
@@ -2072,7 +2136,7 @@
 Gerrit advertises patch set downloads with the `repo download`
 command, assuming that all projects managed by this instance are
 generally worked on with the
-[repo multi-repository tool](https://gerrit.googlesource.com/git-repo).
+https://gerrit.googlesource.com/git-repo[repo multi-repository tool].
 This is not default, as not all instances will deploy repo.
 
 +
@@ -2183,6 +2247,9 @@
 other project managed by the running server. The name is
 relative to `gerrit.basePath`.
 +
+The link:#cache_names[persisted_projects cache] must be
+flushed after this setting is changed.
++
 Defaults to `All-Projects` if not set.
 
 [[gerrit.defaultBranch]]gerrit.defaultBranch::
@@ -2207,7 +2274,7 @@
 or "http://example.com:8080/gerrit/" so Gerrit can output links that point
 back to itself.
 +
-Setting this is highly recommended, as its necessary for the upload
+Setting this is highly recommended, as it is necessary for the upload
 code invoked by "git push" or "repo upload" to output hyperlinks
 to the newly uploaded changes.
 
@@ -2267,7 +2334,7 @@
 By default unset, as the HTTP daemon must be configured externally
 by the system administrator, and might not even be running on the
 same host as Gerrit.
-+
+
 [[gerrit.installBatchModule]]gerrit.installBatchModule::
 +
 Repeatable list of class name of additional Guice modules to load as
@@ -2277,7 +2344,7 @@
 located under the `/lib` directory.
 +
 By default unset.
-+
+
 [[gerrit.installDbModule]]gerrit.installDbModule::
 +
 Repeatable list of class name of additional Guice modules to load at
@@ -2288,6 +2355,25 @@
 +
 By default unset.
 
+[[gerrit.installIndexModule]]gerrit.installIndexModule::
++
+Class name of the Guice modules to load as alternate implementation
+for the Gerrit indexes backend.
+Classes are resolved using the primary Gerrit class loader, hence the
+class needs to be either declared in Gerrit or an additional JAR
+located under the `/lib` directory.
++
+NOTE: The `gerrit.installIndexModule` has precedence over the
+`index.type`.
++
+By default unset.
++
+Example:
+----
+[gerrit]
+  installIndexModule = com.google.gerrit.elasticsearch.ElasticIndexModule
+----
++
 [[gerrit.installModule]]gerrit.installModule::
 +
 Repeatable list of class name of additional Guice modules to load at
@@ -2443,6 +2529,26 @@
 by the target version of the upgrade. Refer to the release notes and check whether
 the rolling upgrade is possible or not and the associated constraints.
 
+[[gerrit.importedServerId]]gerrit.importedServerId::
++
+ServerId of the repositories imported from other Gerrit servers. Changes coming
+associated with the imported serverIds are indexed and displayed in the UI.
++
+Specify multiple `gerrit.importedServerId` for allowing the import from multiple
+Gerrit servers with different serverIds.
++
+[NOTE]
+The account-ids referenced in the imported changes are used for looking up the
+associated account-id locally, using the `imported:` external-id.
+Example: the account-id 1000 from the imported server-id 59a4964e-6376-4ed9-beef
+will be looked up in the local accounts using the `imported:1000@59a4964e-6376-4ed9-beef`
+external-id.
++
+If this value is not set, all changes imported from other Gerrit servers will be
+ignored.
++
+By default empty.
+
 [[gerrit.serverId]]gerrit.serverId::
 +
 Used by NoteDb to, amongst other things, identify author identities from
@@ -2538,8 +2644,9 @@
 set to `custom`.
 +
 Valid replacements are `${project}` for the project name in Gerrit,
-`${file}` for the file name and `${commit}` for the SHA-1 hash for
-the commit.
+`${file}` for the file name, `${hash}` for the SHA-1 hash for the commit,
+and `${commit}` for the change ref or SHA-1 of the commit if no base
+patch set.
 
 [[gitweb.filehistory]]gitweb.filehistory::
 +
@@ -2633,6 +2740,39 @@
 when this parameter is removed and the system group uses the default
 name again.
 
+[[groups.relevantGroup]]groups.relevantGroup::
++
+UUID of an external group that should always be considered as relevant
+when checking whether an account is visible.
++
+This setting is only relevant for external group backends and only if
+the link:#accounts.visibility[account visibility] is set to
+`SAME_GROUP` or `VISIBLE_GROUP`.
++
+If the link:#accounts.visibility[account visibility] is set to
+`SAME_GROUP` or `VISIBLE_GROUP` users should see all accounts that are
+a member of a group that contains themselves or that is visible to
+them. Checking this would require getting all groups of the current
+user and all groups of the accounts for which the visibility is being
+checked, but since getting all groups that a user is a member of is
+expensive for external group backends Gerrit doesn't query these groups
+but instead guesses the relevant groups. Guessing relevant groups
+limits the inspected groups to all groups that are mentioned in the
+ACLs of the projects that are currently cached (i.e. all groups that
+are listed in the link:config-project-config.html#file-groups[groups]
+files of the cached projects). This is not very reliable since it
+depends on which groups are mentioned in the ACLs and which projects
+are currently cached. To make this more reliable this configuration
+parameter allows to configure external groups that should always be
+considered as relevant.
++
+As said this setting is only relevant for external group backends. In
+Gerrit core this is only the LDAP backend, but it may apply to further
+group backends that are added by plugins.
++
+This parameter may be added multiple times to specify multiple relevant
+groups.
+
 [[has-operand-alias]]
 === Section has operand alias
 
@@ -3107,23 +3247,13 @@
 
 [[index.type]]index.type::
 +
-Type of secondary indexing employed by Gerrit.  The supported
-values are:
+*(DEPRECATED)* The only supported value is `LUCENE`, which is the default,
+that means a link:http://lucene.apache.org/[Lucene]
+index is used.
 +
-* `LUCENE`
+For using other indexing backends (e.g. ElasticSearch), refer to
+`gerrit.installIndexModule` setting.
 +
-A link:http://lucene.apache.org/[Lucene] index is used.
-+
-+
-* `ELASTICSEARCH` look into link:#elasticsearch[Elasticsearch section]
-+
-An link:https://www.elastic.co/products/elasticsearch[Elasticsearch,role=external,window=_blank] index is
-used. Refer to the link:#elasticsearch[Elasticsearch section] for further
-configuration details.
-
-+
-By default, `LUCENE`.
-
 [[index.threads]]index.threads::
 +
 Number of threads to use for indexing in normal interactive operations. Setting
@@ -3136,11 +3266,20 @@
 [[index.batchThreads]]index.batchThreads::
 +
 Number of threads to use for indexing in background operations, such as
-online schema upgrades, and also for offline reindexing.
+online schema upgrades, and also the default for offline reindexing.
 +
 If not set or set to a zero, defaults to the number of logical CPUs as returned
 by the JVM. If set to a negative value, defaults to a direct executor.
 
+[[index.cacheQueryResultsByChangeNum]]index.cacheQueryResultsByChangeNum::
++
+Allow to cache and reuse the change JSON elements by their Change number.
+This improves the performance of queries that are returning Changes duplicates.
+It needs to be turned off when having Changes imported from other servers
+because of the potential conflicts of change numbers.
++
+Defaults to true.
+
 [[index.onlineUpgrade]]index.onlineUpgrade::
 +
 Whether to upgrade to new index schema versions while the server is
@@ -3153,17 +3292,31 @@
 +
 Defaults to true.
 
+[[index.paginationType]]index.paginationType::
++
+The pagination type to use when index queries are repeated to
+obtain the next set of results. Supported values are:
++
+* `OFFSET`
++
+Index queries are repeated with a non-zero offset to obtain the
+next set of results.
++
+* `SEARCH_AFTER`
++
+Index queries are repeated using a search-after object. Index
+backends can provide their custom implementations for search-after.
+Note that, `SEARCH_AFTER` does not impact using offsets in Gerrit
+query APIs.
++
+Defaults to `OFFSET`.
+
 [[index.maxLimit]]index.maxLimit::
 +
 Maximum limit to allow for search queries. Requesting results above this
 limit will truncate the list (but will still set `_more_changes` on
 result lists). Set to 0 for no limit.
 +
-When `index.type` is set to `ELASTICSEARCH`, this value should not exceed
-the `index.max_result_window` value configured on the Elasticsearch
-server. If a value is not configured during site initialization, defaults to
-10000, which is the default value of `index.max_result_window` in Elasticsearch.
-+
 When `index.type` is set to `LUCENE`, defaults to no limit.
 
 [[index.maxPages]]index.maxPages::
@@ -3176,6 +3329,44 @@
 +
 Defaults to no limit.
 
+[[index.pageSizeMultiplier]]index.pageSizeMultiplier::
++
+When index queries are repeated to obtain more results, this multiplier
+will be used to determine the limit for the next query. Using a page
+multiplier allows queries to start off small and thus provide good
+latency for queries which may end up only having very few results, and
+then scaling up to have better throughput to handle queries with larger
+result sets without incurring the overhead of making as many queries as
+would be required with a smaller limit. This strategy of using a multiplier
+attempts to create a balance between latency and throughput by dynamically
+adjusting the query size to the number of results being returned by each
+query in the pagination.
++
+The larger the multiplier, the better the throughput on large queries, and
+it also improves latency on large queries by scaling up quickly. However, a
+larger multiplier can hurt latencies a bit by making the "last" query in a
+series longer than needed. The impact of this depends on how much the backend
+latency goes up when specifying a large limit and few results are returned.
+Setting link:#index.maxPageSize[index.maxPageSize] that isn't too large, can
+likely help reduce the impacts of this.
++
+For example, if the limit of the previous query was 500 and pageSizeMultiplier
+is configured to 5, the next query will have a limit of 2500.
++
+Defaults to 1 which effectively turns this feature off.
+
+[[index.maxPageSize]]index.maxPageSize::
++
+Maximum size to allow when index queries are repeated to obtain more results. Note
+that, link:#index.maxLimit[index.maxLimit] will be used to limit page size if it
+is configured to a value lower than maxPageSize.
++
+For example, if the limit of previous query was 500, pageSizeMultiplier is
+configured to 5 and maxPageSize to 2000, the next query will have a limit of
+2000 (instead of 2500).
++
+Defaults to no limit.
+
 [[index.maxTerms]]index.maxTerms::
 +
 Maximum number of leaf terms to allow in a query. Too-large queries may
@@ -3232,7 +3423,7 @@
 +
 Whether the scheduled indexer is enabled. If the scheduled indexer is
 disabled you must implement other means to keep the group index for the
-replica up-to-date (e.g. by using ElasticSearch for the indexes).
+replica up-to-date.
 +
 Defaults to `true`.
 
@@ -3339,6 +3530,11 @@
 +
 Defaults to true (throttling enabled).
 
+During offline reindexing, setting ramBufferSize greater than the size
+of index (size of specific index folder under <site_dir>/index) and
+maxBufferedDocs as -1 avoids unnecessary flushes and triggers only a
+single flush at the end of the process.
+
 Sample Lucene index configuration:
 ----
 [index]
@@ -3360,95 +3556,6 @@
 
 ----
 
-[[elasticsearch]]
-=== Section elasticsearch
-
-WARNING: Support for Elasticsearch is still experimental and is not recommended
-for production use. For compatibility information, please refer to the
-link:https://www.gerritcodereview.com/elasticsearch.html[project homepage,role=external,window=_blank].
-
-Note that when Gerrit is configured to use Elasticsearch, the Elasticsearch
-server(s) must be reachable during the site initialization.
-
-[[elasticsearch.prefix]]elasticsearch.prefix::
-+
-This setting can be used to prefix index names to allow multiple Gerrit
-instances in a single Elasticsearch cluster. Prefix `gerrit1_` would result in a
-change index named `gerrit1_changes_0001`.
-+
-Not set by default.
-
-[[elasticsearch.server]]elasticsearch.server::
-+
-Elasticsearch server URI in the form `http[s]://hostname:port`. The `port` is
-optional and defaults to `9200` if not specified.
-+
-At least one server must be specified. May be specified multiple times to
-configure multiple Elasticsearch servers.
-+
-Note that the site initialization program only allows to configure a single
-server. To configure multiple servers the `gerrit.config` file must be edited
-manually.
-
-[[elasticsearch.numberOfShards]]elasticsearch.numberOfShards::
-+
-Sets the number of shards to use per index. Refer to the
-link:https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#_static_index_settings[
-Elasticsearch documentation,role=external,window=_blank] for details.
-+
-Defaults to 1.
-
-[[elasticsearch.numberOfReplicas]]elasticsearch.numberOfReplicas::
-+
-Sets the number of replicas to use per index. Refer to the
-link:https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#dynamic-index-settings[
-Elasticsearch documentation,role=external,window=_blank] for details.
-+
-Defaults to 1.
-
-[[elasticsearch.maxResultWindow]]elasticsearch.maxResultWindow::
-+
-Sets the maximum value of `from + size` for searches to use per index. Refer to the
-link:https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#dynamic-index-settings[
-Elasticsearch documentation,role=external,window=_blank] for details.
-+
-Defaults to 10000.
-
-[[elasticsearch.connectTimeout]]elasticsearch.connectTimeout::
-+
-Sets the timeout for connecting to elasticsearch.
-+
-Defaults to `1 second`.
-
-[[elasticsearch.socketTimeout]]elasticsearch.socketTimeout::
-+
-Sets the timeout for the underlying connection. For more information, refer to
-link:#httpd.idleTimeout[`httpd.idleTimeout`].
-+
-Defaults to `30 seconds`.
-
-==== Elasticsearch Security
-
-When security is enabled in Elasticsearch, the username and password must be provided.
-Note that the same username and password are used for all servers.
-
-For further information about Elasticsearch security, please refer to
-link:https://www.elastic.co/guide/en/elasticsearch/reference/current/security-getting-started.html[the documentation,role=external,window=_blank].
-This is the current documentation link. Select another Elasticsearch version
-from the dropdown menu available on that page if need be.
-
-[[elasticsearch.username]]elasticsearch.username::
-+
-Username used to connect to Elasticsearch.
-+
-If a password is set, defaults to `elastic`, otherwise not set by default.
-
-[[elasticsearch.password]]elasticsearch.password::
-+
-Password used to connect to Elasticsearch.
-+
-Not set by default.
-
 [[event]]
 === Section event
 
@@ -3973,6 +4080,46 @@
 +
 Defaults to true.
 
+[[metrics]]
+=== Section metrics
+
+[[metrics.reservoir]]metrics.reservoir::
++
+The type of data reservoir used by the metrics system to calculate the percentile
+values for timers and histograms.
+It can be set to one of the following values:
++
+* ExponentiallyDecaying: An exponentially-decaying random reservoir based on
+  Cormode et al's forward-decaying priority reservoir sampling method to produce
+  a statistically representative sampling reservoir, exponentially biased towards
+  newer entries.
+* SlidingTimeWindowArray: A sliding window that stores only the measurements made
+  in the last window using chunks of 512 samples.
+* SlidingTimeWindow: A sliding window that stores only the measurements made in
+  the last window using a skip list.
+* SlidingWindow: A sliding window that stores only the last measurements.
+* Uniform: A random sampling reservoir that uses Vitter's Algorithm R to produce
+  a statistically representative sample.
++
+Defaults to ExponentiallyDecaying.
+
+[[metrics.ExponentiallyDecaying.alpha]]metrics.ExponentiallyDecaying.alpha::
++
+The exponential decay factor; the higher this is, the more biased the reservoir
+will be towards newer values.
+
+[[metrics.reservoirType.size]]metrics.<reservoirType>.size::
++
+The number of samples to keep in the reservoir. Applies to all reservoir types
+except the sliding time-based ones.
++
+Defaults to 1028.
+
+[[metrics.reservoirType.window]]metrics.<reservoirType>.window::
++
+The window of time for keeping data in the reservoir. It only applies to sliding
+time-based reservoir types.
+
 [[mimetype]]
 === Section mimetype
 
@@ -4156,6 +4303,14 @@
 +
 Default is 5 seconds. Negative values will be converted to 0.
 
+[[plugins.transitionalPushOptions]]plugins.transitionalPushOptions::
++
+Additional push options which should be accepted by gerrit as valid
+options even if they are not registered by any plugin(e.g. "myplugin~foo").
++
+This config can be used when gerrit migrates from a deprecated plugin to the new one. The new plugin
+can (temporary) accept push options of the old plugin without registering such options.
+
 [[receive]]
 === Section receive
 
@@ -4194,18 +4349,6 @@
 +
 Default is 5 minutes.
 
-[[receive.changeUpdateThreads]]receive.changeUpdateThreads::
-+
-Number of threads to perform change creation or patch set updates
-concurrently. Each thread uses its own database connection from
-the database connection pool, and if all threads are busy then
-main receive thread will also perform a change creation or patch
-set update.
-+
-Defaults to 1, using only the main receive thread. This feature is for
-databases with very high latency that can benefit from concurrent
-operations when multiple changes are impacted at once.
-
 [[receive.checkMagicRefs]]receive.checkMagicRefs::
 +
 If true, Gerrit will verify the destination repository has
@@ -4447,9 +4590,8 @@
 
 [[repository.name.ownerGroup]]repository.<name>.ownerGroup::
 +
-A name of a group which exists in the database. Zero, one or many
-groups are allowed.  Each on its own line.  Groups which don't exist
-in the database are ignored.
+A name of a link:config-groups.html[group] which exists. Zero, one or many
+groups are allowed.  Each on its own line.  Groups which don't exist are ignored.
 
 [[retry]]
 === Section retry
@@ -4902,16 +5044,6 @@
   replicate = replication start
 ----
 
-[[ssh]]
-=== Section ssh
-
-[[ssh.clientImplementation]]ssh.clientImplementation::
-+
-JCraft JSch client is supported in addition to Apache MINA SSH client.
-To use JSch client set the value to `JSCH`.
-+
-By default, `APACHE`.
-
 [[sshd]]
 === Section sshd
 
@@ -5132,22 +5264,27 @@
 +
 Supported ciphers:
 +
-* `aes128-ctr`
-* `aes192-ctr`
-* `aes256-ctr`
 * `aes128-cbc`
+* `aes128-ctr`
+* `aes128-gcm@openssh.com`
 * `aes192-cbc`
+* `aes192-ctr`
 * `aes256-cbc`
-* `blowfish-cbc`
-* `3des-cbc`
+* `aes256-ctr`
+* `aes256-gcm@openssh.com`
 * `arcfour128`
 * `arcfour256`
+* `blowfish-cbc`
+* `chacha20-poly1305@openssh.com`
+* `3des-cbc`
 * `none`
 +
-By default, all supported ciphers except `none` are available.
-+
 If your setup allows for it, it's recommended to disable all ciphers except
 the AES-CTR modes.
++
+See also link:https://github.com/apache/mina-sshd/tree/master#ciphers[ciphers,role=external,window=_blank].
++
+By default, all supported ciphers except `none` are available.
 
 [[sshd.mac]]sshd.mac::
 +
@@ -5165,6 +5302,11 @@
 * `hmac-sha1-96`
 * `hmac-sha2-256`
 * `hmac-sha2-512`
+* `hmac-sha1-etm@openssh.com`
+* `hmac-sha2-256-etm@openssh.com`
+* `hmac-sha2-512-etm@openssh.com`
++
+See also link:https://github.com/apache/mina-sshd/tree/master#macs[macs,role=external,window=_blank].
 +
 By default, all supported MACs are available.
 
@@ -5193,6 +5335,9 @@
 * `ecdh-sha2-nistp521`
 * `ecdh-sha2-nistp384`
 * `ecdh-sha2-nistp256`
+* `curve25519-sha256`
+* `curve25519-sha256@libssh.org`
+* `curve448-sha512`
 * `diffie-hellman-group-exchange-sha256`
 * `diffie-hellman-group18-sha512`
 * `diffie-hellman-group17-sha512`
@@ -5203,12 +5348,14 @@
 See link:#sshd.enableDeprecatedKexAlgorithms[sshd.enableDeprecatedKexAlgorithms]
 for deprecated key algorithms and how to enable them.
 
-By default, all supported key exchange algorithms are available.
-
 It is strongly recommended to disable at least `diffie-hellman-group1-sha1`
 as it's known to be vulnerable (logjam attack). Additionally, if your setup
 allows for it, it is recommended to disable the remaining two `sha1` key
 exchange algorithms.
+
+See also link:https://github.com/apache/mina-sshd/tree/master#key-exchange[key exchange,role=external,window=_blank].
+
+By default, all supported key exchange algorithms are available.
 --
 
 [[sshd.kerberosKeytab]]sshd.kerberosKeytab::
@@ -5288,6 +5435,7 @@
 used for suggesting accounts when adding members to a group.
 +
 By default 0.
+
 [[suggest.relevantChanges]]suggest.relevantChanges::
 +
 When suggesting reviewers, we go over recent changes of the user, and
@@ -5319,13 +5467,25 @@
 end of the request the performance events are handed over to the
 link:dev-plugins.html#performance-logger[PerformanceLogger] plugins.
 This means if performance logging is enabled, the memory footprint of
-requests is slightly increased.
+requests can be markedly increased.
+In one recorded case the impact was an overall heap increase of 40%
+(using the metrics-reporter-graphite plugin), in other instances the
+heap increase wasn't nearly as dramatic and the impact is most likely
+dependent on which plugin is used.
 +
-This setting has no effect if no
-link:dev-plugins.html#performance-logger[PerformanceLogger] plugins are
-installed, because then performance logging is always disabled.
+By default, false.
+
+[[tracing.exportPerformanceMetrics]]tracing.exportPerformanceMetrics::
 +
-By default, true.
+Whether to export performance metrics.
++
+Performace logged when link:#tracing.performanceLogging[`performanceLogging`] is
+enabled, can be exported as metrics.
++
+NOTE: Since the payload returned could be of tens of thousands metrics,
+assess the latency of the metrics endpoint before enabling this option.
++
+By default, false.
 
 [[tracing.traceid]]
 ==== Subsection tracing.<trace-id>
@@ -5526,7 +5686,7 @@
 
 [[trackingid.name.system]]trackingid.<name>.system::
 +
-The name of the external tracking system (maximum 10 characters).
+The name of the external tracking system (maximum 20 characters).
 It is possible to have several trackingid entries for the same
 tracking system.
 
@@ -5786,10 +5946,6 @@
 [auth]
   registerEmailPrivateKey = 2zHNrXE2bsoylzUqDxZp0H1cqUmjgWb6
 
-[database]
-  username = webuser
-  password = s3kr3t
-
 [ldap]
   password = l3tm3srch
 
diff --git a/Documentation/config-gitweb.txt b/Documentation/config-gitweb.txt
index 46c9ced..00e33a3 100644
--- a/Documentation/config-gitweb.txt
+++ b/Documentation/config-gitweb.txt
@@ -268,7 +268,22 @@
 
 cgit can be used by specifying `gitweb.type` to be 'cgit'.
 
-It is also possible to define custom patterns.
+It is also possible to define custom patterns. Gitea can be used
+with custom patterns for example:
+
+----
+  git config -f $site_path/etc/gerrit.config gitweb.type custom
+  git config -f $site_path/etc/gerrit.config gitweb.urlEncode false
+  git config -f $site_path/etc/gerrit.config gitweb.linkname gitea
+  git config -f $site_path/etc/gerrit.config gitweb.url https://gitea.example.org/
+  git config -f $site_path/etc/gerrit.config gitweb.branch ${project}/src/branch/${branch}
+  git config -f $site_path/etc/gerrit.config gitweb.file ${project}/src/commit/${hash}/${file}
+  git config -f $site_path/etc/gerrit.config gitweb.filehistory ${project}/commits/branch/${branch}/${file}
+  git config -f $site_path/etc/gerrit.config gitweb.project ${project}
+  git config -f $site_path/etc/gerrit.config gitweb.revision ${project}/commit/${commit}
+  git config -f $site_path/etc/gerrit.config gitweb.roottree ${project}/src/commit/${commit}
+  git config -f $site_path/etc/gerrit.config gitweb.tag ${project}/src/tag/${tag}
+----
 
 === SEE ALSO
 
diff --git a/Documentation/config-groups.txt b/Documentation/config-groups.txt
index 0917515..4abb223 100644
--- a/Documentation/config-groups.txt
+++ b/Documentation/config-groups.txt
@@ -34,7 +34,7 @@
 group, there is a ref, stored as a sharded UUID, e.g.
 
 ----
-  refs/groups/ef/deafbeefdeafbeefdeafbeefdeafbeefdeafbeef
+  refs/groups/de/deafbeefdeafbeefdeafbeefdeafbeefdeafbeef
 ----
 
 The ref points to commits holding files. The files are
diff --git a/Documentation/config-labels.txt b/Documentation/config-labels.txt
index 5889c75..ce63295 100644
--- a/Documentation/config-labels.txt
+++ b/Documentation/config-labels.txt
@@ -6,7 +6,47 @@
 link:access-control.html#category_review_labels[access controls].  Gerrit
 comes pre-configured with the Code-Review label that can be granted to
 groups within projects, enabling functionality for that group's members.
+Project owners and admins might also need to configure rules which require
+labels to be voted before a change can be submittable. See the
+link:config-submit-requirements.html[submit requirements] documentation to
+configure such rules.
 
+[[sticky_votes]]
+== Sticky Votes
+
+Whether votes are sticky when a new patch set is created depends on the
+link:#label_copyCondition[copyCondition] of the label. If an approval
+matches the configured condition it is copied from the old current
+patch set to the new current patch set. Votes that are not copied to
+the new patch set, are called `outdated`.
+
+If votes get outdated due to pushing a new patch set the uploader is
+informed about this by a message in the git output. In addition,
+outdated votes are also listed in the email notification that is sent
+for the new patch set (unless this is disabled by a custom email
+template). Note, that the uploader only get this email notification if
+they have configured `Every Comment` for `Email notifications` in their
+user preferences. With any other email preference the email sender, the
+uploader in this case, is not included in the email recipients.
+
+If votes get outdated due to creating a new patch set the user of the
+removed vote is added to the attention set of the change, as they need
+to re-review the change and renew their vote.
+
+If a vote is applied on an outdated patch set (i.e. a patch set that is
+not the current patch set) the vote is copied forward to follow-up
+patch sets if possible. A newly added or updated vote on an outdated
+patch set is copied to follow-up patch sets if:
+
+* the vote is copyable (i.e. it matches the
+link:#label_copyCondition[copyCondition] of the label)
+* neither the follow-up patch set nor an intermediate patch set has a
+  non-copied vote or a deletion vote (vote with value `0`) that
+  overrides the copy vote
+
+If an approval on an outdated patch set is removed or updated to a
+value that is not copyable, existing copies of that approval on
+follow-up patch sets are removed.
 
 [[label_Code-Review]]
 == Label: Code-Review
@@ -18,7 +58,7 @@
 
 The range of values is:
 
-* -2 This shall not be merged
+* -2 This shall not be submitted
 +
 The code is so horribly incorrect/buggy/broken that it must not be
 submitted to this project, or to this branch.  This value is valid
@@ -28,7 +68,7 @@
 +
 *Any -2 blocks submit.*
 
-* -1 I would prefer this is not merged as is
+* -1 I would prefer this is not submitted as is
 +
 The code doesn't look right, or could be done differently, but
 the reviewer is willing to live with it as-is if another reviewer
@@ -61,8 +101,8 @@
 
 For a change to be submittable, the latest patch set must have a
 `+2 Looks good to me, approved` in this category, and no
-`-2 Do not submit`.  Thus `-2` on any patch set can block a submit,
-while `+2` on the latest patch set can enable it.
+`-2 This shall not be submitted`.  Thus `-2` on any patch set can
+block a submit, while `+2` on the latest patch set can enable it.
 
 If a Gerrit installation does not wish to use this label in any project,
 the `[label "Code-Review"]` section can be deleted from `project.config`
@@ -97,7 +137,7 @@
       value = -1 Fails
       value = 0 No score
       value = +1 Verified
-      copyAllScoresIfNoCodeChange = true
+      copyCondition = changekind:NO_CODE_CHANGE
 ----
 
 The range of values is:
@@ -160,9 +200,9 @@
 properties in the child project's configuration; all properties from
 the parent definition must be redefined in the child.
 
-To remove a label in a child project, add an empty label with the same
-name as in the parent. This will override the parent label with
-a label containing the defaults (`function = MaxWithBlock`,
+To remove a label in a child project, add an empty label with a single "0"
+value, with the same name as in the parent. This will override the parent label
+with a label containing the defaults (`function = NoBlock`,
 `defaultValue = 0` and no further allowed values)
 
 [[label_layout]]
@@ -176,6 +216,11 @@
 The name for a label, consisting only of alphanumeric characters and
 `-`.
 
+[[label_description]]
+=== `label.Label-Name.description`
+
+The label description. This field can provide extra information of what the
+label is supposed to do.
 
 [[label_value]]
 === `label.Label-Name.value`
@@ -200,7 +245,21 @@
 
 
 [[label_function]]
-=== `label.Label-Name.function`
+=== `label.Label-Name.function (deprecated)`
+
+Label functions dictate the rules for requiring certain label votes before a
+change is allowed for submission. Label functions are **deprecated** and updates
+that set `function` to a blocking value {`MaxWithBlock`, `MaxNoBlock`,
+`AnyWithBlock`} will be rejected. Existing label function definitions can only
+be updated to {`NoBlock`, `NoOp`, `PatchSetLock`}. New label defintions should
+also explicitly set the `function` attribute to a non-blocking value since the
+default is `MaxWithBlock`.
+
+If your project has a
+blocking label function, we highly encourage you to change it to `NoBlock` and
+add a submit-requirement for the same label. See the
+link:config-submit-requirements.html#code-review-example[submit-requirements
+documentation] for more details.
 
 The name of a function for evaluating multiple votes for a label.  This
 function is only applied if the default submit rule is used for a label.
@@ -262,12 +321,6 @@
 
 Defaults to true.
 
-[[label_copyAnyScore]]
-=== `label.Label-Name.copyAnyScore`
-
-If true, any score for the label is copied forward when a new patch
-set is uploaded. Defaults to false.
-
 [[label_copyCondition]]
 === `label.Label-Name.copyCondition`
 
@@ -280,39 +333,164 @@
 
 Gerrit currently supports the following predicates:
 
-==== changekind:{REWORK,TRIVIAL_REBASE,MERGE_FIRST_PARENT_UPDATE,NO_CODE_CHANGE,NO_CHANGE}
+[[changekind]]
+==== changekind:{NO_CHANGE,NO_CODE_CHANGE,MERGE_FIRST_PARENT_UPDATE,REWORK,TRIVIAL_REBASE}
 
-Matches if the diff between two patch sets was of a certain change kind.
+Matches if the diff between two patch sets was of a certain change kind:
 
+* [[no_change]]`NO_CHANGE`:
++
+Matches when a new patch set is uploaded that has the same parent tree,
+code delta, and commit message as the previous patch set. This means
+that only the patch set SHA-1 is different. This can be used to enable
+sticky approvals, reducing turn-around for this special case.
++
+It is recommended to leave this enabled for both, the Code-Review and
+the Verified labels.
++
+`NO_CHANGE` is more trivial than a trivial rebase, no code change and
+a first parent update, hence this change kind is also matched by
+`changekind:TRIVIAL_REBASE`, `changekind:NO_CODE_CHANGE` and if it's
+a merge commit by `changekind:MERGE_FIRST_PARENT_UPDATE`.
+
+
+* [[no_code_change]]`NO_CODE_CHANGE`:
++
+Matches when a new patch set is uploaded that has the same parent tree
+as the previous patch set and the same code diff (including context
+lines) as the previous patch set. This means only the commit message
+may be different; the change hasn't even been rebased. Also matches if
+the commit message is not different, which means this includes matching
+patch sets that have `NO_CHANGE` as the change kind.
++
+This predicate can be used to enable sticky approvals on labels that
+only depend on the code, reducing turn-around if only the commit
+message is changed prior to submitting a change.
++
+For the Verified label that is optionally installed by the
+link:pgm-init.html[init] site program this predicate is used by
+default.
+
+* [[merge_first_parent_update]]`MERGE_FIRST_PARENT_UPDATE`:
++
+Matches when a new patch set is uploaded that is a new merge commit
+which only differs from the merge commit in the previous patch set in
+its first parent, or has identical parents (aka the change kind of the
+merge commit is `NO_CHANGE`).
++
+The first parent of the merge commit is part of the change's target
+branch, whereas the other parent(s) refer to the feature branch(es) to
+be merged.
++
+Matching this change kind is useful if you don't want to trigger CI or
+human verification again if your target branch moved on but the feature
+branch(es) being merged into the target branch did not change.
++
+This predicate does not match if the patch set is not a merge commit.
+
+* [[trivial_rebase]]`TRIVIAL_REBASE`:
++
+Matches when a new patch set is uploaded that is a trivial rebase. A
+new patch set is considered to be trivial rebase if the commit message
+is the same as in the previous patch set and if it has the same diff
+(including context lines) as the previous patch set. This is the case
+if the change was rebased onto a different parent and that rebase did
+not require git to perform any conflict resolution, or if the parent
+did not change at all (aka the change kind of the commit is
+`NO_CHANGE`).
++
+This predicate can be used to enable sticky approvals, reducing
+turn-around for trivial rebases prior to submitting a change.
++
+For the pre-installed Code-Review label this predicate is used by
+default.
+
+* [[rework]]`REWORK`:
++
+Matches all kind of change kinds because any other change kind
+is just a more trivial version of a rework. This means setting
+`changekind:REWORK` is equivalent to setting `is:ANY`.
+
+[[is_magic]]
 ==== is:{MIN,MAX,ANY}
 
-Matches votes that are equal to the minimal or maximal voting range. Or any votes.
+Matches approvals that have a minimal, maximal or any score:
 
-==== approverin:link:rest-api-groups.html#group-id[\{group-id\}]
+* [[is_min]]`MIN`:
++
+Matches approvals that have a minimal score, i.e. the lowest possible
+(negative) value for this label.
+
+* [[is_max]]`MAX`:
++
+Matches approvals that a maximal score, i.e. the highest possible
+(positive) value for this label.
+
+* [[is_any]]`ANY`:
++
+Matches any approval when a new patch set is uploaded.
+
+[[is_value]]
+==== is:'VALUE'
+
+Matches approvals that have a voting value that is equal to 'VALUE'.
+
+Negative values need to be quoted, e.g.: is:"-1"
+
+[[approverin]]
+==== approverin:link:#group-id[\{group-id\}]
 
 Matches votes granted by a user who is a member of
-link:rest-api-groups.html#group-id[\{group-id\}].
+link:#group-id[\{group-id\}].
 
-Avoid using a group name with spaces (if it has spaces, use the group uuid).
-Although supported for convenience, it's better to use group uuid than group
-name since using names only works as long as the names are unique (and future
-groups with the same name will break the query).
+[[uploaderin]]
+==== uploaderin:link:#group-id[\{group-id\}]
 
-==== uploaderin:link:rest-api-groups.html#group-id[\{group-id\}]
+Matches all votes if the new patch set was uploaded by a member of
+link:#group-id[\{group-id\}].
 
-Matches votes where the new patch set was uploaded by a member of
-link:rest-api-groups.html#group-id[\{group-id\}].
-
-Avoid using a group name with spaces (if it has spaces, use the group uuid).
-Although supported for convenience, it's better to use group uuid than group
-name since using names only works as long as the names are unique (and future
-groups with the same name will break the query).
-
+[[has_unchanged_files]]
 ==== has:unchanged-files
 
-Matches when the new patch-set includes the same files as the old patch-set.
+Matches when the new patch-set has the same list of files as the
+previous patch-set.
 
-Only 'unchanged-files' is supported for 'has'.
+Votes are not copied in the following cases:
+
+  * If one more files are renamed in the new patch set. These files are counted
+  as a deletion of the file at the old path and an addition of the file at the
+  new path. This means the list of files did change.
+  * If one or more files are reverted to their original content, that is files
+  that become same as in the base revision.
+
+This predicate is useful if you don't want to trigger CI or human
+verification again if the list of files didn't change.
+
+Note, "unchanged-files" is the only value that is supported for the
+"has" operator.
+
+[[group-id]]
+==== Group ID
+
+Some predicates (link:#approverin[approverin], link:#uploaderin[uploaderin])
+expect a group ID as value. This group ID can be any of the
+link:rest-api-groups.html#group-id[group identifiers] that are supported in the
+REST API: group UUID, group ID (for Gerrit internal groups only) and group name
+
+It's preferred to reference groups by UUID, rather than name. Referencing
+groups by name is not recommended because:
+
+* Groups may be renamed and then the group reference can no longer be resolved.
+  If this happens another group with different members can take over the group
+  name, so that exemptions which have been granted by this predicate apply to
+  the other group. This is a security concern.
+* Group names that contain spaces are not supported.
+* Ambiguous group names cannot be resolved. This means if another group with
+  the same name gets created at a later point in time, the group name can no
+  longer be resolved and the predicate breaks.
+
+Using the group UUID has a small drawback though, since it makes the condition
+less human-readable.
 
 ==== Example
 
@@ -320,105 +498,6 @@
 copyCondition = is:MIN OR -change-kind:REWORK OR uploaderin:dead...beef
 ----
 
-[[label_copyMinScore]]
-=== `label.Label-Name.copyMinScore`
-
-If true, the lowest possible negative value for the label is copied
-forward when a new patch set is uploaded. Defaults to false, except
-for All-Projects which has it true by default.
-
-[[label_copyMaxScore]]
-=== `label.Label-Name.copyMaxScore`
-
-If true, the highest possible positive value for the label is copied
-forward when a new patch set is uploaded. This can be used to enable
-sticky approvals, reducing turn-around for trivial cleanups prior to
-submitting a change. Defaults to false.
-
-[[label_copyAllScoresIfListOfFilesDidNotChange]]
-=== `label.Label-Name.copyAllScoresIfListOfFilesDidNotChange`
-
-This policy is useful if you don't want to trigger CI or human
-verification again if the list of files didn't change.
-
-If true, all scores for the label are copied forward when a new
-patch-set is uploaded that has the same list of files as the previous
-patch-set.
-
-Renames are considered different files when computing whether new files
-were added or old files were deleted. Hence, if there are renames, scores will
-*NOT* be copied over.
-
-Defaults to false.
-
-[[label_copyAllScoresOnMergeFirstParentUpdate]]
-=== `label.Label-Name.copyAllScoresOnMergeFirstParentUpdate`
-
-This policy is useful if you don't want to trigger CI or human
-verification again if your target branch moved on but the feature
-branch being merged into the target branch did not change. It only
-applies if the patch set is a merge commit.
-
-If true, all scores for the label are copied forward when a new
-patch set is uploaded that is a new merge commit which only
-differs from the previous patch set in its first parent, or has
-identical parents. The first parent would be the parent of the merge
-commit that is part of the change's target branch, whereas the other
-parent(s) refer to the feature branch(es) to be merged.
-
-Defaults to false.
-
-[[label_copyAllScoresOnTrivialRebase]]
-=== `label.Label-Name.copyAllScoresOnTrivialRebase`
-
-If true, all scores for the label are copied forward when a new patch set is
-uploaded that is a trivial rebase. A new patch set is considered to be trivial
-rebase if the commit message is the same as in the previous patch set and if it
-has the same diff (including context lines) as the previous patch set. This is
-the case if the change was rebased onto a different parent and that rebase did
-not require git to perform any conflict resolution, or if the parent did not
-change at all.
-
-This can be used to enable sticky approvals, reducing turn-around for
-trivial rebases prior to submitting a change.
-For the pre-installed Code-Review label this is enabled by default.
-
-Defaults to false.
-
-[[label_copyAllScoresIfNoCodeChange]]
-=== `label.Label-Name.copyAllScoresIfNoCodeChange`
-
-If true, all scores for the label are copied forward when a new patch set is
-uploaded that has the same parent tree as the previous patch set and the same
-code diff (including context lines) as the previous patch set. This means only
-the commit message is different; the change hasn't even been rebased. This can
-be used to enable sticky approvals on labels that only depend on the code,
-reducing turn-around if only the commit message is changed prior to submitting a
-change. For the Verified label that is optionally installed by the
-link:pgm-init.html[init] site program this is enabled by default.
-
-Defaults to false.
-
-[[label_copyAllScoresIfNoChange]]
-=== `label.Label-Name.copyAllScoresIfNoChange`
-
-If true, all scores for the label are copied forward when a new patch
-set is uploaded that has the same parent tree, code delta, and commit
-message as the previous patch set. This means that only the patch
-set SHA-1 is different. This can be used to enable sticky
-approvals, reducing turn-around for this special case.
-It is recommended to leave this enabled for both Verified and
-Code-Review labels.
-
-Defaults to true.
-
-[[label_copyValue]]
-=== `label.Label-Name.copyValue`
-
-Value that should be copied forward when a new patch set is uploaded.
-This can be used to enable sticky votes. Can be specified multiple
-times. By default not set.
-
 [[label_canOverride]]
 === `label.Label-Name.canOverride`
 
@@ -457,7 +536,7 @@
 `${shardeduserid}`.
 
 [[label_ignoreSelfApproval]]
-=== `label.Label-Name.ignoreSelfApproval`
+=== `label.Label-Name.ignoreSelfApproval (deprecated)`
 
 If true, the label may be voted on by the uploader of the latest patch set,
 but their approval does not make a change submittable. Instead, a
@@ -465,6 +544,13 @@
 
 Defaults to false.
 
+The `ignoreSelfApproval` attribute is **deprecated**, favour
+using link:config-submit-requirements.html[submit requirements] and
+define the `submittableIf` expression with the `label` operator and
+the `user=non_uploader` argument. See the
+link:config-submit-requirements.html#code-review-example[Code Review] submit
+requirement example.
+
 [[label_example]]
 === Example
 
diff --git a/Documentation/config-mail.txt b/Documentation/config-mail.txt
index 85006dc..8bd5dc7 100644
--- a/Documentation/config-mail.txt
+++ b/Documentation/config-mail.txt
@@ -20,6 +20,11 @@
 example template to an equivalently named file without the `.example` extension
 and modifying it will allow an administrator to customize the template.
 
+[NOTE]
+The content of the templates at `'$site_path'/etc/mail/.*\.soy` are cached at
+startup by Gerrit. If they are modified Gerrit needs to be restarted before the
+changes takes effect.
+
 == Supported Mail Templates
 
 Each mail that Gerrit sends out is controlled by at least one template.  These
@@ -221,6 +226,10 @@
 The original subject limited to 72 characters, with an ellipsis if it exceeds
 that.
 
+$change.sizeBucket::
++
+Human-readable size bucket of the current change.
+
 $change.ownerEmail::
 +
 The email address of the owner of the change.
diff --git a/Documentation/config-project-config.txt b/Documentation/config-project-config.txt
index 4dff685..31008f6 100644
--- a/Documentation/config-project-config.txt
+++ b/Documentation/config-project-config.txt
@@ -9,6 +9,7 @@
 relevant in an automation scenario of the access controls.
 
 
+[[refs-meta-config]]
 == The +refs/meta/config+ namespace
 
 The namespace contains three different files that play different
@@ -142,7 +143,7 @@
 [[receive.requireContributorAgreement]]receive.requireContributorAgreement::
 +
 Controls whether or not a user must complete a contributor agreement before
-they can upload changes. Default is `INHERIT`. If `All-Project` enables this
+they can upload changes. Default is `INHERIT`. If `All-Projects` enables this
 option then the dependent project must set it to false if users are not
 required to sign a contributor agreement prior to submitting changes for that
 specific project. To use that feature the global option in `gerrit.config`
@@ -318,17 +319,306 @@
 
 [[content_merge]]submit.mergeContent::
 +
-Defines whether Gerrit will try to
-do a content merge when a path conflict occurs. Valid values are
-'true', 'false', or 'INHERIT'.  Default is 'INHERIT'. This option can
-be modified by any project owner through the project console, `Browse`
-> `Repositories` > my/project > `Allow content merges`.
+Defines whether Gerrit will try to do a content merge when a path conflict
+occurs while submitting a change.
++
+A path conflict occurs when the same file has been changed on both sides of a
+merge, e.g. when the same file has been touched in a change and concurrently in
+the target branch.
++
+Doing a content merge means that Gerrit attempts to merge the conflicting file
+contents from both sides of the merge. This is successful if the touched lines
+(plus some surrounding context lines) do not overlap (i.e. both sides touch
+distinct lines).
++
+NOTE: The content merge setting is not relevant when
+link:#fast_forward_only[fast forward only] is configured as the
+link:#submit.action[submit action] because in this case Gerrit will never
+perform a merge, rebase or cherry-pick on submit.
++
+If content merges are disabled, the submit button in the Gerrit web UI is
+disabled, if any path conflict would occur on submitting the change. Users then
+need to rebase the change manually to resolve the path conflict and then get
+the change re-approved so that they can submit it.
++
+NOTE: If only distinct lines have been touched on both sides, rebasing the
+change from the Gerrit UI is sufficient to resolve the path conflict, since the
+rebase action always does the rebase with content merge enabled.
++
+The advantage of enabling content merges on submit is that it makes it less
+likely that change submissions are rejected due to conflicts. Each change
+submission that goes through with content merge, but would be rejected
+otherwise, saves the user from needing to do extra work to get the change
+submitted (rebase the change, get it re-approved and then submit it again).
++
+On the other hand, disabling content merges decreases the chance of breaking
+branches by submitting content merges of incompatible modifications in the same
+file, e.g. a function is removed on one side and a new usage of that function
+is added on the other side. Note, that the chance of breaking a branch by
+incompatible modifications is only reduced, but not eliminated, e.g. even with
+content merges disabled it's possible that a function is removed in one file
+and a new usage of that function is added in another file.
++
+The huge drawback of disabling content merge is that users need to do extra
+work when a change isn't submittable due to a path conflict which could be
+avoided if content merge was enabled (see above). In addition to this, it also
+confuses and frustrates users if a change submission is rejected by Gerrit due
+to a path conflict, but then when they rebase the change manually they do not
+see any conflict (because manual rebases are always done with content merge
+enabled).
++
+In general, the stability gain of disabling content merges is not worth the
+overhead and confusion that this adds for users, which is why disabling content
+merges is highly discouraged.
++
+Valid values are `true`, `false`, or `INHERIT`.
++
+The default is `INHERIT`.
++
+NOTE: Project owners can also set this option in the Gerrit UI:
+`Browse` > `Repositories` > my/repository > `Allow content merges`.
 
 [[submit.action]]submit.action::
 +
-Defines the link:#submit-type[submit type].  Valid
-values are 'fast forward only', 'merge if necessary', 'rebase if necessary',
-'rebase always', 'merge always' and 'cherry pick'.  The default is 'merge if necessary'.
+Defines the submit action aka submit type aka submit strategy that Gerrit
+should use to integrate changes into the target branch when they are submitted.
++
+In general, submitting a change only merges the change if all its dependencies
+are also submitted. The only exception is the `cherry pick` submit action which
+ignores dependencies and hence is not recommended to be used (see
+link:#cherry_pick[below]).
++
+[[submit-type]]
+--
+The following submit actions are supported:
+
+[[merge_if_necessary]]
+* 'merge if necessary':
++
+With this action, when a change is being submitted, Gerrit fast-forwards the
+target branch if possible, and otherwise creates a merge commit automatically.
++
+A fast-forward is possible if the commit that represents the current patch set
+of the change has the current head of the target branch in its parent lineage.
++
+If a fast-forward is not possible, Gerrit automatically creates a merge commit
+that merges the current patch set of the change into the target branch and then
+the target branch is fast-forwarded to the merge commit.
++
+The behavior of this submit action is identical with the classical `git merge`
+behavior, or
+link:https://git-scm.com/docs/git-merge#Documentation/git-merge.txt---ff[git
+merge --ff].
++
+With this submit action the commits that have been reviewed and approved are
+retained in the git history of the target branch. This means, by looking at the
+history of the target branch, you can see for all commits when they were
+originally committed and on which parent commit they were originally based.
+
+[[always_merge]]
+[[merge_always]]
+* 'merge always':
++
+With this action, when a change is being submitted, Gerrit always creates a
+merge commit, even if a fast-forward is possible.
++
+This is identical to the behavior of
+link:https://git-scm.com/docs/git-merge#Documentation/git-merge.txt---no-ff[git merge --no-ff].
++
+With this submit action the commits that have been reviewed and approved are
+retained in the git history of the target branch. This means, by looking at the
+history of the target branch, you can see for all commits when they were
+originally committed and on which parent commit they were originally based. In
+addition, from the merge commits you can see when the changes were submitted
+and it's possible to follow submissions with `git log --first-parent`.
+
+[[rebase_if_necessary]]
+* 'rebase if necessary':
++
+With this action, when a change is being submitted, Gerrit fast-forwards the
+target branch if possible, and otherwise does a rebase automatically.
++
+A fast-forward is possible if the commit that represents the current patch set
+of the change has the current head of the target branch in its parent lineage.
++
+If a fast-forward is not possible, Gerrit automatically rebases the current
+patch set of the change on top of the current head of the target branch and
+then the target branch is fast-forwarded to the rebased commit.
++
+With this submit action, when a rebase is performed, the original commits that
+have been reviewed and approved do not become part of the target branch's
+history. This means the information on when the original commits were committed
+and on which parent they were based is not retained in the branch history.
++
+Using this submit action results in a linear history of the target branch,
+unless merge commits are being submitted. For some people this is an advantage
+since they find the linear history easier to read.
++
+NOTE: Rebasing merge commits is not supported. If a change with a merge commit
+is submitted the link:#merge_if_necessary[merge if necessary] submit action is
+applied.
++
+When rebasing the patchset, Gerrit automatically appends onto the end of the
+commit message a short summary of the change's approvals, and a URL link back
+to the change in the web UI (see link:#submit-footers[below]). If a fast-forward
+is done no footers are added.
+
+[[rebase_always]]
+* 'rebase always':
++
+With this action, when a change is being submitted, Gerrit always does a
+rebase, even if a fast-forward is possible.
++
+With this submit action, the original commits that have been reviewed and
+approved do not become part of the target branch's history. This means the
+information on when the original commits were committed and on which parent
+they were based is not retained in the branch history.
++
+Using this submit action results in a linear history of the target branch,
+unless merge commits are being submitted. For some people this is an advantage
+since they find the linear history easier to read.
++
+NOTE: Rebasing merge commits is not supported. If a change with a merge commit
+is submitted the link:#merge_if_necessary[merge if necessary] submit action is
+applied.
++
+When rebasing the patchset, Gerrit automatically appends onto the end of the
+commit message a short summary of the change's approvals, and a URL link back
+to the change in the web UI (see link:#submit-footers[below]).
++
+The footers that are added are exactly the same footers that are also added by
+the link:cherry_pick[cherry pick] action. Thus, the `rebase always` action can
+be considered similar to the `cherry pick` action, but with the important
+distinction that `rebase always` does not ignore dependencies, which is why
+using the `rebase always` action should be preferred over the `cherry pick`
+submit action.
+
+[[fast_forward_only]]
+* 'fast forward only' (usage generally not recommended):
++
+With this action a change can only be submitted if at submit time the target
+branch can be fast-forwarded to the commit that represents the current patch
+set of the change. This means in order for a change to be submittable its
+current patch set must have the current head of the target branch in its parent
+lineage.
++
+The advantage of using this action is that the target branch is always updated
+to the exact commit that has been reviewed and approved. In particular, if CI
+verification is configured, this means that the CI verified the exact commit to
+which the target branch is being fast-forwarded on submit (assuming no approval
+copying is configured for CI votes).
++
+The huge drawback of using this action is that whenever one change is submitted
+all other open changes for the same branch, that are not successors of the
+submitted change, become non-submittable, since the target branch can no longer
+be fast-forwarded to their current patch sets. Making these changes submittable
+again requires rebasing and re-approving/re-verifying them. For most projects
+this causes an unreasonable amount of overhead that doesn't justify the
+stability gain by verifying exact commits so that using this submit action is
+generally discouraged. Using this action should only be considered for projects
+that have a low frequency of changes and that have special requirements to
+never break any target branch.
++
+NOTE: With this submit action Gerrit does not create merge commits on
+submitting a change, but merge commits that are created on the client, prior to
+uploading to Gerrit for review, may still be submitted.
+
+[[cherry_pick]]
+* 'cherry pick' (usage not recommended, use link:#rebase_always[rebase always]
+instead):
++
+With this submit action Gerrit always performs a cherry pick of the current
+patch set when a change is submitted. This ignores the parent lineage and
+instead creates a brand new commit on top of the current head of the target
+branch. The submitter becomes the committer of the new commit and the original
+commit author is retained.
++
+Ignoring change dependencies on submit is often confusing for users. For users
+that stack changes on top of each other, it's unexpected that these
+dependencies are ignored on submit. Ignoring dependencies also means that
+submitters need to submit the changes individually in the correct order.
+Otherwise it's likely that submissions fail due to conflicts or that the
+target branch gets broken (because it contains the submitted change, but not
+its predecessors which may be required for the submitted change to work
+correctly).
++
+If link:config-gerrit.html#change.submitWholeTopic[`change.submitWholeTopic`]
+is enabled changes that have the same topic are submitted together, the same as
+with all other submit actions. This means by setting the same topic on all
+dependent changes it's possible to submit a stack of changes together and
+overcome the limitation that change dependencies are ignored.
++
+When cherry picking the patchset, Gerrit automatically appends onto the end of
+the commit message a short summary of the change's approvals, and a URL link
+back to the change in the web UI (see link:#submit-footers[below]).
++
+Using this submit action is not recommended because it ignores change
+dependencies, instead link:#rebase_always[rebase always] should be used which
+behaves the same way except that it respects change dependencies (in particular
+`rebase always` adds the same kind of footers to the merged commit as
+`cherry pick`).
+
+--
++
+[[submit_type_inherit]]
+In addition the submit action can be set to `Inherit`, which means that the
+value that is configured in the parent project applies. For new projects
+`Inherit` is the default, unless the default is overridden by the global
+link:config-gerrit.html#repository.name.defaultSubmitType[defaultSubmitType]
+configuration. Configuring `Inherit` for the `All-Projects` root project is
+equivalent to configuring link:#merge_if_necessary[merge if necessary].
++
+If `submit.action` is not set, the default is 'merge if necessary'.
++
+NOTE: The different submit actions are also described in the
+link:https://docs.google.com/presentation/d/1C73UgQdzZDw0gzpaEqIC6SPujZJhqamyqO1XOHjH-uk/edit#slide=id.g4d6c16487b_1_800[Gerrit - Concepts and Workflows]
+presentation, where their behavior is visualized by git commit graphs.
++
+NOTE: If Gerrit performs a merge, rebase or cherry-pick as part of the
+change submission (true for all submit actions, except for
+link:#fast_forward_only[fast forward only]), it is controlled by the
+link:#submit.mergeContent[mergeContent] setting whether a content merge is
+performed when there is a path conflict.
++
+NOTE: If Gerrit performs a merge, rebase or cherry-pick as part of the
+change submission (true for all submit actions, except for
+link:#fast_forward_only[fast forward only]), it can be that trying to submit
+a change would fail due to Git conflicts (if the same lines were modified
+concurrently, or if link:#submit.mergeContent[mergeContent] is disabled also if
+the same files were modified concurrently). In this case the submit button in
+the Gerrit web UI is disabled and a tooltip on the disabled submit button
+informs about the change being non-mergeable.
++
+[[submit-footers]]
+--
+NOTE: If Gerrit performs a rebase or cherry-pick as part of the change
+submission (true for link:#rebase_if_necessary[rebase if necessary],
+link:#rebase_always[rebase always] and link:#cherry_pick[cherry pick]) Gerrit
+inserts additional footers into the commit message of the newly created
+commit: +
+ +
+* `Change-Id: <change-id>` (only if this footer is not already present, see
+  link:user-changeid.html[Change-Id]) +
+* `Reviewed-on: <change-url>` (links to the change in Gerrit where this commit
+  was reviewed) +
+* `Reviewed-by: <reviewer>` (once for every reviewer with a positive
+  `Code-Review` vote) +
+* `Tested-by: <reviewer>` (once for every reviewer with a positive `Verified`
+  vote) +
+* `<label-name>: <reviewer>` (once for every reviewer with a positive vote on
+  any other label) +
+ +
+In addition, plugins that implement a
+link:dev-plugins.html#change-message-modifier[Change Message Modifier] may add
+additional custom footers.
+--
++
+NOTE: For the value of `submit.action` in `project.config` use the exact
+spelling as given above, e.g. 'merge if necessary' (without the single quotes,
+but with the spaces).
++
+NOTE: Project owners can also set the submit action in the Gerrit UI:
+`Browse` > `Repositories` > my/repository > `Submit type`
 
 [[submit.matchAuthorToCommitterDate]]submit.matchAuthorToCommitterDate::
 +
@@ -346,98 +636,6 @@
 is set to 'true' the merge would fail in such a case. An empty commit is still allowed as the
 initial commit on a branch.
 
-[[submit-type]]
-==== Submit Type
-
-'submit.action': The method Gerrit uses to submit a change to a project.
-
-The submit type can also be modified by any project owner through the
-project console, `Browse` > `Repositories` > my/project > 'Submit type'.
-In general, a submitting a change only merges the change if all its
-dependencies are also submitted, with exceptions documented below.
-
-The following submit types are supported:
-
-[[submit_type_inherit]]
-* Inherit
-+
-This is the default for new projects, unless overridden by a global
-link:config-gerrit.html#repository.name.defaultSubmitType[`defaultSubmitType` option].
-+
-Inherit the submit type from the parent project. In `All-Projects`, this
-is equivalent to link:#merge_if_necessary[Merge If Necessary].
-
-[[fast_forward_only]]
-* Fast Forward Only
-+
-With this method Gerrit does not create merge commits on submitting a
-change. Merge commits may still be submitted, but they must be created
-on the client prior to uploading to Gerrit for review.
-+
-To submit a change, the change must be a strict superset of the
-destination branch.  That is, the change must already contain the
-tip of the destination branch at submit time.
-
-[[merge_if_necessary]]
-* Merge If Necessary
-+
-If the change being submitted is a strict superset of the destination
-branch, then the branch is fast-forwarded to the change.  If not,
-then a merge commit is automatically created.  This is identical
-to the classical `git merge` behavior, or `git merge --ff`.
-
-[[always_merge]]
-* Always Merge
-+
-Always produce a merge commit, even if the change is a strict
-superset of the destination branch.  This is identical to the
-behavior of `git merge --no-ff`, and may be useful if the
-project needs to follow submits with `git log --first-parent`.
-
-[[cherry_pick]]
-* Cherry Pick
-+
-Always cherry pick the patch set, ignoring the parent lineage
-and instead creating a brand new commit on top of the current
-branch head.
-+
-When cherry picking a change, Gerrit automatically appends onto the
-end of the commit message a short summary of the change's approvals,
-and a URL link back to the change on the web.  The committer header
-is also set to the submitter, while the author header retains the
-original patch set author.
-+
-Note that Gerrit ignores dependencies between changes when using this
-submit type unless
-link:config-gerrit.html#change.submitWholeTopic[`change.submitWholeTopic`]
-is enabled and depending changes share the same topic. So generally
-submitters must remember to submit changes in the right order when using this
-submit type. If all you want is extra information in the commit message,
-consider using the Rebase Always submit strategy.
-
-[[rebase_if_necessary]]
-* Rebase If Necessary
-+
-If the change being submitted is a strict superset of the destination
-branch, then the branch is fast-forwarded to the change.  If not,
-then the change is automatically rebased and then the branch is
-fast-forwarded to the change.
-+
-When Gerrit tries to do a merge, by default the merge will only
-succeed if there is no path conflict.  A path conflict occurs when
-the same file has also been changed on the other side of the merge.
-
-[[rebase_always]]
-* Rebase Always
-+
-Basically, the same as Rebase If Necessary, but it creates a new patchset even
-if fast forward is possible AND like Cherry Pick it ensures footers such as
-Change-Id, Reviewed-On, and others are present in resulting commit that is
-merged.
-+
-Thus, Rebase Always can be considered similar to Cherry Pick, but with
-the important distinction that Rebase Always does not ignore dependencies.
-
 
 [[access-section]]
 === Access section
@@ -481,6 +679,12 @@
 
 Please refer to link:config-labels.html#label_custom[Custom Labels] documentation.
 
+[[submit-requirement-section]]
+=== Submit Requirement section
+
+Please refer to link:config-submit-requirements.html[Configuring Submit
+Requirements] documentation.
+
 [[branchOrder-section]]
 === branchOrder section
 
@@ -526,7 +730,16 @@
 A boolean indicating if reviewers and CCs that do not currently have a Gerrit
 account can be added to a change by providing their email address.
 +
-This setting only takes affect for changes that are readable by anonymous users.
+This setting only takes effect for changes that are readable by anonymous users.
++
+Default is `INHERIT`, which means that this property is inherited from
+the parent project. If the property is not set in any parent project, the
+default value is `FALSE`.
+
+[[reviewer.skipAddingAuthorAndCommitterAsReviewers]]reviewer.skipAddingAuthorAndCommitterAsReviewers::
++
+Whether to skip adding the Git commit author and committer as reviewers for
+a new change.
 +
 Default is `INHERIT`, which means that this property is inherited from
 the parent project. If the property is not set in any parent project, the
diff --git a/Documentation/config-submit-requirements.txt b/Documentation/config-submit-requirements.txt
new file mode 100644
index 0000000..fb37287
--- /dev/null
+++ b/Documentation/config-submit-requirements.txt
@@ -0,0 +1,347 @@
+= Gerrit Code Review - Submit Requirements
+
+As part of the code review process, project owners need to configure rules that
+govern when changes become submittable. For example, an admin might want to
+prevent changes from being submittable until at least a “+2” vote on the
+“Code-Review” label is granted on a change. Admins can define submit
+requirements to enforce submittability rules for changes.
+
+[[configuring_submit_requirements]]
+== Configuring Submit Requirements
+
+Site administrators and project owners can define submit requirements in the
+link:config-project-config.html[project config]. A submit requirement has the
+following fields:
+
+
+[[submit_requirement_name]]
+=== submit-requirement.Name
+
+A name that uniquely identifies the submit requirement. Submit requirements
+can be overridden in child projects if they are defined with the same name in
+the child project. See the link:#inheritance[inheritance] section for more
+details.
+
+[[submit_requirement_description]]
+=== submit-requirement.Name.description
+
+A detailed description of what the submit requirement is supposed to do. This
+field is optional. The description is visible to the users in the change page
+upon hovering on the submit requirement to help them understand what the
+requirement is about and how it can be fulfilled.
+
+[[submit_requirement_applicable_if]]
+=== submit-requirement.Name.applicableIf
+
+A link:#query_expression_syntax[query expression] that determines if the submit
+requirement is applicable for a change. For example, administrators can exclude
+submit requirements for certain branch patterns. See the
+link:#exempt-branch-example[exempt branch] example.
+
+Often submit requirements should only apply to branches that contain source
+code. In this case this parameter can be used to exclude the
+link:config-project-config.html#refs-meta-config[refs/meta/config] branch from
+a submit requirement:
+
+----
+  applicableIf = -branch:refs/meta/config
+----
+
+This field is optional, and if not specified, the submit requirement is
+considered applicable for all changes in the project.
+
+[[submit_requirement_submittable_if]]
+=== submit-requirement.Name.submittableIf
+
+A link:#query_expression_syntax[query expression] that determines when the
+change becomes submittable. This field is mandatory.
+
+
+[[submit_requirement_override_if]]
+=== submit-requirement.Name.overrideIf
+
+A link:#query_expression_syntax[query expression] that controls when the
+submit requirement is overridden. When this expression is evaluated to true,
+the submit requirement state becomes `OVERRIDDEN` and the submit requirement
+is no longer blocking the change submission.
+This expression can be used to enable bypassing the requirement in some
+circumstances, for example if the change owner is a power user or to allow
+change submission in case of emergencies. +
+
+This field is optional.
+
+[[submit_requirement_can_override_in_child_projects]]
+=== submit-requirement.Name.canOverrideInChildProjects
+
+A boolean (true, false) that determines if child projects can override the
+submit requirement. +
+
+The default value is `false`.
+
+[[evaluation_results]]
+== Evaluation Results
+
+When submit requirements are configured, their results are returned for all
+changes requested by the REST API with the
+link:rest-api-changes.html#submit-requirement-result-info[SubmitRequirementResultInfo]
+entity. +
+
+Submit requirement results are produced from the evaluation of the submit
+requirements in the project config (
+See link:#configuring_submit_requirements[Configuring Submit Requirements])
+as well as the conversion of the results of the legacy submit rules to submit
+requirement results. Legacy submit rules are label functions
+(see link:config-labels.html[config labels]), custom and
+link:prolog-cookbook.html[prolog] submit rules.
+
+The `status` field can be one of:
+
+* `NOT_APPLICABLE`
++
+The link:#submit_requirement_applicable_if[applicableIf] expression evaluates
+to false for the change.
+
+* `UNSATISFIED`
++
+The submit requirement is applicable
+(link:#submit_requirement_applicable_if[applicableIf] evaluates to true), but
+the evaluation of the link:#submit_requirement_submittable_if[submittableIf] and
+link:#submit_requirement_override_if[overrideIf] expressions return false for
+the change.
+
+* `SATISFIED`
++
+The submit requirement is applicable
+(link:#submit_requirement_applicable_if[applicableIf] evaluates to true), the
+link:#submit_requirement_submittable_if[submittableIf] expression evaluates to
+true, and the link:#submit_requirement_override_if[overrideIf] evaluates to
+false for the change.
+
+* `OVERRIDDEN`
++
+The submit requirement is applicable
+(link:#submit_requirement_applicable_if[applicableIf] evaluates to true) and the
+link:#submit_requirement_override_if[overrideIf] expression evaluates to true.
++
+Note that in this case, the change is overridden whether the
+link:#submit_requirement_submittable_if[submittableIf] expression evaluates to
+true or not.
+
+* `BYPASSED`
++
+The change was merged directly bypassing code review by supplying the
+link:user-upload.html#auto_merge[submit] push option while doing a git push.
+
+* `ERROR`
++
+The evaluation of any of the
+link:#submit_requirement_applicable_if[applicableIf],
+link:#submit_requirement_submittable_if[submittableIf] or
+link:#submit_requirement_override_if[overrideIf] expressions resulted in an
+error.
+
+
+[[query_expression_syntax]]
+== Query Expression Syntax
+
+All applicableIf, submittableIf and overrideIf expressions use the same syntax
+and operators available for link:user-search.html[searching changes]. In
+addition to that, submit requirements support extra operators.
+
+
+[[submit_requirements_operators]]
+=== Submit Requirements Operators
+
+[[operator_authoremail]]
+authoremail:'EMAIL_PATTERN'::
++
+An operator that returns true if the change author's email address matches a
+specific regular expression pattern. The
+link:http://www.brics.dk/automaton/[dk.brics.automaton library,role=external,window=_blank]
+is used for the evaluation of such patterns.
+
+[[operator_distinctvoters]]
+distinctvoters:'[Label1,Label2,...,LabelN],value=MAX,count>1'::
++
+An operator that allows checking for distinct voters across more than one label.
++
+2..N labels are supported, filtering by a value (MIN,MAX,integer) is optional.
+Count is mandatory.
++
+Examples:
+`distinctvoters:[Code-Review,Trust],value=MAX,count>1`
++
+`distinctvoters:[Code-Review,Trust,API-Review],count>2`
+
+[[operator_is_true]]
+is:true::
++
+An operator that always returns true for all changes. An example usage is to
+redefine a submit requirement in a child project and make the submit requirement
+always applicable.
+
+[[operator_is_false]]
+is:false::
++
+An operator that always returns false for all changes. An example usage is to
+redefine a submit requirement in a child project and make the submit requirement
+always non-applicable.
+
+[[operator_has_submodule_update]]
+has:submodule-update::
++
+An operator that returns true if the diff of the latest patchset against the
+default parent has a submodule modified file, that is, a ".gitmodules" or a
+git link file.
++
+The optional `base` parameter can also be supplied for merge commits like
+`has:submodule-update,base=1`, or `has:submodule-update,base=2`. In these cases,
+the operator returns true if the diff of the latest patchset against parent
+number identified by `base` has a submodule modified file. Note that the
+operator will return false if the base parameter is greater than the number of
+parents for the latest patchset for the change.
+
+[[operator_file]]
+file:"'<filePattern>',withDiffContaining='<contentPattern>'"::
++
+An operator that returns true if the latest patchset contained a modified file
+matching `<filePattern>` with a modified region matching `<contentPattern>`.
+
+[[unsupported_operators]]
+=== Unsupported Operators
+
+Some operators are not supported with submit requirement expressions.
+
+[[operator_is_submittable]]
+is:submittable::
++
+Cannot be used since it will result in recursive evaluation of expressions.
+
+[[inheritance]]
+== Inheritance
+
+Child projects can override a submit requirement defined in any of their parent
+projects. Overriding a submit requirement overrides all of its properties and
+values. The overriding project needs to define all mandatory fields.
+
+Submit requirements are looked up from the current project up the inheritance
+hierarchy to “All-Projects”. The first project in the hierarchy chain that sets
+link:#submit_requirement_can_override_in_child_projects[canOverrideInChildProjects]
+to false prevents all descendant projects from overriding it.
+
+If a project disallows a submit requirement from being overridden in child
+projects, all definitions of this submit requirement in descendant projects are
+ignored.
+
+To remove a submit requirement in a child project, administrators can redefine
+the requirement with the same name in the child project and set the
+link:#submit_requirement_applicable_if[applicableIf] expression to `is:false`.
+Since the link:#submit_requirement_submittable_if[submittableIf] field is
+mandatory, administrators need to provide it in the child project but can set it
+to anything, for example `is:false` but it will have no effect anyway.
+
+
+[[trigger-votes]]
+== Trigger Votes
+
+Trigger votes are label votes that are not associated with any submit
+requirement expressions. Trigger votes are displayed in a separate section in
+the change page. For more about configuring labels, see the
+link:config-labels.html[config labels] documentation.
+
+
+[[examples]]
+== Examples
+
+[[code-review-example]]
+=== Code-Review Example
+
+To define a submit requirement for code-review that requires a maximum vote for
+the “Code-Review” label from a non-uploader without a maximum negative vote:
+
+----
+[submit-requirement "Code-Review"]
+	description = A maximum vote from a non-uploader is required for the \
+	              'Code-Review' label. A minimum vote is blocking.
+	submittableIf = label:Code-Review=MAX,user=non_uploader AND -label:Code-Review=MIN
+	canOverrideInChildProjects = true
+----
+
+[[exempt-branch-example]]
+=== Exempt a branch Example
+
+We could exempt a submit requirement from certain branches. For example,
+project owners might want to skip the 'Code-Style' requirement from the
+refs/meta/config branch.
+
+----
+[submit-requirement "Code-Style"]
+  description = Code is properly styled and formatted
+  applicableIf = -branch:refs/meta/config
+  submittableIf = label:Code-Style=+1 AND -label:Code-Style=-1
+  canOverrideInChildProjects = true
+----
+
+Branch configuration supports regular expressions as well, e.g. to exempt 'refs/heads/release/*' pattern,
+when migrating from the label Submit-Rule:
+
+----
+[label "Verified"]
+  branch = refs/heads/release/*
+----
+
+The following SR can be configured:
+
+----
+[submit-requirement "Verified"]
+  submittableIf = label:Verified=MAX AND -label:Verified=MIN
+  applicableIf = branch:^refs/heads/release/.*
+----
+
+
+[[test-submit-requirements]]
+== Testing Submit Requirements
+
+The link:rest-api-changes.html#check-submit-requirement[Check Submit Requirement]
+change endpoint can be used to test submit requirements on any change. Users
+are encouraged to test submit requirements before adding them to the project
+to ensure they work as intended.
+
+.Request
+----
+  POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/check.submit_requirement HTTP/1.0
+  Content-Type: application/json; charset=UTF-8
+
+    {
+      "name": "Code-Review",
+      "submittability_expression": "label:Code-Review=+2"
+    }
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "name": "Code-Review",
+    "status": "SATISFIED",
+    "submittability_expression_result": {
+      "expression": "label:Code-Review=+2",
+      "fulfilled": true,
+      "passingAtoms": [
+        "label:Code-Review=+2"
+      ]
+    },
+    "is_legacy": false
+  }
+----
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/config-themes.txt b/Documentation/config-themes.txt
index a83c747..73cfc55 100644
--- a/Documentation/config-themes.txt
+++ b/Documentation/config-themes.txt
@@ -4,11 +4,12 @@
 the browser, allowing organizations to alter the look and
 feel of the application to fit with their general scheme.
 
-== HTML Header/Footer and CSS
+== HTML Header/Footer and CSS for login screens
 
-The HTML header, footer and CSS may be customized for login
-screens (LDAP, OAuth, OpenId) and the internally managed
-Gitweb servlet.
+The HTML header, footer, and CSS may be customized for login screens (LDAP,
+OAuth, OpenId) and the internally managed Gitweb servlet. See
+link:pg-plugin-dev.html[JavaScript Plugin Development and API] for documentation
+on modifying styles for the rest of Gerrit (not login screens).
 
 At startup Gerrit reads the following files (if they exist) and
 uses them to customize the HTML page it sends to clients:
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index c3237ed..d1a5bcf 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -18,7 +18,7 @@
 To build Gerrit from source, you need:
 
 * A Linux or macOS system (Windows is not supported at this time)
-* A JDK for Java 8|11|...
+* A JDK for Java 11 or Java 17
 * Python 3
 * link:https://github.com/nodesource/distributions/blob/master/README.md[Node.js (including npm),role=external,window=_blank]
 * Bower (`npm install -g bower`)
@@ -48,73 +48,30 @@
 
 `java -version`
 
-[[java-8]]
-==== Java 8 support (deprecated)
-
-Java 8 is a legacy Java release and support for Java 8 will be discontinued
-in future gerrit releases. To build Gerrit with Java 8 language level, run:
-
-```
-  $ bazel build --java_toolchain //tools:error_prone_warnings_toolchain :release
-```
-
 [[java-11]]
 ==== Java 11 support
 
-Java language level 11 is the default. To build Gerrit with Java 11 language
-level, run:
+To build Gerrit with Java 11 language level, run:
 
 ```
-  $ bazel build :release
+  $ bazel build --java_toolchain=//tools:error_prone_warnings_toolchain_java11 :release
 ```
 
-[[java-13]]
-==== Java 13 support
+[[java-17]]
+==== Java 17 support
 
-Java 13 (and newer) is supported through vanilla java toolchain
-link:https://docs.bazel.build/versions/master/toolchains.html[Bazel option,role=external,window=_blank].
-To build Gerrit with Java 13 and newer, specify vanilla java toolchain and
-provide the path to JDK home:
+Java 17 is supported. To build Gerrit with Java 17, run:
 
 ```
-  $ bazel build \
-    --define=ABSOLUTE_JAVABASE=<path-to-java-13> \
-    --javabase=@bazel_tools//tools/jdk:absolute_javabase \
-    --host_javabase=@bazel_tools//tools/jdk:absolute_javabase \
-    --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla \
-    --java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla \
-    :release
+  $ bazel build --config=java17 :release
 ```
 
-To run the tests, `--javabase` option must be passed as well, because
-bazel test runs the test using the target javabase:
+To run the tests with Java 17, run:
 
 ```
-  $ bazel test \
-    --define=ABSOLUTE_JAVABASE=<path-to-java-13> \
-    --javabase=@bazel_tools//tools/jdk:absolute_javabase \
-    --host_javabase=@bazel_tools//tools/jdk:absolute_javabase \
-    --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla \
-    --java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla \
-    //...
+  $ bazel test --config=java17 //...
 ```
 
-To avoid passing all those options on every Bazel build invocation,
-they could be added to ~/.bazelrc resource file:
-
-```
-$ cat << EOF > ~/.bazelrc
-build --define=ABSOLUTE_JAVABASE=<path-to-java-13>
-build --javabase=@bazel_tools//tools/jdk:absolute_javabase
-build --host_javabase=@bazel_tools//tools/jdk:absolute_javabase
-build --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla
-build --java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla
-EOF
-```
-
-Now, invoking Bazel with just `bazel build :release` would include
-all those options.
-
 === Node.js and npm packages
 See link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/README.md#installing-node_js-and-npm-packages[Installing Node.js and npm packages,role=external,window=_blank].
 
@@ -268,7 +225,15 @@
   bazel-bin/Documentation/searchfree.zip
 ----
 
-To build the executable WAR with the documentation included:
+To generate HTML files skipping the zip archiving:
+
+----
+  bazel build Documentation
+----
+
+And open `bazel-bin/Documentation/index.html`.
+
+To build the Gerrit executable WAR with the documentation included:
 
 ----
   bazel build withdocs
@@ -317,18 +282,6 @@
   bazel test //javatests/com/google/gerrit/acceptance/rest/account:rest_account
 ----
 
-To run SSH tests using JSch ssh client:
-
-----
-  bazel test --test_env=SSH_CLIENT_IMPLEMENTATION=JSCH //...
-----
-
-To run SSH tests using Apache MINA ssh client:
-
-----
-  bazel test --test_env=SSH_CLIENT_IMPLEMENTATION=APACHE //...
-----
-
 To run only tests that do not use SSH:
 
 ----
@@ -341,12 +294,6 @@
   bazel test --test_tag_filters=-flaky //...
 ----
 
-To exclude tests that require a Docker host:
-
-----
-  bazel test --test_tag_filters=-docker //...
-----
-
 To exclude tests that require very recent git client version:
 
 ----
@@ -370,15 +317,11 @@
   bazel test --test_env=GERRIT_INDEX_TYPE=LUCENE //...
 ----
 
-Elastic search is not currently supported in integration tests.
-
 The following values are currently supported for the group name:
 
 * annotation
 * api
-* docker
 * edit
-* elastic
 * git
 * git-protocol-v2
 * git-upload-archive
@@ -411,23 +354,6 @@
 Now attach with a debugger to the port `5005`. For example use "Remote Java Application" launch
 configuration in Eclipe and specify the port `5005`.
 
-[[elasticsearch]]
-=== Elasticsearch
-
-Successfully running the Elasticsearch tests requires Docker, and
-may require setting the local virtual memory on
-link:https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html[linux,role=external,window=_blank] and
-link:https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#_set_vm_max_map_count_to_at_least_262144[macOS,role=external,window=_blank].
-
-On macOS, if using link:https://docs.docker.com/docker-for-mac/[Docker Desktop,role=external,window=_blank],
-the effective memory value can be set in the Preferences, under the Advanced tab.
-The default value usually does not suffice and is causing premature container exits.
-That default is currently 2 GB and should be set to at least 5 (GB).
-
-If Docker is not available, the Elasticsearch tests will be skipped.
-Note that Bazel currently does not show
-link:https://github.com/bazelbuild/bazel/issues/3476[the skipped tests,role=external,window=_blank].
-
 [[logging]]
 === Controlling logging level
 
@@ -681,6 +607,40 @@
     --disk-size=200
 ```
 
+Due to outdated Git version in official RBE docker images, a custom RBE docker
+image must be used. To build custom docker imager, change to the directory
+`tools/platforms` and build and publish custom RBE docker image.
+
+To build the custom RBE docker image, run:
+
+```
+docker build -t gcr.io/api-project-164060093628/rbe-ubuntu18-04 .
+```
+
+To publish the custom RBE docker image, run:
+
+```
+docker push gcr.io/api-project-164060093628/rbe-ubuntu18-04
+[...]
+latest: digest: sha256:de5186d4313630a6111f9a2449b72563d0bc59ec9fb60956f063b69a38a76834 size: 1584
+```
+
+Re-build rbe_autoconfig project conduct a new release and switch to using it
+in `WORKSPACE` file.
+
+Note, to authenticate to the gcr.io registry, the following command must be
+used:
+
+```
+gcloud auth configure-docker
+```
+
+To see the documentation, developer must be added to this group:
+https://groups.google.com/forum/#!forum/rbe-alpha-customers.
+
+Documentation can be found at:
+https://cloud.google.com/remote-build-execution/docs.
+
 To use RBE, execute
 
 ```
diff --git a/Documentation/dev-community.txt b/Documentation/dev-community.txt
index 7488f74..47c0be2 100644
--- a/Documentation/dev-community.txt
+++ b/Documentation/dev-community.txt
@@ -54,7 +54,6 @@
 * link:dev-build-plugins.html[Building Gerrit plugins]
 * link:pg-plugin-dev.html[JavaScript Plugin Development and API]
 * link:config-validation.html[Validation Interfaces]
-* link:dev-stars.html[Starring Changes]
 * link:quota.html[Quota Enforcement]
 
 [[maintainer]]
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
index fcc8b7e..107473a 100644
--- a/Documentation/dev-contributing.txt
+++ b/Documentation/dev-contributing.txt
@@ -24,33 +24,31 @@
 link:dev-roles.html#maintainer[maintainers,role=external,window=_blank]
 review them adhoc.
 
-For large/complex features, it is required to follow the
-link:#design-driven-contribution-process[design-driven contribution
-process] and specify the feature in a link:dev-design-docs.html[design
-doc,role=external,window=_blank] before starting with the implementation.
+For large/complex features, it is required to specify the feature in a
+link:dev-design-docs.html[design document,role=external,window=_blank] before
+starting implementation, as per the
+link:#design-driven-contribution-process[design-driven contribution process].
 
 If link:dev-roles.html#contributor[contributors,role=external,window=_blank]
-choose the lightweight contribution process and during the review it turns out
-that the feature is too large or complex,
-link:dev-roles.html#maintainer[maintainers,role=external,window=_blank] can
-require to follow the design-driven contribution process instead.
+choose the lightweight contribution process but the feature is found to be 
+large or complex, link:dev-roles.html#maintainer[maintainers,role=external,window=_blank]
+can require that the design-driven contribution process be followed instead.
 
 If you are in doubt which process is right for you, consult the
 link:https://groups.google.com/d/forum/repo-discuss[repo-discuss,role=external,window=_blank]
 mailing list.
 
 These contribution processes apply to everyone who contributes code to
-the Gerrit project, including link:dev-roles.html#maintainer[
-maintainers,role=external,window=_blank]. When reading this document, keep in
-mind that maintainers are also contributors when they contribute code.
+the Gerrit project. link:dev-roles.html#maintainer[
+Maintainers,role=external,window=_blank] are also considered contributors
+when they contribute code.
 
 If a new feature is large or complex, it is often difficult to find a
-maintainer who can take the time that is needed for a thorough review,
-and who can help with getting the changes submitted. To avoid that this
-results in unpredictable long waiting times during code review,
-contributors can ask for link:#mentorship[mentor support]. A mentor
-helps with timely code reviews and technical guidance. Doing the
-implementation is still the responsibility of the contributor.
+maintainer who can take the time that is needed for a thorough review. This
+can result in unpredictably long waiting times before the changes are
+submitted. To avoid that, contributors can ask for link:#mentorship[mentor support].
+A mentor helps with timely code reviews and technical guidance, though the 
+implementation itself is still the responsibility of the contributor.
 
 [[comparison]]
 === Quick Comparison
@@ -66,8 +64,8 @@
 |Review  |adhoc (when reviewer is available)|by a dedicated mentor (if
 a link:#mentorship[mentor] was assigned)
 |Caveats |features may get vetoed after the implementation was already
-done, maintainers may make the design-driven contribution process
-required if a change gets too complex/large|design doc must stay open
+done, maintainers may require the design-driven contribution process
+be followed if a change gets too complex/large|design doc must stay open
 for a minimum of 10 calendar days, a mentor may not be available
 immediately
 |Applicable to|documentation updates, bug fixes, small features|
@@ -83,40 +81,32 @@
 link:#design-driven-contribution-process[design-driven contribution
 process] is required.
 
-As Gerrit is a code review tool, naturally contributions will
-be reviewed before they will get submitted to the code base.  To
-start your contribution, please make a git commit and upload it
-for review to the link:https://gerrit-review.googlesource.com/[
-gerrit-review.googlesource.com,role=external,window=_blank] Gerrit server.  To
-help speed up the review of your change, review these link:dev-crafting-changes.html[
+To start contributing to Gerrit, upload your git commit for review to the
+link:https://gerrit-review.googlesource.com/[gerrit-review.googlesource.com,
+role=external,window=_blank] Gerrit server. Review these link:dev-crafting-changes.html[
 guidelines,role=external,window=_blank] before submitting your change.  You can
-view the pending Gerrit contributions and their statuses
-link:https://gerrit-review.googlesource.com/#/q/status:open+project:gerrit[here,role=external,window=_blank].
+view pending contributions link:https://gerrit-review.googlesource.com/#/q/status:open+project:gerrit[here,role=external,window=_blank].
 
 Depending on the size of that list it might take a while for
-your change to get reviewed.  Naturally there are fewer
-link:dev-roles.html#maintainer[maintainers,role=external,window=_blank], that
-can approve changes, than link:dev-roles.html#contributor[contributors,role=external,window=_blank];
-so anything that you can do to ensure that your contribution will undergo fewer
-revisions will speed up the contribution process.  This includes
-helping out reviewing other people's changes to relieve the load from
-the maintainers.  Even if you are not familiar with Gerrit's internals,
+your change to get reviewed. Anything that you can do to ensure that your
+contribution will undergo fewer revisions will speed up the contribution process.
+This includes helping out reviewing other people's changes to relieve the
+load from the maintainers. Even if you are not familiar with Gerrit's internals,
 it would be of great help if you can download, try out, and comment on
-new features.  If it works as advertised, say so, and if you have the
+new features. If it works as advertised, say so, and if you have the
 privileges to do so, go ahead and give it a `+1 Verified`.  If you
 would find the feature useful, say so and give it a `+1 Code Review`.
 
-And finally, the quicker you respond to the comments of your reviewers,
-the quicker your change might get merged!  Try to reply to every
-comment after submitting your new patch, particularly if you decided
-against making the suggested change. Reviewers don't want to seem like
-nags and pester you if you haven't replied or made a fix, so it helps
-them know if you missed it or decided against it.
+Finally, the quicker you respond to the comments of your reviewers, the
+quicker your change can be merged! Try to reply to every comment after
+submitting your new patch, particularly if you decided against making the
+suggested change. Reviewers don't want to seem like nags and pester you
+if you haven't replied or made a fix, so it helps them know if you missed
+it or decided against it.
 
-Features or API extensions, even if they are small, will incur
-long-time maintenance and support burden, so they should be left
-pending for at least 24 hours to give maintainers in all timezones a
-chance to evaluate.
+A new feature or API extension, even if small, can incur a long-time
+maintenance and support burden and should be left pending for 24 hours
+to give maintainers in all time zones a chance to evaluate the change.
 
 [[design-driven-contribution-process]]
 === Design-driven Contribution Process
@@ -126,19 +116,19 @@
 
 For large/complex features it is important to:
 
-* agree on the functionality and scope before spending too much time
-  on the implementation
+* agree on functionality and scope before spending too much time on
+  implementation
 * ensure that they are in line with Gerrit's project scope and vision
 * ensure that they are well aligned with other features
-* think about possibilities how the feature could be evolved over time
+* consider how the feature could be evolved over time
 
 This is why for large/complex features it is required to describe the
 feature in a link:dev-design-docs.html[design doc,role=external,window=_blank]
 and get it approved by the
-link:dev-processes.html#steering-committee[steering committee,role=external,window=_blank],
+link:dev-processes.html#steering-committee[steering committee,role=external,window=_blank]
 before starting the implementation.
 
-The design-driven contribution process has the following steps:
+The design-driven contribution process consists of the following steps:
 
 * A link:dev-roles.html#contributor[contributor,role=external,window=_blank]
   link:dev-design-docs.html#propose[proposes,role=external,window=_blank] a new
@@ -155,40 +145,31 @@
   be accepted.
 * To be submitted, the design doc needs to be approved by the
   link:dev-processes.html#steering-committee[steering committee,role=external,window=_blank].
-* After the design was approved, the implementation is done by pushing
-  changes for review, see link:#lightweight-contribution-process[
+* After the design is approved, it is implemented by pushing
+  changes for review, see the link:#lightweight-contribution-process[
   lightweight contribution process]. Changes that are associated with
   a design should all share a common hashtag. The contributor is the
-  main driver of the implementation and responsible that it is done.
-  Others from the Gerrit community are usually much welcome to help
-  with the implementation.
+  main driver of the implementation and responsible for its completion.
+  Others from the Gerrit community are usually welcome to help.
 
-In order to be accepted/submitted, it is not necessary that the design
-doc fully specifies all the details, but the idea of the feature and
-how it fits into Gerrit should be sufficiently clear (judged by the
-steering committee). Contributors are expected to keep the design doc
-updated and fill in gaps while they go forward with the implementation.
-We expect that implementing the feature and updating the design doc
-will be an iterative process.
+The design doc does not need to fully specify each detail of the feature,
+but its concept and how it fits into Gerrit should be sufficiently clear,
+as judged by the steering committee. Contributors are expected to keep
+the design doc updated and fill in gaps while they go forward with the
+implementation. We expect that implementing the feature and updating the
+design doc will be an iterative process.
 
-While the design doc is still in review, contributors may already start
-with the implementation (e.g. do some prototyping to demonstrate parts
-of the proposed design), but those changes should not be submitted
-while the design wasn't approved yet. Another way to demonstrate the
-design can be to add screenshots or the like, early enough in the doc.
+While the design doc is still in review, contributors may start with the
+implementation (e.g. do some prototyping to demonstrate parts of the
+proposed design), but those changes should not be submitted while the
+design is not yet approved. Another way to demonstrate the design can be
+mocking screenshots in the doc.
 
 By approving a design, the steering committee commits to:
 
 * Accepting the feature when it is implemented.
 * Supporting the feature by assigning a link:dev-roles.html#mentor[
-  mentor,role=external,window=_blank] (if requested, see link:#mentorship[mentorship]).
-
-If the implementation of a feature gets stuck and it's unclear whether
-the feature gets fully done, it should be discussed with the steering
-committee how to proceed. If the contributor cannot commit to finish
-the implementation and no other contributor can take over, changes that
-have already been submitted for the feature might get reverted so that
-there is no unused or half-finished code in the code base.
+  mentor,role=external,window=_blank] if requested (see link:#mentorship[mentorship]).
 
 For contributors, the design-driven contribution process has the
 following advantages:
@@ -196,12 +177,11 @@
 * By writing a design doc, the feature gets more attention. During the
   design review, feedback from various sides can be collected, which
   likely leads to improvements of the feature.
-* Once a design was approved by the
+* Once a design is approved by the
   link:dev-processes.html#steering-committee[steering committee,role=external,window=_blank],
   the contributor can be almost certain that the feature will be accepted.
-  Hence, there is only a low risk to invest into implementing a feature
-  and see it being rejected later during the code review, as it can
-  happen with the lightweight contribution process.
+  Hence, there little risk of the feature being rejected later in code review,
+  as can occur with the lightweight contribution process.
 * The contributor can link:#mentorship[get a dedicated mentor assigned]
   who provides timely reviews and serves as a contact person for
   technical questions and discussing details of the design.
@@ -249,12 +229,11 @@
 * done criteria that define when the feature is done and the mentorship
   ends
 
-If a feature is not finished in time, it should be discussed with the
-steering committee how to proceed. If the contributor cannot commit to
-finish the implementation in time and no other contributor can take
-over, changes that have already been submitted for the feature might
-get reverted so that there is no unused or half-finished code in the
-code base.
+If a feature implementation is not completed in time and no contributors
+can commit to finishing the implementation, changes that have already been
+submitted for the feature may be reverted to avoid unused or half-finished
+code in the code base. In these circumstances, the steering committee
+determines how to proceed.
 
 [[esc-dd-evaluation]]
 == How the ESC evaluates design documents
@@ -314,7 +293,7 @@
 === Core vs. Plugin decision
 Q: `Would this fit better in a plugin?`
 
-* Yes:The proposed feature or rework is an implementation (e.g. Lucene
+* Yes: The proposed feature or rework is an implementation (e.g. Lucene
   is an index implementation) of a generic concept that others
   might want to implement differently.
 * Yes: The proposed feature or rework is very specific to a custom setup.
diff --git a/Documentation/dev-design-docs.txt b/Documentation/dev-design-docs.txt
index 6dc6f5f..efba520 100644
--- a/Documentation/dev-design-docs.txt
+++ b/Documentation/dev-design-docs.txt
@@ -13,7 +13,7 @@
 * Use-Cases:
   The interactions between a user and a system to attain particular
   goals.
-* Acceptance Criteria
+* Acceptance Criteria:
   Conditions that must be satisfied to consider the feature as done.
 * Background:
   Stuff one needs to know to understand the use-cases (e.g. motivating
diff --git a/Documentation/dev-e2e-tests.txt b/Documentation/dev-e2e-tests.txt
index c50a293..1151f1c 100644
--- a/Documentation/dev-e2e-tests.txt
+++ b/Documentation/dev-e2e-tests.txt
@@ -102,8 +102,7 @@
 === SSH keys
 
 If you are running SSH commands, the private keys of the users used for testing need to go in
-`/tmp/ssh-keys`. The keys need to be generated this way (JSch won't validate them
-link:https://stackoverflow.com/questions/53134212/invalid-privatekey-when-using-jsch[otherwise,role=external,window=_blank]):
+`/tmp/ssh-keys`. The keys need to be generated this way and won't be validated.
 
 ----
 mkdir /tmp/ssh-keys
@@ -174,6 +173,9 @@
 * `-Dcom.google.gerrit.scenarios.ssh_port=29418`
 * `-Dcom.google.gerrit.scenarios.http_port=8080`
 * `-Dcom.google.gerrit.scenarios.http_scheme=http`
+* `-Dcom.google.gerrit.scenarios.username=admin`
+* `-Dcom.google.gerrit.scenarios.replica_hostname=localhost`
+* `-Dcom.google.gerrit.scenarios.project_prefix=`
 
 Above, the properties can be set with values matching specific deployment topologies under test.
 The name of the property corresponds to the uppercase keyword found in the json file. For example,
@@ -195,6 +197,20 @@
 That whole replication time depends on the system under test. Therefore, this property here should
 be set to a value high enough, so that the test checks for a done replication at the right time.
 
+==== Context path
+
+The `context_path` property allows test scenarios to send Gerrit REST requests to Gerrit instances
+that use a context path in the URL. Its default is no context path and can be set using another value:
+
+* `-Dcom.google.gerrit.scenarios.context_path=/context`
+
+==== Authentication
+
+The `authenticated` property allows test scenarios to use authenticated HTTP clones. Its default is
+no authentication:
+
+* `-Dcom.google.gerrit.scenarios.authenticated=false`
+
 ==== Automatic properties
 
 The link:#_input_file[example keywords,role=external,window=_blank] also include `_PROJECT`,
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index e18d7b0..79febe4 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -31,9 +31,6 @@
 ----
 
 First, generate the Eclipse project by running the `tools/eclipse/project.py` script.
-If running Eclipse on Java 8, add the extra parameter
-`-e='--java_toolchain=//tools:error_prone_warnings_toolchain'`
-for generating a compatible project.
 
 Then, in Eclipse, choose 'Import existing project' and select the `gerrit` project
 from the current working directory.
@@ -118,11 +115,11 @@
 
 == Testing
 
-=== The Gerrit web app UI is served by `server.go` process. To launch it,
+=== The Gerrit web app UI is served by `Web Dev Server`. To launch it,
 run this command:
 
 ----
-  $ bazel run polygerrit-ui:devserver
+  $ npm run start
 ----
 
 === Running the Daemon
diff --git a/Documentation/dev-plugins-lifecycle.txt b/Documentation/dev-plugins-lifecycle.txt
index d5bd791..2f5ffb7 100644
--- a/Documentation/dev-plugins-lifecycle.txt
+++ b/Documentation/dev-plugins-lifecycle.txt
@@ -15,14 +15,14 @@
 The idea of creating a new plugin is posted and discussed on the
 link:https://groups.google.com/d/forum/repo-discuss[repo-discuss,role=external,window=_blank] mailing list.
 +
-Also see section link#ideation_discussion[Ideation and discussion] below.
+Also see section <<ideation_discussion>> below.
 
 - Prototyping (optional):
 +
 The author of the plugin creates a working prototype on a public repository
 accessible to the community.
 +
-Also see section link#plugin_prototyping[Plugin Prototyping] below.
+Also see section <<plugin_prototyping>> below.
 
 - Proposal and Hosting:
 +
@@ -37,7 +37,7 @@
 plugins path on link:https://gerrit-review.googlesource.com[the Gerrit project
 site,role=external,window=_blank].
 +
-Also see section link#plugin_proposal[Plugin Proposal] below.
+Also see section <<plugin_proposal>> below.
 
 - Build:
 +
@@ -46,14 +46,14 @@
 link:https://gerrit-ci.gerritforge.com[the GerritForge CI,role=external,window=_blank] that build the
 plugin for each Gerrit version that it supports.
 +
-Also see section link#build[Build] below.
+Also see section <<build>> below.
 
 - Development and Contribution:
 +
 The author develops a production-ready code base of the plugin, with
 contributions, reviews, and help from the Gerrit community.
 +
-Also see section link#development_contribution[Development and contribution]
+Also see section <<development_contribution>>
 below.
 
 - Release:
@@ -62,7 +62,7 @@
 on the link:https://groups.google.com/d/forum/repo-discuss[repo-discuss,role=external,window=_blank]
 mailing list.
 +
-Also see section link#plugin_release[Plugin release] below.
+Also see section <<plugin_release>> below.
 
 - Maintenance:
 +
@@ -75,7 +75,7 @@
 The author declares that the plugin is not maintained anymore or is deprecated
 and should not be used anymore.
 +
-Also see section link#plugin_deprecation[Plugin deprecation] below.
+Also see section <<plugin_deprecation>> below.
 
 [[ideation_discussion]]
 == Ideation and Discussion
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index fa2b78c..33c5bbd 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -2,8 +2,9 @@
 = Gerrit Code Review - Plugin Development
 
 The Gerrit server functionality can be extended by installing plugins.
-This page describes how plugins for Gerrit can be developed and hosted
-on gerrit-review.googlesource.com.
+This page describes how plugins for Gerrit can be developed. See the overall
+link:dev-plugins-lifecycle.html[Plugin Lifecycle] document for how plugins can
+be hosted on gerrit-review.googlesource.com.
 
 For JavaScript plugin development, consult with
 link:pg-plugin-dev.html[JavaScript Plugin Development] guide.
@@ -207,6 +208,28 @@
 The canonical web URL may be injected into any .jar plugin regardless of
 whether or not the plugin provides an HTTP servlet.
 
+[[plugin_resources]]
+=== Plugin resources
+
+Plugins are able to access their own resources without having to go through
+the implementation details on how they are packaged or deployed to Gerrit.
+
+The following example shows a MyClass in a plugin that is able to access the
+last modified time of the "myresource" loaded.
+
+[source,java]
+----
+public class MyClass {
+
+  @Inject
+  public MyClass(Plugin plugin) {
+    long myresourceTime = plugin.getContentScanner().getEntry("myresource").getTime();
+  }
+
+  [...]
+}
+----
+
 [[reload_method]]
 === Reload Method
 
@@ -415,6 +438,17 @@
 +
 Publication of usage data
 
+* `com.google.gerrit.extensions.events.GitReferenceUpdatedListener`:
++
+A git reference was updated. A separate event for every ref updated in
+a BatchRefUpdate will be fired.
+
+* `com.google.gerrit.extensions.events.GitBatchRefUpdateListener`:
++
+One or more git references were updated. Alternative to GitReferenceUpdatedListener.
+A single event will inform about all refs updated by a BatchRefUpdate. Will also be
+fired, if only a single ref was updated.
+
 * `com.google.gerrit.extensions.events.GarbageCollectorListener`:
 +
 Garbage collection ran on a project
@@ -499,6 +533,24 @@
 Certain operations in Gerrit can be validated by plugins by
 implementing the corresponding link:config-validation.html[listeners].
 
+[[taskListeners]]
+== WorkQueue.TaskListeners
+
+It is possible for plugins to listen to
+`com.google.gerrit.server.git.WorkQueue$Task`s directly before they run, and
+directly after they complete. This may be used to delay task executions based
+on custom criteria by blocking, likely on a lock or semaphore, inside
+onStart(), and a lock/semaphore release in onStop(). Plugins may listen to
+tasks by implementing a `com.google.gerrit.server.git.WorkQueue$TaskListener`
+and registering the new listener like this:
+
+[source,java]
+----
+bind(TaskListener.class)
+    .annotatedWith(Exports.named("MyListener"))
+    .to(MyListener.class);
+----
+
 [[change-message-modifier]]
 == Change Message Modifier
 
@@ -838,6 +890,63 @@
 }
 ----
 
+Plugins can receive a bean object for each of the gerrit ssh and the REST API
+commands by implementing BeanParseListener interface and registering it to a
+command class name in the plugin module's `configure()` method. The below
+example shows a plugin that always limits the number of projects returned
+by the ls-projects SSH command.
+
+[source, java]
+----
+protected static class PluginModule extends AbstractModule {
+  @Override
+  public void configure() {
+    bind(DynamicOptions.DynamicBean.class)
+        .annotatedWith(Exports.named(ListProjectsCommand.class))
+        .to(ListProjectsCommandBeanListener.class);
+  }
+
+  protected static class ListProjectsCommandBeanListener
+      implements DynamicOptions.BeanParseListener {
+    @Override
+    public void onBeanParseStart(String plugin, Object bean) {
+      ListProjectsCommand command = (ListProjectsCommand) bean;
+      command.impl.setLimit(1);
+    }
+
+    @Override
+    public void onBeanParseEnd(String plugin, Object bean) {}
+  }
+}
+----
+
+The below example shows a plugin that always limits the number of projects
+returned by the /projects/ REST API.
+
+[source, java]
+----
+protected static class PluginModule extends AbstractModule {
+  @Override
+  public void configure() {
+    bind(DynamicOptions.DynamicBean.class)
+        .annotatedWith(Exports.named(ListProjects.class))
+        .to(ListProjectsBeanListener.class);
+  }
+
+  protected static class ListProjectsBeanListener
+      implements DynamicOptions.BeanParseListener {
+    @Override
+    public void onBeanParseStart(String plugin, Object bean) {
+      ListProjects listProjects = (ListProjects) bean;
+      listProjects.setLimit(1);
+    }
+
+    @Override
+    public void onBeanParseEnd(String plugin, Object bean) {}
+  }
+}
+----
+
 === Calling Command Options ===
 
 Within an OptionHandler, during the processing of an option, plugins can
@@ -899,7 +1008,7 @@
 [[query_attributes]]
 == Change Attributes
 
-==== ChangePluginDefinedInfoFactory
+=== ChangePluginDefinedInfoFactory
 
 Plugins can provide additional attributes to be returned from the Get Change and
 Query Change APIs by implementing the `ChangePluginDefinedInfoFactory` interface
@@ -2131,6 +2240,8 @@
 DiffWebLinks will appear in the side-by-side and unified diff screen in
 the header next to the navigation icons.
 
+EditWebLinks will appear in the top-right part of the file diff page.
+
 ProjectWebLinks will appear in the project list in the
 `Repository Browser` column.
 
@@ -2209,8 +2320,7 @@
 DropWizard Metrics,role=external,window=_blank].
 
 Metric reporting plugin implementations are provided for
-link:https://gerrit.googlesource.com/plugins/metrics-reporter-jmx/[JMX,role=external,window=_blank],
-link:https://gerrit.googlesource.com/plugins/metrics-reporter-elasticsearch/[Elastic Search,role=external,window=_blank],
+link:https://gerrit.googlesource.com/plugins/metrics-reporter-jmx/[JMX,role=external,window=_blank]
 and link:https://gerrit.googlesource.com/plugins/metrics-reporter-graphite/[Graphite,role=external,window=_blank].
 
 There is also a working example of reporting metrics to the console in the
@@ -2628,7 +2738,7 @@
 Plugins may also decide not to vote on a given change by returning an
 `Optional.empty()` (ie: the plugin is not enabled for this repository).
 
-If a plugin decides not to vote, it's name will not be displayed in the UI and
+If a plugin decides not to vote, its name will not be displayed in the UI and
 it will not be recoded in the database.
 
 .Gerrit's Pre-submit handling with three plugins
@@ -2712,6 +2822,23 @@
 prevent callers using ETags from potentially seeing outdated submittability
 information.
 
+`SubmitRule` interface will soon deprecated. Instead, a global `SubmitRequirement`
+can be bound by plugin.
+
+[source, java]
+----
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.inject.AbstractModule;
+
+public class MyPluginModule extends AbstractModule {
+  @Override
+  protected void configure() {
+    bind(SubmitRequirement.class).annotatedWith(Exports.named("myPlugin"))
+        .toInstance(myPluginSubmitRequirement);
+  }
+}
+----
+
 [[change-etag-computation]]
 == Change ETag Computation
 
diff --git a/Documentation/dev-processes.txt b/Documentation/dev-processes.txt
index e43e021..3bd88f5 100644
--- a/Documentation/dev-processes.txt
+++ b/Documentation/dev-processes.txt
@@ -366,7 +366,10 @@
 * If the library is used within Google, the version of the library must be compatible with the
   version that is used at Google.
 
-Only maintainers from Google can vote on the `Library-Compliance` label.
+Only maintainers from Google can vote on the `Library-Compliance` label. The
+Gerrit team at Google uses this
+link:https://gerrit-review.googlesource.com/q/label:%2522Library-Compliance%253Dneed%2522+-ownerin:google-gerrit-team+status:open+project:gerrit+-age:4week+-is:wip+-is:private+label:Code-Review%252B2[change query]
+to find changes that require a `Library-Compliance` approval.
 
 Gerrit's library dependencies should only be upgraded if the new version contains
 something we need in Gerrit. This includes new features, API changes as well as bug
@@ -378,6 +381,12 @@
 that they are vetted long enough before they go into a release and we can be sure
 that the update doesn't introduce a regression.
 
+[[escalation-channel-to-google]]
+== Escalation channel to Google
+
+If anything urgent is blocking that requires the attention of a Googler you may
+escalate this by writing an email to Han-Wen Nienhuys: hanwen@google.com
+
 [[deprecating-features]]
 == Deprecating features
 
diff --git a/Documentation/dev-readme.txt b/Documentation/dev-readme.txt
index f045ab8..6ff064c 100644
--- a/Documentation/dev-readme.txt
+++ b/Documentation/dev-readme.txt
@@ -178,6 +178,9 @@
 NOTE: To learn why using `java -jar` isn't sufficient, see
 <<special_bazel_java_version,this explanation>>.
 
+NOTE: When launching the daemong this way, the settings from the `[container]` section from the
+`$GERRIT_SITE/etc/gerrit.config` are not honored.
+
 To debug the Gerrit server of this test site:
 
 .  Open a debug port (such as port 5005). To do so, insert the following code
@@ -188,6 +191,49 @@
 -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
 ----
 
+=== Running the Daemon honoring the [container] section settings
+
+To run the Daemon and honor the `[container]` section settings use the `gerrit.sh` script:
+
+----
+  $ cd $GERRIT_SITE
+  $ bin/gerrit.sh run
+----
+
+To run the Daemon in debug mode use the `--debug` option:
+
+----
+  $ bin/gerrit.sh run --debug
+----
+
+The default debug port is `8000`. To specify a different debug port use the `--debug-port` option:
+
+----
+  $ bin/gerrit.sh run --debug --debug-port=5005
+----
+
+The `--debug-address` option also exists and is a synonym for the ``--debug-port` option:
+
+----
+  $ bin/gerrit.sh run --debug --debug-address=5005
+----
+
+Note that, by default, the debugger will only accept connections from the localhost. To enable
+debug connections from other host(s) provide also a host name or wildcard in the `--debug-address`
+value:
+
+----
+  $ bin/gerrit.sh run --debug --debug-address=*:5005
+----
+
+Debugging the Daemon startup requires starting the JVM in suspended debug mode. The JVM will await
+for a debugger to attach before proceeding with the start. Use the `--suspend` option for that
+scenario:
+
+----
+  $ bin/gerrit.sh run --debug --suspend
+----
+
 === Running the Daemon with Gerrit Inspector
 
 link:dev-inspector.html[Gerrit Inspector] is an interactive scriptable
diff --git a/Documentation/dev-release-deploy-config.txt b/Documentation/dev-release-deploy-config.txt
index a4ccccf..db08da5 100644
--- a/Documentation/dev-release-deploy-config.txt
+++ b/Documentation/dev-release-deploy-config.txt
@@ -44,15 +44,13 @@
 +
 Generate and publish a PGP key as described in
 link:http://central.sonatype.org/pages/working-with-pgp-signatures.html[
-Working with PGP Signatures,role=external,window=_blank]. In addition to the keyserver mentioned
-there it is recommended to also publish the key to the
-link:https://keyserver.ubuntu.com/[Ubuntu key server].
+Working with PGP Signatures,role=external,window=_blank].
 +
 Please be aware that after publishing your public key it may take a
 while until it is visible to the Sonatype server.
 +
 Add an entry for the public key in the
-link:https://gerrit.googlesource.com/homepage/+/md-pages/releases/public-keys.md[key list,role=external,window=_blank]
+link:https://gerrit.googlesource.com/homepage/+/master/pages/site/releases/public-keys.md[key list,role=external,window=_blank]
 on the homepage.
 +
 The PGP passphrase can be put in `~/.m2/settings.xml`:
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt
index 0849c56..40470a6 100644
--- a/Documentation/dev-release.txt
+++ b/Documentation/dev-release.txt
@@ -327,6 +327,20 @@
   git submodule foreach '[ "$sm_path" == "modules/jgit" ] || git push gerrit-review tag "v$version"'
 ----
 
+[[publish-typescript-plugin-api]]
+==== Publish TypeScript Plugin API
+
+Only applies to major and minor releases! Not required for patch releases.
+
+* Publish a new version of the npm package `@gerritcodereview/typescript-api`.
+See link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/app/api/README.md[api/README.md,role=external,window=_blank]
+for details.
+
+* The plugins in the stable branch of the minor release and the master branch
+be changed to use the new API version, see
+link:https://gerrit-review.googlesource.com/c/gerrit/+/340069[
+example change,role=external,window=_blank]
+
 [[upload-documentation]]
 ==== Upload the Documentation
 
diff --git a/Documentation/dev-roles.txt b/Documentation/dev-roles.txt
index cecaedc..d8f7e11 100644
--- a/Documentation/dev-roles.txt
+++ b/Documentation/dev-roles.txt
@@ -66,11 +66,12 @@
 link:https://groups.google.com/d/forum/repo-discuss[repo-discuss,role=external,window=_blank]
 mailing list):
 
-* become member of the `gerrit-verifiers` group, which allows to:
+* become member of the `gerrit-trusted-contributors` group, which allows to:
 ** vote on the `Verified` and `Code-Style` labels
 ** edit hashtags on all changes
 ** edit topics on all open changes
 ** abandon changes
+** revert changes
 * approve posts to the
   link:https://groups.google.com/d/forum/repo-discuss[repo-discuss,role=external,window=_blank]
   mailing list
@@ -106,14 +107,14 @@
 have. In addition they have signed a link:dev-cla.html[contributor
 license agreement] which enables them to push changes.
 
-Regular contributors can ask to be added to the `gerrit-verifiers`
+Regular contributors can ask to be added to the `gerrit-trusted-contributors`
 group, which allows to:
 
 * add patch sets to changes of other users
 * propose project config changes (push changes for the
   `refs/meta/config` branch
 
-Being member of the `gerrit-verifiers` group includes further
+Being member of the `gerrit-trusted-contributors` group includes further
 permissions (see link:#supporter[supporter] section above).
 
 It's highly appreciated if contributors engage in code reviews,
diff --git a/Documentation/dev-stars.txt b/Documentation/dev-stars.txt
deleted file mode 100644
index 764e326..0000000
--- a/Documentation/dev-stars.txt
+++ /dev/null
@@ -1,82 +0,0 @@
-= Gerrit Code Review - Stars
-
-== Description
-
-Changes can be starred with labels that behave like private hashtags.
-Any label can be applied to a change, but these labels are only visible
-to the user for which the labels have been set.
-
-Stars allow users to categorize changes by self-defined criteria and
-then build link:user-dashboards.html[dashboards] for them by making use
-of the link:#query-stars[star query operators].
-
-[[star-api]]
-== Star API
-
-The link:rest-api-accounts.html#star-endpoints[star REST API] supports:
-
-* link:rest-api-accounts.html#get-stars[
-  get star labels from a change]
-* link:rest-api-accounts.html#set-stars[
-  update star labels on a change]
-* link:rest-api-accounts.html#get-starred-changes[
-  list changes that are starred by any label]
-
-Star labels are also included in
-link:rest-api-changes.html#change-info[ChangeInfo] entities that are
-returned by the link:rest-api-changes.html[changes REST API].
-
-There are link:rest-api-accounts.html#default-star-endpoints[
-additional REST endpoints] for the link:#default-star[default star].
-
-[[default-star]]
-== Default Star
-
-If the default star is set by a user, this user is automatically
-notified by email whenever updates are made to that change.
-
-The default star is the star that is shown in the WebUI and which can
-be updated from there.
-
-The default star is represented by the special star label 'star'.
-
-[[ignore-star]]
-== Ignore Star
-
-If the ignore star is set by a user, this user gets no email
-notifications for updates of that change, even if this user is a
-reviewer of the change or the change is matched by a project watch of
-the user.
-
-Since changes can only be ignored once they are created, users that
-watch a project will always get the email notifications for the change
-creation. Only then the change can be ignored.
-
-Users that are added as reviewer or assignee to a change that they have
-ignored will be notified about this, so that they know about the review
-request. They can then decide to remove the ignore star.
-
-The ignore star is represented by the special star label 'ignore'.
-
-[[query-stars]]
-== Query Stars
-
-There are several query operators to find changes with stars:
-
-* link:user-search.html#is-starred[is:starred] /
-  link:user-search.html#has-star[has:star]:
-  Matches any change that was starred by the current user with the
-  link:#default-star[default star].
-
-[[syntax]]
-== Syntax
-
-Star labels cannot contain whitespace characters. All other characters
-are allowed.
-
-GERRIT
-------
-Part of link:index.html[Gerrit Code Review]
-
-SEARCHBOX
----------
diff --git a/Documentation/externalid-case-insensitivity.txt b/Documentation/externalid-case-insensitivity.txt
new file mode 100644
index 0000000..b4e8140
--- /dev/null
+++ b/Documentation/externalid-case-insensitivity.txt
@@ -0,0 +1,128 @@
+:linkattrs:
+= Gerrit Code Review - ExternalId case insensitivity
+
+Gerrit usernames are case insensitive by default: e.g. johndoe and JohnDoe
+represents the same account. However, for installations older than v3.5.x,
+the usernames were case sensitive, e.g. johndoe and JohnDoe can both exist
+as separate accounts. This could lead to issues when migrating an account
+from LDAP to an internal account, if ldap.localUsernameToLowerCase was set.
+Such usernames can also be rather confusing for users, if they try to identify
+authors of comments or changes.
+
+When Gerrit handles case insensitive usernames (external IDs using the
+`gerrit:` or `username:` scheme, their external IDs SHA-1 is always computed
+using the lowercase external ID, hence there cannot be any account differing
+only in the capitalization of their usernames.
+
+Gerrit installations older than v3.5.x that are switching to the case-insensitive
+username need to migrating all their existing accounts SHA-1s.
+
+[[migration]]
+== Migration
+
+Migrating external ID notes can take several minutes for large sites(for example
+migration ~45000 accounts can take up to five minutes), so administrators choose
+whether to do the migration offline or online, depending on their available
+resources and tolerance for downtime.
+
+NOTE: Migration is required only on Gerrit primary instances.
+
+[[offline-migration]]
+=== Offline
+
+To run the offline migration execute following steps:
+* Stop all Gerrit primary instances
+* Set the `auth.userNameCaseInsensitive` to false
+----
+[auth]
+  userNameCaseInsensitive = false
+----
+
+* Run:
+[verse]
+--
+_java_ -jar gerrit.war _ChangeExternalIdCaseSensitivity_
+  -d <SITE_PATH>
+  [--batch]
+--
+
+See: link:pgm-ChangeExternalIdCaseSensitivity.html
+
+* During the migration `auth.userNameCaseInsensitive` will be set to true
+on a node which is executing the migration. When the migration is finished,
+on all other primary nodes set `auth.userNameCaseInsensitive` to true
+* Start all Gerrit primary instances
+
+[[online-migration]]
+=== Online
+
+To start the online migration, set the `auth.userNameCaseInsensitive` and
+`auth.userNameCaseInsensitiveMigrationMode` options in `gerrit.config` and
+restart Gerrit:
+----
+[auth]
+  userNameCaseInsensitive = true
+  userNameCaseInsensitiveMigrationMode = true
+----
+* Trigger online migration:
+----
+$ ssh -p <port> <host> gerrit migrate-externalids-to-insensitive
+----
+
+See: link:cmd-migrate-externalids-to-insensitive.html
+
+[online-ha-migration]
+== Online migration for high-availability setup
+
+To start the online migration with a setup containing multiple primary
+instances execute following steps:
+* On all Gerrit primary instances set `auth.userNameCaseInsensitive` and
+`auth.userNameCaseInsensitiveMigrationMode` and perform a rolling restart
+----
+[auth]
+  userNameCaseInsensitive = true
+  userNameCaseInsensitiveMigrationMode = true
+----
+* Trigger online migration:
+----
+$ ssh -p <port> <host> gerrit migrate-externalids-to-insensitive
+----
+
+See: link:cmd-migrate-externalids-to-insensitive.html
+
+* When the migration is finished, on all other primary nodes set
+`auth.userNameCaseInsensitiveMigrationMode` to false and perform a
+rolling restart
+----
+[auth]
+  userNameCaseInsensitive = true
+  userNameCaseInsensitiveMigrationMode = false
+----
+
+== External ID case insensitivity rollback
+
+The offline migration tool allows to calculate external ID notes named with the SHA-1
+from the case sensitive external ID.
+
+To rollback external ID notes migration execute following steps:
+* Stop all Gerrit primary instances
+* Set the `auth.userNameCaseInsensitive` to true
+----
+[auth]
+  userNameCaseInsensitive = true
+----
+
+* Run:
+[verse]
+--
+_java_ -jar gerrit.war _ChangeExternalIdCaseSensitivity_
+  -d <SITE_PATH>
+  [--batch]
+--
+
+See: link:pgm-ChangeExternalIdCaseSensitivity.html
+
+* During the migration `auth.userNameCaseInsensitive` will be set to false
+on a node which is executing the migration. When the migration is finished,
+on all other primary nodes set `auth.userNameCaseInsensitive` to false
+* Start all Gerrit primary instances
diff --git a/Documentation/images/browser-notification-example.png b/Documentation/images/browser-notification-example.png
new file mode 100644
index 0000000..2b60054
--- /dev/null
+++ b/Documentation/images/browser-notification-example.png
Binary files differ
diff --git a/Documentation/images/browser-notification-preference.png b/Documentation/images/browser-notification-preference.png
new file mode 100644
index 0000000..57d5fd6
--- /dev/null
+++ b/Documentation/images/browser-notification-preference.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-apply-fix.png b/Documentation/images/user-review-ui-apply-fix.png
new file mode 100644
index 0000000..d838d48
--- /dev/null
+++ b/Documentation/images/user-review-ui-apply-fix.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-change-metadata.png b/Documentation/images/user-review-ui-change-metadata.png
new file mode 100644
index 0000000..23abc07
--- /dev/null
+++ b/Documentation/images/user-review-ui-change-metadata.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-change-screen-annotated.png b/Documentation/images/user-review-ui-change-screen-annotated.png
index 5c3f80a..4e12c96 100644
--- a/Documentation/images/user-review-ui-change-screen-annotated.png
+++ b/Documentation/images/user-review-ui-change-screen-annotated.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-change-screen-change-info-labels.png b/Documentation/images/user-review-ui-change-screen-change-info-labels.png
deleted file mode 100644
index 61e2b25..0000000
--- a/Documentation/images/user-review-ui-change-screen-change-info-labels.png
+++ /dev/null
Binary files differ
diff --git a/Documentation/images/user-review-ui-change-screen-comments-tab.png b/Documentation/images/user-review-ui-change-screen-comments-tab.png
new file mode 100644
index 0000000..d522f60
--- /dev/null
+++ b/Documentation/images/user-review-ui-change-screen-comments-tab.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-change-screen-file-list.png b/Documentation/images/user-review-ui-change-screen-file-list.png
index 721b229..b0c2af3 100644
--- a/Documentation/images/user-review-ui-change-screen-file-list.png
+++ b/Documentation/images/user-review-ui-change-screen-file-list.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-change-screen-keyboard-shortcuts.png b/Documentation/images/user-review-ui-change-screen-keyboard-shortcuts.png
index 9ef8f27..224de2d 100644
--- a/Documentation/images/user-review-ui-change-screen-keyboard-shortcuts.png
+++ b/Documentation/images/user-review-ui-change-screen-keyboard-shortcuts.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-change-screen-reply.png b/Documentation/images/user-review-ui-change-screen-reply.png
index 1c50fc5..201db13 100644
--- a/Documentation/images/user-review-ui-change-screen-reply.png
+++ b/Documentation/images/user-review-ui-change-screen-reply.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-change-screen-topleft.png b/Documentation/images/user-review-ui-change-screen-topleft.png
index a1f7813..b3bf8e7f 100644
--- a/Documentation/images/user-review-ui-change-screen-topleft.png
+++ b/Documentation/images/user-review-ui-change-screen-topleft.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-change-screen.png b/Documentation/images/user-review-ui-change-screen.png
index ff2570b..98a5d6d 100644
--- a/Documentation/images/user-review-ui-change-screen.png
+++ b/Documentation/images/user-review-ui-change-screen.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-copy-links.png b/Documentation/images/user-review-ui-copy-links.png
new file mode 100644
index 0000000..f8fa114
--- /dev/null
+++ b/Documentation/images/user-review-ui-copy-links.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-side-by-side-diff-screen-inline-comments.png b/Documentation/images/user-review-ui-side-by-side-diff-screen-inline-comments.png
index 047034c..98cf7af 100644
--- a/Documentation/images/user-review-ui-side-by-side-diff-screen-inline-comments.png
+++ b/Documentation/images/user-review-ui-side-by-side-diff-screen-inline-comments.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-side-by-side-diff-screen.png b/Documentation/images/user-review-ui-side-by-side-diff-screen.png
index 74d02e3..ebdd177 100644
--- a/Documentation/images/user-review-ui-side-by-side-diff-screen.png
+++ b/Documentation/images/user-review-ui-side-by-side-diff-screen.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-submit-requirements.png b/Documentation/images/user-review-ui-submit-requirements.png
new file mode 100644
index 0000000..e4b88c1
--- /dev/null
+++ b/Documentation/images/user-review-ui-submit-requirements.png
Binary files differ
diff --git a/Documentation/images/user-review-ui-suggest-fix.png b/Documentation/images/user-review-ui-suggest-fix.png
new file mode 100644
index 0000000..e08fb26
--- /dev/null
+++ b/Documentation/images/user-review-ui-suggest-fix.png
Binary files differ
diff --git a/Documentation/index.txt b/Documentation/index.txt
index 782a6a9..89b88aa 100644
--- a/Documentation/index.txt
+++ b/Documentation/index.txt
@@ -15,6 +15,7 @@
 == Contributor Guides
 . link:dev-community.html[Gerrit Community]
 . link:dev-community.html#how-to-contribute[How to Contribute]
+.. link:dev-readme.html[Developer Setup]
 
 == User Guides
 . link:intro-user.html[User Guide]
@@ -23,7 +24,7 @@
 
 == Tutorials
 . Web
-.. link:user-review-ui.html[Reviewing Changes]
+.. link:user-review-ui.html[Review UI Overview]
 .. link:user-search.html[Searching Changes]
 .. link:user-inline-edit.html[Manipulating Changes in Browser]
 .. link:user-notify.html[Subscribing to Email Notifications]
@@ -43,6 +44,7 @@
 == Project Management
 . link:project-configuration.html[Project Configuration]
 .. link:config-labels.html[Review Labels]
+.. link:config-submit-requirements.html[Submit Requirements]
 .. link:config-project-config.html[Project Configuration File Format]
 . link:access-control.html[Access Controls]
 . Multi-project management
diff --git a/Documentation/intro-gerrit-walkthrough.txt b/Documentation/intro-gerrit-walkthrough.txt
index 6565ba4..345eb1c 100644
--- a/Documentation/intro-gerrit-walkthrough.txt
+++ b/Documentation/intro-gerrit-walkthrough.txt
@@ -130,7 +130,7 @@
 In general, the *Code-Review* check requires an individual to look at the code,
 while the *Verified* check is done by an automated build server, through a
 mechanism such as the
-link:https://wiki.jenkins-ci.org/display/JENKINS/Gerrit+Trigger[Gerrit Trigger
+link:https://plugins.jenkins.io/gerrit-trigger/[Gerrit Trigger
 Jenkins Plugin,role=external,window=_blank].
 
 IMPORTANT: The Code-Review and Verified checks require different permissions
@@ -161,8 +161,8 @@
 * `+2 Looks good to me, approved`
 * `+1 Looks good to me, but someone else must approve`
 * `0 No score`
-* `-1 I would prefer that you didn't submit this`
-* `-2 Do not submit`
+* `-1 I would prefer this is not submitted as is`
+* `-2 This shall not be submitted`
 
 In addition, a change must have at least one `+2` vote and no `-2` votes before
 it can be submitted. These numerical values do not accumulate. Two
@@ -253,7 +253,7 @@
 can add custom checks or even remove the Verified check entirely.
 
 Verification is typically an automated process using the
-link:https://wiki.jenkins-ci.org/display/JENKINS/Gerrit+Trigger[Gerrit Trigger Jenkins Plugin,role=external,window=_blank]
+link:https://plugins.jenkins.io/gerrit-trigger/[Gerrit Trigger Jenkins Plugin,role=external,window=_blank]
 or a similar mechanism. However, there are still times when a change requires
 manual verification, or a reviewer needs to check how or if a change works.
 To accommodate these and other similar circumstances, Gerrit exposes each change
diff --git a/Documentation/intro-project-owner.txt b/Documentation/intro-project-owner.txt
index 8a3b10e..f13bc22 100644
--- a/Documentation/intro-project-owner.txt
+++ b/Documentation/intro-project-owner.txt
@@ -316,9 +316,9 @@
 
 A useful feature on labels is the possibility to automatically copy
 scores forward to new patch sets if it was a
-link:config-labels.html#label_copyAllScoresOnTrivialRebase[trivial
-rebase] or if link:config-labels.html#label_copyAllScoresIfNoCodeChange[
-there was no code change] (e.g. only the commit message was edited).
+link:config-labels.html#trivial_rebase[trivial rebase] or if
+link:config-labels.html#no_code_change[there was no code change] (e.g.
+only the commit message was edited).
 
 [[submit-rules]]
 == Submit Rules
@@ -374,7 +374,7 @@
 There are several solutions for integrating continuous integration
 systems. The most commonly used are:
 
-- link:https://wiki.jenkins-ci.org/display/JENKINS/Gerrit+Trigger[
+- link:https://plugins.jenkins.io/gerrit-trigger/[
   Gerrit Trigger,role=external,window=_blank] plugin for link:http://jenkins-ci.org/[Jenkins,role=external,window=_blank]
 
 - link:http://www.mediawiki.org/wiki/Continuous_integration/Zuul[
diff --git a/Documentation/intro-user.txt b/Documentation/intro-user.txt
index f619c99..7a042ba 100644
--- a/Documentation/intro-user.txt
+++ b/Documentation/intro-user.txt
@@ -368,6 +368,10 @@
 project-level]. The project dashboards can be seen in the web UI under
 `Projects` > `List` > <project-name> > `Dashboards`.
 
+All dashboards and search pages allow an action to be applied to multiple
+changes at once. Select the changes with the checkboxes on the left side and
+choose the action from the action bar at the top of the change section.
+
 [[submit]]
 == Submit a Change
 
@@ -459,7 +463,7 @@
 push permission] on the destination branch.
 
 The move operation will not update the change's parent and users will have
-to link:#rebase[rebase] the change. Also, merge commits cannot be moved.
+to link:#rebase[rebase] the change.
 
 [[abandon]]
 [[restore]]
@@ -662,19 +666,10 @@
 * If you have a series of private changes and share one with reviewers,
   the reviewers can also see the commits of the predecessor private
   changes through the commit parent relationship.
-
-[[ignore]]
-== Ignoring Or Marking Changes As 'Reviewed'
-
-Changes can be ignored, which means they will not appear in the 'Incoming
-Reviews' dashboard and any related email notifications will be suppressed.
-This can be useful when you are added as a reviewer to a change on which
-you do not actively participate in the review, but do not want to completely
-remove yourself.
-
-Alternatively, rather than completely ignoring the change, it can be marked
-as 'Reviewed'. Marking a change as 'Reviewed' means it will not be highlighted
-in the dashboard, until a new patch set is uploaded.
+* Users who would have permission to access the change except for its
+  private status and knowledge of its commit ID (e.g. through CI logs
+  or build artifacts containing build numbers) can fetch the code
+  using the commit ID.
 
 [[inline-edit]]
 == Inline Edit
@@ -807,16 +802,21 @@
 +
 This setting controls the email notifications.
 +
-** `Enabled`:
-+
-Email notifications are enabled.
-+
 ** [[cc-me]]`Every comment`:
 +
 Email notifications are enabled and you get notified by email as CC
 on comments that you write yourself.
 +
-** `Disabled`:
+** `Only comments left by others`
++
+Email notifications are enabled for all activities excluding comments or
+reviews authored by you.
++
+** `Only when I am in the attention set`
++
+Email notifications are only sent if the recipient is in the attention set.
++
+** `None`:
 +
 Email notifications are disabled.
 
@@ -1014,7 +1014,7 @@
 inline comment ("Yeah, I see why, let me try again.").
 
 [[security-fixes]]
--- Security Fixes
+== Security Fixes
 
 If a security vulnerability is discovered you normally want to have an
 embargo about it until fixed releases have been made available. This
diff --git a/Documentation/js_licenses.txt b/Documentation/js_licenses.txt
index ab79c8f..e2afbf5 100644
--- a/Documentation/js_licenses.txt
+++ b/Documentation/js_licenses.txt
@@ -2,6 +2,7 @@
 [[Apache2_0]]
 Apache2.0
 
+* fonts:material-icons
 * fonts:robotofonts
 
 [[Apache2_0_license]]
@@ -403,6 +404,7 @@
 * @polymer/iron-resizable-behavior
 * @polymer/iron-selector
 * @polymer/iron-validatable-behavior
+* @polymer/marked-element
 * @polymer/neon-animation
 * @polymer/paper-behaviors
 * @polymer/paper-button
@@ -1120,6 +1122,126 @@
 ----
 
 
+[[highlight_js]]
+highlight.js
+
+* highlight.js
+
+[[highlight_js_license]]
+----
+BSD 3-Clause License
+
+Copyright (c) 2006, Ivan Sagalaev.
+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 copyright holder 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.
+
+----
+
+
+[[highlightjs-closure-templates]]
+highlightjs-closure-templates
+
+* highlightjs-closure-templates
+
+[[highlightjs-closure-templates_license]]
+----
+BSD 3-Clause License
+
+Copyright (c) 2006, Ivan Sagalaev.
+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 copyright holder 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.
+
+----
+
+
+[[highlightjs-structured-text]]
+highlightjs-structured-text
+
+* highlightjs-structured-text
+
+[[highlightjs-structured-text_license]]
+----
+BSD 3-Clause License

+

+Copyright (c) 2006, Ivan Sagalaev

+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 holder 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.
+
+----
+
+
 [[immer]]
 immer
 
@@ -1184,6 +1306,60 @@
 ----
 
 
+[[marked]]
+marked
+
+* marked
+
+[[marked_license]]
+----
+# License information
+
+## Contribution License Agreement
+
+If you contribute code to this project, you are implicitly allowing your code
+to be distributed under the MIT license. You are also implicitly verifying that
+all code is your original work. `</legalese>`
+
+## Marked
+
+Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/)
+
+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.
+
+## Markdown
+
+Copyright © 2004, John Gruber 
+http://daringfireball.net/ 
+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 “Markdown” 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.
+
+----
+
+
 [[page]]
 page
 
@@ -1491,6 +1667,219 @@
 ----
 
 
+[[safevalues]]
+safevalues
+
+* safevalues
+
+[[safevalues_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.
+
+----
+
+
 [[tslib]]
 tslib
 
@@ -1513,3 +1902,216 @@
 
 ----
 
+
+[[web-vitals]]
+web-vitals
+
+* web-vitals
+
+[[web-vitals_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 2020 Google LLC
+
+   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
+
+       https://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.
+
+----
+
diff --git a/Documentation/licenses.txt b/Documentation/licenses.txt
index 86ff904..8ccbcab 100644
--- a/Documentation/licenses.txt
+++ b/Documentation/licenses.txt
@@ -32,7 +32,7 @@
 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
+Gerrit includes an SSH client (Apache SSHD), to support authenticated
 replication of changes to remote systems, such as for automatic
 updates of mirror servers, or realtime backups.
 
@@ -42,31 +42,30 @@
 [[Apache2_0]]
 Apache2.0
 
+* auto:auto-factory
 * 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:text
 * commons:validator
 * dropwizard:dropwizard-core
 * errorprone:annotations
 * flogger:api
+* fonts:material-icons
 * fonts:robotofonts
 * 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
@@ -1114,22 +1113,6 @@
 ----
 
 
-[[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
 
@@ -2394,43 +2377,6 @@
 ----
 
 
-[[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
 
@@ -3362,6 +3308,7 @@
 * @polymer/iron-resizable-behavior
 * @polymer/iron-selector
 * @polymer/iron-validatable-behavior
+* @polymer/marked-element
 * @polymer/neon-animation
 * @polymer/paper-behaviors
 * @polymer/paper-button
@@ -4079,6 +4026,126 @@
 ----
 
 
+[[highlight_js]]
+highlight.js
+
+* highlight.js
+
+[[highlight_js_license]]
+----
+BSD 3-Clause License
+
+Copyright (c) 2006, Ivan Sagalaev.
+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 copyright holder 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.
+
+----
+
+
+[[highlightjs-closure-templates]]
+highlightjs-closure-templates
+
+* highlightjs-closure-templates
+
+[[highlightjs-closure-templates_license]]
+----
+BSD 3-Clause License
+
+Copyright (c) 2006, Ivan Sagalaev.
+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 copyright holder 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.
+
+----
+
+
+[[highlightjs-structured-text]]
+highlightjs-structured-text
+
+* highlightjs-structured-text
+
+[[highlightjs-structured-text_license]]
+----
+BSD 3-Clause License

+

+Copyright (c) 2006, Ivan Sagalaev

+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 holder 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.
+
+----
+
+
 [[immer]]
 immer
 
@@ -4143,6 +4210,60 @@
 ----
 
 
+[[marked]]
+marked
+
+* marked
+
+[[marked_license]]
+----
+# License information
+
+## Contribution License Agreement
+
+If you contribute code to this project, you are implicitly allowing your code
+to be distributed under the MIT license. You are also implicitly verifying that
+all code is your original work. `</legalese>`
+
+## Marked
+
+Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/)
+
+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.
+
+## Markdown
+
+Copyright © 2004, John Gruber 
+http://daringfireball.net/ 
+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 “Markdown” 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.
+
+----
+
+
 [[page]]
 page
 
@@ -4450,6 +4571,219 @@
 ----
 
 
+[[safevalues]]
+safevalues
+
+* safevalues
+
+[[safevalues_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.
+
+----
+
+
 [[tslib]]
 tslib
 
@@ -4473,6 +4807,219 @@
 ----
 
 
+[[web-vitals]]
+web-vitals
+
+* web-vitals
+
+[[web-vitals_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 2020 Google LLC
+
+   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
+
+       https://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.
+
+----
+
+
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/linux-quickstart.txt b/Documentation/linux-quickstart.txt
index c45de05..13873ed 100644
--- a/Documentation/linux-quickstart.txt
+++ b/Documentation/linux-quickstart.txt
@@ -29,10 +29,10 @@
 . Download the desired Gerrit archive.
 
 To view previous archives, see
-link:https://gerrit-releases.storage.googleapis.com/index.html[Gerrit Code Review: Releases,role=external,window=_blank]. The steps below install Gerrit 3.1.3:
+link:https://gerrit-releases.storage.googleapis.com/index.html[Gerrit Code Review: Releases,role=external,window=_blank]. The steps below install Gerrit 3.5.1:
 
 ....
-wget https://gerrit-releases.storage.googleapis.com/gerrit-3.1.3.war
+wget https://gerrit-releases.storage.googleapis.com/gerrit-3.5.1.war
 ....
 
 NOTE: To build and install Gerrit from the source files, see
diff --git a/Documentation/logs.txt b/Documentation/logs.txt
index ba370b1..e120fdd 100644
--- a/Documentation/logs.txt
+++ b/Documentation/logs.txt
@@ -56,6 +56,11 @@
   CPU time in kernel mode is `total_cpu - user_cpu`.
 * `memory`: memory allocated in bytes to execute command. -1 if the JVM does
   not support this metric.
+* `command status`: the overall result of the git command over HTTP. Currently
+   populated only for the transfer phase of `git-upload-pack` commands.
+   Possible values:
+** `-1`: The `git-upload-pack` transfer was ultimately not successful
+** `0`: The `git-upload-pack` transfer was ultimately successful
 
 Example:
 ```
diff --git a/Documentation/metrics.txt b/Documentation/metrics.txt
index d8b6250..70352dc 100644
--- a/Documentation/metrics.txt
+++ b/Documentation/metrics.txt
@@ -93,6 +93,9 @@
 ** `plugin`:
    The name of the plugin that performed the operation.
 
+Performance metrics can be enabled via the
+link:config.gerrit.html#tracing.exportPerformanceMetrics[`tracing.exportPerformanceMetrics`]
+setting.
 
 === Pushes
 
@@ -453,6 +456,15 @@
 === Group
 
 * `group/guess_relevant_groups_latency`: Latency for guessing relevant groups.
+* `group/handles_count`: Number of calls to GroupBackend.handles.
+* `group/get_count`: Number of calls to GroupBackend.get.
+* `group/suggest_count`: Number of calls to GroupBackend.suggest.
+* `group/contains_count`: Number of calls to GroupMemberships.contains.
+* `group/contains_any_of_count`: Number of calls to
+  GroupMemberships.containsAnyOf.
+* `group/intersection_count`: Number of calls to GroupMemberships.intersection.
+* `group/known_groups_count`: Number of calls to GroupMemberships.getKnownGroups.
+
 
 === Replication Plugin
 
diff --git a/Documentation/note-db.txt b/Documentation/note-db.txt
index b376d6e..0e1dfd0 100644
--- a/Documentation/note-db.txt
+++ b/Documentation/note-db.txt
@@ -253,5 +253,5 @@
 
 In case of rollback from NoteDB to ReviewDB, all the meta refs and the
 sequence ref need to be removed.
-The [remove-notedb-refs.sh,role=external,window=_blank](https://gerrit.googlesource.com/gerrit/+/refs/heads/master/contrib/remove-notedb-refs.sh)
+The link:https://gerrit.googlesource.com/gerrit/+/refs/heads/master/contrib/remove-notedb-refs.sh[remove-notedb-refs.sh,role=external,window=_blank]
 script has been written to automate this process.
diff --git a/Documentation/pg-plugin-checks-api.txt b/Documentation/pg-plugin-checks-api.txt
index 4e93da1..e4cb5d0 100644
--- a/Documentation/pg-plugin-checks-api.txt
+++ b/Documentation/pg-plugin-checks-api.txt
@@ -10,6 +10,10 @@
 when a change page is loaded. Such a call would return a list of `Runs` and each
 run can contain a list of `Results`.
 
+`Results` messages will render as markdown. It follows the
+[CommonMark](https://commonmark.org/help/) spec except inline images and direct
+HTML are not rendered and kept as plaintext.
+
 The details of the ChecksApi are documented in the
 link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/app/api/checks.ts[source code].
 Note that this link points to the `master` branch and might thus reflect a
@@ -24,8 +28,8 @@
 Here are some examples of open source plugins that make use of the Checks API:
 
 * link:https://gerrit.googlesource.com/plugins/checks/+/master/gr-checks/plugin.js[Gerrit Checks Plugin]
-* link:https://chromium.googlesource.com/infra/gerrit-plugins/buildbucket/+/master/src/main/resources/static/buildbucket.js[Chromium Buildbucket Plugin]
-* link:https://chromium.googlesource.com/infra/gerrit-plugins/code-coverage/+/master/src/main/resources/static/chromium-coverage.js[Chromium Coverage Plugin]
+* link:https://chromium.googlesource.com/infra/gerrit-plugins/buildbucket/+/main/web/plugin.ts[Chromium Buildbucket Plugin]
+* link:https://chromium.googlesource.com/infra/gerrit-plugins/code-coverage/+/main/web/plugin.ts[Chromium Coverage Plugin]
 
 [[register]]
 == register
diff --git a/Documentation/pg-plugin-dev.txt b/Documentation/pg-plugin-dev.txt
index dc7986f..d7343c2 100644
--- a/Documentation/pg-plugin-dev.txt
+++ b/Documentation/pg-plugin-dev.txt
@@ -30,10 +30,6 @@
 });
 ```
 
-You can find more elaborate examples in the
-link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/app/samples/[polygerrit-ui/app/samples/]
-directory of the source tree.
-
 [[low-level-api-concepts]]
 == Low-level DOM API concepts
 
@@ -95,31 +91,36 @@
 [[low-level-style]]
 === Styling DOM Elements
 
-A plugin may provide Polymer's
-https://polymer-library.polymer-project.org/3.0/docs/devguide/style-shadow-dom[style
-modules,role=external,window=_blank] to style individual endpoints using
-`plugin.registerStyleModule(endpointName, moduleName)`. A style must be defined
-as a standalone `<dom-module>` defined in the same .js file.
+Gerrit only offers customized CSS styling by setting
+link:https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_[custom_properties]
+(aka css variables).
 
-See
-link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/app/samples/theme-plugin.js[samples/theme-plugin.js]
-for an example.
+See link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/app/styles/themes/[app-theme.ts]
+for the list of available variables.
+
+You can just create `<style>` elements yourself and add them to the
+`document.head`, but for your convenience the Plugin API provides a simple
+`styleApi().insertCSSRule()` method for doing just that. Typically you would
+define a CSS rule for `html`, which is always applied, or for a specific theme
+such as `html.lightTheme`. 
 
 ``` js
-const styleElement = document.createElement('dom-module');
-styleElement.innerHTML =
- `<template>
-    <style>
-    html {
-      --primary-text-color: red;
-    }
-   </style>
- </template>`;
-
-styleElement.register('some-style-module');
-
 Gerrit.install(plugin => {
-  plugin.registerStyleModule('change-metadata', 'some-style-module');
+  plugin.styleApi().insertCSSRule(`
+    html {
+      --header-text-color: black;
+    }
+  `);
+  plugin.styleApi().insertCSSRule(`
+    html.lightTheme {
+      --header-background-color: red;
+    }
+  `);
+  plugin.styleApi().insertCSSRule(`
+    html.darkTheme {
+      --header-background-color: blue;
+    }
+  `);
 });
 ```
 
@@ -146,10 +147,6 @@
 binding,role=external,window=_blank] for plugins that don't use Polymer. Can be used to bind element
 attribute changes to callbacks.
 
-See
-link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/app/samples/bind-parameters.js[samples/bind-parameters.js]
-for an example.
-
 === hook
 `plugin.hook(endpointName, opt_options)`
 
@@ -166,11 +163,6 @@
 
 See link:pg-plugin-endpoints.html[endpoints].
 
-=== registerStyleModule
-`plugin.registerStyleModule(endpointName, moduleName)`
-
-See link:#low-level-style[above].
-
 === on
 Register a JavaScript callback to be invoked when events occur within
 the web interface. Signature
diff --git a/Documentation/pg-plugin-endpoints.txt b/Documentation/pg-plugin-endpoints.txt
index c16d0d4..dd82f27 100644
--- a/Documentation/pg-plugin-endpoints.txt
+++ b/Documentation/pg-plugin-endpoints.txt
@@ -132,6 +132,10 @@
 === settings-screen
 This endpoint is situated at the end of the body of the settings screen.
 
+=== profile
+This endpoint is situated at the top of the Profile section of the settings
+screen below the section description text.
+
 === reply-text
 This endpoint wraps the textarea in the reply dialog.
 
@@ -141,6 +145,11 @@
 === header-title
 This endpoint wraps the title-text in the application header.
 
+=== cherrypick-main
+This endpoint is located in the cherrypick dialog. It has two slots `top`
+and `bottom` and `changes` as a parameter with the list of changes (or
+just the one change) to be cherrypicked.
+
 === confirm-revert-change
 This endpoint is inside the confirm revert dialog. By default it displays a
 generic confirmation message regarding reverting the change. Plugins may add
@@ -228,3 +237,13 @@
 +
 current revision displayed, an instance of
 link:rest-api-changes.html#revision-info[RevisionInfo]
+
+=== account-status-icon
+The `account-status-icon` extension point adds an icon to all account chips and
+labels.
+
+In addition to default parameters, the following are available:
+
+* `accountId`
++
+the Id of the account that the status icon should correspond to.
\ No newline at end of file
diff --git a/Documentation/pgm-daemon.txt b/Documentation/pgm-daemon.txt
index 586f685..560fb92 100644
--- a/Documentation/pgm-daemon.txt
+++ b/Documentation/pgm-daemon.txt
@@ -10,6 +10,10 @@
   -d <SITE_PATH>
   [--enable-httpd | --disable-httpd]
   [--enable-sshd | --disable-sshd]
+  [--debug]
+  [--debug-port]
+  [--debug_address]
+  [--suspend]
   [--console-log]
   [--replica]
   [--headless]
@@ -39,6 +43,17 @@
 	Enable (or disable) the internal SSH daemon, answering SSH
 	clients and remotely executed commands.  Enabled by default.
 
+--debug::
+	Start JVM in debug mode.
+
+--debug-port::
+--debug_address:
+	Specify which JVM debug port/address to use. The default debug address is 8000.
+
+--suspend::
+	Start JVM debug in suspended mode. The JVM will await for a debugger
+	to attach before proceeding with the start.
+
 --replica::
 	Run in replica mode, permitting only read operations
     by clients.  Commands which modify state such as
diff --git a/Documentation/pgm-reindex.txt b/Documentation/pgm-reindex.txt
index 5167277..0653d8d 100644
--- a/Documentation/pgm-reindex.txt
+++ b/Documentation/pgm-reindex.txt
@@ -20,7 +20,8 @@
 
 == OPTIONS
 --threads::
-	Number of threads to use for indexing.
+	Number of threads to use for indexing. Default is
+	link:config-gerrit.html#index.batchThreads[index.batchThreads]
 
 --changes-schema-version::
 	Schema version to reindex; default is most recent version.
@@ -35,6 +36,10 @@
 	Reindex only index with given name. This option can be supplied
 	more than once to reindex multiple indices.
 
+--disable-cache-stats::
+	Disables printing cache statistics at the end of program to reduce
+	noise. Defaulted when reindex is run from init on a new site.
+
 == CONTEXT
 The secondary index must be enabled. See
 link:config-gerrit.html#index.type[index.type].
diff --git a/Documentation/rest-api-access.txt b/Documentation/rest-api-access.txt
index 45a39d8..991f36c 100644
--- a/Documentation/rest-api-access.txt
+++ b/Documentation/rest-api-access.txt
@@ -388,7 +388,7 @@
 |`revision`           ||
 The revision of the `refs/meta/config` branch from which the access
 rights were loaded.
-|`inherits_from`      |not set for the `All-Project` project|
+|`inherits_from`      |not set for the `All-Projects` project|
 The parent project from which permissions are inherited as a
 link:rest-api-projects.html#project-info[ProjectInfo] entity.
 |`local`              ||
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index ae0c0a6..7e06e4a 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1284,7 +1284,7 @@
   )]}'
   {
     "changes_per_page": 25,
-    "theme": "LIGHT",
+    "theme": "AUTO",
     "date_format": "STD",
     "time_format": "HHMM_12",
     "diff_view": "SIDE_BY_SIDE",
@@ -1292,6 +1292,7 @@
     "mute_common_path_prefixes": true,
     "publish_comments_on_push": true,
     "work_in_progress_by_default": true,
+    "allow_browser_notifications": true,
     "default_base_for_merges": "FIRST_PARENT",
     "my": [
       {
@@ -1344,6 +1345,7 @@
     "size_bar_in_change_table": true,
     "disable_keyboard_shortcuts": true,
     "disable_token_highlighting": true,
+    "allow_browser_notifications": false,
     "diff_view": "SIDE_BY_SIDE",
     "mute_common_path_prefixes": true,
     "my": [
@@ -2658,7 +2660,7 @@
 Allowed values are `10`, `25`, `50`, `100`.
 |`theme`                        ||
 Which theme to use.
-Allowed values are `DARK` or `LIGHT`.
+Allowed values are `AUTO` or `DARK` or `LIGHT`.
 |`expand_inline_diffs`          |not set if `false`|
 Whether to expand diffs inline instead of opening as separate page
 (Gerrit web app UI only).
@@ -2686,6 +2688,8 @@
 |`signed_off_by`                |not set if `false`|
 Whether to insert Signed-off-by footer in changes created with the
 inline edit feature.
+|`allow_browser_notifications`  |not set if `false`|
+Whether to prompt user to enable browser notification in browser.
 |`my`                           ||
 The menu items of the `MY` top menu as a list of
 link:rest-api-config.html#top-menu-item-info[TopMenuItemInfo] entities.
@@ -2729,7 +2733,7 @@
 Allowed values are `10`, `25`, `50`, `100`.
 |`theme`                        |optional|
 Which theme to use.
-Allowed values are `DARK` or `LIGHT`.
+Allowed values are `AUTO` or `DARK` or `LIGHT`.
 |`expand_inline_diffs`          |not set if `false`|
 Whether to expand diffs inline instead of opening as separate page
 (Gerrit web app UI only).
@@ -2755,6 +2759,8 @@
 |`signed_off_by`                |optional|
 Whether to insert Signed-off-by footer in changes created with the
 inline edit feature.
+|`allow_browser_notifications`  |not set if `false`|
+Whether to prompt user to enable browser notification in browser.
 |`my`                           |optional|
 The menu items of the `MY` top menu as a list of
 link:rest-api-config.html#top-menu-item-info[TopMenuItemInfo] entities.
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 33e7804..2f144c6 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -77,8 +77,8 @@
 link:user-search.html#_search_operators[query string] must be provided
 by the `q` parameter. The `n` parameter can be used to limit the
 returned results. The `no-limit` parameter can be used remove the default
-limit on queries and return all results. This might not be supported by
-all index backends.
+limit on queries and return all results (does not apply to anonymous requests).
+This might not be supported by all index backends.
 
 As result a list of link:#change-info[ChangeInfo] entries is returned.
 The change output is sorted by the last update time, most recently
@@ -103,15 +103,17 @@
       "id": "demo~master~Idaf5e098d70898b7119f6f4af5a6c13343d64b57",
       "project": "demo",
       "branch": "master",
-      "attention_set": [
-        {
+      "attention_set": {
+        "1000096": {
           "account": {
-            "name": "John Doe"
+            "_account_id": 1000096,
+            "name": "John Doe",
+            "email": "john.doe@example.com"
           },
-         "last_update": "2012-07-17 07:19:27.766000000",
-         "reason": "reviewer or cc replied"
+          "last_update": "2012-07-17 07:19:27.766000000",
+          "reason": "reviewer or cc replied"
         }
-      ]
+      },
       "change_id": "Idaf5e098d70898b7119f6f4af5a6c13343d64b57",
       "subject": "One change",
       "status": "NEW",
@@ -224,19 +226,18 @@
 
 [[labels]]
 --
-* `LABELS`: a summary of each label required for submit, and
-  approvers that have granted (or rejected) with that label
-  as well as all reviewers by state, and reviewers that may
-  be removed by the current user.
+* `LABELS`: Includes the `labels` field which contains all information about
+labels and approvers that have granted (or rejected) that label. Does not
+include the `permitted_voting_range` attribute with the list of
+link:#approval-info[ApprovalInfo] of the `all` attribute.
 --
 
 [[detailed-labels]]
 --
-* `DETAILED_LABELS`: detailed label information, including numeric
-  values of all existing approvals, recognized label values, values
-  permitted to be set by any reviewer and the change owner, all
-  reviewers by state, and reviewers that may be removed by the
-  current user.
+* `DETAILED_LABELS`: Includes the `labels` field which contains all information
+about labels and approvers that have granted (or rejected) that label, including
+the `permitted_voting_range` attribute with the list of
+link:#approval-info[ApprovalInfo] of the `all` attribute.
 --
 
 [[submit-requirements]]
@@ -546,15 +547,17 @@
     "id": "myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940",
     "project": "myProject",
     "branch": "master",
-    "attention_set": [
-      {
+    "attention_set": {
+      "1000096": {
         "account": {
-          "name": "John Doe"
+          "_account_id": 1000096,
+          "name": "John Doe",
+          "email": "john.doe@example.com"
         },
-       "last_update": "2013-02-21 11:16:36.775000000",
-       "reason": "reviewer or cc replied"
+        "last_update": "2013-02-21 11:16:36.775000000",
+        "reason": "reviewer or cc replied"
       }
-    ]
+    },
     "change_id": "I8473b95934b5732ac55d26311a706c9c2bde9940",
     "subject": "Implementing Feature X",
     "status": "NEW",
@@ -613,15 +616,17 @@
   )]}'
   {
     "added": {
-      "attention_set": [
-        {
+      "attention_set": {
+        "1000096": {
           "account": {
-            "name": "John Doe"
+            "_account_id": 1000096,
+            "name": "John Doe",
+            "email": "john.doe@example.com"
           },
-         "last_update": "2013-02-21 11:16:36.775000000",
-         "reason": "reviewer or cc replied"
+          "last_update": "2013-02-21 11:16:36.775000000",
+          "reason": "reviewer or cc replied"
         }
-      ]
+      },
       "updated": "2013-02-21 11:16:36.775000000",
       "topic": "new-topic"
     },
@@ -652,15 +657,17 @@
       "id": "myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940",
       "project": "myProject",
       "branch": "master",
-      "attention_set": [
-        {
+      "attention_set": {
+        "1000096": {
           "account": {
-            "name": "John Doe"
+            "_account_id": 1000096,
+            "name": "John Doe",
+            "email": "john.doe@example.com"
           },
-         "last_update": "2013-02-21 11:16:36.775000000",
-         "reason": "reviewer or cc replied"
+          "last_update": "2013-02-21 11:16:36.775000000",
+          "reason": "reviewer or cc replied"
         }
-      ],
+      },
       "change_id": "I8473b95934b5732ac55d26311a706c9c2bde9940",
       "subject": "Implementing Feature X",
       "status": "NEW",
@@ -720,18 +727,18 @@
     "id": "myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940",
     "project": "myProject",
     "branch": "master",
-    "attention_set": [
-      {
+    "attention_set": {
+      "1000096": {
         "account": {
           "_account_id": 1000096,
           "name": "John Doe",
           "email": "john.doe@example.com",
           "username": "jdoe"
         },
-       "last_update": "2013-02-21 11:16:36.775000000",
-       "reason": "reviewer or cc replied"
+        "last_update": "2013-02-21 11:16:36.775000000",
+        "reason": "reviewer or cc replied"
       }
-    ]
+    },
     "change_id": "I8473b95934b5732ac55d26311a706c9c2bde9940",
     "subject": "Implementing Feature X",
     "status": "NEW",
@@ -795,8 +802,8 @@
           }
         ]
         "values": {
-          "-2": "This shall not be merged",
-          "-1": "I would prefer this is not merged as is",
+          "-2": "This shall not be submitted",
+          "-1": "I would prefer this is not submitted as is",
           " 0": "No score",
           "+1": "Looks good to me, but someone else must approve",
           "+2": "Looks good to me, approved"
@@ -817,6 +824,26 @@
         "+2"
       ]
     },
+    "removable_labels": {
+      "Code-Review": {
+        "-1": [
+          {
+            "_account_id": 1000096,
+            "name": "John Doe",
+            "email": "john.doe@example.com",
+            "username": "jdoe"
+          }
+        ],
+        "+1": [
+          {
+            "_account_id": 1000097,
+            "name": "Jane Roe",
+            "email": "jane.roe@example.com",
+            "username": "jroe"
+          }
+        ]
+      }
+    },
     "removable_reviewers": [
       {
         "_account_id": 1000096,
@@ -1506,6 +1533,223 @@
   The change could not be rebased due to a path conflict during merge.
 ----
 
+[[rebase-chain]]
+=== Rebase Chain
+--
+'POST /changes/link:#change-id[\{change-id\}]/rebase:chain'
+--
+
+Rebases an ancestry chain of changes.
+
+The operated change is treated as the chain tip. All unsubmitted ancestors are rebased.
+
+Requires a linear ancestry relation (single parenting throughout the chain).
+
+Optionally, the parent revision (of the oldest ancestor to be rebased) can be changed to another
+change, revision or branch through the link:#rebase-input[RebaseInput] entity.
+
+If the chain is outdated, i.e., there's a change that depends on an old revision of its parent, the
+result is the same as individually rebasing all outdated changes on top of their parent's latest
+revision before running the rebase chain action.
+
+.Request
+----
+  POST /changes/myProject~master~I08a021fb07b83fe845140a2c11508b3bdd93b48f/rebase:chain HTTP/1.0
+  Content-Type: application/json;charset=UTF-8
+
+  {
+    "base" : "1234",
+  }
+----
+
+As response a link:#rebase-chain-info[RebaseChainInfo] entity is returned that
+describes the rebased changes. Information about the current patch sets
+are included.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "rebased_changes": [
+      {
+        "id": "myProject~master~I0e534de9d7f0d6f35b71f7d726acf835b2110c66",
+        "project": "myProject",
+        "branch": "master",
+        "hashtags": [
+
+        ],
+        "change_id": "I0e534de9d7f0d6f35b71f7d726acf835b2110c66",
+        "subject": "456",
+        "status": "NEW",
+        "created": "2022-11-21 20: 51: 31.000000000",
+        "updated": "2022-11-21 20: 56: 49.000000000",
+        "submit_type": "MERGE_IF_NECESSARY",
+        "insertions": 0,
+        "deletions": 0,
+        "total_comment_count": 0,
+        "unresolved_comment_count": 0,
+        "has_review_started": true,
+        "meta_rev_id": "a2a6692213f546e1045ecf4647439fac8d6d8faa",
+        "_number": 21,
+        "owner": {
+          "_account_id": 1000000
+        },
+        "current_revision": "c3b2ba222d42a56e05c90f88d4509a124620517d",
+        "revisions": {
+          "c3b2ba222d42a56e05c90f88d4509a124620517d": {
+            "kind": "NO_CHANGE",
+            "_number": 2,
+            "created": "2022-11-21 20: 56: 49.000000000",
+            "uploader": {
+              "_account_id": 1000000
+            },
+            "ref": "refs/changes/21/21/2",
+            "fetch": {
+
+            },
+            "commit": {
+              "parents": [
+                {
+                  "commit": "7803f427dd7c4a2441466e4d740a1850dcee1af4",
+                  "subject": "123"
+                }
+              ],
+              "author": {
+                "name": "Nitzan Gur-Furman",
+                "email": "nitzan@google.com",
+                "date": "2022-11-21 20: 49: 39.000000000",
+                "tz": 60
+              },
+              "committer": {
+                "name": "Administrator",
+                "email": "admin@example.com",
+                "date": "2022-11-21 20: 56: 49.000000000",
+                "tz": 60
+              },
+              "subject": "456",
+              "message": "456\n"
+            },
+            "description": "Rebase"
+          }
+        },
+        "requirements": [
+
+        ],
+        "submit_records": [
+          {
+            "rule_name": "gerrit~DefaultSubmitRule",
+            "status": "NOT_READY",
+            "labels": [
+              {
+                "label": "Code-Review",
+                "status": "NEED"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "id": "myProject~master~I08a021fb07b83fe845140a2c11508b3bdd93b48f",
+        "project": "myProject",
+        "branch": "master",
+        "hashtags": [
+
+        ],
+        "change_id": "I08a021fb07b83fe845140a2c11508b3bdd93b48f",
+        "subject": "789",
+        "status": "NEW",
+        "created": "2022-11-21 20: 51: 31.000000000",
+        "updated": "2022-11-21 20: 56: 49.000000000",
+        "submit_type": "MERGE_IF_NECESSARY",
+        "insertions": 0,
+        "deletions": 0,
+        "total_comment_count": 0,
+        "unresolved_comment_count": 0,
+        "has_review_started": true,
+        "meta_rev_id": "3bfb843fea471f96e16b9199c3a30fff0285bc45",
+        "_number": 22,
+        "owner": {
+          "_account_id": 1000000
+        },
+        "current_revision": "77eb17a9501a5c21963bc6af56085e60f281acbb",
+        "revisions": {
+          "77eb17a9501a5c21963bc6af56085e60f281acbb": {
+            "kind": "NO_CHANGE",
+            "_number": 2,
+            "created": "2022-11-21 20: 56: 49.000000000",
+            "uploader": {
+              "_account_id": 1000000
+            },
+            "ref": "refs/changes/22/22/2",
+            "fetch": {
+
+            },
+            "commit": {
+              "parents": [
+                {
+                  "commit": "c3b2ba222d42a56e05c90f88d4509a124620517d",
+                  "subject": "456"
+                }
+              ],
+              "author": {
+                "name": "Nitzan Gur-Furman",
+                "email": "nitzan@google.com",
+                "date": "2022-11-21 20: 51: 07.000000000",
+                "tz": 60
+              },
+              "committer": {
+                "name": "Administrator",
+                "email": "admin@example.com",
+                "date": "2022-11-21 20: 56: 49.000000000",
+                "tz": 60
+              },
+              "subject": "789",
+              "message": "789\n"
+            },
+            "description": "Rebase"
+          }
+        },
+        "requirements": [
+
+        ],
+        "submit_records": [
+          {
+            "rule_name": "gerrit~DefaultSubmitRule",
+            "status": "NOT_READY",
+            "labels": [
+              {
+                "label": "Code-Review",
+                "status": "NEED"
+              }
+            ]
+          }
+        ]
+      }
+    ],
+  }
+----
+
+If the change cannot be rebased, e.g. due to conflicts, the response is
+"`409 Conflict`" and the error message is contained in the response
+body.
+
+.Response
+----
+  HTTP/1.1 409 Conflict
+  Content-Disposition: attachment
+  Content-Type: text/plain; charset=UTF-8
+
+  Change I0e534de9d7f0d6f35b71f7d726acf835b2110c66 could not be rebased due to a conflict during
+  merge.
+
+  merge conflict(s):
+  a.txt
+----
+
 [[move-change]]
 === Move Change
 --
@@ -1857,7 +2101,8 @@
 
 * The given change.
 * If link:config-gerrit.html#change.submitWholeTopic[`change.submitWholeTopic`]
-  is enabled, include all open changes with the same topic.
+  is enabled OR if the `o=TOPIC_CLOSURE` query parameter is passed, include all
+  open changes with the same topic.
 * For each change whose submit type is not CHERRY_PICK, include unmerged
   ancestors targeting the same branch.
 
@@ -1884,7 +2129,7 @@
 
 Standard link:#query-options[formatting options] can be specified
 with the `o` parameter, as well as the `submitted_together` specific
-option `NON_VISIBLE_CHANGES`.
+options `NON_VISIBLE_CHANGES` and `TOPIC_CLOSURE`.
 
 .Response
 ----
@@ -1925,8 +2170,8 @@
             }
           ],
           "values": {
-            "-2": "This shall not be merged",
-            "-1": "I would prefer this is not merged as is",
+            "-2": "This shall not be submitted",
+            "-1": "I would prefer this is not submitted as is",
             " 0": "No score",
             "+1": "Looks good to me, but someone else must approve",
             "+2": "Looks good to me, approved"
@@ -2022,8 +2267,8 @@
             }
           ],
           "values": {
-            "-2": "This shall not be merged",
-            "-1": "I would prefer this is not merged as is",
+            "-2": "This shall not be submitted",
+            "-1": "I would prefer this is not submitted as is",
             " 0": "No score",
             "+1": "Looks good to me, but someone else must approve",
             "+2": "Looks good to me, approved"
@@ -2124,6 +2369,66 @@
   HTTP/1.1 204 No Content
 ----
 
+[[apply-patch]]
+=== Create patch-set from patch
+--
+'POST /changes/link:#change-id[\{change-id\}]/patch:apply'
+--
+
+Creates a new patch set on a destination change from the provided patch.
+
+The patch must be provided in the request body, inside a
+link:#applypatchpatchset-input[ApplyPatchPatchSetInput] entity.
+
+If a base commit is given, the patch is applied on top of it. Otherwise, the
+patch is applied on top of the target change's branch tip.
+
+Applying the patch will fail if the destination change is closed, or in case of any conflicts.
+
+.Request
+----
+  POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/patch:apply HTTP/1.0
+  Content-Type: application/json; charset=UTF-8
+
+  {
+    "patch": {
+      "patch": "new file mode 100644\n--- /dev/null\n+++ b/a_new_file.txt\n@@ -0,0 +1,2 @@ \
++Patch compatible `git diff` output \
++For example: `link:#get-patch[<gerrit patch>] | base64 -d | sed -z 's/\n/\\n/g'`"
+    }
+  }
+----
+
+As response a link:#change-info[ChangeInfo] entity is returned that
+describes the destination change after applying the patch.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "id": "myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9941",
+    "project": "myProject",
+    "branch": "release-branch",
+    "change_id": "I8473b95934b5732ac55d26311a706c9c2bde9941",
+    "subject": "Original change subject",
+    "status": "NEW",
+    "created": "2013-02-01 09:59:32.126000000",
+    "updated": "2013-02-21 11:16:36.775000000",
+    "mergeable": true,
+    "insertions": 12,
+    "deletions": 11,
+    "_number": 3965,
+    "owner": {
+      "name": "John Doe"
+    },
+    "current_revision": "184ebe53805e102605d11f6b143486d15c23a09c"
+  }
+----
+
 [[get-included-in]]
 === Get Included In
 --
@@ -2579,35 +2884,6 @@
   }
 ----
 
-[[ignore]]
-=== Ignore
---
-'PUT /changes/link:#change-id[\{change-id\}]/ignore'
---
-
-Marks a change as ignored. The change will not be shown in the incoming
-reviews dashboard, and email notifications will be suppressed. Ignoring
-a change does not cause the change's "updated" timestamp to be modified,
-and the owner is not notified.
-
-.Request
-----
-  PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/ignore HTTP/1.0
-----
-
-[[unignore]]
-=== Unignore
---
-'PUT /changes/link:#change-id[\{change-id\}]/unignore'
---
-
-Un-marks a change as ignored.
-
-.Request
-----
-  PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/unignore HTTP/1.0
-----
-
 [[get-hashtags]]
 === Get Hashtags
 --
@@ -2825,8 +3101,18 @@
 --
 
 Tests a submit requirement and returns the result as a
-link:#submit-requirement-result-info[SubmitRequirementResultInfo]. The request
-body must contain a link:#submit-requirement-input[SubmitRequirementInput].
+link:#submit-requirement-result-info[SubmitRequirementResultInfo].
+
+The submit requirement can be specified in one of the following ways:
+
+  * In the request body as a link:#submit-requirement-input[SubmitRequirementInput].
+  * By passing the two request parameters `sr-name` and
+ `refs-config-change-id`. The submit requirement will then be loaded from
+ the project config pointed to by the latest patchset of this change ID.
+ The `sr-name` should point to an existing submit-requirement and the
+ `refs-config-change-id` must be a valid change identifier for a change in the
+ refs/meta/config branch, otherwise the request would fail with
+ `400 Bad Request`.
 
 Note that this endpoint does not modify the change resource.
 
@@ -2946,12 +3232,19 @@
   Content-Type: application/json; charset=UTF-8
 
   {
-    "binary_content": "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=="
+    "binary_content": "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==",
+    "file_mode": 100755
   }
 ----
 
 Note that it must be base-64 encoded data uri.
 
+The "file_mode" field is optional, and if provided must be in octal format. The field
+indicates whether the file is executable or not and has a value of either 100755
+(executable) or 100644 (not executable). If it's unset, this indicates no change
+has been made. New files default to not being executable if this parameter is not
+provided
+
 When change edit doesn't exist for this change yet it is created. When file
 content isn't provided, it is wiped out for that file. As response
 "`204 No Content`" is returned.
@@ -3973,8 +4266,8 @@
           }
         ]
         "values": {
-          "-2": "This shall not be merged",
-          "-1": "I would prefer this is not merged as is",
+          "-2": "This shall not be submitted",
+          "-1": "I would prefer this is not submitted as is",
           " 0": "No score",
           "+1": "Looks good to me, but someone else must approve",
           "+2": "Looks good to me, approved"
@@ -4047,6 +4340,16 @@
 Retrieves related changes of a revision.  Related changes are changes that either
 depend on, or are dependencies of the revision.
 
+Additional fields can be obtained by adding `o` parameters. Since these may slow
+down processing they are disabled by default. Currently a single parameter is
+supported:
+
+[[get-related-changes-submittable]]
+--
+* `SUBMITTABLE`: Compute the `submittable` field in the returned
+  link:#related-change-and-commit-info[RelatedChangeAndCommitInfo] entities.
+--
+
 .Request
 ----
   GET /changes/gerrit~master~I5e4fc08ce34d33c090c9e0bf320de1b17309f774/revisions/b1cb4caa6be46d12b94c25aa68aebabcbb3f53fe/related HTTP/1.0
@@ -4581,60 +4884,6 @@
 If the `path` parameter is set, the returned content is a diff of the single
 file that the path refers to.
 
-[[submit-preview]]
-=== Submit Preview
---
-'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/preview_submit'
---
-Gets a file containing thin bundles of all modified projects if this
-change was submitted. The bundles are named `${ProjectName}.git`.
-Each thin bundle contains enough to construct the state in which a project would
-be in if this change were submitted. The base of the thin bundles are the
-current target branches, so to make use of this call in a non-racy way, first
-get the bundles and then fetch all projects contained in the bundle.
-(This assumes no non-fastforward pushes).
-
-You need to give a parameter '?format=zip' or '?format=tar' to specify the
-format for the outer container. It is always possible to use tgz, even if
-tgz is not in the list of allowed archive formats.
-
-To make good use of this call, you would roughly need code as found at:
-----
- $ curl -Lo preview_submit_test.sh http://review.example.com:8080/tools/scripts/preview_submit_test.sh
-----
-.Request
-----
-  GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/current/preview_submit?zip HTTP/1.0
-----
-
-.Response
-----
-  HTTP/1.1 200 OK
-  Date: Tue, 13 Sep 2016 19:13:46 GMT
-  Content-Disposition: attachment; filename="submit-preview-147.zip"
-  X-Content-Type-Options: nosniff
-  Cache-Control: no-cache, no-store, max-age=0, must-revalidate
-  Pragma: no-cache
-  Expires: Mon, 01 Jan 1990 00:00:00 GMT
-  Content-Type: application/x-zip
-  Transfer-Encoding: chunked
-
-  [binary stuff]
-----
-
-In case of an error, the response is not a zip file but a regular json response,
-containing only the error message:
-
-.Response
-----
-  HTTP/1.1 200 OK
-  Content-Disposition: attachment
-  Content-Type: application/json; charset=UTF-8
-
-  )]}'
-  "Anonymous users cannot submit"
-----
-
 [[get-mergeable]]
 === Get Mergeable
 --
@@ -5084,6 +5333,9 @@
 with a new message, which contains the name of the user who deletes
 the comment and the reason why it's deleted.
 
+This endpoint also marks the comment as resolved since deleted comments are not
+actionable.
+
 Note that only users with the
 link:access-control.html#capability_administrateServer[Administrate Server]
 global capability are permitted to delete a comment.
@@ -5224,8 +5476,8 @@
   }
 ----
 
-[[get-ported-comments]]
-=== Get Ported Comments
+[[list-ported-comments]]
+=== List Ported Comments
 --
 'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/ported_comments'
 --
@@ -5308,24 +5560,24 @@
   }
 ----
 
-[[get-ported-drafts]]
-=== Get Ported Drafts
+[[list-ported-drafts]]
+=== List Ported Drafts
 --
 'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/ported_drafts'
 --
 
 Ports draft comments of other revisions to the requested revision.
 
-This endpoint behaves similarly to the link:#get-ported-comments[Get Ported Comments] endpoint.
+This endpoint behaves similarly to the link:#list-ported-comments[List Ported Comments] endpoint.
 With this endpoint, only draft comments of the calling user are ported, though. If a draft comment
 is a reply to a published comment, only the ported draft comment is returned.
 
 Depending on the filtering rules, it's possible that this endpoint returns a draft comment which is
 a reply to a comment thread which is not returned by the
-link:#get-ported-comments[Get Ported Comments] endpoint. That's intended behavior. Callers must be
+link:#list-ported-comments[List Ported Comments] endpoint. That's intended behavior. Callers must be
 able to handle this situation. The same holds for drafts which are a reply to a robot comment.
 
-Different than the link:#get-ported-comments[Get Ported Comments] endpoint, the `author` of the
+Different than the link:#list-ported-comments[List Ported Comments] endpoint, the `author` of the
 returned comments is not filled for this endpoint as only comments of the calling user are returned.
 
 This endpoint requires authentication.
@@ -5357,8 +5609,8 @@
   }
 ----
 
-[[apply-fix]]
-=== Apply Fix
+[[apply-stored-fix]]
+=== Apply Stored Fix
 --
 'POST /changes/<<change-id,\{change-id\}>>/revisions/<<revision-id,\{revision-id\}>>/fixes/<<fix-id,\{fix-id\}>>/apply'
 --
@@ -5425,6 +5677,101 @@
   The existing change edit could not be merged with another tree.
 ----
 
+[[apply-provided-fix]]
+==== Apply Provided Fix
+--
+'POST /changes/<<change-id,\{change-id\}>>/revisions/<<revision-id,\{revision-id\}>>/fix:apply'
+--
+Applies a list of <<fix-replacement-info,FixReplacementInfo>> loaded from the
+<<apply-provided-fix-input,ApplyProvidedFixInput>> entity. The fixes are passed as part of the request body. The
+application of the fixes creates a new change edit. `Apply Provided Fix` can only be applied on the current
+patchset.
+
+.Request
+----
+  POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/fix:apply HTTP/1.0
+  Content-Type: application/json; charset=UTF-8
+
+  {
+    "fix_replacement_infos": [
+      {
+        "path": "abcd.txt",
+        "range": {
+          "start_line": 2,
+          "start_character": 2,
+          "end_line": 2,
+          "end_character": 5
+        },
+        "replacement": "abcdefg"
+      }
+    ]
+  }
+----
+
+If the `ApplyProvidedFixInput` was successfully applied, an link:#edit-info[EditInfo] describing the
+resulting change edit is returned.
+
+.Response
+----
+  HTTP/1.1 200 OK
+    Content-Disposition: attachment
+    Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "commit": {
+      "commit": "bd43e48c33d2b1a03485040eba38cefc505f7997",
+      "parents": [
+        {
+          "commit": "9825962f8ab6da89afebad3f5034db05fb4b7560"
+        }
+      ],
+      "author": {
+          "name": "John Doe",
+          "email": "john.doe@example.com",
+          "date": "2022-05-07 15:21:27.000000000",
+          "tz": 120
+      },
+      "committer": {
+           "name": "Jane Doe",
+           "email": "jane.doe@example.com",
+           "date": "2022-05-07 15:35:43.000000000",
+           "tz": 120
+      },
+      "subject": "Implement feature X",
+      "message": "Implement feature X\n\nWith this feature ..."
+    },
+    "base_patch_set_number": 1,
+    "base_revision": "86d87686ce0ef7f7c536bfc7e9a66f5a6fa5d658",
+    "ref": "refs/users/01/1000001/edit-1/1"
+  }
+----
+
+If the application failed due to presence of an existing change edit,
+the response "`400 Bad Request`" including an error message in the response body
+is returned.
+
+.Response
+----
+  HTTP/1.1 400 Bad Request
+  Content-Disposition: attachment
+  Content-Type: text/plain; charset=UTF-8
+
+  Change edit already exists. A new change edit can't be created
+----
+
+If the application failed due to application on a previous patch set, the response
+"`409 Conflict`" including an error message in the response body is returned.
+
+.Response
+----
+  HTTP/1.1 409 Conflict
+  Content-Disposition: attachment
+  Content-Type: text/plain; charset=UTF-8
+
+  A change edit may only be created for the current patch set 1,2 (and not for 1,1)
+----
+
 [[list-files]]
 === List Files
 --
@@ -5781,8 +6128,8 @@
 differences are reported in the result.  Valid values are `IGNORE_NONE`,
 `IGNORE_TRAILING`, `IGNORE_LEADING_AND_TRAILING` or `IGNORE_ALL`.
 
-[[preview-fix]]
-=== Preview fix
+[[preview-stored-fix]]
+=== Preview Stored Fix
 --
 'GET /changes/<<change-id,\{change-id\}>>/revisions/<<revision-id,\{revision-id\}>>/fixes/<<fix-id,\{fix-id\}>>/preview'
 --
@@ -5792,6 +6139,102 @@
 
 Each link:#diff-info[DiffInfo] is the differences between the patch set indicated by revision-id and a virtual patch set with the applied fix.
 
+[[preview-provided-fix]]
+=== Preview Provided fix
+--
+'POST /changes/<<change-id,\{change-id\}>>/revisions/<<revision-id,\{revision-id\}>>/fix:preview'
+--
+
+Gets the diffs of all files for a list of <<fix-replacement-info,FixReplacementInfo>> loaded from
+the <<apply-provided-fix-input,ApplyProvidedFixInput>> entity. The fixes are passed as part of the
+request body.
+As response, a map of link:#diff-info[DiffInfo] is returned that describes the diffs.
+
+Each link:#diff-info[DiffInfo] is the differences between the patch set indicated by revision-id
+and a virtual patch set with the applied fix. No content on the server will be modified as part of this request.
+
+.Request
+----
+  POST /changes/myProject~master~Id6f0b9d946791f8aba90ace53074eda565983452/revisions/1/fix:preview HTTP/1.0
+  Content-Type: application/json; charset=UTF-8
+
+  {
+    "fix_replacement_infos": [
+      {
+        "path": "abcd.txt",
+        "range": {
+          "start_line": 2,
+          "start_character": 2,
+          "end_line": 2,
+          "end_character": 5
+        },
+        "replacement": "abcdefg"
+      }
+    ]
+  }
+----
+
+If the `Preview Provided Fix` operation was successful, a link:#diff-info[DiffInfo] previewing the
+change is returned.
+
+.Response
+----
+  HTTP/1.1 200 OK
+    Content-Disposition: attachment
+    Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "abcd.txt": {
+      "meta_a": {
+        "name": "abcd.txt",
+        "content_type": "text/plain",
+        "lines": 3
+      },
+      "meta_b": {
+        "name": "abcd.txt",
+        "content_type": "text/plain",
+        "lines": 3
+      },
+      "intraline_status": "OK",
+      "change_type": "MODIFIED",
+      "content": [
+        {
+          "ab": [
+            "ABCDEFGHI"
+          ]
+        },
+        {
+          "a": [
+            "JKLMNOPQR"
+          ],
+          "b": [
+            "JKabcdefgOPQR"
+          ],
+          "edit_a": [
+            [
+              2,
+              3
+            ]
+          ],
+          "edit_b": [
+            [
+              2,
+              7
+            ]
+          ]
+        },
+        {
+          "ab": [
+            ""
+          ]
+        }
+      ]
+    }
+  }
+----
+
+
 [[get-blame]]
 === Get Blame
 --
@@ -6322,7 +6765,7 @@
 * a commit ID ("674ac754f91e64a0efb8087e59a176484bd534d1")
 * an abbreviated commit ID that uniquely identifies one revision of the
   change ("674ac754"), at least 4 digits are required
-* a legacy numeric patch number ("1" for first patch set of the change)
+* a numeric patch number ("1" for first patch set of the change)
 * "0" or the literal `edit` for a change edit
 
 [[json-entities]]
@@ -6375,6 +6818,46 @@
 at the server or permissions are modified. Not present if false.
 |====================================
 
+[[applypatch-input]]
+=== ApplyPatchInput
+The `ApplyPatchInput` entity contains information about a patch to apply.
+
+A new commit will be created from the patch, and saved as a new patch set.
+
+[options="header",cols="1,^1,5"]
+|=================================
+|Field Name          ||Description
+|`patch`             |required|
+The patch to be applied. Must be compatible with `git diff` output.
+For example, link:#get-patch[Get Patch] output.
+|=================================
+
+[[applypatchpatchset-input]]
+=== ApplyPatchPatchSetInput
+The `ApplyPatchPatchSetInput` entity contains information for creating a new patch set from a
+given patch.
+
+[options="header",cols="1,^1,5"]
+|=================================
+|Field Name           ||Description
+|`patch`              |required|
+The details of the patch to be applied as a link:#applypatch-input[ApplyPatchInput] entity.
+|`commit_message`     |optional|
+The commit message for the new patch set. If not specified, a predefined message will be used.
+|`base`               |optional|
+40-hex digit SHA-1 of the commit which will be the parent commit of the newly created patch set.
+If set, it must be a merged commit or a change revision on the destination branch.
+Otherwise, the target change's branch tip will be used.
+|`author`             |optional|
+The author of the commit to create. Must be an
+link:rest-api-accounts.html#account-input[AccountInput] entity with at least
+the `name` and `email` fields set.
+The caller needs "Forge Author" permission when using this field, unless specifies their own details.
+This field does not affect the owner of the change, which will continue to use the identity of the
+caller.
+|=================================
+
+
 [[approval-info]]
 === ApprovalInfo
 The `ApprovalInfo` entity contains information about an approval from a
@@ -6520,7 +7003,14 @@
 |`topic`              |optional|The topic to which this change belongs.
 |`attention_set`      |optional|
 The map that maps link:rest-api-accounts.html#account-id[account IDs]
-to link:#attention-set-info[AttentionSetInfo] of that account.
+to link:#attention-set-info[AttentionSetInfo] of that account. Those are all
+accounts that are currently in the attention set.
+|`removed_from_attention_set`      |optional|
+The map that maps link:rest-api-accounts.html#account-id[account IDs]
+to link:#attention-set-info[AttentionSetInfo] of that account. Those are all
+accounts that were in the attention set but were removed. The
+link:#attention-set-info[AttentionSetInfo] is the latest and most recent removal
+ of the account from the attention set.
 |`assignee`           |optional|
 The assignee of the change as an link:rest-api-accounts.html#account-info[
 AccountInfo] entity.
@@ -6601,6 +7091,13 @@
 A map of the permitted labels that maps a label name to the list of
 values that are allowed for that label. +
 Only set if link:#detailed-labels[detailed labels] are requested.
+|`removable_labels`   |optional|
+A map of the removable labels that maps a label name to the map of
+values and reviewers (
+link:rest-api-accounts.html#account-info[AccountInfo] entities)
+that are allowed to be removed from the change. +
+Only set if link:#labels[labels] or
+link:#detailed-labels[detailed labels] are requested.
 |`removable_reviewers`|optional|
 The reviewers that can be removed by the calling user as a list of
 link:rest-api-accounts.html#account-info[AccountInfo] entities. +
@@ -6685,7 +7182,8 @@
 by one of the following REST endpoints: link:#create-change[Create
 Change], link:#create-merge-patch-set-for-change[Create Merge Patch Set
 For Change], link:#cherry-pick[Cherry Pick Revision],
-link:rest-api-project.html#cherry-pick-commit[Cherry Pick Commit]
+link:rest-api-project.html#cherry-pick-commit[Cherry Pick Commit],
+link:#rebase-change[Rebase Change]
 |==================================
 
 [[change-input]]
@@ -6718,11 +7216,16 @@
 Whether the new change should be set to work in progress.
 |`base_change`        |optional|
 A link:#change-id[\{change-id\}] that identifies the base change for a create
-change operation. Mutually exclusive with `base_commit`.
+change operation. +
+Mutually exclusive with `base_commit`. +
+If neither `base_commit` nor `base_change` are set, the target branch tip will
+be used as the parent commit.
 |`base_commit`        |optional|
 A 40-digit hex SHA-1 of the commit which will be the parent commit of the newly
-created change. If set, it must be a merged commit on the destination branch.
-Mutually exclusive with `base_change`.
+created change. If set, it must be a merged commit on the destination branch. +
+Mutually exclusive with `base_change`. +
+If neither `base_commit` nor `base_change` are set, the target branch tip will
+be used as the parent commit.
 |`new_branch`         |optional, default to `false`|
 Allow creating a new branch when set to `true`. Using this option is
 only possible for non-merge commits (if the `merge` field is not set).
@@ -6739,10 +7242,12 @@
 If set, the target branch (see  `branch` field) must exist (it is not
 possible to create it automatically by setting the `new_branch` field
 to `true`.
+|`patch`              |optional|
+The detail of a patch to be applied as an link:#applypatch-input[ApplyPatchInput] entity.
 |`author`             |optional|
-An link:rest-api-accounts.html#account-input[AccountInput] entity
-that will set the author of the commit to create. The author must be
-specified as name/email combination.
+The author of the commit to create. Must be an
+link:rest-api-accounts.html#account-input[AccountInput] entity with at least
+the `name` and `email` fields set.
 The caller needs "Forge Author" permission when using this field.
 This field does not affect the owner of the change, which will
 continue to use the identity of the caller.
@@ -6798,28 +7303,28 @@
 
 [options="header",cols="1,^1,5"]
 |===========================
-|Field Name         ||Description
-|`message`          |optional|
+|Field Name          ||Description
+|`message`           |optional|
 Commit message for the cherry-pick change. If not set, the commit message of
 the cherry-picked commit is used.
-|`destination`      ||Destination branch
-|`base`             |optional|
+|`destination`       ||Destination branch
+|`base`              |optional|
 40-hex digit SHA-1 of the commit which will be the parent commit of the newly created change.
 If set, it must be a merged commit or a change revision on the destination branch.
-|`parent`           |optional, defaults to 1|
+|`parent`            |optional, defaults to 1|
 Number of the parent relative to which the cherry-pick should be considered.
-|`notify`           |optional|
+|`notify`            |optional|
 Notify handling that defines to whom email notifications should be sent
 after the cherry-pick. +
 Allowed values are `NONE`, `OWNER`, `OWNER_REVIEWERS` and `ALL`. +
 If not set, the default is `ALL`.
-|`notify_details`   |optional|
+|`notify_details`    |optional|
 Additional information about whom to notify about the update as a map
 of link:user-notify.html#recipient-types[recipient type] to
 link:#notify-info[NotifyInfo] entity.
-|`keep_reviewers`   |optional, defaults to false|
+|`keep_reviewers`    |optional, defaults to false|
 If `true`, carries reviewers and ccs over from original change to newly created one.
-|`allow_conflicts`  |optional, defaults to false|
+|`allow_conflicts`   |optional, defaults to false|
 If `true`, the cherry-pick uses content merge and succeeds also if
 there are conflicts. If there are conflicts the file contents of the
 created change contain git conflict markers to indicate the conflicts.
@@ -6827,7 +7332,7 @@
 `contains_git_conflicts` field in the link:#change-info[ChangeInfo]. If
 there are conflicts the cherry-pick change is marked as
 work-in-progress.
-|`topic`            |optional|
+|`topic`             |optional|
 The topic of the created cherry-picked change. If not set, the default depends
 on the source. If the source is a change with a topic, the resulting topic
 of the cherry-picked change will be {source_change_topic}-{destination_branch}.
@@ -6835,10 +7340,18 @@
 the created change will have no topic.
 If the change already exists, the topic will not change if not set. If set, the
 topic will be overridden.
-|`allow_empty`      |optional, defaults to false|
+|`allow_empty`       |optional, defaults to false|
 If `true`, the cherry-pick succeeds also if the created commit will be empty.
 If `false`, a cherry-pick that would create an empty commit fails without creating
 the commit.
+|`validation_options`|optional|
+Map with key-value pairs that are forwarded as options to the commit validation
+listeners (e.g. can be used to skip certain validations). Which validation
+options are supported depends on the installed commit validation listeners.
+Gerrit core doesn't support any validation options, but commit validation
+listeners that are implemented in plugins may. Please refer to the
+documentation of the installed plugins to learn whether they support validation
+options. Unknown validation options are silently ignored.
 |===========================
 
 [[comment-info]]
@@ -7104,6 +7617,12 @@
 Additional information about whom to notify about the update as a map
 of link:user-notify.html#recipient-types[recipient type] to
 link:#notify-info[NotifyInfo] entity.
+|`ignore_automatic_attention_set_rules`|optional|
+If set to true, ignore all automatic attention set rules described in the
+link:#attention-set[attention set]. When not set, the default is false.
+|`reason`         |optional|
+The reason why this vote is deleted. Will +
+go into the change message.
 |=============================
 
 [[description-input]]
@@ -7155,7 +7674,11 @@
 |==========================
 |Field Name    ||Description
 |`name`        ||The name of the file.
-|`content_type`||The content type of the file.
+|`content_type`||The content type of the file. For the commit message and merge
+list the value is `text/x-gerrit-commit-message` and `text/x-gerrit-merge-list`
+respectively. For git links the value is `x-git/gitlink`. For symlinks the value
+is `x-git/symlink`. For regular files the value is the file mime type (e.g.
+`text/x-java`, `text/x-c++src`, etc...).
 |`lines`       ||The total number of lines in the file.
 |`web_links`   |optional|
 Links to the file in external sites as a list of
@@ -7227,6 +7750,16 @@
 Whether the web link should be shown on the unified diff screen.
 |=======================
 
+[[apply-provided-fix-input]]
+=== ApplyProvidedFixInput
+The `ApplyProvidedFixInput` entity contains the fixes to be applied on a review.
+
+[options="header",cols="1,6"]
+|=======================
+|Field Name              |Description
+|`fix_replacement_infos` |A list of <<fix-replacement-info,FixReplacementInfo>>.
+|=======================
+
 [[edit-file-info]]
 === EditFileInfo
 The `EditFileInfo` entity contains additional information
@@ -7296,16 +7829,21 @@
 Number of inserted lines. +
 Not set for binary files or if no lines were inserted. +
 An empty last line is not included in the count and hence this number can
-differ by one from details provided in <<#diff-info,DiffInfo>>.
+differ by one from details provided in <<diff-info,DiffInfo>>.
 |`lines_deleted` |optional|
 Number of deleted lines. +
 Not set for binary files or if no lines were deleted. +
 An empty last line is not included in the count and hence this number can
-differ by one from details provided in <<#diff-info,DiffInfo>>.
+differ by one from details provided in <<diff-info,DiffInfo>>.
 |`size_delta`    ||
 Number of bytes by which the file size increased/decreased.
-|`size`          ||
-File size in bytes.
+|`size`          || File size in bytes.
+|`old_mode`        |optional|File mode in octal (e.g. 100644) at the old commit.
+The first three digits indicate the file type and the last three digits contain
+the file permission bits. For added files, this field will not be present.
+|`new_mode`        |optional|File mode in octal (e.g. 100644) at the new commit.
+The first three digits indicate the file type and the last three digits contain
+the file permission bits. For deleted files, this field will not be present.
 |=============================
 
 [[fix-input]]
@@ -7424,11 +7962,10 @@
 There are two options that control the contents of `LabelInfo`:
 link:#labels[`LABELS`] and link:#detailed-labels[`DETAILED_LABELS`].
 
-* For a quick summary of the state of labels, use `LABELS`.
-* For detailed information about labels, including exact numeric votes for all
-  users and the allowed range of votes for the current user, use `DETAILED_LABELS`.
+* Using `LABELS` will skip the `all.permitted_voting_range` attribute.
+* Using `DETAILED_LABELS` will include the `all.permitted_voting_range`
+  attribute.
 
-==== Common fields
 [options="header",cols="1,^1,5"]
 |===========================
 |Field Name    ||Description
@@ -7436,12 +7973,7 @@
 Whether the label is optional. Optional means the label may be set, but
 it's neither necessary for submission nor does it block submission if
 set.
-|===========================
-
-==== Fields set by `LABELS`
-[options="header",cols="1,^1,5"]
-|===========================
-|Field Name    ||Description
+|`description` |optional| The description of the label.
 |`approved`    |optional|One user who approved this label on the change
 (voted the maximum value) as an
 link:rest-api-accounts.html#account-info[AccountInfo] entity.
@@ -7461,16 +7993,14 @@
 "`+1`"/"`-1`".
 |`default_value`|optional|The default voting value for the label.
 This value may be outside the range specified in permitted_labels.
-|===========================
-
-==== Fields set by `DETAILED_LABELS`
-[options="header",cols="1,^1,5"]
-|===========================
-|Field Name    ||Description
+|votes|optional|A list of integers containing the vote values applied to this
+label at the latest patchset.
 |`all`         |optional|List of all approvals for this label as a list
 of link:#approval-info[ApprovalInfo] entities. Items in this list may
 not represent actual votes cast by users; if a user votes on any label,
-a corresponding ApprovalInfo will appear in this list for all labels.
+a corresponding ApprovalInfo will appear in this list for all labels. +
+The `permitted_voting_range` attribute is only set if the `DETAILED_LABELS`
+option is requested.
 |`values`      |optional|A map of all values that are allowed for this
 label. The map maps the values ("`-2`", "`-1`", " `0`", "`+1`", "`+2`")
 to the value descriptions.
@@ -7555,11 +8085,11 @@
 The detail of the source commit for merge as a link:#merge-input[MergeInput]
 entity.
 |`author`             |optional|
-An link:rest-api-accounts.html#account-input[AccountInput] entity
-that will set the author of the commit to create. The author must be
-specified as name/email combination.
+The author of the commit to create. Must be an
+link:rest-api-accounts.html#account-input[AccountInput] entity with at least
+the `name` and `email` fields set.
 The caller needs "Forge Author" permission when using this field.
-This field does not affect the owner of the change, which will
+This field does not affect the owner or the committer of the change, which will
 continue to use the identity of the caller.
 |==================================
 
@@ -7688,22 +8218,46 @@
 === RebaseInput
 The `RebaseInput` entity contains information for changing parent when rebasing.
 
-[options="header",width="50%",cols="1,^1,5"]
+[options="header",cols="1,^1,5"]
 |===========================
-|Field Name    ||Description
-|`base`        |optional|
+|Field Name          ||Description
+|`base`              |optional|
 The new parent revision. This can be a ref or a SHA-1 to a concrete patchset. +
 Alternatively, a change number can be specified, in which case the current
 patch set is inferred. +
 Empty string is used for rebasing directly on top of the target branch,
 which effectively breaks dependency towards a parent change.
-|`allow_conflicts`|optional, defaults to false|
+|`allow_conflicts`   |optional, defaults to false|
 If `true`, the rebase also succeeds if there are conflicts. +
 If there are conflicts the file contents of the rebased patch set contain
 git conflict markers to indicate the conflicts. +
 Callers can find out whether there were conflicts by checking the
 `contains_git_conflicts` field in the returned link:#change-info[ChangeInfo]. +
 If there are conflicts the change is marked as work-in-progress.
+|`validation_options`|optional|
+Map with key-value pairs that are forwarded as options to the commit validation
+listeners (e.g. can be used to skip certain validations). Which validation
+options are supported depends on the installed commit validation listeners.
+Gerrit core doesn't support any validation options, but commit validation
+listeners that are implemented in plugins may. Please refer to the
+documentation of the installed plugins to learn whether they support validation
+options. Unknown validation options are silently ignored.
+|===========================
+
+[[rebase-chain-info]]
+=== RebaseChainInfo
+
+The `RebaseChainInfo` entity contains information about a chain of changes
+that were rebased.
+
+[options="header",cols="1,^1,5"]
+|===========================
+|Field Name                ||Description
+|`rebased_changes`         ||List of the unsubmitted ancestors, as link:#change-info[ChangeInfo]
+entities. Includes both rebased changes, and previously up-to-date ancestors. The list is ordered by
+ancestry, where the oldest ancestor is the first.
+|`contains_git_conflicts`  ||Whether any of the rebased changes has conflicts
+due to rebasing.
 |===========================
 
 [[related-change-and-commit-info]]
@@ -7724,6 +8278,9 @@
 |`_current_revision_number`|optional|The current revision number.
 |`status`                  |optional|The status of the change. The status of
 the change is one of (`NEW`, `MERGED`, `ABANDONED`).
+|`submittable`             |optional|Boolean indicating whether the change is
+submittable. +
+Only populated if link:#get-related-changes-submittable[requested].
 |===========================
 
 [[related-changes-info]]
@@ -7774,30 +8331,40 @@
 The `RevertInput` entity contains information for reverting a change.
 
 [options="header",cols="1,^1,5"]
-|=============================
-|Field Name      ||Description
-|`message`       |optional|
+|=================================
+|Field Name          ||Description
+|`message`           |optional|
 Message to be added as review comment to the change when reverting the
 change.
-|`notify`        |optional|
+|`notify`            |optional|
 Notify handling that defines to whom email notifications should be sent
 for reverting the change. +
 Allowed values are `NONE`, `OWNER`, `OWNER_REVIEWERS` and `ALL`. +
 If not set, the default is `ALL`.
-|`notify_details`|optional|
+|`notify_details`    |optional|
 Additional information about whom to notify about the revert as a map
 of link:user-notify.html#recipient-types[recipient type] to
 link:#notify-info[NotifyInfo] entity.
-|`topic`         |optional|
+|`topic`             |optional|
 Name of the topic for the revert change. If not set, the default for Revert
 endpoint is the topic of the change being reverted, and the default for the
 RevertSubmission endpoint is `revert-{submission_id}-{timestamp.now}`.
 Topic can't contain quotation marks.
-|`work_in_progress` |optional|
+|`work_in_progress`  |optional|
 When present, change is marked as Work In Progress. The `notify` input is
-used if it's present, otherwise it will be overridden to `OWNER`. +
+used if it's present, otherwise it will be overridden to `NONE`. +
+Notifications for the reverted change will only sent once the result change is
+no longer WIP. +
 If not set, the default is false.
-|=============================
+|`validation_options`|optional|
+Map with key-value pairs that are forwarded as options to the commit validation
+listeners (e.g. can be used to skip certain validations). Which validation
+options are supported depends on the installed commit validation listeners.
+Gerrit core doesn't support any validation options, but commit validation
+listeners that are implemented in plugins may. Please refer to the
+documentation of the installed plugins to learn whether they support validation
+options. Unknown validation options are silently ignored.
+|=================================
 
 [[revert-submission-info]]
 === RevertSubmissionInfo
@@ -7943,6 +8510,8 @@
 |`ready`                  |optional|
 If true, the change was moved from WIP to ready for review as a result of this
 action. Not set if false.
+|`error`                  |optional|
+Error message for non-200 responses.
 |============================
 
 [[reviewer-info]]
@@ -8183,7 +8752,13 @@
 Notify handling that defines to whom email notifications should be sent after
 the change is submitted. +
 Allowed values are `NONE`, `OWNER`, `OWNER_REVIEWERS` and `ALL`. +
-If not set, the default is `ALL`.
+If not set, the default is `ALL`.+
+Ignored if a post approval diff is present (i.e. if the change is submitted
+with copied approvals), because in this case everyone should be informed
+about the non-reviewed diff which has been applied after the change has been
+approved so that they can take action if the post approval diff looks
+unexpected. In other words if a post approval diff is present `ALL` is
+enforced.
 |`notify_details`|optional|
 Additional information about whom to notify about the update as a map
 of link:user-notify.html#recipient-types[recipient type] to
@@ -8271,6 +8846,13 @@
 `branch:refs/heads/foo and label:verified=+1`.
 |`fulfilled`||
 True if the submit requirement is fulfilled for the change.
+|`status`||
+A string containing the status of evaluating the expression which can be one
+of the following: +
+  * `PASS` - expression was evaluated and result is true. +
+  * `FAIL` - expression was evaluated and result is false. +
+  * `ERROR` - an error occurred while evaluating the expression. +
+  * `NOT_EVALUATED` - expression was not evaluated.
 |`passing_atoms`|optional|
 A list of passing atoms as strings. For the above expression,
 `passing_atoms` can contain ["branch:refs/heads/foo"] if the branch predicate is
@@ -8278,6 +8860,9 @@
 |`failing_atoms`|optional|
 A list of failing atoms. This is similar to `passing_atoms` except that it
 contains the list of predicates that are not fulfilled for the change.
+|`error_message`|optional|
+If the submit requirement fails during evaluation, this string will contain
+an error message describing why it failed.
 |===========================
 
 [[submit-requirement-input]]
@@ -8320,7 +8905,8 @@
 Description of the submit requirement.
 |`status`||
 Status describing the result of evaluating the submit requirement. The status
-is one of (`SATISFIED`, `UNSATISFIED`, `OVERRIDDEN`, `NOT_APPLICABLE`, `ERROR`).
+is one of (`SATISFIED`, `UNSATISFIED`, `OVERRIDDEN`, `NOT_APPLICABLE`, `ERROR`,
+`FORCED`).
 |`is_legacy`||
 If true, this submit requirement result was created from a legacy
 link:#submit-record[SubmitRecord]. Otherwise, it was created by evaluating a
@@ -8333,11 +8919,15 @@
 omitted for the `applicability_expression_result`.
 |`submittability_expression_result`||
 A link:#submit-requirement-expression-info[SubmitRequirementExpressionInfo]
-containing the result of evaluating the submittability expression.
+containing the result of evaluating the submittability expression. +
+If the submit requirement does not apply, the `status` field of the result
+will be set to `NOT_EVALUATED`.
 |`override_expression_result`|optional|
 A link:#submit-requirement-expression-info[SubmitRequirementExpressionInfo]
-containing the result of evaluating the override expression. Not set if the
-submit requirement did not define an override expression.
+containing the result of evaluating the override expression. +
+Not set if the submit requirement did not define an override expression. If the
+submit requirement does not apply, the `status` field of the result will be set
+to `NOT_EVALUATED`.
 |===========================
 
 [[submitted-together-info]]
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index bd93b8b..505def0 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -1380,7 +1380,14 @@
   POST /config/server/index.changes HTTP/1.0
   Content-Type: application/json; charset=UTF-8
 
-  {changes: ["foo~101", "bar~202"]}
+  {
+    "changes": [
+      "foo~101",
+      "bar~202",
+      "303"
+    ],
+    "delete_missing": "true"
+  }
 ----
 
 .Response
@@ -1389,6 +1396,9 @@
   Content-Disposition: attachment
 ----
 
+When `delete_missing` is set to `true` changes to be reindexed which are missing in NoteDb
+will be deleted in the index.
+
 
 [[ids]]
 == IDs
@@ -1876,6 +1886,10 @@
 |Field Name         ||Description
 |`changes`   ||
 List of link:rest-api-changes.html#change-id[change-ids]
+|`delete_missing`  |optional|
+Delete changes which are missing in NoteDb from the index. This can be used
+to get rid of stale index entries. Possible values are `true` and `false`.
+By default set to `false`.
 |================================
 
 [[jvm-summary-info]]
@@ -2008,6 +2022,10 @@
 |`default_theme`           |optional|
 URL to a default Gerrit UI theme plugin, if available.
 Located in `/static/gerrit-theme.js` by default.
+|`submit_requirement_dashboard_columns` ||
+The list of submit requirement names that should be displayed as separate
+columns in the dashboard. If empty, the default is to display all submit
+requirements that are applicable for changes appearing in the dashboard.
 |=======================================
 
 [[sshd-info]]
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 92c6dbf..9e71df7 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -402,12 +402,12 @@
   Content-Type: application/json; charset=UTF-8
 
   )]}'
-  {
-    "test": {
+  [
+    {
       "id": "test",
       "description": "\u003chtml\u003e is escaped"
     }
-  }
+  ]
 ----
 
 [[project-query-limit]]
@@ -421,11 +421,11 @@
 ----
 
 The `/projects/` URL also accepts a start integer in the `start`
-parameter. The results will skip `start` groups from project list.
+parameter. The results will skip `start` projects from project list.
 
 Query 25 projects starting from index 50.
 ----
-  GET /groups/?query=<query>&limit=25&start=50 HTTP/1.0
+  GET /projects/?query=<query>&limit=25&start=50 HTTP/1.0
 ----
 
 [[project-query-options]]
@@ -1541,6 +1541,47 @@
   }
 ----
 
+[[commits-included-in]]
+=== Get Commits Included In Refs
+--
+'GET /projects/link:#project-name[\{project-name\}]/commits:in'
+--
+
+Gets refs in which the specified commits were merged into. Returns a map of commits
+to sets of full ref names.
+
+One or more `commit` query parameters are required and each specifies a
+commit-id (SHA-1 in 40 digit hex representation). Commits will not be contained
+in the map if they do not exist or are not reachable from visible, specified refs.
+
+One or more `ref` query parameters are required and each specifies a full ref name.
+Refs which are not visible to the calling user according to the project's read
+permissions and refs which do not exist will be filtered out from the result.
+
+.Request
+----
+  GET /projects/work%2Fmy-project/commits:in?commit=a8a477efffbbf3b44169bb9a1d3a334cbbd9aa96&commit=6d2a3adb10e844c33617fc948dbeb88e868d396e&ref=refs/heads/master&ref=refs/heads/branch1 HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json;charset=UTF-8
+
+  )]}'
+  {
+    "a8a477efffbbf3b44169bb9a1d3a334cbbd9aa96": [
+      "refs/heads/master"
+    ],
+    "6d2a3adb10e844c33617fc948dbeb88e868d396e": [
+      "refs/heads/branch1",
+      "refs/heads/master"
+    ]
+  }
+
+----
+
 [[branch-endpoints]]
 == Branch Endpoints
 
@@ -3031,16 +3072,14 @@
       "function": "MaxWithBlock",
       "values": {
         " 0": "No score",
-        "-1": "I would prefer this is not merged as is",
-        "-2": "This shall not be merged",
+        "-1": "I would prefer this is not submitted as is",
+        "-2": "This shall not be submitted",
         "+1": "Looks good to me, but someone else must approve",
         "+2": "Looks good to me, approved"
       },
       "default_value": 0,
       "can_override": true,
-      "copy_min_score": true,
-      "copy_all_scores_if_no_change": true,
-      "copy_all_scores_on_trivial_rebase": true,
+      "copy_condition": "changekind:NO_CHANGE OR changekind:TRIVIAL_REBASE or is:MIN",
       "allow_post_submit": true
     }
   ]
@@ -3078,16 +3117,14 @@
       "function": "MaxWithBlock",
       "values": {
         " 0": "No score",
-        "-1": "I would prefer this is not merged as is",
-        "-2": "This shall not be merged",
+        "-1": "I would prefer this is not submitted as is",
+        "-2": "This shall not be submitted",
         "+1": "Looks good to me, but someone else must approve",
         "+2": "Looks good to me, approved"
       },
       "default_value": 0,
       "can_override": true,
-      "copy_min_score": true,
-      "copy_all_scores_if_no_change": true,
-      "copy_all_scores_on_trivial_rebase": true,
+      "copy_condition": "changekind:NO_CHANGE OR changekind:TRIVIAL_REBASE or is:MIN",
       "allow_post_submit": true
     },
     {
@@ -3096,14 +3133,14 @@
       "function": "MaxWithBlock",
       "values": {
         " 0": "No score",
-        "-1": "I would prefer this is not merged as is",
-        "-2": "This shall not be merged",
+        "-1": "I would prefer this is not submitted as is",
+        "-2": "This shall not be submitted",
         "+1": "Looks good to me, but someone else must approve",
         "+2": "Looks good to me, approved"
       },
       "default_value": 0,
       "can_override": true,
-      "copy_any_score": true,
+      "copy_condition": "is:ANY",
       "allow_post_submit": true
     }
   ]
@@ -3141,16 +3178,14 @@
     "function": "MaxWithBlock",
     "values": {
       " 0": "No score",
-      "-1": "I would prefer this is not merged as is",
-      "-2": "This shall not be merged",
+      "-1": "I would prefer this is not submitted as is",
+      "-2": "This shall not be submitted",
       "+1": "Looks good to me, but someone else must approve",
       "+2": "Looks good to me, approved"
     },
     "default_value": 0,
     "can_override": true,
-    "copy_min_score": true,
-    "copy_all_scores_if_no_change": true,
-    "copy_all_scores_on_trivial_rebase": true,
+    "copy_condition": "changekind:NO_CHANGE OR changekind:TRIVIAL_REBASE OR is:MIN",
     "allow_post_submit": true
   }
 ----
@@ -3178,8 +3213,8 @@
     "commit_message": "Create Foo Label",
     "values": {
       " 0": "No score",
-      "-1": "I would prefer this is not merged as is",
-      "-2": "This shall not be merged",
+      "-1": "I would prefer this is not submitted as is",
+      "-2": "This shall not be submitted",
       "+1": "Looks good to me, but someone else must approve",
       "+2": "Looks good to me, approved"
     }
@@ -3202,14 +3237,14 @@
     "function": "MaxWithBlock",
     "values": {
       " 0": "No score",
-      "-1": "I would prefer this is not merged as is",
-      "-2": "This shall not be merged",
+      "-1": "I would prefer this is not submitted as is",
+      "-2": "This shall not be submitted",
       "+1": "Looks good to me, but someone else must approve",
       "+2": "Looks good to me, approved"
     },
     "default_value": 0,
     "can_override": true,
-    "copy_all_scores_if_no_change": true,
+    "copy_condition": "changekind:NO_CHANGE",
     "allow_post_submit": true
   }
 ----
@@ -3254,16 +3289,14 @@
     "function": "MaxWithBlock",
     "values": {
       " 0": "No score",
-      "-1": "I would prefer this is not merged as is",
-      "-2": "This shall not be merged",
+      "-1": "I would prefer this is not submitted as is",
+      "-2": "This shall not be submitted",
       "+1": "Looks good to me, but someone else must approve",
       "+2": "Looks good to me, approved"
     },
     "default_value": 0,
     "can_override": true,
-    "copy_min_score": true,
-    "copy_all_scores_if_no_change": true,
-    "copy_all_scores_on_trivial_rebase": true,
+    "copy_condition": "changekind:NO_CHANGE OR changekind:TRIVIAL_REBASE OR is:MIN",
     "allow_post_submit": true,
     "ignore_self_approval": true
   }
@@ -3336,8 +3369,8 @@
         "name": "Foo-Review",
         "values": {
           " 0": "No score",
-          "-1": "I would prefer this is not merged as is",
-          "-2": "This shall not be merged",
+          "-1": "I would prefer this is not submitted as is",
+          "-2": "This shall not be submitted",
           "+1": "Looks good to me, but someone else must approve",
           "+2": "Looks good to me, approved"
       }
@@ -3347,7 +3380,7 @@
         "function": "MaxWithBlock"
       },
       "Baz-Review": {
-        "copy_min_score": true
+        "copy_condition": "is:MIN"
       }
     }
   }
@@ -3360,6 +3393,198 @@
   HTTP/1.1 200 OK
 ----
 
+[[submit-requirement-endpoints]]
+== Submit Requirement Endpoints
+
+[[create-submit-requirement]]
+=== Create Submit Requirement
+
+--
+'PUT /projects/link:#project-name[\{project-name\}]/submit_requirements/link:#submit-requirement-name[\{submit-requirement-name\}]'
+--
+
+Creates a new submit requirement definition in this project.
+
+The calling user must have write access to the `refs/meta/config` branch of the
+project.
+
+If a submit requirement with this name is already defined in this project, this
+submit requirement definition is updated
+(see link:#update-submit-requirement[Update Submit Requirement]).
+
+The submit requirement data must be provided in the request body as
+link:#submit-requirement-input[SubmitRequirementInput].
+
+.Request
+----
+  PUT /projects/My-Project/submit_requirements/Code-Review HTTP/1.0
+  Content-Type: application/json; charset=UTF-8
+
+  {
+    "name": "Code-Review",
+    "description": "At least one maximum vote for the Code-Review label is required",
+    "submittability_expression": "label:Code-Review=+2"
+  }
+----
+
+As response a link:#submit-requirement-info[SubmitRequirementInfo] entity is
+returned that describes the created submit requirement.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+      "name": "Code-Review",
+      "description": "At least one maximum vote for the Code-Review label is required",
+      "submittability_expression": "label:Code-Review=+2",
+      "allow_override_in_child_projects": false
+   }
+----
+
+[[update-submit-requirement]]
+=== Update Submit Requirement
+
+--
+'PUT /projects/link:#project-name[\{project-name\}]/submit_requirements/link:#submit-requirement-name[\{submit-requirement-name\}]'
+--
+
+Updates the definition of a submit requirement that is defined in this project.
+
+The calling user must have write access to the `refs/meta/config` branch of the
+project.
+
+The new submit requirement will overwrite the existing submit requirement.
+That is, unspecified attributes will be set to their defaults.
+
+.Request
+----
+  PUT /projects/My-Project/submit_requirements/Code-Review HTTP/1.0
+  Content-Type: application/json; charset=UTF-8
+
+  {
+    "name": "Code-Review",
+    "description": "At least one maximum vote for the Code-Review label is required",
+    "submittability_expression": "label:Code-Review=+2"
+  }
+----
+
+As response a link:#submit-requirement-info[SubmitRequirementInfo] entity is
+returned that describes the created submit requirement.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+      "name": "Code-Review",
+      "description": "At least one maximum vote for the Code-Review label is required",
+      "submittability_expression": "label:Code-Review=+2",
+      "allow_override_in_child_projects": false
+   }
+----
+
+[[get-submit-requirement]]
+=== Get Submit Requirement
+--
+'GET /projects/link:#project-name[\{project-name\}]/submit_requirements/link:#submit-requirement-name[\{submit-requirement-name\}]'
+--
+
+Retrieves the definition of a submit requirement that is defined in this project.
+
+The calling user must have read access to the `refs/meta/config` branch of the
+project.
+
+.Request
+----
+  GET /projects/All-Projects/submit-requirement/Code-Review HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+      "name": "Code-Review",
+      "description": "At least one maximum vote for the Code-Review label is required",
+      "submittability_expression": "label:Code-Review=+2",
+      "allow_override_in_child_projects": false
+   }
+----
+
+[[list-submit-requirements]]
+=== List Submit Requirements
+--
+'GET /projects/link:#project-name[\{project-name\}]/submit_requirements'
+--
+
+Retrieves a list of all submit requirements for this project. The `inherited`
+parameter can be supplied to also list submit requirements from parent projects.
+
+The calling user must have read access to the `refs/meta/config` branch of the
+project. If the `inherited` option is used, the caller must have read access to
+the `refs/meta/config` branch of all parent projects as well.
+
+As response a list of link:#submit-requirement-info[SubmitRequirementInfo]
+entities is returned.
+
+.Request
+----
+  GET /projects/All-Projects/submit-requirements HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  [
+    {
+      "name": "Code-Review",
+      "description": "At least one maximum vote for the Code-Review label is required",
+      "submittability_expression": "label:Code-Review=+2",
+      "allow_override_in_child_projects": false
+    }
+  ]
+----
+
+[[delete-submit-requirement]]
+=== Delete Submit Requirement
+--
+'DELETE /projects/link:#project-name[\{project-name\}]/submit_requirements/link:#submit-requirement-name[\{submit-requirement-name\}]'
+--
+
+Deletes the definition of a submit requirement that is defined in this project.
+
+The calling user must have write access to the `refs/meta/config` branch of the
+project. The request would fail with `404 Not Found` if there is no submit
+requirement with this name for this project.
+
+No request body is needed.
+
+.Request
+----
+  DELETE /projects/My-Project/submit_requirements/Foo-Review HTTP/1.0
+  Content-Type: application/json; charset=UTF-8
+----
+
+If a submit requirement was deleted the response is "`204 No Content`".
+
+.Response
+----
+  HTTP/1.1 204 No Content
+----
 
 [[ids]]
 == IDs
@@ -3388,6 +3613,11 @@
 === \{label-name\}
 The name of a review label.
 
+[[submit-requirement-name]]
+=== \{submit-requirement-name\}
+The name of a submit requirement.
+
+
 [[project-name]]
 === \{project-name\}
 The name of the project.
@@ -3506,14 +3736,22 @@
 
 [options="header",cols="1,^2,4"]
 |=======================
-|Field Name||Description
-|`ref`     |optional|
+|Field Name          ||Description
+|`ref`               |optional|
 The name of the branch. The prefix `refs/heads/` can be
 omitted. +
 If set, must match the branch ID in the URL.
-|`revision`|optional|
+|`revision`          |optional|
 The base revision of the new branch. +
 If not set, `HEAD` will be used as base revision.
+|`validation_options`|optional|
+Map with key-value pairs that are forwarded as options to the ref operation
+validation listeners (e.g. can be used to skip certain validations). Which
+validation options are supported depends on the installed ref operation
+validation listeners. Gerrit core doesn't support any validation options, but
+ref operation validation listeners that are implemented in plugins may. Please
+refer to the documentation of the installed plugins to learn whether they
+support validation options. Unknown validation options are silently ignored.
 |=======================
 
 [[check-project-input]]
@@ -3560,16 +3798,19 @@
 |`link`     |        |The URL to direct the user to whenever the
 regular expression is matched, as documented in
 link:config-gerrit.html#commentlink.name.link[commentlink.name.link].
+|`prefix`   |optional|Text inserted before the link.
+|`suffix`   |optional|Text inserted after the link.
+|`text`     |optional|Text of the link.
 |`enabled`  |optional|Whether the commentlink is enabled, as documented
 in link:config-gerrit.html#commentlink.name.enabled[
 commentlink.name.enabled]. If not set the commentlink is enabled.
+|==================================================
 
 [[commentlink-input]]
 === CommentLinkInput
 The `CommentLinkInput` entity describes the input for a
 link:config-gerrit.html#commentlink[commentlink].
 
-|==================================================
 [options="header",cols="1,^2,4"]
 |==================================================
 |Field Name |        |Description
@@ -3579,6 +3820,9 @@
 |`link`     |        |The URL to direct the user to whenever the
 regular expression is matched, as documented in
 link:config-gerrit.html#commentlink.name.link[commentlink.name.link].
+|`prefix`   |optional|Text inserted before the link.
+|`suffix`   |optional|Text inserted after the link.
+|`text`     |optional|Text of the link.
 |`enabled`  |optional|Whether the commentlink is enabled, as documented
 in link:config-gerrit.html#commentlink.name.enabled[
 commentlink.name.enabled]. If not set the commentlink is enabled.
@@ -3652,17 +3896,19 @@
 Map with the comment link configurations of the project. The name of
 the comment link configuration is mapped to a link:#commentlink-info[
 CommentlinkInfo] entity.
-|`plugin_config`                           |optional|
+|`plugin_config`                                    |optional|
 Plugin configuration as map which maps the plugin name to a map of
 parameter names to link:#config-parameter-info[ConfigParameterInfo]
 entities. Only filled for users who have read access to `refs/meta/config`.
-|`actions`                                 |optional|
+|`actions`                                          |optional|
 Actions the caller might be able to perform on this project. The
 information is a map of view names to
-|`reject_empty_commit`                     |optional|
+|`reject_empty_commit`                              |optional|
 link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
 empty commits should be rejected when a change is merged.
 link:rest-api-changes.html#action-info[ActionInfo] entities.
+|`skip_adding_author_and_committer_as_reviewers`    |optional|
+Whether to skip adding the Git commit author and committer as reviewers for a new change.
 |=======================================================
 
 [[config-input]]
@@ -3929,7 +4175,7 @@
 |`value`            ||
 The effective boolean value.
 |`configured_value` ||
-The configured value, can be `TRUE`, `FALSE` or `INHERITED`.
+The configured value, can be `TRUE`, `FALSE` or `INHERIT`.
 |`inherited_value`  |optional|
 The boolean value inherited from the parent. +
 Not set if there is no parent.
@@ -3945,6 +4191,8 @@
 |Field Name      ||Description
 |`name`          ||
 The link:config-labels.html#label_name[name] of the label.
+|`description`   |optional|
+The description of the label.
 |`project_name`  ||
 The name of the project in which this label is defined.
 |`function`      ||
@@ -3964,34 +4212,8 @@
 |`can_override`  |`false` if not set|
 Whether this label can be link:config-labels.html#label_canOverride[overridden]
 by child projects.
-|`copy_any_score`|`false` if not set|
-Whether link:config-labels.html#label_copyAnyScore[copyAnyScore] is set on the
-label.
 |`copy_condition`|optional|
 See link:config-labels.html#label_copyCondition[copyCondition].
-|`copy_min_score`|`false` if not set|
-Whether link:config-labels.html#label_copyMinScore[copyMinScore] is set on the
-label.
-|`copy_max_score`|`false` if not set|
-Whether link:config-labels.html#label_copyMaxScore[copyMaxScore] is set on the
-label.
-|`copy_all_scores_if_no_change`|`false` if not set|
-Whether link:config-labels.html#label_copyAllScoresIfNoChange[
-copyAllScoresIfNoChange] is set on the label.
-|`copy_all_scores_if_no_code_change`|`false` if not set|
-Whether link:config-labels.html#label_copyAllScoresIfNoCodeChange[
-copyAllScoresIfNoCodeChange] is set on the label.
-|`copy_all_scores_on_trivial_rebase`|`false` if not set|
-Whether link:config-labels.html#label_copyAllScoresOnTrivialRebase[
-copyAllScoresOnTrivialRebase] is set on the label.
-|`copy_all_scores_if_list_of_files_did_not_change`|`false` if not set|
-Whether link:config-labels.html#label_copyAllScoresIfListOfFilesDidNotChange[
-copyAllScoresIfListOfFilesDidNotChange] is set on the label.
-|`copy_all_scores_on_merge_first_parent_update`|`false` if not set|
-Whether link:config-labels.html#label_copyAllScoresOnMergeFirstParentUpdate[
-copyAllScoresOnMergeFirstParentUpdate] is set on the label.
-|`copy_values`   |optional|
-List of values that should be copied forward when a new patch set is uploaded.
 |`allow_post_submit`|`false` if not set|
 Whether link:config-labels.html#label_allowPostSubmit[allowPostSubmit] is set
 on the label.
@@ -4018,6 +4240,8 @@
 For label creation the name is required if this `LabelDefinitionInput` entity
 is contained in a link:#batch-label-input[BatchLabelInput]
 entity.
+|`description`          |optional|
+The new description for the label.
 |`function`      |optional|
 The new link:config-labels.html#label_function[function] of the label (can be
 `MaxWithBlock`, `AnyWithBlock`, `MaxNoBlock`, `NoBlock`, `NoOp` and `PatchSetLock`.
@@ -4036,36 +4260,10 @@
 |`can_override`  |optional|
 Whether this label can be link:config-labels.html#label_canOverride[overridden]
 by child projects.
-|`copy_any_score`|optional|
-Whether link:config-labels.html#label_copyAnyScore[copyAnyScore] is set on the
-label.
 |`copy_condition`|optional|
 See link:config-labels.html#label_copyCondition[copyCondition].
 |`unset_copy_condition`|optional|
 If true, clears the value stored in `copy_condition`.
-|`copy_min_score`|optional|
-Whether link:config-labels.html#label_copyMinScore[copyMinScore] is set on the
-label.
-|`copy_max_score`|optional|
-Whether link:config-labels.html#label_copyMaxScore[copyMaxScore] is set on the
-label.
-|`copy_all_scores_if_no_change`|optional|
-Whether link:config-labels.html#label_copyAllScoresIfNoChange[
-copyAllScoresIfNoChange] is set on the label.
-|`copy_all_scores_if_no_code_change`|optional|
-Whether link:config-labels.html#label_copyAllScoresIfNoCodeChange[
-copyAllScoresIfNoCodeChange] is set on the label.
-|`copy_all_scores_on_trivial_rebase`|optional|
-Whether link:config-labels.html#label_copyAllScoresOnTrivialRebase[
-copyAllScoresOnTrivialRebase] is set on the label.
-|`copy_all_scores_if_list_of_files_did_not_change`|optional|
-Whether link:config-labels.html#label_copyAllScoresIfListOfFilesDidNotChange[
-copyAllScoresIfListOfFilesDidNotChange] is set on the label.
-|`copy_all_scores_on_merge_first_parent_update`|optional|
-Whether link:config-labels.html#label_copyAllScoresOnMergeFirstParentUpdate[
-copyAllScoresOnMergeFirstParentUpdate] is set on the label.
-|`copy_values`   |optional|
-List of values that should be copied forward when a new patch set is uploaded.
 |`allow_post_submit`|optional|
 Whether link:config-labels.html#label_allowPostSubmit[allowPostSubmit] is set
 on the label.
@@ -4319,6 +4517,57 @@
 |`size_of_packed_objects`  |Size of packed objects in bytes.
 |======================================
 
+[[submit-requirement-info]]
+=== SubmitRequirementInfo
+The `SubmitRequirementInfo` entity describes a submit requirement.
+
+[options="header",cols="1,^1,5"]
+|===========================
+|Field Name      ||Description
+|`name`||
+The submit requirement name.
+|`description`|optional|
+Description of the submit requirement.
+|`applicability_expression`|optional|
+Query expression that can be evaluated on any change. If evaluated to true on a
+change, the submit requirement is then applicable for this change.
+If not specified, the submit requirement is applicable for all changes.
+|`submittability_expression`||
+Query expression that can be evaluated on any change. If evaluated to true on a
+change, the submit requirement is fulfilled and not blocking change submission.
+|`override_expression`|optional|
+Query expression that can be evaluated on any change. If evaluated to true on a
+change, the submit requirement is overridden and not blocking change submission.
+|`allow_override_in_child_projects`||
+Whether this submit requirement can be overridden in child projects.
+|===========================
+
+[[submit-requirement-input]]
+=== SubmitRequirementInput
+The `SubmitRequirementInput` entity describes a submit requirement.
+
+[options="header",cols="1,^1,5"]
+|===========================
+|Field Name      ||Description
+|`name`||
+The submit requirement name.
+|`description`|optional|
+Description of the submit requirement.
+|`applicability_expression`|optional|
+Query expression that can be evaluated on any change. If evaluated to true on a
+change, the submit requirement is then applicable for this change.
+If not specified, the submit requirement is applicable for all changes.
+|`submittability_expression`||
+Query expression that can be evaluated on any change. If evaluated to true on a
+change, the submit requirement is fulfilled and not blocking change submission.
+|`override_expression`|optional|
+Query expression that can be evaluated on any change. If evaluated to true on a
+change, the submit requirement is overridden and not blocking change submission.
+|`allow_override_in_child_projects`|optional|
+Whether this submit requirement can be overridden in child projects. Default is
+`false`.
+|===========================
+
 [[submit-type-info]]
 === SubmitTypeInfo
 Information about the link:config-project-config.html#submit-type[default submit
diff --git a/Documentation/rest-api.txt b/Documentation/rest-api.txt
index 469bee5..348af76 100644
--- a/Documentation/rest-api.txt
+++ b/Documentation/rest-api.txt
@@ -76,6 +76,16 @@
 headers. If the named resource already exists the server will respond
 with HTTP 412 Precondition Failed.
 
+[[backwards-compatibility]]
+=== Backwards Compatibility
+
+The REST API is regularly extended (e.g. addition of new REST endpoints or new fields in existing
+JSON entities). Callers of the REST API must be able to deal with this (e.g. ignore unknown fields
+in the REST responses). Incompatible changes (e.g. removal of REST endpoints, altering/removal of
+existing fields in JSON entities) are avoided if possible, but can happen in rare cases. If they
+happen, they are announced in the link:https://www.gerritcodereview.com/releases-readme.html[release
+notes].
+
 [[output]]
 === Output Format
 JSON responses are encoded using UTF-8 and use content type
diff --git a/Documentation/user-attention-set.txt b/Documentation/user-attention-set.txt
index 1f67fc7..5e5d3f8 100644
--- a/Documentation/user-attention-set.txt
+++ b/Documentation/user-attention-set.txt
@@ -2,8 +2,6 @@
 
 Report a bug or send feedback using
 link:https://bugs.chromium.org/p/gerrit/issues/entry?template=Attention+Set[this Monorail template].
-You can also report a bug through the bug icon in the user hovercard and in the
-reply dialog.
 
 [[whose-turn]]
 == Whose turn is it?
@@ -49,6 +47,9 @@
   an unresolved comment.
 * If another user removed a user's vote, the user with the deleted vote will be
   added to the attention set.
+* If a vote becomes outdated by uploading a new patch set (vote is not sticky),
+  the user whose vote has been removed is added to the attention set, as they
+  need to re-review the change and vote newly.
 * Only owner, uploader, reviewers and ccs can be in the attention set.
 * The rules for service accounts are different, see link:#bots[Bots].
 * Users are not added by automatic rules when the change is work in progress.
@@ -133,23 +134,26 @@
 Gerrit-Attention: Marian Harbach <mharbach@google.com>
 ----
 
-=== Assignee
+=== Browser notifications
 
-While the "Assignee" feature can still be used together with the attention set,
-we do not recommend doing so. Using both features is likely confusing. The
-distinct feature of the "Assignee" compared to the attention set is that only
-one user can be the assignee at the same time. So the assignee can be used to
-single out one person or escalate, if there are multiple reviewers. Since
-*every* reviewer in the attention set is expected to take action, singling out
-is not likely to be important and also still achievable with the attention set.
-Otherwise "Assignee" and "Attention Set" are very much overlapping, so we
-recommend to only use one of them.
+You'll automatically get notifications when you are in the attention set. You
+must enable desktop notifications on your browser to see them.
 
-If you don't expect action from reviewers, then consider adding them to CC
-instead.
+image::images/browser-notification-example.png["browser notification example", align="center"]
 
-The "Assignee" feature can be turned on/off with the
-link:config-gerrit.html#change.enableAssignee[enableAssignee] config option.
+You can turn off automatic notifications in user preferences. They are enabled
+by default.
+
+image::images/browser-notification-preference.png["user preference for browser notifications", align="center"]
+
+The notifications work only when Gerrit is open in one of the browser tabs.
+The latency to get the notification is up to 5 minutes.
+
+If you are not getting notifications:
+ - Check your user preferences - Allow browser notification setting
+ - Make sure notifications are turned on for the Gerrit site in the browser
+ - Make sure browser notifications are turned on in your operating system
+ - Your host can have browser notifications disabled for some user groups
 
 === Bold Changes / Mark Reviewed
 
diff --git a/Documentation/user-dashboards.txt b/Documentation/user-dashboards.txt
index e64d625..364b0d9 100644
--- a/Documentation/user-dashboards.txt
+++ b/Documentation/user-dashboards.txt
@@ -12,7 +12,7 @@
 
 Dashboards are available via URLs like:
 ----
-  /#/dashboard/?title=Custom+View&To+Review=reviewer:john.doe@example.com&Pending+In+myproject=project:myproject+is:open
+  /dashboard/?title=Custom+View&To+Review=reviewer:john.doe@example.com&Pending+In+myproject=project:myproject+is:open
 ----
 This opens a view showing the title "Custom View" with two sections,
 "To Review" and "Pending in myproject":
@@ -51,7 +51,7 @@
 to changes for the current user:
 
 ----
-  /#/dashboard/?title=Mine&foreach=owner:self&My+Pending=is:open&My+Merged=is:merged
+  /dashboard/?title=Mine&foreach=owner:self&My+Pending=is:open&My+Merged=is:merged
 ----
 
 
diff --git a/Documentation/user-notify.txt b/Documentation/user-notify.txt
index 20ad07c..24c35f0 100644
--- a/Documentation/user-notify.txt
+++ b/Documentation/user-notify.txt
@@ -18,8 +18,8 @@
 [[user]]
 == User Level Settings
 
-Individual users can configure email subscriptions by editing
-watched projects through Settings > Watched Projects with the web UI.
+Individual users can configure email subscriptions by editing their
+notifications in the Web UI under `Settings` > `Notifications`.
 
 Specific projects may be watched, or the special project
 `All-Projects` can be watched to watch all projects that
diff --git a/Documentation/user-privacy.txt b/Documentation/user-privacy.txt
index d61ee76..afedb7e 100644
--- a/Documentation/user-privacy.txt
+++ b/Documentation/user-privacy.txt
@@ -8,7 +8,7 @@
 |===
 | Note: Gerrit has extensive support for link:config-plugins.html[plugins]
   which extend Gerrits functionality, and these plugins could access, export, or
-  maniuplate user data. This document only focuses on the behavior of Gerrit
+  manipulate user data. This document only focuses on the behavior of Gerrit
   core and its link:dev-core-plugins.html[core plugins].
 |===
 
@@ -98,11 +98,6 @@
 * Remove a user's e-mail from all existing commits
 * Remove a user's username
 
-There is also a known
-link:https://bugs.chromium.org/p/gerrit/issues/detail?id=14185[bug] where a
-user's username is stored in metadata for link:user-attention-set.html[Attention
-Set].
-
 
 ## Open Source Software Limitations
 
@@ -110,4 +105,4 @@
 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.
\ No newline at end of file
+language governing permissions and limitations under the License.
diff --git a/Documentation/user-review-ui.txt b/Documentation/user-review-ui.txt
index 6f5f7297..73668d7 100644
--- a/Documentation/user-review-ui.txt
+++ b/Documentation/user-review-ui.txt
@@ -1,21 +1,15 @@
 :linkattrs:
-= Review UI
+= Review UI Overview
 
 Reviewing changes is an important task and the Gerrit Web UI provides
 many functionalities to make the review process comfortable and
 efficient.
 
-The UI has three different main views,
-
-** The dashboard, which shows all changes that are relevant to you
-** The change screen, which shows the change with all its metadata
-** The diff view, which shows changes to a single file
-
 [[change-screen]]
 == Change Screen
 
-The change screen shows the details of a single change and provides
-various actions on it.
+The change screen is the main view for a change. It shows the details of a
+single change and allows various actions on it.
 
 image::images/user-review-ui-change-screen.png[width=800, link="images/user-review-ui-change-screen.png"]
 
@@ -28,44 +22,81 @@
 
 Top left, you find the status of the change, and a permalink.
 
-image::images/user-review-ui-change-screen-topleft.png[width=400, link="images/user-review-ui-change-screen-topleft.png"]
+image::images/user-review-ui-change-screen-topleft.png[width=600, link="images/user-review-ui-change-screen-topleft.png"]
 
 [[change-status]]
 The change status shows the state of the change:
 
-- [[active]]`Active`:
+- `Active`:
 +
 The change is under active review.
 
-- [[merge-conflict]]`Merge Conflict`:
+- `Merge Conflict`:
 +
-The change can't be merged due to conflicts.
+The change can't be merged into the destination branch due to conflicts.
 
-- [[ready-to-submit]]`Ready to Submit`:
+- `Ready to Submit`:
 +
-The change has all necessary approvals and may be submitted.
+The change has all necessary approvals and fulfils all other submit
+requirements. It can be submitted.
 
-- [[merged]]`Merged`:
+- `Merged`:
 +
 The change was successfully merged into the destination branch.
 
-- [[abandoned]]`Abandoned`:
+- `Abandoned`:
 +
-The change was abandoned.
+The change was abandoned. It is not intended to be updated, reviewed or
+submitted anymore.
+
+- `Private`:
++
+The change is marked as link:intro-user.html#private-changes[Private]. And has
+reduced visibility.
+
+- `Revert Created|Revert Submitted`:
++
+The change has a corresponding revert change. Revert changes can be created
+through UI (see <<actions, Actions section>>).
+
+- `WIP`:
++
+The change was marked as "Work in Progress". For example to indicate to
+reviewers that they shouldn't review the change yet.
 
 [[star]]
 === Star Change
 
-Clicking the star icon marks the change as a favorite: it turns on
+Clicking the star icon bookmarks the change: it turns on
 email notifications for this change, and the change is added to the
 list under `Your` > `Starred Changes`. They can be queried by the
 link:user-search.html#is[is:starred] search operator.
 
+[[quick-links]]
+=== Links Menu
+
+Links menu contains various change related strings for quick copying. Such as:
+Change Number, URL, Title+Url, etc. The lines in this menu can also be accessed
+via shortcuts for convenience.
+
+image::images/user-review-ui-copy-links.png[width=600, link="images/user-review-ui-copy-links.png"]
+
 [[change-info]]
 === Change metadata
 
-The change metadata block contains detailed information about the change
-and offers actions on the change.
+The change metadata block contains detailed information about the change.
+
+image::images/user-review-ui-change-metadata.png[width=600, link="images/user-review-ui-change-metadata.png"]
+
+- [[owner]]Owner/Uploader/Author/Committer
++
+Owner is the person who created the change
++
+Uploader is the person who uploaded the latest patchset (the patchset that will
+be merged if the change is submitted)
++
+Author/Committer are concepts from Git and are retrieved from the commit when
+it's sent for review.
 
 - [[reviewers]]Reviewers:
 +
@@ -74,16 +105,36 @@
 For each reviewer there is a tooltip that shows on which labels the
 reviewer is allowed to vote.
 +
-New reviewers can be added by clicking on the pencil icon. Typing
-into the pop-up text field activates auto completion of user and group
-names.
+New reviewers can be added through reply dialog that is opened by clicking on
+the pencil icon or on "Reply" button. Typing into the reviewer text field
+activates auto completion of user and group names.
 +
+
+- [[cc-list]]CC:
++
+Accounts in CC receive notifications for the updates on the change, but don't
+need to vote/review. If the CC'ed user votes they are moved to reviewers.
++
+
+- [[attention-set]]Attention set:
++
+Users in attention set are marked by "chevron" symbol (see screenshot above).
+The mark indicates that there are actions their attention is required on the
+change: Something updated/changed since last review, their vote is required,
+etc.
++
+Changes for which you are currently in attention set can be found using
+`attention:<User>` in search and show up in a separate category of personal
+dashboard.
++
+Clicking on the mark removes the user from attention set.
+
+
 [[remove-reviewer]]
-Reviewers can be removed from the change by clicking on the `x` icon
-in the reviewer's chip token. Removing a reviewer also removes the
-current votes of the reviewer. The removal of votes is recorded as a
-message on the change.
-+
+Reviewers can be removed from the change by selecting the appropriate option on
+the chip's hovercard. Removing a reviewer also removes current votes of the
+reviewer. The removal of votes is recorded in the change log.
+
 Removing reviewers is protected by permissions:
 
 ** Users can always remove themselves.
@@ -92,10 +143,7 @@
    Remove Reviewer] access right, the branch owner, the project owner
    and Gerrit administrators may remove anyone.
 
-+
-image::images/user-review-ui-change-screen-info-reviewers.png[width=600, link="images/user-review-ui-change-screen-reviewers.png"]
-
-- [[project-branch-topic]]Project / Branch / Topic:
+- [[repo-branch-topic]]Project (Repo) / Branch / Topic:
 +
 The name of the project for which the change was done is displayed as a
 link to the link:user-dashboards.html#project-default-dashboard[default
@@ -112,15 +160,55 @@
 access right. To be able to set a topic on a closed change, the
 `Edit Topic Name` must be assigned with the `force` flag.
 
+- [[parent]]Parent:
++
+Parent commit of the latest uploaded patchset. Or if the change has been merged
+the parent of the commit it was merged as into the destination branch.
+
+- [[merged-as]]Merged As:
++
+The SHA of the commit corresponding to the merged change on the destination
+branch.
+
+- [[revert-created-as]]Revert (Created|Submitted) As
++
+Points to the revert change, if one was created.
+
+- [[cherry-pick-of]]Cherry-pick of
++
+If the change was created as cherry-pick of some other change to a different
+branch, points to the original change.
+
 - [[submit-strategy]]Submit Strategy:
 +
 The link:project-setup.html#submit_type[submit strategy] that will be
 used to submit the change. The submit strategy is only displayed for
 open changes.
 
-- [[actions]]Actions:
+- [[hastags]]Hashtags:
 +
-Actions buttons are at the top, and in the overflow menu.
+Arbitrary string hashtags, that can be used to categorize changes and later use
+hashtags for search queries.
+
+[[submit-requirements]]
+=== Submit Requirements
+
+image::images/user-review-ui-submit-requirements.png[width=600, link="images/user-review-ui-copy-links.png"]
+
+Submit Requirements describe various conditions that must be fulfilled before
+the change can be submitted. Hovering over the requirement will show the
+description of the requirement, as well as additional information, such as:
+corresponding expression that is being evaluated, who can vote on the related
+labels etc.
+
+Approving votes are colored green; negative votes are colored red.
+
+For more detail on Submit Requirements see
+link:config-submit-requirements.html[Submit Requirement Configuration] page.
+
+[[actions]]
+=== Actions:
+Actions buttons are at the top right and in the overflow menu.
 Depending on the change state and the permissions of the user, different
 actions are available on the change:
 
@@ -220,13 +308,7 @@
 +
 image::images/user-review-ui-change-screen-change-info-actions.png[width=400, link="images/user-review-ui-change-screen-change-info-actions.png"]
 
-- [[labels]]Labels & Votes:
-+
-Approving votes are colored green; negative votes are colored red.
-+
-image::images/user-review-ui-change-screen-change-info-labels.png[width=400, link="images/user-review-ui-change-screen-change-info-labels.png"]
-
-[[files]]
+[[files-tab]]
 === File List
 
 The file list shows the files that are modified in the currently viewed
@@ -251,17 +333,40 @@
 The list of commits that are being integrated into the destination
 branch by submitting the merge commit.
 
+Every file is accompanied by a number of extra information, such as status
+(modified, added, deleted, etc.), number of changed lines, type (executable,
+link, plain), comments and others. Hovering over most icons and columns reveals
+additional information.
+
+Each file can be expanded to view the contents of the file and diff. For more
+information see <<diff-view, Diff View>> section.
+
+[[comments-tab]]
+=== Comments Tab
+
+Instead of the file list, a comments tab can be selected. Comments tab presents
+comments along with related file/diff snippets. It also offers some filtering
+opportunities at the top (ex. only unresolved, only comments from user X, etc.)
+
+image::images/user-review-ui-change-screen-comments-tab.png[width=800, link="images/user-review-ui-change-screen-comments-tab.png"]
+
+[[checks-tab]]
+=== Checks Tab
+Checks tab contains results of different "Check Runs" installed by plugins. For
+more information see link:pg-plugin-checks-api.html[Checks API] page.
 
 [[patch-sets]]
 === Patch Sets
 
-The change screen only presents one patch set at a time. Which patch
-set is currently viewed can be seen from the `Patch Sets` drop-down
-panel in the change header.
+The change screen only presents one pair of patch sets (`Patchset A` and
+`Patchset B`) at a time. `A` is always an earlier upload than `B` and serves as
+a base for diffing when viewing changes in the files. Which patch
+sets is currently viewed can be seen from the `Patch Sets` drop-down
+panel in the change header. If patchset 'A' is not selected a parent commit of
+patchset 'B' is used by default.
 
 image::images/user-review-ui-change-screen-patch-sets.png[width=300, link="images/user-review-ui-change-screen-patch-sets.png"]
 
-
 [[download]]
 === Download
 
@@ -278,7 +383,8 @@
 
 Each command has a copy-to-clipboard icon that allows the command to be
 copied into the clipboard. This makes it easy to paste and execute the
-command on a Git command line.
+command on a Git command line. Additionally each line can copied to clipboard
+using number (1..9) of the appropriate line as a keyboard shortcut.
 
 If several download schemes are configured on the server (e.g. SSH and
 HTTP) there is a drop-down list to switch between the download schemes.
@@ -306,22 +412,20 @@
 
 image::images/user-review-ui-change-screen-included-in.png[width=800, link="images/user-review-ui-change-screen-included-in.png"]
 
-
-
 [[related-changes]]
 === Related Changes
 
 If there are changes that are related to the currently viewed change
 they are displayed in the third column of the change screen.
 
-There are several lists of related changes and a tab control is used to
-display each list of related changes in its own tab.
+There are several lists of related changes that are displayed in separate
+sectionsunder each other.
 
-The following tabs may be displayed:
+The following sections may be displayed:
 
-- [[related-changes-tab]]`Related Changes`:
+- [[related-changes-section]]`Related Changes`:
 +
-This tab page shows changes on which the current change depends
+This section shows changes on which the current change depends
 (ancestors) and open changes that depend on the current change
 (descendants). For merge commits it also shows the closed changes that
 will be merged into the destination branch by submitting the merge
@@ -341,10 +445,10 @@
 +
 ** [[not-current]]Not current:
 +
-The selected patch set of the change is outdated; it is not the current
-patch set of the change.
+The patch set of the related change which is related to the current change is
+outdated; it is not the current patch set of the change.
 +
-It means that the
+For ancestor it means that the
 currently viewed patch set depends on a outdated patch set of the
 ancestor change. This is because a new patch set for the ancestor
 change was uploaded in the meantime and as result the currently viewed
@@ -364,20 +468,24 @@
 note that following the link to an indirect descendant change may
 result in a completely different related changes listing.
 
-** [[closed-ancestor]]Closed ancestor:
+** [[merged-related-change]]Merged
 +
-Indicates a closed ancestor, e.g. the commit was directly pushed into
-the repository bypassing code review, or the ancestor change was
-reviewed and submitted on another branch. The latter may indicate that
-the user has accidentally pushed the commit to the wrong branch, e.g.
-the commit was done on `branch-a`, but was then pushed to
-`refs/for/branch-b`.
+The change has been  merged.
++
+If the relationship to submitted change falls under conditions described in
+<<not-current, Not current>> the status is orange. Such changes can appear as
+both ancestors and descendants of the change.
+
+** [[submittable-related-change]]Submittable
++
+All the submit requirements are fulfilled for the related change and it can be
+submitted when all of its ancestors are submitted.
 
 ** [[closed-ancestor-abandoned]]Abandoned:
 +
 Indicates an abandoned change.
 
-- [[conflicts-with]]`Conflicts With`:
+- [[conflicts-with]]`Merge Conflicts`:
 +
 This section shows changes that conflict with the current change.
 Non-mergeable changes are filtered out; only conflicting changes that
@@ -393,10 +501,9 @@
 currently viewed change, when clicking the submit button. It includes
 ancestors of the current patch set.
 +
-This may include changes and its ancestors with the same topic if
-`change.submitWholeTopic` is enabled. Only open changes with the
-same topic are included in the list.
-+
+If `change.submitWholeTopic` is enabled this section also includes changes with
+the same topic. The list recursively includes all changes that can be reached by
+ancestor and topic relationships. Only open changes are included in the result.
 
 - [[cherry-picks]]`Cherry-Picks`:
 +
@@ -411,12 +518,18 @@
 
 If there are no related changes for a tab, the tab is not displayed.
 
+- [[same-topic]]`Same Topic`:
++
+This section shows changes which are part of the same topic. If
+`change.submitWholeTopic` is enabled, then this section is omitted and changes
+are included as part of <<submitted-together, `Submitted Together`>>
+
 [[reply]]
 === Reply
 
 The `Reply...` button in the change header allows to reply to the
 currently viewed patch set; one can add a summary comment, publish
-inline draft comments, and vote on the labels.
+inline draft comments, vote on the labels and adjust attention set.
 
 image::images/user-review-ui-change-screen-reply.png[width=800, link="images/user-review-ui-change-screen-reply.png"]
 
@@ -424,10 +537,8 @@
 
 [[summary-comment]]
 A text box allows to type a summary comment for the currently viewed
-patch set. Some basic markdown-like syntax is supported which renders
-indented lines preformatted, lines starting with "- " or "* " as list
-items, and lines starting with "> " as block quotes (also see replying to
-link:#reply-to-message[messages] and link:#reply-inline-comment[inline comments]).
+patch set. Markdown syntax is supported same as in other
+<<comments-markdown, Comments>>.
 
 [[vote]]
 If the current patch set is viewed, buttons are displayed for
@@ -439,7 +550,7 @@
 are links to navigate to the inline comments which can be used if a
 comment needs to be edited.
 
-The `Post` button publishes the comments and the votes.
+The `SEND` button publishes the comments and the votes.
 
 [[quick-approve]]
 If a user can approve a label that is still required, a quick approve
@@ -460,12 +571,12 @@
 
 image::images/user-review-ui-change-screen-quick-approve.png[width=800, link="images/user-review-ui-change-screen-quick-approve.png"]
 
-[[history]]
-=== History
+[[change-log]]
+=== Change Log
 
 The history of the change can be seen in the lower part of the screen.
 
-The history contains messages for all kinds of change updates, e.g. a
+The log contains messages for all kinds of change updates, e.g. a
 message is added when a new patch set is uploaded or when a review was
 done.
 
@@ -491,12 +602,12 @@
 
 image::images/user-review-ui-change-screen-plugin-extensions.png[width=300, link="images/user-review-ui-change-screen-plugin-extensions.png"]
 
-[[side-by-side]]
+[[diff-view]]
 == Side-by-Side Diff Screen
 
-The side-by-side diff screen shows a single patch; the old file version
-is displayed on the left side of the screen; the new file version is
-displayed on the right side of the screen.
+The side-by-side diff screen shows a single patch (or difference between two
+patchsets); the old file version is displayed on the left side of the screen;
+the new file version is displayed on the right side of the screen.
 
 This screen allows to review a patch and to comment on it.
 
@@ -557,6 +668,10 @@
 Code blocks with comments may overlap. This means it is possible to
 attach several comments to the same code.
 
+[[comments-markdown]]
+The comments support markdown. It follows the CommonMark spec, except inline
+images and direct HTML are not rendered and kept as plaintext.
+
 [[line-links]]
 The lines of the patch file are linkable: simply append
 '#<linenumber>' to the URL, or click on the line-number. This not only
@@ -565,15 +680,14 @@
 [[reply-inline-comment]]
 Clicking on the `Reply` button opens an editor to type the reply.
 
-Quoting is supported, but only by manually copying & pasting the old
-comment that should be quoted and prefixing every line by "> ". Please
-note that for a correct rendering it is important to leave a blank line
-between a quoted block and the reply to it.
+Previous comment can be quoted using "Quote" button. A new draft would be open
+on the same comment thread with the text of the previoused comment quoted using
+markdown syntax.
 
 image::images/user-review-ui-side-by-side-diff-screen-inline-comments.png[width=800, link="images/user-review-ui-side-by-side-diff-screen-inline-comments.png"]
 
 Comments are first saved as drafts, and you can revisit the drafts as
-you read through code review. Finally, they should be published by
+you read through code review. Finally, they will be published by
 clicking the "Reply".
 
 [[done]]
@@ -610,6 +724,21 @@
 make it visible to other users it must be published from the change
 screen by link:#reply[replying] to the change.
 
+[[suggest-fix]]
+=== Suggest fix (WIP)
+Comments can contain suggested fixes.
+
+Clicking "Suggest Fix" will insert a special code-block in the text of the
+comment. The contents of this code block will replace the lines the comment is
+attached to (what gets highlighted when hovering over comment).
+
+image::images/user-review-ui-suggest-fix.png[width=400, link="images/user-review-ui-suggest-fix.png"]
+
+The author of the change can then preview and apply the change. This will created
+a new patchset with changes applied.
+
+image::images/user-review-ui-apply-fix.png[width=800, link="images/user-review-ui-apply-fix.png"]
+
 [[file-level-comments]]
 === File Level Comments
 
diff --git a/Documentation/user-search-accounts.txt b/Documentation/user-search-accounts.txt
index 6bcd18e..d5318c9 100644
--- a/Documentation/user-search-accounts.txt
+++ b/Documentation/user-search-accounts.txt
@@ -26,7 +26,10 @@
 [[cansee]]
 cansee:'CHANGE'::
 +
-Matches accounts that can see the change 'CHANGE'.
+Matches accounts that can see the change 'CHANGE'. If the change is private,
+this operator will match with the owner/reviewers/ccs of the change if the
+caller is in owner/reviewers/ccs of the change. Otherwise, the request will fail
+with 404 `Bad Request` with "change not found" message.
 
 [[email]]
 email:'EMAIL'::
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 6e6b9d7..e12c27c 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -8,16 +8,18 @@
 query, execute it, and present the results.
 
 [options="header"]
-|=================================================
-|Description          | Default Query
-|All > Open           | status:open '(or is:open)'
-|All > Merged         | status:merged
-|All > Abandoned      | status:abandoned
-|My > Watched Changes | is:watched is:open
-|My > Starred Changes | is:starred
-|My > Draft Comments  | has:draft
-|Open changes in Foo  | status:open project:Foo
-|=================================================
+|=======================================================
+|Description                | Default Query
+|Changes > Open             | status:open '(or is:open)'
+|Changes > Merged           | status:merged
+|Changes > Abandoned        | status:abandoned
+|Your > Watched Changes     | is:watched is:open
+|Your > Starred Changes     | is:starred
+|Your > Draft Comments      | has:draft
+|Your > Edits               | has:edit
+|Your > All Visible Changes | is:visible
+|Open changes in Foo        | status:open project:Foo
+|=======================================================
 
 
 == Basic Change Search
@@ -41,6 +43,17 @@
 For more predictable results, use explicit search operators as described
 in the following section.
 
+[IMPORTANT]
+--
+The change search API is backed by a secondary index and might sometimes return
+stale results if the re-indexing operation failed for a change update.
+
+Please also note that changes are not re-indexed if the project configuration
+is updated with newly added or modified
+link:config-submit-requirements.html[submit requirements].
+--
+
+
 [[search-operators]]
 == Search Operators
 
@@ -49,6 +62,10 @@
 returned results. Search can also be performed by typing only a
 text with no operator, which will match against a variety of fields.
 
+Characters in operator values can be escaped by enclosing the value with
+double quotes and escaping characters with a backslash. For example
+`message:"This \"is\" fixing a bug"`.
+
 [[age]]
 age:'AGE'::
 +
@@ -264,6 +281,11 @@
 often combined with 'branch:' and 'project:' operators to select
 all related changes in a series.
 
+[[prefixtopic]]
+prefixtopic:'TOPIC'::
++
+Changes whose designated topic start with 'TOPIC'.
+
 [[inhashtag]]
 inhashtag:'HASHTAG'::
 +
@@ -280,6 +302,12 @@
 Changes whose link:intro-user.html#hashtags[hashtag] matches 'HASHTAG'.
 The match is case-insensitive.
 
+[[prefixhashtag]]
+prefixhashtag:'HASHTAG'::
++
+Changes whose link:intro-user.html#hashtags[hashtag] start with 'HASHTAG'.
+The match is case-insensitive.
+
 [[cherrypickof]]
 cherrypickof:'CHANGE[,PATCHSET]'::
 +
@@ -323,6 +351,24 @@
 message:'MESSAGE'::
 +
 Changes that match 'MESSAGE' arbitrary string in the commit message body.
+By default full text matching is used, but regular expressions can be
+enabled by starting with `^`.
+The link:http://www.brics.dk/automaton/[dk.brics.automaton library,role=external,window=_blank]
+is used for the evaluation of such patterns. Note, that searching with
+regular expressions is limited to the first 32766 bytes of the
+commit message due to limitations in Lucene.
+
+[[subject]]
+subject:'SUBJECT'::
++
+Changes that have a commit message where the first line (aka the subject)
+matches 'SUBJECT'. The matching is done by full text search over the subject.
+
+[[prefixsubject]]
+prefixsubject:'PREFIX'::
++
+Changes that have a commit message where the first line (aka the subject)
+has the prefix 'PREFIX'.
 
 [[comment]]
 comment:'TEXT'::
@@ -344,7 +390,7 @@
 files, use `file:^.*\.java`.
 +
 The entire regular expression pattern, including the `^` character,
-should be double quoted. For example, to match all XML
+can be double quoted. For example, to match all XML
 files named like 'name1.xml', 'name2.xml', and 'name3.xml' use
 `file:"^name[1-3].xml"`.
 +
@@ -352,8 +398,8 @@
 +
 *More examples:*
 
-* `-file:^path/.*` - changes that do not modify files from `path/`.
-* `file:{^~(path/.*)}` - changes that modify files not from `path/` (but may
+* `-path:^path/.*` - changes that do not modify files from `path/`.
+* `path:{^~(path/.*)}` - changes that modify files not from `path/` (but may
 contain files from `path/`).
 
 [[file]]
@@ -408,18 +454,11 @@
 current patch set. 'FOOTER' can be specified verbatim ('<key>: <value>', must
 be quoted) or as '<key>=<value>'. The matching is done case-insensitive.
 
-[[star]]
-star:'LABEL'::
+[[hasfooter-operator]]
+hasfooter:'FOOTERNAME'::
 +
-Matches any change that was starred by the current user with the label
-'LABEL'.
-+
-E.g. if changes that are not interesting are marked with an `ignore`
-star, they could be filtered out by '-star:ignore'.
-+
-'star:star' is the same as 'has:star' and 'is:starred'.
-
-Only "ignore" and "star" are supported labels.
+Matches any change that has a commit message with a footer where the footer
+name is equal to 'FOOTERNAME'.The matching is done case-sensitive.
 
 [[has]]
 has:draft::
@@ -429,8 +468,8 @@
 [[has-star]]
 has:star::
 +
-Same as 'is:starred' and 'star:star', true if the change has been
-starred by the current user with the default label.
+Same as 'is:starred', true if the change has been starred by the current user
+with the default label.
 
 has:edit::
 +
@@ -507,6 +546,7 @@
 +
 Same as <<status,status:'STATE'>>.
 
+[[is-submittable]]
 is:submittable::
 +
 True if the change is submittable according to the submit rules for
@@ -518,8 +558,6 @@
 use the
 link:rest-api-changes.html#get-revision-actions[Get Revision Actions]
 API.
-+
-Equivalent to <<submittable,submittable:ok>>.
 
 [[mergeable]]
 is:mergeable::
@@ -531,14 +569,9 @@
 not find any abandoned but mergeable changes.
 +
 This operator only works if Gerrit indexes 'mergeable'. See
-link:config-gerrit.html#index.change.indexMergeable[indexMergeable]
+link:config-gerrit.html#change.mergeabilityComputationBehavior[change.mergeabilityComputationBehavior]
 for details.
 
-[[ignored]]
-is:ignored::
-+
-True if the change is ignored. Same as `star:ignore`.
-
 [[private]]
 is:private::
 +
@@ -610,7 +643,7 @@
 +
 Changes containing a top-level or inline comment by 'USER'. The special
 case of `commentby:self` will find changes where the caller has
-commented.
+commented. Note that setting a vote is also considered as a comment.
 
 [[from]]
 from:'USER'::
@@ -639,16 +672,6 @@
 email address. The special case of `committer:self` will find changes committed
 by the caller.
 
-
-[[submittable]]
-submittable:'SUBMIT_STATUS'::
-+
-Changes having the given submit record status after applying submit
-rules. Valid statuses are in the `status` field of
-link:rest-api-changes.html#submit-record[SubmitRecord]. This operator
-only applies to the top-level status; individual label statuses can be
-searched link:#labels[by label].
-
 [[rule]]
 rule:'SUBMIT_RULE_NAME'::
 +
@@ -656,8 +679,7 @@
 FORCED}. This means that the submit rule has passed and is not blocking the
 change submission. 'SUBMIT_RULE_NAME' should be in the form of
 '$plugin_name~$rule_name'. For gerrit core rules, 'SUBMIT_RULE_NAME' should
-be in the form of '$rule_name' (example: `DefaultSubmitRule`), or
-'gerrit~$rule_name' (example: `gerrit~DefaultSubmitRule`).
+be in the form of 'gerrit~$rule_name' (example: `gerrit~DefaultSubmitRule`).
 
 rule:'SUBMIT_RULE_NAME'='STATUS'::
 +
@@ -699,17 +721,20 @@
 `-is:starred` is the exact opposite of `is:starred` and will
 therefore return changes that are *not* starred by the current user.
 
-The operator `NOT` (in all caps) is a synonym.
+The operator `NOT` (in all caps) or `not` (all lower case) is a
+synonym.
 
 === AND
-The boolean operator `AND` (in all caps) can be used to join two
-other operators together.  This results in a restriction of the
-results, returning only changes that match both operators.
+The boolean operator `AND` (in all caps) or `and` (all lower case)
+can be used to join two other operators together.  This results in
+a restriction of the results, returning only changes that match both
+operators.
 
 === OR
-The boolean operator `OR` (in all caps) can be used to find changes
-that match either operator.  This increases the number of results
-that are returned, as more changes are considered.
+The boolean operator `OR` (in all caps) or `or` (all lower case)
+can be used to find changes that match either operator. This
+increases the number of results that are returned, as more changes
+are considered.
 
 
 [[labels]]
@@ -793,12 +818,25 @@
 Note that a query like `label:Code-Review=+1,count<x` will not match with
 changes having zero +1 votes to this label.
 
-`label:Non-Author-Code-Review=need`::
+`label:Non-Author-Code-Review=need` (deprecated)::
 +
 Matches changes where the submit rules indicate that a label named
 `Non-Author-Code-Review` is needed. (See the
 link:prolog-cookbook.html#NonAuthorCodeReview[Prolog Cookbook] for how
 this label can be configured.)
++
+This operator is also compatible with
+link:config-submit-requirements.html[submit requirement] results. A submit
+requirement name could be used instead of the label name. The submit record
+statuses are mapped to submit requirement result statuses as follows:
++
+  * {`need`, `reject`} -> {`UNSATISFED`}
+  * {`ok`, `may`} -> {`SATISFIED`, `OVERRIDDEN`}
++
+For example, a query like `label:Code-Review=ok` will also match changes
+having a submit requirement with a result that is either `SATISFIED` or
+`OVERRIDDEN`. Users are encouraged not to rely on this operator since submit
+records are deprecated.
 
 `label:Code-Review=+2,aname`::
 `label:Code-Review=ok,aname`::
@@ -836,7 +874,7 @@
 +
 Matches changes that are ready to be submitted according to one common
 label configuration. (For a more general check, use
-link:#submittable[submittable:ok].)
+link:#is-submittable[is:submittable].)
 
 `is:open (label:Verified-1 OR label:Code-Review-2)`::
 `is:open (label:Verified=reject OR label:Code-Review=reject)`::
diff --git a/Documentation/user-upload.txt b/Documentation/user-upload.txt
index 2bfc62d..8c51207 100644
--- a/Documentation/user-upload.txt
+++ b/Documentation/user-upload.txt
@@ -340,7 +340,7 @@
 To avoid confusion in parsing the git ref, at least the following characters
 must be percent-encoded: " %^@.~-+_:/!". Note that some of the reserved
 characters (like tilde) are not escaped in the standard URL encoding rules,
-so a language-provided function (e.g. encodeURIComponent(), in javascript)
+so a language-provided function (e.g. encodeURIComponent(), in JavaScript)
 might not suffice. To be safest, you might consider percent-encoding all
 non-alphanumeric characters (and all multibyte UTF-8 code points).
 
diff --git a/README.md b/README.md
index 8a4379b..c8f0b70 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
 [Gerrit](https://www.gerritcodereview.com) is a code review and project
 management tool for Git based projects.
 
-[![Build Status](https://gerrit-ci.gerritforge.com/job/Gerrit-bazel-java11-master/badge/icon)](https://gerrit-ci.gerritforge.com/job/Gerrit-bazel-java11-master/)
+[![Build Status](https://gerrit-ci.gerritforge.com/view/Gerrit/job/Gerrit-bazel-master/badge/icon)](https://gerrit-ci.gerritforge.com/view/Gerrit/job/Gerrit-bazel-master/)
 ![Maven Central](https://img.shields.io/maven-central/v/com.google.gerrit/gerrit-war)
 
 ## Objective
@@ -60,7 +60,7 @@
 
 On Debian/Ubuntu run:
 
-        apt-get update & apt-get install gerrit=<version>-<release>
+        apt-get update && apt-get install gerrit=<version>-<release>
 
 _NOTE: release is a counter that starts with 1 and indicates the number of packages that have
 been released with the same version of the software._
diff --git a/WORKSPACE b/WORKSPACE
index 93cae7d..047da6a 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,62 +1,61 @@
-# npm packages are split into different node_modules directories based on their usage.
-# 1. /node_modules (referenced as @npm) - contains packages to run tests, check code, etc...
-#    It is expected that @npm is used ONLY to run tools. No packages from @npm are used by
-#    other code in gerrit.
-# 2. @tools_npm (tools/node_tools/node_modules) - the tools/node_tools folder contains self-written tools
-#    which are run for building and/or testing. The @tools_npm directory contains all the packages needed to
-#    run this tools.
-# 3. @ui_npm (polygerrit-ui/app/node_modules) - packages with source code which are necessary to run polygerrit
-#    and to bundle it. Only code from these packages can be included in the final bundle for polygerrit.
-#    @ui_npm folder must not have devDependencies. All dev dependencies must be placed in @ui_dev_npm
-# 4. @ui_dev_npm (polygerrit-ui/node_modules) - devDependencies for polygerrit. The packages from these
-#    folder can be used for testing, but must not be included in the final bundle.
-# 5. @plugins_npm (plugins/node_modules) - plugin dependencies for polygerrit plugins.
-#    The packages here are expected to be used in plugins.
-# Note: separation between @ui_npm and @ui_dev_npm is necessary because with bazel we can't generate
-#    two managed directories from the same package.json. At the same time we want to avoid accidental
-#    usages of code from devDependencies in polygerrit bundle.
+# npm packages are split into different node_modules directories based on their
+# usage.
+# 1. @npm (node_modules) - contains packages to run tests, check code, etc...
+#    It is expected that @npm is used ONLY to run tools. No packages from @npm
+#    are used by other code in gerrit.
+# 2. @tools_npm (tools/node_tools/node_modules) - the tools/node_tools folder
+#    contains self-written tools which are run for building and/or testing. The
+#    @tools_npm directory contains all the packages needed to run this tools.
+# 3. @ui_npm (polygerrit-ui/app/node_modules) - packages with source code which
+#    are necessary to run polygerrit and to bundle it. Only code from these
+#    packages can be included in the final bundle for polygerrit. @ui_npm folder
+#    must not have devDependencies. All devDependencies must be placed in
+#    @ui_dev_npm.
+# 4. @ui_dev_npm (polygerrit-ui/node_modules) - devDependencies for polygerrit.
+#    The packages from these folder can be used for testing, but must not be
+#    included in the final bundle.
+# 5. @plugins_npm (plugins/node_modules) - plugin dependencies for polygerrit
+#    plugins. The packages here are expected to be used in plugins.
+# Note: separation between @ui_npm and @ui_dev_npm is necessary because with
+#    rules_nodejs we can't generate two external repositories from the same
+#    package.json. At the same time we want to avoid accidental usages of code
+#    from devDependencies in polygerrit bundle.
 workspace(
     name = "gerrit",
-    managed_directories = {
-        "@npm": ["node_modules"],
-        "@ui_npm": ["polygerrit-ui/app/node_modules"],
-        "@ui_dev_npm": ["polygerrit-ui/node_modules"],
-        "@tools_npm": ["tools/node_tools/node_modules"],
-        "@plugins_npm": ["plugins/node_modules"],
-    },
 )
 
 load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
 load("//tools/bzl:maven_jar.bzl", "GERRIT", "MAVEN_LOCAL", "maven_jar")
 load("//plugins:external_plugin_deps.bzl", "external_plugin_deps")
-load("//tools:nongoogle.bzl", "TESTCONTAINERS_VERSION", "declare_nongoogle_deps")
+load("//tools:nongoogle.bzl", "declare_nongoogle_deps")
+load("//tools:deps.bzl", "CAFFEINE_VERS", "java_dependencies")
 
 http_archive(
-    name = "bazel_toolchains",
-    sha256 = "1adf7a8e9901287c644dcf9ca08dd8d67a69df94bedbd57a841490a84dc1e9ed",
-    strip_prefix = "bazel-toolchains-5.0.0",
+    name = "platforms",
+    sha256 = "379113459b0feaf6bfbb584a91874c065078aa673222846ac765f86661c27407",
     urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/v5.0.0.tar.gz",
-        "https://github.com/bazelbuild/bazel-toolchains/archive/v5.0.0.tar.gz",
+        "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.5/platforms-0.0.5.tar.gz",
+        "https://github.com/bazelbuild/platforms/releases/download/0.0.5/platforms-0.0.5.tar.gz",
     ],
 )
 
-load("@bazel_toolchains//rules:rbe_repo.bzl", "rbe_autoconfig")
-
-# Creates a default toolchain config for RBE.
-rbe_autoconfig(
+http_archive(
     name = "rbe_jdk11",
-    java_home = "/usr/lib/jvm/11.29.3-ca-jdk11.0.2/reduced",
-    use_checked_in_confs = "Force",
+    sha256 = "dbcfd6f26589ef506b91fe03a12dc559ca9c84699e4cf6381150522287f0e6f6",
+    strip_prefix = "rbe_autoconfig-3.1.0",
+    urls = [
+        "https://gerrit-bazel.storage.googleapis.com/rbe_autoconfig/v3.1.0.tar.gz",
+        "https://github.com/davido/rbe_autoconfig/archive/v3.1.0.tar.gz",
+    ],
 )
 
 http_archive(
     name = "com_google_protobuf",
-    sha256 = "d0f5f605d0d656007ce6c8b5a82df3037e1d8fe8b121ed42e536f569dec16113",
-    strip_prefix = "protobuf-3.14.0",
+    sha256 = "3bd7828aa5af4b13b99c191e8b1e884ebfa9ad371b0ce264605d347f135d2568",
+    strip_prefix = "protobuf-3.19.4",
     urls = [
-        "https://github.com/protocolbuffers/protobuf/archive/v3.14.0.tar.gz",
+        "https://github.com/protocolbuffers/protobuf/archive/v3.19.4.tar.gz",
     ],
 )
 
@@ -66,58 +65,43 @@
 
 http_archive(
     name = "build_bazel_rules_nodejs",
-    sha256 = "10f534e1c80f795cffe1f2822becd4897754d18564612510c59b3c73544ae7c6",
-    urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/3.5.0/rules_nodejs-3.5.0.tar.gz"],
+    sha256 = "c29944ba9b0b430aadcaf3bf2570fece6fc5ebfb76df145c6cdad40d65c20811",
+    urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/5.7.0/rules_nodejs-5.7.0.tar.gz"],
 )
 
+load("@build_bazel_rules_nodejs//:repositories.bzl", "build_bazel_rules_nodejs_dependencies")
+
+build_bazel_rules_nodejs_dependencies()
+
+# This is required just because we have a dependency on @bazel/concatjs.
+# We don't actually use any of this web_testing stuff.
+# TODO: Remove this dependency.
 http_archive(
-    name = "rules_pkg",
-    sha256 = "038f1caa773a7e35b3663865ffb003169c6a71dc995e39bf4815792f385d837d",
+    name = "io_bazel_rules_webtesting",
+    sha256 = "e9abb7658b6a129740c0b3ef6f5a2370864e102a5ba5ffca2cea565829ed825a",
     urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.4.0/rules_pkg-0.4.0.tar.gz",
-        "https://github.com/bazelbuild/rules_pkg/releases/download/0.4.0/rules_pkg-0.4.0.tar.gz",
+        "https://github.com/bazelbuild/rules_webtesting/releases/download/0.3.5/rules_webtesting.tar.gz",
     ],
 )
 
-load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies")
+# TODO: Remove this, see comments on `io_bazel_rules_webtesting`.
+load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories")
 
-rules_pkg_dependencies()
+# TODO: Remove this, see comments on `io_bazel_rules_webtesting`.
+web_test_repositories()
 
-# Golang support for PolyGerrit local dev server.
-http_archive(
-    name = "io_bazel_rules_go",
-    sha256 = "4d838e2d70b955ef9dd0d0648f673141df1bc1d7ecf5c2d621dcc163f47dd38a",
-    urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.24.12/rules_go-v0.24.12.tar.gz",
-        "https://github.com/bazelbuild/rules_go/releases/download/v0.24.12/rules_go-v0.24.12.tar.gz",
-    ],
+# TODO: Remove this, see comments on `io_bazel_rules_webtesting`.
+load("@io_bazel_rules_webtesting//web/versioned:browsers-0.3.3.bzl", "browser_repositories")
+
+# TODO: Remove this, see comments on `io_bazel_rules_webtesting`.
+browser_repositories(
+    chromium = True,
+    firefox = True,
 )
 
-load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
+register_toolchains("//tools:error_prone_warnings_toolchain_java11_definition")
 
-go_rules_dependencies()
-
-go_register_toolchains()
-
-http_archive(
-    name = "bazel_gazelle",
-    sha256 = "222e49f034ca7a1d1231422cdb67066b885819885c356673cb1f72f748a3c9d4",
-    urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.22.3/bazel-gazelle-v0.22.3.tar.gz",
-        "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.22.3/bazel-gazelle-v0.22.3.tar.gz",
-    ],
-)
-
-load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository")
-
-gazelle_dependencies()
-
-# Dependencies for PolyGerrit local dev server.
-go_repository(
-    name = "com_github_howeyc_fsnotify",
-    commit = "441bbc86b167f3c1f4786afae9931403b99fdacf",
-    importpath = "github.com/howeyc/fsnotify",
-)
+register_toolchains("//tools:error_prone_warnings_toolchain_java17_definition")
 
 # JGit external repository consumed from git submodule
 local_repository(
@@ -125,92 +109,9 @@
     path = "modules/jgit",
 )
 
-ANTLR_VERS = "3.5.2"
+java_dependencies()
 
-maven_jar(
-    name = "java-runtime",
-    artifact = "org.antlr:antlr-runtime:" + ANTLR_VERS,
-    sha1 = "cd9cd41361c155f3af0f653009dcecb08d8b4afd",
-)
-
-maven_jar(
-    name = "stringtemplate",
-    artifact = "org.antlr:stringtemplate:4.0.2",
-    sha1 = "e28e09e2d44d60506a7bcb004d6c23ff35c6ac08",
-)
-
-maven_jar(
-    name = "org-antlr",
-    artifact = "org.antlr:antlr:" + ANTLR_VERS,
-    sha1 = "c4a65c950bfc3e7d04309c515b2177c00baf7764",
-)
-
-maven_jar(
-    name = "antlr27",
-    artifact = "antlr:antlr:2.7.7",
-    attach_source = False,
-    sha1 = "83cd2cd674a217ade95a4bb83a8a14f351f48bd0",
-)
-
-maven_jar(
-    name = "aopalliance",
-    artifact = "aopalliance:aopalliance:1.0",
-    sha1 = "0235ba8b489512805ac13a8f9ea77a1ca5ebe3e8",
-)
-
-maven_jar(
-    name = "javax_inject",
-    artifact = "javax.inject:javax.inject:1",
-    sha1 = "6975da39a7040257bd51d21a231b76c915872d38",
-)
-
-maven_jar(
-    name = "servlet-api",
-    artifact = "org.apache.tomcat:tomcat-servlet-api:8.5.23",
-    sha1 = "021a212688ec94fe77aff74ab34cc74f6f940e60",
-)
-
-# JGit's transitive dependencies
-maven_jar(
-    name = "hamcrest-library",
-    artifact = "org.hamcrest:hamcrest-library:1.3",
-    sha1 = "4785a3c21320980282f9f33d0d1264a69040538f",
-)
-
-maven_jar(
-    name = "jzlib",
-    artifact = "com.jcraft:jzlib:1.1.1",
-    sha1 = "a1551373315ffc2f96130a0e5704f74e151777ba",
-)
-
-maven_jar(
-    name = "javaewah",
-    artifact = "com.googlecode.javaewah:JavaEWAH:1.1.6",
-    attach_source = False,
-    sha1 = "94ad16d728b374d65bd897625f3fbb3da223a2b6",
-)
-
-maven_jar(
-    name = "error-prone-annotations",
-    artifact = "com.google.errorprone:error_prone_annotations:2.3.3",
-    sha1 = "42aa5155a54a87d70af32d4b0d06bf43779de0e2",
-)
-
-maven_jar(
-    name = "gson",
-    artifact = "com.google.code.gson:gson:2.8.5",
-    sha1 = "f645ed69d595b24d4cf8b3fbb64cc505bede8829",
-)
-
-CAFFEINE_VERS = "2.8.5"
-
-maven_jar(
-    name = "caffeine",
-    artifact = "com.github.ben-manes.caffeine:caffeine:" + CAFFEINE_VERS,
-    sha1 = "f0eafef6e1529a44e36549cd9d1fc06d3a57f384",
-)
-
-CAFFEINE_GUAVA_SHA256 = "a7ce6d29c40bccd688815a6734070c55b20cd326351a06886a6144005aa32299"
+CAFFEINE_GUAVA_SHA256 = "6e48965614557ba4d3c55a197e20c38f23a20032ef8aace37e95ed64d2ebc9a6"
 
 # TODO(davido): Rename guava.jar to caffeine-guava.jar on fetch to prevent potential
 # naming collision between caffeine guava adapter and guava library itself.
@@ -230,701 +131,22 @@
     ],
 )
 
-maven_jar(
-    name = "guava-failureaccess",
-    artifact = "com.google.guava:failureaccess:1.0.1",
-    sha1 = "1dcf1de382a0bf95a3d8b0849546c88bac1292c9",
-)
-
-maven_jar(
-    name = "jsch",
-    artifact = "com.jcraft:jsch:0.1.54",
-    sha1 = "da3584329a263616e277e15462b387addd1b208d",
-)
-
-maven_jar(
-    name = "juniversalchardet",
-    artifact = "com.github.albfernandez:juniversalchardet:2.0.0",
-    sha1 = "28c59f58f5adcc307604602e2aa89e2aca14c554",
-)
-
-SLF4J_VERS = "1.7.26"
-
-maven_jar(
-    name = "log-api",
-    artifact = "org.slf4j:slf4j-api:" + SLF4J_VERS,
-    sha1 = "77100a62c2e6f04b53977b9f541044d7d722693d",
-)
-
-maven_jar(
-    name = "log-ext",
-    artifact = "org.slf4j:slf4j-ext:" + SLF4J_VERS,
-    sha1 = "31cdf122e000322e9efcb38913e9ab07825b17ef",
-)
-
-maven_jar(
-    name = "impl-log4j",
-    artifact = "org.slf4j:slf4j-log4j12:" + SLF4J_VERS,
-    sha1 = "12f5c685b71c3027fd28bcf90528ec4ec74bf818",
-)
-
-maven_jar(
-    name = "jcl-over-slf4j",
-    artifact = "org.slf4j:jcl-over-slf4j:" + SLF4J_VERS,
-    sha1 = "33fbc2d93de829fa5e263c5ce97f5eab8f57d53e",
-)
-
-maven_jar(
-    name = "log4j",
-    artifact = "log4j:log4j:1.2.17",
-    sha1 = "5af35056b4d257e4b64b9e8069c0746e8b08629f",
-)
-
-maven_jar(
-    name = "json-smart",
-    artifact = "net.minidev:json-smart:1.1.1",
-    sha1 = "24a2f903d25e004de30ac602c5b47f2d4e420a59",
-)
-
-maven_jar(
-    name = "args4j",
-    artifact = "args4j:args4j:2.33",
-    sha1 = "bd87a75374a6d6523de82fef51fc3cfe9baf9fc9",
-)
-
-maven_jar(
-    name = "commons-codec",
-    artifact = "commons-codec:commons-codec:1.10",
-    sha1 = "4b95f4897fa13f2cd904aee711aeafc0c5295cd8",
-)
-
-# When upgrading commons-compress, also upgrade tukaani-xz
-maven_jar(
-    name = "commons-compress",
-    artifact = "org.apache.commons:commons-compress:1.18",
-    sha1 = "1191f9f2bc0c47a8cce69193feb1ff0a8bcb37d5",
-)
-
-maven_jar(
-    name = "commons-lang",
-    artifact = "commons-lang:commons-lang:2.6",
-    sha1 = "0ce1edb914c94ebc388f086c6827e8bdeec71ac2",
-)
-
-maven_jar(
-    name = "commons-lang3",
-    artifact = "org.apache.commons:commons-lang3:3.8.1",
-    sha1 = "6505a72a097d9270f7a9e7bf42c4238283247755",
-)
-
-maven_jar(
-    name = "commons-text",
-    artifact = "org.apache.commons:commons-text:1.2",
-    sha1 = "74acdec7237f576c4803fff0c1008ab8a3808b2b",
-)
-
-maven_jar(
-    name = "commons-dbcp",
-    artifact = "commons-dbcp:commons-dbcp:1.4",
-    sha1 = "30be73c965cc990b153a100aaaaafcf239f82d39",
-)
-
-# Transitive dependency of commons-dbcp, do not update without
-# also updating commons-dbcp
-maven_jar(
-    name = "commons-pool",
-    artifact = "commons-pool:commons-pool:1.5.5",
-    sha1 = "7d8ffbdc47aa0c5a8afe5dc2aaf512f369f1d19b",
-)
-
-maven_jar(
-    name = "commons-net",
-    artifact = "commons-net:commons-net:3.6",
-    sha1 = "b71de00508dcb078d2b24b5fa7e538636de9b3da",
-)
-
-maven_jar(
-    name = "commons-validator",
-    artifact = "commons-validator:commons-validator:1.6",
-    sha1 = "e989d1e87cdd60575df0765ed5bac65c905d7908",
-)
-
-maven_jar(
-    name = "automaton",
-    artifact = "dk.brics:automaton:1.12-1",
-    sha1 = "959a0c62f9a5c2309e0ad0b0589c74d69e101241",
-)
-
-COMMONMARK_VERS = "0.10.0"
-
-# commonmark must match the version used in Gitiles
-maven_jar(
-    name = "commonmark",
-    artifact = "com.atlassian.commonmark:commonmark:" + COMMONMARK_VERS,
-    sha1 = "119cb7bedc3570d9ecb64ec69ab7686b5c20559b",
-)
-
-maven_jar(
-    name = "cm-autolink",
-    artifact = "com.atlassian.commonmark:commonmark-ext-autolink:" + COMMONMARK_VERS,
-    sha1 = "a6056a5efbd68f57d420bc51bbc54b28a5d3c56b",
-)
-
-maven_jar(
-    name = "gfm-strikethrough",
-    artifact = "com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:" + COMMONMARK_VERS,
-    sha1 = "40837da951b421b545edddac57012e15fcc9e63c",
-)
-
-maven_jar(
-    name = "gfm-tables",
-    artifact = "com.atlassian.commonmark:commonmark-ext-gfm-tables:" + COMMONMARK_VERS,
-    sha1 = "c075db2a3301100cf70c7dced8ecf86b494458a2",
-)
-
-FLEXMARK_VERS = "0.50.42"
-
-maven_jar(
-    name = "flexmark",
-    artifact = "com.vladsch.flexmark:flexmark:" + FLEXMARK_VERS,
-    sha1 = "ed537d7bc31883b008cc17d243a691c7efd12a72",
-)
-
-maven_jar(
-    name = "flexmark-ext-abbreviation",
-    artifact = "com.vladsch.flexmark:flexmark-ext-abbreviation:" + FLEXMARK_VERS,
-    sha1 = "dc27c3e7abbc8d2cfb154f41c68645c365bb9d22",
-)
-
-maven_jar(
-    name = "flexmark-ext-anchorlink",
-    artifact = "com.vladsch.flexmark:flexmark-ext-anchorlink:" + FLEXMARK_VERS,
-    sha1 = "6a8edb0165f695c9c19b7143a7fbd78c25c3b99c",
-)
-
-maven_jar(
-    name = "flexmark-ext-autolink",
-    artifact = "com.vladsch.flexmark:flexmark-ext-autolink:" + FLEXMARK_VERS,
-    sha1 = "5da7a4d009ea08ef2d8714cc73e54a992c6d2d9a",
-)
-
-maven_jar(
-    name = "flexmark-ext-definition",
-    artifact = "com.vladsch.flexmark:flexmark-ext-definition:" + FLEXMARK_VERS,
-    sha1 = "862d17812654624ed81ce8fc89c5ef819ff45f87",
-)
-
-maven_jar(
-    name = "flexmark-ext-emoji",
-    artifact = "com.vladsch.flexmark:flexmark-ext-emoji:" + FLEXMARK_VERS,
-    sha1 = "f0d7db64cb546798742b1ffc6db316a33f6acd76",
-)
-
-maven_jar(
-    name = "flexmark-ext-escaped-character",
-    artifact = "com.vladsch.flexmark:flexmark-ext-escaped-character:" + FLEXMARK_VERS,
-    sha1 = "6fd9ab77619df417df949721cb29c45914b326f8",
-)
-
-maven_jar(
-    name = "flexmark-ext-footnotes",
-    artifact = "com.vladsch.flexmark:flexmark-ext-footnotes:" + FLEXMARK_VERS,
-    sha1 = "e36bd69e43147cc6e19c3f55e4b27c0fc5a3d88c",
-)
-
-maven_jar(
-    name = "flexmark-ext-gfm-issues",
-    artifact = "com.vladsch.flexmark:flexmark-ext-gfm-issues:" + FLEXMARK_VERS,
-    sha1 = "5c825dd4e4fa4f7ccbe30dc92d7e35cdcb8a8c24",
-)
-
-maven_jar(
-    name = "flexmark-ext-gfm-strikethrough",
-    artifact = "com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:" + FLEXMARK_VERS,
-    sha1 = "3256735fd77e7228bf40f7888b4d3dc56787add4",
-)
-
-maven_jar(
-    name = "flexmark-ext-gfm-tables",
-    artifact = "com.vladsch.flexmark:flexmark-ext-gfm-tables:" + FLEXMARK_VERS,
-    sha1 = "62f0efcfb974756940ebe749fd4eb01323babc29",
-)
-
-maven_jar(
-    name = "flexmark-ext-gfm-tasklist",
-    artifact = "com.vladsch.flexmark:flexmark-ext-gfm-tasklist:" + FLEXMARK_VERS,
-    sha1 = "76d4971ad9ce02f0e70351ab6bd06ad8e405e40d",
-)
-
-maven_jar(
-    name = "flexmark-ext-gfm-users",
-    artifact = "com.vladsch.flexmark:flexmark-ext-gfm-users:" + FLEXMARK_VERS,
-    sha1 = "7b0fc7e42e4da508da167fcf8e1cbf9ba7e21147",
-)
-
-maven_jar(
-    name = "flexmark-ext-ins",
-    artifact = "com.vladsch.flexmark:flexmark-ext-ins:" + FLEXMARK_VERS,
-    sha1 = "9e51809867b9c4db0fb1c29599b4574e3d2a78e9",
-)
-
-maven_jar(
-    name = "flexmark-ext-jekyll-front-matter",
-    artifact = "com.vladsch.flexmark:flexmark-ext-jekyll-front-matter:" + FLEXMARK_VERS,
-    sha1 = "44eb6dbb33b3831d3b40af938ddcd99c9c16a654",
-)
-
-maven_jar(
-    name = "flexmark-ext-superscript",
-    artifact = "com.vladsch.flexmark:flexmark-ext-superscript:" + FLEXMARK_VERS,
-    sha1 = "35815b8cb91000344d1fe5df21cacde8553d2994",
-)
-
-maven_jar(
-    name = "flexmark-ext-tables",
-    artifact = "com.vladsch.flexmark:flexmark-ext-tables:" + FLEXMARK_VERS,
-    sha1 = "f6768e98c7210b79d5e8bab76fff27eec6db51e6",
-)
-
-maven_jar(
-    name = "flexmark-ext-toc",
-    artifact = "com.vladsch.flexmark:flexmark-ext-toc:" + FLEXMARK_VERS,
-    sha1 = "1968d038fc6c8156f244f5a7eecb34e7e2f33705",
-)
-
-maven_jar(
-    name = "flexmark-ext-typographic",
-    artifact = "com.vladsch.flexmark:flexmark-ext-typographic:" + FLEXMARK_VERS,
-    sha1 = "6549b9862b61c4434a855a733237103df9162849",
-)
-
-maven_jar(
-    name = "flexmark-ext-wikilink",
-    artifact = "com.vladsch.flexmark:flexmark-ext-wikilink:" + FLEXMARK_VERS,
-    sha1 = "e105b09dd35aab6e6f5c54dfe062ee59bd6f786a",
-)
-
-maven_jar(
-    name = "flexmark-ext-yaml-front-matter",
-    artifact = "com.vladsch.flexmark:flexmark-ext-yaml-front-matter:" + FLEXMARK_VERS,
-    sha1 = "b2d3a1e7f3985841062e8d3203617e29c6c21b52",
-)
-
-maven_jar(
-    name = "flexmark-formatter",
-    artifact = "com.vladsch.flexmark:flexmark-formatter:" + FLEXMARK_VERS,
-    sha1 = "a50c6cb10f6d623fc4354a572c583de1372d217f",
-)
-
-maven_jar(
-    name = "flexmark-html-parser",
-    artifact = "com.vladsch.flexmark:flexmark-html-parser:" + FLEXMARK_VERS,
-    sha1 = "46c075f30017e131c1ada8538f1d8eacf652b044",
-)
-
-maven_jar(
-    name = "flexmark-profile-pegdown",
-    artifact = "com.vladsch.flexmark:flexmark-profile-pegdown:" + FLEXMARK_VERS,
-    sha1 = "d9aafd47629959cbeddd731f327ae090fc92b60f",
-)
-
-maven_jar(
-    name = "flexmark-util",
-    artifact = "com.vladsch.flexmark:flexmark-util:" + FLEXMARK_VERS,
-    sha1 = "417a9821d5d80ddacbfecadc6843ae7b259d5112",
-)
-
-# Transitive dependency of flexmark and gitiles
-maven_jar(
-    name = "autolink",
-    artifact = "org.nibor.autolink:autolink:0.7.0",
-    sha1 = "649f9f13422cf50c926febe6035662ae25dc89b2",
-)
-
-GREENMAIL_VERS = "1.5.5"
-
-maven_jar(
-    name = "greenmail",
-    artifact = "com.icegreen:greenmail:" + GREENMAIL_VERS,
-    sha1 = "9ea96384ad2cb8118c22f493b529eb72c212691c",
-)
-
-MAIL_VERS = "1.6.0"
-
-maven_jar(
-    name = "mail",
-    artifact = "com.sun.mail:javax.mail:" + MAIL_VERS,
-    sha1 = "a055c648842c4954c1f7db7254f45d9ad565e278",
-)
-
-MIME4J_VERS = "0.8.1"
-
-maven_jar(
-    name = "mime4j-core",
-    artifact = "org.apache.james:apache-mime4j-core:" + MIME4J_VERS,
-    sha1 = "c62dfe18a3b827a2c626ade0ffba44562ddf3f61",
-)
-
-maven_jar(
-    name = "mime4j-dom",
-    artifact = "org.apache.james:apache-mime4j-dom:" + MIME4J_VERS,
-    sha1 = "f2d653c617004193f3350330d907f77b60c88c56",
-)
-
-maven_jar(
-    name = "jsoup",
-    artifact = "org.jsoup:jsoup:1.9.2",
-    sha1 = "5e3bda828a80c7a21dfbe2308d1755759c2fd7b4",
-)
-
-OW2_VERS = "9.0"
-
-maven_jar(
-    name = "ow2-asm",
-    artifact = "org.ow2.asm:asm:" + OW2_VERS,
-    sha1 = "af582ff60bc567c42d931500c3fdc20e0141ddf9",
-)
-
-maven_jar(
-    name = "ow2-asm-analysis",
-    artifact = "org.ow2.asm:asm-analysis:" + OW2_VERS,
-    sha1 = "4630afefbb43939c739445dde0af1a5729a0fb4e",
-)
-
-maven_jar(
-    name = "ow2-asm-commons",
-    artifact = "org.ow2.asm:asm-commons:" + OW2_VERS,
-    sha1 = "5a34a3a9ac44f362f35d1b27932380b0031a3334",
-)
-
-maven_jar(
-    name = "ow2-asm-tree",
-    artifact = "org.ow2.asm:asm-tree:" + OW2_VERS,
-    sha1 = "9df939f25c556b0c7efe00701d47e77a49837f24",
-)
-
-maven_jar(
-    name = "ow2-asm-util",
-    artifact = "org.ow2.asm:asm-util:" + OW2_VERS,
-    sha1 = "7c059a94ab5eed3347bf954e27fab58e52968848",
-)
-
-AUTO_VALUE_VERSION = "1.7.4"
-
-maven_jar(
-    name = "auto-value",
-    artifact = "com.google.auto.value:auto-value:" + AUTO_VALUE_VERSION,
-    sha1 = "6b126cb218af768339e4d6e95a9b0ae41f74e73d",
-)
-
-maven_jar(
-    name = "auto-value-annotations",
-    artifact = "com.google.auto.value:auto-value-annotations:" + AUTO_VALUE_VERSION,
-    sha1 = "eff48ed53995db2dadf0456426cc1f8700136f86",
-)
-
-AUTO_VALUE_GSON_VERSION = "1.3.0"
-
-maven_jar(
-    name = "auto-value-gson-runtime",
-    artifact = "com.ryanharter.auto.value:auto-value-gson-runtime:" + AUTO_VALUE_GSON_VERSION,
-    sha1 = "a69a9db5868bb039bd80f60661a771b643eaba59",
-)
-
-maven_jar(
-    name = "auto-value-gson-extension",
-    artifact = "com.ryanharter.auto.value:auto-value-gson-extension:" + AUTO_VALUE_GSON_VERSION,
-    sha1 = "6a61236d17b58b05e32b4c532bcb348280d2212b",
-)
-
-maven_jar(
-    name = "auto-value-gson-factory",
-    artifact = "com.ryanharter.auto.value:auto-value-gson-factory:" + AUTO_VALUE_GSON_VERSION,
-    sha1 = "b1f01918c0d6cb1f5482500e6b9e62589334dbb0",
-)
-
-maven_jar(
-    name = "javapoet",
-    artifact = "com.squareup:javapoet:1.13.0",
-    sha1 = "d6562d385049f35eb50403fa86bb11cce76b866a",
-)
-
-maven_jar(
-    name = "autotransient",
-    artifact = "io.sweers.autotransient:autotransient:1.0.0",
-    sha1 = "38b1c630b8e76560221622289f37be40105abb3d",
-)
-
 declare_nongoogle_deps()
 
-maven_jar(
-    name = "mime-util",
-    artifact = "eu.medsea.mimeutil:mime-util:2.1.3",
-    attach_source = False,
-    sha1 = "0c9cfae15c74f62491d4f28def0dff1dabe52a47",
+load("@build_bazel_rules_nodejs//:index.bzl", "node_repositories", "yarn_install")
+
+node_repositories(
+    node_version = "17.9.1",
+    yarn_version = "1.22.19",
 )
 
-PROLOG_VERS = "1.4.4"
-
-PROLOG_REPO = GERRIT
-
-maven_jar(
-    name = "prolog-runtime",
-    artifact = "com.googlecode.prolog-cafe:prolog-runtime:" + PROLOG_VERS,
-    attach_source = False,
-    repository = PROLOG_REPO,
-    sha1 = "e9a364f4233481cce63239e8e68a6190c8f58acd",
-)
-
-maven_jar(
-    name = "prolog-compiler",
-    artifact = "com.googlecode.prolog-cafe:prolog-compiler:" + PROLOG_VERS,
-    attach_source = False,
-    repository = PROLOG_REPO,
-    sha1 = "570295026f6aa7b905e423d107cb2e081eecdc04",
-)
-
-maven_jar(
-    name = "prolog-io",
-    artifact = "com.googlecode.prolog-cafe:prolog-io:" + PROLOG_VERS,
-    attach_source = False,
-    repository = PROLOG_REPO,
-    sha1 = "1f25c4e27d22bdbc31481ee0c962a2a2853e4428",
-)
-
-maven_jar(
-    name = "cafeteria",
-    artifact = "com.googlecode.prolog-cafe:prolog-cafeteria:" + PROLOG_VERS,
-    attach_source = False,
-    repository = PROLOG_REPO,
-    sha1 = "0e6c2deeaf5054815a561cbd663566fd59b56c6c",
-)
-
-maven_jar(
-    name = "guava-retrying",
-    artifact = "com.github.rholder:guava-retrying:2.0.0",
-    sha1 = "974bc0a04a11cc4806f7c20a34703bd23c34e7f4",
-)
-
-maven_jar(
-    name = "jsr305",
-    artifact = "com.google.code.findbugs:jsr305:3.0.1",
-    sha1 = "f7be08ec23c21485b9b5a1cf1654c2ec8c58168d",
-)
-
-GITILES_VERS = "0.4-1"
-
-GITILES_REPO = GERRIT
-
-maven_jar(
-    name = "blame-cache",
-    artifact = "com.google.gitiles:blame-cache:" + GITILES_VERS,
-    attach_source = False,
-    repository = GITILES_REPO,
-    sha1 = "0df80c6b8822147e1f116fd7804b8a0de544f402",
-)
-
-maven_jar(
-    name = "gitiles-servlet",
-    artifact = "com.google.gitiles:gitiles-servlet:" + GITILES_VERS,
-    repository = GITILES_REPO,
-    sha1 = "60870897d22b840e65623fd024eabd9cc9706ebe",
-)
-
-# prettify must match the version used in Gitiles
-maven_jar(
-    name = "prettify",
-    artifact = "com.github.twalcari:java-prettify:1.2.2",
-    sha1 = "b8ba1c1eb8b2e45cfd465d01218c6060e887572e",
-)
-
-maven_jar(
-    name = "html-types",
-    artifact = "com.google.common.html.types:types:1.0.8",
-    sha1 = "9e9cf7bc4b2a60efeb5f5581fe46d17c068e0777",
-)
-
-maven_jar(
-    name = "icu4j",
-    artifact = "com.ibm.icu:icu4j:57.1",
-    sha1 = "198ea005f41219f038f4291f0b0e9f3259730e92",
-)
-
-# When updating Bouncy Castle, also update it in bazlets.
-BC_VERS = "1.61"
-
-maven_jar(
-    name = "bcprov",
-    artifact = "org.bouncycastle:bcprov-jdk15on:" + BC_VERS,
-    sha1 = "00df4b474e71be02c1349c3292d98886f888d1f7",
-)
-
-maven_jar(
-    name = "bcpg",
-    artifact = "org.bouncycastle:bcpg-jdk15on:" + BC_VERS,
-    sha1 = "422656435514ab8a28752b117d5d2646660a0ace",
-)
-
-maven_jar(
-    name = "bcpkix",
-    artifact = "org.bouncycastle:bcpkix-jdk15on:" + BC_VERS,
-    sha1 = "89bb3aa5b98b48e584eee2a7401b7682a46779b4",
-)
-
-maven_jar(
-    name = "h2",
-    artifact = "com.h2database:h2:1.3.176",
-    sha1 = "fd369423346b2f1525c413e33f8cf95b09c92cbd",
-)
-
-# Base the following org.apache.httpcomponents versions on what
-# elasticsearch-rest-client explicitly depends on, except for
-# commons-codec (non-http) which is not necessary yet. Note that
-# below httpcore version(s) differs from the HTTPCOMP_VERS range,
-# upstream: that specific dependency has no HTTPCOMP_VERS version
-# equivalent currently.
-HTTPCOMP_VERS = "4.5.2"
-
-maven_jar(
-    name = "fluent-hc",
-    artifact = "org.apache.httpcomponents:fluent-hc:" + HTTPCOMP_VERS,
-    sha1 = "7bfdfa49de6d720ad3c8cedb6a5238eec564dfed",
-)
-
-maven_jar(
-    name = "httpclient",
-    artifact = "org.apache.httpcomponents:httpclient:" + HTTPCOMP_VERS,
-    sha1 = "733db77aa8d9b2d68015189df76ab06304406e50",
-)
-
-maven_jar(
-    name = "httpcore",
-    artifact = "org.apache.httpcomponents:httpcore:4.4.4",
-    sha1 = "b31526a230871fbe285fbcbe2813f9c0839ae9b0",
-)
-
-# Test-only dependencies below.
-
-maven_jar(
-    name = "junit",
-    artifact = "junit:junit:4.12",
-    sha1 = "2973d150c0dc1fefe998f834810d68f278ea58ec",
-)
-
-maven_jar(
-    name = "hamcrest-core",
-    artifact = "org.hamcrest:hamcrest-core:1.3",
-    sha1 = "42a25dc3219429f0e5d060061f71acb49bf010a0",
-)
-
-maven_jar(
-    name = "diffutils",
-    artifact = "com.googlecode.java-diff-utils:diffutils:1.3.0",
-    sha1 = "7e060dd5b19431e6d198e91ff670644372f60fbd",
-)
-
-JETTY_VERS = "9.4.36.v20210114"
-
-maven_jar(
-    name = "jetty-servlet",
-    artifact = "org.eclipse.jetty:jetty-servlet:" + JETTY_VERS,
-    sha1 = "b189e52a5ee55ae172e4e99e29c5c314f5daf4b9",
-)
-
-maven_jar(
-    name = "jetty-security",
-    artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VERS,
-    sha1 = "42030d6ed7dfc0f75818cde0adcf738efc477574",
-)
-
-maven_jar(
-    name = "jetty-server",
-    artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VERS,
-    sha1 = "88a7d342974aadca658e7386e8d0fcc5c0788f41",
-)
-
-maven_jar(
-    name = "jetty-jmx",
-    artifact = "org.eclipse.jetty:jetty-jmx:" + JETTY_VERS,
-    sha1 = "bb3847eabe085832aeaedd30e872b40931632e54",
-)
-
-maven_jar(
-    name = "jetty-http",
-    artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VERS,
-    sha1 = "1eee89a55e04ff94df0f85d95200fc48acb43d86",
-)
-
-maven_jar(
-    name = "jetty-io",
-    artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VERS,
-    sha1 = "84a8faf9031eb45a5a2ddb7681e22c483d81ab3a",
-)
-
-maven_jar(
-    name = "jetty-util",
-    artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VERS,
-    sha1 = "925257fbcca6b501a25252c7447dbedb021f7404",
-)
-
-maven_jar(
-    name = "jetty-util-ajax",
-    artifact = "org.eclipse.jetty:jetty-util-ajax:" + JETTY_VERS,
-    sha1 = "2f478130c21787073facb64d7242e06f94980c60",
-    src_sha1 = "7153d7ca38878d971fd90992c303bb7719ba7a21",
-)
-
-maven_jar(
-    name = "asciidoctor",
-    artifact = "org.asciidoctor:asciidoctorj:1.5.7",
-    sha1 = "8e8c1d8fc6144405700dd8df3b177f2801ac5987",
-)
-
-maven_jar(
-    name = "javax-activation",
-    artifact = "javax.activation:activation:1.1.1",
-    sha1 = "485de3a253e23f645037828c07f1d7f1af40763a",
-)
-
-maven_jar(
-    name = "javax-annotation",
-    artifact = "javax.annotation:javax.annotation-api:1.3.2",
-    sha1 = "934c04d3cfef185a8008e7bf34331b79730a9d43",
-)
-
-maven_jar(
-    name = "mockito",
-    artifact = "org.mockito:mockito-core:3.3.3",
-    sha1 = "4878395d4e63173f3825e17e5e0690e8054445f1",
-)
-
-BYTE_BUDDY_VERSION = "1.10.7"
-
-maven_jar(
-    name = "bytebuddy",
-    artifact = "net.bytebuddy:byte-buddy:" + BYTE_BUDDY_VERSION,
-    sha1 = "1eefb7dd1b032b33c773ca0a17d5cc9e6b56ea1a",
-)
-
-maven_jar(
-    name = "bytebuddy-agent",
-    artifact = "net.bytebuddy:byte-buddy-agent:" + BYTE_BUDDY_VERSION,
-    sha1 = "c472fad33f617228601172682aa64f8b78508045",
-)
-
-maven_jar(
-    name = "objenesis",
-    artifact = "org.objenesis:objenesis:3.0.1",
-    sha1 = "11cfac598df9dc48bb9ed9357ed04212694b7808",
-)
-
-load("@build_bazel_rules_nodejs//:index.bzl", "yarn_install")
-
 yarn_install(
     name = "npm",
+    exports_directories_only = False,
     frozen_lockfile = False,
     package_json = "//:package.json",
     package_path = "",
+    symlink_node_modules = True,
     yarn_lock = "//:yarn.lock",
 )
 
@@ -941,51 +163,43 @@
         # explicitly added to package.json.
         "--ignore-optional",
     ],
+    exports_directories_only = False,
     frozen_lockfile = False,
     package_json = "//:polygerrit-ui/app/package.json",
     package_path = "polygerrit-ui/app",
+    symlink_node_modules = True,
     yarn_lock = "//:polygerrit-ui/app/yarn.lock",
 )
 
 yarn_install(
     name = "ui_dev_npm",
+    exports_directories_only = False,
     frozen_lockfile = False,
     package_json = "//:polygerrit-ui/package.json",
     package_path = "polygerrit-ui",
+    symlink_node_modules = True,
     yarn_lock = "//:polygerrit-ui/yarn.lock",
 )
 
 yarn_install(
     name = "tools_npm",
+    exports_directories_only = False,
     frozen_lockfile = False,
     package_json = "//:tools/node_tools/package.json",
     package_path = "tools/node_tools",
+    symlink_node_modules = True,
     yarn_lock = "//:tools/node_tools/yarn.lock",
 )
 
 yarn_install(
     name = "plugins_npm",
     args = ["--prod"],
+    exports_directories_only = False,
     frozen_lockfile = False,
     package_json = "//:plugins/package.json",
     package_path = "plugins",
+    symlink_node_modules = True,
     yarn_lock = "//:plugins/yarn.lock",
 )
 
 external_plugin_deps()
-
-# When upgrading elasticsearch-rest-client, also upgrade httpcore-nio
-# and httpasyncclient as necessary in tools/nongoogle.bzl. Consider
-# also the other org.apache.httpcomponents dependencies in
-# WORKSPACE.
-maven_jar(
-    name = "elasticsearch-rest-client",
-    artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.8.1",
-    sha1 = "59feefe006a96a39f83b0dfb6780847e06c1d0a8",
-)
-
-maven_jar(
-    name = "testcontainers-elasticsearch",
-    artifact = "org.testcontainers:elasticsearch:" + TESTCONTAINERS_VERSION,
-    sha1 = "595e3a50f59cd3c1d281ca6c1bc4037e277a1353",
-)
diff --git a/antlr3/BUILD b/antlr3/BUILD
index 549946a..23641e3 100644
--- a/antlr3/BUILD
+++ b/antlr3/BUILD
@@ -22,6 +22,7 @@
     srcs = [":query"],
     visibility = [
         "//java/com/google/gerrit/index:__subpackages__",
+        "//java/com/google/gerrit/server:__subpackages__",
         "//javatests/com/google/gerrit:__subpackages__",
         "//javatests/com/google/gerrit/index:__pkg__",
         "//plugins:__pkg__",
diff --git a/antlr3/com/google/gerrit/index/query/Query.g b/antlr3/com/google/gerrit/index/query/Query.g
index 1bf20aa..ea521f9 100644
--- a/antlr3/com/google/gerrit/index/query/Query.g
+++ b/antlr3/com/google/gerrit/index/query/Query.g
@@ -142,9 +142,9 @@
   | EXACT_PHRASE
   ;
 
-AND: 'AND' ;
-OR:  'OR'  ;
-NOT: 'NOT' ;
+AND: 'AND' | 'and';
+OR:  'OR' | 'or'  ;
+NOT: 'NOT' | 'not' ;
 
 COLON: ':' ;
 
@@ -152,18 +152,19 @@
   :  ( ' ' | '\r' | '\t' | '\n' ) { $channel=HIDDEN; }
   ;
 
+fragment LOWERCASE_AND_UNDERSCORE: ('a'..'z' | '_')+ ;
+
 FIELD_NAME
-  : ('a'..'z' | '_')+
+  : LOWERCASE_AND_UNDERSCORE ( '-' LOWERCASE_AND_UNDERSCORE )*
   ;
 
 EXACT_PHRASE
-  : '"' ( ~('"') )* '"' {
-      String s = $text;
-      setText(s.substring(1, s.length() - 1));
+@init { final StringBuilder buf = new StringBuilder(); }
+  : '"' ( ESCAPE[buf] | i = ~('\\'|'"') { buf.appendCodePoint(i); } )* '"' {
+      setText(buf.toString());
     }
-  | '{' ( ~('{'|'}') )* '}' {
-      String s = $text;
-      setText(s.substring(1, s.length() - 1));
+  | '{' ( ESCAPE[buf] | i = ~('\\'|'{'|'}') { buf.appendCodePoint(i); } )* '}' {
+      setText(buf.toString());
     }
   ;
 
@@ -197,3 +198,11 @@
      // | '~' permit
      )
   ;
+
+fragment ESCAPE[StringBuilder buf] :
+    '\\'
+    ( 't' { buf.append('\t'); }
+    | 'n' { buf.append('\n'); }
+    | 'r' { buf.append('\r'); }
+    | i = ~('t'|'n'|'r') { buf.appendCodePoint(i); }
+    );
diff --git a/contrib/convertkey/src/main/java/com/googlesource/gerrit/convertkey/ConvertKey.java b/contrib/convertkey/src/main/java/com/googlesource/gerrit/convertkey/ConvertKey.java
deleted file mode 100644
index 08a529c..0000000
--- a/contrib/convertkey/src/main/java/com/googlesource/gerrit/convertkey/ConvertKey.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (C) 2015 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.googlesource.gerrit.convertkey;
-
-import com.jcraft.jsch.HostKey;
-import com.jcraft.jsch.JSchException;
-import java.io.File;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import org.apache.sshd.common.util.Buffer;
-import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
-import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
-
-public class ConvertKey {
-  public static void main(String[] args)
-      throws GeneralSecurityException, JSchException, IOException {
-    SimpleGeneratorHostKeyProvider p;
-
-    if (args.length != 1) {
-      System.err.println("Error: requires path to the SSH host key");
-      return;
-    } else {
-      File file = new File(args[0]);
-      if (!file.exists() || !file.isFile() || !file.canRead()) {
-        System.err.println("Error: ssh key should exist and be readable");
-        return;
-      }
-    }
-
-    p = new SimpleGeneratorHostKeyProvider();
-    // Gerrit's SSH "simple" keys are always RSA.
-    p.setPath(args[0]);
-    p.setAlgorithm("RSA");
-    Iterable<KeyPair> keys = p.loadKeys(); // forces the key to generate.
-    for (KeyPair k : keys) {
-      System.out.println("Public Key (" + k.getPublic().getAlgorithm() + "):");
-      // From Gerrit's SshDaemon class; use JSch to get the public
-      // key/type
-      final Buffer buf = new Buffer();
-      buf.putRawPublicKey(k.getPublic());
-      final byte[] keyBin = buf.getCompactData();
-      HostKey pub = new HostKey("localhost", keyBin);
-      System.out.println(pub.getType() + " " + pub.getKey());
-      System.out.println("Private Key:");
-      // Use Bouncy Castle to write the private key back in PEM format
-      // (PKCS#1)
-      // http://stackoverflow.com/questions/25129822/export-rsa-public-key-to-pem-string-using-java
-      StringWriter privout = new StringWriter();
-      JcaPEMWriter privWriter = new JcaPEMWriter(privout);
-      privWriter.writeObject(k.getPrivate());
-      privWriter.close();
-      System.out.println(privout);
-    }
-  }
-}
diff --git a/contrib/find-duplicate-usernames.sh b/contrib/find-duplicate-usernames.sh
index b59e5be..7a5750f 100755
--- a/contrib/find-duplicate-usernames.sh
+++ b/contrib/find-duplicate-usernames.sh
@@ -29,6 +29,18 @@
   usage
 fi
 
+if [ -z "$(git ls-remote . refs/meta/external-ids)" ]; then
+  cat <<EOF
+Could not find 'refs/meta/external-ids' in the local repository.
+
+Please fetch it using:
+
+  git fetch "$(git remote)" refs/meta/external-ids:refs/meta/external-ids
+
+EOF
+  exit 1
+fi
+
 # 1. find lines with user name and subsequent line in external-ids notes branch
 #    example output of git grep -A1 "\[externalId \"username:" refs/meta/external-ids:
 #    refs/meta/external-ids:00/1d/abd037e437f71d42134e6ad532a06948a2ba:[externalId "username:johndoe"]
@@ -45,12 +57,12 @@
 # 7. flip columns
 # 8. uniq case-insensitive, only show duplicates, avoid comparing first field
 # 9. flip columns back
-git grep -A1 "\[externalId \"$1:" refs/meta/external-ids \
+git grep -A1 "\[externalId \"$1:" refs/meta/external-ids -- \
   | sed -E "/$1/,/accountId/!d" \
   | paste -d ' ' - - \
   | tr \"= : \
   | cut -d: --output-delimiter="" -f 5,8 \
   | sort -f \
-  | sed -E "s/(.*) (.*)/\2 \1/" \
+  | sed -E "s/(.*) ([0-9]+)/\2 \1/" \
   | uniq -Di -f1 \
-  | sed -E "s/(.*) (.*)/\2 \1/"
+  | sed -E "s/([0-9]+) (.*)/\2 \1/"
diff --git a/contrib/git-gc-preserve b/contrib/git-gc-preserve
new file mode 100755
index 0000000..a886721
--- /dev/null
+++ b/contrib/git-gc-preserve
@@ -0,0 +1,149 @@
+#!/bin/bash
+# Copyright (C) 2022 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.
+
+usage() { # exit code
+  cat <<-EOF
+NAME
+    git-gc-preserve - Run git gc and preserve old packs to avoid races for JGit
+
+SYNOPSIS
+    git gc-preserve
+
+DESCRIPTION
+    Runs git gc and can preserve old packs to avoid races with concurrently
+    executed commands in JGit.
+
+    This command uses custom git config options to configure if preserved packs
+    from the last run of git gc should be pruned and if packs should be preserved.
+
+    This is similar to the implementation in JGit [1] which is used by
+    JGit to avoid errors [2] in such situations.
+
+    The command prevents concurrent runs of the command on the same repository
+    by acquiring an exclusive file lock on the file
+      "\$repopath/gc-preserve.pid"
+    If it cannot acquire the lock it fails immediately with exit code 3.
+
+    Failure Exit Codes
+        1: General failure
+        2: Couldn't determine repository path. If the current working directory
+           is outside of the working tree of the git repository use git option
+           --git-dir to pass the root path of the repository.
+           E.g.
+              $ git --git-dir ~/git/foo gc-preserve
+        3: Another process already runs $0 on the same repository
+
+    [1] https://git.eclipse.org/r/c/jgit/jgit/+/87969
+    [2] https://git.eclipse.org/r/c/jgit/jgit/+/122288
+
+CONFIGURATION
+    "gc.prunepreserved": if set to "true" preserved packs from the last gc run
+      are pruned before current packs are preserved.
+
+    "gc.preserveoldpacks": if set to "true" current packs will be hard linked
+      to objects/pack/preserved before git gc is executed. JGit will
+      fallback to the preserved packs in this directory in case it comes
+      across missing objects which might be caused by a concurrent run of
+      git gc.
+EOF
+  exit "$1"
+}
+
+# acquire file lock, unlock when the script exits
+lock() { # repo
+  readonly LOCKFILE="$1/gc-preserve.pid"
+  test -f "$LOCKFILE" || touch "$LOCKFILE"
+  exec 9> "$LOCKFILE"
+  if flock -nx 9; then
+    echo -n "$$ $USERNAME@$HOSTNAME" >&9
+    trap unlock EXIT
+  else
+    echo "$0 is already running"
+    exit 3
+  fi
+}
+
+unlock() {
+  # only delete if the file descriptor 9 is open
+  if { : >&9 ; } &> /dev/null; then
+    rm -f "$LOCKFILE"
+  fi
+  # close the file handle to release file lock
+  exec 9>&-
+}
+
+# prune preserved packs if gc.prunepreserved == true
+prune_preserved() { # repo
+  configured=$(git --git-dir="$1" config --get gc.prunepreserved)
+  if [ "$configured" != "true" ]; then
+    return 0
+  fi
+  local preserved=$1/objects/pack/preserved
+  if [ -d "$preserved" ]; then
+    printf "Pruning old preserved packs: "
+    count=$(find "$preserved" -name "*.old-pack" | wc -l)
+    rm -rf "$preserved"
+    echo "$count, done."
+  fi
+}
+
+# preserve packs if gc.preserveoldpacks == true
+preserve_packs() { # repo
+  configured=$(git --git-dir="$1" config --get gc.preserveoldpacks)
+  if [ "$configured" != "true" ]; then
+    return 0
+  fi
+  local packdir=$1/objects/pack
+  pushd "$packdir" >/dev/null || exit 1
+  mkdir -p preserved
+  printf "Preserving packs: "
+  count=0
+  for file in pack-*{.pack,.idx} ; do
+    ln -f "$file" preserved/"$(get_preserved_packfile_name "$file")"
+    if [[ "$file" == pack-*.pack ]]; then
+      ((count++))
+    fi
+  done
+  echo "$count, done."
+  popd >/dev/null || exit 1
+}
+
+# pack-0...2.pack to pack-0...2.old-pack
+# pack-0...2.idx to pack-0...2.old-idx
+get_preserved_packfile_name() { # packfile > preserved_packfile
+  local old=${1/%\.pack/.old-pack}
+  old=${old/%\.idx/.old-idx}
+  echo "$old"
+}
+
+# main
+
+while [ $# -gt 0 ] ; do
+    case "$1" in
+        -u|-h)  usage 0 ;;
+    esac
+    shift
+done
+args=$(git rev-parse --sq-quote "$@")
+
+repopath=$(git rev-parse --git-dir)
+if [ -z "$repopath" ]; then
+  usage 2
+fi
+
+lock "$repopath"
+prune_preserved "$repopath"
+preserve_packs "$repopath"
+git gc ${args:+"$args"} || { echo "git gc failed"; exit "$?"; }
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/AbandonChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/AbandonChange.json
index 665cc4d..9d24b5b 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/AbandonChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/AbandonChange.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/changes/",
     "number": "NUMBER"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
index 665cc4d..9d24b5b 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/changes/",
     "number": "NUMBER"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json
index 5b892aa..594903a 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT1/a/projects/PROJECT/branches/master"
+    "url": "HTTP_SCHEME://REPLICA_HOSTNAME:HTTP_PORT1/a/projects/PROJECT/branches/master"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckNewProjectReplica1.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckNewProjectReplica1.json
index f15ddae..5221d95 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckNewProjectReplica1.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckNewProjectReplica1.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT1/_PROJECT",
+    "url": "HTTP_SCHEME://REPLICA_HOSTNAME:HTTP_PORT1/_PROJECT",
     "cmd": "clone"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json
index 467661b..75e895e 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/config/server/caches/projects",
     "entries": "PROJECTS_ENTRIES"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
index 30f5f23..195ed09 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
@@ -1,10 +1,10 @@
 [
   {
-    "url": "ssh://admin@HOSTNAME:SSH_PORT/_PROJECT",
+    "url": "ssh://USERNAME@HOSTNAME:SSH_PORT/_PROJECT",
     "cmd": "clone"
   },
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/_PROJECT",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/_PROJECT",
     "cmd": "clone"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateBranch.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateBranch.json
index 5459f11..487cf02 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateBranch.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateBranch.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/PROJECT/branches/",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/projects/PROJECT/branches/",
     "project": "PROJECT"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
index 70e79ca..8b8a163 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/changes/",
     "project": "PROJECT"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
index c141bb8..756313a 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/PROJECT",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/projects/PROJECT",
     "parent": "PARENT"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
index 665cc4d..9d24b5b 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/changes/",
     "number": "NUMBER"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
index 5720f53..8bec9de 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/PROJECT/delete-project~delete"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/projects/PROJECT/delete-project~delete"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json
index e30a2cf..c1dc674 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects/flush"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/config/server/caches/projects/flush"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCacheThenRebuild.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCacheThenRebuild.json
index e30a2cf..c1dc674 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCacheThenRebuild.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCacheThenRebuild.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects/flush"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/config/server/caches/projects/flush"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json
index 86a3c28..4f6a104 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/PROJECT/branches/master"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/projects/PROJECT/branches/master"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json
index e4e2643..e77d83b 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/config/server/caches/projects"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ListProjects.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ListProjects.json
index f6350be..528ef3e 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ListProjects.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ListProjects.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/projects/"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json
index 30f5f23..195ed09 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json
@@ -1,10 +1,10 @@
 [
   {
-    "url": "ssh://admin@HOSTNAME:SSH_PORT/_PROJECT",
+    "url": "ssh://USERNAME@HOSTNAME:SSH_PORT/_PROJECT",
     "cmd": "clone"
   },
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/_PROJECT",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/_PROJECT",
     "cmd": "clone"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/RestoreChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/RestoreChange.json
index 665cc4d..9d24b5b 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/RestoreChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/RestoreChange.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/changes/",
     "number": "NUMBER"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
index 301c65b..13d5802 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/changes/"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChangeInBranch.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChangeInBranch.json
index 301c65b..13d5802 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChangeInBranch.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChangeInBranch.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORTCONTEXT_PATH/a/changes/"
   }
 ]
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/AbandonChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/AbandonChange.scala
index d387a3e..d8ebf7f 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/AbandonChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/AbandonChange.scala
@@ -24,7 +24,6 @@
 
 class AbandonChange extends GerritSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).circular
-  private val projectName = className
   private var numbersCopy: mutable.Queue[Int] = mutable.Queue[Int]()
   private var createChange: Option[CreateChange] = Some(new CreateChange(projectName))
 
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckNewProjectReplica1.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckNewProjectReplica1.scala
index ae4fa80..692d576 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckNewProjectReplica1.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckNewProjectReplica1.scala
@@ -22,7 +22,6 @@
 
 class CheckNewProjectReplica1 extends GitSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
-  private val projectName = className
 
   private lazy val replicationDuration = replicationDelay + SecondsPerWeightUnit
 
@@ -30,7 +29,6 @@
 
   override def replaceOverride(in: String): String = {
     var next = replaceProperty("http_port1", 8081, in)
-    next = replaceKeyWith("_project", projectName, next)
     super.replaceOverride(next)
   }
 
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala
index c283861..d1d9c88 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala
@@ -22,12 +22,8 @@
 
 class CloneUsingBothProtocols extends GitSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).circular
-  private val projectName = className
   private val duration = 2 * numberOfUsers
 
-  override def replaceOverride(in: String): String = {
-    replaceKeyWith("_project", projectName, in)
-  }
 
   private val test: ScenarioBuilder = scenario(uniqueName)
       .feed(data)
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/FlushProjectsCache.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/FlushProjectsCache.scala
index 9fef2cf..a7bda3c 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/FlushProjectsCache.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/FlushProjectsCache.scala
@@ -22,7 +22,6 @@
 
 class FlushProjectsCache extends CacheFlushSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
-  private val projectName = className
 
   override def relativeRuntimeWeight = 2
 
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
index c199dd9..580ae81 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
@@ -23,6 +23,7 @@
 class GerritSimulation extends Simulation {
   implicit val conf: GatlingGitConfiguration = GatlingGitConfiguration()
 
+  private val defaultHostname: String = "localhost"
   protected val numberKey: String = "number"
 
   private val packageName = getClass.getPackage.getName
@@ -42,7 +43,7 @@
   protected val SecondsPerWeightUnit = 2
   val maxExecutionTime: Int = (SecondsPerWeightUnit * relativeRuntimeWeight * powerFactor).toInt
   private var cumulativeWaitTime = 0
-
+  protected var projectName: String = className
   /**
    * How long a scenario step should wait before starting to execute.
    * This is also registering that step's resulting wait time, so that time
@@ -73,16 +74,23 @@
       replaceProperty("parent", "All-Projects", parent.toString)
     case ("project", project) =>
       var precedes = replaceKeyWith("_project", className, project.toString)
-      precedes = replaceOverride(precedes)
+      precedes = replaceProperty("project", getFullProjectName(projectName), precedes)
       replaceProperty("project", precedes)
     case ("url", url) =>
       var in = replaceOverride(url.toString)
-      in = replaceProperty("hostname", "localhost", in)
+      in = replaceProperty("replica_hostname", getProperty("hostname", defaultHostname), in)
+      in = replaceProperty("hostname", defaultHostname, in)
       in = replaceProperty("http_port", 8080, in)
       in = replaceProperty("http_scheme", "http", in)
+      in = replaceProperty("username", "admin", in)
+      in = replaceProperty("context_path", "", in)
       replaceProperty("ssh_port", 29418, in)
   }
 
+  protected def getFullProjectName(projectName: String): String = {
+    getProperty("project_prefix", "") + projectName
+  }
+
   private def replaceProperty(term: String, in: String): String = {
     replaceProperty(term, term, in)
   }
@@ -100,7 +108,7 @@
     val property = packageName + "." + term
     var value = default
     default match {
-      case _: String | _: Double =>
+      case _: String | _: Double | _: Boolean =>
         val propertyValue = Option(System.getProperty(property))
         if (propertyValue.nonEmpty) {
           value = propertyValue.get
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GitSimulation.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GitSimulation.scala
index e2f13a4..53f942d 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GitSimulation.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GitSimulation.scala
@@ -14,7 +14,9 @@
 
 package com.google.gerrit.scenarios
 
+import static java.nio.charset.StandardCharsets.UTF_8
 import java.io.{File, IOException}
+import java.net.URLEncoder
 
 import com.github.barbasa.gatling.git.GitRequestSession
 import com.github.barbasa.gatling.git.protocol.GitProtocol
@@ -29,6 +31,14 @@
   protected val gitRequest = new GitRequestBuilder(GitRequestSession("${cmd}", "${url}"))
   protected val gitProtocol: GitProtocol = GitProtocol()
 
+  override def replaceOverride(in: String): String = {
+    var next = replaceKeyWith("_project", URLEncoder.encode(getFullProjectName(projectName), UTF_8), in)
+    val authenticated = getProperty("authenticated", false).toBoolean
+    val value = "CONTEXT_PATH" + (if (authenticated) "/a" else "")
+    next = replaceKeyWith("context_path", value, next)
+    super.replaceOverride(next)
+  }
+
   after {
     Thread.sleep(5000)
     val path = conf.tmpBasePath
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ProjectSimulation.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ProjectSimulation.scala
index d6cb937..f0c6f68 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ProjectSimulation.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ProjectSimulation.scala
@@ -14,10 +14,13 @@
 
 package com.google.gerrit.scenarios
 
+import static java.nio.charset.StandardCharsets.UTF_8
+import java.net.URLEncoder
+
 class ProjectSimulation extends GerritSimulation {
-  protected var projectName: String = "defaultTestProject"
+  projectName = "defaultTestProject"
 
   override def replaceOverride(in: String): String = {
-    replaceProperty("project", projectName, in)
+    replaceProperty("project", URLEncoder.encode(getFullProjectName(projectName), UTF_8, in)
   }
 }
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.scala
index 0bd9e4a..c049f09 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.scala
@@ -22,13 +22,9 @@
 
 class ReplayRecordsFromFeeder extends GitSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).circular
-  private val projectName = className
 
   override def relativeRuntimeWeight = 30
 
-  override def replaceOverride(in: String): String = {
-    replaceKeyWith("_project", projectName, in)
-  }
 
   private val test: ScenarioBuilder = scenario(uniqueName)
       .repeat(10) {
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/RestoreChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/RestoreChange.scala
index 81096b0..756a239 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/RestoreChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/RestoreChange.scala
@@ -24,7 +24,6 @@
 
 class RestoreChange extends GerritSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).circular
-  private val projectName = className
   private var numbersCopy: mutable.Queue[Int] = mutable.Queue[Int]()
 
   override def relativeRuntimeWeight = 10
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala
index 20be28a..49f0c4b 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala
@@ -23,7 +23,6 @@
 
 class SubmitChange extends GerritSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
-  private val projectName = className
   private var createChange = new CreateChange(projectName)
 
   override def relativeRuntimeWeight = 10
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChangeInBranch.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChangeInBranch.scala
index 9e1431b..ca618c3 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChangeInBranch.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChangeInBranch.scala
@@ -25,7 +25,6 @@
 class SubmitChangeInBranch extends GerritSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).circular
   private var changesCopy: mutable.Queue[Int] = mutable.Queue[Int]()
-  private val projectName = className
 
   override def relativeRuntimeWeight = 10
 
diff --git a/java/Main.java b/java/Main.java
index 11d8234..09c8c76 100644
--- a/java/Main.java
+++ b/java/Main.java
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+@SuppressWarnings("DefaultPackage")
 public final class Main {
   private static final String FLOGGER_BACKEND_PROPERTY = "flogger.backend_factory";
   private static final String FLOGGER_LOGGING_CONTEXT = "flogger.logging_context";
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index f7b343b..ccf74d1 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -26,11 +26,13 @@
 import static com.google.gerrit.entities.Patch.COMMIT_MSG;
 import static com.google.gerrit.entities.Patch.MERGE_LIST;
 import static com.google.gerrit.extensions.api.changes.SubmittedTogetherOption.NON_VISIBLE_CHANGES;
+import static com.google.gerrit.extensions.api.changes.SubmittedTogetherOption.TOPIC_CLOSURE;
 import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
 import static com.google.gerrit.server.project.ProjectCache.illegalState;
 import static com.google.gerrit.server.project.testing.TestLabels.label;
 import static com.google.gerrit.server.project.testing.TestLabels.value;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Objects.requireNonNull;
 import static java.util.stream.Collectors.toList;
@@ -41,9 +43,9 @@
 import com.google.common.base.Ticker;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
-import com.google.common.jimfs.Jimfs;
 import com.google.common.primitives.Chars;
 import com.google.common.testing.FakeTicker;
 import com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context;
@@ -80,6 +82,7 @@
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.RevisionApi;
 import com.google.gerrit.extensions.api.changes.SubmittedTogetherInfo;
+import com.google.gerrit.extensions.api.changes.SubmittedTogetherOption;
 import com.google.gerrit.extensions.api.projects.BranchApi;
 import com.google.gerrit.extensions.api.projects.BranchInfo;
 import com.google.gerrit.extensions.api.projects.BranchInput;
@@ -87,6 +90,7 @@
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.client.ProjectWatchInfo;
+import com.google.gerrit.extensions.client.ReviewerState;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -96,9 +100,8 @@
 import com.google.gerrit.extensions.common.EditInfo;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.index.project.ProjectIndex;
-import com.google.gerrit.index.project.ProjectIndexCollection;
 import com.google.gerrit.json.OutputFormat;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.IdentifiedUser;
@@ -125,11 +128,7 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.index.account.AccountIndex;
-import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.index.account.AccountIndexer;
-import com.google.gerrit.server.index.change.ChangeIndex;
-import com.google.gerrit.server.index.change.ChangeIndexCollection;
 import com.google.gerrit.server.index.change.ChangeIndexer;
 import com.google.gerrit.server.notedb.AbstractChangeNotes;
 import com.google.gerrit.server.notedb.ChangeNoteUtil;
@@ -157,15 +156,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.lang.reflect.Modifier;
-import java.nio.file.DirectoryStream;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.KeyPair;
 import java.sql.Timestamp;
 import java.time.Instant;
 import java.util.ArrayList;
@@ -179,6 +170,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.UUID;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -187,7 +179,6 @@
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
@@ -197,11 +188,7 @@
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.transport.FetchResult;
-import org.eclipse.jgit.transport.RefSpec;
 import org.eclipse.jgit.transport.Transport;
-import org.eclipse.jgit.transport.TransportBundleStream;
-import org.eclipse.jgit.transport.URIish;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.SystemReader;
 import org.junit.After;
@@ -313,14 +300,11 @@
   protected BlockStrategy noSleepBlockStrategy = t -> {}; // Don't sleep in tests.
 
   @Inject private AbstractChangeNotes.Args changeNotesArgs;
-  @Inject private AccountIndexCollection accountIndexes;
   @Inject private AccountIndexer accountIndexer;
-  @Inject private ChangeIndexCollection changeIndexes;
   @Inject private EventRecorder.Factory eventRecorderFactory;
   @Inject private InProcessProtocol inProcessProtocol;
   @Inject private PluginGuiceEnvironment pluginGuiceEnvironment;
   @Inject private PluginUser.Factory pluginUserFactory;
-  @Inject private ProjectIndexCollection projectIndexes;
   @Inject private RequestScopeOperations requestScopeOperations;
   @Inject private SitePaths sitePaths;
   @Inject private ProjectOperations projectOperations;
@@ -461,7 +445,6 @@
 
     baseConfig.setInt("index", null, "batchThreads", -1);
 
-    baseConfig.setInt("receive", null, "changeUpdateThreads", 4);
     Module module = createModule();
     Module auditModule = createAuditModule();
     Module sshModule = createSshModule();
@@ -583,8 +566,7 @@
         && SshMode.useSsh()
         && (adminSshSession == null || userSshSession == null)) {
       // Create Ssh sessions
-      KeyPair adminKeyPair = sshKeys.getKeyPair(admin);
-      SshSessionFactory.initSsh(adminKeyPair);
+      SshSessionFactory.initSsh();
       Context ctx = newRequestContext(user);
       atrScope.set(ctx);
       userSshSession = ctx.getSession();
@@ -659,7 +641,10 @@
   }
 
   protected Project.NameKey createProjectOverAPI(
-      String nameSuffix, Project.NameKey parent, boolean createEmptyCommit, SubmitType submitType)
+      String nameSuffix,
+      @Nullable Project.NameKey parent,
+      boolean createEmptyCommit,
+      @Nullable SubmitType submitType)
       throws RestApiException {
     ProjectInput in = new ProjectInput();
     in.name = name(nameSuffix);
@@ -972,7 +957,7 @@
             repo,
             "new subject",
             "new file",
-            "new content");
+            "new content " + UUID.randomUUID());
     return result;
   }
 
@@ -1069,87 +1054,6 @@
     };
   }
 
-  protected void disableChangeIndexWrites() {
-    for (ChangeIndex i : changeIndexes.getWriteIndexes()) {
-      if (!(i instanceof ReadOnlyChangeIndex)) {
-        changeIndexes.addWriteIndex(new ReadOnlyChangeIndex(i));
-      }
-    }
-  }
-
-  protected void enableChangeIndexWrites() {
-    for (ChangeIndex i : changeIndexes.getWriteIndexes()) {
-      if (i instanceof ReadOnlyChangeIndex) {
-        changeIndexes.addWriteIndex(((ReadOnlyChangeIndex) i).unwrap());
-      }
-    }
-  }
-
-  protected AutoCloseable disableChangeIndex() {
-    disableChangeIndexWrites();
-    ChangeIndex maybeDisabledSearchIndex = changeIndexes.getSearchIndex();
-    if (!(maybeDisabledSearchIndex instanceof DisabledChangeIndex)) {
-      changeIndexes.setSearchIndex(new DisabledChangeIndex(maybeDisabledSearchIndex), false);
-    }
-
-    return () -> {
-      enableChangeIndexWrites();
-      ChangeIndex maybeEnabledSearchIndex = changeIndexes.getSearchIndex();
-      if (maybeEnabledSearchIndex instanceof DisabledChangeIndex) {
-        changeIndexes.setSearchIndex(
-            ((DisabledChangeIndex) maybeEnabledSearchIndex).unwrap(), false);
-      }
-    };
-  }
-
-  protected AutoCloseable disableAccountIndex() {
-    AccountIndex maybeDisabledSearchIndex = accountIndexes.getSearchIndex();
-    if (!(maybeDisabledSearchIndex instanceof DisabledAccountIndex)) {
-      accountIndexes.setSearchIndex(new DisabledAccountIndex(maybeDisabledSearchIndex), false);
-    }
-
-    return () -> {
-      AccountIndex maybeEnabledSearchIndex = accountIndexes.getSearchIndex();
-      if (maybeEnabledSearchIndex instanceof DisabledAccountIndex) {
-        accountIndexes.setSearchIndex(
-            ((DisabledAccountIndex) maybeEnabledSearchIndex).unwrap(), false);
-      }
-    };
-  }
-
-  protected AutoCloseable disableProjectIndex() {
-    disableProjectIndexWrites();
-    ProjectIndex maybeDisabledSearchIndex = projectIndexes.getSearchIndex();
-    if (!(maybeDisabledSearchIndex instanceof DisabledProjectIndex)) {
-      projectIndexes.setSearchIndex(new DisabledProjectIndex(maybeDisabledSearchIndex), false);
-    }
-
-    return () -> {
-      enableProjectIndexWrites();
-      ProjectIndex maybeEnabledSearchIndex = projectIndexes.getSearchIndex();
-      if (maybeEnabledSearchIndex instanceof DisabledProjectIndex) {
-        projectIndexes.setSearchIndex(
-            ((DisabledProjectIndex) maybeEnabledSearchIndex).unwrap(), false);
-      }
-    };
-  }
-
-  protected void disableProjectIndexWrites() {
-    for (ProjectIndex i : projectIndexes.getWriteIndexes()) {
-      if (!(i instanceof DisabledProjectIndex)) {
-        projectIndexes.addWriteIndex(new DisabledProjectIndex(i));
-      }
-    }
-  }
-
-  protected void enableProjectIndexWrites() {
-    for (ProjectIndex i : projectIndexes.getWriteIndexes()) {
-      if (i instanceof DisabledProjectIndex) {
-        projectIndexes.addWriteIndex(((DisabledProjectIndex) i).unwrap());
-      }
-    }
-  }
-
   protected static Gson newGson() {
     return OutputFormat.JSON_COMPACT.newGson();
   }
@@ -1163,7 +1067,7 @@
       ProjectConfig config = projectConfigFactory.read(md);
       config.updateProject(p -> p.setBooleanConfig(BooleanProjectConfig.USE_SIGNED_OFF_BY, value));
       config.commit(md);
-      projectCache.evict(config.getProject());
+      projectCache.evictAndReindex(config.getProject());
     }
   }
 
@@ -1172,7 +1076,20 @@
       ProjectConfig config = projectConfigFactory.read(md);
       config.updateProject(p -> p.setBooleanConfig(BooleanProjectConfig.REQUIRE_CHANGE_ID, value));
       config.commit(md);
-      projectCache.evict(config.getProject());
+      projectCache.evictAndReindex(config.getProject());
+    }
+  }
+
+  protected void setSkipAddingAuthorAndCommitterAsReviewers(InheritableBoolean value)
+      throws Exception {
+    try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
+      ProjectConfig config = projectConfigFactory.read(md);
+      config.updateProject(
+          p ->
+              p.setBooleanConfig(
+                  BooleanProjectConfig.SKIP_ADDING_AUTHOR_AND_COMMITTER_AS_REVIEWERS, value));
+      config.commit(md);
+      projectCache.evictAndReindex(config.getProject());
     }
   }
 
@@ -1199,10 +1116,74 @@
     gApi.changes().id(id).current().review(ReviewInput.recommend());
   }
 
+  protected void assertThatAccountIsNotVisible(TestAccount... testAccounts) {
+    for (TestAccount testAccount : testAccounts) {
+      assertThrows(
+          ResourceNotFoundException.class, () -> gApi.accounts().id(testAccount.id().get()).get());
+    }
+  }
+
+  protected void assertReviewers(String changeId, TestAccount... expectedReviewers)
+      throws RestApiException {
+    Map<ReviewerState, Collection<AccountInfo>> reviewerMap =
+        gApi.changes().id(changeId).get().reviewers;
+    assertThat(reviewerMap).containsKey(ReviewerState.REVIEWER);
+    List<Integer> reviewers =
+        reviewerMap.get(ReviewerState.REVIEWER).stream().map(a -> a._accountId).collect(toList());
+    assertThat(reviewers)
+        .containsExactlyElementsIn(
+            Arrays.stream(expectedReviewers).map(a -> a.id().get()).collect(toList()));
+  }
+
+  protected void assertCcs(String changeId, TestAccount... expectedCcs) throws RestApiException {
+    Map<ReviewerState, Collection<AccountInfo>> reviewerMap =
+        gApi.changes().id(changeId).get().reviewers;
+    assertThat(reviewerMap).containsKey(ReviewerState.CC);
+    List<Integer> ccs =
+        reviewerMap.get(ReviewerState.CC).stream().map(a -> a._accountId).collect(toList());
+    assertThat(ccs)
+        .containsExactlyElementsIn(
+            Arrays.stream(expectedCcs).map(a -> a.id().get()).collect(toList()));
+  }
+
+  protected void assertNoCcs(String changeId) throws RestApiException {
+    Map<ReviewerState, Collection<AccountInfo>> reviewerMap =
+        gApi.changes().id(changeId).get().reviewers;
+    assertThat(reviewerMap).doesNotContainKey(ReviewerState.CC);
+  }
+
   protected void assertSubmittedTogether(String chId, String... expected) throws Exception {
-    List<ChangeInfo> actual = gApi.changes().id(chId).submittedTogether();
+    assertSubmittedTogether(chId, ImmutableSet.of(), expected);
+  }
+
+  protected void assertSubmittedTogetherWithTopicClosure(String chId, String... expected)
+      throws Exception {
+    assertSubmittedTogether(chId, ImmutableSet.of(TOPIC_CLOSURE), expected);
+  }
+
+  protected void assertSubmittedTogether(
+      String chId,
+      ImmutableSet<SubmittedTogetherOption> submittedTogetherOptions,
+      String... expected)
+      throws Exception {
+    // This does not include NON_VISIBILE_CHANGES
+    List<ChangeInfo> actual =
+        submittedTogetherOptions.isEmpty()
+            ? gApi.changes().id(chId).submittedTogether()
+            : gApi.changes()
+                .id(chId)
+                .submittedTogether(EnumSet.copyOf(submittedTogetherOptions))
+                .changes;
+
+    EnumSet<SubmittedTogetherOption> enumSetIncludingNonVisibleChanges =
+        submittedTogetherOptions.isEmpty()
+            ? EnumSet.of(NON_VISIBLE_CHANGES)
+            : EnumSet.copyOf(submittedTogetherOptions);
+    enumSetIncludingNonVisibleChanges.add(NON_VISIBLE_CHANGES);
+
+    // This includes NON_VISIBLE_CHANGES for comparison.
     SubmittedTogetherInfo info =
-        gApi.changes().id(chId).submittedTogether(EnumSet.of(NON_VISIBLE_CHANGES));
+        gApi.changes().id(chId).submittedTogether(enumSetIncludingNonVisibleChanges);
 
     assertThat(info.nonVisibleChanges).isEqualTo(0);
     assertThat(Iterables.transform(actual, i1 -> i1.changeId))
@@ -1256,62 +1237,10 @@
     assertThat(replyTo.getString()).contains(email);
   }
 
-  protected Map<BranchNameKey, ObjectId> fetchFromSubmitPreview(String changeId) throws Exception {
-    try (BinaryResult result = gApi.changes().id(changeId).current().submitPreview()) {
-      return fetchFromBundles(result);
-    }
-  }
-
-  /**
-   * Fetches each bundle into a newly cloned repository, then it applies the bundle, and returns the
-   * resulting tree id.
-   *
-   * <p>Omits NoteDb meta refs.
-   */
-  protected Map<BranchNameKey, ObjectId> fetchFromBundles(BinaryResult bundles) throws Exception {
-    assertThat(bundles.getContentType()).isEqualTo("application/x-zip");
-
-    FileSystem fs = Jimfs.newFileSystem();
-    Path previewPath = fs.getPath("preview.zip");
-    try (OutputStream out = Files.newOutputStream(previewPath)) {
-      bundles.writeTo(out);
-    }
-    Map<BranchNameKey, ObjectId> ret = new HashMap<>();
-    try (FileSystem zipFs = FileSystems.newFileSystem(previewPath, (ClassLoader) null);
-        DirectoryStream<Path> dirStream =
-            Files.newDirectoryStream(Iterables.getOnlyElement(zipFs.getRootDirectories()))) {
-      for (Path p : dirStream) {
-        if (!Files.isRegularFile(p)) {
-          continue;
-        }
-        String bundleName = p.getFileName().toString();
-        int len = bundleName.length();
-        assertThat(bundleName).endsWith(".git");
-        String repoName = bundleName.substring(0, len - 4);
-        Project.NameKey proj = Project.nameKey(repoName);
-        TestRepository<?> localRepo = cloneProject(proj);
-
-        try (InputStream bundleStream = Files.newInputStream(p);
-            TransportBundleStream tbs =
-                new TransportBundleStream(
-                    localRepo.getRepository(), new URIish(bundleName), bundleStream)) {
-          FetchResult fr =
-              tbs.fetch(
-                  NullProgressMonitor.INSTANCE,
-                  Arrays.asList(new RefSpec("refs/*:refs/preview/*")));
-          for (Ref r : fr.getAdvertisedRefs()) {
-            String refName = r.getName();
-            if (RefNames.isNoteDbMetaRef(refName)) {
-              continue;
-            }
-            RevCommit c = localRepo.getRevWalk().parseCommit(r.getObjectId());
-            ret.put(BranchNameKey.create(proj, refName), c.getTree().copy());
-          }
-        }
-      }
-    }
-    assertThat(ret).isNotEmpty();
-    return ret;
+  protected void assertMailNotReplyTo(Message message, String email) throws Exception {
+    assertThat(message.headers()).containsKey("Reply-To");
+    StringEmailHeader replyTo = (StringEmailHeader) message.headers().get("Reply-To");
+    assertThat(replyTo.getString()).doesNotContain(email);
   }
 
   /** Assert that the given branches have the given tree ids. */
@@ -1337,34 +1266,61 @@
     assertThat(refValues.keySet()).containsAnyIn(trees.keySet());
   }
 
-  protected void assertDiffForNewFile(
-      DiffInfo diff, RevCommit commit, String path, String expectedContentSideB) throws Exception {
-    List<String> expectedLines = ImmutableList.copyOf(expectedContentSideB.split("\n", -1));
+  protected void assertDiffForFullyModifiedFile(
+      DiffInfo diff,
+      String commitName,
+      String path,
+      String expectedContentSideA,
+      String expectedContentSideB)
+      throws Exception {
+    assertDiffForFile(diff, commitName, path);
 
-    assertThat(diff.binary).isNull();
+    ImmutableList<String> expectedOldLines =
+        ImmutableList.copyOf(expectedContentSideA.split("\n", -1));
+    ImmutableList<String> expectedNewLines =
+        ImmutableList.copyOf(expectedContentSideB.split("\n", -1));
+
+    assertThat(diff.changeType).isEqualTo(ChangeType.MODIFIED);
+
+    assertThat(diff.metaA).isNotNull();
+    assertThat(diff.metaB).isNotNull();
+
+    assertThat(diff.metaA.name).isEqualTo(path);
+    assertThat(diff.metaA.lines).isEqualTo(expectedOldLines.size());
+    assertThat(diff.metaB.name).isEqualTo(path);
+    assertThat(diff.metaB.lines).isEqualTo(expectedNewLines.size());
+
+    DiffInfo.ContentEntry contentEntry = diff.content.get(0);
+    assertThat(contentEntry.a).containsExactlyElementsIn(expectedOldLines).inOrder();
+    assertThat(contentEntry.b).containsExactlyElementsIn(expectedNewLines).inOrder();
+    assertThat(contentEntry.ab).isNull();
+    assertThat(contentEntry.common).isNull();
+    assertThat(contentEntry.editA).isNull();
+    assertThat(contentEntry.editB).isNull();
+    assertThat(contentEntry.skip).isNull();
+  }
+
+  protected void assertDiffForNewFile(
+      DiffInfo diff, @Nullable RevCommit commit, String path, String expectedContentSideB)
+      throws Exception {
+    assertDiffForNewFile(diff, commit.name(), path, expectedContentSideB);
+  }
+
+  protected void assertDiffForNewFile(
+      DiffInfo diff, String commitName, String path, String expectedContentSideB) throws Exception {
+    assertDiffForFile(diff, commitName, path);
+
+    ImmutableList<String> expectedLines =
+        ImmutableList.copyOf(expectedContentSideB.split("\n", -1));
+
     assertThat(diff.changeType).isEqualTo(ChangeType.ADDED);
-    assertThat(diff.diffHeader).isNotNull();
-    assertThat(diff.intralineStatus).isNull();
-    assertThat(diff.webLinks).isNull();
-    assertThat(diff.editWebLinks).isNull();
 
     assertThat(diff.metaA).isNull();
     assertThat(diff.metaB).isNotNull();
-    assertThat(diff.metaB.commitId).isEqualTo(commit.name());
 
-    String expectedContentType = "text/plain";
-    if (COMMIT_MSG.equals(path)) {
-      expectedContentType = FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE;
-    } else if (MERGE_LIST.equals(path)) {
-      expectedContentType = FileContentUtil.TEXT_X_GERRIT_MERGE_LIST;
-    }
-    assertThat(diff.metaB.contentType).isEqualTo(expectedContentType);
-
-    assertThat(diff.metaB.lines).isEqualTo(expectedLines.size());
     assertThat(diff.metaB.name).isEqualTo(path);
-    assertThat(diff.metaB.webLinks).isNull();
+    assertThat(diff.metaB.lines).isEqualTo(expectedLines.size());
 
-    assertThat(diff.content).hasSize(1);
     DiffInfo.ContentEntry contentEntry = diff.content.get(0);
     assertThat(contentEntry.b).containsExactlyElementsIn(expectedLines).inOrder();
     assertThat(contentEntry.a).isNull();
@@ -1375,6 +1331,57 @@
     assertThat(contentEntry.skip).isNull();
   }
 
+  protected void assertDiffForDeletedFile(DiffInfo diff, String path, String expectedContentSideA)
+      throws Exception {
+    assertDiffHeaders(diff);
+
+    ImmutableList<String> expectedOriginalLines =
+        ImmutableList.copyOf(expectedContentSideA.split("\n", -1));
+
+    assertThat(diff.changeType).isEqualTo(ChangeType.DELETED);
+
+    assertThat(diff.metaA).isNotNull();
+    assertThat(diff.metaB).isNull();
+
+    assertThat(diff.metaA.name).isEqualTo(path);
+    assertThat(diff.metaA.lines).isEqualTo(expectedOriginalLines.size());
+
+    DiffInfo.ContentEntry contentEntry = diff.content.get(0);
+    assertThat(contentEntry.a).containsExactlyElementsIn(expectedOriginalLines).inOrder();
+    assertThat(contentEntry.b).isNull();
+    assertThat(contentEntry.ab).isNull();
+    assertThat(contentEntry.common).isNull();
+    assertThat(contentEntry.editA).isNull();
+    assertThat(contentEntry.editB).isNull();
+    assertThat(contentEntry.skip).isNull();
+  }
+
+  private void assertDiffForFile(DiffInfo diff, String commitName, String path) throws Exception {
+    assertDiffHeaders(diff);
+
+    assertThat(diff.metaB.commitId).isEqualTo(commitName);
+
+    String expectedContentType = "text/plain";
+    if (COMMIT_MSG.equals(path)) {
+      expectedContentType = FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE;
+    } else if (MERGE_LIST.equals(path)) {
+      expectedContentType = FileContentUtil.TEXT_X_GERRIT_MERGE_LIST;
+    }
+
+    assertThat(diff.metaB.contentType).isEqualTo(expectedContentType);
+
+    assertThat(diff.metaB.name).isEqualTo(path);
+    assertThat(diff.metaB.webLinks).isNull();
+  }
+
+  private void assertDiffHeaders(DiffInfo diff) throws Exception {
+    assertThat(diff.binary).isNull();
+    assertThat(diff.diffHeader).isNotNull();
+    assertThat(diff.intralineStatus).isNull();
+    assertThat(diff.webLinks).isNull();
+    assertThat(diff.editWebLinks).isNull();
+  }
+
   protected void assertPermitted(ChangeInfo info, String label, Integer... expected) {
     assertThat(info.permittedLabels).isNotNull();
     Collection<String> strs = info.permittedLabels.get(label);
@@ -1386,6 +1393,17 @@
     }
   }
 
+  protected void assertOnlyRemovableLabel(
+      ChangeInfo info, String labelId, String labelValue, TestAccount reviewer) {
+    assertThat(info.removableLabels).hasSize(1);
+    assertThat(info.removableLabels).containsKey(labelId);
+    assertThat(info.removableLabels.get(labelId)).hasSize(1);
+    assertThat(info.removableLabels.get(labelId)).containsKey(labelValue);
+    assertThat(info.removableLabels.get(labelId).get(labelValue)).hasSize(1);
+    assertThat(info.removableLabels.get(labelId).get(labelValue).get(0).email)
+        .isEqualTo(reviewer.email());
+  }
+
   protected void assertPermissions(
       Project.NameKey project,
       GroupReference groupReference,
@@ -1614,6 +1632,13 @@
     }
   }
 
+  protected void clearSubmitRequirements(Project.NameKey project) throws Exception {
+    try (ProjectConfigUpdate u = updateProject(project)) {
+      u.getConfig().clearSubmitRequirements();
+      u.save();
+    }
+  }
+
   protected void configLabel(String label, LabelFunction func) throws Exception {
     configLabel(label, func, ImmutableList.of());
   }
@@ -1686,7 +1711,7 @@
       projectConfig.commit(metaDataUpdate);
       metaDataUpdate.close();
       metaDataUpdate = null;
-      projectCache.evict(projectConfig.getProject());
+      projectCache.evictAndReindex(projectConfig.getProject());
     }
 
     @Override
diff --git a/java/com/google/gerrit/acceptance/AbstractDynamicOptionsTest.java b/java/com/google/gerrit/acceptance/AbstractDynamicOptionsTest.java
index a4ed80a..4e8d20d 100644
--- a/java/com/google/gerrit/acceptance/AbstractDynamicOptionsTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDynamicOptionsTest.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.acceptance;
 
 import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
+import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.annotations.Exports;
@@ -50,7 +51,7 @@
 
     public void display(OutputStream displayOutputStream) throws Exception {
       PrintWriter stdout =
-          new PrintWriter(new BufferedWriter(new OutputStreamWriter(displayOutputStream, "UTF-8")));
+          new PrintWriter(new BufferedWriter(new OutputStreamWriter(displayOutputStream, UTF_8)));
       try {
         OutputFormat.JSON
             .newGson()
diff --git a/java/com/google/gerrit/acceptance/AbstractNotificationTest.java b/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
index 6c9a2b1..da033c1 100644
--- a/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
@@ -98,6 +98,7 @@
 
   protected static class FakeEmailSenderSubject extends Subject {
     private final FakeEmailSender fakeEmailSender;
+    private String emailTitle;
     private Message message;
     private StagedUsers users;
     private Map<RecipientType, List<String>> recipients = new HashMap<>();
@@ -145,6 +146,10 @@
                     ? ((StringEmailHeader) header).getString()
                     : header));
       }
+      EmailHeader titleHeader = message.headers().get("Subject");
+      if (titleHeader instanceof StringEmailHeader) {
+        emailTitle = ((StringEmailHeader) titleHeader).getString();
+      }
 
       return this;
     }
@@ -190,6 +195,15 @@
       return rcpt(users.supportReviewersByEmail ? BCC : null, emails);
     }
 
+    public FakeEmailSenderSubject title(String expectedEmailTitle) {
+      if (!emailTitle.equals(expectedEmailTitle)) {
+        failWithoutActual(
+            fact("Expected email title", expectedEmailTitle),
+            fact("but actual title is", emailTitle));
+      }
+      return this;
+    }
+
     private FakeEmailSenderSubject rcpt(@Nullable RecipientType type, String[] emails) {
       for (String email : emails) {
         rcpt(type, email);
diff --git a/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java b/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java
index 91fbf9e..fe845c0 100644
--- a/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java
@@ -303,6 +303,7 @@
     return pluginInfoFromChangeInfo(changeInfos.get(0));
   }
 
+  @Nullable
   protected static List<PluginDefinedInfo> pluginInfoFromChangeInfo(ChangeInfo changeInfo) {
     List<PluginDefinedInfo> pluginInfo = changeInfo.plugins;
     if (pluginInfo == null) {
@@ -331,6 +332,7 @@
    * @param plugins list of {@code MyInfo} objects, each as a raw map returned from Gson.
    * @return decoded list of {@code MyInfo}s.
    */
+  @Nullable
   protected static List<PluginDefinedInfo> decodeRawPluginsList(
       Gson gson, @Nullable Object plugins) {
     if (plugins == null) {
diff --git a/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java b/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
index 50536d8..c4bf20c 100644
--- a/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
+++ b/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
@@ -162,7 +162,7 @@
       new Scope() {
         @Override
         public <T> Provider<T> scope(Key<T> key, Provider<T> creator) {
-          return new Provider<T>() {
+          return new Provider<>() {
             @Override
             public T get() {
               return requireContext().get(key, creator);
diff --git a/java/com/google/gerrit/acceptance/AccountCreator.java b/java/com/google/gerrit/acceptance/AccountCreator.java
index c67991d..ff5bc00 100644
--- a/java/com/google/gerrit/acceptance/AccountCreator.java
+++ b/java/com/google/gerrit/acceptance/AccountCreator.java
@@ -141,6 +141,10 @@
     return create(username, null, username, null, (String[]) null);
   }
 
+  public TestAccount createValid(String username) throws Exception {
+    return create(username, username + "@example.com", username, username);
+  }
+
   public TestAccount admin() throws Exception {
     return create("admin", "admin@example.com", "Administrator", "Adminny", "Administrators");
   }
diff --git a/java/com/google/gerrit/acceptance/BUILD b/java/com/google/gerrit/acceptance/BUILD
index fe6e160..9d237af 100644
--- a/java/com/google/gerrit/acceptance/BUILD
+++ b/java/com/google/gerrit/acceptance/BUILD
@@ -39,11 +39,9 @@
     "//lib:gson",
     "//lib:guava-retrying",
     "//lib:jgit",
-    "//lib:jgit-ssh-jsch",
     "//lib:jgit-ssh-apache",
-    "//lib:jsch",
     "//lib/commons:compress",
-    "//lib/commons:lang",
+    "//lib/commons:lang3",
     "//lib/flogger:api",
     "//lib/guice",
     "//lib/guice:guice-assistedinject",
@@ -76,12 +74,12 @@
     "//java/com/google/gerrit/gpg/testing:gpg-test-util",
     "//java/com/google/gerrit/git/testing",
     "//java/com/google/gerrit/index/testing",
+    "//lib/errorprone:annotations",
 ]
 
 PGM_DEPLOY_ENV = [
     "//lib:caffeine",
     "//lib:caffeine-guava",
-    "//lib/jackson:jackson-core",
     "//lib/prolog:cafeteria",
 ]
 
diff --git a/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java b/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
index 1ff7d0e..5991646 100644
--- a/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
+++ b/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
@@ -46,6 +46,10 @@
   }
 
   public void assertReindexOf(ChangeInfo info, long expectedCount) {
+    if (expectedCount == 0) {
+      assertThat(countsByChange.asMap()).isEmpty();
+      return;
+    }
     assertThat(countsByChange.asMap()).containsExactly(info._number, expectedCount);
     clear();
   }
diff --git a/java/com/google/gerrit/acceptance/DisabledAccountIndex.java b/java/com/google/gerrit/acceptance/DisabledAccountIndex.java
index 271d15c..7660948 100644
--- a/java/com/google/gerrit/acceptance/DisabledAccountIndex.java
+++ b/java/com/google/gerrit/acceptance/DisabledAccountIndex.java
@@ -45,11 +45,21 @@
   }
 
   @Override
+  public void insert(AccountState obj) {
+    throw new UnsupportedOperationException("AccountIndex is disabled");
+  }
+
+  @Override
   public void replace(AccountState obj) {
     throw new UnsupportedOperationException("AccountIndex is disabled");
   }
 
   @Override
+  public void deleteByValue(AccountState value) {
+    throw new UnsupportedOperationException("AccountIndex is disabled");
+  }
+
+  @Override
   public void delete(Account.Id key) {
     throw new UnsupportedOperationException("AccountIndex is disabled");
   }
diff --git a/java/com/google/gerrit/acceptance/DisabledChangeIndex.java b/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
index 34f72f5c..c028a8e 100644
--- a/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
+++ b/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
@@ -52,11 +52,21 @@
   }
 
   @Override
+  public void insert(ChangeData obj) {
+    throw new UnsupportedOperationException("ChangeIndex is disabled");
+  }
+
+  @Override
   public void replace(ChangeData obj) {
     throw new UnsupportedOperationException("ChangeIndex is disabled");
   }
 
   @Override
+  public void deleteByValue(ChangeData value) {
+    throw new UnsupportedOperationException("ChangeIndex is disabled");
+  }
+
+  @Override
   public void delete(Change.Id key) {
     throw new UnsupportedOperationException("ChangeIndex is disabled");
   }
diff --git a/java/com/google/gerrit/acceptance/DisabledProjectIndex.java b/java/com/google/gerrit/acceptance/DisabledProjectIndex.java
index ed119ff..f2aad4a 100644
--- a/java/com/google/gerrit/acceptance/DisabledProjectIndex.java
+++ b/java/com/google/gerrit/acceptance/DisabledProjectIndex.java
@@ -50,11 +50,21 @@
   }
 
   @Override
+  public void insert(ProjectData obj) {
+    throw new UnsupportedOperationException("ProjectIndex is disabled");
+  }
+
+  @Override
   public void replace(ProjectData obj) {
     throw new UnsupportedOperationException("ProjectIndex is disabled");
   }
 
   @Override
+  public void deleteByValue(ProjectData value) {
+    throw new UnsupportedOperationException("ProjectIndex is disabled");
+  }
+
+  @Override
   public void delete(Project.NameKey key) {
     throw new UnsupportedOperationException("ProjectIndex is disabled");
   }
diff --git a/java/com/google/gerrit/acceptance/ExtensionRegistry.java b/java/com/google/gerrit/acceptance/ExtensionRegistry.java
index 1e5598e..3d90bf0 100644
--- a/java/com/google/gerrit/acceptance/ExtensionRegistry.java
+++ b/java/com/google/gerrit/acceptance/ExtensionRegistry.java
@@ -14,14 +14,17 @@
 
 package com.google.gerrit.acceptance;
 
+import com.google.gerrit.entities.SubmitRequirement;
 import com.google.gerrit.extensions.api.changes.ActionVisitor;
 import com.google.gerrit.extensions.config.CapabilityDefinition;
 import com.google.gerrit.extensions.config.DownloadScheme;
 import com.google.gerrit.extensions.config.PluginProjectPermissionDefinition;
 import com.google.gerrit.extensions.events.AccountActivationListener;
 import com.google.gerrit.extensions.events.AccountIndexedListener;
+import com.google.gerrit.extensions.events.AttentionSetListener;
 import com.google.gerrit.extensions.events.ChangeIndexedListener;
 import com.google.gerrit.extensions.events.CommentAddedListener;
+import com.google.gerrit.extensions.events.GitBatchRefUpdateListener;
 import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
 import com.google.gerrit.extensions.events.GroupIndexedListener;
 import com.google.gerrit.extensions.events.ProjectIndexedListener;
@@ -36,6 +39,7 @@
 import com.google.gerrit.extensions.registration.RegistrationHandle;
 import com.google.gerrit.extensions.webui.EditWebLink;
 import com.google.gerrit.extensions.webui.FileHistoryWebLink;
+import com.google.gerrit.extensions.webui.FileWebLink;
 import com.google.gerrit.extensions.webui.PatchSetWebLink;
 import com.google.gerrit.extensions.webui.ResolveConflictsWebLink;
 import com.google.gerrit.server.ExceptionHook;
@@ -48,6 +52,8 @@
 import com.google.gerrit.server.git.validators.OnSubmitValidationListener;
 import com.google.gerrit.server.git.validators.RefOperationValidationListener;
 import com.google.gerrit.server.logging.PerformanceLogger;
+import com.google.gerrit.server.query.change.ChangeQueryBuilder.ChangeHasOperandFactory;
+import com.google.gerrit.server.query.change.ChangeQueryBuilder.ChangeIsOperandFactory;
 import com.google.gerrit.server.restapi.change.OnPostReview;
 import com.google.gerrit.server.rules.SubmitRule;
 import com.google.gerrit.server.validators.AccountActivationValidationListener;
@@ -70,6 +76,7 @@
   private final DynamicSet<PerformanceLogger> performanceLoggers;
   private final DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners;
   private final DynamicSet<SubmitRule> submitRules;
+  private final DynamicSet<SubmitRequirement> submitRequirements;
   private final DynamicSet<ChangeMessageModifier> changeMessageModifiers;
   private final DynamicSet<ChangeETagComputation> changeETagComputations;
   private final DynamicSet<ActionVisitor> actionVisitors;
@@ -77,10 +84,12 @@
   private final DynamicSet<RefOperationValidationListener> refOperationValidationListeners;
   private final DynamicSet<CommentAddedListener> commentAddedListeners;
   private final DynamicSet<GitReferenceUpdatedListener> refUpdatedListeners;
+  private final DynamicSet<GitBatchRefUpdateListener> batchRefUpdateListeners;
   private final DynamicSet<FileHistoryWebLink> fileHistoryWebLinks;
   private final DynamicSet<PatchSetWebLink> patchSetWebLinks;
   private final DynamicSet<ResolveConflictsWebLink> resolveConflictsWebLinks;
   private final DynamicSet<EditWebLink> editWebLinks;
+  private final DynamicSet<FileWebLink> fileWebLinks;
   private final DynamicSet<RevisionCreatedListener> revisionCreatedListeners;
   private final DynamicSet<GroupBackend> groupBackends;
   private final DynamicSet<AccountActivationValidationListener>
@@ -95,6 +104,10 @@
   private final DynamicSet<OnPostReview> onPostReviews;
   private final DynamicSet<ReviewerAddedListener> reviewerAddedListeners;
   private final DynamicSet<ReviewerDeletedListener> reviewerDeletedListeners;
+  private final DynamicSet<AttentionSetListener> attentionSetListeners;
+
+  private final DynamicMap<ChangeHasOperandFactory> hasOperands;
+  private final DynamicMap<ChangeIsOperandFactory> isOperands;
 
   @Inject
   ExtensionRegistry(
@@ -108,6 +121,7 @@
       DynamicSet<PerformanceLogger> performanceLoggers,
       DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners,
       DynamicSet<SubmitRule> submitRules,
+      DynamicSet<SubmitRequirement> submitRequirements,
       DynamicSet<ChangeMessageModifier> changeMessageModifiers,
       DynamicSet<ChangeETagComputation> changeETagComputations,
       DynamicSet<ActionVisitor> actionVisitors,
@@ -115,10 +129,12 @@
       DynamicSet<RefOperationValidationListener> refOperationValidationListeners,
       DynamicSet<CommentAddedListener> commentAddedListeners,
       DynamicSet<GitReferenceUpdatedListener> refUpdatedListeners,
+      DynamicSet<GitBatchRefUpdateListener> batchRefUpdateListeners,
       DynamicSet<FileHistoryWebLink> fileHistoryWebLinks,
       DynamicSet<PatchSetWebLink> patchSetWebLinks,
       DynamicSet<ResolveConflictsWebLink> resolveConflictsWebLinks,
       DynamicSet<EditWebLink> editWebLinks,
+      DynamicSet<FileWebLink> fileWebLinks,
       DynamicSet<RevisionCreatedListener> revisionCreatedListeners,
       DynamicSet<GroupBackend> groupBackends,
       DynamicSet<AccountActivationValidationListener> accountActivationValidationListeners,
@@ -131,7 +147,10 @@
       DynamicSet<PluginPushOption> pluginPushOption,
       DynamicSet<OnPostReview> onPostReviews,
       DynamicSet<ReviewerAddedListener> reviewerAddedListeners,
-      DynamicSet<ReviewerDeletedListener> reviewerDeletedListeners) {
+      DynamicSet<ReviewerDeletedListener> reviewerDeletedListeners,
+      DynamicMap<ChangeHasOperandFactory> hasOperands,
+      DynamicMap<ChangeIsOperandFactory> isOperands,
+      DynamicSet<AttentionSetListener> attentionSetListeners) {
     this.accountIndexedListeners = accountIndexedListeners;
     this.changeIndexedListeners = changeIndexedListeners;
     this.groupIndexedListeners = groupIndexedListeners;
@@ -142,6 +161,7 @@
     this.performanceLoggers = performanceLoggers;
     this.projectCreationValidationListeners = projectCreationValidationListeners;
     this.submitRules = submitRules;
+    this.submitRequirements = submitRequirements;
     this.changeMessageModifiers = changeMessageModifiers;
     this.changeETagComputations = changeETagComputations;
     this.actionVisitors = actionVisitors;
@@ -149,9 +169,11 @@
     this.refOperationValidationListeners = refOperationValidationListeners;
     this.commentAddedListeners = commentAddedListeners;
     this.refUpdatedListeners = refUpdatedListeners;
+    this.batchRefUpdateListeners = batchRefUpdateListeners;
     this.fileHistoryWebLinks = fileHistoryWebLinks;
     this.patchSetWebLinks = patchSetWebLinks;
     this.editWebLinks = editWebLinks;
+    this.fileWebLinks = fileWebLinks;
     this.resolveConflictsWebLinks = resolveConflictsWebLinks;
     this.revisionCreatedListeners = revisionCreatedListeners;
     this.groupBackends = groupBackends;
@@ -166,6 +188,9 @@
     this.onPostReviews = onPostReviews;
     this.reviewerAddedListeners = reviewerAddedListeners;
     this.reviewerDeletedListeners = reviewerDeletedListeners;
+    this.hasOperands = hasOperands;
+    this.isOperands = isOperands;
+    this.attentionSetListeners = attentionSetListeners;
   }
 
   public Registration newRegistration() {
@@ -216,6 +241,18 @@
       return add(submitRules, submitRule);
     }
 
+    public Registration add(SubmitRequirement submitRequirement) {
+      return add(submitRequirements, submitRequirement);
+    }
+
+    public Registration add(ChangeHasOperandFactory hasOperand, String exportName) {
+      return add(hasOperands, hasOperand, exportName);
+    }
+
+    public Registration add(ChangeIsOperandFactory isOperand, String exportName) {
+      return add(isOperands, isOperand, exportName);
+    }
+
     public Registration add(ChangeMessageModifier changeMessageModifier) {
       return add(changeMessageModifiers, changeMessageModifier);
     }
@@ -248,6 +285,10 @@
       return add(refUpdatedListeners, refUpdatedListener);
     }
 
+    public Registration add(GitBatchRefUpdateListener batchRefUpdateListener) {
+      return add(batchRefUpdateListeners, batchRefUpdateListener);
+    }
+
     public Registration add(FileHistoryWebLink fileHistoryWebLink) {
       return add(fileHistoryWebLinks, fileHistoryWebLink);
     }
@@ -264,6 +305,10 @@
       return add(editWebLinks, editWebLink);
     }
 
+    public Registration add(FileWebLink fileWebLink) {
+      return add(fileWebLinks, fileWebLink);
+    }
+
     public Registration add(RevisionCreatedListener revisionCreatedListener) {
       return add(revisionCreatedListeners, revisionCreatedListener);
     }
@@ -289,6 +334,10 @@
       return add(workInProgressStateChangedListeners, workInProgressStateChangedListener);
     }
 
+    public Registration add(AttentionSetListener attentionSetListener) {
+      return add(attentionSetListeners, attentionSetListener);
+    }
+
     public Registration add(CapabilityDefinition capabilityDefinition, String exportName) {
       return add(capabilityDefinitions, capabilityDefinition, exportName);
     }
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index 402d21d..a149f29 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -61,6 +61,7 @@
 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.index.options.AutoFlush;
 import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
 import com.google.gerrit.server.ssh.NoSshModule;
 import com.google.gerrit.server.util.ReplicaUtil;
@@ -492,11 +493,11 @@
     }
     if (indexType.isLucene()) {
       daemon.setIndexModule(
-          LuceneIndexModule.singleVersionAllLatest(0, ReplicaUtil.isReplica(baseConfig)));
+          LuceneIndexModule.singleVersionAllLatest(
+              0, ReplicaUtil.isReplica(baseConfig), AutoFlush.ENABLED));
     } else {
       daemon.setIndexModule(FakeIndexModule.latestVersion(false));
     }
-    // Elastic search is not supported in integration tests yet.
 
     daemon.setEnableHttpd(desc.httpd());
     daemon.setInMemory(true);
diff --git a/java/com/google/gerrit/acceptance/GitClientVersion.java b/java/com/google/gerrit/acceptance/GitClientVersion.java
index 4c9a32d..2415781 100644
--- a/java/com/google/gerrit/acceptance/GitClientVersion.java
+++ b/java/com/google/gerrit/acceptance/GitClientVersion.java
@@ -16,6 +16,8 @@
 
 import static java.util.stream.Collectors.joining;
 
+import com.google.common.base.Splitter;
+import java.util.List;
 import java.util.stream.IntStream;
 
 /** Class to parse and represent version of git-core client */
@@ -38,11 +40,11 @@
    */
   public GitClientVersion(String version) {
     // "git version x.y.z", at Google "git version x.y.z.gXXXXXXXXXX-goog"
-    String parts[] = version.split(" ")[2].split("\\.");
-    int numParts = Math.min(parts.length, 3); // ignore Google-specific part of the version
+    List<String> parts = Splitter.on(".").splitToList(Splitter.on(" ").splitToList(version).get(2));
+    int numParts = Math.min(parts.size(), 3); // ignore Google-specific part of the version
     v = new int[numParts];
     for (int i = 0; i < numParts; i++) {
-      v[i] = Integer.valueOf(parts[i]);
+      v[i] = Integer.valueOf(parts.get(i));
     }
   }
 
diff --git a/java/com/google/gerrit/acceptance/HttpResponse.java b/java/com/google/gerrit/acceptance/HttpResponse.java
index 88079a4..76c0f04 100644
--- a/java/com/google/gerrit/acceptance/HttpResponse.java
+++ b/java/com/google/gerrit/acceptance/HttpResponse.java
@@ -19,6 +19,7 @@
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.ImmutableList;
+import com.google.gerrit.common.Nullable;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
@@ -59,6 +60,7 @@
     return getHeader("X-FYI-Content-Type");
   }
 
+  @Nullable
   public String getHeader(String name) {
     Header hdr = response.getFirstHeader(name);
     return hdr != null ? hdr.getValue() : null;
diff --git a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
index 373246a..9a652e3 100644
--- a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
+++ b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
@@ -21,11 +21,13 @@
 import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.metrics.MetricsReservoirConfig;
 import com.google.gerrit.server.config.AllProjectsConfigProvider;
 import com.google.gerrit.server.config.FileBasedAllProjectsConfigProvider;
 import com.google.gerrit.server.config.FileBasedGlobalPluginConfigProvider;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.GlobalPluginConfigProvider;
+import com.google.gerrit.server.config.MetricsReservoirConfigImpl;
 import com.google.gerrit.server.config.SitePath;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.config.TrackingFooters;
@@ -36,6 +38,7 @@
 import com.google.gerrit.testing.InMemoryRepositoryManager;
 import com.google.inject.Inject;
 import com.google.inject.ProvisionException;
+import com.google.inject.Scopes;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -69,6 +72,7 @@
       bind(InMemoryRepositoryManager.class).in(SINGLETON);
     }
 
+    bind(MetricsReservoirConfig.class).to(MetricsReservoirConfigImpl.class).in(Scopes.SINGLETON);
     bind(MetricMaker.class).to(TestMetricMaker.class);
 
     listener().to(CreateSchema.class);
diff --git a/java/com/google/gerrit/acceptance/InProcessProtocol.java b/java/com/google/gerrit/acceptance/InProcessProtocol.java
index 83c63f9..17ce595 100644
--- a/java/com/google/gerrit/acceptance/InProcessProtocol.java
+++ b/java/com/google/gerrit/acceptance/InProcessProtocol.java
@@ -100,7 +100,7 @@
       new Scope() {
         @Override
         public <T> Provider<T> scope(Key<T> key, Provider<T> creator) {
-          return new Provider<T>() {
+          return new Provider<>() {
             @Override
             public T get() {
               Context ctx = current.get();
@@ -235,9 +235,9 @@
 
       PermissionBackend.ForProject perm = permissionBackend.currentUser().project(req.project);
       try {
-        perm.check(ProjectPermission.RUN_UPLOAD_PACK);
-      } catch (AuthException e) {
-        throw new ServiceNotAuthorizedException(e.getMessage(), e);
+        if (!perm.test(ProjectPermission.RUN_UPLOAD_PACK)) {
+          throw new ServiceNotAuthorizedException("upload pack not permitted");
+        }
       } catch (PermissionBackendException e) {
         throw new RuntimeException(e);
       }
diff --git a/java/com/google/gerrit/acceptance/ProjectResetter.java b/java/com/google/gerrit/acceptance/ProjectResetter.java
index d885303..46f7496 100644
--- a/java/com/google/gerrit/acceptance/ProjectResetter.java
+++ b/java/com/google/gerrit/acceptance/ProjectResetter.java
@@ -307,7 +307,7 @@
         Sets.union(
             projectsWithConfigChanges(restoredRefsByProject),
             projectsWithConfigChanges(deletedRefsByProject))) {
-      projectCache.evict(project);
+      projectCache.evictAndReindex(project);
     }
   }
 
diff --git a/java/com/google/gerrit/acceptance/PushOneCommit.java b/java/com/google/gerrit/acceptance/PushOneCommit.java
index d46fb78..5b1fa9b 100644
--- a/java/com/google/gerrit/acceptance/PushOneCommit.java
+++ b/java/com/google/gerrit/acceptance/PushOneCommit.java
@@ -24,6 +24,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.UsedAt;
 import com.google.gerrit.common.UsedAt.Project;
@@ -263,7 +264,11 @@
     if (changeId != null) {
       commitBuilder = testRepo.amendRef("HEAD").insertChangeId(changeId.substring(1));
     } else {
-      commitBuilder = testRepo.branch("HEAD").commit().insertChangeId(nextChangeId());
+      if (subject.contains("\nChange-Id: ")) {
+        commitBuilder = testRepo.amendRef("HEAD");
+      } else {
+        commitBuilder = testRepo.branch("HEAD").commit().insertChangeId(nextChangeId());
+      }
     }
     commitBuilder.message(subject).author(i).committer(new PersonIdent(i, testRepo.getDate()));
   }
@@ -276,6 +281,12 @@
     return this;
   }
 
+  @CanIgnoreReturnValue
+  public PushOneCommit setTopLevelTreeId(ObjectId treeId) throws Exception {
+    commitBuilder.setTopLevelTree(treeId);
+    return this;
+  }
+
   public PushOneCommit setParent(RevCommit parent) throws Exception {
     commitBuilder.noParents();
     commitBuilder.parent(parent);
@@ -287,6 +298,19 @@
     return this;
   }
 
+  public PushOneCommit addFile(String path, String content, int fileMode) throws Exception {
+    RevBlob blobId = testRepo.blob(content);
+    commitBuilder.edit(
+        new PathEdit(path) {
+          @Override
+          public void apply(DirCacheEntry ent) {
+            ent.setFileMode(FileMode.fromBits(fileMode));
+            ent.setObjectId(blobId);
+          }
+        });
+    return this;
+  }
+
   public PushOneCommit addSymlink(String path, String target) throws Exception {
     RevBlob blobId = testRepo.blob(target);
     commitBuilder.edit(
diff --git a/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java b/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java
deleted file mode 100644
index e943519..0000000
--- a/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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.
-
-package com.google.gerrit.acceptance;
-
-import com.google.gerrit.entities.Change;
-import com.google.gerrit.index.QueryOptions;
-import com.google.gerrit.index.Schema;
-import com.google.gerrit.index.query.DataSource;
-import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.server.index.change.ChangeIndex;
-import com.google.gerrit.server.query.change.ChangeData;
-
-class ReadOnlyChangeIndex implements ChangeIndex {
-  private final ChangeIndex index;
-
-  ReadOnlyChangeIndex(ChangeIndex index) {
-    this.index = index;
-  }
-
-  ChangeIndex unwrap() {
-    return index;
-  }
-
-  @Override
-  public Schema<ChangeData> getSchema() {
-    return index.getSchema();
-  }
-
-  @Override
-  public void close() {
-    index.close();
-  }
-
-  @Override
-  public void replace(ChangeData obj) {
-    // do nothing
-  }
-
-  @Override
-  public void delete(Change.Id key) {
-    // do nothing
-  }
-
-  @Override
-  public void deleteAll() {
-    // do nothing
-  }
-
-  @Override
-  public DataSource<ChangeData> getSource(Predicate<ChangeData> p, QueryOptions opts)
-      throws QueryParseException {
-    return index.getSource(p, opts);
-  }
-
-  @Override
-  public void markReady(boolean ready) {
-    // do nothing
-  }
-}
diff --git a/java/com/google/gerrit/acceptance/SshSessionJsch.java b/java/com/google/gerrit/acceptance/SshSessionJsch.java
deleted file mode 100644
index a86c2d6..0000000
--- a/java/com/google/gerrit/acceptance/SshSessionJsch.java
+++ /dev/null
@@ -1,174 +0,0 @@
-// 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.acceptance;
-
-import static java.nio.charset.StandardCharsets.US_ASCII;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.gerrit.acceptance.testsuite.account.TestAccount;
-import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
-import com.jcraft.jsch.ChannelExec;
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.Session;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.net.InetSocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.Properties;
-import java.util.Scanner;
-import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
-import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
-import org.bouncycastle.util.io.pem.PemObject;
-import org.eclipse.jgit.transport.JschConfigSessionFactory;
-import org.eclipse.jgit.transport.OpenSshConfig.Host;
-import org.eclipse.jgit.transport.SshSessionFactory;
-import org.eclipse.jgit.util.FS;
-
-public class SshSessionJsch extends SshSession {
-
-  private Session session;
-
-  public static void initClient(KeyPair keyPair) {
-    Properties config = new Properties();
-    config.put("StrictHostKeyChecking", "no");
-    JSch.setConfig(config);
-
-    // register a JschConfigSessionFactory that adds the private key as identity
-    // to the JSch instance of JGit so that SSH communication via JGit can
-    // succeed
-    SshSessionFactory.setInstance(
-        new JschConfigSessionFactory() {
-          @Override
-          protected void configure(Host hc, Session session) {
-            try {
-              JSch jsch = getJSch(hc, FS.DETECTED);
-              jsch.addIdentity(
-                  "KeyPair", privateKey(keyPair), TestSshKeys.publicKeyBlob(keyPair), null);
-            } catch (JSchException | GeneralSecurityException | IOException e) {
-              throw new RuntimeException(e);
-            }
-          }
-        });
-  }
-
-  public static KeyPairGenerator initKeyPairGenerator() throws NoSuchAlgorithmException {
-    KeyPairGenerator gen;
-    gen = KeyPairGenerator.getInstance("RSA");
-    gen.initialize(512, new SecureRandom());
-    return gen;
-  }
-
-  public SshSessionJsch(TestSshKeys sshKeys, InetSocketAddress addr, TestAccount account) {
-    super(sshKeys, addr, account);
-  }
-
-  @Override
-  public void open() throws Exception {
-    getJschSession();
-  }
-
-  @Override
-  public void close() {
-    if (session != null) {
-      session.disconnect();
-      session = null;
-    }
-  }
-
-  @SuppressWarnings("resource")
-  @Override
-  public String exec(String command) throws Exception {
-    ChannelExec channel = (ChannelExec) getJschSession().openChannel("exec");
-    try {
-      channel.setCommand(command);
-      InputStream in = channel.getInputStream();
-      InputStream err = channel.getErrStream();
-      channel.connect();
-
-      Scanner s = new Scanner(err, UTF_8.name()).useDelimiter("\\A");
-      error = s.hasNext() ? s.next() : null;
-
-      s = new Scanner(in, UTF_8.name()).useDelimiter("\\A");
-      return s.hasNext() ? s.next() : "";
-    } finally {
-      channel.disconnect();
-    }
-  }
-
-  @SuppressWarnings("resource")
-  @Override
-  public int execAndReturnStatus(String command) throws Exception {
-    ChannelExec channel = (ChannelExec) getJschSession().openChannel("exec");
-    try {
-      channel.setCommand(command);
-      InputStream err = channel.getErrStream();
-      channel.connect();
-
-      Scanner s = new Scanner(err, UTF_8.name()).useDelimiter("\\A");
-      error = s.hasNext() ? s.next() : null;
-      return channel.getExitStatus();
-    } finally {
-      channel.disconnect();
-    }
-  }
-
-  @Override
-  public Reader execAndReturnReader(String command) throws Exception {
-    ChannelExec channel = (ChannelExec) getJschSession().openChannel("exec");
-    channel.setCommand(command);
-    channel.connect();
-
-    return new InputStreamReader(channel.getInputStream(), StandardCharsets.UTF_8) {
-      @Override
-      public void close() throws IOException {
-        super.close();
-        channel.disconnect();
-      }
-    };
-  }
-
-  private Session getJschSession() throws Exception {
-    if (session == null) {
-      KeyPair keyPair = sshKeys.getKeyPair(account);
-      JSch jsch = new JSch();
-      jsch.addIdentity("KeyPair", privateKey(keyPair), TestSshKeys.publicKeyBlob(keyPair), null);
-      String username = getUsername();
-      session = jsch.getSession(username, addr.getAddress().getHostAddress(), addr.getPort());
-      session.setConfig("StrictHostKeyChecking", "no");
-      session.connect();
-    }
-    return session;
-  }
-
-  private static byte[] privateKey(KeyPair keyPair) throws IOException {
-    // unencrypted form of PKCS#8 file
-    JcaPKCS8Generator gen1 = new JcaPKCS8Generator(keyPair.getPrivate(), null);
-    PemObject obj1 = gen1.generate();
-    StringWriter sw1 = new StringWriter();
-    try (JcaPEMWriter pw = new JcaPEMWriter(sw1)) {
-      pw.writeObject(obj1);
-    }
-    return sw1.toString().getBytes(US_ASCII.name());
-  }
-}
diff --git a/java/com/google/gerrit/acceptance/SshSessionMina.java b/java/com/google/gerrit/acceptance/SshSessionMina.java
index 3b0ba3b..89096e4 100644
--- a/java/com/google/gerrit/acceptance/SshSessionMina.java
+++ b/java/com/google/gerrit/acceptance/SshSessionMina.java
@@ -16,6 +16,7 @@
 
 import static com.google.common.io.RecursiveDeleteOption.ALLOW_INSECURE;
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.nio.file.Files.createTempDirectory;
 
 import com.google.common.io.CharSink;
 import com.google.common.io.Files;
@@ -140,7 +141,7 @@
                   + addr.getPort());
 
       // TODO(davido): Switch to memory only key resolving mode.
-      File userhome = Files.createTempDir();
+      File userhome = createTempDirectory("home-").toFile();
 
       FS fs = FS.DETECTED.setUserHome(userhome);
       File sshDir = new File(userhome, ".ssh");
@@ -168,6 +169,7 @@
                 MoreFiles.deleteRecursively(userhome.toPath(), ALLOW_INSECURE);
               } catch (IOException e) {
                 e.printStackTrace();
+                throw new RuntimeException("Failed to cleanup userhome", e);
               }
             });
       }
diff --git a/java/com/google/gerrit/acceptance/TestMetricMaker.java b/java/com/google/gerrit/acceptance/TestMetricMaker.java
index 2620f99..85c5b6d 100644
--- a/java/com/google/gerrit/acceptance/TestMetricMaker.java
+++ b/java/com/google/gerrit/acceptance/TestMetricMaker.java
@@ -18,9 +18,8 @@
 import com.google.gerrit.metrics.Description;
 import com.google.gerrit.metrics.DisabledMetricMaker;
 import com.google.inject.Singleton;
-import java.util.HashMap;
-import java.util.Map;
-import org.apache.commons.lang.mutable.MutableLong;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.commons.lang3.mutable.MutableLong;
 
 /**
  * {@link com.google.gerrit.metrics.MetricMaker} to be bound in tests.
@@ -49,7 +48,7 @@
  */
 @Singleton
 public class TestMetricMaker extends DisabledMetricMaker {
-  private final Map<String, MutableLong> counts = new HashMap<>();
+  private final ConcurrentHashMap<String, MutableLong> counts = new ConcurrentHashMap<>();
 
   public long getCount(String counter0Name) {
     return get(counter0Name).longValue();
diff --git a/java/com/google/gerrit/acceptance/TestOnStoreSubmitRequirementResultModifier.java b/java/com/google/gerrit/acceptance/TestOnStoreSubmitRequirementResultModifier.java
new file mode 100644
index 0000000..0138ffa
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/TestOnStoreSubmitRequirementResultModifier.java
@@ -0,0 +1,86 @@
+// Copyright (C) 2022 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.acceptance;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.SubmitRequirement;
+import com.google.gerrit.entities.SubmitRequirementExpressionResult;
+import com.google.gerrit.entities.SubmitRequirementResult;
+import com.google.gerrit.server.project.OnStoreSubmitRequirementResultModifier;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.update.ChangeContext;
+import java.util.Optional;
+
+/** Implementation of {@link OnStoreSubmitRequirementResultModifier} that is used in tests. */
+public class TestOnStoreSubmitRequirementResultModifier
+    implements OnStoreSubmitRequirementResultModifier {
+
+  private ModificationStrategy modificationStrategy = ModificationStrategy.KEEP;
+
+  private boolean hide = false;
+
+  /**
+   * The strategy, used by this modifier to transform {@link SubmitRequirementResult} on {@link
+   * OnStoreSubmitRequirementResultModifier#modifyResultOnStore} invocations.
+   */
+  public enum ModificationStrategy {
+    KEEP,
+    FAIL,
+    PASS,
+    OVERRIDE
+  }
+
+  public void setModificationStrategy(ModificationStrategy modificationStrategy) {
+    this.modificationStrategy = modificationStrategy;
+  }
+
+  public void hide(boolean hide) {
+    this.hide = hide;
+  }
+
+  @Override
+  public SubmitRequirementResult modifyResultOnStore(
+      SubmitRequirement submitRequirement,
+      SubmitRequirementResult result,
+      ChangeData cd,
+      ChangeContext ctx) {
+    if (modificationStrategy.equals(ModificationStrategy.KEEP)) {
+      return result;
+    }
+    SubmitRequirementResult.Builder srResultBuilder = result.toBuilder().hidden(Optional.of(hide));
+    if (modificationStrategy.equals(ModificationStrategy.OVERRIDE)) {
+      return srResultBuilder
+          .overrideExpressionResult(
+              Optional.of(
+                  SubmitRequirementExpressionResult.create(
+                      submitRequirement.submittabilityExpression(),
+                      SubmitRequirementExpressionResult.Status.PASS,
+                      ImmutableList.of(),
+                      ImmutableList.of(
+                          submitRequirement.submittabilityExpression().expressionString()))))
+          .build();
+    }
+    return srResultBuilder
+        .submittabilityExpressionResult(
+            SubmitRequirementExpressionResult.create(
+                submitRequirement.submittabilityExpression(),
+                modificationStrategy.equals(ModificationStrategy.FAIL)
+                    ? SubmitRequirementExpressionResult.Status.FAIL
+                    : SubmitRequirementExpressionResult.Status.PASS,
+                ImmutableList.of(),
+                ImmutableList.of(submitRequirement.submittabilityExpression().expressionString())))
+        .build();
+  }
+}
diff --git a/java/com/google/gerrit/acceptance/config/BUILD b/java/com/google/gerrit/acceptance/config/BUILD
index a8ccc1f..0da68b0 100644
--- a/java/com/google/gerrit/acceptance/config/BUILD
+++ b/java/com/google/gerrit/acceptance/config/BUILD
@@ -7,6 +7,7 @@
     srcs = glob(["*.java"]),
     visibility = ["//visibility:public"],
     deps = [
+        "//java/com/google/gerrit/common:annotations",
         "//lib:guava",
         "//lib:jgit",
         "//lib/auto:auto-value",
diff --git a/java/com/google/gerrit/acceptance/config/ConfigAnnotationParser.java b/java/com/google/gerrit/acceptance/config/ConfigAnnotationParser.java
index 24a2117..27ce857 100644
--- a/java/com/google/gerrit/acceptance/config/ConfigAnnotationParser.java
+++ b/java/com/google/gerrit/acceptance/config/ConfigAnnotationParser.java
@@ -18,6 +18,7 @@
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
+import com.google.gerrit.common.Nullable;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -27,6 +28,7 @@
 public class ConfigAnnotationParser {
   private static Splitter splitter = Splitter.on(".").trimResults();
 
+  @Nullable
   public static Config parse(Config base, GerritConfigs annotation) {
     if (annotation == null) {
       return null;
@@ -45,6 +47,7 @@
     return cfg;
   }
 
+  @Nullable
   public static Map<String, Config> parse(GlobalPluginConfigs annotation) {
     if (annotation == null || annotation.value().length < 1) {
       return null;
@@ -67,6 +70,7 @@
     return result;
   }
 
+  @Nullable
   public static Map<String, Config> parse(GlobalPluginConfig annotation) {
     if (annotation == null) {
       return null;
diff --git a/java/com/google/gerrit/acceptance/rest/PluginResource.java b/java/com/google/gerrit/acceptance/rest/PluginResource.java
index 745d4fa..56710b9 100644
--- a/java/com/google/gerrit/acceptance/rest/PluginResource.java
+++ b/java/com/google/gerrit/acceptance/rest/PluginResource.java
@@ -20,6 +20,5 @@
 
 public class PluginResource extends ConfigResource {
 
-  static final TypeLiteral<RestView<PluginResource>> PLUGIN_KIND =
-      new TypeLiteral<RestView<PluginResource>>() {};
+  static final TypeLiteral<RestView<PluginResource>> PLUGIN_KIND = new TypeLiteral<>() {};
 }
diff --git a/java/com/google/gerrit/acceptance/testsuite/change/ChangeOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/change/ChangeOperationsImpl.java
index 3b15b57..c1029be 100644
--- a/java/com/google/gerrit/acceptance/testsuite/change/ChangeOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/change/ChangeOperationsImpl.java
@@ -47,9 +47,8 @@
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.inject.Inject;
 import java.io.IOException;
-import java.sql.Timestamp;
+import java.time.Instant;
 import java.util.Arrays;
-import java.util.Date;
 import java.util.Objects;
 import java.util.Optional;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -138,15 +137,16 @@
     try (Repository repository = repositoryManager.openRepository(project);
         ObjectInserter objectInserter = repository.newObjectInserter();
         RevWalk revWalk = new RevWalk(objectInserter.newReader())) {
-      Timestamp now = TimeUtil.nowTs();
+      Instant now = TimeUtil.now();
       IdentifiedUser changeOwner = getChangeOwner(changeCreation);
-      PersonIdent authorAndCommitter =
-          changeOwner.newCommitterIdent(now, serverIdent.getTimeZone());
+      PersonIdent authorAndCommitter = changeOwner.newCommitterIdent(now, serverIdent.getZoneId());
       ObjectId commitId =
           createCommit(repository, revWalk, objectInserter, changeCreation, authorAndCommitter);
 
       String refName = RefNames.fullName(changeCreation.branch());
       ChangeInserter inserter = getChangeInserter(changeId, refName, commitId);
+      changeCreation.topic().ifPresent(t -> inserter.setTopic(t));
+      inserter.setApprovals(changeCreation.approvals());
 
       try (BatchUpdate batchUpdate = batchUpdateFactory.create(project, changeOwner, now)) {
         batchUpdate.setRepository(repository, revWalk, objectInserter);
@@ -431,7 +431,7 @@
       try (Repository repository = repositoryManager.openRepository(project);
           ObjectInserter objectInserter = repository.newObjectInserter();
           RevWalk revWalk = new RevWalk(objectInserter.newReader())) {
-        Timestamp now = TimeUtil.nowTs();
+        Instant now = TimeUtil.now();
         ObjectId newPatchsetCommit =
             createPatchsetCommit(
                 repository, revWalk, objectInserter, changeNotes, patchsetCreation, now);
@@ -457,7 +457,7 @@
         ObjectInserter objectInserter,
         ChangeNotes changeNotes,
         TestPatchsetCreation patchsetCreation,
-        Timestamp now)
+        Instant now)
         throws IOException, BadRequestException {
       ObjectId oldPatchsetCommitId = changeNotes.getCurrentPatchSet().commitId();
       RevCommit oldPatchsetCommit = repository.parseCommit(oldPatchsetCommitId);
@@ -494,10 +494,10 @@
       return Optional.ofNullable(oldPatchsetCommit.getAuthorIdent()).orElse(serverIdent);
     }
 
-    private PersonIdent getCommitter(RevCommit oldPatchsetCommit, Timestamp now) {
+    private PersonIdent getCommitter(RevCommit oldPatchsetCommit, Instant now) {
       PersonIdent oldPatchsetCommitter =
           Optional.ofNullable(oldPatchsetCommit.getCommitterIdent()).orElse(serverIdent);
-      if (asSeconds(now) == asSeconds(oldPatchsetCommitter.getWhen())) {
+      if (asSeconds(now) == asSeconds(oldPatchsetCommitter.getWhenAsInstant())) {
         /* We need to ensure that the resulting commit SHA-1 is different from the old patchset.
          * In real situations, this automatically happens as two patchsets won't have exactly the
          * same commit timestamp even when the tree and commit message are the same. In tests,
@@ -505,13 +505,13 @@
          * We could of course require that tests must use TestTimeUtil#setClockStep but
          * that would be an unnecessary nuisance for test writers. Hence, go with a simple solution
          * here and simply add a second. */
-        now = Timestamp.from(now.toInstant().plusSeconds(1));
+        now = now.plusSeconds(1);
       }
       return new PersonIdent(oldPatchsetCommitter, now);
     }
 
-    private long asSeconds(Date date) {
-      return date.getTime() / 1000;
+    private long asSeconds(Instant date) {
+      return date.getEpochSecond();
     }
 
     private ImmutableList<ObjectId> getParents(
diff --git a/java/com/google/gerrit/acceptance/testsuite/change/FileContentBuilder.java b/java/com/google/gerrit/acceptance/testsuite/change/FileContentBuilder.java
index d0ccd5b..1971c57 100644
--- a/java/com/google/gerrit/acceptance/testsuite/change/FileContentBuilder.java
+++ b/java/com/google/gerrit/acceptance/testsuite/change/FileContentBuilder.java
@@ -28,13 +28,18 @@
 public class FileContentBuilder<T> {
   private final T builder;
   private final String filePath;
+  private final int newGitFileMode;
   private final Consumer<TreeModification> modificationToBuilderAdder;
 
   FileContentBuilder(
-      T builder, String filePath, Consumer<TreeModification> modificationToBuilderAdder) {
+      T builder,
+      String filePath,
+      int newGitFileMode,
+      Consumer<TreeModification> modificationToBuilderAdder) {
     checkNotNull(Strings.emptyToNull(filePath), "File path must not be null or empty.");
     this.builder = builder;
     this.filePath = filePath;
+    this.newGitFileMode = newGitFileMode;
     this.modificationToBuilderAdder = modificationToBuilderAdder;
   }
 
@@ -44,7 +49,7 @@
         Strings.emptyToNull(content),
         "Empty file content is not supported. Adjust test API if necessary.");
     modificationToBuilderAdder.accept(
-        new ChangeFileContentModification(filePath, RawInputUtil.create(content)));
+        new ChangeFileContentModification(filePath, RawInputUtil.create(content), newGitFileMode));
     return builder;
   }
 
diff --git a/java/com/google/gerrit/acceptance/testsuite/change/IndexOperations.java b/java/com/google/gerrit/acceptance/testsuite/change/IndexOperations.java
new file mode 100644
index 0000000..cba9b15
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/change/IndexOperations.java
@@ -0,0 +1,162 @@
+// Copyright (C) 2022 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.acceptance.testsuite.change;
+
+import com.google.gerrit.acceptance.DisabledAccountIndex;
+import com.google.gerrit.acceptance.DisabledChangeIndex;
+import com.google.gerrit.acceptance.DisabledProjectIndex;
+import com.google.gerrit.index.project.ProjectIndex;
+import com.google.gerrit.index.project.ProjectIndexCollection;
+import com.google.gerrit.server.index.account.AccountIndex;
+import com.google.gerrit.server.index.account.AccountIndexCollection;
+import com.google.gerrit.server.index.change.ChangeIndex;
+import com.google.gerrit.server.index.change.ChangeIndexCollection;
+import com.google.inject.Inject;
+
+/** Helpers to enable and disable reads/writes to secondary indices during testing. */
+public interface IndexOperations {
+  /**
+   * Disables reads from the secondary index that this instance is scoped to. Reads fail with {@code
+   * UnsupportedOperationException}.
+   */
+  AutoCloseable disableReads();
+
+  /**
+   * Disables writes to the secondary index that this instance is scoped to. Writes fail with {@code
+   * UnsupportedOperationException}.
+   */
+  AutoCloseable disableWrites();
+
+  /** Disables reads from and writes to the secondary index that this instance is scoped to. */
+  default AutoCloseable disableReadsAndWrites() {
+    AutoCloseable reads = disableReads();
+    AutoCloseable writes = disableWrites();
+    return () -> {
+      reads.close();
+      writes.close();
+    };
+  }
+
+  class Change implements IndexOperations {
+    @Inject private ChangeIndexCollection indices;
+
+    @Override
+    public AutoCloseable disableReads() {
+      ChangeIndex maybeDisabledSearchIndex = indices.getSearchIndex();
+      if (!(maybeDisabledSearchIndex instanceof DisabledChangeIndex)) {
+        indices.setSearchIndex(
+            new DisabledChangeIndex(maybeDisabledSearchIndex), /* closeOld */ false);
+      }
+
+      return () -> {
+        ChangeIndex maybeEnabledSearchIndex = indices.getSearchIndex();
+        if (maybeEnabledSearchIndex instanceof DisabledChangeIndex) {
+          indices.setSearchIndex(
+              ((DisabledChangeIndex) maybeEnabledSearchIndex).unwrap(), /* closeOld */ false);
+        }
+      };
+    }
+
+    @Override
+    public AutoCloseable disableWrites() {
+      for (ChangeIndex i : indices.getWriteIndexes()) {
+        if (!(i instanceof DisabledChangeIndex)) {
+          indices.addWriteIndex(new DisabledChangeIndex(i));
+        }
+      }
+      return () -> {
+        for (ChangeIndex i : indices.getWriteIndexes()) {
+          if (i instanceof DisabledChangeIndex) {
+            indices.addWriteIndex(((DisabledChangeIndex) i).unwrap());
+          }
+        }
+      };
+    }
+  }
+
+  class Account implements IndexOperations {
+    @Inject private AccountIndexCollection indices;
+
+    @Override
+    public AutoCloseable disableReads() {
+      AccountIndex maybeDisabledSearchIndex = indices.getSearchIndex();
+      if (!(maybeDisabledSearchIndex instanceof DisabledAccountIndex)) {
+        indices.setSearchIndex(
+            new DisabledAccountIndex(maybeDisabledSearchIndex), /* closeOld */ false);
+      }
+
+      return () -> {
+        AccountIndex maybeEnabledSearchIndex = indices.getSearchIndex();
+        if (maybeEnabledSearchIndex instanceof DisabledAccountIndex) {
+          indices.setSearchIndex(
+              ((DisabledAccountIndex) maybeEnabledSearchIndex).unwrap(), /* closeOld */ false);
+        }
+      };
+    }
+
+    @Override
+    public AutoCloseable disableWrites() {
+      for (AccountIndex i : indices.getWriteIndexes()) {
+        if (!(i instanceof DisabledAccountIndex)) {
+          indices.addWriteIndex(new DisabledAccountIndex(i));
+        }
+      }
+      return () -> {
+        for (AccountIndex i : indices.getWriteIndexes()) {
+          if (i instanceof DisabledAccountIndex) {
+            indices.addWriteIndex(((DisabledAccountIndex) i).unwrap());
+          }
+        }
+      };
+    }
+  }
+
+  class Project implements IndexOperations {
+    @Inject private ProjectIndexCollection indices;
+
+    @Override
+    public AutoCloseable disableReads() {
+      ProjectIndex maybeDisabledSearchIndex = indices.getSearchIndex();
+      if (!(maybeDisabledSearchIndex instanceof DisabledProjectIndex)) {
+        indices.setSearchIndex(
+            new DisabledProjectIndex(maybeDisabledSearchIndex), /* closeOld */ false);
+      }
+
+      return () -> {
+        ProjectIndex maybeEnabledSearchIndex = indices.getSearchIndex();
+        if (maybeEnabledSearchIndex instanceof DisabledProjectIndex) {
+          indices.setSearchIndex(
+              ((DisabledProjectIndex) maybeEnabledSearchIndex).unwrap(), /* closeOld */ false);
+        }
+      };
+    }
+
+    @Override
+    public AutoCloseable disableWrites() {
+      for (ProjectIndex i : indices.getWriteIndexes()) {
+        if (!(i instanceof DisabledProjectIndex)) {
+          indices.addWriteIndex(new DisabledProjectIndex(i));
+        }
+      }
+      return () -> {
+        for (ProjectIndex i : indices.getWriteIndexes()) {
+          if (i instanceof DisabledProjectIndex) {
+            indices.addWriteIndex(((DisabledProjectIndex) i).unwrap());
+          }
+        }
+      };
+    }
+  }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/change/PerPatchsetOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/change/PerPatchsetOperationsImpl.java
index eda6c7e..9b393ef 100644
--- a/java/com/google/gerrit/acceptance/testsuite/change/PerPatchsetOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/change/PerPatchsetOperationsImpl.java
@@ -38,7 +38,7 @@
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
-import java.sql.Timestamp;
+import java.time.Instant;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevWalk;
@@ -106,7 +106,7 @@
     try (Repository repository = repositoryManager.openRepository(project);
         ObjectInserter objectInserter = repository.newObjectInserter();
         RevWalk revWalk = new RevWalk(objectInserter.newReader())) {
-      Timestamp now = TimeUtil.nowTs();
+      Instant now = TimeUtil.now();
 
       IdentifiedUser author = getAuthor(commentCreation);
       CommentAdditionOp commentAdditionOp = new CommentAdditionOp(commentCreation);
@@ -165,8 +165,7 @@
       short side = commentCreation.side().orElse(CommentSide.PATCHSET_COMMIT).getNumericSide();
       Boolean unresolved = commentCreation.unresolved().orElse(null);
       String parentUuid = commentCreation.parentUuid().orElse(null);
-      Timestamp createdOn =
-          commentCreation.createdOn().map(Timestamp::from).orElse(context.getWhen());
+      Instant createdOn = commentCreation.createdOn().orElse(context.getWhen());
       HumanComment newComment =
           commentsUtil.newHumanComment(
               context.getNotes(),
@@ -202,7 +201,7 @@
     try (Repository repository = repositoryManager.openRepository(project);
         ObjectInserter objectInserter = repository.newObjectInserter();
         RevWalk revWalk = new RevWalk(objectInserter.newReader())) {
-      Timestamp now = TimeUtil.nowTs();
+      Instant now = TimeUtil.now();
 
       IdentifiedUser author = getAuthor(robotCommentCreation);
       RobotCommentAdditionOp robotCommentAdditionOp =
diff --git a/java/com/google/gerrit/acceptance/testsuite/change/TestChangeCreation.java b/java/com/google/gerrit/acceptance/testsuite/change/TestChangeCreation.java
index 5871e17..f01a138 100644
--- a/java/com/google/gerrit/acceptance/testsuite/change/TestChangeCreation.java
+++ b/java/com/google/gerrit/acceptance/testsuite/change/TestChangeCreation.java
@@ -16,6 +16,7 @@
 
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.gerrit.acceptance.testsuite.ThrowingFunction;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
@@ -34,6 +35,10 @@
 
   public abstract Optional<Account.Id> owner();
 
+  public abstract Optional<String> topic();
+
+  public abstract ImmutableMap<String, Short> approvals();
+
   public abstract String commitMessage();
 
   public abstract ImmutableList<TreeModification> treeModifications();
@@ -50,7 +55,8 @@
         .branch(Constants.R_HEADS + Constants.MASTER)
         .commitMessage("A test change")
         // Which value we choose here doesn't matter. All relevant code paths set the desired value.
-        .mergeStrategy(MergeStrategy.OURS);
+        .mergeStrategy(MergeStrategy.OURS)
+        .approvals(ImmutableMap.of());
   }
 
   @AutoValue.Builder
@@ -66,6 +72,15 @@
     /** The change owner. Must be an existing user account. */
     public abstract Builder owner(Account.Id owner);
 
+    /** The topic to add this change to. */
+    public abstract Builder topic(String topic);
+
+    /**
+     * The approvals to apply to this change. Map of label name to value. All approvals will be
+     * granted by the uploader.
+     */
+    public abstract Builder approvals(ImmutableMap<String, Short> approvals);
+
     /**
      * The commit message. The message may contain a {@code Change-Id} footer but does not need to.
      * If the footer is absent, it will be generated.
@@ -74,7 +89,18 @@
 
     /** Modified file of the change. The file content is specified via the returned builder. */
     public FileContentBuilder<Builder> file(String filePath) {
-      return new FileContentBuilder<>(this, filePath, treeModificationsBuilder()::add);
+      return new FileContentBuilder<>(this, filePath, 0, treeModificationsBuilder()::add);
+    }
+
+    /**
+     * Modified file of the change. The file content is specified via the returned builder. The
+     * second parameter indicates the git file mode for the modified file if it has been changed.
+     *
+     * @see org.eclipse.jgit.lib.FileMode
+     */
+    public FileContentBuilder<Builder> file(String filePath, int newGitFileMode) {
+      return new FileContentBuilder<>(
+          this, filePath, newGitFileMode, treeModificationsBuilder()::add);
     }
 
     abstract ImmutableList.Builder<TreeModification> treeModificationsBuilder();
diff --git a/java/com/google/gerrit/acceptance/testsuite/change/TestPatchsetCreation.java b/java/com/google/gerrit/acceptance/testsuite/change/TestPatchsetCreation.java
index fe9d909..22a4da6 100644
--- a/java/com/google/gerrit/acceptance/testsuite/change/TestPatchsetCreation.java
+++ b/java/com/google/gerrit/acceptance/testsuite/change/TestPatchsetCreation.java
@@ -45,7 +45,13 @@
 
     /** Modified file of the patchset. The file content is specified via the returned builder. */
     public FileContentBuilder<Builder> file(String filePath) {
-      return new FileContentBuilder<>(this, filePath, treeModificationsBuilder()::add);
+      return new FileContentBuilder<>(this, filePath, 0, treeModificationsBuilder()::add);
+    }
+
+    /** Modified file of the patchset. The file content is specified via the returned builder. */
+    public FileContentBuilder<Builder> file(String filePath, int newGitFileMode) {
+      return new FileContentBuilder<>(
+          this, filePath, newGitFileMode, treeModificationsBuilder()::add);
     }
 
     abstract ImmutableList.Builder<TreeModification> treeModificationsBuilder();
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java b/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java
index c885353..0b21e2c 100644
--- a/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java
+++ b/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java
@@ -18,7 +18,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AccountGroup;
-import java.sql.Timestamp;
+import java.time.Instant;
 import java.util.Optional;
 
 @AutoValue
@@ -40,7 +40,7 @@
 
   public abstract boolean visibleToAll();
 
-  public abstract Timestamp createdOn();
+  public abstract Instant createdOn();
 
   public abstract ImmutableSet<Account.Id> members();
 
@@ -67,7 +67,7 @@
 
     public abstract Builder visibleToAll(boolean visibleToAll);
 
-    public abstract Builder createdOn(Timestamp createdOn);
+    public abstract Builder createdOn(Instant createdOn);
 
     public abstract Builder members(ImmutableSet<Account.Id> members);
 
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/BUILD b/java/com/google/gerrit/acceptance/testsuite/project/BUILD
index f9e2fb5..d34b79a 100644
--- a/java/com/google/gerrit/acceptance/testsuite/project/BUILD
+++ b/java/com/google/gerrit/acceptance/testsuite/project/BUILD
@@ -8,6 +8,7 @@
     visibility = ["//visibility:public"],
     deps = [
         "//java/com/google/gerrit/acceptance:function",
+        "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/extensions:api",
@@ -17,7 +18,7 @@
         "//lib:jgit-junit",
         "//lib/auto:auto-value",
         "//lib/auto:auto-value-annotations",
-        "//lib/commons:lang",
+        "//lib/commons:lang3",
         "//lib/guice",
     ],
 )
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
index 16dca66..69139ce 100644
--- a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.TestCapability;
 import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.TestLabelPermission;
 import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.TestPermission;
+import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.AccessSection;
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.GroupReference;
@@ -43,7 +44,7 @@
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.ArrayList;
-import org.apache.commons.lang.RandomStringUtils;
+import org.apache.commons.lang3.RandomStringUtils;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.Config;
@@ -147,7 +148,7 @@
         setExclusiveGroupPermissions(projectConfig, projectUpdate.exclusiveGroupPermissions());
         projectConfig.commit(metaDataUpdate);
       }
-      projectCache.evict(nameKey);
+      projectCache.evictAndReindex(nameKey);
     }
 
     private void removePermissions(
@@ -196,8 +197,13 @@
         PermissionRule.Builder rule = newRule(projectConfig, p.group());
         rule.setAction(p.action());
         rule.setRange(p.min(), p.max());
-        String permissionName =
-            p.impersonation() ? Permission.forLabelAs(p.name()) : Permission.forLabel(p.name());
+        String permissionName;
+        if (p.isAddPermission()) {
+          permissionName =
+              p.impersonation() ? Permission.forLabelAs(p.name()) : Permission.forLabel(p.name());
+        } else {
+          permissionName = Permission.forRemoveLabel(p.name());
+        }
         projectConfig.upsertAccessSection(
             p.ref(), as -> as.upsertPermission(permissionName).add(rule));
       }
@@ -213,6 +219,7 @@
                   as -> as.upsertPermission(key.name()).setExclusiveGroup(exclusive)));
     }
 
+    @Nullable
     private RevCommit headOrNull(String branch) {
       branch = RefNames.fullName(branch);
 
@@ -292,7 +299,7 @@
 
         setConfig(projectConfig);
         try {
-          projectCache.evict(nameKey);
+          projectCache.evictAndReindex(nameKey);
         } catch (Exception e) {
           // Evicting the project from the cache, also triggers a reindex of the project.
           // The reindex step fails if the project config is invalid. That's fine, since it was our
@@ -310,7 +317,7 @@
         testProjectInvalidation.projectConfigUpdater().forEach(c -> c.accept(projectConfig));
         setConfig(projectConfig);
         try {
-          projectCache.evict(nameKey);
+          projectCache.evictAndReindex(nameKey);
         } catch (Exception e) {
           // Evicting the project from the cache, also triggers a reindex of the project.
           // The reindex step fails if the project config is invalid. That's fine, since it was our
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdate.java b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdate.java
index 9a9a21a..5634c78 100644
--- a/java/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdate.java
+++ b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdate.java
@@ -162,12 +162,34 @@
 
   /** Starts a builder for allowing a label permission. */
   public static TestLabelPermission.Builder allowLabel(String name) {
-    return TestLabelPermission.builder().name(name).action(PermissionRule.Action.ALLOW);
+    return TestLabelPermission.builder()
+        .name(name)
+        .isAddPermission(true)
+        .action(PermissionRule.Action.ALLOW);
   }
 
   /** Starts a builder for denying a label permission. */
   public static TestLabelPermission.Builder blockLabel(String name) {
-    return TestLabelPermission.builder().name(name).action(PermissionRule.Action.BLOCK);
+    return TestLabelPermission.builder()
+        .name(name)
+        .isAddPermission(true)
+        .action(PermissionRule.Action.BLOCK);
+  }
+
+  /** Starts a builder for allowing a remove-label permission. */
+  public static TestLabelPermission.Builder allowLabelRemoval(String name) {
+    return TestLabelPermission.builder()
+        .name(name)
+        .isAddPermission(false)