Merge "Fix hover/selected states for non-primary or secondary gr-buttons"
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index a881e3d..d942d9b 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -155,7 +155,8 @@
 
 If an updated classpath is needed, the Eclipse project can be
 refreshed and missing dependency JARs can be downloaded by running
-`project_bzl.py` again.
+`project_bzl.py` again. For IntelliJ, you need to click the `Sync Project
+with BUILD Files` button of link:https://ij.bazel.io[IntelliJ plugin].
 
 [[documentation]]
 === Documentation
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index f0fb4c7..98897a5 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -2474,6 +2474,79 @@
   HTTP/1.1 204 No Content
 ----
 
+[[get-hashtags]]
+=== Get Hashtags
+--
+'GET /changes/link:#change-id[\{change-id\}]/hashtags'
+--
+
+Gets the hashtags associated with a change.
+
+[NOTE] Hashtags are only available when NoteDb is enabled.
+
+.Request
+----
+  GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/hashtags HTTP/1.0
+----
+
+As response the change's hashtags are returned as a list of strings.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  [
+    "hashtag1",
+    "hashtag2"
+  ]
+----
+
+[[set-hashtags]]
+=== Set Hashtags
+--
+'POST /changes/link:#change-id[\{change-id\}]/hashtags'
+--
+
+Adds and/or removes hashtags from a change.
+
+[NOTE] Hashtags are only available when NoteDb is enabled.
+
+The hashtags to add or remove must be provided in the request body inside a
+link:#hashtags-input[HashtagsInput] entity.
+
+.Request
+----
+  POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/hashtags HTTP/1.0
+  Content-Type: application/json; charset=UTF-8
+
+  {
+    "add" : [
+      "hashtag3"
+    ],
+    "remove" : [
+      "hashtag2"
+    ]
+  }
+----
+
+As response the change's hashtags are returned as a list of strings.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  [
+    "hashtag1",
+    "hashtag3"
+  ]
+----
+
 [[reviewer-endpoints]]
 == Reviewer Endpoints
 
@@ -4689,79 +4762,6 @@
   }
 ----
 
-[[get-hashtags]]
-=== Get Hashtags
---
-'GET /changes/link:#change-id[\{change-id\}]/hashtags'
---
-
-Gets the hashtags associated with a change.
-
-[NOTE] Hashtags are only available when NoteDb is enabled.
-
-.Request
-----
-  GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/hashtags HTTP/1.0
-----
-
-As response the change's hashtags are returned as a list of strings.
-
-.Response
-----
-  HTTP/1.1 200 OK
-  Content-Disposition: attachment
-  Content-Type: application/json; charset=UTF-8
-
-  )]}'
-  [
-    "hashtag1",
-    "hashtag2"
-  ]
-----
-
-[[set-hashtags]]
-=== Set Hashtags
---
-'POST /changes/link:#change-id[\{change-id\}]/hashtags'
---
-
-Adds and/or removes hashtags from a change.
-
-[NOTE] Hashtags are only available when NoteDb is enabled.
-
-The hashtags to add or remove must be provided in the request body inside a
-link:#hashtags-input[HashtagsInput] entity.
-
-.Request
-----
-  POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/hashtags HTTP/1.0
-  Content-Type: application/json; charset=UTF-8
-
-  {
-    "add" : [
-      "hashtag3"
-    ],
-    "remove" : [
-      "hashtag2"
-    ]
-  }
-----
-
-As response the change's hashtags are returned as a list of strings.
-
-.Response
-----
-  HTTP/1.1 200 OK
-  Content-Disposition: attachment
-  Content-Type: application/json; charset=UTF-8
-
-  )]}'
-  [
-    "hashtag1",
-    "hashtag3"
-  ]
-----
-
 [[ids]]
 == IDs
 
diff --git a/Documentation/user-search-accounts.txt b/Documentation/user-search-accounts.txt
index 15d87b0..04f40c0 100644
--- a/Documentation/user-search-accounts.txt
+++ b/Documentation/user-search-accounts.txt
@@ -1,6 +1,6 @@
 = Gerrit Code Review - Searching Accounts
 
-== Basic Change Search
+== Basic Account Search
 
 Similar to many popular search engines on the web, just enter some
 text and let Gerrit figure out the meaning:
@@ -57,7 +57,7 @@
 is:visible::
 +
 Magical internal flag to prove the current user has access to read
-the change. This flag is always added to any query.
+the account. This flag is always added to any query.
 
 [[is-active-magic]]
 is:active::
diff --git a/WORKSPACE b/WORKSPACE
index 0d0f414..7b75437 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -134,17 +134,15 @@
 maven_jar(
     name = "jgit",
     artifact = "org.eclipse.jgit:org.eclipse.jgit:" + JGIT_VERS,
-    repository = GERRIT,
-    sha1 = "34315f71bb9becf6ff75947a9c43c415b929ec21",
-    src_sha1 = "8320c18472870904eb7fb860af353fea818d07e4",
+    sha1 = "b705df6cb4c0f4413e6ad52c6cd12011cc6fba4c",
+    src_sha1 = "6ec89bdedccdab092f395016ab761dcdb08f7ffc",
     unsign = True,
 )
 
 maven_jar(
     name = "jgit_servlet",
     artifact = "org.eclipse.jgit:org.eclipse.jgit.http.server:" + JGIT_VERS,
-    repository = GERRIT,
-    sha1 = "927990025d2970995dbb58f03763eeb776fec8fd",
+    sha1 = "5cb46863efcb208a3a9f729c0b6fd00be0dae6ee",
     unsign = True,
 )
 
@@ -158,15 +156,13 @@
 maven_jar(
     name = "jgit_archive",
     artifact = "org.eclipse.jgit:org.eclipse.jgit.archive:" + JGIT_VERS,
-    repository = GERRIT,
-    sha1 = "4a5d058915400c1ef497bfeeb5e87d235213e273",
+    sha1 = "d44816c7b47746948485430e7372b15108b4bf2e",
 )
 
 maven_jar(
     name = "jgit_junit",
     artifact = "org.eclipse.jgit:org.eclipse.jgit.junit:" + JGIT_VERS,
-    repository = GERRIT,
-    sha1 = "8e3cb9b1f632fdfea76b04c286a2c0d8d260ebce",
+    sha1 = "42bd9796a68b0de8abf1e0c23ebd4006bf9645e0",
     unsign = True,
 )
 
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/SshSession.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/SshSession.java
index 794f832..4ce3301 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/SshSession.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/SshSession.java
@@ -49,9 +49,10 @@
       channel.setCommand(command);
       channel.setInputStream(opt);
       InputStream in = channel.getInputStream();
+      InputStream err = channel.getErrStream();
       channel.connect();
 
-      Scanner s = new Scanner(channel.getErrStream()).useDelimiter("\\A");
+      Scanner s = new Scanner(err).useDelimiter("\\A");
       error = s.hasNext() ? s.next() : null;
 
       s = new Scanner(in).useDelimiter("\\A");
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 0e96226..10eb7c5 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -60,9 +60,9 @@
 import com.google.gerrit.gpg.testutil.TestKey;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.account.WatchConfig;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.project.RefPattern;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 8979b9f..d327134 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -30,7 +30,6 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.GerritConfig;
 import com.google.gerrit.acceptance.GitUtil;
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.TestAccount;
@@ -958,14 +957,6 @@
   }
 
   @Test
