Merge "Only link to plugin URL if a plugin URL exists"
diff --git a/Documentation/metrics.txt b/Documentation/metrics.txt
index 47e2505..229c463 100644
--- a/Documentation/metrics.txt
+++ b/Documentation/metrics.txt
@@ -13,6 +13,13 @@
 * `build/label`: Version of Gerrit server software.
 * `events`: Triggered events.
 
+=== Actions
+
+* `action/retry_attempt_counts`: Distribution of number of attempts made
+by RetryHelper to execute an action (1 == single attempt, no retry)
+* `action/retry_timeout_count`: Number of action executions of RetryHelper
+that ultimately timed out
+
 === Process
 
 * `proc/birth_timestamp`: Time at which the Gerrit process started.
@@ -87,10 +94,6 @@
 
 * `batch_update/execute_change_ops`: BatchUpdate change update latency,
 excluding reindexing
-* `batch_update/retry_attempt_counts`: Distribution of number of attempts made
-by RetryHelper (1 == single attempt, no retry)
-* `batch_update/retry_timeout_count`: Number of executions of RetryHelper that
-ultimately timed out
 
 === NoteDb
 
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 384f588..c3349f1 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -348,6 +348,11 @@
     GerritServer.Description methodDesc =
         GerritServer.Description.forTestMethod(description, configName);
 
