Merge "Prevent scrolling when initializing the file list"
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/EventRecorder.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/EventRecorder.java
index fbdfee6..606ad61 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/EventRecorder.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/EventRecorder.java
@@ -19,7 +19,7 @@
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.UserScopedEventListener;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.extensions.registration.RegistrationHandle;
@@ -39,7 +39,7 @@
 
 public class EventRecorder {
   private final RegistrationHandle eventListenerRegistration;
-  private final Multimap<String, RefEvent> recordedEvents;
+  private final ListMultimap<String, RefEvent> recordedEvents;
 
   @Singleton
   public static class Factory {
diff --git a/gerrit-antlr/BUILD b/gerrit-antlr/BUILD
index 8337152..f4ce4c7 100644
--- a/gerrit-antlr/BUILD
+++ b/gerrit-antlr/BUILD
@@ -13,7 +13,7 @@
     cmd = " && ".join([
         "$(location //lib/antlr:antlr-tool) -o $$TMP $<",
         "cd $$TMP",
-        "$$ROOT/$(location @bazel_tools//tools/zip:zipper) cC $$ROOT/$@ $$(find .)",
+        "$$ROOT/$(location @bazel_tools//tools/zip:zipper) cC $$ROOT/$@ $$(find *)",
     ]),
     tools = [
         "//lib/antlr:antlr-tool",
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/AccountExternalIdInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/AccountExternalIdInfo.java
index 39f4847..3bcf387 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/AccountExternalIdInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/AccountExternalIdInfo.java
@@ -44,4 +44,9 @@
     }
     return false;
   }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(identity, emailAddress, trusted, canDelete);
+  }
 }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/config/ExternalIncludedIn.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/config/ExternalIncludedIn.java
index d78fa63..9e12c8a 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/config/ExternalIncludedIn.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/config/ExternalIncludedIn.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.extensions.config;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.extensions.annotations.ExtensionPoint;
 
 import java.util.Collection;
@@ -37,6 +37,6 @@
    * @param branches the branches that include the commit
    * @return additional entries for IncludedInInfo
    */
-  Multimap<String, String> getIncludedIn(String project, String commit,
+  ListMultimap<String, String> getIncludedIn(String project, String commit,
       Collection<String> tags, Collection<String> branches);
 }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/NeedsParams.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/NeedsParams.java
index 213ae13..28dcc99 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/NeedsParams.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/NeedsParams.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.extensions.restapi;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 
 /**
  * Optional interface for {@link RestCollection}.
@@ -28,5 +28,5 @@
    *
    * @param params the request parameter
    */
-  void setParams(Multimap<String, String> params) throws RestApiException;
+  void setParams(ListMultimap<String, String> params) throws RestApiException;
 }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpAutoRegisterModuleGenerator.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpAutoRegisterModuleGenerator.java
index 8ae0e5c..8392f84 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpAutoRegisterModuleGenerator.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpAutoRegisterModuleGenerator.java
@@ -17,7 +17,7 @@
 import static com.google.gerrit.server.plugins.AutoRegisterUtil.calculateBindAnnotation;
 
 import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.extensions.annotations.Export;
 import com.google.gerrit.server.plugins.InvalidPluginException;
 import com.google.gerrit.server.plugins.ModuleGenerator;
