Merge "Add slice to the change indexing task description" into stable-3.5
diff --git a/Documentation/intro-user.txt b/Documentation/intro-user.txt
index 3f23385..1f0dfd0 100644
--- a/Documentation/intro-user.txt
+++ b/Documentation/intro-user.txt
@@ -874,8 +874,8 @@
 
 - [[show-change-number]]`Show Change Number In Changes Table`:
 +
-Whether in change lists and dashboards an `ID` column with the numeric
-change IDs should be shown.
+Whether in change lists and dashboards an `ID` column with the change numbers
+should be shown.
 
 - [[mute-common-path-prefixes]]`Mute Common Path Prefixes In File List`:
 +
diff --git a/Documentation/licenses.txt b/Documentation/licenses.txt
index 685e73b..735553d 100644
--- a/Documentation/licenses.txt
+++ b/Documentation/licenses.txt
@@ -2331,6 +2331,7 @@
 * jgit-archive
 * jgit-servlet
 * jgit-ssh-apache
+* jgit-ssh-jsch
 
 [[jgit_license]]
 ----
diff --git a/Documentation/pg-plugin-dev.txt b/Documentation/pg-plugin-dev.txt
index dc7986f..dc65da6 100644
--- a/Documentation/pg-plugin-dev.txt
+++ b/Documentation/pg-plugin-dev.txt
@@ -34,6 +34,33 @@
 link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/app/samples/[polygerrit-ui/app/samples/]
 directory of the source tree.
 
+== TypeScript API ==
+
+Gerrit provides a TypeScript plugin API.
+
+For a plugin built inline, its `tsconfig.json` can extends Gerrit plugin
+TypeScript configuration:
+
+`tsconfig.json`:
+``` json
+{
+  "extends": "../tsconfig-plugins-base.json"
+}
+```
+
+For standalone plugins (outside of a Gerrit tree), a TypeScript plugin API is
+published:
+link:https://www.npmjs.com/package/@gerritcodereview/typescript-api[@gerritcodereview/typescript-api].
+It provides a TypeScript configuration `tsconfig-plugins-base.json` which can
+be used in your plugin `tsconfig.json`:
+
+``` json
+{
+  "extends": "node_modules/@gerritcodereview/typescript-api/tsconfig-plugins-base.json",
+  // your custom configuration and overrides
+}
+```
+
 [[low-level-api-concepts]]
 == Low-level DOM API concepts
 
diff --git a/Documentation/pgm-init.txt b/Documentation/pgm-init.txt
index f6c3c85..4a758c3 100644
--- a/Documentation/pgm-init.txt
+++ b/Documentation/pgm-init.txt
@@ -20,6 +20,7 @@
   [--dev]
   [--skip-all-downloads]
   [--skip-download=<LIBRARY_NAME>]
+  [--reindex-threads=<N>]
 --
 
 == DESCRIPTION
@@ -99,6 +100,14 @@
 	The administrator must manually install the required library in the `lib/`
 	folder.
 
+--show-cache-stats::
+	Show cache statistics at the end of program.
+
+--reindex-threads::
+	Number of threads to use for reindex after init. Defaults to 1. Can be
+	set to -1 to skip reindex after init. Skipping reindex will also not
+	automatically start the daemon.
+
 == CONTEXT
 This command can only be run on a server which has direct local access to the
 managed Git repositories.
diff --git a/Documentation/pgm-reindex.txt b/Documentation/pgm-reindex.txt
index 0653d8d..b74829d 100644
--- a/Documentation/pgm-reindex.txt
+++ b/Documentation/pgm-reindex.txt
@@ -36,9 +36,8 @@
 	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.
+--show-cache-stats::
+	Show cache statistics at the end of program.
 
 == CONTEXT
 The secondary index must be enabled. See
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index da3018b..5df78cc 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -6260,16 +6260,25 @@
 [[change-id]]
 === \{change-id\}
 Identifier that uniquely identifies one change. It contains the URL-encoded
-project name as well as the change number: "'$$<project>~<numericId>$$'"
+project name as well as the change number: "'$$<project>~<changeNumber>$$'"
 
