Merge "Update codemirror to 5.22.0"
diff --git a/.gitignore b/.gitignore
index 599089a..4ce6ab2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,7 @@
 /buck-out
 /eclipse-out
 /extras
+/infer-out
 /local.properties
 *.pyc
 /gwt-unitCache
diff --git a/BUILD b/BUILD
index b859642..d806bdb 100644
--- a/BUILD
+++ b/BUILD
@@ -1,5 +1,6 @@
 package(default_visibility = ["//visibility:public"])
 
+load("//tools/bzl:genrule2.bzl", "genrule2")
 load("//tools/bzl:pkg_war.bzl", "pkg_war")
 
 genrule(
@@ -42,3 +43,30 @@
     name = "withdocs",
     doc = True,
 )
+
+API_DEPS = [
+  "//gerrit-acceptance-framework:acceptance-framework_deploy.jar",
+  "//gerrit-acceptance-framework:liblib-src.jar",
+  "//gerrit-acceptance-framework:acceptance-framework-javadoc",
+  "//gerrit-extension-api:extension-api_deploy.jar",
+  "//gerrit-extension-api:libapi-src.jar",
+  "//gerrit-extension-api:extension-api-javadoc",
+  "//gerrit-plugin-api:plugin-api_deploy.jar",
+  "//gerrit-plugin-api:plugin-api-sources_deploy.jar",
+  "//gerrit-plugin-api:plugin-api-javadoc",
+  "//gerrit-plugin-gwtui:gwtui-api_deploy.jar",
+  "//gerrit-plugin-gwtui:gwtui-api-source_deploy.jar",
+  "//gerrit-plugin-gwtui:gwtui-api-javadoc",
+]
+
+genrule2(
+    name = 'api',
+    srcs = API_DEPS,
+    testonly = 1,
+    cmd = ' && '.join([
+        'cp $(SRCS) $$TMP',
+        'cd $$TMP',
+        'zip -qr $$ROOT/$@ .',
+    ]),
+    outs = ['api.zip'],
+)
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index b33901f..a881e3d 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -74,16 +74,14 @@
 To build the extension, plugin and GWT API JAR files:
 
 ----
-  bazel build gerrit-plugin-api:plugin-api_deploy.jar
-  bazel build gerrit-extension-api:extension-api_deploy.jar
+  bazel build api
 ----
 
-Java binaries, Java sources and Java docs are generated into corresponding
-project directories, here as example for plugin API:
+The output archive that contains Java binaries, Java sources and
+Java docs will be placed in:
 
 ----
-  bazel-bin/gerrit-plugin-api/plugin-api_deploy.jar
-  bazel-bin/gerrit-extension-api/extension-api_deploy.jar
+  bazel-genfiles/api.zip
 ----
 
 Install {extension,plugin,gwt}-api to the local maven repository:
diff --git a/WORKSPACE b/WORKSPACE
index e8d3a2e..0d0f414 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -185,9 +185,9 @@
 
 maven_jar(
     name = "gwtorm_client",
-    artifact = "com.google.gerrit:gwtorm:1.16",
-    sha1 = "3e41b6d7bb352fa0539ce23b9bce97cf8c26c3bf",
-    src_sha1 = "f45b7bacc79a0e5a7f6cf799a2dba23cc5bca19b",
+    artifact = "com.google.gerrit:gwtorm:1.17",
+    sha1 = "97bdc872f00388910c9af70771f07bbb32f1b949",
+    src_sha1 = "889e35d7295b1af49161a28daaea9905ffa76a63",
 )
 
 maven_jar(
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
index 7b73505..4b57132 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
@@ -30,6 +30,7 @@
 import com.google.gerrit.testutil.FakeEmailSender;
 import com.google.gerrit.testutil.NoteDbChecker;
 import com.google.gerrit.testutil.NoteDbMode;
+import com.google.gerrit.testutil.SshMode;
 import com.google.gerrit.testutil.TempFileUtil;
 import com.google.inject.Injector;
 import com.google.inject.Key;
@@ -136,6 +137,7 @@
       }
     }, Paths.get(baseConfig.getString("gerrit", null, "tempSiteDir")));
     daemon.setEmailModuleForTesting(new FakeEmailSender.Module());
