Merge "Convert VisibleRefFilter to assisted factory"
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java
index 819e918..1219b0a 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java
@@ -16,7 +16,6 @@
 
 import com.google.common.collect.Lists;
 import com.google.gerrit.acceptance.InProcessProtocol.Context;
-import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.Capable;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.reviewdb.client.Account;
@@ -32,12 +31,9 @@
 import com.google.gerrit.server.git.AsyncReceiveCommits;
 import com.google.gerrit.server.git.ReceiveCommits;
 import com.google.gerrit.server.git.ReceivePackInitializer;
-import com.google.gerrit.server.git.SearchingChangeCacheImpl;
-import com.google.gerrit.server.git.TagCache;
 import com.google.gerrit.server.git.TransferConfig;
 import com.google.gerrit.server.git.VisibleRefFilter;
 import com.google.gerrit.server.git.validators.UploadValidators;
-import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.ProjectControl;
 import com.google.gerrit.server.util.RequestContext;
@@ -206,12 +202,9 @@
   }
 
   private static class Upload implements UploadPackFactory<Context> {
-    private final Provider<ReviewDb> dbProvider;
     private final Provider<CurrentUser> userProvider;
-    private final TagCache tagCache;
-    @Nullable private final SearchingChangeCacheImpl changeCache;
     private final ProjectControl.GenericFactory projectControlFactory;
-    private final ChangeNotes.Factory changeNotesFactory;
+    private final VisibleRefFilter.Factory refFilterFactory;
     private final TransferConfig transferConfig;
     private final DynamicSet<PreUploadHook> preUploadHooks;
     private final UploadValidators.Factory uploadValidatorsFactory;
@@ -219,22 +212,16 @@
 
     @Inject
     Upload(
-        Provider<ReviewDb> dbProvider,
         Provider<CurrentUser> userProvider,
-        TagCache tagCache,
-        @Nullable SearchingChangeCacheImpl changeCache,
         ProjectControl.GenericFactory projectControlFactory,
-        ChangeNotes.Factory changeNotesFactory,
+        VisibleRefFilter.Factory refFilterFactory,
         TransferConfig transferConfig,
         DynamicSet<PreUploadHook> preUploadHooks,
         UploadValidators.Factory uploadValidatorsFactory,
         ThreadLocalRequestContext threadContext) {
-      this.dbProvider = dbProvider;
       this.userProvider = userProvider;
-      this.tagCache = tagCache;
-      this.changeCache = changeCache;
       this.projectControlFactory = projectControlFactory;
-      this.changeNotesFactory = changeNotesFactory;
+      this.refFilterFactory = refFilterFactory;
       this.transferConfig = transferConfig;
       this.preUploadHooks = preUploadHooks;
       this.uploadValidatorsFactory = uploadValidatorsFactory;
@@ -258,9 +245,7 @@
         UploadPack up = new UploadPack(repo);
         up.setPackConfig(transferConfig.getPackConfig());
         up.setTimeout(transferConfig.getTimeout());
-        up.setAdvertiseRefsHook(
-            new VisibleRefFilter(
-                tagCache, changeNotesFactory, changeCache, repo, ctl, dbProvider.get(), true));
+        up.setAdvertiseRefsHook(refFilterFactory.create(ctl.getProjectState(), repo));
         List<PreUploadHook> hooks = Lists.newArrayList(preUploadHooks);
         hooks.add(uploadValidatorsFactory.create(ctl.getProject(), repo, "localhost-test"));
         up.setPreUploadHook(PreUploadHookChain.newChain(hooks));
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index f327f63..02a19c6 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
 import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.PushOneCommit;
-import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.common.data.Permission;
@@ -34,23 +33,16 @@
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.config.AnonymousCowardName;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.git.ReceiveCommitsAdvertiseRefsHook;
-import com.google.gerrit.server.git.SearchingChangeCacheImpl;
-import com.google.gerrit.server.git.TagCache;
 import com.google.gerrit.server.git.VisibleRefFilter;
 import com.google.gerrit.server.notedb.ChangeNoteUtil;
 import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.project.ProjectControl;
 import com.google.gerrit.server.project.Util;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.testutil.DisabledReviewDb;
 import com.google.gerrit.testutil.TestChanges;
 import com.google.inject.Inject;
-import com.google.inject.Provider;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -68,16 +60,8 @@
 
 @NoHttpd
 public class RefAdvertisementIT extends AbstractDaemonTest {
-  @Inject private ProjectControl.GenericFactory projectControlFactory;
-
-  @Inject @Nullable private SearchingChangeCacheImpl changeCache;
-
-  @Inject private TagCache tagCache;
-
-  @Inject private Provider<CurrentUser> userProvider;
-
+  @Inject private VisibleRefFilter.Factory refFilterFactory;
   @Inject private ChangeNoteUtil noteUtil;
-
   @Inject @AnonymousCowardName private String anonymousCowardName;
 
   private AccountGroup.UUID admins;
@@ -386,7 +370,7 @@
     try (Repository repo = repoManager.openRepository(project)) {
       assertRefs(
           repo,
-          new VisibleRefFilter(tagCache, notesFactory, null, repo, projectControl(), db, true),
+          refFilterFactory.create(projectCache.get(project), repo),
           // Can't use stored values from the index so DB must be enabled.
           false,
           "HEAD",
@@ -410,12 +394,12 @@
     assume().that(notesMigration.readChangeSequence()).isTrue();
     try (Repository repo = repoManager.openRepository(allProjects)) {
       setApiUser(user);
-      assertRefs(repo, newFilter(db, repo, allProjects), true);
+      assertRefs(repo, newFilter(repo, allProjects), true);
 
       allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
       try {
         setApiUser(user);
-        assertRefs(repo, newFilter(db, repo, allProjects), true, "refs/sequences/changes");
+        assertRefs(repo, newFilter(repo, allProjects), true, "refs/sequences/changes");
       } finally {
         removeGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
       }
@@ -556,17 +540,7 @@
   private void assertUploadPackRefs(String... expectedWithMeta) throws Exception {
     try (Repository repo = repoManager.openRepository(project)) {
       assertRefs(
-          repo,
-          new VisibleRefFilter(
-              tagCache,
-              notesFactory,
-              changeCache,
-              repo,
-              projectControl(),
-              new DisabledReviewDb(),
-              true),
-          true,
-          expectedWithMeta);
+          repo, refFilterFactory.create(projectCache.get(project), repo), true, expectedWithMeta);
     }
   }
 
@@ -602,20 +576,8 @@
     }
   }
 
-  private ProjectControl projectControl() throws Exception {
-    return projectControlFactory.controlFor(project, userProvider.get());
-  }
-
-  private VisibleRefFilter newFilter(ReviewDb db, Repository repo, Project.NameKey project)
-      throws Exception {
-    return new VisibleRefFilter(
-        tagCache,
-        notesFactory,
-        null,
-        repo,
-        projectControlFactory.controlFor(project, userProvider.get()),
-        db,
-        true);
+  private VisibleRefFilter newFilter(Repository repo, Project.NameKey project) {
+    return refFilterFactory.create(projectCache.get(project), repo);
   }
 
   private static ObjectId obj(ChangeData cd, int psNum) throws Exception {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index a77f660..a89e2d9 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -16,12 +16,10 @@
 
 import com.google.common.cache.Cache;
 import com.google.common.collect.Lists;
-import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.Capable;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.AccessPath;
 import com.google.gerrit.server.AnonymousUser;
 import com.google.gerrit.server.CurrentUser;
@@ -29,12 +27,9 @@
 import com.google.gerrit.server.git.AsyncReceiveCommits;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.ReceiveCommits;
-import com.google.gerrit.server.git.SearchingChangeCacheImpl;
-import com.google.gerrit.server.git.TagCache;
 import com.google.gerrit.server.git.TransferConfig;
 import com.google.gerrit.server.git.VisibleRefFilter;
 import com.google.gerrit.server.git.validators.UploadValidators;
-import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
@@ -237,23 +232,14 @@
   }
 
   static class UploadFilter implements Filter {
-    private final Provider<ReviewDb> db;
-    private final TagCache tagCache;
-    private final ChangeNotes.Factory changeNotesFactory;
-    @Nullable private final SearchingChangeCacheImpl changeCache;
+    private final VisibleRefFilter.Factory refFilterFactory;
     private final UploadValidators.Factory uploadValidatorsFactory;
 
     @Inject
     UploadFilter(
-        Provider<ReviewDb> db,
-        TagCache tagCache,
-        ChangeNotes.Factory changeNotesFactory,
-        @Nullable SearchingChangeCacheImpl changeCache,
+        VisibleRefFilter.Factory refFilterFactory,
         UploadValidators.Factory uploadValidatorsFactory) {
-      this.db = db;
-      this.tagCache = tagCache;
-      this.changeNotesFactory = changeNotesFactory;
-      this.changeCache = changeCache;
+      this.refFilterFactory = refFilterFactory;
       this.uploadValidatorsFactory = uploadValidatorsFactory;
     }
 
@@ -279,9 +265,7 @@
           uploadValidatorsFactory.create(pc.getProject(), repo, request.getRemoteHost());
       up.setPreUploadHook(
           PreUploadHookChain.newChain(Lists.newArrayList(up.getPreUploadHook(), uploadValidators)));
-      up.setAdvertiseRefsHook(
-          new VisibleRefFilter(
-              tagCache, changeNotesFactory, changeCache, repo, pc, db.get(), true));
+      up.setAdvertiseRefsHook(refFilterFactory.create(pc.getProjectState(), repo));
 
       next.doFilter(request, response);
     }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index e625219..c051bff 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -57,6 +57,7 @@
 import com.google.gerrit.server.git.ReceiveCommitsExecutorModule;
 import com.google.gerrit.server.git.SearchingChangeCacheImpl;
 import com.google.gerrit.server.git.TagCache;
+import com.google.gerrit.server.git.VisibleRefFilter;
 import com.google.gerrit.server.group.GroupModule;
 import com.google.gerrit.server.mail.send.ReplacePatchSetSender;
 import com.google.gerrit.server.notedb.NoteDbModule;
@@ -130,6 +131,7 @@
     factory(MergeUtil.Factory.class);
     factory(PatchSetInserter.Factory.class);
     factory(RebaseChangeOp.Factory.class);
+    factory(VisibleRefFilter.Factory.class);
 
     // As Reindex is a batch program, don't assume the index is available for
     // the change cache.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 6c3be8c..366cbd1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -123,6 +123,7 @@
 import com.google.gerrit.server.git.ReplaceOp;
 import com.google.gerrit.server.git.TagCache;
 import com.google.gerrit.server.git.TransferConfig;
+import com.google.gerrit.server.git.VisibleRefFilter;
 import com.google.gerrit.server.git.strategy.SubmitStrategy;
 import com.google.gerrit.server.git.validators.CommitValidationListener;
 import com.google.gerrit.server.git.validators.MergeValidationListener;
@@ -261,6 +262,7 @@
     factory(RegisterNewEmailSender.Factory.class);
     factory(ReplacePatchSetSender.Factory.class);
     factory(SetAssigneeSender.Factory.class);
+    factory(VisibleRefFilter.Factory.class);
     bind(PermissionCollection.Factory.class);
     bind(AccountVisibility.class).toProvider(AccountVisibilityProvider.class).in(SINGLETON);
     factory(ProjectOwnerGroupsProvider.Factory.class);
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 a8e4b23..14042ba 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
@@ -384,7 +384,7 @@
       PatchSetUtil psUtil,
       ProjectCache projectCache,
       TagCache tagCache,
-      @Nullable SearchingChangeCacheImpl changeCache,
+      VisibleRefFilter.Factory refFilterFactory,
       ChangeInserter.Factory changeInserterFactory,
       CommitValidators.Factory commitValidatorsFactory,
       RefOperationValidators.Factory refValidatorsFactory,
@@ -494,7 +494,7 @@
     }
 
     rp.setAdvertiseRefsHook(
-        new VisibleRefFilter(tagCache, notesFactory, changeCache, repo, projectControl, db, false));
+        refFilterFactory.create(projectControl.getProjectState(), repo).setShowMetadata(false));
     List<AdvertiseRefsHook> advHooks = new ArrayList<>(3);
     advHooks.add(
         new AdvertiseRefsHook() {
@@ -1076,7 +1076,7 @@
     } catch (AuthException err) {
       ok = false;
     }
-    if (ok && ctl.canCreate(db, rp.getRepository(), obj)) {
+    if (ok && ctl.canCreate(rp.getRepository(), obj)) {
       if (!validRefOperation(cmd)) {
         return;
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java
index 3756d73..e4d78c4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java
@@ -27,11 +27,16 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -52,50 +57,62 @@
 public class VisibleRefFilter extends AbstractAdvertiseRefsHook {
   private static final Logger log = LoggerFactory.getLogger(VisibleRefFilter.class);
 
+  public interface Factory {
+    VisibleRefFilter create(ProjectState projectState, Repository git);
+  }
+
   private final TagCache tagCache;
   private final ChangeNotes.Factory changeNotesFactory;
   @Nullable private final SearchingChangeCacheImpl changeCache;
-  private final Repository db;
-  private final Project.NameKey projectName;
-  private final ProjectControl projectCtl;
-  private final ReviewDb reviewDb;
-  private final boolean showMetadata;
+  private final Provider<ReviewDb> db;
+  private final Provider<CurrentUser> user;
+  private final ProjectState projectState;
+  private final Repository git;
+  private ProjectControl projectCtl;
+  private boolean showMetadata = true;
   private String userEditPrefix;
   private Map<Change.Id, Branch.NameKey> visibleChanges;
 
-  public VisibleRefFilter(
+  @Inject
+  VisibleRefFilter(
       TagCache tagCache,
       ChangeNotes.Factory changeNotesFactory,
       @Nullable SearchingChangeCacheImpl changeCache,
-      Repository db,
-      ProjectControl projectControl,
-      ReviewDb reviewDb,
-      boolean showMetadata) {
+      Provider<ReviewDb> db,
+      Provider<CurrentUser> user,
+      @Assisted ProjectState projectState,
+      @Assisted Repository git) {
     this.tagCache = tagCache;
     this.changeNotesFactory = changeNotesFactory;
     this.changeCache = changeCache;
     this.db = db;
-    this.projectName = projectControl.getProject().getNameKey();
-    this.projectCtl = projectControl;
-    this.reviewDb = reviewDb;
-    this.showMetadata = showMetadata;
+    this.user = user;
+    this.projectState = projectState;
+    this.git = git;
+  }
+
+  /** Show change references. Default is {@code true}. */
+  public VisibleRefFilter setShowMetadata(boolean show) {
+    showMetadata = show;
+    return this;
   }
 
   public Map<String, Ref> filter(Map<String, Ref> refs, boolean filterTagsSeparately) {
-    if (projectCtl.getProjectState().isAllUsers()) {
+    if (projectState.isAllUsers()) {
       refs = addUsersSelfSymref(refs);
     }
 
+    projectCtl = projectState.controlFor(user.get());
     if (projectCtl.allRefsAreVisible(ImmutableSet.of(REFS_CONFIG))) {
       return fastHideRefsMetaConfig(refs);
     }
 
     Account.Id userId;
     boolean viewMetadata;
-    if (projectCtl.getUser().isIdentifiedUser()) {
-      IdentifiedUser user = projectCtl.getUser().asIdentifiedUser();
-      userId = user.getAccountId();
-      viewMetadata = user.getCapabilities().canAccessDatabase();
+    if (user.get().isIdentifiedUser()) {
+      IdentifiedUser u = user.get().asIdentifiedUser();
+      userId = u.getAccountId();
+      viewMetadata = u.getCapabilities().canAccessDatabase();
       userEditPrefix = RefNames.refsEditPrefix(userId);
     } else {
       userId = null;
@@ -109,8 +126,7 @@
       String name = ref.getName();
       Change.Id changeId;
       Account.Id accountId;
-      if (name.startsWith(REFS_CACHE_AUTOMERGE)
-          || (!showMetadata && isMetadata(projectCtl, name))) {
+      if (name.startsWith(REFS_CACHE_AUTOMERGE) || (!showMetadata && isMetadata(name))) {
         continue;
       } else if (RefNames.isRefsEdit(name)) {
         // Edits are visible only to the owning user, if change is visible.
@@ -138,8 +154,7 @@
         if (viewMetadata) {
           result.put(name, ref);
         }
-      } else if (projectCtl.getProjectState().isAllUsers()
-          && name.equals(RefNames.REFS_EXTERNAL_IDS)) {
+      } else if (projectState.isAllUsers() && name.equals(RefNames.REFS_EXTERNAL_IDS)) {
         // The notes branch with the external IDs of all users must not be exposed to normal users.
         if (viewMetadata) {
           result.put(name, ref);
@@ -158,11 +173,11 @@
     if (!deferredTags.isEmpty() && (!result.isEmpty() || filterTagsSeparately)) {
       TagMatcher tags =
           tagCache
-              .get(projectName)
+              .get(projectState.getProject().getNameKey())
               .matcher(
                   tagCache,
-                  db,
-                  filterTagsSeparately ? filter(db.getAllRefs()).values() : result.values());
+                  git,
+                  filterTagsSeparately ? filter(git.getAllRefs()).values() : result.values());
       for (Ref tag : deferredTags) {
         if (tags.isReachable(tag)) {
           result.put(tag.getName(), tag);
@@ -183,8 +198,8 @@
   }
 
   private Map<String, Ref> addUsersSelfSymref(Map<String, Ref> refs) {
-    if (projectCtl.getUser().isIdentifiedUser()) {
-      Ref r = refs.get(RefNames.refsUsers(projectCtl.getUser().getAccountId()));
+    if (user.get().isIdentifiedUser()) {
+      Ref r = refs.get(RefNames.refsUsers(user.get().getAccountId()));
       if (r != null) {
         SymbolicRef s = new SymbolicRef(REFS_USERS_SELF, r);
         refs = new HashMap<>(refs);
@@ -241,8 +256,8 @@
     Project project = projectCtl.getProject();
     try {
       Map<Change.Id, Branch.NameKey> visibleChanges = new HashMap<>();
-      for (ChangeData cd : changeCache.getChangeData(reviewDb, project.getNameKey())) {
-        if (projectCtl.controlForIndexedChange(cd.change()).isVisible(reviewDb, cd)) {
+      for (ChangeData cd : changeCache.getChangeData(db.get(), project.getNameKey())) {
+        if (projectCtl.controlForIndexedChange(cd.change()).isVisible(db.get(), cd)) {
           visibleChanges.put(cd.getId(), cd.change().getDest());
         }
       }
@@ -261,8 +276,8 @@
     Project.NameKey project = projectCtl.getProject().getNameKey();
     try {
       Map<Change.Id, Branch.NameKey> visibleChanges = new HashMap<>();
-      for (ChangeNotes cn : changeNotesFactory.scan(db, reviewDb, project)) {
-        if (projectCtl.controlFor(cn).isVisible(reviewDb)) {
+      for (ChangeNotes cn : changeNotesFactory.scan(git, db.get(), project)) {
+        if (projectCtl.controlFor(cn).isVisible(db.get())) {
           visibleChanges.put(cn.getChangeId(), cn.getChange().getDest());
         }
       }
@@ -274,10 +289,10 @@
     }
   }
 
-  private static boolean isMetadata(ProjectControl projectCtl, String name) {
+  private boolean isMetadata(String name) {
     return name.startsWith(REFS_CHANGES)
         || RefNames.isRefsEdit(name)
-        || (projectCtl.getProjectState().isAllUsers() && name.equals(RefNames.REFS_EXTERNAL_IDS));
+        || (projectState.isAllUsers() && name.equals(RefNames.REFS_EXTERNAL_IDS));
   }
 
   private static boolean isTag(Ref ref) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java
index 0598d75..e42a1cf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -54,7 +53,6 @@
   private final Provider<IdentifiedUser> identifiedUser;
   private final PermissionBackend permissionBackend;
   private final GitRepositoryManager repoManager;
-  private final Provider<ReviewDb> db;
   private final GitReferenceUpdated referenceUpdated;
   private final RefValidationHelper refCreationValidator;
   private String ref;
@@ -64,14 +62,12 @@
       Provider<IdentifiedUser> identifiedUser,
       PermissionBackend permissionBackend,
       GitRepositoryManager repoManager,
-      Provider<ReviewDb> db,
       GitReferenceUpdated referenceUpdated,
       RefValidationHelper.Factory refHelperFactory,
       @Assisted String ref) {
     this.identifiedUser = identifiedUser;
     this.permissionBackend = permissionBackend;
     this.repoManager = repoManager;
-    this.db = db;
     this.referenceUpdated = referenceUpdated;
     this.refCreationValidator = refHelperFactory.create(ReceiveCommand.Type.CREATE);
     this.ref = ref;
@@ -121,7 +117,7 @@
         }
       }
 
-      if (!refControl.canCreate(db.get(), repo, object)) {
+      if (!refControl.canCreate(repo, object)) {
         throw new AuthException("Cannot create \"" + ref + "\"");
       }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListTags.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListTags.java
index 7cbea47..d5a8573 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListTags.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListTags.java
@@ -22,14 +22,11 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommonConverters;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.SearchingChangeCacheImpl;
-import com.google.gerrit.server.git.TagCache;
 import com.google.gerrit.server.git.VisibleRefFilter;
-import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.RefPermission;
 import com.google.inject.Inject;
@@ -55,9 +52,7 @@
   private final GitRepositoryManager repoManager;
   private final PermissionBackend permissionBackend;
   private final Provider<CurrentUser> user;
-  private final Provider<ReviewDb> dbProvider;
-  private final TagCache tagCache;
-  private final ChangeNotes.Factory changeNotesFactory;
+  private final VisibleRefFilter.Factory refFilterFactory;
   @Nullable private final SearchingChangeCacheImpl changeCache;
 
   @Option(
@@ -110,16 +105,12 @@
       GitRepositoryManager repoManager,
       PermissionBackend permissionBackend,
       Provider<CurrentUser> user,
-      Provider<ReviewDb> dbProvider,
-      TagCache tagCache,
-      ChangeNotes.Factory changeNotesFactory,
+      VisibleRefFilter.Factory refFilterFactory,
       @Nullable SearchingChangeCacheImpl changeCache) {
     this.repoManager = repoManager;
     this.permissionBackend = permissionBackend;
     this.user = user;
-    this.dbProvider = dbProvider;
-    this.tagCache = tagCache;
-    this.changeNotesFactory = changeNotesFactory;
+    this.refFilterFactory = refFilterFactory;
     this.changeCache = changeCache;
   }
 
@@ -211,8 +202,9 @@
 
   private Map<String, Ref> visibleTags(
       ProjectControl control, Repository repo, Map<String, Ref> tags) {
-    return new VisibleRefFilter(
-            tagCache, changeNotesFactory, changeCache, repo, control, dbProvider.get(), false)
+    return refFilterFactory
+        .create(control.getProjectState(), repo)
+        .setShowMetadata(false)
         .filter(tags, true);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
index 57c933f..d328f87 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
@@ -43,8 +43,6 @@
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.GitReceivePackGroups;
 import com.google.gerrit.server.config.GitUploadPackGroups;
-import com.google.gerrit.server.git.SearchingChangeCacheImpl;
-import com.google.gerrit.server.git.TagCache;
 import com.google.gerrit.server.git.VisibleRefFilter;
 import com.google.gerrit.server.group.SystemGroupBackend;
 import com.google.gerrit.server.notedb.ChangeNotes;
@@ -136,12 +134,10 @@
   private final String canonicalWebUrl;
   private final CurrentUser user;
   private final ProjectState state;
-  private final ChangeNotes.Factory changeNotesFactory;
   private final ChangeControl.Factory changeControlFactory;
   private final PermissionCollection.Factory permissionFilter;
+  private final VisibleRefFilter.Factory refFilter;
   private final Collection<ContributorAgreement> contributorAgreements;
-  private final TagCache tagCache;
-  @Nullable private final SearchingChangeCacheImpl changeCache;
   private final Provider<InternalChangeQuery> queryProvider;
   private final Metrics metrics;
 
@@ -157,19 +153,15 @@
       @GitReceivePackGroups Set<AccountGroup.UUID> receiveGroups,
       ProjectCache pc,
       PermissionCollection.Factory permissionFilter,
-      ChangeNotes.Factory changeNotesFactory,
       ChangeControl.Factory changeControlFactory,
-      TagCache tagCache,
+      VisibleRefFilter.Factory refFilter,
       Provider<InternalChangeQuery> queryProvider,
-      @Nullable SearchingChangeCacheImpl changeCache,
       @CanonicalWebUrl @Nullable String canonicalWebUrl,
       @Assisted CurrentUser who,
       @Assisted ProjectState ps,
       Metrics metrics) {
-    this.changeNotesFactory = changeNotesFactory;
     this.changeControlFactory = changeControlFactory;
-    this.tagCache = tagCache;
-    this.changeCache = changeCache;
+    this.refFilter = refFilter;
     this.uploadGroups = uploadGroups;
     this.receiveGroups = receiveGroups;
     this.permissionFilter = permissionFilter;
@@ -492,12 +484,12 @@
           "Cannot look up change for commit " + commit.name() + " in " + getProject().getName(), e);
     }
     // Scan all visible refs.
-    return canReadCommitFromVisibleRef(db, repo, commit);
+    return canReadCommitFromVisibleRef(repo, commit);
   }
 
-  private boolean canReadCommitFromVisibleRef(ReviewDb db, Repository repo, RevCommit commit) {
+  private boolean canReadCommitFromVisibleRef(Repository repo, RevCommit commit) {
     try (RevWalk rw = new RevWalk(repo)) {
-      return isMergedIntoVisibleRef(repo, db, rw, commit, repo.getAllRefs().values());
+      return isMergedIntoVisibleRef(repo, rw, commit, repo.getAllRefs().values());
     } catch (IOException e) {
       String msg =
           String.format(
@@ -509,10 +501,9 @@
   }
 
   boolean isMergedIntoVisibleRef(
-      Repository repo, ReviewDb db, RevWalk rw, RevCommit commit, Collection<Ref> unfilteredRefs)
+      Repository repo, RevWalk rw, RevCommit commit, Collection<Ref> unfilteredRefs)
       throws IOException {
-    VisibleRefFilter filter =
-        new VisibleRefFilter(tagCache, changeNotesFactory, changeCache, repo, this, db, true);
+    VisibleRefFilter filter = refFilter.create(state, repo);
     Map<String, Ref> m = Maps.newHashMapWithExpectedSize(unfilteredRefs.size());
     for (Ref r : unfilteredRefs) {
       m.put(r.getName(), r);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
index fa5720f..cf67474 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.group.SystemGroupBackend;
 import com.google.gerrit.server.notedb.ChangeNotes;
@@ -260,12 +259,11 @@
   /**
    * Determines whether the user can create a new Git ref.
    *
-   * @param db db for checking change visibility.
    * @param repo repository on which user want to create
    * @param object the object the user will start the reference with.
    * @return {@code true} if the user specified can create a new Git ref
    */
-  public boolean canCreate(ReviewDb db, Repository repo, RevObject object) {
+  public boolean canCreate(Repository repo, RevObject object) {
     if (!isProjectStatePermittingWrite()) {
       return false;
     }
@@ -275,7 +273,7 @@
         // No create permissions.
         return false;
       }
-      return canCreateCommit(db, repo, (RevCommit) object);
+      return canCreateCommit(repo, (RevCommit) object);
     } else if (object instanceof RevTag) {
       final RevTag tag = (RevTag) object;
       try (RevWalk rw = new RevWalk(repo)) {
@@ -302,11 +300,11 @@
 
       RevObject tagObject = tag.getObject();
       if (tagObject instanceof RevCommit) {
-        if (!canCreateCommit(db, repo, (RevCommit) tagObject)) {
+        if (!canCreateCommit(repo, (RevCommit) tagObject)) {
           return false;
         }
       } else {
-        if (!canCreate(db, repo, tagObject)) {
+        if (!canCreate(repo, tagObject)) {
           return false;
         }
       }
@@ -323,12 +321,12 @@
     }
   }
 
-  private boolean canCreateCommit(ReviewDb db, Repository repo, RevCommit commit) {
+  private boolean canCreateCommit(Repository repo, RevCommit commit) {
     if (canUpdate()) {
       // If the user has push permissions, they can create the ref regardless
       // of whether they are pushing any new objects along with the create.
       return true;
-    } else if (isMergedIntoBranchOrTag(db, repo, commit)) {
+    } else if (isMergedIntoBranchOrTag(repo, commit)) {
       // If the user has no push permissions, check whether the object is
       // merged into a branch or tag readable by this user. If so, they are
       // not effectively "pushing" more objects, so they can create the ref
@@ -338,11 +336,11 @@
     return false;
   }
 
-  private boolean isMergedIntoBranchOrTag(ReviewDb db, Repository repo, RevCommit commit) {
+  private boolean isMergedIntoBranchOrTag(Repository repo, RevCommit commit) {
     try (RevWalk rw = new RevWalk(repo)) {
       List<Ref> refs = new ArrayList<>(repo.getRefDatabase().getRefs(Constants.R_HEADS).values());
       refs.addAll(repo.getRefDatabase().getRefs(Constants.R_TAGS).values());
-      return projectControl.isMergedIntoVisibleRef(repo, db, rw, commit, refs);
+      return projectControl.isMergedIntoVisibleRef(repo, rw, commit, refs);
     } catch (IOException e) {
       String msg =
           String.format(
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
index e377574..51bff6c 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
@@ -906,11 +906,9 @@
         Collections.<AccountGroup.UUID>emptySet(),
         projectCache,
         sectionSorter,
-        null,
         changeControlFactory,
-        null,
+        null, // refFilter
         queryProvider,
-        null,
         canonicalWebUrl,
         new MockUser(name, memberOf),
         newProjectState(local),
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/LsUserRefs.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/LsUserRefs.java
index 0c9cafe..4d93fe8 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/LsUserRefs.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/LsUserRefs.java
@@ -17,20 +17,18 @@
 import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
 import static org.eclipse.jgit.lib.RefDatabase.ALL;
 
-import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountResolver;
 import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.SearchingChangeCacheImpl;
-import com.google.gerrit.server.git.TagCache;
 import com.google.gerrit.server.git.VisibleRefFilter;
-import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.util.ManualRequestContext;
+import com.google.gerrit.server.util.OneOffRequestContext;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.gwtorm.server.OrmException;
@@ -50,16 +48,10 @@
 )
 public class LsUserRefs extends SshCommand {
   @Inject private AccountResolver accountResolver;
-
-  @Inject private IdentifiedUser.GenericFactory userFactory;
-
+  @Inject private OneOffRequestContext requestContext;
+  @Inject private VisibleRefFilter.Factory refFilterFactory;
   @Inject private ReviewDb db;
-
-  @Inject private TagCache tagCache;
-
-  @Inject private ChangeNotes.Factory changeNotesFactory;
-
-  @Inject @Nullable private SearchingChangeCacheImpl changeCache;
+  @Inject private GitRepositoryManager repoManager;
 
   @Option(
     name = "--project",
@@ -82,8 +74,6 @@
   @Option(name = "--only-refs-heads", usage = "list only refs under refs/heads")
   private boolean onlyRefsHeads;
 
-  @Inject private GitRepositoryManager repoManager;
-
   @Override
   protected void run() throws Failure {
     Account userAccount;
@@ -92,21 +82,19 @@
     } catch (OrmException e) {
       throw die(e);
     }
-
     if (userAccount == null) {
       stdout.print("No single user could be found when searching for: " + userName + '\n');
       stdout.flush();
       return;
     }
 
-    IdentifiedUser user = userFactory.create(userAccount.getId());
-    ProjectControl userProjectControl = projectControl.forUser(user);
-    try (Repository repo =
-        repoManager.openRepository(userProjectControl.getProject().getNameKey())) {
+    Project.NameKey projectName = projectControl.getProject().getNameKey();
+    try (Repository repo = repoManager.openRepository(projectName);
+        ManualRequestContext ctx = requestContext.openAs(userAccount.getId())) {
       try {
         Map<String, Ref> refsMap =
-            new VisibleRefFilter(
-                    tagCache, changeNotesFactory, changeCache, repo, userProjectControl, db, true)
+            refFilterFactory
+                .create(projectControl.getProjectState(), repo)
                 .filter(repo.getRefDatabase().getRefs(ALL), false);
 
         for (String ref : refsMap.keySet()) {
@@ -115,13 +103,12 @@
           }
         }
       } catch (IOException e) {
-        throw new Failure(
-            1, "fatal: Error reading refs: '" + projectControl.getProject().getNameKey(), e);
+        throw new Failure(1, "fatal: Error reading refs: '" + projectName, e);
       }
     } catch (RepositoryNotFoundException e) {
-      throw die("'" + projectControl.getProject().getNameKey() + "': not a git archive");
-    } catch (IOException e) {
-      throw die("Error opening: '" + projectControl.getProject().getNameKey());
+      throw die("'" + projectName + "': not a git archive");
+    } catch (IOException | OrmException e) {
+      throw die("Error opening: '" + projectName);
     }
   }
 }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java
index 67dfe96..fc3a917 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java
@@ -15,16 +15,11 @@
 package com.google.gerrit.sshd.commands;
 
 import com.google.common.collect.Lists;
-import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.git.SearchingChangeCacheImpl;
-import com.google.gerrit.server.git.TagCache;
 import com.google.gerrit.server.git.TransferConfig;
 import com.google.gerrit.server.git.VisibleRefFilter;
 import com.google.gerrit.server.git.validators.UploadValidationException;
 import com.google.gerrit.server.git.validators.UploadValidators;
-import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.sshd.AbstractGitCommand;
 import com.google.gerrit.sshd.SshSession;
 import com.google.inject.Inject;
@@ -38,22 +33,11 @@
 
 /** Publishes Git repositories over SSH using the Git upload-pack protocol. */
 final class Upload extends AbstractGitCommand {
-  @Inject private ReviewDb db;
-
   @Inject private TransferConfig config;
-
-  @Inject private TagCache tagCache;
-
-  @Inject private ChangeNotes.Factory changeNotesFactory;
-
-  @Inject @Nullable private SearchingChangeCacheImpl changeCache;
-
+  @Inject private VisibleRefFilter.Factory refFilterFactory;
   @Inject private DynamicSet<PreUploadHook> preUploadHooks;
-
   @Inject private DynamicSet<PostUploadHook> postUploadHooks;
-
   @Inject private UploadValidators.Factory uploadValidatorsFactory;
-
   @Inject private SshSession session;
 
   @Override
@@ -63,9 +47,7 @@
     }
 
     final UploadPack up = new UploadPack(repo);
-    up.setAdvertiseRefsHook(
-        new VisibleRefFilter(
-            tagCache, changeNotesFactory, changeCache, repo, projectControl, db, true));
+    up.setAdvertiseRefsHook(refFilterFactory.create(projectControl.getProjectState(), repo));
     up.setPackConfig(config.getPackConfig());
     up.setTimeout(config.getTimeout());
     up.setPostUploadHook(PostUploadHookChain.newChain(Lists.newArrayList(postUploadHooks)));
diff --git a/plugins/replication b/plugins/replication
index f9e2ef3..2a4d0bf 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit f9e2ef3f01d8b4a0e4951ff7195d2989f75a20dc
+Subproject commit 2a4d0bfe10c63c79ca0d47be21756377703e46c0