Merge "Fix issue with search view not updating"
diff --git a/Documentation/config-submit-requirements.txt b/Documentation/config-submit-requirements.txt
index fb37287..d61dbc43 100644
--- a/Documentation/config-submit-requirements.txt
+++ b/Documentation/config-submit-requirements.txt
@@ -206,6 +206,29 @@
 +
 An operator that returns true if the latest patchset contained a modified file
 matching `<filePattern>` with a modified region matching `<contentPattern>`.
++
+Both `<filePattern>` and `<contentPattern>` support regular expressions if they
+start with the '^' character. Regular expressions are matched with the
+`java.util.regex` engine. When using regular expressions, special characters
+should be double escaped because the config is parsed twice when the server
+reads the `project.config` file and when the submit-requirement expressions
+are parsed as a predicate tree. For example, to match against modified files
+that end with ".cc" or ".cpp" the following `applicableIf` expression can be
+used:
++
+----
+  applicableIf = file:\"^.*\\\\.(cc|cpp)$\"
+----
++
+Below is another example that uses both `<filePattern>` and `<contentPattern>`:
++
+----
+  applicableIf = file:\"'^.*\\\\.(cc|cpp)$',withDiffContaining='^.*th[rR]ee$'\"
+----
++
+If no regular expression is used, the text is matched by checking that the file
+name contains the file pattern, or the edits of the file diff contain the edit
+pattern.
 
 [[unsupported_operators]]
 === Unsupported Operators
diff --git a/java/com/google/gerrit/server/StarredChangesUtil.java b/java/com/google/gerrit/server/StarredChangesUtil.java
index 8f413f9..9403105 100644
--- a/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -266,7 +266,7 @@
     }
   }
 