+    daemon.setEnableSshd(SshMode.useSsh());
 
     final File site;
     ExecutorService daemonService = null;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SshPushForReviewIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SshPushForReviewIT.java
index ecb0be4..ede1d4f 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SshPushForReviewIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SshPushForReviewIT.java
@@ -15,8 +15,8 @@
 package com.google.gerrit.acceptance.git;
 
 import com.google.gerrit.acceptance.NoHttpd;
-
 import com.google.gerrit.acceptance.UseSsh;
+
 import org.junit.Before;
 
 @NoHttpd
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BanCommitIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BanCommitIT.java
index 6ea5976..f786bba 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BanCommitIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BanCommitIT.java
@@ -21,8 +21,8 @@
 
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.NoHttpd;
-
 import com.google.gerrit.acceptance.UseSsh;
+
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.junit.Test;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index a0ab714..c024a67 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -181,6 +181,11 @@
     this.serverStarted = serverStarted;
   }
 
+  @VisibleForTesting
+  public void setEnableSshd(boolean enable) {
+    sshd = enable;
+  }
+
   public void setEnableHttpd(boolean enable) {
     httpd = enable;
   }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/SwitchSecureStore.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/SwitchSecureStore.java
index ac84e82..280795a2 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/SwitchSecureStore.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/SwitchSecureStore.java
@@ -163,20 +163,21 @@
 
   private String getNewSecureStoreClassName(Path secureStore)
       throws IOException {
-    JarScanner scanner = new JarScanner(secureStore);
-    List<String> newSecureStores =
-        scanner.findSubClassesOf(SecureStore.class);
-    if (newSecureStores.isEmpty()) {
-      throw new RuntimeException(String.format(
-          "Cannot find implementation of SecureStore interface in %s",
-          secureStore.toAbsolutePath()));
+    try (JarScanner scanner = new JarScanner(secureStore)) {
+      List<String> newSecureStores =
+          scanner.findSubClassesOf(SecureStore.class);
+      if (newSecureStores.isEmpty()) {
+        throw new RuntimeException(String.format(
+            "Cannot find implementation of SecureStore interface in %s",
+            secureStore.toAbsolutePath()));
+      }
+      if (newSecureStores.size() > 1) {
+        throw new RuntimeException(String.format(
+            "Found too many implementations of SecureStore:\n%s\nin %s", Joiner
+                .on("\n").join(newSecureStores), secureStore.toAbsolutePath()));
+      }
+      return Iterables.getOnlyElement(newSecureStores);
     }
-    if (newSecureStores.size() > 1) {
-      throw new RuntimeException(String.format(
-          "Found too many implementations of SecureStore:\n%s\nin %s", Joiner
-              .on("\n").join(newSecureStores), secureStore.toAbsolutePath()));
-    }
-    return Iterables.getOnlyElement(newSecureStores);
   }
 
   private String getCurrentSecureStoreClassName(SitePaths sitePaths) {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java
index 8ccdebc..1668387 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java
@@ -310,13 +310,12 @@
       return null;
     }
 
-    try {
-      Path secureStoreLib = Paths.get(secureStore);
-      if (!Files.exists(secureStoreLib)) {
-        throw new InvalidSecureStoreException(String.format(
-            "File %s doesn't exist", secureStore));
-      }
-      JarScanner scanner = new JarScanner(secureStoreLib);
+    Path secureStoreLib = Paths.get(secureStore);
+    if (!Files.exists(secureStoreLib)) {
+      throw new InvalidSecureStoreException(String.format(
+          "File %s doesn't exist", secureStore));
+    }
+    try (JarScanner scanner = new JarScanner(secureStoreLib)) {
       List<String> secureStores =
           scanner.findSubClassesOf(SecureStore.class);
       if (secureStores.isEmpty()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/CommentsUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/CommentsUtil.java
index 5d160d9..7349519 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/CommentsUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/CommentsUtil.java
@@ -16,7 +16,6 @@
 
 import static com.google.common.base.MoreObjects.firstNonNull;
 import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage.REVIEW_DB;
 import static java.util.stream.Collectors.toList;
 
 import com.google.common.collect.ComparisonChain;
@@ -44,7 +43,6 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
 import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
@@ -370,11 +368,8 @@
     for (Comment c : comments) {
       update.deleteComment(c);
     }
-    if (PrimaryStorage.of(update.getChange()) == REVIEW_DB) {
-      // Avoid OrmConcurrencyException trying to delete non-existent entities.
-      db.patchComments().delete(toPatchLineComments(update.getId(),
-          PatchLineComment.Status.DRAFT, comments));
-    }
+    db.patchComments().delete(toPatchLineComments(update.getId(),
+        PatchLineComment.Status.DRAFT, comments));
   }
 
   public void deleteAllDraftsFromAllUsers(Change.Id changeId)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/PatchSetUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/PatchSetUtil.java
index ee40cd4..fdc1feb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/PatchSetUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/PatchSetUtil.java
@@ -16,7 +16,6 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage.REVIEW_DB;
 import static com.google.gerrit.server.notedb.PatchSetState.DRAFT;
 import static com.google.gerrit.server.notedb.PatchSetState.PUBLISHED;
 
@@ -28,7 +27,6 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
 import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gerrit.server.notedb.PatchSetState;
 import com.google.gwtorm.server.OrmException;
@@ -130,10 +128,7 @@
     checkArgument(ps.isDraft(),
         "cannot delete non-draft patch set %s", ps.getId());
     update.setPatchSetState(PatchSetState.DELETED);
-    if (PrimaryStorage.of(update.getChange()) == REVIEW_DB) {
-      // Avoid OrmConcurrencyException trying to delete non-existent entities.
-      db.patchSets().delete(Collections.singleton(ps));
-    }
+    db.patchSets().delete(Collections.singleton(ps));
   }
 
   private void ensurePatchSetMatches(PatchSet.Id psId, ChangeUpdate update) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChangeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChangeOp.java
index afec66a..d1f7cac 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChangeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChangeOp.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.change;
 
 import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage.REVIEW_DB;
 
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -33,7 +32,6 @@
 import com.google.gerrit.server.git.BatchUpdate.ChangeContext;
 import com.google.gerrit.server.git.BatchUpdate.RepoContext;
 import com.google.gerrit.server.git.BatchUpdateReviewDb;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -155,10 +153,6 @@
 
   private void deleteChangeElementsFromDb(ChangeContext ctx, Change.Id id)
       throws OrmException {
-    if (PrimaryStorage.of(ctx.getChange()) != REVIEW_DB) {
-      return;
-    }
-    // Avoid OrmConcurrencyException trying to delete non-existent entities.
     // Only delete from ReviewDb here; deletion from NoteDb is handled in
     // BatchUpdate.
     ReviewDb db = unwrap(ctx.getDb());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
index 79cc35b..e473e39 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
@@ -14,8 +14,6 @@
 
 package com.google.gerrit.server.change;
 
-import static com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage.REVIEW_DB;
-
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -36,7 +34,6 @@
 import com.google.gerrit.server.git.BatchUpdate.ChangeContext;
 import com.google.gerrit.server.git.BatchUpdate.RepoContext;
 import com.google.gerrit.server.git.UpdateException;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
 import com.google.gerrit.server.patch.PatchSetInfoFactory;
 import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
 import com.google.gerrit.server.project.NoSuchChangeException;
@@ -149,14 +146,11 @@
       psUtil.delete(ctx.getDb(), ctx.getUpdate(patchSet.getId()), patchSet);
 
       accountPatchReviewStore.get().clearReviewed(psId);
-      if (PrimaryStorage.of(ctx.getChange()) == REVIEW_DB) {
-        // Avoid OrmConcurrencyException trying to delete non-existent entities.
-        // Use the unwrap from DeleteChangeOp to handle BatchUpdateReviewDb.
-        ReviewDb db = DeleteChangeOp.unwrap(ctx.getDb());
-        db.changeMessages().delete(db.changeMessages().byPatchSet(psId));
-        db.patchComments().delete(db.patchComments().byPatchSet(psId));
-        db.patchSetApprovals().delete(db.patchSetApprovals().byPatchSet(psId));
-      }
+      // Use the unwrap from DeleteChangeOp to handle BatchUpdateReviewDb.
+      ReviewDb db = DeleteChangeOp.unwrap(ctx.getDb());
+      db.changeMessages().delete(db.changeMessages().byPatchSet(psId));
+      db.patchComments().delete(db.patchComments().byPatchSet(psId));
+      db.patchSetApprovals().delete(db.patchSetApprovals().byPatchSet(psId));
     }
 
     private void deleteOrUpdateDraftChange(ChangeContext ctx)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
index 47f5901..0e3f089 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
@@ -14,8 +14,6 @@
 
 package com.google.gerrit.server.change;
 
-import static com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage.REVIEW_DB;
-
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.gerrit.common.TimeUtil;
@@ -185,10 +183,7 @@
       } else {
         msg.append(".");
       }
-      if (PrimaryStorage.of(ctx.getChange()) == REVIEW_DB) {
-        // Avoid OrmConcurrencyException trying to update non-existent entities.
-        ctx.getDb().patchSetApprovals().delete(del);
-      }
+      ctx.getDb().patchSetApprovals().delete(del);
       ChangeUpdate update = ctx.getUpdate(currPs.getId());
       update.removeReviewer(reviewerId);
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteVote.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteVote.java
index 951635e..43c3ac7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteVote.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteVote.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.change;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage.REVIEW_DB;
 
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.common.data.LabelTypes;
@@ -45,7 +44,6 @@
 import com.google.gerrit.server.git.UpdateException;
 import com.google.gerrit.server.mail.send.DeleteVoteSender;
 import com.google.gerrit.server.mail.send.ReplyToChangeSender;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.util.LabelVote;
 import com.google.gwtorm.server.OrmException;
@@ -171,11 +169,8 @@
       }
 
       ctx.getUpdate(psId).removeApprovalFor(accountId, label);
