Merge "Merge branch 'stable-3.5'"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 1a535ef..2b20cab 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`
 +
@@ -611,7 +611,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.
@@ -714,8 +714,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
@@ -979,8 +981,8 @@
 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 `"git_modified_files"`::
 +
@@ -1338,7 +1340,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.
 
@@ -2303,7 +2305,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
@@ -2313,7 +2315,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
@@ -2593,8 +2595,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::
 +
@@ -4150,18 +4153,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
@@ -4403,9 +4394,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
@@ -5234,6 +5224,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
@@ -5744,10 +5735,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/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index e739cfe..8f766ea 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
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index ec6c7f1..2083050 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -463,7 +463,6 @@
 
     baseConfig.setInt("index", null, "batchThreads", -1);
 
-    baseConfig.setInt("receive", null, "changeUpdateThreads", 4);
     Module module = createModule();
     Module auditModule = createAuditModule();
     Module sshModule = createSshModule();
diff --git a/java/com/google/gerrit/acceptance/ExtensionRegistry.java b/java/com/google/gerrit/acceptance/ExtensionRegistry.java
index dd2a423..0b3eff1 100644
--- a/java/com/google/gerrit/acceptance/ExtensionRegistry.java
+++ b/java/com/google/gerrit/acceptance/ExtensionRegistry.java
@@ -37,6 +37,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;
@@ -85,6 +86,7 @@
   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>
@@ -127,6 +129,7 @@
       DynamicSet<PatchSetWebLink> patchSetWebLinks,
       DynamicSet<ResolveConflictsWebLink> resolveConflictsWebLinks,
       DynamicSet<EditWebLink> editWebLinks,
+      DynamicSet<FileWebLink> fileWebLinks,
       DynamicSet<RevisionCreatedListener> revisionCreatedListeners,
       DynamicSet<GroupBackend> groupBackends,
       DynamicSet<AccountActivationValidationListener> accountActivationValidationListeners,
@@ -163,6 +166,7 @@
     this.fileHistoryWebLinks = fileHistoryWebLinks;
     this.patchSetWebLinks = patchSetWebLinks;
     this.editWebLinks = editWebLinks;
+    this.fileWebLinks = fileWebLinks;
     this.resolveConflictsWebLinks = resolveConflictsWebLinks;
     this.revisionCreatedListeners = revisionCreatedListeners;
     this.groupBackends = groupBackends;
@@ -289,6 +293,10 @@
       return add(editWebLinks, editWebLink);
     }
 
+    public Registration add(FileWebLink fileWebLink) {
+      return add(fileWebLinks, fileWebLink);
+    }
+
     public Registration add(RevisionCreatedListener revisionCreatedListener) {
       return add(revisionCreatedListeners, revisionCreatedListener);
     }
diff --git a/java/com/google/gerrit/extensions/webui/FileWebLink.java b/java/com/google/gerrit/extensions/webui/FileWebLink.java
index c03d606..dc386b3 100644
--- a/java/com/google/gerrit/extensions/webui/FileWebLink.java
+++ b/java/com/google/gerrit/extensions/webui/FileWebLink.java
@@ -32,8 +32,9 @@
    *
    * @param projectName Name of the project
    * @param revision Name of the revision (e.g. branch or commit ID)
+   * @param hash SHA-1 of the commit
    * @param fileName Name of the file
    * @return WebLinkInfo that links to project in external service, null if there should be no link.
    */
-  WebLinkInfo getFileWebLink(String projectName, String revision, String fileName);
+  WebLinkInfo getFileWebLink(String projectName, String revision, String hash, String fileName);
 }
diff --git a/java/com/google/gerrit/server/StarredChangesUtil.java b/java/com/google/gerrit/server/StarredChangesUtil.java
index 33f2ad1..95d891e 100644
--- a/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -380,18 +380,24 @@
         TraceContext.newTimer(
             "Read star labels", Metadata.builder().noteDbRefName(refName).build())) {
       Ref ref = repo.exactRef(refName);
-      if (ref == null) {
-        return StarRef.MISSING;
-      }
+      return readLabels(repo, ref);
+    }
+  }
 
-      try (ObjectReader reader = repo.newObjectReader()) {
-        ObjectLoader obj = reader.open(ref.getObjectId(), Constants.OBJ_BLOB);
-        return StarRef.create(
-            ref,
-            Splitter.on(CharMatcher.whitespace())
-                .omitEmptyStrings()
-                .split(new String(obj.getCachedBytes(Integer.MAX_VALUE), UTF_8)));
-      }
+  public static StarRef readLabels(Repository repo, Ref ref) throws IOException {
+    if (ref == null) {
+      return StarRef.MISSING;
+    }
+    try (TraceTimer traceTimer =
+            TraceContext.newTimer(
+                String.format("Read star labels from %s (without ref lookup)", ref.getName()));
+        ObjectReader reader = repo.newObjectReader()) {
+      ObjectLoader obj = reader.open(ref.getObjectId(), Constants.OBJ_BLOB);
+      return StarRef.create(
+          ref,
+          Splitter.on(CharMatcher.whitespace())
+              .omitEmptyStrings()
+              .split(new String(obj.getCachedBytes(Integer.MAX_VALUE), UTF_8)));
     }
   }
 
diff --git a/java/com/google/gerrit/server/WebLinks.java b/java/com/google/gerrit/server/WebLinks.java
index dd36066..58396f5 100644
--- a/java/com/google/gerrit/server/WebLinks.java
+++ b/java/com/google/gerrit/server/WebLinks.java
@@ -149,13 +149,15 @@
    * Returns links for files
    *
    * @param project Project name.
-   * @param revision SHA1 of revision.
+   * @param revision Name of the revision (e.g. branch or commit ID)
+   * @param hash SHA1 of revision.
    * @param file File name.
    */
-  public ImmutableList<WebLinkInfo> getFileLinks(String project, String revision, String file) {
+  public ImmutableList<WebLinkInfo> getFileLinks(
+      String project, String revision, String hash, String file) {
     return Patch.isMagic(file)
         ? ImmutableList.of()
-        : filterLinks(fileLinks, webLink -> webLink.getFileWebLink(project, revision, file));
+        : filterLinks(fileLinks, webLink -> webLink.getFileWebLink(project, revision, hash, file));
   }
 
   /**
diff --git a/java/com/google/gerrit/server/account/AccountDeactivator.java b/java/com/google/gerrit/server/account/AccountDeactivator.java
index c7f6496..a4d7608 100644
--- a/java/com/google/gerrit/server/account/AccountDeactivator.java
+++ b/java/com/google/gerrit/server/account/AccountDeactivator.java
@@ -117,7 +117,7 @@
         return true;
       }
     } catch (ResourceConflictException e) {
-      logger.atInfo().log("Account %s already deactivated, continuing...", userName);
+      logger.atInfo().withCause(e).log("Account %s already deactivated, continuing...", userName);
     } catch (Exception e) {
       logger.atSevere().withCause(e).log(
           "Error deactivating account: %s (%s) %s",
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalId.java b/java/com/google/gerrit/server/account/externalids/ExternalId.java
index a5fb733..0a51171 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalId.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalId.java
@@ -106,8 +106,10 @@
   static final String PASSWORD_KEY = "password";
 
   /**
-   * Scheme used for {@link AuthType#LDAP}, {@link AuthType#CLIENT_SSL_CERT_LDAP}, {@link
-   * AuthType#HTTP_LDAP}, and {@link AuthType#LDAP_BIND} usernames.
+   * Scheme used to label accounts created, when using the LDAP-based authentication types {@link
+   * AuthType#LDAP}, {@link AuthType#CLIENT_SSL_CERT_LDAP}, {@link AuthType#HTTP_LDAP}, and {@link
+   * AuthType#LDAP_BIND}. The external ID stores the username. Accounts with such an external ID
+   * will be authenticated against the configured LDAP identity provider.
    *
    * <p>The name {@code gerrit:} was a very poor choice.
    *
diff --git a/java/com/google/gerrit/server/config/GitwebConfig.java b/java/com/google/gerrit/server/config/GitwebConfig.java
index 38a86f0..c477bb5 100644
--- a/java/com/google/gerrit/server/config/GitwebConfig.java
+++ b/java/com/google/gerrit/server/config/GitwebConfig.java
@@ -318,11 +318,13 @@
     }
 
     @Override
-    public WebLinkInfo getFileWebLink(String projectName, String revision, String fileName) {
+    public WebLinkInfo getFileWebLink(
+        String projectName, String revision, String hash, String fileName) {
       if (file != null) {
         return link(
             file.replace("project", encode(projectName))
                 .replace("commit", encode(revision))
+                .replace("hash", encode(hash))
                 .replace("file", encode(fileName))
                 .toString());
       }
diff --git a/java/com/google/gerrit/server/git/validators/CommitValidators.java b/java/com/google/gerrit/server/git/validators/CommitValidators.java
index 767a13d..6118157 100644
--- a/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -372,14 +372,18 @@
       // If there are no SSH keys, the commit-msg hook must be installed via
       // HTTP(S)
       Optional<String> webUrl = urlFormatter.getWebUrl();
+
+      String httpHook =
+          String.format(
+              "f=\"$(git rev-parse --git-dir)/hooks/commit-msg\"; curl -o \"$f\" %stools/hooks/commit-msg ; chmod +x \"$f\"",
+              webUrl.get());
+
       if (hostKeys.isEmpty()) {
         checkState(webUrl.isPresent());
-        return String.format(
-            "  f=\"$(git rev-parse --git-dir)/hooks/commit-msg\"; curl -o \"$f\" %stools/hooks/commit-msg ; chmod +x \"$f\"",
-            webUrl.get());
+        return httpHook;
       }
 
-      // SSH keys exist, so the hook can be installed with scp.
+      // SSH keys exist, so the hook might be able to be installed with scp.
       String sshHost;
       int sshPort;
       String host = hostKeys.get(0).getHost();
@@ -397,9 +401,11 @@
         sshPort = 22;
       }
 
-      return String.format(
-          "  gitdir=$(git rev-parse --git-dir); scp -p -P %d %s@%s:hooks/commit-msg ${gitdir}/hooks/",
-          sshPort, user.getUserName().orElse("<USERNAME>"), sshHost);
+      String sshHook =
+          String.format(
+              "gitdir=$(git rev-parse --git-dir); scp -p -P %d %s@%s:hooks/commit-msg ${gitdir}/hooks/",
+              sshPort, user.getUserName().orElse("<USERNAME>"), sshHost);
+      return String.format("  %s\nor, for http(s):\n  %s", sshHook, httpHook);
     }
   }
 
diff --git a/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
index cba6e47..288ccf8 100644
--- a/java/com/google/gerrit/server/mail/send/ChangeEmail.java
+++ b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
@@ -427,15 +427,25 @@
 
   @Override
   protected void add(RecipientType rt, Account.Id to) {
-    Optional<AccountState> accountState = args.accountCache.get(to);
-    if (!accountState.isPresent()) {
-      return;
-    }
-    if (emailOnlyAttentionSetIfEnabled
-        && accountState.get().generalPreferences().getEmailStrategy()
-            == EmailStrategy.ATTENTION_SET_ONLY
-        && !currentAttentionSet.contains(to)) {
-      return;
+    addRecipient(rt, to, /* isWatcher= */ false);
+  }
+
+  /** This bypasses the EmailStrategy.ATTENTION_SET_ONLY strategy when adding the recipient. */
+  @Override
+  protected void addWatcher(RecipientType rt, Account.Id to) {
+    addRecipient(rt, to, /* isWatcher= */ true);
+  }
+
+  private void addRecipient(RecipientType rt, Account.Id to, boolean isWatcher) {
+    if (!isWatcher) {
+      Optional<AccountState> accountState = args.accountCache.get(to);
+      if (emailOnlyAttentionSetIfEnabled
+          && accountState.isPresent()
+          && accountState.get().generalPreferences().getEmailStrategy()
+              == EmailStrategy.ATTENTION_SET_ONLY
+          && !currentAttentionSet.contains(to)) {
+        return;
+      }
     }
     if (emailOnlyAuthors && !authors.contains(to)) {
       return;
diff --git a/java/com/google/gerrit/server/mail/send/NotificationEmail.java b/java/com/google/gerrit/server/mail/send/NotificationEmail.java
index 04b7972..5b209ce 100644
--- a/java/com/google/gerrit/server/mail/send/NotificationEmail.java
+++ b/java/com/google/gerrit/server/mail/send/NotificationEmail.java
@@ -83,13 +83,15 @@
   /** Add users or email addresses to the TO, CC, or BCC list. */
   protected void add(RecipientType type, WatcherList watcherList) {
     for (Account.Id user : watcherList.accounts) {
-      add(type, user);
+      addWatcher(type, user);
     }
     for (Address addr : watcherList.emails) {
       add(type, addr);
     }
   }
 
+  protected abstract void addWatcher(RecipientType type, Account.Id to);
+
   public String getSshHost() {
     String host = Iterables.getFirst(args.sshAddresses, null);
     if (host == null) {
diff --git a/java/com/google/gerrit/server/patch/AutoMerger.java b/java/com/google/gerrit/server/patch/AutoMerger.java
index fa27657..f29d1c1 100644
--- a/java/com/google/gerrit/server/patch/AutoMerger.java
+++ b/java/com/google/gerrit/server/patch/AutoMerger.java
@@ -174,17 +174,42 @@
       return Optional.empty();
     }
 
+    return Optional.of(
+        new ReceiveCommand(
+            ObjectId.zeroId(),
+            createAutoMergeCommit(repoView, rw, ins, maybeMergeCommit),
+            RefNames.refsCacheAutomerge(maybeMergeCommit.name())));
+  }
+
+  /**
+   * Creates an auto merge commit for the provided merge commit.
+   *
+   * <p>Callers are expected to ensure that the provided commit indeed has 2 parents.
+   *
+   * @return An auto-merge commit. Headers of the returned RevCommit are parsed.
+   */
+  ObjectId createAutoMergeCommit(
+      RepoView repoView, RevWalk rw, ObjectInserter ins, RevCommit mergeCommit) throws IOException {
     ObjectId autoMerge;
     try (Timer1.Context<OperationType> ignored = latency.start(OperationType.ON_DISK_WRITE)) {
       autoMerge =
           createAutoMergeCommit(
-              repoView.getConfig(), rw, ins, maybeMergeCommit, configuredMergeStrategy);
+              repoView.getConfig(), rw, ins, mergeCommit, configuredMergeStrategy);
     }
     counter.increment(OperationType.ON_DISK_WRITE);
     logger.atFine().log("Added %s AutoMerge ref update for commit", autoMerge.name());
-    return Optional.of(
-        new ReceiveCommand(
-            ObjectId.zeroId(), autoMerge, RefNames.refsCacheAutomerge(maybeMergeCommit.name())));
+    return autoMerge;
+  }
+
+  Optional<RevCommit> lookupCommit(Repository repo, RevWalk rw, String refName) throws IOException {
+    Ref ref = repo.getRefDatabase().exactRef(refName);
+    if (ref != null && ref.getObjectId() != null) {
+      RevObject obj = rw.parseAny(ref.getObjectId());
+      if (obj instanceof RevCommit) {
+        return Optional.of((RevCommit) obj);
+      }
+    }
+    return Optional.empty();
   }
 
   /**
@@ -255,18 +280,6 @@
     return rw.parseCommit(ins.insert(cb));
   }
 
-  private Optional<RevCommit> lookupCommit(Repository repo, RevWalk rw, String refName)
-      throws IOException {
-    Ref ref = repo.getRefDatabase().exactRef(refName);
-    if (ref != null && ref.getObjectId() != null) {
-      RevObject obj = rw.parseAny(ref.getObjectId());
-      if (obj instanceof RevCommit) {
-        return Optional.of((RevCommit) obj);
-      }
-    }
-    return Optional.empty();
-  }
-
   private static class NonFlushingWrapper extends ObjectInserter.Filter {
     private final ObjectInserter ins;
 
diff --git a/java/com/google/gerrit/server/patch/BaseCommitUtil.java b/java/com/google/gerrit/server/patch/BaseCommitUtil.java
index ed68dfd..dcd3e85 100644
--- a/java/com/google/gerrit/server/patch/BaseCommitUtil.java
+++ b/java/com/google/gerrit/server/patch/BaseCommitUtil.java
@@ -16,11 +16,11 @@
 
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.git.LockFailureException;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.InMemoryInserter;
-import com.google.gerrit.server.git.MergeUtil;
 import com.google.gerrit.server.update.RepoView;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -33,17 +33,14 @@
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.merge.ThreeWayMergeStrategy;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
 
 /** A utility class for computing the base commit / parent for a specific patchset commit. */
 @Singleton
 class BaseCommitUtil {
   private final AutoMerger autoMerger;
-  private final ThreeWayMergeStrategy mergeStrategy;
   private final GitRepositoryManager repoManager;
 
   /** If true, auto-merge results are stored in the repository. */
@@ -52,7 +49,6 @@
   @Inject
   BaseCommitUtil(AutoMerger am, @GerritServerConfig Config cfg, GitRepositoryManager repoManager) {
     this.autoMerger = am;
-    this.mergeStrategy = MergeUtil.getMergeStrategy(cfg);
     this.saveAutomerge = AutoMerger.cacheAutomerge(cfg);
     this.repoManager = repoManager;
   }
@@ -123,39 +119,29 @@
                 "diff against auto-merge commits is only supported if 'change.cacheAutomerge' config is set to true.");
           }
           // TODO(ghareeb): Avoid persisting auto-merge commits.
-          RevCommit autoMerge = createAutoMergeInGitIfNecessary(repo, ins, rw, current);
-          return autoMerge == null ? getAutoMergeFromGit(repo, current) : autoMerge;
+          return getAutoMergeFromGitOrCreate(repo, ins, rw, current);
         }
         return null;
     }
   }
 
   /**
-   * Creates the auto-merge commit in git. If the auto-merge already exists, this does nothing.
-   * Otherwise, the auto-merge is created, persisted in git and the cache-automerge ref is updated
-   * for the merge commit.
+   * Gets the auto-merge commit from git if it already exists. If not, the auto-merge is created,
+   * persisted in git and the cache-automerge ref is updated for the merge commit.
    *
-   * @return null if the auto-merge already exists in git, or the auto-merge {@link RevCommit}
-   *     object otherwise.
+   * @return the auto-merge {@link RevCommit}
    */
-  private RevCommit createAutoMergeInGitIfNecessary(
+  private RevCommit getAutoMergeFromGitOrCreate(
       Repository repo, ObjectInserter ins, RevWalk rw, RevCommit mergeCommit) throws IOException {
-    Optional<ReceiveCommand> receive =
-        autoMerger.createAutoMergeCommitIfNecessary(
-            new RepoView(repo, rw, ins), rw, ins, mergeCommit);
-    if (receive.isPresent()) {
-      ins.flush();
-      return updateRef(repo, rw, receive.get().getRefName(), receive.get().getNewId(), mergeCommit);
+    String refName = RefNames.refsCacheAutomerge(mergeCommit.name());
+    Optional<RevCommit> autoMergeCommit = autoMerger.lookupCommit(repo, rw, refName);
+    if (autoMergeCommit.isPresent()) {
+      return autoMergeCommit.get();
     }
-    return null;
-  }
-
-  private RevCommit getAutoMergeFromGit(Repository repo, RevCommit mergeCommit) throws IOException {
-    try (InMemoryInserter inMemoryIns = new InMemoryInserter(repo);
-        RevWalk inMemoryRw = new RevWalk(inMemoryIns.newReader())) {
-      return autoMerger.lookupFromGitOrMergeInMemory(
-          repo, inMemoryRw, inMemoryIns, mergeCommit, mergeStrategy);
-    }
+    ObjectId autoMergeId =
+        autoMerger.createAutoMergeCommit(new RepoView(repo, rw, ins), rw, ins, mergeCommit);
+    ins.flush();
+    return updateRef(repo, rw, refName, autoMergeId, mergeCommit);
   }
 
   private static RevCommit updateRef(
@@ -164,7 +150,7 @@
     RefUpdate ru = repo.updateRef(refName);
     ru.setNewObjectId(autoMergeId);
     ru.disableRefLog();
-    switch (ru.update()) {
+    switch (ru.forceUpdate()) {
       case FAST_FORWARD:
       case FORCED:
       case NEW:
diff --git a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
index 66299a8..aa49852 100644
--- a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
@@ -36,7 +36,6 @@
 import com.google.gerrit.server.account.CapabilityCollection;
 import com.google.gerrit.server.cache.PerThreadCache;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -124,11 +123,12 @@
     @Override
     public ForProject project(Project.NameKey project) {
       try {
-        ProjectState state = projectCache.get(project).orElseThrow(illegalState(project));
         ProjectControl control =
             PerThreadCache.getOrCompute(
                 PerThreadCache.Key.create(ProjectControl.class, project, user.getCacheKey()),
-                () -> projectControlFactory.create(user, state));
+                () ->
+                    projectControlFactory.create(
+                        user, projectCache.get(project).orElseThrow(illegalState(project))));
         return control.asForProject();
       } catch (Exception e) {
         Throwable cause = e.getCause() != null ? e.getCause() : e;
diff --git a/java/com/google/gerrit/server/restapi/change/GetDiff.java b/java/com/google/gerrit/server/restapi/change/GetDiff.java
index dd951a8..9424198 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDiff.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDiff.java
@@ -184,6 +184,8 @@
     private final DiffSide sideB;
     private final String revA;
     private final String revB;
+    private final String hashA;
+    private final String hashB;
     private final FileResource resource;
     @Nullable private final PatchSet basePatchSet;
 
@@ -202,6 +204,7 @@
       this.sideB = sideB;
 
       revA = basePatchSet != null ? basePatchSet.refName() : sideA.fileInfo().commitId;
+      hashA = sideA.fileInfo().commitId;
 
       RevisionResource revision = resource.getRevision();
       revB =
@@ -209,8 +212,9 @@
               .getEdit()
               .map(edit -> edit.getRefName())
               .orElseGet(() -> revision.getPatchSet().refName());
+      hashB = sideB.fileInfo().commitId;
 
-      logger.atFine().log("revA = %s, revB = %s", revA, revB);
+      logger.atFine().log("revA = %s, hashA = %s, revB = %s, hashB = %s", revA, hashA, revB, hashB);
     }
 
     @Override
@@ -234,14 +238,19 @@
     @Override
     public ImmutableList<WebLinkInfo> getFileWebLinks(DiffSide.Type type) {
       String rev = getSideRev(type);
+      String hash = getSideHash(type);
       DiffSide side = getDiffSide(type);
-      return webLinks.getFileLinks(projectName.get(), rev, side.fileName());
+      return webLinks.getFileLinks(projectName.get(), rev, hash, side.fileName());
     }
 
     private String getSideRev(DiffSide.Type sideType) {
       return DiffSide.Type.SIDE_A == sideType ? revA : revB;
     }
 
+    private String getSideHash(DiffSide.Type sideType) {
+      return DiffSide.Type.SIDE_A == sideType ? hashA : hashB;
+    }
+
     private DiffSide getDiffSide(DiffSide.Type sideType) {
       return DiffSide.Type.SIDE_A == sideType ? sideA : sideB;
     }
diff --git a/java/com/google/gerrit/server/restapi/change/QueryChanges.java b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
index 91fa2f0..2c15bc9 100644
--- a/java/com/google/gerrit/server/restapi/change/QueryChanges.java
+++ b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
@@ -27,6 +27,7 @@
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.index.query.QueryRequiresAuthException;
 import com.google.gerrit.index.query.QueryResult;
+import com.google.gerrit.server.AnonymousUser;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.DynamicOptions;
 import com.google.gerrit.server.change.ChangeJson;
@@ -95,7 +96,9 @@
     this.start = start;
   }
 
-  @Option(name = "--no-limit", usage = "Return all results, overriding the default limit")
+  @Option(
+      name = "--no-limit",
+      usage = "Return all results, overriding the default limit. Ignored for anonymous users.")
   public void setNoLimit(boolean on) {
     this.noLimit = on;
   }
@@ -168,7 +171,7 @@
     if (start != null) {
       queryProcessor.setStart(start);
     }
-    if (noLimit != null) {
+    if (noLimit != null && !AnonymousUser.class.isAssignableFrom(userProvider.get().getClass())) {
       queryProcessor.setNoLimit(noLimit);
     }
     if (skipVisibility != null) {
diff --git a/java/com/google/gerrit/util/cli/CmdLineParser.java b/java/com/google/gerrit/util/cli/CmdLineParser.java
index c374691..7c42797 100644
--- a/java/com/google/gerrit/util/cli/CmdLineParser.java
+++ b/java/com/google/gerrit/util/cli/CmdLineParser.java
@@ -456,7 +456,7 @@
 
     MyParser(Object bean) {
       super(bean, ParserProperties.defaults().withAtSyntax(false));
-      parseAdditionalOptions(bean, new HashSet<>());
+      parseAdditionalOptions("", bean, new HashSet<>());
       addOptionsWithMetRequirements();
       ensureOptionsInitialized();
     }
@@ -527,7 +527,7 @@
       }
     }
 
-    private void parseAdditionalOptions(Object bean, Set<Object> parsedBeans) {
+    private void parseAdditionalOptions(String prefix, Object bean, Set<Object> parsedBeans) {
       for (Class<?> c = bean.getClass(); c != null; c = c.getSuperclass()) {
         for (Field f : c.getDeclaredFields()) {
           if (f.isAnnotationPresent(Options.class)) {
@@ -537,7 +537,8 @@
             } catch (IllegalAccessException e) {
               throw new IllegalAnnotationError(e);
             }
-            parseWithPrefix(f.getAnnotation(Options.class).prefix(), additionalBean, parsedBeans);
+            parseWithPrefix(
+                prefix + f.getAnnotation(Options.class).prefix(), additionalBean, parsedBeans);
           }
         }
       }
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 1a79b53..db7d302 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -2948,7 +2948,7 @@
   }
 
   @Test
-  public void queryChangesNoLimit() throws Exception {
+  public void queryChangesNoLimitRegisteredUser() throws Exception {
     projectOperations
         .allProjectsForUpdate()
         .add(
@@ -2966,6 +2966,26 @@
   }
 
   @Test
+  public void queryChangesNoLimitIgnoredForAnonymousUser() throws Exception {
+    int limit = 2;
+    projectOperations
+        .allProjectsForUpdate()
+        .add(
+            allowCapability(GlobalCapability.QUERY_LIMIT)
+                .group(SystemGroupBackend.ANONYMOUS_USERS)
+                .range(0, limit))
+        .update();
+    for (int i = 0; i < 3; i++) {
+      createChange();
+    }
+    requestScopeOperations.setApiUserAnonymous();
+    List<ChangeInfo> resultsWithDefaultLimit = gApi.changes().query().get();
+    List<ChangeInfo> resultsWithNoLimit = gApi.changes().query().withNoLimit().get();
+    assertThat(resultsWithDefaultLimit).hasSize(limit);
+    assertThat(resultsWithNoLimit).hasSize(limit);
+  }
+
+  @Test
   public void queryChangesStart() throws Exception {
     PushOneCommit.Result r1 = createChange();
     createChange();
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
index dcd274d..efd3cea 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
@@ -50,6 +50,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.webui.EditWebLink;
+import com.google.gerrit.extensions.webui.FileWebLink;
 import com.google.gerrit.server.patch.DiffOperations;
 import com.google.gerrit.server.patch.DiffOptions;
 import com.google.gerrit.server.patch.filediff.FileDiffOutput;
@@ -197,6 +198,26 @@
   }
 
   @Test
+  public void gitwebFileWebLinkIncludedInDiff() throws Exception {
+    try (Registration registration = newGitwebFileWebLink()) {
+      String fileName = "foo.txt";
+      String fileContent = "bar\n";
+      PushOneCommit.Result result = createChange("Add a file", fileName, fileContent);
+      DiffInfo info =
+          gApi.changes()
+              .id(result.getChangeId())
+              .revision(result.getCommit().name())
+              .file(fileName)
+              .diff();
+      assertThat(info.metaB.webLinks).hasSize(1);
+      assertThat(info.metaB.webLinks.get(0).url)
+          .isEqualTo(
+              String.format(
+                  "http://gitweb/?p=%s;hb=%s;f=%s", project, result.getCommit().name(), fileName));
+    }
+  }
+
+  @Test
   public void deletedFileIsIncludedInDiff() throws Exception {
     gApi.changes().id(changeId).edit().deleteFile(FILE_NAME);
     gApi.changes().id(changeId).edit().publish();
@@ -2965,6 +2986,21 @@
     return extensionRegistry.newRegistration().add(webLink);
   }
 
+  private Registration newGitwebFileWebLink() {
+    FileWebLink fileWebLink =
+        new FileWebLink() {
+          @Override
+          public WebLinkInfo getFileWebLink(
+              String projectName, String revision, String hash, String fileName) {
+            return new WebLinkInfo(
+                "name",
+                "imageURL",
+                String.format("http://gitweb/?p=%s;hb=%s;f=%s", projectName, hash, fileName));
+          }
+        };
+    return extensionRegistry.newRegistration().add(fileWebLink);
+  }
+
   private String updatedCommitMessage() {
     return "An unchanged patchset\n\nChange-Id: " + changeId;
   }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AttentionSetIT.java b/javatests/com/google/gerrit/acceptance/rest/change/AttentionSetIT.java
index b034a42..e013267 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AttentionSetIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AttentionSetIT.java
@@ -1665,11 +1665,7 @@
 
     // Add preference for the user such that they only receive an email on changes that require
     // their attention.
-    requestScopeOperations.setApiUser(user.id());
-    GeneralPreferencesInfo prefs = gApi.accounts().self().getPreferences();
-    prefs.emailStrategy = EmailStrategy.ATTENTION_SET_ONLY;
-    gApi.accounts().self().setPreferences(prefs);
-    requestScopeOperations.setApiUser(admin.id());
+    setEmailStrategyForUser(EmailStrategy.ATTENTION_SET_ONLY);
 
     // Add user to attention set. They receive an email since they are in the attention set.
     change(r).addReviewer(user.id().toString());
@@ -1743,11 +1739,7 @@
   public void attentionSetWithEmailFilterImpactingOnlyChangeEmails() throws Exception {
     // Add preference for the user such that they only receive an email on changes that require
     // their attention.
-    requestScopeOperations.setApiUser(user.id());
-    GeneralPreferencesInfo prefs = gApi.accounts().self().getPreferences();
-    prefs.emailStrategy = EmailStrategy.ATTENTION_SET_ONLY;
-    gApi.accounts().self().setPreferences(prefs);
-    requestScopeOperations.setApiUser(admin.id());
+    setEmailStrategyForUser(EmailStrategy.ATTENTION_SET_ONLY);
 
     // Ensure emails that don't relate to changes are still sent.
     gApi.accounts().id(user.id().get()).generateHttpPassword();
@@ -2024,6 +2016,27 @@
         .isEqualTo(Operation.REMOVE);
   }
 
+  @Test
+  public void outsideAttentionSet_watchProjectEmailReceived() throws Exception {
+    setEmailStrategyForUser(EmailStrategy.ATTENTION_SET_ONLY);
+
+    requestScopeOperations.setApiUser(user.id());
+    watch(project.get());
+
+    createChange();
+
+    assertThat(sender.getMessages()).isNotEmpty();
+    sender.clear();
+  }
+
+  private void setEmailStrategyForUser(EmailStrategy es) throws Exception {
+    requestScopeOperations.setApiUser(user.id());
+    GeneralPreferencesInfo prefs = gApi.accounts().self().getPreferences();
+    prefs.emailStrategy = es;
+    gApi.accounts().self().setPreferences(prefs);
+    requestScopeOperations.setApiUser(admin.id());
+  }
+
   private List<AttentionSetUpdate> getAttentionSetUpdatesForUser(
       PushOneCommit.Result r, TestAccount account) {
     return getAttentionSetUpdates(r.getChange().getId()).stream()
diff --git a/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java b/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
index dbe255c..a8f9ff5 100644
--- a/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
+++ b/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
@@ -19,8 +19,6 @@
 import static com.google.gerrit.server.group.testing.InternalGroupSubject.internalGroups;
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 import static com.google.gerrit.truth.OptionalSubject.assertThat;
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
@@ -115,8 +113,9 @@
     GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
 
     try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
-      Throwable thrown = assertThrows(Throwable.class, () -> groupConfig.commit(metaDataUpdate));
-      assertThat(thrown.getCause(), instanceOf(ConfigInvalidException.class));
+      IOException thrown =
+          assertThrows(IOException.class, () -> groupConfig.commit(metaDataUpdate));
+      assertThat(thrown.getCause()).isInstanceOf(ConfigInvalidException.class);
       assertThat(thrown).hasMessageThat().contains("Name of the group " + groupUuid);
     }
   }
@@ -137,8 +136,9 @@
     GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
 
     try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
-      Throwable thrown = assertThrows(Throwable.class, () -> groupConfig.commit(metaDataUpdate));
-      assertThat(thrown.getCause(), instanceOf(ConfigInvalidException.class));
+      IOException thrown =
+          assertThrows(IOException.class, () -> groupConfig.commit(metaDataUpdate));
+      assertThat(thrown.getCause()).isInstanceOf(ConfigInvalidException.class);
       assertThat(thrown).hasMessageThat().contains("ID of the group " + groupUuid);
     }
   }
@@ -213,8 +213,9 @@
     groupConfig.setGroupDelta(groupDelta, auditLogFormatter);
 
     try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
-      Throwable thrown = assertThrows(Throwable.class, () -> groupConfig.commit(metaDataUpdate));
-      assertThat(thrown.getCause(), instanceOf(ConfigInvalidException.class));
+      IOException thrown =
+          assertThrows(IOException.class, () -> groupConfig.commit(metaDataUpdate));
+      assertThat(thrown.getCause()).isInstanceOf(ConfigInvalidException.class);
       assertThat(thrown).hasMessageThat().contains("Owner UUID of the group " + groupUuid);
     }
   }
@@ -521,8 +522,9 @@
     groupConfig.setGroupDelta(groupDelta, auditLogFormatter);
 
     try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
-      Throwable thrown = assertThrows(Throwable.class, () -> groupConfig.commit(metaDataUpdate));
-      assertThat(thrown.getCause(), instanceOf(ConfigInvalidException.class));
+      IOException thrown =
+          assertThrows(IOException.class, () -> groupConfig.commit(metaDataUpdate));
+      assertThat(thrown.getCause()).isInstanceOf(ConfigInvalidException.class);
       assertThat(thrown).hasMessageThat().contains("Name of the group " + groupUuid);
     }
   }
@@ -585,8 +587,9 @@
     groupConfig.setGroupDelta(groupDelta, auditLogFormatter);
 
     try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
-      Throwable thrown = assertThrows(Throwable.class, () -> groupConfig.commit(metaDataUpdate));
-      assertThat(thrown.getCause(), instanceOf(ConfigInvalidException.class));
+      IOException thrown =
+          assertThrows(IOException.class, () -> groupConfig.commit(metaDataUpdate));
+      assertThat(thrown.getCause()).isInstanceOf(ConfigInvalidException.class);
       assertThat(thrown).hasMessageThat().contains("Owner UUID of the group " + groupUuid);
     }
   }
diff --git a/lib/BUILD b/lib/BUILD
index 7b48e6a..7aa9a45 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -488,17 +488,17 @@
     data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
     visibility = ["//visibility:public"],
     exports = [
-        ":hamcrest-core",
+        ":hamcrest",
         "@junit//jar",
     ],
-    runtime_deps = [":hamcrest-core"],
+    runtime_deps = [":hamcrest"],
 )
 
 java_library(
-    name = "hamcrest-core",
+    name = "hamcrest",
     data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
     visibility = ["//visibility:public"],
-    exports = ["@hamcrest-core//jar"],
+    exports = ["@hamcrest//jar"],
 )
 
 java_library(
diff --git a/lib/nongoogle_test.sh b/lib/nongoogle_test.sh
index 9e9d750..51c50bf 100755
--- a/lib/nongoogle_test.sh
+++ b/lib/nongoogle_test.sh
@@ -25,9 +25,14 @@
 guice-assistedinject
 guice-library
 guice-servlet
+hamcrest
+impl-log4j
 j2objc
+jcl-over-slf4j
 jimfs
 jruby
+log-api
+log-ext
 log4j
 lucene-analyzers-common
 lucene-core
diff --git a/plugins/gitiles b/plugins/gitiles
index 557cca1..44b5333 160000
--- a/plugins/gitiles
+++ b/plugins/gitiles
@@ -1 +1 @@
-Subproject commit 557cca12c1d39fc27cf8c6ce764c0ee091632b90
+Subproject commit 44b5333d08d02c15334f81906134a5613d3f0a3b
diff --git a/tools/deps.bzl b/tools/deps.bzl
index d243957..81708da 100644
--- a/tools/deps.bzl
+++ b/tools/deps.bzl
@@ -3,7 +3,6 @@
 
 CAFFEINE_VERS = "2.9.2"
 ANTLR_VERS = "3.5.2"
-SLF4J_VERS = "1.7.33"
 COMMONMARK_VERS = "0.10.0"
 FLEXMARK_VERS = "0.50.50"
 GREENMAIL_VERS = "1.5.5"
@@ -68,11 +67,6 @@
     )
 
     # JGit's transitive dependencies
-    maven_jar(
-        name = "hamcrest",
-        artifact = "org.hamcrest:hamcrest:2.2",
-        sha1 = "1820c0968dba3a11a1b30669bb1f01978a91dedc",
-    )
 
     maven_jar(
         name = "javaewah",
@@ -112,24 +106,6 @@
     )
 
     maven_jar(
-        name = "log-api",
-        artifact = "org.slf4j:slf4j-api:" + SLF4J_VERS,
-        sha1 = "d375aa1b98d34d5ddf73a3f19eaad66e98975b12",
-    )
-
-    maven_jar(
-        name = "log-ext",
-        artifact = "org.slf4j:slf4j-ext:" + SLF4J_VERS,
-        sha1 = "00da03640ae1ad57f964dcaa542fb5d804dce8a6",
-    )
-
-    maven_jar(
-        name = "jcl-over-slf4j",
-        artifact = "org.slf4j:jcl-over-slf4j:" + SLF4J_VERS,
-        sha1 = "28c441128bc81b6d95cc2857ae5bb46ae5bf658b",
-    )
-
-    maven_jar(
         name = "json-smart",
         artifact = "net.minidev:json-smart:1.1.1",
         sha1 = "24a2f903d25e004de30ac602c5b47f2d4e420a59",
diff --git a/tools/maven/package.bzl b/tools/maven/package.bzl
index b25656d..eacb02b 100644
--- a/tools/maven/package.bzl
+++ b/tools/maven/package.bzl
@@ -40,7 +40,7 @@
         src = {},
         doc = {},
         war = {}):
-    build_cmd = ["bazel_cmd", "build"]
+    build_cmd = ["bazel_cmd", "build", "--java_toolchain=//tools:error_prone_warnings_toolchain_java11"]
     mvn_cmd = ["python", "tools/maven/mvn.py", "-v", version]
     api_cmd = mvn_cmd[:]
     api_targets = []
diff --git a/tools/nongoogle.bzl b/tools/nongoogle.bzl
index 612d897..d8f7020 100644
--- a/tools/nongoogle.bzl
+++ b/tools/nongoogle.bzl
@@ -18,8 +18,34 @@
 
     maven_jar(
         name = "log4j",
-        artifact = "ch.qos.reload4j:reload4j:1.2.18.1",
-        sha1 = "7075022a11e18c1ad230de5be074e0c691fed17b",
+        artifact = "ch.qos.reload4j:reload4j:1.2.19",
+        sha1 = "4eae9978468c5e885a6fb44df7e2bbc07a20e6ce",
+    )
+
+    SLF4J_VERS = "1.7.36"
+
+    maven_jar(
+        name = "log-api",
+        artifact = "org.slf4j:slf4j-api:" + SLF4J_VERS,
+        sha1 = "6c62681a2f655b49963a5983b8b0950a6120ae14",
+    )
+
+    maven_jar(
+        name = "log-ext",
+        artifact = "org.slf4j:slf4j-ext:" + SLF4J_VERS,
+        sha1 = "99f282aea4b6dbca04d00f0ade6e5ed61ee7091a",
+    )
+
+    maven_jar(
+        name = "impl-log4j",
+        artifact = "org.slf4j:slf4j-reload4j:" + SLF4J_VERS,
+        sha1 = "db708f7d959dee1857ac524636e85ecf2e1781c1",
+    )
+
+    maven_jar(
+        name = "jcl-over-slf4j",
+        artifact = "org.slf4j:jcl-over-slf4j:" + SLF4J_VERS,
+        sha1 = "d877e195a05aca4a2f1ad2ff14bfec1393af4b5e",
     )
 
     maven_jar(
@@ -248,3 +274,10 @@
         artifact = "org.apache.lucene:lucene-queryparser:" + LUCENE_VERS,
         sha1 = "2db9ca0086a4b8e0b9bc9f08a9b420303168e37c",
     )
+
+    # JGit's transitive dependencies
+    maven_jar(
+        name = "hamcrest",
+        artifact = "org.hamcrest:hamcrest:2.2",
+        sha1 = "1820c0968dba3a11a1b30669bb1f01978a91dedc",
+    )