diff --git a/.bazelversion b/.bazelversion
index 7ec1d6db40..ccbccc3dc6 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-2.1.0
+2.2.0
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 2292135f38..24da6320c1 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -4424,6 +4424,19 @@ email address, that one address is added to the white list.
 If set to a domain name, any address at that domain can receive
 email from Gerrit.
 +
+If allowrcpt is configured, The set of allowed recipients is:
+`allowrcpt - denyrcpt`.
++
+By default, unset, permitting delivery to any email address.
+
+[[sendemail.denyrcpt]]sendemail.denyrcpt::
++
+If present, each value adds one entry to the blacklist of email
+addresses that Gerrit can send email to.  If set to a complete
+email address, that one address is added to the blacklist.
+If set to a domain name, any address at that domain can *not* receive
+email from Gerrit.
++
 By default, unset, permitting delivery to any email address.
 
 [[sendemail.includeDiff]]sendemail.includeDiff::
diff --git a/Documentation/dev-crafting-changes.txt b/Documentation/dev-crafting-changes.txt
index ac8ca25259..c6c6e053c9 100644
--- a/Documentation/dev-crafting-changes.txt
+++ b/Documentation/dev-crafting-changes.txt
@@ -116,7 +116,7 @@ To format Java source code, Gerrit uses the
 link:https://github.com/google/google-java-format[`google-java-format`,role=external,window=_blank]
 tool (version 1.7), and to format Bazel BUILD, WORKSPACE and .bzl files the
 link:https://github.com/bazelbuild/buildtools/tree/master/buildifier[`buildifier`,role=external,window=_blank]
-tool (version 0.29.0). Unused dependencies are found and removed using the
+tool (version 1.0.0). Unused dependencies are found and removed using the
 link:https://github.com/bazelbuild/buildtools/tree/master/unused_deps[`unused_deps`,role=external,window=_blank]
 build tool, a sibling of `buildifier`.
 
diff --git a/Documentation/metrics.txt b/Documentation/metrics.txt
index 075de2be67..aa31382b23 100644
--- a/Documentation/metrics.txt
+++ b/Documentation/metrics.txt
@@ -144,9 +144,10 @@ topic submissions that concluded successfully.
 
 === JGit
 
-* `jgit/block_cache/cache_used`: Bytes of memory retained in JGit block cache.
-* `jgit/block_cache/open_files`: File handles held open by JGit block cache.
-* `avg_load_time` Average time to load a cache entry for JGit block cache.
+* `jgit/block_cache/cache_used` : Bytes of memory retained in JGit block cache.
+* `jgit/block_cache/open_files` : File handles held open by JGit block cache.
+* `avg_load_time` : Average time to load a cache entry for JGit block cache.
+* `total_load_time` : Total time to load cache entries for JGit block cache.
 * `eviction_count` : Cache evictions for JGit block cache.
 * `eviction_ratio` : Cache eviction ratio for JGit block cache.
 * `hit_count` : Cache hits for JGit block cache.
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 10a2ff3239..08c37357e8 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -666,6 +666,9 @@ link:#email-input[EmailInput].
 If link:config-gerrit.html#sendemail.allowrcpt[sendemail.allowrcpt] is
 configured, the added email address must belong to a domain that is
 allowed, unless `no_confirmation` is set.
+If link:config-gerrit.html#sendemail.denyrcpt[sendemail.denyrcpt]
+is configured, make sure that the added email address is *not* disallowed or
+belongs to a domain that is disallowed.
 
 The link:#email-input[EmailInput] object in the request body may
 contain additional options for the email address.
diff --git a/java/com/google/gerrit/metrics/proc/JGitMetricModule.java b/java/com/google/gerrit/metrics/proc/JGitMetricModule.java
index ec24355954..e8611b3365 100644
--- a/java/com/google/gerrit/metrics/proc/JGitMetricModule.java
+++ b/java/com/google/gerrit/metrics/proc/JGitMetricModule.java
@@ -66,6 +66,17 @@ public class JGitMetricModule extends MetricModule {
           }
         });
 
