Extract the keys for the (Git)ModifiedFilesCaches to separate classes

Change-Id: If8ea827f82ae369eb744fe8eb60e88f528762179
diff --git a/java/com/google/gerrit/server/patch/diff/ModifiedFilesCache.java b/java/com/google/gerrit/server/patch/diff/ModifiedFilesCache.java
index 9fc8150..bcae238 100644
--- a/java/com/google/gerrit/server/patch/diff/ModifiedFilesCache.java
+++ b/java/com/google/gerrit/server/patch/diff/ModifiedFilesCache.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.server.patch.DiffNotAvailableException;
 import com.google.gerrit.server.patch.gitdiff.GitModifiedFilesCache;
 import com.google.gerrit.server.patch.gitdiff.GitModifiedFilesCacheImpl;
+import com.google.gerrit.server.patch.gitdiff.GitModifiedFilesCacheKey;
 import com.google.gerrit.server.patch.gitdiff.ModifiedFile;
 
 /**
@@ -26,10 +27,10 @@
  * <p>The loader uses the underlying {@link GitModifiedFilesCacheImpl} to retrieve the git modified
  * files.
  *
- * <p>If the {@link ModifiedFilesCacheImpl.Key#aCommit()} is equal to {@link
+ * <p>If the {@link ModifiedFilesCacheKey#aCommit()} is equal to {@link
  * org.eclipse.jgit.lib.Constants#EMPTY_TREE_ID}, the diff will be evaluated against the empty tree,
  * and the result will be exactly the same as the caller can get from {@link
- * GitModifiedFilesCache#get(GitModifiedFilesCacheImpl.Key)}
+ * GitModifiedFilesCache#get(GitModifiedFilesCacheKey)}
  */
 public interface ModifiedFilesCache {
 
@@ -40,5 +41,5 @@
    * @throws DiffNotAvailableException the supplied commits IDs of the key do no exist, are not IDs
    *     of a commit, or an exception occurred while reading a pack file.
    */
-  ImmutableList<ModifiedFile> get(ModifiedFilesCacheImpl.Key key) throws DiffNotAvailableException;
+  ImmutableList<ModifiedFile> get(ModifiedFilesCacheKey key) throws DiffNotAvailableException;
 }
diff --git a/java/com/google/gerrit/server/patch/diff/ModifiedFilesCacheImpl.java b/java/com/google/gerrit/server/patch/diff/ModifiedFilesCacheImpl.java
index a87277c..6023c0e 100644
--- a/java/com/google/gerrit/server/patch/diff/ModifiedFilesCacheImpl.java
+++ b/java/com/google/gerrit/server/patch/diff/ModifiedFilesCacheImpl.java
@@ -17,7 +17,6 @@
 import static com.google.common.collect.ImmutableList.toImmutableList;
 import static org.eclipse.jgit.lib.Constants.EMPTY_TREE_ID;
 
-import com.google.auto.value.AutoValue;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableList;
@@ -25,23 +24,19 @@
 import com.google.common.collect.Sets;
 import com.google.common.collect.Streams;
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.entities.Project;
-import com.google.gerrit.entities.Project.NameKey;
 import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.serialize.CacheSerializer;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.patch.DiffNotAvailableException;
 import com.google.gerrit.server.patch.DiffUtil;
-import com.google.gerrit.server.patch.diff.ModifiedFilesCacheImpl.Key.Serializer;
 import com.google.gerrit.server.patch.gitdiff.GitModifiedFilesCache;
 import com.google.gerrit.server.patch.gitdiff.GitModifiedFilesCacheImpl;
+import com.google.gerrit.server.patch.gitdiff.GitModifiedFilesCacheKey;
 import com.google.gerrit.server.patch.gitdiff.ModifiedFile;
 import com.google.inject.Inject;
 import com.google.inject.Module;
 import com.google.inject.TypeLiteral;
 import com.google.inject.name.Named;
 import java.io.IOException;
-import java.io.Serializable;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Stream;
@@ -56,16 +51,17 @@
  * <p>The loader of this cache wraps a {@link GitModifiedFilesCache} to retrieve the git modified
  * files.
  *