-  public ImmutableSet<Change.Id> byAccountId(Account.Id accountId, String label) {
+  public ImmutableSet<Change.Id> byAccountId(Account.Id accountId) {
     try (Repository repo = repoManager.openRepository(allUsers)) {
       ImmutableSet.Builder<Change.Id> builder = ImmutableSet.builder();
       for (Ref ref : repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_STARRED_CHANGES)) {
@@ -277,7 +277,7 @@
         }
         // Skip all refs that don't contain the required label.
         StarRef starRef = readLabels(repo, ref.getName());
-        if (!starRef.labels().contains(label)) {
+        if (!starRef.labels().contains(DEFAULT_LABEL)) {
           continue;
         }
 
diff --git a/java/com/google/gerrit/server/account/AccountResolver.java b/java/com/google/gerrit/server/account/AccountResolver.java
index d2417d7..703693d 100644
--- a/java/com/google/gerrit/server/account/AccountResolver.java
+++ b/java/com/google/gerrit/server/account/AccountResolver.java
@@ -230,10 +230,6 @@
       return true;
     }
 
-    default boolean callerMayAssumeCandidatesAreVisible() {
-      return false;
-    }
-
     /**
      * Searches can be done on behalf of either the current user or another provided user. The
      * results of some searchers, such as BySelf, are affected by the context user.
@@ -312,11 +308,6 @@
     }
 
     @Override
-    public boolean callerMayAssumeCandidatesAreVisible() {
-      return true;
-    }
-
-    @Override
     public boolean requiresContextUser() {
       return true;
     }
@@ -457,33 +448,19 @@
     }
   }
 
-  private class ByFullName implements Searcher<AccountState> {
-    boolean allowSkippingVisibilityCheck = true;
-
+  private class ByFullName extends StringSearcher {
     ByFullName() {
       super();
     }
 
-    ByFullName(boolean allowSkippingVisibilityCheck) {
-      this();
-      this.allowSkippingVisibilityCheck = allowSkippingVisibilityCheck;
+    @Override
+    protected boolean matches(String input) {
+      return true;
     }
 
     @Override
-    public boolean callerMayAssumeCandidatesAreVisible() {
-      return allowSkippingVisibilityCheck;
-    }
-
-    @Override
-    public Optional<AccountState> tryParse(String input) {
-      List<AccountState> results =
-          accountQueryProvider.get().enforceVisibility(true).byFullName(input);
-      return results.size() == 1 ? Optional.of(results.get(0)) : Optional.empty();
-    }
-
-    @Override
-    public Stream<AccountState> search(AccountState input) {
-      return Stream.of(input);
+    public Stream<AccountState> search(String input) {
+      return accountQueryProvider.get().byFullName(input).stream();
     }
 
     @Override
@@ -493,22 +470,10 @@
   }
 
   private class ByDefaultSearch extends StringSearcher {
-    boolean allowSkippingVisibilityCheck = true;
-
     ByDefaultSearch() {
       super();
     }
 
-    ByDefaultSearch(boolean allowSkippingVisibilityCheck) {
-      this();
-      this.allowSkippingVisibilityCheck = allowSkippingVisibilityCheck;
-    }
-
-    @Override
-    public boolean callerMayAssumeCandidatesAreVisible() {
-      return allowSkippingVisibilityCheck;
-    }
-
     @Override
     public boolean requiresContextUser() {
       return true;
@@ -533,8 +498,7 @@
       } catch (PermissionBackendException e) {
         // remains false
       }
-      return accountQueryProvider.get().enforceVisibility(true)
-          .byDefault(input, canSeeSecondaryEmails).stream();
+      return accountQueryProvider.get().byDefault(input, canSeeSecondaryEmails).stream();
     }
 
     @Override
@@ -562,18 +526,6 @@
           .addAll(nameOrEmailSearchers)
           .build();
 
-  private final ImmutableList<Searcher<?>> forcedVisibilitySearchers =
-      ImmutableList.of(
-          new ByNameAndEmail(),
-          new ByEmail(),
-          new FromRealm(),
-          new ByFullName(false),
-          new ByDefaultSearch(false),
-          new BySelf(),
-          new ByExactAccountId(),
-          new ByParenthesizedAccountId(),
-          new ByUsername());
-
   private final AccountCache accountCache;
   private final AccountControl.Factory accountControlFactory;
   private final Emails emails;
@@ -670,25 +622,21 @@
    *
    * @param asUser user to resolve the users by.
    * @param input input string.
-   * @param forceVisibilityCheck whether to force all searchers to check for visibility.
    * @return a result describing matching accounts. Never null even if the result set is empty.
    * @throws ConfigInvalidException if an error occurs.
    * @throws IOException if an error occurs.
    */
-  public Result resolveAsUser(CurrentUser asUser, String input, boolean forceVisibilityCheck)
+  public Result resolveAsUser(CurrentUser asUser, String input)
       throws ConfigInvalidException, IOException {
-    return resolveAsUser(asUser, input, AccountResolver::isActive, forceVisibilityCheck);
+    return resolveAsUser(asUser, input, AccountResolver::isActive);
   }
 
   public Result resolveAsUser(
-      CurrentUser asUser,
-      String input,
-      Predicate<AccountState> accountActivityPredicate,
-      boolean forceVisibilityCheck)
+      CurrentUser asUser, String input, Predicate<AccountState> accountActivityPredicate)
       throws ConfigInvalidException, IOException {
     return searchImpl(
         input,
-        forceVisibilityCheck ? forcedVisibilitySearchers : searchers,
+        searchers,
         asUser,
         new ProvidedUserCanSeePredicate(asUser),
         accountActivityPredicate);
@@ -826,9 +774,9 @@
       }
       Stream<AccountState> results = maybeResults.get();
 
-      if (!searcher.callerMayAssumeCandidatesAreVisible()) {
-        results = results.filter(visibilitySupplier.get());
-      }
+      // Filter out non-visible results, except if it's the BySelf searcher. Since users can always
+      // see themselves checking the visibility is not needed for the BySelf searcher.
+      results = searcher instanceof BySelf ? results : results.filter(visibilitySupplier.get());
 
       List<AccountState> list;
       if (searcher.callerShouldFilterOutInactiveCandidates()) {
diff --git a/java/com/google/gerrit/server/mail/send/MailSoySauceLoader.java b/java/com/google/gerrit/server/mail/send/MailSoySauceLoader.java
index 8ee8fc2..9d75abd 100644
--- a/java/com/google/gerrit/server/mail/send/MailSoySauceLoader.java
+++ b/java/com/google/gerrit/server/mail/send/MailSoySauceLoader.java
@@ -17,6 +17,8 @@
 import static com.google.common.base.Preconditions.checkArgument;
 
 import com.google.common.io.CharStreams;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
 import com.google.inject.Inject;
@@ -24,13 +26,13 @@
 import com.google.inject.Singleton;
 import com.google.template.soy.SoyFileSet;
 import com.google.template.soy.jbcsrc.api.SoySauce;
-import com.google.template.soy.shared.SoyAstCache;
 import java.io.IOException;
 import java.io.Reader;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import org.eclipse.jgit.util.FileUtils;
 
 /**
  * Configures and loads Soy Sauce object for rendering email templates.
@@ -90,56 +92,76 @@
     "SetAssigneeHtml.soy",
   };
 
+  private static final SoySauce DEFAULT = getDefault().build().compileTemplates();
+
   private final SitePaths site;
-  private final SoyAstCache cache;
   private final PluginSetContext<MailSoyTemplateProvider> templateProviders;
 
   @Inject
-  MailSoySauceLoader(
-      SitePaths site,
-      SoyAstCache cache,
-      PluginSetContext<MailSoyTemplateProvider> templateProviders) {
+  MailSoySauceLoader(SitePaths site, PluginSetContext<MailSoyTemplateProvider> templateProviders) {
     this.site = site;
-    this.cache = cache;
     this.templateProviders = templateProviders;
   }
 
   public SoySauce load() {
-    SoyFileSet.Builder builder = SoyFileSet.builder();
-    builder.setSoyAstCache(cache);
-    for (String name : TEMPLATES) {
-      addTemplate(builder, "com/google/gerrit/server/mail/", name);
+    if (!hasCustomTemplates(site, templateProviders)) {
+      return DEFAULT;
     }
+
+    SoyFileSet.Builder builder = getDefault();
     templateProviders.runEach(
-        e -> e.getFileNames().forEach(p -> addTemplate(builder, e.getPath(), p)));
+        e -> e.getFileNames().forEach(p -> addTemplate(builder, site, e.getPath(), p)));
     return builder.build().compileTemplates();
   }
 
-  private void addTemplate(SoyFileSet.Builder builder, String resourcePath, String name)
+  private static boolean hasCustomTemplates(
+      SitePaths site, PluginSetContext<MailSoyTemplateProvider> templateProviders) {
+    try {
+      if (!templateProviders.isEmpty()) {
+        return true;
+      }
+      return Files.exists(site.mail_dir) && FileUtils.hasFiles(site.mail_dir);
+    } catch (IOException e) {
+      throw new StorageException(e);
+    }
+  }
+
+  private static SoyFileSet.Builder getDefault() {
+    SoyFileSet.Builder builder = SoyFileSet.builder();
+    for (String name : TEMPLATES) {
+      addTemplate(builder, null, "com/google/gerrit/server/mail/", name);
+    }
+    return builder;
+  }
+
+  private static void addTemplate(
+      SoyFileSet.Builder builder, @Nullable SitePaths site, String resourcePath, String name)
       throws ProvisionException {
     if (!resourcePath.endsWith("/")) {
       resourcePath += "/";
     }
     String logicalPath = resourcePath + name;
 
-    // Load as a file in the mail templates directory if present.
-    Path tmpl = site.mail_dir.resolve(name);
-    if (Files.isRegularFile(tmpl)) {
-      String content;
-      // TODO(davido): Consider using JGit's FileSnapshot to cache based on
-      // mtime.
-      try (Reader r = Files.newBufferedReader(tmpl, StandardCharsets.UTF_8)) {
-        content = CharStreams.toString(r);
-      } catch (IOException err) {
-        throw new ProvisionException(
-            "Failed to read template file " + tmpl.toAbsolutePath().toString(), err);
+    if (site != null) {
+      // Load as a file in the mail templates directory if present.
+      Path tmpl = site.mail_dir.resolve(name);
+      if (Files.isRegularFile(tmpl)) {
+        String content;
+        // TODO(davido): Consider using JGit's FileSnapshot to cache based on
+        // mtime.
+        try (Reader r = Files.newBufferedReader(tmpl, StandardCharsets.UTF_8)) {
+          content = CharStreams.toString(r);
+        } catch (IOException err) {
+          throw new ProvisionException(
+              "Failed to read template file " + tmpl.toAbsolutePath(), err);
+        }
+        builder.add(content, logicalPath);
+        return;
       }
-      builder.add(content, logicalPath);
-      return;
     }
 
     // Otherwise load the template as a resource.
-    URL resource = this.getClass().getClassLoader().getResource(logicalPath);
+    URL resource = MailSoySauceLoader.class.getClassLoader().getResource(logicalPath);
     checkArgument(resource != null, "resource %s not found.", logicalPath);
     builder.add(resource, logicalPath);
   }
diff --git a/java/com/google/gerrit/server/mail/send/ProjectWatch.java b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
index cbf47c5..f4c211d 100644
--- a/java/com/google/gerrit/server/mail/send/ProjectWatch.java
+++ b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
@@ -253,7 +253,6 @@
       qb = WatcherChangeQueryBuilder.asUser(args.queryBuilder.get(), user);
       p = qb.isVisible();
     }
-    qb.forceAccountVisibilityCheck();
 
     if (filter != null) {
       Predicate<ChangeData> filterPredicate = qb.parse(filter);
diff --git a/java/com/google/gerrit/server/query/change/ChangePredicates.java b/java/com/google/gerrit/server/query/change/ChangePredicates.java
index f40e530..5f909a1 100644
--- a/java/com/google/gerrit/server/query/change/ChangePredicates.java
+++ b/java/com/google/gerrit/server/query/change/ChangePredicates.java
@@ -23,12 +23,10 @@
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.git.ObjectIds;
 import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.change.HashtagsUtil;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.inject.ImplementedBy;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -71,25 +69,12 @@
     return new ChangeIndexPredicate(ChangeField.COMMENTBY_SPEC, id.toString());
   }
 
-  @ImplementedBy(IndexEditByPredicateProvider.class)
-  public interface EditByPredicateProvider {
-
-    /**
-     * Returns a predicate that matches changes where the provided {@link
-     * com.google.gerrit.entities.Account.Id} has a pending change edit.
-     */
-    Predicate<ChangeData> editBy(Account.Id id) throws QueryParseException;
-  }
-
   /**
-   * A default implementation of {@link EditByPredicateProvider}, based on th {@link
-   * ChangeField#EDITBY_SPEC} index field.
+   * Returns a predicate that matches changes where the provided {@link
+   * com.google.gerrit.entities.Account.Id} has a pending change edit.
    */
-  public static class IndexEditByPredicateProvider implements EditByPredicateProvider {
-    @Override
-    public Predicate<ChangeData> editBy(Account.Id id) {
-      return new ChangeIndexPredicate(ChangeField.EDITBY_SPEC, id.toString());
-    }
+  public static Predicate<ChangeData> editBy(Account.Id id) {
+    return new ChangeIndexPredicate(ChangeField.EDITBY_SPEC, id.toString());
   }
 
   /**
@@ -110,10 +95,9 @@
    * Returns a predicate that matches changes where the provided {@link
    * com.google.gerrit.entities.Account.Id} has starred changes with {@code label}.
    */
-  public static Predicate<ChangeData> starBy(
-      StarredChangesUtil starredChangesUtil, Account.Id id, String label) {
+  public static Predicate<ChangeData> starBy(StarredChangesUtil starredChangesUtil, Account.Id id) {
     Set<Predicate<ChangeData>> starredChanges =
-        starredChangesUtil.byAccountId(id, label).stream()
+        starredChangesUtil.byAccountId(id).stream()
             .map(ChangePredicates::idStr)
             .collect(toImmutableSet());
     return starredChanges.isEmpty() ? ChangeIndexPredicate.none() : Predicate.or(starredChanges);
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index c87a2d9..622c4bf 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -86,7 +86,6 @@
 import com.google.gerrit.server.plugincontext.PluginSetContext;
 import com.google.gerrit.server.project.ChildProjects;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.query.change.ChangePredicates.EditByPredicateProvider;
 import com.google.gerrit.server.query.change.PredicateArgs.ValOp;
 import com.google.gerrit.server.rules.SubmitRule;
 import com.google.gerrit.server.submit.SubmitDryRun;
@@ -276,8 +275,6 @@
 
     private final Provider<CurrentUser> self;
 
-    private final EditByPredicateProvider editByPredicateProvider;
-
     @Inject
     @VisibleForTesting
     public Arguments(
@@ -311,8 +308,7 @@
         ExperimentFeatures experimentFeatures,
         HasOperandAliasConfig hasOperandAliasConfig,
         ChangeIsVisibleToPredicate.Factory changeIsVisbleToPredicateFactory,
-        PluginSetContext<SubmitRule> submitRules,
-        EditByPredicateProvider editByPredicateProvider) {
+        PluginSetContext<SubmitRule> submitRules) {
       this(
           queryProvider,
           rewriter,
@@ -345,8 +341,7 @@
           experimentFeatures,
           hasOperandAliasConfig,
           changeIsVisbleToPredicateFactory,
-          submitRules,
-          editByPredicateProvider);
+          submitRules);
     }
 
     private Arguments(
@@ -381,8 +376,7 @@
         ExperimentFeatures experimentFeatures,
         HasOperandAliasConfig hasOperandAliasConfig,
         ChangeIsVisibleToPredicate.Factory changeIsVisbleToPredicateFactory,
-        PluginSetContext<SubmitRule> submitRules,
-        EditByPredicateProvider editByPredicateProvider) {
+        PluginSetContext<SubmitRule> submitRules) {
       this.queryProvider = queryProvider;
       this.rewriter = rewriter;
       this.opFactories = opFactories;
@@ -415,7 +409,6 @@
       this.experimentFeatures = experimentFeatures;
       this.hasOperandAliasConfig = hasOperandAliasConfig;
       this.submitRules = submitRules;
-      this.editByPredicateProvider = editByPredicateProvider;
     }
 
     public Arguments asUser(CurrentUser otherUser) {
@@ -451,8 +444,7 @@
           experimentFeatures,
           hasOperandAliasConfig,
           changeIsVisbleToPredicateFactory,
-          submitRules,
-          editByPredicateProvider);
+          submitRules);
     }
 
     Arguments asUser(Account.Id otherId) {
@@ -496,7 +488,6 @@
   private final Arguments args;
   protected Map<String, String> hasOperandAliases = Collections.emptyMap();
   private Map<Account.Id, DestinationList> destinationListByAccount = new HashMap<>();
-  private boolean forceAccountVisibilityCheck = false;
 
   private static final Splitter RULE_SPLITTER = Splitter.on("=");
   private static final Splitter PLUGIN_SPLITTER = Splitter.on("_");
@@ -527,11 +518,6 @@
     return args;
   }
 
-  /** Whether to force account visibility check when searching for changes by account(s). */
-  public void forceAccountVisibilityCheck() {
-    forceAccountVisibilityCheck = true;
-  }
-
   @Operator
   public Predicate<ChangeData> age(String value) {
     return new AgePredicate(value);
@@ -645,7 +631,7 @@
     }
 
     if ("edit".equalsIgnoreCase(value)) {
-      return this.args.editByPredicateProvider.editBy(self());
+      return ChangePredicates.editBy(self());
     }
 
     if ("attention".equalsIgnoreCase(value)) {
@@ -1177,8 +1163,7 @@
   }
 
   private Predicate<ChangeData> starredBySelf() throws QueryParseException {
-    return ChangePredicates.starBy(
-        args.starredChangesUtil, self(), StarredChangesUtil.DEFAULT_LABEL);
+    return ChangePredicates.starBy(args.starredChangesUtil, self());
   }
 
   private Predicate<ChangeData> draftBySelf() throws QueryParseException {
@@ -1719,9 +1704,7 @@
   private Set<Account.Id> parseAccount(String who)
       throws QueryParseException, IOException, ConfigInvalidException {
     try {
-      return args.accountResolver
-          .resolveAsUser(args.getUser(), who, forceAccountVisibilityCheck)
-          .asNonEmptyIdSet();
+      return args.accountResolver.resolveAsUser(args.getUser(), who).asNonEmptyIdSet();
     } catch (UnresolvableAccountException e) {
       if (e.isSelf()) {
         throw new QueryRequiresAuthException(e.getMessage(), e);
@@ -1735,7 +1718,7 @@
       throws QueryParseException, IOException, ConfigInvalidException {
     try {
       return args.accountResolver
-          .resolveAsUser(args.getUser(), who, activityFilter, forceAccountVisibilityCheck)
+          .resolveAsUser(args.getUser(), who, activityFilter)
           .asNonEmptyIdSet();
     } catch (UnresolvableAccountException e) {
       if (e.isSelf()) {
@@ -1782,7 +1765,8 @@
     return value;
   }
 
-  private Account.Id self() throws QueryParseException {
+  /** Returns {@link Account.Id} of the identified calling user. */
+  public Account.Id self() throws QueryParseException {
     return args.getIdentifiedUser().getAccountId();
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
index 0cfa0f8..6f3aa15 100644
--- a/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
@@ -26,11 +26,13 @@
 import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
 import com.google.gerrit.acceptance.testsuite.account.TestAccount;
+import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.extensions.common.AccountVisibility;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.ServerInitiated;
+import com.google.gerrit.server.account.AccountControl;
 import com.google.gerrit.server.account.AccountResolver;
 import com.google.gerrit.server.account.AccountResolver.Result;
 import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException;
@@ -56,7 +58,9 @@
   @Inject @ServerInitiated private Provider<AccountsUpdate> accountsUpdateProvider;
   @Inject private AccountOperations accountOperations;
   @Inject private AccountResolver accountResolver;
+  @Inject private AccountControl.Factory accountControlFactory;
   @Inject private Provider<CurrentUser> self;
+  @Inject private GroupOperations groupOperations;
   @Inject private RequestScopeOperations requestScopeOperations;
   @Inject private Sequences sequences;
 
@@ -365,6 +369,37 @@
     assertThat(resolve("doe")).containsExactly(id2);
   }
 
+  @Test
+  @GerritConfig(name = "accounts.visibility", value = "SAME_GROUP")
+  public void resolveAsUser_byFullName_accountThatIsNotVisibleToCurrentUserIsFound()
+      throws Exception {
+    Account.Id currentUser = accountOperations.newAccount().create();
+    Account.Id resolveAsUser = accountOperations.newAccount().create();
+    Account.Id userToBeFound = accountOperations.newAccount().fullname("Somebodys Name").create();
+
+    // Create a group that contains resolveAsUser and userToBeFound, so that resolveAsUser can see
+    // userToBeFound.
+    groupOperations.newGroup().addMember(resolveAsUser).addMember(userToBeFound).create();
+
+    // Verify that resolveAsUser can see userToBeFound.
+    assertThat(canSee(resolveAsUser, userToBeFound)).isTrue();
+
+    // Verify that currentUser cannot see userToBeFound
+    assertThat(canSee(currentUser, userToBeFound)).isFalse();
+
+    // Resolving userToBeFound as resolveAsUser should work even if the currentUser cannot see
+    // userToBeFound.
+    requestScopeOperations.setApiUser(currentUser);
+    String input = accountOperations.account(userToBeFound).get().fullname().get();
+    assertThat(resolveAsUser(resolveAsUser, input)).containsExactly(userToBeFound);
+  }
+
+  private boolean canSee(Account.Id currentUser, Account.Id userToBeSeen) {
+    return accountControlFactory
+        .get(identifiedUserFactory.create(currentUser))
+        .canSee(userToBeSeen);
+  }
+
   private ImmutableSet<Account.Id> resolve(Object input) throws Exception {
     return resolveAsResult(input).asIdSet();
   }
@@ -373,6 +408,16 @@
     return accountResolver.resolve(input.toString());
   }
 
+  private ImmutableSet<Account.Id> resolveAsUser(Account.Id resolveAsUser, Object input)
+      throws Exception {
+    return resolveAsUserAsResult(resolveAsUser, input).asIdSet();
+  }
+
+  private Result resolveAsUserAsResult(Account.Id resolveAsUser, Object input) throws Exception {
+    return accountResolver.resolveAsUser(
+        identifiedUserFactory.create(resolveAsUser), input.toString());
+  }
+
   @SuppressWarnings("deprecation")
   private ImmutableSet<Account.Id> resolveByNameOrEmail(Object input) throws Exception {
     return accountResolver.resolveByNameOrEmail(input.toString()).asIdSet();
diff --git a/javatests/com/google/gerrit/server/account/AccountResolverTest.java b/javatests/com/google/gerrit/server/account/AccountResolverTest.java
index 37728f7..34f746a 100644
--- a/javatests/com/google/gerrit/server/account/AccountResolverTest.java
+++ b/javatests/com/google/gerrit/server/account/AccountResolverTest.java
@@ -43,7 +43,6 @@
     private final String pattern;
     private final boolean shortCircuit;
     private final ImmutableList<AccountState> accounts;
-    private boolean assumeVisible;
     private boolean filterInactive;
 
     private TestSearcher(String pattern, boolean shortCircuit, AccountState... accounts) {
@@ -68,15 +67,6 @@
     }
 
     @Override
-    public boolean callerMayAssumeCandidatesAreVisible() {
-      return assumeVisible;
-    }
-
-    void setCallerMayAssumeCandidatesAreVisible() {
-      this.assumeVisible = true;
-    }
-
-    @Override
     public boolean callerShouldFilterOutInactiveCandidates() {
       return filterInactive;
     }
@@ -139,17 +129,6 @@
   }
 
   @Test
-  public void skipVisibilityCheck() throws Exception {
-    TestSearcher searcher = new TestSearcher("foo", false, newAccount(1), newAccount(2));
-    ImmutableList<Searcher<?>> searchers = ImmutableList.of(searcher);
-
-    assertThat(search("foo", searchers, only(2)).asIdSet()).containsExactlyElementsIn(ids(2));
-
-    searcher.setCallerMayAssumeCandidatesAreVisible();
-    assertThat(search("foo", searchers, only(2)).asIdSet()).containsExactlyElementsIn(ids(1, 2));
-  }
-
-  @Test
   public void dontFilterInactive() throws Exception {
     ImmutableList<Searcher<?>> searchers =
         ImmutableList.of(
diff --git a/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java b/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java
index bfeb947..e879170 100644
--- a/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java
+++ b/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java
@@ -58,7 +58,6 @@
             null,
             null,
             null,
-            null,
             null));
   }
 
diff --git a/javatests/com/google/gerrit/server/mail/send/MailSoySauceLoaderTest.java b/javatests/com/google/gerrit/server/mail/send/MailSoySauceLoaderTest.java
index fbeabe1..7f893f1 100644
--- a/javatests/com/google/gerrit/server/mail/send/MailSoySauceLoaderTest.java
+++ b/javatests/com/google/gerrit/server/mail/send/MailSoySauceLoaderTest.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.plugincontext.PluginContext.PluginMetrics;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.template.soy.shared.SoyAstCache;
 import java.nio.file.Paths;
 import org.junit.Before;
 import org.junit.Test;
@@ -40,9 +39,7 @@
   public void soyCompilation() {
     MailSoySauceLoader loader =
         new MailSoySauceLoader(
-            sitePaths,
-            new SoyAstCache(),
-            new PluginSetContext<>(set, PluginMetrics.DISABLED_INSTANCE));
+            sitePaths, new PluginSetContext<>(set, PluginMetrics.DISABLED_INSTANCE));
     assertThat(loader.load()).isNotNull(); // should not throw
   }
 }
diff --git a/modules/jgit b/modules/jgit
index a190130..66b871b 160000
--- a/modules/jgit
+++ b/modules/jgit
@@ -1 +1 @@
-Subproject commit a1901305b26ed5e0116f138bc02837713d2cf5c3
+Subproject commit 66b871b777c1a58337e80dd03db68bb76b145a93