+    metrics.newCallbackMetric(
+        "jgit/block_cache/total_load_time",
+        Long.class,
+        new Description("Total time to load cache entries for JGit block cache.").setGauge(),
+        new Supplier<Long>() {
+          @Override
+          public Long get() {
+            return WindowCacheStats.getStats().getTotalLoadTime();
+          }
+        });
+
     metrics.newCallbackMetric(
         "jgit/block_cache/eviction_count",
         Long.class,
diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index d48ed5985a..528755a15e 100644
--- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -521,9 +521,7 @@ public abstract class OutgoingEmail {
     if (addr != null && addr.getEmail() != null && addr.getEmail().length() > 0) {
       if (!args.validator.isValid(addr.getEmail())) {
         logger.atWarning().log("Not emailing %s (invalid email address)", addr.getEmail());
-      } else if (!args.emailSender.canEmail(addr.getEmail())) {
-        logger.atWarning().log("Not emailing %s (prohibited by allowrcpt)", addr.getEmail());
-      } else {
+      } else if (args.emailSender.canEmail(addr.getEmail())) {
         if (!smtpRcptTo.add(addr)) {
           if (!override) {
             return;
diff --git a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
index 3f103fca07..7207c0099c 100644
--- a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.mail.send;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import com.google.common.flogger.FluentLogger;
 import com.google.common.io.BaseEncoding;
 import com.google.common.primitives.Ints;
 import com.google.gerrit.common.Nullable;
@@ -56,6 +57,8 @@ public class SmtpEmailSender implements EmailSender {
   /** The socket's connect timeout (0 = infinite timeout) */
   private static final int DEFAULT_CONNECT_TIMEOUT = 0;
 
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
   public static class Module extends AbstractModule {
     @Override
     protected void configure() {
@@ -73,6 +76,7 @@ public class SmtpEmailSender implements EmailSender {
   private Encryption smtpEncryption;
   private boolean sslVerify;
   private Set<String> allowrcpt;
+  private Set<String> denyrcpt;
   private String importance;
   private int expiryDays;
 
@@ -117,6 +121,9 @@ public class SmtpEmailSender implements EmailSender {
     Set<String> rcpt = new HashSet<>();
     Collections.addAll(rcpt, cfg.getStringList("sendemail", null, "allowrcpt"));
     allowrcpt = Collections.unmodifiableSet(rcpt);
+    Set<String> rcptdeny = new HashSet<>();
+    Collections.addAll(rcptdeny, cfg.getStringList("sendemail", null, "denyrcpt"));
+    denyrcpt = Collections.unmodifiableSet(rcptdeny);
     importance = cfg.getString("sendemail", null, "importance");
     expiryDays = cfg.getInt("sendemail", null, "expiryDays", 0);
   }
@@ -129,22 +136,47 @@ public class SmtpEmailSender implements EmailSender {
   @Override
   public boolean canEmail(String address) {
     if (!isEnabled()) {
+      logger.atWarning().log("Not emailing %s (email is disabled)", address);
       return false;
     }
 
+    String domain = address.substring(address.lastIndexOf('@') + 1);
+    if (isDenied(address, domain)) {
+      return false;
+    }
+
+    return isAllowed(address, domain);
+  }
+
+  private boolean isDenied(String address, String domain) {
+
+    if (denyrcpt.isEmpty()) {
+      return false;
+    }
+
+    if (denyrcpt.contains(address)
+        || denyrcpt.contains(domain)
+        || denyrcpt.contains("@" + domain)) {
+      logger.atWarning().log("Not emailing %s (prohibited by sendemail.denyrcpt)", address);
+      return true;
+    }
+
+    return false;
+  }
+
+  private boolean isAllowed(String address, String domain) {
+
     if (allowrcpt.isEmpty()) {
       return true;
     }
 
-    if (allowrcpt.contains(address)) {
-      return true;
-    }
-
-    String domain = address.substring(address.lastIndexOf('@') + 1);
-    if (allowrcpt.contains(domain) || allowrcpt.contains("@" + domain)) {
+    if (allowrcpt.contains(address)
+        || allowrcpt.contains(domain)
+        || allowrcpt.contains("@" + domain)) {
       return true;
     }
 
+    logger.atWarning().log("Not emailing %s (prohibited by sendemail.allowrcpt)", address);
     return false;
   }
 
diff --git a/java/com/google/gerrit/sshd/AbstractGitCommand.java b/java/com/google/gerrit/sshd/AbstractGitCommand.java
index 0922037917..8bf6cd5cb7 100644
--- a/java/com/google/gerrit/sshd/AbstractGitCommand.java
+++ b/java/com/google/gerrit/sshd/AbstractGitCommand.java
@@ -39,12 +39,12 @@ public abstract class AbstractGitCommand extends BaseCommand {
 
   @Inject private GitRepositoryManager repoManager;
 
-  @Inject private SshSession session;
-
   @Inject private SshScope.Context context;
 
   @Inject private IdentifiedUser.GenericFactory userFactory;
 
+  @Inject protected SshSession session;
+
   protected Repository repo;
   protected Project.NameKey projectName;
   protected Project project;
diff --git a/java/com/google/gerrit/sshd/commands/Receive.java b/java/com/google/gerrit/sshd/commands/Receive.java
index 6791b704a5..92666f3b13 100644
--- a/java/com/google/gerrit/sshd/commands/Receive.java
+++ b/java/com/google/gerrit/sshd/commands/Receive.java
@@ -20,7 +20,7 @@ import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.Capable;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
 import com.google.gerrit.server.permissions.PermissionBackend;
@@ -28,7 +28,6 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.sshd.AbstractGitCommand;
 import com.google.gerrit.sshd.CommandMetaData;
-import com.google.gerrit.sshd.SshSession;
 import com.google.inject.Inject;
 import java.io.IOException;
 import org.eclipse.jgit.errors.TooLargeObjectInPackException;
@@ -45,8 +44,6 @@ final class Receive extends AbstractGitCommand {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   @Inject private AsyncReceiveCommits.Factory factory;
-  @Inject private IdentifiedUser currentUser;
-  @Inject private SshSession session;
   @Inject private PermissionBackend permissionBackend;
 
   private final SetMultimap<ReviewerStateInternal, Account.Id> reviewers =
@@ -72,6 +69,7 @@ final class Receive extends AbstractGitCommand {
 
   @Override
   protected void runImpl() throws IOException, Failure {
+    CurrentUser currentUser = session.getUser();
     try {
       permissionBackend
           .user(currentUser)
@@ -83,7 +81,8 @@ final class Receive extends AbstractGitCommand {
       throw new Failure(1, "fatal: unable to check permissions " + e);
     }
 
-    AsyncReceiveCommits arc = factory.create(projectState, currentUser, repo, null);
+    AsyncReceiveCommits arc =
+        factory.create(projectState, currentUser.asIdentifiedUser(), repo, null);
 
     try {
       Capable r = arc.canUpload();
diff --git a/java/com/google/gerrit/sshd/commands/Upload.java b/java/com/google/gerrit/sshd/commands/Upload.java
index de4508a3df..b80b879f58 100644
--- a/java/com/google/gerrit/sshd/commands/Upload.java
+++ b/java/com/google/gerrit/sshd/commands/Upload.java
@@ -33,7 +33,6 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
 import com.google.gerrit.sshd.AbstractGitCommand;
-import com.google.gerrit.sshd.SshSession;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.List;
@@ -53,7 +52,6 @@ final class Upload extends AbstractGitCommand {
   @Inject private DynamicSet<UploadPackInitializer> uploadPackInitializers;
   @Inject private PluginSetContext<RequestListener> requestListeners;
   @Inject private UploadValidators.Factory uploadValidatorsFactory;
-  @Inject private SshSession session;
   @Inject private PermissionBackend permissionBackend;
   @Inject private UsersSelfAdvertiseRefsHook usersSelfAdvertiseRefsHook;
 
diff --git a/javatests/com/google/gerrit/acceptance/git/ForcePushIT.java b/javatests/com/google/gerrit/acceptance/git/AbstractForcePush.java
similarity index 97%
rename from javatests/com/google/gerrit/acceptance/git/ForcePushIT.java
rename to javatests/com/google/gerrit/acceptance/git/AbstractForcePush.java
index 5ec7b0e349..3b80312a35 100644
--- a/javatests/com/google/gerrit/acceptance/git/ForcePushIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractForcePush.java
@@ -22,7 +22,6 @@ import static org.eclipse.jgit.transport.RemoteRefUpdate.Status.OK;
 import static org.eclipse.jgit.transport.RemoteRefUpdate.Status.REJECTED_OTHER_REASON;
 
 import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.common.data.Permission;
@@ -34,8 +33,7 @@ import org.eclipse.jgit.transport.PushResult;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.junit.Test;
 
-@NoHttpd
-public class ForcePushIT extends AbstractDaemonTest {
+public abstract class AbstractForcePush extends AbstractDaemonTest {
   @Inject private ProjectOperations projectOperations;
 
   @Test
diff --git a/javatests/com/google/gerrit/acceptance/git/HttpForcePushIT.java b/javatests/com/google/gerrit/acceptance/git/HttpForcePushIT.java
new file mode 100644
index 0000000000..54524a0a13
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/git/HttpForcePushIT.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2020 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.git;
+
+import com.google.gerrit.acceptance.GitUtil;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
+import org.junit.Before;
+
+public class HttpForcePushIT extends AbstractForcePush {
+  @Before
+  public void cloneProjectOverHttp() throws Exception {
+    CredentialsProvider.setDefault(
+        new UsernamePasswordCredentialsProvider(admin.username(), admin.httpPassword()));
+    testRepo = GitUtil.cloneProject(project, admin.getHttpUrl(server) + "/" + project.get());
+  }
+}
diff --git a/javatests/com/google/gerrit/acceptance/git/SshForcePushIT.java b/javatests/com/google/gerrit/acceptance/git/SshForcePushIT.java
new file mode 100644
index 0000000000..4ccb347355
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/git/SshForcePushIT.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2020 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.git;
+
+import com.google.gerrit.acceptance.GitUtil;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.UseSsh;
+import org.junit.Before;
+
+@NoHttpd
+@UseSsh
+public class SshForcePushIT extends AbstractForcePush {
+  @Before
+  public void cloneProjectOverSsh() throws Exception {
+    testRepo = GitUtil.cloneProject(project, adminSshSession.getUrl() + "/" + project.get());
+  }
+}
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
index 34bda263f3..b6e2884398 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
@@ -46,6 +46,7 @@
     'text/x-elm': 'elm',
     'text/x-erlang': 'erlang',
     'text/x-fortran': 'fortran',
+    'text/x-fsharp': 'fsharp',
     'text/x-go': 'go',
     'text/x-groovy': 'groovy',
     'text/x-haml': 'haml',
diff --git a/tools/bzl/js.bzl b/tools/bzl/js.bzl
index 6132e9915f..b428a2d903 100644
--- a/tools/bzl/js.bzl
+++ b/tools/bzl/js.bzl
@@ -536,7 +536,7 @@ def polygerrit_plugin(name, app, srcs = [], deps = [], externs = [], assets = No
                 cmd = "cp $(SRCS) $(@D)",
                 output_to_bindir = True,
             )
-            static_files += [":" + name + "_copy_assets"]
+            static_files.append(":" + name + "_copy_assets")
 
     native.filegroup(
         name = name,
diff --git a/tools/nongoogle.bzl b/tools/nongoogle.bzl
index 73af4fd8f3..49a52c212f 100644
--- a/tools/nongoogle.bzl
+++ b/tools/nongoogle.bzl
@@ -108,8 +108,8 @@ def declare_nongoogle_deps():
 
     maven_jar(
         name = "jackson-core",
-        artifact = "com.fasterxml.jackson.core:jackson-core:2.10.2",
-        sha1 = "73d4322a6bda684f676a2b5fe918361c4e5c7cca",
+        artifact = "com.fasterxml.jackson.core:jackson-core:2.10.3",
+        sha1 = "f7ee7b55c7d292ac72fbaa7648c089f069c938d2",
     )
 
     # Test-only dependencies below.