Merge "Update jgit to v6.8.0.202311291450-r-91-gc35deb6d8"
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 4747957..578fabf 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -1044,6 +1044,41 @@
   }
 ----
 
+[[get-message]]
+=== Get Commit Message
+--
+'GET /changes/link:#change-id[\{change-id\}]/message'
+--
+
+Returns the commit message of the change (from the current patch set).
+
+.Request
+----
+  GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/message HTTP/1.0
+----
+
+The commit message is returned as a link:#commit-message-info[
+CommitMessageInfo] entity.
+
+Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "subject": "Add feature X",
+    "full_message": "Add Feature X\n\nFeature X helps with foo.\n\nBug: 123\nChange-Id: I10394472cbd17dd12454f229e4f6de00b143a444\n",
+    "footers": {
+      "Bug": "123",
+      "Change-Id": "I10394472cbd17dd12454f229e4f6de00b143a444"
+    }
+  }
+----
+
+----
+
 [[set-message]]
 === Set Commit Message
 --
@@ -7643,6 +7678,21 @@
 link:#web-link-info[WebLinkInfo] entities.
 |===========================
 
+[[commit-message-info]]
+=== CommitMessageInfo
+The `CommitMessageInfo` entity contains information about the commit
+message of a change.
+
+[options="header",cols="1,6"]
+|============================
+|Field Name     |Description
+|`subject`      |The subject of the change (first line of the commit
+message).
+|`full_message` |Full commit message of the change.
+|`footers`      |The footers from the commit message as a map of
+key-value pairs.
+|============================
+
 [[commit-message-input]]
 === CommitMessageInput
 The `CommitMessageInput` entity contains information for changing
diff --git a/Documentation/user-suggest-edits.txt b/Documentation/user-suggest-edits.txt
index 3b2c83a..55181a2 100644
--- a/Documentation/user-suggest-edits.txt
+++ b/Documentation/user-suggest-edits.txt
@@ -1,7 +1,7 @@
-= Gerrit Code Review - User suggested edits (Experiment)
+= Gerrit Code Review - User suggested edits
 
 Easy and fast way for reviewers to suggest code changes that can be easily applied
-by change owner.
+by the change owner.
 
 == Reviewer workflow
 
@@ -26,15 +26,16 @@
 
 == Author workflow
 
-You can apply one or more suggested fixes. When suggested fix is applied - it creates
-a change edit that you can modify in gerrit. link:user-inline-edit.html#editing-change[More about editing mode.]
+You can apply one or more suggested edits. When a suggested edit is applied it
+creates a change edit that you can further modify in Gerrit. You can read more
+about all the features of link:user-inline-edit.html#editing-change[change edit mode].
 
-FYI: Publishing a new patchset in gerrit will make gerrit change out of sync with
-your local git. You can checkout latest gerrit by using commands from download drop-down panel.
-link:user-review-ui.html#download[More about download drop-down panel]
+FYI: Publishing a new patchset in Gerrit will make your Gerrit change out of
+sync with your local git commit. You can checkout the latest Gerrit patchset
+by using the commands from the link:user-review-ui.html#download[download drop-down panel].
 
-You can use copy to clipboard button to copy suggestion to clipboard and then you can paste it
-in your editor.
+Alternatively, you can use the copy to clipboard button to copy a suggested
+edit to your clipboard and then you can paste it into your editor.
 
 == Generate Suggestion
 
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index b61c9cd..4285f3c 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -937,7 +937,7 @@
             if (accountState.userName().isPresent()) {
               assertThat(fullMessage).doesNotContain(accountState.userName().get());
             }
-            List<String> allEmails =
+            ImmutableList<String> allEmails =
                 accountState.externalIds().stream()
                     .map(ExternalId::email)
                     .filter(Objects::nonNull)
diff --git a/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java b/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java
index fe845c0..3dced64 100644
--- a/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java
@@ -20,6 +20,7 @@
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.gerrit.acceptance.testsuite.change.ChangeOperations;
 import com.google.gerrit.common.Nullable;
@@ -297,14 +298,15 @@
     assertThat(getter.call(id).get(id)).isNull();
   }
 