@@ -35,7 +35,8 @@
 class HttpAutoRegisterModuleGenerator extends ServletModule
     implements ModuleGenerator {
   private final Map<String, Class<HttpServlet>> serve = new HashMap<>();
-  private final Multimap<TypeLiteral<?>, Class<?>> listeners = LinkedListMultimap.create();
+  private final ListMultimap<TypeLiteral<?>, Class<?>> listeners =
+      LinkedListMultimap.create();
 
   @Override
   protected void configureServlets() {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/ParameterParser.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/ParameterParser.java
index f80cc49..782055c 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/ParameterParser.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/ParameterParser.java
@@ -23,7 +23,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.Url;
@@ -59,7 +59,7 @@
   }
 
   <T> boolean parse(T param,
-      Multimap<String, String> in,
+      ListMultimap<String, String> in,
       HttpServletRequest req,
       HttpServletResponse res)
       throws IOException {
@@ -90,8 +90,8 @@
   }
 
   static void splitQueryString(String queryString,
-      Multimap<String, String> config,
-      Multimap<String, String> params) {
+      ListMultimap<String, String> config,
+      ListMultimap<String, String> params) {
     if (!Strings.isNullOrEmpty(queryString)) {
       for (String kvPair : Splitter.on('&').split(queryString)) {
         Iterator<String> i = Splitter.on('=').limit(2).split(kvPair).iterator();
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 7b09eff..cc2f7d7 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -46,12 +46,12 @@
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.MultimapBuilder;
 import com.google.common.io.BaseEncoding;
 import com.google.common.io.CountingOutputStream;
 import com.google.common.math.IntMath;
@@ -245,8 +245,10 @@
     int status = SC_OK;
     long responseBytes = -1;
     Object result = null;
-    Multimap<String, String> params = LinkedHashMultimap.create();
-    Multimap<String, String> config = LinkedHashMultimap.create();
+    ListMultimap<String, String> params =
+        MultimapBuilder.hashKeys().arrayListValues().build();
+    ListMultimap<String, String> config =
+        MultimapBuilder.hashKeys().arrayListValues().build();
     Object inputRequestBody = null;
     RestResource rsrc = TopLevelResource.INSTANCE;
     ViewData viewData = null;
@@ -460,9 +462,9 @@
           metric,
           System.nanoTime() - startNanos,
           TimeUnit.NANOSECONDS);
-      globals.auditService.dispatch(new ExtendedHttpAuditEvent(globals.webSession.get()
-          .getSessionId(), globals.currentUser.get(), req,
-          auditStartTs, params, inputRequestBody, status,
+      globals.auditService.dispatch(new ExtendedHttpAuditEvent(
+          globals.webSession.get().getSessionId(), globals.currentUser.get(),
+          req, auditStartTs, params, inputRequestBody, status,
           result, rsrc, viewData == null ? null : viewData.view));
     }
   }
@@ -777,7 +779,7 @@
 
   public static long replyJson(@Nullable HttpServletRequest req,
       HttpServletResponse res,
-      Multimap<String, String> config,
+      ListMultimap<String, String> config,
       Object result)
       throws IOException {
     TemporaryBuffer.Heap buf = heap(HEAP_EST_SIZE, Integer.MAX_VALUE);
@@ -796,7 +798,7 @@
       .setCharacterEncoding(UTF_8));
   }
 
-  private static Gson newGson(Multimap<String, String> config,
+  private static Gson newGson(ListMultimap<String, String> config,
       @Nullable HttpServletRequest req) {
     GsonBuilder gb = OutputFormat.JSON_COMPACT.newGsonBuilder();
 
@@ -807,7 +809,7 @@
   }
 
   private static void enablePrettyPrint(GsonBuilder gb,
-      Multimap<String, String> config,
+      ListMultimap<String, String> config,
       @Nullable HttpServletRequest req) {
     String pp = Iterables.getFirst(config.get("pp"), null);
     if (pp == null) {
@@ -822,7 +824,7 @@
   }
 
   private static void enablePartialGetFields(GsonBuilder gb,
-      Multimap<String, String> config) {
+      ListMultimap<String, String> config) {
     final Set<String> want = new HashSet<>();
     for (String p : config.get("fields")) {
       Iterables.addAll(want, OptionUtil.splitOptionValue(p));
@@ -1130,7 +1132,8 @@
   static long replyText(@Nullable HttpServletRequest req,
       HttpServletResponse res, String text) throws IOException {
     if ((req == null || isRead(req)) && isMaybeHTML(text)) {
-      return replyJson(req, res, ImmutableMultimap.of("pp", "0"), new JsonPrimitive(text));
+      return replyJson(req, res, ImmutableListMultimap.of("pp", "0"),
+          new JsonPrimitive(text));
     }
     if (!text.endsWith("\n")) {
       text += "\n";
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java
index 319907b..e0b48dc 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.httpd.rpc;
 
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.audit.AuditService;
 import com.google.gerrit.audit.RpcAuditEvent;
 import com.google.gerrit.common.TimeUtil;
@@ -133,25 +133,29 @@
       }
       Audit note = method.getAnnotation(Audit.class);
       if (note != null) {
-        final String sid = call.getWebSession().getSessionId();
-        final CurrentUser username = call.getWebSession().getUser();
-        final Multimap<String, ?> args =
-            extractParams(note, call);
-        final String what = extractWhat(note, call);
-        final Object result = call.getResult();
+        String sid = call.getWebSession().getSessionId();
+        CurrentUser username = call.getWebSession().getUser();
+        ListMultimap<String, ?> args = extractParams(note, call);
+        String what = extractWhat(note, call);
+        Object result = call.getResult();
 
-        audit.dispatch(new RpcAuditEvent(sid, username, what, call.getWhen(),
-            args, call.getHttpServletRequest().getMethod(), call.getHttpServletRequest().getMethod(),
-            ((AuditedHttpServletResponse) (call.getHttpServletResponse()))
-                .getStatus(), result));
+        audit.dispatch(
+            new RpcAuditEvent(
+                sid, username, what, call.getWhen(), args,
+                call.getHttpServletRequest().getMethod(),
+                call.getHttpServletRequest().getMethod(),
+                ((AuditedHttpServletResponse) (call.getHttpServletResponse()))
+                    .getStatus(),
+                result));
       }
     } catch (Throwable all) {
       log.error("Unable to log the call", all);
     }
   }
 
-  private Multimap<String, ?> extractParams(final Audit note, final GerritCall call) {
-    Multimap<String, Object> args = ArrayListMultimap.create();
+  private ListMultimap<String, ?> extractParams(Audit note, GerritCall call) {
+    ListMultimap<String, Object> args =
+        MultimapBuilder.hashKeys().arrayListValues().build();
 
     Object[] params = call.getParams();
     for (int i = 0; i < params.length; i++) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/doc/QueryDocumentationFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/doc/QueryDocumentationFilter.java
index 973adbe..3458ffc 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/doc/QueryDocumentationFilter.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/doc/QueryDocumentationFilter.java
@@ -15,8 +15,7 @@
 package com.google.gerrit.httpd.rpc.doc;
 
 import com.google.common.base.Strings;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ImmutableListMultimap;
 import com.google.gerrit.httpd.restapi.RestApiServlet;
 import com.google.gerrit.server.documentation.QueryDocumentationExecutor;
 import com.google.gerrit.server.documentation.QueryDocumentationExecutor.DocQueryException;
@@ -68,8 +67,7 @@
       HttpServletResponse rsp = (HttpServletResponse) response;
       try {
         List<DocResult> result = searcher.doQuery(request.getParameter("q"));
-        Multimap<String, String> config = LinkedHashMultimap.create();
-        RestApiServlet.replyJson(req, rsp, config, result);
+        RestApiServlet.replyJson(req, rsp, ImmutableListMultimap.of(), result);
       } catch (DocQueryException e) {
         log.error("Doc search failed:", e);
         rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 385bd70..688d4e7 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -24,11 +24,11 @@
 import static java.util.stream.Collectors.toList;
 
 import com.google.common.base.Throwables;
-import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListeningExecutorService;
@@ -408,10 +408,10 @@
     }
   }
 
-  private static Multimap<String, IndexableField> fields(Document doc,
+  private static ListMultimap<String, IndexableField> fields(Document doc,
       Set<String> fields) {
-    Multimap<String, IndexableField> stored =
-        ArrayListMultimap.create(fields.size(), 4);
+    ListMultimap<String, IndexableField> stored =
+        MultimapBuilder.hashKeys(fields.size()).arrayListValues(4).build();
     for (IndexableField f : doc) {
       String name = f.name();
       if (fields.contains(name)) {
@@ -421,7 +421,7 @@
     return stored;
   }
 
-  private ChangeData toChangeData(Multimap<String, IndexableField> doc,
+  private ChangeData toChangeData(ListMultimap<String, IndexableField> doc,
       Set<String> fields, String idFieldName) {
     ChangeData cd;
     // Either change or the ID field was guaranteed to be included in the call
@@ -482,7 +482,8 @@
     return cd;
   }
 
-  private void decodePatchSets(Multimap<String, IndexableField> doc, ChangeData cd) {
+  private void decodePatchSets(ListMultimap<String, IndexableField> doc,
+      ChangeData cd) {
     List<PatchSet> patchSets =
         decodeProtos(doc, PATCH_SET_FIELD, PatchSetProtoField.CODEC);
     if (!patchSets.isEmpty()) {
@@ -492,12 +493,14 @@
     }
   }
 
-  private void decodeApprovals(Multimap<String, IndexableField> doc, ChangeData cd) {
+  private void decodeApprovals(ListMultimap<String, IndexableField> doc,
+      ChangeData cd) {
     cd.setCurrentApprovals(
         decodeProtos(doc, APPROVAL_FIELD, PatchSetApprovalProtoField.CODEC));
   }
 
-  private void decodeChangedLines(Multimap<String, IndexableField> doc, ChangeData cd) {
+  private void decodeChangedLines(ListMultimap<String, IndexableField> doc,
+      ChangeData cd) {
     IndexableField added = Iterables.getFirst(doc.get(ADDED_FIELD), null);
     IndexableField deleted = Iterables.getFirst(doc.get(DELETED_FIELD), null);
     if (added != null && deleted != null) {
@@ -513,7 +516,8 @@
     }
   }
 
-  private void decodeMergeable(Multimap<String, IndexableField> doc, ChangeData cd) {
+  private void decodeMergeable(ListMultimap<String, IndexableField> doc,
+      ChangeData cd) {
     IndexableField f = Iterables.getFirst(doc.get(MERGEABLE_FIELD), null);
     if (f != null) {
       String mergeable = f.stringValue();
@@ -525,7 +529,8 @@
     }
   }
 
-  private void decodeReviewedBy(Multimap<String, IndexableField> doc, ChangeData cd) {
+  private void decodeReviewedBy(ListMultimap<String, IndexableField> doc,
+      ChangeData cd) {
     Collection<IndexableField> reviewedBy = doc.get(REVIEWEDBY_FIELD);
     if (reviewedBy.size() > 0) {
       Set<Account.Id> accounts =
@@ -541,7 +546,8 @@
     }
   }
 
-  private void decodeHashtags(Multimap<String, IndexableField> doc, ChangeData cd) {
+  private void decodeHashtags(ListMultimap<String, IndexableField> doc,
+      ChangeData cd) {
     Collection<IndexableField> hashtag = doc.get(HASHTAG_FIELD);
     Set<String> hashtags = Sets.newHashSetWithExpectedSize(hashtag.size());
     for (IndexableField r : hashtag) {
@@ -550,9 +556,11 @@
     cd.setHashtags(hashtags);
   }
 
-  private void decodeStar(Multimap<String, IndexableField> doc, ChangeData cd) {
+  private void decodeStar(ListMultimap<String, IndexableField> doc,
+      ChangeData cd) {
     Collection<IndexableField> star = doc.get(STAR_FIELD);
-    Multimap<Account.Id, String> stars = ArrayListMultimap.create();
+    ListMultimap<Account.Id, String> stars =
+        MultimapBuilder.hashKeys().arrayListValues().build();
     for (IndexableField r : star) {
       StarredChangesUtil.StarField starField =
           StarredChangesUtil.StarField.parse(r.stringValue());
@@ -563,7 +571,7 @@
     cd.setStars(stars);
   }
 
-  private void decodeReviewers(Multimap<String, IndexableField> doc,
+  private void decodeReviewers(ListMultimap<String, IndexableField> doc,
       ChangeData cd) {
     cd.setReviewers(
         ChangeField.parseReviewerFieldValues(
@@ -571,7 +579,7 @@
                 .transform(IndexableField::stringValue)));
   }
 
-  private void decodeSubmitRecords(Multimap<String, IndexableField> doc,
+  private void decodeSubmitRecords(ListMultimap<String, IndexableField> doc,
       String field, SubmitRuleOptions opts, ChangeData cd) {
     ChangeField.parseSubmitRecords(
         Collections2.transform(
@@ -579,17 +587,18 @@
         opts, cd);
   }
 
-  private void decodeRefStates(Multimap<String, IndexableField> doc,
+  private void decodeRefStates(ListMultimap<String, IndexableField> doc,
       ChangeData cd) {
     cd.setRefStates(copyAsBytes(doc.get(REF_STATE_FIELD)));
   }
 
-  private void decodeRefStatePatterns(Multimap<String, IndexableField> doc,
+  private void decodeRefStatePatterns(ListMultimap<String, IndexableField> doc,
       ChangeData cd) {
     cd.setRefStatePatterns(copyAsBytes(doc.get(REF_STATE_PATTERN_FIELD)));
   }
 
-  private static <T> List<T> decodeProtos(Multimap<String, IndexableField> doc,
+  private static <T> List<T> decodeProtos(
+      ListMultimap<String, IndexableField> doc,
       String fieldName, ProtobufCodec<T> codec) {
     Collection<IndexableField> fields = doc.get(fieldName);
     if (fields.isEmpty()) {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNoteDb.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNoteDb.java
index 755ab1b..52ce6b2 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNoteDb.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNoteDb.java
@@ -20,10 +20,10 @@
 
 import com.google.common.base.Predicates;
 import com.google.common.base.Stopwatch;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Ordering;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -153,7 +153,7 @@
     ListeningExecutorService executor = newExecutor();
     System.out.println("Rebuilding the NoteDb");
 
-    final ImmutableMultimap<Project.NameKey, Change.Id> changesByProject =
+    ImmutableListMultimap<Project.NameKey, Change.Id> changesByProject =
         getChangesByProject();
     boolean ok;
     Stopwatch sw = Stopwatch.createStarted();
@@ -241,12 +241,12 @@
     return MoreExecutors.newDirectExecutorService();
   }
 
-  private ImmutableMultimap<Project.NameKey, Change.Id> getChangesByProject()
+  private ImmutableListMultimap<Project.NameKey, Change.Id> getChangesByProject()
       throws OrmException {
     // Memorize all changes so we can close the db connection and allow
     // rebuilder threads to use the full connection pool.
-    Multimap<Project.NameKey, Change.Id> changesByProject =
-        ArrayListMultimap.create();
+    ListMultimap<Project.NameKey, Change.Id> changesByProject =
+        MultimapBuilder.hashKeys().arrayListValues().build();
     try (ReviewDb db = schemaFactory.open()) {
       if (projects.isEmpty() && !changes.isEmpty()) {
         Iterable<Change> todo = unwrapDb(db).changes()
@@ -270,12 +270,12 @@
           }
         }
       }
-      return ImmutableMultimap.copyOf(changesByProject);
+      return ImmutableListMultimap.copyOf(changesByProject);
     }
   }
 
   private boolean rebuildProject(ReviewDb db,
-      ImmutableMultimap<Project.NameKey, Change.Id> allChanges,
+      ImmutableListMultimap<Project.NameKey, Change.Id> allChanges,
       Project.NameKey project, Repository allUsersRepo)
       throws IOException, OrmException {
     checkArgument(allChanges.containsKey(project));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/audit/AuditEvent.java b/gerrit-server/src/main/java/com/google/gerrit/audit/AuditEvent.java
index 5ca04c7..3184b15 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/audit/AuditEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/audit/AuditEvent.java
@@ -17,22 +17,22 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.server.CurrentUser;
 
 public class AuditEvent {
 
   public static final String UNKNOWN_SESSION_ID = "000000000000000000000000000";
-  protected static final Multimap<String, ?> EMPTY_PARAMS =
-      MultimapBuilder.hashKeys().hashSetValues().build();
+  protected static final ListMultimap<String, ?> EMPTY_PARAMS =
+      ImmutableListMultimap.of();
 
   public final String sessionId;
   public final CurrentUser who;
   public final long when;
   public final String what;
-  public final Multimap<String, ?> params;
+  public final ListMultimap<String, ?> params;
   public final Object result;
   public final long timeAtStart;
   public final long elapsed;
@@ -59,7 +59,7 @@
    * @param result result of the event
    */
   public AuditEvent(String sessionId, CurrentUser who, String what, long when,
-      Multimap<String, ?> params, Object result) {
+      ListMultimap<String, ?> params, Object result) {
     Preconditions.checkNotNull(what, "what is a mandatory not null param !");
 
     this.sessionId = MoreObjects.firstNonNull(sessionId, UNKNOWN_SESSION_ID);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/audit/ExtendedHttpAuditEvent.java b/gerrit-server/src/main/java/com/google/gerrit/audit/ExtendedHttpAuditEvent.java
index 90cddeeb..6bd7deb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/audit/ExtendedHttpAuditEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/audit/ExtendedHttpAuditEvent.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.audit;
 
 import com.google.common.base.Preconditions;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.extensions.restapi.RestResource;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.server.CurrentUser;
@@ -45,11 +45,11 @@
    * @param view view rendering object
    */
   public ExtendedHttpAuditEvent(String sessionId, CurrentUser who,
-      HttpServletRequest httpRequest, long when, Multimap<String, ?> params,
+      HttpServletRequest httpRequest, long when, ListMultimap<String, ?> params,
       Object input, int status, Object result, RestResource resource,
       RestView<RestResource> view) {
-    super(sessionId, who, httpRequest.getRequestURI(), when, params, httpRequest.getMethod(),
-        input, status, result);
+    super(sessionId, who, httpRequest.getRequestURI(), when, params,
+        httpRequest.getMethod(), input, status, result);
     this.httpRequest = Preconditions.checkNotNull(httpRequest);
     this.resource = resource;
     this.view = view;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/audit/HttpAuditEvent.java b/gerrit-server/src/main/java/com/google/gerrit/audit/HttpAuditEvent.java
index 805e050..300d760 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/audit/HttpAuditEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/audit/HttpAuditEvent.java
@@ -13,7 +13,7 @@
 // limitations under the License.
 package com.google.gerrit.audit;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.server.CurrentUser;
 
 public class HttpAuditEvent extends AuditEvent {
@@ -34,8 +34,9 @@
    * @param status HTTP status
    * @param result result of the event
    */
-  public HttpAuditEvent(String sessionId, CurrentUser who, String what, long when,
-      Multimap<String, ?> params, String httpMethod, Object input, int status, Object result) {
+  public HttpAuditEvent(String sessionId, CurrentUser who, String what,
+      long when, ListMultimap<String, ?> params, String httpMethod,
+      Object input, int status, Object result) {
     super(sessionId, who, what, when, params, result);
     this.httpMethod = httpMethod;
     this.input = input;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/audit/RpcAuditEvent.java b/gerrit-server/src/main/java/com/google/gerrit/audit/RpcAuditEvent.java
index 157b72d..cefc3a2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/audit/RpcAuditEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/audit/RpcAuditEvent.java
@@ -11,9 +11,10 @@
 // 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.audit;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.server.CurrentUser;
 
 public class RpcAuditEvent extends HttpAuditEvent {
@@ -32,8 +33,8 @@
    * @param result result of the event
    */
   public RpcAuditEvent(String sessionId, CurrentUser who, String what,
-      long when, Multimap<String, ?> params, String httpMethod, Object input,
-      int status, Object result) {
+      long when, ListMultimap<String, ?> params, String httpMethod,
+      Object input, int status, Object result) {
     super(sessionId, who, what, when, params, httpMethod, input, status, result);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/audit/SshAuditEvent.java b/gerrit-server/src/main/java/com/google/gerrit/audit/SshAuditEvent.java
index 58864c8..6823de3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/audit/SshAuditEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/audit/SshAuditEvent.java
@@ -11,15 +11,16 @@
 // 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.audit;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.server.CurrentUser;
 
 public class SshAuditEvent extends AuditEvent {
 
   public SshAuditEvent(String sessionId, CurrentUser who, String what,
-      long when, Multimap<String, ?> params, Object result) {
+      long when, ListMultimap<String, ?> params, Object result) {
     super(sessionId, who, what, when, params, result);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/PredicateClassLoader.java b/gerrit-server/src/main/java/com/google/gerrit/rules/PredicateClassLoader.java
index eb2d264..52a9363 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/PredicateClassLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/PredicateClassLoader.java
@@ -11,10 +11,11 @@
 // 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.rules;
 
 import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.SetMultimap;
 import com.google.gerrit.extensions.registration.DynamicSet;
 
 import java.util.Collection;
@@ -24,7 +25,7 @@
  */
 public class PredicateClassLoader extends ClassLoader {
 
-  private final Multimap<String, ClassLoader> packageClassLoaderMap =
+  private final SetMultimap<String, ClassLoader> packageClassLoaderMap =
       LinkedHashMultimap.create();
 
   public PredicateClassLoader(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
index 5de21b6..4451331 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -23,8 +23,8 @@
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Joiner;
 import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.primitives.Ints;
@@ -314,7 +314,7 @@
     }
   }
 
-  public ImmutableMultimap<Account.Id, String> byChangeFromIndex(
+  public ImmutableListMultimap<Account.Id, String> byChangeFromIndex(
       Change.Id changeId) throws OrmException {
     Set<String> fields = ImmutableSet.of(
         ChangeField.ID.getName(),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/ExternalIdCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/ExternalIdCacheImpl.java
index 22fc67f..8585ffd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/ExternalIdCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/ExternalIdCacheImpl.java
@@ -17,7 +17,7 @@
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
@@ -76,7 +76,7 @@
   public void onCreate(Iterable<AccountExternalId> extIds) {
     lock.lock();
     try {
-      Multimap<Account.Id, AccountExternalId> n = MultimapBuilder.hashKeys()
+      ListMultimap<Account.Id, AccountExternalId> n = MultimapBuilder.hashKeys()
           .arrayListValues().build(extIdsByAccount.get(AllKey.ALL));
       for (AccountExternalId extId : extIds) {
         n.put(extId.getAccountId(), extId);
@@ -93,7 +93,7 @@
   public void onRemove(Iterable<AccountExternalId> extIds) {
     lock.lock();
     try {
-      Multimap<Account.Id, AccountExternalId> n = MultimapBuilder.hashKeys()
+      ListMultimap<Account.Id, AccountExternalId> n = MultimapBuilder.hashKeys()
           .arrayListValues().build(extIdsByAccount.get(AllKey.ALL));
       for (AccountExternalId extId : extIds) {
         n.remove(extId.getAccountId(), extId);
@@ -111,7 +111,7 @@
       Iterable<AccountExternalId.Key> extIdKeys) {
     lock.lock();
     try {
-      Multimap<Account.Id, AccountExternalId> n = MultimapBuilder.hashKeys()
+      ListMultimap<Account.Id, AccountExternalId> n = MultimapBuilder.hashKeys()
           .arrayListValues().build(extIdsByAccount.get(AllKey.ALL));
       for (AccountExternalId extId : byAccount(accountId)) {
         for (AccountExternalId.Key extIdKey : extIdKeys) {
@@ -133,7 +133,7 @@
   public void onUpdate(AccountExternalId updatedExtId) {
     lock.lock();
     try {
-      Multimap<Account.Id, AccountExternalId> n = MultimapBuilder.hashKeys()
+      ListMultimap<Account.Id, AccountExternalId> n = MultimapBuilder.hashKeys()
           .arrayListValues().build(extIdsByAccount.get(AllKey.ALL));
       for (AccountExternalId extId : byAccount(updatedExtId.getAccountId())) {
         if (updatedExtId.getKey().equals(extId.getKey())) {
@@ -181,7 +181,7 @@
     public ImmutableSetMultimap<Account.Id, AccountExternalId> load(AllKey key)
         throws Exception {
       try (ReviewDb db = schema.open()) {
-        Multimap<Account.Id, AccountExternalId> extIdsByAccount =
+        ListMultimap<Account.Id, AccountExternalId> extIdsByAccount =
             MultimapBuilder.hashKeys().arrayListValues().build();
         for (AccountExternalId extId : db.accountExternalIds().all()) {
           extIdsByAccount.put(extId.getAccountId(), extId);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/UniversalGroupBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/UniversalGroupBackend.java
index 77cee7a..86b2d7a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/UniversalGroupBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/UniversalGroupBackend.java
@@ -18,7 +18,7 @@
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
@@ -138,7 +138,7 @@
 
     @Override
     public boolean containsAnyOf(Iterable<AccountGroup.UUID> uuids) {
-      Multimap<GroupMembership, AccountGroup.UUID> lookups =
+      ListMultimap<GroupMembership, AccountGroup.UUID> lookups =
           MultimapBuilder.hashKeys().arrayListValues().build();
       for (AccountGroup.UUID uuid : uuids) {
         if (uuid == null) {
@@ -168,7 +168,7 @@
 
     @Override
     public Set<AccountGroup.UUID> intersection(Iterable<AccountGroup.UUID> uuids) {
-      Multimap<GroupMembership, AccountGroup.UUID> lookups =
+      ListMultimap<GroupMembership, AccountGroup.UUID> lookups =
           MultimapBuilder.hashKeys().arrayListValues().build();
       for (AccountGroup.UUID uuid : uuids) {
         if (uuid == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/WatchConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/WatchConfig.java
index ea53d14..6ec9b88 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/WatchConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/WatchConfig.java
@@ -25,7 +25,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
@@ -269,7 +269,7 @@
       cfg.unsetSection(PROJECT, projectName);
     }
 
-    Multimap<String, String> notifyValuesByProject =
+    ListMultimap<String, String> notifyValuesByProject =
         MultimapBuilder.hashKeys().arrayListValues().build();
     for (Map.Entry<ProjectWatchKey, Set<NotifyType>> e : projectWatches
         .entrySet()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java
index 1641a03..54c217d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.change;
 
 import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.extensions.api.changes.AbandonInput;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -95,7 +95,7 @@
 
   public Change abandon(ChangeControl control, String msgTxt,
       NotifyHandling notifyHandling,
-      Multimap<RecipientType, Account.Id> accountsToNotify)
+      ListMultimap<RecipientType, Account.Id> accountsToNotify)
           throws RestApiException, UpdateException {
     CurrentUser user = control.getUser();
     Account account = user.isIdentifiedUser()
@@ -125,7 +125,7 @@
   public void batchAbandon(Project.NameKey project, CurrentUser user,
       Collection<ChangeControl> controls, String msgTxt,
       NotifyHandling notifyHandling,
-      Multimap<RecipientType, Account.Id> accountsToNotify)
+      ListMultimap<RecipientType, Account.Id> accountsToNotify)
           throws RestApiException, UpdateException {
     if (controls.isEmpty()) {
       return;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/AbandonUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/AbandonUtil.java
index d1cc73b..566e9fa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/AbandonUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/AbandonUtil.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.server.change;
 
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.InternalUser;
 import com.google.gerrit.server.config.ChangeCleanupConfig;
@@ -78,15 +78,15 @@
               .enforceVisibility(false)
               .query(queryBuilder.parse(query))
               .entities();
-      ImmutableMultimap.Builder<Project.NameKey, ChangeControl> builder =
-          ImmutableMultimap.builder();
+      ImmutableListMultimap.Builder<Project.NameKey, ChangeControl> builder =
+          ImmutableListMultimap.builder();
       for (ChangeData cd : changesToAbandon) {
         ChangeControl control = cd.changeControl(internalUser);
         builder.put(control.getProject().getNameKey(), control);
       }
 
       int count = 0;
-      Multimap<Project.NameKey, ChangeControl> abandons = builder.build();
+      ListMultimap<Project.NameKey, ChangeControl> abandons = builder.build();
       String message = cfg.getAbandonMessage();
       for (Project.NameKey project : abandons.keySet()) {
         Collection<ChangeControl> changes =
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
index d36be88..1956643 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -21,7 +21,7 @@
 
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.FooterConstants;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
@@ -114,7 +114,7 @@
   private CommitValidators.Policy validatePolicy =
       CommitValidators.Policy.GERRIT;
   private NotifyHandling notify = NotifyHandling.ALL;
-  private Multimap<RecipientType, Account.Id> accountsToNotify =
+  private ListMultimap<RecipientType, Account.Id> accountsToNotify =
       ImmutableListMultimap.of();
   private Set<Account.Id> reviewers;
   private Set<Account.Id> extraCC;
@@ -240,7 +240,7 @@
   }
 
   public ChangeInserter setAccountsToNotify(
-      Multimap<RecipientType, Account.Id> accountsToNotify) {
+      ListMultimap<RecipientType, Account.Id> accountsToNotify) {
     this.accountsToNotify = checkNotNull(accountsToNotify);
     return this;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index ae863d3..f4d7f8a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -50,7 +50,6 @@
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
@@ -821,7 +820,7 @@
     }
 
     Set<String> labelNames = new HashSet<>();
-    Multimap<Account.Id, PatchSetApproval> current =
+    SetMultimap<Account.Id, PatchSetApproval> current =
         MultimapBuilder.hashKeys().hashSetValues().build();
     for (PatchSetApproval a : cd.currentApprovals()) {
       allUsers.add(a.getAccountId());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java
index 15d8f8c..4b8d695 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java
@@ -23,8 +23,8 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.SetMultimap;
 import com.google.gerrit.common.FooterConstants;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.TimeUtil;
@@ -126,7 +126,7 @@
   private RevWalk rw;
 
   private RevCommit tip;
-  private Multimap<ObjectId, PatchSet> patchSetsBySha;
+  private SetMultimap<ObjectId, PatchSet> patchSetsBySha;
   private PatchSet currPs;
   private RevCommit currPsCommit;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java
index 9994085..efa853a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.server.CommentsUtil.COMMENT_ORDER;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
@@ -53,7 +53,7 @@
   interface Factory {
     EmailReviewComments create(
         NotifyHandling notify,
-        Multimap<RecipientType, Account.Id> accountsToNotify,
+        ListMultimap<RecipientType, Account.Id> accountsToNotify,
         ChangeNotes notes,
         PatchSet patchSet,
         IdentifiedUser user,
@@ -70,7 +70,7 @@
   private final ThreadLocalRequestContext requestContext;
 
   private final NotifyHandling notify;
-  private final Multimap<RecipientType, Account.Id> accountsToNotify;
+  private final ListMultimap<RecipientType, Account.Id> accountsToNotify;
   private final ChangeNotes notes;
   private final PatchSet patchSet;
   private final IdentifiedUser user;
@@ -88,7 +88,7 @@
       SchemaFactory<ReviewDb> schemaFactory,
       ThreadLocalRequestContext requestContext,
       @Assisted NotifyHandling notify,
-      @Assisted Multimap<RecipientType, Account.Id> accountsToNotify,
+      @Assisted ListMultimap<RecipientType, Account.Id> accountsToNotify,
       @Assisted ChangeNotes notes,
       @Assisted PatchSet patchSet,
       @Assisted IdentifiedUser user,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedIn.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedIn.java
index d0ba624..c7f1886 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedIn.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedIn.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.change;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.extensions.config.ExternalIncludedIn;
 import com.google.gerrit.extensions.registration.DynamicSet;
@@ -81,10 +81,10 @@
       }
 
       IncludedInResolver.Result d = IncludedInResolver.resolve(r, rw, rev);
-      Multimap<String, String> external =
+      ListMultimap<String, String> external =
           MultimapBuilder.hashKeys().arrayListValues().build();
       for (ExternalIncludedIn ext : includedIn) {
-        Multimap<String, String> extIncludedIns = ext.getIncludedIn(
+        ListMultimap<String, String> extIncludedIns = ext.getIncludedIn(
             project.get(), rev.name(), d.getTags(), d.getBranches());
         if (extIncludedIns != null) {
           external.putAll(extIncludedIns);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedInResolver.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedInResolver.java
index 0c3ecd9..e6db8e0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedInResolver.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedInResolver.java
@@ -15,8 +15,8 @@
 package com.google.gerrit.server.change;
 
 import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
@@ -76,7 +76,7 @@
   private final RevCommit target;
 
   private final RevFlag containsTarget;
-  private Multimap<RevCommit, String> commitToRef;
+  private ListMultimap<RevCommit, String> commitToRef;
   private List<RevCommit> tipsByCommitTime;
 
   private IncludedInResolver(Repository repo, RevWalk rw, RevCommit target,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/NotifyUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/NotifyUtil.java
index 2bab427..e5633cd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/NotifyUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/NotifyUtil.java
@@ -16,9 +16,9 @@
 
 import static java.util.stream.Collectors.joining;
 
-import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.NotifyInfo;
@@ -77,19 +77,19 @@
     return notifyInfo.accounts == null || notifyInfo.accounts.isEmpty();
   }
 
-  public Multimap<RecipientType, Account.Id> resolveAccounts(
+  public ListMultimap<RecipientType, Account.Id> resolveAccounts(
       @Nullable Map<RecipientType, NotifyInfo> notifyDetails)
           throws OrmException, BadRequestException {
     if (isNullOrEmpty(notifyDetails)) {
       return ImmutableListMultimap.of();
     }
 
-    Multimap<RecipientType, Account.Id> m = null;
+    ListMultimap<RecipientType, Account.Id> m = null;
     for (Entry<RecipientType, NotifyInfo> e : notifyDetails.entrySet()) {
       List<String> accounts = e.getValue().accounts;
       if (accounts != null) {
         if (m == null) {
-          m = ArrayListMultimap.create();
+          m = MultimapBuilder.hashKeys().arrayListValues().build();
         }
         m.putAll(e.getKey(), find(dbProvider.get(), accounts));
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
index 6b94232..c9b4df2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -20,7 +20,7 @@
 import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
 
 import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -100,7 +100,7 @@
   private List<String> groups = Collections.emptyList();
   private boolean fireRevisionCreated = true;
   private NotifyHandling notify = NotifyHandling.ALL;
-  private Multimap<RecipientType, Account.Id> accountsToNotify =
+  private ListMultimap<RecipientType, Account.Id> accountsToNotify =
       ImmutableListMultimap.of();
   private boolean allowClosed;
   private boolean copyApprovals = true;
@@ -185,7 +185,7 @@
   }
 
   public PatchSetInserter setAccountsToNotify(
-      Multimap<RecipientType, Account.Id> accountsToNotify) {
+      ListMultimap<RecipientType, Account.Id> accountsToNotify) {
     this.accountsToNotify = checkNotNull(accountsToNotify);
     return this;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index 9e54180..aaea82c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -27,9 +27,9 @@
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.Ordering;
 import com.google.common.hash.HashCode;
 import com.google.common.hash.Hashing;
@@ -207,7 +207,7 @@
       input.notify = NotifyHandling.NONE;
     }
 
-    Multimap<RecipientType, Account.Id> accountsToNotify =
+    ListMultimap<RecipientType, Account.Id> accountsToNotify =
         notifyUtil.resolveAccounts(input.notifyDetails);
 
     Map<String, AddReviewerResult> reviewerJsonResults = null;
@@ -304,7 +304,7 @@
 
   private void emailReviewers(Change change,
       List<PostReviewers.Addition> reviewerAdditions, NotifyHandling notify,
-      Multimap<RecipientType, Account.Id> accountsToNotify) {
+      ListMultimap<RecipientType, Account.Id> accountsToNotify) {
     List<Account.Id> to = new ArrayList<>();
     List<Account.Id> cc = new ArrayList<>();
     for (PostReviewers.Addition addition : reviewerAdditions) {
@@ -671,7 +671,7 @@
   private class Op extends BatchUpdate.Op {
     private final PatchSet.Id psId;
     private final ReviewInput in;
-    private final Multimap<RecipientType, Account.Id> accountsToNotify;
+    private final ListMultimap<RecipientType, Account.Id> accountsToNotify;
     private final List<PostReviewers.Addition> reviewerResults;
 
     private IdentifiedUser user;
@@ -684,7 +684,7 @@
     private Map<String, Short> oldApprovals = new HashMap<>();
 
     private Op(PatchSet.Id psId, ReviewInput in,
-        Multimap<RecipientType, Account.Id> accountsToNotify,
+        ListMultimap<RecipientType, Account.Id> accountsToNotify,
         List<PostReviewers.Addition> reviewerResults) {
       this.psId = psId;
       this.in = in;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
index a17447f..116f84e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
@@ -21,8 +21,8 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.common.errors.NoSuchGroupException;
@@ -201,7 +201,7 @@
 
   private Addition putAccount(String reviewer, ReviewerResource rsrc,
       ReviewerState state, NotifyHandling notify,
-      Multimap<RecipientType, Account.Id> accountsToNotify)
+      ListMultimap<RecipientType, Account.Id> accountsToNotify)
           throws UnprocessableEntityException {
     Account member = rsrc.getReviewerUser().getAccount();
     ChangeControl control = rsrc.getReviewerControl();
@@ -303,7 +303,7 @@
     protected Addition(String reviewer, ChangeResource rsrc,
         Map<Account.Id, ChangeControl> reviewers, ReviewerState state,
         NotifyHandling notify,
-        Multimap<RecipientType, Account.Id> accountsToNotify) {
+        ListMultimap<RecipientType, Account.Id> accountsToNotify) {
       result = new AddReviewerResult(reviewer);
       if (reviewers == null) {
         this.reviewers = ImmutableMap.of();
@@ -342,7 +342,7 @@
     final Map<Account.Id, ChangeControl> reviewers;
     final ReviewerState state;
     final NotifyHandling notify;
-    final Multimap<RecipientType, Account.Id> accountsToNotify;
+    final ListMultimap<RecipientType, Account.Id> accountsToNotify;
     List<PatchSetApproval> addedReviewers;
     Collection<Account.Id> addedCCs;
 
@@ -351,7 +351,7 @@
 
     Op(ChangeResource rsrc, Map<Account.Id, ChangeControl> reviewers,
         ReviewerState state, NotifyHandling notify,
-        Multimap<RecipientType, Account.Id> accountsToNotify) {
+        ListMultimap<RecipientType, Account.Id> accountsToNotify) {
       this.rsrc = rsrc;
       this.reviewers = reviewers;
       this.state = state;
@@ -407,7 +407,7 @@
 
   public void emailReviewers(Change change, Collection<Account.Id> added,
       Collection<Account.Id> copied, NotifyHandling notify,
-      Multimap<RecipientType, Account.Id> accountsToNotify) {
+      ListMultimap<RecipientType, Account.Id> accountsToNotify) {
     if (added.isEmpty() && copied.isEmpty()) {
       return;
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
index e80e758..654471f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
@@ -21,7 +21,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.data.ParameterizedString;
 import com.google.gerrit.extensions.api.changes.SubmitInput;
@@ -418,7 +418,7 @@
       mergeabilityMap.add(change);
     }
 
-    Multimap<Branch.NameKey, ChangeData> cbb = cs.changesByBranch();
+    ListMultimap<Branch.NameKey, ChangeData> cbb = cs.changesByBranch();
     for (Branch.NameKey branch : cbb.keySet()) {
       Collection<ChangeData> targetBranch = cbb.get(branch);
       HashMap<Change.Id, RevCommit> commits =
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/WalkSorter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/WalkSorter.java
index 47c32d7..e0113b2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/WalkSorter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/WalkSorter.java
@@ -20,7 +20,7 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Ordering;
 import com.google.gerrit.reviewdb.client.PatchSet;
@@ -105,7 +105,7 @@
 
   public Iterable<PatchSetData> sort(Iterable<ChangeData> in)
       throws OrmException, IOException {
-    Multimap<Project.NameKey, ChangeData> byProject =
+    ListMultimap<Project.NameKey, ChangeData> byProject =
         MultimapBuilder.hashKeys().arrayListValues().build();
     for (ChangeData cd : in) {
       byProject.put(cd.change().getProject(), cd);
@@ -126,7 +126,7 @@
     try (Repository repo = repoManager.openRepository(project);
         RevWalk rw = new RevWalk(repo)) {
       rw.setRetainBody(retainBody);
-      Multimap<RevCommit, PatchSetData> byCommit = byCommit(rw, in);
+      ListMultimap<RevCommit, PatchSetData> byCommit = byCommit(rw, in);
       if (byCommit.isEmpty()) {
         return ImmutableList.of();
       } else if (byCommit.size() == 1) {
@@ -151,8 +151,8 @@
       // the input size is small enough that this is not an issue.)
 
       Set<RevCommit> commits = byCommit.keySet();
-      Multimap<RevCommit, RevCommit> children = collectChildren(commits);
-      Multimap<RevCommit, RevCommit> pending =
+      ListMultimap<RevCommit, RevCommit> children = collectChildren(commits);
+      ListMultimap<RevCommit, RevCommit> pending =
           MultimapBuilder.hashKeys().arrayListValues().build();
       Deque<RevCommit> todo = new ArrayDeque<>();
 
@@ -195,9 +195,9 @@
     }
   }
 
-  private static Multimap<RevCommit, RevCommit> collectChildren(
+  private static ListMultimap<RevCommit, RevCommit> collectChildren(
       Set<RevCommit> commits) {
-    Multimap<RevCommit, RevCommit> children =
+    ListMultimap<RevCommit, RevCommit> children =
         MultimapBuilder.hashKeys().arrayListValues().build();
     for (RevCommit c : commits) {
       for (RevCommit p : c.getParents()) {
@@ -209,8 +209,9 @@
     return children;
   }
 
-  private static int emit(RevCommit c, Multimap<RevCommit, PatchSetData> byCommit,
-      List<PatchSetData> result, RevFlag done) {
+  private static int emit(RevCommit c,
+      ListMultimap<RevCommit, PatchSetData> byCommit, List<PatchSetData> result,
+      RevFlag done) {
     if (c.has(done)) {
       return 0;
     }
@@ -223,9 +224,9 @@
     return 0;
   }
 
-  private Multimap<RevCommit, PatchSetData> byCommit(RevWalk rw,
+  private ListMultimap<RevCommit, PatchSetData> byCommit(RevWalk rw,
       Collection<ChangeData> in) throws OrmException, IOException {
-    Multimap<RevCommit, PatchSetData> byCommit =
+    ListMultimap<RevCommit, PatchSetData> byCommit =
         MultimapBuilder.hashKeys(in.size()).arrayListValues(1).build();
     for (ChangeData cd : in) {
       PatchSet maxPs = null;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java
index eb6169e..e12cc24 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java
@@ -274,7 +274,11 @@
 
     try {
       cfg.load();
-    } catch (IOException | ConfigInvalidException e) {
+    } catch (ConfigInvalidException e) {
+      // This is an error in user input, don't spam logs with a stack trace.
+      log.warn(
+          "Failed to load " + pluginConfigFile.toAbsolutePath() + ": " + e);
+    } catch (IOException e) {
       log.warn("Failed to load " + pluginConfigFile.toAbsolutePath(), e);
     }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/TrackingFooters.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/TrackingFooters.java
index 672c461..ee01308 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/TrackingFooters.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/TrackingFooters.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.server.config;
 
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
 
 import org.eclipse.jgit.revwalk.FooterLine;
 
@@ -37,8 +37,9 @@
     return trackingFooters.isEmpty();
   }
 
-  public Multimap<String, String> extract(List<FooterLine> lines) {
-    Multimap<String, String> r = ArrayListMultimap.create();
+  public ListMultimap<String, String> extract(List<FooterLine> lines) {
+    ListMultimap<String, String> r =
+        MultimapBuilder.hashKeys().arrayListValues().build();
     if (lines == null) {
       return r;
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
index 3ed14e6..784d4d6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
@@ -16,7 +16,7 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
@@ -171,7 +171,7 @@
    * @throws RestApiException
    */
   public void publish(final ChangeEdit edit, NotifyHandling notify,
-      Multimap<RecipientType, Account.Id> accountsToNotify)
+      ListMultimap<RecipientType, Account.Id> accountsToNotify)
       throws IOException, OrmException, RestApiException, UpdateException {
     Change change = edit.getChange();
     try (Repository repo = gitManager.openRepository(change.getProject());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
index fbeb835..750a3f9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -17,8 +17,8 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static java.util.Comparator.comparing;
 
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
@@ -354,7 +354,8 @@
     return d;
   }
 
-  public void addTrackingIds(ChangeAttribute a, Multimap<String, String> set) {
+  public void addTrackingIds(ChangeAttribute a,
+      ListMultimap<String, String> set) {
     if (!set.isEmpty()) {
       a.trackingIds = new ArrayList<>(set.size());
       for (Map.Entry<String, Collection<String>> e : set.asMap().entrySet()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/AbandonOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/AbandonOp.java
index f152e6b..f4e84b2e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/AbandonOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/AbandonOp.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.git;
 
 import com.google.common.base.Strings;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
@@ -49,7 +49,7 @@
 
   private final String msgTxt;
   private final NotifyHandling notifyHandling;
-  private final Multimap<RecipientType, Account.Id> accountsToNotify;
+  private final ListMultimap<RecipientType, Account.Id> accountsToNotify;
   private final Account account;
 
   private Change change;
@@ -61,7 +61,7 @@
         @Assisted @Nullable Account account,
         @Assisted @Nullable String msgTxt,
         @Assisted NotifyHandling notifyHandling,
-        @Assisted Multimap<RecipientType, Account.Id> accountsToNotify);
+        @Assisted ListMultimap<RecipientType, Account.Id> accountsToNotify);
   }
 
   @AssistedInject
@@ -73,7 +73,7 @@
       @Assisted @Nullable Account account,
       @Assisted @Nullable String msgTxt,
       @Assisted NotifyHandling notifyHandling,
-      @Assisted Multimap<RecipientType, Account.Id> accountsToNotify) {
+      @Assisted ListMultimap<RecipientType, Account.Id> accountsToNotify) {
     this.abandonedSenderFactory = abandonedSenderFactory;
     this.cmUtil = cmUtil;
     this.psUtil = psUtil;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeSet.java
index 8e2028f..400532d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeSet.java
@@ -19,7 +19,6 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
@@ -82,7 +81,7 @@
     return changeData;
   }
 
-  public Multimap<Branch.NameKey, ChangeData> changesByBranch()
+  public ListMultimap<Branch.NameKey, ChangeData> changesByBranch()
       throws OrmException {
     ListMultimap<Branch.NameKey, ChangeData> ret =
         MultimapBuilder.hashKeys().arrayListValues().build();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/EmailMerge.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/EmailMerge.java
index 6273702..1da5257 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/EmailMerge.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/EmailMerge.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.git;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
@@ -46,7 +46,7 @@
   public interface Factory {
     EmailMerge create(Project.NameKey project, Change.Id changeId,
         Account.Id submitter, NotifyHandling notifyHandling,
-        Multimap<RecipientType, Account.Id> accountsToNotify);
+        ListMultimap<RecipientType, Account.Id> accountsToNotify);
   }
 
   private final ExecutorService sendEmailsExecutor;
@@ -59,7 +59,7 @@
   private final Change.Id changeId;
   private final Account.Id submitter;
   private final NotifyHandling notifyHandling;
-  private final Multimap<RecipientType, Account.Id> accountsToNotify;
+  private final ListMultimap<RecipientType, Account.Id> accountsToNotify;
 
   private ReviewDb db;
 
@@ -73,7 +73,7 @@
       @Assisted Change.Id changeId,
       @Assisted @Nullable Account.Id submitter,
       @Assisted NotifyHandling notifyHandling,
-      @Assisted Multimap<RecipientType, Account.Id> accountsToNotify) {
+      @Assisted ListMultimap<RecipientType, Account.Id> accountsToNotify) {
     this.sendEmailsExecutor = executor;
     this.mergedSenderFactory = mergedSenderFactory;
     this.schemaFactory = schemaFactory;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupCollector.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupCollector.java
index 8ff5a43..0ac39ad 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupCollector.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupCollector.java
@@ -22,7 +22,6 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Multimaps;
 import com.google.common.collect.SetMultimap;
@@ -103,16 +102,16 @@
     List<String> lookup(PatchSet.Id psId) throws OrmException;
   }
 
-  private final Multimap<ObjectId, PatchSet.Id> patchSetsBySha;
-  private final Multimap<ObjectId, String> groups;
+  private final ListMultimap<ObjectId, PatchSet.Id> patchSetsBySha;
+  private final ListMultimap<ObjectId, String> groups;
   private final SetMultimap<String, String> groupAliases;
   private final Lookup groupLookup;
 
   private boolean done;
 
-  public static GroupCollector create(Multimap<ObjectId, Ref> changeRefsById,
-      final ReviewDb db, final PatchSetUtil psUtil,
-      final ChangeNotes.Factory notesFactory, final Project.NameKey project) {
+  public static GroupCollector create(ListMultimap<ObjectId, Ref> changeRefsById,
+      ReviewDb db, PatchSetUtil psUtil, ChangeNotes.Factory notesFactory,
+      Project.NameKey project) {
     return new GroupCollector(
         transformRefs(changeRefsById),
         new Lookup() {
@@ -128,7 +127,7 @@
   }
 
   public static GroupCollector createForSchemaUpgradeOnly(
-      Multimap<ObjectId, Ref> changeRefsById, final ReviewDb db) {
+      ListMultimap<ObjectId, Ref> changeRefsById, ReviewDb db) {
     return new GroupCollector(
         transformRefs(changeRefsById),
         new Lookup() {
@@ -141,7 +140,7 @@
   }
 
   private GroupCollector(
-      Multimap<ObjectId, PatchSet.Id> patchSetsBySha,
+      ListMultimap<ObjectId, PatchSet.Id> patchSetsBySha,
       Lookup groupLookup) {
     this.patchSetsBySha = patchSetsBySha;
     this.groupLookup = groupLookup;
@@ -149,16 +148,16 @@
     groupAliases = MultimapBuilder.hashKeys().hashSetValues().build();
   }
 
-  private static Multimap<ObjectId, PatchSet.Id> transformRefs(
-      Multimap<ObjectId, Ref> refs) {
+  private static ListMultimap<ObjectId, PatchSet.Id> transformRefs(
+      ListMultimap<ObjectId, Ref> refs) {
     return Multimaps.transformValues(
         refs, r -> PatchSet.Id.fromRef(r.getName()));
   }
 
   @VisibleForTesting
   GroupCollector(
-      Multimap<ObjectId, PatchSet.Id> patchSetsBySha,
-      final ListMultimap<PatchSet.Id, String> groupLookup) {
+      ListMultimap<ObjectId, PatchSet.Id> patchSetsBySha,
+      ListMultimap<PatchSet.Id, String> groupLookup) {
     this(
         patchSetsBySha,
         new Lookup() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index de8c5b7..60c2ed2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -21,12 +21,13 @@
 
 import com.google.auto.value.AutoValue;
 import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.TimeUtil;
@@ -109,7 +110,7 @@
     private final ImmutableMap<Change.Id, ChangeData> changes;
     private final ImmutableSetMultimap<Branch.NameKey, Change.Id> byBranch;
     private final Map<Change.Id, CodeReviewCommit> commits;
-    private final Multimap<Change.Id, String> problems;
+    private final ListMultimap<Change.Id, String> problems;
 
     private CommitStatus(ChangeSet cs) throws OrmException {
       checkArgument(!cs.furtherHiddenChanges(),
@@ -163,8 +164,8 @@
       return problems.isEmpty();
     }
 
-    public ImmutableMultimap<Change.Id, String> getProblems() {
-      return ImmutableMultimap.copyOf(problems);
+    public ImmutableListMultimap<Change.Id, String> getProblems() {
+      return ImmutableListMultimap.copyOf(problems);
     }
 
     public List<SubmitRecord> getSubmitRecords(Change.Id id) {
@@ -228,7 +229,7 @@
   private CommitStatus commits;
   private ReviewDb db;
   private SubmitInput submitInput;
-  private Multimap<RecipientType, Account.Id> accountsToNotify;
+  private ListMultimap<RecipientType, Account.Id> accountsToNotify;
   private Set<Project.NameKey> allProjects;
   private boolean dryrun;
 
@@ -452,7 +453,7 @@
     logDebug("Beginning merge attempt on {}", cs);
     Map<Branch.NameKey, BranchBatch> toSubmit = new HashMap<>();
 
-    Multimap<Branch.NameKey, ChangeData> cbb;
+    ListMultimap<Branch.NameKey, ChangeData> cbb;
     try {
       cbb = cs.changesByBranch();
     } catch (OrmException e) {
@@ -596,7 +597,7 @@
       Collection<ChangeData> submitted) throws IntegrationException {
     logDebug("Validating {} changes", submitted.size());
     List<ChangeData> toSubmit = new ArrayList<>(submitted.size());
-    Multimap<ObjectId, PatchSet.Id> revisions = getRevisions(or, submitted);
+    SetMultimap<ObjectId, PatchSet.Id> revisions = getRevisions(or, submitted);
 
     SubmitType submitType = null;
     ChangeData choseSubmitTypeFrom = null;
@@ -696,7 +697,7 @@
     return new AutoValue_MergeOp_BranchBatch(submitType, toSubmit);
   }
 
-  private Multimap<ObjectId, PatchSet.Id> getRevisions(OpenRepo or,
+  private SetMultimap<ObjectId, PatchSet.Id> getRevisions(OpenRepo or,
       Collection<ChangeData> cds) throws IntegrationException {
     try {
       List<String> refNames = new ArrayList<>(cds.size());
@@ -706,7 +707,7 @@
           refNames.add(c.currentPatchSetId().toRefName());
         }
       }
-      Multimap<ObjectId, PatchSet.Id> revisions =
+      SetMultimap<ObjectId, PatchSet.Id> revisions =
           MultimapBuilder.hashKeys(cds.size()).hashSetValues(1).build();
       for (Map.Entry<String, Ref> e : or.repo.getRefDatabase().exactRef(
           refNames.toArray(new String[refNames.size()])).entrySet()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 7bd9d84..873c017 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -44,9 +44,7 @@
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
 import com.google.common.collect.SortedSetMultimap;
 import com.google.gerrit.common.Nullable;
@@ -325,7 +323,7 @@
   private final Set<ObjectId> validCommits = new HashSet<>();
 
   private ListMultimap<Change.Id, Ref> refsByChange;
-  private SetMultimap<ObjectId, Ref> refsById;
+  private ListMultimap<ObjectId, Ref> refsById;
   private Map<String, Ref> allRefs;
 
   private final SubmoduleOp.Factory subOpFactory;
@@ -1325,8 +1323,8 @@
       return new MailRecipients(reviewer, cc);
     }
 
-    Multimap<RecipientType, Account.Id> getAccountsToNotify() {
-      Multimap<RecipientType, Account.Id> accountsToNotify =
+    ListMultimap<RecipientType, Account.Id> getAccountsToNotify() {
+      ListMultimap<RecipientType, Account.Id> accountsToNotify =
           MultimapBuilder.hashKeys().arrayListValues().build();
       accountsToNotify.putAll(RecipientType.TO, tos);
       accountsToNotify.putAll(RecipientType.CC, ccs);
@@ -1673,8 +1671,9 @@
     logDebug("Finding new and replaced changes");
     newChanges = new ArrayList<>();
 
-    SetMultimap<ObjectId, Ref> existing = changeRefsById();
-    GroupCollector groupCollector = GroupCollector.create(changeRefsById(), db, psUtil,
+    ListMultimap<ObjectId, Ref> existing = changeRefsById();
+    GroupCollector groupCollector = GroupCollector.create(
+        changeRefsById(), db, psUtil,
         notesFactory, project.getNameKey());
 
     try {
@@ -2527,7 +2526,7 @@
   private void initChangeRefMaps() {
     if (refsByChange == null) {
       int estRefsPerChange = 4;
-      refsById = MultimapBuilder.hashKeys().hashSetValues().build();
+      refsById = MultimapBuilder.hashKeys().arrayListValues().build();
       refsByChange =
           MultimapBuilder.hashKeys(allRefs.size() / estRefsPerChange)
               .arrayListValues(estRefsPerChange)
@@ -2550,7 +2549,7 @@
     return refsByChange;
   }
 
-  private SetMultimap<ObjectId, Ref> changeRefsById() {
+  private ListMultimap<ObjectId, Ref> changeRefsById() {
     initChangeRefMaps();
     return refsById;
   }
@@ -2630,7 +2629,7 @@
       if (!(parsedObject instanceof RevCommit)) {
         return;
       }
-      SetMultimap<ObjectId, Ref> existing = changeRefsById();
+      ListMultimap<ObjectId, Ref> existing = changeRefsById();
       walk.markStart((RevCommit)parsedObject);
       markHeadsAsUninteresting(walk, cmd.getRefName());
       int i = 0;
@@ -2727,7 +2726,7 @@
         rw.markUninteresting(rw.parseCommit(cmd.getOldId()));
       }
 
-      SetMultimap<ObjectId, Ref> byCommit = changeRefsById();
+      ListMultimap<ObjectId, Ref> byCommit = changeRefsById();
       Map<Change.Key, ChangeNotes> byKey = null;
       List<ReplaceRequest> replaceAndClose = new ArrayList<>();
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java
index 5fbd80e..bd53ff5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.git;
 
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.SetMultimap;
 import com.google.gerrit.common.data.SubscribeSection;
@@ -115,7 +114,7 @@
   // sorted version of affectedBranches
   private final ImmutableSet<Branch.NameKey> sortedBranches;
   // map of superproject branch and its submodule subscriptions
-  private final Multimap<Branch.NameKey, SubmoduleSubscription> targets;
+  private final SetMultimap<Branch.NameKey, SubmoduleSubscription> targets;
   // map of superproject and its branches which has submodule subscriptions
   private final SetMultimap<Project.NameKey, Branch.NameKey> branchesByProject;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
index 053b8f1..2d63fd6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
@@ -463,8 +463,14 @@
       try {
         rc.fromText(text);
       } catch (ConfigInvalidException err) {
-        throw new ConfigInvalidException("Invalid config file " + fileName
-            + " in commit " + revision.name(), err);
+        StringBuilder msg = new StringBuilder("Invalid config file ")
+            .append(fileName)
+            .append(" in commit ")
+            .append(revision.name());
+        if (err.getCause() != null) {
+          msg.append(": ").append(err.getCause());
+        }
+        throw new ConfigInvalidException(msg.toString(), err);
       }
     }
     return rc;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java
index c77cb05..e0e2ae7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java
@@ -16,7 +16,7 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Sets;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
@@ -99,7 +99,7 @@
           Set<RevCommit> alreadyAccepted,
           RequestId submissionId,
           NotifyHandling notifyHandling,
-          Multimap<RecipientType, Account.Id> accountsToNotify,
+          ListMultimap<RecipientType, Account.Id> accountsToNotify,
           SubmoduleOp submoduleOp,
           boolean dryrun);
     }
@@ -133,7 +133,7 @@
     final RequestId submissionId;
     final SubmitType submitType;
     final NotifyHandling notifyHandling;
-    final Multimap<RecipientType, Account.Id> accountsToNotify;
+    final ListMultimap<RecipientType, Account.Id> accountsToNotify;
     final SubmoduleOp submoduleOp;
 
     final ProjectState project;
@@ -172,7 +172,7 @@
         @Assisted RequestId submissionId,
         @Assisted SubmitType submitType,
         @Assisted NotifyHandling notifyHandling,
-        @Assisted Multimap<RecipientType, Account.Id> accountsToNotify,
+        @Assisted ListMultimap<RecipientType, Account.Id> accountsToNotify,
         @Assisted SubmoduleOp submoduleOp,
         @Assisted boolean dryrun) {
       this.accountCache = accountCache;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java
index 4591c9e..2e246392 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.git.strategy;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.extensions.client.SubmitType;
@@ -59,7 +59,7 @@
       Branch.NameKey destBranch, IdentifiedUser caller, MergeTip mergeTip,
       CommitStatus commits, RequestId submissionId,
       NotifyHandling notifyHandling,
-      Multimap<RecipientType, Account.Id> accountsToNotify,
+      ListMultimap<RecipientType, Account.Id> accountsToNotify,
       SubmoduleOp submoduleOp, boolean dryrun) throws IntegrationException {
     SubmitStrategy.Arguments args = argsFactory.create(submitType, destBranch,
         commits, rw, caller, mergeTip, inserter, repo, canMergeFlag, db,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupsCollection.java
index 17d3366..72c29d0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupsCollection.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.group;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.common.data.GroupDescriptions;
 import com.google.gerrit.common.data.GroupReference;
@@ -70,7 +70,7 @@
   }
 
   @Override
-  public void setParams(Multimap<String, String> params)
+  public void setParams(ListMultimap<String, String> params)
       throws BadRequestException {
     if (params.containsKey("query") && params.containsKey("query2")) {
       throw new BadRequestException(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/AllChangesIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
index 9ab6506..080aa5b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
@@ -23,8 +23,8 @@
 import com.google.common.base.Stopwatch;
 import com.google.common.collect.ComparisonChain;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -217,7 +217,7 @@
     return new Callable<Void>() {
       @Override
       public Void call() throws Exception {
-        Multimap<ObjectId, ChangeData> byId =
+        ListMultimap<ObjectId, ChangeData> byId =
             MultimapBuilder.hashKeys().arrayListValues().build();
         // TODO(dborowitz): Opening all repositories in a live server may be
         // wasteful; see if we can determine which ones it is safe to close
@@ -260,7 +260,7 @@
     private final ChangeIndexer indexer;
     private final ThreeWayMergeStrategy mergeStrategy;
     private final AutoMerger autoMerger;
-    private final Multimap<ObjectId, ChangeData> byId;
+    private final ListMultimap<ObjectId, ChangeData> byId;
     private final ProgressMonitor done;
     private final ProgressMonitor failed;
     private final PrintWriter verboseWriter;
@@ -269,7 +269,7 @@
     private ProjectIndexer(ChangeIndexer indexer,
         ThreeWayMergeStrategy mergeStrategy,
         AutoMerger autoMerger,
-        Multimap<ObjectId, ChangeData> changesByCommitId,
+        ListMultimap<ObjectId, ChangeData> changesByCommitId,
         Repository repo,
         ProgressMonitor done,
         ProgressMonitor failed,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/StalenessChecker.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/StalenessChecker.java
index 011b2474..8194b6f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/StalenessChecker.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/StalenessChecker.java
@@ -24,7 +24,6 @@
 import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
@@ -110,7 +109,7 @@
       Change indexChange,
       @Nullable Change reviewDbChange,
       SetMultimap<Project.NameKey, RefState> states,
-      Multimap<Project.NameKey, RefStatePattern> patterns) {
+      ListMultimap<Project.NameKey, RefStatePattern> patterns) {
     return reviewDbChangeIsStale(indexChange, reviewDbChange)
         || refsAreStale(repoManager, id, states, patterns);
   }
@@ -119,7 +118,7 @@
   static boolean refsAreStale(GitRepositoryManager repoManager,
       Change.Id id,
       SetMultimap<Project.NameKey, RefState> states,
-      Multimap<Project.NameKey, RefStatePattern> patterns) {
+      ListMultimap<Project.NameKey, RefStatePattern> patterns) {
     Set<Project.NameKey> projects =
         Sets.union(states.keySet(), patterns.keySet());
 
@@ -172,7 +171,7 @@
     return result;
   }
 
-  private Multimap<Project.NameKey, RefStatePattern> parsePatterns(
+  private ListMultimap<Project.NameKey, RefStatePattern> parsePatterns(
       ChangeData cd) {
     return parsePatterns(cd.getRefStatePatterns());
   }
@@ -197,7 +196,7 @@
   private static boolean refsAreStale(GitRepositoryManager repoManager,
       Change.Id id, Project.NameKey project,
       SetMultimap<Project.NameKey, RefState> allStates,
-      Multimap<Project.NameKey, RefStatePattern> allPatterns) {
+      ListMultimap<Project.NameKey, RefStatePattern> allPatterns) {
     try (Repository repo = repoManager.openRepository(project)) {
       Set<RefState> states = allStates.get(project);
       for (RefState state : states) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java
index 4d8c56f..c1608b3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.mail.send;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
@@ -310,7 +310,7 @@
       // BCC anyone who has starred this change
       // and remove anyone who has ignored this change.
       //
-      Multimap<Account.Id, String> stars =
+      ListMultimap<Account.Id, String> stars =
           args.starredChangesUtil.byChangeFromIndex(change.getId());
       for (Map.Entry<Account.Id, Collection<String>> e :
           stars.asMap().entrySet()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index 9f25897..b8358c5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -20,7 +20,7 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
@@ -76,7 +76,7 @@
   private Address smtpFromAddress;
   private StringBuilder textBody;
   private StringBuilder htmlBody;
-  private Multimap<RecipientType, Account.Id> accountsToNotify =
+  private ListMultimap<RecipientType, Account.Id> accountsToNotify =
       ImmutableListMultimap.of();
   protected VelocityContext velocityContext;
   protected Map<String, Object> soyContext;
@@ -101,7 +101,7 @@
   }
 
   public void setAccountsToNotify(
-      Multimap<RecipientType, Account.Id> accountsToNotify) {
+      ListMultimap<RecipientType, Account.Id> accountsToNotify) {
     this.accountsToNotify = checkNotNull(accountsToNotify);
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeBundle.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeBundle.java
index 29b6f2b..a107bda 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeBundle.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeBundle.java
@@ -37,9 +37,9 @@
 import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.Ordering;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
@@ -562,7 +562,7 @@
     // but easy to reason about.
     List<ChangeMessage> as = new LinkedList<>(bundleA.filterChangeMessages());
 
-    Multimap<ChangeMessageCandidate, ChangeMessage> bs =
+    ListMultimap<ChangeMessageCandidate, ChangeMessage> bs =
         LinkedListMultimap.create();
     for (ChangeMessage b : bundleB.filterChangeMessages()) {
       bs.put(ChangeMessageCandidate.create(b), b);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
index 607fdf1..d47e462 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
@@ -21,7 +21,7 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.primitives.Ints;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
@@ -525,7 +525,7 @@
    *     side.
    * @param out output stream to write to.
    */
-  void buildNote(Multimap<Integer, Comment> comments,
+  void buildNote(ListMultimap<Integer, Comment> comments,
       OutputStream out) {
     if (comments.isEmpty()) {
       return;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index 719b51c..07de733 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -40,8 +40,8 @@
 import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Table;
@@ -130,7 +130,7 @@
   private final List<Account.Id> allPastReviewers;
   private final List<ReviewerStatusUpdate> reviewerUpdates;
   private final List<SubmitRecord> submitRecords;
-  private final Multimap<RevId, Comment> comments;
+  private final ListMultimap<RevId, Comment> comments;
   private final Map<PatchSet.Id, PatchSet> patchSets;
   private final Set<PatchSet.Id> deletedPatchSets;
   private final Map<PatchSet.Id, PatchSetState> patchSetStates;
@@ -138,7 +138,8 @@
   private final Map<ApprovalKey, PatchSetApproval> approvals;
   private final List<PatchSetApproval> bufferedApprovals;
   private final List<ChangeMessage> allChangeMessages;
-  private final Multimap<PatchSet.Id, ChangeMessage> changeMessagesByPatchSet;
+  private final ListMultimap<PatchSet.Id, ChangeMessage>
+      changeMessagesByPatchSet;
 
   // Non-final private members filled in during the parsing process.
   private String branch;
@@ -248,8 +249,8 @@
     return null;
   }
 
-  private Multimap<PatchSet.Id, PatchSetApproval> buildApprovals() {
-    Multimap<PatchSet.Id, PatchSetApproval> result =
+  private ListMultimap<PatchSet.Id, PatchSetApproval> buildApprovals() {
+    ListMultimap<PatchSet.Id, PatchSetApproval> result =
         MultimapBuilder.hashKeys().arrayListValues().build();
     for (PatchSetApproval a : approvals.values()) {
       if (!patchSets.containsKey(a.getPatchSetId())) {
@@ -283,7 +284,7 @@
     return Lists.reverse(allChangeMessages);
   }
 
-  private Multimap<PatchSet.Id, ChangeMessage> buildMessagesByPatchSet() {
+  private ListMultimap<PatchSet.Id, ChangeMessage> buildMessagesByPatchSet() {
     for (Collection<ChangeMessage> v :
         changeMessagesByPatchSet.asMap().values()) {
       Collections.reverse((List<ChangeMessage>) v);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesState.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesState.java
index 337bf93..6c15c7e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesState.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesState.java
@@ -22,7 +22,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.reviewdb.client.Account;
@@ -95,14 +95,14 @@
       @Nullable Set<Account.Id> pastAssignees,
       @Nullable Set<String> hashtags,
       Map<PatchSet.Id, PatchSet> patchSets,
-      Multimap<PatchSet.Id, PatchSetApproval> approvals,
+      ListMultimap<PatchSet.Id, PatchSetApproval> approvals,
       ReviewerSet reviewers,
       List<Account.Id> allPastReviewers,
       List<ReviewerStatusUpdate> reviewerUpdates,
       List<SubmitRecord> submitRecords,
       List<ChangeMessage> allChangeMessages,
-      Multimap<PatchSet.Id, ChangeMessage> changeMessagesByPatchSet,
-      Multimap<RevId, Comment> publishedComments) {
+      ListMultimap<PatchSet.Id, ChangeMessage> changeMessagesByPatchSet,
+      ListMultimap<RevId, Comment> publishedComments) {
     if (hashtags == null) {
       hashtags = ImmutableSet.of();
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
index f2015be..4feac2e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
@@ -21,7 +21,7 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.metrics.Timer1;
@@ -167,7 +167,7 @@
     revisionNoteMap = RevisionNoteMap.parse(
         args.noteUtil, getChangeId(), reader, NoteMap.read(reader, tipCommit),
         PatchLineComment.Status.DRAFT);
-    Multimap<RevId, Comment> cs =
+    ListMultimap<RevId, Comment> cs =
         MultimapBuilder.hashKeys().arrayListValues().build();
     for (ChangeRevisionNote rn : revisionNoteMap.revisionNotes.values()) {
       for (Comment c : rn.getComments()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
index 69fcc02..46a0802 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
@@ -18,8 +18,8 @@
 import static com.google.gerrit.server.CommentsUtil.COMMENT_ORDER;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.RevId;
@@ -112,8 +112,8 @@
     this.pushCert = pushCert;
   }
 
-  private Multimap<Integer, Comment> buildCommentMap() {
-    Multimap<Integer, Comment> all =
+  private ListMultimap<Integer, Comment> buildCommentMap() {
+    ListMultimap<Integer, Comment> all =
         MultimapBuilder.hashKeys().arrayListValues().build();
 
     for (Comment c : baseComments) {
@@ -131,7 +131,7 @@
 
   private void buildNoteJson(ChangeNoteUtil noteUtil, OutputStream out)
       throws IOException {
-    Multimap<Integer, Comment> comments = buildCommentMap();
+    ListMultimap<Integer, Comment> comments = buildCommentMap();
     if (comments.isEmpty() && pushCert == null) {
       return;
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RobotCommentNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
index c89bf33..ae2edd6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.notedb;
 
 import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.reviewdb.client.Change;
@@ -95,7 +95,7 @@
     ObjectReader reader = handle.walk().getObjectReader();
     revisionNoteMap = RevisionNoteMap.parseRobotComments(args.noteUtil, reader,
         NoteMap.read(reader, tipCommit));
-    Multimap<RevId, RobotComment> cs =
+    ListMultimap<RevId, RobotComment> cs =
         MultimapBuilder.hashKeys().arrayListValues().build();
     for (RobotCommentsRevisionNote rn :
         revisionNoteMap.revisionNotes.values()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
index 18d357c..ffd11a7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
@@ -25,9 +25,9 @@
 import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Ordering;
 import com.google.common.collect.Sets;
@@ -299,7 +299,7 @@
     // We will rebuild all events, except for draft comments, in buckets based
     // on author and timestamp.
     List<Event> events = new ArrayList<>();
-    Multimap<Account.Id, DraftCommentEvent> draftCommentEvents =
+    ListMultimap<Account.Id, DraftCommentEvent> draftCommentEvents =
         MultimapBuilder.hashKeys().arrayListValues().build();
 
     events.addAll(getHashtagsEvents(change, manager));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/AutoRegisterModules.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/AutoRegisterModules.java
index 438add6..5d7e139 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/AutoRegisterModules.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/AutoRegisterModules.java
@@ -19,7 +19,7 @@
 import static com.google.gerrit.server.plugins.PluginGuiceEnvironment.is;
 
 import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.extensions.annotations.Export;
 import com.google.gerrit.extensions.annotations.ExtensionPoint;
 import com.google.gerrit.extensions.annotations.Listen;
@@ -54,7 +54,7 @@
   private final ModuleGenerator httpGen;
 
   private Set<Class<?>> sysSingletons;
-  private Multimap<TypeLiteral<?>, Class<?>> sysListen;
+  private ListMultimap<TypeLiteral<?>, Class<?>> sysListen;
   private String initJs;
 
   Module sysModule;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java
index db564cc..81e7433 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java
@@ -19,9 +19,9 @@
 
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
 
 import org.eclipse.jgit.util.IO;
@@ -67,7 +67,7 @@
       String pluginName, Iterable<Class<? extends Annotation>> annotations)
       throws InvalidPluginException {
     Set<String> descriptors = new HashSet<>();
-    Multimap<String, JarScanner.ClassData> rawMap =
+    ListMultimap<String, JarScanner.ClassData> rawMap =
         MultimapBuilder.hashKeys().arrayListValues().build();
     Map<Class<? extends Annotation>, String> classObjToClassDescr =
         new HashMap<>();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
index 5667003..5826c58 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
@@ -24,8 +24,8 @@
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.Ordering;
+import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
 import com.google.common.io.ByteStreams;
 import com.google.gerrit.extensions.annotations.PluginName;
@@ -399,7 +399,7 @@
   }
 
   public synchronized void rescan() {
-    Multimap<String, Path> pluginsFiles = prunePlugins(pluginsDir);
+    SetMultimap<String, Path> pluginsFiles = prunePlugins(pluginsDir);
     if (pluginsFiles.isEmpty()) {
       return;
     }
@@ -478,7 +478,7 @@
     return sortedPlugins;
   }
 
-  private void syncDisabledPlugins(Multimap<String, Path> jars) {
+  private void syncDisabledPlugins(SetMultimap<String, Path> jars) {
     stopRemovedPlugins(jars);
     dropRemovedDisabledPlugins(jars);
   }
@@ -525,7 +525,7 @@
     }
   }
 
-  private void stopRemovedPlugins(Multimap<String, Path> jars) {
+  private void stopRemovedPlugins(SetMultimap<String, Path> jars) {
     Set<String> unload = Sets.newHashSet(running.keySet());
     for (Map.Entry<String, Collection<Path>> entry : jars.asMap().entrySet()) {
       for (Path path : entry.getValue()) {
@@ -539,7 +539,7 @@
     }
   }
 
-  private void dropRemovedDisabledPlugins(Multimap<String, Path> jars) {
+  private void dropRemovedDisabledPlugins(SetMultimap<String, Path> jars) {
     Set<String> unload = Sets.newHashSet(disabled.keySet());
     for (Map.Entry<String, Collection<Path>> entry : jars.asMap().entrySet()) {
       for (Path path : entry.getValue()) {
@@ -644,7 +644,7 @@
   // Only one active plugin per plugin name can exist for each plugin name.
   // Filter out disabled plugins and transform the multimap to a map
   private static Map<String, Path> filterDisabled(
-      Multimap<String, Path> pluginPaths) {
+      SetMultimap<String, Path> pluginPaths) {
     Map<String, Path> activePlugins = Maps.newHashMapWithExpectedSize(
         pluginPaths.keys().size());
     for (String name : pluginPaths.keys()) {
@@ -667,9 +667,9 @@
   //
   // NOTE: Bear in mind that the plugin name can be reassigned after load by the
   //       Server plugin provider.
-  public Multimap<String, Path> prunePlugins(Path pluginsDir) {
+  public SetMultimap<String, Path> prunePlugins(Path pluginsDir) {
     List<Path> pluginPaths = scanPathsInPluginsDirectory(pluginsDir);
-    Multimap<String, Path> map;
+    SetMultimap<String, Path> map;
     map = asMultimap(pluginPaths);
     for (String plugin : map.keySet()) {
       Collection<Path> files = map.asMap().get(plugin);
@@ -735,8 +735,8 @@
     return null;
   }
 
-  private Multimap<String, Path> asMultimap(List<Path> plugins) {
-    Multimap<String, Path> map = LinkedHashMultimap.create();
+  private SetMultimap<String, Path> asMultimap(List<Path> plugins) {
+    SetMultimap<String, Path> map = LinkedHashMultimap.create();
     for (Path srcPath : plugins) {
       map.put(getPluginName(srcPath), srcPath);
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java
index f0ea487..bb1b20e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java
@@ -18,9 +18,9 @@
 import static com.google.gerrit.server.project.RefPattern.isRE;
 
 import com.google.auto.value.AutoValue;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.AccessSection;
@@ -118,7 +118,7 @@
       HashMap<String, List<PermissionRule>> permissions = new HashMap<>();
       HashMap<String, List<PermissionRule>> overridden = new HashMap<>();
       Map<PermissionRule, ProjectRef> ruleProps = Maps.newIdentityHashMap();
-      Multimap<Project.NameKey, String> exclusivePermissionsByProject =
+      ListMultimap<Project.NameKey, String> exclusivePermissionsByProject =
           MultimapBuilder.hashKeys().arrayListValues().build();
       for (AccessSection section : sections) {
         Project.NameKey project = sectionToProject.get(section);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index 24f66f8..98f6cb5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -24,12 +24,10 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
 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;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.common.data.SubmitTypeRecord;
@@ -352,7 +350,7 @@
   private Map<Account.Id, Ref> draftsByUser;
   @Deprecated
   private Set<Account.Id> starredByUser;
-  private ImmutableMultimap<Account.Id, String> stars;
+  private ImmutableListMultimap<Account.Id, String> stars;
   private ImmutableMap<Account.Id, StarRef> starRefs;
   private ReviewerSet reviewers;
   private List<ReviewerStatusUpdate> reviewerUpdates;
@@ -1230,13 +1228,13 @@
     this.starredByUser = starredByUser;
   }
 
-  public ImmutableMultimap<Account.Id, String> stars() throws OrmException {
+  public ImmutableListMultimap<Account.Id, String> stars() throws OrmException {
     if (stars == null) {
       if (!lazyLoad) {
-        return ImmutableMultimap.of();
+        return ImmutableListMultimap.of();
       }
-      ImmutableMultimap.Builder<Account.Id, String> b =
-          ImmutableMultimap.builder();
+      ImmutableListMultimap.Builder<Account.Id, String> b =
+          ImmutableListMultimap.builder();
       for (Map.Entry<Account.Id, StarRef> e : starRefs().entrySet()) {
         b.putAll(e.getKey(), e.getValue().labels());
       }
@@ -1245,8 +1243,8 @@
     return stars;
   }
 
-  public void setStars(Multimap<Account.Id, String> stars) {
-    this.stars = ImmutableMultimap.copyOf(stars);
+  public void setStars(ListMultimap<Account.Id, String> stars) {
+    this.stars = ImmutableListMultimap.copyOf(stars);
   }
 
   public ImmutableMap<Account.Id, StarRef> starRefs() throws OrmException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_108.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_108.java
index f29aa50..d568cba 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_108.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_108.java
@@ -16,7 +16,7 @@
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
@@ -100,9 +100,9 @@
       }
     }
 
-    Multimap<ObjectId, Ref> changeRefsBySha =
+    ListMultimap<ObjectId, Ref> changeRefsBySha =
         MultimapBuilder.hashKeys().arrayListValues().build();
-    Multimap<ObjectId, PatchSet.Id> patchSetsBySha =
+    ListMultimap<ObjectId, PatchSet.Id> patchSetsBySha =
         MultimapBuilder.hashKeys().arrayListValues().build();
     for (Ref ref : refdb.getRefs(RefNames.REFS_CHANGES).values()) {
       ObjectId id = ref.getObjectId();
@@ -132,7 +132,7 @@
   }
 
   private static void updateGroups(ReviewDb db, GroupCollector collector,
-      Multimap<ObjectId, PatchSet.Id> patchSetsBySha) throws OrmException {
+      ListMultimap<ObjectId, PatchSet.Id> patchSetsBySha) throws OrmException {
     Map<PatchSet.Id, PatchSet> patchSets =
         db.patchSets().toMap(db.patchSets().get(patchSetsBySha.values()));
     for (Map.Entry<ObjectId, Collection<String>> e
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_123.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_123.java
index 1594829..95c0257 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_123.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_123.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.schema;
 
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
@@ -57,7 +57,7 @@
   @Override
   protected void migrateData(ReviewDb db, UpdateUI ui)
       throws OrmException, SQLException {
-    Multimap<Account.Id, Change.Id> imports =
+    ListMultimap<Account.Id, Change.Id> imports =
         MultimapBuilder.hashKeys().arrayListValues().build();
     try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
       ResultSet rs = stmt.executeQuery(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_124.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_124.java
index 4f4a866..3dd44cd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_124.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_124.java
@@ -17,7 +17,7 @@
 import static java.util.Comparator.comparing;
 
 import com.google.common.base.Strings;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Ordering;
 import com.google.gerrit.reviewdb.client.Account;
@@ -71,7 +71,7 @@
   @Override
   protected void migrateData(ReviewDb db, UpdateUI ui)
       throws OrmException, SQLException {
-    Multimap<Account.Id, AccountSshKey> imports =
+    ListMultimap<Account.Id, AccountSshKey> imports =
         MultimapBuilder.hashKeys().arrayListValues().build();
     try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
       ResultSet rs = stmt.executeQuery(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_139.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_139.java
index f426585..614320b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_139.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_139.java
@@ -15,8 +15,8 @@
 package com.google.gerrit.server.schema;
 
 import com.google.auto.value.AutoValue;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Project;
@@ -73,7 +73,8 @@
  @Override
  protected void migrateData(ReviewDb db, UpdateUI ui)
      throws OrmException, SQLException {
-   Multimap<Account.Id, ProjectWatch> imports = ArrayListMultimap.create();
+   ListMultimap<Account.Id, ProjectWatch> imports =
+       MultimapBuilder.hashKeys().arrayListValues().build();
    try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
        ResultSet rs = stmt.executeQuery(
          "SELECT "
@@ -183,4 +184,4 @@
      abstract ProjectWatch build();
    }
  }
-}
\ No newline at end of file
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/GroupCollectorTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/GroupCollectorTest.java
index ba0599d..ff9b81b 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/git/GroupCollectorTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/GroupCollectorTest.java
@@ -17,9 +17,8 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.SortedSetMultimap;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 
@@ -47,7 +46,7 @@
     RevCommit branchTip = tr.commit().create();
     RevCommit a = tr.commit().parent(branchTip).create();
 
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         newWalk(a, branchTip),
         patchSets(),
         groups());
@@ -62,7 +61,7 @@
     RevCommit a = tr.commit().parent(branchTip).create();
     RevCommit b = tr.commit().parent(a).create();
 
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         newWalk(b, branchTip),
         patchSets(),
         groups());
@@ -79,7 +78,7 @@
     RevCommit b = tr.commit().parent(a).create();
 
     String group = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         newWalk(b, branchTip),
         patchSets().put(a, psId(1, 1)),
         groups().put(psId(1, 1), group));
@@ -95,7 +94,7 @@
     RevCommit a = tr.commit().parent(branchTip).create();
     RevCommit b = tr.commit().parent(a).create();
 
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         newWalk(b, branchTip),
         patchSets().put(a, psId(1, 1)),
         groups());
@@ -111,7 +110,7 @@
     RevCommit b = tr.commit().parent(branchTip).create();
     RevCommit m = tr.commit().parent(a).parent(b).create();
 
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         newWalk(m, branchTip),
         patchSets(),
         groups());
@@ -129,7 +128,7 @@
     RevCommit m = tr.commit().parent(a).parent(b).create();
 
     String group = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         newWalk(m, branchTip),
         patchSets().put(b, psId(1, 1)),
         groups().put(psId(1, 1), group));
@@ -150,7 +149,7 @@
 
     String group1 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
     String group2 = "1234567812345678123456781234567812345678";
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         newWalk(m, branchTip),
         patchSets()
             .put(a, psId(1, 1))
@@ -176,7 +175,7 @@
     String group1 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
     String group2a = "1234567812345678123456781234567812345678";
     String group2b = "ef123456ef123456ef123456ef123456ef123456";
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         newWalk(m, branchTip),
         patchSets()
             .put(a, psId(1, 1))
@@ -202,7 +201,7 @@
     RevCommit m = tr.commit().parent(branchTip).parent(a).create();
 
     String group = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         newWalk(m, branchTip),
         patchSets().put(a, psId(1, 1)),
         groups().put(psId(1, 1), group));
@@ -218,7 +217,7 @@
     RevCommit a = tr.commit().parent(branchTip).create();
     RevCommit m = tr.commit().parent(branchTip).parent(a).create();
 
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         newWalk(m, branchTip),
         patchSets(),
         groups());
@@ -237,7 +236,7 @@
     RevCommit m1 = tr.commit().parent(b).parent(c).create();
     RevCommit m2 = tr.commit().parent(a).parent(m1).create();
 
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         newWalk(m2, branchTip),
         patchSets(),
         groups());
@@ -259,7 +258,7 @@
     assertThat(m.getParentCount()).isEqualTo(2);
     assertThat(m.getParent(0)).isEqualTo(m.getParent(1));
 
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         newWalk(m, branchTip),
         patchSets(),
         groups());
@@ -279,7 +278,7 @@
 
     String group1 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
     String group2 = "1234567812345678123456781234567812345678";
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         newWalk(m, branchTip),
         patchSets()
             .put(a, psId(1, 1))
@@ -307,7 +306,7 @@
     rw.markStart(rw.parseCommit(d));
     // Schema upgrade case: all commits are existing patch sets, but none have
     // groups assigned yet.
-    Multimap<ObjectId, String> groups = collectGroups(
+    SortedSetMultimap<ObjectId, String> groups = collectGroups(
         rw,
         patchSets()
             .put(branchTip, psId(1, 1))
@@ -339,9 +338,9 @@
     return rw;
   }
 
-  private static Multimap<ObjectId, String> collectGroups(
+  private static SortedSetMultimap<ObjectId, String> collectGroups(
       RevWalk rw,
-      ImmutableMultimap.Builder<ObjectId, PatchSet.Id> patchSetsBySha,
+      ImmutableListMultimap.Builder<ObjectId, PatchSet.Id> patchSetsBySha,
       ImmutableListMultimap.Builder<PatchSet.Id, String> groupLookup)
       throws Exception {
     GroupCollector gc =
@@ -355,8 +354,9 @@
 
   // Helper methods for constructing various map arguments, to avoid lots of
   // type specifications.
-  private static ImmutableMultimap.Builder<ObjectId, PatchSet.Id> patchSets() {
-    return ImmutableMultimap.builder();
+  private static ImmutableListMultimap.Builder<ObjectId, PatchSet.Id>
+      patchSets() {
+    return ImmutableListMultimap.builder();
   }
 
   private static ImmutableListMultimap.Builder<PatchSet.Id, String> groups() {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/change/StalenessCheckerTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/change/StalenessCheckerTest.java
index 913ce93..adfd1fe 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/change/StalenessCheckerTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/change/StalenessCheckerTest.java
@@ -21,7 +21,7 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.stream.Collectors.toList;
 
-import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ImmutableSetMultimap;
 import com.google.common.collect.ListMultimap;
 import com.google.gerrit.reviewdb.client.Account;
@@ -194,7 +194,7 @@
                 ImmutableSetMultimap.of(
                     P1, RefState.create(ref1, id1.name()),
                     P2, RefState.create(ref2, id2.name())),
-                ImmutableMultimap.of()))
+                ImmutableListMultimap.of()))
         .isFalse();
 
     // Wrong ref value.
@@ -204,7 +204,7 @@
                 ImmutableSetMultimap.of(
                     P1, RefState.create(ref1, SHA1),
                     P2, RefState.create(ref2, id2.name())),
-                ImmutableMultimap.of()))
+                ImmutableListMultimap.of()))
         .isTrue();
 
     // Swapped repos.
@@ -214,7 +214,7 @@
                 ImmutableSetMultimap.of(
                     P1, RefState.create(ref1, id2.name()),
                     P2, RefState.create(ref2, id1.name())),
-                ImmutableMultimap.of()))
+                ImmutableListMultimap.of()))
         .isTrue();
 
     // Two refs in same repo, not stale.
@@ -227,7 +227,7 @@
                 ImmutableSetMultimap.of(
                     P1, RefState.create(ref1, id1.name()),
                     P1, RefState.create(ref3, id3.name())),
-                ImmutableMultimap.of()))
+                ImmutableListMultimap.of()))
         .isFalse();
 
     // Ignore ref not mentioned.
@@ -236,7 +236,7 @@
                 repoManager, C,
                 ImmutableSetMultimap.of(
                     P1, RefState.create(ref1, id1.name())),
-                ImmutableMultimap.of()))
+                ImmutableListMultimap.of()))
         .isFalse();
 
     // One ref wrong.
@@ -246,7 +246,7 @@
                 ImmutableSetMultimap.of(
                     P1, RefState.create(ref1, id1.name()),
                     P1, RefState.create(ref3, SHA1)),
-                ImmutableMultimap.of()))
+                ImmutableListMultimap.of()))
         .isTrue();
   }
 
@@ -261,7 +261,7 @@
                 repoManager, C,
                 ImmutableSetMultimap.of(
                     P1, RefState.create(ref1, id1.name())),
-                ImmutableMultimap.of(
+                ImmutableListMultimap.of(
                     P1, RefStatePattern.create("refs/heads/*"))))
         .isFalse();
 
@@ -273,7 +273,7 @@
                 repoManager, C,
                 ImmutableSetMultimap.of(
                     P1, RefState.create(ref1, id1.name())),
-                ImmutableMultimap.of(
+                ImmutableListMultimap.of(
                     P1, RefStatePattern.create("refs/heads/*"))))
         .isTrue();
     assertThat(
@@ -282,7 +282,7 @@
                 ImmutableSetMultimap.of(
                     P1, RefState.create(ref1, id1.name()),
                     P1, RefState.create(ref2, id2.name())),
-                ImmutableMultimap.of(
+                ImmutableListMultimap.of(
                     P1, RefStatePattern.create("refs/heads/*"))))
         .isFalse();
   }
@@ -299,7 +299,7 @@
                 repoManager, C,
                 ImmutableSetMultimap.of(
                     P1, RefState.create(ref1, id1.name())),
-                ImmutableMultimap.of(
+                ImmutableListMultimap.of(
                     P1, RefStatePattern.create("refs/*/foo"))))
         .isFalse();
 
@@ -311,7 +311,7 @@
                 repoManager, C,
                 ImmutableSetMultimap.of(
                     P1, RefState.create(ref1, id1.name())),
-                ImmutableMultimap.of(
+                ImmutableListMultimap.of(
                     P1, RefStatePattern.create("refs/*/foo"))))
         .isTrue();
     assertThat(
@@ -320,7 +320,7 @@
                 ImmutableSetMultimap.of(
                     P1, RefState.create(ref1, id1.name()),
                     P1, RefState.create(ref3, id3.name())),
-                ImmutableMultimap.of(
+                ImmutableListMultimap.of(
                     P1, RefStatePattern.create("refs/*/foo"))))
         .isFalse();
   }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
index 03cb41b..3d7abb0 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -26,7 +26,6 @@
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableTable;
 import com.google.common.collect.Iterables;
@@ -350,7 +349,7 @@
 
     notes = newNotes(c);
     assertThat(notes.getApprovals()).containsExactlyEntriesIn(
-        ImmutableMultimap.of(
+        ImmutableListMultimap.of(
             psa.getPatchSetId(),
             new PatchSetApproval(psa.getKey(), (short) 0, update.getWhen())));
   }
@@ -375,7 +374,7 @@
 
     notes = newNotes(c);
     assertThat(notes.getApprovals()).containsExactlyEntriesIn(
-        ImmutableMultimap.of(
+        ImmutableListMultimap.of(
             psa.getPatchSetId(),
             new PatchSetApproval(psa.getKey(), (short) 0, update.getWhen())));
 
@@ -1452,7 +1451,7 @@
 
     ChangeNotes notes = newNotes(c);
     assertThat(notes.getComments())
-        .isEqualTo(ImmutableMultimap.of(revId, comment));
+        .isEqualTo(ImmutableListMultimap.of(revId, comment));
   }
 
   @Test
@@ -1472,7 +1471,7 @@
 
     ChangeNotes notes = newNotes(c);
     assertThat(notes.getComments())
-        .isEqualTo(ImmutableMultimap.of(revId, comment));
+        .isEqualTo(ImmutableListMultimap.of(revId, comment));
   }
 
   @Test
@@ -1492,7 +1491,7 @@
 
     ChangeNotes notes = newNotes(c);
     assertThat(notes.getComments())
-        .isEqualTo(ImmutableMultimap.of(revId, comment));
+        .isEqualTo(ImmutableListMultimap.of(revId, comment));
   }
 
   @Test
@@ -1512,7 +1511,7 @@
 
     ChangeNotes notes = newNotes(c);
     assertThat(notes.getComments())
-        .isEqualTo(ImmutableMultimap.of(revId, comment));
+        .isEqualTo(ImmutableListMultimap.of(revId, comment));
   }
 
   @Test
@@ -1822,7 +1821,7 @@
       }
     }
     assertThat(notes.getComments()).isEqualTo(
-        ImmutableMultimap.of(
+        ImmutableListMultimap.of(
             revId, comment1,
             revId, comment2,
             revId, comment3));
@@ -1879,7 +1878,7 @@
       }
     }
     assertThat(notes.getComments())
-        .isEqualTo(ImmutableMultimap.of(revId, comment));
+        .isEqualTo(ImmutableListMultimap.of(revId, comment));
   }
 
   @Test
@@ -1934,7 +1933,7 @@
       }
     }
     assertThat(notes.getComments())
-        .isEqualTo(ImmutableMultimap.of(new RevId(comment.revId), comment));
+        .isEqualTo(ImmutableListMultimap.of(new RevId(comment.revId), comment));
   }
 
   @Test
@@ -1969,7 +1968,7 @@
     update.commit();
 
     assertThat(newNotes(c).getComments()).containsExactlyEntriesIn(
-        ImmutableMultimap.of(
+        ImmutableListMultimap.of(
             new RevId(rev1), commentForBase,
             new RevId(rev2), commentForPS));
   }
@@ -2004,7 +2003,7 @@
     update.commit();
 
     assertThat(newNotes(c).getComments()).containsExactlyEntriesIn(
-        ImmutableMultimap.of(
+        ImmutableListMultimap.of(
           new RevId(rev), comment1,
           new RevId(rev), comment2)).inOrder();
   }
@@ -2039,7 +2038,7 @@
     update.commit();
 
     assertThat(newNotes(c).getComments()).containsExactlyEntriesIn(
-        ImmutableMultimap.of(
+        ImmutableListMultimap.of(
           new RevId(rev), comment1,
           new RevId(rev), comment2)).inOrder();
   }
@@ -2077,7 +2076,7 @@
     update.commit();
 
     assertThat(newNotes(c).getComments()).containsExactlyEntriesIn(
-        ImmutableMultimap.of(
+        ImmutableListMultimap.of(
           new RevId(rev1), comment1,
           new RevId(rev2), comment2));
   }
@@ -2103,7 +2102,7 @@
 
     ChangeNotes notes = newNotes(c);
     assertThat(notes.getDraftComments(otherUserId)).containsExactlyEntriesIn(
-        ImmutableMultimap.of(new RevId(rev), comment1));
+        ImmutableListMultimap.of(new RevId(rev), comment1));
     assertThat(notes.getComments()).isEmpty();
 
     update = newUpdate(c, otherUser);
@@ -2114,7 +2113,7 @@
     notes = newNotes(c);
     assertThat(notes.getDraftComments(otherUserId)).isEmpty();
     assertThat(notes.getComments()).containsExactlyEntriesIn(
-        ImmutableMultimap.of(new RevId(rev), comment1));
+        ImmutableListMultimap.of(new RevId(rev), comment1));
   }
 
   @Test
@@ -2146,7 +2145,7 @@
 
     ChangeNotes notes = newNotes(c);
     assertThat(notes.getDraftComments(otherUserId)).containsExactlyEntriesIn(
-        ImmutableMultimap.of(
+        ImmutableListMultimap.of(
           new RevId(rev), comment1,
           new RevId(rev), comment2)).inOrder();
     assertThat(notes.getComments()).isEmpty();
@@ -2159,9 +2158,9 @@
 
     notes = newNotes(c);
     assertThat(notes.getDraftComments(otherUserId)).containsExactlyEntriesIn(
-        ImmutableMultimap.of(new RevId(rev), comment2));
+        ImmutableListMultimap.of(new RevId(rev), comment2));
     assertThat(notes.getComments()).containsExactlyEntriesIn(
-        ImmutableMultimap.of(new RevId(rev), comment1));
+        ImmutableListMultimap.of(new RevId(rev), comment1));
   }
 
   @Test
@@ -2194,7 +2193,7 @@
 
     ChangeNotes notes = newNotes(c);
     assertThat(notes.getDraftComments(otherUserId)).containsExactlyEntriesIn(
-        ImmutableMultimap.of(
+        ImmutableListMultimap.of(
             new RevId(rev1), baseComment,
             new RevId(rev2), psComment));
     assertThat(notes.getComments()).isEmpty();
@@ -2210,7 +2209,7 @@
     notes = newNotes(c);
     assertThat(notes.getDraftComments(otherUserId)).isEmpty();
     assertThat(notes.getComments()).containsExactlyEntriesIn(
-        ImmutableMultimap.of(
+        ImmutableListMultimap.of(
             new RevId(rev1), baseComment,
             new RevId(rev2), psComment));
   }
@@ -2373,7 +2372,7 @@
     update.commit();
 
     assertThat(newNotes(c).getComments()).containsExactlyEntriesIn(
-        ImmutableMultimap.of(new RevId(rev), comment));
+        ImmutableListMultimap.of(new RevId(rev), comment));
   }
 
   @Test
@@ -2393,7 +2392,7 @@
     update.commit();
 
     assertThat(newNotes(c).getComments()).containsExactlyEntriesIn(
-        ImmutableMultimap.of(new RevId(rev), comment));
+        ImmutableListMultimap.of(new RevId(rev), comment));
   }
 
   @Test
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshAutoRegisterModuleGenerator.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshAutoRegisterModuleGenerator.java
index 8b468a7..effed2c 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshAutoRegisterModuleGenerator.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshAutoRegisterModuleGenerator.java
@@ -18,7 +18,7 @@
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
 import com.google.gerrit.extensions.annotations.Export;
 import com.google.gerrit.server.plugins.InvalidPluginException;
 import com.google.gerrit.server.plugins.ModuleGenerator;
@@ -36,7 +36,8 @@
     extends AbstractModule
     implements ModuleGenerator {
   private final Map<String, Class<Command>> commands = new HashMap<>();
-  private final Multimap<TypeLiteral<?>, Class<?>> listeners = LinkedListMultimap.create();
+  private final ListMultimap<TypeLiteral<?>, Class<?>> listeners =
+      LinkedListMultimap.create();
   private CommandName command;
 
   @Override
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
index fed8226..74f2017 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.sshd;
 
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.audit.AuditService;
 import com.google.gerrit.audit.SshAuditEvent;
 import com.google.gerrit.common.TimeUtil;
@@ -156,14 +156,15 @@
     audit(context.get(), status, dcmd);
   }
 
-  private Multimap<String, ?> extractParameters(DispatchCommand dcmd) {
+  private ListMultimap<String, ?> extractParameters(DispatchCommand dcmd) {
     if (dcmd == null) {
-      return ArrayListMultimap.create(0, 0);
+      return MultimapBuilder.hashKeys(0).arrayListValues(0).build();
     }
     String[] cmdArgs = dcmd.getArguments();
     String paramName = null;
     int argPos = 0;
-    Multimap<String, String> parms = ArrayListMultimap.create();
+    ListMultimap<String, String> parms =
+        MultimapBuilder.hashKeys().arrayListValues().build();
     for (int i = 2; i < cmdArgs.length; i++) {
       String arg = cmdArgs[i];
       // -- stop parameters parsing
@@ -258,7 +259,8 @@
     audit(ctx, result, extractWhat(cmd), extractParameters(cmd));
   }
 
-  private void audit(Context ctx, Object result, String cmd, Multimap<String, ?> params) {
+  private void audit(Context ctx, Object result, String cmd,
+      ListMultimap<String, ?> params) {
     String sessionId;
     CurrentUser currentUser;
     long created;
diff --git a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java
index b5888b50..75f11f2 100644
--- a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java
+++ b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java
@@ -35,9 +35,9 @@
 package com.google.gerrit.util.cli;
 
 import com.google.common.base.Strings;
-import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.MultimapBuilder;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
@@ -224,7 +224,8 @@
 
   public void parseOptionMap(Map<String, String[]> parameters)
       throws CmdLineException {
-    Multimap<String, String> map = LinkedHashMultimap.create();
+    ListMultimap<String, String> map =
+        MultimapBuilder.hashKeys().arrayListValues().build();
     for (Map.Entry<String, String[]> ent : parameters.entrySet()) {
       for (String val : ent.getValue()) {
         map.put(ent.getKey(), val);
@@ -233,7 +234,7 @@
     parseOptionMap(map);
   }
 
-  public void parseOptionMap(Multimap<String, String> params)
+  public void parseOptionMap(ListMultimap<String, String> params)
       throws CmdLineException {
     List<String> tmp = Lists.newArrayListWithCapacity(2 * params.size());
     for (final String key : params.keySet()) {
diff --git a/plugins/cookbook-plugin b/plugins/cookbook-plugin
index 8607421..da18513 160000
--- a/plugins/cookbook-plugin
+++ b/plugins/cookbook-plugin
@@ -1 +1 @@
-Subproject commit 860742125749aec5444bfa44bb727153c2649893
+Subproject commit da185134efc5f1f45f7a0cfd4cb68110312fd216
diff --git a/plugins/replication b/plugins/replication
index 0ed3b13..66f08f2 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 0ed3b13df0b4f88c67ece722e56e554f8f38e83a
+Subproject commit 66f08f2460ba89b6827f4e456be1c1a948a718aa
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 8917a46..4b3549b 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -391,7 +391,7 @@
         on-iron-overlay-opened="_handleReplyOverlayOpen"
         with-backdrop>
       <gr-reply-dialog id="replyDialog"
-          change="[[_change]]"
+          change="{{_change}}"
           patch-num="[[_computeLatestPatchNum(_allPatchSets)]]"
           permitted-labels="[[_change.permitted_labels]]"
           diff-drafts="[[_diffDrafts]]"
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
index bdc6d6c..9cee168 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
@@ -168,7 +168,8 @@
           <div class="peopleListLabel">Reviewers</div>
           <gr-account-list
               id="reviewers"
-              accounts="[[_reviewers]]"
+              accounts="{{_reviewers}}"
+              removable-values="[[change.removable_reviewers]]"
               change="[[change]]"
               filter="[[filterReviewerSuggestion]]"
               pending-confirmation="{{_reviewerPendingConfirmation}}"
@@ -180,7 +181,7 @@
             <div class="peopleListLabel">CC</div>
             <gr-account-list
                 id="ccs"
-                accounts="[[_ccs]]"
+                accounts="{{_ccs}}"
                 change="[[change]]"
                 filter="[[filterReviewerSuggestion]]"
                 pending-confirmation="{{_ccPendingConfirmation}}"
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
index aa4041b..26b8b73 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
@@ -23,6 +23,11 @@
     REVIEWERS: 'reviewers',
   };
 
+  var ReviewerTypes = {
+    REVIEWER: 'REVIEWER',
+    CC: 'CC',
+  };
+
   Polymer({
     is: 'gr-reply-dialog',
 
@@ -95,6 +100,13 @@
         value: false,
         observer: '_handleHeightChanged',
       },
+      _reviewersPendingRemove: {
+        type: Object,
+        value: {
+          CC: [],
+          REVIEWER: [],
+        },
+      },
     },
 
     FocusTarget: FocusTarget,
@@ -105,6 +117,8 @@
 
     observers: [
       '_changeUpdated(change.reviewers.*, change.owner, serverConfig)',
+      '_ccsChanged(_ccs.splices)',
+      '_reviewersChanged(_reviewers.splices)',
     ],
 
     attached: function() {
@@ -144,6 +158,76 @@
       selectorEl.selectIndex(selectorEl.indexOf(item));
     },
 
+    _ccsChanged: function(splices) {
+      if (splices && splices.indexSplices) {
+        this._processReviewerChange(splices.indexSplices, ReviewerTypes.CC);
+      }
+    },
+
+    _reviewersChanged: function(splices) {
+      if (splices && splices.indexSplices) {
+        this._processReviewerChange(splices.indexSplices,
+            ReviewerTypes.REVIEWER);
+      }
+    },
+
+    _processReviewerChange: function(indexSplices, type) {
+      indexSplices.forEach(function(splice) {
+        splice.removed.forEach(function(account) {
+          if (!this._reviewersPendingRemove[type]) {
+            console.err('Invalid type ' + type + ' for reviewer.');
+            return;
+          }
+          this._reviewersPendingRemove[type].push(account);
+        }.bind(this));
+      }.bind(this));
+    },
+
+    /**
+     * Resets the state of the _reviewersPendingRemove object, and removes
+     * accounts if necessary.
+     *
+     * @param {Boolean} isCancel true if the action is a cancel.
+     */
+    _purgeReviewersPendingRemove: function(isCancel) {
+      var reviewerArr;
+      for (var type in this._reviewersPendingRemove) {
+        if (this._reviewersPendingRemove.hasOwnProperty(type)) {
+          if (!isCancel) {
+            reviewerArr = this._reviewersPendingRemove[type];
+            for (var i = 0; i < reviewerArr.length; i++) {
+              this._removeAccount(reviewerArr[i], type);
+            }
+          }
+          this._reviewersPendingRemove[type] = [];
+        }
+      }
+    },
+
+    /**
+     * Removes an account from the change, both on the backend and the client.
+     * Does nothing if the account is a pending addition.
+     *
+     * @param {Object} account
+     * @param {ReviewerTypes} type
+     */
+    _removeAccount: function(account, type) {
+      if (account._pendingAdd) { return; }
+
+      return this.$.restAPI.removeChangeReviewer(this.change._number,
+          account._account_id).then(function(response) {
+        if (!response.ok) { return response; }
+
+        var reviewers = this.change.reviewers[type] || [];
+        for (var i = 0; i < reviewers.length; i++) {
+          if (reviewers[i]._account_id == account._account_id) {
+            this.splice(['change', 'reviewers', type], i, 1);
+            break;
+          }
+        }
+      }.bind(this));
+    },
+
     _mapReviewer: function(reviewer) {
       var reviewerId;
       var confirmed;
@@ -161,6 +245,7 @@
         drafts: 'PUBLISH_ALL_REVISIONS',
         labels: {},
       };
+
       for (var label in this.permittedLabels) {
         if (!this.permittedLabels.hasOwnProperty(label)) { continue; }
 
@@ -341,17 +426,21 @@
     },
 
     _changeUpdated: function(changeRecord, owner, serverConfig) {
+      this._rebuildReviewerArrays(changeRecord.base, owner, serverConfig);
+    },
+
+    _rebuildReviewerArrays: function(change, owner, serverConfig) {
       this._owner = owner;
 
       var reviewers = [];
       var ccs = [];
 
-      for (var key in changeRecord.base) {
+      for (var key in change) {
         if (key !== 'REVIEWER' && key !== 'CC') {
           console.warn('unexpected reviewer state:', key);
           continue;
         }
-        changeRecord.base[key].forEach(function(entry) {
+        change[key].forEach(function(entry) {
           if (entry._account_id === owner._account_id) {
             return;
           }
@@ -409,11 +498,16 @@
     _cancelTapHandler: function(e) {
       e.preventDefault();
       this.fire('cancel', null, {bubbles: false});
+      this._purgeReviewersPendingRemove(true);
+      this._rebuildReviewerArrays(this.change.reviewers, this._owner,
+          this.serverConfig);
     },
 
     _sendTapHandler: function(e) {
       e.preventDefault();
-      this.send();
+      this.send().then(function() {
+        this._purgeReviewersPendingRemove();
+      }.bind(this));
     },
 
     _saveReview: function(review, opt_errFn) {
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
index 23a6c86..f7058bd 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
@@ -41,6 +41,10 @@
     var setDraftCommentStub;
     var eraseDraftCommentStub;
 
+    var lastId = 0;
+    var makeAccount = function() { return {_account_id: lastId++}; };
+    var makeGroup = function() { return {id: lastId++}; };
+
     setup(function() {
       sandbox = sinon.sandbox.create();
 
@@ -381,14 +385,6 @@
     });
 
     test('filterReviewerSuggestion', function() {
-      var counter = 0;
-      function makeAccount() {
-        return {_account_id: counter++};
-      }
-      function makeGroup() {
-        return {id: counter++};
-      }
-
       var owner = makeAccount();
       var reviewer1 = makeAccount();
       var reviewer2 = makeGroup();
@@ -476,7 +472,7 @@
             ' 0': 'No score',
             '+1': 'Verified'
           },
-          default_value: 0
+          default_value: 0,
         },
         'Code-Review': {
           values: {
@@ -486,8 +482,8 @@
             '+1': 'Looks good to me, but someone else must approve',
             '+2': 'Looks good to me, approved'
           },
-          default_value: 0
-        }
+          default_value: 0,
+        },
       };
 
       flushAsynchronousOperations();
@@ -510,5 +506,57 @@
       verifiedBtn._handleHideTooltip();
       assert.isNotOk(verifiedBtn._tooltip);
     });
+
+    test('_processReviewerChange', function() {
+      var mockIndexSplices = function(toRemove) {
+        return [{
+          removed: [toRemove],
+        }];
+      };
+
+      element._processReviewerChange(
+          mockIndexSplices(makeAccount()), 'REVIEWER');
+      assert.equal(element._reviewersPendingRemove.REVIEWER.length, 1);
+    });
+
+    test('_purgeReviewersPendingRemove', function() {
+      var removeStub = sandbox.stub(element, '_removeAccount');
+      var mock = function() {
+        element._reviewersPendingRemove = {
+          test: [makeAccount()],
+          test2: [makeAccount(), makeAccount()],
+        };
+      };
+      var checkObjEmpty = function(obj) {
+        for (var prop in obj) {
+          if (obj.hasOwnProperty(prop) && obj[prop].length) { return false; }
+        }
+        return true;
+      };
+      mock();
+      element._purgeReviewersPendingRemove(true); // Cancel
+      assert.isFalse(removeStub.called);
+      assert.isTrue(checkObjEmpty(element._reviewersPendingRemove));
+
+      mock();
+      element._purgeReviewersPendingRemove(false); // Submit
+      assert.isTrue(removeStub.called);
+      assert.isTrue(checkObjEmpty(element._reviewersPendingRemove));
+    });
+
+    test('_removeAccount', function(done) {
+      sandbox.stub(element.$.restAPI, 'removeChangeReviewer')
+          .returns(Promise.resolve({ok: true}));
+      var arr = [makeAccount(), makeAccount()];
+      element.change.reviewers = {
+        REVIEWER: arr.slice(),
+      };
+
+      element._removeAccount(arr[1], 'REVIEWER').then(function() {
+        assert.equal(element.change.reviewers.REVIEWER.length, 1);
+        assert.deepEqual(element.change.reviewers.REVIEWER, arr.slice(0, 1));
+        done();
+      });
+    });
   });
 </script>