-      if (PrimaryStorage.of(ctx.getChange()) == REVIEW_DB) {
-        // Avoid OrmConcurrencyException trying to update non-existent entities.
-        ctx.getDb().patchSetApprovals().upsert(
-            Collections.singleton(deletedApproval(ctx)));
-      }
+      ctx.getDb().patchSetApprovals().upsert(
+          Collections.singleton(deletedApproval(ctx)));
 
       StringBuilder msg = new StringBuilder();
       msg.append("Removed ");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index a0a3296..973e97a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -17,7 +17,6 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
-import static com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage.REVIEW_DB;
 import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.stream.Collectors.joining;
@@ -92,7 +91,6 @@
 import com.google.gerrit.server.git.UpdateException;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
 import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gerrit.server.project.ChangeControl;
@@ -1095,11 +1093,8 @@
       }
 
       forceCallerAsReviewer(ctx, current, ups, del);
-      if (PrimaryStorage.of(update.getChange()) == REVIEW_DB) {
-        // Avoid OrmConcurrencyException trying to delete non-existent entities.
-        ctx.getDb().patchSetApprovals().delete(del);
-        ctx.getDb().patchSetApprovals().upsert(ups);
-      }
+      ctx.getDb().patchSetApprovals().delete(del);
+      ctx.getDb().patchSetApprovals().upsert(ups);
       return !del.isEmpty() || !ups.isEmpty();
     }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/BatchUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/BatchUpdate.java