-Gerrit also supports the following identifiers:
+==== Alternative identifiers
+Gerrit also supports an array of other change identifiers.
+
+[NOTE]
+Even though these identifiers will work in the majority of cases it is highly
+recommended to use "'$$<project>~<changeNumber>$$'" whenever possible.
+Since these identifiers require additional lookups from index and caches, to
+be translated to the "'$$<project>~<changeNumber>$$'" identifier, they
+may result in both false-positives and false-negatives.
+Furthermore the additional lookup mean that they come with a performance penalty.
 
 * an ID of the change in the format "'$$<project>~<branch>~<Change-Id>$$'",
   where for the branch the `refs/heads/` prefix can be omitted
   ("$$myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940$$")
 * a Change-Id if it uniquely identifies one change
   ("I8473b95934b5732ac55d26311a706c9c2bde9940")
-* a numeric change ID ("4247")
+* a change number if it uniquely identifies one change ("4247")
 
 [[change-message-id]]
 === \{change-message-id\}
@@ -6573,8 +6582,8 @@
 Number of unresolved inline comment threads across all patch sets. Not set if
 the current change index doesn't have the data.
 |`_number`            ||
-The numeric ID of the change. (The underscore is just a relict of a prior
-attempt to deprecate the numeric ID.)
+The change number. (The underscore is just a relict of a prior
+attempt to deprecate the change number.)
 |`owner`              ||
 The owner of the change as an link:rest-api-accounts.html#account-info[
 AccountInfo] entity.
@@ -6660,15 +6669,15 @@
 |`has_review_started` |optional, not set if `false`|
 When present, change has been marked Ready at some point in time.
 |`revert_of`          |optional|
-The numeric Change-Id of the change that this change reverts.
+The change number of the change that this change reverts.
 |`submission_id`      |optional|
 ID of the submission of this change. Only set if the status is `MERGED`.
-This ID is equal to the numeric ID of the change that triggered the submission.
-If the change that triggered the submission also has a topic, it will be
-"<id>-<topic>" of the change that triggered the submission.
+This ID is equal to the change number of the change that triggered the
+submission. If the change that triggered the submission also has a topic,
+it will be "<id>-<topic>" of the change that triggered the submission.
 The callers must not rely on the format of the submission ID.
 |`cherry_pick_of_change`   |optional|
-The numeric Change-Id of the change that this change was cherry-picked from.
+The change number of the change that this change was cherry-picked from.
 Only set if the cherry-pick has been done through the Gerrit REST API (and
 not if a cherry-picked commit was pushed).
 |`cherry_pick_of_patch_set`|optional|
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index bd93b8b..4056816 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -1869,7 +1869,7 @@
 
 [[index-changes-input]]
 === IndexChangesInput
-The `IndexChangesInput` contains a list of numerical changes IDs to index.
+The `IndexChangesInput` contains a list of change numbers of changes to index.
 
 [options="header",cols="1,^2,4"]
 |================================
diff --git a/Documentation/user-notify.txt b/Documentation/user-notify.txt
index 128bae6..0e658c7 100644
--- a/Documentation/user-notify.txt
+++ b/Documentation/user-notify.txt
@@ -190,7 +190,7 @@
 
 [[Gerrit-Change-Number]]Gerrit-Change-Number::
 
-The change number footer states the numeric ID of the change, for
+The change number footer states the change number of the change, for
 example `92191`.
 
 [[Gerrit-PatchSet]]Gerrit-PatchSet::
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index f07a504..ccc83eb 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -28,13 +28,13 @@
 [options="header"]
 |=============================================================
 |Description                      | Examples
-|Legacy numerical id              | 15183
+|Change Id                        | 15183
 |Full or abbreviated Change-Id    | Ic0ff33
 |Full or abbreviated commit SHA-1 | d81b32ef
 |Email address                    | user@example.com
 |=============================================================
 