+    testRequiresSsh = classDesc.useSshAnnotation() || methodDesc.useSshAnnotation();
+    if (!testRequiresSsh) {
+      baseConfig.setString("sshd", null, "listenAddress", "off");
+    }
+
     baseConfig.setInt("receive", null, "changeUpdateThreads", 4);
     if (classDesc.equals(methodDesc) && !classDesc.sandboxed() && !methodDesc.sandboxed()) {
       if (commonServer == null) {
@@ -388,7 +393,6 @@
     adminRestSession = new RestSession(server, admin);
     userRestSession = new RestSession(server, user);
 
-    testRequiresSsh = classDesc.useSshAnnotation() || methodDesc.useSshAnnotation();
     if (testRequiresSsh
         && SshMode.useSsh()
         && (adminSshSession == null || userSshSession == null)) {
diff --git a/java/com/google/gerrit/acceptance/AccountCreator.java b/java/com/google/gerrit/acceptance/AccountCreator.java
index ef42ad3..21e3cbb 100644
--- a/java/com/google/gerrit/acceptance/AccountCreator.java
+++ b/java/com/google/gerrit/acceptance/AccountCreator.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.account.VersionedAuthorizedKeys;
 import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gerrit.server.account.externalids.ExternalIdsUpdate;
 import com.google.gerrit.server.group.InternalGroup;
 import com.google.gerrit.server.group.ServerInitiated;
 import com.google.gerrit.server.group.db.GroupsUpdate;
@@ -65,7 +64,6 @@
   private final GroupCache groupCache;
   private final Provider<GroupsUpdate> groupsUpdateProvider;
   private final SshKeyCache sshKeyCache;
-  private final ExternalIdsUpdate.Server externalIdsUpdate;
   private final boolean sshEnabled;
 
   @Inject
@@ -77,7 +75,6 @@
       GroupCache groupCache,
       @ServerInitiated Provider<GroupsUpdate> groupsUpdateProvider,
       SshKeyCache sshKeyCache,
-      ExternalIdsUpdate.Server externalIdsUpdate,
       @SshEnabled boolean sshEnabled) {
     accounts = new HashMap<>();
     reviewDbProvider = schema;
@@ -87,7 +84,6 @@
     this.groupCache = groupCache;
     this.groupsUpdateProvider = groupsUpdateProvider;
     this.sshKeyCache = sshKeyCache;
-    this.externalIdsUpdate = externalIdsUpdate;
     this.sshEnabled = sshEnabled;
   }
 
@@ -115,11 +111,13 @@
       if (email != null) {
         extIds.add(ExternalId.createEmail(id, email));
       }
-      externalIdsUpdate.create().insert(extIds);
 
       accountsUpdate
           .create()
-          .insert("Create Test Account", id, u -> u.setFullName(fullName).setPreferredEmail(email));
+          .insert(
+              "Create Test Account",
+              id,
+              u -> u.setFullName(fullName).setPreferredEmail(email).addExternalIds(extIds));
 
       if (groupNames != null) {
         for (String n : groupNames) {
diff --git a/java/com/google/gerrit/acceptance/BUILD b/java/com/google/gerrit/acceptance/BUILD
index 9d551c4..f42749a 100644
--- a/java/com/google/gerrit/acceptance/BUILD
+++ b/java/com/google/gerrit/acceptance/BUILD
@@ -15,6 +15,7 @@
         "//java/com/google/gerrit/extensions/restapi/testing:restapi-test-util",
         "//java/com/google/gerrit/gpg/testing:gpg-test-util",
         "//java/com/google/gerrit/httpd",
+        "//java/com/google/gerrit/index",
         "//java/com/google/gerrit/launcher",
         "//java/com/google/gerrit/lucene",
         "//java/com/google/gerrit/metrics",
@@ -25,6 +26,7 @@
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/server/git/receive",
         "//java/com/google/gerrit/server/project/testing:project-test-util",
+        "//java/com/google/gerrit/server/restapi",
         "//java/com/google/gerrit/sshd",
         "//java/com/google/gerrit/testing:gerrit-test-util",
         "//lib:args4j",
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index f8ed3b6..0b14cf1 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.lucene.LuceneIndexModule;
 import com.google.gerrit.pgm.Daemon;
 import com.google.gerrit.pgm.Init;
+import com.google.gerrit.pgm.init.InitSshd;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
 import com.google.gerrit.server.ssh.NoSshModule;
@@ -376,7 +377,10 @@
     String url = "http://" + forceEphemeralPort + "/";
     cfg.setString("gerrit", null, "canonicalWebUrl", url);
     cfg.setString("httpd", null, "listenUrl", url);
-    cfg.setString("sshd", null, "listenAddress", forceEphemeralPort);
+
+    if (cfg.getString("sshd", null, "listenAddress") == null) {
+      cfg.setString("sshd", null, "listenAddress", forceEphemeralPort);
+    }
     cfg.setBoolean("sshd", null, "testUseInsecureRandom", true);
     cfg.unset("cache", null, "directory");
     cfg.setString("gerrit", null, "basePath", "git");
@@ -452,7 +456,10 @@
     url = cfg.getString("gerrit", null, "canonicalWebUrl");
     URI uri = URI.create(url);
 
-    sshdAddress = SocketUtil.resolve(cfg.getString("sshd", null, "listenAddress"), 0);
+    String addr = cfg.getString("sshd", null, "listenAddress");
+    if (!InitSshd.isOff(addr)) {
+      sshdAddress = SocketUtil.resolve(cfg.getString("sshd", null, "listenAddress"), 0);
+    }
     httpAddress = new InetSocketAddress(uri.getHost(), uri.getPort());
   }
 
diff --git a/java/com/google/gerrit/gpg/server/GpgKeys.java b/java/com/google/gerrit/gpg/server/GpgKeys.java
index 303499e..63c0476 100644
--- a/java/com/google/gerrit/gpg/server/GpgKeys.java
+++ b/java/com/google/gerrit/gpg/server/GpgKeys.java
@@ -91,11 +91,6 @@
   public GpgKey parse(AccountResource parent, IdString id)
       throws ResourceNotFoundException, PGPException, OrmException, IOException {
     checkVisible(self, parent);
-    String str = CharMatcher.whitespace().removeFrom(id.get()).toUpperCase();
-    if ((str.length() != 8 && str.length() != 40)
-        || !CharMatcher.anyOf("0123456789ABCDEF").matchesAllOf(str)) {
-      throw new ResourceNotFoundException(id);
-    }
 
     byte[] fp = parseFingerprint(id.get(), getGpgExtIds(parent));
     try (PublicKeyStore store = storeProvider.get()) {
diff --git a/java/com/google/gerrit/httpd/raw/HostPageServlet.java b/java/com/google/gerrit/httpd/raw/HostPageServlet.java
index 51340ae..ad903c7 100644
--- a/java/com/google/gerrit/httpd/raw/HostPageServlet.java
+++ b/java/com/google/gerrit/httpd/raw/HostPageServlet.java
@@ -32,12 +32,12 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountResource;
-import com.google.gerrit.server.account.GetDiffPreferences;
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.restapi.account.GetDiffPreferences;
 import com.google.gwtexpui.server.CacheHeaders;
 import com.google.gwtjsonrpc.server.JsonServlet;
 import com.google.gwtjsonrpc.server.RPCServletUtils;
diff --git a/java/com/google/gerrit/httpd/restapi/AccessRestApiServlet.java b/java/com/google/gerrit/httpd/restapi/AccessRestApiServlet.java
index 0d1e53c..7e3e0ca 100644
--- a/java/com/google/gerrit/httpd/restapi/AccessRestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/AccessRestApiServlet.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.httpd.restapi;
 
-import com.google.gerrit.server.access.AccessCollection;
+import com.google.gerrit.server.restapi.access.AccessCollection;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/httpd/restapi/AccountsRestApiServlet.java b/java/com/google/gerrit/httpd/restapi/AccountsRestApiServlet.java
index ee57000..a1effb1 100644
--- a/java/com/google/gerrit/httpd/restapi/AccountsRestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/AccountsRestApiServlet.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.httpd.restapi;
 
-import com.google.gerrit.server.account.AccountsCollection;
+import com.google.gerrit.server.restapi.account.AccountsCollection;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/httpd/restapi/ChangesRestApiServlet.java b/java/com/google/gerrit/httpd/restapi/ChangesRestApiServlet.java
index ccafc6d..d35eb3e 100644
--- a/java/com/google/gerrit/httpd/restapi/ChangesRestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/ChangesRestApiServlet.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.httpd.restapi;
 
-import com.google.gerrit.server.change.ChangesCollection;
+import com.google.gerrit.server.restapi.change.ChangesCollection;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/httpd/restapi/GroupsRestApiServlet.java b/java/com/google/gerrit/httpd/restapi/GroupsRestApiServlet.java
index 5c7502f..fff696a 100644
--- a/java/com/google/gerrit/httpd/restapi/GroupsRestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/GroupsRestApiServlet.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.httpd.restapi;
 
-import com.google.gerrit.server.group.GroupsCollection;
+import com.google.gerrit.server.restapi.group.GroupsCollection;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/httpd/restapi/ProjectsRestApiServlet.java b/java/com/google/gerrit/httpd/restapi/ProjectsRestApiServlet.java
index f34608a..d6b5db0 100644
--- a/java/com/google/gerrit/httpd/restapi/ProjectsRestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/ProjectsRestApiServlet.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.httpd.restapi;
 
-import com.google.gerrit.server.project.ProjectsCollection;
+import com.google.gerrit.server.restapi.project.ProjectsCollection;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java b/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
index 7427c42..4af124f 100644
--- a/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
+++ b/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
@@ -31,7 +31,7 @@
 import com.google.gerrit.server.project.ContributorAgreementsChecker;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.SetParent;
+import com.google.gerrit.server.restapi.project.SetParent;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
diff --git a/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java b/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
index 14885b3..0240e2e 100644
--- a/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
+++ b/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
@@ -45,7 +45,7 @@
 import com.google.gerrit.server.project.ContributorAgreementsChecker;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.RefPattern;
-import com.google.gerrit.server.project.SetParent;
+import com.google.gerrit.server.restapi.project.SetParent;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Provider;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java b/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
index bdb7fe5..81957d6 100644
--- a/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
+++ b/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
@@ -35,8 +35,6 @@
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.change.ChangeInserter;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.ChangesCollection;
-import com.google.gerrit.server.change.PostReviewers;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
@@ -47,7 +45,9 @@
 import com.google.gerrit.server.permissions.RefPermission;
 import com.google.gerrit.server.project.ContributorAgreementsChecker;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.SetParent;
+import com.google.gerrit.server.restapi.change.ChangesCollection;
+import com.google.gerrit.server.restapi.change.PostReviewers;
+import com.google.gerrit.server.restapi.project.SetParent;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/pgm/init/InitSshd.java b/java/com/google/gerrit/pgm/init/InitSshd.java
index 0cad722..043f3ee 100644
--- a/java/com/google/gerrit/pgm/init/InitSshd.java
+++ b/java/com/google/gerrit/pgm/init/InitSshd.java
@@ -31,7 +31,7 @@
 
 /** Initialize the {@code sshd} configuration section. */
 @Singleton
-class InitSshd implements InitStep {
+public class InitSshd implements InitStep {
   private final ConsoleUI ui;
   private final SitePaths site;
   private final Section sshd;
@@ -73,7 +73,7 @@
     remover.remove("bc(pg|pkix|prov)-.*[.]jar");
   }
 
-  private static boolean isOff(String listenHostname) {
+  public static boolean isOff(String listenHostname) {
     return "off".equalsIgnoreCase(listenHostname)
         || "none".equalsIgnoreCase(listenHostname)
         || "no".equalsIgnoreCase(listenHostname);
diff --git a/java/com/google/gerrit/pgm/util/BUILD b/java/com/google/gerrit/pgm/util/BUILD
index d94211c..d6e44bd 100644
--- a/java/com/google/gerrit/pgm/util/BUILD
+++ b/java/com/google/gerrit/pgm/util/BUILD
@@ -13,6 +13,7 @@
         "//java/com/google/gerrit/server:module",
         "//java/com/google/gerrit/server/cache/h2",
         "//java/com/google/gerrit/server/git/receive",
+        "//java/com/google/gerrit/server/restapi",
         "//java/com/google/gerrit/server/schema",
         "//java/com/google/gerrit/util/cli",
         "//lib:args4j",
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 3fc2a4a..d39c73a 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -56,7 +56,6 @@
 import com.google.gerrit.server.git.TagCache;
 import com.google.gerrit.server.git.VisibleRefFilter;
 import com.google.gerrit.server.git.receive.ReceiveCommitsExecutorModule;
-import com.google.gerrit.server.group.GroupModule;
 import com.google.gerrit.server.mail.send.ReplacePatchSetSender;
 import com.google.gerrit.server.notedb.NoteDbModule;
 import com.google.gerrit.server.patch.DiffExecutorModule;
@@ -70,6 +69,7 @@
 import com.google.gerrit.server.project.SubmitRuleEvaluator;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeQueryProcessor;
+import com.google.gerrit.server.restapi.group.GroupModule;
 import com.google.gerrit.server.rules.PrologModule;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/access/AccessResource.java b/java/com/google/gerrit/server/access/AccessResource.java
index 22888b8..a1fe0c9 100644
--- a/java/com/google/gerrit/server/access/AccessResource.java
+++ b/java/com/google/gerrit/server/access/AccessResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/java/com/google/gerrit/server/account/AccountsUpdate.java b/java/com/google/gerrit/server/account/AccountsUpdate.java
index f88f7e2..ee3cf87f 100644
--- a/java/com/google/gerrit/server/account/AccountsUpdate.java
+++ b/java/com/google/gerrit/server/account/AccountsUpdate.java
@@ -29,6 +29,7 @@
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.externalids.ExternalIdNotes;
+import com.google.gerrit.server.account.externalids.ExternalIdNotes.ExternalIdNotesLoader;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -37,6 +38,7 @@
 import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
 import com.google.gerrit.server.update.RefUpdateUtil;
 import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.update.RetryHelper.ActionType;
 import com.google.gwtorm.server.OrmDuplicateKeyException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -160,6 +162,62 @@
   }
 
   /**
+   * Factory to create an AccountsUpdate instance for updating accounts by the Gerrit server.
+   *
+   * <p>Using this class no reindex will be performed for the affected accounts and they will also
+   * not be evicted from the account cache.
+   *
+   * <p>The Gerrit server identity will be used as author and committer for all commits that update
+   * the accounts.
+   */
+  @Singleton
+  public static class ServerNoReindex {
+    private final GitRepositoryManager repoManager;
+    private final GitReferenceUpdated gitRefUpdated;
+    private final AllUsersName allUsersName;
+    private final OutgoingEmailValidator emailValidator;
+    private final Provider<PersonIdent> serverIdentProvider;
+    private final Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory;
+    private final RetryHelper retryHelper;
+    private final ExternalIdNotes.FactoryNoReindex extIdNotesFactory;
+
+    @Inject
+    public ServerNoReindex(
+        GitRepositoryManager repoManager,
+        GitReferenceUpdated gitRefUpdated,
+        AllUsersName allUsersName,
+        OutgoingEmailValidator emailValidator,
+        @GerritPersonIdent Provider<PersonIdent> serverIdentProvider,
+        Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory,
+        RetryHelper retryHelper,
+        ExternalIdNotes.FactoryNoReindex extIdNotesFactory) {
+      this.repoManager = repoManager;
+      this.gitRefUpdated = gitRefUpdated;
+      this.allUsersName = allUsersName;
+      this.emailValidator = emailValidator;
+      this.serverIdentProvider = serverIdentProvider;
+      this.metaDataUpdateInternalFactory = metaDataUpdateInternalFactory;
+      this.retryHelper = retryHelper;
+      this.extIdNotesFactory = extIdNotesFactory;
+    }
+
+    public AccountsUpdate create() {
+      PersonIdent serverIdent = serverIdentProvider.get();
+      return new AccountsUpdate(
+          repoManager,
+          gitRefUpdated,
+          null,
+          allUsersName,
+          emailValidator,
+          metaDataUpdateInternalFactory,
+          retryHelper,
+          extIdNotesFactory,
+          serverIdent,
+          serverIdent);
+    }
+  }
+
+  /**
    * Factory to create an AccountsUpdate instance for updating accounts by the current user.
    *
    * <p>The identity of the current user will be used as author for all commits that update the
@@ -228,7 +286,7 @@
   private final OutgoingEmailValidator emailValidator;
   private final Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory;
   private final RetryHelper retryHelper;
-  private final ExternalIdNotes.Factory extIdNotesFactory;
+  private final ExternalIdNotesLoader extIdNotesLoader;
   private final PersonIdent committerIdent;
   private final PersonIdent authorIdent;
   private final Runnable afterReadRevision;
@@ -241,7 +299,7 @@
       OutgoingEmailValidator emailValidator,
       Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory,
       RetryHelper retryHelper,
-      ExternalIdNotes.Factory extIdNotesFactory,
+      ExternalIdNotesLoader extIdNotesLoader,
       PersonIdent committerIdent,
       PersonIdent authorIdent) {
     this(
@@ -252,7 +310,7 @@
         emailValidator,
         metaDataUpdateInternalFactory,
         retryHelper,
-        extIdNotesFactory,
+        extIdNotesLoader,
         committerIdent,
         authorIdent,
         Runnables.doNothing());
@@ -267,7 +325,7 @@
       OutgoingEmailValidator emailValidator,
       Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory,
       RetryHelper retryHelper,
-      ExternalIdNotes.Factory extIdNotesFactory,
+      ExternalIdNotesLoader extIdNotesLoader,
       PersonIdent committerIdent,
       PersonIdent authorIdent,
       Runnable afterReadRevision) {
@@ -279,7 +337,7 @@
     this.metaDataUpdateInternalFactory =
         checkNotNull(metaDataUpdateInternalFactory, "metaDataUpdateInternalFactory");
     this.retryHelper = checkNotNull(retryHelper, "retryHelper");
-    this.extIdNotesFactory = checkNotNull(extIdNotesFactory, "extIdNotesFactory");
+    this.extIdNotesLoader = checkNotNull(extIdNotesLoader, "extIdNotesLoader");
     this.committerIdent = checkNotNull(committerIdent, "committerIdent");
     this.authorIdent = checkNotNull(authorIdent, "authorIdent");
     this.afterReadRevision = afterReadRevision;
@@ -416,6 +474,7 @@
   private Account deleteAccount(Account.Id accountId)
       throws IOException, OrmException, ConfigInvalidException {
     return retryHelper.execute(
+        ActionType.ACCOUNT_UPDATE,
         () -> {
           deleteUserBranch(accountId);
           return null;
@@ -468,6 +527,7 @@
   private Account updateAccount(AccountUpdate accountUpdate)
       throws IOException, ConfigInvalidException, OrmException {
     return retryHelper.execute(
+        ActionType.ACCOUNT_UPDATE,
         () -> {
           try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
             UpdatedAccount updatedAccount = accountUpdate.update(allUsersRepo);
@@ -491,7 +551,7 @@
             update.getDeletedExternalIds()),
         accountId);
 
-    ExternalIdNotes extIdNotes = extIdNotesFactory.load(allUsersRepo);
+    ExternalIdNotes extIdNotes = extIdNotesLoader.load(allUsersRepo);
     extIdNotes.replace(update.getDeletedExternalIds(), update.getCreatedExternalIds());
     extIdNotes.upsert(update.getUpdatedExternalIds());
     return extIdNotes;
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java b/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
index d8148c6..f663247 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
@@ -78,8 +78,12 @@
 
   private static final int MAX_NOTE_SZ = 1 << 19;
 
+  public interface ExternalIdNotesLoader {
+    ExternalIdNotes load(Repository allUsersRepo) throws IOException, ConfigInvalidException;
+  }
+
   @Singleton
-  public static class Factory {
+  public static class Factory implements ExternalIdNotesLoader {
     private final ExternalIdCache externalIdCache;
     private final AccountCache accountCache;
 
@@ -89,6 +93,7 @@
       this.accountCache = accountCache;
     }
 
+    @Override
     public ExternalIdNotes load(Repository allUsersRepo)
         throws IOException, ConfigInvalidException {
       return new ExternalIdNotes(externalIdCache, accountCache, allUsersRepo).load();
@@ -96,7 +101,7 @@
   }
 
   @Singleton
-  public static class FactoryNoReindex {
+  public static class FactoryNoReindex implements ExternalIdNotesLoader {
     private final ExternalIdCache externalIdCache;
 
     @Inject
@@ -104,12 +109,19 @@
       this.externalIdCache = externalIdCache;
     }
 
+    @Override
     public ExternalIdNotes load(Repository allUsersRepo)
         throws IOException, ConfigInvalidException {
       return new ExternalIdNotes(externalIdCache, null, allUsersRepo).load();
     }
   }
 
+  /**
+   * Loads the external ID notes for reading only. The external ID notes are loaded from the current
+   * HEAD revision of the {@code refs/meta/external-ids} branch.
+   *
+   * @return read-only {@link ExternalIdNotes} instance
+   */
   public static ExternalIdNotes loadReadOnly(Repository allUsersRepo)
       throws IOException, ConfigInvalidException {
     return new ExternalIdNotes(new DisabledExternalIdCache(), null, allUsersRepo)
@@ -117,13 +129,27 @@
         .load();
   }
 
-  public static ExternalIdNotes loadReadOnly(Repository allUsersRepo, ObjectId rev)
+  /**
+   * Loads the external ID notes for reading only. The external ID notes are loaded from the
+   * specified revision of the {@code refs/meta/external-ids} branch.
+   *
+   * @param rev the revision from which the external ID notes should be loaded, if {@code null} the
+   *     external ID notes are loaded from the current HEAD revision
+   * @return read-only {@link ExternalIdNotes} instance
+   */
+  public static ExternalIdNotes loadReadOnly(Repository allUsersRepo, @Nullable ObjectId rev)
       throws IOException, ConfigInvalidException {
     return new ExternalIdNotes(new DisabledExternalIdCache(), null, allUsersRepo)
         .setReadOnly()
         .load(rev);
   }
 
+  /**
+   * Loads the external ID notes for updates without cache evictions. The external ID notes are
+   * loaded from the current HEAD revision of the {@code refs/meta/external-ids} branch.
+   *
+   * @return {@link ExternalIdNotes} instance that doesn't updates caches on save
+   */
   public static ExternalIdNotes loadNoCacheUpdate(Repository allUsersRepo)
       throws IOException, ConfigInvalidException {
     return new ExternalIdNotes(new DisabledExternalIdCache(), null, allUsersRepo).load();
@@ -173,12 +199,29 @@
     return RefNames.REFS_EXTERNAL_IDS;
   }
 
+  /**
+   * Loads the external ID notes from the current HEAD revision of the {@code
+   * refs/meta/external-ids} branch.
+   *
+   * @return {@link ExternalIdNotes} instance for chaining
+   */
   ExternalIdNotes load() throws IOException, ConfigInvalidException {
     load(repo);
     return this;
   }
 
-  ExternalIdNotes load(ObjectId rev) throws IOException, ConfigInvalidException {
+  /**
+   * Loads the external ID notes from the specified revision of the {@code refs/meta/external-ids}
+   * branch.
+   *
+   * @param rev the revision from which the external ID notes should be loaded, if {@code null} the
+   *     external ID notes are loaded from the current HEAD revision
+   * @return {@link ExternalIdNotes} instance for chaining
+   */
+  ExternalIdNotes load(@Nullable ObjectId rev) throws IOException, ConfigInvalidException {
+    if (rev == null) {
+      return load();
+    }
     load(repo, rev);
     return this;
   }
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdReader.java b/java/com/google/gerrit/server/account/externalids/ExternalIdReader.java
index f97757f..b4ff539 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdReader.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdReader.java
@@ -103,10 +103,14 @@
   }
 
   /**
-   * Reads and returns all external IDs from the specified revision of the refs/meta/external-ids
-   * branch.
+   * Reads and returns all external IDs from the specified revision of the {@code
+   * refs/meta/external-ids} branch.
+   *
+   * @param rev the revision from which the external IDs should be read, if {@code null} the
+   *     external IDs are read from the current HEAD revision
+   * @return all external IDs that were read from the specified revision
    */
-  Set<ExternalId> all(ObjectId rev) throws IOException, ConfigInvalidException {
+  Set<ExternalId> all(@Nullable ObjectId rev) throws IOException, ConfigInvalidException {
     checkReadEnabled();
 
     try (Timer0.Context ctx = readAllLatency.start();
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java b/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
index 8a05a6c..028fd8d 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
@@ -28,6 +28,7 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.update.RetryHelper.ActionType;
 import com.google.gwtorm.server.OrmDuplicateKeyException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -427,6 +428,7 @@
   private void updateNoteMap(ExternalIdUpdater updater)
       throws IOException, ConfigInvalidException, OrmException {
     retryHelper.execute(
+        ActionType.ACCOUNT_UPDATE,
         () -> {
           try (Repository repo = repoManager.openRepository(allUsersName)) {
             ExternalIdNotes extIdNotes =
diff --git a/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java b/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
index 7b14725..232cc63 100644
--- a/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
+++ b/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
@@ -44,38 +44,38 @@
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.account.AccountResource;
-import com.google.gerrit.server.account.AddSshKey;
-import com.google.gerrit.server.account.CreateEmail;
-import com.google.gerrit.server.account.DeleteActive;
-import com.google.gerrit.server.account.DeleteEmail;
-import com.google.gerrit.server.account.DeleteExternalIds;
-import com.google.gerrit.server.account.DeleteSshKey;
-import com.google.gerrit.server.account.DeleteWatchedProjects;
-import com.google.gerrit.server.account.GetActive;
-import com.google.gerrit.server.account.GetAgreements;
-import com.google.gerrit.server.account.GetAvatar;
-import com.google.gerrit.server.account.GetDiffPreferences;
-import com.google.gerrit.server.account.GetEditPreferences;
-import com.google.gerrit.server.account.GetEmails;
-import com.google.gerrit.server.account.GetExternalIds;
-import com.google.gerrit.server.account.GetGroups;
-import com.google.gerrit.server.account.GetPreferences;
-import com.google.gerrit.server.account.GetSshKeys;
-import com.google.gerrit.server.account.GetWatchedProjects;
 import com.google.gerrit.server.account.GpgApiAdapter;
-import com.google.gerrit.server.account.Index;
-import com.google.gerrit.server.account.PostWatchedProjects;
-import com.google.gerrit.server.account.PutActive;
-import com.google.gerrit.server.account.PutAgreement;
-import com.google.gerrit.server.account.PutStatus;
-import com.google.gerrit.server.account.SetDiffPreferences;
-import com.google.gerrit.server.account.SetEditPreferences;
-import com.google.gerrit.server.account.SetPreferences;
-import com.google.gerrit.server.account.SshKeys;
-import com.google.gerrit.server.account.StarredChanges;
-import com.google.gerrit.server.account.Stars;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.ChangesCollection;
+import com.google.gerrit.server.restapi.account.AddSshKey;
+import com.google.gerrit.server.restapi.account.CreateEmail;
+import com.google.gerrit.server.restapi.account.DeleteActive;
+import com.google.gerrit.server.restapi.account.DeleteEmail;
+import com.google.gerrit.server.restapi.account.DeleteExternalIds;
+import com.google.gerrit.server.restapi.account.DeleteSshKey;
+import com.google.gerrit.server.restapi.account.DeleteWatchedProjects;
+import com.google.gerrit.server.restapi.account.GetActive;
+import com.google.gerrit.server.restapi.account.GetAgreements;
+import com.google.gerrit.server.restapi.account.GetAvatar;
+import com.google.gerrit.server.restapi.account.GetDiffPreferences;
+import com.google.gerrit.server.restapi.account.GetEditPreferences;
+import com.google.gerrit.server.restapi.account.GetEmails;
+import com.google.gerrit.server.restapi.account.GetExternalIds;
+import com.google.gerrit.server.restapi.account.GetGroups;
+import com.google.gerrit.server.restapi.account.GetPreferences;
+import com.google.gerrit.server.restapi.account.GetSshKeys;
+import com.google.gerrit.server.restapi.account.GetWatchedProjects;
+import com.google.gerrit.server.restapi.account.Index;
+import com.google.gerrit.server.restapi.account.PostWatchedProjects;
+import com.google.gerrit.server.restapi.account.PutActive;
+import com.google.gerrit.server.restapi.account.PutAgreement;
+import com.google.gerrit.server.restapi.account.PutStatus;
+import com.google.gerrit.server.restapi.account.SetDiffPreferences;
+import com.google.gerrit.server.restapi.account.SetEditPreferences;
+import com.google.gerrit.server.restapi.account.SetPreferences;
+import com.google.gerrit.server.restapi.account.SshKeys;
+import com.google.gerrit.server.restapi.account.StarredChanges;
+import com.google.gerrit.server.restapi.account.Stars;
+import com.google.gerrit.server.restapi.change.ChangesCollection;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
diff --git a/java/com/google/gerrit/server/api/accounts/AccountsImpl.java b/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
index 5257aec..fac16dc 100644
--- a/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
+++ b/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
@@ -29,11 +29,11 @@
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.account.AccountResource;
-import com.google.gerrit.server.account.AccountsCollection;
-import com.google.gerrit.server.account.CreateAccount;
-import com.google.gerrit.server.account.QueryAccounts;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.restapi.account.AccountsCollection;
+import com.google.gerrit.server.restapi.account.CreateAccount;
+import com.google.gerrit.server.restapi.account.QueryAccounts;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index f368c17..baf6d36 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -52,46 +52,46 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
-import com.google.gerrit.server.change.Abandon;
-import com.google.gerrit.server.change.ChangeIncludedIn;
 import com.google.gerrit.server.change.ChangeJson;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.Check;
-import com.google.gerrit.server.change.CreateMergePatchSet;
-import com.google.gerrit.server.change.DeleteAssignee;
-import com.google.gerrit.server.change.DeleteChange;
-import com.google.gerrit.server.change.DeletePrivate;
-import com.google.gerrit.server.change.GetAssignee;
-import com.google.gerrit.server.change.GetHashtags;
-import com.google.gerrit.server.change.GetPastAssignees;
-import com.google.gerrit.server.change.GetTopic;
-import com.google.gerrit.server.change.Ignore;
-import com.google.gerrit.server.change.Index;
-import com.google.gerrit.server.change.ListChangeComments;
-import com.google.gerrit.server.change.ListChangeDrafts;
-import com.google.gerrit.server.change.ListChangeRobotComments;
-import com.google.gerrit.server.change.MarkAsReviewed;
-import com.google.gerrit.server.change.MarkAsUnreviewed;
-import com.google.gerrit.server.change.Move;
-import com.google.gerrit.server.change.PostHashtags;
-import com.google.gerrit.server.change.PostPrivate;
-import com.google.gerrit.server.change.PostReviewers;
 import com.google.gerrit.server.change.PureRevert;
-import com.google.gerrit.server.change.PutAssignee;
-import com.google.gerrit.server.change.PutMessage;
-import com.google.gerrit.server.change.PutTopic;
-import com.google.gerrit.server.change.Rebase;
-import com.google.gerrit.server.change.Restore;
-import com.google.gerrit.server.change.Revert;
-import com.google.gerrit.server.change.Reviewers;
 import com.google.gerrit.server.change.Revisions;
-import com.google.gerrit.server.change.SetPrivateOp;
-import com.google.gerrit.server.change.SetReadyForReview;
-import com.google.gerrit.server.change.SetWorkInProgress;
-import com.google.gerrit.server.change.SubmittedTogether;
-import com.google.gerrit.server.change.SuggestChangeReviewers;
-import com.google.gerrit.server.change.Unignore;
 import com.google.gerrit.server.change.WorkInProgressOp;
+import com.google.gerrit.server.restapi.change.Abandon;
+import com.google.gerrit.server.restapi.change.ChangeIncludedIn;
+import com.google.gerrit.server.restapi.change.Check;
+import com.google.gerrit.server.restapi.change.CreateMergePatchSet;
+import com.google.gerrit.server.restapi.change.DeleteAssignee;
+import com.google.gerrit.server.restapi.change.DeleteChange;
+import com.google.gerrit.server.restapi.change.DeletePrivate;
+import com.google.gerrit.server.restapi.change.GetAssignee;
+import com.google.gerrit.server.restapi.change.GetHashtags;
+import com.google.gerrit.server.restapi.change.GetPastAssignees;
+import com.google.gerrit.server.restapi.change.GetTopic;
+import com.google.gerrit.server.restapi.change.Ignore;
+import com.google.gerrit.server.restapi.change.Index;
+import com.google.gerrit.server.restapi.change.ListChangeComments;
+import com.google.gerrit.server.restapi.change.ListChangeDrafts;
+import com.google.gerrit.server.restapi.change.ListChangeRobotComments;
+import com.google.gerrit.server.restapi.change.MarkAsReviewed;
+import com.google.gerrit.server.restapi.change.MarkAsUnreviewed;
+import com.google.gerrit.server.restapi.change.Move;
+import com.google.gerrit.server.restapi.change.PostHashtags;
+import com.google.gerrit.server.restapi.change.PostPrivate;
+import com.google.gerrit.server.restapi.change.PostReviewers;
+import com.google.gerrit.server.restapi.change.PutAssignee;
+import com.google.gerrit.server.restapi.change.PutMessage;
+import com.google.gerrit.server.restapi.change.PutTopic;
+import com.google.gerrit.server.restapi.change.Rebase;
+import com.google.gerrit.server.restapi.change.Restore;
+import com.google.gerrit.server.restapi.change.Revert;
+import com.google.gerrit.server.restapi.change.Reviewers;
+import com.google.gerrit.server.restapi.change.SetPrivateOp;
+import com.google.gerrit.server.restapi.change.SetReadyForReview;
+import com.google.gerrit.server.restapi.change.SetWorkInProgress;
+import com.google.gerrit.server.restapi.change.SubmittedTogether;
+import com.google.gerrit.server.restapi.change.SuggestChangeReviewers;
+import com.google.gerrit.server.restapi.change.Unignore;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
index 823e771..92aae03 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
@@ -28,11 +28,11 @@
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.change.ChangeEditResource;
-import com.google.gerrit.server.change.ChangeEdits;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.DeleteChangeEdit;
-import com.google.gerrit.server.change.PublishChangeEdit;
-import com.google.gerrit.server.change.RebaseChangeEdit;
+import com.google.gerrit.server.restapi.change.ChangeEdits;
+import com.google.gerrit.server.restapi.change.DeleteChangeEdit;
+import com.google.gerrit.server.restapi.change.PublishChangeEdit;
+import com.google.gerrit.server.restapi.change.RebaseChangeEdit;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
diff --git a/java/com/google/gerrit/server/api/changes/ChangesImpl.java b/java/com/google/gerrit/server/api/changes/ChangesImpl.java
index a372ed6..cefabf4 100644
--- a/java/com/google/gerrit/server/api/changes/ChangesImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangesImpl.java
@@ -30,9 +30,9 @@
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.extensions.restapi.Url;
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.change.ChangesCollection;
-import com.google.gerrit.server.change.CreateChange;
-import com.google.gerrit.server.change.QueryChanges;
+import com.google.gerrit.server.restapi.change.ChangesCollection;
+import com.google.gerrit.server.restapi.change.CreateChange;
+import com.google.gerrit.server.restapi.change.QueryChanges;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/api/changes/CommentApiImpl.java b/java/com/google/gerrit/server/api/changes/CommentApiImpl.java
index 6a2501e..418187d 100644
--- a/java/com/google/gerrit/server/api/changes/CommentApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/CommentApiImpl.java
@@ -21,8 +21,8 @@
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.change.CommentResource;
-import com.google.gerrit.server.change.DeleteComment;
-import com.google.gerrit.server.change.GetComment;
+import com.google.gerrit.server.restapi.change.DeleteComment;
+import com.google.gerrit.server.restapi.change.GetComment;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
diff --git a/java/com/google/gerrit/server/api/changes/DraftApiImpl.java b/java/com/google/gerrit/server/api/changes/DraftApiImpl.java
index eada51b..4d26b11 100644
--- a/java/com/google/gerrit/server/api/changes/DraftApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/DraftApiImpl.java
@@ -22,10 +22,10 @@
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.NotImplementedException;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.change.DeleteDraftComment;
 import com.google.gerrit.server.change.DraftCommentResource;
-import com.google.gerrit.server.change.GetDraftComment;
-import com.google.gerrit.server.change.PutDraftComment;
+import com.google.gerrit.server.restapi.change.DeleteDraftComment;
+import com.google.gerrit.server.restapi.change.GetDraftComment;
+import com.google.gerrit.server.restapi.change.PutDraftComment;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
diff --git a/java/com/google/gerrit/server/api/changes/FileApiImpl.java b/java/com/google/gerrit/server/api/changes/FileApiImpl.java
index f51cdac..6e18bb8 100644
--- a/java/com/google/gerrit/server/api/changes/FileApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/FileApiImpl.java
@@ -21,8 +21,8 @@
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.change.FileResource;
-import com.google.gerrit.server.change.GetContent;
-import com.google.gerrit.server.change.GetDiff;
+import com.google.gerrit.server.restapi.change.GetContent;
+import com.google.gerrit.server.restapi.change.GetDiff;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
diff --git a/java/com/google/gerrit/server/api/changes/ReviewerApiImpl.java b/java/com/google/gerrit/server/api/changes/ReviewerApiImpl.java
index 2f8b7d8..11536cb 100644
--- a/java/com/google/gerrit/server/api/changes/ReviewerApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ReviewerApiImpl.java
@@ -20,11 +20,11 @@
 import com.google.gerrit.extensions.api.changes.DeleteVoteInput;
 import com.google.gerrit.extensions.api.changes.ReviewerApi;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.change.DeleteReviewer;
-import com.google.gerrit.server.change.DeleteVote;
 import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.change.VoteResource;
-import com.google.gerrit.server.change.Votes;
+import com.google.gerrit.server.restapi.change.DeleteReviewer;
+import com.google.gerrit.server.restapi.change.DeleteVote;
+import com.google.gerrit.server.restapi.change.Votes;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
diff --git a/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java b/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
index 7ecfce7..8357568 100644
--- a/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
@@ -47,35 +47,35 @@
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.change.ApplyFix;
-import com.google.gerrit.server.change.CherryPick;
-import com.google.gerrit.server.change.Comments;
-import com.google.gerrit.server.change.CreateDraftComment;
-import com.google.gerrit.server.change.DraftComments;
 import com.google.gerrit.server.change.FileResource;
-import com.google.gerrit.server.change.Files;
-import com.google.gerrit.server.change.Fixes;
-import com.google.gerrit.server.change.GetCommit;
-import com.google.gerrit.server.change.GetDescription;
-import com.google.gerrit.server.change.GetMergeList;
-import com.google.gerrit.server.change.GetPatch;
-import com.google.gerrit.server.change.GetRevisionActions;
-import com.google.gerrit.server.change.ListRevisionComments;
-import com.google.gerrit.server.change.ListRevisionDrafts;
-import com.google.gerrit.server.change.ListRobotComments;
-import com.google.gerrit.server.change.Mergeable;
-import com.google.gerrit.server.change.PostReview;
-import com.google.gerrit.server.change.PreviewSubmit;
-import com.google.gerrit.server.change.PutDescription;
-import com.google.gerrit.server.change.Rebase;
 import com.google.gerrit.server.change.RebaseUtil;
-import com.google.gerrit.server.change.Reviewed;
 import com.google.gerrit.server.change.RevisionResource;
-import com.google.gerrit.server.change.RevisionReviewers;
-import com.google.gerrit.server.change.RobotComments;
-import com.google.gerrit.server.change.Submit;
-import com.google.gerrit.server.change.TestSubmitType;
 import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.restapi.change.ApplyFix;
+import com.google.gerrit.server.restapi.change.CherryPick;
+import com.google.gerrit.server.restapi.change.Comments;
+import com.google.gerrit.server.restapi.change.CreateDraftComment;
+import com.google.gerrit.server.restapi.change.DraftComments;
+import com.google.gerrit.server.restapi.change.Files;
+import com.google.gerrit.server.restapi.change.Fixes;
+import com.google.gerrit.server.restapi.change.GetCommit;
+import com.google.gerrit.server.restapi.change.GetDescription;
+import com.google.gerrit.server.restapi.change.GetMergeList;
+import com.google.gerrit.server.restapi.change.GetPatch;
+import com.google.gerrit.server.restapi.change.GetRevisionActions;
+import com.google.gerrit.server.restapi.change.ListRevisionComments;
+import com.google.gerrit.server.restapi.change.ListRevisionDrafts;
+import com.google.gerrit.server.restapi.change.ListRobotComments;
+import com.google.gerrit.server.restapi.change.Mergeable;
+import com.google.gerrit.server.restapi.change.PostReview;
+import com.google.gerrit.server.restapi.change.PreviewSubmit;
+import com.google.gerrit.server.restapi.change.PutDescription;
+import com.google.gerrit.server.restapi.change.Rebase;
+import com.google.gerrit.server.restapi.change.Reviewed;
+import com.google.gerrit.server.restapi.change.RevisionReviewers;
+import com.google.gerrit.server.restapi.change.RobotComments;
+import com.google.gerrit.server.restapi.change.Submit;
+import com.google.gerrit.server.restapi.change.TestSubmitType;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
diff --git a/java/com/google/gerrit/server/api/changes/RevisionReviewerApiImpl.java b/java/com/google/gerrit/server/api/changes/RevisionReviewerApiImpl.java
index 60dc1d2..8cad507 100644
--- a/java/com/google/gerrit/server/api/changes/RevisionReviewerApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/RevisionReviewerApiImpl.java
@@ -19,10 +19,10 @@
 import com.google.gerrit.extensions.api.changes.DeleteVoteInput;
 import com.google.gerrit.extensions.api.changes.RevisionReviewerApi;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.change.DeleteVote;
 import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.change.VoteResource;
-import com.google.gerrit.server.change.Votes;
+import com.google.gerrit.server.restapi.change.DeleteVote;
+import com.google.gerrit.server.restapi.change.Votes;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
diff --git a/java/com/google/gerrit/server/api/changes/RobotCommentApiImpl.java b/java/com/google/gerrit/server/api/changes/RobotCommentApiImpl.java
index b19939b..37a56fe 100644
--- a/java/com/google/gerrit/server/api/changes/RobotCommentApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/RobotCommentApiImpl.java
@@ -19,8 +19,8 @@
 import com.google.gerrit.extensions.api.changes.RobotCommentApi;
 import com.google.gerrit.extensions.common.RobotCommentInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.change.GetRobotComment;
 import com.google.gerrit.server.change.RobotCommentResource;
+import com.google.gerrit.server.restapi.change.GetRobotComment;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
diff --git a/java/com/google/gerrit/server/api/groups/GroupApiImpl.java b/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
index 28cc60f..9909ed7 100644
--- a/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
+++ b/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
@@ -26,25 +26,25 @@
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.common.NameInput;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.group.AddMembers;
-import com.google.gerrit.server.group.AddSubgroups;
-import com.google.gerrit.server.group.DeleteMembers;
-import com.google.gerrit.server.group.DeleteSubgroups;
-import com.google.gerrit.server.group.GetAuditLog;
-import com.google.gerrit.server.group.GetDescription;
-import com.google.gerrit.server.group.GetDetail;
-import com.google.gerrit.server.group.GetGroup;
-import com.google.gerrit.server.group.GetName;
-import com.google.gerrit.server.group.GetOptions;
-import com.google.gerrit.server.group.GetOwner;
 import com.google.gerrit.server.group.GroupResource;
-import com.google.gerrit.server.group.Index;
-import com.google.gerrit.server.group.ListMembers;
-import com.google.gerrit.server.group.ListSubgroups;
-import com.google.gerrit.server.group.PutDescription;
-import com.google.gerrit.server.group.PutName;
-import com.google.gerrit.server.group.PutOptions;
-import com.google.gerrit.server.group.PutOwner;
+import com.google.gerrit.server.restapi.group.AddMembers;
+import com.google.gerrit.server.restapi.group.AddSubgroups;
+import com.google.gerrit.server.restapi.group.DeleteMembers;
+import com.google.gerrit.server.restapi.group.DeleteSubgroups;
+import com.google.gerrit.server.restapi.group.GetAuditLog;
+import com.google.gerrit.server.restapi.group.GetDescription;
+import com.google.gerrit.server.restapi.group.GetDetail;
+import com.google.gerrit.server.restapi.group.GetGroup;
+import com.google.gerrit.server.restapi.group.GetName;
+import com.google.gerrit.server.restapi.group.GetOptions;
+import com.google.gerrit.server.restapi.group.GetOwner;
+import com.google.gerrit.server.restapi.group.Index;
+import com.google.gerrit.server.restapi.group.ListMembers;
+import com.google.gerrit.server.restapi.group.ListSubgroups;
+import com.google.gerrit.server.restapi.group.PutDescription;
+import com.google.gerrit.server.restapi.group.PutName;
+import com.google.gerrit.server.restapi.group.PutOptions;
+import com.google.gerrit.server.restapi.group.PutOwner;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Arrays;
diff --git a/java/com/google/gerrit/server/api/groups/GroupsImpl.java b/java/com/google/gerrit/server/api/groups/GroupsImpl.java
index f439f7d..0b3bc64 100644
--- a/java/com/google/gerrit/server/api/groups/GroupsImpl.java
+++ b/java/com/google/gerrit/server/api/groups/GroupsImpl.java
@@ -27,15 +27,15 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.AccountsCollection;
-import com.google.gerrit.server.group.CreateGroup;
-import com.google.gerrit.server.group.GroupsCollection;
-import com.google.gerrit.server.group.ListGroups;
-import com.google.gerrit.server.group.QueryGroups;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.project.ProjectResource;
-import com.google.gerrit.server.project.ProjectsCollection;
+import com.google.gerrit.server.restapi.account.AccountsCollection;
+import com.google.gerrit.server.restapi.group.CreateGroup;
+import com.google.gerrit.server.restapi.group.GroupsCollection;
+import com.google.gerrit.server.restapi.group.ListGroups;
+import com.google.gerrit.server.restapi.group.QueryGroups;
+import com.google.gerrit.server.restapi.project.ProjectsCollection;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/api/projects/BranchApiImpl.java b/java/com/google/gerrit/server/api/projects/BranchApiImpl.java
index aee9b3f..78b34b8 100644
--- a/java/com/google/gerrit/server/api/projects/BranchApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/BranchApiImpl.java
@@ -26,15 +26,15 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.BranchResource;
-import com.google.gerrit.server.project.BranchesCollection;
-import com.google.gerrit.server.project.CreateBranch;
-import com.google.gerrit.server.project.DeleteBranch;
 import com.google.gerrit.server.project.FileResource;
-import com.google.gerrit.server.project.FilesCollection;
-import com.google.gerrit.server.project.GetBranch;
-import com.google.gerrit.server.project.GetContent;
-import com.google.gerrit.server.project.GetReflog;
 import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.restapi.project.BranchesCollection;
+import com.google.gerrit.server.restapi.project.CreateBranch;
+import com.google.gerrit.server.restapi.project.DeleteBranch;
+import com.google.gerrit.server.restapi.project.FilesCollection;
+import com.google.gerrit.server.restapi.project.GetBranch;
+import com.google.gerrit.server.restapi.project.GetContent;
+import com.google.gerrit.server.restapi.project.GetReflog;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java b/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java
index 1595682..d7c9bc7 100644
--- a/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java
@@ -18,7 +18,7 @@
 import com.google.gerrit.extensions.common.ProjectInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.project.ChildProjectResource;
-import com.google.gerrit.server.project.GetChildProject;
+import com.google.gerrit.server.restapi.project.GetChildProject;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
diff --git a/java/com/google/gerrit/server/api/projects/CommitApiImpl.java b/java/com/google/gerrit/server/api/projects/CommitApiImpl.java
index cbdd03d..a81e0de 100644
--- a/java/com/google/gerrit/server/api/projects/CommitApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/CommitApiImpl.java
@@ -21,8 +21,8 @@
 import com.google.gerrit.extensions.api.changes.CherryPickInput;
 import com.google.gerrit.extensions.api.projects.CommitApi;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.change.CherryPickCommit;
 import com.google.gerrit.server.project.CommitResource;
+import com.google.gerrit.server.restapi.change.CherryPickCommit;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
diff --git a/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java b/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java
index 12c4244..016a593 100644
--- a/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java
@@ -25,10 +25,10 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.DashboardResource;
-import com.google.gerrit.server.project.DashboardsCollection;
-import com.google.gerrit.server.project.GetDashboard;
 import com.google.gerrit.server.project.ProjectResource;
-import com.google.gerrit.server.project.SetDashboard;
+import com.google.gerrit.server.restapi.project.DashboardsCollection;
+import com.google.gerrit.server.restapi.project.GetDashboard;
+import com.google.gerrit.server.restapi.project.SetDashboard;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
diff --git a/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
index fae5bba..501b3a4 100644
--- a/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.api.projects;
 
 import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
-import static com.google.gerrit.server.project.DashboardsCollection.DEFAULT_DASHBOARD_NAME;
+import static com.google.gerrit.server.restapi.project.DashboardsCollection.DEFAULT_DASHBOARD_NAME;
 import static java.util.stream.Collectors.toList;
 
 import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
@@ -51,30 +51,30 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.project.CheckAccess;
-import com.google.gerrit.server.project.ChildProjectsCollection;
-import com.google.gerrit.server.project.CommitsCollection;
-import com.google.gerrit.server.project.CreateAccessChange;
-import com.google.gerrit.server.project.CreateProject;
-import com.google.gerrit.server.project.DeleteBranches;
-import com.google.gerrit.server.project.DeleteTags;
-import com.google.gerrit.server.project.GetAccess;
-import com.google.gerrit.server.project.GetConfig;
-import com.google.gerrit.server.project.GetDescription;
-import com.google.gerrit.server.project.GetHead;
-import com.google.gerrit.server.project.GetParent;
-import com.google.gerrit.server.project.ListBranches;
-import com.google.gerrit.server.project.ListChildProjects;
-import com.google.gerrit.server.project.ListDashboards;
-import com.google.gerrit.server.project.ListTags;
 import com.google.gerrit.server.project.ProjectJson;
 import com.google.gerrit.server.project.ProjectResource;
-import com.google.gerrit.server.project.ProjectsCollection;
-import com.google.gerrit.server.project.PutConfig;
-import com.google.gerrit.server.project.PutDescription;
-import com.google.gerrit.server.project.SetAccess;
-import com.google.gerrit.server.project.SetHead;
-import com.google.gerrit.server.project.SetParent;
+import com.google.gerrit.server.restapi.project.CheckAccess;
+import com.google.gerrit.server.restapi.project.ChildProjectsCollection;
+import com.google.gerrit.server.restapi.project.CommitsCollection;
+import com.google.gerrit.server.restapi.project.CreateAccessChange;
+import com.google.gerrit.server.restapi.project.CreateProject;
+import com.google.gerrit.server.restapi.project.DeleteBranches;
+import com.google.gerrit.server.restapi.project.DeleteTags;
+import com.google.gerrit.server.restapi.project.GetAccess;
+import com.google.gerrit.server.restapi.project.GetConfig;
+import com.google.gerrit.server.restapi.project.GetDescription;
+import com.google.gerrit.server.restapi.project.GetHead;
+import com.google.gerrit.server.restapi.project.GetParent;
+import com.google.gerrit.server.restapi.project.ListBranches;
+import com.google.gerrit.server.restapi.project.ListChildProjects;
+import com.google.gerrit.server.restapi.project.ListDashboards;
+import com.google.gerrit.server.restapi.project.ListTags;
+import com.google.gerrit.server.restapi.project.ProjectsCollection;
+import com.google.gerrit.server.restapi.project.PutConfig;
+import com.google.gerrit.server.restapi.project.PutDescription;
+import com.google.gerrit.server.restapi.project.SetAccess;
+import com.google.gerrit.server.restapi.project.SetHead;
+import com.google.gerrit.server.restapi.project.SetParent;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
diff --git a/java/com/google/gerrit/server/api/projects/ProjectsImpl.java b/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
index dee9ebb..4552e7a 100644
--- a/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
+++ b/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
@@ -25,10 +25,10 @@
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ListProjects;
-import com.google.gerrit.server.project.ListProjects.FilterType;
-import com.google.gerrit.server.project.ProjectsCollection;
-import com.google.gerrit.server.project.QueryProjects;
+import com.google.gerrit.server.restapi.project.ListProjects;
+import com.google.gerrit.server.restapi.project.ListProjects.FilterType;
+import com.google.gerrit.server.restapi.project.ProjectsCollection;
+import com.google.gerrit.server.restapi.project.QueryProjects;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/api/projects/TagApiImpl.java b/java/com/google/gerrit/server/api/projects/TagApiImpl.java
index 9f19c6d..84af4e8 100644
--- a/java/com/google/gerrit/server/api/projects/TagApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/TagApiImpl.java
@@ -22,12 +22,12 @@
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.project.CreateTag;
-import com.google.gerrit.server.project.DeleteTag;
-import com.google.gerrit.server.project.ListTags;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.gerrit.server.project.TagResource;
-import com.google.gerrit.server.project.TagsCollection;
+import com.google.gerrit.server.restapi.project.CreateTag;
+import com.google.gerrit.server.restapi.project.DeleteTag;
+import com.google.gerrit.server.restapi.project.ListTags;
+import com.google.gerrit.server.restapi.project.TagsCollection;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/change/AccountPatchReviewStore.java b/java/com/google/gerrit/server/change/AccountPatchReviewStore.java
index b7a6e82..69825ea 100644
--- a/java/com/google/gerrit/server/change/AccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/change/AccountPatchReviewStore.java
@@ -37,9 +37,9 @@
   /** Represents patch set id with reviewed files. */
   @AutoValue
   abstract class PatchSetWithReviewedFiles {
-    abstract PatchSet.Id patchSetId();
+    public abstract PatchSet.Id patchSetId();
 
-    abstract ImmutableSet<String> files();
+    public abstract ImmutableSet<String> files();
 
     public static PatchSetWithReviewedFiles create(PatchSet.Id id, ImmutableSet<String> files) {
       return new AutoValue_AccountPatchReviewStore_PatchSetWithReviewedFiles(id, files);
diff --git a/java/com/google/gerrit/server/change/ArchiveFormat.java b/java/com/google/gerrit/server/change/ArchiveFormat.java
index 3fefcd4..0316c5f 100644
--- a/java/com/google/gerrit/server/change/ArchiveFormat.java
+++ b/java/com/google/gerrit/server/change/ArchiveFormat.java
@@ -48,15 +48,15 @@
     return name().toLowerCase();
   }
 
-  String getMimeType() {
+  public String getMimeType() {
     return mimeType;
   }
 
-  String getDefaultSuffix() {
+  public String getDefaultSuffix() {
     return getSuffixes().iterator().next();
   }
 
-  Iterable<String> getSuffixes() {
+  public Iterable<String> getSuffixes() {
     return format.suffixes();
   }
 
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index a03f60a..81558e3 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -1361,7 +1361,7 @@
     return out;
   }
 
-  CommitInfo toCommit(
+  public CommitInfo toCommit(
       Project.NameKey project, RevWalk rw, RevCommit commit, boolean addLinks, boolean fillCommit)
       throws IOException {
     CommitInfo info = new CommitInfo();
diff --git a/java/com/google/gerrit/server/change/CommentResource.java b/java/com/google/gerrit/server/change/CommentResource.java
index f7fc576..1b7cbf8 100644
--- a/java/com/google/gerrit/server/change/CommentResource.java
+++ b/java/com/google/gerrit/server/change/CommentResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -37,19 +37,19 @@
     return rev.getPatchSet();
   }
 
-  Comment getComment() {
+  public Comment getComment() {
     return comment;
   }
 
-  String getId() {
+  public String getId() {
     return comment.key.uuid;
   }
 
-  Account.Id getAuthorId() {
+  public Account.Id getAuthorId() {
     return comment.author.getId();
   }
 
-  RevisionResource getRevisionResource() {
+  public RevisionResource getRevisionResource() {
     return rev;
   }
 }
diff --git a/java/com/google/gerrit/server/change/DraftCommentResource.java b/java/com/google/gerrit/server/change/DraftCommentResource.java
index 0b1b15d..ef31725 100644
--- a/java/com/google/gerrit/server/change/DraftCommentResource.java
+++ b/java/com/google/gerrit/server/change/DraftCommentResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -46,11 +46,11 @@
     return rev.getPatchSet();
   }
 
-  Comment getComment() {
+  public Comment getComment() {
     return comment;
   }
 
-  String getId() {
+  public String getId() {
     return comment.key.uuid;
   }
 }
diff --git a/java/com/google/gerrit/server/change/FileInfoJson.java b/java/com/google/gerrit/server/change/FileInfoJson.java
index 6ccd460..f4b7457 100644
--- a/java/com/google/gerrit/server/change/FileInfoJson.java
+++ b/java/com/google/gerrit/server/change/FileInfoJson.java
@@ -41,24 +41,25 @@
     this.patchListCache = patchListCache;
   }
 
-  Map<String, FileInfo> toFileInfoMap(Change change, PatchSet patchSet)
+  public Map<String, FileInfo> toFileInfoMap(Change change, PatchSet patchSet)
       throws PatchListNotAvailableException {
     return toFileInfoMap(change, patchSet.getRevision(), null);
   }
 
-  Map<String, FileInfo> toFileInfoMap(Change change, RevId revision, @Nullable PatchSet base)
+  public Map<String, FileInfo> toFileInfoMap(Change change, RevId revision, @Nullable PatchSet base)
       throws PatchListNotAvailableException {
     ObjectId objectId = ObjectId.fromString(revision.get());
     return toFileInfoMap(change, objectId, base);
   }
 
-  Map<String, FileInfo> toFileInfoMap(Change change, ObjectId objectId, @Nullable PatchSet base)
+  public Map<String, FileInfo> toFileInfoMap(
+      Change change, ObjectId objectId, @Nullable PatchSet base)
       throws PatchListNotAvailableException {
     ObjectId a = (base == null) ? null : ObjectId.fromString(base.getRevision().get());
     return toFileInfoMap(change, PatchListKey.againstCommit(a, objectId, Whitespace.IGNORE_NONE));
   }
 
-  Map<String, FileInfo> toFileInfoMap(Change change, RevId revision, int parent)
+  public Map<String, FileInfo> toFileInfoMap(Change change, RevId revision, int parent)
       throws PatchListNotAvailableException {
     ObjectId b = ObjectId.fromString(revision.get());
     return toFileInfoMap(
diff --git a/java/com/google/gerrit/server/change/FileResource.java b/java/com/google/gerrit/server/change/FileResource.java
index ca47fb9..bd7557f 100644
--- a/java/com/google/gerrit/server/change/FileResource.java
+++ b/java/com/google/gerrit/server/change/FileResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -40,7 +40,7 @@
     return rev.isCacheable();
   }
 
-  Account.Id getAccountId() {
+  public Account.Id getAccountId() {
     return rev.getAccountId();
   }
 
diff --git a/java/com/google/gerrit/server/change/RebaseUtil.java b/java/com/google/gerrit/server/change/RebaseUtil.java
index fdb1cfc..bfb1692 100644
--- a/java/com/google/gerrit/server/change/RebaseUtil.java
+++ b/java/com/google/gerrit/server/change/RebaseUtil.java
@@ -78,7 +78,7 @@
   }
 
   @AutoValue
-  abstract static class Base {
+  public abstract static class Base {
     private static Base create(ChangeNotes notes, PatchSet ps) {
       if (notes == null) {
         return null;
@@ -86,12 +86,12 @@
       return new AutoValue_RebaseUtil_Base(notes, ps);
     }
 
-    abstract ChangeNotes notes();
+    public abstract ChangeNotes notes();
 
-    abstract PatchSet patchSet();
+    public abstract PatchSet patchSet();
   }
 
-  Base parseBase(RevisionResource rsrc, String base) throws OrmException {
+  public Base parseBase(RevisionResource rsrc, String base) throws OrmException {
     ReviewDb db = dbProvider.get();
 
     // Try parsing the base as a ref string.
@@ -152,7 +152,7 @@
    * @throws IOException if accessing the repository fails.
    * @throws OrmException if accessing the database fails.
    */
-  ObjectId findBaseRevision(
+  public ObjectId findBaseRevision(
       PatchSet patchSet, Branch.NameKey destBranch, Repository git, RevWalk rw)
       throws RestApiException, IOException, OrmException {
     String baseRev = null;
diff --git a/java/com/google/gerrit/server/change/ReviewerResource.java b/java/com/google/gerrit/server/change/ReviewerResource.java
index 47e25b04..778897e 100644
--- a/java/com/google/gerrit/server/change/ReviewerResource.java
+++ b/java/com/google/gerrit/server/change/ReviewerResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -64,14 +64,14 @@
     this.address = null;
   }
 
-  ReviewerResource(ChangeResource change, Address address) {
+  public ReviewerResource(ChangeResource change, Address address) {
     this.change = change;
     this.address = address;
     this.revision = null;
     this.user = null;
   }
 
-  ReviewerResource(RevisionResource revision, Address address) {
+  public ReviewerResource(RevisionResource revision, Address address) {
     this.revision = revision;
     this.change = revision.getChangeResource();
     this.address = address;
diff --git a/java/com/google/gerrit/server/change/RevisionResource.java b/java/com/google/gerrit/server/change/RevisionResource.java
index b9b2d1d..3ddfc63 100644
--- a/java/com/google/gerrit/server/change/RevisionResource.java
+++ b/java/com/google/gerrit/server/change/RevisionResource.java
@@ -84,21 +84,22 @@
     return h.hash().toString();
   }
 
-  void prepareETag(Hasher h, CurrentUser user) {
+  public void prepareETag(Hasher h, CurrentUser user) {
     // Conservative estimate: refresh the revision if its parent change has changed, so we don't
     // have to check whether a given modification affected this revision specifically.
     change.prepareETag(h, user);
   }
 
-  Account.Id getAccountId() {
+  public Account.Id getAccountId() {
     return getUser().getAccountId();
   }
 
-  CurrentUser getUser() {
+  public CurrentUser getUser() {
     return getChangeResource().getUser();
   }
 
-  RevisionResource doNotCache() {
+  public RevisionResource doNotCache() {
+    // TODO(hanwen): return a copy so cacheable can be final.
     cacheable = false;
     return this;
   }
diff --git a/java/com/google/gerrit/server/change/RobotCommentResource.java b/java/com/google/gerrit/server/change/RobotCommentResource.java
index 856c777..c4fab58 100644
--- a/java/com/google/gerrit/server/change/RobotCommentResource.java
+++ b/java/com/google/gerrit/server/change/RobotCommentResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2016 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -37,15 +37,15 @@
     return rev.getPatchSet();
   }
 
-  RobotComment getComment() {
+  public RobotComment getComment() {
     return comment;
   }
 
-  String getId() {
+  public String getId() {
     return comment.key.uuid;
   }
 
-  Account.Id getAuthorId() {
+  public Account.Id getAuthorId() {
     return comment.author.getId();
   }
 }
diff --git a/java/com/google/gerrit/server/change/TestSubmitInput.java b/java/com/google/gerrit/server/change/TestSubmitInput.java
new file mode 100644
index 0000000..b681bf8
--- /dev/null
+++ b/java/com/google/gerrit/server/change/TestSubmitInput.java
@@ -0,0 +1,20 @@
+package com.google.gerrit.server.change;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.gerrit.extensions.api.changes.SubmitInput;
+import java.util.Queue;
+
+/**
+ * Subclass of {@link SubmitInput} with special bits that may be flipped for testing purposes only.
+ */
+@VisibleForTesting
+public class TestSubmitInput extends SubmitInput {
+  public boolean failAfterRefUpdates;
+
+  /**
+   * For each change being submitted, an element is removed from this queue and, if the value is
+   * true, a bogus ref update is added to the batch, in order to generate a lock failure during
+   * execution.
+   */
+  public Queue<Boolean> generateLockFailures;
+}
diff --git a/java/com/google/gerrit/server/change/VoteResource.java b/java/com/google/gerrit/server/change/VoteResource.java
index 4dfaff0..27b5bec 100644
--- a/java/com/google/gerrit/server/change/VoteResource.java
+++ b/java/com/google/gerrit/server/change/VoteResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2014 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/java/com/google/gerrit/server/change/WalkSorter.java b/java/com/google/gerrit/server/change/WalkSorter.java
index 56d7ec0..509f774 100644
--- a/java/com/google/gerrit/server/change/WalkSorter.java
+++ b/java/com/google/gerrit/server/change/WalkSorter.java
@@ -62,7 +62,7 @@
  * timestamp, as it will not unexpectedly reorder large groups of changes on subsequent calls if one
  * of the changes was updated.
  */
-class WalkSorter {
+public class WalkSorter {
   private static final Logger log = LoggerFactory.getLogger(WalkSorter.class);
 
   private static final Ordering<List<PatchSetData>> PROJECT_LIST_SORTER =
@@ -254,13 +254,13 @@
   }
 
   @AutoValue
-  abstract static class PatchSetData {
+  public abstract static class PatchSetData {
     @VisibleForTesting
     static PatchSetData create(ChangeData cd, PatchSet ps, RevCommit commit) {
       return new AutoValue_WalkSorter_PatchSetData(cd, ps, commit);
     }
 
-    abstract ChangeData data();
+    public abstract ChangeData data();
 
     abstract PatchSet patchSet();
 
diff --git a/java/com/google/gerrit/server/change/WorkInProgressOp.java b/java/com/google/gerrit/server/change/WorkInProgressOp.java
index 43de55c3..75a9323 100644
--- a/java/com/google/gerrit/server/change/WorkInProgressOp.java
+++ b/java/com/google/gerrit/server/change/WorkInProgressOp.java
@@ -38,9 +38,9 @@
 /* Set work in progress or ready for review state on a change */
 public class WorkInProgressOp implements BatchUpdateOp {
   public static class Input {
-    @Nullable String message;
+    @Nullable public String message;
 
-    @Nullable NotifyHandling notify;
+    @Nullable public NotifyHandling notify;
 
     public Input() {}
 
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index e0d4827..1dda0e5 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -137,7 +137,6 @@
 import com.google.gerrit.server.git.validators.RefOperationValidators;
 import com.google.gerrit.server.git.validators.UploadValidationListener;
 import com.google.gerrit.server.git.validators.UploadValidators;
-import com.google.gerrit.server.group.GroupModule;
 import com.google.gerrit.server.index.change.ReindexAfterRefUpdate;
 import com.google.gerrit.server.mail.EmailModule;
 import com.google.gerrit.server.mail.ListMailFilter;
@@ -176,6 +175,7 @@
 import com.google.gerrit.server.query.change.ChangeQueryProcessor;
 import com.google.gerrit.server.query.change.ConflictsCacheImpl;
 import com.google.gerrit.server.restapi.config.ConfigRestModule;
+import com.google.gerrit.server.restapi.group.GroupModule;
 import com.google.gerrit.server.rules.PrologModule;
 import com.google.gerrit.server.rules.RulesCache;
 import com.google.gerrit.server.ssh.SshAddressesModule;
@@ -305,12 +305,13 @@
 
     install(new AuditModule());
     bind(UiActions.class);
-    install(new com.google.gerrit.server.access.Module());
-    install(new com.google.gerrit.server.account.Module());
-    install(new com.google.gerrit.server.change.Module());
+    install(new com.google.gerrit.server.restapi.access.Module());
     install(new ConfigRestModule());
+    install(new com.google.gerrit.server.restapi.change.Module());
     install(new com.google.gerrit.server.group.Module(groupsMigration));
-    install(new com.google.gerrit.server.project.Module());
+    install(new com.google.gerrit.server.restapi.account.Module());
+    install(new com.google.gerrit.server.restapi.project.Module());
+    install(new com.google.gerrit.server.restapi.group.Module());
 
     bind(GitReferenceUpdated.class);
     DynamicMap.mapOf(binder(), new TypeLiteral<Cache<?, ?>>() {});
diff --git a/java/com/google/gerrit/server/events/EventFactory.java b/java/com/google/gerrit/server/events/EventFactory.java
index 2759de0..a292f9e 100644
--- a/java/com/google/gerrit/server/events/EventFactory.java
+++ b/java/com/google/gerrit/server/events/EventFactory.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
 import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.index.IndexConfig;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
@@ -93,6 +94,7 @@
   private final ChangeKindCache changeKindCache;
   private final Provider<InternalChangeQuery> queryProvider;
   private final SchemaFactory<ReviewDb> schema;
+  private final IndexConfig indexConfig;
 
   @Inject
   EventFactory(
@@ -105,7 +107,8 @@
       ApprovalsUtil approvalsUtil,
       ChangeKindCache changeKindCache,
       Provider<InternalChangeQuery> queryProvider,
-      SchemaFactory<ReviewDb> schema) {
+      SchemaFactory<ReviewDb> schema,
+      IndexConfig indexConfig) {
     this.accountCache = accountCache;
     this.emails = emails;
     this.urlProvider = urlProvider;
@@ -116,6 +119,7 @@
     this.changeKindCache = changeKindCache;
     this.queryProvider = queryProvider;
     this.schema = schema;
+    this.indexConfig = indexConfig;
   }
 
   /**
@@ -313,7 +317,8 @@
     // Find changes in the same related group as this patch set, having a patch
     // set whose parent matches this patch set's revision.
     for (ChangeData cd :
-        queryProvider.get().byProjectGroups(change.getProject(), currentPs.getGroups())) {
+        InternalChangeQuery.byProjectGroups(
+            queryProvider, indexConfig, change.getProject(), currentPs.getGroups())) {
       PATCH_SETS:
       for (PatchSet ps : cd.patchSets()) {
         RevCommit commit = rw.parseCommit(ObjectId.fromString(ps.getRevision().get()));
diff --git a/java/com/google/gerrit/server/git/MergeSuperSet.java b/java/com/google/gerrit/server/git/MergeSuperSet.java
index be4add0..84676d7 100644
--- a/java/com/google/gerrit/server/git/MergeSuperSet.java
+++ b/java/com/google/gerrit/server/git/MergeSuperSet.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.change.Submit;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.permissions.ChangePermission;
@@ -96,6 +95,10 @@
     this.permissionBackend = permissionBackend;
   }
 
+  public static boolean wholeTopicEnabled(Config config) {
+    return config.getBoolean("change", null, "submitWholeTopic", false);
+  }
+
   public MergeSuperSet setMergeOpRepoManager(MergeOpRepoManager orm) {
     checkState(this.orm == null);
     this.orm = checkNotNull(orm);
@@ -115,7 +118,7 @@
       ChangeSet changeSet =
           new ChangeSet(
               cd, permissionBackend.user(user).change(cd).database(db).test(ChangePermission.READ));
-      if (Submit.wholeTopicEnabled(cfg)) {
+      if (wholeTopicEnabled(cfg)) {
         return completeChangeSetIncludingTopics(db, changeSet, user);
       }
       return mergeSuperSetComputation.get().completeWithoutTopic(db, orm, changeSet, user);
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 585f2e0..a058aec 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -147,6 +147,8 @@
 import com.google.gerrit.server.update.Context;
 import com.google.gerrit.server.update.RepoContext;
 import com.google.gerrit.server.update.RepoOnlyOp;
+import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.update.RetryHelper.ActionType;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.LabelVote;
 import com.google.gerrit.server.util.MagicBranch;
@@ -324,6 +326,7 @@
   private final ReceiveConfig receiveConfig;
   private final RefOperationValidators.Factory refValidatorsFactory;
   private final ReplaceOp.Factory replaceOpFactory;
+  private final RetryHelper retryHelper;
   private final RequestScopePropagator requestScopePropagator;
   private final ReviewDb db;
   private final Sequences seq;
@@ -410,6 +413,7 @@
       ReceiveConfig receiveConfig,
       RefOperationValidators.Factory refValidatorsFactory,
       ReplaceOp.Factory replaceOpFactory,
+      RetryHelper retryHelper,
       RequestScopePropagator requestScopePropagator,
       ReviewDb db,
       Sequences seq,
@@ -453,6 +457,7 @@
     this.receiveConfig = receiveConfig;
     this.refValidatorsFactory = refValidatorsFactory;
     this.replaceOpFactory = replaceOpFactory;
+    this.retryHelper = retryHelper;
     this.requestScopePropagator = requestScopePropagator;
     this.seq = seq;
     this.sshInfo = sshInfo;
@@ -2831,94 +2836,113 @@
         refName);
     // TODO(dborowitz): Combine this BatchUpdate with the main one in
     // insertChangesAndPatchSets.
-    try (BatchUpdate bu =
-            batchUpdateFactory.create(db, projectState.getNameKey(), user, TimeUtil.nowTs());
-        ObjectInserter ins = repo.newObjectInserter();
-        ObjectReader reader = ins.newReader();
-        RevWalk rw = new RevWalk(reader)) {
-      bu.setRepository(repo, rw, ins).updateChangesInParallel();
-      bu.setRequestId(receiveId);
-      // TODO(dborowitz): Teach BatchUpdate to ignore missing changes.
+    try {
+      retryHelper.execute(
+          updateFactory -> {
+            try (BatchUpdate bu =
+                    updateFactory.create(db, projectState.getNameKey(), user, TimeUtil.nowTs());
+                ObjectInserter ins = repo.newObjectInserter();
+                ObjectReader reader = ins.newReader();
+                RevWalk rw = new RevWalk(reader)) {
+              bu.setRepository(repo, rw, ins).updateChangesInParallel();
+              bu.setRequestId(receiveId);
+              // TODO(dborowitz): Teach BatchUpdate to ignore missing changes.
 
-      RevCommit newTip = rw.parseCommit(cmd.getNewId());
-      Branch.NameKey branch = new Branch.NameKey(project.getNameKey(), refName);
+              RevCommit newTip = rw.parseCommit(cmd.getNewId());
+              Branch.NameKey branch = new Branch.NameKey(project.getNameKey(), refName);
 
-      rw.reset();
-      rw.markStart(newTip);
-      if (!ObjectId.zeroId().equals(cmd.getOldId())) {
-        rw.markUninteresting(rw.parseCommit(cmd.getOldId()));
-      }
+              rw.reset();
+              rw.markStart(newTip);
+              if (!ObjectId.zeroId().equals(cmd.getOldId())) {
+                rw.markUninteresting(rw.parseCommit(cmd.getOldId()));
+              }
 
-      ListMultimap<ObjectId, Ref> byCommit = changeRefsById();
-      Map<Change.Key, ChangeNotes> byKey = null;
-      List<ReplaceRequest> replaceAndClose = new ArrayList<>();
+              ListMultimap<ObjectId, Ref> byCommit = changeRefsById();
+              Map<Change.Key, ChangeNotes> byKey = null;
+              List<ReplaceRequest> replaceAndClose = new ArrayList<>();
 
-      int existingPatchSets = 0;
-      int newPatchSets = 0;
-      COMMIT:
-      for (RevCommit c; (c = rw.next()) != null; ) {
-        rw.parseBody(c);
+              int existingPatchSets = 0;
+              int newPatchSets = 0;
+              COMMIT:
+              for (RevCommit c; (c = rw.next()) != null; ) {
+                rw.parseBody(c);
 
-        for (Ref ref : byCommit.get(c.copy())) {
-          PatchSet.Id psId = PatchSet.Id.fromRef(ref.getName());
-          Optional<ChangeData> cd = byLegacyId(psId.getParentKey());
-          if (cd.isPresent() && cd.get().change().getDest().equals(branch)) {
-            existingPatchSets++;
-            bu.addOp(
-                psId.getParentKey(),
-                mergedByPushOpFactory.create(requestScopePropagator, psId, refName));
-            continue COMMIT;
-          }
-        }
+                for (Ref ref : byCommit.get(c.copy())) {
+                  PatchSet.Id psId = PatchSet.Id.fromRef(ref.getName());
+                  Optional<ChangeData> cd =
+                      retryHelper.execute(
+                          ActionType.CHANGE_QUERY,
+                          () -> byLegacyId(psId.getParentKey()),
+                          t -> t instanceof OrmException);
+                  if (cd.isPresent() && cd.get().change().getDest().equals(branch)) {
+                    existingPatchSets++;
+                    bu.addOp(
+                        psId.getParentKey(),
+                        mergedByPushOpFactory.create(requestScopePropagator, psId, refName));
+                    continue COMMIT;
+                  }
+                }
 
-        for (String changeId : c.getFooterLines(CHANGE_ID)) {
-          if (byKey == null) {
-            byKey = openChangesByKeyByBranch(branch);
-          }
+                for (String changeId : c.getFooterLines(CHANGE_ID)) {
+                  if (byKey == null) {
+                    byKey =
+                        retryHelper.execute(
+                            ActionType.CHANGE_QUERY,
+                            () -> openChangesByKeyByBranch(branch),
+                            t -> t instanceof OrmException);
+                  }
 
-          ChangeNotes onto = byKey.get(new Change.Key(changeId.trim()));
-          if (onto != null) {
-            newPatchSets++;
-            // Hold onto this until we're done with the walk, as the call to
-            // req.validate below calls isMergedInto which resets the walk.
-            ReplaceRequest req = new ReplaceRequest(onto.getChangeId(), c, cmd, false);
-            req.notes = onto;
-            replaceAndClose.add(req);
-            continue COMMIT;
-          }
-        }
-      }
+                  ChangeNotes onto = byKey.get(new Change.Key(changeId.trim()));
+                  if (onto != null) {
+                    newPatchSets++;
+                    // Hold onto this until we're done with the walk, as the call to
+                    // req.validate below calls isMergedInto which resets the walk.
+                    ReplaceRequest req = new ReplaceRequest(onto.getChangeId(), c, cmd, false);
+                    req.notes = onto;
+                    replaceAndClose.add(req);
+                    continue COMMIT;
+                  }
+                }
+              }
 
-      for (ReplaceRequest req : replaceAndClose) {
-        Change.Id id = req.notes.getChangeId();
-        if (!req.validate(true)) {
-          logDebug("Not closing {} because validation failed", id);
-          continue;
-        }
-        req.addOps(bu, null);
-        bu.addOp(
-            id,
-            mergedByPushOpFactory
-                .create(requestScopePropagator, req.psId, refName)
-                .setPatchSetProvider(
-                    new Provider<PatchSet>() {
-                      @Override
-                      public PatchSet get() {
-                        return req.replaceOp.getPatchSet();
-                      }
-                    }));
-        bu.addOp(id, new ChangeProgressOp(closeProgress));
-      }
+              for (ReplaceRequest req : replaceAndClose) {
+                Change.Id id = req.notes.getChangeId();
+                if (!req.validate(true)) {
+                  logDebug("Not closing {} because validation failed", id);
+                  continue;
+                }
+                req.addOps(bu, null);
+                bu.addOp(
+                    id,
+                    mergedByPushOpFactory
+                        .create(requestScopePropagator, req.psId, refName)
+                        .setPatchSetProvider(
+                            new Provider<PatchSet>() {
+                              @Override
+                              public PatchSet get() {
+                                return req.replaceOp.getPatchSet();
+                              }
+                            }));
+                bu.addOp(id, new ChangeProgressOp(closeProgress));
+              }
 
-      logDebug(
-          "Auto-closing {} changes with existing patch sets and {} with new patch sets",
-          existingPatchSets,
-          newPatchSets);
-      bu.execute();
+              logDebug(
+                  "Auto-closing {} changes with existing patch sets and {} with new patch sets",
+                  existingPatchSets,
+                  newPatchSets);
+              bu.execute();
+            } catch (IOException | OrmException | PermissionBackendException e) {
+              logError("Failed to auto-close changes", e);
+            }
+            return null;
+          },
+          // Use a multiple of the default timeout to account for inner retries that may otherwise
+          // eat up the whole timeout so that no time is left to retry this outer action.
+          RetryHelper.options().timeout(retryHelper.getDefaultTimeout().multipliedBy(5)).build());
     } catch (RestApiException e) {
       logError("Can't insert patchset", e);
-    } catch (IOException | OrmException | UpdateException | PermissionBackendException e) {
-      logError("Can't scan for changes to close", e);
+    } catch (UpdateException e) {
+      logError("Failed to auto-close changes", e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java b/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java
index 79c0cdb..5a5a751 100644
--- a/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java
+++ b/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java
@@ -33,7 +33,7 @@
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.change.RebaseChangeOp;
-import com.google.gerrit.server.change.Submit.TestSubmitInput;
+import com.google.gerrit.server.change.TestSubmitInput;
 import com.google.gerrit.server.extensions.events.ChangeMerged;
 import com.google.gerrit.server.git.CodeReviewCommit;
 import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
diff --git a/java/com/google/gerrit/server/git/strategy/SubmitStrategyListener.java b/java/com/google/gerrit/server/git/strategy/SubmitStrategyListener.java
index 97291e5..57094af 100644
--- a/java/com/google/gerrit/server/git/strategy/SubmitStrategyListener.java
+++ b/java/com/google/gerrit/server/git/strategy/SubmitStrategyListener.java
@@ -19,7 +19,7 @@
 import com.google.gerrit.extensions.api.changes.SubmitInput;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.change.Submit.TestSubmitInput;
+import com.google.gerrit.server.change.TestSubmitInput;
 import com.google.gerrit.server.git.CodeReviewCommit;
 import com.google.gerrit.server.git.IntegrationException;
 import com.google.gerrit.server.git.MergeOp.CommitStatus;
diff --git a/java/com/google/gerrit/server/git/strategy/TestHelperOp.java b/java/com/google/gerrit/server/git/strategy/TestHelperOp.java
index 8d95045..a0ebb4c 100644
--- a/java/com/google/gerrit/server/git/strategy/TestHelperOp.java
+++ b/java/com/google/gerrit/server/git/strategy/TestHelperOp.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.git.strategy;
 
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.change.Submit.TestSubmitInput;
+import com.google.gerrit.server.change.TestSubmitInput;
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.RepoContext;
 import com.google.gerrit.server.util.RequestId;
diff --git a/java/com/google/gerrit/server/group/GroupResource.java b/java/com/google/gerrit/server/group/GroupResource.java
index 44e770f..1050314 100644
--- a/java/com/google/gerrit/server/group/GroupResource.java
+++ b/java/com/google/gerrit/server/group/GroupResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/java/com/google/gerrit/server/group/MemberResource.java b/java/com/google/gerrit/server/group/MemberResource.java
index 52a37a8..b12cadd 100644
--- a/java/com/google/gerrit/server/group/MemberResource.java
+++ b/java/com/google/gerrit/server/group/MemberResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/java/com/google/gerrit/server/group/Module.java b/java/com/google/gerrit/server/group/Module.java
index acef16d..f40e3b9 100644
--- a/java/com/google/gerrit/server/group/Module.java
+++ b/java/com/google/gerrit/server/group/Module.java
@@ -1,37 +1,11 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package com.google.gerrit.server.group;
 
-import static com.google.gerrit.server.group.GroupResource.GROUP_KIND;
-import static com.google.gerrit.server.group.MemberResource.MEMBER_KIND;
-import static com.google.gerrit.server.group.SubgroupResource.SUBGROUP_KIND;
-
-import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.restapi.RestApiModule;
-import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.audit.GroupMemberAuditListener;
-import com.google.gerrit.server.group.AddMembers.UpdateMember;
-import com.google.gerrit.server.group.AddSubgroups.UpdateSubgroup;
-import com.google.gerrit.server.group.DeleteMembers.DeleteMember;
-import com.google.gerrit.server.group.DeleteSubgroups.DeleteSubgroup;
-import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.notedb.GroupsMigration;
-import com.google.inject.Provides;
 
-public class Module extends RestApiModule {
+public class Module extends FactoryModule {
   private final GroupsMigration groupsMigration;
 
   public Module(GroupsMigration groupsMigration) {
@@ -40,47 +14,6 @@
 
   @Override
   protected void configure() {
-    bind(GroupsCollection.class);
-
-    DynamicMap.mapOf(binder(), GROUP_KIND);
-    DynamicMap.mapOf(binder(), MEMBER_KIND);
-    DynamicMap.mapOf(binder(), SUBGROUP_KIND);
-
-    get(GROUP_KIND).to(GetGroup.class);
-    put(GROUP_KIND).to(PutGroup.class);
-    get(GROUP_KIND, "detail").to(GetDetail.class);
-    post(GROUP_KIND, "index").to(Index.class);
-    post(GROUP_KIND, "members").to(AddMembers.class);
-    post(GROUP_KIND, "members.add").to(AddMembers.class);
-    post(GROUP_KIND, "members.delete").to(DeleteMembers.class);
-    post(GROUP_KIND, "groups").to(AddSubgroups.class);
-    post(GROUP_KIND, "groups.add").to(AddSubgroups.class);
-    post(GROUP_KIND, "groups.delete").to(DeleteSubgroups.class);
-    get(GROUP_KIND, "description").to(GetDescription.class);
-    put(GROUP_KIND, "description").to(PutDescription.class);
-    delete(GROUP_KIND, "description").to(PutDescription.class);
-    get(GROUP_KIND, "name").to(GetName.class);
-    put(GROUP_KIND, "name").to(PutName.class);
-    get(GROUP_KIND, "owner").to(GetOwner.class);
-    put(GROUP_KIND, "owner").to(PutOwner.class);
-    get(GROUP_KIND, "options").to(GetOptions.class);
-    put(GROUP_KIND, "options").to(PutOptions.class);
-    get(GROUP_KIND, "log.audit").to(GetAuditLog.class);
-    post(GROUP_KIND, "rebuild").to(Rebuild.class);
-
-    child(GROUP_KIND, "members").to(MembersCollection.class);
-    get(MEMBER_KIND).to(GetMember.class);
-    put(MEMBER_KIND).to(UpdateMember.class);
-    delete(MEMBER_KIND).to(DeleteMember.class);
-
-    child(GROUP_KIND, "groups").to(SubgroupsCollection.class);
-    get(SUBGROUP_KIND).to(GetSubgroup.class);
-    put(SUBGROUP_KIND).to(UpdateSubgroup.class);
-    delete(SUBGROUP_KIND).to(DeleteSubgroup.class);
-
-    factory(CreateGroup.Factory.class);
-    factory(GroupsUpdate.Factory.class);
-
     if (!groupsMigration.disableGroupReviewDb()) {
       // DbGroupMemberAuditListener is used solely for the ReviewDb audit log. It does not respect
       // ReviewDb wrappers that disable reads. Hence, we don't want to bind it if ReviewDb is
@@ -89,17 +22,4 @@
           .to(DbGroupMemberAuditListener.class);
     }
   }
-
-  @Provides
-  @ServerInitiated
-  GroupsUpdate provideServerInitiatedGroupsUpdate(GroupsUpdate.Factory groupsUpdateFactory) {
-    return groupsUpdateFactory.create(null);
-  }
-
-  @Provides
-  @UserInitiated
-  GroupsUpdate provideUserInitiatedGroupsUpdate(
-      GroupsUpdate.Factory groupsUpdateFactory, IdentifiedUser currentUser) {
-    return groupsUpdateFactory.create(currentUser);
-  }
 }
diff --git a/java/com/google/gerrit/server/group/SubgroupResource.java b/java/com/google/gerrit/server/group/SubgroupResource.java
index 50c769d..a33e96b 100644
--- a/java/com/google/gerrit/server/group/SubgroupResource.java
+++ b/java/com/google/gerrit/server/group/SubgroupResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/java/com/google/gerrit/server/project/ChildProjectResource.java b/java/com/google/gerrit/server/project/ChildProjectResource.java
index b372b38..4b641ca 100644
--- a/java/com/google/gerrit/server/project/ChildProjectResource.java
+++ b/java/com/google/gerrit/server/project/ChildProjectResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/java/com/google/gerrit/server/project/CommitResource.java b/java/com/google/gerrit/server/project/CommitResource.java
index 0925524..f71c7fe 100644
--- a/java/com/google/gerrit/server/project/CommitResource.java
+++ b/java/com/google/gerrit/server/project/CommitResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2014 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/java/com/google/gerrit/server/project/DashboardResource.java b/java/com/google/gerrit/server/project/DashboardResource.java
index 87b6fdf..54f958a 100644
--- a/java/com/google/gerrit/server/project/DashboardResource.java
+++ b/java/com/google/gerrit/server/project/DashboardResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/java/com/google/gerrit/server/project/FileResource.java b/java/com/google/gerrit/server/project/FileResource.java
index 82462b2..6e5375a 100644
--- a/java/com/google/gerrit/server/project/FileResource.java
+++ b/java/com/google/gerrit/server/project/FileResource.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/java/com/google/gerrit/server/project/ProjectControl.java b/java/com/google/gerrit/server/project/ProjectControl.java
index 97d0173..3e6d10e 100644
--- a/java/com/google/gerrit/server/project/ProjectControl.java
+++ b/java/com/google/gerrit/server/project/ProjectControl.java
@@ -62,8 +62,8 @@
 import org.eclipse.jgit.revwalk.RevCommit;
 
 /** Access control management for a user accessing a project's data. */
-class ProjectControl {
-  static class GenericFactory {
+public class ProjectControl {
+  public static class GenericFactory {
     private final ProjectCache projectCache;
 
     @Inject
@@ -71,7 +71,7 @@
       projectCache = pc;
     }
 
-    ProjectControl controlFor(Project.NameKey nameKey, CurrentUser user)
+    public ProjectControl controlFor(Project.NameKey nameKey, CurrentUser user)
         throws NoSuchProjectException, IOException {
       final ProjectState p = projectCache.checkedGet(nameKey);
       if (p == null) {
@@ -151,7 +151,7 @@
     return controlForRef(ref.get());
   }
 
-  RefControl controlForRef(String refName) {
+  public RefControl controlForRef(String refName) {
     if (refControls == null) {
       refControls = new HashMap<>();
     }
diff --git a/java/com/google/gerrit/server/project/RefControl.java b/java/com/google/gerrit/server/project/RefControl.java
index d674039..0651b1d 100644
--- a/java/com/google/gerrit/server/project/RefControl.java
+++ b/java/com/google/gerrit/server/project/RefControl.java
@@ -45,7 +45,7 @@
 import java.util.Set;
 
 /** Manages access control for Git references (aka branches, tags). */
-class RefControl {
+public class RefControl {
   private final ProjectControl projectControl;
   private final String refName;
 
@@ -377,7 +377,7 @@
   }
 
   /** True if the user has this permission. Works only for non labels. */
-  boolean canPerform(String permissionName) {
+  public boolean canPerform(String permissionName) {
     return canPerform(permissionName, false);
   }
 
diff --git a/java/com/google/gerrit/server/project/RefUtil.java b/java/com/google/gerrit/server/project/RefUtil.java
index 8a7e5f1..62e48be 100644
--- a/java/com/google/gerrit/server/project/RefUtil.java
+++ b/java/com/google/gerrit/server/project/RefUtil.java
@@ -120,7 +120,7 @@
   }
 
   /** Error indicating the revision is invalid as supplied. */
-  static class InvalidRevisionException extends Exception {
+  public static class InvalidRevisionException extends Exception {
     private static final long serialVersionUID = 1L;
 
     public static final String MESSAGE = "Invalid Revision";
diff --git a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
index e7ccc5a..6e63a32 100644
--- a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
+++ b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
@@ -22,6 +22,7 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.gerrit.index.FieldDef;
@@ -37,10 +38,12 @@
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import org.eclipse.jgit.lib.ObjectId;
@@ -275,12 +278,39 @@
     return query(new SubmissionIdPredicate(cs));
   }
 
-  public List<ChangeData> byProjectGroups(Project.NameKey project, Collection<String> groups)
+  private List<ChangeData> byProjectGroups(Project.NameKey project, Collection<String> groups)
       throws OrmException {
+    int n = indexConfig.maxTerms() - 1;
+    checkArgument(groups.size() <= n, "cannot exceed %s groups", n);
     List<GroupPredicate> groupPredicates = new ArrayList<>(groups.size());
     for (String g : groups) {
       groupPredicates.add(new GroupPredicate(g));
     }
     return query(and(project(project), or(groupPredicates)));
   }
+
+  // Batching via multiple queries requires passing in a Provider since the underlying
+  // QueryProcessor instance is not reusable.
+  public static List<ChangeData> byProjectGroups(
+      Provider<InternalChangeQuery> queryProvider,
+      IndexConfig indexConfig,
+      Project.NameKey project,
+      Collection<String> groups)
+      throws OrmException {
+    int batchSize = indexConfig.maxTerms() - 1;
+    if (groups.size() <= batchSize) {
+      return queryProvider.get().enforceVisibility(true).byProjectGroups(project, groups);
+    }
+    Set<Change.Id> seen = new HashSet<>();
+    List<ChangeData> result = new ArrayList<>();
+    for (List<String> part : Iterables.partition(groups, batchSize)) {
+      for (ChangeData cd :
+          queryProvider.get().enforceVisibility(true).byProjectGroups(project, part)) {
+        if (!seen.add(cd.getId())) {
+          result.add(cd);
+        }
+      }
+    }
+    return result;
+  }
 }
diff --git a/java/com/google/gerrit/server/restapi/BUILD b/java/com/google/gerrit/server/restapi/BUILD
index e0262bb..5b9800b 100644
--- a/java/com/google/gerrit/server/restapi/BUILD
+++ b/java/com/google/gerrit/server/restapi/BUILD
@@ -6,16 +6,29 @@
     name = "restapi",
     srcs = glob(["**/*.java"]),
     deps = [
+        "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/extensions:api",
+        "//java/com/google/gerrit/index",
+        "//java/com/google/gerrit/index:query_exception",
+        "//java/com/google/gerrit/metrics",
+        "//java/com/google/gerrit/prettify:server",
         "//java/com/google/gerrit/reviewdb:server",
         "//java/com/google/gerrit/server",
+        "//java/com/google/gerrit/server/ioutil",
         "//java/org/eclipse/jgit:server",
         "//lib:args4j",
+        "//lib:blame-cache",
+        "//lib:gson",
         "//lib:guava",
         "//lib:gwtorm",
         "//lib:servlet-api-3_1",
+        "//lib/auto:auto-value",
+        "//lib/commons:codec",
+        "//lib/commons:compress",
+        "//lib/commons:lang",
         "//lib/guice",
+        "//lib/guice:guice-assistedinject",
         "//lib/jgit/org.eclipse.jgit:jgit",
         "//lib/log:api",
     ],
diff --git a/java/com/google/gerrit/server/access/AccessCollection.java b/java/com/google/gerrit/server/restapi/access/AccessCollection.java
similarity index 94%
rename from java/com/google/gerrit/server/access/AccessCollection.java
rename to java/com/google/gerrit/server/restapi/access/AccessCollection.java
index 2e90889..4e12291 100644
--- a/java/com/google/gerrit/server/access/AccessCollection.java
+++ b/java/com/google/gerrit/server/restapi/access/AccessCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.access;
+package com.google.gerrit.server.restapi.access;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.IdString;
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.restapi.RestCollection;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
+import com.google.gerrit.server.access.AccessResource;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/access/ListAccess.java b/java/com/google/gerrit/server/restapi/access/ListAccess.java
similarity index 94%
rename from java/com/google/gerrit/server/access/ListAccess.java
rename to java/com/google/gerrit/server/restapi/access/ListAccess.java
index 99e6a9f..a79afd2 100644
--- a/java/com/google/gerrit/server/access/ListAccess.java
+++ b/java/com/google/gerrit/server/restapi/access/ListAccess.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.access;
+package com.google.gerrit.server.restapi.access;
 
 import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -21,7 +21,7 @@
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.GetAccess;
+import com.google.gerrit.server.restapi.project.GetAccess;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/access/Module.java b/java/com/google/gerrit/server/restapi/access/Module.java
similarity index 94%
rename from java/com/google/gerrit/server/access/Module.java
rename to java/com/google/gerrit/server/restapi/access/Module.java
index cd0d334..21357fa 100644
--- a/java/com/google/gerrit/server/access/Module.java
+++ b/java/com/google/gerrit/server/restapi/access/Module.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.access;
+package com.google.gerrit.server.restapi.access;
 
 import static com.google.gerrit.server.access.AccessResource.ACCESS_KIND;
 
diff --git a/java/com/google/gerrit/server/account/AccountsCollection.java b/java/com/google/gerrit/server/restapi/account/AccountsCollection.java
similarity index 96%
rename from java/com/google/gerrit/server/account/AccountsCollection.java
rename to java/com/google/gerrit/server/restapi/account/AccountsCollection.java
index 19a8259..197dadb 100644
--- a/java/com/google/gerrit/server/account/AccountsCollection.java
+++ b/java/com/google/gerrit/server/restapi/account/AccountsCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.registration.DynamicMap;
@@ -28,6 +28,9 @@
 import com.google.gerrit.server.AnonymousUser;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountControl;
+import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/account/AddSshKey.java b/java/com/google/gerrit/server/restapi/account/AddSshKey.java
similarity index 95%
rename from java/com/google/gerrit/server/account/AddSshKey.java
rename to java/com/google/gerrit/server/restapi/account/AddSshKey.java
index 5680b56..be0ca6a 100644
--- a/java/com/google/gerrit/server/account/AddSshKey.java
+++ b/java/com/google/gerrit/server/restapi/account/AddSshKey.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
@@ -29,6 +29,8 @@
 import com.google.gerrit.reviewdb.client.AccountSshKey;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.VersionedAuthorizedKeys;
 import com.google.gerrit.server.mail.send.AddKeySender;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/account/Capabilities.java b/java/com/google/gerrit/server/restapi/account/Capabilities.java
similarity index 96%
rename from java/com/google/gerrit/server/account/Capabilities.java
rename to java/com/google/gerrit/server/restapi/account/Capabilities.java
index 08eecd7..e337662 100644
--- a/java/com/google/gerrit/server/account/Capabilities.java
+++ b/java/com/google/gerrit/server/restapi/account/Capabilities.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
 import com.google.gerrit.extensions.api.access.PluginPermission;
@@ -24,6 +24,7 @@
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.AccountResource.Capability;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/account/CreateAccount.java b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
similarity index 95%
rename from java/com/google/gerrit/server/account/CreateAccount.java
rename to java/com/google/gerrit/server/restapi/account/CreateAccount.java
index 2ce13ea..45128f1 100644
--- a/java/com/google/gerrit/server/account/CreateAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
 import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
@@ -38,13 +38,17 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.Sequences;
+import com.google.gerrit.server.account.AccountExternalIdCreator;
+import com.google.gerrit.server.account.AccountLoader;
+import com.google.gerrit.server.account.AccountsUpdate;
+import com.google.gerrit.server.account.VersionedAuthorizedKeys;
 import com.google.gerrit.server.account.externalids.DuplicateExternalIdKeyException;
 import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gerrit.server.group.GroupsCollection;
 import com.google.gerrit.server.group.UserInitiated;
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
+import com.google.gerrit.server.restapi.group.GroupsCollection;
 import com.google.gerrit.server.ssh.SshKeyCache;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/account/CreateEmail.java b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
similarity index 94%
rename from java/com/google/gerrit/server/account/CreateEmail.java
rename to java/com/google/gerrit/server/restapi/account/CreateEmail.java
index dd02b0b..8ec024e 100644
--- a/java/com/google/gerrit/server/account/CreateEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import static com.google.gerrit.extensions.client.AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT;
 
@@ -29,6 +29,11 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountException;
+import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.AuthRequest;
+import com.google.gerrit.server.account.Realm;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
 import com.google.gerrit.server.mail.send.RegisterNewEmailSender;
diff --git a/java/com/google/gerrit/server/account/DeleteActive.java b/java/com/google/gerrit/server/restapi/account/DeleteActive.java
similarity index 92%
rename from java/com/google/gerrit/server/account/DeleteActive.java
rename to java/com/google/gerrit/server/restapi/account/DeleteActive.java
index 4b3bf39..fda28c9 100644
--- a/java/com/google/gerrit/server/account/DeleteActive.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteActive.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
@@ -22,6 +22,8 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.SetInactiveFlag;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/account/DeleteEmail.java b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
similarity index 93%
rename from java/com/google/gerrit/server/account/DeleteEmail.java
rename to java/com/google/gerrit/server/restapi/account/DeleteEmail.java
index cccac63..d36dfe9 100644
--- a/java/com/google/gerrit/server/account/DeleteEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import static java.util.stream.Collectors.toSet;
 
@@ -26,6 +26,10 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountException;
+import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.Realm;
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.account.externalids.ExternalIds;
 import com.google.gerrit.server.permissions.GlobalPermission;
diff --git a/java/com/google/gerrit/server/account/DeleteExternalIds.java b/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
similarity index 94%
rename from java/com/google/gerrit/server/account/DeleteExternalIds.java
rename to java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
index 72c1a41..3d103ec 100644
--- a/java/com/google/gerrit/server/account/DeleteExternalIds.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
 import static java.util.stream.Collectors.toMap;
@@ -25,6 +25,9 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountException;
+import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.account.externalids.ExternalIds;
 import com.google.gerrit.server.permissions.GlobalPermission;
diff --git a/java/com/google/gerrit/server/account/DeleteSshKey.java b/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
similarity index 93%
rename from java/com/google/gerrit/server/account/DeleteSshKey.java
rename to java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
index 8dec7d9..f6f3045 100644
--- a/java/com/google/gerrit/server/account/DeleteSshKey.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
@@ -12,13 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.VersionedAuthorizedKeys;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/account/DeleteWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
similarity index 93%
rename from java/com/google/gerrit/server/account/DeleteWatchedProjects.java
rename to java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
index ffb405c..1388523 100644
--- a/java/com/google/gerrit/server/account/DeleteWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import static java.util.stream.Collectors.toList;
 
@@ -24,6 +24,9 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.WatchConfig;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/account/EmailsCollection.java b/java/com/google/gerrit/server/restapi/account/EmailsCollection.java
similarity index 96%
rename from java/com/google/gerrit/server/account/EmailsCollection.java
rename to java/com/google/gerrit/server/restapi/account/EmailsCollection.java
index c8c1db8..d75a01a 100644
--- a/java/com/google/gerrit/server/account/EmailsCollection.java
+++ b/java/com/google/gerrit/server/restapi/account/EmailsCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.registration.DynamicMap;
@@ -23,6 +23,7 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.AccountResource.Email;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/account/GetAccount.java b/java/com/google/gerrit/server/restapi/account/GetAccount.java
similarity index 88%
rename from java/com/google/gerrit/server/account/GetAccount.java
rename to java/com/google/gerrit/server/restapi/account/GetAccount.java
index 05f8300..0d8e25e 100644
--- a/java/com/google/gerrit/server/account/GetAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/GetAccount.java
@@ -12,10 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.account.AccountLoader;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/account/GetActive.java b/java/com/google/gerrit/server/restapi/account/GetActive.java
similarity index 90%
rename from java/com/google/gerrit/server/account/GetActive.java
rename to java/com/google/gerrit/server/restapi/account/GetActive.java
index 9864b45..66493f8 100644
--- a/java/com/google/gerrit/server/account/GetActive.java
+++ b/java/com/google/gerrit/server/restapi/account/GetActive.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/account/GetAgreements.java b/java/com/google/gerrit/server/restapi/account/GetAgreements.java
similarity index 95%
rename from java/com/google/gerrit/server/account/GetAgreements.java
rename to java/com/google/gerrit/server/restapi/account/GetAgreements.java
index dfbde96..719cb21 100644
--- a/java/com/google/gerrit/server/account/GetAgreements.java
+++ b/java/com/google/gerrit/server/restapi/account/GetAgreements.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.common.data.ContributorAgreement;
 import com.google.gerrit.common.data.PermissionRule;
@@ -25,9 +25,10 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.config.AgreementJson;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.restapi.config.AgreementJson;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/account/GetAvatar.java b/java/com/google/gerrit/server/restapi/account/GetAvatar.java
similarity index 94%
rename from java/com/google/gerrit/server/account/GetAvatar.java
rename to java/com/google/gerrit/server/restapi/account/GetAvatar.java
index 0818a0e..2f8570e 100644
--- a/java/com/google/gerrit/server/account/GetAvatar.java
+++ b/java/com/google/gerrit/server/restapi/account/GetAvatar.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.registration.DynamicItem;
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.avatar.AvatarProvider;
 import com.google.inject.Inject;
 import java.util.concurrent.TimeUnit;
diff --git a/java/com/google/gerrit/server/account/GetAvatarChangeUrl.java b/java/com/google/gerrit/server/restapi/account/GetAvatarChangeUrl.java
similarity index 93%
rename from java/com/google/gerrit/server/account/GetAvatarChangeUrl.java
rename to java/com/google/gerrit/server/restapi/account/GetAvatarChangeUrl.java
index d340772..904b15f 100644
--- a/java/com/google/gerrit/server/account/GetAvatarChangeUrl.java
+++ b/java/com/google/gerrit/server/restapi/account/GetAvatarChangeUrl.java
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.avatar.AvatarProvider;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/account/GetCapabilities.java b/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
similarity index 96%
rename from java/com/google/gerrit/server/account/GetCapabilities.java
rename to java/com/google/gerrit/server/restapi/account/GetCapabilities.java
index 616ea79..5260bef0 100644
--- a/java/com/google/gerrit/server/account/GetCapabilities.java
+++ b/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import static com.google.gerrit.common.data.GlobalCapability.PRIORITY;
 
@@ -29,6 +29,8 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.OptionUtil;
 import com.google.gerrit.server.OutputFormat;
+import com.google.gerrit.server.account.AccountLimits;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.AccountResource.Capability;
 import com.google.gerrit.server.git.QueueProvider;
 import com.google.gerrit.server.permissions.GlobalPermission;
diff --git a/java/com/google/gerrit/server/account/GetDetail.java b/java/com/google/gerrit/server/restapi/account/GetDetail.java
similarity index 92%
rename from java/com/google/gerrit/server/account/GetDetail.java
rename to java/com/google/gerrit/server/restapi/account/GetDetail.java
index 30eb377..de9928c 100644
--- a/java/com/google/gerrit/server/account/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/account/GetDetail.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Throwables;
 import com.google.gerrit.extensions.common.AccountInfo;
@@ -20,6 +20,8 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.account.AccountDirectory.DirectoryException;
 import com.google.gerrit.server.account.AccountDirectory.FillOptions;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.InternalAccountDirectory;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/account/GetDiffPreferences.java b/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
similarity index 96%
rename from java/com/google/gerrit/server/account/GetDiffPreferences.java
rename to java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
index 8215c6b..c173079 100644
--- a/java/com/google/gerrit/server/account/GetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import static com.google.gerrit.server.config.ConfigUtil.loadSection;
 import static com.google.gerrit.server.config.ConfigUtil.skipField;
@@ -22,6 +22,8 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.VersionedAccountPreferences;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.UserConfigSections;
diff --git a/java/com/google/gerrit/server/account/GetEditPreferences.java b/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
similarity index 94%
rename from java/com/google/gerrit/server/account/GetEditPreferences.java
rename to java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
index bb207f0..95a6e7c 100644
--- a/java/com/google/gerrit/server/account/GetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import static com.google.gerrit.server.config.ConfigUtil.loadSection;
 
@@ -21,6 +21,8 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.VersionedAccountPreferences;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.UserConfigSections;
diff --git a/java/com/google/gerrit/server/account/GetEmail.java b/java/com/google/gerrit/server/restapi/account/GetEmail.java
similarity index 91%
rename from java/com/google/gerrit/server/account/GetEmail.java
rename to java/com/google/gerrit/server/restapi/account/GetEmail.java
index 82e0944..3118380 100644
--- a/java/com/google/gerrit/server/account/GetEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEmail.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.common.EmailInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
diff --git a/java/com/google/gerrit/server/account/GetEmails.java b/java/com/google/gerrit/server/restapi/account/GetEmails.java
similarity index 93%
rename from java/com/google/gerrit/server/account/GetEmails.java
rename to java/com/google/gerrit/server/restapi/account/GetEmails.java
index 184780f..9c482a3 100644
--- a/java/com/google/gerrit/server/account/GetEmails.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEmails.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.common.EmailInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.inject.Singleton;
 import java.util.ArrayList;
 import java.util.Collections;
diff --git a/java/com/google/gerrit/server/account/GetExternalIds.java b/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
similarity index 96%
rename from java/com/google/gerrit/server/account/GetExternalIds.java
rename to java/com/google/gerrit/server/restapi/account/GetExternalIds.java
index 709bfc3..8e456a2 100644
--- a/java/com/google/gerrit/server/account/GetExternalIds.java
+++ b/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
 
@@ -22,6 +22,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.account.externalids.ExternalIds;
 import com.google.gerrit.server.config.AuthConfig;
diff --git a/java/com/google/gerrit/server/account/GetGroups.java b/java/com/google/gerrit/server/restapi/account/GetGroups.java
similarity index 90%
rename from java/com/google/gerrit/server/account/GetGroups.java
rename to java/com/google/gerrit/server/restapi/account/GetGroups.java
index 757cb44d..992a85a 100644
--- a/java/com/google/gerrit/server/account/GetGroups.java
+++ b/java/com/google/gerrit/server/restapi/account/GetGroups.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.common.errors.NoSuchGroupException;
 import com.google.gerrit.extensions.common.GroupInfo;
@@ -20,7 +20,9 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.group.GroupJson;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.GroupControl;
+import com.google.gerrit.server.restapi.group.GroupJson;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/account/GetName.java b/java/com/google/gerrit/server/restapi/account/GetName.java
similarity index 89%
rename from java/com/google/gerrit/server/account/GetName.java
rename to java/com/google/gerrit/server/restapi/account/GetName.java
index 7add77a..bdf379e 100644
--- a/java/com/google/gerrit/server/account/GetName.java
+++ b/java/com/google/gerrit/server/restapi/account/GetName.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/account/GetOAuthToken.java b/java/com/google/gerrit/server/restapi/account/GetOAuthToken.java
similarity index 96%
rename from java/com/google/gerrit/server/account/GetOAuthToken.java
rename to java/com/google/gerrit/server/restapi/account/GetOAuthToken.java
index 587f268..43838e8 100644
--- a/java/com/google/gerrit/server/account/GetOAuthToken.java
+++ b/java/com/google/gerrit/server/restapi/account/GetOAuthToken.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.auth.oauth.OAuthTokenCache;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/account/GetPreferences.java b/java/com/google/gerrit/server/restapi/account/GetPreferences.java
similarity index 92%
rename from java/com/google/gerrit/server/account/GetPreferences.java
rename to java/com/google/gerrit/server/restapi/account/GetPreferences.java
index 3ebf864..b071ade 100644
--- a/java/com/google/gerrit/server/account/GetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetPreferences.java
@@ -12,13 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/account/GetSshKey.java b/java/com/google/gerrit/server/restapi/account/GetSshKey.java
similarity index 90%
rename from java/com/google/gerrit/server/account/GetSshKey.java
rename to java/com/google/gerrit/server/restapi/account/GetSshKey.java
index ee75432..dc72663 100644
--- a/java/com/google/gerrit/server/account/GetSshKey.java
+++ b/java/com/google/gerrit/server/restapi/account/GetSshKey.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.common.SshKeyInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.AccountResource.SshKey;
 import com.google.inject.Singleton;
 
diff --git a/java/com/google/gerrit/server/account/GetSshKeys.java b/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
similarity index 94%
rename from java/com/google/gerrit/server/account/GetSshKeys.java
rename to java/com/google/gerrit/server/restapi/account/GetSshKeys.java
index 9f5b9d5..362812c 100644
--- a/java/com/google/gerrit/server/account/GetSshKeys.java
+++ b/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
@@ -22,6 +22,8 @@
 import com.google.gerrit.reviewdb.client.AccountSshKey;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.VersionedAuthorizedKeys;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/account/GetStatus.java b/java/com/google/gerrit/server/restapi/account/GetStatus.java
similarity index 89%
rename from java/com/google/gerrit/server/account/GetStatus.java
rename to java/com/google/gerrit/server/restapi/account/GetStatus.java
index 5d57c4c..bc7094f 100644
--- a/java/com/google/gerrit/server/account/GetStatus.java
+++ b/java/com/google/gerrit/server/restapi/account/GetStatus.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/account/GetUsername.java b/java/com/google/gerrit/server/restapi/account/GetUsername.java
similarity index 91%
rename from java/com/google/gerrit/server/account/GetUsername.java
rename to java/com/google/gerrit/server/restapi/account/GetUsername.java
index 6541f55..34eb701 100644
--- a/java/com/google/gerrit/server/account/GetUsername.java
+++ b/java/com/google/gerrit/server/restapi/account/GetUsername.java
@@ -12,11 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
diff --git a/java/com/google/gerrit/server/account/GetWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
similarity index 95%
rename from java/com/google/gerrit/server/account/GetWatchedProjects.java
rename to java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
index c2c0547..b465029 100644
--- a/java/com/google/gerrit/server/account/GetWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.ComparisonChain;
@@ -21,6 +21,8 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.WatchConfig;
 import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
 import com.google.gerrit.server.permissions.GlobalPermission;
diff --git a/java/com/google/gerrit/server/account/Index.java b/java/com/google/gerrit/server/restapi/account/Index.java
similarity index 92%
rename from java/com/google/gerrit/server/account/Index.java
rename to java/com/google/gerrit/server/restapi/account/Index.java
index 8436d1d..2f4a87c 100644
--- a/java/com/google/gerrit/server/account/Index.java
+++ b/java/com/google/gerrit/server/restapi/account/Index.java
@@ -12,13 +12,15 @@
 //See the License for the specific language governing permissions and
 //limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/account/Module.java b/java/com/google/gerrit/server/restapi/account/Module.java
similarity index 98%
rename from java/com/google/gerrit/server/account/Module.java
rename to java/com/google/gerrit/server/restapi/account/Module.java
index 44060be..dad84e5 100644
--- a/java/com/google/gerrit/server/account/Module.java
+++ b/java/com/google/gerrit/server/restapi/account/Module.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import static com.google.gerrit.server.account.AccountResource.ACCOUNT_KIND;
 import static com.google.gerrit.server.account.AccountResource.CAPABILITY_KIND;
diff --git a/java/com/google/gerrit/server/account/PostWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
similarity index 94%
rename from java/com/google/gerrit/server/account/PostWatchedProjects.java
rename to java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
index 38887f6..145ce0e 100644
--- a/java/com/google/gerrit/server/account/PostWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.client.ProjectWatchInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -21,12 +21,15 @@
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.WatchConfig;
 import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ProjectsCollection;
+import com.google.gerrit.server.restapi.project.ProjectsCollection;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/account/PutAccount.java b/java/com/google/gerrit/server/restapi/account/PutAccount.java
similarity index 91%
rename from java/com/google/gerrit/server/account/PutAccount.java
rename to java/com/google/gerrit/server/restapi/account/PutAccount.java
index da5a58f..4c84c19 100644
--- a/java/com/google/gerrit/server/account/PutAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/PutAccount.java
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.api.accounts.AccountInput;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/account/PutActive.java b/java/com/google/gerrit/server/restapi/account/PutActive.java
similarity index 91%
rename from java/com/google/gerrit/server/account/PutActive.java
rename to java/com/google/gerrit/server/restapi/account/PutActive.java
index cbfa172..147b20f 100644
--- a/java/com/google/gerrit/server/account/PutActive.java
+++ b/java/com/google/gerrit/server/restapi/account/PutActive.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
@@ -20,6 +20,8 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.SetInactiveFlag;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/account/PutAgreement.java b/java/com/google/gerrit/server/restapi/account/PutAgreement.java
similarity index 95%
rename from java/com/google/gerrit/server/account/PutAgreement.java
rename to java/com/google/gerrit/server/restapi/account/PutAgreement.java
index dab05a4..ae84081 100644
--- a/java/com/google/gerrit/server/account/PutAgreement.java
+++ b/java/com/google/gerrit/server/restapi/account/PutAgreement.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
@@ -30,10 +30,11 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.extensions.events.AgreementSignup;
-import com.google.gerrit.server.group.AddMembers;
 import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.restapi.group.AddMembers;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/account/PutEmail.java b/java/com/google/gerrit/server/restapi/account/PutEmail.java
similarity index 91%
rename from java/com/google/gerrit/server/account/PutEmail.java
rename to java/com/google/gerrit/server/restapi/account/PutEmail.java
index acdbbf4..6ee9003 100644
--- a/java/com/google/gerrit/server/account/PutEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/PutEmail.java
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.api.accounts.EmailInput;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/account/PutHttpPassword.java b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
similarity index 87%
rename from java/com/google/gerrit/server/account/PutHttpPassword.java
rename to java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
index 5005212..25b3f76 100644
--- a/java/com/google/gerrit/server/account/PutHttpPassword.java
+++ b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
 
@@ -25,9 +25,10 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.AccountsUpdate;
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.account.externalids.ExternalIds;
-import com.google.gerrit.server.account.externalids.ExternalIdsUpdate;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -55,18 +56,18 @@
   private final Provider<CurrentUser> self;
   private final PermissionBackend permissionBackend;
   private final ExternalIds externalIds;
-  private final ExternalIdsUpdate.User externalIdsUpdate;
+  private final AccountsUpdate.User accountsUpdate;
 
   @Inject
   PutHttpPassword(
       Provider<CurrentUser> self,
       PermissionBackend permissionBackend,
       ExternalIds externalIds,
-      ExternalIdsUpdate.User externalIdsUpdate) {
+      AccountsUpdate.User accountsUpdate) {
     this.self = self;
     this.permissionBackend = permissionBackend;
     this.externalIds = externalIds;
-    this.externalIdsUpdate = externalIdsUpdate;
+    this.accountsUpdate = accountsUpdate;
   }
 
   @Override
@@ -106,9 +107,15 @@
     if (extId == null) {
       throw new ResourceNotFoundException();
     }
-    ExternalId newExtId =
-        ExternalId.createWithPassword(extId.key(), extId.accountId(), extId.email(), newPassword);
-    externalIdsUpdate.create().upsert(newExtId);
+    accountsUpdate
+        .create()
+        .update(
+            "Set HTTP Password via API",
+            extId.accountId(),
+            u ->
+                u.updateExternalId(
+                    ExternalId.createWithPassword(
+                        extId.key(), extId.accountId(), extId.email(), newPassword)));
 
     return Strings.isNullOrEmpty(newPassword) ? Response.<String>none() : Response.ok(newPassword);
   }
diff --git a/java/com/google/gerrit/server/account/PutName.java b/java/com/google/gerrit/server/restapi/account/PutName.java
similarity index 94%
rename from java/com/google/gerrit/server/account/PutName.java
rename to java/com/google/gerrit/server/restapi/account/PutName.java
index b63c548..4981e7a 100644
--- a/java/com/google/gerrit/server/account/PutName.java
+++ b/java/com/google/gerrit/server/restapi/account/PutName.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.client.AccountFieldName;
@@ -25,6 +25,9 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.AccountsUpdate;
+import com.google.gerrit.server.account.Realm;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/account/PutPreferred.java b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
similarity index 95%
rename from java/com/google/gerrit/server/account/PutPreferred.java
rename to java/com/google/gerrit/server/restapi/account/PutPreferred.java
index 40e2f7a..b66a611 100644
--- a/java/com/google/gerrit/server/account/PutPreferred.java
+++ b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -22,6 +22,8 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.AccountsUpdate;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/account/PutStatus.java b/java/com/google/gerrit/server/restapi/account/PutStatus.java
similarity index 94%
rename from java/com/google/gerrit/server/account/PutStatus.java
rename to java/com/google/gerrit/server/restapi/account/PutStatus.java
index 85db1da..23958a2 100644
--- a/java/com/google/gerrit/server/account/PutStatus.java
+++ b/java/com/google/gerrit/server/restapi/account/PutStatus.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.api.accounts.StatusInput;
@@ -23,6 +23,8 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.AccountsUpdate;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/account/PutUsername.java b/java/com/google/gerrit/server/restapi/account/PutUsername.java
similarity index 92%
rename from java/com/google/gerrit/server/account/PutUsername.java
rename to java/com/google/gerrit/server/restapi/account/PutUsername.java
index f4ff79d..646fd44 100644
--- a/java/com/google/gerrit/server/account/PutUsername.java
+++ b/java/com/google/gerrit/server/restapi/account/PutUsername.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.common.errors.NameAlreadyUsedException;
 import com.google.gerrit.extensions.api.accounts.UsernameInput;
@@ -23,6 +23,10 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.ChangeUserName;
+import com.google.gerrit.server.account.InvalidUserNameException;
+import com.google.gerrit.server.account.Realm;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/account/QueryAccounts.java b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
similarity index 96%
rename from java/com/google/gerrit/server/account/QueryAccounts.java
rename to java/com/google/gerrit/server/restapi/account/QueryAccounts.java
index 88f0bbc..f508fa2 100644
--- a/java/com/google/gerrit/server/account/QueryAccounts.java
+++ b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
@@ -28,6 +28,9 @@
 import com.google.gerrit.index.query.QueryResult;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.account.AccountDirectory.FillOptions;
+import com.google.gerrit.server.account.AccountInfoComparator;
+import com.google.gerrit.server.account.AccountLoader;
+import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.query.account.AccountPredicates;
 import com.google.gerrit.server.query.account.AccountQueryBuilder;
diff --git a/java/com/google/gerrit/server/account/SetDiffPreferences.java b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
similarity index 91%
rename from java/com/google/gerrit/server/account/SetDiffPreferences.java
rename to java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
index 88e9e20..c16e0f2 100644
--- a/java/com/google/gerrit/server/account/SetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
@@ -12,12 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
-import static com.google.gerrit.server.account.GetDiffPreferences.readDefaultsFromGit;
-import static com.google.gerrit.server.account.GetDiffPreferences.readFromGit;
 import static com.google.gerrit.server.config.ConfigUtil.loadSection;
 import static com.google.gerrit.server.config.ConfigUtil.storeSection;
+import static com.google.gerrit.server.restapi.account.GetDiffPreferences.readDefaultsFromGit;
+import static com.google.gerrit.server.restapi.account.GetDiffPreferences.readFromGit;
 
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -25,6 +25,8 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.VersionedAccountPreferences;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.MetaDataUpdate;
diff --git a/java/com/google/gerrit/server/account/SetEditPreferences.java b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
similarity index 93%
rename from java/com/google/gerrit/server/account/SetEditPreferences.java
rename to java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
index 53285db..3574377 100644
--- a/java/com/google/gerrit/server/account/SetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
@@ -12,11 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
-import static com.google.gerrit.server.account.GetEditPreferences.readFromGit;
 import static com.google.gerrit.server.config.ConfigUtil.loadSection;
 import static com.google.gerrit.server.config.ConfigUtil.storeSection;
+import static com.google.gerrit.server.restapi.account.GetEditPreferences.readFromGit;
 
 import com.google.gerrit.extensions.client.EditPreferencesInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -24,6 +24,8 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.VersionedAccountPreferences;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.MetaDataUpdate;
diff --git a/java/com/google/gerrit/server/account/SetPreferences.java b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
similarity index 95%
rename from java/com/google/gerrit/server/account/SetPreferences.java
rename to java/com/google/gerrit/server/restapi/account/SetPreferences.java
index d25a5a7..2c9f97a 100644
--- a/java/com/google/gerrit/server/account/SetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import static com.google.gerrit.server.config.ConfigUtil.storeSection;
 import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE_COLUMN;
@@ -34,6 +34,10 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.GeneralPreferencesLoader;
+import com.google.gerrit.server.account.VersionedAccountPreferences;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.UserConfigSections;
diff --git a/java/com/google/gerrit/server/account/SshKeys.java b/java/com/google/gerrit/server/restapi/account/SshKeys.java
similarity index 95%
rename from java/com/google/gerrit/server/account/SshKeys.java
rename to java/com/google/gerrit/server/restapi/account/SshKeys.java
index 70c02a1..20fd5cc 100644
--- a/java/com/google/gerrit/server/account/SshKeys.java
+++ b/java/com/google/gerrit/server/restapi/account/SshKeys.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -23,6 +23,8 @@
 import com.google.gerrit.reviewdb.client.AccountSshKey;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.VersionedAuthorizedKeys;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/account/StarredChanges.java b/java/com/google/gerrit/server/restapi/account/StarredChanges.java
similarity index 96%
rename from java/com/google/gerrit/server/account/StarredChanges.java
rename to java/com/google/gerrit/server/restapi/account/StarredChanges.java
index 6dfd132..f908d9f 100644
--- a/java/com/google/gerrit/server/account/StarredChanges.java
+++ b/java/com/google/gerrit/server/restapi/account/StarredChanges.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AcceptsCreate;
@@ -34,10 +34,11 @@
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
 import com.google.gerrit.server.StarredChangesUtil.MutuallyExclusiveLabelsException;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.ChangesCollection;
-import com.google.gerrit.server.change.QueryChanges;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.restapi.change.ChangesCollection;
+import com.google.gerrit.server.restapi.change.QueryChanges;
 import com.google.gwtorm.server.OrmDuplicateKeyException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/account/Stars.java b/java/com/google/gerrit/server/restapi/account/Stars.java
similarity index 95%
rename from java/com/google/gerrit/server/account/Stars.java
rename to java/com/google/gerrit/server/restapi/account/Stars.java
index 5eb8d7b..2ee6aef 100644
--- a/java/com/google/gerrit/server/account/Stars.java
+++ b/java/com/google/gerrit/server/restapi/account/Stars.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.restapi.account;
 
 import com.google.gerrit.extensions.api.changes.StarsInput;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -30,11 +30,12 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.AccountResource.Star;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.ChangesCollection;
-import com.google.gerrit.server.change.QueryChanges;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.restapi.change.ChangesCollection;
+import com.google.gerrit.server.restapi.change.QueryChanges;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/change/Abandon.java b/java/com/google/gerrit/server/restapi/change/Abandon.java
similarity index 95%
rename from java/com/google/gerrit/server/change/Abandon.java
rename to java/com/google/gerrit/server/restapi/change/Abandon.java
index f07efea..5e2b166 100644
--- a/java/com/google/gerrit/server/change/Abandon.java
+++ b/java/com/google/gerrit/server/restapi/change/Abandon.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
 
@@ -29,6 +29,10 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.AbandonOp;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.NotifyUtil;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.permissions.ChangePermission;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/change/AllowedFormats.java b/java/com/google/gerrit/server/restapi/change/AllowedFormats.java
similarity index 94%
rename from java/com/google/gerrit/server/change/AllowedFormats.java
rename to java/com/google/gerrit/server/restapi/change/AllowedFormats.java
index 20e586f..2e313a1 100644
--- a/java/com/google/gerrit/server/change/AllowedFormats.java
+++ b/java/com/google/gerrit/server/restapi/change/AllowedFormats.java
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
+import com.google.gerrit.server.change.ArchiveFormat;
 import com.google.gerrit.server.config.DownloadConfig;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/change/ApplyFix.java b/java/com/google/gerrit/server/restapi/change/ApplyFix.java
similarity index 95%
rename from java/com/google/gerrit/server/change/ApplyFix.java
rename to java/com/google/gerrit/server/restapi/change/ApplyFix.java
index fa26eec..e4940ec 100644
--- a/java/com/google/gerrit/server/change/ApplyFix.java
+++ b/java/com/google/gerrit/server/restapi/change/ApplyFix.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.EditInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -22,6 +22,8 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.change.FixResource;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.edit.ChangeEdit;
 import com.google.gerrit.server.edit.ChangeEditJson;
 import com.google.gerrit.server.edit.ChangeEditModifier;
diff --git a/java/com/google/gerrit/server/change/ChangeEdits.java b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
similarity index 97%
rename from java/com/google/gerrit/server/change/ChangeEdits.java
rename to java/com/google/gerrit/server/restapi/change/ChangeEdits.java
index 1ca98b7..334b7aa 100644
--- a/java/com/google/gerrit/server/change/ChangeEdits.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.common.DiffWebLinkInfo;
@@ -40,6 +40,12 @@
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.WebLinks;
+import com.google.gerrit.server.change.ChangeEditResource;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.FileContentUtil;
+import com.google.gerrit.server.change.FileInfoJson;
+import com.google.gerrit.server.change.RevisionResource;
+import com.google.gerrit.server.change.Revisions;
 import com.google.gerrit.server.edit.ChangeEdit;
 import com.google.gerrit.server.edit.ChangeEditJson;
 import com.google.gerrit.server.edit.ChangeEditModifier;
diff --git a/java/com/google/gerrit/server/change/ChangeIncludedIn.java b/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
similarity index 91%
rename from java/com/google/gerrit/server/change/ChangeIncludedIn.java
rename to java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
index 47f5a16..12b3797 100644
--- a/java/com/google/gerrit/server/change/ChangeIncludedIn.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.api.changes.IncludedInInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
@@ -20,6 +20,8 @@
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.IncludedIn;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/change/ChangesCollection.java b/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
similarity index 97%
rename from java/com/google/gerrit/server/change/ChangesCollection.java
rename to java/com/google/gerrit/server/restapi/change/ChangesCollection.java
index 6ce661e..9203134 100644
--- a/java/com/google/gerrit/server/change/ChangesCollection.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AcceptsPost;
@@ -27,6 +27,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeFinder;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.permissions.ChangePermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/change/Check.java b/java/com/google/gerrit/server/restapi/change/Check.java
similarity index 94%
rename from java/com/google/gerrit/server/change/Check.java
rename to java/com/google/gerrit/server/restapi/change/Check.java
index c07659f..15f013d 100644
--- a/java/com/google/gerrit/server/change/Check.java
+++ b/java/com/google/gerrit/server/restapi/change/Check.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.api.changes.FixInput;
 import com.google.gerrit.extensions.client.ListChangesOption;
@@ -23,6 +23,8 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/change/CherryPick.java b/java/com/google/gerrit/server/restapi/change/CherryPick.java
similarity index 96%
rename from java/com/google/gerrit/server/change/CherryPick.java
rename to java/com/google/gerrit/server/restapi/change/CherryPick.java
index 7fffd3a..c1479b7 100644
--- a/java/com/google/gerrit/server/change/CherryPick.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPick.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
 
@@ -26,6 +26,8 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.git.IntegrationException;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
similarity index 98%
rename from java/com/google/gerrit/server/change/CherryPickChange.java
rename to java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index 8b84f2b..279cc57 100644
--- a/java/com/google/gerrit/server/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.FooterConstants;
@@ -39,6 +39,9 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.ReviewerSet;
 import com.google.gerrit.server.Sequences;
+import com.google.gerrit.server.change.ChangeInserter;
+import com.google.gerrit.server.change.NotifyUtil;
+import com.google.gerrit.server.change.PatchSetInserter;
 import com.google.gerrit.server.git.CodeReviewCommit;
 import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
 import com.google.gerrit.server.git.GitRepositoryManager;
diff --git a/java/com/google/gerrit/server/change/CherryPickCommit.java b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
similarity index 97%
rename from java/com/google/gerrit/server/change/CherryPickCommit.java
rename to java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
index 4980975..039c3ca6 100644
--- a/java/com/google/gerrit/server/change/CherryPickCommit.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.api.changes.CherryPickInput;
@@ -25,6 +25,7 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.ChangeJson;
 import com.google.gerrit.server.git.IntegrationException;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/change/CommentJson.java b/java/com/google/gerrit/server/restapi/change/CommentJson.java
similarity index 99%
rename from java/com/google/gerrit/server/change/CommentJson.java
rename to java/com/google/gerrit/server/restapi/change/CommentJson.java
index 0ebd84b..4d06c73 100644
--- a/java/com/google/gerrit/server/change/CommentJson.java
+++ b/java/com/google/gerrit/server/restapi/change/CommentJson.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.server.CommentsUtil.COMMENT_INFO_ORDER;
 import static java.util.stream.Collectors.toList;
diff --git a/java/com/google/gerrit/server/change/Comments.java b/java/com/google/gerrit/server/restapi/change/Comments.java
similarity index 93%
rename from java/com/google/gerrit/server/change/Comments.java
rename to java/com/google/gerrit/server/restapi/change/Comments.java
index 935aa4e..f563cc6 100644
--- a/java/com/google/gerrit/server/change/Comments.java
+++ b/java/com/google/gerrit/server/restapi/change/Comments.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.ChildCollection;
@@ -22,6 +22,8 @@
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.change.CommentResource;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
similarity index 97%
rename from java/com/google/gerrit/server/change/CreateChange.java
rename to java/com/google/gerrit/server/restapi/change/CreateChange.java
index 56ad000..d4e1c40 100644
--- a/java/com/google/gerrit/server/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static org.eclipse.jgit.lib.Constants.SIGNED_OFF_BY_TAG;
 
@@ -47,6 +47,9 @@
 import com.google.gerrit.server.Sequences;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.change.ChangeInserter;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.NotifyUtil;
 import com.google.gerrit.server.config.AnonymousCowardName;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -56,12 +59,12 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.RefPermission;
-import com.google.gerrit.server.project.CommitsCollection;
 import com.google.gerrit.server.project.ContributorAgreementsChecker;
 import com.google.gerrit.server.project.InvalidChangeOperationException;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.project.ProjectsCollection;
+import com.google.gerrit.server.restapi.project.CommitsCollection;
+import com.google.gerrit.server.restapi.project.ProjectsCollection;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryingRestModifyView;
diff --git a/java/com/google/gerrit/server/change/CreateDraftComment.java b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
similarity index 97%
rename from java/com/google/gerrit/server/change/CreateDraftComment.java
rename to java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
index 002c8b7..afcc8f7 100644
--- a/java/com/google/gerrit/server/change/CreateDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
 
@@ -32,6 +32,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.BatchUpdateOp;
diff --git a/java/com/google/gerrit/server/change/CreateMergePatchSet.java b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
similarity index 97%
rename from java/com/google/gerrit/server/change/CreateMergePatchSet.java
rename to java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
index 35a57cb..33a7453 100644
--- a/java/com/google/gerrit/server/change/CreateMergePatchSet.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
@@ -41,6 +41,9 @@
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.PatchSetInserter;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.MergeIdenticalTreeException;
 import com.google.gerrit.server.git.MergeUtil;
@@ -48,10 +51,10 @@
 import com.google.gerrit.server.permissions.ChangePermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 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.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.restapi.project.CommitsCollection;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryingRestModifyView;
diff --git a/java/com/google/gerrit/server/change/DeleteAssignee.java b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
similarity index 97%
rename from java/com/google/gerrit/server/change/DeleteAssignee.java
rename to java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
index 8d8d72e..2eae7ad 100644
--- a/java/com/google/gerrit/server/change/DeleteAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.extensions.common.AccountInfo;
@@ -26,6 +26,7 @@
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountLoader;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.extensions.events.AssigneeChanged;
 import com.google.gerrit.server.notedb.ChangeUpdate;
 import com.google.gerrit.server.permissions.ChangePermission;
diff --git a/java/com/google/gerrit/server/change/DeleteChange.java b/java/com/google/gerrit/server/restapi/change/DeleteChange.java
similarity index 96%
rename from java/com/google/gerrit/server/change/DeleteChange.java
rename to java/com/google/gerrit/server/restapi/change/DeleteChange.java
index 69f6178..e33b4a4 100644
--- a/java/com/google/gerrit/server/change/DeleteChange.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChange.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
 
@@ -24,6 +24,7 @@
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.ChangePermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/change/DeleteChangeEdit.java b/java/com/google/gerrit/server/restapi/change/DeleteChangeEdit.java
similarity index 94%
rename from java/com/google/gerrit/server/change/DeleteChangeEdit.java
rename to java/com/google/gerrit/server/restapi/change/DeleteChangeEdit.java
index 480aca1..942b191 100644
--- a/java/com/google/gerrit/server/change/DeleteChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChangeEdit.java
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.edit.ChangeEdit;
 import com.google.gerrit.server.edit.ChangeEditUtil;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/server/change/DeleteChangeOp.java b/java/com/google/gerrit/server/restapi/change/DeleteChangeOp.java
similarity index 97%
rename from java/com/google/gerrit/server/change/DeleteChangeOp.java
rename to java/com/google/gerrit/server/restapi/change/DeleteChangeOp.java
index 130d040..1853853 100644
--- a/java/com/google/gerrit/server/change/DeleteChangeOp.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChangeOp.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.common.base.Preconditions.checkState;
 
@@ -25,6 +25,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.StarredChangesUtil;
+import com.google.gerrit.server.change.AccountPatchReviewStore;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.BatchUpdateReviewDb;
diff --git a/java/com/google/gerrit/server/change/DeleteComment.java b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
similarity index 97%
rename from java/com/google/gerrit/server/change/DeleteComment.java
rename to java/com/google/gerrit/server/restapi/change/DeleteComment.java
index 17665b0..4320cd6 100644
--- a/java/com/google/gerrit/server/change/DeleteComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.TimeUtil;
@@ -26,6 +26,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.CommentResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/change/DeleteDraftComment.java b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
similarity index 97%
rename from java/com/google/gerrit/server/change/DeleteDraftComment.java
rename to java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
index 6d82139..ee57c20 100644
--- a/java/com/google/gerrit/server/change/DeleteDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
 
@@ -27,6 +27,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.DraftCommentResource;
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.BatchUpdateOp;
diff --git a/java/com/google/gerrit/server/change/DeletePrivate.java b/java/com/google/gerrit/server/restapi/change/DeletePrivate.java
similarity index 96%
rename from java/com/google/gerrit/server/change/DeletePrivate.java
rename to java/com/google/gerrit/server/restapi/change/DeletePrivate.java
index ba5403a..4ff1b66 100644
--- a/java/com/google/gerrit/server/change/DeletePrivate.java
+++ b/java/com/google/gerrit/server/restapi/change/DeletePrivate.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.extensions.conditions.BooleanCondition.or;
 
@@ -24,6 +24,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeMessagesUtil;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.update.BatchUpdate;
diff --git a/java/com/google/gerrit/server/change/DeletePrivateByPost.java b/java/com/google/gerrit/server/restapi/change/DeletePrivateByPost.java
similarity index 94%
rename from java/com/google/gerrit/server/change/DeletePrivateByPost.java
rename to java/com/google/gerrit/server/restapi/change/DeletePrivateByPost.java
index a392492..cf0143a 100644
--- a/java/com/google/gerrit/server/change/DeletePrivateByPost.java
+++ b/java/com/google/gerrit/server/restapi/change/DeletePrivateByPost.java
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
 
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeMessagesUtil;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/DeleteReviewer.java b/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
similarity index 96%
rename from java/com/google/gerrit/server/change/DeleteReviewer.java
rename to java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
index c2bcd69..3210a95 100644
--- a/java/com/google/gerrit/server/change/DeleteReviewer.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.RetryHelper;
diff --git a/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java b/java/com/google/gerrit/server/restapi/change/DeleteReviewerByEmailOp.java
similarity index 97%
rename from java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
rename to java/com/google/gerrit/server/restapi/change/DeleteReviewerByEmailOp.java
index c8b5bea..f06709d 100644
--- a/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteReviewerByEmailOp.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -20,6 +20,7 @@
 import com.google.gerrit.reviewdb.client.ChangeMessage;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.change.NotifyUtil;
 import com.google.gerrit.server.mail.Address;
 import com.google.gerrit.server.mail.send.DeleteReviewerSender;
 import com.google.gerrit.server.update.BatchUpdateOp;
diff --git a/java/com/google/gerrit/server/change/DeleteReviewerOp.java b/java/com/google/gerrit/server/restapi/change/DeleteReviewerOp.java
similarity index 98%
rename from java/com/google/gerrit/server/change/DeleteReviewerOp.java
rename to java/com/google/gerrit/server/restapi/change/DeleteReviewerOp.java
index a398195..aac16660 100644
--- a/java/com/google/gerrit/server/change/DeleteReviewerOp.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteReviewerOp.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.collect.Iterables;
 import com.google.gerrit.common.data.LabelType;
@@ -33,6 +33,7 @@
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.NotifyUtil;
 import com.google.gerrit.server.extensions.events.ReviewerDeleted;
 import com.google.gerrit.server.mail.send.DeleteReviewerSender;
 import com.google.gerrit.server.notedb.ChangeUpdate;
diff --git a/java/com/google/gerrit/server/change/DeleteVote.java b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
similarity index 97%
rename from java/com/google/gerrit/server/change/DeleteVote.java
rename to java/com/google/gerrit/server/restapi/change/DeleteVote.java
index 8c6c3cc..268425e 100644
--- a/java/com/google/gerrit/server/change/DeleteVote.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -37,6 +37,9 @@
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.NotifyUtil;
+import com.google.gerrit.server.change.ReviewerResource;
+import com.google.gerrit.server.change.VoteResource;
 import com.google.gerrit.server.extensions.events.VoteDeleted;
 import com.google.gerrit.server.mail.send.DeleteVoteSender;
 import com.google.gerrit.server.mail.send.ReplyToChangeSender;
diff --git a/java/com/google/gerrit/server/change/DownloadContent.java b/java/com/google/gerrit/server/restapi/change/DownloadContent.java
similarity index 89%
rename from java/com/google/gerrit/server/change/DownloadContent.java
rename to java/com/google/gerrit/server/restapi/change/DownloadContent.java
index 311a25c..b6564c0 100644
--- a/java/com/google/gerrit/server/change/DownloadContent.java
+++ b/java/com/google/gerrit/server/restapi/change/DownloadContent.java
@@ -12,11 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.FileContentUtil;
+import com.google.gerrit.server.change.FileResource;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/server/change/DraftComments.java b/java/com/google/gerrit/server/restapi/change/DraftComments.java
similarity index 94%
rename from java/com/google/gerrit/server/change/DraftComments.java
rename to java/com/google/gerrit/server/restapi/change/DraftComments.java
index 4befc5b..b8e24a5 100644
--- a/java/com/google/gerrit/server/change/DraftComments.java
+++ b/java/com/google/gerrit/server/restapi/change/DraftComments.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -24,6 +24,8 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.DraftCommentResource;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/change/Files.java b/java/com/google/gerrit/server/restapi/change/Files.java
similarity index 97%
rename from java/com/google/gerrit/server/change/Files.java
rename to java/com/google/gerrit/server/restapi/change/Files.java
index c167e31..8d24942 100644
--- a/java/com/google/gerrit/server/change/Files.java
+++ b/java/com/google/gerrit/server/restapi/change/Files.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.collect.Lists;
 import com.google.common.hash.Hasher;
@@ -36,7 +36,12 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.AccountPatchReviewStore;
 import com.google.gerrit.server.change.AccountPatchReviewStore.PatchSetWithReviewedFiles;
+import com.google.gerrit.server.change.FileInfoJson;
+import com.google.gerrit.server.change.FileResource;
+import com.google.gerrit.server.change.RevisionResource;
+import com.google.gerrit.server.change.Revisions;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.patch.PatchList;
 import com.google.gerrit.server.patch.PatchListCache;
diff --git a/java/com/google/gerrit/server/change/Fixes.java b/java/com/google/gerrit/server/restapi/change/Fixes.java
similarity index 93%
rename from java/com/google/gerrit/server/change/Fixes.java
rename to java/com/google/gerrit/server/restapi/change/Fixes.java
index af9f60a..1d8726d 100644
--- a/java/com/google/gerrit/server/change/Fixes.java
+++ b/java/com/google/gerrit/server/restapi/change/Fixes.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.ChildCollection;
@@ -22,6 +22,8 @@
 import com.google.gerrit.reviewdb.client.FixSuggestion;
 import com.google.gerrit.reviewdb.client.RobotComment;
 import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.change.FixResource;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/GetArchive.java b/java/com/google/gerrit/server/restapi/change/GetArchive.java
similarity index 95%
rename from java/com/google/gerrit/server/change/GetArchive.java
rename to java/com/google/gerrit/server/restapi/change/GetArchive.java
index 7269a60..1bd1bce 100644
--- a/java/com/google/gerrit/server/change/GetArchive.java
+++ b/java/com/google/gerrit/server/restapi/change/GetArchive.java
@@ -12,13 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.ArchiveFormat;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.inject.Inject;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/change/GetAssignee.java b/java/com/google/gerrit/server/restapi/change/GetAssignee.java
similarity index 93%
rename from java/com/google/gerrit/server/change/GetAssignee.java
rename to java/com/google/gerrit/server/restapi/change/GetAssignee.java
index d491b91..f78fae2 100644
--- a/java/com/google/gerrit/server/change/GetAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/GetAssignee.java
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.account.AccountLoader;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/change/GetBlame.java b/java/com/google/gerrit/server/restapi/change/GetBlame.java
similarity index 98%
rename from java/com/google/gerrit/server/change/GetBlame.java
rename to java/com/google/gerrit/server/restapi/change/GetBlame.java
index 4702b5a..6bba936 100644
--- a/java/com/google/gerrit/server/change/GetBlame.java
+++ b/java/com/google/gerrit/server/restapi/change/GetBlame.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
@@ -25,6 +25,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.change.FileResource;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.MergeUtil;
diff --git a/java/com/google/gerrit/server/change/GetChange.java b/java/com/google/gerrit/server/restapi/change/GetChange.java
similarity index 89%
rename from java/com/google/gerrit/server/change/GetChange.java
rename to java/com/google/gerrit/server/restapi/change/GetChange.java
index 22b0b1c..a8f8bbb 100644
--- a/java/com/google/gerrit/server/change/GetChange.java
+++ b/java/com/google/gerrit/server/restapi/change/GetChange.java
@@ -12,12 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.util.EnumSet;
diff --git a/java/com/google/gerrit/server/change/GetComment.java b/java/com/google/gerrit/server/restapi/change/GetComment.java
similarity index 92%
rename from java/com/google/gerrit/server/change/GetComment.java
rename to java/com/google/gerrit/server/restapi/change/GetComment.java
index d601737..b8db6a5 100644
--- a/java/com/google/gerrit/server/change/GetComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetComment.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.CommentResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/change/GetCommit.java b/java/com/google/gerrit/server/restapi/change/GetCommit.java
similarity index 93%
rename from java/com/google/gerrit/server/change/GetCommit.java
rename to java/com/google/gerrit/server/restapi/change/GetCommit.java
index 694379e..645d7d1 100644
--- a/java/com/google/gerrit/server/change/GetCommit.java
+++ b/java/com/google/gerrit/server/restapi/change/GetCommit.java
@@ -12,13 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.CommitInfo;
 import com.google.gerrit.extensions.restapi.CacheControl;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.inject.Inject;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/change/GetContent.java b/java/com/google/gerrit/server/restapi/change/GetContent.java
similarity index 96%
rename from java/com/google/gerrit/server/change/GetContent.java
rename to java/com/google/gerrit/server/restapi/change/GetContent.java
index f6b24b8..6b9bf17 100644
--- a/java/com/google/gerrit/server/change/GetContent.java
+++ b/java/com/google/gerrit/server/restapi/change/GetContent.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.BinaryResult;
@@ -23,6 +23,8 @@
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.FileContentUtil;
+import com.google.gerrit.server.change.FileResource;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.patch.ComparisonType;
diff --git a/java/com/google/gerrit/server/change/GetDescription.java b/java/com/google/gerrit/server/restapi/change/GetDescription.java
similarity index 89%
rename from java/com/google/gerrit/server/change/GetDescription.java
rename to java/com/google/gerrit/server/restapi/change/GetDescription.java
index b8a34d2..1a7ec63 100644
--- a/java/com/google/gerrit/server/change/GetDescription.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDescription.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/change/GetDetail.java b/java/com/google/gerrit/server/restapi/change/GetDetail.java
similarity index 94%
rename from java/com/google/gerrit/server/change/GetDetail.java
rename to java/com/google/gerrit/server/restapi/change/GetDetail.java
index 8213193..ab75ab7 100644
--- a/java/com/google/gerrit/server/change/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDetail.java
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import org.kohsuke.args4j.Option;
diff --git a/java/com/google/gerrit/server/change/GetDiff.java b/java/com/google/gerrit/server/restapi/change/GetDiff.java
similarity index 98%
rename from java/com/google/gerrit/server/change/GetDiff.java
rename to java/com/google/gerrit/server/restapi/change/GetDiff.java
index 25902b9..29ca382 100644
--- a/java/com/google/gerrit/server/change/GetDiff.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDiff.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.common.base.Preconditions.checkState;
 
@@ -44,6 +44,10 @@
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.WebLinks;
+import com.google.gerrit.server.change.FileContentUtil;
+import com.google.gerrit.server.change.FileResource;
+import com.google.gerrit.server.change.RevisionResource;
+import com.google.gerrit.server.change.Revisions;
 import com.google.gerrit.server.git.LargeObjectException;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.patch.PatchScriptFactory;
diff --git a/java/com/google/gerrit/server/change/GetDraftComment.java b/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
similarity index 91%
rename from java/com/google/gerrit/server/change/GetDraftComment.java
rename to java/com/google/gerrit/server/restapi/change/GetDraftComment.java
index a380ce3..787c93e 100644
--- a/java/com/google/gerrit/server/change/GetDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.DraftCommentResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/change/GetHashtags.java b/java/com/google/gerrit/server/restapi/change/GetHashtags.java
similarity index 93%
rename from java/com/google/gerrit/server/change/GetHashtags.java
rename to java/com/google/gerrit/server/restapi/change/GetHashtags.java
index c285734..8369acf 100644
--- a/java/com/google/gerrit/server/change/GetHashtags.java
+++ b/java/com/google/gerrit/server/restapi/change/GetHashtags.java
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/change/GetMergeList.java b/java/com/google/gerrit/server/restapi/change/GetMergeList.java
similarity index 95%
rename from java/com/google/gerrit/server/change/GetMergeList.java
rename to java/com/google/gerrit/server/restapi/change/GetMergeList.java
index 88677d6..2f3b536 100644
--- a/java/com/google/gerrit/server/change/GetMergeList.java
+++ b/java/com/google/gerrit/server/restapi/change/GetMergeList.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.extensions.common.CommitInfo;
@@ -21,6 +21,8 @@
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.patch.MergeListBuilder;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/GetPastAssignees.java b/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java
similarity index 94%
rename from java/com/google/gerrit/server/change/GetPastAssignees.java
rename to java/com/google/gerrit/server/restapi/change/GetPastAssignees.java
index 76114ac..354558b 100644
--- a/java/com/google/gerrit/server/change/GetPastAssignees.java
+++ b/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static java.util.stream.Collectors.toList;
 
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.account.AccountLoader;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/change/GetPatch.java b/java/com/google/gerrit/server/restapi/change/GetPatch.java
similarity index 98%
rename from java/com/google/gerrit/server/change/GetPatch.java
rename to java/com/google/gerrit/server/restapi/change/GetPatch.java
index b59c17c..ccad9e0 100644
--- a/java/com/google/gerrit/server/change/GetPatch.java
+++ b/java/com/google/gerrit/server/restapi/change/GetPatch.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.inject.Inject;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/change/GetPureRevert.java b/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
similarity index 91%
rename from java/com/google/gerrit/server/change/GetPureRevert.java
rename to java/com/google/gerrit/server/restapi/change/GetPureRevert.java
index 815ce07..4b26c5c 100644
--- a/java/com/google/gerrit/server/change/GetPureRevert.java
+++ b/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.common.PureRevertInfo;
@@ -20,6 +20,8 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.PureRevert;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/change/GetRelated.java b/java/com/google/gerrit/server/restapi/change/GetRelated.java
similarity index 86%
rename from java/com/google/gerrit/server/change/GetRelated.java
rename to java/com/google/gerrit/server/restapi/change/GetRelated.java
index 44f65e1..57ec2d9 100644
--- a/java/com/google/gerrit/server/change/GetRelated.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRelated.java
@@ -12,20 +12,24 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
+import static java.util.stream.Collectors.toSet;
+
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.Lists;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.common.CommitInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.index.IndexConfig;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 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.PatchSetUtil;
-import com.google.gerrit.server.change.RelatedChangesSorter.PatchSetData;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.NoSuchProjectException;
@@ -38,7 +42,6 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -50,17 +53,20 @@
   private final Provider<InternalChangeQuery> queryProvider;
   private final PatchSetUtil psUtil;
   private final RelatedChangesSorter sorter;
+  private final IndexConfig indexConfig;
 
   @Inject
   GetRelated(
       Provider<ReviewDb> db,
       Provider<InternalChangeQuery> queryProvider,
       PatchSetUtil psUtil,
-      RelatedChangesSorter sorter) {
+      RelatedChangesSorter sorter,
+      IndexConfig indexConfig) {
     this.db = db;
     this.queryProvider = queryProvider;
     this.psUtil = psUtil;
     this.sorter = sorter;
+    this.indexConfig = indexConfig;
   }
 
   @Override
@@ -74,16 +80,14 @@
 
   private List<ChangeAndCommit> getRelated(RevisionResource rsrc)
       throws OrmException, IOException, PermissionBackendException {
-    Set<String> groups = getAllGroups(rsrc.getNotes());
+    Set<String> groups = getAllGroups(rsrc.getNotes(), db.get(), psUtil);
     if (groups.isEmpty()) {
       return Collections.emptyList();
     }
 
     List<ChangeData> cds =
-        queryProvider
-            .get()
-            .enforceVisibility(true)
-            .byProjectGroups(rsrc.getChange().getProject(), groups);
+        InternalChangeQuery.byProjectGroups(
+            queryProvider, indexConfig, rsrc.getChange().getProject(), groups);
     if (cds.isEmpty()) {
       return Collections.emptyList();
     }
@@ -97,7 +101,7 @@
 
     reloadChangeIfStale(cds, basePs);
 
-    for (PatchSetData d : sorter.sort(cds, basePs, rsrc.getUser())) {
+    for (RelatedChangesSorter.PatchSetData d : sorter.sort(cds, basePs, rsrc.getUser())) {
       PatchSet ps = d.patchSet();
       RevCommit commit;
       if (isEdit && ps.getId().equals(basePs.getId())) {
@@ -119,12 +123,14 @@
     return result;
   }
 
-  private Set<String> getAllGroups(ChangeNotes notes) throws OrmException {
-    Set<String> result = new HashSet<>();
-    for (PatchSet ps : psUtil.byChange(db.get(), notes)) {
-      result.addAll(ps.getGroups());
-    }
-    return result;
+  @VisibleForTesting
+  public static Set<String> getAllGroups(ChangeNotes notes, ReviewDb db, PatchSetUtil psUtil)
+      throws OrmException {
+    return psUtil
+        .byChange(db, notes)
+        .stream()
+        .flatMap(ps -> ps.getGroups().stream())
+        .collect(toSet());
   }
 
   private void reloadChangeIfStale(List<ChangeData> cds, PatchSet wantedPs) throws OrmException {
diff --git a/java/com/google/gerrit/server/change/GetReview.java b/java/com/google/gerrit/server/restapi/change/GetReview.java
similarity index 92%
rename from java/com/google/gerrit/server/change/GetReview.java
rename to java/com/google/gerrit/server/restapi/change/GetReview.java
index f379d83..40e132d 100644
--- a/java/com/google/gerrit/server/change/GetReview.java
+++ b/java/com/google/gerrit/server/restapi/change/GetReview.java
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/change/GetReviewer.java b/java/com/google/gerrit/server/restapi/change/GetReviewer.java
similarity index 92%
rename from java/com/google/gerrit/server/change/GetReviewer.java
rename to java/com/google/gerrit/server/restapi/change/GetReviewer.java
index db9af1d..b9b6b09 100644
--- a/java/com/google/gerrit/server/change/GetReviewer.java
+++ b/java/com/google/gerrit/server/restapi/change/GetReviewer.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.api.changes.ReviewerInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/GetRevisionActions.java b/java/com/google/gerrit/server/restapi/change/GetRevisionActions.java
similarity index 91%
rename from java/com/google/gerrit/server/change/GetRevisionActions.java
rename to java/com/google/gerrit/server/restapi/change/GetRevisionActions.java
index 2a7bd4b..1d6d068 100644
--- a/java/com/google/gerrit/server/change/GetRevisionActions.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRevisionActions.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.hash.Hasher;
 import com.google.common.hash.Hashing;
@@ -21,6 +21,9 @@
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.ActionJson;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.ChangeSet;
 import com.google.gerrit.server.git.MergeSuperSet;
@@ -68,7 +71,7 @@
     CurrentUser user = rsrc.getUser();
     try {
       rsrc.getChangeResource().prepareETag(h, user);
-      h.putBoolean(Submit.wholeTopicEnabled(config));
+      h.putBoolean(MergeSuperSet.wholeTopicEnabled(config));
       ReviewDb db = dbProvider.get();
       ChangeSet cs = mergeSuperSet.get().completeChangeSet(db, rsrc.getChange(), user);
       for (ChangeData cd : cs.changes()) {
diff --git a/java/com/google/gerrit/server/change/GetRobotComment.java b/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
similarity index 92%
rename from java/com/google/gerrit/server/change/GetRobotComment.java
rename to java/com/google/gerrit/server/restapi/change/GetRobotComment.java
index d4d53ad..bd1f66a 100644
--- a/java/com/google/gerrit/server/change/GetRobotComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.RobotCommentInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.RobotCommentResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/change/GetTopic.java b/java/com/google/gerrit/server/restapi/change/GetTopic.java
similarity index 89%
rename from java/com/google/gerrit/server/change/GetTopic.java
rename to java/com/google/gerrit/server/restapi/change/GetTopic.java
index 0746588..7ab1cb1 100644
--- a/java/com/google/gerrit/server/change/GetTopic.java
+++ b/java/com/google/gerrit/server/restapi/change/GetTopic.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/change/Ignore.java b/java/com/google/gerrit/server/restapi/change/Ignore.java
similarity index 96%
rename from java/com/google/gerrit/server/change/Ignore.java
rename to java/com/google/gerrit/server/restapi/change/Ignore.java
index c2c2d1f..d710539 100644
--- a/java/com/google/gerrit/server/change/Ignore.java
+++ b/java/com/google/gerrit/server/restapi/change/Ignore.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -24,6 +24,7 @@
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
 import com.google.gerrit.server.StarredChangesUtil.MutuallyExclusiveLabelsException;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/change/Index.java b/java/com/google/gerrit/server/restapi/change/Index.java
similarity index 95%
rename from java/com/google/gerrit/server/change/Index.java
rename to java/com/google/gerrit/server/restapi/change/Index.java
index 85e13cc..55f53a6 100644
--- a/java/com/google/gerrit/server/change/Index.java
+++ b/java/com/google/gerrit/server/restapi/change/Index.java
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.index.change.ChangeIndexer;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/change/ListChangeComments.java b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
similarity index 95%
rename from java/com/google/gerrit/server/change/ListChangeComments.java
rename to java/com/google/gerrit/server/restapi/change/ListChangeComments.java
index 0048657..37dc207 100644
--- a/java/com/google/gerrit/server/change/ListChangeComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/ListChangeDrafts.java b/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
similarity index 95%
rename from java/com/google/gerrit/server/change/ListChangeDrafts.java
rename to java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
index 02713de..d7a102a 100644
--- a/java/com/google/gerrit/server/change/ListChangeDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -20,6 +20,7 @@
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/ListChangeRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
similarity index 95%
rename from java/com/google/gerrit/server/change/ListChangeRobotComments.java
rename to java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
index fff7f82..dd8de6f 100644
--- a/java/com/google/gerrit/server/change/ListChangeRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.RobotCommentInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/ListReviewers.java b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
similarity index 93%
rename from java/com/google/gerrit/server/change/ListReviewers.java
rename to java/com/google/gerrit/server/restapi/change/ListReviewers.java
index ba2a10b..750e74f 100644
--- a/java/com/google/gerrit/server/change/ListReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
@@ -12,13 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.api.changes.ReviewerInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ApprovalsUtil;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.mail.Address;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/server/change/ListRevisionComments.java b/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
similarity index 93%
rename from java/com/google/gerrit/server/change/ListRevisionComments.java
rename to java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
index 037a856..964e560 100644
--- a/java/com/google/gerrit/server/change/ListRevisionComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
@@ -12,11 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/ListRevisionDrafts.java b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
similarity index 95%
rename from java/com/google/gerrit/server/change/ListRevisionDrafts.java
rename to java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
index 0463601..b7dc553 100644
--- a/java/com/google/gerrit/server/change/ListRevisionDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/change/ListRevisionReviewers.java b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
similarity index 94%
rename from java/com/google/gerrit/server/change/ListRevisionReviewers.java
rename to java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
index 6d9dc79..d0630b7 100644
--- a/java/com/google/gerrit/server/change/ListRevisionReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.api.changes.ReviewerInfo;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
@@ -20,6 +20,8 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ApprovalsUtil;
+import com.google.gerrit.server.change.ReviewerResource;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.mail.Address;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/server/change/ListRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
similarity index 95%
rename from java/com/google/gerrit/server/change/ListRobotComments.java
rename to java/com/google/gerrit/server/restapi/change/ListRobotComments.java
index de2b91a..61219d3 100644
--- a/java/com/google/gerrit/server/change/ListRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.RobotCommentInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.RobotComment;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/change/MarkAsReviewed.java b/java/com/google/gerrit/server/restapi/change/MarkAsReviewed.java
similarity index 96%
rename from java/com/google/gerrit/server/change/MarkAsReviewed.java
rename to java/com/google/gerrit/server/restapi/change/MarkAsReviewed.java
index 9e77805..af64a92 100644
--- a/java/com/google/gerrit/server/change/MarkAsReviewed.java
+++ b/java/com/google/gerrit/server/restapi/change/MarkAsReviewed.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.Response;
@@ -22,6 +22,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/MarkAsUnreviewed.java b/java/com/google/gerrit/server/restapi/change/MarkAsUnreviewed.java
similarity index 96%
rename from java/com/google/gerrit/server/change/MarkAsUnreviewed.java
rename to java/com/google/gerrit/server/restapi/change/MarkAsUnreviewed.java
index 436548b..2e74d61 100644
--- a/java/com/google/gerrit/server/change/MarkAsUnreviewed.java
+++ b/java/com/google/gerrit/server/restapi/change/MarkAsUnreviewed.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.Response;
@@ -21,6 +21,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/Mergeable.java b/java/com/google/gerrit/server/restapi/change/Mergeable.java
similarity index 97%
rename from java/com/google/gerrit/server/change/Mergeable.java
rename to java/com/google/gerrit/server/restapi/change/Mergeable.java
index d1c085a..26f755b 100644
--- a/java/com/google/gerrit/server/change/Mergeable.java
+++ b/java/com/google/gerrit/server/restapi/change/Mergeable.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.common.data.SubmitTypeRecord;
 import com.google.gerrit.extensions.client.SubmitType;
@@ -27,6 +27,8 @@
 import com.google.gerrit.reviewdb.server.ReviewDbUtil;
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.MergeabilityCache;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.git.BranchOrderSection;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.MergeUtil;
diff --git a/java/com/google/gerrit/server/change/Module.java b/java/com/google/gerrit/server/restapi/change/Module.java
similarity index 90%
rename from java/com/google/gerrit/server/change/Module.java
rename to java/com/google/gerrit/server/restapi/change/Module.java
index b4f71af..c18a8c5c 100644
--- a/java/com/google/gerrit/server/change/Module.java
+++ b/java/com/google/gerrit/server/restapi/change/Module.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.server.change.ChangeEditResource.CHANGE_EDIT_KIND;
 import static com.google.gerrit.server.change.ChangeResource.CHANGE_KIND;
@@ -24,12 +24,23 @@
 import static com.google.gerrit.server.change.RevisionResource.REVISION_KIND;
 import static com.google.gerrit.server.change.RobotCommentResource.ROBOT_COMMENT_KIND;
 import static com.google.gerrit.server.change.VoteResource.VOTE_KIND;
+import static com.google.gerrit.server.project.CommitResource.COMMIT_KIND;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.RestApiModule;
 import com.google.gerrit.server.account.AccountLoader;
-import com.google.gerrit.server.change.Reviewed.DeleteReviewed;
-import com.google.gerrit.server.change.Reviewed.PutReviewed;
+import com.google.gerrit.server.change.ChangeInserter;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.EmailReviewComments;
+import com.google.gerrit.server.change.PatchSetInserter;
+import com.google.gerrit.server.change.RebaseChangeOp;
+import com.google.gerrit.server.change.ReviewerResource;
+import com.google.gerrit.server.change.Revisions;
+import com.google.gerrit.server.change.SetAssigneeOp;
+import com.google.gerrit.server.change.SetHashtagsOp;
+import com.google.gerrit.server.change.WorkInProgressOp;
+import com.google.gerrit.server.restapi.change.Reviewed.DeleteReviewed;
+import com.google.gerrit.server.restapi.change.Reviewed.PutReviewed;
 
 public class Module extends RestApiModule {
   @Override
@@ -162,6 +173,7 @@
     delete(CHANGE_EDIT_KIND).to(ChangeEdits.DeleteContent.class);
     get(CHANGE_EDIT_KIND, "/").to(ChangeEdits.Get.class);
     get(CHANGE_EDIT_KIND, "meta").to(ChangeEdits.GetMeta.class);
+    post(COMMIT_KIND, "cherrypick").to(CherryPickCommit.class);
 
     factory(AccountLoader.Factory.class);
     factory(ChangeEdits.Create.Factory.class);
diff --git a/java/com/google/gerrit/server/change/Move.java b/java/com/google/gerrit/server/restapi/change/Move.java
similarity index 98%
rename from java/com/google/gerrit/server/change/Move.java
rename to java/com/google/gerrit/server/restapi/change/Move.java
index 27d4eb1..2607f9c 100644
--- a/java/com/google/gerrit/server/change/Move.java
+++ b/java/com/google/gerrit/server/restapi/change/Move.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
 import static com.google.gerrit.server.permissions.ChangePermission.ABANDON;
@@ -44,6 +44,8 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeUpdate;
 import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/change/PostHashtags.java b/java/com/google/gerrit/server/restapi/change/PostHashtags.java
similarity index 94%
rename from java/com/google/gerrit/server/change/PostHashtags.java
rename to java/com/google/gerrit/server/restapi/change/PostHashtags.java
index 1ff0fdd..f31d04e 100644
--- a/java/com/google/gerrit/server/change/PostHashtags.java
+++ b/java/com/google/gerrit/server/restapi/change/PostHashtags.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.gerrit.common.TimeUtil;
@@ -21,6 +21,8 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.SetHashtagsOp;
 import com.google.gerrit.server.permissions.ChangePermission;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.update.BatchUpdate;
diff --git a/java/com/google/gerrit/server/change/PostPrivate.java b/java/com/google/gerrit/server/restapi/change/PostPrivate.java
similarity index 97%
rename from java/com/google/gerrit/server/change/PostPrivate.java
rename to java/com/google/gerrit/server/restapi/change/PostPrivate.java
index 307d6df..9f02fa8 100644
--- a/java/com/google/gerrit/server/change/PostPrivate.java
+++ b/java/com/google/gerrit/server/restapi/change/PostPrivate.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
 import static com.google.gerrit.extensions.conditions.BooleanCondition.or;
@@ -26,6 +26,7 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeMessagesUtil;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.update.BatchUpdate;
diff --git a/java/com/google/gerrit/server/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
similarity index 98%
rename from java/com/google/gerrit/server/change/PostReview.java
rename to java/com/google/gerrit/server/restapi/change/PostReview.java
index 0022656..7507666 100644
--- a/java/com/google/gerrit/server/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
@@ -86,7 +86,11 @@
 import com.google.gerrit.server.OutputFormat;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.ReviewerSet;
-import com.google.gerrit.server.account.AccountsCollection;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.EmailReviewComments;
+import com.google.gerrit.server.change.NotifyUtil;
+import com.google.gerrit.server.change.RevisionResource;
+import com.google.gerrit.server.change.WorkInProgressOp;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.extensions.events.CommentAdded;
 import com.google.gerrit.server.mail.Address;
@@ -105,6 +109,7 @@
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.restapi.account.AccountsCollection;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.ChangeContext;
diff --git a/java/com/google/gerrit/server/change/PostReviewers.java b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
similarity index 97%
rename from java/com/google/gerrit/server/change/PostReviewers.java
rename to java/com/google/gerrit/server/restapi/change/PostReviewers.java
index 782980b..84dc3e3 100644
--- a/java/com/google/gerrit/server/change/PostReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.gerrit.extensions.client.ReviewerState.CC;
@@ -47,10 +47,13 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountLoader;
-import com.google.gerrit.server.account.AccountsCollection;
 import com.google.gerrit.server.account.GroupMembers;
+import com.google.gerrit.server.change.ChangeMessages;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.NotifyUtil;
+import com.google.gerrit.server.change.ReviewerResource;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.group.GroupsCollection;
 import com.google.gerrit.server.group.SystemGroupBackend;
 import com.google.gerrit.server.mail.Address;
 import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
@@ -63,6 +66,8 @@
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.restapi.account.AccountsCollection;
+import com.google.gerrit.server.restapi.group.GroupsCollection;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryingRestModifyView;
diff --git a/java/com/google/gerrit/server/change/PostReviewersOp.java b/java/com/google/gerrit/server/restapi/change/PostReviewersOp.java
similarity index 98%
rename from java/com/google/gerrit/server/change/PostReviewersOp.java
rename to java/com/google/gerrit/server/restapi/change/PostReviewersOp.java
index aed6cd0..9ec8414 100644
--- a/java/com/google/gerrit/server/change/PostReviewersOp.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReviewersOp.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.gerrit.extensions.client.ReviewerState.CC;
@@ -37,6 +37,7 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.extensions.events.ReviewerAdded;
 import com.google.gerrit.server.mail.Address;
 import com.google.gerrit.server.mail.send.AddReviewerSender;
diff --git a/java/com/google/gerrit/server/change/PreviewSubmit.java b/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
similarity index 97%
rename from java/com/google/gerrit/server/change/PreviewSubmit.java
rename to java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
index 21266a0..e90c60f 100644
--- a/java/com/google/gerrit/server/change/PreviewSubmit.java
+++ b/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.api.changes.SubmitInput;
@@ -29,6 +29,8 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.change.ArchiveFormat;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.MergeOp;
 import com.google.gerrit.server.git.MergeOpRepoManager;
diff --git a/java/com/google/gerrit/server/change/PublishChangeEdit.java b/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
similarity index 94%
rename from java/com/google/gerrit/server/change/PublishChangeEdit.java
rename to java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
index c4e2f3b..b356f18 100644
--- a/java/com/google/gerrit/server/change/PublishChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.api.changes.PublishChangeEditInput;
 import com.google.gerrit.extensions.registration.DynamicMap;
@@ -24,6 +24,9 @@
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.server.change.ChangeEditResource;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.NotifyUtil;
 import com.google.gerrit.server.edit.ChangeEdit;
 import com.google.gerrit.server.edit.ChangeEditUtil;
 import com.google.gerrit.server.project.ContributorAgreementsChecker;
diff --git a/java/com/google/gerrit/server/change/PutAssignee.java b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
similarity index 94%
rename from java/com/google/gerrit/server/change/PutAssignee.java
rename to java/com/google/gerrit/server/restapi/change/PutAssignee.java
index d53c85c..b6fc010 100644
--- a/java/com/google/gerrit/server/change/PutAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.TimeUtil;
@@ -29,10 +29,12 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountLoader;
-import com.google.gerrit.server.account.AccountsCollection;
-import com.google.gerrit.server.change.PostReviewers.Addition;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.SetAssigneeOp;
 import com.google.gerrit.server.permissions.ChangePermission;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.restapi.account.AccountsCollection;
+import com.google.gerrit.server.restapi.change.PostReviewers.Addition;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryingRestModifyView;
diff --git a/java/com/google/gerrit/server/change/PutDescription.java b/java/com/google/gerrit/server/restapi/change/PutDescription.java
similarity index 97%
rename from java/com/google/gerrit/server/change/PutDescription.java
rename to java/com/google/gerrit/server/restapi/change/PutDescription.java
index 0d932ec..38fc2e2 100644
--- a/java/com/google/gerrit/server/change/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDescription.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.TimeUtil;
@@ -25,6 +25,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.notedb.ChangeUpdate;
 import com.google.gerrit.server.permissions.ChangePermission;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/change/PutDraftComment.java b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
similarity index 98%
rename from java/com/google/gerrit/server/change/PutDraftComment.java
rename to java/com/google/gerrit/server/restapi/change/PutDraftComment.java
index c5693c6..3017d89 100644
--- a/java/com/google/gerrit/server/change/PutDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
 
@@ -30,6 +30,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.DraftCommentResource;
 import com.google.gerrit.server.notedb.ChangeUpdate;
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gerrit.server.update.BatchUpdate;
diff --git a/java/com/google/gerrit/server/change/PutMessage.java b/java/com/google/gerrit/server/restapi/change/PutMessage.java
similarity index 97%
rename from java/com/google/gerrit/server/change/PutMessage.java
rename to java/com/google/gerrit/server/restapi/change/PutMessage.java
index 419d789..f277d2c 100644
--- a/java/com/google/gerrit/server/change/PutMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/PutMessage.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.common.FooterConstants;
 import com.google.gerrit.common.TimeUtil;
@@ -30,6 +30,9 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.NotifyUtil;
+import com.google.gerrit.server.change.PatchSetInserter;
 import com.google.gerrit.server.edit.UnchangedCommitMessageException;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeNotes;
diff --git a/java/com/google/gerrit/server/change/PutTopic.java b/java/com/google/gerrit/server/restapi/change/PutTopic.java
similarity index 97%
rename from java/com/google/gerrit/server/change/PutTopic.java
rename to java/com/google/gerrit/server/restapi/change/PutTopic.java
index a461bf0..4685905 100644
--- a/java/com/google/gerrit/server/change/PutTopic.java
+++ b/java/com/google/gerrit/server/restapi/change/PutTopic.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.TimeUtil;
@@ -26,6 +26,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.extensions.events.TopicEdited;
 import com.google.gerrit.server.notedb.ChangeUpdate;
 import com.google.gerrit.server.permissions.ChangePermission;
diff --git a/java/com/google/gerrit/server/change/QueryChanges.java b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
similarity index 97%
rename from java/com/google/gerrit/server/change/QueryChanges.java
rename to java/com/google/gerrit/server/restapi/change/QueryChanges.java
index 51f53b7..7beef20 100644
--- a/java/com/google/gerrit/server/change/QueryChanges.java
+++ b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -26,6 +26,7 @@
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.index.query.QueryRequiresAuthException;
 import com.google.gerrit.index.query.QueryResult;
+import com.google.gerrit.server.change.ChangeJson;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
 import com.google.gerrit.server.query.change.ChangeQueryProcessor;
diff --git a/java/com/google/gerrit/server/change/Rebase.java b/java/com/google/gerrit/server/restapi/change/Rebase.java
similarity index 96%
rename from java/com/google/gerrit/server/change/Rebase.java
rename to java/com/google/gerrit/server/restapi/change/Rebase.java
index 59ab190..7ee5709 100644
--- a/java/com/google/gerrit/server/change/Rebase.java
+++ b/java/com/google/gerrit/server/restapi/change/Rebase.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
 
@@ -36,7 +36,12 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.RebaseChangeOp;
+import com.google.gerrit.server.change.RebaseUtil;
 import com.google.gerrit.server.change.RebaseUtil.Base;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.permissions.ChangePermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/change/RebaseChangeEdit.java b/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
similarity index 95%
rename from java/com/google/gerrit/server/change/RebaseChangeEdit.java
rename to java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
index 2909827..7e1bb4d 100644
--- a/java/com/google/gerrit/server/change/RebaseChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.registration.DynamicMap;
@@ -27,6 +27,8 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.change.ChangeEditResource;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.edit.ChangeEditModifier;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/change/Rebuild.java b/java/com/google/gerrit/server/restapi/change/Rebuild.java
similarity index 97%
rename from java/com/google/gerrit/server/change/Rebuild.java
rename to java/com/google/gerrit/server/restapi/change/Rebuild.java
index a3ed670..4508a99 100644
--- a/java/com/google/gerrit/server/change/Rebuild.java
+++ b/java/com/google/gerrit/server/restapi/change/Rebuild.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static java.util.stream.Collectors.joining;
 
@@ -24,6 +24,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.reviewdb.server.ReviewDbUtil;
 import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.notedb.ChangeBundle;
 import com.google.gerrit.server.notedb.ChangeBundleReader;
 import com.google.gerrit.server.notedb.ChangeNotes;
diff --git a/java/com/google/gerrit/server/change/RelatedChangesSorter.java b/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
similarity index 99%
rename from java/com/google/gerrit/server/change/RelatedChangesSorter.java
rename to java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
index 22ff2b7..1261373 100644
--- a/java/com/google/gerrit/server/change/RelatedChangesSorter.java
+++ b/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
diff --git a/java/com/google/gerrit/server/change/Restore.java b/java/com/google/gerrit/server/restapi/change/Restore.java
similarity index 97%
rename from java/com/google/gerrit/server/change/Restore.java
rename to java/com/google/gerrit/server/restapi/change/Restore.java
index 05e8b4a2..4bf1254 100644
--- a/java/com/google/gerrit/server/change/Restore.java
+++ b/java/com/google/gerrit/server/restapi/change/Restore.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
 
@@ -31,6 +31,8 @@
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.extensions.events.ChangeRestored;
 import com.google.gerrit.server.mail.send.ReplyToChangeSender;
 import com.google.gerrit.server.mail.send.RestoredSender;
diff --git a/java/com/google/gerrit/server/change/Revert.java b/java/com/google/gerrit/server/restapi/change/Revert.java
similarity index 97%
rename from java/com/google/gerrit/server/change/Revert.java
rename to java/com/google/gerrit/server/restapi/change/Revert.java
index 2dfda08..bdab012 100644
--- a/java/com/google/gerrit/server/change/Revert.java
+++ b/java/com/google/gerrit/server/restapi/change/Revert.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
 import static com.google.gerrit.server.permissions.RefPermission.CREATE_CHANGE;
@@ -39,6 +39,10 @@
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.ReviewerSet;
 import com.google.gerrit.server.Sequences;
+import com.google.gerrit.server.change.ChangeInserter;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.ChangeMessages;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.extensions.events.ChangeReverted;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.mail.send.RevertedSender;
diff --git a/java/com/google/gerrit/server/change/Reviewed.java b/java/com/google/gerrit/server/restapi/change/Reviewed.java
similarity index 93%
rename from java/com/google/gerrit/server/change/Reviewed.java
rename to java/com/google/gerrit/server/restapi/change/Reviewed.java
index c412adf..d1a2168 100644
--- a/java/com/google/gerrit/server/change/Reviewed.java
+++ b/java/com/google/gerrit/server/restapi/change/Reviewed.java
@@ -12,12 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.change.AccountPatchReviewStore;
+import com.google.gerrit.server.change.FileResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/change/ReviewerJson.java b/java/com/google/gerrit/server/restapi/change/ReviewerJson.java
similarity index 97%
rename from java/com/google/gerrit/server/change/ReviewerJson.java
rename to java/com/google/gerrit/server/restapi/change/ReviewerJson.java
index c2c69d8..228eb47 100644
--- a/java/com/google/gerrit/server/change/ReviewerJson.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewerJson.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.common.data.LabelValue.formatValue;
 
@@ -29,6 +29,7 @@
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.account.AccountLoader;
+import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.permissions.LabelPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/ReviewerRecommender.java b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
similarity index 98%
rename from java/com/google/gerrit/server/ReviewerRecommender.java
rename to java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
index 50289dd..a27d376 100644
--- a/java/com/google/gerrit/server/ReviewerRecommender.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
 import static java.util.stream.Collectors.toList;
@@ -27,8 +27,8 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.change.ReviewerSuggestion;
-import com.google.gerrit.server.change.SuggestReviewers;
 import com.google.gerrit.server.change.SuggestedReviewer;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.WorkQueue;
diff --git a/java/com/google/gerrit/server/change/Reviewers.java b/java/com/google/gerrit/server/restapi/change/Reviewers.java
similarity index 93%
rename from java/com/google/gerrit/server/change/Reviewers.java
rename to java/com/google/gerrit/server/restapi/change/Reviewers.java
index 8794083..a4cfbd2 100644
--- a/java/com/google/gerrit/server/change/Reviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/Reviewers.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -24,8 +24,10 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ApprovalsUtil;
-import com.google.gerrit.server.account.AccountsCollection;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.mail.Address;
+import com.google.gerrit.server.restapi.account.AccountsCollection;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/ReviewersUtil.java b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
similarity index 98%
rename from java/com/google/gerrit/server/ReviewersUtil.java
rename to java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
index a8410d8..70ebbc1 100644
--- a/java/com/google/gerrit/server/ReviewersUtil.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server;
+package com.google.gerrit.server.restapi.change;
 
 import static java.util.stream.Collectors.toList;
 
@@ -41,8 +41,6 @@
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupMembers;
-import com.google.gerrit.server.change.PostReviewers;
-import com.google.gerrit.server.change.SuggestReviewers;
 import com.google.gerrit.server.index.account.AccountField;
 import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.notedb.ChangeNotes;
diff --git a/java/com/google/gerrit/server/change/RevisionReviewers.java b/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
similarity index 93%
rename from java/com/google/gerrit/server/change/RevisionReviewers.java
rename to java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
index be8bce0..7cf30e2 100644
--- a/java/com/google/gerrit/server/change/RevisionReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -25,8 +25,10 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ApprovalsUtil;
-import com.google.gerrit.server.account.AccountsCollection;
+import com.google.gerrit.server.change.ReviewerResource;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.mail.Address;
+import com.google.gerrit.server.restapi.account.AccountsCollection;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/change/RobotComments.java b/java/com/google/gerrit/server/restapi/change/RobotComments.java
similarity index 92%
rename from java/com/google/gerrit/server/change/RobotComments.java
rename to java/com/google/gerrit/server/restapi/change/RobotComments.java
index d1443af..6570ae0 100644
--- a/java/com/google/gerrit/server/change/RobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/RobotComments.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.ChildCollection;
@@ -21,6 +21,8 @@
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.reviewdb.client.RobotComment;
 import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.change.RevisionResource;
+import com.google.gerrit.server.change.RobotCommentResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/SetPrivateOp.java b/java/com/google/gerrit/server/restapi/change/SetPrivateOp.java
similarity index 98%
rename from java/com/google/gerrit/server/change/SetPrivateOp.java
rename to java/com/google/gerrit/server/restapi/change/SetPrivateOp.java
index 9aa4636..1b50834 100644
--- a/java/com/google/gerrit/server/change/SetPrivateOp.java
+++ b/java/com/google/gerrit/server/restapi/change/SetPrivateOp.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
diff --git a/java/com/google/gerrit/server/change/SetReadyForReview.java b/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
similarity index 94%
rename from java/com/google/gerrit/server/change/SetReadyForReview.java
rename to java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
index 3d258c3..e701bb0 100644
--- a/java/com/google/gerrit/server/change/SetReadyForReview.java
+++ b/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -24,6 +24,8 @@
 import com.google.gerrit.reviewdb.client.Change.Status;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.WorkInProgressOp;
 import com.google.gerrit.server.change.WorkInProgressOp.Input;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.RetryHelper;
diff --git a/java/com/google/gerrit/server/change/SetWorkInProgress.java b/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
similarity index 94%
rename from java/com/google/gerrit/server/change/SetWorkInProgress.java
rename to java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
index 565f67f..9f82433 100644
--- a/java/com/google/gerrit/server/change/SetWorkInProgress.java
+++ b/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -24,6 +24,8 @@
 import com.google.gerrit.reviewdb.client.Change.Status;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.WorkInProgressOp;
 import com.google.gerrit.server.change.WorkInProgressOp.Input;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.RetryHelper;
diff --git a/java/com/google/gerrit/server/change/Submit.java b/java/com/google/gerrit/server/restapi/change/Submit.java
similarity index 95%
rename from java/com/google/gerrit/server/change/Submit.java
rename to java/com/google/gerrit/server/restapi/change/Submit.java
index 84ba88e..04aafff 100644
--- a/java/com/google/gerrit/server/change/Submit.java
+++ b/java/com/google/gerrit/server/restapi/change/Submit.java
@@ -12,11 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static java.util.stream.Collectors.joining;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
 import com.google.common.collect.FluentIterable;
@@ -45,7 +44,9 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.ProjectUtil;
-import com.google.gerrit.server.account.AccountsCollection;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.ChangeSet;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -58,6 +59,7 @@
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.server.restapi.account.AccountsCollection;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.OrmRuntimeException;
@@ -71,7 +73,6 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Queue;
 import java.util.Set;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -113,22 +114,6 @@
     }
   }
 
-  /**
-   * Subclass of {@link SubmitInput} with special bits that may be flipped for testing purposes
-   * only.
-   */
-  @VisibleForTesting
-  public static class TestSubmitInput extends SubmitInput {
-    public boolean failAfterRefUpdates;
-
-    /**
-     * For each change being submitted, an element is removed from this queue and, if the value is
-     * true, a bogus ref update is added to the batch, in order to generate a lock failure during
-     * execution.
-     */
-    public Queue<Boolean> generateLockFailures;
-  }
-
   private final Provider<ReviewDb> dbProvider;
   private final GitRepositoryManager repoManager;
   private final PermissionBackend permissionBackend;
@@ -187,7 +172,7 @@
             MoreObjects.firstNonNull(
                 cfg.getString("change", null, "submitTooltipAncestors"),
                 DEFAULT_TOOLTIP_ANCESTORS));
-    submitWholeTopic = wholeTopicEnabled(cfg);
+    submitWholeTopic = MergeSuperSet.wholeTopicEnabled(cfg);
     this.submitTopicLabel =
         MoreObjects.firstNonNull(
             Strings.emptyToNull(cfg.getString("change", null, "submitTopicLabel")),
@@ -489,10 +474,6 @@
     return submitter;
   }
 
-  public static boolean wholeTopicEnabled(Config config) {
-    return config.getBoolean("change", null, "submitWholeTopic", false);
-  }
-
   private List<ChangeData> getChangesByTopic(String topic) {
     try {
       return queryProvider.get().byTopicOpen(topic);
diff --git a/java/com/google/gerrit/server/change/SubmittedTogether.java b/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
similarity index 96%
rename from java/com/google/gerrit/server/change/SubmittedTogether.java
rename to java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
index 98e47a9..9e7769c 100644
--- a/java/com/google/gerrit/server/change/SubmittedTogether.java
+++ b/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import static com.google.gerrit.extensions.api.changes.SubmittedTogetherOption.NON_VISIBLE_CHANGES;
 
@@ -26,6 +26,9 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.change.ChangeJson;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.WalkSorter;
 import com.google.gerrit.server.change.WalkSorter.PatchSetData;
 import com.google.gerrit.server.git.ChangeSet;
 import com.google.gerrit.server.git.MergeSuperSet;
diff --git a/java/com/google/gerrit/server/change/SuggestChangeReviewers.java b/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
similarity index 95%
rename from java/com/google/gerrit/server/change/SuggestChangeReviewers.java
rename to java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
index fe21858..b8792c4 100644
--- a/java/com/google/gerrit/server/change/SuggestChangeReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.AccountVisibility;
 import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
@@ -24,12 +24,12 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.IdentifiedUser.GenericFactory;
-import com.google.gerrit.server.ReviewersUtil;
-import com.google.gerrit.server.ReviewersUtil.VisibilityControl;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.RefPermission;
 import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.restapi.change.ReviewersUtil.VisibilityControl;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/change/SuggestReviewers.java b/java/com/google/gerrit/server/restapi/change/SuggestReviewers.java
similarity index 97%
rename from java/com/google/gerrit/server/change/SuggestReviewers.java
rename to java/com/google/gerrit/server/restapi/change/SuggestReviewers.java
index 2ed80718..dcb35ab 100644
--- a/java/com/google/gerrit/server/change/SuggestReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/SuggestReviewers.java
@@ -12,12 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.AccountVisibility;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.ReviewersUtil;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/change/TestSubmitRule.java b/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
similarity index 97%
rename from java/com/google/gerrit/server/change/TestSubmitRule.java
rename to java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
index 3b8d439..efb0f4d 100644
--- a/java/com/google/gerrit/server/change/TestSubmitRule.java
+++ b/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.Lists;
@@ -24,6 +24,7 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.account.AccountLoader;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.project.SubmitRuleEvaluator;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.rules.RulesCache;
diff --git a/java/com/google/gerrit/server/change/TestSubmitType.java b/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
similarity index 96%
rename from java/com/google/gerrit/server/change/TestSubmitType.java
rename to java/com/google/gerrit/server/restapi/change/TestSubmitType.java
index 1d869a3..2782a66 100644
--- a/java/com/google/gerrit/server/change/TestSubmitType.java
+++ b/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.common.base.MoreObjects;
 import com.google.gerrit.common.data.SubmitTypeRecord;
@@ -24,6 +24,7 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.project.SubmitRuleEvaluator;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.rules.RulesCache;
diff --git a/java/com/google/gerrit/server/change/Unignore.java b/java/com/google/gerrit/server/restapi/change/Unignore.java
similarity index 94%
rename from java/com/google/gerrit/server/change/Unignore.java
rename to java/com/google/gerrit/server/restapi/change/Unignore.java
index 39a82d7..d1be312 100644
--- a/java/com/google/gerrit/server/change/Unignore.java
+++ b/java/com/google/gerrit/server/restapi/change/Unignore.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.Response;
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/change/Votes.java b/java/com/google/gerrit/server/restapi/change/Votes.java
similarity index 95%
rename from java/com/google/gerrit/server/change/Votes.java
rename to java/com/google/gerrit/server/restapi/change/Votes.java
index c2631d5..b931c7e 100644
--- a/java/com/google/gerrit/server/change/Votes.java
+++ b/java/com/google/gerrit/server/restapi/change/Votes.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.change;
+package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -25,6 +25,8 @@
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ApprovalsUtil;
+import com.google.gerrit.server.change.ReviewerResource;
+import com.google.gerrit.server.change.VoteResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/config/AgreementJson.java b/java/com/google/gerrit/server/restapi/config/AgreementJson.java
similarity index 96%
rename from java/com/google/gerrit/server/config/AgreementJson.java
rename to java/com/google/gerrit/server/restapi/config/AgreementJson.java
index 83eca9c..3ad965d 100644
--- a/java/com/google/gerrit/server/config/AgreementJson.java
+++ b/java/com/google/gerrit/server/restapi/config/AgreementJson.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.config;
+package com.google.gerrit.server.restapi.config;
 
 import com.google.gerrit.common.data.ContributorAgreement;
 import com.google.gerrit.common.data.GroupReference;
@@ -21,8 +21,8 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.GroupControl;
-import com.google.gerrit.server.group.GroupJson;
 import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.restapi.group.GroupJson;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
index c353c5b..4c23f59 100644
--- a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
+++ b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
@@ -45,10 +45,7 @@
 import com.google.gerrit.server.account.AccountVisibilityProvider;
 import com.google.gerrit.server.account.Realm;
 import com.google.gerrit.server.avatar.AvatarProvider;
-import com.google.gerrit.server.change.AllowedFormats;
 import com.google.gerrit.server.change.ArchiveFormat;
-import com.google.gerrit.server.change.Submit;
-import com.google.gerrit.server.config.AgreementJson;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.config.AnonymousCowardName;
@@ -59,10 +56,12 @@
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.documentation.QueryDocumentationExecutor;
+import com.google.gerrit.server.git.MergeSuperSet;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
 import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.restapi.change.AllowedFormats;
 import com.google.inject.Inject;
 import java.net.MalformedURLException;
 import java.nio.file.Files;
@@ -246,7 +245,7 @@
         Optional.ofNullable(cfg.getString("change", null, "replyLabel")).orElse("Reply") + "\u2026";
     info.updateDelay =
         (int) ConfigUtil.getTimeUnit(cfg, "change", null, "updateDelay", 300, TimeUnit.SECONDS);
-    info.submitWholeTopic = Submit.wholeTopicEnabled(cfg);
+    info.submitWholeTopic = MergeSuperSet.wholeTopicEnabled(cfg);
     return info;
   }
 
diff --git a/java/com/google/gerrit/server/restapi/config/SetPreferences.java b/java/com/google/gerrit/server/restapi/config/SetPreferences.java
index 89dc9e5..17908c3 100644
--- a/java/com/google/gerrit/server/restapi/config/SetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/SetPreferences.java
@@ -83,8 +83,8 @@
       p.load(md);
       storeSection(
           p.getConfig(), UserConfigSections.GENERAL, null, i, GeneralPreferencesInfo.defaults());
-      com.google.gerrit.server.account.SetPreferences.storeMyMenus(p, i.my);
-      com.google.gerrit.server.account.SetPreferences.storeUrlAliases(p, i.urlAliases);
+      com.google.gerrit.server.restapi.account.SetPreferences.storeMyMenus(p, i.my);
+      com.google.gerrit.server.restapi.account.SetPreferences.storeUrlAliases(p, i.urlAliases);
       p.commit(md);
 
       accountCache.evictAllNoReindex();
diff --git a/java/com/google/gerrit/server/group/AddMembers.java b/java/com/google/gerrit/server/restapi/group/AddMembers.java
similarity index 95%
rename from java/com/google/gerrit/server/group/AddMembers.java
rename to java/com/google/gerrit/server/restapi/group/AddMembers.java
index b57a462..8971531 100644
--- a/java/com/google/gerrit/server/group/AddMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/AddMembers.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
@@ -35,13 +35,16 @@
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.account.AccountManager;
 import com.google.gerrit.server.account.AccountResolver;
-import com.google.gerrit.server.account.AccountsCollection;
 import com.google.gerrit.server.account.AuthRequest;
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.config.AuthConfig;
-import com.google.gerrit.server.group.AddMembers.Input;
+import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.group.MemberResource;
+import com.google.gerrit.server.group.UserInitiated;
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
+import com.google.gerrit.server.restapi.account.AccountsCollection;
+import com.google.gerrit.server.restapi.group.AddMembers.Input;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/group/AddSubgroups.java b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
similarity index 95%
rename from java/com/google/gerrit/server/group/AddSubgroups.java
rename to java/com/google/gerrit/server/restapi/group/AddSubgroups.java
index af0657a..48e6651 100644
--- a/java/com/google/gerrit/server/group/AddSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
@@ -30,9 +30,12 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.account.GroupControl;
-import com.google.gerrit.server.group.AddSubgroups.Input;
+import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.group.SubgroupResource;
+import com.google.gerrit.server.group.UserInitiated;
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
+import com.google.gerrit.server.restapi.group.AddSubgroups.Input;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/group/CreateGroup.java b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
similarity index 96%
rename from java/com/google/gerrit/server/group/CreateGroup.java
rename to java/com/google/gerrit/server/restapi/group/CreateGroup.java
index b1c8e9b..6201a19 100644
--- a/java/com/google/gerrit/server/group/CreateGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
@@ -42,6 +42,10 @@
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.account.GroupUUID;
 import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.group.InternalGroupDescription;
+import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.group.UserInitiated;
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupCreation;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
diff --git a/java/com/google/gerrit/server/group/DeleteMembers.java b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
similarity index 92%
rename from java/com/google/gerrit/server/group/DeleteMembers.java
rename to java/com/google/gerrit/server/restapi/group/DeleteMembers.java
index 3c04187..f8304d4 100644
--- a/java/com/google/gerrit/server/group/DeleteMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.data.GroupDescription;
@@ -26,11 +26,14 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.account.AccountsCollection;
 import com.google.gerrit.server.account.GroupControl;
-import com.google.gerrit.server.group.AddMembers.Input;
+import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.group.MemberResource;
+import com.google.gerrit.server.group.UserInitiated;
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
+import com.google.gerrit.server.restapi.account.AccountsCollection;
+import com.google.gerrit.server.restapi.group.AddMembers.Input;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/group/DeleteSubgroups.java b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
similarity index 94%
rename from java/com/google/gerrit/server/group/DeleteSubgroups.java
rename to java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
index 901847c..e12fe12 100644
--- a/java/com/google/gerrit/server/group/DeleteSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
@@ -27,9 +27,12 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.account.GroupControl;
-import com.google.gerrit.server.group.AddSubgroups.Input;
+import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.group.SubgroupResource;
+import com.google.gerrit.server.group.UserInitiated;
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
+import com.google.gerrit.server.restapi.group.AddSubgroups.Input;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/group/GetAuditLog.java b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
similarity index 96%
rename from java/com/google/gerrit/server/group/GetAuditLog.java
rename to java/com/google/gerrit/server/restapi/group/GetAuditLog.java
index 52a9dd9..000df6c 100644
--- a/java/com/google/gerrit/server/group/GetAuditLog.java
+++ b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import static java.util.Comparator.comparing;
 
@@ -33,6 +33,9 @@
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.group.InternalGroupDescription;
 import com.google.gerrit.server.group.db.Groups;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/group/GetDescription.java b/java/com/google/gerrit/server/restapi/group/GetDescription.java
similarity index 92%
rename from java/com/google/gerrit/server/group/GetDescription.java
rename to java/com/google/gerrit/server/restapi/group/GetDescription.java
index 0610843..284ff2e 100644
--- a/java/com/google/gerrit/server/group/GetDescription.java
+++ b/java/com/google/gerrit/server/restapi/group/GetDescription.java
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.group.GroupResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/group/GetDetail.java b/java/com/google/gerrit/server/restapi/group/GetDetail.java
similarity index 92%
rename from java/com/google/gerrit/server/group/GetDetail.java
rename to java/com/google/gerrit/server/restapi/group/GetDetail.java
index 47fe319..e7b240e 100644
--- a/java/com/google/gerrit/server/group/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/group/GetDetail.java
@@ -12,11 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.extensions.client.ListGroupsOption;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.group.GroupResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/group/GetGroup.java b/java/com/google/gerrit/server/restapi/group/GetGroup.java
similarity index 91%
rename from java/com/google/gerrit/server/group/GetGroup.java
rename to java/com/google/gerrit/server/restapi/group/GetGroup.java
index 03c6d6c..81057fd 100644
--- a/java/com/google/gerrit/server/group/GetGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/GetGroup.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.group.GroupResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/group/GetMember.java b/java/com/google/gerrit/server/restapi/group/GetMember.java
similarity index 92%
rename from java/com/google/gerrit/server/group/GetMember.java
rename to java/com/google/gerrit/server/restapi/group/GetMember.java
index 9d270ec..db33785 100644
--- a/java/com/google/gerrit/server/group/GetMember.java
+++ b/java/com/google/gerrit/server/restapi/group/GetMember.java
@@ -12,11 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.AccountLoader;
+import com.google.gerrit.server.group.MemberResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/group/GetName.java b/java/com/google/gerrit/server/restapi/group/GetName.java
similarity index 89%
rename from java/com/google/gerrit/server/group/GetName.java
rename to java/com/google/gerrit/server/restapi/group/GetName.java
index ce4df2a..8cc1fe0 100644
--- a/java/com/google/gerrit/server/group/GetName.java
+++ b/java/com/google/gerrit/server/restapi/group/GetName.java
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.group.GroupResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/group/GetOptions.java b/java/com/google/gerrit/server/restapi/group/GetOptions.java
similarity index 90%
rename from java/com/google/gerrit/server/group/GetOptions.java
rename to java/com/google/gerrit/server/restapi/group/GetOptions.java
index 7b55666..e5bfe30 100644
--- a/java/com/google/gerrit/server/group/GetOptions.java
+++ b/java/com/google/gerrit/server/restapi/group/GetOptions.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.extensions.common.GroupOptionsInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.group.GroupResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/group/GetOwner.java b/java/com/google/gerrit/server/restapi/group/GetOwner.java
similarity index 94%
rename from java/com/google/gerrit/server/group/GetOwner.java
rename to java/com/google/gerrit/server/restapi/group/GetOwner.java
index 03d0788..f46826f 100644
--- a/java/com/google/gerrit/server/group/GetOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/GetOwner.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.common.errors.NoSuchGroupException;
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.GroupControl;
+import com.google.gerrit.server.group.GroupResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/group/GetSubgroup.java b/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
similarity index 91%
rename from java/com/google/gerrit/server/group/GetSubgroup.java
rename to java/com/google/gerrit/server/restapi/group/GetSubgroup.java
index a710188..98e6ce5 100644
--- a/java/com/google/gerrit/server/group/GetSubgroup.java
+++ b/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.group.SubgroupResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/group/GroupJson.java b/java/com/google/gerrit/server/restapi/group/GroupJson.java
similarity index 97%
rename from java/com/google/gerrit/server/group/GroupJson.java
rename to java/com/google/gerrit/server/restapi/group/GroupJson.java
index aed4358..3c7799b 100644
--- a/java/com/google/gerrit/server/group/GroupJson.java
+++ b/java/com/google/gerrit/server/restapi/group/GroupJson.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import static com.google.gerrit.extensions.client.ListGroupsOption.INCLUDES;
 import static com.google.gerrit.extensions.client.ListGroupsOption.MEMBERS;
@@ -27,6 +27,7 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupControl;
+import com.google.gerrit.server.group.GroupResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/group/GroupModule.java b/java/com/google/gerrit/server/restapi/group/GroupModule.java
similarity index 93%
rename from java/com/google/gerrit/server/group/GroupModule.java
rename to java/com/google/gerrit/server/restapi/group/GroupModule.java
index 5939fd6..2bf6bcc 100644
--- a/java/com/google/gerrit/server/group/GroupModule.java
+++ b/java/com/google/gerrit/server/restapi/group/GroupModule.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import static com.google.inject.Scopes.SINGLETON;
 
@@ -23,6 +23,7 @@
 import com.google.gerrit.server.account.IncludingGroupMembership;
 import com.google.gerrit.server.account.InternalGroupBackend;
 import com.google.gerrit.server.account.UniversalGroupBackend;
+import com.google.gerrit.server.group.SystemGroupBackend;
 
 public class GroupModule extends FactoryModule {
 
diff --git a/java/com/google/gerrit/server/group/GroupsCollection.java b/java/com/google/gerrit/server/restapi/group/GroupsCollection.java
similarity index 98%
rename from java/com/google/gerrit/server/group/GroupsCollection.java
rename to java/com/google/gerrit/server/restapi/group/GroupsCollection.java
index 4d3bd11..b7a8d55 100644
--- a/java/com/google/gerrit/server/group/GroupsCollection.java
+++ b/java/com/google/gerrit/server/restapi/group/GroupsCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.data.GroupDescription;
@@ -35,6 +35,7 @@
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupBackends;
 import com.google.gerrit.server.account.GroupControl;
+import com.google.gerrit.server.group.GroupResource;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
diff --git a/java/com/google/gerrit/server/group/Index.java b/java/com/google/gerrit/server/restapi/group/Index.java
similarity index 92%
rename from java/com/google/gerrit/server/group/Index.java
rename to java/com/google/gerrit/server/restapi/group/Index.java
index b2845fa..22003c0 100644
--- a/java/com/google/gerrit/server/group/Index.java
+++ b/java/com/google/gerrit/server/restapi/group/Index.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -21,6 +21,8 @@
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.group.InternalGroup;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/group/ListGroups.java b/java/com/google/gerrit/server/restapi/group/ListGroups.java
similarity index 98%
rename from java/com/google/gerrit/server/group/ListGroups.java
rename to java/com/google/gerrit/server/restapi/group/ListGroups.java
index 77778f3..91aee6d 100644
--- a/java/com/google/gerrit/server/group/ListGroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListGroups.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import static com.google.common.collect.ImmutableList.toImmutableList;
 
@@ -37,12 +37,13 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountResource;
-import com.google.gerrit.server.account.GetGroups;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.account.GroupControl;
+import com.google.gerrit.server.group.InternalGroupDescription;
 import com.google.gerrit.server.group.db.Groups;
 import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.restapi.account.GetGroups;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/group/ListMembers.java b/java/com/google/gerrit/server/restapi/group/ListMembers.java
similarity index 96%
rename from java/com/google/gerrit/server/group/ListMembers.java
rename to java/com/google/gerrit/server/restapi/group/ListMembers.java
index fa33957..de0decd 100644
--- a/java/com/google/gerrit/server/group/ListMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/ListMembers.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.ImmutableSet.toImmutableSet;
@@ -30,6 +30,9 @@
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.account.GroupControl;
+import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.group.InternalGroupDescription;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.util.ArrayList;
diff --git a/java/com/google/gerrit/server/group/ListSubgroups.java b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
similarity index 96%
rename from java/com/google/gerrit/server/group/ListSubgroups.java
rename to java/com/google/gerrit/server/restapi/group/ListSubgroups.java
index 42dcad3..bea079f 100644
--- a/java/com/google/gerrit/server/group/ListSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import static com.google.common.base.Strings.nullToEmpty;
 
@@ -23,6 +23,7 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.account.GroupControl;
+import com.google.gerrit.server.group.GroupResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/group/MembersCollection.java b/java/com/google/gerrit/server/restapi/group/MembersCollection.java
similarity index 91%
rename from java/com/google/gerrit/server/group/MembersCollection.java
rename to java/com/google/gerrit/server/restapi/group/MembersCollection.java
index 59bd62f..1523a343 100644
--- a/java/com/google/gerrit/server/group/MembersCollection.java
+++ b/java/com/google/gerrit/server/restapi/group/MembersCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.extensions.registration.DynamicMap;
@@ -25,8 +25,10 @@
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountsCollection;
-import com.google.gerrit.server.group.AddMembers.PutMember;
+import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.group.MemberResource;
+import com.google.gerrit.server.restapi.account.AccountsCollection;
+import com.google.gerrit.server.restapi.group.AddMembers.PutMember;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/restapi/group/Module.java b/java/com/google/gerrit/server/restapi/group/Module.java
new file mode 100644
index 0000000..cfe4efb
--- /dev/null
+++ b/java/com/google/gerrit/server/restapi/group/Module.java
@@ -0,0 +1,91 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.restapi.group;
+
+import static com.google.gerrit.server.group.GroupResource.GROUP_KIND;
+import static com.google.gerrit.server.group.MemberResource.MEMBER_KIND;
+import static com.google.gerrit.server.group.SubgroupResource.SUBGROUP_KIND;
+
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.RestApiModule;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.group.ServerInitiated;
+import com.google.gerrit.server.group.UserInitiated;
+import com.google.gerrit.server.group.db.GroupsUpdate;
+import com.google.gerrit.server.restapi.group.AddMembers.UpdateMember;
+import com.google.gerrit.server.restapi.group.AddSubgroups.UpdateSubgroup;
+import com.google.gerrit.server.restapi.group.DeleteMembers.DeleteMember;
+import com.google.gerrit.server.restapi.group.DeleteSubgroups.DeleteSubgroup;
+import com.google.inject.Provides;
+
+public class Module extends RestApiModule {
+
+  @Override
+  protected void configure() {
+    bind(GroupsCollection.class);
+
+    DynamicMap.mapOf(binder(), GROUP_KIND);
+    DynamicMap.mapOf(binder(), MEMBER_KIND);
+    DynamicMap.mapOf(binder(), SUBGROUP_KIND);
+
+    get(GROUP_KIND).to(GetGroup.class);
+    put(GROUP_KIND).to(PutGroup.class);
+    get(GROUP_KIND, "detail").to(GetDetail.class);
+    post(GROUP_KIND, "index").to(Index.class);
+    post(GROUP_KIND, "members").to(AddMembers.class);
+    post(GROUP_KIND, "members.add").to(AddMembers.class);
+    post(GROUP_KIND, "members.delete").to(DeleteMembers.class);
+    post(GROUP_KIND, "groups").to(AddSubgroups.class);
+    post(GROUP_KIND, "groups.add").to(AddSubgroups.class);
+    post(GROUP_KIND, "groups.delete").to(DeleteSubgroups.class);
+    get(GROUP_KIND, "description").to(GetDescription.class);
+    put(GROUP_KIND, "description").to(PutDescription.class);
+    delete(GROUP_KIND, "description").to(PutDescription.class);
+    get(GROUP_KIND, "name").to(GetName.class);
+    put(GROUP_KIND, "name").to(PutName.class);
+    get(GROUP_KIND, "owner").to(GetOwner.class);
+    put(GROUP_KIND, "owner").to(PutOwner.class);
+    get(GROUP_KIND, "options").to(GetOptions.class);
+    put(GROUP_KIND, "options").to(PutOptions.class);
+    get(GROUP_KIND, "log.audit").to(GetAuditLog.class);
+    post(GROUP_KIND, "rebuild").to(Rebuild.class);
+
+    child(GROUP_KIND, "members").to(MembersCollection.class);
+    get(MEMBER_KIND).to(GetMember.class);
+    put(MEMBER_KIND).to(UpdateMember.class);
+    delete(MEMBER_KIND).to(DeleteMember.class);
+
+    child(GROUP_KIND, "groups").to(SubgroupsCollection.class);
+    get(SUBGROUP_KIND).to(GetSubgroup.class);
+    put(SUBGROUP_KIND).to(UpdateSubgroup.class);
+    delete(SUBGROUP_KIND).to(DeleteSubgroup.class);
+
+    factory(CreateGroup.Factory.class);
+    factory(GroupsUpdate.Factory.class);
+  }
+
+  @Provides
+  @ServerInitiated
+  GroupsUpdate provideServerInitiatedGroupsUpdate(GroupsUpdate.Factory groupsUpdateFactory) {
+    return groupsUpdateFactory.create(null);
+  }
+
+  @Provides
+  @UserInitiated
+  GroupsUpdate provideUserInitiatedGroupsUpdate(
+      GroupsUpdate.Factory groupsUpdateFactory, IdentifiedUser currentUser) {
+    return groupsUpdateFactory.create(currentUser);
+  }
+}
diff --git a/java/com/google/gerrit/server/group/PutDescription.java b/java/com/google/gerrit/server/restapi/group/PutDescription.java
similarity index 95%
rename from java/com/google/gerrit/server/group/PutDescription.java
rename to java/com/google/gerrit/server/restapi/group/PutDescription.java
index bf9f9a0..d48637d 100644
--- a/java/com/google/gerrit/server/group/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/group/PutDescription.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.data.GroupDescription;
@@ -25,6 +25,8 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.group.UserInitiated;
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/server/group/PutGroup.java b/java/com/google/gerrit/server/restapi/group/PutGroup.java
similarity index 91%
rename from java/com/google/gerrit/server/group/PutGroup.java
rename to java/com/google/gerrit/server/restapi/group/PutGroup.java
index abaa317..33dcb8d 100644
--- a/java/com/google/gerrit/server/group/PutGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/PutGroup.java
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.extensions.api.groups.GroupInput;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.group.GroupResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/group/PutName.java b/java/com/google/gerrit/server/restapi/group/PutName.java
similarity index 95%
rename from java/com/google/gerrit/server/group/PutName.java
rename to java/com/google/gerrit/server/restapi/group/PutName.java
index 404c918..a86cea0 100644
--- a/java/com/google/gerrit/server/group/PutName.java
+++ b/java/com/google/gerrit/server/restapi/group/PutName.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.data.GroupDescription;
@@ -26,6 +26,8 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.group.UserInitiated;
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gwtorm.server.OrmDuplicateKeyException;
diff --git a/java/com/google/gerrit/server/group/PutOptions.java b/java/com/google/gerrit/server/restapi/group/PutOptions.java
similarity index 95%
rename from java/com/google/gerrit/server/group/PutOptions.java
rename to java/com/google/gerrit/server/restapi/group/PutOptions.java
index 7486066..c7f8552 100644
--- a/java/com/google/gerrit/server/group/PutOptions.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOptions.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.common.errors.NoSuchGroupException;
@@ -24,6 +24,8 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.group.UserInitiated;
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/server/group/PutOwner.java b/java/com/google/gerrit/server/restapi/group/PutOwner.java
similarity index 95%
rename from java/com/google/gerrit/server/group/PutOwner.java
rename to java/com/google/gerrit/server/restapi/group/PutOwner.java
index 792640d..6fb3698 100644
--- a/java/com/google/gerrit/server/group/PutOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOwner.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.data.GroupDescription;
@@ -27,6 +27,8 @@
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.group.UserInitiated;
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/server/group/QueryGroups.java b/java/com/google/gerrit/server/restapi/group/QueryGroups.java
similarity index 96%
rename from java/com/google/gerrit/server/group/QueryGroups.java
rename to java/com/google/gerrit/server/restapi/group/QueryGroups.java
index 0a89b2a..df04a2c 100644
--- a/java/com/google/gerrit/server/group/QueryGroups.java
+++ b/java/com/google/gerrit/server/restapi/group/QueryGroups.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
@@ -24,6 +24,8 @@
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.index.query.QueryResult;
+import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.group.InternalGroupDescription;
 import com.google.gerrit.server.query.group.GroupQueryBuilder;
 import com.google.gerrit.server.query.group.GroupQueryProcessor;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/server/group/Rebuild.java b/java/com/google/gerrit/server/restapi/group/Rebuild.java
similarity index 96%
rename from java/com/google/gerrit/server/group/Rebuild.java
rename to java/com/google/gerrit/server/restapi/group/Rebuild.java
index d3d59f8..9e8cb3f 100644
--- a/java/com/google/gerrit/server/group/Rebuild.java
+++ b/java/com/google/gerrit/server/restapi/group/Rebuild.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import static com.google.common.base.MoreObjects.firstNonNull;
 import static java.util.stream.Collectors.joining;
@@ -29,10 +29,11 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.group.Rebuild.Input;
+import com.google.gerrit.server.group.GroupResource;
 import com.google.gerrit.server.group.db.GroupBundle;
 import com.google.gerrit.server.group.db.GroupRebuilder;
 import com.google.gerrit.server.notedb.GroupsMigration;
+import com.google.gerrit.server.restapi.group.Rebuild.Input;
 import com.google.gerrit.server.update.RefUpdateUtil;
 import com.google.gwtorm.server.OrmDuplicateKeyException;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/server/group/SubgroupsCollection.java b/java/com/google/gerrit/server/restapi/group/SubgroupsCollection.java
similarity index 92%
rename from java/com/google/gerrit/server/group/SubgroupsCollection.java
rename to java/com/google/gerrit/server/restapi/group/SubgroupsCollection.java
index 96300ad..10b9e04 100644
--- a/java/com/google/gerrit/server/group/SubgroupsCollection.java
+++ b/java/com/google/gerrit/server/restapi/group/SubgroupsCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.group;
+package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.extensions.registration.DynamicMap;
@@ -24,7 +24,9 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.server.group.AddSubgroups.PutSubgroup;
+import com.google.gerrit.server.group.GroupResource;
+import com.google.gerrit.server.group.SubgroupResource;
+import com.google.gerrit.server.restapi.group.AddSubgroups.PutSubgroup;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
diff --git a/java/com/google/gerrit/server/project/BanCommit.java b/java/com/google/gerrit/server/restapi/project/BanCommit.java
similarity index 94%
rename from java/com/google/gerrit/server/project/BanCommit.java
rename to java/com/google/gerrit/server/restapi/project/BanCommit.java
index c3623c6..3d101b2 100644
--- a/java/com/google/gerrit/server/project/BanCommit.java
+++ b/java/com/google/gerrit/server/restapi/project/BanCommit.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.api.projects.BanCommitInput;
@@ -20,7 +20,8 @@
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.server.git.BanCommitResult;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.BanCommit.BanResultInfo;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.restapi.project.BanCommit.BanResultInfo;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryingRestModifyView;
diff --git a/java/com/google/gerrit/server/project/BranchesCollection.java b/java/com/google/gerrit/server/restapi/project/BranchesCollection.java
similarity index 95%
rename from java/com/google/gerrit/server/project/BranchesCollection.java
rename to java/com/google/gerrit/server/restapi/project/BranchesCollection.java
index 52072d8..b90bd9c 100644
--- a/java/com/google/gerrit/server/project/BranchesCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/BranchesCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AcceptsCreate;
@@ -28,6 +28,8 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.BranchResource;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/CheckAccess.java b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
similarity index 97%
rename from java/com/google/gerrit/server/project/CheckAccess.java
rename to java/com/google/gerrit/server/restapi/project/CheckAccess.java
index b8d3fbc..deefa1a 100644
--- a/java/com/google/gerrit/server/project/CheckAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.api.config.AccessCheckInfo;
@@ -31,6 +31,7 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/CheckMergeability.java b/java/com/google/gerrit/server/restapi/project/CheckMergeability.java
similarity index 97%
rename from java/com/google/gerrit/server/project/CheckMergeability.java
rename to java/com/google/gerrit/server/restapi/project/CheckMergeability.java
index 1ab2dbd..dd1c9a5 100644
--- a/java/com/google/gerrit/server/project/CheckMergeability.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckMergeability.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.common.MergeableInfo;
@@ -23,6 +23,7 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.InMemoryInserter;
 import com.google.gerrit.server.git.MergeUtil;
+import com.google.gerrit.server.project.BranchResource;
 import com.google.inject.Inject;
 import java.io.IOException;
 import org.eclipse.jgit.lib.Config;
diff --git a/java/com/google/gerrit/server/project/ChildProjectsCollection.java b/java/com/google/gerrit/server/restapi/project/ChildProjectsCollection.java
similarity index 91%
rename from java/com/google/gerrit/server/project/ChildProjectsCollection.java
rename to java/com/google/gerrit/server/restapi/project/ChildProjectsCollection.java
index e008d66..8f0e03c 100644
--- a/java/com/google/gerrit/server/project/ChildProjectsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/ChildProjectsCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -22,6 +22,9 @@
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.ChildProjectResource;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/CommitIncludedIn.java b/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
similarity index 93%
rename from java/com/google/gerrit/server/project/CommitIncludedIn.java
rename to java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
index 5b36916..d43edfb 100644
--- a/java/com/google/gerrit/server/project/CommitIncludedIn.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.api.changes.IncludedInInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.change.IncludedIn;
+import com.google.gerrit.server.project.CommitResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/CommitsCollection.java b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
similarity index 93%
rename from java/com/google/gerrit/server/project/CommitsCollection.java
rename to java/com/google/gerrit/server/restapi/project/CommitsCollection.java
index 481c2c8..6aa639f 100644
--- a/java/com/google/gerrit/server/project/CommitsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.ChildCollection;
@@ -21,6 +21,10 @@
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.CommitResource;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.project.Reachable;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/server/project/ConfigInfoImpl.java b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
similarity index 96%
rename from java/com/google/gerrit/server/project/ConfigInfoImpl.java
rename to java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
index 943f606..70dc317 100644
--- a/java/com/google/gerrit/server/project/ConfigInfoImpl.java
+++ b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
@@ -33,6 +33,9 @@
 import com.google.gerrit.server.config.ProjectConfigEntry;
 import com.google.gerrit.server.extensions.webui.UiActions;
 import com.google.gerrit.server.git.TransferConfig;
+import com.google.gerrit.server.project.BooleanProjectConfigTransformations;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
 import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.Map;
diff --git a/java/com/google/gerrit/server/project/CreateAccessChange.java b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
similarity index 98%
rename from java/com/google/gerrit/server/project/CreateAccessChange.java
rename to java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
index 31b48cd..a06c8c5 100644
--- a/java/com/google/gerrit/server/project/CreateAccessChange.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.gerrit.common.TimeUtil;
@@ -40,6 +40,7 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/server/project/CreateBranch.java b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
similarity index 95%
rename from java/com/google/gerrit/server/project/CreateBranch.java
rename to java/com/google/gerrit/server/restapi/project/CreateBranch.java
index 0a648d9..0b62c15 100644
--- a/java/com/google/gerrit/server/project/CreateBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.api.projects.BranchInfo;
 import com.google.gerrit.extensions.api.projects.BranchInput;
@@ -28,6 +28,11 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.CreateRefControl;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.RefUtil;
+import com.google.gerrit.server.project.RefValidationHelper;
 import com.google.gerrit.server.util.MagicBranch;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -157,6 +162,7 @@
               }
               refPrefix = RefUtil.getRefPrefix(refPrefix);
             }
+            // fall through
             // $FALL-THROUGH$
           case FORCED:
           case IO_FAILURE:
diff --git a/java/com/google/gerrit/server/project/CreateProject.java b/java/com/google/gerrit/server/restapi/project/CreateProject.java
similarity index 97%
rename from java/com/google/gerrit/server/project/CreateProject.java
rename to java/com/google/gerrit/server/restapi/project/CreateProject.java
index 6a4de1c..56c004a 100644
--- a/java/com/google/gerrit/server/project/CreateProject.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateProject.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
@@ -56,8 +56,13 @@
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.git.RepositoryCaseMismatchException;
-import com.google.gerrit.server.group.GroupsCollection;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.CreateProjectArgs;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectJson;
+import com.google.gerrit.server.project.ProjectNameLockManager;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.restapi.group.GroupsCollection;
 import com.google.gerrit.server.validators.ProjectCreationValidationListener;
 import com.google.gerrit.server.validators.ValidationException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/project/CreateTag.java b/java/com/google/gerrit/server/restapi/project/CreateTag.java
similarity index 95%
rename from java/com/google/gerrit/server/project/CreateTag.java
rename to java/com/google/gerrit/server/restapi/project/CreateTag.java
index 8e706a2..0b0ce10 100644
--- a/java/com/google/gerrit/server/project/CreateTag.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateTag.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static org.eclipse.jgit.lib.Constants.R_TAGS;
 
@@ -35,6 +35,11 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.RefControl;
+import com.google.gerrit.server.project.RefUtil;
 import com.google.gerrit.server.project.RefUtil.InvalidRevisionException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/project/DashboardsCollection.java b/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java
similarity index 97%
rename from java/com/google/gerrit/server/project/DashboardsCollection.java
rename to java/com/google/gerrit/server/restapi/project/DashboardsCollection.java
index d5c591f..e3ffa4d 100644
--- a/java/com/google/gerrit/server/project/DashboardsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static com.google.gerrit.reviewdb.client.RefNames.REFS_DASHBOARDS;
 
@@ -41,6 +41,9 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.DashboardResource;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/DeleteBranch.java b/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
similarity index 95%
rename from java/com/google/gerrit/server/project/DeleteBranch.java
rename to java/com/google/gerrit/server/restapi/project/DeleteBranch.java
index 7c7d6af..09bbca9 100644
--- a/java/com/google/gerrit/server/project/DeleteBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static org.eclipse.jgit.lib.Constants.R_HEADS;
 
@@ -25,6 +25,7 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.BranchResource;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/project/DeleteBranches.java b/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
similarity index 94%
rename from java/com/google/gerrit/server/project/DeleteBranches.java
rename to java/com/google/gerrit/server/restapi/project/DeleteBranches.java
index fa7e917..d8166e1 100644
--- a/java/com/google/gerrit/server/project/DeleteBranches.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static org.eclipse.jgit.lib.Constants.R_HEADS;
 
@@ -22,6 +22,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/DeleteDashboard.java b/java/com/google/gerrit/server/restapi/project/DeleteDashboard.java
similarity index 94%
rename from java/com/google/gerrit/server/project/DeleteDashboard.java
rename to java/com/google/gerrit/server/restapi/project/DeleteDashboard.java
index 958de55..0aa5752 100644
--- a/java/com/google/gerrit/server/project/DeleteDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteDashboard.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.api.projects.DashboardInfo;
 import com.google.gerrit.extensions.common.SetDashboardInput;
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.DashboardResource;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/DeleteRef.java b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
similarity index 98%
rename from java/com/google/gerrit/server/project/DeleteRef.java
rename to java/com/google/gerrit/server/restapi/project/DeleteRef.java
index 3b4638f..b1b575b 100644
--- a/java/com/google/gerrit/server/project/DeleteRef.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static java.lang.String.format;
 import static java.util.stream.Collectors.toList;
@@ -29,6 +29,8 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.RefValidationHelper;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/project/DeleteTag.java b/java/com/google/gerrit/server/restapi/project/DeleteTag.java
similarity index 93%
rename from java/com/google/gerrit/server/project/DeleteTag.java
rename to java/com/google/gerrit/server/restapi/project/DeleteTag.java
index 234f1d5..cce7103 100644
--- a/java/com/google/gerrit/server/project/DeleteTag.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteTag.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.Response;
@@ -22,6 +22,8 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.RefUtil;
+import com.google.gerrit.server.project.TagResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/project/DeleteTags.java b/java/com/google/gerrit/server/restapi/project/DeleteTags.java
similarity index 94%
rename from java/com/google/gerrit/server/project/DeleteTags.java
rename to java/com/google/gerrit/server/restapi/project/DeleteTags.java
index c020351..83fa1ea 100644
--- a/java/com/google/gerrit/server/project/DeleteTags.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteTags.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static org.eclipse.jgit.lib.Constants.R_TAGS;
 
@@ -22,6 +22,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/FilesCollection.java b/java/com/google/gerrit/server/restapi/project/FilesCollection.java
similarity index 92%
rename from java/com/google/gerrit/server/project/FilesCollection.java
rename to java/com/google/gerrit/server/restapi/project/FilesCollection.java
index dd32f85..888ecf2 100644
--- a/java/com/google/gerrit/server/project/FilesCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/FilesCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.ChildCollection;
@@ -20,6 +20,8 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.BranchResource;
+import com.google.gerrit.server.project.FileResource;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/project/FilesInCommitCollection.java b/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java
similarity index 92%
rename from java/com/google/gerrit/server/project/FilesInCommitCollection.java
rename to java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java
index 7144099..53411b8 100644
--- a/java/com/google/gerrit/server/project/FilesInCommitCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.ChildCollection;
@@ -21,6 +21,8 @@
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.reviewdb.client.Patch;
 import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.CommitResource;
+import com.google.gerrit.server.project.FileResource;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/project/GarbageCollect.java b/java/com/google/gerrit/server/restapi/project/GarbageCollect.java
similarity index 96%
rename from java/com/google/gerrit/server/project/GarbageCollect.java
rename to java/com/google/gerrit/server/restapi/project/GarbageCollect.java
index f81a0f3..ea1620b 100644
--- a/java/com/google/gerrit/server/project/GarbageCollect.java
+++ b/java/com/google/gerrit/server/restapi/project/GarbageCollect.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
@@ -29,7 +29,8 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.LocalDiskRepositoryManager;
 import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.project.GarbageCollect.Input;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.restapi.project.GarbageCollect.Input;
 import com.google.gerrit.server.util.IdGenerator;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/project/GetAccess.java b/java/com/google/gerrit/server/restapi/project/GetAccess.java
similarity index 97%
rename from java/com/google/gerrit/server/project/GetAccess.java
rename to java/com/google/gerrit/server/restapi/project/GetAccess.java
index d85a03b..1568a4c 100644
--- a/java/com/google/gerrit/server/project/GetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/GetAccess.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static com.google.gerrit.server.permissions.GlobalPermission.ADMINISTRATE_SERVER;
 import static com.google.gerrit.server.permissions.ProjectPermission.CREATE_REF;
@@ -46,12 +46,16 @@
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
-import com.google.gerrit.server.group.GroupJson;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectJson;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.restapi.group.GroupJson;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/project/GetBranch.java b/java/com/google/gerrit/server/restapi/project/GetBranch.java
similarity index 92%
rename from java/com/google/gerrit/server/project/GetBranch.java
rename to java/com/google/gerrit/server/restapi/project/GetBranch.java
index d312bde..7d32f3d 100644
--- a/java/com/google/gerrit/server/project/GetBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/GetBranch.java
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.api.projects.BranchInfo;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.BranchResource;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/GetChildProject.java b/java/com/google/gerrit/server/restapi/project/GetChildProject.java
similarity index 90%
rename from java/com/google/gerrit/server/project/GetChildProject.java
rename to java/com/google/gerrit/server/restapi/project/GetChildProject.java
index afffdfc..e69907e 100644
--- a/java/com/google/gerrit/server/project/GetChildProject.java
+++ b/java/com/google/gerrit/server/restapi/project/GetChildProject.java
@@ -12,11 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.common.ProjectInfo;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.project.ChildProjectResource;
+import com.google.gerrit.server.project.ProjectJson;
 import com.google.inject.Inject;
 import org.kohsuke.args4j.Option;
 
diff --git a/java/com/google/gerrit/server/project/GetCommit.java b/java/com/google/gerrit/server/restapi/project/GetCommit.java
similarity index 90%
rename from java/com/google/gerrit/server/project/GetCommit.java
rename to java/com/google/gerrit/server/restapi/project/GetCommit.java
index d8fc5b6..1c1ae90 100644
--- a/java/com/google/gerrit/server/project/GetCommit.java
+++ b/java/com/google/gerrit/server/restapi/project/GetCommit.java
@@ -12,11 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.common.CommitInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.git.CommitUtil;
+import com.google.gerrit.server.project.CommitResource;
 import com.google.inject.Singleton;
 import java.io.IOException;
 
diff --git a/java/com/google/gerrit/server/project/GetConfig.java b/java/com/google/gerrit/server/restapi/project/GetConfig.java
similarity index 95%
rename from java/com/google/gerrit/server/project/GetConfig.java
rename to java/com/google/gerrit/server/restapi/project/GetConfig.java
index c2f816e..aafff9e 100644
--- a/java/com/google/gerrit/server/project/GetConfig.java
+++ b/java/com/google/gerrit/server/restapi/project/GetConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.api.projects.ConfigInfo;
 import com.google.gerrit.extensions.registration.DynamicMap;
@@ -24,6 +24,7 @@
 import com.google.gerrit.server.config.ProjectConfigEntry;
 import com.google.gerrit.server.extensions.webui.UiActions;
 import com.google.gerrit.server.git.TransferConfig;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
diff --git a/java/com/google/gerrit/server/project/GetContent.java b/java/com/google/gerrit/server/restapi/project/GetContent.java
similarity index 93%
rename from java/com/google/gerrit/server/project/GetContent.java
rename to java/com/google/gerrit/server/restapi/project/GetContent.java
index b5294c4..132b644 100644
--- a/java/com/google/gerrit/server/project/GetContent.java
+++ b/java/com/google/gerrit/server/restapi/project/GetContent.java
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.FileContentUtil;
+import com.google.gerrit.server.project.FileResource;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/project/GetDashboard.java b/java/com/google/gerrit/server/restapi/project/GetDashboard.java
similarity index 92%
rename from java/com/google/gerrit/server/project/GetDashboard.java
rename to java/com/google/gerrit/server/restapi/project/GetDashboard.java
index d4d9a54..dde77e5 100644
--- a/java/com/google/gerrit/server/project/GetDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/GetDashboard.java
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static com.google.gerrit.reviewdb.client.RefNames.REFS_DASHBOARDS;
-import static com.google.gerrit.server.project.DashboardsCollection.isDefaultDashboard;
+import static com.google.gerrit.server.restapi.project.DashboardsCollection.isDefaultDashboard;
 
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
@@ -30,6 +30,9 @@
 import com.google.gerrit.extensions.restapi.Url;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.DashboardResource;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.List;
diff --git a/java/com/google/gerrit/server/project/GetDescription.java b/java/com/google/gerrit/server/restapi/project/GetDescription.java
similarity index 89%
rename from java/com/google/gerrit/server/project/GetDescription.java
rename to java/com/google/gerrit/server/restapi/project/GetDescription.java
index dd03e97..d387ff1 100644
--- a/java/com/google/gerrit/server/project/GetDescription.java
+++ b/java/com/google/gerrit/server/restapi/project/GetDescription.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/project/GetHead.java b/java/com/google/gerrit/server/restapi/project/GetHead.java
similarity index 96%
rename from java/com/google/gerrit/server/project/GetHead.java
rename to java/com/google/gerrit/server/restapi/project/GetHead.java
index daaf4ef..a6533ff 100644
--- a/java/com/google/gerrit/server/project/GetHead.java
+++ b/java/com/google/gerrit/server/restapi/project/GetHead.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -22,6 +22,7 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/project/GetParent.java b/java/com/google/gerrit/server/restapi/project/GetParent.java
similarity index 92%
rename from java/com/google/gerrit/server/project/GetParent.java
rename to java/com/google/gerrit/server/restapi/project/GetParent.java
index df87575..a4942e3 100644
--- a/java/com/google/gerrit/server/project/GetParent.java
+++ b/java/com/google/gerrit/server/restapi/project/GetParent.java
@@ -12,11 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
diff --git a/java/com/google/gerrit/server/project/GetProject.java b/java/com/google/gerrit/server/restapi/project/GetProject.java
similarity index 87%
rename from java/com/google/gerrit/server/project/GetProject.java
rename to java/com/google/gerrit/server/restapi/project/GetProject.java
index 8288610..a1b2fb1 100644
--- a/java/com/google/gerrit/server/project/GetProject.java
+++ b/java/com/google/gerrit/server/restapi/project/GetProject.java
@@ -12,10 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.common.ProjectInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.project.ProjectJson;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
diff --git a/java/com/google/gerrit/server/project/GetReflog.java b/java/com/google/gerrit/server/restapi/project/GetReflog.java
similarity index 97%
rename from java/com/google/gerrit/server/project/GetReflog.java
rename to java/com/google/gerrit/server/restapi/project/GetReflog.java
index 9643e09..0339e15 100644
--- a/java/com/google/gerrit/server/project/GetReflog.java
+++ b/java/com/google/gerrit/server/restapi/project/GetReflog.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.api.projects.ReflogEntryInfo;
@@ -26,6 +26,7 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.project.BranchResource;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.sql.Timestamp;
diff --git a/java/com/google/gerrit/server/project/GetStatistics.java b/java/com/google/gerrit/server/restapi/project/GetStatistics.java
similarity index 95%
rename from java/com/google/gerrit/server/project/GetStatistics.java
rename to java/com/google/gerrit/server/restapi/project/GetStatistics.java
index 36d558c..048c018 100644
--- a/java/com/google/gerrit/server/project/GetStatistics.java
+++ b/java/com/google/gerrit/server/restapi/project/GetStatistics.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/project/GetTag.java b/java/com/google/gerrit/server/restapi/project/GetTag.java
similarity index 89%
rename from java/com/google/gerrit/server/project/GetTag.java
rename to java/com/google/gerrit/server/restapi/project/GetTag.java
index a94d17e..6d5a510 100644
--- a/java/com/google/gerrit/server/project/GetTag.java
+++ b/java/com/google/gerrit/server/restapi/project/GetTag.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.api.projects.TagInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.project.TagResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/project/Index.java b/java/com/google/gerrit/server/restapi/project/Index.java
similarity index 96%
rename from java/com/google/gerrit/server/project/Index.java
rename to java/com/google/gerrit/server/restapi/project/Index.java
index 8c8314b..24f32f6 100644
--- a/java/com/google/gerrit/server/project/Index.java
+++ b/java/com/google/gerrit/server/restapi/project/Index.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
 
@@ -29,6 +29,7 @@
 import com.google.gerrit.server.index.IndexExecutor;
 import com.google.gerrit.server.index.change.AllChangesIndexer;
 import com.google.gerrit.server.index.change.ChangeIndexer;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/ListBranches.java b/java/com/google/gerrit/server/restapi/project/ListBranches.java
similarity index 96%
rename from java/com/google/gerrit/server/project/ListBranches.java
rename to java/com/google/gerrit/server/restapi/project/ListBranches.java
index 645058f..5675be1 100644
--- a/java/com/google/gerrit/server/project/ListBranches.java
+++ b/java/com/google/gerrit/server/restapi/project/ListBranches.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.collect.ComparisonChain;
 import com.google.common.collect.ImmutableList;
@@ -35,6 +35,10 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.BranchResource;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.project.RefFilter;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/project/ListChildProjects.java b/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
similarity index 90%
rename from java/com/google/gerrit/server/project/ListChildProjects.java
rename to java/com/google/gerrit/server/restapi/project/ListChildProjects.java
index 9de0c87..c514f90 100644
--- a/java/com/google/gerrit/server/project/ListChildProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static java.util.stream.Collectors.toList;
 
@@ -24,6 +24,11 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.project.ChildProjects;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectJson;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.util.HashMap;
diff --git a/java/com/google/gerrit/server/project/ListDashboards.java b/java/com/google/gerrit/server/restapi/project/ListDashboards.java
similarity index 96%
rename from java/com/google/gerrit/server/project/ListDashboards.java
rename to java/com/google/gerrit/server/restapi/project/ListDashboards.java
index 6960b47..829d409 100644
--- a/java/com/google/gerrit/server/project/ListDashboards.java
+++ b/java/com/google/gerrit/server/restapi/project/ListDashboards.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static com.google.gerrit.reviewdb.client.RefNames.REFS_DASHBOARDS;
 
@@ -26,6 +26,8 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/project/ListProjects.java b/java/com/google/gerrit/server/restapi/project/ListProjects.java
similarity index 98%
rename from java/com/google/gerrit/server/project/ListProjects.java
rename to java/com/google/gerrit/server/restapi/project/ListProjects.java
index 19a5f55..d2aecca 100644
--- a/java/com/google/gerrit/server/project/ListProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/ListProjects.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static com.google.gerrit.extensions.client.ProjectState.HIDDEN;
 import static java.nio.charset.StandardCharsets.UTF_8;
@@ -41,12 +41,15 @@
 import com.google.gerrit.server.WebLinks;
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.group.GroupsCollection;
 import com.google.gerrit.server.ioutil.StringUtil;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectNode;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.restapi.group.GroupsCollection;
 import com.google.gerrit.server.util.RegexListSearcher;
 import com.google.gerrit.server.util.TreeFormatter;
 import com.google.gson.reflect.TypeToken;
diff --git a/java/com/google/gerrit/server/project/ListTags.java b/java/com/google/gerrit/server/restapi/project/ListTags.java
similarity index 97%
rename from java/com/google/gerrit/server/project/ListTags.java
rename to java/com/google/gerrit/server/restapi/project/ListTags.java
index 02544ac..a2b7082 100644
--- a/java/com/google/gerrit/server/project/ListTags.java
+++ b/java/com/google/gerrit/server/restapi/project/ListTags.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.gerrit.extensions.api.projects.ProjectApi.ListRefsRequest;
@@ -30,6 +30,9 @@
 import com.google.gerrit.server.git.VisibleRefFilter;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.project.RefFilter;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/project/Module.java b/java/com/google/gerrit/server/restapi/project/Module.java
similarity index 96%
rename from java/com/google/gerrit/server/project/Module.java
rename to java/com/google/gerrit/server/restapi/project/Module.java
index d0753eb..b74b640 100644
--- a/java/com/google/gerrit/server/project/Module.java
+++ b/java/com/google/gerrit/server/restapi/project/Module.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static com.google.gerrit.server.project.BranchResource.BRANCH_KIND;
 import static com.google.gerrit.server.project.ChildProjectResource.CHILD_PROJECT_KIND;
@@ -24,7 +24,7 @@
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.RestApiModule;
-import com.google.gerrit.server.change.CherryPickCommit;
+import com.google.gerrit.server.project.RefValidationHelper;
 
 public class Module extends RestApiModule {
   @Override
@@ -98,7 +98,6 @@
 
     get(PROJECT_KIND, "config").to(GetConfig.class);
     put(PROJECT_KIND, "config").to(PutConfig.class);
-    post(COMMIT_KIND, "cherrypick").to(CherryPickCommit.class);
 
     factory(DeleteRef.Factory.class);
   }
diff --git a/java/com/google/gerrit/server/project/ProjectsCollection.java b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
similarity index 96%
rename from java/com/google/gerrit/server/project/ProjectsCollection.java
rename to java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
index 8d7b156..186ff99 100644
--- a/java/com/google/gerrit/server/project/ProjectsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.Nullable;
@@ -33,6 +33,9 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/PutBranch.java b/java/com/google/gerrit/server/restapi/project/PutBranch.java
similarity index 91%
rename from java/com/google/gerrit/server/project/PutBranch.java
rename to java/com/google/gerrit/server/restapi/project/PutBranch.java
index 8e8efaf..fec8abf 100644
--- a/java/com/google/gerrit/server/project/PutBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/PutBranch.java
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.api.projects.BranchInfo;
 import com.google.gerrit.extensions.api.projects.BranchInput;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.project.BranchResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/project/PutConfig.java b/java/com/google/gerrit/server/restapi/project/PutConfig.java
similarity index 97%
rename from java/com/google/gerrit/server/project/PutConfig.java
rename to java/com/google/gerrit/server/restapi/project/PutConfig.java
index 77e60d0..69c4c05 100644
--- a/java/com/google/gerrit/server/project/PutConfig.java
+++ b/java/com/google/gerrit/server/restapi/project/PutConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.base.Joiner;
 import com.google.common.base.Strings;
@@ -43,6 +43,10 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.project.BooleanProjectConfigTransformations;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/PutDescription.java b/java/com/google/gerrit/server/restapi/project/PutDescription.java
similarity index 95%
rename from java/com/google/gerrit/server/project/PutDescription.java
rename to java/com/google/gerrit/server/restapi/project/PutDescription.java
index a2808fc..9be9ee0 100644
--- a/java/com/google/gerrit/server/project/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/project/PutDescription.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
@@ -29,6 +29,8 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/project/PutProject.java b/java/com/google/gerrit/server/restapi/project/PutProject.java
similarity index 91%
rename from java/com/google/gerrit/server/project/PutProject.java
rename to java/com/google/gerrit/server/restapi/project/PutProject.java
index 1d2384f..5b11143 100644
--- a/java/com/google/gerrit/server/project/PutProject.java
+++ b/java/com/google/gerrit/server/restapi/project/PutProject.java
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.api.projects.ProjectInput;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Singleton;
 
 @Singleton
diff --git a/java/com/google/gerrit/server/project/PutTag.java b/java/com/google/gerrit/server/restapi/project/PutTag.java
similarity index 91%
rename from java/com/google/gerrit/server/project/PutTag.java
rename to java/com/google/gerrit/server/restapi/project/PutTag.java
index b8a8f6d..06c5157 100644
--- a/java/com/google/gerrit/server/project/PutTag.java
+++ b/java/com/google/gerrit/server/restapi/project/PutTag.java
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.api.projects.TagInfo;
 import com.google.gerrit.extensions.api.projects.TagInput;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.project.TagResource;
 
 public class PutTag implements RestModifyView<TagResource, TagInput> {
 
diff --git a/java/com/google/gerrit/server/project/QueryProjects.java b/java/com/google/gerrit/server/restapi/project/QueryProjects.java
similarity index 95%
rename from java/com/google/gerrit/server/project/QueryProjects.java
rename to java/com/google/gerrit/server/restapi/project/QueryProjects.java
index 998bdb2..9a1c36a 100644
--- a/java/com/google/gerrit/server/project/QueryProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/QueryProjects.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
@@ -25,6 +25,8 @@
 import com.google.gerrit.index.query.QueryResult;
 import com.google.gerrit.server.index.project.ProjectIndex;
 import com.google.gerrit.server.index.project.ProjectIndexCollection;
+import com.google.gerrit.server.project.ProjectData;
+import com.google.gerrit.server.project.ProjectJson;
 import com.google.gerrit.server.query.project.ProjectQueryBuilder;
 import com.google.gerrit.server.query.project.ProjectQueryProcessor;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/server/project/RepositoryStatistics.java b/java/com/google/gerrit/server/restapi/project/RepositoryStatistics.java
similarity index 89%
rename from java/com/google/gerrit/server/project/RepositoryStatistics.java
rename to java/com/google/gerrit/server/restapi/project/RepositoryStatistics.java
index 3cb4bac..2a2fc866 100644
--- a/java/com/google/gerrit/server/project/RepositoryStatistics.java
+++ b/java/com/google/gerrit/server/restapi/project/RepositoryStatistics.java
@@ -12,14 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.base.CaseFormat;
 import java.util.Map.Entry;
 import java.util.Properties;
 import java.util.TreeMap;
 
-class RepositoryStatistics extends TreeMap<String, Object> {
+public class RepositoryStatistics extends TreeMap<String, Object> {
   private static final long serialVersionUID = 1L;
 
   RepositoryStatistics(Properties p) {
diff --git a/java/com/google/gerrit/server/project/SetAccess.java b/java/com/google/gerrit/server/restapi/project/SetAccess.java
similarity index 97%
rename from java/com/google/gerrit/server/project/SetAccess.java
rename to java/com/google/gerrit/server/restapi/project/SetAccess.java
index a1b2b0f..5a34522 100644
--- a/java/com/google/gerrit/server/project/SetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/SetAccess.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
@@ -36,6 +36,8 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/project/SetAccessUtil.java b/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
similarity index 97%
rename from java/com/google/gerrit/server/project/SetAccessUtil.java
rename to java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
index fefefaa..3fb9bb9 100644
--- a/java/com/google/gerrit/server/project/SetAccessUtil.java
+++ b/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.collect.Iterables;
 import com.google.gerrit.common.data.AccessSection;
@@ -33,8 +33,9 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.git.ProjectConfig;
-import com.google.gerrit.server.group.GroupsCollection;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.RefPattern;
+import com.google.gerrit.server.restapi.group.GroupsCollection;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/SetDashboard.java b/java/com/google/gerrit/server/restapi/project/SetDashboard.java
similarity index 94%
rename from java/com/google/gerrit/server/project/SetDashboard.java
rename to java/com/google/gerrit/server/restapi/project/SetDashboard.java
index 21ec077..891978b 100644
--- a/java/com/google/gerrit/server/project/SetDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/SetDashboard.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.api.projects.DashboardInfo;
 import com.google.gerrit.extensions.common.SetDashboardInput;
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.DashboardResource;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/SetDefaultDashboard.java b/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
similarity index 96%
rename from java/com/google/gerrit/server/project/SetDefaultDashboard.java
rename to java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
index 0dd5f85..4fd46c5 100644
--- a/java/com/google/gerrit/server/project/SetDefaultDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
@@ -31,6 +31,9 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.project.DashboardResource;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/project/SetHead.java b/java/com/google/gerrit/server/restapi/project/SetHead.java
similarity index 97%
rename from java/com/google/gerrit/server/project/SetHead.java
rename to java/com/google/gerrit/server/restapi/project/SetHead.java
index a8d93c1..aa1bf63 100644
--- a/java/com/google/gerrit/server/project/SetHead.java
+++ b/java/com/google/gerrit/server/restapi/project/SetHead.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.api.projects.HeadInput;
@@ -31,6 +31,7 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/project/SetParent.java b/java/com/google/gerrit/server/restapi/project/SetParent.java
similarity index 96%
rename from java/com/google/gerrit/server/project/SetParent.java
rename to java/com/google/gerrit/server/restapi/project/SetParent.java
index 60efa6f..21fef97 100644
--- a/java/com/google/gerrit/server/project/SetParent.java
+++ b/java/com/google/gerrit/server/restapi/project/SetParent.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -35,6 +35,9 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
diff --git a/java/com/google/gerrit/server/project/TagsCollection.java b/java/com/google/gerrit/server/restapi/project/TagsCollection.java
similarity index 93%
rename from java/com/google/gerrit/server/project/TagsCollection.java
rename to java/com/google/gerrit/server/restapi/project/TagsCollection.java
index 7ee0a8e..a1b0395 100644
--- a/java/com/google/gerrit/server/project/TagsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/TagsCollection.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.server.restapi.project;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AcceptsCreate;
@@ -20,6 +20,8 @@
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.TagResource;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/schema/AllUsersCreator.java b/java/com/google/gerrit/server/schema/AllUsersCreator.java
index 8a11ca8..8e38754 100644
--- a/java/com/google/gerrit/server/schema/AllUsersCreator.java
+++ b/java/com/google/gerrit/server/schema/AllUsersCreator.java
@@ -37,7 +37,9 @@
 import java.io.IOException;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
 
 /** Creates the {@code All-Users} repository. */
@@ -76,6 +78,8 @@
     } catch (RepositoryNotFoundException notFound) {
       try (Repository git = mgr.createRepository(allUsersName)) {
         initAllUsers(git);
+        RefUpdate u = git.updateRef(Constants.HEAD);
+        u.link(RefNames.REFS_CONFIG);
       } catch (RepositoryNotFoundException err) {
         String name = allUsersName.get();
         throw new IOException("Cannot create repository " + name, err);
diff --git a/java/com/google/gerrit/server/schema/SchemaVersion.java b/java/com/google/gerrit/server/schema/SchemaVersion.java
index 61a6235..4b8c13f 100644
--- a/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -35,7 +35,7 @@
 /** A version of the database schema. */
 public abstract class SchemaVersion {
   /** The current schema version. */
-  public static final Class<Schema_165> C = Schema_165.class;
+  public static final Class<Schema_166> C = Schema_166.class;
 
   public static int getBinaryVersion() {
     return guessVersion(C);
diff --git a/java/com/google/gerrit/server/schema/Schema_166.java b/java/com/google/gerrit/server/schema/Schema_166.java
new file mode 100644
index 0000000..aa6f4e6
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/Schema_166.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.schema;
+
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import java.io.IOException;
+import java.sql.SQLException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+
+/** Set HEAD for All-Users to refs/meta/config. */
+public class Schema_166 extends SchemaVersion {
+  private final GitRepositoryManager repoManager;
+  private final AllUsersName allUsersName;
+
+  @Inject
+  Schema_166(
+      Provider<Schema_165> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
+    super(prior);
+    this.repoManager = repoManager;
+    this.allUsersName = allUsersName;
+  }
+
+  @Override
+  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
+    try (Repository git = repoManager.openRepository(allUsersName)) {
+      RefUpdate u = git.updateRef(Constants.HEAD);
+      u.link(RefNames.REFS_CONFIG);
+    } catch (IOException e) {
+      throw new OrmException(String.format("Failed to update HEAD for %s", allUsersName.get()), e);
+    }
+  }
+}
diff --git a/java/com/google/gerrit/server/update/RetryHelper.java b/java/com/google/gerrit/server/update/RetryHelper.java
index 54726fe..0f5b00f 100644
--- a/java/com/google/gerrit/server/update/RetryHelper.java
+++ b/java/com/google/gerrit/server/update/RetryHelper.java
@@ -32,9 +32,10 @@
 import com.google.common.base.Throwables;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.metrics.Counter0;
+import com.google.gerrit.metrics.Counter1;
 import com.google.gerrit.metrics.Description;
-import com.google.gerrit.metrics.Histogram0;
+import com.google.gerrit.metrics.Field;
+import com.google.gerrit.metrics.Histogram1;
 import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.LockFailureException;
@@ -61,6 +62,12 @@
     T call() throws Exception;
   }
 
+  public enum ActionType {
+    ACCOUNT_UPDATE,
+    CHANGE_QUERY,
+    CHANGE_UPDATE
+  }
+
   /**
    * Options for retrying a single operation.
    *
@@ -96,25 +103,29 @@
   @VisibleForTesting
   @Singleton
   public static class Metrics {
-    final Histogram0 attemptCounts;
-    final Counter0 timeoutCount;
+    final Histogram1<ActionType> attemptCounts;
+    final Counter1<ActionType> timeoutCount;
 
     @Inject
     Metrics(MetricMaker metricMaker) {
+      Field<ActionType> view = Field.ofEnum(ActionType.class, "action_type");
       attemptCounts =
           metricMaker.newHistogram(
-              "batch_update/retry_attempt_counts",
+              "action/retry_attempt_counts",
               new Description(
-                      "Distribution of number of attempts made by RetryHelper"
+                      "Distribution of number of attempts made by RetryHelper to execute an action"
                           + " (1 == single attempt, no retry)")
                   .setCumulative()
-                  .setUnit("attempts"));
+                  .setUnit("attempts"),
+              view);
       timeoutCount =
           metricMaker.newCounter(
-              "batch_update/retry_timeout_count",
-              new Description("Number of executions of RetryHelper that ultimately timed out")
+              "action/retry_timeout_count",
+              new Description(
+                      "Number of action executions of RetryHelper that ultimately timed out")
                   .setCumulative()
-                  .setUnit("timeouts"));
+                  .setUnit("timeouts"),
+              view);
     }
   }
 
@@ -171,9 +182,16 @@
     return defaultTimeout;
   }
 
-  public <T> T execute(Action<T> action) throws IOException, ConfigInvalidException, OrmException {
+  public <T> T execute(ActionType actionType, Action<T> action)
+      throws IOException, ConfigInvalidException, OrmException {
+    return execute(actionType, action, t -> t instanceof LockFailureException);
+  }
+
+  public <T> T execute(
+      ActionType actionType, Action<T> action, Predicate<Throwable> exceptionPredicate)
+      throws IOException, ConfigInvalidException, OrmException {
     try {
-      return execute(action, defaults(), t -> t instanceof LockFailureException);
+      return execute(actionType, action, defaults(), exceptionPredicate);
     } catch (Throwable t) {
       Throwables.throwIfUnchecked(t);
       Throwables.throwIfInstanceOf(t, IOException.class);
@@ -195,10 +213,13 @@
         // transactions. Either way, retrying a partially-failed operation is not idempotent, so
         // don't do it automatically. Let the end user decide whether they want to retry.
         return execute(
-            () -> changeAction.call(updateFactory), RetryerBuilder.<T>newBuilder().build());
+            ActionType.CHANGE_UPDATE,
+            () -> changeAction.call(updateFactory),
+            RetryerBuilder.<T>newBuilder().build());
       }
 
       return execute(
+          ActionType.CHANGE_UPDATE,
           () -> changeAction.call(updateFactory),
           opts,
           t -> {
@@ -218,6 +239,7 @@
   /**
    * Executes an action with a given retryer.
    *
+   * @param actionType the type of the action
    * @param action the action which should be executed and retried on failure
    * @param opts options for retrying the action on failure
    * @param exceptionPredicate predicate to control on which exception the action should be retried
@@ -225,33 +247,39 @@
    * @throws Throwable any error or exception that made the action fail, callers are expected to
    *     catch and inspect this Throwable to decide carefully whether it should be re-thrown
    */
-  private <T> T execute(Action<T> action, Options opts, Predicate<Throwable> exceptionPredicate)
+  private <T> T execute(
+      ActionType actionType,
+      Action<T> action,
+      Options opts,
+      Predicate<Throwable> exceptionPredicate)
       throws Throwable {
     MetricListener listener = new MetricListener();
     try {
       RetryerBuilder<T> retryerBuilder = createRetryerBuilder(opts, exceptionPredicate);
       retryerBuilder.withRetryListener(listener);
-      return execute(action, retryerBuilder.build());
+      return execute(actionType, action, retryerBuilder.build());
     } finally {
-      metrics.attemptCounts.record(listener.getAttemptCount());
+      metrics.attemptCounts.record(actionType, listener.getAttemptCount());
     }
   }
 
   /**
    * Executes an action with a given retryer.
    *
+   * @param actionType the type of the action
    * @param action the action which should be executed and retried on failure
    * @param retryer the retryer
    * @return the result of executing the action
    * @throws Throwable any error or exception that made the action fail, callers are expected to
    *     catch and inspect this Throwable to decide carefully whether it should be re-thrown
    */
-  private <T> T execute(Action<T> action, Retryer<T> retryer) throws Throwable {
+  private <T> T execute(ActionType actionType, Action<T> action, Retryer<T> retryer)
+      throws Throwable {
     try {
       return retryer.call(() -> action.call());
     } catch (ExecutionException | RetryException e) {
       if (e instanceof RetryException) {
-        metrics.timeoutCount.increment();
+        metrics.timeoutCount.increment(actionType);
       }
       if (e.getCause() != null) {
         throw e.getCause();
diff --git a/java/com/google/gerrit/sshd/ChangeArgumentParser.java b/java/com/google/gerrit/sshd/ChangeArgumentParser.java
index d5fc4547..f442032 100644
--- a/java/com/google/gerrit/sshd/ChangeArgumentParser.java
+++ b/java/com/google/gerrit/sshd/ChangeArgumentParser.java
@@ -22,13 +22,13 @@
 import com.google.gerrit.server.ChangeFinder;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.ChangesCollection;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.permissions.ChangePermission;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.restapi.change.ChangesCollection;
 import com.google.gerrit.sshd.BaseCommand.UnloggedFailure;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/sshd/commands/AdminSetParent.java b/java/com/google/gerrit/sshd/commands/AdminSetParent.java
index c6e00aa..d9a0a37 100644
--- a/java/com/google/gerrit/sshd/commands/AdminSetParent.java
+++ b/java/com/google/gerrit/sshd/commands/AdminSetParent.java
@@ -24,10 +24,10 @@
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ListChildProjects;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.restapi.project.ListChildProjects;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/sshd/commands/BanCommitCommand.java b/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
index d514e2c..cceb16b 100644
--- a/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
+++ b/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
@@ -19,10 +19,10 @@
 import com.google.common.base.Joiner;
 import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.api.projects.BanCommitInput;
-import com.google.gerrit.server.project.BanCommit;
-import com.google.gerrit.server.project.BanCommit.BanResultInfo;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.restapi.project.BanCommit;
+import com.google.gerrit.server.restapi.project.BanCommit.BanResultInfo;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java b/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
index 56ff5ea..68490f7 100644
--- a/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
+++ b/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
@@ -20,9 +20,9 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.server.OutputFormat;
-import com.google.gerrit.server.change.ChangesCollection;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.change.Revisions;
+import com.google.gerrit.server.restapi.change.ChangesCollection;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.inject.Inject;
 import java.nio.ByteBuffer;
diff --git a/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java b/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
index 0c63fb3..32bff8c 100644
--- a/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
@@ -23,7 +23,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.server.account.CreateAccount;
+import com.google.gerrit.server.restapi.account.CreateAccount;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
index 83a62fd..1e1e254 100644
--- a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
@@ -25,11 +25,11 @@
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.server.group.AddMembers;
-import com.google.gerrit.server.group.AddSubgroups;
-import com.google.gerrit.server.group.CreateGroup;
 import com.google.gerrit.server.group.GroupResource;
-import com.google.gerrit.server.group.GroupsCollection;
+import com.google.gerrit.server.restapi.group.AddMembers;
+import com.google.gerrit.server.restapi.group.AddSubgroups;
+import com.google.gerrit.server.restapi.group.CreateGroup;
+import com.google.gerrit.server.restapi.group.GroupsCollection;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java b/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
index bb33dea..3ed856b 100644
--- a/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
@@ -17,8 +17,8 @@
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.Index;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.restapi.change.Index;
 import com.google.gerrit.sshd.ChangeArgumentParser;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
diff --git a/java/com/google/gerrit/sshd/commands/IndexProjectCommand.java b/java/com/google/gerrit/sshd/commands/IndexProjectCommand.java
index ba937a2..e6abc17 100644
--- a/java/com/google/gerrit/sshd/commands/IndexProjectCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexProjectCommand.java
@@ -17,9 +17,9 @@
 import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
 
 import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
-import com.google.gerrit.server.project.Index;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.restapi.project.Index;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java b/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
index ac3784a..473cb0c 100644
--- a/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
@@ -23,8 +23,8 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.group.InternalGroup;
-import com.google.gerrit.server.group.ListGroups;
 import com.google.gerrit.server.ioutil.ColumnFormatter;
+import com.google.gerrit.server.restapi.group.ListGroups;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.gerrit.util.cli.Options;
diff --git a/java/com/google/gerrit/sshd/commands/ListMembersCommand.java b/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
index 2585e5c..bf3dd44 100644
--- a/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
@@ -24,8 +24,8 @@
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.group.InternalGroup;
-import com.google.gerrit.server.group.ListMembers;
 import com.google.gerrit.server.ioutil.ColumnFormatter;
+import com.google.gerrit.server.restapi.group.ListMembers;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.gwtorm.server.OrmException;
@@ -35,7 +35,7 @@
 import java.util.Optional;
 import org.kohsuke.args4j.Argument;
 
-/** Implements a command that allows the user to see the members of a group. */
+/** Implements a command that allows the user to see the members of a account. */
 @CommandMetaData(
   name = "ls-members",
   description = "List the members of a given group",
diff --git a/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java b/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
index 7ef1e20..664f87b 100644
--- a/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
 
-import com.google.gerrit.server.project.ListProjects;
+import com.google.gerrit.server.restapi.project.ListProjects;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.gerrit.util.cli.Options;
diff --git a/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java b/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
index e8e104c..9e334e6 100644
--- a/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
+++ b/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
@@ -19,8 +19,8 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.server.group.GroupResource;
-import com.google.gerrit.server.group.GroupsCollection;
-import com.google.gerrit.server.group.PutName;
+import com.google.gerrit.server.restapi.group.GroupsCollection;
+import com.google.gerrit.server.restapi.group.PutName;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index 16cc49a..a7cc790 100644
--- a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -35,18 +35,18 @@
 import com.google.gerrit.reviewdb.client.AccountSshKey;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountResource;
-import com.google.gerrit.server.account.AddSshKey;
-import com.google.gerrit.server.account.CreateEmail;
-import com.google.gerrit.server.account.DeleteActive;
-import com.google.gerrit.server.account.DeleteEmail;
-import com.google.gerrit.server.account.DeleteSshKey;
-import com.google.gerrit.server.account.GetEmails;
-import com.google.gerrit.server.account.GetSshKeys;
-import com.google.gerrit.server.account.PutActive;
-import com.google.gerrit.server.account.PutHttpPassword;
-import com.google.gerrit.server.account.PutName;
-import com.google.gerrit.server.account.PutPreferred;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.restapi.account.AddSshKey;
+import com.google.gerrit.server.restapi.account.CreateEmail;
+import com.google.gerrit.server.restapi.account.DeleteActive;
+import com.google.gerrit.server.restapi.account.DeleteEmail;
+import com.google.gerrit.server.restapi.account.DeleteSshKey;
+import com.google.gerrit.server.restapi.account.GetEmails;
+import com.google.gerrit.server.restapi.account.GetSshKeys;
+import com.google.gerrit.server.restapi.account.PutActive;
+import com.google.gerrit.server.restapi.account.PutHttpPassword;
+import com.google.gerrit.server.restapi.account.PutName;
+import com.google.gerrit.server.restapi.account.PutPreferred;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.gwtorm.server.OrmException;
diff --git a/java/com/google/gerrit/sshd/commands/SetHeadCommand.java b/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
index ef7ab916..fd7ef75 100644
--- a/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
@@ -18,7 +18,7 @@
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.project.SetHead;
+import com.google.gerrit.server.restapi.project.SetHead;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/sshd/commands/SetMembersCommand.java b/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
index 9062b52..fdb4b1e9 100644
--- a/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
@@ -26,13 +26,13 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.group.AddMembers;
-import com.google.gerrit.server.group.AddSubgroups;
-import com.google.gerrit.server.group.DeleteMembers;
-import com.google.gerrit.server.group.DeleteSubgroups;
 import com.google.gerrit.server.group.GroupResource;
-import com.google.gerrit.server.group.GroupsCollection;
 import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.restapi.group.AddMembers;
+import com.google.gerrit.server.restapi.group.AddSubgroups;
+import com.google.gerrit.server.restapi.group.DeleteMembers;
+import com.google.gerrit.server.restapi.group.DeleteSubgroups;
+import com.google.gerrit.server.restapi.group.GroupsCollection;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/sshd/commands/SetProjectCommand.java b/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
index a963a35..a5759f0 100644
--- a/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
@@ -22,7 +22,7 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.project.PutConfig;
+import com.google.gerrit.server.restapi.project.PutConfig;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
index 85cf467..4c7d59d 100644
--- a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
@@ -20,11 +20,11 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.DeleteReviewer;
-import com.google.gerrit.server.change.PostReviewers;
 import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.restapi.change.DeleteReviewer;
+import com.google.gerrit.server.restapi.change.PostReviewers;
 import com.google.gerrit.sshd.ChangeArgumentParser;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
diff --git a/java/com/google/gerrit/sshd/commands/TestSubmitRuleCommand.java b/java/com/google/gerrit/sshd/commands/TestSubmitRuleCommand.java
index a7d529b..ce43c3d 100644
--- a/java/com/google/gerrit/sshd/commands/TestSubmitRuleCommand.java
+++ b/java/com/google/gerrit/sshd/commands/TestSubmitRuleCommand.java
@@ -17,7 +17,7 @@
 import com.google.gerrit.extensions.common.TestSubmitRuleInput;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.change.RevisionResource;
-import com.google.gerrit.server.change.TestSubmitRule;
+import com.google.gerrit.server.restapi.change.TestSubmitRule;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.inject.Inject;
 
diff --git a/java/com/google/gerrit/sshd/commands/TestSubmitTypeCommand.java b/java/com/google/gerrit/sshd/commands/TestSubmitTypeCommand.java
index ebe8925..90d54d5 100644
--- a/java/com/google/gerrit/sshd/commands/TestSubmitTypeCommand.java
+++ b/java/com/google/gerrit/sshd/commands/TestSubmitTypeCommand.java
@@ -18,7 +18,7 @@
 import com.google.gerrit.extensions.common.TestSubmitRuleInput;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.change.RevisionResource;
-import com.google.gerrit.server.change.TestSubmitType;
+import com.google.gerrit.server.restapi.change.TestSubmitType;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.inject.Inject;
 
diff --git a/java/com/google/gerrit/sshd/commands/UploadArchive.java b/java/com/google/gerrit/sshd/commands/UploadArchive.java
index 41cc485b..7518cbb 100644
--- a/java/com/google/gerrit/sshd/commands/UploadArchive.java
+++ b/java/com/google/gerrit/sshd/commands/UploadArchive.java
@@ -18,12 +18,12 @@
 
 import com.google.common.collect.ImmutableMap;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.server.change.AllowedFormats;
 import com.google.gerrit.server.change.ArchiveFormat;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
-import com.google.gerrit.server.project.CommitsCollection;
+import com.google.gerrit.server.restapi.change.AllowedFormats;
+import com.google.gerrit.server.restapi.project.CommitsCollection;
 import com.google.gerrit.sshd.AbstractGitCommand;
 import com.google.inject.Inject;
 import java.io.IOException;
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 7aa7fd0..fa683cf 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -130,12 +130,12 @@
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.PostReview;
 import com.google.gerrit.server.git.ChangeMessageModifier;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.group.SystemGroupBackend;
 import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
 import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.restapi.change.PostReview;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.ChangeContext;
diff --git a/javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java b/javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java
index 6f4495e..37a86d2 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java
@@ -30,7 +30,7 @@
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.project.DashboardsCollection;
+import com.google.gerrit.server.restapi.project.DashboardsCollection;
 import java.util.List;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.Repository;
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index 9c7f7fb..e3d0699 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -81,9 +81,9 @@
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.server.change.GetRevisionActions;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.restapi.change.GetRevisionActions;
 import com.google.inject.Inject;
 import java.io.ByteArrayOutputStream;
 import java.sql.Timestamp;
diff --git a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
index 9d32e3a..28c1641 100644
--- a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
+++ b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -56,11 +56,11 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeMessagesUtil;
-import com.google.gerrit.server.change.ChangeEdits.EditMessage;
-import com.google.gerrit.server.change.ChangeEdits.Post;
-import com.google.gerrit.server.change.ChangeEdits.Put;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.restapi.change.ChangeEdits.EditMessage;
+import com.google.gerrit.server.restapi.change.ChangeEdits.Post;
+import com.google.gerrit.server.restapi.change.ChangeEdits.Put;
 import com.google.gerrit.testing.TestTimeUtil;
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.stream.JsonReader;
diff --git a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index 0e39ee1..cc5b5d3 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -66,6 +66,7 @@
 import java.util.function.Predicate;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
@@ -650,7 +651,8 @@
             RefNames.refsGroups(nonInteractiveUsers),
             RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS,
             RefNames.REFS_SEQUENCES + Sequences.NAME_GROUPS,
-            RefNames.REFS_CONFIG);
+            RefNames.REFS_CONFIG,
+            Constants.HEAD);
 
     List<String> expectedMetaRefs =
         new ArrayList<>(ImmutableList.of(mr.getPatchSetId().toRefName()));
diff --git a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
index adfbdef..95b6a2a 100644
--- a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
@@ -26,7 +26,7 @@
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.change.Submit.TestSubmitInput;
+import com.google.gerrit.server.change.TestSubmitInput;
 import com.google.gerrit.testing.ConfigSuite;
 import java.util.ArrayDeque;
 import java.util.Map;
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
index dcd40b9..08322d8 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
@@ -20,7 +20,7 @@
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.account.GetDetail.AccountDetailInfo;
+import com.google.gerrit.server.restapi.account.GetDetail.AccountDetailInfo;
 import org.junit.Test;
 
 public class GetAccountDetailIT extends AbstractDaemonTest {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index 3a41fae..67307e2 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -66,12 +66,12 @@
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.change.RevisionResource;
-import com.google.gerrit.server.change.Submit;
-import com.google.gerrit.server.change.Submit.TestSubmitInput;
+import com.google.gerrit.server.change.TestSubmitInput;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.git.validators.OnSubmitValidationListener;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.restapi.change.Submit;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.ChangeContext;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
index b4d8557..29a81ca 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
@@ -27,7 +27,7 @@
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.change.Submit.TestSubmitInput;
+import com.google.gerrit.server.change.TestSubmitInput;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
index 5269329..d551595 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
@@ -35,7 +35,7 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.change.Submit.TestSubmitInput;
+import com.google.gerrit.server.change.TestSubmitInput;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.project.testing.Util;
 import org.eclipse.jgit.lib.ObjectId;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/BUILD b/javatests/com/google/gerrit/acceptance/rest/change/BUILD
index dd675cb..6a4b4a7 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/BUILD
+++ b/javatests/com/google/gerrit/acceptance/rest/change/BUILD
@@ -33,5 +33,6 @@
     srcs = SUBMIT_UTIL_SRCS,
     deps = [
         "//java/com/google/gerrit/acceptance:lib",
+        "//java/com/google/gerrit/server/restapi",
     ],
 )
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
index 3abd290..f1bba8a 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
@@ -47,8 +47,8 @@
 import com.google.gerrit.extensions.common.ReviewerUpdateInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.change.PostReviewers;
 import com.google.gerrit.server.mail.Address;
+import com.google.gerrit.server.restapi.change.PostReviewers;
 import com.google.gerrit.testing.FakeEmailSender.Message;
 import com.google.gson.stream.JsonReader;
 import java.util.ArrayList;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
index d895bc7..e508664 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
@@ -35,7 +35,7 @@
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.change.Submit.TestSubmitInput;
+import com.google.gerrit.server.change.TestSubmitInput;
 import com.google.gerrit.server.git.ChangeMessageModifier;
 import com.google.gerrit.server.git.strategy.CommitMergeStatus;
 import com.google.inject.Inject;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
index d4397d64..2ebf3ca 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
@@ -29,7 +29,7 @@
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.change.Submit.TestSubmitInput;
+import com.google.gerrit.server.change.TestSubmitInput;
 import java.util.Map;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
index 05abd4c..8e3618d 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
@@ -23,11 +23,11 @@
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.extensions.client.ChangeStatus;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.change.Submit;
 import com.google.gerrit.server.git.ChangeSet;
 import com.google.gerrit.server.git.MergeSuperSet;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.restapi.change.Submit;
 import com.google.gerrit.testing.ConfigSuite;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
index 9b7a8cb..21a1e76 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
@@ -32,8 +32,8 @@
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.group.CreateGroup;
 import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.restapi.group.CreateGroup;
 import com.google.inject.Inject;
 import java.util.Arrays;
 import java.util.List;
diff --git a/javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java b/javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
index 8ec145f..38462c0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.GerritConfig;
 import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.UseSsh;
 import com.google.gerrit.common.RawInputUtil;
 import com.google.gerrit.extensions.client.AccountFieldName;
 import com.google.gerrit.extensions.client.AuthType;
@@ -32,6 +33,7 @@
 import org.junit.Test;
 
 @NoHttpd
+@UseSsh
 public class ServerInfoIT extends AbstractDaemonTest {
   private static final byte[] JS_PLUGIN_CONTENT =
       "Gerrit.install(function(self){});\n".getBytes(UTF_8);
diff --git a/javatests/com/google/gerrit/acceptance/rest/group/GroupsIT.java b/javatests/com/google/gerrit/acceptance/rest/group/GroupsIT.java
index 2acd358..a52dd6d 100644
--- a/javatests/com/google/gerrit/acceptance/rest/group/GroupsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/group/GroupsIT.java
@@ -22,9 +22,9 @@
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.group.Rebuild;
 import com.google.gerrit.server.group.db.GroupBundle;
 import com.google.gerrit.server.notedb.GroupsMigration;
+import com.google.gerrit.server.restapi.group.Rebuild;
 import com.google.inject.Inject;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.ObjectId;
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/BanCommitIT.java b/javatests/com/google/gerrit/acceptance/rest/project/BanCommitIT.java
index 14fa715..19f6295 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/BanCommitIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/BanCommitIT.java
@@ -22,7 +22,7 @@
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.extensions.api.projects.BanCommitInput;
-import com.google.gerrit.server.project.BanCommit.BanResultInfo;
+import com.google.gerrit.server.restapi.project.BanCommit.BanResultInfo;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.junit.Test;
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java b/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
index ffde073..4af329a 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
@@ -182,8 +182,7 @@
   public void listProjectWithType() throws Exception {
     Map<String, ProjectInfo> result =
         gApi.projects().list().withType(FilterType.PERMISSIONS).getAsMap();
-    assertThat(result).hasSize(1);
-    assertThat(result).containsKey(allProjects.get());
+    assertThat(result.keySet()).containsExactly(allProjects.get(), allUsers.get());
 
     assertThatNameList(filter(gApi.projects().list().withType(FilterType.ALL).get()))
         .containsExactly(allProjects, allUsers, project)
diff --git a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
index 348f027..343eacc 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
@@ -49,11 +49,11 @@
 import com.google.gerrit.reviewdb.client.Patch;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.ChangesCollection;
-import com.google.gerrit.server.change.PostReview;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.notedb.ChangeNoteUtil;
 import com.google.gerrit.server.notedb.DeleteCommentRewriter;
+import com.google.gerrit.server.restapi.change.ChangesCollection;
+import com.google.gerrit.server.restapi.change.PostReview;
 import com.google.gerrit.testing.FakeEmailSender;
 import com.google.gerrit.testing.FakeEmailSender.Message;
 import com.google.inject.Inject;
diff --git a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
index eed8feb..3877239 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.acceptance.server.change;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
 import static com.google.gerrit.acceptance.GitUtil.pushHead;
 import static com.google.gerrit.extensions.common.testing.EditInfoSubject.assertThat;
 import static java.util.concurrent.TimeUnit.SECONDS;
@@ -29,20 +30,26 @@
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.extensions.common.CommitInfo;
 import com.google.gerrit.extensions.common.EditInfo;
+import com.google.gerrit.index.IndexConfig;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.change.ChangesCollection;
-import com.google.gerrit.server.change.GetRelated.ChangeAndCommit;
-import com.google.gerrit.server.change.GetRelated.RelatedInfo;
 import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.restapi.change.ChangesCollection;
+import com.google.gerrit.server.restapi.change.GetRelated;
+import com.google.gerrit.server.restapi.change.GetRelated.ChangeAndCommit;
+import com.google.gerrit.server.restapi.change.GetRelated.RelatedInfo;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.ChangeContext;
+import com.google.gerrit.testing.ConfigSuite;
 import com.google.gerrit.testing.TestTimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.junit.After;
@@ -50,6 +57,15 @@
 import org.junit.Test;
 
 public class GetRelatedIT extends AbstractDaemonTest {
+  private static final int MAX_TERMS = 10;
+
+  @ConfigSuite.Default
+  public static Config defaultConfig() {
+    Config cfg = new Config();
+    cfg.setInt("index", null, "maxTerms", MAX_TERMS);
+    return cfg;
+  }
+
   private String systemTimeZone;
 
   @Before
@@ -64,6 +80,7 @@
     System.setProperty("user.timezone", systemTimeZone);
   }
 
+  @Inject private IndexConfig indexConfig;
   @Inject private ChangesCollection changes;
 
   @Test
@@ -539,6 +556,27 @@
     assertRelated(psId2_2, changeAndCommit(psId2_2, c2_2, 2), changeAndCommit(psId1_1, c1_1, 1));
   }
 
+  @Test
+  public void getRelatedManyGroups() throws Exception {
+    List<RevCommit> commits = new ArrayList<>();
+    RevCommit last = null;
+    int n = 2 * MAX_TERMS;
+    assertThat(n).isGreaterThan(indexConfig.maxTerms());
+    for (int i = 1; i <= n; i++) {
+      TestRepository<?>.CommitBuilder cb = last != null ? amendBuilder() : commitBuilder();
+      last = cb.add("a.txt", Integer.toString(i)).message("subject: " + i).create();
+      testRepo.reset(last);
+      assertPushOk(pushHead(testRepo, "refs/for/master", false), "refs/for/master");
+      commits.add(last);
+    }
+
+    ChangeData cd = getChange(last);
+    assertThat(cd.patchSets().size()).isEqualTo(n);
+    assertThat(GetRelated.getAllGroups(cd.notes(), db, psUtil).size()).isEqualTo(n);
+
+    assertRelated(cd.change().currentPatchSetId());
+  }
+
   private List<ChangeAndCommit> getRelated(PatchSet.Id ps) throws Exception {
     return getRelated(ps.getParentKey(), ps.get());
   }
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
index cce54fc..11edb50 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
@@ -46,9 +46,9 @@
 import com.google.gerrit.extensions.client.ReviewerState;
 import com.google.gerrit.extensions.common.CommitInfo;
 import com.google.gerrit.extensions.common.CommitMessageInput;
-import com.google.gerrit.server.change.PostReview;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.restapi.change.PostReview;
 import org.junit.Before;
 import org.junit.Test;
 
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
index be0f793..79fed8b 100644
--- a/javatests/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
@@ -58,8 +58,6 @@
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.change.PostReview;
-import com.google.gerrit.server.change.Rebuild;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.git.RepoRefCache;
@@ -75,6 +73,8 @@
 import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
 import com.google.gerrit.server.project.testing.Util;
 import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.restapi.change.PostReview;
+import com.google.gerrit.server.restapi.change.Rebuild;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.ChangeContext;
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/ReflogIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/ReflogIT.java
index 312974f..834dbfa 100644
--- a/javatests/com/google/gerrit/acceptance/server/notedb/ReflogIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/notedb/ReflogIT.java
@@ -51,7 +51,7 @@
       gApi.changes().id(id.get()).topic("foo");
       ReflogEntry last = repo.getReflogReader(changeMetaRef(id)).getLastEntry();
       assertThat(last).named("last RefLogEntry").isNotNull();
-      assertThat(last.getComment()).isEqualTo("change.PutTopic");
+      assertThat(last.getComment()).isEqualTo("restapi.change.PutTopic");
     }
   }
 }
diff --git a/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java b/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
index 620fee7..267c622 100644
--- a/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
+++ b/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.restapi.project.CommitsCollection;
 import com.google.gerrit.server.schema.SchemaCreator;
 import com.google.gerrit.server.util.RequestContext;
 import com.google.gerrit.server.util.ThreadLocalRequestContext;
diff --git a/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java b/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java
index ec6f1ea..7dd265e 100644
--- a/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java
+++ b/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java
@@ -24,7 +24,7 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.AccountGroup.Id;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.group.CreateGroup;
+import com.google.gerrit.server.restapi.group.CreateGroup;
 import com.google.gerrit.testing.SchemaUpgradeTestEnvironment;
 import com.google.gerrit.testing.TestUpdateUI;
 import com.google.gwtorm.jdbc.JdbcSchema;
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
index 37b2c21..a7b5e54 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
@@ -117,40 +117,32 @@
         if (linkCopy.name === 'Repositories' && this._repoName) {
           linkCopy.subsection = {
             name: this._repoName,
-            view: 'gr-repo',
+            view: Gerrit.Nav.View.REPO,
             noBaseUrl: true,
-            url: `/admin/repos/${this.encodeURL(this._repoName, true)}`,
+            url: Gerrit.Nav.getUrlForRepo(this._repoName),
             children: [{
               name: 'Access',
-              detailType: 'access',
-              view: 'gr-repo-access',
-              noBaseUrl: true,
-              url: `/admin/repos/` +
-                  `${this.encodeURL(this._repoName, true)},access`,
+              view: Gerrit.Nav.View.REPO,
+              detailType: Gerrit.Nav.RepoDetailView.ACCESS,
+              url: Gerrit.Nav.getUrlForRepoAccess(this._repoName),
             },
             {
               name: 'Commands',
-              detailType: 'commands',
-              view: 'gr-repo-commands',
-              noBaseUrl: true,
-              url: `/admin/repos/` +
-                  `${this.encodeURL(this._repoName, true)},commands`,
+              view: Gerrit.Nav.View.REPO,
+              detailType: Gerrit.Nav.RepoDetailView.COMMANDS,
+              url: Gerrit.Nav.getUrlForRepoCommands(this._repoName),
             },
             {
               name: 'Branches',
-              detailType: 'branches',
-              view: 'gr-repo-detail-list',
-              noBaseUrl: true,
-              url: `/admin/repos/` +
-                  `${this.encodeURL(this._repoName, true)},branches`,
+              view: Gerrit.Nav.View.REPO,
+              detailType: Gerrit.Nav.RepoDetailView.BRANCHES,
+              url: Gerrit.Nav.getUrlForRepoBranches(this._repoName),
             },
             {
               name: 'Tags',
-              detailType: 'tags',
-              view: 'gr-repo-detail-list',
-              noBaseUrl: true,
-              url: `/admin/repos/` +
-                  `${this.encodeURL(this._repoName, true)},tags`,
+              view: Gerrit.Nav.View.REPO,
+              detailType: Gerrit.Nav.RepoDetailView.TAGS,
+              url: Gerrit.Nav.getUrlForRepoTags(this._repoName),
             }],
           };
         }
@@ -268,6 +260,13 @@
         return '';
       }
 
+      if (params.view === Gerrit.Nav.View.REPO &&
+          itemView === Gerrit.Nav.View.REPO) {
+        if (!params.detail && !opt_detailType) { return 'selected'; }
+        if (params.detail === opt_detailType) { return 'selected'; }
+        return '';
+      }
+
       if (params.detailType && params.detailType !== opt_detailType) {
         return '';
       }
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html
index 6a90488..bd79872 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html
@@ -258,9 +258,8 @@
 
         test('repo', done => {
           element.params = {
-            view: Gerrit.Nav.View.ADMIN,
-            project: 'foo',
-            adminView: 'gr-repo',
+            view: Gerrit.Nav.View.REPO,
+            repoName: 'foo',
           };
           element._repoName = 'foo';
           element.reload().then(() => {
@@ -275,10 +274,9 @@
 
         test('repo access', done => {
           element.params = {
-            view: Gerrit.Nav.View.ADMIN,
-            adminView: 'gr-repo-access',
-            detailType: 'access',
-            repo: 'foo',
+            view: Gerrit.Nav.View.REPO,
+            detail: Gerrit.Nav.RepoDetailView.ACCESS,
+            repoName: 'foo',
           };
           element._repoName = 'foo';
           element.reload().then(() => {
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.html b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
index e93e710..526cc86 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
@@ -195,6 +195,7 @@
           <span class="date">
             <gr-date-formatter
                 has-tooltip
+                show-date-and-time
                 date-str="[[message.date]]"></gr-date-formatter>
           </span>
         </template>
@@ -202,6 +203,7 @@
           <a class="date" href$="[[_computeMessageHash(message)]]" on-tap="_handleLinkTap">
             <gr-date-formatter
                 has-tooltip
+                show-date-and-time
                 date-str="[[message.date]]"></gr-date-formatter>
           </a>
         </template>
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
index f8c7e74..7b17f22 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -55,6 +55,12 @@
     //    - `groupId`, required, String, the ID of the group.
     //    - `detail`, optional, String, the name of the group detail view.
     //      Takes any value from Gerrit.Nav.GroupDetailView.
+    //
+    //  - Gerrit.Nav.View.REPO:
+    //    - `repoName`, required, String, the name of the repo
+    //    - `detail`, optional, String, the name of the repo detail view.
+    //      Takes any value from Gerrit.Nav.RepoDetailView.
+
 
     window.Gerrit = window.Gerrit || {};
 
@@ -79,6 +85,7 @@
         EDIT: 'edit',
         GROUP: 'group',
         PLUGIN_SCREEN: 'plugin-screen',
+        REPO: 'repo',
         SEARCH: 'search',
         SETTINGS: 'settings',
       },
@@ -88,6 +95,13 @@
         LOG: 'log',
       },
 
+      RepoDetailView: {
+        ACCESS: 'access',
+        BRANCHES: 'branches',
+        COMMANDS: 'commands',
+        TAGS: 'tags',
+      },
+
       WeblinkType: {
         CHANGE: 'change',
         FILE: 'file',
@@ -362,6 +376,65 @@
       },
 
       /**
+       * @param {string} repoName
+       * @return {string}
+       */
+      getUrlForRepo(repoName) {
+        return this._getUrlFor({
+          view: Gerrit.Nav.View.REPO,
+          repoName,
+        });
+      },
+
+      /**
+       * @param {string} repoName
+       * @return {string}
+       */
+      getUrlForRepoTags(repoName) {
+        return this._getUrlFor({
+          view: Gerrit.Nav.View.REPO,
+          repoName,
+          detail: Gerrit.Nav.RepoDetailView.TAGS,
+        });
+      },
+
+      /**
+       * @param {string} repoName
+       * @return {string}
+       */
+      getUrlForRepoBranches(repoName) {
+        return this._getUrlFor({
+          view: Gerrit.Nav.View.REPO,
+          repoName,
+          detail: Gerrit.Nav.RepoDetailView.BRANCHES,
+        });
+      },
+
+      /**
+       * @param {string} repoName
+       * @return {string}
+       */
+      getUrlForRepoAccess(repoName) {
+        return this._getUrlFor({
+          view: Gerrit.Nav.View.REPO,
+          repoName,
+          detail: Gerrit.Nav.RepoDetailView.ACCESS,
+        });
+      },
+
+      /**
+       * @param {string} repoName
+       * @return {string}
+       */
+      getUrlForRepoCommands(repoName) {
+        return this._getUrlFor({
+          view: Gerrit.Nav.View.REPO,
+          repoName,
+          detail: Gerrit.Nav.RepoDetailView.COMMANDS,
+        });
+      },
+
+      /**
        * @param {string} groupId
        * @return {string}
        */
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index f309747..37f9f23 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -233,6 +233,8 @@
         url = this._generateDiffOrEditUrl(params);
       } else if (params.view === Views.GROUP) {
         url = this._generateGroupUrl(params);
+      } else if (params.view === Views.REPO) {
+        url = this._generateRepoUrl(params);
       } else if (params.view === Views.SETTINGS) {
         url = this._generateSettingsUrl(params);
       } else {
@@ -448,6 +450,24 @@
      * @param {!Object} params
      * @return {string}
      */
+    _generateRepoUrl(params) {
+      let url = `/admin/repos/${this.encodeURL(params.repoName + '', true)}`;
+      if (params.detail === Gerrit.Nav.RepoDetailView.ACCESS) {
+        url += ',access';
+      } else if (params.detail === Gerrit.Nav.RepoDetailView.BRANCHES) {
+        url += ',branches';
+      } else if (params.detail === Gerrit.Nav.RepoDetailView.TAGS) {
+        url += ',tags';
+      } else if (params.detail === Gerrit.Nav.RepoDetailView.COMMANDS) {
+        url += ',commands';
+      }
+      return url;
+    },
+
+    /**
+     * @param {!Object} params
+     * @return {string}
+     */
     _generateSettingsUrl(params) {
       return '/settings';
     },
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
index 21552d9..fb41ca6 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
@@ -30,7 +30,7 @@
       }
     </style>
     <span>
-      [[_computeDateStr(dateStr, _timeFormat, _relative)]]
+      [[_computeDateStr(dateStr, _timeFormat, _relative, showDateAndTime)]]
     </span>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
   </template>
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
index 65a4c68..3f7b8db 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
@@ -37,6 +37,10 @@
         value: null,
         notify: true,
       },
+      showDateAndTime: {
+        type: Boolean,
+        value: false,
+      },
 
       /**
        * When true, the detailed date appears in a GR-TOOLTIP rather than in the
@@ -131,7 +135,7 @@
           diff < 180 * Duration.DAY;
     },
 
-    _computeDateStr(dateStr, timeFormat, relative) {
+    _computeDateStr(dateStr, timeFormat, relative, showDateAndTime) {
       if (!dateStr) { return ''; }
       const date = moment(util.parseDate(dateStr));
       if (!date.isValid()) { return ''; }
@@ -147,8 +151,13 @@
       let format = TimeFormats.MONTH_DAY_YEAR;
       if (this._isWithinDay(now, date)) {
         format = timeFormat;
-      } else if (this._isWithinHalfYear(now, date)) {
-        format = TimeFormats.MONTH_DAY;
+      } else {
+        if (this._isWithinHalfYear(now, date)) {
+          format = TimeFormats.MONTH_DAY;
+        }
+        if (this.showDateAndTime) {
+          format = `${format} ${timeFormat}`;
+        }
       }
       return date.format(format);
     },
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
index 2c15ef6..b418a42 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
@@ -55,7 +55,8 @@
       return d;
     }
 
-    function testDates(nowStr, dateStr, expected, expectedTooltip, done) {
+    function testDates(nowStr, dateStr, expected, expectedWithDateAndTime,
+        expectedTooltip, done) {
       // Normalize and convert the date to mimic server response.
       dateStr = normalizedDate(dateStr)
           .toJSON().replace('T', ' ').slice(0, -1);
@@ -65,6 +66,9 @@
         const span = element.$$('span');
         assert.equal(span.textContent.trim(), expected);
         assert.equal(element.title, expectedTooltip);
+        element.showDateAndTime = true;
+        flushAsynchronousOperations();
+        assert.equal(span.textContent.trim(), expectedWithDateAndTime);
         done();
       });
     }
@@ -98,25 +102,33 @@
       test('Within 24 hours on same day', done => {
         testDates('2015-07-29 20:34:14.985000000',
             '2015-07-29 15:34:14.985000000',
-            '15:34', 'Jul 29, 2015, 15:34:14', done);
+            '15:34',
+            '15:34',
+            'Jul 29, 2015, 15:34:14', done);
       });
 
       test('Within 24 hours on different days', done => {
         testDates('2015-07-29 03:34:14.985000000',
             '2015-07-28 20:25:14.985000000',
-            'Jul 28', 'Jul 28, 2015, 20:25:14', done);
+            'Jul 28',
+            'Jul 28 20:25',
+            'Jul 28, 2015, 20:25:14', done);
       });
 
       test('More than 24 hours but less than six months', done => {
         testDates('2015-07-29 20:34:14.985000000',
             '2015-06-15 03:25:14.985000000',
-            'Jun 15', 'Jun 15, 2015, 03:25:14', done);
+            'Jun 15',
+            'Jun 15 03:25',
+            'Jun 15, 2015, 03:25:14', done);
       });
 
       test('More than six months', done => {
         testDates('2015-09-15 20:34:00.000000000',
             '2015-01-15 03:25:00.000000000',
-            'Jan 15, 2015', 'Jan 15, 2015, 03:25:00', done);
+            'Jan 15, 2015',
+            'Jan 15, 2015 03:25',
+            'Jan 15, 2015, 03:25:00', done);
       });
     });
 
@@ -135,7 +147,9 @@
       test('Within 24 hours on same day', done => {
         testDates('2015-07-29 20:34:14.985000000',
             '2015-07-29 15:34:14.985000000',
-            '3:34 PM', 'Jul 29, 2015, 3:34:14 PM', done);
+            '3:34 PM',
+            '3:34 PM',
+            'Jul 29, 2015, 3:34:14 PM', done);
       });
     });
 
@@ -153,13 +167,17 @@
       test('Within 24 hours on same day', done => {
         testDates('2015-07-29 20:34:14.985000000',
             '2015-07-29 15:34:14.985000000',
-            '5 hours ago', 'Jul 29, 2015, 3:34:14 PM', done);
+            '5 hours ago',
+            '5 hours ago',
+            'Jul 29, 2015, 3:34:14 PM', done);
       });
 
       test('More than six months', done => {
         testDates('2015-09-15 20:34:00.000000000',
             '2015-01-15 03:25:00.000000000',
-            '8 months ago', 'Jan 15, 2015, 3:25:00 AM', done);
+            '8 months ago',
+            '8 months ago',
+            'Jan 15, 2015, 3:25:00 AM', done);
       });
     });
 
diff --git a/resources/com/google/gerrit/httpd/auth/ldap/LoginForm.html b/resources/com/google/gerrit/httpd/auth/ldap/LoginForm.html
index 64d16c5..39900e8 100644
--- a/resources/com/google/gerrit/httpd/auth/ldap/LoginForm.html
+++ b/resources/com/google/gerrit/httpd/auth/ldap/LoginForm.html
@@ -15,13 +15,13 @@
     </style>
     <style id="gerrit_sitecss" type="text/css"></style>
   </head>
-  <body>
+  <body class="login" id="login_ldap">
     <div id="gerrit_topmenu" style="height:45px;" class="gerritTopMenu"></div>
     <div id="gerrit_header"></div>
     <div id="gerrit_body" class="gerritBody">
       <h1>Sign In to Gerrit Code Review at <span id="hostName">example.com</span></h1>
       <div id="error_message">Invalid username or password.</div>
-      <form method="POST" action="#" id="login_form">
+      <form method="POST" action="#" id="login_form" onsubmit="return shouldSubmit()">
         <table style="border: 0;">
         <tr>
           <th>Username</th>
@@ -50,7 +50,7 @@
         <tr>
           <td></td>
           <td>
-            <input type="submit" value="Sign In" tabindex="4"/>
+            <input id="b_signin" type="submit" value="Sign In" tabindex="4"/>
             <a href="../" id="cancel_link">Cancel</a>
           </td>
         </tr>
@@ -62,6 +62,17 @@
     </div>
 
     <script type="text/javascript">
+    <![CDATA[
+      var submitted = false;
+      function shouldSubmit() {
+        if(!submitted) {
+          submitted = true;
+          document.getElementById('b_signin').disabled=true;
+          return true;
+        }
+        return false;
+      }
+
       var login_form = document.getElementById('login_form');
       var f_user = document.getElementById('f_user');
       var f_pass = document.getElementById('f_pass');
@@ -75,12 +86,13 @@
         }
       }
       f_pass.onkeyup = function(e) {
-        if (e.keyCode == 13) {
+        if (e.keyCode == 13 && shouldSubmit()) {
           login_form.submit();
           return false;
         }
       }
       f_user.focus();
+    ]]>
     </script>
   </body>
 </html>
diff --git a/resources/com/google/gerrit/httpd/auth/oauth/LoginForm.html b/resources/com/google/gerrit/httpd/auth/oauth/LoginForm.html
index f7814c0..67c40c3 100644
--- a/resources/com/google/gerrit/httpd/auth/oauth/LoginForm.html
+++ b/resources/com/google/gerrit/httpd/auth/oauth/LoginForm.html
@@ -24,7 +24,7 @@
     </style>
     <style id="gerrit_sitecss" type="text/css"></style>
   </head>
-  <body>
+  <body class="login" id="login_oauth">
     <div id="gerrit_topmenu" style="height:45px;" class="gerritTopMenu"></div>
     <div id="gerrit_header"></div>
     <div id="gerrit_body" class="gerritBody">
diff --git a/resources/com/google/gerrit/httpd/auth/openid/LoginForm.html b/resources/com/google/gerrit/httpd/auth/openid/LoginForm.html
index 07e09f5..4923143 100644
--- a/resources/com/google/gerrit/httpd/auth/openid/LoginForm.html
+++ b/resources/com/google/gerrit/httpd/auth/openid/LoginForm.html
@@ -39,7 +39,7 @@
     </style>
     <style id="gerrit_sitecss" type="text/css"></style>
   </head>
-  <body>
+  <body class="login" id="login_openid">
     <div id="gerrit_topmenu" style="height:45px;" class="gerritTopMenu"></div>
     <div id="gerrit_header"></div>
     <div id="gerrit_body" class="gerritBody">
diff --git a/tools/bzl/asciidoc.bzl b/tools/bzl/asciidoc.bzl
index 9fb53c0..62fa4c6 100644
--- a/tools/bzl/asciidoc.bzl
+++ b/tools/bzl/asciidoc.bzl
@@ -122,9 +122,9 @@
 }
 
 _asciidoc = rule(
-    attrs = _asciidoc_attrs + {
+    attrs = dict(_asciidoc_attrs.items() + {
         "outs": attr.output_list(mandatory = True),
-    },
+    }.items()),
     implementation = _asciidoc_impl,
 )
 
diff --git a/tools/bzl/js.bzl b/tools/bzl/js.bzl
index 2e74091..945b8e1 100644
--- a/tools/bzl/js.bzl
+++ b/tools/bzl/js.bzl
@@ -177,10 +177,10 @@
 
 js_component = rule(
     _js_component,
-    attrs = _common_attrs + {
+    attrs = dict(_common_attrs.items() + {
         "srcs": attr.label_list(allow_files = [".js"]),
         "license": attr.label(allow_single_file = True),
-    },
+    }.items()),
     outputs = {
         "zip": "%{name}.zip",
     },
@@ -188,14 +188,14 @@
 
 _bower_component = rule(
     _bower_component_impl,
-    attrs = _common_attrs + {
+    attrs = dict(_common_attrs.items() + {
         "zipfile": attr.label(allow_single_file = [".zip"]),
         "license": attr.label(allow_single_file = True),
         "version_json": attr.label(allow_files = [".json"]),
 
         # If set, define by hand, and don't regenerate this entry in bower2bazel.
         "seed": attr.bool(default = False),
-    },
+    }.items()),
 )
 
 # TODO(hanwen): make license mandatory.