index 32a7123..1b5c7fa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/BatchUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/BatchUpdate.java
@@ -1053,6 +1053,10 @@
           .create(ctx.getProject())
           .setChangeRepo(ctx.getRepository(), ctx.getRevWalk(), null,
               new ChainedReceiveCommands(repo));
+      if (ctx.getUser().isIdentifiedUser()) {
+        updateManager.setRefLogIdent(
+            ctx.getUser().asIdentifiedUser().newRefLogIdent(ctx.getWhen(), tz));
+      }
       for (ChangeUpdate u : ctx.updates.values()) {
         updateManager.add(u);
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModules.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModules.java
index 26c59c2..192691d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModules.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModules.java
@@ -65,35 +65,40 @@
     this.submissionId = orm.getSubmissionId();
     Project.NameKey project = branch.getParentKey();
     logDebug("Loading .gitmodules of {} for project {}", branch, project);
-    OpenRepo or;
+    OpenRepo or = null;
     try {
       or = orm.openRepo(project);
+      ObjectId id = or.repo.resolve(branch.get());
+      if (id == null) {
+        throw new IOException("Cannot open branch " + branch.get());
+      }
+      RevCommit commit = or.rw.parseCommit(id);
+
+      try (TreeWalk tw =
+          TreeWalk.forPath(or.repo, GIT_MODULES, commit.getTree())) {
+        if (tw == null
+            || (tw.getRawMode(0) & FileMode.TYPE_MASK) != FileMode.TYPE_FILE) {
+          subscriptions = Collections.emptySet();
+          logDebug("The .gitmodules file doesn't exist in " + branch);
+          return;
+        }
+      }
+      BlobBasedConfig bbc;
+      try {
+        bbc = new BlobBasedConfig(null, or.repo, commit, GIT_MODULES);
+      } catch (ConfigInvalidException e) {
+        throw new IOException("Could not read .gitmodules of super project: " +
+                branch.getParentKey(), e);
+      }
+      subscriptions = new SubmoduleSectionParser(bbc, canonicalWebUrl,
+            branch).parseAllSections();
     } catch (NoSuchProjectException e) {
       throw new IOException(e);
+    } finally {
+      if (or != null) {
+        or.close();
+      }
     }
-
-    ObjectId id = or.repo.resolve(branch.get());
-    if (id == null) {
-      throw new IOException("Cannot open branch " + branch.get());
-    }
-    RevCommit commit = or.rw.parseCommit(id);
-
-    TreeWalk tw = TreeWalk.forPath(or.repo, GIT_MODULES, commit.getTree());
-    if (tw == null
-        || (tw.getRawMode(0) & FileMode.TYPE_MASK) != FileMode.TYPE_FILE) {
-      subscriptions = Collections.emptySet();
-      logDebug("The .gitmodules file doesn't exist in " + branch);
-      return;
-    }
-    BlobBasedConfig bbc;
-    try {
-      bbc = new BlobBasedConfig(null, or.repo, commit, GIT_MODULES);
-    } catch (ConfigInvalidException e) {
-      throw new IOException("Could not read .gitmodules of super project: " +
-              branch.getParentKey(), e);
-    }
-    subscriptions = new SubmoduleSectionParser(bbc, canonicalWebUrl,
-          branch).parseAllSections();
   }
 
   public Collection<SubmoduleSubscription> subscribedTo(Branch.NameKey src) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
