Reduce usage of ProjectControl

Reduce the usage of ProjectControl by replacing it with ProjectState and
CurrentUser where it was just used as a container for these.

Eventually, {Ref,Change,Project}Control should be package-private. This
commit is an incremental step towards that goal.

Change-Id: I204d7cae3816e81a7859672a6ac3b5d5273997fa
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
index 252d023..ecbcb39 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
@@ -147,7 +147,8 @@
           setParent
               .get()
               .validateParentUpdate(
-                  projectControl,
+                  projectControl.getProject().getNameKey(),
+                  projectControl.getUser().asIdentifiedUser(),
                   MoreObjects.firstNonNull(parentProjectName, allProjects).get(),
                   checkIfOwner);
         } catch (AuthException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewerRecommender.java b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewerRecommender.java
index 7374833..e9152d0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewerRecommender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewerRecommender.java
@@ -37,7 +37,7 @@
 import com.google.gerrit.server.git.WorkQueue;
 import com.google.gerrit.server.index.change.ChangeField;
 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.gerrit.server.query.change.ChangeQueryBuilder;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
@@ -108,7 +108,7 @@
   public List<Account.Id> suggestReviewers(
       ChangeNotes changeNotes,
       SuggestReviewers suggestReviewers,
-      ProjectControl projectControl,
+      ProjectState projectState,
       List<Account.Id> candidateList)
       throws OrmException, IOException, ConfigInvalidException {
     String query = suggestReviewers.getQuery();
@@ -118,7 +118,7 @@
     if (Strings.isNullOrEmpty(query)) {
       reviewerScores = baseRankingForEmptyQuery(baseWeight);
     } else {
-      reviewerScores = baseRankingForCandidateList(candidateList, projectControl, baseWeight);
+      reviewerScores = baseRankingForCandidateList(candidateList, projectState, baseWeight);
     }
 
     // Send the query along with a candidate list to all plugins and merge the
@@ -135,7 +135,7 @@
                   .getProvider()
                   .get()
                   .suggestReviewers(
-                      projectControl.getProject().getNameKey(),
+                      projectState.getProject().getNameKey(),
                       changeNotes.getChangeId(),
                       query,
                       reviewerScores.keySet()));
@@ -227,7 +227,7 @@
   }
 
   private Map<Account.Id, MutableDouble> baseRankingForCandidateList(
-      List<Account.Id> candidates, ProjectControl projectControl, double baseWeight)
+      List<Account.Id> candidates, ProjectState projectState, double baseWeight)
       throws OrmException, IOException, ConfigInvalidException {
     // Get each reviewer's activity based on number of applied labels
     // (weighted 10d), number of comments (weighted 0.5d) and number of owned
@@ -240,11 +240,11 @@
     for (Account.Id id : candidates) {
       try {
         Predicate<ChangeData> projectQuery =
-            changeQueryBuilder.project(projectControl.getProject().getName());
+            changeQueryBuilder.project(projectState.getProject().getName());
 
         // Get all labels for this project and create a compound OR query to
         // fetch all changes where users have applied one of these labels
-        List<LabelType> labelTypes = projectControl.getLabelTypes().getLabelTypes();
+        List<LabelType> labelTypes = projectState.getLabelTypes().getLabelTypes();
         List<Predicate<ChangeData>> labelPredicates = new ArrayList<>(labelTypes.size());
         for (LabelType type : labelTypes) {
           labelPredicates.add(changeQueryBuilder.label(type.getName() + ",user=" + id));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
index 0c1f871..ee25d54 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
@@ -42,7 +42,7 @@
 import com.google.gerrit.server.change.SuggestReviewers;
 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.project.ProjectState;
 import com.google.gerrit.server.query.account.AccountPredicates;
 import com.google.gerrit.server.query.account.AccountQueryBuilder;
 import com.google.gerrit.server.query.account.AccountQueryProcessor;
@@ -145,7 +145,7 @@
   public List<SuggestedReviewerInfo> suggestReviewers(
       ChangeNotes changeNotes,
       SuggestReviewers suggestReviewers,
-      ProjectControl projectControl,
+      ProjectState projectState,
       VisibilityControl visibilityControl,
       boolean excludeGroups)
       throws IOException, OrmException, ConfigInvalidException {
@@ -162,7 +162,7 @@
     }
 
     List<Account.Id> sortedRecommendations =
-        recommendAccounts(changeNotes, suggestReviewers, projectControl, candidateList);
+        recommendAccounts(changeNotes, suggestReviewers, projectState, candidateList);
 
     // Filter accounts by visibility and enforce limit
     List<Account.Id> filteredRecommendations = new ArrayList<>();
@@ -183,10 +183,7 @@
       // important.
       suggestedReviewer.addAll(
           suggestAccountGroups(
-              suggestReviewers,
-              projectControl,
-              visibilityControl,
-              limit - suggestedReviewer.size()));
+              suggestReviewers, projectState, visibilityControl, limit - suggestedReviewer.size()));
     }
 
     if (suggestedReviewer.size() <= limit) {
@@ -215,12 +212,12 @@
   private List<Account.Id> recommendAccounts(
       ChangeNotes changeNotes,
       SuggestReviewers suggestReviewers,
-      ProjectControl projectControl,
+      ProjectState projectState,
       List<Account.Id> candidateList)
       throws OrmException, IOException, ConfigInvalidException {
     try (Timer0.Context ctx = metrics.recommendAccountsLatency.start()) {
       return reviewerRecommender.suggestReviewers(
-          changeNotes, suggestReviewers, projectControl, candidateList);
+          changeNotes, suggestReviewers, projectState, candidateList);
     }
   }
 
@@ -247,16 +244,16 @@
 
   private List<SuggestedReviewerInfo> suggestAccountGroups(
       SuggestReviewers suggestReviewers,
-      ProjectControl projectControl,
+      ProjectState projectState,
       VisibilityControl visibilityControl,
       int limit)
       throws OrmException, IOException {
     try (Timer0.Context ctx = metrics.queryGroupsLatency.start()) {
       List<SuggestedReviewerInfo> groups = new ArrayList<>();
-      for (GroupReference g : suggestAccountGroups(suggestReviewers, projectControl)) {
+      for (GroupReference g : suggestAccountGroups(suggestReviewers, projectState)) {
         GroupAsReviewer result =
             suggestGroupAsReviewer(
-                suggestReviewers, projectControl.getProject(), g, visibilityControl);
+                suggestReviewers, projectState.getProject(), g, visibilityControl);
         if (result.allowed || result.allowedWithConfirmation) {
           GroupBaseInfo info = new GroupBaseInfo();
           info.id = Url.encode(g.getUUID().get());
@@ -278,10 +275,11 @@
   }
 
   private List<GroupReference> suggestAccountGroups(
-      SuggestReviewers suggestReviewers, ProjectControl ctl) {
+      SuggestReviewers suggestReviewers, ProjectState projectState) {
     return Lists.newArrayList(
         Iterables.limit(
-            groupBackend.suggest(suggestReviewers.getQuery(), ctl), suggestReviewers.getLimit()));
+            groupBackend.suggest(suggestReviewers.getQuery(), projectState),
+            suggestReviewers.getLimit()));
   }
 
   private static class GroupAsReviewer {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackend.java
index bf71732..2d46260 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackend.java
@@ -20,7 +20,7 @@
 import com.google.gerrit.extensions.annotations.ExtensionPoint;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
 import java.util.Collection;
 
 /** Implementations of GroupBackend provide lookup and membership accessors to a group system. */
@@ -39,7 +39,7 @@
   GroupDescription.Basic get(AccountGroup.UUID uuid);
 
   /** @return suggestions for the group name sorted by name. */
-  Collection<GroupReference> suggest(String name, @Nullable ProjectControl project);
+  Collection<GroupReference> suggest(String name, @Nullable ProjectState project);
 
   /** @return the group membership checker for the backend. */
   GroupMembership membershipsOf(IdentifiedUser user);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackends.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackends.java
index e029954..803d491 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackends.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackends.java
@@ -17,7 +17,7 @@
 import com.google.common.collect.Iterables;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
 import java.util.Collection;
 import java.util.Comparator;
 
@@ -33,7 +33,7 @@
       };
 
   /**
-   * Runs {@link GroupBackend#suggest(String, ProjectControl)} and filters the result to return the
+   * Runs {@link GroupBackend#suggest(String, ProjectState)} and filters the result to return the
    * best suggestion, or null if one does not exist.
    *
    * @param groupBackend the group backend
@@ -46,7 +46,7 @@
   }
 
   /**
-   * Runs {@link GroupBackend#suggest(String, ProjectControl)} and filters the result to return the
+   * Runs {@link GroupBackend#suggest(String, ProjectState)} and filters the result to return the
    * best suggestion, or null if one does not exist.
    *
    * @param groupBackend the group backend
@@ -56,7 +56,7 @@
    */
   @Nullable
   public static GroupReference findBestSuggestion(
-      GroupBackend groupBackend, String name, @Nullable ProjectControl project) {
+      GroupBackend groupBackend, String name, @Nullable ProjectState project) {
     Collection<GroupReference> refs = groupBackend.suggest(name, project);
     if (refs.size() == 1) {
       return Iterables.getOnlyElement(refs);
@@ -71,7 +71,7 @@
   }
 
   /**
-   * Runs {@link GroupBackend#suggest(String, ProjectControl)} and filters the result to return the
+   * Runs {@link GroupBackend#suggest(String, ProjectState)} and filters the result to return the
    * exact suggestion, or null if one does not exist.
    *
    * @param groupBackend the group backend
@@ -84,7 +84,7 @@
   }
 
   /**
-   * Runs {@link GroupBackend#suggest(String, ProjectControl)} and filters the result to return the
+   * Runs {@link GroupBackend#suggest(String, ProjectState)} and filters the result to return the
    * exact suggestion, or null if one does not exist.
    *
    * @param groupBackend the group backend
@@ -94,7 +94,7 @@
    */
   @Nullable
   public static GroupReference findExactSuggestion(
-      GroupBackend groupBackend, String name, ProjectControl project) {
+      GroupBackend groupBackend, String name, ProjectState project) {
     Collection<GroupReference> refs = groupBackend.suggest(name, project);
     for (GroupReference ref : refs) {
       if (isExactSuggestion(ref, name)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/InternalGroupBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/InternalGroupBackend.java
index 9764d80..0721212 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/InternalGroupBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/InternalGroupBackend.java
@@ -21,7 +21,7 @@
 import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.Collection;
@@ -64,7 +64,7 @@
   }
 
   @Override
-  public Collection<GroupReference> suggest(String name, ProjectControl project) {
+  public Collection<GroupReference> suggest(String name, ProjectState project) {
     return groupCache
         .all()
         .stream()
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 a3223d9..fc9b58a 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
@@ -31,7 +31,7 @@
 import com.google.gerrit.server.StartupCheck;
 import com.google.gerrit.server.StartupException;
 import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.Collection;
@@ -87,7 +87,7 @@
   }
 
   @Override
-  public Collection<GroupReference> suggest(String name, ProjectControl project) {
+  public Collection<GroupReference> suggest(String name, ProjectState project) {
     Set<GroupReference> groups = Sets.newTreeSet(GROUP_REF_NAME_COMPARATOR);
     for (GroupBackend g : backends) {
       groups.addAll(g.suggest(name, project));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
index f560c79..a9fe235 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
@@ -277,7 +277,7 @@
     if (project == null) {
       throw new ResourceNotFoundException(name);
     }
-    return projectJson.format(project.getControl());
+    return projectJson.format(project.getProjectState());
   }
 
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
index 3683b35..51a2ded 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
@@ -34,7 +34,7 @@
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.auth.ldap.Helper.LdapSchema;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.name.Named;
@@ -158,7 +158,7 @@
   }
 
   @Override
-  public Collection<GroupReference> suggest(String name, ProjectControl project) {
+  public Collection<GroupReference> suggest(String name, ProjectState project) {
     AccountGroup.UUID uuid = new AccountGroup.UUID(name);
     if (isLdapUUID(uuid)) {
       GroupDescription.Basic g = get(uuid);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickCommit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickCommit.java
index 5d5a6ae..a3927b5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickCommit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickCommit.java
@@ -76,14 +76,14 @@
     input.message = message.isEmpty() ? commit.getFullMessage() : message;
     String destination = Strings.nullToEmpty(input.destination).trim();
     input.parent = input.parent == null ? 1 : input.parent;
-    Project.NameKey projectName = rsrc.getProject().getProject().getNameKey();
+    Project.NameKey projectName = rsrc.getProjectState().getProject().getNameKey();
 
     if (destination.isEmpty()) {
       throw new BadRequestException("destination must be non-empty");
     }
 
     String refName = RefNames.fullName(destination);
-    CreateChange.checkValidCLA(rsrc.getProject());
+    CreateChange.checkValidCLA(rsrc.getProjectState().controlFor(user.get()));
     permissionBackend
         .user(user)
         .project(projectName)
@@ -99,7 +99,7 @@
               projectName,
               commit,
               input,
-              new Branch.NameKey(rsrc.getProject().getProject().getNameKey(), refName));
+              new Branch.NameKey(rsrc.getProjectState().getProject().getNameKey(), refName));
       return json.noOptions().format(projectName, cherryPickedChangeId);
     } catch (InvalidChangeOperationException e) {
       throw new BadRequestException(e.getMessage());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
index c9827f1..3cdab63 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
@@ -194,7 +194,7 @@
       ObjectId parentCommit;
       List<String> groups;
       if (input.baseChange != null) {
-        List<ChangeControl> ctls = changeFinder.find(input.baseChange, rsrc.getControl().getUser());
+        List<ChangeControl> ctls = changeFinder.find(input.baseChange, rsrc.getUser());
         if (ctls.size() != 1) {
           throw new UnprocessableEntityException("Base change not found: " + input.baseChange);
         }
@@ -253,7 +253,7 @@
         }
         c =
             newMergeCommit(
-                git, oi, rw, rsrc.getControl(), mergeTip, input.merge, author, commitMessage);
+                git, oi, rw, rsrc.getProjectState(), mergeTip, input.merge, author, commitMessage);
       } else {
         // create an empty commit
         c = newCommit(oi, rw, author, mergeTip, commitMessage);
@@ -310,7 +310,7 @@
       Repository repo,
       ObjectInserter oi,
       RevWalk rw,
-      ProjectControl projectControl,
+      ProjectState projectState,
       RevCommit mergeTip,
       MergeInput merge,
       PersonIdent authorIdent,
@@ -320,13 +320,12 @@
       throw new BadRequestException("merge.source must be non-empty");
     }
 
-    ProjectState state = projectControl.getProjectState();
     RevCommit sourceCommit = MergeUtil.resolveCommit(repo, rw, merge.source);
-    if (!commits.canRead(state, repo, sourceCommit)) {
+    if (!commits.canRead(projectState, repo, sourceCommit)) {
       throw new BadRequestException("do not have read permission for: " + merge.source);
     }
 
-    MergeUtil mergeUtil = mergeUtilFactory.create(state);
+    MergeUtil mergeUtil = mergeUtilFactory.create(projectState);
     // default merge strategy from project settings
     String mergeStrategy =
         MoreObjects.firstNonNull(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateMergePatchSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateMergePatchSet.java
index dfc0639..b02f31b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateMergePatchSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateMergePatchSet.java
@@ -44,7 +44,7 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.CommitsCollection;
 import com.google.gerrit.server.project.InvalidChangeOperationException;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.RetryHelper;
@@ -79,6 +79,7 @@
   private final PatchSetUtil psUtil;
   private final MergeUtil.Factory mergeUtilFactory;
   private final PatchSetInserter.Factory patchSetInserterFactory;
+  private final ProjectCache projectCache;
 
   @Inject
   CreateMergePatchSet(
@@ -91,7 +92,8 @@
       PatchSetUtil psUtil,
       MergeUtil.Factory mergeUtilFactory,
       RetryHelper retryHelper,
-      PatchSetInserter.Factory patchSetInserterFactory) {
+      PatchSetInserter.Factory patchSetInserterFactory,
+      ProjectCache projectCache) {
     super(retryHelper);
     this.db = db;
     this.gitManager = gitManager;
@@ -102,6 +104,7 @@
     this.psUtil = psUtil;
     this.mergeUtilFactory = mergeUtilFactory;
     this.patchSetInserterFactory = patchSetInserterFactory;
+    this.projectCache = projectCache;
   }
 
   @Override
@@ -117,8 +120,7 @@
     }
 
     PatchSet ps = psUtil.current(db.get(), rsrc.getNotes());
-    ProjectControl projectControl = rsrc.getControl().getProjectControl();
-    ProjectState state = projectControl.getProjectState();
+    ProjectState projectState = projectCache.checkedGet(rsrc.getProject());
     Change change = rsrc.getChange();
     Project.NameKey project = change.getProject();
     Branch.NameKey dest = change.getDest();
@@ -128,7 +130,7 @@
         RevWalk rw = new RevWalk(reader)) {
 
       RevCommit sourceCommit = MergeUtil.resolveCommit(git, rw, merge.source);
-      if (!commits.canRead(state, git, sourceCommit)) {
+      if (!commits.canRead(projectState, git, sourceCommit)) {
         throw new ResourceNotFoundException(
             "cannot find source commit: " + merge.source + " to merge.");
       }
@@ -140,7 +142,7 @@
       RevCommit newCommit =
           createMergeCommit(
               in,
-              projectControl,
+              projectState,
               dest,
               git,
               oi,
@@ -172,7 +174,7 @@
 
   private RevCommit createMergeCommit(
       MergePatchSetInput in,
-      ProjectControl projectControl,
+      ProjectState projectState,
       Branch.NameKey dest,
       Repository git,
       ObjectInserter oi,
@@ -210,7 +212,7 @@
     String mergeStrategy =
         MoreObjects.firstNonNull(
             Strings.emptyToNull(in.merge.strategy),
-            mergeUtilFactory.create(projectControl.getProjectState()).mergeStrategyName());
+            mergeUtilFactory.create(projectState).mergeStrategyName());
 
     return MergeUtil.createMergeCommit(
         oi, git.getConfig(), mergeTip, sourceCommit, mergeStrategy, author, commitMsg, rw);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/SuggestChangeReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/SuggestChangeReviewers.java
index 0ba0a14..75b60c7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/SuggestChangeReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/SuggestChangeReviewers.java
@@ -74,7 +74,7 @@
     return reviewersUtil.suggestReviewers(
         rsrc.getNotes(),
         this,
-        rsrc.getControl().getProjectControl(),
+        rsrc.getControl().getProjectControl().getProjectState(),
         getVisibility(rsrc),
         excludeGroups);
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
index e1b1dca..23c5fee 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
@@ -302,11 +302,12 @@
       throw new BadRequestException(
           "You should only have no more than one --project and -n with --suggest");
     }
-
     List<GroupReference> groupRefs =
         Lists.newArrayList(
             Iterables.limit(
-                groupBackend.suggest(suggest, Iterables.getFirst(projects, null)),
+                groupBackend.suggest(
+                    suggest,
+                    projects.stream().findFirst().map(pc -> pc.getProjectState()).orElse(null)),
                 limit <= 0 ? 10 : Math.min(limit, 10)));
 
     List<GroupInfo> groupInfos = Lists.newArrayListWithCapacity(groupRefs.size());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/SystemGroupBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/SystemGroupBackend.java
index cbab0fc..56c0208 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/SystemGroupBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/SystemGroupBackend.java
@@ -32,7 +32,7 @@
 import com.google.gerrit.server.account.GroupMembership;
 import com.google.gerrit.server.account.ListGroupMembership;
 import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.ArrayList;
@@ -156,7 +156,7 @@
   }
 
   @Override
-  public Collection<GroupReference> suggest(String name, ProjectControl project) {
+  public Collection<GroupReference> suggest(String name, ProjectState project) {
     String nameLC = name.toLowerCase(Locale.US);
     SortedMap<String, GroupReference> matches = names.tailMap(nameLC);
     if (matches.isEmpty()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectResource.java
index f0d127d..edd3fb6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectResource.java
@@ -24,9 +24,9 @@
       new TypeLiteral<RestView<ChildProjectResource>>() {};
 
   private final ProjectResource parent;
-  private final ProjectControl child;
+  private final ProjectState child;
 
-  public ChildProjectResource(ProjectResource parent, ProjectControl child) {
+  public ChildProjectResource(ProjectResource parent, ProjectState child) {
     this.parent = parent;
     this.child = child;
   }
@@ -35,12 +35,12 @@
     return parent;
   }
 
-  public ProjectControl getChild() {
+  public ProjectState getChild() {
     return child;
   }
 
   public boolean isDirectChild() {
-    ProjectState firstParent = Iterables.getFirst(child.getProjectState().parents(), null);
+    ProjectState firstParent = Iterables.getFirst(child.parents(), null);
     return firstParent != null && parent.getNameKey().equals(firstParent.getProject().getNameKey());
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectsCollection.java
index 2110034..0cd7d19 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectsCollection.java
@@ -55,7 +55,7 @@
     ProjectResource p = projectsCollection.parse(TopLevelResource.INSTANCE, id);
     for (ProjectState pp : p.getControl().getProjectState().parents()) {
       if (parent.getNameKey().equals(pp.getProject().getNameKey())) {
-        return new ChildProjectResource(parent, p.getControl());
+        return new ChildProjectResource(parent, p.getProjectState());
       }
     }
     throw new ResourceNotFoundException(id);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitIncludedIn.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitIncludedIn.java
index 8d9127b..3152e97 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitIncludedIn.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitIncludedIn.java
@@ -38,7 +38,7 @@
   public IncludedInInfo apply(CommitResource rsrc)
       throws RestApiException, OrmException, IOException {
     RevCommit commit = rsrc.getCommit();
-    Project.NameKey project = rsrc.getProject().getProject().getNameKey();
+    Project.NameKey project = rsrc.getProjectState().getProject().getNameKey();
     return includedIn.apply(project, commit.getId().getName());
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitResource.java
index 8065e0f..0925524 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitResource.java
@@ -31,8 +31,8 @@
     this.commit = commit;
   }
 
-  public ProjectControl getProject() {
-    return project.getControl();
+  public ProjectState getProjectState() {
+    return project.getProjectState();
   }
 
   public RevCommit getCommit() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
index 8177e41..9b355f1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
@@ -91,7 +91,6 @@
   private final Provider<GroupsCollection> groupsCollection;
   private final DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners;
   private final ProjectJson json;
-  private final ProjectControl.GenericFactory projectControlFactory;
   private final GitRepositoryManager repoManager;
   private final DynamicSet<NewProjectCreatedListener> createdListeners;
   private final ProjectCache projectCache;
@@ -112,7 +111,6 @@
       Provider<GroupsCollection> groupsCollection,
       ProjectJson json,
       DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners,
-      ProjectControl.GenericFactory projectControlFactory,
       GitRepositoryManager repoManager,
       DynamicSet<NewProjectCreatedListener> createdListeners,
       ProjectCache projectCache,
@@ -130,7 +128,6 @@
     this.groupsCollection = groupsCollection;
     this.projectCreationValidationListeners = projectCreationValidationListeners;
     this.json = json;
-    this.projectControlFactory = projectControlFactory;
     this.repoManager = repoManager;
     this.createdListeners = createdListeners;
     this.projectCache = projectCache;
@@ -163,7 +160,7 @@
 
     String parentName =
         MoreObjects.firstNonNull(Strings.emptyToNull(input.parent), allProjects.get());
-    args.newParent = projectsCollection.get().parse(parentName, false).getControl();
+    args.newParent = projectsCollection.get().parse(parentName, false).getNameKey();
     args.createEmptyCommit = input.createEmptyCommit;
     args.permissionsOnly = input.permissionsOnly;
     args.projectDescription = Strings.emptyToNull(input.description);
@@ -203,25 +200,17 @@
       }
     }
 
-    Project p = createProject(args);
-
-    ProjectControl projectControl;
-    try {
-      projectControl = projectControlFactory.controlFor(p.getNameKey(), identifiedUser.get());
-    } catch (NoSuchProjectException e) {
-      throw new ResourceNotFoundException(p.getName());
-    }
-
+    ProjectState projectState = createProject(args);
     if (input.pluginConfigValues != null) {
       ConfigInput in = new ConfigInput();
       in.pluginConfigValues = input.pluginConfigValues;
-      putConfig.get().apply(projectControl, in);
+      putConfig.get().apply(projectState, in);
     }
 
-    return Response.created(json.format(projectControl));
+    return Response.created(json.format(projectState));
   }
 
-  private Project createProject(CreateProjectArgs args)
+  private ProjectState createProject(CreateProjectArgs args)
       throws BadRequestException, ResourceConflictException, IOException, ConfigInvalidException {
     final Project.NameKey nameKey = args.getProject();
     try {
@@ -246,7 +235,7 @@
 
         fire(nameKey, head);
 
-        return projectCache.get(nameKey).getProject();
+        return projectCache.get(nameKey);
       }
     } catch (RepositoryCaseMismatchException e) {
       throw new ResourceConflictException(
@@ -281,7 +270,7 @@
       newProject.setRequireChangeID(args.changeIdRequired);
       newProject.setMaxObjectSizeLimit(args.maxObjectSizeLimit);
       if (args.newParent != null) {
-        newProject.setParentName(args.newParent.getProject().getNameKey());
+        newProject.setParentName(args.newParent);
       }
 
       if (!args.ownerIds.isEmpty()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProjectArgs.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProjectArgs.java
index 5642721..b98ffc2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProjectArgs.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProjectArgs.java
@@ -24,7 +24,7 @@
 
   private Project.NameKey projectName;
   public List<AccountGroup.UUID> ownerIds;
-  public ProjectControl newParent;
+  public Project.NameKey newParent;
   public String projectDescription;
   public SubmitType submitType;
   public InheritableBoolean contributorAgreements;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/FileResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/FileResource.java
index 43b849f..8c1c1e2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/FileResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/FileResource.java
@@ -32,30 +32,30 @@
       new TypeLiteral<RestView<FileResource>>() {};
 
   public static FileResource create(
-      GitRepositoryManager repoManager, ProjectControl project, ObjectId rev, String path)
+      GitRepositoryManager repoManager, ProjectState projectState, ObjectId rev, String path)
       throws ResourceNotFoundException, IOException {
-    try (Repository repo = repoManager.openRepository(project.getProject().getNameKey());
+    try (Repository repo = repoManager.openRepository(projectState.getProject().getNameKey());
         RevWalk rw = new RevWalk(repo)) {
       RevTree tree = rw.parseTree(rev);
       if (TreeWalk.forPath(repo, path, tree) != null) {
-        return new FileResource(project, rev, path);
+        return new FileResource(projectState, rev, path);
       }
     }
     throw new ResourceNotFoundException(IdString.fromDecoded(path));
   }
 
-  private final ProjectControl project;
+  private final ProjectState projectState;
   private final ObjectId rev;
   private final String path;
 
-  public FileResource(ProjectControl project, ObjectId rev, String path) {
-    this.project = project;
+  public FileResource(ProjectState projectState, ObjectId rev, String path) {
+    this.projectState = projectState;
     this.rev = rev;
     this.path = path;
   }
 
-  public ProjectControl getProject() {
-    return project;
+  public ProjectState getProjectState() {
+    return projectState;
   }
 
   public ObjectId getRev() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/FilesCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/FilesCollection.java
index 8d462a1..dd32f85 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/FilesCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/FilesCollection.java
@@ -45,7 +45,7 @@
   public FileResource parse(BranchResource parent, IdString id)
       throws ResourceNotFoundException, IOException {
     return FileResource.create(
-        repoManager, parent.getControl(), ObjectId.fromString(parent.getRevision()), id.get());
+        repoManager, parent.getProjectState(), ObjectId.fromString(parent.getRevision()), id.get());
   }
 
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/FilesInCommitCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/FilesInCommitCollection.java
index 807ac53..7144099 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/FilesInCommitCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/FilesInCommitCollection.java
@@ -46,9 +46,9 @@
   public FileResource parse(CommitResource parent, IdString id)
       throws ResourceNotFoundException, IOException {
     if (Patch.isMagic(id.get())) {
-      return new FileResource(parent.getProject(), parent.getCommit(), id.get());
+      return new FileResource(parent.getProjectState(), parent.getCommit(), id.get());
     }
-    return FileResource.create(repoManager, parent.getProject(), parent.getCommit(), id.get());
+    return FileResource.create(repoManager, parent.getProjectState(), parent.getCommit(), id.get());
   }
 
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetContent.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetContent.java
index 387c966..b5294c4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetContent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetContent.java
@@ -35,7 +35,6 @@
   @Override
   public BinaryResult apply(FileResource rsrc)
       throws ResourceNotFoundException, BadRequestException, IOException {
-    return fileContentUtil.getContent(
-        rsrc.getProject().getProjectState(), rsrc.getRev(), rsrc.getPath(), null);
+    return fileContentUtil.getContent(rsrc.getProjectState(), rsrc.getRev(), rsrc.getPath(), null);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDescription.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDescription.java
index bace0a8..dd03e97 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDescription.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDescription.java
@@ -16,14 +16,12 @@
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Project;
 import com.google.inject.Singleton;
 
 @Singleton
 public class GetDescription implements RestReadView<ProjectResource> {
   @Override
-  public String apply(ProjectResource resource) {
-    Project project = resource.getControl().getProject();
-    return Strings.nullToEmpty(project.getDescription());
+  public String apply(ProjectResource rsrc) {
+    return Strings.nullToEmpty(rsrc.getProjectState().getProject().getDescription());
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetHead.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetHead.java
index f8f631e..31dc7bf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetHead.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetHead.java
@@ -59,7 +59,7 @@
       } else if (head.isSymbolic()) {
         String n = head.getTarget().getName();
         permissionBackend
-            .user(rsrc.getControl().getUser())
+            .user(rsrc.getUser())
             .project(rsrc.getNameKey())
             .ref(n)
             .check(RefPermission.READ);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetParent.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetParent.java
index 8161cfd..8f0b6f0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetParent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetParent.java
@@ -31,7 +31,7 @@
 
   @Override
   public String apply(ProjectResource resource) {
-    Project project = resource.getControl().getProject();
+    Project project = resource.getProjectState().getProject();
     Project.NameKey parentName = project.getParent(allProjectsName);
     return parentName != null ? parentName.get() : "";
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetProject.java
index cf64a21..8288610 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetProject.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetProject.java
@@ -31,6 +31,6 @@
 
   @Override
   public ProjectInfo apply(ProjectResource rsrc) {
-    return json.format(rsrc.getControl());
+    return json.format(rsrc.getProjectState());
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListDashboards.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListDashboards.java
index cdeabc3..e1d6c14 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListDashboards.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListDashboards.java
@@ -71,13 +71,13 @@
       throws ResourceNotFoundException, IOException, PermissionBackendException {
     String project = rsrc.getName();
     if (!inherited) {
-      return scan(rsrc.getControl(), project, true);
+      return scan(rsrc.getProjectState(), project, true);
     }
 
     List<List<DashboardInfo>> all = new ArrayList<>();
     boolean setDefault = true;
     for (ProjectState ps : tree(rsrc)) {
-      List<DashboardInfo> list = scan(ps.controlFor(user.get()), project, setDefault);
+      List<DashboardInfo> list = scan(ps, project, setDefault);
       for (DashboardInfo d : list) {
         if (d.isDefault != null && Boolean.TRUE.equals(d.isDefault)) {
           setDefault = false;
@@ -100,17 +100,17 @@
     return tree.values();
   }
 
-  private List<DashboardInfo> scan(ProjectControl ctl, String project, boolean setDefault)
+  private List<DashboardInfo> scan(ProjectState state, String project, boolean setDefault)
       throws ResourceNotFoundException, IOException, PermissionBackendException {
-    Project.NameKey projectName = ctl.getProject().getNameKey();
+    Project.NameKey projectName = state.getProject().getNameKey();
     PermissionBackend.ForProject perm =
-        permissionBackend.user(user).project(ctl.getProject().getNameKey());
+        permissionBackend.user(user).project(state.getProject().getNameKey());
     try (Repository git = gitManager.openRepository(projectName);
         RevWalk rw = new RevWalk(git)) {
       List<DashboardInfo> all = new ArrayList<>();
       for (Ref ref : git.getRefDatabase().getRefs(REFS_DASHBOARDS).values()) {
         if (perm.ref(ref.getName()).test(RefPermission.READ)) {
-          all.addAll(scanDashboards(ctl.getProject(), git, rw, ref, project, setDefault));
+          all.addAll(scanDashboards(state.getProject(), git, rw, ref, project, setDefault));
         }
       }
       return all;
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 8d03b6a..302f8e6 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
@@ -122,12 +122,11 @@
     PermissionBackend.ForProject perm = permissionBackend.user(user).project(resource.getNameKey());
     try (Repository repo = getRepository(resource.getNameKey());
         RevWalk rw = new RevWalk(repo)) {
-      ProjectControl pctl = resource.getControl();
       Map<String, Ref> all =
-          visibleTags(pctl, repo, repo.getRefDatabase().getRefs(Constants.R_TAGS));
+          visibleTags(
+              resource.getProjectState(), repo, repo.getRefDatabase().getRefs(Constants.R_TAGS));
       for (Ref ref : all.values()) {
-        tags.add(
-            createTagInfo(perm.ref(ref.getName()), ref, rw, pctl.getProject().getNameKey(), links));
+        tags.add(createTagInfo(perm.ref(ref.getName()), ref, rw, resource.getNameKey(), links));
       }
     }
 
@@ -157,16 +156,17 @@
         tagName = Constants.R_TAGS + tagName;
       }
       Ref ref = repo.getRefDatabase().exactRef(tagName);
-      ProjectControl pctl = resource.getControl();
-      if (ref != null && !visibleTags(pctl, repo, ImmutableMap.of(ref.getName(), ref)).isEmpty()) {
+      if (ref != null
+          && !visibleTags(resource.getProjectState(), repo, ImmutableMap.of(ref.getName(), ref))
+              .isEmpty()) {
         return createTagInfo(
             permissionBackend
-                .user(pctl.getUser())
+                .user(resource.getUser())
                 .project(resource.getNameKey())
                 .ref(ref.getName()),
             ref,
             rw,
-            pctl.getProject().getNameKey(),
+            resource.getNameKey(),
             links);
       }
     }
@@ -213,11 +213,7 @@
     }
   }
 
-  private Map<String, Ref> visibleTags(
-      ProjectControl pctl, Repository repo, Map<String, Ref> tags) {
-    return refFilterFactory
-        .create(pctl.getProjectState(), repo)
-        .setShowMetadata(false)
-        .filter(tags, true);
+  private Map<String, Ref> visibleTags(ProjectState state, Repository repo, Map<String, Ref> tags) {
+    return refFilterFactory.create(state, repo).setShowMetadata(false).filter(tags, true);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectJson.java
index 610eb20..5ffd4da 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectJson.java
@@ -42,10 +42,10 @@
     this.webLinks = webLinks;
   }
 
-  public ProjectInfo format(ProjectControl ctl) {
-    ProjectInfo info = format(ctl.getProject());
+  public ProjectInfo format(ProjectState state) {
+    ProjectInfo info = format(state.getProject());
     info.labels = new HashMap<>();
-    for (LabelType t : ctl.getLabelTypes().getLabelTypes()) {
+    for (LabelType t : state.getLabelTypes().getLabelTypes()) {
       LabelTypeInfo labelInfo = new LabelTypeInfo();
       labelInfo.values =
           t.getValues()
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectResource.java
index 2601a4a..a91ba62 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectResource.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.extensions.restapi.RestResource;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.CurrentUser;
 import com.google.inject.TypeLiteral;
 
 public class ProjectResource implements RestResource {
@@ -45,6 +46,10 @@
     return control.getProjectState();
   }
 
+  public CurrentUser getUser() {
+    return getControl().getUser();
+  }
+
   public ProjectControl getControl() {
     return control;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
index b0fa036..0775bcf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
@@ -100,12 +100,12 @@
     if (!rsrc.getControl().isOwner()) {
       throw new AuthException("restricted to project owner");
     }
-    return apply(rsrc.getControl(), input);
+    return apply(rsrc.getProjectState(), input);
   }
 
-  public ConfigInfo apply(ProjectControl ctrl, ConfigInput input)
+  public ConfigInfo apply(ProjectState projectState, ConfigInput input)
       throws ResourceNotFoundException, BadRequestException, ResourceConflictException {
-    Project.NameKey projectName = ctrl.getProject().getNameKey();
+    Project.NameKey projectName = projectState.getProject().getNameKey();
     if (input == null) {
       throw new BadRequestException("config is required");
     }
@@ -172,7 +172,7 @@
       }
 
       if (input.pluginConfigValues != null) {
-        setPluginConfigValues(ctrl.getProjectState(), projectConfig, input.pluginConfigValues);
+        setPluginConfigValues(projectState, projectConfig, input.pluginConfigValues);
       }
 
       md.setMessage("Modified project settings\n");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java
index 6c55c37..ce0f979 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java
@@ -190,7 +190,8 @@
           setParent
               .get()
               .validateParentUpdate(
-                  projectControl,
+                  projectControl.getProject().getNameKey(),
+                  projectControl.getUser().asIdentifiedUser(),
                   MoreObjects.firstNonNull(newParentProjectName, allProjects).get(),
                   true);
         } catch (UnprocessableEntityException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
index 56e3273..07f7ead 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
@@ -74,10 +74,11 @@
   public String apply(ProjectResource rsrc, Input input, boolean checkIfAdmin)
       throws AuthException, ResourceConflictException, ResourceNotFoundException,
           UnprocessableEntityException, IOException, PermissionBackendException {
-    ProjectControl ctl = rsrc.getControl();
+    IdentifiedUser user = rsrc.getUser().asIdentifiedUser();
     String parentName =
         MoreObjects.firstNonNull(Strings.emptyToNull(input.parent), allProjects.get());
-    validateParentUpdate(ctl, parentName, checkIfAdmin);
+    validateParentUpdate(
+        rsrc.getProjectState().getProject().getNameKey(), user, parentName, checkIfAdmin);
     try (MetaDataUpdate md = updateFactory.create(rsrc.getNameKey())) {
       ProjectConfig config = ProjectConfig.read(md);
       Project project = config.getProject();
@@ -89,10 +90,10 @@
       } else if (!msg.endsWith("\n")) {
         msg += "\n";
       }
-      md.setAuthor(ctl.getUser().asIdentifiedUser());
+      md.setAuthor(user);
       md.setMessage(msg);
       config.commit(md);
-      cache.evict(ctl.getProject());
+      cache.evict(rsrc.getProjectState().getProject());
 
       Project.NameKey parent = project.getParent(allProjects);
       checkNotNull(parent);
@@ -105,15 +106,15 @@
     }
   }
 
-  public void validateParentUpdate(ProjectControl ctl, String newParent, boolean checkIfAdmin)
+  public void validateParentUpdate(
+      Project.NameKey project, IdentifiedUser user, String newParent, boolean checkIfAdmin)
       throws AuthException, ResourceConflictException, UnprocessableEntityException,
           PermissionBackendException {
-    IdentifiedUser user = ctl.getUser().asIdentifiedUser();
     if (checkIfAdmin) {
       permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
     }
 
-    if (ctl.getProject().getNameKey().equals(allProjects)) {
+    if (project.equals(allProjects)) {
       throw new ResourceConflictException("cannot set parent of " + allProjects.get());
     }
 
@@ -127,14 +128,11 @@
       if (Iterables.tryFind(
               parent.tree(),
               p -> {
-                return p.getProject().getNameKey().equals(ctl.getProject().getNameKey());
+                return p.getProject().getNameKey().equals(project);
               })
           .isPresent()) {
         throw new ResourceConflictException(
-            "cycle exists between "
-                + ctl.getProject().getName()
-                + " and "
-                + parent.getProject().getName());
+            "cycle exists between " + project.get() + " and " + parent.getProject().getName());
       }
     }
   }
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
index 50d04dd..262ba95 160000
--- a/plugins/singleusergroup
+++ b/plugins/singleusergroup
@@ -1 +1 @@
-Subproject commit 50d04dd09a747868cd2f2707d1aac3d9b2a2d630
+Subproject commit 262ba951fbb24af6094be40637fbd261d5e08218