-  protected static List<PluginDefinedInfo> pluginInfoFromSingletonList(
+  protected static ImmutableList<PluginDefinedInfo> pluginInfoFromSingletonList(
       List<ChangeInfo> changeInfos) {
     assertThat(changeInfos).hasSize(1);
     return pluginInfoFromChangeInfo(changeInfos.get(0));
   }
 
   @Nullable
-  protected static List<PluginDefinedInfo> pluginInfoFromChangeInfo(ChangeInfo changeInfo) {
+  protected static ImmutableList<PluginDefinedInfo> pluginInfoFromChangeInfo(
+      ChangeInfo changeInfo) {
     List<PluginDefinedInfo> pluginInfo = changeInfo.plugins;
     if (pluginInfo == null) {
       return null;
diff --git a/java/com/google/gerrit/acceptance/ProjectResetter.java b/java/com/google/gerrit/acceptance/ProjectResetter.java
index cba5a86..c71641b 100644
--- a/java/com/google/gerrit/acceptance/ProjectResetter.java
+++ b/java/com/google/gerrit/acceptance/ProjectResetter.java
@@ -21,6 +21,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Sets;
@@ -151,7 +152,7 @@
     }
 
     public static class Builder {
-      private final Multimap<Project.NameKey, String> refsByProject;
+      private final ListMultimap<Project.NameKey, String> refsByProject;
 
       public Builder() {
         this.refsByProject = MultimapBuilder.hashKeys().arrayListValues().build();
@@ -185,12 +186,12 @@
   private final Multimap<Project.NameKey, String> refsPatternByProject;
 
   // State to which to reset to.
-  private final Multimap<Project.NameKey, RefState> savedRefStatesByProject;
+  private final ListMultimap<Project.NameKey, RefState> savedRefStatesByProject;
 
   // Results of the resetting
-  private Multimap<Project.NameKey, String> keptRefsByProject;
-  private Multimap<Project.NameKey, String> restoredRefsByProject;
-  private Multimap<Project.NameKey, String> deletedRefsByProject;
+  private ListMultimap<Project.NameKey, String> keptRefsByProject;
+  private ListMultimap<Project.NameKey, String> restoredRefsByProject;
+  private ListMultimap<Project.NameKey, String> deletedRefsByProject;
 
   private ProjectResetter(
       GitRepositoryManager repoManager,
@@ -231,13 +232,13 @@
   }
 
   /** Read the states of all matching refs. */
-  private Multimap<Project.NameKey, RefState> readRefStates() throws IOException {
-    Multimap<Project.NameKey, RefState> refStatesByProject =
+  private ListMultimap<Project.NameKey, RefState> readRefStates() throws IOException {
+    ListMultimap<Project.NameKey, RefState> refStatesByProject =
         MultimapBuilder.hashKeys().arrayListValues().build();
     for (Map.Entry<Project.NameKey, Collection<String>> e :
         refsPatternByProject.asMap().entrySet()) {
       try (Repository repo = repoManager.openRepository(e.getKey())) {
-        Collection<Ref> refs = repo.getRefDatabase().getRefs();
+        List<Ref> refs = repo.getRefDatabase().getRefs();
         for (String refPattern : e.getValue()) {
           RefPatternMatcher matcher = RefPatternMatcher.getMatcher(refPattern);
           for (Ref ref : refs) {
@@ -281,7 +282,7 @@
     for (Map.Entry<Project.NameKey, Collection<String>> e :
         refsPatternByProject.asMap().entrySet()) {
       try (Repository repo = repoManager.openRepository(e.getKey())) {
-        Collection<Ref> nonRestoredRefs =
+        Set<Ref> nonRestoredRefs =
             repo.getRefDatabase().getRefs().stream()
                 .filter(
                     r ->
diff --git a/java/com/google/gerrit/acceptance/PushOneCommit.java b/java/com/google/gerrit/acceptance/PushOneCommit.java
index 00d4e43..ba1dbbc 100644
--- a/java/com/google/gerrit/acceptance/PushOneCommit.java
+++ b/java/com/google/gerrit/acceptance/PushOneCommit.java
@@ -22,6 +22,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
@@ -533,7 +534,7 @@
 
     private void assertReviewers(
         Change c, ReviewerStateInternal state, List<TestAccount> expectedReviewers) {
-      Iterable<Account.Id> actualIds =
+      ImmutableSet<Account.Id> actualIds =
           approvalsUtil.getReviewers(notesFactory.createChecked(c)).byState(state);
       assertThat(actualIds)
           .containsExactlyElementsIn(Sets.newHashSet(TestAccount.ids(expectedReviewers)));
diff --git a/java/com/google/gerrit/acceptance/testsuite/change/TestChangeCreation.java b/java/com/google/gerrit/acceptance/testsuite/change/TestChangeCreation.java
index a84e3f04..eb714d45 100644
--- a/java/com/google/gerrit/acceptance/testsuite/change/TestChangeCreation.java
+++ b/java/com/google/gerrit/acceptance/testsuite/change/TestChangeCreation.java
@@ -19,6 +19,7 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import com.google.gerrit.acceptance.testsuite.ThrowingFunction;
 import com.google.gerrit.common.UsedAt;
 import com.google.gerrit.entities.Account;
@@ -246,6 +247,7 @@
      *
      * @return the {@code Change.Id} of the created change
      */
+    @CanIgnoreReturnValue
     public Change.Id create() {
       TestChangeCreation changeUpdate = build();
       return changeUpdate.changeCreator().applyAndThrowSilently(changeUpdate);
diff --git a/java/com/google/gerrit/extensions/api/changes/ChangeApi.java b/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
index a663e25..1c83bc2 100644
--- a/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
+++ b/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.extensions.common.ChangeInfoDifference;
 import com.google.gerrit.extensions.common.ChangeMessageInfo;
 import com.google.gerrit.extensions.common.CommentInfo;
+import com.google.gerrit.extensions.common.CommitMessageInfo;
 import com.google.gerrit.extensions.common.CommitMessageInput;
 import com.google.gerrit.extensions.common.MergePatchSetInput;
 import com.google.gerrit.extensions.common.PureRevertInfo;
@@ -321,6 +322,8 @@
    */
   ChangeEditApi edit() throws RestApiException;
 
+  CommitMessageInfo getMessage() throws RestApiException;
+
   /** Create a new patch set with a new commit message. */
   default void setMessage(String message) throws RestApiException {
     CommitMessageInput in = new CommitMessageInput();
@@ -717,6 +720,11 @@
     }
 
     @Override
+    public CommitMessageInfo getMessage() throws RestApiException {
+      throw new NotImplementedException();
+    }
+
+    @Override
     public void setMessage(CommitMessageInput in) throws RestApiException {
       throw new NotImplementedException();
     }
diff --git a/java/com/google/gerrit/extensions/common/CommitMessageInfo.java b/java/com/google/gerrit/extensions/common/CommitMessageInfo.java
new file mode 100644
index 0000000..fdd7cc3
--- /dev/null
+++ b/java/com/google/gerrit/extensions/common/CommitMessageInfo.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2024 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.extensions.common;
+
+import java.util.Map;
+
+/** Representation of a commit message used in the API. */
+public class CommitMessageInfo {
+  /**
+   * The subject of the change.
+   *
+   * <p>First line of the commit message.
+   */
+  public String subject;
+
+  /** Full commit message of the change. */
+  public String fullMessage;
+
+  /**
+   * The footers from the commit message.
+   *
+   * <p>Key-value pairs from the last paragraph of the commit message.
+   */
+  public Map<String, String> footers;
+}
diff --git a/java/com/google/gerrit/httpd/init/WebAppInitializer.java b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
index fe4eb1c..8d7fe67 100644
--- a/java/com/google/gerrit/httpd/init/WebAppInitializer.java
+++ b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
@@ -51,6 +51,7 @@
 import com.google.gerrit.server.LibModuleType;
 import com.google.gerrit.server.ModuleOverloader;
 import com.google.gerrit.server.StartupChecks.StartupChecksModule;
+import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountCacheImpl;
 import com.google.gerrit.server.account.AccountDeactivator.AccountDeactivatorModule;
 import com.google.gerrit.server.account.InternalAccountDirectory.InternalAccountDirectoryModule;
@@ -310,7 +311,16 @@
     modules.add(new SysExecutorModule());
     modules.add(new DiffExecutorModule());
     modules.add(new MimeUtil2Module());
+
     modules.add(cfgInjector.getInstance(AccountCacheImpl.AccountCacheModule.class));
+    modules.add(
+        new AbstractModule() {
+          @Override
+          protected void configure() {
+            bind(AccountCache.class).to(AccountCacheImpl.class);
+          }
+        });
+
     modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
     modules.add(new GerritApiModule());
     modules.add(new ProjectQueryBuilderModule());
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index d1307b1..ff00391 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -60,6 +60,7 @@
 import com.google.gerrit.server.LibModuleType;
 import com.google.gerrit.server.ModuleOverloader;
 import com.google.gerrit.server.StartupChecks.StartupChecksModule;
+import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountCacheImpl;
 import com.google.gerrit.server.account.AccountDeactivator.AccountDeactivatorModule;
 import com.google.gerrit.server.account.InternalAccountDirectory.InternalAccountDirectoryModule;
@@ -469,7 +470,15 @@
     modules.add(new SysExecutorModule());
     modules.add(new DiffExecutorModule());
     modules.add(new MimeUtil2Module());
+
     modules.add(cfgInjector.getInstance(AccountCacheImpl.AccountCacheModule.class));
+    modules.add(
+        new AbstractModule() {
+          @Override
+          protected void configure() {
+            bind(AccountCache.class).to(AccountCacheImpl.class);
+          }
+        });
 
     modules.add(new AccountNoteDbWriteStorageModule());
     modules.add(new AccountNoteDbReadStorageModule());
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 55312b1..58abe12 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -34,6 +34,7 @@
 import com.google.gerrit.server.LibModuleLoader;
 import com.google.gerrit.server.LibModuleType;
 import com.google.gerrit.server.ModuleOverloader;
+import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountCacheImpl;
 import com.google.gerrit.server.account.AccountVisibilityProvider;
 import com.google.gerrit.server.account.CapabilityCollection;
@@ -229,6 +230,7 @@
     bind(WorkInProgressStateChanged.class).toInstance(WorkInProgressStateChanged.DISABLED);
     bind(AccountVisibility.class).toProvider(AccountVisibilityProvider.class).in(SINGLETON);
     bind(AttentionSetObserver.class).toInstance(AttentionSetObserver.DISABLED);
+    bind(AccountCache.class).to(AccountCacheImpl.class);
 
     ModuleOverloader.override(
             modules,
diff --git a/java/com/google/gerrit/server/account/AccountCacheImpl.java b/java/com/google/gerrit/server/account/AccountCacheImpl.java
index 3cddda3..8549409 100644
--- a/java/com/google/gerrit/server/account/AccountCacheImpl.java
+++ b/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -67,7 +67,6 @@
           .loader(Loader.class);
 
       bind(AccountCacheImpl.class);
-      bind(AccountCache.class).to(AccountCacheImpl.class);
     }
   }
 
diff --git a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index 33dbf0c..a3df786 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -50,6 +50,7 @@
 import com.google.gerrit.extensions.common.ChangeInfoDifference;
 import com.google.gerrit.extensions.common.ChangeMessageInfo;
 import com.google.gerrit.extensions.common.CommentInfo;
+import com.google.gerrit.extensions.common.CommitMessageInfo;
 import com.google.gerrit.extensions.common.CommitMessageInput;
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.common.InputWithMessage;
@@ -84,6 +85,7 @@
 import com.google.gerrit.server.restapi.change.GetChange;
 import com.google.gerrit.server.restapi.change.GetCustomKeyedValues;
 import com.google.gerrit.server.restapi.change.GetHashtags;
+import com.google.gerrit.server.restapi.change.GetMessage;
 import com.google.gerrit.server.restapi.change.GetMetaDiff;
 import com.google.gerrit.server.restapi.change.GetPureRevert;
 import com.google.gerrit.server.restapi.change.GetTopic;
@@ -172,6 +174,7 @@
   private final DeletePrivate deletePrivate;
   private final SetWorkInProgress setWip;
   private final SetReadyForReview setReady;
+  private final GetMessage getMessage;
   private final PutMessage putMessage;
   private final Provider<GetPureRevert> getPureRevertProvider;
   private final DynamicOptionParser dynamicOptionParser;
@@ -224,6 +227,7 @@
       DeletePrivate deletePrivate,
       SetWorkInProgress setWip,
       SetReadyForReview setReady,
+      GetMessage getMessage,
       PutMessage putMessage,
       Provider<GetPureRevert> getPureRevertProvider,
       DynamicOptionParser dynamicOptionParser,
@@ -274,6 +278,7 @@
     this.deletePrivate = deletePrivate;
     this.setWip = setWip;
     this.setReady = setReady;
+    this.getMessage = getMessage;
     this.putMessage = putMessage;
     this.getPureRevertProvider = getPureRevertProvider;
     this.dynamicOptionParser = dynamicOptionParser;
@@ -561,6 +566,15 @@
   }
 
   @Override
+  public CommitMessageInfo getMessage() throws RestApiException {
+    try {
+      return getMessage.apply(change).value();
+    } catch (Exception e) {
+      throw asRestApiException("Cannot get message", e);
+    }
+  }
+
+  @Override
   public void setMessage(CommitMessageInput in) throws RestApiException {
     try {
       @SuppressWarnings("unused")
diff --git a/java/com/google/gerrit/server/events/EventFactory.java b/java/com/google/gerrit/server/events/EventFactory.java
index 46fe994..2827f59 100644
--- a/java/com/google/gerrit/server/events/EventFactory.java
+++ b/java/com/google/gerrit/server/events/EventFactory.java
@@ -76,7 +76,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.Set;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -601,7 +600,7 @@
   }
 
   private void addHashTags(ChangeAttribute changeAttribute, ChangeNotes notes) {
-    Set<String> hashtags = notes.load().getHashtags();
+    ImmutableSet<String> hashtags = notes.load().getHashtags();
     if (!hashtags.isEmpty()) {
       changeAttribute.hashtags = new ArrayList<>(hashtags.size());
       changeAttribute.hashtags.addAll(hashtags);
diff --git a/java/com/google/gerrit/server/events/EventTypes.java b/java/com/google/gerrit/server/events/EventTypes.java
index 4cc7198..d08a3c2 100644
--- a/java/com/google/gerrit/server/events/EventTypes.java
+++ b/java/com/google/gerrit/server/events/EventTypes.java
@@ -72,7 +72,7 @@
    *
    * @return ImmutableMap of event types, Event classes.
    */
-  public static Map<String, Class<?>> getRegisteredEvents() {
+  public static ImmutableMap<String, Class<?>> getRegisteredEvents() {
     return ImmutableMap.copyOf(typesByString);
   }
 }
diff --git a/java/com/google/gerrit/server/git/ChangesByProjectCacheImpl.java b/java/com/google/gerrit/server/git/ChangesByProjectCacheImpl.java
index 3ca9f99..ed16006 100644
--- a/java/com/google/gerrit/server/git/ChangesByProjectCacheImpl.java
+++ b/java/com/google/gerrit/server/git/ChangesByProjectCacheImpl.java
@@ -119,13 +119,13 @@
         ours == projectChanges ? "Scanning" : "Updating");
   }
 
-  private Collection<ChangeData> queryChangeDatasAndLoad(Project.NameKey project) {
-    Collection<ChangeData> cds = queryChangeDatas(project);
+  private List<ChangeData> queryChangeDatasAndLoad(Project.NameKey project) {
+    List<ChangeData> cds = queryChangeDatas(project);
     cache.put(project, new CachedProjectChanges(cds));
     return cds;
   }
 
-  private Collection<ChangeData> queryChangeDatas(Project.NameKey project) {
+  private List<ChangeData> queryChangeDatas(Project.NameKey project) {
     try (TraceTimer timer =
         TraceContext.newTimer(
             "Querying changes of project", Metadata.builder().projectName(project.get()).build())) {
diff --git a/java/com/google/gerrit/server/git/GroupCollector.java b/java/com/google/gerrit/server/git/GroupCollector.java
index 72d8bd9..b87eeea 100644
--- a/java/com/google/gerrit/server/git/GroupCollector.java
+++ b/java/com/google/gerrit/server/git/GroupCollector.java
@@ -75,11 +75,11 @@
 public class GroupCollector {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  public static List<String> getDefaultGroups(ObjectId commit) {
+  public static ImmutableList<String> getDefaultGroups(ObjectId commit) {
     return ImmutableList.of(commit.name());
   }
 
-  public static List<String> getGroups(RevisionResource rsrc) {
+  public static ImmutableList<String> getGroups(RevisionResource rsrc) {
     if (rsrc.getEdit().isPresent()) {
       // Groups for an edit are just the base revision's groups, since they have
       // the same parent.
diff --git a/java/com/google/gerrit/server/git/receive/testing/TestRefAdvertiser.java b/java/com/google/gerrit/server/git/receive/testing/TestRefAdvertiser.java
index 4d2805d..47df03a 100644
--- a/java/com/google/gerrit/server/git/receive/testing/TestRefAdvertiser.java
+++ b/java/com/google/gerrit/server/git/receive/testing/TestRefAdvertiser.java
@@ -19,12 +19,12 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.StreamSupport;
@@ -61,7 +61,7 @@
 
   @Override
   protected void writeOne(CharSequence line) throws IOException {
-    List<String> lineParts =
+    ImmutableList<String> lineParts =
         StreamSupport.stream(Splitter.on(' ').split(line).spliterator(), false)
             .map(String::trim)
             .collect(toImmutableList());
diff --git a/java/com/google/gerrit/server/git/validators/MergeValidators.java b/java/com/google/gerrit/server/git/validators/MergeValidators.java
index 514dee1..710e688 100644
--- a/java/com/google/gerrit/server/git/validators/MergeValidators.java
+++ b/java/com/google/gerrit/server/git/validators/MergeValidators.java
@@ -101,7 +101,7 @@
       PatchSet.Id patchSetId,
       IdentifiedUser caller)
       throws MergeValidationException {
-    List<MergeValidationListener> validators =
+    ImmutableList<MergeValidationListener> validators =
         ImmutableList.of(
             new PluginMergeValidationListener(mergeValidationListeners),
             projectConfigValidatorFactory.create(),
diff --git a/java/com/google/gerrit/server/index/IndexModule.java b/java/com/google/gerrit/server/index/IndexModule.java
index c0bd62f..96870ea 100644
--- a/java/com/google/gerrit/server/index/IndexModule.java
+++ b/java/com/google/gerrit/server/index/IndexModule.java
@@ -19,8 +19,8 @@
 
 import com.google.common.base.Ticker;
 import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.gerrit.extensions.events.LifecycleListener;
@@ -65,7 +65,6 @@
 import com.google.inject.Singleton;
 import com.google.inject.multibindings.OptionalBinder;
 import java.util.Collection;
-import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import org.eclipse.jgit.lib.Config;
 
@@ -77,7 +76,7 @@
  */
 @SuppressWarnings("ProvidesMethodOutsideOfModule")
 public class IndexModule extends LifecycleModule {
-  public static final ImmutableCollection<SchemaDefinitions<?>> ALL_SCHEMA_DEFS =
+  public static final ImmutableList<SchemaDefinitions<?>> ALL_SCHEMA_DEFS =
       ImmutableList.of(
           AccountSchemaDefinitions.INSTANCE,
           ChangeSchemaDefinitions.INSTANCE,
@@ -174,9 +173,10 @@
 
     ImmutableList<IndexDefinition<?, ?, ?>> result =
         ImmutableList.of(accounts, groups, changes, projects);
-    Set<String> expected =
+    ImmutableSet<String> expected =
         FluentIterable.from(ALL_SCHEMA_DEFS).transform(SchemaDefinitions::getName).toSet();
-    Set<String> actual = FluentIterable.from(result).transform(IndexDefinition::getName).toSet();
+    ImmutableSet<String> actual =
+        FluentIterable.from(result).transform(IndexDefinition::getName).toSet();
     if (!expected.equals(actual)) {
       throw new ProvisionException(
           "need index definitions for all schemas: " + expected + " != " + actual);
diff --git a/java/com/google/gerrit/server/index/IndexUtils.java b/java/com/google/gerrit/server/index/IndexUtils.java
index 352d376..f2a2904 100644
--- a/java/com/google/gerrit/server/index/IndexUtils.java
+++ b/java/com/google/gerrit/server/index/IndexUtils.java
@@ -76,7 +76,7 @@
     // Ensure we request enough fields to construct a ChangeData. We need both
     // change ID and project, which can either come via the Change field or
     // separate fields.
-    Set<String> fs = opts.fields();
+    ImmutableSet<String> fs = opts.fields();
     if (fs.contains(CHANGE_SPEC.getName())) {
       // A Change is always sufficient.
       return fs;
@@ -93,7 +93,7 @@
    * is temporary and should be removed after the migration is done.
    */
   public static Set<String> groupFields(QueryOptions opts) {
-    Set<String> fs = opts.fields();
+    ImmutableSet<String> fs = opts.fields();
     return fs.contains(GroupField.UUID_FIELD_SPEC.getName())
         ? fs
         : Sets.union(fs, ImmutableSet.of(GroupField.UUID_FIELD_SPEC.getName()));
@@ -115,7 +115,7 @@
    * doesn't support.
    */
   public static Set<String> projectFields(QueryOptions opts) {
-    Set<String> fs = opts.fields();
+    ImmutableSet<String> fs = opts.fields();
     return fs.contains(ProjectField.NAME_SPEC.getName())
         ? fs
         : Sets.union(fs, ImmutableSet.of(ProjectField.NAME_SPEC.getName()));
diff --git a/java/com/google/gerrit/server/index/SingleVersionModule.java b/java/com/google/gerrit/server/index/SingleVersionModule.java
index 4b65e6e..bad5ffe 100644
--- a/java/com/google/gerrit/server/index/SingleVersionModule.java
+++ b/java/com/google/gerrit/server/index/SingleVersionModule.java
@@ -31,7 +31,6 @@
 import com.google.inject.util.Providers;
 import java.util.Collection;
 import java.util.Map;
-import java.util.Set;
 import org.eclipse.jgit.lib.Config;
 
 /**
@@ -59,7 +58,7 @@
   /** Listener to Gerrit's lifecycle events to specify which index versions to use. */
   @Singleton
   public static class SingleVersionListener implements LifecycleListener {
-    private final Set<String> disabled;
+    private final ImmutableSet<String> disabled;
     private final Collection<IndexDefinition<?, ?, ?>> defs;
     private final Map<String, Integer> singleVersions;
 
diff --git a/java/com/google/gerrit/server/index/account/StalenessChecker.java b/java/com/google/gerrit/server/index/account/StalenessChecker.java
index 699dfbe..f98f893 100644
--- a/java/com/google/gerrit/server/index/account/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/account/StalenessChecker.java
@@ -42,7 +42,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.Set;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
@@ -137,7 +136,7 @@
       }
     }
 
-    Set<ExternalId> extIds = externalIds.byAccount(id);
+    ImmutableSet<ExternalId> extIds = externalIds.byAccount(id);
 
     ListMultimap<ObjectId, ObjectId> extIdStates =
         parseExternalIdStates(
diff --git a/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java b/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
index 3773d435..52668c5 100644
--- a/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
+++ b/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
@@ -18,6 +18,7 @@
 import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
 
 import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
 import com.google.common.flogger.FluentLogger;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -74,7 +75,7 @@
     ProgressMonitor progress = new TextProgressMonitor(newPrintWriter(progressOut));
     progress.start(2);
     Stopwatch sw = Stopwatch.createStarted();
-    List<AccountGroup.UUID> uuids;
+    ImmutableList<AccountGroup.UUID> uuids;
     try {
       uuids = collectGroups(progress);
     } catch (IOException | ConfigInvalidException e) {
@@ -138,7 +139,7 @@
     return SiteIndexer.Result.create(sw, ok.get(), done.get(), failed.get());
   }
 
-  private List<AccountGroup.UUID> collectGroups(ProgressMonitor progress)
+  private ImmutableList<AccountGroup.UUID> collectGroups(ProgressMonitor progress)
       throws IOException, ConfigInvalidException {
     progress.beginTask("Collecting groups", ProgressMonitor.UNKNOWN);
     try {
diff --git a/java/com/google/gerrit/server/index/project/StalenessChecker.java b/java/com/google/gerrit/server/index/project/StalenessChecker.java
index b2e24e4..5f0d901 100644
--- a/java/com/google/gerrit/server/index/project/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/project/StalenessChecker.java
@@ -17,6 +17,7 @@
 import static com.google.gerrit.server.project.ProjectCache.illegalState;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.SetMultimap;
 import com.google.gerrit.entities.Project;
@@ -73,7 +74,7 @@
       return StalenessCheckResult.stale("Document %s missing from index", project);
     }
 
-    SetMultimap<Project.NameKey, RefState> indexedRefStates =
+    ImmutableSetMultimap<Project.NameKey, RefState> indexedRefStates =
         RefState.parseStates(result.get().getValue(ProjectField.REF_STATE_SPEC));
 
     SetMultimap<Project.NameKey, RefState> currentRefStates =
diff --git a/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index 0648a18..df30573 100644
--- a/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -22,6 +22,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.entities.Account;
@@ -200,7 +201,7 @@
       return;
     }
 
-    Set<Account.Id> accountIds = emails.getAccountFor(metadata.author);
+    ImmutableSet<Account.Id> accountIds = emails.getAccountFor(metadata.author);
 
     if (accountIds.size() != 1) {
       logger.atSevere().log(
diff --git a/java/com/google/gerrit/server/mail/send/ChangeEmailImpl.java b/java/com/google/gerrit/server/mail/send/ChangeEmailImpl.java
index 4ecbd52..388b0d0 100644
--- a/java/com/google/gerrit/server/mail/send/ChangeEmailImpl.java
+++ b/java/com/google/gerrit/server/mail/send/ChangeEmailImpl.java
@@ -19,6 +19,7 @@
 
 import com.google.auto.factory.AutoFactory;
 import com.google.auto.factory.Provided;
+import com.google.common.collect.ImmutableList;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Account;
@@ -58,7 +59,6 @@
 import java.time.Instant;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
@@ -88,7 +88,7 @@
 
   // Available after init or after being explicitly set.
   protected OutgoingEmail email;
-  private List<Account.Id> stars;
+  private ImmutableList<Account.Id> stars;
   protected PatchSet patchSet;
   protected PatchSetInfo patchSetInfo;
   private String changeMessage;
@@ -447,7 +447,7 @@
     return watch.getWatchers(type, includeWatchersFromNotifyConfig);
   }
 
-  /** Any user who has published comments on this change. */
+  /** CC all users who are added as reviewer or cc to the change. */
   @Override
   public void ccAllApprovals() {
     if (!NotifyHandling.ALL.equals(email.getNotify().handling())
@@ -459,6 +459,9 @@
       for (Account.Id id : changeData.reviewers().all()) {
         email.addByAccountId(RecipientType.CC, id);
       }
+      for (Address addr : this.changeData.reviewersByEmail().all()) {
+        email.addByEmail(RecipientType.CC, addr);
+      }
     } catch (StorageException err) {
       logger.atWarning().withCause(err).log("Cannot CC users that reviewed updated change");
     }
@@ -476,6 +479,9 @@
       for (Account.Id id : changeData.reviewers().byState(ReviewerStateInternal.REVIEWER)) {
         email.addByAccountId(RecipientType.CC, id);
       }
+      for (Address addr : changeData.reviewersByEmail().byState(ReviewerStateInternal.REVIEWER)) {
+        email.addByEmail(RecipientType.CC, addr);
+      }
     } catch (StorageException err) {
       logger.atWarning().withCause(err).log("Cannot CC users that commented on updated change");
     }
@@ -619,17 +625,6 @@
         currentAttentionSet.stream().map(email::getNameFor).sorted().collect(toImmutableList()));
 
     setChangeSubjectHeader();
-    if (email.getNotify().handling().equals(NotifyHandling.OWNER_REVIEWERS)
-        || email.getNotify().handling().equals(NotifyHandling.ALL)) {
-      try {
-        this.changeData.reviewersByEmail().byState(ReviewerStateInternal.CC).stream()
-            .forEach(address -> email.addByEmail(RecipientType.CC, address));
-        this.changeData.reviewersByEmail().byState(ReviewerStateInternal.REVIEWER).stream()
-            .forEach(address -> email.addByEmail(RecipientType.CC, address));
-      } catch (StorageException e) {
-        throw new EmailException("Failed to add unregistered CCs " + change.getChangeId(), e);
-      }
-    }
 
     if (email.useHtml()) {
       email.appendHtml(email.soyHtmlTemplate("ChangeHeaderHtml"));
diff --git a/java/com/google/gerrit/server/mail/send/CommentChangeEmailDecoratorImpl.java b/java/com/google/gerrit/server/mail/send/CommentChangeEmailDecoratorImpl.java
index c54c488..b5f8014 100644
--- a/java/com/google/gerrit/server/mail/send/CommentChangeEmailDecoratorImpl.java
+++ b/java/com/google/gerrit/server/mail/send/CommentChangeEmailDecoratorImpl.java
@@ -397,7 +397,7 @@
           commentData.put("lines", getLinesOfComment(comment, group.fileData));
         }
         commentData.put("message", comment.message.trim());
-        List<CommentFormatter.Block> blocks = CommentFormatter.parse(comment.message);
+        ImmutableList<CommentFormatter.Block> blocks = CommentFormatter.parse(comment.message);
         commentData.put("messageBlocks", commentBlocksToSoyData(blocks));
 
         // Set the prefix.
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index 07d4108..011c5e8 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -49,6 +49,7 @@
 import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.collect.ImmutableTable;
@@ -393,7 +394,8 @@
       return copiedApprovals;
     }
     List<PatchSetApproval> approvalsOnLatestPs = allApprovals.get(latestPs);
-    ListMultimap<Account.Id, PatchSetApproval> approvalsByUser = getApprovalsByUser(allApprovals);
+    ImmutableListMultimap<Account.Id, PatchSetApproval> approvalsByUser =
+        getApprovalsByUser(allApprovals);
     List<SubmitRecord.Label> submitRecordLabels =
         submitRecords.stream()
             .filter(r -> r.labels != null)
@@ -431,7 +433,7 @@
     return allApprovals.values().stream().anyMatch(approval -> approval.copied());
   }
 
-  private ListMultimap<Account.Id, PatchSetApproval> getApprovalsByUser(
+  private ImmutableListMultimap<Account.Id, PatchSetApproval> getApprovalsByUser(
       ListMultimap<PatchSet.Id, PatchSetApproval> allApprovals) {
     return allApprovals.values().stream()
         .collect(
@@ -951,7 +953,7 @@
     revisionNoteMap =
         RevisionNoteMap.parse(
             changeNoteJson, reader, NoteMap.read(reader, tipCommit), HumanComment.Status.PUBLISHED);
-    Map<ObjectId, ChangeRevisionNote> rns = revisionNoteMap.revisionNotes;
+    ImmutableMap<ObjectId, ChangeRevisionNote> rns = revisionNoteMap.revisionNotes;
 
     for (Map.Entry<ObjectId, ChangeRevisionNote> e : rns.entrySet()) {
       for (HumanComment c : e.getValue().getEntities()) {
diff --git a/java/com/google/gerrit/server/notedb/CommitRewriter.java b/java/com/google/gerrit/server/notedb/CommitRewriter.java
index df4ee77..ff5d85f 100644
--- a/java/com/google/gerrit/server/notedb/CommitRewriter.java
+++ b/java/com/google/gerrit/server/notedb/CommitRewriter.java
@@ -1186,7 +1186,7 @@
             .collect(
                 ImmutableMap.toImmutableMap(
                     Map.Entry::getKey, e -> Optional.ofNullable(e.getValue()))));
-    Map<Account.Id, AccountState> possibleReplacements = ImmutableMap.of();
+    ImmutableMap<Account.Id, AccountState> possibleReplacements = ImmutableMap.of();
     if (accountInfo.email().isPresent()) {
       possibleReplacements =
           changeFixProgress.parsedAccounts.entrySet().stream()
diff --git a/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java b/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java
index 7d00d2c..4c7e268 100644
--- a/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java
+++ b/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java
@@ -19,6 +19,7 @@
 import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
 import static org.eclipse.jgit.lib.Constants.EMPTY_TREE_ID;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.ListMultimap;
 import com.google.common.flogger.FluentLogger;
@@ -256,7 +257,7 @@
 
   private void deleteZombieDraftsBatch(Collection<Ref> refsBatch) throws IOException {
     try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
-      List<ReceiveCommand> deleteCommands =
+      ImmutableList<ReceiveCommand> deleteCommands =
           refsBatch.stream()
               .map(
                   zombieRef ->
diff --git a/java/com/google/gerrit/server/patch/AutoMerger.java b/java/com/google/gerrit/server/patch/AutoMerger.java
index 9cabf7e..1dacde7 100644
--- a/java/com/google/gerrit/server/patch/AutoMerger.java
+++ b/java/com/google/gerrit/server/patch/AutoMerger.java
@@ -210,7 +210,6 @@
               configuredMergeStrategy);
     }
     counter.increment(OperationType.ON_DISK_WRITE);
-    logger.atFine().log("Added %s AutoMerge ref update for commit", autoMerge.name());
     return autoMerge;
   }
 
diff --git a/java/com/google/gerrit/server/patch/DiffOperationsImpl.java b/java/com/google/gerrit/server/patch/DiffOperationsImpl.java
index 3047d30..27c6ca6 100644
--- a/java/com/google/gerrit/server/patch/DiffOperationsImpl.java
+++ b/java/com/google/gerrit/server/patch/DiffOperationsImpl.java
@@ -308,7 +308,7 @@
 
   private FileDiffOutput getModifiedFileForKey(FileDiffCacheKey key)
       throws DiffNotAvailableException {
-    Map<String, FileDiffOutput> diffList =
+    ImmutableMap<String, FileDiffOutput> diffList =
         getModifiedFilesForKeys(ImmutableList.of(key), DiffOptions.DEFAULTS);
     return diffList.containsKey(key.newFilePath())
         ? diffList.get(key.newFilePath())
@@ -427,7 +427,7 @@
    * multiple times loads the modified files only once (for the first call, for further calls the
    * cached modified files are returned).
    */
-  private Map<String, ModifiedFile> loadModifiedFilesWithoutCacheIfNecessary(
+  private ImmutableMap<String, ModifiedFile> loadModifiedFilesWithoutCacheIfNecessary(
       Project.NameKey project,
       DiffParameters diffParams,
       RevWalk revWalk,
@@ -456,7 +456,7 @@
     if (enableRenameDetection) {
       modifiedFilesLoader.withRenameDetection(RENAME_SCORE);
     }
-    Map<String, ModifiedFile> modifiedFiles =
+    ImmutableMap<String, ModifiedFile> modifiedFiles =
         toMap(
             modifiedFilesLoader.load(
                 project, repoConfig, revWalk, diffParams.baseCommit(), diffParams.newCommit()));
diff --git a/java/com/google/gerrit/server/patch/GitPositionTransformer.java b/java/com/google/gerrit/server/patch/GitPositionTransformer.java
index b2c02e7..aca918b 100644
--- a/java/com/google/gerrit/server/patch/GitPositionTransformer.java
+++ b/java/com/google/gerrit/server/patch/GitPositionTransformer.java
@@ -94,7 +94,8 @@
       Collection<PositionedEntity<T>> entities, Set<Mapping> mappings) {
     // Update the file paths first as copied files might exist. For copied files, this operation
     // will duplicate the PositionedEntity instances of the original file.
-    List<PositionedEntity<T>> filePathUpdatedEntities = updateFilePaths(entities, mappings);
+    ImmutableList<PositionedEntity<T>> filePathUpdatedEntities =
+        updateFilePaths(entities, mappings);
 
     return shiftRanges(filePathUpdatedEntities, mappings);
   }
diff --git a/java/com/google/gerrit/server/patch/PatchList.java b/java/com/google/gerrit/server/patch/PatchList.java
index 4efbc69..3c5be17 100644
--- a/java/com/google/gerrit/server/patch/PatchList.java
+++ b/java/com/google/gerrit/server/patch/PatchList.java
@@ -59,7 +59,7 @@
    */
   @VisibleForTesting
   static class ChangeTypeCmp implements Comparator<ChangeType> {
-    static final List<ChangeType> order =
+    static final ImmutableList<ChangeType> order =
         ImmutableList.of(
             ChangeType.ADDED,
             ChangeType.RENAMED,
diff --git a/java/com/google/gerrit/server/patch/filediff/AllDiffsEvaluator.java b/java/com/google/gerrit/server/patch/filediff/AllDiffsEvaluator.java
index 0923252..fb13920 100644
--- a/java/com/google/gerrit/server/patch/filediff/AllDiffsEvaluator.java
+++ b/java/com/google/gerrit/server/patch/filediff/AllDiffsEvaluator.java
@@ -75,7 +75,7 @@
     // First batch: "old commit vs. new commit" and "new parent vs. new commit"
     // Second batch: "old parent vs. old commit" and "old parent vs. new parent"
 
-    Map<FileDiffCacheKey, GitDiffEntity> mainDiffs =
+    ImmutableMap<FileDiffCacheKey, GitDiffEntity> mainDiffs =
         computeGitFileDiffs(
             createGitKeys(
                 augmentedKeys,
@@ -83,7 +83,7 @@
                 k -> k.key().newCommit(),
                 k -> k.key().newFilePath()));
 
-    Map<FileDiffCacheKey, GitDiffEntity> oldVsParentDiffs =
+    ImmutableMap<FileDiffCacheKey, GitDiffEntity> oldVsParentDiffs =
         computeGitFileDiffs(
             createGitKeys(
                 keysWithRebaseEdits,
@@ -91,7 +91,7 @@
                 k -> k.key().oldCommit(),
                 k -> mainDiffs.get(k.key()).gitDiff().oldPath().orElse(null)));
 
-    Map<FileDiffCacheKey, GitDiffEntity> newVsParentDiffs =
+    ImmutableMap<FileDiffCacheKey, GitDiffEntity> newVsParentDiffs =
         computeGitFileDiffs(
             createGitKeys(
                 keysWithRebaseEdits,
@@ -99,7 +99,7 @@
                 k -> k.key().newCommit(),
                 k -> k.key().newFilePath()));
 
-    Map<FileDiffCacheKey, GitDiffEntity> parentsDiffs =
+    ImmutableMap<FileDiffCacheKey, GitDiffEntity> parentsDiffs =
         computeGitFileDiffs(
             createGitKeys(
                 keysWithRebaseEdits,
@@ -149,7 +149,7 @@
    * Computes the git diff for the git keys of the input map {@code keys} parameter. The computation
    * uses the underlying {@link GitFileDiffCache}.
    */
-  private Map<FileDiffCacheKey, GitDiffEntity> computeGitFileDiffs(
+  private ImmutableMap<FileDiffCacheKey, GitDiffEntity> computeGitFileDiffs(
       Map<FileDiffCacheKey, GitFileDiffCacheKey> keys) throws DiffNotAvailableException {
     ImmutableMap.Builder<FileDiffCacheKey, GitDiffEntity> result =
         ImmutableMap.builderWithExpectedSize(keys.size());
diff --git a/java/com/google/gerrit/server/patch/filediff/EditTransformer.java b/java/com/google/gerrit/server/patch/filediff/EditTransformer.java
index 55568e4..7083744 100644
--- a/java/com/google/gerrit/server/patch/filediff/EditTransformer.java
+++ b/java/com/google/gerrit/server/patch/filediff/EditTransformer.java
@@ -50,7 +50,7 @@
 
   private final GitPositionTransformer positionTransformer =
       new GitPositionTransformer(OmitPositionOnConflict.INSTANCE);
-  private List<ContextAwareEdit> edits;
+  private ImmutableList<ContextAwareEdit> edits;
 
   /**
    * Creates a new {@code EditTransformer} for the edits contained in the specified {@code
@@ -113,7 +113,7 @@
   }
 
   public static Stream<ContextAwareEdit> toEdits(FileEdits in) {
-    List<Edit> edits = in.edits();
+    ImmutableList<Edit> edits = in.edits();
     if (edits.isEmpty()) {
       return Stream.of(ContextAwareEdit.createForNoContentEdit(in.oldPath(), in.newPath()));
     }
diff --git a/java/com/google/gerrit/server/patch/filediff/FileDiffCacheImpl.java b/java/com/google/gerrit/server/patch/filediff/FileDiffCacheImpl.java
index d1bda5c..82d7e93 100644
--- a/java/com/google/gerrit/server/patch/filediff/FileDiffCacheImpl.java
+++ b/java/com/google/gerrit/server/patch/filediff/FileDiffCacheImpl.java
@@ -408,7 +408,7 @@
         if (!augmentedKey.ignoreRebase()) {
           rebaseFileEdits = computeRebaseEdits(allDiffs);
         }
-        List<Edit> rebaseEdits = rebaseFileEdits.edits();
+        ImmutableList<Edit> rebaseEdits = rebaseFileEdits.edits();
 
         ObjectId oldTreeId = allDiffs.mainDiff().gitKey().oldTree();
 
diff --git a/java/com/google/gerrit/server/patch/gitfilediff/GitFileDiff.java b/java/com/google/gerrit/server/patch/gitfilediff/GitFileDiff.java
index d0d024c..580aef5 100644
--- a/java/com/google/gerrit/server/patch/gitfilediff/GitFileDiff.java
+++ b/java/com/google/gerrit/server/patch/gitfilediff/GitFileDiff.java
@@ -31,7 +31,6 @@
 import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
 import com.google.gerrit.server.patch.filediff.Edit;
 import com.google.protobuf.Descriptors.FieldDescriptor;
-import java.util.Map;
 import java.util.Optional;
 import org.eclipse.jgit.diff.DiffEntry;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
@@ -45,7 +44,7 @@
  */
 @AutoValue
 public abstract class GitFileDiff {
-  private static final Map<FileMode, Patch.FileMode> fileModeMap =
+  private static final ImmutableMap<FileMode, Patch.FileMode> fileModeMap =
       ImmutableMap.<FileMode, Patch.FileMode>builder()
           .put(FileMode.TREE, Patch.FileMode.TREE)
           .put(FileMode.SYMLINK, Patch.FileMode.SYMLINK)
diff --git a/java/com/google/gerrit/server/patch/gitfilediff/GitFileDiffCacheImpl.java b/java/com/google/gerrit/server/patch/gitfilediff/GitFileDiffCacheImpl.java
index 72dc434..84eda51 100644
--- a/java/com/google/gerrit/server/patch/gitfilediff/GitFileDiffCacheImpl.java
+++ b/java/com/google/gerrit/server/patch/gitfilediff/GitFileDiffCacheImpl.java
@@ -55,7 +55,6 @@
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
@@ -233,7 +232,7 @@
      *
      * @return The git file diffs for all input keys.
      */
-    private Map<GitFileDiffCacheKey, GitFileDiff> loadAllImpl(
+    private ImmutableMap<GitFileDiffCacheKey, GitFileDiff> loadAllImpl(
         Repository repo, DiffOptions options, List<GitFileDiffCacheKey> keys)
         throws IOException, DiffNotAvailableException {
       ImmutableMap.Builder<GitFileDiffCacheKey, GitFileDiff> result =
@@ -279,7 +278,7 @@
     private static ListMultimap<String, DiffEntry> loadDiffEntries(
         DiffFormatter diffFormatter, DiffOptions diffOptions, Collection<String> filePaths)
         throws IOException {
-      Set<String> filePathsSet = ImmutableSet.copyOf(filePaths);
+      ImmutableSet<String> filePathsSet = ImmutableSet.copyOf(filePaths);
       List<DiffEntry> diffEntries =
           diffFormatter.scan(
               diffOptions.oldTree().equals(ObjectId.zeroId()) ? null : diffOptions.oldTree(),
diff --git a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
index a4ee052..677ee18 100644
--- a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
@@ -19,6 +19,7 @@
 import static java.util.Objects.requireNonNull;
 import static java.util.stream.Collectors.toSet;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.entities.Account;
@@ -41,7 +42,6 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.util.Collection;
-import java.util.List;
 import java.util.Optional;
 import java.util.Set;
 
@@ -238,7 +238,7 @@
     }
 
     private boolean canEmailReviewers() {
-      List<PermissionRule> email = capabilities().emailReviewers;
+      ImmutableList<PermissionRule> email = capabilities().emailReviewers;
       if (allow(email)) {
         logger.atFinest().log(
             "user %s can email reviewers (allowed by %s)", user.getLoggableName(), email);
diff --git a/java/com/google/gerrit/server/plugins/JarScanner.java b/java/com/google/gerrit/server/plugins/JarScanner.java
index 122e3f4..b92a9b2 100644
--- a/java/com/google/gerrit/server/plugins/JarScanner.java
+++ b/java/com/google/gerrit/server/plugins/JarScanner.java
@@ -19,6 +19,7 @@
 import static com.google.common.collect.Iterables.transform;
 
 import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Maps;
@@ -112,7 +113,7 @@
 
     for (Class<? extends Annotation> annotoation : annotations) {
       String descr = classObjToClassDescr.get(annotoation);
-      Collection<ClassData> discoverdData = rawMap.get(descr);
+      List<ClassData> discoverdData = rawMap.get(descr);
       Collection<ClassData> values = firstNonNull(discoverdData, Collections.emptySet());
 
       result.put(
@@ -331,7 +332,7 @@
     return Maps.transformEntries(attributes, (key, value) -> (String) value);
   }
 
-  private static Iterable<JarEntry> entriesOf(JarFile jarFile) {
+  private static ImmutableList<JarEntry> entriesOf(JarFile jarFile) {
     return jarFile.stream().collect(toImmutableList());
   }
 }
diff --git a/java/com/google/gerrit/server/plugins/PluginLoader.java b/java/com/google/gerrit/server/plugins/PluginLoader.java
index 64050e2..9e23c0b 100644
--- a/java/com/google/gerrit/server/plugins/PluginLoader.java
+++ b/java/com/google/gerrit/server/plugins/PluginLoader.java
@@ -707,8 +707,8 @@
       assert winner != null;
       // Disable all loser plugins by renaming their file names to
       // "file.disabled" and replace the disabled files in the multimap.
-      Collection<Path> elementsToRemove = new ArrayList<>();
-      Collection<Path> elementsToAdd = new ArrayList<>();
+      List<Path> elementsToRemove = new ArrayList<>();
+      List<Path> elementsToAdd = new ArrayList<>();
       for (Path loser : Iterables.skip(enabled, 1)) {
         logger.atWarning().log(
             "Plugin <%s> was disabled, because"
diff --git a/java/com/google/gerrit/server/project/CommentLinkProvider.java b/java/com/google/gerrit/server/project/CommentLinkProvider.java
index 88f045e..c799b56 100644
--- a/java/com/google/gerrit/server/project/CommentLinkProvider.java
+++ b/java/com/google/gerrit/server/project/CommentLinkProvider.java
@@ -35,14 +35,14 @@
 public class CommentLinkProvider implements Provider<List<CommentLinkInfo>>, GerritConfigListener {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  private volatile List<CommentLinkInfo> commentLinks;
+  private volatile ImmutableList<CommentLinkInfo> commentLinks;
 
   @Inject
   CommentLinkProvider(@GerritServerConfig Config cfg) {
     this.commentLinks = parseConfig(cfg);
   }
 
-  private List<CommentLinkInfo> parseConfig(Config cfg) {
+  private ImmutableList<CommentLinkInfo> parseConfig(Config cfg) {
     Set<String> subsections = cfg.getSubsections(ProjectConfig.COMMENTLINK);
     ImmutableList.Builder<CommentLinkInfo> cls =
         ImmutableList.builderWithExpectedSize(subsections.size());
diff --git a/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java b/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java
index f054e84..aa0f87e 100644
--- a/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java
+++ b/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java
@@ -17,6 +17,8 @@
 import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.AccountGroup.UUID;
 import com.google.gerrit.entities.BooleanProjectConfig;
@@ -36,7 +38,6 @@
 import com.google.inject.Singleton;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
@@ -89,7 +90,7 @@
     }
 
     IdentifiedUser iUser = user.asIdentifiedUser();
-    Collection<ContributorAgreement> contributorAgreements =
+    ImmutableCollection<ContributorAgreement> contributorAgreements =
         projectCache.getAllProjects().getConfig().getContributorAgreements().values();
     List<UUID> okGroupIds = new ArrayList<>();
     for (ContributorAgreement ca : contributorAgreements) {
@@ -97,14 +98,14 @@
       groupIds = okGroupIds;
 
       // matchProjects defaults to match all projects when missing.
-      List<String> matchProjectsRegexes = ca.getMatchProjectsRegexes();
+      ImmutableList<String> matchProjectsRegexes = ca.getMatchProjectsRegexes();
       if (!matchProjectsRegexes.isEmpty()
           && !projectMatchesAnyPattern(project.get(), matchProjectsRegexes)) {
         // Doesn't match, isn't checked.
         continue;
       }
       // excludeProjects defaults to exclude no projects when missing.
-      List<String> excludeProjectsRegexes = ca.getExcludeProjectsRegexes();
+      ImmutableList<String> excludeProjectsRegexes = ca.getExcludeProjectsRegexes();
       if (!excludeProjectsRegexes.isEmpty()
           && projectMatchesAnyPattern(project.get(), excludeProjectsRegexes)) {
         // Matches, isn't checked.
diff --git a/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java b/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
index b2da2b4..a317361 100644
--- a/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
+++ b/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
@@ -257,7 +257,7 @@
     }
 
     try {
-      List<ChangeData> queryResult =
+      ImmutableList<ChangeData> queryResult =
           retryHelper
               .changeIndexQuery(
                   "projectsConsistencyCheckerQueryChanges",
diff --git a/java/com/google/gerrit/server/project/Reachable.java b/java/com/google/gerrit/server/project/Reachable.java
index 342c2bc..e8ecaa2 100644
--- a/java/com/google/gerrit/server/project/Reachable.java
+++ b/java/com/google/gerrit/server/project/Reachable.java
@@ -77,7 +77,7 @@
               .orElse(permissionBackend.currentUser())
               .project(project)
               .filter(refs, repo, RefFilterOptions.defaults());
-      Collection<RevCommit> visible = new ArrayList<>();
+      List<RevCommit> visible = new ArrayList<>();
       for (Ref r : filtered) {
         try {
           visible.add(rw.parseCommit(r.getObjectId()));
diff --git a/java/com/google/gerrit/server/project/SubmitRequirementsAdapter.java b/java/com/google/gerrit/server/project/SubmitRequirementsAdapter.java
index 39ba8b4..7f73cd3 100644
--- a/java/com/google/gerrit/server/project/SubmitRequirementsAdapter.java
+++ b/java/com/google/gerrit/server/project/SubmitRequirementsAdapter.java
@@ -50,7 +50,7 @@
    * Retrieve legacy submit records (created by label functions and other {@link
    * com.google.gerrit.server.rules.SubmitRule}s) and convert them to submit requirement results.
    */
-  public static Map<SubmitRequirement, SubmitRequirementResult> getLegacyRequirements(
+  public static ImmutableMap<SubmitRequirement, SubmitRequirementResult> getLegacyRequirements(
       ChangeData cd) {
     // We use SubmitRuleOptions.defaults() which does not recompute submit rules for closed changes.
     // This doesn't have an effect since we never call this class (i.e. to evaluate submit
@@ -105,9 +105,9 @@
   }
 
   @VisibleForTesting
-  static List<SubmitRequirementResult> createResult(
+  static ImmutableList<SubmitRequirementResult> createResult(
       SubmitRecord record, List<LabelType> labelTypes, ObjectId psCommitId, boolean isForced) {
-    List<SubmitRequirementResult> results;
+    ImmutableList<SubmitRequirementResult> results;
     if (record.ruleName != null && record.ruleName.equals(DefaultSubmitRule.RULE_NAME)) {
       results = createFromDefaultSubmitRecord(record.labels, labelTypes, psCommitId, isForced);
     } else {
@@ -117,7 +117,7 @@
     return results;
   }
 
-  private static List<SubmitRequirementResult> createFromDefaultSubmitRecord(
+  private static ImmutableList<SubmitRequirementResult> createFromDefaultSubmitRecord(
       @Nullable List<Label> labels,
       List<LabelType> labelTypes,
       ObjectId psCommitId,
@@ -159,7 +159,7 @@
     return result.build();
   }
 
-  private static List<SubmitRequirementResult> createFromCustomSubmitRecord(
+  private static ImmutableList<SubmitRequirementResult> createFromCustomSubmitRecord(
       SubmitRecord record, ObjectId psCommitId, boolean isForced) {
     String ruleName = record.ruleName != null ? record.ruleName : "Custom-Rule";
     if (record.labels == null || record.labels.isEmpty()) {
diff --git a/java/com/google/gerrit/server/project/SubmitRequirementsEvaluatorImpl.java b/java/com/google/gerrit/server/project/SubmitRequirementsEvaluatorImpl.java
index 09a232a..995bdcb 100644
--- a/java/com/google/gerrit/server/project/SubmitRequirementsEvaluatorImpl.java
+++ b/java/com/google/gerrit/server/project/SubmitRequirementsEvaluatorImpl.java
@@ -181,7 +181,7 @@
    * SubmitRequirement#allowOverrideInChildProjects} of global {@link SubmitRequirement}.
    */
   private ImmutableMap<SubmitRequirement, SubmitRequirementResult> getRequirements(ChangeData cd) {
-    Map<String, SubmitRequirement> globalRequirements = getGlobalRequirements();
+    ImmutableMap<String, SubmitRequirement> globalRequirements = getGlobalRequirements();
 
     ProjectState state = projectCache.get(cd.project()).orElseThrow(illegalState(cd.project()));
     Map<String, SubmitRequirement> projectConfigRequirements = state.getSubmitRequirements();
@@ -213,7 +213,7 @@
    *
    * <p>The global {@link SubmitRequirement}s apply to all projects and can be bound by plugins.
    */
-  private Map<String, SubmitRequirement> getGlobalRequirements() {
+  private ImmutableMap<String, SubmitRequirement> getGlobalRequirements() {
     return globalSubmitRequirements.stream()
         .collect(
             toImmutableMap(
diff --git a/java/com/google/gerrit/server/query/change/ChangeData.java b/java/com/google/gerrit/server/query/change/ChangeData.java
index a6c8878..5676ab4 100644
--- a/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -1156,7 +1156,7 @@
    */
   public Map<SubmitRequirement, SubmitRequirementResult> submitRequirementsIncludingLegacy() {
     Map<SubmitRequirement, SubmitRequirementResult> projectConfigReqs = submitRequirements();
-    Map<SubmitRequirement, SubmitRequirementResult> legacyReqs =
+    ImmutableMap<SubmitRequirement, SubmitRequirementResult> legacyReqs =
         SubmitRequirementsAdapter.getLegacyRequirements(this);
     return submitRequirementsUtil.mergeLegacyAndNonLegacyRequirements(
         projectConfigReqs, legacyReqs, this);
diff --git a/java/com/google/gerrit/server/query/change/EqualsLabelPredicates.java b/java/com/google/gerrit/server/query/change/EqualsLabelPredicates.java
index 0d6dc3c..45a723b 100644
--- a/java/com/google/gerrit/server/query/change/EqualsLabelPredicates.java
+++ b/java/com/google/gerrit/server/query/change/EqualsLabelPredicates.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.query.change;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Account;
@@ -35,7 +36,6 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData.StorageConstraint;
 import java.io.IOException;
-import java.util.List;
 import java.util.Optional;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 
@@ -237,7 +237,7 @@
      */
     private boolean matchAccount(String email, Account.Id accountId) {
       try {
-        List<AccountState> accountsList = accountResolver.resolve(email).asList();
+        ImmutableList<AccountState> accountsList = accountResolver.resolve(email).asList();
         return accountsList.stream().anyMatch(c -> c.account().id().equals(accountId));
       } catch (ConfigInvalidException | IOException e) {
         logger.atWarning().withCause(e).log("Failed to resolve account %s", email);
diff --git a/java/com/google/gerrit/server/query/change/GroupPredicate.java b/java/com/google/gerrit/server/query/change/GroupPredicate.java
index c4aba0d..bff2575 100644
--- a/java/com/google/gerrit/server/query/change/GroupPredicate.java
+++ b/java/com/google/gerrit/server/query/change/GroupPredicate.java
@@ -14,9 +14,9 @@
 
 package com.google.gerrit.server.query.change;
 
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.server.index.change.ChangeField;
-import java.util.List;
 
 public class GroupPredicate extends ChangeIndexPredicate {
   public GroupPredicate(String group) {
@@ -26,7 +26,7 @@
   @Override
   public boolean match(ChangeData cd) {
     for (PatchSet ps : cd.patchSets()) {
-      List<String> groups = ps.groups();
+      ImmutableList<String> groups = ps.groups();
       if (groups != null && groups.contains(getValue())) {
         return true;
       }
diff --git a/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java b/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
index 054a69e..046d24c 100644
--- a/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
@@ -22,9 +22,9 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 public class IsWatchedByPredicate extends AndPredicate<ChangeData> {
   protected static String describe(CurrentUser user) {
@@ -41,7 +41,7 @@
     this.user = args.getUser();
   }
 
-  protected static List<Predicate<ChangeData>> filters(ChangeQueryBuilder.Arguments args)
+  protected static ImmutableList<Predicate<ChangeData>> filters(ChangeQueryBuilder.Arguments args)
       throws QueryParseException {
     List<Predicate<ChangeData>> r = new ArrayList<>();
     ChangeQueryBuilder builder = new ChangeQueryBuilder(args);
@@ -85,7 +85,7 @@
     return ImmutableList.of(or(r));
   }
 
-  protected static Collection<ProjectWatchKey> getWatches(ChangeQueryBuilder.Arguments args)
+  protected static Set<ProjectWatchKey> getWatches(ChangeQueryBuilder.Arguments args)
       throws QueryParseException {
     CurrentUser user = args.getUser();
     if (user.isIdentifiedUser()) {
diff --git a/java/com/google/gerrit/server/query/change/OrSource.java b/java/com/google/gerrit/server/query/change/OrSource.java
index f2de536..9633a18 100644
--- a/java/com/google/gerrit/server/query/change/OrSource.java
+++ b/java/com/google/gerrit/server/query/change/OrSource.java
@@ -25,7 +25,6 @@
 import com.google.gerrit.index.query.ResultSet;
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Optional;
 import java.util.Set;
 
@@ -45,7 +44,7 @@
   public ResultSet<ChangeData> read() {
     // ResultSets are lazy. Calling #read here first and then dealing with ResultSets only when
     // requested allows the index to run asynchronous queries.
-    List<ResultSet<ChangeData>> results =
+    ImmutableList<ResultSet<ChangeData>> results =
         getChildren().stream().map(p -> ((ChangeDataSource) p).read()).collect(toImmutableList());
     return new LazyResultSet<>(
         () -> {
diff --git a/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
index b4ca302..d3b5605 100644
--- a/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
+++ b/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -17,6 +17,7 @@
 import static com.google.common.base.Preconditions.checkState;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.flogger.FluentLogger;
@@ -348,7 +349,7 @@
       eventFactory.addDependencies(rw, c, d.change(), d.currentPatchSet());
     }
 
-    List<PluginDefinedInfo> pluginInfos = pluginInfosByChange.get(d.getId());
+    ImmutableList<PluginDefinedInfo> pluginInfos = pluginInfosByChange.get(d.getId());
     if (!pluginInfos.isEmpty()) {
       c.plugins = pluginInfos;
     }
diff --git a/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java b/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java
index c659975..c5aa84c 100644
--- a/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java
+++ b/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java
@@ -65,7 +65,8 @@
 
     // PluginSets can change their content when plugins (de-)register. Copy the currently registered
     // plugins so that we can iterate twice on a stable list.
-    List<PluginSetEntryContext<QuotaEnforcer>> enforcers = ImmutableList.copyOf(quotaEnforcers);
+    ImmutableList<PluginSetEntryContext<QuotaEnforcer>> enforcers =
+        ImmutableList.copyOf(quotaEnforcers);
     List<QuotaResponse> responses = new ArrayList<>(enforcers.size());
     for (PluginSetEntryContext<QuotaEnforcer> enforcer : enforcers) {
       try {
@@ -107,7 +108,8 @@
       QuotaRequestContext requestContext) {
     // PluginSets can change their content when plugins (de-)register. Copy the currently registered
     // plugins so that we can iterate twice on a stable list.
-    List<PluginSetEntryContext<QuotaEnforcer>> enforcers = ImmutableList.copyOf(quotaEnforcers);
+    ImmutableList<PluginSetEntryContext<QuotaEnforcer>> enforcers =
+        ImmutableList.copyOf(quotaEnforcers);
     List<QuotaResponse> responses = new ArrayList<>(enforcers.size());
     for (PluginSetEntryContext<QuotaEnforcer> enforcer : enforcers) {
       responses.add(enforcer.call(p -> p.availableTokens(quotaGroup, requestContext)));
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeRestApiModule.java b/java/com/google/gerrit/server/restapi/change/ChangeRestApiModule.java
index fbacf39..8c95e93 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeRestApiModule.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeRestApiModule.java
@@ -99,6 +99,7 @@
     post(CHANGE_KIND, "index").to(Index.class);
     get(CHANGE_KIND, "meta_diff").to(GetMetaDiff.class);
     post(CHANGE_KIND, "merge").to(CreateMergePatchSet.class);
+    get(CHANGE_KIND, "message").to(GetMessage.class);
     put(CHANGE_KIND, "message").to(PutMessage.class);
 
     child(CHANGE_KIND, "messages").to(ChangeMessages.class);
diff --git a/java/com/google/gerrit/server/restapi/change/GetMessage.java b/java/com/google/gerrit/server/restapi/change/GetMessage.java
new file mode 100644
index 0000000..5715caa
--- /dev/null
+++ b/java/com/google/gerrit/server/restapi/change/GetMessage.java
@@ -0,0 +1,53 @@
+// Copyright (C) 2024 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.server.restapi.change;
+
+import static java.util.stream.Collectors.toMap;
+
+import com.google.gerrit.extensions.common.CommitMessageInfo;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.eclipse.jgit.revwalk.FooterLine;
+
+@Singleton
+public class GetMessage implements RestReadView<ChangeResource> {
+  private final ChangeData.Factory changeDataFactory;
+
+  @Inject
+  GetMessage(ChangeData.Factory changeDataFactory) {
+    this.changeDataFactory = changeDataFactory;
+  }
+
+  @Override
+  public Response<CommitMessageInfo> apply(ChangeResource resource)
+      throws AuthException, BadRequestException, ResourceConflictException, Exception {
+    CommitMessageInfo commitMessageInfo = new CommitMessageInfo();
+    commitMessageInfo.subject = resource.getChange().getSubject();
+
+    ChangeData cd = changeDataFactory.create(resource.getNotes());
+    commitMessageInfo.fullMessage = cd.commitMessage();
+    commitMessageInfo.footers =
+        cd.commitFooters().stream().collect(toMap(FooterLine::getKey, FooterLine::getValue));
+
+    return Response.ok(commitMessageInfo);
+  }
+}
diff --git a/java/com/google/gerrit/server/rules/DefaultSubmitRule.java b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
index 8cd0a58..1efb8a6 100644
--- a/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
+++ b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
@@ -17,6 +17,7 @@
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.ImmutableList.toImmutableList;
 
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.entities.LabelFunction;
 import com.google.gerrit.entities.LabelType;
 import com.google.gerrit.entities.PatchSetApproval;
@@ -26,7 +27,6 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.inject.Singleton;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
 
@@ -67,7 +67,7 @@
           t.getName(),
           cd.getId());
 
-      Collection<PatchSetApproval> approvalsForLabel = getApprovalsForLabel(approvals, t);
+      ImmutableList<PatchSetApproval> approvalsForLabel = getApprovalsForLabel(approvals, t);
       SubmitRecord.Label label = labelFunction.check(t, approvalsForLabel);
       submitRecord.labels.add(label);
 
@@ -87,7 +87,7 @@
     return Optional.of(submitRecord);
   }
 
-  private static List<PatchSetApproval> getApprovalsForLabel(
+  private static ImmutableList<PatchSetApproval> getApprovalsForLabel(
       List<PatchSetApproval> approvals, LabelType t) {
     return approvals.stream()
         .filter(input -> input.label().equals(t.getLabelId().get()))
diff --git a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
index 216405d..c915e6e 100644
--- a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
+++ b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
@@ -17,6 +17,7 @@
 import static com.google.common.collect.ImmutableList.toImmutableList;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.LabelFunction;
 import com.google.gerrit.entities.LabelType;
@@ -75,7 +76,7 @@
         continue;
       }
 
-      Collection<PatchSetApproval> allApprovalsForLabel = filterApprovalsByLabel(approvals, t);
+      ImmutableList<PatchSetApproval> allApprovalsForLabel = filterApprovalsByLabel(approvals, t);
       SubmitRecord.Label allApprovalsCheckResult = labelFunction.check(t, allApprovalsForLabel);
       SubmitRecord.Label ignoreSelfApprovalCheckResult =
           labelFunction.check(t, filterOutPositiveApprovalsOfUser(allApprovalsForLabel, uploader));
@@ -119,7 +120,7 @@
   }
 
   @VisibleForTesting
-  static Collection<PatchSetApproval> filterOutPositiveApprovalsOfUser(
+  static ImmutableList<PatchSetApproval> filterOutPositiveApprovalsOfUser(
       Collection<PatchSetApproval> approvals, Account.Id user) {
     return approvals.stream()
         .filter(input -> input.value() < 0 || !input.accountId().equals(user))
@@ -127,7 +128,7 @@
   }
 
   @VisibleForTesting
-  static Collection<PatchSetApproval> filterApprovalsByLabel(
+  static ImmutableList<PatchSetApproval> filterApprovalsByLabel(
       Collection<PatchSetApproval> approvals, LabelType t) {
     return approvals.stream()
         .filter(input -> input.labelId().get().equals(t.getLabelId().get()))
diff --git a/java/com/google/gerrit/server/submit/GitModules.java b/java/com/google/gerrit/server/submit/GitModules.java
index f8f6bc4..601e9ee 100644
--- a/java/com/google/gerrit/server/submit/GitModules.java
+++ b/java/com/google/gerrit/server/submit/GitModules.java
@@ -27,8 +27,8 @@
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.BlobBasedConfig;
@@ -89,8 +89,8 @@
     }
   }
 
-  Collection<SubmoduleSubscription> subscribedTo(BranchNameKey src) {
-    Collection<SubmoduleSubscription> ret = new ArrayList<>();
+  List<SubmoduleSubscription> subscribedTo(BranchNameKey src) {
+    List<SubmoduleSubscription> ret = new ArrayList<>();
     for (SubmoduleSubscription s : subscriptions) {
       if (s.getSubmodule().equals(src)) {
         ret.add(s);
diff --git a/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java b/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java
index 86d6c674..2e66941 100644
--- a/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java
+++ b/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java
@@ -106,8 +106,8 @@
   @Override
   public ChangeSet completeWithoutTopic(
       MergeOpRepoManager orm, ChangeSet changeSet, CurrentUser user) throws IOException {
-    Collection<ChangeData> visibleChanges = new ArrayList<>();
-    Collection<ChangeData> nonVisibleChanges = new ArrayList<>();
+    List<ChangeData> visibleChanges = new ArrayList<>();
+    List<ChangeData> nonVisibleChanges = new ArrayList<>();
 
     // For each target branch we run a separate rev walk to find open changes
     // reachable from changes already in the merge super set.
@@ -194,7 +194,7 @@
       Set<String> nonVisibleHashes,
       CurrentUser user)
       throws IOException {
-    List<ChangeData> potentiallyVisibleChanges =
+    ImmutableList<ChangeData> potentiallyVisibleChanges =
         byCommitsOnBranchNotMerged(or, branch, visibleHashes);
     List<ChangeData> invisibleChanges =
         new ArrayList<>(byCommitsOnBranchNotMerged(or, branch, nonVisibleHashes));
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java
index d94baa6..bc14f34 100644
--- a/java/com/google/gerrit/server/submit/MergeOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -308,7 +308,7 @@
   private CommitStatus commitStatus;
   private SubmitInput submitInput;
   private NotifyResolver.Result notify;
-  private Set<Project.NameKey> projects;
+  private ImmutableSet<Project.NameKey> projects;
   private boolean dryrun;
   private TopicMetrics topicMetrics;
 
@@ -815,7 +815,7 @@
       boolean dryrun)
       throws IntegrationConflictException, NoSuchProjectException, IOException {
     List<SubmitStrategy> strategies = new ArrayList<>();
-    Set<BranchNameKey> allBranches = updateOrderCalculator.getBranchesInOrder();
+    ImmutableSet<BranchNameKey> allBranches = updateOrderCalculator.getBranchesInOrder();
     Set<CodeReviewCommit> allCommits =
         toSubmit.values().stream().map(BranchBatch::commits).flatMap(Set::stream).collect(toSet());
 
@@ -828,7 +828,7 @@
         requireNonNull(
             submitting.submitType(),
             String.format("null submit type for %s; expected to previously fail fast", submitting));
-        Set<CodeReviewCommit> commitsToSubmit = submitting.commits();
+        ImmutableSet<CodeReviewCommit> commitsToSubmit = submitting.commits();
         checkImplicitMerges(
             branch, or.rw, submitting.commits(), submitting.submitType(), ob.oldTip);
 
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategy.java b/java/com/google/gerrit/server/submit/SubmitStrategy.java
index 229bb8a..4b95685 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategy.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategy.java
@@ -278,7 +278,7 @@
    *     place.
    */
   public final void addOps(BatchUpdate bu, Set<CodeReviewCommit> toMerge) {
-    List<SubmitStrategyOp> ops = buildOps(toMerge);
+    ImmutableList<SubmitStrategyOp> ops = buildOps(toMerge);
     Set<CodeReviewCommit> added = Sets.newHashSetWithExpectedSize(ops.size());
 
     for (SubmitStrategyOp op : ops) {
diff --git a/java/com/google/gerrit/server/submit/SubscriptionGraph.java b/java/com/google/gerrit/server/submit/SubscriptionGraph.java
index d434890..711950c 100644
--- a/java/com/google/gerrit/server/submit/SubscriptionGraph.java
+++ b/java/com/google/gerrit/server/submit/SubscriptionGraph.java
@@ -37,7 +37,6 @@
 import java.io.IOException;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -283,7 +282,7 @@
 
       currentVisited.add(current);
       try {
-        Collection<SubmoduleSubscription> subscriptions =
+        List<SubmoduleSubscription> subscriptions =
             superProjectSubscriptionsForSubmoduleBranch(current, branchGitModules, orm);
         for (SubmoduleSubscription sub : subscriptions) {
           BranchNameKey superBranch = sub.getSuperProject();
@@ -310,7 +309,7 @@
       allVisited.add(current);
     }
 
-    private Collection<BranchNameKey> getDestinationBranches(
+    private ImmutableSet<BranchNameKey> getDestinationBranches(
         BranchNameKey src, SubscribeSection s, MergeOpRepoManager orm) throws IOException {
       OpenRepo or;
       try {
@@ -326,12 +325,12 @@
       return s.getDestinationBranches(src, refs);
     }
 
-    private Collection<SubmoduleSubscription> superProjectSubscriptionsForSubmoduleBranch(
+    private List<SubmoduleSubscription> superProjectSubscriptionsForSubmoduleBranch(
         BranchNameKey srcBranch,
         Map<BranchNameKey, GitModules> branchGitModules,
         MergeOpRepoManager orm)
         throws IOException {
-      Collection<SubmoduleSubscription> ret = new ArrayList<>();
+      List<SubmoduleSubscription> ret = new ArrayList<>();
       if (RefNames.isGerritRef(srcBranch.branch())) return ret;
 
       Project.NameKey srcProject = srcBranch.project();
@@ -340,7 +339,7 @@
               .get(srcProject)
               .orElseThrow(illegalState(srcProject))
               .getSubscribeSections(srcBranch)) {
-        Collection<BranchNameKey> branches = getDestinationBranches(srcBranch, s, orm);
+        ImmutableSet<BranchNameKey> branches = getDestinationBranches(srcBranch, s, orm);
         for (BranchNameKey targetBranch : branches) {
           Project.NameKey targetProject = targetBranch.project();
           try {
diff --git a/java/com/google/gerrit/server/submit/UpdateOrderCalculator.java b/java/com/google/gerrit/server/submit/UpdateOrderCalculator.java
index 517c708..2f584aa 100644
--- a/java/com/google/gerrit/server/submit/UpdateOrderCalculator.java
+++ b/java/com/google/gerrit/server/submit/UpdateOrderCalculator.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.SubmoduleSubscription;
-import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Set;
@@ -68,7 +67,8 @@
     current.add(project);
     Set<Project.NameKey> subprojects = new HashSet<>();
     for (BranchNameKey branch : subscriptionGraph.getAffectedSuperBranches(project)) {
-      Collection<SubmoduleSubscription> subscriptions = subscriptionGraph.getSubscriptions(branch);
+      ImmutableSet<SubmoduleSubscription> subscriptions =
+          subscriptionGraph.getSubscriptions(branch);
       for (SubmoduleSubscription s : subscriptions) {
         subprojects.add(s.getSubmodule().project());
       }
diff --git a/java/com/google/gerrit/testing/InMemoryModule.java b/java/com/google/gerrit/testing/InMemoryModule.java
index fc045a5..9f1e4c5 100644
--- a/java/com/google/gerrit/testing/InMemoryModule.java
+++ b/java/com/google/gerrit/testing/InMemoryModule.java
@@ -47,6 +47,7 @@
 import com.google.gerrit.server.PluginUser;
 import com.google.gerrit.server.Sequence;
 import com.google.gerrit.server.Sequence.LightweightGroups;
+import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountCacheImpl;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.storage.notedb.AccountNoteDbReadStorageModule;
@@ -201,6 +202,8 @@
     bind(MetricMaker.class).to(DisabledMetricMaker.class);
 
     install(cfgInjector.getInstance(AccountCacheImpl.AccountCacheModule.class));
+    bind(AccountCache.class).to(AccountCacheImpl.class);
+
     install(cfgInjector.getInstance(GerritGlobalModule.class));
     install(new AccountNoteDbWriteStorageModule());
     install(new AccountNoteDbReadStorageModule());
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index dd4a1a6..3c21045 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -829,7 +829,7 @@
     reviewerInput.reviewer = user.email();
     input.reviewers.add(reviewerInput);
     gApi.changes().id(r.getChangeId()).current().review(input);
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message message = messages.get(0);
     assertThat(message.rcpt()).containsExactly(user.getNameEmail());
@@ -850,7 +850,7 @@
     input2.reviewers.add(reviewerInput3);
 
     gApi.changes().id(r.getChangeId()).current().review(input2);
-    List<Message> messages2 = sender.getMessages();
+    ImmutableList<Message> messages2 = sender.getMessages();
     assertThat(messages2).hasSize(1);
     Message message2 = messages2.get(0);
     assertThat(message2.rcpt()).containsExactly(user2.getNameEmail());
@@ -870,7 +870,7 @@
     input3.reviewers.add(reviewerInput5);
 
     gApi.changes().id(r.getChangeId()).current().review(input3);
-    List<Message> messages3 = sender.getMessages();
+    ImmutableList<Message> messages3 = sender.getMessages();
     assertThat(messages3).isEmpty();
   }
 
@@ -882,7 +882,7 @@
     ReviewerInput reviewerInput = new ReviewerInput();
     reviewerInput.reviewer = user.email();
     gApi.changes().id(r.getChangeId()).addReviewer(reviewerInput);
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message message = messages.get(0);
     assertThat(message.rcpt()).containsExactly(user.getNameEmail());
@@ -895,7 +895,7 @@
     ReviewerInput reviewerInput2 = new ReviewerInput();
     reviewerInput2.reviewer = user2.email();
     gApi.changes().id(r.getChangeId()).addReviewer(reviewerInput2);
-    List<Message> messages2 = sender.getMessages();
+    ImmutableList<Message> messages2 = sender.getMessages();
     assertThat(messages2).hasSize(1);
     Message message2 = messages2.get(0);
     assertThat(message2.rcpt()).containsExactly(user2.getNameEmail());
@@ -907,7 +907,7 @@
     ReviewerInput reviewerInput3 = new ReviewerInput();
     reviewerInput3.reviewer = user2.email();
     gApi.changes().id(r.getChangeId()).addReviewer(reviewerInput3);
-    List<Message> messages3 = sender.getMessages();
+    ImmutableList<Message> messages3 = sender.getMessages();
     assertThat(messages3).isEmpty();
   }
 
@@ -1032,8 +1032,9 @@
     AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
-      List<String> emails = ImmutableList.of("new.email@example.com", "new.email@example.systems");
-      Set<String> currentEmails = getEmails();
+      ImmutableList<String> emails =
+          ImmutableList.of("new.email@example.com", "new.email@example.systems");
+      ImmutableSet<String> currentEmails = getEmails();
       for (String email : emails) {
         assertThat(currentEmails).doesNotContain(email);
         EmailInput input = newEmailInput(email);
@@ -1048,7 +1049,7 @@
 
   @Test
   public void addInvalidEmail() throws Exception {
-    List<String> emails =
+    ImmutableList<String> emails =
         ImmutableList.of(
             // Missing domain part
             "new.email",
@@ -1230,7 +1231,7 @@
     gApi.accounts().self().addEmail(input);
 
     requestScopeOperations.resetCurrentApiUser();
-    Set<String> allEmails = getEmails();
+    ImmutableSet<String> allEmails = getEmails();
     assertThat(allEmails).hasSize(2);
 
     for (String email : allEmails) {
@@ -1452,7 +1453,7 @@
 
   @Test
   public void putStatus() throws Exception {
-    List<String> statuses = ImmutableList.of("OOO", "Busy");
+    ImmutableList<String> statuses = ImmutableList.of("OOO", "Busy");
     AccountInfo info;
     AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
     try (Registration registration =
@@ -1743,7 +1744,7 @@
     AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
-      List<TestKey> keys = allValidKeys();
+      ImmutableList<TestKey> keys = allValidKeys();
       List<String> toAdd = new ArrayList<>(keys.size());
       for (TestKey key : keys) {
         addExternalIdEmail(
@@ -2213,7 +2214,7 @@
 
   @Test
   public void failAfterRetryerGivesUp() throws Exception {
-    List<String> status = ImmutableList.of("foo", "bar", "baz");
+    ImmutableList<String> status = ImmutableList.of("foo", "bar", "baz");
     String fullName = "Foo";
     AtomicInteger bgCounter = new AtomicInteger(0);
     AccountsUpdate update =
@@ -3412,7 +3413,7 @@
           }
 
           @Override
-          public Set<AccountGroup.UUID> getKnownGroups() {
+          public ImmutableSet<AccountGroup.UUID> getKnownGroups() {
             // Typically for external group backends it's too expensive to query all groups that the
             // user is a member of. Instead limit the group membership check to groups that are
             // guessed to be relevant.
@@ -3490,7 +3491,7 @@
   }
 
   private static void assertIteratorSize(int size, Iterator<?> it) {
-    List<?> lst = ImmutableList.copyOf(it);
+    ImmutableList<?> lst = ImmutableList.copyOf(it);
     assertThat(lst).hasSize(size);
   }
 
@@ -3529,7 +3530,7 @@
     Account.Id currAccountId = localCtx.getContext().getUser().getAccountId();
     Iterable<String> expectedFps =
         expected.transform(k -> BaseEncoding.base16().encode(k.getPublicKey().getFingerprint()));
-    Iterable<String> actualFps =
+    Set<String> actualFps =
         externalIds.byAccount(currAccountId, SCHEME_GPGKEY).stream()
             .map(e -> e.key().id())
             .collect(toSet());
@@ -3551,7 +3552,7 @@
     assertWithMessage(id)
         .that(actual.fingerprint)
         .isEqualTo(Fingerprint.toString(expected.getPublicKey().getFingerprint()));
-    List<String> userIds = ImmutableList.copyOf(expected.getPublicKey().getUserIDs());
+    ImmutableList<String> userIds = ImmutableList.copyOf(expected.getPublicKey().getUserIDs());
     assertWithMessage(id).that(actual.userIds).containsExactlyElementsIn(userIds);
     String key = actual.key;
     assertWithMessage(id).that(key).startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----\n");
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
index 01e0bd4..ead4c40 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
@@ -127,7 +127,7 @@
       assertThat(info.auth.useContributorAgreements).isTrue();
       assertThat(info.auth.contributorAgreements).hasSize(2);
       // Sort to get a stable assertion as the API does not guarantee ordering.
-      List<AgreementInfo> agreements =
+      ImmutableList<AgreementInfo> agreements =
           ImmutableList.sortedCopyOf(comparing(a -> a.name), info.auth.contributorAgreements);
       assertAgreement(agreements.get(0), caAutoVerify);
       assertAgreement(agreements.get(1), caNoAutoVerify);
diff --git a/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java b/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java
index b9c5429..f0f262f 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java
@@ -79,7 +79,7 @@
     CurrentUser user = localCtx.getContext().getUser();
     PushOneCommit.Result a = createChange();
     PushOneCommit.Result b = createChange();
-    List<ChangeData> list = ImmutableList.of(a.getChange(), b.getChange());
+    ImmutableList<ChangeData> list = ImmutableList.of(a.getChange(), b.getChange());
     batchAbandon.batchAbandon(batchUpdateFactory, a.getChange().project(), user, list, "deadbeef");
 
     ChangeInfo info = get(a.getChangeId(), MESSAGES);
@@ -109,7 +109,7 @@
     CurrentUser user = localCtx.getContext().getUser();
     PushOneCommit.Result a = createChange(project1, "master", "x", "x", "x", "");
     PushOneCommit.Result b = createChange(project2, "master", "x", "x", "x", "");
-    List<ChangeData> list = ImmutableList.of(a.getChange(), b.getChange());
+    ImmutableList<ChangeData> list = ImmutableList.of(a.getChange(), b.getChange());
     ResourceConflictException thrown =
         assertThrows(
             ResourceConflictException.class,
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 91be148..12d3ced 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -148,6 +148,7 @@
 import com.google.gerrit.extensions.common.ChangeMessageInfo;
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.common.CommitInfo;
+import com.google.gerrit.extensions.common.CommitMessageInfo;
 import com.google.gerrit.extensions.common.LabelInfo;
 import com.google.gerrit.extensions.common.RevisionInfo;
 import com.google.gerrit.extensions.common.TrackingIdInfo;
@@ -187,6 +188,7 @@
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.context.RefUpdateContext;
 import com.google.gerrit.server.util.AccountTemplateUtil;
+import com.google.gerrit.server.util.CommitMessageUtil;
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.gerrit.testing.FakeEmailSender.Message;
 import com.google.inject.AbstractModule;
@@ -214,6 +216,7 @@
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.RefUpdate.Result;
@@ -1285,7 +1288,7 @@
     assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
     assertThat(change.reviewers.get(REVIEWER)).isNull();
 
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.from().name()).isEqualTo("Administrator (Code Review)");
@@ -1355,7 +1358,7 @@
     assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
     assertThat(change.reviewers.get(CC)).isNull();
 
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
@@ -1530,7 +1533,7 @@
 
     addReviewer.call(r.getChangeId(), user.email());
 
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
@@ -1658,7 +1661,7 @@
     in.reviewer = "abc";
     gApi.changes().id(r.getChangeId()).addReviewer(in.reviewer);
 
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(Address.create(fullname, email));
@@ -1719,7 +1722,7 @@
     in.reviewer = testGroup;
     gApi.changes().id(r.getChangeId()).addReviewer(in.reviewer);
 
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(Address.create(myGroupUserFullname, myGroupUserEmail));
@@ -2369,7 +2372,7 @@
         .reviewer(user.id().toString())
         .deleteVote(LabelId.CODE_REVIEW);
 
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message msg = messages.get(0);
     assertThat(msg.rcpt()).containsExactly(user.getNameEmail());
@@ -3988,6 +3991,22 @@
   }
 
   @Test
+  public void getCommitMessage() throws Exception {
+    String subject = "Change Subject";
+    String changeId = "I" + ObjectId.toString(CommitMessageUtil.generateChangeId());
+    String commitMessage =
+        String.format(
+            "%s\n\nFirst Paragraph.\n\nSecond Paragraph\n\nFoo: Bar\nChange-Id: %s\n",
+            subject, changeId);
+    changeOperations.newChange().project(project).commitMessage(commitMessage).create();
+
+    CommitMessageInfo commitMessageInfo = gApi.changes().id(changeId).getMessage();
+    assertThat(commitMessageInfo.subject).isEqualTo(subject);
+    assertThat(commitMessageInfo.fullMessage).isEqualTo(commitMessage);
+    assertThat(commitMessageInfo.footers).containsExactly("Foo", "Bar", "Change-Id", changeId);
+  }
+
+  @Test
   public void changeCommitMessage() throws Exception {
     // Tests mutating the commit message as both the owner of the change and a regular user with
     // addPatchSet permission. Asserts that both cases succeed.
@@ -4527,7 +4546,7 @@
   @Test
   public void changeDetailsDoesNotRequireIndex() throws Exception {
     // This set of options must be kept in sync with gr-rest-api-interface.js
-    Set<ListChangesOption> options =
+    ImmutableSet<ListChangesOption> options =
         ImmutableSet.of(
             ListChangesOption.ALL_COMMITS,
             ListChangesOption.ALL_REVISIONS,
@@ -4748,7 +4767,7 @@
     }
     sender.clear();
     gApi.changes().id(change).addReviewer(user.email());
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     assertThat(((StringEmailHeader) messages.get(0).headers().get("Subject")).getString())
         .contains("[" + expectedSizeBucket + "]");
diff --git a/javatests/com/google/gerrit/acceptance/api/change/QueryChangesIT.java b/javatests/com/google/gerrit/acceptance/api/change/QueryChangesIT.java
index ed349d3..c36c9f1 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/QueryChangesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/QueryChangesIT.java
@@ -86,7 +86,7 @@
     assertThat(result.get(0)).hasSize(2);
     assertThat(result.get(1)).hasSize(1);
 
-    List<Integer> firstResultIds =
+    ImmutableList<Integer> firstResultIds =
         ImmutableList.of(result.get(0).get(0)._number, result.get(0).get(1)._number);
     assertThat(firstResultIds).containsExactly(numericId1, numericId2);
     assertThat(result.get(1).get(0)._number).isEqualTo(numericId2);
@@ -291,7 +291,7 @@
     assertThat(result.get(0)).hasSize(2);
     assertThat(result.get(1)).hasSize(1);
 
-    List<Integer> firstResultIds =
+    ImmutableList<Integer> firstResultIds =
         ImmutableList.of(result.get(0).get(0)._number, result.get(0).get(1)._number);
     assertThat(firstResultIds).containsExactly(numericId1, numericId2);
     assertThat(result.get(1).get(0)._number).isEqualTo(numericId2);
diff --git a/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
index 3506404..cd3e76d 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
@@ -318,7 +318,7 @@
     sender.clear();
     ChangeInfo revertChange = gApi.changes().id(r.getChangeId()).revert().get();
 
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(2);
     assertThat(sender.getMessages(revertChange.changeId, "newchange")).hasSize(1);
     assertThat(sender.getMessages(r.getChangeId(), "revert")).hasSize(1);
@@ -751,7 +751,7 @@
     RevertSubmissionInfo revertChanges =
         gApi.changes().id(secondResult).revertSubmission(revertInput);
 
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
 
     assertThat(messages).hasSize(4);
     assertThat(sender.getMessages(revertChanges.revertChanges.get(0).changeId, "newchange"))
diff --git a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
index 2668d1f..5f44f99 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
@@ -33,6 +33,7 @@
 import static java.util.Comparator.comparing;
 
 import com.google.common.cache.Cache;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.MoreCollectors;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -471,7 +472,7 @@
 
     // The code-review approval is copied for the second change between PS1 and PS2 since the only
     // modified file is due to rebase.
-    List<PatchSetApproval> patchSetApprovals =
+    ImmutableList<PatchSetApproval> patchSetApprovals =
         r2.getChange().notes().getApprovals().all().values().stream()
             .sorted(comparing(a -> a.patchSetId().get()))
             .collect(toImmutableList());
@@ -1024,7 +1025,7 @@
       amendChange(r.getChangeId());
     }
 
-    List<PatchSetApproval> patchSetApprovals =
+    ImmutableList<PatchSetApproval> patchSetApprovals =
         r.getChange().notes().getApprovals().all().values().stream()
             .sorted(comparing(a -> a.patchSetId().get()))
             .collect(toImmutableList());
@@ -1065,7 +1066,7 @@
     // Rebase the second change
     gApi.changes().id(r2.getChangeId()).rebase();
 
-    List<PatchSetApproval> patchSetApprovals =
+    ImmutableList<PatchSetApproval> patchSetApprovals =
         r2.getChange().notes().getApprovals().all().values().stream()
             .sorted(comparing(a -> a.patchSetId().get()))
             .collect(toImmutableList());
@@ -1102,7 +1103,7 @@
     // Make a new patchset, keeping the Code-Review +1 vote.
     amendChange(r.getChangeId());
 
-    List<PatchSetApproval> patchSetApprovals =
+    ImmutableList<PatchSetApproval> patchSetApprovals =
         r.getChange().notes().getApprovals().all().values().stream()
             .sorted(comparing(a -> a.patchSetId().get()))
             .collect(toImmutableList());
@@ -1152,7 +1153,7 @@
     // Make a new patchset, keeping the Code-Review +1 vote.
     amendChange(r.getChangeId());
 
-    List<PatchSetApproval> patchSetApprovals =
+    ImmutableList<PatchSetApproval> patchSetApprovals =
         r.getChange().notes().getApprovals().all().values().stream()
             .sorted(comparing(a -> a.patchSetId().get()))
             .collect(toImmutableList());
@@ -1288,7 +1289,7 @@
           .create();
       vote(admin, changeId, 2, 1);
 
-      List<PatchSetApproval> patchSetApprovals =
+      ImmutableList<PatchSetApproval> patchSetApprovals =
           notesFactory.create(project, r.getChange().getId()).getApprovals().all().values().stream()
               .sorted(comparing(a -> a.patchSetId().get()))
               .collect(toImmutableList());
diff --git a/javatests/com/google/gerrit/acceptance/api/change/SubmitRequirementPredicateIT.java b/javatests/com/google/gerrit/acceptance/api/change/SubmitRequirementPredicateIT.java
index c3d36a7..8643489 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/SubmitRequirementPredicateIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/SubmitRequirementPredicateIT.java
@@ -49,7 +49,6 @@
 import com.google.gerrit.server.project.SubmitRequirementsEvaluatorImpl;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.inject.Inject;
-import java.util.List;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.ObjectId;
@@ -107,7 +106,7 @@
     Account.Id user15 = accountCreator.create("user15").id();
     Account.Id user16 = accountCreator.create("user16").id();
     Account.Id user17 = accountCreator.create("user17").id();
-    List<Account.Id> allUsers =
+    ImmutableList<Account.Id> allUsers =
         ImmutableList.of(user11, user12, user13, user14, user15, user16, user17);
 
     // Give voting permissions to all users
diff --git a/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java b/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
index 7d9895c..d1d3620 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
@@ -42,7 +42,6 @@
 import com.google.gerrit.server.git.meta.VersionedMetaData;
 import com.google.gerrit.testing.ConfigSuite;
 import java.io.IOException;
-import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -202,7 +201,7 @@
     gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
     gApi.changes().id(r.getChangeId()).current().submit();
 
-    List<RevCommit> log = log("master", 1);
+    ImmutableList<RevCommit> log = log("master", 1);
     assertThat(log.get(0).getShortMessage()).isEqualTo("CHERRY_PICK 1");
     assertThat(log.get(0).name()).isNotEqualTo(r.getCommit().name());
     assertThat(log.get(0).getFullMessage()).contains("Change-Id: " + r.getChangeId());
@@ -231,7 +230,7 @@
 
     assertThat(log("master", 1).get(0).name()).isEqualTo(r1.getCommit().name());
 
-    List<RevCommit> branchLog = log("branch", 1);
+    ImmutableList<RevCommit> branchLog = log("branch", 1);
     assertThat(branchLog.get(0).getParents()).hasLength(2);
     assertThat(branchLog.get(0).getParent(1).name()).isEqualTo(r2.getCommit().name());
   }
@@ -293,7 +292,7 @@
     return info;
   }
 
-  private List<RevCommit> log(String commitish, int n) throws Exception {
+  private ImmutableList<RevCommit> log(String commitish, int n) throws Exception {
     try (Repository repo = repoManager.openRepository(project);
         Git git = new Git(repo)) {
       ObjectId id = repo.resolve(commitish);
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
index 125c5a7..2f3ef24 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
@@ -41,7 +41,6 @@
 import com.google.inject.Provider;
 import java.io.IOException;
 import java.util.Optional;
-import java.util.Set;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.junit.Rule;
 import org.junit.Test;
@@ -68,7 +67,7 @@
 
     groupIndexer.index(groupUuid);
 
-    Set<AccountGroup.UUID> parentGroups =
+    ImmutableSet<AccountGroup.UUID> parentGroups =
         groupQueryProvider.get().bySubgroups(ImmutableSet.of(subgroupUuid)).get(subgroupUuid);
     assertThat(parentGroups).hasSize(1);
     assertThat(parentGroups).containsExactly(groupUuid);
@@ -87,7 +86,7 @@
 
     groupIndexer.index(groupUuid);
 
-    Set<AccountGroup.UUID> parentGroups =
+    ImmutableSet<AccountGroup.UUID> parentGroups =
         groupQueryProvider.get().bySubgroups(ImmutableSet.of(subgroupUuid)).get(subgroupUuid);
     assertThat(parentGroups).hasSize(1);
     assertThat(parentGroups).containsExactly(groupUuid);
@@ -117,7 +116,7 @@
 
     groupIndexer.reindexIfStale(groupUuid);
 
-    Set<AccountGroup.UUID> parentGroups =
+    ImmutableSet<AccountGroup.UUID> parentGroups =
         groupQueryProvider.get().bySubgroups(ImmutableSet.of(subgroupUuid)).get(subgroupUuid);
     assertThat(parentGroups).hasSize(1);
     assertThat(parentGroups).containsExactly(groupUuid);
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
index db663da..0f7289f 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -117,6 +117,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Stream;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
 import org.eclipse.jgit.junit.TestRepository;
@@ -944,7 +945,7 @@
 
   @Test
   public void defaultGroupsCreated() throws Exception {
-    Iterable<String> names = gApi.groups().list().getAsMap().keySet();
+    Set<String> names = gApi.groups().list().getAsMap().keySet();
     assertThat(names)
         .containsAtLeast("Administrators", ServiceUserClassifier.SERVICE_USERS)
         .inOrder();
@@ -1700,7 +1701,7 @@
   }
 
   private static void assertIncludes(List<GroupInfo> includes, String... expectedNames) {
-    List<String> names = includes.stream().map(i -> i.name).collect(toImmutableList());
+    ImmutableList<String> names = includes.stream().map(i -> i.name).collect(toImmutableList());
     assertThat(names).containsExactlyElementsIn(Arrays.asList(expectedNames));
     assertThat(names).isInOrder();
   }
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
index 27193dd..a5fa55a 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
@@ -227,7 +227,7 @@
 
   @Test
   public void accessible() throws Exception {
-    List<TestCase> inputs =
+    ImmutableList<TestCase> inputs =
         ImmutableList.of(
             // Test 1
             TestCase.projectRefPerm(
diff --git a/javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java b/javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java
index b234ea3..a99cf52 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java
@@ -18,6 +18,7 @@
 import static com.google.gerrit.acceptance.GitUtil.fetch;
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.testsuite.change.IndexOperations;
@@ -36,7 +37,6 @@
 import com.google.gerrit.server.project.ProjectConfig;
 import com.google.inject.Inject;
 import java.util.Collection;
-import java.util.Map;
 import java.util.Optional;
 import java.util.function.Consumer;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
@@ -68,7 +68,8 @@
     Iterable<byte[]> refState = result.get().getValue(ProjectField.REF_STATE_SPEC);
     assertThat(refState).isNotEmpty();
 
-    Map<Project.NameKey, Collection<RefState>> states = RefState.parseStates(refState).asMap();
+    ImmutableMap<Project.NameKey, Collection<RefState>> states =
+        RefState.parseStates(refState).asMap();
 
     fetch(testRepo, "refs/meta/config:refs/meta/config");
     Ref projectConfigRef = testRepo.getRepository().exactRef("refs/meta/config");
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/PortedCommentsIT.java b/javatests/com/google/gerrit/acceptance/api/revision/PortedCommentsIT.java
index ba45fb2..1aea889 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/PortedCommentsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/PortedCommentsIT.java
@@ -77,7 +77,7 @@
     newComment(patchset2Id).create();
     newComment(patchset3Id).create();
 
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
 
     assertThatList(portedComments).comparingElementsUsing(hasUuid()).containsExactly(comment1Uuid);
   }
@@ -94,7 +94,7 @@
     String comment1Uuid = newComment(patchset1Id).create();
     String comment3Uuid = newComment(patchset3Id).create();
 
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset4Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset4Id));
 
     assertThat(portedComments)
         .comparingElementsUsing(hasUuid())
@@ -111,7 +111,7 @@
     String comment1Uuid = newComment(patchset1Id).create();
     String comment2Uuid = newComment(patchset1Id).create();
 
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
 
     assertThat(portedComments)
         .comparingElementsUsing(hasUuid())
@@ -129,7 +129,7 @@
     String child1CommentUuid = newComment(patchset1Id).parentUuid(rootCommentUuid).create();
     String child2CommentUuid = newComment(patchset1Id).parentUuid(child1CommentUuid).create();
 
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
 
     assertThat(portedComments)
         .comparingElementsUsing(hasUuid())
@@ -146,7 +146,7 @@
     newComment(patchset1Id).resolved().create();
     String comment2Uuid = newComment(patchset1Id).unresolved().create();
 
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
 
     assertThat(portedComments).comparingElementsUsing(hasUuid()).containsExactly(comment2Uuid);
   }
@@ -174,7 +174,7 @@
     rebaseChangeOn(changeId.toString(), newBase);
     PatchSet.Id ps3Id = changeOps.change(changeId).currentPatchset().get().patchsetId();
 
-    List<CommentInfo> portedComments = flatten(getPortedComments(ps3Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(ps3Id));
     assertThat(portedComments).hasSize(1);
     int portedLine = portedComments.get(0).line;
     BinaryResult fileContent = gApi.changes().id(changeId.get()).current().file(fileName).content();
@@ -195,7 +195,7 @@
     String comment1Uuid = newDraftComment(patchset1Id).author(accountId).resolved().create();
     String comment2Uuid = newDraftComment(patchset1Id).author(accountId).unresolved().create();
 
-    List<CommentInfo> portedComments =
+    ImmutableList<CommentInfo> portedComments =
         flatten(getPortedDraftCommentsOfUser(patchset2Id, accountId));
 
     assertThat(portedComments)
@@ -216,7 +216,7 @@
     String rootComment2Uuid = newComment(patchset1Id).unresolved().create();
     newComment(patchset1Id).parentUuid(rootComment2Uuid).resolved().create();
 
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
 
     assertThat(portedComments)
         .comparingElementsUsing(hasUuid())
@@ -246,7 +246,7 @@
             .createdOn(now.plusSeconds(10))
             .create();
 
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
 
     assertThat(portedComments)
         .comparingElementsUsing(hasUuid())
@@ -272,7 +272,7 @@
 
     // Draft comments are only visible to their author.
     requestScopeOps.setApiUser(accountId);
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
 
     assertThat(portedComments).comparingElementsUsing(hasUuid()).containsExactly(rootComment2Uuid);
   }
@@ -289,7 +289,7 @@
 
     // Draft comments are only visible to their author.
     requestScopeOps.setApiUser(accountId);
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
 
     assertThatList(portedComments).isEmpty();
   }
@@ -304,7 +304,7 @@
     // Add comment.
     newComment(patchset1Id).author(accountId).create();
 
-    List<CommentInfo> portedComments =
+    ImmutableList<CommentInfo> portedComments =
         flatten(getPortedDraftCommentsOfUser(patchset2Id, accountId));
 
     assertThatList(portedComments).isEmpty();
@@ -320,7 +320,7 @@
     // Add draft comment.
     newComment(patchset1Id).author(accountId).create();
 
-    List<CommentInfo> portedComments =
+    ImmutableList<CommentInfo> portedComments =
         flatten(getPortedDraftCommentsOfUser(patchset2Id, accountId));
 
     assertThatList(portedComments).isEmpty();
@@ -337,7 +337,8 @@
     // Add draft comment.
     newComment(patchset1Id).author(otherUserId).create();
 
-    List<CommentInfo> portedComments = flatten(getPortedDraftCommentsOfUser(patchset2Id, userId));
+    ImmutableList<CommentInfo> portedComments =
+        flatten(getPortedDraftCommentsOfUser(patchset2Id, userId));
 
     assertThatList(portedComments).isEmpty();
   }
@@ -365,7 +366,7 @@
     String patchsetLevelCommentUuid =
         newComment(patchset1Id).message("Patchset-level comment").onPatchsetLevel().create();
 
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
 
     assertThat(portedComments)
         .comparingElementsUsing(hasUuid())
@@ -382,7 +383,7 @@
     // Add comments.
     String commentUuid = newComment(patchset1Id).onParentCommit().create();
 
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
 
     assertThat(portedComments).comparingElementsUsing(hasUuid()).containsExactly(commentUuid);
   }
@@ -396,7 +397,7 @@
     // Add comments.
     String commentUuid = newComment(patchset1Id).onSecondParentCommit().create();
 
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
 
     assertThat(portedComments).comparingElementsUsing(hasUuid()).containsExactly(commentUuid);
   }
@@ -411,7 +412,7 @@
     String commentUuid1 = newComment(patchset1Id).onFileLevelOf("not-existing file").create();
     String commentUuid2 = newComment(patchset1Id).onLine(3).ofFile("myFile").create();
 
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
 
     assertThat(portedComments)
         .comparingElementsUsing(hasUuid())
@@ -441,7 +442,7 @@
     // Add comment.
     String commentUuid = newComment(patchset1Id).create();
 
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchset2Id));
 
     assertThatList(portedComments).onlyElement().uuid().isEqualTo(commentUuid);
   }
@@ -1859,7 +1860,7 @@
         .revision(patchsetId1.get())
         .comment(commentUuid)
         .delete(new DeleteCommentInput());
-    List<CommentInfo> portedComments = flatten(getPortedComments(patchsetId2));
+    ImmutableList<CommentInfo> portedComments = flatten(getPortedComments(patchsetId2));
 
     assertThatList(portedComments).isEmpty();
   }
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index 77ee574..e4d4610 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -1693,7 +1693,8 @@
 
   @Test
   public void queryRevisionFiles() throws Exception {
-    Map<String, String> files = ImmutableMap.of("file1.txt", "content 1", "file2.txt", "content 2");
+    ImmutableMap<String, String> files =
+        ImmutableMap.of("file1.txt", "content 1", "file2.txt", "content 2");
     PushOneCommit.Result result =
         pushFactory.create(admin.newIdent(), testRepo, SUBJECT, files).to("refs/for/master");
     result.assertOkStatus();
@@ -1813,7 +1814,7 @@
   @Test
   @GerritConfig(name = "change.maxFileSizeDownload", value = "10")
   public void content_maxFileSizeDownload() throws Exception {
-    Map<String, String> files =
+    ImmutableMap<String, String> files =
         ImmutableMap.of("dir/file1.txt", " 9 bytes ", "dir/file2.txt", "11 bytes xx");
     PushOneCommit.Result result =
         pushFactory.create(admin.newIdent(), testRepo, SUBJECT, files).to("refs/for/master");
@@ -1853,7 +1854,7 @@
 
   @Test
   public void cannotGetContentOfDirectory() throws Exception {
-    Map<String, String> files = ImmutableMap.of("dir/file1.txt", "content 1");
+    ImmutableMap<String, String> files = ImmutableMap.of("dir/file1.txt", "content 1");
     PushOneCommit.Result result =
         pushFactory.create(admin.newIdent(), testRepo, SUBJECT, files).to("refs/for/master");
     result.assertOkStatus();
@@ -2235,7 +2236,7 @@
 
     // check that reviewer is notified.
     amendChange(r.getChangeId());
-    List<FakeEmailSender.Message> messages = sender.getMessages();
+    ImmutableList<FakeEmailSender.Message> messages = sender.getMessages();
     FakeEmailSender.Message m = Iterables.getOnlyElement(messages);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
     assertThat(m.body()).contains("I'd like you to reexamine a change.");
@@ -2264,7 +2265,7 @@
 
     // check that watcher is notified
     amendChange(r.getChangeId());
-    List<FakeEmailSender.Message> messages = sender.getMessages();
+    ImmutableList<FakeEmailSender.Message> messages = sender.getMessages();
     FakeEmailSender.Message m = Iterables.getOnlyElement(messages);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
     assertThat(m.body()).contains(admin.fullName() + " has uploaded a new patch set (#2).");
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 089938e..8861a9e 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -1533,7 +1533,7 @@
   public void pushForMasterWithHashtags() throws Exception {
     // specify a single hashtag as option
     String hashtag1 = "tag1";
-    Set<String> expected = ImmutableSet.of(hashtag1);
+    ImmutableSet<String> expected = ImmutableSet.of(hashtag1);
     PushOneCommit.Result r = pushTo("refs/for/master%hashtag=#" + hashtag1);
     r.assertOkStatus();
     r.assertChange(Change.Status.NEW, null);
@@ -1563,7 +1563,7 @@
     // specify multiple hashtags as options
     String hashtag1 = "tag1";
     String hashtag2 = "tag2";
-    Set<String> expected = ImmutableSet.of(hashtag1, hashtag2);
+    ImmutableSet<String> expected = ImmutableSet.of(hashtag1, hashtag2);
     PushOneCommit.Result r =
         pushTo("refs/for/master%hashtag=#" + hashtag1 + ",hashtag=##" + hashtag2);
     r.assertOkStatus();
@@ -2383,7 +2383,7 @@
     addDraft(r.getChangeId(), rev2, newDraft(FILE_NAME, 1, "comment_PS3."));
     amendChange(r.getChangeId(), "refs/for/master%publish-comments");
 
-    Collection<CommentInfo> comments = getPublishedComments(r.getChangeId());
+    List<CommentInfo> comments = getPublishedComments(r.getChangeId());
     List<ChangeMessageInfo> allMessages = getMessages(r.getChangeId());
 
     assertThat(allMessages.stream().map(m -> m.message).collect(toList()))
@@ -2446,7 +2446,7 @@
     sender.clear();
     amendChange(r.getChangeId(), "refs/for/master%publish-comments");
 
-    Collection<CommentInfo> comments = getPublishedComments(r.getChangeId());
+    List<CommentInfo> comments = getPublishedComments(r.getChangeId());
     assertThat(comments.stream().map(c -> c.id)).containsExactly(c1.id, c2.id, c3.id);
     assertThat(comments.stream().map(c -> c.message))
         .containsExactly("comment1", "comment2", "comment3");
@@ -2511,7 +2511,7 @@
 
     r = amendChange(r.getChangeId(), "refs/for/master%publish-comments,m=The_message");
 
-    Collection<CommentInfo> comments = getPublishedComments(r.getChangeId());
+    List<CommentInfo> comments = getPublishedComments(r.getChangeId());
     assertThat(comments.stream().map(c -> c.message)).containsExactly("comment1");
     assertThat(getLastMessage(r.getChangeId())).isEqualTo("Patch Set 2:\n" + "\n" + "(1 comment)");
   }
@@ -2530,7 +2530,7 @@
 
     amendChanges(initialHead, commits, "refs/for/master%publish-comments");
 
-    Collection<CommentInfo> cs1 = getPublishedComments(id1);
+    List<CommentInfo> cs1 = getPublishedComments(id1);
     List<ChangeMessageInfo> messages1 = getMessages(id1);
     assertThat(cs1.stream().map(c -> c.message)).containsExactly("comment1");
     assertThat(cs1.stream().map(c -> c.id)).containsExactly(c1.id);
@@ -2539,7 +2539,7 @@
         .isEqualTo("Uploaded patch set 2: Commit message was updated.");
     assertThat(messages1.get(2).message).isEqualTo("Patch Set 2:\n\n(1 comment)");
 
-    Collection<CommentInfo> cs2 = getPublishedComments(id2);
+    List<CommentInfo> cs2 = getPublishedComments(id2);
     List<ChangeMessageInfo> messages2 = getMessages(id2);
     assertThat(cs2.stream().map(c -> c.message)).containsExactly("comment2");
     assertThat(cs2.stream().map(c -> c.id)).containsExactly(c2.id);
@@ -2566,7 +2566,7 @@
     assertThat(getPublishedComments(id1)).isEmpty();
     assertThat(gApi.changes().id(id1).drafts()).hasSize(1);
 
-    Collection<CommentInfo> cs2 = getPublishedComments(id2);
+    List<CommentInfo> cs2 = getPublishedComments(id2);
     assertThat(cs2.stream().map(c -> c.message)).containsExactly("comment2");
     assertThat(cs2.stream().map(c -> c.id)).containsExactly(c2.id);
 
@@ -2837,7 +2837,7 @@
     assertThat(pr.getMessages()).contains("Invalid value in -o notedb=foobar");
     assertPushRejected(pr, ref, "NoteDb update requires -o notedb=allow");
 
-    List<String> opts = ImmutableList.of("notedb=allow");
+    ImmutableList<String> opts = ImmutableList.of("notedb=allow");
     pr = pushOne(testRepo, c.name(), ref, false, false, opts);
     assertPushRejected(pr, ref, "NoteDb update requires access database permission");
 
@@ -3197,7 +3197,7 @@
     return gApi.changes().id(changeId).revision(revId).createDraft(in).get();
   }
 
-  private Collection<CommentInfo> getPublishedComments(String changeId) throws Exception {
+  private List<CommentInfo> getPublishedComments(String changeId) throws Exception {
     return gApi.changes().id(changeId).commentsRequest().get().values().stream()
         .flatMap(Collection::stream)
         .collect(toList());
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java b/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java
index 8295550..1b5b637 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java
@@ -20,6 +20,7 @@
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
 import static java.util.stream.Collectors.toList;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.PushOneCommit;
@@ -45,7 +46,6 @@
 import com.google.gerrit.testing.FakeEmailSender.Message;
 import com.google.gerrit.testing.RefUpdateContextCollector;
 import com.google.inject.Inject;
-import java.util.List;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.InvalidRemoteException;
 import org.eclipse.jgit.api.errors.TransportException;
@@ -228,7 +228,7 @@
     PushOneCommit.Result r = push("refs/for/master", PushOneCommit.SUBJECT, "two.txt", "Two");
     startEventRecorder();
     git().push().setRefSpecs(new RefSpec(r.getCommit().name() + ":refs/heads/master")).call();
-    List<ChangeMergedEvent> changeMergedEvents =
+    ImmutableList<ChangeMergedEvent> changeMergedEvents =
         eventRecorder.getChangeMergedEvents(project.get(), "refs/heads/master", 2);
     assertThat(changeMergedEvents.get(0).newRev).isEqualTo(r.getPatchSet().commitId().name());
     assertThat(changeMergedEvents.get(1).newRev).isEqualTo(r.getPatchSet().commitId().name());
diff --git a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index 0dd026f..6c5febd 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -1406,7 +1406,7 @@
             .to("refs/for/" + RefNames.REFS_USERS_SELF);
     mr.assertOkStatus();
 
-    List<String> expectedNonMetaRefs =
+    ImmutableList<String> expectedNonMetaRefs =
         ImmutableList.of(
             RefNames.refsUsers(admin.id()),
             RefNames.refsUsers(user.id()),
@@ -1548,7 +1548,7 @@
     return AccountGroup.uuid(gApi.groups().create(groupInput).get().id);
   }
 
-  private static Collection<String> names(Collection<Ref> refs) {
+  private static ImmutableList<String> names(Collection<Ref> refs) {
     return refs.stream().map(Ref::getName).collect(toImmutableList());
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java b/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java
index 3cf54f3..e18c79d 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java
@@ -91,7 +91,7 @@
             .add(
                 new RefOperationValidationListener() {
                   @Override
-                  public List<ValidationMessage> onRefOperation(RefReceivedEvent refEvent)
+                  public ImmutableList<ValidationMessage> onRefOperation(RefReceivedEvent refEvent)
                       throws ValidationException {
                     return ImmutableList.of(
                         new ValidationMessage(message1, ValidationMessage.Type.HINT),
diff --git a/javatests/com/google/gerrit/acceptance/pgm/AbstractReindexTests.java b/javatests/com/google/gerrit/acceptance/pgm/AbstractReindexTests.java
index b7a3999..c5059c3 100644
--- a/javatests/com/google/gerrit/acceptance/pgm/AbstractReindexTests.java
+++ b/javatests/com/google/gerrit/acceptance/pgm/AbstractReindexTests.java
@@ -21,6 +21,7 @@
 import static com.google.gerrit.extensions.client.ListGroupsOption.MEMBERS;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.io.MoreFiles;
 import com.google.common.io.RecursiveDeleteOption;
 import com.google.gerrit.acceptance.NoHttpd;
@@ -44,7 +45,6 @@
 import com.google.inject.TypeLiteral;
 import java.nio.file.Files;
 import java.util.Collection;
-import java.util.Set;
 import java.util.function.Consumer;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
@@ -289,7 +289,8 @@
   }
 
   private void assertReady(int expectedReady) throws Exception {
-    Set<Integer> allVersions = ChangeSchemaDefinitions.INSTANCE.getSchemas().keySet();
+    ImmutableSortedSet<Integer> allVersions =
+        ChangeSchemaDefinitions.INSTANCE.getSchemas().keySet();
     GerritIndexStatus status = new GerritIndexStatus(sitePaths);
     assertWithMessage("ready state for index versions")
         .that(
diff --git a/javatests/com/google/gerrit/acceptance/rest/RestApiServletIT.java b/javatests/com/google/gerrit/acceptance/rest/RestApiServletIT.java
index 94b83cb..5c1f7c2 100644
--- a/javatests/com/google/gerrit/acceptance/rest/RestApiServletIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/RestApiServletIT.java
@@ -20,6 +20,7 @@
 import static com.google.gerrit.httpd.restapi.RestApiServlet.X_GERRIT_UPDATED_REF_ENABLED;
 import static org.apache.http.HttpStatus.SC_OK;
 
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.PushOneCommit.Result;
 import com.google.gerrit.acceptance.RestResponse;
@@ -34,7 +35,6 @@
 import com.google.gerrit.httpd.restapi.RestApiServlet;
 import com.google.inject.Inject;
 import java.io.IOException;
-import java.util.List;
 import java.util.regex.Pattern;
 import org.apache.http.message.BasicHeader;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
@@ -202,7 +202,7 @@
             "/changes/" + change.getChangeId(), X_GERRIT_UPDATED_REF_ENABLED_HEADER);
     response.assertNoContent();
 
-    List<String> headers = response.getHeaders(X_GERRIT_UPDATED_REF);
+    ImmutableList<String> headers = response.getHeaders(X_GERRIT_UPDATED_REF);
 
     // The change was deleted, so the refs were deleted which means they are ObjectId.zeroId().
     assertThat(headers)
@@ -236,7 +236,7 @@
             "/changes/" + change.getChangeId(), X_GERRIT_UPDATED_REF_ENABLED_HEADER);
     response.assertNoContent();
 
-    List<String> headers = response.getHeaders(X_GERRIT_UPDATED_REF);
+    ImmutableList<String> headers = response.getHeaders(X_GERRIT_UPDATED_REF);
 
     // The change was deleted, so the refs were deleted which means they are ObjectId.zeroId().
     assertThat(headers)
@@ -286,7 +286,7 @@
       ObjectId firstMetaRefSha1 = getMetaRefSha1(change1);
       ObjectId secondMetaRefSha1 = getMetaRefSha1(change2);
 
-      List<String> headers = response.getHeaders(X_GERRIT_UPDATED_REF);
+      ImmutableList<String> headers = response.getHeaders(X_GERRIT_UPDATED_REF);
 
       String branch = change1.getChange().change().getDest().branch();
       String branchSha1 =
@@ -363,7 +363,7 @@
       ObjectId firstMetaRefSha1 = getMetaRefSha1(change1);
       ObjectId secondMetaRefSha1 = getMetaRefSha1(change2);
 
-      List<String> headers = response.getHeaders(X_GERRIT_UPDATED_REF);
+      ImmutableList<String> headers = response.getHeaders(X_GERRIT_UPDATED_REF);
 
       String branchSha1Project1 =
           repository1
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java b/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
index 79e8ab0..9b6b260 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
@@ -16,6 +16,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.TestAccount;
 import com.google.gerrit.entities.Account;
@@ -49,7 +50,7 @@
    * @param actual the AccountInfos that should be asserted
    */
   public static void assertAccountInfos(List<TestAccount> expected, List<AccountInfo> actual) {
-    Iterable<Account.Id> expectedIds = TestAccount.ids(expected);
+    ImmutableList<Account.Id> expectedIds = TestAccount.ids(expected);
     Iterable<Account.Id> actualIds = Iterables.transform(actual, a -> Account.id(a._accountId));
     assertThat(actualIds).containsExactlyElementsIn(expectedIds).inOrder();
     for (int i = 0; i < expected.size(); i++) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
index acd2c15..e23df31 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
@@ -109,7 +109,7 @@
 
   @Test
   public void getExternalIds() throws Exception {
-    Collection<ExternalId> expectedIds = getAccountState(user.id()).externalIds();
+    ImmutableSet<ExternalId> expectedIds = getAccountState(user.id()).externalIds();
     List<AccountExternalIdInfo> expectedIdInfos = toExternalIdInfos(expectedIds);
 
     RestResponse response = userRestSession.get("/accounts/self/external.ids");
@@ -139,7 +139,7 @@
         .add(allowCapability(GlobalCapability.MODIFY_ACCOUNT).group(REGISTERED_USERS))
         .update();
 
-    Collection<ExternalId> expectedIds = getAccountState(admin.id()).externalIds();
+    ImmutableSet<ExternalId> expectedIds = getAccountState(admin.id()).externalIds();
     List<AccountExternalIdInfo> expectedIdInfos = toExternalIdInfos(expectedIds);
 
     RestResponse response = userRestSession.get("/accounts/" + admin.id() + "/external.ids");
@@ -509,11 +509,11 @@
     insertValidExternalIds();
     insertInvalidButParsableExternalIds();
 
-    Set<ExternalId> parseableExtIds = externalIds.all();
+    ImmutableSet<ExternalId> parseableExtIds = externalIds.all();
 
     insertNonParsableExternalIds();
 
-    Set<ExternalId> extIds = externalIds.all();
+    ImmutableSet<ExternalId> extIds = externalIds.all();
     assertThat(extIds).containsExactlyElementsIn(parseableExtIds);
 
     for (ExternalId parseableExtId : parseableExtIds) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
index 75f335a..cc86d02 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
@@ -78,6 +78,7 @@
           RestCall.get("/changes/%s/meta_diff"),
           RestCall.post("/changes/%s/merge"),
           RestCall.get("/changes/%s/messages"),
+          RestCall.get("/changes/%s/message"),
           RestCall.put("/changes/%s/message"),
           RestCall.post("/changes/%s/move"),
           RestCall.post("/changes/%s/patch:apply"),
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
index 4917fe3..32b9010 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
@@ -52,7 +52,6 @@
 import com.google.inject.Inject;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Optional;
@@ -179,7 +178,7 @@
         messageTemplate,
         Iterables.getLast(gApi.changes().id(changeId).get(MESSAGES).messages).message);
 
-    Collection<ChangeMessageInfo> listMessages = gApi.changes().id(changeId).messages();
+    List<ChangeMessageInfo> listMessages = gApi.changes().id(changeId).messages();
     assertThat(listMessages).hasSize(2);
     ChangeMessageInfo changeMessageApi = Iterables.getLast(gApi.changes().id(changeId).messages());
     assertMessage("Review by " + admin.getNameEmail(), changeMessageApi.message);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
index 0182f58..6cadf33 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
@@ -221,7 +221,7 @@
       input.state = state;
       gApi.changes().id(r.getChangeId()).addReviewer(input);
 
-      List<Message> messages = sender.getMessages();
+      ImmutableList<Message> messages = sender.getMessages();
       assertThat(messages).hasSize(1);
       assertThat(messages.get(0).rcpt()).containsExactly(Address.parse(input.reviewer));
       sender.clear();
@@ -252,7 +252,7 @@
       // Delete as admin
       gApi.changes().id(r.getChangeId()).reviewer(addInput.reviewer).remove();
 
-      List<Message> messages = sender.getMessages();
+      ImmutableList<Message> messages = sender.getMessages();
       assertThat(messages).hasSize(1);
       assertThat(messages.get(0).rcpt())
           .containsExactly(Address.parse(addInput.reviewer), user.getNameEmail());
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
index 66b6fc2..cf88b5a 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
@@ -154,7 +154,7 @@
     assertReviewers(c, CC, user);
 
     // Verify email was sent to CCed account.
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
@@ -232,7 +232,7 @@
     assertReviewers(c, CC, firstUsers);
 
     // Verify emails were sent to each of the group's accounts.
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     List<Address> expectedAddresses = new ArrayList<>(firstUsers.size());
@@ -444,7 +444,7 @@
     assertReviewers(c, CC, observer);
 
     // Verify emails were sent to added reviewers.
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(2);
 
     Message m = messages.get(0);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
index f8eccb5..dcdbce3 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -89,7 +89,6 @@
 import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CyclicBarrier;
 import java.util.concurrent.ExecutorService;
@@ -371,7 +370,7 @@
     requestScopeOperations.setApiUser(admin.id());
     assertCreateSucceeds(newChangeInput(ChangeStatus.NEW));
 
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
@@ -564,7 +563,7 @@
 
   @Test
   public void createChangeWithParentCommit() throws Exception {
-    Map<String, PushOneCommit.Result> setup =
+    ImmutableMap<String, PushOneCommit.Result> setup =
         changeInTwoBranches("foo", "foo.txt", "bar", "bar.txt");
     ChangeInput input = newChangeInput(ChangeStatus.NEW);
     input.baseCommit = setup.get("master").getCommit().getId().name();
@@ -603,7 +602,7 @@
 
   @Test
   public void createChangeWithParentCommitOnWrongBranchFails() throws Exception {
-    Map<String, PushOneCommit.Result> setup =
+    ImmutableMap<String, PushOneCommit.Result> setup =
         changeInTwoBranches("foo", "foo.txt", "bar", "bar.txt");
     ChangeInput input = newChangeInput(ChangeStatus.NEW);
     input.branch = "foo";
@@ -638,7 +637,7 @@
 
   @Test
   public void createChangeWithoutAccessToParentCommitFails() throws Exception {
-    Map<String, PushOneCommit.Result> results =
+    ImmutableMap<String, PushOneCommit.Result> results =
         changeInTwoBranches("invisible-branch", "a.txt", "visible-branch", "b.txt");
     projectOperations
         .project(project)
@@ -1470,7 +1469,7 @@
    * @param fileB name of file to commit to branchB
    * @return A {@code Map} of branchName => commit result.
    */
-  private Map<String, Result> changeInTwoBranches(
+  private ImmutableMap<String, Result> changeInTwoBranches(
       String branchA, String fileA, String branchB, String fileB) throws Exception {
     return changeInTwoBranches(
         branchA, "change A", fileA, "A content", branchB, "change B", fileB, "B content");
@@ -1489,7 +1488,7 @@
    * @param contentB file content to commit to branchB
    * @return A {@code Map} of branchName => commit result.
    */
-  private Map<String, Result> changeInTwoBranches(
+  private ImmutableMap<String, Result> changeInTwoBranches(
       String branchA,
       String subjectA,
       String fileA,
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java b/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
index 6491202..b1e8ba1 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
@@ -22,6 +22,7 @@
 import static com.google.gerrit.extensions.client.ReviewerState.REVIEWER;
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -42,7 +43,6 @@
 import com.google.gson.reflect.TypeToken;
 import com.google.inject.Inject;
 import java.util.Collection;
-import java.util.List;
 import java.util.Map;
 import org.junit.Test;
 
@@ -192,7 +192,7 @@
     RestResponse response = userRestSession.delete(deleteAdminVoteEndPoint);
     response.assertNoContent();
 
-    List<FakeEmailSender.Message> messages = sender.getMessages();
+    ImmutableList<FakeEmailSender.Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     FakeEmailSender.Message msg = messages.get(0);
     assertThat(msg.rcpt()).containsExactly(admin.getNameEmail(), user2.getNameEmail());
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
index 10f4942..d3a622f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
@@ -26,6 +26,7 @@
 import static java.util.stream.Collectors.toList;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.TestAccount;
@@ -48,7 +49,6 @@
 import com.google.inject.Inject;
 import java.util.List;
 import java.util.Locale;
-import java.util.Set;
 import java.util.stream.IntStream;
 import org.junit.Before;
 import org.junit.Test;
@@ -654,7 +654,7 @@
   }
 
   private AccountGroup.UUID createGroupWithArbitraryMembers(int numMembers) {
-    Set<Account.Id> members =
+    ImmutableSet<Account.Id> members =
         IntStream.rangeClosed(1, numMembers)
             .mapToObj(i -> accountOperations.newAccount().create())
             .collect(toImmutableSet());
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
index 2f68522..03af621 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
@@ -126,7 +126,7 @@
             .add(
                 new RefOperationValidationListener() {
                   @Override
-                  public List<ValidationMessage> onRefOperation(RefReceivedEvent refEvent)
+                  public ImmutableList<ValidationMessage> onRefOperation(RefReceivedEvent refEvent)
                       throws ValidationException {
                     try (Repository repo = repoManager.openRepository(project)) {
                       RefUpdate u = repo.updateRef(testBranch.branch());
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java b/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
index 21a4c98..191444f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
@@ -37,7 +37,6 @@
 import com.google.gerrit.server.restapi.project.ProjectsCollection;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
-import java.util.List;
 import org.junit.Test;
 
 @NoHttpd
@@ -290,7 +289,7 @@
     listBranches.setNextPageToken(ListBranches.encodeToken("refs/heads/someBranch1"));
     Response<ImmutableList<BranchInfo>> response =
         listBranches.apply(projects.parse(project.get()));
-    List<String> continuationToken =
+    ImmutableList<String> continuationToken =
         response.headers().get(ListBranches.NEXT_PAGE_TOKEN_HEADER).asList();
     // Since branch1 does not exist, the server continues from branch2.
     assertRefs(ImmutableList.of(branch2), response.value());
@@ -307,7 +306,7 @@
     listBranches.setNextPageToken(ListBranches.encodeToken("refs/heads/someBranch4"));
     Response<ImmutableList<BranchInfo>> response =
         listBranches.apply(projects.parse(project.get()));
-    List<String> continuationToken =
+    ImmutableList<String> continuationToken =
         response.headers().get(ListBranches.NEXT_PAGE_TOKEN_HEADER).asList();
     // Since branch1 does not exist, the server continues from branch2.
     assertRefs(ImmutableList.of(), response.value());
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ProjectAssert.java b/javatests/com/google/gerrit/acceptance/rest/project/ProjectAssert.java
index 3f583a2..f267958 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ProjectAssert.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ProjectAssert.java
@@ -26,12 +26,11 @@
 import com.google.gerrit.extensions.common.ProjectInfo;
 import com.google.gerrit.extensions.restapi.Url;
 import com.google.gerrit.server.project.ProjectState;
-import java.util.List;
 import java.util.Set;
 
 public class ProjectAssert {
   public static IterableSubject assertThatNameList(Iterable<ProjectInfo> actualIt) {
-    List<ProjectInfo> actual = ImmutableList.copyOf(actualIt);
+    ImmutableList<ProjectInfo> actual = ImmutableList.copyOf(actualIt);
     for (ProjectInfo info : actual) {
       assertWithMessage("missing project name").that(info.name).isNotNull();
       assertWithMessage("project name does not match id")
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java b/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java
index 795e22c..cd687f3 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java
@@ -51,7 +51,7 @@
 
 @NoHttpd
 public class TagsIT extends AbstractDaemonTest {
-  private static final List<String> testTags =
+  private static final ImmutableList<String> testTags =
       ImmutableList.of("tag-A", "tag-B", "tag-C", "tag-D", "tag-E", "tag-F", "tag-G", "tag-H");
 
   private static final String SIGNED_ANNOTATION =
diff --git a/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java b/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java
index 83a0153..807fd5b 100644
--- a/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java
@@ -48,7 +48,6 @@
 import com.google.gerrit.testing.TestCommentHelper;
 import com.google.inject.Inject;
 import com.google.inject.Module;
-import java.util.List;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -131,7 +130,7 @@
     testCommentHelper.addDraft(changeId, revId, comment);
     amendChange(changeId, "refs/for/master%publish-comments", admin, testRepo);
 
-    List<FakeEmailSender.Message> messages = sender.getMessages();
+    ImmutableList<FakeEmailSender.Message> messages = sender.getMessages();
     assertThat(messages).hasSize(2);
 
     FakeEmailSender.Message newPatchsetMessage = messages.get(0);
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
index b606271..2ee5360 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
@@ -294,11 +294,7 @@
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added", null);
     addReviewer(adder, sc.changeId, sc.owner, reviewer.email());
     // TODO(logan): Should CCs be included?
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(reviewer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
-        .noOneElse();
+    assertThat(sender).sent("newchange", sc).to(reviewer).noOneElse();
     assertThat(sender).didNotSend();
   }
 
@@ -336,12 +332,7 @@
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added", null);
     addReviewer(adder, sc.changeId, sc.owner, reviewer.email(), CC_ON_OWN_COMMENTS, null);
     // TODO(logan): Should CCs be included?
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(reviewer)
-        .cc(sc.owner)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
-        .noOneElse();
+    assertThat(sender).sent("newchange", sc).to(reviewer).cc(sc.owner).noOneElse();
     assertThat(sender).didNotSend();
   }
 
@@ -361,11 +352,7 @@
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added", null);
     addReviewer(adder, sc.changeId, other, reviewer.email());
     // TODO(logan): Should CCs be included?
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(reviewer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
-        .noOneElse();
+    assertThat(sender).sent("newchange", sc).to(reviewer).noOneElse();
     assertThat(sender).didNotSend();
   }
 
@@ -385,12 +372,7 @@
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added", null);
     addReviewer(adder, sc.changeId, other, reviewer.email(), CC_ON_OWN_COMMENTS, null);
     // TODO(logan): Should CCs be included?
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(reviewer)
-        .cc(other)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
-        .noOneElse();
+    assertThat(sender).sent("newchange", sc).to(reviewer).cc(other).noOneElse();
     assertThat(sender).didNotSend();
   }
 
@@ -409,11 +391,7 @@
     StagedChange sc = stageReviewableChange();
     addReviewer(adder, sc.changeId, sc.owner, email);
     // TODO(logan): Should CCs be included?
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(email)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
-        .noOneElse();
+    assertThat(sender).sent("newchange", sc).to(email).noOneElse();
     assertThat(sender).didNotSend();
   }
 
@@ -464,11 +442,7 @@
     // For a review-started WIP change, same as in the notify=ALL case. It's not especially
     // important to notify just because a reviewer is added, but we do want to notify in the other
     // case that hits this codepath: posting an actual review.
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(reviewer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
-        .noOneElse();
+    assertThat(sender).sent("newchange", sc).to(reviewer).noOneElse();
   }
 
   private void addReviewerToWipChangeNotifyAll(Adder adder) throws Exception {
@@ -476,11 +450,7 @@
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added", null);
     addReviewer(adder, sc.changeId, sc.owner, reviewer.email(), NotifyHandling.ALL);
     // TODO(logan): Should CCs be included?
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(reviewer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
-        .noOneElse();
+    assertThat(sender).sent("newchange", sc).to(reviewer).noOneElse();
     assertThat(sender).didNotSend();
   }
 
@@ -499,11 +469,7 @@
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added", null);
     addReviewer(adder, sc.changeId, sc.owner, reviewer.email(), OWNER_REVIEWERS);
     // TODO(logan): Should CCs be included?
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(reviewer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
-        .noOneElse();
+    assertThat(sender).sent("newchange", sc).to(reviewer).noOneElse();
     assertThat(sender).didNotSend();
   }
 
@@ -556,11 +522,7 @@
   private void addNonUserReviewerByEmail(Adder adder) throws Exception {
     StagedChange sc = stageReviewableChange();
     addReviewer(adder, sc.changeId, sc.owner, "nonexistent@example.com");
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to("nonexistent@example.com")
-        .cc(StagedUsers.CC_BY_EMAIL, StagedUsers.REVIEWER_BY_EMAIL)
-        .noOneElse();
+    assertThat(sender).sent("newchange", sc).to("nonexistent@example.com").noOneElse();
     assertThat(sender).didNotSend();
   }
 
@@ -577,11 +539,7 @@
   private void addNonUserCcByEmail(Adder adder) throws Exception {
     StagedChange sc = stageReviewableChange();
     addReviewer(adder, sc.changeId, sc.owner, "nonexistent@example.com");
-    assertThat(sender)
-        .sent("newchange", sc)
-        .cc("nonexistent@example.com")
-        .cc(StagedUsers.CC_BY_EMAIL, StagedUsers.REVIEWER_BY_EMAIL)
-        .noOneElse();
+    assertThat(sender).sent("newchange", sc).cc("nonexistent@example.com").noOneElse();
     assertThat(sender).didNotSend();
   }
 
@@ -1023,11 +981,7 @@
         .bcc(ALL_COMMENTS)
         .noOneElse();
     // TODO(logan): Should CCs be included?
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(other)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
-        .noOneElse();
+    assertThat(sender).sent("newchange", sc).to(other).noOneElse();
     assertThat(sender).didNotSend();
   }
 
@@ -1932,7 +1886,6 @@
         .sent("newpatchset", sc)
         .to(sc.reviewer)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .bcc(sc.starrer)
         .bcc(NEW_PATCHSETS)
         .noOneElse();
@@ -1948,7 +1901,6 @@
         .notTo(sc.owner) // TODO(logan): This shouldn't be sent *from* the owner.
         .to(sc.reviewer, other)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .bcc(sc.starrer)
         .bcc(NEW_PATCHSETS)
         .noOneElse();
@@ -1964,7 +1916,6 @@
         .notTo(sc.owner) // TODO(logan): This shouldn't be sent *from* the owner.
         .to(sc.reviewer, other)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .bcc(sc.starrer)
         .bcc(NEW_PATCHSETS)
         .noOneElse();
@@ -1981,7 +1932,6 @@
         .to(sc.reviewer)
         .cc(other)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .noOneElse();
     assertThat(sender).didNotSend();
   }
@@ -1997,7 +1947,6 @@
         .to(sc.reviewer)
         .cc(other)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .noOneElse();
     assertThat(sender).didNotSend();
   }
@@ -2056,7 +2005,6 @@
         .sent("newpatchset", sc)
         .to(sc.reviewer)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .bcc(sc.starrer)
         .bcc(NEW_PATCHSETS)
         .noOneElse();
@@ -2071,7 +2019,6 @@
         .sent("newpatchset", sc)
         .to(sc.reviewer)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .bcc(sc.starrer)
         .bcc(NEW_PATCHSETS)
         .noOneElse();
@@ -2094,7 +2041,6 @@
         .sent("newpatchset", sc)
         .to(sc.reviewer, newReviewer)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .bcc(sc.starrer)
         .bcc(NEW_PATCHSETS)
         .noOneElse();
@@ -2118,7 +2064,6 @@
         .sent("newpatchset", sc)
         .to(sc.reviewer, newReviewer)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .bcc(sc.starrer)
         .bcc(NEW_PATCHSETS)
         .noOneElse();
@@ -2133,7 +2078,6 @@
         .sent("newpatchset", sc)
         .to(sc.reviewer)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .bcc(sc.starrer)
         .bcc(NEW_PATCHSETS)
         .noOneElse();
@@ -2170,7 +2114,6 @@
         .sent("newpatchset", sc)
         .to(sc.reviewer)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .bcc(sc.starrer)
         .bcc(NEW_PATCHSETS)
         .noOneElse();
@@ -2185,7 +2128,6 @@
         .sent("newpatchset", sc)
         .to(sc.owner, sc.reviewer)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .bcc(sc.starrer)
         .bcc(NEW_PATCHSETS)
         .noOneElse();
@@ -2200,7 +2142,6 @@
         .sent("newpatchset", sc)
         .to(sc.owner, sc.reviewer, other)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .bcc(sc.starrer)
         .bcc(NEW_PATCHSETS)
         .noOneElse();
@@ -2211,12 +2152,7 @@
   public void editCommitMessageByOtherOnReviewableChangeNotifyOwnerReviewers() throws Exception {
     StagedChange sc = stageReviewableChange();
     editCommitMessage(sc, other, OWNER_REVIEWERS);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .to(sc.owner, sc.reviewer)
-        .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
-        .noOneElse();
+    assertThat(sender).sent("newpatchset", sc).to(sc.owner, sc.reviewer).cc(sc.ccer).noOneElse();
     assertThat(sender).didNotSend();
   }
 
@@ -2229,7 +2165,6 @@
         .sent("newpatchset", sc)
         .to(sc.owner, sc.reviewer)
         .cc(sc.ccer, other)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .noOneElse();
     assertThat(sender).didNotSend();
   }
@@ -2295,7 +2230,6 @@
         .sent("newpatchset", sc)
         .to(sc.reviewer)
         .cc(sc.ccer)
-        .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
         .bcc(sc.starrer)
         .bcc(NEW_PATCHSETS)
         .noOneElse();
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
index 1ad27eb..d7f19aa 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
@@ -16,6 +16,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.PushOneCommit;
@@ -35,7 +36,6 @@
 import java.time.ZonedDateTime;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import org.junit.Test;
 
@@ -50,7 +50,7 @@
     PushOneCommit.Result newChange = createChange();
     gApi.changes().id(newChange.getChangeId()).addReviewer(user.id().toString());
 
-    List<FakeEmailSender.Message> emails = sender.getMessages();
+    ImmutableList<FakeEmailSender.Message> emails = sender.getMessages();
     assertThat(emails).hasSize(1);
     FakeEmailSender.Message message = emails.get(0);
 
@@ -87,7 +87,7 @@
         gApi.changes().id(newChange.getChangeId()).get().messages;
     assertThat(result).isNotEmpty();
 
-    List<FakeEmailSender.Message> emails = sender.getMessages();
+    ImmutableList<FakeEmailSender.Message> emails = sender.getMessages();
     assertThat(emails).hasSize(1);
     FakeEmailSender.Message message = emails.get(0);
 
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
index 25bedd1..6197132 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
@@ -17,6 +17,7 @@
 import static com.google.common.truth.Truth.assertThat;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.gerrit.acceptance.Sandboxed;
 import com.google.gerrit.acceptance.UseLocalDisk;
 import com.google.gerrit.acceptance.config.GerritConfig;
@@ -41,7 +42,7 @@
     createChangeWithReview(user);
     // Check that the custom address was added as Reply-To
     assertThat(sender.getMessages()).hasSize(1);
-    Map<String, EmailHeader> headers = sender.getMessages().iterator().next().headers();
+    ImmutableMap<String, EmailHeader> headers = sender.getMessages().iterator().next().headers();
     assertThat(headerString(headers, "Reply-To")).isEqualTo("custom@example.com");
   }
 
@@ -50,7 +51,7 @@
     createChangeWithReview(user);
     // Check that the user's email was added as Reply-To
     assertThat(sender.getMessages()).hasSize(1);
-    Map<String, EmailHeader> headers = sender.getMessages().iterator().next().headers();
+    ImmutableMap<String, EmailHeader> headers = sender.getMessages().iterator().next().headers();
     assertThat(headerString(headers, "Reply-To")).contains(user.email());
   }
 
@@ -59,7 +60,7 @@
     String changeId = createChangeWithReview(user);
     // Check that the mail has the expected headers
     assertThat(sender.getMessages()).hasSize(1);
-    Map<String, EmailHeader> headers = sender.getMessages().iterator().next().headers();
+    ImmutableMap<String, EmailHeader> headers = sender.getMessages().iterator().next().headers();
     String hostname = URI.create(canonicalWebUrl.get()).getHost();
     String listId = String.format("<gerrit-%s.%s>", project.get(), hostname);
     String unsubscribeLink = String.format("<%ssettings?usp=email>", canonicalWebUrl.get());
diff --git a/javatests/com/google/gerrit/acceptance/server/permissions/ExternalUserPermissionIT.java b/javatests/com/google/gerrit/acceptance/server/permissions/ExternalUserPermissionIT.java
index 7a55ecb..9488c5f 100644
--- a/javatests/com/google/gerrit/acceptance/server/permissions/ExternalUserPermissionIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/permissions/ExternalUserPermissionIT.java
@@ -53,7 +53,6 @@
 import com.google.inject.AbstractModule;
 import com.google.inject.Module;
 import java.util.Collection;
-import java.util.Set;
 import java.util.stream.StreamSupport;
 import javax.inject.Inject;
 import org.eclipse.jgit.lib.Ref;
@@ -152,7 +151,7 @@
                       }
 
                       @Override
-                      public Set<AccountGroup.UUID> intersection(
+                      public ImmutableSet<AccountGroup.UUID> intersection(
                           Iterable<AccountGroup.UUID> groupIds) {
                         return StreamSupport.stream(groupIds.spliterator(), /* parallel= */ false)
                             .filter(g -> contains(g))
@@ -160,7 +159,7 @@
                       }
 
                       @Override
-                      public Set<AccountGroup.UUID> getKnownGroups() {
+                      public ImmutableSet<AccountGroup.UUID> getKnownGroups() {
                         return ImmutableSet.of();
                       }
                     };
diff --git a/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java b/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
index 432a6c6..014933f 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
@@ -36,7 +36,6 @@
 import com.google.gerrit.testing.FakeEmailSender.Message;
 import com.google.inject.Inject;
 import java.util.EnumSet;
-import java.util.List;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
 import org.eclipse.jgit.junit.TestRepository;
 import org.junit.Test;
@@ -90,7 +89,7 @@
             .to("refs/for/master");
     r.assertOkStatus();
 
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactlyElementsIn(watchers.build());
@@ -248,7 +247,7 @@
     r.assertOkStatus();
 
     // assert email notification
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
@@ -280,7 +279,7 @@
     r.assertOkStatus();
 
     // assert email notification for user
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
@@ -329,7 +328,7 @@
     r.assertOkStatus();
 
     // assert email notification for user
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
@@ -393,7 +392,7 @@
     gApi.changes().id(r.getChangeId()).addReviewer(user2.email());
 
     // assert email notification
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertNotifyTo(user2);
@@ -419,7 +418,7 @@
     r.assertOkStatus();
 
     // assert email notification for user
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
@@ -455,7 +454,7 @@
     // change user can see the non-visible account.
     // Even if watching by the non-visible account was not possible, user could just watch all
     // changes that are visible to them and then filter them by the non-visible account locally.
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
@@ -494,7 +493,7 @@
     // is sent to the admin user
     requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).current().review(reviewInput);
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(admin.getNameEmail());
@@ -531,7 +530,7 @@
     r.assertOkStatus();
 
     // assert email notification
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
@@ -559,7 +558,7 @@
     r.assertOkStatus();
 
     // assert email notification for user
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
@@ -608,7 +607,7 @@
     r.assertOkStatus();
 
     // assert email notification for user
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(user.getNameEmail());
@@ -688,7 +687,7 @@
     r.assertOkStatus();
 
     // assert email notification
-    List<Message> messages = sender.getMessages();
+    ImmutableList<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(userThatCanViewPrivateChanges.getNameEmail());
diff --git a/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java b/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
index 15cd1a9..0c24b14 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
@@ -20,6 +20,7 @@
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
 import static com.google.gerrit.server.project.testing.TestLabels.value;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.MoreCollectors;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.ExtensionRegistry;
@@ -53,7 +54,6 @@
 import com.google.gerrit.server.query.change.SubmitRequirementPredicate;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
-import java.util.Map;
 import java.util.Optional;
 import org.junit.Before;
 import org.junit.Test;
@@ -167,7 +167,7 @@
               /* overrideExpr= */ "", /*allowOverrideInChildProjects*/
               false);
       configSubmitRequirement(project, projectSubmitRequirement);
-      Map<SubmitRequirement, SubmitRequirementResult> results =
+      ImmutableMap<SubmitRequirement, SubmitRequirementResult> results =
           evaluator.evaluateAllRequirements(changeData);
       assertThat(results).hasSize(2);
       assertThat(results.get(globalSubmitRequirement).status())
@@ -198,7 +198,7 @@
               /* overrideExpr= */ "", /*allowOverrideInChildProjects*/
               false);
       configSubmitRequirement(project, projectSubmitRequirement);
-      Map<SubmitRequirement, SubmitRequirementResult> results =
+      ImmutableMap<SubmitRequirement, SubmitRequirementResult> results =
           evaluator.evaluateAllRequirements(changeData);
       assertThat(results).hasSize(1);
       assertThat(results.get(projectSubmitRequirement).status())
@@ -227,7 +227,7 @@
               /* overrideExpr= */ "", /*allowOverrideInChildProjects*/
               false);
       configSubmitRequirement(project, projectSubmitRequirement);
-      Map<SubmitRequirement, SubmitRequirementResult> results =
+      ImmutableMap<SubmitRequirement, SubmitRequirementResult> results =
           evaluator.evaluateAllRequirements(changeData);
       assertThat(results).hasSize(1);
       assertThat(results.get(globalSubmitRequirement).status())
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/prolog/PrologRuleEvaluatorIT.java b/javatests/com/google/gerrit/acceptance/server/rules/prolog/PrologRuleEvaluatorIT.java
index 850fe8e..1ab70e9 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/prolog/PrologRuleEvaluatorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/prolog/PrologRuleEvaluatorIT.java
@@ -45,7 +45,7 @@
     StructureTerm verifiedLabel = makeLabel(LabelId.VERIFIED, "may");
     StructureTerm labels = new StructureTerm("label", verifiedLabel);
 
-    List<Term> terms = ImmutableList.of(makeTerm("ok", labels));
+    ImmutableList<Term> terms = ImmutableList.of(makeTerm("ok", labels));
     SubmitRecord record = evaluator.resultsToSubmitRecord(null, terms);
 
     assertThat(record.status).isEqualTo(SubmitRecord.Status.OK);
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/prolog/RulesIT.java b/javatests/com/google/gerrit/acceptance/server/rules/prolog/RulesIT.java
index 74bdb56..1f47958 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/prolog/RulesIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/prolog/RulesIT.java
@@ -31,8 +31,7 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.inject.Inject;
 import java.util.Arrays;
-import java.util.Collection;
-import java.util.Map;
+import java.util.List;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.Repository;
 import org.junit.Test;
@@ -180,7 +179,7 @@
   }
 
   private SubmitRecord.Status statusForRuleAddFile(String... filenames) throws Exception {
-    Map<String, String> fileToContentMap =
+    ImmutableMap<String, String> fileToContentMap =
         Arrays.stream(filenames).collect(ImmutableMap.toImmutableMap(f -> f, f -> "file content"));
     String oldHead = projectOperations.project(project).getHead("master").name();
     PushOneCommit push =
@@ -243,7 +242,7 @@
   private SubmitRecord.Status getStatus(PushOneCommit.Result result) throws Exception {
     ChangeData cd = result.getChange();
 
-    Collection<SubmitRecord> records;
+    List<SubmitRecord> records;
     try (AutoCloseable ignored1 = changeIndexOperations.disableReadsAndWrites();
         AutoCloseable ignored2 = accountIndexOperations.disableReadsAndWrites()) {
       SubmitRuleEvaluator ruleEvaluator = evaluatorFactory.create(SubmitRuleOptions.defaults());
diff --git a/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java b/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java
index b646d2b..54a8b26 100644
--- a/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java
+++ b/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java
@@ -374,7 +374,7 @@
     assertThat(p.getParameterNames()).hasSize(2);
     assertThat(p.getParameterNames()).containsExactly("patchSet", "branch");
 
-    Map<String, String> params =
+    ImmutableMap<String, String> params =
         ImmutableMap.of(
             "patchSet", "42",
             "branch", "foo");
@@ -388,7 +388,7 @@
   @Test
   public void replaceSubmitTooltipWithoutVariables() {
     ParameterizedString p = new ParameterizedString("Submit patch set 40 into master");
-    Map<String, String> params =
+    ImmutableMap<String, String> params =
         ImmutableMap.of(
             "patchSet", "42",
             "branch", "foo");
diff --git a/javatests/com/google/gerrit/entities/LabelFunctionTest.java b/javatests/com/google/gerrit/entities/LabelFunctionTest.java
index 80d97db..ffbdaf1 100644
--- a/javatests/com/google/gerrit/entities/LabelFunctionTest.java
+++ b/javatests/com/google/gerrit/entities/LabelFunctionTest.java
@@ -70,7 +70,8 @@
 
   @Test
   public void checkMaxNoBlockIgnoresMin() {
-    List<PatchSetApproval> approvals = ImmutableList.of(APPROVAL_M2, APPROVAL_2, APPROVAL_M2);
+    ImmutableList<PatchSetApproval> approvals =
+        ImmutableList.of(APPROVAL_M2, APPROVAL_2, APPROVAL_M2);
 
     SubmitRecord.Label myLabel = LabelFunction.MAX_NO_BLOCK.check(VERIFIED_LABEL, approvals);
 
@@ -98,7 +99,8 @@
   }
 
   private static void checkBlockWorks(LabelFunction function) {
-    List<PatchSetApproval> approvals = ImmutableList.of(APPROVAL_1, APPROVAL_M2, APPROVAL_2);
+    ImmutableList<PatchSetApproval> approvals =
+        ImmutableList.of(APPROVAL_1, APPROVAL_M2, APPROVAL_2);
 
     SubmitRecord.Label myLabel = function.check(VERIFIED_LABEL, approvals);
 
@@ -121,7 +123,7 @@
   }
 
   private static void checkMaxIsEnforced(LabelFunction function) {
-    List<PatchSetApproval> approvals = ImmutableList.of(APPROVAL_1, APPROVAL_0);
+    ImmutableList<PatchSetApproval> approvals = ImmutableList.of(APPROVAL_1, APPROVAL_0);
 
     SubmitRecord.Label myLabel = function.check(VERIFIED_LABEL, approvals);
 
@@ -129,7 +131,8 @@
   }
 
   private static void checkMaxValidatesTheLabel(LabelFunction function) {
-    List<PatchSetApproval> approvals = ImmutableList.of(APPROVAL_1, APPROVAL_2, APPROVAL_M1);
+    ImmutableList<PatchSetApproval> approvals =
+        ImmutableList.of(APPROVAL_1, APPROVAL_2, APPROVAL_M1);
 
     SubmitRecord.Label myLabel = function.check(VERIFIED_LABEL, approvals);
 
diff --git a/javatests/com/google/gerrit/entities/SubmitRecordTest.java b/javatests/com/google/gerrit/entities/SubmitRecordTest.java
index 578bc18..7f0f42c 100644
--- a/javatests/com/google/gerrit/entities/SubmitRecordTest.java
+++ b/javatests/com/google/gerrit/entities/SubmitRecordTest.java
@@ -18,7 +18,7 @@
 
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
-import java.util.Collection;
+import java.util.List;
 import org.junit.Test;
 
 public class SubmitRecordTest {
@@ -39,7 +39,7 @@
 
   @Test
   public void okIfAllOkay() {
-    Collection<SubmitRecord> submitRecords = new ArrayList<>();
+    List<SubmitRecord> submitRecords = new ArrayList<>();
     submitRecords.add(OK_RECORD);
 
     assertThat(SubmitRecord.allRecordsOK(submitRecords)).isTrue();
@@ -47,14 +47,14 @@
 
   @Test
   public void okWhenEmpty() {
-    Collection<SubmitRecord> submitRecords = new ArrayList<>();
+    List<SubmitRecord> submitRecords = new ArrayList<>();
 
     assertThat(SubmitRecord.allRecordsOK(submitRecords)).isTrue();
   }
 
   @Test
   public void okWhenForced() {
-    Collection<SubmitRecord> submitRecords = new ArrayList<>();
+    List<SubmitRecord> submitRecords = new ArrayList<>();
     submitRecords.add(FORCED_RECORD);
 
     assertThat(SubmitRecord.allRecordsOK(submitRecords)).isTrue();
@@ -62,7 +62,7 @@
 
   @Test
   public void emptyResultIfInvalid() {
-    Collection<SubmitRecord> submitRecords = new ArrayList<>();
+    List<SubmitRecord> submitRecords = new ArrayList<>();
     submitRecords.add(NOT_READY_RECORD);
     submitRecords.add(OK_RECORD);
 
diff --git a/javatests/com/google/gerrit/index/IndexUpgradeTest.java b/javatests/com/google/gerrit/index/IndexUpgradeTest.java
index 724964b..27abdad 100644
--- a/javatests/com/google/gerrit/index/IndexUpgradeTest.java
+++ b/javatests/com/google/gerrit/index/IndexUpgradeTest.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
 import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
 import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
-import java.util.Collection;
 import java.util.Map.Entry;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -43,7 +42,7 @@
   @Parameter public SchemaDefinitions<?> schemaDefinitions;
 
   @Parameters(name = "schema: {0}")
-  public static Collection<SchemaDefinitions<?>> indexes() {
+  public static ImmutableList<SchemaDefinitions<?>> indexes() {
     return ImmutableList.of(
         AccountSchemaDefinitions.INSTANCE,
         ChangeSchemaDefinitions.INSTANCE,
diff --git a/javatests/com/google/gerrit/index/SchemaUtilTest.java b/javatests/com/google/gerrit/index/SchemaUtilTest.java
index 4f67f8e..a92c13e 100644
--- a/javatests/com/google/gerrit/index/SchemaUtilTest.java
+++ b/javatests/com/google/gerrit/index/SchemaUtilTest.java
@@ -21,7 +21,7 @@
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 
 import com.google.common.collect.ImmutableList;
-import java.util.Map;
+import com.google.common.collect.ImmutableMap;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -53,7 +53,8 @@
 
   @Test
   public void schemasFromClassBuildsMap() {
-    Map<Integer, Schema<String>> all = SchemaUtil.schemasFromClass(TestSchemas.class, String.class);
+    ImmutableMap<Integer, Schema<String>> all =
+        SchemaUtil.schemasFromClass(TestSchemas.class, String.class);
     assertThat(all.keySet()).containsExactly(1, 2, 4);
     assertThat(all.get(1)).isEqualTo(TestSchemas.V1);
     assertThat(all.get(2)).isEqualTo(TestSchemas.V2);
diff --git a/javatests/com/google/gerrit/index/query/AndPredicateTest.java b/javatests/com/google/gerrit/index/query/AndPredicateTest.java
index 860a9fd..fc53031 100644
--- a/javatests/com/google/gerrit/index/query/AndPredicateTest.java
+++ b/javatests/com/google/gerrit/index/query/AndPredicateTest.java
@@ -98,8 +98,8 @@
     final TestPredicate<String> a = f("author", "alice");
     final TestPredicate<String> b = f("author", "bob");
     final TestPredicate<String> c = f("author", "charlie");
-    final List<TestPredicate<String>> s2 = ImmutableList.of(a, b);
-    final List<TestPredicate<String>> s3 = ImmutableList.of(a, b, c);
+    final ImmutableList<TestPredicate<String>> s2 = ImmutableList.of(a, b);
+    final ImmutableList<TestPredicate<String>> s3 = ImmutableList.of(a, b, c);
     final Predicate<String> n2 = and(a, b);
 
     assertNotSame(n2, n2.copy(s2));
diff --git a/javatests/com/google/gerrit/index/query/OrPredicateTest.java b/javatests/com/google/gerrit/index/query/OrPredicateTest.java
index e5c9672..4b00a52 100644
--- a/javatests/com/google/gerrit/index/query/OrPredicateTest.java
+++ b/javatests/com/google/gerrit/index/query/OrPredicateTest.java
@@ -98,8 +98,8 @@
     final TestPredicate<String> a = f("author", "alice");
     final TestPredicate<String> b = f("author", "bob");
     final TestPredicate<String> c = f("author", "charlie");
-    final List<TestPredicate<String>> s2 = ImmutableList.of(a, b);
-    final List<TestPredicate<String>> s3 = ImmutableList.of(a, b, c);
+    final ImmutableList<TestPredicate<String>> s2 = ImmutableList.of(a, b);
+    final ImmutableList<TestPredicate<String>> s3 = ImmutableList.of(a, b, c);
     final Predicate<String> n2 = or(a, b);
 
     assertNotSame(n2, n2.copy(s2));
diff --git a/javatests/com/google/gerrit/metrics/FieldSanitizeProjectNameTest.java b/javatests/com/google/gerrit/metrics/FieldSanitizeProjectNameTest.java
index f12b1b3..196a1b4 100644
--- a/javatests/com/google/gerrit/metrics/FieldSanitizeProjectNameTest.java
+++ b/javatests/com/google/gerrit/metrics/FieldSanitizeProjectNameTest.java
@@ -17,7 +17,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import java.util.Arrays;
-import java.util.Collection;
+import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -25,7 +25,7 @@
 @RunWith(Parameterized.class)
 public class FieldSanitizeProjectNameTest {
   @Parameterized.Parameters
-  public static Collection<Object[]> testData() {
+  public static List<Object[]> testData() {
     return Arrays.asList(
         new Object[][] {
           {"repoName", "repoName"},
diff --git a/javatests/com/google/gerrit/server/account/WatchConfigTest.java b/javatests/com/google/gerrit/server/account/WatchConfigTest.java
index 708df7c..a277c3b 100644
--- a/javatests/com/google/gerrit/server/account/WatchConfigTest.java
+++ b/javatests/com/google/gerrit/server/account/WatchConfigTest.java
@@ -17,6 +17,7 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.NotifyConfig.NotifyType;
@@ -54,7 +55,7 @@
             + "[project \"otherProject\"]\n"
             + "  notify = [NEW_PATCHSETS]\n"
             + "  notify = * [NEW_PATCHSETS, ALL_COMMENTS]\n");
-    Map<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches =
+    ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches =
         ProjectWatches.parse(Account.id(1000000), cfg, this);
 
     assertThat(validationErrors).isEmpty();
diff --git a/javatests/com/google/gerrit/server/change/WalkSorterTest.java b/javatests/com/google/gerrit/server/change/WalkSorterTest.java
index 0d000a5..ca2163a 100644
--- a/javatests/com/google/gerrit/server/change/WalkSorterTest.java
+++ b/javatests/com/google/gerrit/server/change/WalkSorterTest.java
@@ -59,7 +59,7 @@
     ChangeData cd2 = newChange(p, c2_1);
     ChangeData cd3 = newChange(p, c3_1);
 
-    List<ChangeData> changes = ImmutableList.of(cd1, cd2, cd3);
+    ImmutableList<ChangeData> changes = ImmutableList.of(cd1, cd2, cd3);
     WalkSorter sorter = new WalkSorter(repoManager);
 
     assertSorted(
@@ -107,7 +107,7 @@
     ChangeData cd3 = newChange(p, c3);
     ChangeData cd4 = newChange(p, c4);
 
-    List<ChangeData> changes = ImmutableList.of(cd1, cd2, cd3, cd4);
+    ImmutableList<ChangeData> changes = ImmutableList.of(cd1, cd2, cd3, cd4);
     WalkSorter sorter = new WalkSorter(repoManager);
 
     assertSorted(
@@ -174,7 +174,7 @@
     ChangeData cd7 = newChange(p, c7);
     ChangeData cd8 = newChange(p, c8);
 
-    List<ChangeData> changes = ImmutableList.of(cd1, cd2, cd3, cd4, cd5, cd6, cd7, cd8);
+    ImmutableList<ChangeData> changes = ImmutableList.of(cd1, cd2, cd3, cd4, cd5, cd6, cd7, cd8);
     WalkSorter sorter = new WalkSorter(repoManager);
 
     // Do not use assertSorted because it tests all permutations. We don't need it for this test
@@ -203,7 +203,7 @@
     ChangeData cd1 = newChange(p, c1_1);
     ChangeData cd3 = newChange(p, c3_1);
 
-    List<ChangeData> changes = ImmutableList.of(cd1, cd3);
+    ImmutableList<ChangeData> changes = ImmutableList.of(cd1, cd3);
     WalkSorter sorter = new WalkSorter(repoManager);
 
     assertSorted(
@@ -230,7 +230,7 @@
     ChangeData cd3 = newChange(p, c3);
     ChangeData cd4 = newChange(p, c4);
 
-    List<ChangeData> changes = ImmutableList.of(cd1, cd2, cd3, cd4);
+    ImmutableList<ChangeData> changes = ImmutableList.of(cd1, cd2, cd3, cd4);
     WalkSorter sorter = new WalkSorter(repoManager);
 
     assertSorted(
@@ -263,7 +263,7 @@
     ChangeData cd3 = newChange(p, c3);
     ChangeData cd4 = newChange(p, c4);
 
-    List<ChangeData> changes = ImmutableList.of(cd1, cd2, cd3, cd4);
+    ImmutableList<ChangeData> changes = ImmutableList.of(cd1, cd2, cd3, cd4);
     WalkSorter sorter = new WalkSorter(repoManager);
 
     assertSorted(
@@ -295,9 +295,9 @@
     ChangeData cd2 = newChange(p, c2);
     ChangeData cd4 = newChange(p, c4);
 
-    List<ChangeData> changes = ImmutableList.of(cd1, cd2, cd4);
+    ImmutableList<ChangeData> changes = ImmutableList.of(cd1, cd2, cd4);
     WalkSorter sorter = new WalkSorter(repoManager);
-    List<PatchSetData> expected =
+    ImmutableList<PatchSetData> expected =
         ImmutableList.of(patchSetData(cd4, c4), patchSetData(cd2, c2), patchSetData(cd1, c1));
 
     for (List<ChangeData> list : permutations(changes)) {
@@ -326,7 +326,7 @@
     ChangeData cd3 = newChange(p, c3);
     ChangeData cd4 = newChange(p, c4);
 
-    List<ChangeData> changes = ImmutableList.of(cd1, cd2, cd3, cd4);
+    ImmutableList<ChangeData> changes = ImmutableList.of(cd1, cd2, cd3, cd4);
     WalkSorter sorter = new WalkSorter(repoManager);
 
     assertSorted(
@@ -379,7 +379,7 @@
     addPatchSet(cd1, c1_2);
     addPatchSet(cd2, c2_2);
 
-    List<ChangeData> changes = ImmutableList.of(cd1, cd2);
+    ImmutableList<ChangeData> changes = ImmutableList.of(cd1, cd2);
     WalkSorter sorter = new WalkSorter(repoManager);
 
     assertSorted(
@@ -402,7 +402,7 @@
     ChangeData cd1 = newChange(pa, c1);
     ChangeData cd2 = newChange(pb, c2);
 
-    List<ChangeData> changes = ImmutableList.of(cd1, cd2);
+    ImmutableList<ChangeData> changes = ImmutableList.of(cd1, cd2);
     WalkSorter sorter =
         new WalkSorter(repoManager).includePatchSets(ImmutableSet.of(cd1.currentPatchSet().id()));
 
@@ -415,7 +415,7 @@
     RevCommit c = p.commit().message("message").create();
     ChangeData cd = newChange(p, c);
 
-    List<ChangeData> changes = ImmutableList.of(cd);
+    ImmutableList<ChangeData> changes = ImmutableList.of(cd);
     RevCommit actual =
         new WalkSorter(repoManager).setRetainBody(true).sort(changes).iterator().next().commit();
     assertThat(actual.getRawBuffer()).isNotNull();
@@ -432,7 +432,7 @@
     RevCommit c = p.commit().create();
     ChangeData cd = newChange(p, c);
 
-    List<ChangeData> changes = ImmutableList.of(cd);
+    ImmutableList<ChangeData> changes = ImmutableList.of(cd);
     WalkSorter sorter = new WalkSorter(repoManager);
 
     assertSorted(sorter, changes, ImmutableList.of(patchSetData(cd, c)));
diff --git a/javatests/com/google/gerrit/server/git/receive/ReceivePackRefCacheTest.java b/javatests/com/google/gerrit/server/git/receive/ReceivePackRefCacheTest.java
index 7eb6bc7..2d7f698 100644
--- a/javatests/com/google/gerrit/server/git/receive/ReceivePackRefCacheTest.java
+++ b/javatests/com/google/gerrit/server/git/receive/ReceivePackRefCacheTest.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.RefNames;
-import java.util.Map;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdRef;
 import org.eclipse.jgit.lib.Ref;
@@ -93,7 +92,7 @@
 
   @Test
   public void advertisedRefs_prefixScansChangeId() throws Exception {
-    Map<String, Ref> refs = setupTwoChanges();
+    ImmutableMap<String, Ref> refs = setupTwoChanges();
     ReceivePackRefCache cache = ReceivePackRefCache.withAdvertisedRefs(() -> refs);
 
     assertThat(cache.byPrefix(RefNames.changeRefPrefix(Change.id(1))))
@@ -102,7 +101,7 @@
 
   @Test
   public void advertisedRefs_exactRef() throws Exception {
-    Map<String, Ref> refs = setupTwoChanges();
+    ImmutableMap<String, Ref> refs = setupTwoChanges();
     ReceivePackRefCache cache = ReceivePackRefCache.withAdvertisedRefs(() -> refs);
 
     assertThat(cache.exactRef("refs/changes/01/1/1")).isEqualTo(refs.get("refs/changes/01/1/1"));
@@ -110,7 +109,7 @@
 
   @Test
   public void advertisedRefs_patchSetIdsFromObjectId() throws Exception {
-    Map<String, Ref> refs = setupTwoChanges();
+    ImmutableMap<String, Ref> refs = setupTwoChanges();
     ReceivePackRefCache cache = ReceivePackRefCache.withAdvertisedRefs(() -> refs);
 
     assertThat(
@@ -123,7 +122,7 @@
     return new ObjectIdRef.Unpeeled(Ref.Storage.NEW, name, ObjectId.fromString(sha1), 1);
   }
 
-  private Map<String, Ref> setupTwoChanges() {
+  private ImmutableMap<String, Ref> setupTwoChanges() {
     Ref ref1 = newRef("refs/changes/01/1/1", "badc0feebadc0feebadc0feebadc0feebadc0fee");
     Ref ref2 = newRef("refs/changes/02/2/1", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
     return ImmutableMap.of(ref1.getName(), ref1, ref2.getName(), ref2);
diff --git a/javatests/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyCheckerTest.java b/javatests/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyCheckerTest.java
index 6745b1d..1017ed0 100644
--- a/javatests/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyCheckerTest.java
+++ b/javatests/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyCheckerTest.java
@@ -22,14 +22,13 @@
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
 import com.google.gerrit.server.group.db.testing.GroupTestUtil;
-import java.util.List;
 import org.junit.Test;
 
 public class GroupsNoteDbConsistencyCheckerTest extends AbstractGroupTest {
 
   @Test
   public void groupNamesRefIsMissing() throws Exception {
-    List<ConsistencyProblemInfo> problems =
+    ImmutableList<ConsistencyProblemInfo> problems =
         GroupsNoteDbConsistencyChecker.checkWithGroupNameNotes(
             allUsersRepo, AccountGroup.nameKey("g-1"), AccountGroup.uuid("uuid-1"));
     assertThat(problems)
@@ -39,7 +38,7 @@
   @Test
   public void groupNameNoteIsMissing() throws Exception {
     updateGroupNamesRef("g-2", "[group]\n\tuuid = uuid-2\n\tname = g-2\n");
-    List<ConsistencyProblemInfo> problems =
+    ImmutableList<ConsistencyProblemInfo> problems =
         GroupsNoteDbConsistencyChecker.checkWithGroupNameNotes(
             allUsersRepo, AccountGroup.nameKey("g-1"), AccountGroup.uuid("uuid-1"));
     assertThat(problems)
@@ -49,7 +48,7 @@
   @Test
   public void groupNameNoteIsConsistent() throws Exception {
     updateGroupNamesRef("g-1", "[group]\n\tuuid = uuid-1\n\tname = g-1\n");
-    List<ConsistencyProblemInfo> problems =
+    ImmutableList<ConsistencyProblemInfo> problems =
         GroupsNoteDbConsistencyChecker.checkWithGroupNameNotes(
             allUsersRepo, AccountGroup.nameKey("g-1"), AccountGroup.uuid("uuid-1"));
     assertThat(problems).isEmpty();
@@ -58,7 +57,7 @@
   @Test
   public void groupNameNoteHasDifferentUUID() throws Exception {
     updateGroupNamesRef("g-1", "[group]\n\tuuid = uuid-2\n\tname = g-1\n");
-    List<ConsistencyProblemInfo> problems =
+    ImmutableList<ConsistencyProblemInfo> problems =
         GroupsNoteDbConsistencyChecker.checkWithGroupNameNotes(
             allUsersRepo, AccountGroup.nameKey("g-1"), AccountGroup.uuid("uuid-1"));
     assertThat(problems)
@@ -71,7 +70,7 @@
   @Test
   public void groupNameNoteHasDifferentName() throws Exception {
     updateGroupNamesRef("g-1", "[group]\n\tuuid = uuid-1\n\tname = g-2\n");
-    List<ConsistencyProblemInfo> problems =
+    ImmutableList<ConsistencyProblemInfo> problems =
         GroupsNoteDbConsistencyChecker.checkWithGroupNameNotes(
             allUsersRepo, AccountGroup.nameKey("g-1"), AccountGroup.uuid("uuid-1"));
     assertThat(problems)
@@ -81,7 +80,7 @@
   @Test
   public void groupNameNoteHasDifferentNameAndUUID() throws Exception {
     updateGroupNamesRef("g-1", "[group]\n\tuuid = uuid-2\n\tname = g-2\n");
-    List<ConsistencyProblemInfo> problems =
+    ImmutableList<ConsistencyProblemInfo> problems =
         GroupsNoteDbConsistencyChecker.checkWithGroupNameNotes(
             allUsersRepo, AccountGroup.nameKey("g-1"), AccountGroup.uuid("uuid-1"));
     assertThat(problems)
diff --git a/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java b/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java
index 59b354c..8cb4974 100644
--- a/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java
+++ b/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java
@@ -260,7 +260,7 @@
   }
 
   private static void assertStoredRecordRoundTrip(SubmitRecord... records) {
-    List<SubmitRecord> recordList = ImmutableList.copyOf(records);
+    ImmutableList<SubmitRecord> recordList = ImmutableList.copyOf(records);
     List<String> stored =
         ChangeField.storedSubmitRecords(recordList).stream()
             .map(s -> new String(s, UTF_8))
diff --git a/javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java b/javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java
index 6f40680..b7e733f 100644
--- a/javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java
+++ b/javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java
@@ -29,6 +29,7 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.index.change.StalenessChecker.RefStatePattern;
 import com.google.gerrit.testing.InMemoryRepositoryManager;
+import java.util.List;
 import java.util.stream.Stream;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.ObjectId;
@@ -311,7 +312,7 @@
         .isFalse();
   }
 
-  private static Iterable<byte[]> byteArrays(String... strs) {
+  private static List<byte[]> byteArrays(String... strs) {
     return Stream.of(strs).map(s -> s != null ? s.getBytes(UTF_8) : null).collect(toList());
   }
 }
diff --git a/javatests/com/google/gerrit/server/ioutil/RegexListSearcherTest.java b/javatests/com/google/gerrit/server/ioutil/RegexListSearcherTest.java
index ddb616d..6f7832d 100644
--- a/javatests/com/google/gerrit/server/ioutil/RegexListSearcherTest.java
+++ b/javatests/com/google/gerrit/server/ioutil/RegexListSearcherTest.java
@@ -30,7 +30,7 @@
 
   @Test
   public void anchors() {
-    List<String> list = ImmutableList.of("foo");
+    ImmutableList<String> list = ImmutableList.of("foo");
     assertSearchReturns(list, "^f.*", list);
     assertSearchReturns(list, "^f.*o$", list);
     assertSearchReturns(list, "f.*o$", list);
@@ -40,7 +40,7 @@
 
   @Test
   public void noCommonPrefix() {
-    List<String> list = ImmutableList.of("bar", "foo", "quux");
+    ImmutableList<String> list = ImmutableList.of("bar", "foo", "quux");
     assertSearchReturns(ImmutableList.of("foo"), "f.*", list);
     assertSearchReturns(ImmutableList.of("foo"), ".*o.*", list);
     assertSearchReturns(ImmutableList.of("bar", "foo", "quux"), ".*[aou].*", list);
@@ -48,7 +48,7 @@
 
   @Test
   public void commonPrefix() {
-    List<String> list = ImmutableList.of("bar", "baz", "foo1", "foo2", "foo3", "quux");
+    ImmutableList<String> list = ImmutableList.of("bar", "baz", "foo1", "foo2", "foo3", "quux");
     assertSearchReturns(ImmutableList.of("bar", "baz"), "b.*", list);
     assertSearchReturns(ImmutableList.of("foo1", "foo2"), "foo[12]", list);
     assertSearchReturns(ImmutableList.of("foo1", "foo2", "foo3"), "foo.*", list);
diff --git a/javatests/com/google/gerrit/server/mail/send/HumanCommentFormatterTest.java b/javatests/com/google/gerrit/server/mail/send/HumanCommentFormatterTest.java
index 46ea8b2..68cb231 100644
--- a/javatests/com/google/gerrit/server/mail/send/HumanCommentFormatterTest.java
+++ b/javatests/com/google/gerrit/server/mail/send/HumanCommentFormatterTest.java
@@ -20,6 +20,7 @@
 import static com.google.gerrit.server.mail.send.CommentFormatter.BlockType.PRE_FORMATTED;
 import static com.google.gerrit.server.mail.send.CommentFormatter.BlockType.QUOTE;
 
+import com.google.common.collect.ImmutableList;
 import java.util.List;
 import org.junit.Test;
 
@@ -63,7 +64,7 @@
   @Test
   public void parseSimple() {
     String comment = "Para1";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(1);
     assertBlock(result, 0, PARAGRAPH, comment);
@@ -72,7 +73,7 @@
   @Test
   public void parseMultilinePara() {
     String comment = "Para 1\nStill para 1";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(1);
     assertBlock(result, 0, PARAGRAPH, comment);
@@ -81,7 +82,7 @@
   @Test
   public void parseParaBreak() {
     String comment = "Para 1\n\nPara 2\n\nPara 3";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(3);
     assertBlock(result, 0, PARAGRAPH, "Para 1");
@@ -92,7 +93,7 @@
   @Test
   public void parseQuote() {
     String comment = "> Quote text";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(1);
     assertQuoteBlock(result, 0, 1);
@@ -102,7 +103,7 @@
   @Test
   public void parseExcludesEmpty() {
     String comment = "Para 1\n\n\n\nPara 2";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(2);
     assertBlock(result, 0, PARAGRAPH, "Para 1");
@@ -112,7 +113,7 @@
   @Test
   public void parseQuoteLeadSpace() {
     String comment = " > Quote text";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(1);
     assertQuoteBlock(result, 0, 1);
@@ -122,7 +123,7 @@
   @Test
   public void parseMultiLineQuote() {
     String comment = "> Quote line 1\n> Quote line 2\n > Quote line 3\n";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(1);
     assertQuoteBlock(result, 0, 1);
@@ -133,7 +134,7 @@
   @Test
   public void parsePre() {
     String comment = "    Four space indent.";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(1);
     assertBlock(result, 0, PRE_FORMATTED, comment);
@@ -142,7 +143,7 @@
   @Test
   public void parseOneSpacePre() {
     String comment = " One space indent.\n Another line.";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(1);
     assertBlock(result, 0, PRE_FORMATTED, comment);
@@ -151,7 +152,7 @@
   @Test
   public void parseTabPre() {
     String comment = "\tOne tab indent.\n\tAnother line.\n  Yet another!";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(1);
     assertBlock(result, 0, PRE_FORMATTED, comment);
@@ -160,7 +161,7 @@
   @Test
   public void parseIntermediateLeadingWhitespacePre() {
     String comment = "No indent.\n\tNonzero indent.\nNo indent again.";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(1);
     assertBlock(result, 0, PRE_FORMATTED, comment);
@@ -169,7 +170,7 @@
   @Test
   public void parseStarList() {
     String comment = "* Item 1\n* Item 2\n* Item 3";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(1);
     assertListBlock(result, 0, 0, "Item 1");
@@ -180,7 +181,7 @@
   @Test
   public void parseDashList() {
     String comment = "- Item 1\n- Item 2\n- Item 3";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(1);
     assertListBlock(result, 0, 0, "Item 1");
@@ -191,7 +192,7 @@
   @Test
   public void parseMixedList() {
     String comment = "- Item 1\n* Item 2\n- Item 3\n* Item 4";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(1);
     assertListBlock(result, 0, 0, "Item 1");
@@ -216,7 +217,7 @@
             + "\tPreformatted text."
             + "\n\n"
             + "Parting words.";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(7);
     assertBlock(result, 0, PARAGRAPH, "Paragraph\nacross\na\nfew\nlines.");
@@ -235,7 +236,7 @@
   @Test
   public void bulletList1() {
     String comment = "A\n\n* line 1\n* 2nd line";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(2);
     assertBlock(result, 0, PARAGRAPH, "A");
@@ -246,7 +247,7 @@
   @Test
   public void bulletList2() {
     String comment = "A\n\n* line 1\n* 2nd line\n\nB";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(3);
     assertBlock(result, 0, PARAGRAPH, "A");
@@ -258,7 +259,7 @@
   @Test
   public void bulletList3() {
     String comment = "* line 1\n* 2nd line\n\nB";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(2);
     assertListBlock(result, 0, 0, "line 1");
@@ -272,7 +273,7 @@
         "To see this bug, you have to:\n" //
             + "* Be on IMAP or EAS (not on POP)\n" //
             + "* Be very unlucky\n";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(2);
     assertBlock(result, 0, PARAGRAPH, "To see this bug, you have to:");
@@ -287,7 +288,7 @@
             + "you have to:\n" //
             + "* Be on IMAP or EAS (not on POP)\n" //
             + "* Be very unlucky\n";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(2);
     assertBlock(result, 0, PARAGRAPH, "To see this bug, you have to:");
@@ -298,7 +299,7 @@
   @Test
   public void dashList1() {
     String comment = "A\n\n- line 1\n- 2nd line";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(2);
     assertBlock(result, 0, PARAGRAPH, "A");
@@ -309,7 +310,7 @@
   @Test
   public void dashList2() {
     String comment = "A\n\n- line 1\n- 2nd line\n\nB";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(3);
     assertBlock(result, 0, PARAGRAPH, "A");
@@ -321,7 +322,7 @@
   @Test
   public void dashList3() {
     String comment = "- line 1\n- 2nd line\n\nB";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(2);
     assertListBlock(result, 0, 0, "line 1");
@@ -332,7 +333,7 @@
   @Test
   public void preformat1() {
     String comment = "A\n\n  This is pre\n  formatted";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(2);
     assertBlock(result, 0, PARAGRAPH, "A");
@@ -342,7 +343,7 @@
   @Test
   public void preformat2() {
     String comment = "A\n\n  This is pre\n  formatted\n\nbut this is not";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(3);
     assertBlock(result, 0, PARAGRAPH, "A");
@@ -353,7 +354,7 @@
   @Test
   public void preformat3() {
     String comment = "A\n\n  Q\n    <R>\n  S\n\nB";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(3);
     assertBlock(result, 0, PARAGRAPH, "A");
@@ -364,7 +365,7 @@
   @Test
   public void preformat4() {
     String comment = "  Q\n    <R>\n  S\n\nB";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(2);
     assertBlock(result, 0, PRE_FORMATTED, "  Q\n    <R>\n  S");
@@ -374,7 +375,7 @@
   @Test
   public void quote1() {
     String comment = "> I'm happy\n > with quotes!\n\nSee above.";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(2);
     assertQuoteBlock(result, 0, 1);
@@ -385,7 +386,7 @@
   @Test
   public void quote2() {
     String comment = "See this said:\n\n > a quoted\n > string block\n\nOK?";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(3);
     assertBlock(result, 0, PARAGRAPH, "See this said:");
@@ -397,7 +398,7 @@
   @Test
   public void nestedQuotes1() {
     String comment = " > > prior\n > \n > next\n";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(1);
     assertQuoteBlock(result, 0, 2);
@@ -428,7 +429,7 @@
             + "> Paragraph 6.\n"
             + "\n"
             + "Paragraph 7.\n";
-    List<CommentFormatter.Block> result = CommentFormatter.parse(comment);
+    ImmutableList<CommentFormatter.Block> result = CommentFormatter.parse(comment);
 
     assertThat(result).hasSize(2);
     assertQuoteBlock(result, 0, 2);
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index 99761e5..3c1abbd 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -31,11 +31,11 @@
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.collect.ImmutableTable;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import com.google.gerrit.common.Nullable;
@@ -69,7 +69,6 @@
 import java.time.Instant;
 import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import java.util.TreeMap;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -291,7 +290,7 @@
 
     ChangeNotes notes = newNotes(c);
     assertThat(notes.getApprovals().all().keySet()).containsExactly(c.currentPatchSetId());
-    List<PatchSetApproval> psas = notes.getApprovals().all().get(c.currentPatchSetId());
+    ImmutableList<PatchSetApproval> psas = notes.getApprovals().all().get(c.currentPatchSetId());
     assertThat(psas).hasSize(2);
 
     assertThat(psas.get(0).patchSetId()).isEqualTo(c.currentPatchSetId());
@@ -324,7 +323,7 @@
     PatchSet.Id ps2 = c.currentPatchSetId();
 
     ChangeNotes notes = newNotes(c);
-    ListMultimap<PatchSet.Id, PatchSetApproval> psas = notes.getApprovals().all();
+    ImmutableListMultimap<PatchSet.Id, PatchSetApproval> psas = notes.getApprovals().all();
     assertThat(psas).hasSize(2);
 
     PatchSetApproval psa1 = Iterables.getOnlyElement(psas.get(ps1));
@@ -382,7 +381,7 @@
 
     ChangeNotes notes = newNotes(c);
     assertThat(notes.getApprovals().all().keySet()).containsExactly(c.currentPatchSetId());
-    List<PatchSetApproval> psas = notes.getApprovals().all().get(c.currentPatchSetId());
+    ImmutableList<PatchSetApproval> psas = notes.getApprovals().all().get(c.currentPatchSetId());
     assertThat(psas).hasSize(2);
 
     assertThat(psas.get(0).patchSetId()).isEqualTo(c.currentPatchSetId());
@@ -605,7 +604,7 @@
 
     ChangeNotes notes = newNotes(c);
     assertThat(notes.getApprovals().all().keySet()).containsExactly(c.currentPatchSetId());
-    List<PatchSetApproval> patchSetApprovals =
+    ImmutableList<PatchSetApproval> patchSetApprovals =
         notes.getApprovals().all().get(c.currentPatchSetId());
     assertThat(patchSetApprovals).hasSize(2);
     assertThat(
@@ -630,7 +629,7 @@
 
     ChangeNotes notes = newNotes(c);
     assertThat(notes.getApprovals().all().keySet()).containsExactly(c.currentPatchSetId());
-    List<PatchSetApproval> patchSetApprovals =
+    ImmutableList<PatchSetApproval> patchSetApprovals =
         notes.getApprovals().all().get(c.currentPatchSetId());
     assertThat(patchSetApprovals).hasSize(2);
     assertThat(
@@ -1219,7 +1218,7 @@
     update.commit();
 
     ChangeNotes notes = newNotes(c);
-    List<PatchSetApproval> psas = notes.getApprovals().all().get(c.currentPatchSetId());
+    ImmutableList<PatchSetApproval> psas = notes.getApprovals().all().get(c.currentPatchSetId());
     assertThat(psas).hasSize(2);
     assertThat(psas.get(0).accountId()).isEqualTo(changeOwner.getAccount().id());
     assertThat(psas.get(1).accountId()).isEqualTo(otherUser.getAccount().id());
@@ -1257,7 +1256,7 @@
     update.commit();
 
     ChangeNotes notes = newNotes(c);
-    List<SubmitRecord> recs = notes.getSubmitRecords();
+    ImmutableList<SubmitRecord> recs = notes.getSubmitRecords();
     assertThat(recs).hasSize(2);
     assertThat(recs.get(0))
         .isEqualTo(
@@ -2008,7 +2007,7 @@
     update.commit();
 
     ChangeNotes notes = newNotes(c);
-    Map<PatchSet.Id, PatchSet> patchSets = notes.getPatchSets();
+    ImmutableMap<PatchSet.Id, PatchSet> patchSets = notes.getPatchSets();
     assertThat(patchSets.get(psId1).pushCertificate()).isEmpty();
     assertThat(patchSets.get(psId2).pushCertificate()).hasValue(pushCert);
     assertThat(notes.getHumanComments()).isEmpty();
@@ -2065,7 +2064,7 @@
     }
 
     ChangeNotes notes = newNotes(c);
-    List<PatchSetApproval> psas = notes.getApprovals().all().get(c.currentPatchSetId());
+    ImmutableList<PatchSetApproval> psas = notes.getApprovals().all().get(c.currentPatchSetId());
     assertThat(psas).hasSize(2);
 
     assertThat(psas.get(0).accountId()).isEqualTo(changeOwner.getAccount().id());
@@ -2317,7 +2316,7 @@
 
     ChangeNotes notes = newNotes(c);
 
-    List<ChangeMessage> cm = notes.getChangeMessages();
+    ImmutableList<ChangeMessage> cm = notes.getChangeMessages();
     assertThat(cm).hasSize(2);
     assertThat(cm.get(0).getMessage()).isEqualTo("First change message.\n");
     assertThat(cm.get(0).getAuthor()).isEqualTo(changeOwner.getAccount().id());
@@ -3543,7 +3542,7 @@
     }
 
     ChangeNotes notes = newNotes(c);
-    List<HumanComment> comments = notes.getHumanComments().get(commitId);
+    ImmutableList<HumanComment> comments = notes.getHumanComments().get(commitId);
     assertThat(comments).hasSize(2);
     assertThat(comments.get(0).message).isEqualTo("comment 1");
     assertThat(comments.get(1).message).isEqualTo("comment 2");
diff --git a/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java b/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
index b6a0b36..1e45af2 100644
--- a/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
+++ b/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
@@ -54,7 +54,6 @@
 import java.time.Instant;
 import java.util.Arrays;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.IntStream;
@@ -180,7 +179,7 @@
 
     assertFixedCommits(ImmutableList.of(commitToFix), backfillResult, c.getId());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(backfillResult, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(backfillResult, c.getId());
     assertThat(commitHistoryDiff).containsExactly("");
     BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options);
     assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty();
@@ -385,7 +384,7 @@
     assertThat(invalidUpdateCommit.getCommitterIdent())
         .isEqualTo(fixedUpdateCommit.getCommitterIdent());
     assertThat(fixedUpdateCommit.getFullMessage()).doesNotContain(changeOwner.getName());
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff).hasSize(1);
     assertThat(commitHistoryDiff.get(0)).contains("-author Change Owner <1@gerrit>");
     assertThat(commitHistoryDiff.get(0)).contains("+author Gerrit User 1 <1@gerrit>");
@@ -448,7 +447,7 @@
         commitsBeforeRewrite, commitsAfterRewrite, ImmutableList.of(invalidCommitIndex));
     assertFixedCommits(ImmutableList.of(invalidUpdateCommit), result, c.getId());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -9 +9 @@\n"
@@ -526,7 +525,7 @@
     assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits);
     assertFixedCommits(commitsToFix, result, c.getId());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -9 +9 @@\n"
@@ -618,7 +617,7 @@
     assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits);
     assertFixedCommits(commitsToFix.build(), result, c.getId());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -6 +6 @@\n" + "-Removed reviewer Other Account.\n" + "+Removed reviewer\n",
@@ -656,7 +655,7 @@
     BackfillResult result = rewriter.backfillProject(project, repo, options);
     assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId()));
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -6 +6 @@\n" + "-Removed reviewer Other Account.\n" + "+Removed reviewer\n",
@@ -783,7 +782,7 @@
     assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits);
     assertFixedCommits(commitsToFix, result, c.getId());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -7,2 +7,2 @@\n"
@@ -908,7 +907,7 @@
     assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits);
     assertFixedCommits(commitsToFix, result, c.getId());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -6 +6 @@\n"
@@ -947,7 +946,7 @@
     BackfillResult result = rewriter.backfillProject(project, repo, options);
     assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId()));
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     // Other Account does not applier in any change updates, replaced with default
     assertThat(commitHistoryDiff)
         .containsExactly(
@@ -991,7 +990,7 @@
     BackfillResult result = rewriter.backfillProject(project, repo, options);
     assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId()));
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -6 +6 @@\n"
@@ -1025,7 +1024,7 @@
     BackfillResult result = rewriter.backfillProject(project, repo, options);
     assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId()));
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -6 +6 @@\n"
@@ -1054,7 +1053,7 @@
     BackfillResult result = rewriter.backfillProject(project, repo, options);
     assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId()));
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -6 +6 @@\n"
@@ -1101,7 +1100,7 @@
     BackfillResult result = rewriter.backfillProject(project, repo, options);
     assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId()));
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -6 +6 @@\n"
@@ -1161,7 +1160,7 @@
     BackfillResult result = rewriter.backfillProject(project, repo, options);
     assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId()));
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -7 +7 @@\n"
@@ -1359,7 +1358,7 @@
     assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits);
     assertFixedCommits(commitsToFix.build(), result, c.getId());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff).hasSize(4);
     assertThat(commitHistoryDiff.get(0))
         .isEqualTo(
@@ -1571,7 +1570,7 @@
     assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits);
     assertFixedCommits(commitsToFix.build(), result, c.getId());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -6 +6 @@\n"
@@ -1621,7 +1620,7 @@
     BackfillResult result = rewriter.backfillProject(project, repo, options);
     assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId()));
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -1 +1 @@\n"
@@ -1702,7 +1701,7 @@
         commitsBeforeRewrite, commitsAfterRewrite, ImmutableList.of(invalidCommitIndex));
     assertFixedCommits(ImmutableList.of(invalidUpdateCommit.getId()), result, c.getId());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -10 +10 @@\n"
@@ -1777,7 +1776,7 @@
     assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits);
     assertFixedCommits(commitsToFix.build(), result, c.getId());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -6 +6 @@\n"
@@ -1890,7 +1889,7 @@
     assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits);
     assertFixedCommits(commitsToFix.build(), result, c.getId());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -6 +6 @@\n"
@@ -2065,7 +2064,7 @@
     assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits);
     assertFixedCommits(commitsToFix.build(), result, c.getId());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -8 +8 @@\n"
@@ -2172,7 +2171,7 @@
     String expectedFixedIdent = getValidIdentAsString(changeOwner.getAccount());
     assertThat(fixedUpdateCommit.getFullMessage()).contains(expectedFixedIdent);
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -9 +9 @@\n"
@@ -2255,7 +2254,7 @@
     assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits);
     assertFixedCommits(commitsToFix, result, c.getId());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -6 +6 @@\n"
@@ -2319,7 +2318,7 @@
     options.dryRun = false;
     BackfillResult result = rewriter.backfillProject(project, repo, options);
     assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId()));
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff)
         .containsExactly(
             "@@ -6 +6 @@\n"
@@ -2384,7 +2383,7 @@
     assertThat(fixedUpdateCommit.getFullMessage()).doesNotContain(changeOwner.getName());
     assertThat(fixedUpdateCommit.getFullMessage()).doesNotContain(otherUser.getName());
 
-    List<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
+    ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId());
     assertThat(commitHistoryDiff).hasSize(1);
     assertThat(commitHistoryDiff.get(0)).contains("-author Change Owner <1@gerrit>");
     assertThat(commitHistoryDiff.get(0)).contains("+author Gerrit User 1 <1@gerrit>");
diff --git a/javatests/com/google/gerrit/server/notedb/OpenRepoTest.java b/javatests/com/google/gerrit/server/notedb/OpenRepoTest.java
index 323aee9..b74089f 100644
--- a/javatests/com/google/gerrit/server/notedb/OpenRepoTest.java
+++ b/javatests/com/google/gerrit/server/notedb/OpenRepoTest.java
@@ -20,7 +20,6 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
 import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.AttentionSetUpdate;
 import com.google.gerrit.entities.Change;
@@ -55,7 +54,7 @@
       ChangeUpdate update = newUpdate(c, changeOwner);
       update.setStatus(Change.Status.NEW);
 
-      ListMultimap<String, ChangeUpdate> changeUpdates =
+      ImmutableListMultimap<String, ChangeUpdate> changeUpdates =
           new ImmutableListMultimap.Builder<String, ChangeUpdate>().put("one", update).build();
 
       assertThrows(
@@ -73,7 +72,7 @@
 
       addToAttentionSet(update);
 
-      ListMultimap<String, ChangeUpdate> changeUpdates =
+      ImmutableListMultimap<String, ChangeUpdate> changeUpdates =
           new ImmutableListMultimap.Builder<String, ChangeUpdate>().put("one", update).build();
 
       openRepo.addUpdates(changeUpdates, NO_UPDATES_AT_ALL, MAX_PATCH_SETS);
@@ -96,7 +95,7 @@
                   submitLabel("Verified", "OK", changeOwner.getAccountId()),
                   submitLabel("Alternative-Code-Review", "NEED", null))));
 
-      ListMultimap<String, ChangeUpdate> changeUpdates =
+      ImmutableListMultimap<String, ChangeUpdate> changeUpdates =
           new ImmutableListMultimap.Builder<String, ChangeUpdate>().put("one", update).build();
 
       openRepo.addUpdates(changeUpdates, NO_UPDATES_AT_ALL, MAX_PATCH_SETS);
@@ -112,7 +111,7 @@
       ChangeUpdate update = newUpdate(c, changeOwner);
       update.setStatus(Change.Status.ABANDONED);
 
-      ListMultimap<String, ChangeUpdate> changeUpdates =
+      ImmutableListMultimap<String, ChangeUpdate> changeUpdates =
           new ImmutableListMultimap.Builder<String, ChangeUpdate>().put("one", update).build();
 
       openRepo.addUpdates(changeUpdates, NO_UPDATES_AT_ALL, MAX_PATCH_SETS);
@@ -132,7 +131,7 @@
       ChangeUpdate update2 = newUpdateForNewChange(c1, changeOwner);
       update2.setStatus(Change.Status.NEW);
 
-      ListMultimap<String, ChangeUpdate> changeUpdates =
+      ImmutableListMultimap<String, ChangeUpdate> changeUpdates =
           new ImmutableListMultimap.Builder<String, ChangeUpdate>().put("two", update2).build();
 
       openRepo.addUpdates(changeUpdates, ONLY_TWO_UPDATES, MAX_PATCH_SETS);
@@ -153,7 +152,7 @@
       ChangeUpdate update2 = newUpdateForNewChange(c1, changeOwner);
       update2.setStatus(Change.Status.NEW);
 
-      ListMultimap<String, ChangeUpdate> changeUpdates =
+      ImmutableListMultimap<String, ChangeUpdate> changeUpdates =
           new ImmutableListMultimap.Builder<String, ChangeUpdate>().put("two", update2).build();
 
       assertThrows(
@@ -176,7 +175,7 @@
       ChangeUpdate update2 = newUpdateForNewChange(c1, changeOwner);
       update2.setStatus(Change.Status.NEW);
 
-      ListMultimap<String, ChangeUpdate> changeUpdates =
+      ImmutableListMultimap<String, ChangeUpdate> changeUpdates =
           new ImmutableListMultimap.Builder<String, ChangeUpdate>().put("two", update2).build();
 
       assertThrows(
@@ -197,7 +196,7 @@
       ChangeUpdate update2 = newUpdateForNewChange(c1, changeOwner);
       update2.setStatus(Change.Status.NEW);
 
-      ListMultimap<String, ChangeUpdate> changeUpdates =
+      ImmutableListMultimap<String, ChangeUpdate> changeUpdates =
           new ImmutableListMultimap.Builder<String, ChangeUpdate>().put("two", update2).build();
 
       assertThrows(
@@ -218,7 +217,7 @@
       ChangeUpdate update2 = newUpdateForNewChange(c1, changeOwner);
       update2.setStatus(Change.Status.NEW);
 
-      ListMultimap<String, ChangeUpdate> changeUpdates =
+      ImmutableListMultimap<String, ChangeUpdate> changeUpdates =
           new ImmutableListMultimap.Builder<String, ChangeUpdate>().put("two", update2).build();
 
       assertThrows(
@@ -235,7 +234,7 @@
       ChangeUpdate update2 = newUpdateForNewChange(c1, changeOwner);
       update2.setStatus(Change.Status.NEW);
 
-      ListMultimap<String, ChangeUpdate> changeUpdates =
+      ImmutableListMultimap<String, ChangeUpdate> changeUpdates =
           new ImmutableListMultimap.Builder<String, ChangeUpdate>().put("two", update2).build();
 
       assertThrows(
diff --git a/javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java b/javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java
index 52a81ad..cc8685d 100644
--- a/javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java
+++ b/javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java
@@ -163,7 +163,7 @@
         IntraLineLoader.compute(aText, bText, ImmutableList.of(lines), ImmutableSet.of());
 
     assertThat(diff.getStatus()).isEqualTo(IntraLineDiff.Status.EDIT_LIST);
-    List<Edit> actualEdits = diff.getEdits();
+    ImmutableList<Edit> actualEdits = diff.getEdits();
     assertThat(actualEdits).hasSize(1);
     Edit actualEdit = actualEdits.get(0);
     assertThat(actualEdit.getBeginA()).isEqualTo(lines.getBeginA());
diff --git a/javatests/com/google/gerrit/server/project/SubmitRequirementsAdapterTest.java b/javatests/com/google/gerrit/server/project/SubmitRequirementsAdapterTest.java
index b05f3c7..08aa670 100644
--- a/javatests/com/google/gerrit/server/project/SubmitRequirementsAdapterTest.java
+++ b/javatests/com/google/gerrit/server/project/SubmitRequirementsAdapterTest.java
@@ -94,7 +94,7 @@
                 createLabel("Code-Review", Label.Status.OK),
                 createLabel("Verified", Label.Status.OK)));
 
-    List<SubmitRequirementResult> requirements =
+    ImmutableList<SubmitRequirementResult> requirements =
         SubmitRequirementsAdapter.createResult(
             submitRecord, labelTypes, psCommitId, /* isForced= */ false);
 
@@ -123,7 +123,7 @@
                 createLabel("Code-Review", Label.Status.NEED),
                 createLabel("Verified", Label.Status.NEED)));
 
-    List<SubmitRequirementResult> requirements =
+    ImmutableList<SubmitRequirementResult> requirements =
         SubmitRequirementsAdapter.createResult(
             submitRecord, labelTypes, psCommitId, /* isForced= */ false);
 
@@ -155,7 +155,7 @@
     // to indicate that all other records were forced, that's why we explicitly pass isForced=true
     // to the "submit requirements adapter". The resulting submit requirement result has a
     // status=FORCED.
-    List<SubmitRequirementResult> requirements =
+    ImmutableList<SubmitRequirementResult> requirements =
         SubmitRequirementsAdapter.createResult(
             submitRecord, labelTypes, psCommitId, /* isForced= */ true);
 
@@ -176,7 +176,7 @@
             Status.NOT_READY,
             Arrays.asList(createLabel("ISA-Label", Label.Status.NEED)));
 
-    List<SubmitRequirementResult> requirements =
+    ImmutableList<SubmitRequirementResult> requirements =
         SubmitRequirementsAdapter.createResult(
             submitRecord, labelTypes, psCommitId, /* isForced= */ false);
 
@@ -197,7 +197,7 @@
             Status.OK,
             Arrays.asList(createLabel("ISA-Label", Label.Status.OK)));
 
-    List<SubmitRequirementResult> requirements =
+    ImmutableList<SubmitRequirementResult> requirements =
         SubmitRequirementsAdapter.createResult(
             submitRecord, labelTypes, psCommitId, /* isForced= */ false);
 
@@ -218,7 +218,7 @@
             Status.OK,
             Arrays.asList(createLabel("Non-Existing", Label.Status.OK)));
 
-    List<SubmitRequirementResult> requirements =
+    ImmutableList<SubmitRequirementResult> requirements =
         SubmitRequirementsAdapter.createResult(
             submitRecord, labelTypes, psCommitId, /* isForced= */ false);
 
@@ -235,7 +235,7 @@
                 createLabel("Non-Existing", Label.Status.OK),
                 createLabel("Code-Review", Label.Status.OK)));
 
-    List<SubmitRequirementResult> requirements =
+    ImmutableList<SubmitRequirementResult> requirements =
         SubmitRequirementsAdapter.createResult(
             submitRecord, labelTypes, psCommitId, /* isForced= */ false);
 
@@ -254,7 +254,7 @@
     SubmitRecord submitRecord =
         createSubmitRecord("gerrit~IgnoreSelfApprovalRule", Status.OK, Arrays.asList());
 
-    List<SubmitRequirementResult> requirements =
+    ImmutableList<SubmitRequirementResult> requirements =
         SubmitRequirementsAdapter.createResult(
             submitRecord, labelTypes, psCommitId, /* isForced= */ false);
 
@@ -272,7 +272,7 @@
     SubmitRecord submitRecord =
         createSubmitRecord("gerrit~IgnoreSelfApprovalRule", Status.OK, /* labels= */ null);
 
-    List<SubmitRequirementResult> requirements =
+    ImmutableList<SubmitRequirementResult> requirements =
         SubmitRequirementsAdapter.createResult(
             submitRecord, labelTypes, psCommitId, /* isForced= */ false);
 
@@ -290,7 +290,7 @@
     SubmitRecord submitRecord =
         createSubmitRecord("gerrit~IgnoreSelfApprovalRule", Status.NOT_READY, Arrays.asList());
 
-    List<SubmitRequirementResult> requirements =
+    ImmutableList<SubmitRequirementResult> requirements =
         SubmitRequirementsAdapter.createResult(
             submitRecord, labelTypes, psCommitId, /* isForced= */ false);
 
@@ -313,7 +313,7 @@
                 createLabel("custom-label-1", Label.Status.NEED),
                 createLabel("custom-label-2", Label.Status.REJECT)));
 
-    List<SubmitRequirementResult> requirements =
+    ImmutableList<SubmitRequirementResult> requirements =
         SubmitRequirementsAdapter.createResult(
             submitRecord, labelTypes, psCommitId, /* isForced= */ false);
 
@@ -342,7 +342,7 @@
                 createLabel("custom-label-1", Label.Status.OK),
                 createLabel("custom-label-2", Label.Status.REJECT)));
 
-    List<SubmitRequirementResult> requirements =
+    ImmutableList<SubmitRequirementResult> requirements =
         SubmitRequirementsAdapter.createResult(
             submitRecord, labelTypes, psCommitId, /* isForced= */ false);
 
diff --git a/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java b/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
index f2ba446..0075121 100644
--- a/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
+++ b/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
@@ -904,7 +904,7 @@
   protected List<AccountInfo> assertQuery(QueryRequest query, List<AccountInfo> accounts)
       throws Exception {
     List<AccountInfo> result = query.get();
-    Iterable<Integer> ids = ids(result);
+    List<Integer> ids = ids(result);
     assertWithMessage(format(query, result, accounts))
         .that(ids)
         .containsExactlyElementsIn(ids(accounts))
@@ -954,11 +954,11 @@
     return b.toString();
   }
 
-  protected static Iterable<Integer> ids(AccountInfo... accounts) {
+  protected static List<Integer> ids(AccountInfo... accounts) {
     return ids(Arrays.asList(accounts));
   }
 
-  protected static Iterable<Integer> ids(List<AccountInfo> accounts) {
+  protected static List<Integer> ids(List<AccountInfo> accounts) {
     return accounts.stream().map(a -> a._accountId).collect(toList());
   }
 
diff --git a/javatests/com/google/gerrit/server/query/account/BUILD b/javatests/com/google/gerrit/server/query/account/BUILD
index a1fa70b..fb0e739 100644
--- a/javatests/com/google/gerrit/server/query/account/BUILD
+++ b/javatests/com/google/gerrit/server/query/account/BUILD
@@ -42,6 +42,7 @@
         ":abstract_query_tests",
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/testing:gerrit-test-util",
+        "//lib:guava",
         "//lib:jgit",
         "//lib/guice",
     ],
@@ -58,6 +59,7 @@
         ":abstract_query_tests",
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/testing:gerrit-test-util",
+        "//lib:guava",
         "//lib:jgit",
         "//lib/guice",
     ],
diff --git a/javatests/com/google/gerrit/server/query/account/FakeQueryAccountsTest.java b/javatests/com/google/gerrit/server/query/account/FakeQueryAccountsTest.java
index 31d256e..69ed948 100644
--- a/javatests/com/google/gerrit/server/query/account/FakeQueryAccountsTest.java
+++ b/javatests/com/google/gerrit/server/query/account/FakeQueryAccountsTest.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.query.account;
 
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
 import com.google.gerrit.testing.ConfigSuite;
 import com.google.gerrit.testing.InMemoryModule;
@@ -21,7 +22,6 @@
 import com.google.gerrit.testing.IndexVersions;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
-import java.util.List;
 import java.util.Map;
 import org.eclipse.jgit.lib.Config;
 
@@ -34,7 +34,7 @@
   @ConfigSuite.Configs
   public static Map<String, Config> againstPreviousIndexVersion() {
     // the current schema version is already tested by the inherited default config suite
-    List<Integer> schemaVersions =
+    ImmutableList<Integer> schemaVersions =
         IndexVersions.getWithoutLatest(AccountSchemaDefinitions.INSTANCE);
     return IndexVersions.asConfigMap(
         AccountSchemaDefinitions.INSTANCE, schemaVersions, "againstIndexVersion", defaultConfig());
diff --git a/javatests/com/google/gerrit/server/query/account/LuceneQueryAccountsTest.java b/javatests/com/google/gerrit/server/query/account/LuceneQueryAccountsTest.java
index e36b79e..424b5c4 100644
--- a/javatests/com/google/gerrit/server/query/account/LuceneQueryAccountsTest.java
+++ b/javatests/com/google/gerrit/server/query/account/LuceneQueryAccountsTest.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.query.account;
 
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
 import com.google.gerrit.testing.ConfigSuite;
 import com.google.gerrit.testing.InMemoryModule;
@@ -21,7 +22,6 @@
 import com.google.gerrit.testing.IndexVersions;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
-import java.util.List;
 import java.util.Map;
 import org.eclipse.jgit.lib.Config;
 
@@ -34,7 +34,7 @@
   @ConfigSuite.Configs
   public static Map<String, Config> againstPreviousIndexVersion() {
     // the current schema version is already tested by the inherited default config suite
-    List<Integer> schemaVersions =
+    ImmutableList<Integer> schemaVersions =
         IndexVersions.getWithoutLatest(AccountSchemaDefinitions.INSTANCE);
     return IndexVersions.asConfigMap(
         AccountSchemaDefinitions.INSTANCE, schemaVersions, "againstIndexVersion", defaultConfig());
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 30d2c8d..7360de3 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -41,6 +41,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
@@ -1435,7 +1436,7 @@
     assertThat(m).hasSize(1);
     assertThat(m).containsEntry("Code-Review", Short.valueOf((short) 1));
 
-    Multimap<Integer, Change> changes =
+    ListMultimap<Integer, Change> changes =
         Multimaps.newListMultimap(Maps.newLinkedHashMap(), () -> Lists.newArrayList());
     changes.put(2, reviewPlus2Change);
     changes.put(1, reviewTwoPlus1Change);
@@ -2658,7 +2659,7 @@
     }
   }
 
-  private List<Change> setUpHashtagChanges() throws Exception {
+  private ImmutableList<Change> setUpHashtagChanges() throws Exception {
     Project.NameKey project = Project.nameKey("repo");
     repo = createAndOpenProject(project);
     Change change1 = insert(project, newChange(repo));
@@ -2678,7 +2679,7 @@
 
   @Test
   public void byHashtag() throws Exception {
-    List<Change> changes = setUpHashtagChanges();
+    ImmutableList<Change> changes = setUpHashtagChanges();
     assertQuery("hashtag:foo", changes.get(1), changes.get(0));
     assertQuery("hashtag:bar", changes.get(1));
     assertQuery("hashtag:\"a tag\"", changes.get(1));
@@ -2693,7 +2694,7 @@
   @Test
   public void byHashtagFullText() throws Exception {
     assume().that(getSchema().hasField(ChangeField.FUZZY_HASHTAG)).isTrue();
-    List<Change> changes = setUpHashtagChanges();
+    ImmutableList<Change> changes = setUpHashtagChanges();
     assertQuery("inhashtag:foo", changes.get(1), changes.get(0));
     assertQuery("inhashtag:bbb", changes.get(0));
     assertQuery("inhashtag:tag", changes.get(1));
@@ -2702,7 +2703,7 @@
   @Test
   public void byHashtagPrefix() throws Exception {
     assume().that(getSchema().hasField(ChangeField.PREFIX_HASHTAG)).isTrue();
-    List<Change> changes = setUpHashtagChanges();
+    ImmutableList<Change> changes = setUpHashtagChanges();
     assertQuery("prefixhashtag:a", changes.get(1), changes.get(0));
     assertQuery("prefixhashtag:aa", changes.get(0));
     assertQuery("prefixhashtag:bar", changes.get(1));
@@ -4726,7 +4727,7 @@
   protected List<ChangeInfo> assertQueryByIds(QueryRequest query, Change.Id... expectedChangeIds)
       throws Exception {
     List<ChangeInfo> result = query.get();
-    Iterable<Change.Id> actualIds = ids(result);
+    List<Change.Id> actualIds = ids(result);
     assertThat(actualIds).containsExactlyElementsIn(Arrays.asList(expectedChangeIds)).inOrder();
     return result;
   }
@@ -4785,11 +4786,11 @@
     return b.toString();
   }
 
-  protected static Iterable<Change.Id> ids(Change... changes) {
+  protected static List<Change.Id> ids(Change... changes) {
     return Arrays.stream(changes).map(Change::getId).collect(toList());
   }
 
-  protected static Iterable<Change.Id> ids(Iterable<ChangeInfo> changes) {
+  protected static List<Change.Id> ids(Iterable<ChangeInfo> changes) {
     return Streams.stream(changes).map(c -> Change.id(c._number)).collect(toList());
   }
 
diff --git a/javatests/com/google/gerrit/server/query/change/FakeQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/FakeQueryChangesTest.java
index 1e6e52b..c7f4f64 100644
--- a/javatests/com/google/gerrit/server/query/change/FakeQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/FakeQueryChangesTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.acceptance.UseClockStep;
 import com.google.gerrit.entities.Permission;
 import com.google.gerrit.entities.Project;
@@ -238,7 +239,8 @@
 
   @SuppressWarnings("unused")
   private void executeQuery(String query) throws QueryParseException {
-    List<ChangeData> unused = queryProvider.get().query(queryBuilderProvider.get().parse(query));
+    ImmutableList<ChangeData> unused =
+        queryProvider.get().query(queryBuilderProvider.get().parse(query));
   }
 
   private void assertThatSearchQueryWasNotPaginated(int queryCount) {
diff --git a/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java b/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
index cffa577..572e7af 100644
--- a/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
+++ b/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
@@ -504,7 +504,7 @@
   protected List<GroupInfo> assertQuery(QueryRequest query, List<GroupInfo> groups)
       throws Exception {
     List<GroupInfo> result = query.get();
-    Iterable<String> uuids = uuids(result);
+    List<String> uuids = uuids(result);
     assertWithMessage(format(query, result, groups))
         .that(uuids)
         .containsExactlyElementsIn(uuids(groups))
@@ -577,11 +577,11 @@
     return b == null ? false : b;
   }
 
-  protected static Iterable<String> ids(GroupInfo... groups) {
+  protected static List<String> ids(GroupInfo... groups) {
     return uuids(Arrays.asList(groups));
   }
 
-  protected static Iterable<String> uuids(List<GroupInfo> groups) {
+  protected static List<String> uuids(List<GroupInfo> groups) {
     return groups.stream().map(g -> g.id).sorted().collect(toList());
   }
 
diff --git a/javatests/com/google/gerrit/server/query/group/BUILD b/javatests/com/google/gerrit/server/query/group/BUILD
index 047feb0..25cd76b 100644
--- a/javatests/com/google/gerrit/server/query/group/BUILD
+++ b/javatests/com/google/gerrit/server/query/group/BUILD
@@ -42,6 +42,7 @@
         ":abstract_query_tests",
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/testing:gerrit-test-util",
+        "//lib:guava",
         "//lib:jgit",
         "//lib/guice",
     ],
@@ -58,6 +59,7 @@
         ":abstract_query_tests",
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/testing:gerrit-test-util",
+        "//lib:guava",
         "//lib:jgit",
         "//lib/guice",
     ],
diff --git a/javatests/com/google/gerrit/server/query/group/FakeQueryGroupsTest.java b/javatests/com/google/gerrit/server/query/group/FakeQueryGroupsTest.java
index d347716..d623f30 100644
--- a/javatests/com/google/gerrit/server/query/group/FakeQueryGroupsTest.java
+++ b/javatests/com/google/gerrit/server/query/group/FakeQueryGroupsTest.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.query.group;
 
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
 import com.google.gerrit.testing.ConfigSuite;
 import com.google.gerrit.testing.InMemoryModule;
@@ -21,7 +22,6 @@
 import com.google.gerrit.testing.IndexVersions;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
-import java.util.List;
 import java.util.Map;
 import org.eclipse.jgit.lib.Config;
 
@@ -48,7 +48,8 @@
   @ConfigSuite.Configs
   public static Map<String, Config> againstPreviousIndexVersion() {
     // the current schema version is already tested by the inherited default config suite
-    List<Integer> schemaVersions = IndexVersions.getWithoutLatest(GroupSchemaDefinitions.INSTANCE);
+    ImmutableList<Integer> schemaVersions =
+        IndexVersions.getWithoutLatest(GroupSchemaDefinitions.INSTANCE);
     return IndexVersions.asConfigMap(
         GroupSchemaDefinitions.INSTANCE, schemaVersions, "againstIndexVersion", defaultConfig());
   }
diff --git a/javatests/com/google/gerrit/server/query/group/LuceneQueryGroupsTest.java b/javatests/com/google/gerrit/server/query/group/LuceneQueryGroupsTest.java
index 2a453a0..3f3eb58 100644
--- a/javatests/com/google/gerrit/server/query/group/LuceneQueryGroupsTest.java
+++ b/javatests/com/google/gerrit/server/query/group/LuceneQueryGroupsTest.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.query.group;
 
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
 import com.google.gerrit.testing.ConfigSuite;
 import com.google.gerrit.testing.InMemoryModule;
@@ -21,7 +22,6 @@
 import com.google.gerrit.testing.IndexVersions;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
-import java.util.List;
 import java.util.Map;
 import org.eclipse.jgit.lib.Config;
 
@@ -34,7 +34,8 @@
   @ConfigSuite.Configs
   public static Map<String, Config> againstPreviousIndexVersion() {
     // the current schema version is already tested by the inherited default config suite
-    List<Integer> schemaVersions = IndexVersions.getWithoutLatest(GroupSchemaDefinitions.INSTANCE);
+    ImmutableList<Integer> schemaVersions =
+        IndexVersions.getWithoutLatest(GroupSchemaDefinitions.INSTANCE);
     return IndexVersions.asConfigMap(
         GroupSchemaDefinitions.INSTANCE, schemaVersions, "againstIndexVersion", defaultConfig());
   }
diff --git a/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java b/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
index d6bf580..d766e16 100644
--- a/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
+++ b/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
@@ -501,7 +501,7 @@
   protected List<ProjectInfo> assertQuery(QueryRequest query, List<ProjectInfo> projects)
       throws Exception {
     List<ProjectInfo> result = query.get();
-    Iterable<String> names = names(result);
+    List<String> names = names(result);
     assertWithMessage(format(query, result, projects))
         .that(names)
         .containsExactlyElementsIn(names(projects))
@@ -566,11 +566,11 @@
     return indexes.getSearchIndex().getSchema();
   }
 
-  protected static Iterable<String> names(ProjectInfo... projects) {
+  protected static List<String> names(ProjectInfo... projects) {
     return names(Arrays.asList(projects));
   }
 
-  protected static Iterable<String> names(List<ProjectInfo> projects) {
+  protected static List<String> names(List<ProjectInfo> projects) {
     return projects.stream().map(p -> p.name).collect(toList());
   }
 
diff --git a/javatests/com/google/gerrit/server/query/project/BUILD b/javatests/com/google/gerrit/server/query/project/BUILD
index b034c2d..ae94e69 100644
--- a/javatests/com/google/gerrit/server/query/project/BUILD
+++ b/javatests/com/google/gerrit/server/query/project/BUILD
@@ -39,6 +39,7 @@
         "//java/com/google/gerrit/index/project",
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/testing:gerrit-test-util",
+        "//lib:guava",
         "//lib:jgit",
         "//lib/guice",
     ],
@@ -56,6 +57,7 @@
         "//java/com/google/gerrit/index/project",
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/testing:gerrit-test-util",
+        "//lib:guava",
         "//lib:jgit",
         "//lib/guice",
     ],
diff --git a/javatests/com/google/gerrit/server/query/project/FakeQueryProjectsTest.java b/javatests/com/google/gerrit/server/query/project/FakeQueryProjectsTest.java
index 6fc0568..19fbe66 100644
--- a/javatests/com/google/gerrit/server/query/project/FakeQueryProjectsTest.java
+++ b/javatests/com/google/gerrit/server/query/project/FakeQueryProjectsTest.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.query.project;
 
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.index.project.ProjectSchemaDefinitions;
 import com.google.gerrit.testing.ConfigSuite;
 import com.google.gerrit.testing.InMemoryModule;
@@ -21,7 +22,6 @@
 import com.google.gerrit.testing.IndexVersions;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
-import java.util.List;
 import java.util.Map;
 import org.eclipse.jgit.lib.Config;
 
@@ -34,7 +34,7 @@
   @ConfigSuite.Configs
   public static Map<String, Config> againstPreviousIndexVersion() {
     // the current schema version is already tested by the inherited default config suite
-    List<Integer> schemaVersions =
+    ImmutableList<Integer> schemaVersions =
         IndexVersions.getWithoutLatest(
             com.google.gerrit.index.project.ProjectSchemaDefinitions.INSTANCE);
     return IndexVersions.asConfigMap(
diff --git a/javatests/com/google/gerrit/server/query/project/LuceneQueryProjectsTest.java b/javatests/com/google/gerrit/server/query/project/LuceneQueryProjectsTest.java
index 77a56ed..09e8388 100644
--- a/javatests/com/google/gerrit/server/query/project/LuceneQueryProjectsTest.java
+++ b/javatests/com/google/gerrit/server/query/project/LuceneQueryProjectsTest.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.query.project;
 
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.index.project.ProjectSchemaDefinitions;
 import com.google.gerrit.testing.ConfigSuite;
 import com.google.gerrit.testing.InMemoryModule;
@@ -21,7 +22,6 @@
 import com.google.gerrit.testing.IndexVersions;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
-import java.util.List;
 import java.util.Map;
 import org.eclipse.jgit.lib.Config;
 
@@ -34,7 +34,7 @@
   @ConfigSuite.Configs
   public static Map<String, Config> againstPreviousIndexVersion() {
     // the current schema version is already tested by the inherited default config suite
-    List<Integer> schemaVersions =
+    ImmutableList<Integer> schemaVersions =
         IndexVersions.getWithoutLatest(
             com.google.gerrit.index.project.ProjectSchemaDefinitions.INSTANCE);
     return IndexVersions.asConfigMap(
diff --git a/javatests/com/google/gerrit/server/restapi/change/ListChangeCommentsTest.java b/javatests/com/google/gerrit/server/restapi/change/ListChangeCommentsTest.java
index eb1d275..6f504c4 100644
--- a/javatests/com/google/gerrit/server/restapi/change/ListChangeCommentsTest.java
+++ b/javatests/com/google/gerrit/server/restapi/change/ListChangeCommentsTest.java
@@ -86,7 +86,7 @@
     linkAuthor(comments.get(5), accountId1);
 
     // Change massages have exactly same timestamps
-    List<ChangeMessage> changeMessages =
+    ImmutableList<ChangeMessage> changeMessages =
         ImmutableList.of(
             createChangeMessage("cm0", "10", Account.id(accountId1)),
             createChangeMessage("cm1", "11", Account.id(accountId1)),
@@ -142,7 +142,7 @@
     linkAuthor(comments.get(1), accountId2Imported);
     linkAuthor(comments.get(2), accountId3);
 
-    List<ChangeMessage> changeMessages =
+    ImmutableList<ChangeMessage> changeMessages =
         ImmutableList.of(
             createChangeMessage("changeMessage0", tsCm0, Account.id(accountId1)),
             createChangeMessage("changeMessage1", tsCm1, Account.id(accountId2)),
diff --git a/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
index 509447a..c7ac533 100644
--- a/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
+++ b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.entities.PatchSetApproval;
 import java.time.Instant;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import org.junit.Test;
 
@@ -42,7 +41,7 @@
     PatchSetApproval approvalVerified = makeApproval(VERIFIED.getLabelId(), USER1, 2);
     PatchSetApproval approvalCr = makeApproval(codeReview.getLabelId(), USER1, 2);
 
-    Collection<PatchSetApproval> filteredApprovals =
+    ImmutableList<PatchSetApproval> filteredApprovals =
         IgnoreSelfApprovalRule.filterApprovalsByLabel(
             ImmutableList.of(approvalVerified, approvalCr), VERIFIED);
 
@@ -62,7 +61,7 @@
             makeApproval(VERIFIED.getLabelId(), USER1, +1),
             makeApproval(VERIFIED.getLabelId(), USER1, +2));
 
-    Collection<PatchSetApproval> filteredApprovals =
+    ImmutableList<PatchSetApproval> filteredApprovals =
         IgnoreSelfApprovalRule.filterOutPositiveApprovalsOfUser(approvals, USER1);
 
     assertThat(filteredApprovals).containsExactly(approvalM1, approvalM2);
diff --git a/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java b/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java
index 35d4961..325961c 100644
--- a/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java
+++ b/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java
@@ -109,7 +109,7 @@
 
   private boolean hasGroup(String name) throws Exception {
     try (Repository repo = repositoryManager.openRepository(allUsersName)) {
-      List<GroupReference> nameNotes = GroupNameNotes.loadAllGroups(repo);
+      ImmutableList<GroupReference> nameNotes = GroupNameNotes.loadAllGroups(repo);
       return nameNotes.stream().anyMatch(g -> g.getName().equals(name));
     }
   }
diff --git a/javatests/com/google/gerrit/testing/BUILD b/javatests/com/google/gerrit/testing/BUILD
index 9443b0d..136938a 100644
--- a/javatests/com/google/gerrit/testing/BUILD
+++ b/javatests/com/google/gerrit/testing/BUILD
@@ -7,6 +7,7 @@
     deps = [
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/testing:gerrit-test-util",
+        "//lib:guava",
         "//lib:jgit",
         "//lib/truth",
     ],
diff --git a/javatests/com/google/gerrit/testing/IndexVersionsTest.java b/javatests/com/google/gerrit/testing/IndexVersionsTest.java
index 0362ddc..3bd3e75 100644
--- a/javatests/com/google/gerrit/testing/IndexVersionsTest.java
+++ b/javatests/com/google/gerrit/testing/IndexVersionsTest.java
@@ -21,6 +21,7 @@
 import static com.google.gerrit.testing.IndexVersions.CURRENT;
 import static com.google.gerrit.testing.IndexVersions.PREVIOUS;
 
+import com.google.common.collect.ImmutableList;
 import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
 import java.util.ArrayList;
 import java.util.List;
@@ -129,7 +130,7 @@
             + SCHEMA_DEF.getSchemas().keySet());
   }
 
-  private static List<Integer> get(String value) {
+  private static ImmutableList<Integer> get(String value) {
     return IndexVersions.get(ChangeSchemaDefinitions.INSTANCE, "test", value);
   }
 
diff --git a/plugins/delete-project b/plugins/delete-project
index 75b79c1..e2c70ab 160000
--- a/plugins/delete-project
+++ b/plugins/delete-project
@@ -1 +1 @@
-Subproject commit 75b79c115ba3dd03aa56a3165e6c319fa30c6c4a
+Subproject commit e2c70ab914716f3441d06a7b4ab4320eaf667c42
diff --git a/plugins/replication b/plugins/replication
index 877f526..d464511 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 877f52684857aa1e5cf6d7146d493cbe9702e314
+Subproject commit d464511adf2f5560f1aea9932fc6a80e0cf3595d
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
index baba0635..899f860 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
@@ -997,7 +997,7 @@
   }
 
   private renderFixSuggestionPreview() {
-    if (!this.comment?.fix_suggestions) return nothing;
+    if (!this.comment?.fix_suggestions || isDraft(this.comment)) return nothing;
     return html`<gr-suggestion-diff-preview
       .fixReplacementInfos=${this.comment?.fix_suggestions?.[0].replacements}
     ></gr-suggestion-diff-preview>`;
@@ -1028,6 +1028,7 @@
   private renderGeneratedSuggestionPreview() {
     if (!this.showGeneratedSuggestion() || !this.generateSuggestion)
       return nothing;
+    if (!isDraft(this.comment)) return nothing;
 
     if (this.generatedFixSuggestion) {
       return html`<gr-suggestion-diff-preview
@@ -1065,6 +1066,15 @@
               );
               if (this.generateSuggestion) {
                 this.generateSuggestionTrigger$.next();
+              } else {
+                if (
+                  this.flagsService.isEnabled(
+                    KnownExperimentId.ML_SUGGESTED_EDIT_V2
+                  )
+                ) {
+                  this.generatedFixSuggestion = undefined;
+                  this.autoSaveTrigger$.next();
+                }
               }
               this.reporting.reportInteraction(
                 this.generateSuggestion
@@ -1101,7 +1111,7 @@
     if (!this.generateSuggestion) {
       return '';
     }
-    if (this.generatedSuggestion) {
+    if (this.generatedSuggestion || this.generatedFixSuggestion) {
       return '(1)';
     } else {
       return '(0)';
@@ -1224,6 +1234,7 @@
     const suggestion = suggestionResponse.fix_suggestions?.[0];
     if (!suggestion) return;
     this.generatedFixSuggestion = suggestion;
+    this.autoSaveTrigger$.next();
   }
 
   private renderRobotActions() {
@@ -1570,7 +1581,7 @@
     assert(isDraft(this.comment), 'only drafts are editable');
     const messageToSave = this.messageText.trimEnd();
     if (messageToSave === '') return;
-    if (messageToSave === this.comment.message) return;
+    if (!this.somethingToSave()) return;
 
     try {
       this.autoSaving = this.rawSave({showToast: false});
@@ -1634,7 +1645,8 @@
     return (
       isError(this.comment) ||
       this.messageText.trimEnd() !== this.comment?.message ||
-      this.unresolved !== this.comment.unresolved
+      this.unresolved !== this.comment.unresolved ||
+      this.comment?.fix_suggestions !== this.getFixSuggestions()
     );
   }
 
@@ -1647,11 +1659,20 @@
         ...this.comment,
         message: this.messageText.trimEnd(),
         unresolved: this.unresolved,
+        fix_suggestions: this.getFixSuggestions(),
       },
       options.showToast
     );
   }
 
+  getFixSuggestions(): FixSuggestionInfo[] | undefined {
+    if (!this.flagsService.isEnabled(KnownExperimentId.ML_SUGGESTED_EDIT_V2))
+      return undefined;
+    if (!this.generateSuggestion) return undefined;
+    if (!this.generatedFixSuggestion) return undefined;
+    return [this.generatedFixSuggestion];
+  }
+
   private handleToggleResolved() {
     this.unresolved = !this.unresolved;
     if (!this.editing) {
diff --git a/tools/BUILD b/tools/BUILD
index 5f8783f..71ad096 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -336,6 +336,7 @@
         "-Xep:PeriodTimeMath:ERROR",
         "-Xep:PreconditionsCheckNotNullRepeated:ERROR",
         "-Xep:PreconditionsInvalidPlaceholder:ERROR",
+        "-Xep:PreferredInterfaceType:ERROR",
         "-Xep:PrimitiveAtomicReference:ERROR",
         "-Xep:PrivateSecurityContractProtoAccess:ERROR",
         "-Xep:ProtectedMembersInFinalClass:ERROR",