Don't attempt to cache refs containing '[:|@]' in resolve call The 'ref + ":" + filename' syntax is used to resolve the file's ObjectId under the specific ref. That attempt fails to succeed in 'exactRef' call therefore it falls back to the delegate 'resolve' call. The `ref + "@"` syntax (according to [1]) is used to reach out to a reflog entry or to a value of ref prior the point in time. The result (for both cases) is consistent with delegate 'resolve' call but cycles are wasted on 'exactRef' call that returns 'null'. This change adds refs that contain '[:|@]' as non cacheable. The check leverages on Guava's CharMatcher to check all chars in a single input String's loop. Note that the CachedRefRepositoryIT was rewritten to work on a FileRepository so that reflog could be enabled and `@`-related checks performed. [1] https://git-scm.com/docs/git-rev-parse#_specifying_revisions Bug: Issue 16553 Change-Id: Ia86e6bd2c0da4c7eceffc090ad1e7a63d63552fc
diff --git a/src/main/java/com/googlesource/gerrit/plugins/cachedrefdb/CachedRefRepository.java b/src/main/java/com/googlesource/gerrit/plugins/cachedrefdb/CachedRefRepository.java index c47e34f..d5b5337 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/cachedrefdb/CachedRefRepository.java +++ b/src/main/java/com/googlesource/gerrit/plugins/cachedrefdb/CachedRefRepository.java
@@ -14,6 +14,7 @@ package com.googlesource.gerrit.plugins.cachedrefdb; +import com.google.common.base.CharMatcher; import com.google.gerrit.entities.RefNames; import com.google.gerrit.server.git.DelegateRepository; import com.google.inject.Inject; @@ -58,6 +59,8 @@ private final RefUpdateWithCacheUpdate.Factory updateFactory; private final RefRenameWithCacheUpdate.Factory renameFactory; + private static final CharMatcher REVISION_CHARS = CharMatcher.anyOf("^~:@"); + @Inject CachedRefRepository( CachedRefDatabase.Factory refDbFactory, @@ -365,6 +368,6 @@ } private boolean isCacheableReference(String ref) { - return ref.startsWith(RefNames.REFS) && !(ref.contains("^") || ref.contains("~")); + return ref.startsWith(RefNames.REFS) && REVISION_CHARS.matchesNoneOf(ref); } }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/cachedrefdb/CachedRefRepositoryIT.java b/src/test/java/com/googlesource/gerrit/plugins/cachedrefdb/CachedRefRepositoryIT.java index 5f2c2e4..b04cac0 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/cachedrefdb/CachedRefRepositoryIT.java +++ b/src/test/java/com/googlesource/gerrit/plugins/cachedrefdb/CachedRefRepositoryIT.java
@@ -21,10 +21,12 @@ import com.google.gerrit.entities.RefNames; import com.google.gerrit.extensions.registration.DynamicItem; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Optional; import java.util.concurrent.Callable; -import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; -import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; +import org.eclipse.jgit.internal.storage.file.FileRepository; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; @@ -33,16 +35,28 @@ import org.eclipse.jgit.revwalk.RevCommit; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; public class CachedRefRepositoryIT { - TestRepository<Repository> tr; - CachedRefRepository objectUnderTest; - TestRefByNameCacheImpl cache; + @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private TestRepository<Repository> tr; + private CachedRefRepository objectUnderTest; + private TestRefByNameCacheImpl cache; @Before public void setUp() throws IOException { - Repository repo = new InMemoryRepository(new DfsRepositoryDescription("repo")); + Path repoPath = temporaryFolder.newFolder().toPath(); + Repository repo = new FileRepository(repoPath.toFile()); + repo.create(true); + + // enable reflog for a repository so that references to it could be resolved + Files.write( + repoPath.resolve("config"), + "[core]\n logAllRefUpdates = true\n".getBytes(StandardCharsets.UTF_8)); + objectUnderTest = createCachedRepository(repo); tr = new TestRepository<>(repo); } @@ -72,7 +86,8 @@ @Test public void shouldNotResolveRefsFromCache() throws Exception { String master = RefNames.fullName("master"); - RevCommit first = tr.update(master, tr.commit().add("first", "foo").create()); + String filename = "first"; + RevCommit first = tr.update(master, tr.commit().add(filename, "foo").create()); String tag = "test_tag"; String fullTag = RefNames.REFS_TAGS + tag; tr.update(fullTag, tr.tag(tag, first)); @@ -92,6 +107,14 @@ assertThat(resolvedByTilde).isEqualTo(first); assertThat(resolvedByTilde).isEqualTo(repo().resolve(mastersParent)); + String mastersFilename = master + ":" + filename; + ObjectId resolvedByFilename = objectUnderTest.resolve(mastersFilename); + assertThat(resolvedByFilename).isEqualTo(repo().resolve(mastersFilename)); + + String mastersPreviousRevision = master + "@{1}"; + ObjectId resolvedByPreviousRevision = objectUnderTest.resolve(mastersPreviousRevision); + assertThat(resolvedByPreviousRevision).isEqualTo(repo().resolve(mastersPreviousRevision)); + assertThat(cache.cacheCalled).isEqualTo(0); }