-For change searches (i.e. those using a numerical id, Change-Id, or commit
+For change searches (i.e. those using a change number, Change-Id, or commit
 SHA-1), if the search results in a single change that change will be
 presented instead of a list.
 
@@ -107,15 +107,13 @@
 [[change]]
 change:'ID'::
 +
-Either a legacy numerical 'ID' such as 15183, or a newer style
-Change-Id that was scraped out of the commit message.
+Either a change number such as 15183, or a Change-Id from the Change-Id footer.
 
 [[conflicts]]
 conflicts:'ID'::
 +
 Changes that conflict with change 'ID'. Change 'ID' can be specified
-as a legacy numerical 'ID' such as 15183, or a newer style Change-Id
-that was scraped out of the commit message.
+as a change number such as 15183, or a Change-Id from the Change-Id footer.
 
 [[destination]]
 destination:'[name=]NAME[,user=USER]'::
@@ -173,7 +171,7 @@
 [[revertof]]
 revertof:'ID'::
 +
-Changes that revert the change specified by the numeric 'ID'.
+Changes that revert the change specified by the change number.
 
 [[submissionid]]
 submissionid:'ID'::
@@ -206,8 +204,8 @@
 [[parentof]]
 parentof:'ID'::
 Changes which are parent to the change specified by 'ID'. Change 'ID' can be
-specified as a legacy numerical 'ID' such as 15183, or a Change-Id that can be
-picked from the commit message. This operator will return immediate parents
+specified as a change number such as 15183, or a Change-Id from the 'Change-Id'
+footer of the commit message. This operator will return immediate parents
 and will not return grand parents or higher level ancestors of the given change.
 
 [[parentproject]]
diff --git a/java/com/google/gerrit/index/query/AndSource.java b/java/com/google/gerrit/index/query/AndSource.java
index f7feaa0..ebd115b 100644
--- a/java/com/google/gerrit/index/query/AndSource.java
+++ b/java/com/google/gerrit/index/query/AndSource.java
@@ -62,7 +62,7 @@
     int minCardinality = Integer.MAX_VALUE;
     for (Predicate<T> p : getChildren()) {
       if (p instanceof DataSource) {
-        DataSource<T> source = (DataSource<T>) p;
+        DataSource<?> source = (DataSource<?>) p;
         int cardinality = source.getCardinality();
         c = Math.min(c, source.getCardinality());
 
diff --git a/java/com/google/gerrit/pgm/Init.java b/java/com/google/gerrit/pgm/Init.java
index 4c7b47b..c05bff5 100644
--- a/java/com/google/gerrit/pgm/Init.java
+++ b/java/com/google/gerrit/pgm/Init.java
@@ -95,6 +95,9 @@
   @Option(name = "--reindex-threads", usage = "Number of threads to use for reindex after init")
   private int reindexThreads = 1;
 
+  @Option(name = "--show-cache-stats", usage = "Show cache statistics at the end")
+  private boolean showCacheStats;
+
   @Inject Browser browser;
 
   private GerritIndexStatus indexStatus;
@@ -160,14 +163,14 @@
         });
     modules.add(new GerritServerConfigModule());
     Guice.createInjector(modules).injectMembers(this);
-    if (!ReplicaUtil.isReplica(run.flags.cfg)) {
+    if (reindexThreads != -1 && !ReplicaUtil.isReplica(run.flags.cfg)) {
       List<String> indicesToReindex = new ArrayList<>();
       for (SchemaDefinitions<?> schemaDef : schemaDefs) {
         if (!indexStatus.exists(schemaDef.getName())) {
           indicesToReindex.add(schemaDef.getName());
         }
       }
-      reindex(indicesToReindex, run.flags.isNew);
+      reindex(indicesToReindex);
     }
     start(run);
   }
@@ -223,7 +226,7 @@
   }
 
   void start(SiteRun run) throws Exception {
-    if (run.flags.autoStart) {
+    if (reindexThreads != -1 && run.flags.autoStart) {
       if (HostPlatform.isWin32()) {
         System.err.println("Automatic startup not supported on Win32.");
       } else {
@@ -280,7 +283,7 @@
     }
   }
 
-  private void reindex(List<String> indices, boolean isNewSite) throws Exception {
+  private void reindex(List<String> indices) throws Exception {
     if (indices.isEmpty()) {
       return;
     }
@@ -291,8 +294,8 @@
       reindexArgs.add("--index");
       reindexArgs.add(index);
     }
-    if (isNewSite) {
-      reindexArgs.add("--disable-cache-stats");
+    if (showCacheStats) {
+      reindexArgs.add("--show-cache-stats");
     }
 
     getConsoleUI()
diff --git a/java/com/google/gerrit/pgm/Reindex.java b/java/com/google/gerrit/pgm/Reindex.java
index c4e185d..7ee799f 100644
--- a/java/com/google/gerrit/pgm/Reindex.java
+++ b/java/com/google/gerrit/pgm/Reindex.java
@@ -86,12 +86,8 @@
   @Option(name = "--index", usage = "Only reindex specified indices")
   private List<String> indices = new ArrayList<>();
 
-  @Option(
-      name = "--disable-cache-stats",
-      usage =
-          "Disables printing the cache statistics."
-              + "Defaults to true when reindex is run from init on a new site, false otherwise")
-  private boolean disableCacheStats;
+  @Option(name = "--show-cache-stats", usage = "Show cache statistics at the end.")
+  private boolean showCacheStats;
 
   private Injector dbInjector;
   private Injector sysInjector;
@@ -123,7 +119,7 @@
 
     try {
       boolean ok = list ? list() : reindex();
-      if (!disableCacheStats) {
+      if (showCacheStats) {
         printCacheStats();
       }
       return ok ? 0 : 1;
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 9a59f46..6be072a 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -1018,91 +1018,112 @@
             Strings.nullToEmpty(magicBranchCmd.getMessage()));
         return;
       }
+      try {
+        retryHelper
+            .changeUpdate(
+                "insertChangesAndPatchSets",
+                updateFactory -> {
+                  try (BatchUpdate bu =
+                          updateFactory.create(
+                              project.getNameKey(), user.materializedCopy(), TimeUtil.nowTs());
+                      ObjectInserter ins = repo.newObjectInserter();
+                      ObjectReader reader = ins.newReader();
+                      RevWalk rw = new RevWalk(reader)) {
+                    bu.setRepository(repo, rw, ins);
+                    bu.setRefLogMessage("push");
+                    if (magicBranch != null) {
+                      bu.setNotify(magicBranch.getNotifyForNewChange());
+                    }
 
-      try (BatchUpdate bu =
-              batchUpdateFactory.create(
-                  project.getNameKey(), user.materializedCopy(), TimeUtil.nowTs());
-          ObjectInserter ins = repo.newObjectInserter();
-          ObjectReader reader = ins.newReader();
-          RevWalk rw = new RevWalk(reader)) {
-        bu.setRepository(repo, rw, ins);
-        bu.setRefLogMessage("push");
-        if (magicBranch != null) {
-          bu.setNotify(magicBranch.getNotifyForNewChange());
-        }
+                    logger.atFine().log("Adding %d replace requests", newChanges.size());
+                    for (ReplaceRequest replace : replaceByChange.values()) {
+                      replace.addOps(bu, replaceProgress);
+                      if (magicBranch != null) {
+                        bu.setNotifyHandling(
+                            replace.ontoChange, magicBranch.getNotifyHandling(replace.notes));
+                        if (magicBranch.shouldPublishComments()) {
+                          bu.addOp(
+                              replace.notes.getChangeId(),
+                              publishCommentsOp.create(replace.psId, project.getNameKey()));
+                          Optional<ChangeNotes> changeNotes =
+                              getChangeNotes(replace.notes.getChangeId());
+                          if (!changeNotes.isPresent()) {
+                            // If not present, no need to update attention set here since this is a
+                            // new change.
+                            continue;
+                          }
+                          List<HumanComment> drafts =
+                              commentsUtil.draftByChangeAuthor(
+                                  changeNotes.get(), user.getAccountId());
+                          if (drafts.isEmpty()) {
+                            // If no comments, attention set shouldn't update since the user didn't
+                            // reply.
+                            continue;
+                          }
+                          replyAttentionSetUpdates.processAutomaticAttentionSetRulesOnReply(
+                              bu,
+                              changeNotes.get(),
+                              isReadyForReview(changeNotes.get()),
+                              user,
+                              drafts);
+                        }
+                      }
+                    }
 
-        logger.atFine().log("Adding %d replace requests", newChanges.size());
-        for (ReplaceRequest replace : replaceByChange.values()) {
-          replace.addOps(bu, replaceProgress);
-          if (magicBranch != null) {
-            bu.setNotifyHandling(replace.ontoChange, magicBranch.getNotifyHandling(replace.notes));
-            if (magicBranch.shouldPublishComments()) {
-              bu.addOp(
-                  replace.notes.getChangeId(),
-                  publishCommentsOp.create(replace.psId, project.getNameKey()));
-              Optional<ChangeNotes> changeNotes = getChangeNotes(replace.notes.getChangeId());
-              if (!changeNotes.isPresent()) {
-                // If not present, no need to update attention set here since this is a new change.
-                continue;
-              }
-              List<HumanComment> drafts =
-                  commentsUtil.draftByChangeAuthor(changeNotes.get(), user.getAccountId());
-              if (drafts.isEmpty()) {
-                // If no comments, attention set shouldn't update since the user didn't reply.
-                continue;
-              }
-              replyAttentionSetUpdates.processAutomaticAttentionSetRulesOnReply(
-                  bu, changeNotes.get(), isReadyForReview(changeNotes.get()), user, drafts);
-            }
-          }
-        }
+                    logger.atFine().log("Adding %d create requests", newChanges.size());
+                    for (CreateRequest create : newChanges) {
+                      create.addOps(bu);
+                    }
 
-        logger.atFine().log("Adding %d create requests", newChanges.size());
-        for (CreateRequest create : newChanges) {
-          create.addOps(bu);
-        }
+                    logger.atFine().log("Adding %d group update requests", newChanges.size());
+                    updateGroups.forEach(r -> r.addOps(bu));
 
-        logger.atFine().log("Adding %d group update requests", newChanges.size());
-        updateGroups.forEach(r -> r.addOps(bu));
+                    logger.atFine().log("Executing batch");
+                    try {
+                      bu.execute();
+                    } catch (UpdateException e) {
+                      throw asRestApiException(e);
+                    }
 
-        logger.atFine().log("Executing batch");
-        try {
-          bu.execute();
-        } catch (UpdateException e) {
-          throw asRestApiException(e);
-        }
+                    replaceByChange.values().stream()
+                        .forEach(
+                            req ->
+                                result.addChange(
+                                    ReceiveCommitsResult.ChangeStatus.REPLACED, req.ontoChange));
+                    newChanges.stream()
+                        .forEach(
+                            req ->
+                                result.addChange(
+                                    ReceiveCommitsResult.ChangeStatus.CREATED, req.changeId));
 
-        replaceByChange.values().stream()
-            .forEach(
-                req ->
-                    result.addChange(ReceiveCommitsResult.ChangeStatus.REPLACED, req.ontoChange));
-        newChanges.stream()
-            .forEach(
-                req -> result.addChange(ReceiveCommitsResult.ChangeStatus.CREATED, req.changeId));
-
-        if (magicBranchCmd != null) {
-          magicBranchCmd.setResult(OK);
-        }
-        for (ReplaceRequest replace : replaceByChange.values()) {
-          String rejectMessage = replace.getRejectMessage();
-          if (rejectMessage == null) {
-            if (replace.inputCommand.getResult() == NOT_ATTEMPTED) {
-              // Not necessarily the magic branch, so need to set OK on the original value.
-              replace.inputCommand.setResult(OK);
-            }
-          } else {
-            logger.atFine().log("Rejecting due to message from ReplaceOp");
-            reject(replace.inputCommand, rejectMessage);
-          }
-        }
-
+                    if (magicBranchCmd != null) {
+                      magicBranchCmd.setResult(OK);
+                    }
+                    for (ReplaceRequest replace : replaceByChange.values()) {
+                      String rejectMessage = replace.getRejectMessage();
+                      if (rejectMessage == null) {
+                        if (replace.inputCommand.getResult() == NOT_ATTEMPTED) {
+                          // Not necessarily the magic branch, so need to set OK on the original
+                          // value.
+                          replace.inputCommand.setResult(OK);
+                        }
+                      } else {
+                        logger.atFine().log("Rejecting due to message from ReplaceOp");
+                        reject(replace.inputCommand, rejectMessage);
+                      }
+                    }
+                  }
+                  return null;
+                })
+            .defaultTimeoutMultiplier(5)
+            .call();
       } catch (ResourceConflictException e) {
         addError(e.getMessage());
         reject(magicBranchCmd, "conflict");
       } catch (BadRequestException | UnprocessableEntityException | AuthException e) {
         logger.atFine().withCause(e).log("Rejecting due to client error");
         reject(magicBranchCmd, e.getMessage());
-      } catch (RestApiException | IOException e) {
+      } catch (RestApiException | UpdateException e) {
         throw new StorageException("Can't insert change/patch set for " + project.getName(), e);
       }
 
diff --git a/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java b/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
index 7d6b1ed..4a56cdf 100644
--- a/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
@@ -150,6 +150,7 @@
         return 50_000;
       case ABANDONED:
         return 50_000;
+      case NEW:
       default:
         return 2000;
     }
diff --git a/java/com/google/gerrit/sshd/BUILD b/java/com/google/gerrit/sshd/BUILD
index f3bd5e1..f679138 100644
--- a/java/com/google/gerrit/sshd/BUILD
+++ b/java/com/google/gerrit/sshd/BUILD
@@ -31,6 +31,7 @@
         "//lib:jgit",
         "//lib:jgit-archive",
         "//lib:jgit-ssh-apache",
+        "//lib:jgit-ssh-jsch",
         "//lib:servlet-api",
         "//lib/auto:auto-value",
         "//lib/auto:auto-value-annotations",
diff --git a/java/com/google/gerrit/sshd/SshSessionFactoryInitializer.java b/java/com/google/gerrit/sshd/SshSessionFactoryInitializer.java
index 1cdf923..d8e968a 100644
--- a/java/com/google/gerrit/sshd/SshSessionFactoryInitializer.java
+++ b/java/com/google/gerrit/sshd/SshSessionFactoryInitializer.java
@@ -18,6 +18,7 @@
 
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.transport.ssh.jsch.JschConfigSessionFactory;
 import org.eclipse.jgit.transport.sshd.DefaultProxyDataFactory;
 import org.eclipse.jgit.transport.sshd.JGitKeyCache;
 import org.eclipse.jgit.transport.sshd.SshdSessionFactory;
@@ -25,11 +26,16 @@
 
 public class SshSessionFactoryInitializer {
   public static void init(Config config) {
-    if (APACHE == config.getEnum("ssh", null, "clientImplementation", APACHE)) {
-      SshdSessionFactory factory =
-          new SshdSessionFactory(new JGitKeyCache(), new DefaultProxyDataFactory());
-      factory.setHomeDirectory(FS.DETECTED.userHome());
-      SshSessionFactory.setInstance(factory);
+    switch (config.getEnum("ssh", null, "clientImplementation", APACHE)) {
+      case APACHE:
+        SshdSessionFactory factory =
+            new SshdSessionFactory(new JGitKeyCache(), new DefaultProxyDataFactory());
+        factory.setHomeDirectory(FS.DETECTED.userHome());
+        SshSessionFactory.setInstance(factory);
+        break;
+
+      case JSCH:
+        SshSessionFactory.setInstance(new JschConfigSessionFactory());
     }
   }
 
diff --git a/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java b/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java
index 0a4f31c..8c25edf 100644
--- a/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java
+++ b/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java
@@ -130,7 +130,7 @@
     assertThat(out.getChild(0)).isEqualTo(query(firstIndexedSubQuery));
 
     assertThat(out.getChild(1).getClass()).isSameInstanceAs(OrPredicate.class);
-    OrPredicate indexedSubTree = (OrPredicate) out.getChild(1);
+    OrPredicate<ChangeData> indexedSubTree = (OrPredicate<ChangeData>) out.getChild(1);
 
     Predicate<ChangeData> secondIndexedSubQuery = parse("foo:a OR file:b");
     assertThat(indexedSubTree.getChildren())
diff --git a/modules/jgit b/modules/jgit
index 5bf7472..82b5aaf 160000
--- a/modules/jgit
+++ b/modules/jgit
@@ -1 +1 @@
-Subproject commit 5bf7472e0ca967893f9fdb653c3f1b22fb6d56e5
+Subproject commit 82b5aaf7e3e3f881056dd2d4486e02537b0493da
diff --git a/plugins/replication b/plugins/replication
index 590f9ba..e7f3522 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 590f9bac1b8f9add33b6a98fa22f7ef1e1d4cf6a
+Subproject commit e7f35226d0ad3ccb5751c468168409705ae8e719
diff --git a/polygerrit-ui/app/api/BUILD_for_publishing_api_only b/polygerrit-ui/app/api/BUILD_for_publishing_api_only
index 9d3029b..c1bb6bd 100644
--- a/polygerrit-ui/app/api/BUILD_for_publishing_api_only
+++ b/polygerrit-ui/app/api/BUILD_for_publishing_api_only
@@ -38,13 +38,18 @@
 # Use this rule for publishing the js plugin api as a package to the npm repo.
 pkg_npm(
     name = "js_plugin_api_npm_package",
+    package_name = "@gerritcodereview/typescript-api",
     srcs = glob(
         ["**/*"],
         exclude = [
             "BUILD",
+            "BUILD_for_publishing_api_only",
             "tsconfig.json",
             "publish.sh",
         ],
     ),
-    deps = [":js_plugin_api_compiled"],
+    deps = [
+        ":js_plugin_api_compiled",
+        "//plugins:tsconfig-plugins-base.json",
+    ],
 )
diff --git a/polygerrit-ui/app/api/publish.sh b/polygerrit-ui/app/api/publish.sh
index 16de4c9..1d6368c 100755
--- a/polygerrit-ui/app/api/publish.sh
+++ b/polygerrit-ui/app/api/publish.sh
@@ -7,19 +7,24 @@
 #
 # Adding the `--upload` argument will also publish the package.
 
+set -e
+
 bazel_bin=$(which bazelisk 2>/dev/null)
 if [[ -z "$bazel_bin" ]]; then
     echo "Warning: bazelisk is not installed; falling back to bazel."
     bazel_bin=bazel
 fi
 api_path=polygerrit-ui/app/api
+plugins_path=plugins
 
 function cleanup() {
   echo "Cleaning up ..."
   rm -f ${api_path}/BUILD
+  rm -f ${api_path}/tsconfig-plugins-base.json
 }
 trap cleanup EXIT
 cp ${api_path}/BUILD_for_publishing_api_only ${api_path}/BUILD
+cp ${plugins_path}/tsconfig-plugins-base.json ${api_path}/tsconfig-plugins-base.json
 
 ${bazel_bin} build //${api_path}:js_plugin_api_npm_package
 
diff --git a/polygerrit-ui/app/api/tsconfig.json b/polygerrit-ui/app/api/tsconfig.json
index 037e4f2..6960192 100644
--- a/polygerrit-ui/app/api/tsconfig.json
+++ b/polygerrit-ui/app/api/tsconfig.json
@@ -1,9 +1,9 @@
 {
   "extends": "../../../plugins/tsconfig-plugins-base.json",
   "compilerOptions": {
-    "rootDir": ".",
+    "rootDir": "."
   },
   "include": [
-    "**/*",
-  ],
+    "**/*"
+  ]
 }
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.ts b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.ts
index b623e8e..9ea9dc1 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.ts
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.ts
@@ -223,13 +223,15 @@
       </gr-endpoint-decorator>
       </div>
       <div class="accountContainer" id="accountContainer">
-        <iron-icon
-          id="mobileSearch"
-          icon="gr-icons:search"
-          on-click="_onMobileSearchTap"
-          role="button"
-          aria-label="[[_computeShowHideAriaLabel(mobileSearchHidden)]]"
-        ></iron-icon>
+        <div>
+          <iron-icon
+            id="mobileSearch"
+            icon="gr-icons:search"
+            on-click="_onMobileSearchTap"
+            role="button"
+            aria-label="[[_computeShowHideAriaLabel(mobileSearchHidden)]]"
+          ></iron-icon>
+        </div>
         <div
           class="registerDiv"
           hidden="[[_computeRegisterHidden(_registerURL)]]"
diff --git a/resources/com/google/gerrit/server/mime/mime-types.properties b/resources/com/google/gerrit/server/mime/mime-types.properties
index fd2280c..61606de 100644
--- a/resources/com/google/gerrit/server/mime/mime-types.properties
+++ b/resources/com/google/gerrit/server/mime/mime-types.properties
@@ -38,6 +38,7 @@
 cpp = text/x-c++src
 cql = text/x-cassandra
 cxx = text/x-c++src
+cu = text/x-c++src
 cyp = application/x-cypher-query
 cypher = application/x-cypher-query
 c++ = text/x-c++src