index f7752dc..053b8f1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
@@ -503,14 +503,15 @@
   }
 
   public List<PathInfo> getPathInfos(boolean recursive) throws IOException {
-    TreeWalk tw = new TreeWalk(reader);
-    tw.addTree(revision.getTree());
-    tw.setRecursive(recursive);
-    List<PathInfo> paths = new ArrayList<>();
-    while (tw.next()) {
-      paths.add(new PathInfo(tw));
+    try (TreeWalk tw = new TreeWalk(reader)) {
+      tw.addTree(revision.getTree());
+      tw.setRecursive(recursive);
+      List<PathInfo> paths = new ArrayList<>();
+      while (tw.next()) {
+        paths.add(new PathInfo(tw));
+      }
+      return paths;
     }
-    return paths;
   }
 
   protected static void set(Config rc, String section, String subsection,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
index d276f34..387ccfa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -607,6 +607,7 @@
         if (manager == null) {
           return super.openHandle(repo, oldId); // May be null in tests.
         }
+        manager.setRefLogMessage("Auto-rebuilding change");
         r = manager.stageAndApplyDelta(change);
         try {
           rebuilder.execute(db, cid, manager);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
index 2eaa459..b5adea6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
@@ -33,6 +33,7 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.ChainedReceiveCommands;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -41,6 +42,7 @@
 import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
 import com.google.gwtorm.server.OrmConcurrencyException;
 import com.google.gwtorm.server.OrmException;
+import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
 
@@ -49,6 +51,7 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevWalk;
@@ -175,6 +178,7 @@
     }
   }
 
+  private final Provider<PersonIdent> serverIdent;
   private final GitRepositoryManager repoManager;
   private final NotesMigration migration;
   private final AllUsersName allUsersName;
@@ -189,13 +193,17 @@
   private OpenRepo allUsersRepo;
   private Map<Change.Id, StagedResult> staged;
   private boolean checkExpectedState = true;
+  private String refLogMessage;
+  private PersonIdent refLogIdent;
 
   @AssistedInject
-  NoteDbUpdateManager(GitRepositoryManager repoManager,
+  NoteDbUpdateManager(@GerritPersonIdent Provider<PersonIdent> serverIdent,
+      GitRepositoryManager repoManager,
       NotesMigration migration,
       AllUsersName allUsersName,
       NoteDbMetrics metrics,
       @Assisted Project.NameKey projectName) {
+    this.serverIdent = serverIdent;
     this.repoManager = repoManager;
     this.migration = migration;
     this.allUsersName = allUsersName;
@@ -243,6 +251,16 @@
     return this;
   }
 
+  public NoteDbUpdateManager setRefLogMessage(String message) {
+    this.refLogMessage = message;
+    return this;
+  }
+
+  public NoteDbUpdateManager setRefLogIdent(PersonIdent ident) {
+    this.refLogIdent = ident;
+    return this;
+  }
+
   public OpenRepo getChangeRepo() throws IOException {
     initChangeRepo();
     return changeRepo;
@@ -435,12 +453,15 @@
     }
   }
 
-  private static void execute(OpenRepo or) throws IOException {
+  private void execute(OpenRepo or) throws IOException {
     if (or == null || or.cmds.isEmpty()) {
       return;
     }
     or.flush();
     BatchRefUpdate bru = or.repo.getRefDatabase().newBatchUpdate();
+    bru.setRefLogMessage(
+        firstNonNull(refLogMessage, "Update NoteDb refs"), false);
+    bru.setRefLogIdent(refLogIdent != null ? refLogIdent : serverIdent.get());
     or.cmds.addTo(bru);
     bru.setAllowNonFastForwards(true);
     bru.execute(or.rw, NullProgressMonitor.INSTANCE);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
index 60a4842..03bd6ec 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
@@ -264,7 +264,8 @@
   @Override
   public void buildUpdates(NoteDbUpdateManager manager, ChangeBundle bundle)
       throws IOException, OrmException {
-    manager.setCheckExpectedState(false);
+    manager.setCheckExpectedState(false)
+        .setRefLogMessage("Rebuilding change");
     Change change = new Change(bundle.getChange());
     if (bundle.getPatchSets().isEmpty()) {
       throw new NoPatchSetsException(change.getId());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java
index 3dc4c0b..db564cc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java
@@ -53,7 +53,7 @@
 import java.util.jar.JarFile;
 import java.util.jar.Manifest;
 
-public class JarScanner implements PluginContentScanner {
+public class JarScanner implements PluginContentScanner, AutoCloseable {
   private static final int SKIP_ALL = ClassReader.SKIP_CODE
       | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;
   private final JarFile jarFile;
@@ -131,6 +131,11 @@
     return findSubClassesOf(superClass.getName());
   }
 
+  @Override
+  public void close() throws IOException {
+    jarFile.close();
+  }
+
   private List<String> findSubClassesOf(String superClass) throws IOException {
     String name = superClass.replace('.', '/');
 
diff --git a/lib/BUCK b/lib/BUCK
index 96365eb..6d26f9e 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -40,9 +40,9 @@
 
 maven_jar(
   name = 'gwtorm_client',
-  id = 'com.google.gerrit:gwtorm:1.16',
-  bin_sha1 = '3e41b6d7bb352fa0539ce23b9bce97cf8c26c3bf',
-  src_sha1 = 'f45b7bacc79a0e5a7f6cf799a2dba23cc5bca19b',
+  id = 'com.google.gerrit:gwtorm:1.17',
+  bin_sha1 = "97bdc872f00388910c9af70771f07bbb32f1b949",
+  src_sha1 = "889e35d7295b1af49161a28daaea9905ffa76a63",
   license = 'Apache2.0',
 )
 
diff --git a/plugins/cookbook-plugin b/plugins/cookbook-plugin
index 4c199ce..8607421 160000
--- a/plugins/cookbook-plugin
+++ b/plugins/cookbook-plugin
@@ -1 +1 @@
-Subproject commit 4c199ce39282293c6f7a628c03313868da1857c3
+Subproject commit 860742125749aec5444bfa44bb727153c2649893
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 50b889c..7145f8d 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -305,7 +305,7 @@
                   [[patchNum.num]]
                   /
                   [[_computeLatestPatchNum(_allPatchSets)]]
-                  [[patchNum.desc]]
+                  [[_computePatchSetDescription(_change, patchNum.num)]]
                 </option>
               </template>
             </select>
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 93d7a49..eb23c61 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -14,6 +14,9 @@
 (function() {
   'use strict';
 
+  // Maximum length for patch set descriptions.
+  var PATCH_DESC_MAX_LENGTH = 500;
+
   Polymer({
     is: 'gr-change-view',
 
@@ -839,7 +842,8 @@
 
     _computePatchSetDescription: function(change, patchNum) {
       var rev = this.getRevisionByPatchNum(change.revisions, patchNum);
-      return (rev && rev.description) ? rev.description : '';
+      return (rev && rev.description) ?
+          rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : '';
     },
 
     _computeDescriptionPlaceholder: function(readOnly) {
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 3fb2c11..9d9d3d9 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -14,6 +14,9 @@
 (function() {
   'use strict';
 
+  // Maximum length for patch set descriptions.
+  var PATCH_DESC_MAX_LENGTH = 500;
+
   var COMMIT_MESSAGE_PATH = '/COMMIT_MSG';
 
   Polymer({
@@ -633,7 +636,8 @@
 
     _computePatchSetDescription: function(revisions, patchNum) {
       var rev = this.getRevisionByPatchNum(revisions, patchNum);
-      return (rev && rev.description) ? rev.description : '';
+      return (rev && rev.description) ?
+          rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : '';
     },
   });
 })();
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
index ca38a8c..58d29bd 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
@@ -14,6 +14,9 @@
 (function() {
   'use strict';
 
+  // Maximum length for patch set descriptions.
+  var PATCH_DESC_MAX_LENGTH = 500;
+
   Polymer({
     is: 'gr-patch-range-select',
 
@@ -73,7 +76,8 @@
 
     _computePatchSetDescription: function(revisions, patchNum) {
       var rev = this.getRevisionByPatchNum(revisions, patchNum);
-      return (rev && rev.description) ? rev.description : '';
+      return (rev && rev.description) ?
+          rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : '';
     },
   });
 })();
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
index e742e1c..13ee10e 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
@@ -33,27 +33,30 @@
     var element;
     var plugin;
     var errorStub;
+    var sandbox;
+
     var throwErrFn = function() {
       throw Error('Unfortunately, this handler has stopped');
     };
 
     setup(function() {
+      sandbox = sinon.sandbox.create();
       stub('gr-rest-api-interface', {
         getAccount: function() {
           return Promise.resolve({name: 'Judy Hopps'});
         },
       });
       element = fixture('basic');
-      errorStub = sinon.stub(console, 'error');
+      errorStub = sandbox.stub(console, 'error');
       Gerrit._setPluginsCount(1);
       Gerrit.install(function(p) { plugin = p; }, '0.1',
           'http://test.com/plugins/testplugin/static/test.js');
     });
 
     teardown(function() {
+      sandbox.restore();
       element._removeEventCallbacks();
       plugin = null;
-      errorStub.restore();
     });
 
     test('url', function() {
@@ -94,7 +97,7 @@
         _number: 42,
         revisions: {def: {_number: 2}, abc: {_number: 1}},
       };
-      var spy = sinon.spy();
+      var spy = sandbox.spy();
       Gerrit._setPluginsCount(1);
       plugin.on(element.EventType.SHOW_CHANGE, spy);
       element.handleEvent(element.EventType.SHOW_CHANGE,
@@ -118,11 +121,10 @@
       element.handleEvent(element.EventType.COMMENT, {node: testCommentNode});
     });
 
-    test('revert event', function(done) {
+    test('revert event', function() {
       function appendToRevertMsg(c, revertMsg, originalMsg) {
         return revertMsg + '\n' + originalMsg.replace(/^/gm, '> ') + '\ninfo';
       }
-      done();
 
       assert.equal(element.modifyRevertMsg(null, 'test', 'origTest'), 'test');
       assert.equal(errorStub.callCount, 0);
@@ -139,19 +141,18 @@
       assert.isTrue(errorStub.calledTwice);
     });
 
-    test('postrevert event', function(done) {
+    test('postrevert event', function() {
       function getLabels(c) {
         return {'Code-Review': 1};
       }
-      done();
 
       assert.deepEqual(element.getLabelValuesPostRevert(null), {});
       assert.equal(errorStub.callCount, 0);
 
       plugin.on(element.EventType.POST_REVERT, throwErrFn);
       plugin.on(element.EventType.POST_REVERT, getLabels);
-      assert.deepEqual(element.getLabelValuesPostRevert(null),
-                       {'Code-Review': 1});
+      assert.deepEqual(
+          element.getLabelValuesPostRevert(null), {'Code-Review': 1});
       assert.isTrue(errorStub.calledOnce);
     });
 
@@ -189,7 +190,7 @@
     });
 
     test('versioning', function() {
-      var callback = sinon.spy();
+      var callback = sandbox.spy();
       Gerrit.install(callback, '0.0pre-alpha');
       assert(callback.notCalled);
     });
@@ -222,6 +223,7 @@
     test('_pluginInstalled', function(done) {
       stub('gr-reporting', {
         pluginsLoaded: function() {
+          assert.equal(Gerrit._pluginsPending, 0);
           done();
         }
       });
@@ -232,18 +234,33 @@
     });
 
     test('install calls _pluginInstalled', function() {
-      var stub = sinon.stub(Gerrit, '_pluginInstalled');
+      sandbox.stub(Gerrit, '_pluginInstalled');
       Gerrit.install(function(p) { plugin = p; }, '0.1',
           'http://test.com/plugins/testplugin/static/test.js');
-      assert.isTrue(stub.calledOnce);
-      stub.restore();
+      assert.isTrue(Gerrit._pluginInstalled.calledOnce);
     });
 
     test('install calls _pluginInstalled on error', function() {
-      var stub = sinon.stub(Gerrit, '_pluginInstalled');
+      sandbox.stub(Gerrit, '_pluginInstalled');
       Gerrit.install(function() {}, '0.0pre-alpha');
-      assert.isTrue(stub.calledOnce);
-      stub.restore();
+      assert.isTrue(Gerrit._pluginInstalled.calledOnce);
+    });
+
+    test('installGwt calls _pluginInstalled', function() {
+      sandbox.stub(Gerrit, '_pluginInstalled');
+      Gerrit.installGwt();
+      assert.isTrue(Gerrit._pluginInstalled.calledOnce);
+    });
+
+    test('installGwt returns a stub object', function() {
+      var plugin = Gerrit.installGwt();
+      sandbox.stub(console, 'warn');
+      assert.isAbove(Object.keys(plugin).length, 0);
+      Object.keys(plugin).forEach(function(name) {
+        console.warn.reset();
+        plugin[name]();
+        assert.isTrue(console.warn.calledOnce);
+      });
     });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
index 588dc8b..b3ae649 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
@@ -14,6 +14,16 @@
 (function(window) {
   'use strict';
 
+  var warnNotSupported = function(opt_name) {
+    console.warn('Plugin API method ' + (opt_name || '') + ' is not supported');
+  };
+
+  var stubbedMethods = ['_loadedGwt', 'screen', 'settingsScreen', 'panel'];
+  var GWT_PLUGIN_STUB = {};
+  stubbedMethods.forEach(function(name) {
+    GWT_PLUGIN_STUB[name] = warnNotSupported.bind(null, name);
+  });
+
   var API_VERSION = '0.1';
 
   // GWT JSNI uses $wnd to refer to window.
@@ -98,7 +108,13 @@
 
     // TODO(andybons): Polyfill currentScript for IE10/11 (edge supports it).
     var src = opt_src || (document.currentScript && document.currentScript.src);
-    callback(new Plugin(src));
+    var plugin = new Plugin(src);
+    try {
+      callback(plugin);
+    } catch (e) {
+      console.warn(plugin.getPluginName() + ' install failed: ' +
+          e.name + ': ' + e.message);
+    }
     Gerrit._pluginInstalled();
   };
 
@@ -106,9 +122,14 @@
     return document.createElement('gr-rest-api-interface').getLoggedIn();
   };
 
+  /**
+   * Polyfill GWT API dependencies to avoid runtime exceptions when loading
+   * GWT-compiled plugins.
+   * @deprecated Not supported in PolyGerrit.
+   */
   Gerrit.installGwt = function() {
-    // NOOP since PolyGerrit doesn’t support GWT plugins.
     Gerrit._pluginInstalled();
+    return GWT_PLUGIN_STUB;
   };
 
   Gerrit._allPluginsPromise = null;