-  // TODO(dborowitz): This is to exercise a specific case in the database search
-  // path. Once the account index becomes obligatory this method can be removed.
-  @GerritConfig(name = "index.testDisable", value = "accounts")
-  public void pushWithNameInFooterNotFoundWithDbSearch() throws Exception {
-    pushWithReviewerInFooter("Notauser", null);
-  }
-
-  @Test
   public void pushNewPatchsetOverridingStickyLabel() throws Exception {
     ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
     LabelType codeReview = Util.codeReview();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
index 767207a..48a8cd4 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.api.projects.ProjectInput;
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.client.SubmitType;
@@ -83,7 +84,7 @@
 
   @Test
   @UseLocalDisk
-  public void testCreateProjectHttpWithUnreasonableName_BadRequest()
+  public void createProjectHttpWithUnreasonableName_BadRequest()
       throws Exception {
     adminRestSession
         .put("/projects/" + Url.encode(name("invalid/../name")))
@@ -244,6 +245,22 @@
   }
 
   @Test
+  public void createProjectWithCapability() throws Exception {
+    allowGlobalCapabilities(SystemGroupBackend.REGISTERED_USERS,
+        GlobalCapability.CREATE_PROJECT);
+    try {
+      setApiUser(user);
+      ProjectInput in = new ProjectInput();
+      in.name = name("newProject");
+      ProjectInfo p = gApi.projects().create(in).get();
+      assertThat(p.name).isEqualTo(in.name);
+    } finally {
+      removeGlobalCapabilities(SystemGroupBackend.REGISTERED_USERS,
+          GlobalCapability.CREATE_PROJECT);
+    }
+  }
+
+  @Test
   public void createProjectWithoutCapability_Forbidden() throws Exception {
     setApiUser(user);
     ProjectInput in = new ProjectInput();
@@ -259,6 +276,26 @@
     assertCreateFails(in, ResourceConflictException.class);
   }
 
+  @Test
+  public void createProjectWithCreateProjectCapabilityAndParentNotVisible()
+      throws Exception {
+    Project parent = projectCache.get(allProjects).getProject();
+    parent.setState(com.google.gerrit.extensions.client.ProjectState.HIDDEN);
+    allowGlobalCapabilities(SystemGroupBackend.REGISTERED_USERS,
+        GlobalCapability.CREATE_PROJECT);
+    try {
+      setApiUser(user);
+      ProjectInput in = new ProjectInput();
+      in.name = name("newProject");
+      ProjectInfo p = gApi.projects().create(in).get();
+      assertThat(p.name).isEqualTo(in.name);
+    } finally {
+      parent.setState(com.google.gerrit.extensions.client.ProjectState.ACTIVE);
+      removeGlobalCapabilities(SystemGroupBackend.REGISTERED_USERS,
+          GlobalCapability.CREATE_PROJECT);
+    }
+  }
+
   private AccountGroup.UUID groupUuid(String groupName) {
     return groupCache.get(new AccountGroup.NameKey(groupName)).getGroupUUID();
   }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
index e6efc0a..b0b50fd 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
@@ -23,8 +23,8 @@
 import com.google.gerrit.acceptance.TestAccount;
 import com.google.gerrit.extensions.client.ProjectWatchInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.git.NotifyConfig;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.mail.Address;
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
index c65b114..be929ed 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
@@ -23,12 +23,10 @@
 import com.google.common.io.BaseEncoding;
 import com.google.gerrit.common.PageLinks;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -63,8 +61,6 @@
 
   @Singleton
   public static class Factory {
-    private final Provider<ReviewDb> db;
-    private final AccountIndexCollection accountIndexes;
     private final Provider<InternalAccountQuery> accountQueryProvider;
     private final String webUrl;
     private final IdentifiedUser.GenericFactory userFactory;
@@ -73,13 +69,9 @@
 
     @Inject
     Factory(@GerritServerConfig Config cfg,
-        Provider<ReviewDb> db,
-        AccountIndexCollection accountIndexes,
         Provider<InternalAccountQuery> accountQueryProvider,
         IdentifiedUser.GenericFactory userFactory,
         @CanonicalWebUrl String webUrl) {
-      this.db = db;
-      this.accountIndexes = accountIndexes;
       this.accountQueryProvider = accountQueryProvider;
       this.webUrl = webUrl;
       this.userFactory = userFactory;
@@ -113,8 +105,6 @@
     }
   }
 
-  private final Provider<ReviewDb> db;
-  private final AccountIndexCollection accountIndexes;
   private final Provider<InternalAccountQuery> accountQueryProvider;
   private final String webUrl;
   private final IdentifiedUser.GenericFactory userFactory;
@@ -122,8 +112,6 @@
   private IdentifiedUser expectedUser;
 
   private GerritPublicKeyChecker(Factory factory) {
-    this.db = factory.db;
-    this.accountIndexes = factory.accountIndexes;
     this.accountQueryProvider = factory.accountQueryProvider;
     this.webUrl = factory.webUrl;
     this.userFactory = factory.userFactory;
@@ -174,25 +162,15 @@
 
   private CheckResult checkIdsForArbitraryUser(PGPPublicKey key)
       throws PGPException, OrmException {
-    IdentifiedUser user;
-    if (accountIndexes.getSearchIndex() != null) {
-      List<AccountState> accountStates =
-          accountQueryProvider.get().byExternalId(toExtIdKey(key).get());
-      if (accountStates.isEmpty()) {
-        return CheckResult.bad("Key is not associated with any users");
-      }
-      if (accountStates.size() > 1) {
-        return CheckResult.bad("Key is associated with multiple users");
-      }
-      user = userFactory.create(accountStates.get(0));
-    } else {
-      AccountExternalId extId = db.get().accountExternalIds().get(
-          toExtIdKey(key));
-      if (extId == null) {
-        return CheckResult.bad("Key is not associated with any users");
-      }
-      user = userFactory.create(extId.getAccountId());
+    List<AccountState> accountStates =
+        accountQueryProvider.get().byExternalId(toExtIdKey(key).get());
+    if (accountStates.isEmpty()) {
+      return CheckResult.bad("Key is not associated with any users");
     }
+    if (accountStates.size() > 1) {
+      return CheckResult.bad("Key is associated with multiple users");
+    }
+    IdentifiedUser user = userFactory.create(accountStates.get(0));
 
     Set<String> allowedUserIds = getAllowedUserIds(user);
     if (allowedUserIds.isEmpty()) {
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
index d2ccb88..20db31f 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
@@ -47,7 +47,6 @@
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.mail.send.AddKeySender;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
 import com.google.gwtorm.server.OrmException;
@@ -90,7 +89,6 @@
   private final GerritPublicKeyChecker.Factory checkerFactory;
   private final AddKeySender.Factory addKeyFactory;
   private final AccountCache accountCache;
-  private final AccountIndexCollection accountIndexes;
   private final Provider<InternalAccountQuery> accountQueryProvider;
 
   @Inject
@@ -101,7 +99,6 @@
       GerritPublicKeyChecker.Factory checkerFactory,
       AddKeySender.Factory addKeyFactory,
       AccountCache accountCache,
-      AccountIndexCollection accountIndexes,
       Provider<InternalAccountQuery> accountQueryProvider) {
     this.serverIdent = serverIdent;
     this.db = db;
@@ -110,7 +107,6 @@
     this.checkerFactory = checkerFactory;
     this.addKeyFactory = addKeyFactory;
     this.accountCache = accountCache;
-    this.accountIndexes = accountIndexes;
     this.accountQueryProvider = accountQueryProvider;
   }
 
@@ -131,28 +127,15 @@
       for (PGPPublicKeyRing keyRing : newKeys) {
         PGPPublicKey key = keyRing.getPublicKey();
         AccountExternalId.Key extIdKey = toExtIdKey(key.getFingerprint());
-        if (accountIndexes.getSearchIndex() != null) {
-          Account account = getAccountByExternalId(extIdKey.get());
-          if (account != null) {
-            if (!account.getId().equals(rsrc.getUser().getAccountId())) {
-              throw new ResourceConflictException(
-                  "GPG key already associated with another account");
-            }
-          } else {
-            newExtIds.add(
-                new AccountExternalId(rsrc.getUser().getAccountId(), extIdKey));
+        Account account = getAccountByExternalId(extIdKey.get());
+        if (account != null) {
+          if (!account.getId().equals(rsrc.getUser().getAccountId())) {
+            throw new ResourceConflictException(
+                "GPG key already associated with another account");
           }
         } else {
-          AccountExternalId existing = db.get().accountExternalIds().get(extIdKey);
-          if (existing != null) {
-            if (!existing.getAccountId().equals(rsrc.getUser().getAccountId())) {
-              throw new ResourceConflictException(
-                  "GPG key already associated with another account");
-            }
-          } else {
-            newExtIds.add(
-                new AccountExternalId(rsrc.getUser().getAccountId(), extIdKey));
-          }
+          newExtIds.add(
+              new AccountExternalId(rsrc.getUser().getAccountId(), extIdKey));
         }
       }
 
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index e6d395c..385bd70 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -28,12 +28,11 @@
 import com.google.common.collect.Collections2;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
@@ -96,6 +95,7 @@
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Secondary index implementation using Apache Lucene.
@@ -190,21 +190,18 @@
   }
 
   @Override
+  public void stop() {
+    MoreExecutors.shutdownAndAwaitTermination(
+        executor, Long.MAX_VALUE, TimeUnit.SECONDS);
+  }
+
+  @Override
   public void close() {
-    List<ListenableFuture<?>> closeFutures = Lists.newArrayListWithCapacity(2);
-    closeFutures.add(executor.submit(new Runnable() {
-      @Override
-      public void run() {
-        openIndex.close();
-      }
-    }));
-    closeFutures.add(executor.submit(new Runnable() {
-      @Override
-      public void run() {
-        closedIndex.close();
-      }
-    }));
-    Futures.getUnchecked(Futures.allAsList(closeFutures));
+    try {
+      openIndex.close();
+    } finally {
+      closedIndex.close();
+    }
   }
 
   @Override
diff --git a/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java b/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
index 35f79c9..9deec44 100644
--- a/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
+++ b/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
@@ -44,6 +44,7 @@
 import java.io.IOException;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
+import java.util.Optional;
 
 import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
@@ -165,10 +166,10 @@
   private boolean authenticateWithIdentityClaimedDuringHandshake(
       AuthRequest req, HttpServletResponse rsp, String claimedIdentifier)
       throws AccountException, IOException {
-    Account.Id claimedId = accountManager.lookup(claimedIdentifier);
-    Account.Id actualId = accountManager.lookup(user.getExternalId());
-    if (claimedId != null && actualId != null) {
-      if (claimedId.equals(actualId)) {
+    Optional<Account.Id> claimedId = accountManager.lookup(claimedIdentifier);
+    Optional<Account.Id> actualId = accountManager.lookup(user.getExternalId());
+    if (claimedId.isPresent() && actualId.isPresent()) {
+      if (claimedId.get().equals(actualId.get())) {
         // Both link to the same account, that's what we expected.
         log.debug("OAuth2: claimed identity equals current id");
       } else {
@@ -176,23 +177,23 @@
         // for what might be the same user.
         //
         log.error("OAuth accounts disagree over user identity:\n"
-            + "  Claimed ID: " + claimedId + " is " + claimedIdentifier
-            + "\n" + "  Delgate ID: " + actualId + " is "
+            + "  Claimed ID: " + claimedId.get() + " is " + claimedIdentifier
+            + "\n" + "  Delgate ID: " + actualId.get() + " is "
             + user.getExternalId());
         rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
         return false;
       }
-    } else if (claimedId != null && actualId == null) {
+    } else if (claimedId.isPresent() && !actualId.isPresent()) {
       // Claimed account already exists: link to it.
       //
       log.info("OAuth2: linking claimed identity to {}",
-          claimedId.toString());
+          claimedId.get().toString());
       try {
-        accountManager.link(claimedId, req);
+        accountManager.link(claimedId.get(), req);
       } catch (OrmException e) {
         log.error("Cannot link: " +  user.getExternalId()
             + " to user identity:\n"
-            + "  Claimed ID: " + claimedId + " is " + claimedIdentifier);
+            + "  Claimed ID: " + claimedId.get() + " is " + claimedIdentifier);
         rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
         return false;
       }
diff --git a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
index 67ac895..dccb6f7 100644
--- a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
+++ b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
@@ -43,6 +43,7 @@
 import java.io.IOException;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
+import java.util.Optional;
 
 import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
@@ -122,8 +123,9 @@
     AuthResult arsp = null;
     try {
       String claimedIdentifier = user.getClaimedIdentity();
-      Account.Id actualId = accountManager.lookup(user.getExternalId());
-      Account.Id claimedId = null;
+      Optional<Account.Id> actualId =
+          accountManager.lookup(user.getExternalId());
+      Optional<Account.Id> claimedId = Optional.empty();
 
       // We try to retrieve claimed identity.
       // For some reason, for example staging instance
@@ -133,17 +135,17 @@
       // That why we query it here, not to lose linking mode.
       if (!Strings.isNullOrEmpty(claimedIdentifier)) {
         claimedId = accountManager.lookup(claimedIdentifier);
-        if (claimedId == null) {
+        if (!claimedId.isPresent()) {
           log.debug("Claimed identity is unknown");
         }
       }
 
       // Use case 1: claimed identity was provided during handshake phase
       // and user account exists for this identity
-      if (claimedId != null) {
+      if (claimedId.isPresent()) {
         log.debug("Claimed identity is set and is known");
-        if (actualId != null) {
-          if (claimedId.equals(actualId)) {
+        if (actualId.isPresent()) {
+          if (claimedId.get().equals(actualId.get())) {
             // Both link to the same account, that's what we expected.
             log.debug("Both link to the same account. All is fine.");
           } else {
@@ -151,8 +153,8 @@
             // for what might be the same user. The admin would have to
             // link the accounts manually.
             log.error("OAuth accounts disagree over user identity:\n"
-                + "  Claimed ID: " + claimedId + " is " + claimedIdentifier
-                + "\n" + "  Delgate ID: " + actualId + " is "
+                + "  Claimed ID: " + claimedId.get() + " is " + claimedIdentifier
+                + "\n" + "  Delgate ID: " + actualId.get() + " is "
                 + user.getExternalId());
             rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
             return;
@@ -161,11 +163,11 @@
           // Claimed account already exists: link to it.
           log.debug("Claimed account already exists: link to it.");
           try {
-            accountManager.link(claimedId, areq);
+            accountManager.link(claimedId.get(), areq);
           } catch (OrmException e) {
             log.error("Cannot link: " +  user.getExternalId()
                 + " to user identity:\n"
-                + "  Claimed ID: " + claimedId + " is " + claimedIdentifier);
+                + "  Claimed ID: " + claimedId.get() + " is " + claimedIdentifier);
             rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
             return;
           }
diff --git a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
index 36947a9..f3130e7 100644
--- a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
+++ b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
@@ -62,6 +62,7 @@
 import java.io.IOException;
 import java.net.URL;
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 
 import javax.servlet.http.Cookie;
@@ -364,25 +365,26 @@
       // identity we have in our AuthRequest above. We still should have a
       // link between the two, so set one up if not present.
       //
-      Account.Id claimedId = accountManager.lookup(claimedIdentifier);
-      Account.Id actualId = accountManager.lookup(areq.getExternalId());
+      Optional<Account.Id> claimedId = accountManager.lookup(claimedIdentifier);
+      Optional<Account.Id> actualId =
+          accountManager.lookup(areq.getExternalId());
 
-      if (claimedId != null && actualId != null) {
-        if (claimedId.equals(actualId)) {
+      if (claimedId.isPresent() && actualId.isPresent()) {
+        if (claimedId.get().equals(actualId.get())) {
           // Both link to the same account, that's what we expected.
         } else {
           // This is (for now) a fatal error. There are two records
           // for what might be the same user.
           //
           log.error("OpenID accounts disagree over user identity:\n"
-              + "  Claimed ID: " + claimedId + " is " + claimedIdentifier
-              + "\n" + "  Delgate ID: " + actualId + " is "
+              + "  Claimed ID: " + claimedId.get() + " is " + claimedIdentifier
+              + "\n" + "  Delgate ID: " + actualId.get() + " is "
               + areq.getExternalId());
           cancelWithError(req, rsp, "Contact site administrator");
           return;
         }
 
-      } else if (claimedId == null && actualId != null) {
+      } else if (!claimedId.isPresent() && actualId.isPresent()) {
         // Older account, the actual was already created but the claimed
         // was missing due to a bug in Gerrit. Link the claimed.
         //
@@ -390,14 +392,14 @@
             new com.google.gerrit.server.account.AuthRequest(claimedIdentifier);
         linkReq.setDisplayName(areq.getDisplayName());
         linkReq.setEmailAddress(areq.getEmailAddress());
-        accountManager.link(actualId, linkReq);
+        accountManager.link(actualId.get(), linkReq);
 
-      } else if (claimedId != null && actualId == null) {
+      } else if (claimedId.isPresent() && !actualId.isPresent()) {
         // Claimed account already exists, but it smells like the user has
         // changed their delegate to point to a different provider. Link
         // the new provider.
         //
-        accountManager.link(claimedId, areq);
+        accountManager.link(claimedId.get(), areq);
 
       } else {
         // Both are null, we are going to create a new account below.
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
index de2134b..81d642d 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
@@ -42,9 +42,6 @@
  * managed {@link AccountGroup}. Multiple records can exist when the user is a
  * member of more than one group.</li>
  *
- * <li>{@link AccountProjectWatch}: user's email settings related to a specific
- * {@link Project}. One record per project the user is interested in tracking.</li>
- *
  * <li>{@link AccountSshKey}: user's public SSH keys, for authentication through
  * the internal SSH daemon. One record per SSH key uploaded by the user, keys
  * are checked in random order until a match is found.</li>
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountProjectWatch.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountProjectWatch.java
deleted file mode 100644
index a6796e7..0000000
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountProjectWatch.java
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright (C) 2008 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.reviewdb.client;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.CompoundKey;
-import com.google.gwtorm.client.StringKey;
-
-/** An {@link Account} interested in a {@link Project}. */
-public final class AccountProjectWatch {
-
-  public enum NotifyType {
-    // sort by name, except 'ALL' which should stay last
-    ABANDONED_CHANGES,
-    ALL_COMMENTS,
-    NEW_CHANGES,
-    NEW_PATCHSETS,
-    SUBMITTED_CHANGES,
-
-    ALL
-  }
-
-  public static final String FILTER_ALL = "*";
-
-  public static class Key extends CompoundKey<Account.Id> {
-    private static final long serialVersionUID = 1L;
-
-    @Column(id = 1)
-    protected Account.Id accountId;
-
-    @Column(id = 2)
-    protected Project.NameKey projectName;
-
-    @Column(id = 3)
-    protected Filter filter;
-
-    protected Key() {
-      accountId = new Account.Id();
-      projectName = new Project.NameKey();
-      filter = new Filter();
-    }
-
-    public Key(Account.Id a, Project.NameKey g, String f) {
-      accountId = a;
-      projectName = g;
-      filter = new Filter(f);
-    }
-
-    @Override
-    public Account.Id getParentKey() {
-      return accountId;
-    }
-
-    public Project.NameKey getProjectName() {
-      return projectName;
-    }
-
-    public Filter getFilter() {
-      return filter;
-    }
-
-    @Override
-    public com.google.gwtorm.client.Key<?>[] members() {
-      return new com.google.gwtorm.client.Key<?>[] {projectName, filter};
-    }
-  }
-
-  public static class Filter extends StringKey<com.google.gwtorm.client.Key<?>> {
-    private static final long serialVersionUID = 1L;
-
-    @Column(id = 1)
-    protected String filter;
-
-    protected Filter() {
-    }
-
-    public Filter(String f) {
-      filter = f != null && !f.isEmpty() ? f : FILTER_ALL;
-    }
-
-    @Override
-    public String get() {
-      return filter;
-    }
-
-    @Override
-    protected void set(String newValue) {
-      filter = newValue;
-    }
-  }
-
-  @Column(id = 1, name = Column.NONE)
-  protected Key key;
-
-  /** Automatically send email notifications of new changes? */
-  @Column(id = 2)
-  protected boolean notifyNewChanges;
-
-  /** Automatically receive comments published to this project */
-  @Column(id = 3)
-  protected boolean notifyAllComments;
-
-  /** Automatically receive changes submitted to this project */
-  @Column(id = 4)
-  protected boolean notifySubmittedChanges;
-
-  @Column(id = 5)
-  protected boolean notifyNewPatchSets;
-
-  @Column(id = 6)
-  protected boolean notifyAbandonedChanges;
-
-  protected AccountProjectWatch() {
-  }
-
-  public AccountProjectWatch(final AccountProjectWatch.Key k) {
-    key = k;
-  }
-
-  public AccountProjectWatch.Key getKey() {
-    return key;
-  }
-
-  public Account.Id getAccountId() {
-    return key.accountId;
-  }
-
-  public Project.NameKey getProjectNameKey() {
-    return key.projectName;
-  }
-
-  public String getFilter() {
-    return FILTER_ALL.equals(key.filter.get()) ? null : key.filter.get();
-  }
-
-  public boolean isNotify(final NotifyType type) {
-    switch (type) {
-      case NEW_CHANGES:
-        return notifyNewChanges;
-
-      case NEW_PATCHSETS:
-        return notifyNewPatchSets;
-
-      case ALL_COMMENTS:
-        return notifyAllComments;
-
-      case SUBMITTED_CHANGES:
-        return notifySubmittedChanges;
-
-      case ABANDONED_CHANGES:
-        return notifyAbandonedChanges;
-
-      case ALL:
-        break;
-    }
-    return false;
-  }
-
-  public void setNotify(final NotifyType type, final boolean v) {
-    switch (type) {
-      case NEW_CHANGES:
-        notifyNewChanges = v;
-        break;
-
-      case NEW_PATCHSETS:
-        notifyNewPatchSets = v;
-        break;
-
-      case ALL_COMMENTS:
-        notifyAllComments = v;
-        break;
-
-      case SUBMITTED_CHANGES:
-        notifySubmittedChanges = v;
-        break;
-
-      case ABANDONED_CHANGES:
-        notifyAbandonedChanges = v;
-        break;
-
-      case ALL:
-        notifyNewChanges = v;
-        notifyNewPatchSets = v;
-        notifyAllComments = v;
-        notifySubmittedChanges = v;
-        notifyAbandonedChanges = v;
-        break;
-    }
-  }
-}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountExternalIdAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountExternalIdAccess.java
index 97b8437..b930356 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountExternalIdAccess.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountExternalIdAccess.java
@@ -28,20 +28,9 @@
   @PrimaryKey("key")
   AccountExternalId get(AccountExternalId.Key key) throws OrmException;
 
-  @Query("WHERE key >= ? AND key <= ? ORDER BY key LIMIT ?")
-  ResultSet<AccountExternalId> suggestByKey(AccountExternalId.Key keyA,
-      AccountExternalId.Key keyB, int limit) throws OrmException;
-
   @Query("WHERE accountId = ?")
   ResultSet<AccountExternalId> byAccount(Account.Id id) throws OrmException;
 
-  @Query("WHERE emailAddress = ?")
-  ResultSet<AccountExternalId> byEmailAddress(String email) throws OrmException;
-
-  @Query("WHERE emailAddress >= ? AND emailAddress <= ? ORDER BY emailAddress LIMIT ?")
-  ResultSet<AccountExternalId> suggestByEmailAddress(String emailA,
-      String emailB, int limit) throws OrmException;
-
   @Query
   ResultSet<AccountExternalId> all() throws OrmException;
 }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountProjectWatchAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountProjectWatchAccess.java
deleted file mode 100644
index c6f4775..0000000
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountProjectWatchAccess.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2008 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.reviewdb.server;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-public interface AccountProjectWatchAccess extends
-    Access<AccountProjectWatch, AccountProjectWatch.Key> {
-  @Override
-  @PrimaryKey("key")
-  AccountProjectWatch get(AccountProjectWatch.Key key) throws OrmException;
-
-  @Query("WHERE key.accountId = ?")
-  ResultSet<AccountProjectWatch> byAccount(Account.Id id) throws OrmException;
-
-  @Query("WHERE key.projectName = ?")
-  ResultSet<AccountProjectWatch> byProject(Project.NameKey name) throws OrmException;
-}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDb.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDb.java
index c585ca5..9df3169 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDb.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDb.java
@@ -71,8 +71,7 @@
 
   // Deleted @Relation(id = 18)
 
-  @Relation(id = 19)
-  AccountProjectWatchAccess accountProjectWatches();
+  // Deleted @Relation(id = 19)
 
   // Deleted @Relation(id = 20)
 
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
index 6b25378..2bf47e5 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
@@ -109,11 +109,6 @@
   }
 
   @Override
-  public AccountProjectWatchAccess accountProjectWatches() {
-    return delegate.accountProjectWatches();
-  }
-
-  @Override
   public ChangeAccess changes() {
     return delegate.changes();
   }
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
index 2110295..deceab9 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
@@ -21,10 +21,6 @@
 CREATE INDEX account_external_ids_byAccount
 ON account_external_ids (account_id);
 
---    covers:             byEmailAddress, suggestByEmailAddress
-CREATE INDEX account_external_ids_byEmail
-ON account_external_ids (email_address);
-
 
 -- *********************************************************************
 -- AccountGroupMemberAccess
@@ -39,13 +35,6 @@
 CREATE INDEX account_group_id_byInclude
 ON account_group_by_id (include_uuid);
 
--- *********************************************************************
--- AccountProjectWatchAccess
---    @PrimaryKey covers: byAccount
---    covers:             byProject
-CREATE INDEX account_project_watches_byP
-ON account_project_watches (project_name);
-
 
 -- *********************************************************************
 -- ApprovalCategoryAccess
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
index 334b6c4..1ec8ea6 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
@@ -25,11 +25,6 @@
 ON account_external_ids (account_id)
 #
 
---    covers:             byEmailAddress, suggestByEmailAddress
-CREATE INDEX account_external_ids_byEmail
-ON account_external_ids (email_address)
-#
-
 
 -- *********************************************************************
 -- AccountGroupMemberAccess
@@ -47,14 +42,6 @@
 
 
 -- *********************************************************************
--- AccountProjectWatchAccess
---    @PrimaryKey covers: byAccount
---    covers:             byProject
-CREATE INDEX acc_project_watches_byProject
-ON account_project_watches (project_name)
-#
-
--- *********************************************************************
 -- ApprovalCategoryAccess
 --    too small to bother indexing
 
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
index bdceb7b..a11c86b 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
@@ -68,10 +68,6 @@
 CREATE INDEX account_external_ids_byAccount
 ON account_external_ids (account_id);
 
---    covers:             byEmailAddress, suggestByEmailAddress
-CREATE INDEX account_external_ids_byEmail
-ON account_external_ids (email_address);
-
 
 -- *********************************************************************
 -- AccountGroupMemberAccess
@@ -86,13 +82,6 @@
 CREATE INDEX account_group_id_byInclude
 ON account_group_by_id (include_uuid);
 
--- *********************************************************************
--- AccountProjectWatchAccess
---    @PrimaryKey covers: byAccount
---    covers:             byProject
-CREATE INDEX account_project_watches_byP
-ON account_project_watches (project_name);
-
 
 -- *********************************************************************
 -- ApprovalCategoryAccess
diff --git a/gerrit-server/BUILD b/gerrit-server/BUILD
index 6ca423f..bbe9653 100644
--- a/gerrit-server/BUILD
+++ b/gerrit-server/BUILD
@@ -193,7 +193,7 @@
 
 junit_tests(
     name = "query_tests",
-    size = "medium",
+    size = "large",
     srcs = QUERY_TESTS,
     visibility = ["//visibility:public"],
     deps = TESTUTIL_DEPS + [
@@ -208,7 +208,7 @@
 
 junit_tests(
     name = "server_tests",
-    size = "medium",
+    size = "large",
     srcs = glob(
         ["src/test/java/**/*.java"],
         exclude = TESTUTIL + PROLOG_TESTS + PROLOG_TEST_CASE + QUERY_TESTS,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
index 03b5d2b..a848c6c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
@@ -30,11 +30,7 @@
 import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.metrics.Timer0;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.account.AccountControl;
 import com.google.gerrit.server.account.AccountDirectory.FillOptions;
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.account.AccountState;
@@ -42,8 +38,6 @@
 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.AccountIndex;
-import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.ProjectControl;
@@ -102,47 +96,34 @@
     }
   }
 
-  private static final String MAX_SUFFIX = "\u9fa5";
   // Generate a candidate list at 3x the size of what the user wants to see to
   // give the ranking algorithm a good set of candidates it can work with
   private static final int CANDIDATE_LIST_MULTIPLIER = 3;
 
-  private final AccountCache accountCache;
-  private final AccountControl accountControl;
-  private final AccountIndexCollection accountIndexes;
   private final AccountLoader accountLoader;
   private final AccountQueryBuilder accountQueryBuilder;
   private final AccountQueryProcessor accountQueryProcessor;
   private final GroupBackend groupBackend;
   private final GroupMembers.Factory groupMembersFactory;
   private final Provider<CurrentUser> currentUser;
-  private final Provider<ReviewDb> dbProvider;
   private final ReviewerRecommender reviewerRecommender;
   private final Metrics metrics;
 
   @Inject
-  ReviewersUtil(AccountCache accountCache,
-      AccountControl.Factory accountControlFactory,
-      AccountIndexCollection accountIndexes,
-      AccountLoader.Factory accountLoaderFactory,
+  ReviewersUtil(AccountLoader.Factory accountLoaderFactory,
       AccountQueryBuilder accountQueryBuilder,
       AccountQueryProcessor accountQueryProcessor,
       GroupBackend groupBackend,
       GroupMembers.Factory groupMembersFactory,
       Provider<CurrentUser> currentUser,
-      Provider<ReviewDb> dbProvider,
       ReviewerRecommender reviewerRecommender,
       Metrics metrics) {
     Set<FillOptions> fillOptions = EnumSet.of(FillOptions.SECONDARY_EMAILS);
     fillOptions.addAll(AccountLoader.DETAILED_OPTIONS);
-    this.accountCache = accountCache;
-    this.accountControl = accountControlFactory.get();
-    this.accountIndexes = accountIndexes;
     this.accountLoader = accountLoaderFactory.create(fillOptions);
     this.accountQueryBuilder = accountQueryBuilder;
     this.accountQueryProcessor = accountQueryProcessor;
     this.currentUser = currentUser;
-    this.dbProvider = dbProvider;
     this.groupBackend = groupBackend;
     this.groupMembersFactory = groupMembersFactory;
     this.reviewerRecommender = reviewerRecommender;
@@ -189,90 +170,24 @@
   }
 
   private List<Account.Id> suggestAccounts(SuggestReviewers suggestReviewers,
-      VisibilityControl visibilityControl)
-      throws OrmException {
+      VisibilityControl visibilityControl) throws OrmException {
     try (Timer0.Context ctx = metrics.queryAccountsLatency.start()) {
-      AccountIndex searchIndex = accountIndexes.getSearchIndex();
-      if (searchIndex != null) {
-        return suggestAccountsFromIndex(suggestReviewers, visibilityControl);
-      }
-      return suggestAccountsFromDb(suggestReviewers, visibilityControl);
-    }
-  }
-
-  private List<Account.Id> suggestAccountsFromIndex(
-      SuggestReviewers suggestReviewers, VisibilityControl visibilityControl)
-          throws OrmException {
-    try {
-      Set<Account.Id> matches = new HashSet<>();
-      QueryResult<AccountState> result = accountQueryProcessor
-          .setLimit(suggestReviewers.getLimit() * CANDIDATE_LIST_MULTIPLIER)
-          .query(accountQueryBuilder.defaultQuery(suggestReviewers.getQuery()));
-      for (AccountState accountState : result.entities()) {
-        Account.Id id = accountState.getAccount().getId();
-        if (visibilityControl.isVisibleTo(id)) {
-          matches.add(id);
-        }
-      }
-      return new ArrayList<>(matches);
-    } catch (QueryParseException e) {
-      return ImmutableList.of();
-    }
-  }
-
-  private List<Account.Id> suggestAccountsFromDb(
-      SuggestReviewers suggestReviewers, VisibilityControl visibilityControl)
-          throws OrmException {
-    String query = suggestReviewers.getQuery();
-    int limit = suggestReviewers.getLimit() * CANDIDATE_LIST_MULTIPLIER;
-
-    String a = query;
-    String b = a + MAX_SUFFIX;
-
-    Set<Account.Id> r = new HashSet<>();
-
-    for (Account p : dbProvider.get().accounts()
-        .suggestByFullName(a, b, limit)) {
-      if (p.isActive()) {
-        addSuggestion(r, p.getId(), visibilityControl);
-      }
-    }
-
-    if (r.size() < limit) {
-      for (Account p : dbProvider.get().accounts()
-          .suggestByPreferredEmail(a, b, limit - r.size())) {
-        if (p.isActive()) {
-          addSuggestion(r, p.getId(), visibilityControl);
-        }
-      }
-    }
-
-    if (r.size() < limit) {
-      for (AccountExternalId e : dbProvider.get().accountExternalIds()
-          .suggestByEmailAddress(a, b, limit - r.size())) {
-        if (!r.contains(e.getAccountId())) {
-          Account p = accountCache.get(e.getAccountId()).getAccount();
-          if (p.isActive()) {
-            addSuggestion(r, p.getId(), visibilityControl);
+      try {
+        Set<Account.Id> matches = new HashSet<>();
+        QueryResult<AccountState> result = accountQueryProcessor
+            .setLimit(suggestReviewers.getLimit() * CANDIDATE_LIST_MULTIPLIER)
+            .query(accountQueryBuilder.defaultQuery(suggestReviewers.getQuery()));
+        for (AccountState accountState : result.entities()) {
+          Account.Id id = accountState.getAccount().getId();
+          if (visibilityControl.isVisibleTo(id)) {
+            matches.add(id);
           }
         }
+        return new ArrayList<>(matches);
+      } catch (QueryParseException e) {
+        return ImmutableList.of();
       }
     }
-    return new ArrayList<>(r);
-  }
-
-  private boolean addSuggestion(Set<Account.Id> map,
-      Account.Id account, VisibilityControl visibilityControl)
-      throws OrmException {
-    if (!map.contains(account)
-        // Can the suggestion see the change?
-        && visibilityControl.isVisibleTo(account)
-        // Can the current user see the account?
-        && accountControl.canSee(account)) {
-      map.add(account);
-      return true;
-    }
-    return false;
   }
 
   private List<Account.Id> recommendAccounts(ChangeNotes changeNotes,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java
index 0856616..2ddea85d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Inject;
@@ -87,15 +86,12 @@
 
   static class Loader extends CacheLoader<String, Set<Account.Id>> {
     private final SchemaFactory<ReviewDb> schema;
-    private final AccountIndexCollection accountIndexes;
     private final Provider<InternalAccountQuery> accountQueryProvider;
 
     @Inject
     Loader(SchemaFactory<ReviewDb> schema,
-        AccountIndexCollection accountIndexes,
         Provider<InternalAccountQuery> accountQueryProvider) {
       this.schema = schema;
-      this.accountIndexes = accountIndexes;
       this.accountQueryProvider = accountQueryProvider;
     }
 
@@ -106,18 +102,11 @@
         for (Account a : db.accounts().byPreferredEmail(email)) {
           r.add(a.getId());
         }
-        if (accountIndexes.getSearchIndex() != null) {
-          for (AccountState accountState : accountQueryProvider.get()
-              .byExternalId(
-                  (new AccountExternalId.Key(AccountExternalId.SCHEME_MAILTO,
-                      email)).get())) {
-            r.add(accountState.getAccount().getId());
-          }
-        } else {
-          for (AccountExternalId a : db.accountExternalIds()
-              .byEmailAddress(email)) {
-            r.add(a.getAccountId());
-          }
+        for (AccountState accountState : accountQueryProvider.get()
+            .byExternalId(
+                (new AccountExternalId.Key(AccountExternalId.SCHEME_MAILTO,
+                    email)).get())) {
+          r.add(accountState.getAccount().getId());
         }
         return ImmutableSet.copyOf(r);
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
index 846b44b..0ba84f3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -23,12 +23,10 @@
 import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
 import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.index.account.AccountIndexer;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
 import com.google.gwtorm.server.OrmException;
@@ -41,7 +39,6 @@
 import com.google.inject.name.Named;
 
 import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -50,7 +47,6 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -158,7 +154,6 @@
     private final GroupCache groupCache;
     private final GeneralPreferencesLoader loader;
     private final LoadingCache<String, Optional<Account.Id>> byName;
-    private final boolean readFromGit;
     private final Provider<WatchConfig.Accessor> watchConfig;
 
     @Inject
@@ -167,14 +162,11 @@
         GeneralPreferencesLoader loader,
         @Named(BYUSER_NAME) LoadingCache<String,
             Optional<Account.Id>> byUsername,
-        @GerritServerConfig Config cfg,
         Provider<WatchConfig.Accessor> watchConfig) {
       this.schema = sf;
       this.groupCache = groupCache;
       this.loader = loader;
       this.byName = byUsername;
-      this.readFromGit =
-          cfg.getBoolean("user", null, "readProjectWatchesFromGit", false);
       this.watchConfig = watchConfig;
     }
 
@@ -220,46 +212,28 @@
         account.setGeneralPreferences(GeneralPreferencesInfo.defaults());
       }
 
-      Map<ProjectWatchKey, Set<NotifyType>> projectWatches =
-          readFromGit
-              ? watchConfig.get().getProjectWatches(who)
-              : GetWatchedProjects.readProjectWatchesFromDb(db, who);
-
       return new AccountState(account, internalGroups, externalIds,
-          projectWatches);
+          watchConfig.get().getProjectWatches(who));
     }
   }
 
   static class ByNameLoader extends CacheLoader<String, Optional<Account.Id>> {
-    private final SchemaFactory<ReviewDb> schema;
-    private final AccountIndexCollection accountIndexes;
     private final Provider<InternalAccountQuery> accountQueryProvider;
 
     @Inject
-    ByNameLoader(SchemaFactory<ReviewDb> sf,
-        AccountIndexCollection accountIndexes,
-        Provider<InternalAccountQuery> accountQueryProvider) {
-      this.schema = sf;
-      this.accountIndexes = accountIndexes;
+    ByNameLoader(Provider<InternalAccountQuery> accountQueryProvider) {
       this.accountQueryProvider = accountQueryProvider;
     }
 
     @Override
     public Optional<Account.Id> load(String username) throws Exception {
-        AccountExternalId.Key key = new AccountExternalId.Key( //
-            AccountExternalId.SCHEME_USERNAME, //
-            username);
-      if (accountIndexes.getSearchIndex() != null) {
-        AccountState accountState =
-            accountQueryProvider.get().oneByExternalId(key.get());
-        return Optional.ofNullable(accountState)
-            .map(s -> s.getAccount().getId());
-      }
-
-      try (ReviewDb db = schema.open()) {
-        return Optional.ofNullable(db.accountExternalIds().get(key))
-            .map(AccountExternalId::getAccountId);
-      }
+      AccountExternalId.Key key = new AccountExternalId.Key( //
+          AccountExternalId.SCHEME_USERNAME, //
+          username);
+      AccountState accountState =
+          accountQueryProvider.get().oneByExternalId(key.get());
+      return Optional.ofNullable(accountState)
+          .map(s -> s.getAccount().getId());
     }
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
index 3e5a046..7236604 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
@@ -28,7 +28,6 @@
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
 import com.google.gwtorm.server.OrmException;
@@ -45,6 +44,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /** Tracks authentication related details for user accounts. */
@@ -62,7 +62,6 @@
   private final ProjectCache projectCache;
   private final AtomicBoolean awaitsFirstAccountCheck;
   private final AuditService auditService;
-  private final AccountIndexCollection accountIndexes;
   private final Provider<InternalAccountQuery> accountQueryProvider;
 
   @Inject
@@ -74,7 +73,6 @@
       ChangeUserName.Factory changeUserNameFactory,
       ProjectCache projectCache,
       AuditService auditService,
-      AccountIndexCollection accountIndexes,
       Provider<InternalAccountQuery> accountQueryProvider) {
     this.schema = schema;
     this.byIdCache = byIdCache;
@@ -85,28 +83,20 @@
     this.projectCache = projectCache;
     this.awaitsFirstAccountCheck = new AtomicBoolean(true);
     this.auditService = auditService;
-    this.accountIndexes = accountIndexes;
     this.accountQueryProvider = accountQueryProvider;
   }
 
   /**
-   * @return user identified by this external identity string, or null.
+   * @return user identified by this external identity string
    */
-  public Account.Id lookup(String externalId) throws AccountException {
+  public Optional<Account.Id> lookup(String externalId)
+      throws AccountException {
     try {
-      if (accountIndexes.getSearchIndex() != null) {
-        AccountState accountState =
-            accountQueryProvider.get().oneByExternalId(externalId);
-        return accountState != null
-            ? accountState.getAccount().getId()
-            : null;
-      }
-
-      try (ReviewDb db = schema.open()) {
-        AccountExternalId ext =
-            db.accountExternalIds().get(new AccountExternalId.Key(externalId));
-        return ext != null ? ext.getAccountId() : null;
-      }
+      AccountState accountState =
+          accountQueryProvider.get().oneByExternalId(externalId);
+      return accountState != null
+          ? Optional.of(accountState.getAccount().getId())
+          : Optional.empty();
     } catch (OrmException e) {
       throw new AccountException("Cannot lookup account " + externalId, e);
     }
@@ -126,7 +116,7 @@
     try {
       try (ReviewDb db = schema.open()) {
         AccountExternalId.Key key = id(who);
-        AccountExternalId id = getAccountExternalId(db, key);
+        AccountExternalId id = getAccountExternalId(key);
         if (id == null) {
           // New account, automatically create and return.
           //
@@ -148,37 +138,18 @@
     }
   }
 
-  private AccountExternalId getAccountExternalId(ReviewDb db,
-      AccountExternalId.Key key) throws OrmException {
-    if (accountIndexes.getSearchIndex() != null) {
-      AccountState accountState =
-          accountQueryProvider.get().oneByExternalId(key.get());
-      if (accountState != null) {
-        for (AccountExternalId extId : accountState.getExternalIds()) {
-          if (extId.getKey().equals(key)) {
-            return extId;
-          }
-        }
-      }
-      return null;
-    }
-
-    // We don't have at the moment an account_by_external_id cache
-    // but by using the accounts cache we get the list of external_ids
-    // without having to query the DB every time
-    if (key.getScheme().equals(AccountExternalId.SCHEME_GERRIT)
-        || key.getScheme().equals(AccountExternalId.SCHEME_USERNAME)) {
-      AccountState state = byIdCache.getByUsername(
-          key.get().substring(key.getScheme().length()));
-      if (state != null) {
-        for (AccountExternalId accountExternalId : state.getExternalIds()) {
-          if (accountExternalId.getKey().equals(key)) {
-            return accountExternalId;
-          }
+  private AccountExternalId getAccountExternalId(AccountExternalId.Key key)
+      throws OrmException {
+    AccountState accountState =
+        accountQueryProvider.get().oneByExternalId(key.get());
+    if (accountState != null) {
+      for (AccountExternalId extId : accountState.getExternalIds()) {
+        if (extId.getKey().equals(key)) {
+          return extId;
         }
       }
     }
-    return db.accountExternalIds().get(key);
+    return null;
   }
 
   private void update(ReviewDb db, AuthRequest who, AccountExternalId extId)
@@ -386,7 +357,7 @@
       throws AccountException, OrmException, IOException {
     try (ReviewDb db = schema.open()) {
       AccountExternalId.Key key = id(who);
-      AccountExternalId extId = getAccountExternalId(db, key);
+      AccountExternalId extId = getAccountExternalId(key);
       if (extId != null) {
         if (!extId.getAccountId().equals(to)) {
           throw new AccountException("Identity in use by another account");
@@ -470,7 +441,7 @@
       throws AccountException, OrmException, IOException {
     try (ReviewDb db = schema.open()) {
       AccountExternalId.Key key = id(who);
-      AccountExternalId extId = getAccountExternalId(db, key);
+      AccountExternalId extId = getAccountExternalId(key);
       if (extId != null) {
         if (!extId.getAccountId().equals(from)) {
           throw new AccountException(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResolver.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResolver.java
index b400eb7..f3356e5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResolver.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResolver.java
@@ -17,9 +17,7 @@
 import static java.util.stream.Collectors.toSet;
 
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -38,19 +36,16 @@
   private final Realm realm;
   private final AccountByEmailCache byEmail;
   private final AccountCache byId;
-  private final AccountIndexCollection accountIndexes;
   private final Provider<InternalAccountQuery> accountQueryProvider;
 
   @Inject
   AccountResolver(Realm realm,
       AccountByEmailCache byEmail,
       AccountCache byId,
-      AccountIndexCollection accountIndexes,
       Provider<InternalAccountQuery> accountQueryProvider) {
     this.realm = realm;
     this.byEmail = byEmail;
     this.byId = byId;
-    this.accountIndexes = accountIndexes;
     this.accountQueryProvider = accountQueryProvider;
   }
 
@@ -183,42 +178,15 @@
       return Collections.singleton(id);
     }
 
-    if (accountIndexes.getSearchIndex() != null) {
-      List<AccountState> m = accountQueryProvider.get().byFullName(nameOrEmail);
-      if (m.size() == 1) {
-        return Collections.singleton(m.get(0).getAccount().getId());
-      }
-
-      // At this point we have no clue. Just perform a whole bunch of suggestions
-      // and pray we come up with a reasonable result list.
-      return accountQueryProvider.get().byDefault(nameOrEmail).stream()
-          .map(a -> a.getAccount().getId())
-          .collect(toSet());
-    }
-
-    List<Account> m = db.accounts().byFullName(nameOrEmail).toList();
+    List<AccountState> m = accountQueryProvider.get().byFullName(nameOrEmail);
     if (m.size() == 1) {
-      return Collections.singleton(m.get(0).getId());
+      return Collections.singleton(m.get(0).getAccount().getId());
     }
 
     // At this point we have no clue. Just perform a whole bunch of suggestions
     // and pray we come up with a reasonable result list.
-    Set<Account.Id> result = new HashSet<>();
-    String a = nameOrEmail;
-    String b = nameOrEmail + "\u9fa5";
-    for (Account act : db.accounts().suggestByFullName(a, b, 10)) {
-      result.add(act.getId());
-    }
-    for (AccountExternalId extId : db.accountExternalIds()
-        .suggestByKey(
-            new AccountExternalId.Key(AccountExternalId.SCHEME_USERNAME, a),
-            new AccountExternalId.Key(AccountExternalId.SCHEME_USERNAME, b), 10)) {
-      result.add(extId.getAccountId());
-    }
-    for (AccountExternalId extId : db.accountExternalIds()
-        .suggestByEmailAddress(a, b, 10)) {
-      result.add(extId.getAccountId());
-    }
-    return result;
+    return accountQueryProvider.get().byDefault(nameOrEmail).stream()
+        .map(a -> a.getAccount().getId())
+        .collect(toSet());
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java
index ed99266..827bdee 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java
@@ -24,9 +24,9 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.server.CurrentUser.PropertyKey;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
 
 import java.util.Collection;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java
index 0e9bc2e..990a563 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java
@@ -22,13 +22,10 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
 import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -36,24 +33,19 @@
 import org.eclipse.jgit.errors.ConfigInvalidException;
 
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.List;
 
 @Singleton
 public class DeleteWatchedProjects
     implements RestModifyView<AccountResource, List<ProjectWatchInfo>> {
-  private final Provider<ReviewDb> dbProvider;
   private final Provider<IdentifiedUser> self;
   private final AccountCache accountCache;
   private final WatchConfig.Accessor watchConfig;
 
   @Inject
-  DeleteWatchedProjects(Provider<ReviewDb> dbProvider,
-      Provider<IdentifiedUser> self,
+  DeleteWatchedProjects(Provider<IdentifiedUser> self,
       AccountCache accountCache,
       WatchConfig.Accessor watchConfig) {
-    this.dbProvider = dbProvider;
     this.self = self;
     this.accountCache = accountCache;
     this.watchConfig = watchConfig;
@@ -73,42 +65,12 @@
     }
 
     Account.Id accountId = rsrc.getUser().getAccountId();
-    deleteFromDb(accountId, input);
-    deleteFromGit(accountId, input);
-    accountCache.evict(accountId);
-    return Response.none();
-  }
-
-  private void deleteFromDb(Account.Id accountId, List<ProjectWatchInfo> input)
-      throws OrmException, IOException {
-    ResultSet<AccountProjectWatch> watchedProjects =
-        dbProvider.get().accountProjectWatches().byAccount(accountId);
-    HashMap<AccountProjectWatch.Key, AccountProjectWatch> watchedProjectsMap =
-        new HashMap<>();
-    for (AccountProjectWatch watchedProject : watchedProjects) {
-      watchedProjectsMap.put(watchedProject.getKey(), watchedProject);
-    }
-
-    List<AccountProjectWatch> watchesToDelete = new LinkedList<>();
-    for (ProjectWatchInfo projectInfo : input) {
-      AccountProjectWatch.Key key = new AccountProjectWatch.Key(accountId,
-          new Project.NameKey(projectInfo.project), projectInfo.filter);
-      if (watchedProjectsMap.containsKey(key)) {
-        watchesToDelete.add(watchedProjectsMap.get(key));
-      }
-    }
-    if (!watchesToDelete.isEmpty()) {
-      dbProvider.get().accountProjectWatches().delete(watchesToDelete);
-      accountCache.evict(accountId);
-    }
-  }
-
-  private void deleteFromGit(Account.Id accountId, List<ProjectWatchInfo> input)
-      throws IOException, ConfigInvalidException {
     watchConfig.deleteProjectWatches(
         accountId,
         input.stream().map(w -> ProjectWatchKey.create(
                 new Project.NameKey(w.project), w.filter))
             .collect(toList()));
+    accountCache.evict(accountId);
+    return Response.none();
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetWatchedProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetWatchedProjects.java
index 3748e17..7f4a873 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetWatchedProjects.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetWatchedProjects.java
@@ -20,25 +20,19 @@
 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.reviewdb.client.AccountProjectWatch;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
-import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
 import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
 
 import java.io.IOException;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -47,20 +41,13 @@
 @Singleton
 public class GetWatchedProjects implements RestReadView<AccountResource> {
 
-  private final Provider<ReviewDb> dbProvider;
   private final Provider<IdentifiedUser> self;
-  private final boolean readFromGit;
   private final WatchConfig.Accessor watchConfig;
 
   @Inject
-  public GetWatchedProjects(Provider<ReviewDb> dbProvider,
-      Provider<IdentifiedUser> self,
-      @GerritServerConfig Config cfg,
+  public GetWatchedProjects(Provider<IdentifiedUser> self,
       WatchConfig.Accessor watchConfig) {
-    this.dbProvider = dbProvider;
     this.self = self;
-    this.readFromGit =
-        cfg.getBoolean("user", null, "readProjectWatchesFromGit", false);
     this.watchConfig = watchConfig;
   }
 
@@ -73,14 +60,9 @@
           + "of other users");
     }
     Account.Id accountId = rsrc.getUser().getAccountId();
-    Map<ProjectWatchKey, Set<NotifyType>> projectWatches =
-        readFromGit
-            ? watchConfig.getProjectWatches(accountId)
-            : readProjectWatchesFromDb(dbProvider.get(), accountId);
-
     List<ProjectWatchInfo> projectWatchInfos = new LinkedList<>();
-    for (Map.Entry<ProjectWatchKey, Set<NotifyType>> e : projectWatches
-        .entrySet()) {
+    for (Map.Entry<ProjectWatchKey, Set<NotifyType>> e : watchConfig
+        .getProjectWatches(accountId).entrySet()) {
       ProjectWatchInfo pwi = new ProjectWatchInfo();
       pwi.filter = e.getKey().filter();
       pwi.project = e.getKey().project().get();
@@ -112,22 +94,4 @@
   private static Boolean toBoolean(boolean value) {
     return value ? true : null;
   }
-
-  public static Map<ProjectWatchKey, Set<NotifyType>> readProjectWatchesFromDb(
-      ReviewDb db, Account.Id who) throws OrmException {
-    Map<ProjectWatchKey, Set<NotifyType>> projectWatches =
-        new HashMap<>();
-    for (AccountProjectWatch apw : db.accountProjectWatches().byAccount(who)) {
-      ProjectWatchKey key =
-          ProjectWatchKey.create(apw.getProjectNameKey(), apw.getFilter());
-      Set<NotifyType> notifyValues = EnumSet.noneOf(NotifyType.class);
-      for (NotifyType notifyType : NotifyType.values()) {
-        if (apw.isNotify(notifyType)) {
-          notifyValues.add(notifyType);
-        }
-      }
-      projectWatches.put(key, notifyValues);
-    }
-    return projectWatches;
-  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PostWatchedProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PostWatchedProjects.java
index d54ec50..92fe837 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PostWatchedProjects.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PostWatchedProjects.java
@@ -21,11 +21,8 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
 import com.google.gerrit.server.project.ProjectsCollection;
 import com.google.gwtorm.server.OrmException;
@@ -38,8 +35,6 @@
 import java.io.IOException;
 import java.util.EnumSet;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -47,7 +42,6 @@
 @Singleton
 public class PostWatchedProjects
     implements RestModifyView<AccountResource, List<ProjectWatchInfo>> {
-  private final Provider<ReviewDb> dbProvider;
   private final Provider<IdentifiedUser> self;
   private final GetWatchedProjects getWatchedProjects;
   private final ProjectsCollection projectsCollection;
@@ -55,13 +49,11 @@
   private final WatchConfig.Accessor watchConfig;
 
   @Inject
-  public PostWatchedProjects(Provider<ReviewDb> dbProvider,
-      Provider<IdentifiedUser> self,
+  public PostWatchedProjects(Provider<IdentifiedUser> self,
       GetWatchedProjects getWatchedProjects,
       ProjectsCollection projectsCollection,
       AccountCache accountCache,
       WatchConfig.Accessor watchConfig) {
-    this.dbProvider = dbProvider;
     this.self = self;
     this.getWatchedProjects = getWatchedProjects;
     this.projectsCollection = projectsCollection;
@@ -78,52 +70,11 @@
       throw new AuthException("not allowed to edit project watches");
     }
     Account.Id accountId = rsrc.getUser().getAccountId();
-    updateInDb(accountId, input);
-    updateInGit(accountId, input);
+    watchConfig.upsertProjectWatches(accountId, asMap(input));
     accountCache.evict(accountId);
     return getWatchedProjects.apply(rsrc);
   }
 
-  private void updateInDb(Account.Id accountId, List<ProjectWatchInfo> input)
-      throws BadRequestException, UnprocessableEntityException, IOException,
-      OrmException {
-    Set<AccountProjectWatch.Key> keys = new HashSet<>();
-    List<AccountProjectWatch> watchedProjects = new LinkedList<>();
-    for (ProjectWatchInfo a : input) {
-      if (a.project == null) {
-        throw new BadRequestException("project name must be specified");
-      }
-
-      Project.NameKey projectKey =
-          projectsCollection.parse(a.project).getNameKey();
-      AccountProjectWatch.Key key =
-          new AccountProjectWatch.Key(accountId, projectKey, a.filter);
-      if (!keys.add(key)) {
-        throw new BadRequestException("duplicate entry for project "
-            + format(key.getProjectName().get(), key.getFilter().get()));
-      }
-      AccountProjectWatch apw = new AccountProjectWatch(key);
-      apw.setNotify(AccountProjectWatch.NotifyType.ABANDONED_CHANGES,
-          toBoolean(a.notifyAbandonedChanges));
-      apw.setNotify(AccountProjectWatch.NotifyType.ALL_COMMENTS,
-          toBoolean(a.notifyAllComments));
-      apw.setNotify(AccountProjectWatch.NotifyType.NEW_CHANGES,
-          toBoolean(a.notifyNewChanges));
-      apw.setNotify(AccountProjectWatch.NotifyType.NEW_PATCHSETS,
-          toBoolean(a.notifyNewPatchSets));
-      apw.setNotify(AccountProjectWatch.NotifyType.SUBMITTED_CHANGES,
-          toBoolean(a.notifySubmittedChanges));
-      watchedProjects.add(apw);
-    }
-    dbProvider.get().accountProjectWatches().upsert(watchedProjects);
-  }
-
-  private void updateInGit(Account.Id accountId, List<ProjectWatchInfo> input)
-      throws BadRequestException, UnprocessableEntityException, IOException,
-      ConfigInvalidException {
-    watchConfig.upsertProjectWatches(accountId, asMap(input));
-  }
-
   private Map<ProjectWatchKey, Set<NotifyType>> asMap(
       List<ProjectWatchInfo> input) throws BadRequestException,
           UnprocessableEntityException, IOException {
@@ -168,7 +119,7 @@
 
   private static String format(String project, String filter) {
     return project
-        + (filter != null && !AccountProjectWatch.FILTER_ALL.equals(filter)
+        + (filter != null && !WatchConfig.FILTER_ALL.equals(filter)
             ? " and filter " + filter
             : "");
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/QueryAccounts.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/QueryAccounts.java
index 000637a..cef829f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/QueryAccounts.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/QueryAccounts.java
@@ -23,13 +23,9 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountExternalId;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.account.AccountDirectory.FillOptions;
 import com.google.gerrit.server.api.accounts.AccountInfoComparator;
 import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.index.account.AccountIndex;
-import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.query.Predicate;
 import com.google.gerrit.server.query.QueryParseException;
 import com.google.gerrit.server.query.QueryResult;
@@ -43,7 +39,6 @@
 
 import java.util.Collections;
 import java.util.EnumSet;
-import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -51,15 +46,10 @@
 
 public class QueryAccounts implements RestReadView<TopLevelResource> {
   private static final int MAX_SUGGEST_RESULTS = 100;
-  private static final String MAX_SUFFIX = "\u9fa5";
 
-  private final AccountControl accountControl;
   private final AccountLoader.Factory accountLoaderFactory;
-  private final AccountCache accountCache;
-  private final AccountIndexCollection indexes;
   private final AccountQueryBuilder queryBuilder;
   private final AccountQueryProcessor queryProcessor;
-  private final ReviewDb db;
   private final boolean suggestConfig;
   private final int suggestFrom;
 
@@ -110,21 +100,13 @@
   }
 
   @Inject
-  QueryAccounts(AccountControl.Factory accountControlFactory,
-      AccountLoader.Factory accountLoaderFactory,
-      AccountCache accountCache,
-      AccountIndexCollection indexes,
+  QueryAccounts(AccountLoader.Factory accountLoaderFactory,
       AccountQueryBuilder queryBuilder,
       AccountQueryProcessor queryProcessor,
-      ReviewDb db,
       @GerritServerConfig Config cfg) {
-    this.accountControl = accountControlFactory.get();
     this.accountLoaderFactory = accountLoaderFactory;
-    this.accountCache = accountCache;
-    this.indexes = indexes;
     this.queryBuilder = queryBuilder;
     this.queryProcessor = queryProcessor;
-    this.db = db;
     this.suggestFrom = cfg.getInt("suggest", null, "from", 0);
     this.options = EnumSet.noneOf(ListAccountsOption.class);
 
@@ -169,22 +151,6 @@
     }
     accountLoader = accountLoaderFactory.create(fillOptions);
 
-    AccountIndex searchIndex = indexes.getSearchIndex();
-    if (searchIndex != null) {
-      return queryFromIndex();
-    }
-
-    if (!suggest) {
-      throw new MethodNotAllowedException();
-    }
-    if (start != null) {
-      throw new MethodNotAllowedException("option start not allowed");
-    }
-    return queryFromDb();
-  }
-
-  public List<AccountInfo> queryFromIndex()
-      throws BadRequestException, MethodNotAllowedException, OrmException {
     if (queryProcessor.isDisabled()) {
       throw new MethodNotAllowedException("query disabled");
     }
@@ -223,57 +189,4 @@
       throw new BadRequestException(e.getMessage());
     }
   }
-
-  public List<AccountInfo> queryFromDb() throws OrmException {
-    String a = query;
-    String b = a + MAX_SUFFIX;
-
-    Map<Account.Id, AccountInfo> matches = new LinkedHashMap<>();
-    Map<Account.Id, String> queryEmail = new HashMap<>();
-
-    for (Account p : db.accounts().suggestByFullName(a, b, suggestLimit)) {
-      addSuggestion(matches, p);
-    }
-    if (matches.size() < suggestLimit) {
-      for (Account p : db.accounts()
-          .suggestByPreferredEmail(a, b, suggestLimit - matches.size())) {
-        addSuggestion(matches, p);
-      }
-    }
-    if (matches.size() < suggestLimit) {
-      for (AccountExternalId e : db.accountExternalIds()
-          .suggestByEmailAddress(a, b, suggestLimit - matches.size())) {
-        if (addSuggestion(matches, e.getAccountId())) {
-          queryEmail.put(e.getAccountId(), e.getEmailAddress());
-        }
-      }
-    }
-
-    accountLoader.fill();
-    for (Map.Entry<Account.Id, String> p : queryEmail.entrySet()) {
-      AccountInfo info = matches.get(p.getKey());
-      if (info != null) {
-        info.email = p.getValue();
-      }
-    }
-
-    return AccountInfoComparator.ORDER_NULLS_LAST.sortedCopy(matches.values());
-  }
-
-  private boolean addSuggestion(Map<Account.Id, AccountInfo> map, Account a) {
-    if (!a.isActive()) {
-      return false;
-    }
-    Account.Id id = a.getId();
-    if (!map.containsKey(id) && accountControl.canSee(id)) {
-      map.put(id, accountLoader.get(id));
-      return true;
-    }
-    return false;
-  }
-
-  private boolean addSuggestion(Map<Account.Id, AccountInfo> map, Account.Id id) {
-    Account a = accountCache.get(id).getAccount();
-    return addSuggestion(map, a);
-  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/WatchConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/WatchConfig.java
index c43e4da..ea53d14 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/WatchConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/WatchConfig.java
@@ -30,8 +30,6 @@
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.IdentifiedUser;
@@ -177,6 +175,19 @@
     public abstract @Nullable String filter();
   }
 
+  public enum NotifyType {
+    // sort by name, except 'ALL' which should stay last
+    ABANDONED_CHANGES,
+    ALL_COMMENTS,
+    NEW_CHANGES,
+    NEW_PATCHSETS,
+    SUBMITTED_CHANGES,
+
+    ALL
+  }
+
+  public static final String FILTER_ALL = "*";
+
   public static final String WATCH_CONFIG = "watch.config";
   public static final String PROJECT = "project";
   public static final String KEY_NOTIFY = "notify";
@@ -316,7 +327,7 @@
         return null;
       }
       String filter = notifyValue.substring(0, i).trim();
-      if (filter.isEmpty() || AccountProjectWatch.FILTER_ALL.equals(filter)) {
+      if (filter.isEmpty() || FILTER_ALL.equals(filter)) {
         filter = null;
       }
 
@@ -353,7 +364,7 @@
     public String toString() {
       List<NotifyType> notifyTypes = new ArrayList<>(notifyTypes());
       StringBuilder notifyValue = new StringBuilder();
-      notifyValue.append(firstNonNull(filter(), AccountProjectWatch.FILTER_ALL))
+      notifyValue.append(firstNonNull(filter(), FILTER_ALL))
           .append(" [");
       Joiner.on(", ").appendTo(notifyValue, notifyTypes);
       notifyValue.append("]");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index 973e97a..c89f0df 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -245,7 +245,7 @@
     try (BatchUpdate bu = batchUpdateFactory.create(db.get(),
           revision.getChange().getProject(), revision.getUser(), ts)) {
       Account.Id id = bu.getUser().getAccountId();
-      boolean ccOrReviewer = input.labels != null;
+      boolean ccOrReviewer = input.labels != null && !input.labels.isEmpty();
 
       if (!ccOrReviewer) {
         // Check if user was already CCed or reviewing prior to this review.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/NotifyConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/NotifyConfig.java
index d8ed075..b04b0f7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/NotifyConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/NotifyConfig.java
@@ -16,7 +16,7 @@
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.mail.Address;
 
 import java.util.EnumSet;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
index f3ed9f2..c101bbb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
@@ -45,11 +45,11 @@
 import com.google.gerrit.extensions.client.ProjectState;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.config.PluginConfig;
 import com.google.gerrit.server.mail.Address;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/Index.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/Index.java
index 84eb3bb..887715f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/Index.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/Index.java
@@ -37,6 +37,9 @@
   /** @return the schema version used by this index. */
   Schema<V> getSchema();
 
+  /** Stop and await termination of all executor threads */
+  default void stop() {}
+
   /** Close this index. */
   void close();
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexCollection.java
index ca0fab2..61c4675 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexCollection.java
@@ -98,6 +98,7 @@
     }
     for (I write : writeIndexes) {
       if (write != read) {
+        write.stop();
         write.close();
       }
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/AbandonedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/AbandonedSender.java
index 254d3f1..8ddfe5c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/AbandonedSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/AbandonedSender.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.gerrit.common.errors.EmailException;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java
index e0e48ce..4d8c56f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
 import com.google.gerrit.reviewdb.client.Patch;
@@ -30,6 +29,7 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.mail.send.ProjectWatch.Watchers;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
 import com.google.gerrit.server.patch.PatchList;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CommentSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CommentSender.java
index a4d0e24..a870d7a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CommentSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CommentSender.java
@@ -20,13 +20,13 @@
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.common.errors.NoSuchEntityException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.Patch;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RobotComment;
 import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.mail.MailUtil;
 import com.google.gerrit.server.patch.PatchFile;
 import com.google.gerrit.server.patch.PatchList;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CreateChangeSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
index d01e89d..51f8cef 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
@@ -18,9 +18,9 @@
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.mail.send.ProjectWatch.Watchers;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
index c456828..dafa786 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
@@ -17,9 +17,9 @@
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
index f8d1745..a9e8cc4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.gerrit.common.errors.EmailException;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/EmailArguments.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/EmailArguments.java
index 818023f..c870474 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/EmailArguments.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/EmailArguments.java
@@ -32,7 +32,6 @@
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.mail.EmailSettings;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.patch.PatchListCache;
@@ -82,7 +81,6 @@
   final EmailSettings settings;
   final DynamicSet<OutgoingEmailValidationListener> outgoingEmailValidationListeners;
   final StarredChangesUtil starredChangesUtil;
-  final AccountIndexCollection accountIndexes;
   final Provider<InternalAccountQuery> accountQueryProvider;
 
   @Inject
@@ -111,7 +109,6 @@
       SitePaths site,
       DynamicSet<OutgoingEmailValidationListener> outgoingEmailValidationListeners,
       StarredChangesUtil starredChangesUtil,
-      AccountIndexCollection accountIndexes,
       Provider<InternalAccountQuery> accountQueryProvider) {
     this.server = server;
     this.projectCache = projectCache;
@@ -141,7 +138,6 @@
     this.site = site;
     this.outgoingEmailValidationListeners = outgoingEmailValidationListeners;
     this.starredChangesUtil = starredChangesUtil;
-    this.accountIndexes = accountIndexes;
     this.accountQueryProvider = accountQueryProvider;
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/MergedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/MergedSender.java
index a5c8940..38a2d5b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/MergedSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/MergedSender.java
@@ -21,10 +21,10 @@
 import com.google.gerrit.common.data.LabelValue;
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/NotificationEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/NotificationEmail.java
index c08f24d..7d10a3f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/NotificationEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/NotificationEmail.java
@@ -18,8 +18,8 @@
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.mail.Address;
 import com.google.gerrit.server.mail.send.ProjectWatch.Watchers;
 import com.google.gwtorm.server.OrmException;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ProjectWatch.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ProjectWatch.java
index 3a43691..2269b66 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ProjectWatch.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ProjectWatch.java
@@ -21,13 +21,12 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
 import com.google.gerrit.server.git.NotifyConfig;
 import com.google.gerrit.server.mail.Address;
@@ -66,32 +65,6 @@
 
   /** Returns all watchers that are relevant */
   public final Watchers getWatchers(NotifyType type) throws OrmException {
-    Watchers matching;
-    if (args.accountIndexes.getSearchIndex() != null) {
-      matching = getWatchersFromIndex(type);
-    } else {
-      matching = getWatchersFromDb(type);
-    }
-
-    for (ProjectState state : projectState.tree()) {
-      for (NotifyConfig nc : state.getConfig().getNotifyConfigs()) {
-        if (nc.isNotify(type)) {
-          try {
-            add(matching, nc);
-          } catch (QueryParseException e) {
-            log.warn("Project {} has invalid notify {} filter \"{}\": {}",
-                state.getProject().getName(), nc.getName(),
-                nc.getFilter(), e.getMessage());
-          }
-        }
-      }
-    }
-
-    return matching;
-  }
-
-  private Watchers getWatchersFromIndex(NotifyType type)
-      throws OrmException {
     Watchers matching = new Watchers();
     Set<Account.Id> projectWatchers = new HashSet<>();
 
@@ -120,28 +93,21 @@
         }
       }
     }
-    return matching;
-  }
 
-  private Watchers getWatchersFromDb(NotifyType type)
-      throws OrmException {
-    Watchers matching = new Watchers();
-    Set<Account.Id> projectWatchers = new HashSet<>();
-
-    for (AccountProjectWatch w : args.db.get().accountProjectWatches()
-        .byProject(project)) {
-      if (add(matching, w, type)) {
-        // We only want to prevent matching All-Projects if this filter hits
-        projectWatchers.add(w.getAccountId());
+    for (ProjectState state : projectState.tree()) {
+      for (NotifyConfig nc : state.getConfig().getNotifyConfigs()) {
+        if (nc.isNotify(type)) {
+          try {
+            add(matching, nc);
+          } catch (QueryParseException e) {
+            log.warn("Project {} has invalid notify {} filter \"{}\": {}",
+                state.getProject().getName(), nc.getName(),
+                nc.getFilter(), e.getMessage());
+          }
+        }
       }
     }
 
-    for (AccountProjectWatch w : args.db.get().accountProjectWatches()
-        .byProject(args.allProjectsName)) {
-      if (!projectWatchers.contains(w.getAccountId())) {
-        add(matching, w, type);
-      }
-    }
     return matching;
   }
 
@@ -240,25 +206,6 @@
     return false;
   }
 
-  private boolean add(Watchers matching, AccountProjectWatch w, NotifyType type)
-      throws OrmException {
-    IdentifiedUser user = args.identifiedUserFactory.create(w.getAccountId());
-
-    try {
-      if (filterMatch(user, w.getFilter())) {
-        // If we are set to notify on this type, add the user.
-        // Otherwise, still return true to stop notifications for this user.
-        if (w.isNotify(type)) {
-          matching.bcc.accounts.add(w.getAccountId());
-        }
-        return true;
-      }
-    } catch (QueryParseException e) {
-      // Ignore broken filter expressions.
-    }
-    return false;
-  }
-
   private boolean filterMatch(CurrentUser user, String filter)
       throws OrmException, QueryParseException {
     ChangeQueryBuilder qb;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
index 39affb7..d5ba4a8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
@@ -17,9 +17,9 @@
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/RestoredSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/RestoredSender.java
index 1b1823e..fe37984 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/RestoredSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/RestoredSender.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.gerrit.common.errors.EmailException;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/RevertedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/RevertedSender.java
index b297ee1..bad72ab 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/RevertedSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/RevertedSender.java
@@ -15,9 +15,9 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.gerrit.common.errors.EmailException;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
index 41392fb..4db1ccd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
@@ -163,7 +163,8 @@
 
     String parentName = MoreObjects.firstNonNull(
         Strings.emptyToNull(input.parent), allProjects.get());
-    args.newParent = projectsCollection.get().parse(parentName).getControl();
+    args.newParent =
+        projectsCollection.get().parse(parentName, false).getControl();
     args.createEmptyCommit = input.createEmptyCommit;
     args.permissionsOnly = input.permissionsOnly;
     args.projectDescription = Strings.emptyToNull(input.description);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java
index 0ffbe3e..51603fc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java
@@ -63,7 +63,7 @@
   @Override
   public ProjectResource parse(TopLevelResource parent, IdString id)
       throws ResourceNotFoundException, IOException {
-    ProjectResource rsrc = _parse(id.get());
+    ProjectResource rsrc = _parse(id.get(), true);
     if (rsrc == null) {
       throw new ResourceNotFoundException(id);
     }
@@ -81,7 +81,24 @@
    */
   public ProjectResource parse(String id)
       throws UnprocessableEntityException, IOException {
-    ProjectResource rsrc = _parse(id);
+    return parse(id, true);
+  }
+
+  /**
+   * Parses a project ID from a request body and returns the project.
+   *
+   * @param id ID of the project, can be a project name
+   * @param checkVisibility Whether to check or not that project is visible to
+   *        the calling user
+   * @return the project
+   * @throws UnprocessableEntityException thrown if the project ID cannot be
+   *         resolved or if the project is not visible to the calling user and
+   *         checkVisibility is true.
+   * @throws IOException thrown when there is an error.
+   */
+  public ProjectResource parse(String id, boolean checkVisibility)
+      throws UnprocessableEntityException, IOException {
+    ProjectResource rsrc = _parse(id, checkVisibility);
     if (rsrc == null) {
       throw new UnprocessableEntityException(String.format(
           "Project Not Found: %s", id));
@@ -89,7 +106,8 @@
     return rsrc;
   }
 
-  private ProjectResource _parse(String id) throws IOException {
+  private ProjectResource _parse(String id, boolean checkVisibility)
+      throws IOException {
     if (id.endsWith(Constants.DOT_GIT_EXT)) {
       id = id.substring(0, id.length() - Constants.DOT_GIT_EXT.length());
     }
@@ -101,7 +119,7 @@
     } catch (NoSuchProjectException e) {
       return null;
     }
-    if (!ctl.isVisible() && !ctl.isOwner()) {
+    if (checkVisibility && !ctl.isVisible() && !ctl.isOwner()) {
       return null;
     }
     return new ProjectResource(ctl);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
index 40fb3b6..1c945e3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
@@ -34,10 +34,6 @@
  * Parses a query string meant to be applied to account objects.
  */
 public class AccountQueryBuilder extends QueryBuilder<AccountState> {
-  public interface ChangeOperatorFactory
-      extends OperatorFactory<AccountState, AccountQueryBuilder> {
-  }
-
   public static final String FIELD_ACCOUNT = "account";
   public static final String FIELD_EMAIL = "email";
   public static final String FIELD_LIMIT = "limit";
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
index f0a063e..f4e5f7f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -36,7 +36,7 @@
 /** A version of the database schema. */
 public abstract class SchemaVersion {
   /** The current schema version. */
-  public static final Class<Schema_138> C = Schema_138.class;
+  public static final Class<Schema_139> C = Schema_139.class;
 
   public static int getBinaryVersion() {
     return guessVersion(C);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_139.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_139.java
new file mode 100644
index 0000000..f426585
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_139.java
@@ -0,0 +1,186 @@
+//Copyright (C) 2016 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.auto.value.AutoValue;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.GerritPersonIdent;
+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.config.AllUsersName;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.gwtorm.server.OrmDuplicateKeyException;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+import java.io.IOException;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class Schema_139 extends SchemaVersion {
+ private static final String MSG = "Migrate project watches to git";
+
+ private final GitRepositoryManager repoManager;
+ private final AllUsersName allUsersName;
+ private final PersonIdent serverUser;
+
+ @Inject
+ Schema_139(Provider<Schema_138> prior,
+     GitRepositoryManager repoManager,
+     AllUsersName allUsersName,
+     @GerritPersonIdent PersonIdent serverUser) {
+   super(prior);
+   this.repoManager = repoManager;
+   this.allUsersName = allUsersName;
+   this.serverUser = serverUser;
+ }
+
+ @Override
+ protected void migrateData(ReviewDb db, UpdateUI ui)
+     throws OrmException, SQLException {
+   Multimap<Account.Id, ProjectWatch> imports = ArrayListMultimap.create();
+   try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
+       ResultSet rs = stmt.executeQuery(
+         "SELECT "
+         + "account_id, "
+         + "project_name, "
+         + "filter, "
+         + "notify_abandoned_changes, "
+         + "notify_all_comments, "
+         + "notify_new_changes, "
+         + "notify_new_patch_sets, "
+         + "notify_submitted_changes "
+         + "FROM account_project_watches")) {
+     while (rs.next()) {
+       Account.Id accountId = new Account.Id(rs.getInt(1));
+       ProjectWatch.Builder b = ProjectWatch.builder()
+           .project(new Project.NameKey(rs.getString(2)))
+           .filter(rs.getString(3))
+           .notifyAbandonedChanges(rs.getBoolean(4))
+           .notifyAllComments(rs.getBoolean(5))
+           .notifyNewChanges(rs.getBoolean(6))
+           .notifyNewPatchSets(rs.getBoolean(7))
+           .notifySubmittedChanges(rs.getBoolean(8));
+       imports.put(accountId, b.build());
+     }
+   }
+
+   if (imports.isEmpty()) {
+     return;
+   }
+
+   try (Repository git = repoManager.openRepository(allUsersName);
+       RevWalk rw = new RevWalk(git)) {
+     BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
+     bru.setRefLogIdent(serverUser);
+     bru.setRefLogMessage(MSG, false);
+
+     for (Map.Entry<Account.Id, Collection<ProjectWatch>> e : imports.asMap()
+         .entrySet()) {
+       Map<ProjectWatchKey, Set<NotifyType>> projectWatches = new HashMap<>();
+       for (ProjectWatch projectWatch : e.getValue()) {
+         ProjectWatchKey key = ProjectWatchKey.create(projectWatch.project(),
+             projectWatch.filter());
+         if (projectWatches.containsKey(key)) {
+           throw new OrmDuplicateKeyException(
+               "Duplicate key for watched project: " + key.toString());
+         }
+         Set<NotifyType> notifyValues = EnumSet.noneOf(NotifyType.class);
+         if (projectWatch.notifyAbandonedChanges()) {
+           notifyValues.add(NotifyType.ABANDONED_CHANGES);
+         }
+         if (projectWatch.notifyAllComments()) {
+           notifyValues.add(NotifyType.ALL_COMMENTS);
+         }
+         if (projectWatch.notifyNewChanges()) {
+           notifyValues.add(NotifyType.NEW_CHANGES);
+         }
+         if (projectWatch.notifyNewPatchSets()) {
+           notifyValues.add(NotifyType.NEW_PATCHSETS);
+         }
+         if (projectWatch.notifySubmittedChanges()) {
+           notifyValues.add(NotifyType.SUBMITTED_CHANGES);
+         }
+         projectWatches.put(key, notifyValues);
+       }
+
+       try (MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED,
+               allUsersName, git, bru)) {
+         md.getCommitBuilder().setAuthor(serverUser);
+         md.getCommitBuilder().setCommitter(serverUser);
+         md.setMessage(MSG);
+
+         WatchConfig watchConfig = new WatchConfig(e.getKey());
+         watchConfig.load(md);
+         watchConfig.setProjectWatches(projectWatches);
+         watchConfig.commit(md);
+       }
+     }
+     bru.execute(rw, NullProgressMonitor.INSTANCE);
+   } catch (IOException | ConfigInvalidException ex) {
+     throw new OrmException(ex);
+   }
+ }
+
+ @AutoValue
+ abstract static class ProjectWatch {
+   abstract Project.NameKey project();
+   abstract @Nullable String filter();
+   abstract boolean notifyAbandonedChanges();
+   abstract boolean notifyAllComments();
+   abstract boolean notifyNewChanges();
+   abstract boolean notifyNewPatchSets();
+   abstract boolean notifySubmittedChanges();
+
+   static Builder builder() {
+     return new AutoValue_Schema_139_ProjectWatch.Builder();
+   }
+
+   @AutoValue.Builder
+   abstract static class Builder {
+     abstract Builder project(Project.NameKey project);
+     abstract Builder filter(@Nullable String filter);
+     abstract Builder notifyAbandonedChanges(boolean notifyAbandonedChanges);
+     abstract Builder notifyAllComments(boolean notifyAllComments);
+     abstract Builder notifyNewChanges(boolean notifyNewChanges);
+     abstract Builder notifyNewPatchSets(boolean notifyNewPatchSets);
+     abstract Builder notifySubmittedChanges(boolean notifySubmittedChanges);
+     abstract ProjectWatch build();
+   }
+ }
+}
\ No newline at end of file
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/account/WatchConfigTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/account/WatchConfigTest.java
index 0619a78..c40ca83 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/account/WatchConfigTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/account/WatchConfigTest.java
@@ -17,8 +17,8 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.account.WatchConfig.NotifyValue;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
 import com.google.gerrit.server.git.ValidationError;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
index 9d05fc6..33e34b6 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
@@ -25,9 +25,9 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
 import com.google.gerrit.server.mail.Address;
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/DisabledReviewDb.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/DisabledReviewDb.java
index 11d7ad0..47dbd48 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/DisabledReviewDb.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/DisabledReviewDb.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.reviewdb.server.AccountGroupMemberAccess;
 import com.google.gerrit.reviewdb.server.AccountGroupMemberAuditAccess;
 import com.google.gerrit.reviewdb.server.AccountGroupNameAccess;
-import com.google.gerrit.reviewdb.server.AccountProjectWatchAccess;
 import com.google.gerrit.reviewdb.server.ChangeAccess;
 import com.google.gerrit.reviewdb.server.ChangeMessageAccess;
 import com.google.gerrit.reviewdb.server.PatchLineCommentAccess;
@@ -115,11 +114,6 @@
   }
 
   @Override
-  public AccountProjectWatchAccess accountProjectWatches() {
-    throw new Disabled();
-  }
-
-  @Override
   public ChangeAccess changes() {
     throw new Disabled();
   }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/FakeAccountCache.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/FakeAccountCache.java
index 07cd63e..76f24df 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/FakeAccountCache.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/FakeAccountCache.java
@@ -19,9 +19,9 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
 
 import java.util.HashMap;
diff --git a/lib/JGIT_VERSION b/lib/JGIT_VERSION
index a6ed50a..569cf59 100644
--- a/lib/JGIT_VERSION
+++ b/lib/JGIT_VERSION
@@ -1,5 +1,4 @@
 include_defs('//lib/jgit/jgit.bzl')
 include_defs('//lib/maven.defs')
 
-REPO = GERRIT
-#REPO = MAVEN_CENTRAL # Leave here even if set to MAVEN_CENTRAL.
+REPO = MAVEN_CENTRAL # Leave here even if set to MAVEN_CENTRAL.
diff --git a/lib/jgit/jgit.bzl b/lib/jgit/jgit.bzl
index 11bcec5..2982588 100644
--- a/lib/jgit/jgit.bzl
+++ b/lib/jgit/jgit.bzl
@@ -1,6 +1,5 @@
-JGIT_VERS = "4.5.0.201609210915-r.115-g81f9c1843"
+JGIT_VERS = "4.6.0.201612231935-r"
 
-DOC_VERS = "4.5.0.201609210915-r"
+DOC_VERS = JGIT_VERS # Set to JGIT_VERS unless using a snapshot
 
-#DOC_VERS = JGIT_VERS # Set to VERS unless using a snapshot
 JGIT_DOC_URL = "http://download.eclipse.org/jgit/site/" + DOC_VERS + "/apidocs"
diff --git a/lib/jgit/org.eclipse.jgit.archive/BUCK b/lib/jgit/org.eclipse.jgit.archive/BUCK
index c0272af..b73987f 100644
--- a/lib/jgit/org.eclipse.jgit.archive/BUCK
+++ b/lib/jgit/org.eclipse.jgit.archive/BUCK
@@ -4,7 +4,7 @@
 maven_jar(
   name = 'jgit-archive',
   id = 'org.eclipse.jgit:org.eclipse.jgit.archive:' + JGIT_VERS,
-  sha1 = '4a5d058915400c1ef497bfeeb5e87d235213e273',
+  sha1 = 'd44816c7b47746948485430e7372b15108b4bf2e',
   license = 'jgit',
   repository = REPO,
   deps = ['//lib/jgit/org.eclipse.jgit:jgit'],
diff --git a/lib/jgit/org.eclipse.jgit.http.server/BUCK b/lib/jgit/org.eclipse.jgit.http.server/BUCK
index 29e7e27..ab71e7d 100644
--- a/lib/jgit/org.eclipse.jgit.http.server/BUCK
+++ b/lib/jgit/org.eclipse.jgit.http.server/BUCK
@@ -4,7 +4,7 @@
 maven_jar(
   name = 'jgit-servlet',
   id = 'org.eclipse.jgit:org.eclipse.jgit.http.server:' + JGIT_VERS,
-  sha1 = '927990025d2970995dbb58f03763eeb776fec8fd',
+  sha1 = '5cb46863efcb208a3a9f729c0b6fd00be0dae6ee',
   license = 'jgit',
   repository = REPO,
   deps = ['//lib/jgit/org.eclipse.jgit:jgit'],
diff --git a/lib/jgit/org.eclipse.jgit.junit/BUCK b/lib/jgit/org.eclipse.jgit.junit/BUCK
index 255b47c..d3808b7 100644
--- a/lib/jgit/org.eclipse.jgit.junit/BUCK
+++ b/lib/jgit/org.eclipse.jgit.junit/BUCK
@@ -4,7 +4,7 @@
 maven_jar(
   name = 'junit',
   id = 'org.eclipse.jgit:org.eclipse.jgit.junit:' + JGIT_VERS,
-  sha1 = '8e3cb9b1f632fdfea76b04c286a2c0d8d260ebce',
+  sha1 = '42bd9796a68b0de8abf1e0c23ebd4006bf9645e0',
   license = 'DO_NOT_DISTRIBUTE',
   repository = REPO,
   unsign = True,
diff --git a/lib/jgit/org.eclipse.jgit/BUCK b/lib/jgit/org.eclipse.jgit/BUCK
index 4f5da75..97d2edf6 100644
--- a/lib/jgit/org.eclipse.jgit/BUCK
+++ b/lib/jgit/org.eclipse.jgit/BUCK
@@ -4,8 +4,8 @@
 maven_jar(
   name = 'jgit',
   id = 'org.eclipse.jgit:org.eclipse.jgit:' + JGIT_VERS,
-  bin_sha1 = '34315f71bb9becf6ff75947a9c43c415b929ec21',
-  src_sha1 = '8320c18472870904eb7fb860af353fea818d07e4',
+  bin_sha1 = 'b705df6cb4c0f4413e6ad52c6cd12011cc6fba4c',
+  src_sha1 = '6ec89bdedccdab092f395016ab761dcdb08f7ffc',
   license = 'jgit',
   repository = REPO,
   unsign = True,
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
index 7a9b878..d0d51ea 160000
--- a/plugins/singleusergroup
+++ b/plugins/singleusergroup
@@ -1 +1 @@
-Subproject commit 7a9b8781cf0850815969f861f5f57178957f55c8
+Subproject commit d0d51ea46e9e3220c1d22fda241a50ac341df5de
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js
index 00719b2..9638544 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js
@@ -126,7 +126,10 @@
     },
 
     _createReplyComment: function(parent, content, opt_isEditing) {
-      var reply = this._newReply(parent.id, parent.line, content);
+      var reply = this._newReply(
+          this._orderedComments[this._orderedComments.length - 1].id,
+          parent.line,
+          content);
 
       if (opt_isEditing) {
         reply.__editing = true;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html
index 29e63aa..9a42dec 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html
@@ -355,5 +355,35 @@
       assert.isTrue(expandCollapseStub.lastCall.calledWith(true));
       expandCollapseStub.restore();
     });
+
+    test('comment in_reply_to is either null or most recent comment id',
+        function() {
+      var comments = [
+        {
+          id: 'jacks_reply',
+          message: 'i like you, too',
+          in_reply_to: 'sallys_confession',
+          updated: '2015-12-25 15:00:20.396000000',
+        }, {
+          id: 'sallys_confession',
+          in_reply_to: 'nonexistent_comment',
+          message: 'i like you, jack',
+          updated: '2015-12-24 15:00:20.396000000',
+        }, {
+          id: 'sally_to_dr_finklestein',
+          in_reply_to: 'nonexistent_comment',
+          message: 'i’m running away',
+          updated: '2015-10-31 09:00:20.396000000',
+        }, {
+          id: 'sallys_defiance',
+          message: 'i will poison you so i can get away',
+          updated: '2015-10-31 15:00:20.396000000',
+        }];
+      element.set('comments', comments);
+      element._createReplyComment(comments[3], 'dummy', true);
+      flushAsynchronousOperations();
+      assert.equal(element._orderedComments.length, 5);
+      assert.equal(element._orderedComments[4].in_reply_to, 'jacks_reply');
+    });
   });
 </script>