- * <p>If the {@link Key#aCommit()} is equal to {@link org.eclipse.jgit.lib.Constants#EMPTY_TREE_ID},
- * the diff will be evaluated against the empty tree, and the result will be exactly the same as the
- * caller can get from {@link GitModifiedFilesCache#get(GitModifiedFilesCacheImpl.Key)}
+ * <p>If the {@link ModifiedFilesCacheKey#aCommit()} is equal to {@link
+ * org.eclipse.jgit.lib.Constants#EMPTY_TREE_ID}, the diff will be evaluated against the empty tree,
+ * and the result will be exactly the same as the caller can get from {@link
+ * GitModifiedFilesCache#get(GitModifiedFilesCacheKey)}
  */
 public class ModifiedFilesCacheImpl implements ModifiedFilesCache {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   private static final String MODIFIED_FILES = "modified_files";
 
-  private final LoadingCache<Key, ImmutableList<ModifiedFile>> cache;
+  private final LoadingCache<ModifiedFilesCacheKey, ImmutableList<ModifiedFile>> cache;
 
   public static Module module() {
     return new CacheModule() {
@@ -80,9 +76,9 @@
         // in the cache documentation link.
         persist(
                 ModifiedFilesCacheImpl.MODIFIED_FILES,
-                Key.class,
+                ModifiedFilesCacheKey.class,
                 new TypeLiteral<ImmutableList<ModifiedFile>>() {})
-            .keySerializer(Serializer.INSTANCE)
+            .keySerializer(ModifiedFilesCacheKey.Serializer.INSTANCE)
             .valueSerializer(GitModifiedFilesCacheImpl.ValueSerializer.INSTANCE)
             .maximumWeight(10 << 20)
             .weigher(ModifiedFilesWeigher.class)
@@ -95,12 +91,13 @@
   @Inject
   public ModifiedFilesCacheImpl(
       @Named(ModifiedFilesCacheImpl.MODIFIED_FILES)
-          LoadingCache<Key, ImmutableList<ModifiedFile>> cache) {
+          LoadingCache<ModifiedFilesCacheKey, ImmutableList<ModifiedFile>> cache) {
     this.cache = cache;
   }
 
   @Override
-  public ImmutableList<ModifiedFile> get(Key key) throws DiffNotAvailableException {
+  public ImmutableList<ModifiedFile> get(ModifiedFilesCacheKey key)
+      throws DiffNotAvailableException {
     try {
       return cache.get(key);
     } catch (Exception e) {
@@ -108,7 +105,8 @@
     }
   }
 
-  static class ModifiedFilesLoader extends CacheLoader<Key, ImmutableList<ModifiedFile>> {
+  static class ModifiedFilesLoader
+      extends CacheLoader<ModifiedFilesCacheKey, ImmutableList<ModifiedFile>> {
     private final GitModifiedFilesCache gitCache;
     private final GitRepositoryManager repoManager;
 
@@ -119,22 +117,23 @@
     }
 
     @Override
-    public ImmutableList<ModifiedFile> load(Key key) throws IOException, DiffNotAvailableException {
+    public ImmutableList<ModifiedFile> load(ModifiedFilesCacheKey key)
+        throws IOException, DiffNotAvailableException {
       try (Repository repo = repoManager.openRepository(key.project());
           RevWalk rw = new RevWalk(repo.newObjectReader())) {
         return loadModifiedFiles(key, rw);
       }
     }
 
-    private ImmutableList<ModifiedFile> loadModifiedFiles(Key key, RevWalk rw)
+    private ImmutableList<ModifiedFile> loadModifiedFiles(ModifiedFilesCacheKey key, RevWalk rw)
         throws IOException, DiffNotAvailableException {
       ObjectId aTree =
           key.aCommit().equals(EMPTY_TREE_ID)
               ? key.aCommit()
               : DiffUtil.getTreeId(rw, key.aCommit());
       ObjectId bTree = DiffUtil.getTreeId(rw, key.bCommit());
-      GitModifiedFilesCacheImpl.Key gitKey =
-          GitModifiedFilesCacheImpl.Key.builder()
+      GitModifiedFilesCacheKey gitKey =
+          GitModifiedFilesCacheKey.builder()
               .project(key.project())
               .aTree(aTree)
               .bTree(bTree)
@@ -161,21 +160,22 @@
      * Returns the paths of files that were modified between the old and new commits versus their
      * parents (i.e. old commit vs. its parent, and new commit vs. its parent).
      *
-     * @param key the {@link Key} representing the commits we are diffing
+     * @param key the {@link ModifiedFilesCacheKey} representing the commits we are diffing
      * @param rw a {@link RevWalk} for the repository
      * @return The list of modified files between the old/new commits and their parents
      */
     private Set<String> getTouchedFilesWithParents(
-        Key key, ObjectId parentOfA, ObjectId parentOfB, RevWalk rw) throws IOException {
+        ModifiedFilesCacheKey key, ObjectId parentOfA, ObjectId parentOfB, RevWalk rw)
+        throws IOException {
       try {
         // TODO(ghareeb): as an enhancement: the 3 calls of the underlying git cache can be combined
-        GitModifiedFilesCacheImpl.Key oldVsBaseKey =
-            GitModifiedFilesCacheImpl.Key.create(
+        GitModifiedFilesCacheKey oldVsBaseKey =
+            GitModifiedFilesCacheKey.create(
                 key.project(), parentOfA, key.aCommit(), key.renameScore(), rw);
         List<ModifiedFile> oldVsBase = gitCache.get(oldVsBaseKey);
 
-        GitModifiedFilesCacheImpl.Key newVsBaseKey =
-            GitModifiedFilesCacheImpl.Key.create(
+        GitModifiedFilesCacheKey newVsBaseKey =
+            GitModifiedFilesCacheKey.create(
                 key.project(), parentOfB, key.bCommit(), key.renameScore(), rw);
         List<ModifiedFile> newVsBase = gitCache.get(newVsBaseKey);
 
@@ -203,85 +203,4 @@
       return touchedFilePaths.contains(oldFilePath) || touchedFilePaths.contains(newFilePath);
     }
   }
-
-  @AutoValue
-  public abstract static class Key implements Serializable {
-    public abstract Project.NameKey project();
-
-    /** @return the old commit ID used in the git tree diff */
-    public abstract ObjectId aCommit();
-
-    /** @return the new commit ID used in the git tree diff */
-    public abstract ObjectId bCommit();
-
-    /**
-     * Percentage score used to identify a file as a "rename". A special value of -1 means that the
-     * computation will ignore renames and rename detection will be disabled.
-     */
-    public abstract int renameScore();
-
-    public boolean renameDetectionEnabled() {
-      return renameScore() != -1;
-    }
-
-    public int weight() {
-      return project().get().length() + 20 * 2 + 4;
-    }
-
-    public static Builder builder() {
-      return new AutoValue_ModifiedFilesCacheImpl_Key.Builder();
-    }
-
-    @AutoValue.Builder
-    public abstract static class Builder {
-
-      public abstract Builder project(NameKey value);
-
-      public abstract Builder aCommit(ObjectId value);
-
-      public abstract Builder bCommit(ObjectId value);
-
-      public Builder disableRenameDetection() {
-        renameScore(-1);
-        return this;
-      }
-
-      public abstract Builder renameScore(int value);
-
-      public abstract Key build();
-    }
-
-    public enum Serializer implements CacheSerializer<Key> {
-      INSTANCE;
-
-      @Override
-      public byte[] serialize(Key key) {
-        // We are reusing the serializer of the GitModifiedFilesCacheImpl#Key since both classes
-        // contain exactly the same fields, with the difference that the Object Ids here refer
-        // to the commit SHA-1s instead of the tree SHA-1s, but they are still can be serialized
-        // and deserialized in the same way.
-        GitModifiedFilesCacheImpl.Key gitKey =
-            GitModifiedFilesCacheImpl.Key.builder()
-                .project(key.project())
-                .aTree(key.aCommit())
-                .bTree(key.bCommit())
-                .renameScore(key.renameScore())
-                .build();
-
-        return GitModifiedFilesCacheImpl.Key.KeySerializer.INSTANCE.serialize(gitKey);
-      }
-
-      @Override
-      public Key deserialize(byte[] in) {
-        GitModifiedFilesCacheImpl.Key gitKey =
-            GitModifiedFilesCacheImpl.Key.KeySerializer.INSTANCE.deserialize(in);
-        return Key.builder()
-            .project(gitKey.project())
-            .aCommit(gitKey.aTree())
-            .bCommit(gitKey.bTree())
-            .renameScore(gitKey.renameScore())
-            .build();
-      }
-    }
-  }
 }
diff --git a/java/com/google/gerrit/server/patch/diff/ModifiedFilesCacheKey.java b/java/com/google/gerrit/server/patch/diff/ModifiedFilesCacheKey.java
new file mode 100644
index 0000000..5aa31ec
--- /dev/null
+++ b/java/com/google/gerrit/server/patch/diff/ModifiedFilesCacheKey.java
@@ -0,0 +1,116 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.patch.diff;
+
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.Project.NameKey;
+import com.google.gerrit.proto.Protos;
+import com.google.gerrit.server.cache.proto.Cache.ModifiedFilesKeyProto;
+import com.google.gerrit.server.cache.serialize.CacheSerializer;
+import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
+import org.eclipse.jgit.lib.ObjectId;
+
+/** Cache key for the {@link com.google.gerrit.server.patch.diff.ModifiedFilesCache} */
+@AutoValue
+public abstract class ModifiedFilesCacheKey {
+
+  /** A specific git project / repository. */
+  public abstract Project.NameKey project();
+
+  /** @return the old commit ID used in the git tree diff */
+  public abstract ObjectId aCommit();
+
+  /** @return the new commit ID used in the git tree diff */
+  public abstract ObjectId bCommit();
+
+  /**
+   * Percentage score used to identify a file as a "rename". A special value of -1 means that the
+   * computation will ignore renames and rename detection will be disabled.
+   */
+  public abstract int renameScore();
+
+  public boolean renameDetectionEnabled() {
+    return renameScore() != -1;
+  }
+
+  /** Returns the size of the object in bytes */
+  public int weight() {
+    return stringSize(project().get()) // project
+        + 20 * 2 // aCommit and bCommit
+        + 4; // renameScore
+  }
+
+  public static ModifiedFilesCacheKey.Builder builder() {
+    return new AutoValue_ModifiedFilesCacheKey.Builder();
+  }
+
+  @AutoValue.Builder
+  public abstract static class Builder {
+
+    public abstract ModifiedFilesCacheKey.Builder project(NameKey value);
+
+    public abstract ModifiedFilesCacheKey.Builder aCommit(ObjectId value);
+
+    public abstract ModifiedFilesCacheKey.Builder bCommit(ObjectId value);
+
+    public ModifiedFilesCacheKey.Builder disableRenameDetection() {
+      renameScore(-1);
+      return this;
+    }
+
+    public abstract ModifiedFilesCacheKey.Builder renameScore(int value);
+
+    public abstract ModifiedFilesCacheKey build();
+  }
+
+  public enum Serializer implements CacheSerializer<ModifiedFilesCacheKey> {
+    INSTANCE;
+
+    @Override
+    public byte[] serialize(ModifiedFilesCacheKey key) {
+      ObjectIdConverter idConverter = ObjectIdConverter.create();
+      return Protos.toByteArray(
+          ModifiedFilesKeyProto.newBuilder()
+              .setProject(key.project().get())
+              .setACommit(idConverter.toByteString(key.aCommit()))
+              .setBCommit(idConverter.toByteString(key.bCommit()))
+              .setRenameScore(key.renameScore())
+              .build());
+    }
+
+    @Override
+    public ModifiedFilesCacheKey deserialize(byte[] in) {
+      ModifiedFilesKeyProto proto = Protos.parseUnchecked(ModifiedFilesKeyProto.parser(), in);
+      ObjectIdConverter idConverter = ObjectIdConverter.create();
+      return ModifiedFilesCacheKey.builder()
+          .project(NameKey.parse(proto.getProject()))
+          .aCommit(idConverter.fromByteString(proto.getACommit()))
+          .bCommit(idConverter.fromByteString(proto.getBCommit()))
+          .renameScore(proto.getRenameScore())
+          .build();
+    }
+  }
+
+  private static int stringSize(String str) {
+    if (str != null) {
+      // each character in the string occupies 2 bytes. Ignoring the fixed overhead for the string
+      // (length, offset and hash code) since they are negligible and do not
+      // affect the comparison of 2 strings
+      return str.length() * 2;
+    }
+    return 0;
+  }
+}
diff --git a/java/com/google/gerrit/server/patch/diff/ModifiedFilesWeigher.java b/java/com/google/gerrit/server/patch/diff/ModifiedFilesWeigher.java
index 0b2c69e..512da6f 100644
--- a/java/com/google/gerrit/server/patch/diff/ModifiedFilesWeigher.java
+++ b/java/com/google/gerrit/server/patch/diff/ModifiedFilesWeigher.java
@@ -19,9 +19,9 @@
 import com.google.gerrit.server.patch.gitdiff.ModifiedFile;
 
 public class ModifiedFilesWeigher
-    implements Weigher<ModifiedFilesCacheImpl.Key, ImmutableList<ModifiedFile>> {
+    implements Weigher<ModifiedFilesCacheKey, ImmutableList<ModifiedFile>> {
   @Override
-  public int weigh(ModifiedFilesCacheImpl.Key key, ImmutableList<ModifiedFile> modifiedFiles) {
+  public int weigh(ModifiedFilesCacheKey key, ImmutableList<ModifiedFile> modifiedFiles) {
     int weight = key.weight();
     for (ModifiedFile modifiedFile : modifiedFiles) {
       weight += modifiedFile.weight();
diff --git a/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesCache.java b/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesCache.java
index 36af9fe..d178f22 100644
--- a/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesCache.java
+++ b/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesCache.java
@@ -36,6 +36,5 @@
    * @return the list of {@link ModifiedFile}s between the 2 git trees identified by the key.
    * @throws DiffNotAvailableException trees cannot be read or file contents cannot be read.
    */
-  ImmutableList<ModifiedFile> get(GitModifiedFilesCacheImpl.Key key)
-      throws DiffNotAvailableException;
+  ImmutableList<ModifiedFile> get(GitModifiedFilesCacheKey key) throws DiffNotAvailableException;
 }
diff --git a/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesCacheImpl.java b/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesCacheImpl.java
index 230e12d..b3b82bb 100644
--- a/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesCacheImpl.java
+++ b/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesCacheImpl.java
@@ -16,23 +16,17 @@
 
 import static com.google.common.collect.ImmutableList.toImmutableList;
 
-import com.google.auto.value.AutoValue;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.gerrit.entities.Patch;
-import com.google.gerrit.entities.Project;
-import com.google.gerrit.entities.Project.NameKey;
 import com.google.gerrit.proto.Protos;
 import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.proto.Cache.GitModifiedFilesKeyProto;
 import com.google.gerrit.server.cache.proto.Cache.ModifiedFilesProto;
 import com.google.gerrit.server.cache.serialize.CacheSerializer;
-import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.patch.DiffNotAvailableException;
-import com.google.gerrit.server.patch.DiffUtil;
 import com.google.inject.Inject;
 import com.google.inject.Module;
 import com.google.inject.TypeLiteral;
@@ -44,10 +38,8 @@
 import org.eclipse.jgit.diff.DiffEntry;
 import org.eclipse.jgit.diff.DiffEntry.ChangeType;
 import org.eclipse.jgit.diff.DiffFormatter;
-import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.util.io.DisabledOutputStream;
 
 /** Implementation of the {@link GitModifiedFilesCache} */
@@ -66,7 +58,7 @@
           DiffEntry.ChangeType.COPY,
           Patch.ChangeType.COPIED);
 
-  private LoadingCache<Key, ImmutableList<ModifiedFile>> cache;
+  private LoadingCache<GitModifiedFilesCacheKey, ImmutableList<ModifiedFile>> cache;
 
   public static Module module() {
     return new CacheModule() {
@@ -74,8 +66,11 @@
       protected void configure() {
         bind(GitModifiedFilesCache.class).to(GitModifiedFilesCacheImpl.class);
 
-        persist(GIT_MODIFIED_FILES, Key.class, new TypeLiteral<ImmutableList<ModifiedFile>>() {})
-            .keySerializer(Key.KeySerializer.INSTANCE)
+        persist(
+                GIT_MODIFIED_FILES,
+                GitModifiedFilesCacheKey.class,
+                new TypeLiteral<ImmutableList<ModifiedFile>>() {})
+            .keySerializer(GitModifiedFilesCacheKey.Serializer.INSTANCE)
             .valueSerializer(ValueSerializer.INSTANCE)
             // The documentation has some defaults and recommendations for setting the cache
             // attributes:
@@ -92,12 +87,14 @@
 
   @Inject
   public GitModifiedFilesCacheImpl(
-      @Named(GIT_MODIFIED_FILES) LoadingCache<Key, ImmutableList<ModifiedFile>> cache) {
+      @Named(GIT_MODIFIED_FILES)
+          LoadingCache<GitModifiedFilesCacheKey, ImmutableList<ModifiedFile>> cache) {
     this.cache = cache;
   }
 
   @Override
-  public ImmutableList<ModifiedFile> get(Key key) throws DiffNotAvailableException {
+  public ImmutableList<ModifiedFile> get(GitModifiedFilesCacheKey key)
+      throws DiffNotAvailableException {
     try {
       return cache.get(key);
     } catch (ExecutionException e) {
@@ -105,7 +102,7 @@
     }
   }
 
-  static class Loader extends CacheLoader<Key, ImmutableList<ModifiedFile>> {
+  static class Loader extends CacheLoader<GitModifiedFilesCacheKey, ImmutableList<ModifiedFile>> {
     private final GitRepositoryManager repoManager;
 
     @Inject
@@ -114,7 +111,7 @@
     }
 
     @Override
-    public ImmutableList<ModifiedFile> load(Key key) throws IOException {
+    public ImmutableList<ModifiedFile> load(GitModifiedFilesCacheKey key) throws IOException {
       try (Repository repo = repoManager.openRepository(key.project());
           ObjectReader reader = repo.newObjectReader()) {
         List<DiffEntry> entries = getGitTreeDiff(repo, reader, key);
@@ -124,8 +121,7 @@
     }
 
     private List<DiffEntry> getGitTreeDiff(
-        Repository repo, ObjectReader reader, GitModifiedFilesCacheImpl.Key key)
-        throws IOException {
+        Repository repo, ObjectReader reader, GitModifiedFilesCacheKey key) throws IOException {
       try (DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE)) {
         df.setReader(reader, repo.getConfig());
         if (key.renameDetection()) {
@@ -156,109 +152,6 @@
     }
   }
 
-  /**
-   * In this cache, we evaluate the diffs between two git trees (instead of git commits), hence the
-   * key contains the tree IDs.
-   */
-  @AutoValue
-  public abstract static class Key {
-
-    public abstract Project.NameKey project();
-
-    /**
-     * The git SHA-1 {@link ObjectId} of the first git tree object for which the diff should be
-     * computed.
-     */
-    public abstract ObjectId aTree();
-
-    /**
-     * The git SHA-1 {@link ObjectId} of the second git tree object for which the diff should be
-     * computed.
-     */
-    public abstract ObjectId bTree();
-
-    /**
-     * Percentage score used to identify a file as a rename. This value is only available if {@link
-     * #renameDetection()} is true. Otherwise, this method will return -1.
-     *
-     * <p>This value will be used to set the rename score of {@link
-     * DiffFormatter#getRenameDetector()}.
-     */
-    public abstract int renameScore();
-
-    /** Returns true if rename detection was set for this key. */
-    public boolean renameDetection() {
-      return renameScore() != -1;
-    }
-
-    public static Key create(
-        Project.NameKey project, ObjectId aCommit, ObjectId bCommit, int renameScore, RevWalk rw)
-        throws IOException {
-      ObjectId aTree = DiffUtil.getTreeId(rw, aCommit);
-      ObjectId bTree = DiffUtil.getTreeId(rw, bCommit);
-      return builder().project(project).aTree(aTree).bTree(bTree).renameScore(renameScore).build();
-    }
-
-    public static Builder builder() {
-      return new AutoValue_GitModifiedFilesCacheImpl_Key.Builder();
-    }
-
-    /** Returns the size of the object in bytes */
-    public int weight() {
-      return project().get().length()
-          + 20 * 2 // old and new tree IDs
-          + 4; // rename score
-    }
-
-    @AutoValue.Builder
-    public abstract static class Builder {
-
-      public abstract Builder project(NameKey value);
-
-      public abstract Builder aTree(ObjectId value);
-
-      public abstract Builder bTree(ObjectId value);
-
-      public abstract Builder renameScore(int value);
-
-      public Builder disableRenameDetection() {
-        renameScore(-1);
-        return this;
-      }
-
-      public abstract Key build();
-    }
-
-    public enum KeySerializer implements CacheSerializer<Key> {
-      INSTANCE;
-
-      @Override
-      public byte[] serialize(Key key) {
-        ObjectIdConverter idConverter = ObjectIdConverter.create();
-        return Protos.toByteArray(
-            GitModifiedFilesKeyProto.newBuilder()
-                .setProject(key.project().get())
-                .setATree(idConverter.toByteString(key.aTree()))
-                .setBTree(idConverter.toByteString(key.bTree()))
-                .setRenameScore(key.renameScore())
-                .build());
-      }
-
-      @Override
-      public Key deserialize(byte[] in) {
-        GitModifiedFilesKeyProto proto =
-            Protos.parseUnchecked(GitModifiedFilesKeyProto.parser(), in);
-        ObjectIdConverter idConverter = ObjectIdConverter.create();
-        return Key.builder()
-            .project(Project.NameKey.parse(proto.getProject()))
-            .aTree(idConverter.fromByteString(proto.getATree()))
-            .bTree(idConverter.fromByteString(proto.getBTree()))
-            .renameScore(proto.getRenameScore())
-            .build();
-      }
-    }
-  }
-
   public enum ValueSerializer implements CacheSerializer<ImmutableList<ModifiedFile>> {
     INSTANCE;
 
diff --git a/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesCacheKey.java b/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesCacheKey.java
new file mode 100644
index 0000000..f94f2c9
--- /dev/null
+++ b/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesCacheKey.java
@@ -0,0 +1,137 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.patch.gitdiff;
+
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.Project.NameKey;
+import com.google.gerrit.proto.Protos;
+import com.google.gerrit.server.cache.proto.Cache.GitModifiedFilesKeyProto;
+import com.google.gerrit.server.cache.serialize.CacheSerializer;
+import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
+import com.google.gerrit.server.patch.DiffUtil;
+import java.io.IOException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+/** Cache key for the {@link GitModifiedFilesCache}. */
+@AutoValue
+public abstract class GitModifiedFilesCacheKey {
+
+  /** A specific git project / repository. */
+  public abstract Project.NameKey project();
+
+  /**
+   * The git SHA-1 {@link ObjectId} of the first git tree object for which the diff should be
+   * computed.
+   */
+  public abstract ObjectId aTree();
+
+  /**
+   * The git SHA-1 {@link ObjectId} of the second git tree object for which the diff should be
+   * computed.
+   */
+  public abstract ObjectId bTree();
+
+  /**
+   * Percentage score used to identify a file as a rename. This value is only available if {@link
+   * #renameDetection()} is true. Otherwise, this method will return -1.
+   *
+   * <p>This value will be used to set the rename score of {@link
+   * org.eclipse.jgit.diff.DiffFormatter#getRenameDetector()}.
+   */
+  public abstract int renameScore();
+
+  /** Returns true if rename detection was set for this key. */
+  public boolean renameDetection() {
+    return renameScore() != -1;
+  }
+
+  public static GitModifiedFilesCacheKey create(
+      Project.NameKey project, ObjectId aCommit, ObjectId bCommit, int renameScore, RevWalk rw)
+      throws IOException {
+    ObjectId aTree = DiffUtil.getTreeId(rw, aCommit);
+    ObjectId bTree = DiffUtil.getTreeId(rw, bCommit);
+    return builder().project(project).aTree(aTree).bTree(bTree).renameScore(renameScore).build();
+  }
+
+  public static Builder builder() {
+    return new AutoValue_GitModifiedFilesCacheKey.Builder();
+  }
+
+  /** Returns the size of the object in bytes */
+  public int weight() {
+    return stringSize(project().get())
+        + 20 * 2 // old and new tree IDs
+        + 4; // rename score
+  }
+
+  @AutoValue.Builder
+  public abstract static class Builder {
+
+    public abstract Builder project(NameKey value);
+
+    public abstract Builder aTree(ObjectId value);
+
+    public abstract Builder bTree(ObjectId value);
+
+    public abstract Builder renameScore(int value);
+
+    public Builder disableRenameDetection() {
+      renameScore(-1);
+      return this;
+    }
+
+    public abstract GitModifiedFilesCacheKey build();
+  }
+
+  public enum Serializer implements CacheSerializer<GitModifiedFilesCacheKey> {
+    INSTANCE;
+
+    @Override
+    public byte[] serialize(GitModifiedFilesCacheKey key) {
+      ObjectIdConverter idConverter = ObjectIdConverter.create();
+      return Protos.toByteArray(
+          GitModifiedFilesKeyProto.newBuilder()
+              .setProject(key.project().get())
+              .setATree(idConverter.toByteString(key.aTree()))
+              .setBTree(idConverter.toByteString(key.bTree()))
+              .setRenameScore(key.renameScore())
+              .build());
+    }
+
+    @Override
+    public GitModifiedFilesCacheKey deserialize(byte[] in) {
+      GitModifiedFilesKeyProto proto = Protos.parseUnchecked(GitModifiedFilesKeyProto.parser(), in);
+      ObjectIdConverter idConverter = ObjectIdConverter.create();
+      return GitModifiedFilesCacheKey.builder()
+          .project(NameKey.parse(proto.getProject()))
+          .aTree(idConverter.fromByteString(proto.getATree()))
+          .bTree(idConverter.fromByteString(proto.getBTree()))
+          .renameScore(proto.getRenameScore())
+          .build();
+    }
+  }
+
+  private static int stringSize(String str) {
+    if (str != null) {
+      // each character in the string occupies 2 bytes. Ignoring the fixed overhead for the string
+      // (length, offset and hash code) since they are negligible and do not
+      // affect the comparison of 2 strings
+      return str.length() * 2;
+    }
+    return 0;
+  }
+}
diff --git a/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesWeigher.java b/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesWeigher.java
index f096cef..a678379 100644
--- a/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesWeigher.java
+++ b/java/com/google/gerrit/server/patch/gitdiff/GitModifiedFilesWeigher.java
@@ -18,9 +18,9 @@
 import com.google.common.collect.ImmutableList;
 
 public class GitModifiedFilesWeigher
-    implements Weigher<GitModifiedFilesCacheImpl.Key, ImmutableList<ModifiedFile>> {
+    implements Weigher<GitModifiedFilesCacheKey, ImmutableList<ModifiedFile>> {
   @Override
-  public int weigh(GitModifiedFilesCacheImpl.Key key, ImmutableList<ModifiedFile> modifiedFiles) {
+  public int weigh(GitModifiedFilesCacheKey key, ImmutableList<ModifiedFile> modifiedFiles) {
     return key.weight() + modifiedFiles.stream().mapToInt(ModifiedFile::weight).sum();
   }
 }
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/GitModifiedFilesCacheKeySerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/GitModifiedFilesCacheKeySerializerTest.java
index 67edd55..caf1fbb 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/entities/GitModifiedFilesCacheKeySerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/GitModifiedFilesCacheKeySerializerTest.java
@@ -17,8 +17,8 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.gerrit.entities.Project;
-import com.google.gerrit.server.patch.gitdiff.GitModifiedFilesCacheImpl;
-import com.google.gerrit.server.patch.gitdiff.GitModifiedFilesCacheImpl.Key.KeySerializer;
+import com.google.gerrit.server.patch.gitdiff.GitModifiedFilesCacheKey;
+import com.google.gerrit.server.patch.gitdiff.GitModifiedFilesCacheKey.Serializer;
 import org.eclipse.jgit.lib.ObjectId;
 import org.junit.Test;
 
@@ -30,14 +30,14 @@
 
   @Test
   public void roundTrip() {
-    GitModifiedFilesCacheImpl.Key key =
-        GitModifiedFilesCacheImpl.Key.builder()
+    GitModifiedFilesCacheKey key =
+        GitModifiedFilesCacheKey.builder()
             .project(Project.NameKey.parse("Project/X"))
             .aTree(TREE_ID_1)
             .bTree(TREE_ID_2)
             .renameScore(65)
             .build();
-    byte[] serialized = KeySerializer.INSTANCE.serialize(key);
-    assertThat(KeySerializer.INSTANCE.deserialize(serialized)).isEqualTo(key);
+    byte[] serialized = Serializer.INSTANCE.serialize(key);
+    assertThat(Serializer.INSTANCE.deserialize(serialized)).isEqualTo(key);
   }
 }
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/ModifiedFilesCacheKeySerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/ModifiedFilesCacheKeySerializerTest.java
index dbf11d4..b39ba57 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/entities/ModifiedFilesCacheKeySerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/ModifiedFilesCacheKeySerializerTest.java
@@ -17,7 +17,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.gerrit.entities.Project;
-import com.google.gerrit.server.patch.diff.ModifiedFilesCacheImpl;
+import com.google.gerrit.server.patch.diff.ModifiedFilesCacheKey;
 import org.eclipse.jgit.lib.ObjectId;
 import org.junit.Test;
 
@@ -29,15 +29,14 @@
 
   @Test
   public void roundTrip() {
-    ModifiedFilesCacheImpl.Key key =
-        ModifiedFilesCacheImpl.Key.builder()
+    ModifiedFilesCacheKey key =
+        ModifiedFilesCacheKey.builder()
             .project(Project.NameKey.parse("Project/X"))
             .aCommit(COMMIT_ID_1)
             .bCommit(COMMIT_ID_2)
             .renameScore(65)
             .build();
-    byte[] serialized = ModifiedFilesCacheImpl.Key.Serializer.INSTANCE.serialize(key);
-    assertThat(ModifiedFilesCacheImpl.Key.Serializer.INSTANCE.deserialize(serialized))
-        .isEqualTo(key);
+    byte[] serialized = ModifiedFilesCacheKey.Serializer.INSTANCE.serialize(key);
+    assertThat(ModifiedFilesCacheKey.Serializer.INSTANCE.deserialize(serialized)).isEqualTo(key);
   }
 }
diff --git a/proto/cache.proto b/proto/cache.proto
index e7fbe6d..aa71b87 100644
--- a/proto/cache.proto
+++ b/proto/cache.proto
@@ -528,7 +528,7 @@
 }
 
 // Serialized key for
-// com.google.gerrit.server.patch.gitdiff.GitModifiedCacheImpl.Key
+// com.google.gerrit.server.patch.gitdiff.GitModifiedFilesCacheKey
 // Next ID: 5
 message GitModifiedFilesKeyProto {
   string project = 1;
@@ -537,6 +537,16 @@
   int32 rename_score = 4;
 }
 
+// Serialized key for
+// com.google.gerrit.server.patch.diff.ModifiedFilesCacheKey
+// Next ID: 5
+message ModifiedFilesKeyProto {
+  string project = 1;
+  bytes a_commit = 2; // SHA-1 hash of the left commit ID in the diff
+  bytes b_commit = 3; // SHA-1 hash of the right commit ID in the diff
+  int32 rename_score = 4;
+}
+
 // Serialized form of com.google.gerrit.server.patch.gitdiff.ModifiedFile
 // Next ID: 4
 message ModifiedFileProto {