Merge "Do not strip out "-- >8 --" comment in commit-msg hook"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 066d2ff..f5a226d 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1214,6 +1214,15 @@
 +
 Default is true.
 
+[[change.api.excludeMergeableInChangeInfo]]change.api.excludeMergeableInChangeInfo::
++
+If true, the mergeability bit in
+link:rest-api-changes.html#change-info[ChangeInfo] will never be set. It can
+be requested separately through the
+link:rest-api-changes.html#get-mergeable[get-mergeable] endpoint.
++
+Default is false.
+
 [[change.showAssigneeInChangesTable]]change.showAssigneeInChangesTable::
 +
 Show assignee field in changes table. If set to false, assignees will
diff --git a/Documentation/config-plugins.txt b/Documentation/config-plugins.txt
index 90cff4e..be09c7e 100644
--- a/Documentation/config-plugins.txt
+++ b/Documentation/config-plugins.txt
@@ -256,6 +256,17 @@
 link:https://gerrit.googlesource.com/plugins/emoticons/+doc/master/src/main/resources/Documentation/config.md[
 Configuration]
 
+[[find-owners]]
+=== find-owners
+This plugin provides (1) a change review action button `[FIND OWNERS]`
+that shows owners of changed files to be included as code reviewers, and
+(2) Prolog predicates to make sure that a CL is submittable
+only with owner Code-Review +1 votes.
+
+link:https://gerrit-review.googlesource.com/admin/repos/plugins/find-owners[Project] |
+link:https://gerrit.googlesource.com/plugins/find-owners/+doc/master/src/main/resources/Documentation/about.md[Documentation] |
+link:https://gerrit.googlesource.com/plugins/find-owners/+doc/master/src/main/resources/Documentation/config.md[Configuration]
+
 [[gitblit]]
 === gitblit
 
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index a0f8a35..325fbeb 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -309,6 +309,10 @@
 * `SKIP_MERGEABLE`: skip the `mergeable` field in
 link:#change-info[ChangeInfo]. For fast moving projects, this field must
 be recomputed often, which is slow for projects with big trees.
++
+This option is deprecated. In a future release, `mergeable` will not be
+populated in link:#change-info[ChangeInfo]. It can be requested separately
+by calling the link:#get-mergeable[get-mergeable] endpoint.
 --
 
 [[submittable]]
@@ -5770,7 +5774,9 @@
 |`mergeable`          |optional|
 Whether the change is mergeable. +
 Not set for merged changes, if the change has not yet been tested, or
-if the link:#skip_mergeable[skip_mergeable] option is set.
+if the link:#skip_mergeable[skip_mergeable] option is set or when
+link:config-gerrit.html#change.api.excludeMergeableInChangeInfo[change.api.excludeMergeableInChangeInfo]
+is set.
 |`submittable`        |optional|
 Whether the change has been approved by the project submit rules. +
 Only set if link:#submittable[requested].
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index d195f54..1712746 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -455,7 +455,9 @@
     ProjectInput in = projectInput(description);
     gApi.projects().create(in);
     project = new Project.NameKey(in.name);
-    testRepo = cloneProject(project, getCloneAsAccount(description));
+    if (!classDesc.skipProjectClone()) {
+      testRepo = cloneProject(project, getCloneAsAccount(description));
+    }
   }
 
   /** Override to bind an additional Guice module */
diff --git a/java/com/google/gerrit/acceptance/BUILD b/java/com/google/gerrit/acceptance/BUILD
index b590e64..5e59007 100644
--- a/java/com/google/gerrit/acceptance/BUILD
+++ b/java/com/google/gerrit/acceptance/BUILD
@@ -110,6 +110,7 @@
         "//java/com/google/gerrit/pgm/init",
         "//java/com/google/gerrit/reviewdb:server",
         "//java/com/google/gerrit/server",
+        "//java/com/google/gerrit/server/audit",
         "//java/com/google/gerrit/server/git/receive",
         "//java/com/google/gerrit/server/restapi",
         "//java/com/google/gerrit/server/schema",
diff --git a/java/com/google/gerrit/acceptance/FakeGroupAuditService.java b/java/com/google/gerrit/acceptance/FakeGroupAuditService.java
new file mode 100644
index 0000000..48dc408
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/FakeGroupAuditService.java
@@ -0,0 +1,95 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance;
+
+import static java.util.Comparator.comparing;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.primitives.Ints;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.httpd.GitOverHttpServlet;
+import com.google.gerrit.server.AuditEvent;
+import com.google.gerrit.server.audit.AuditListener;
+import com.google.gerrit.server.audit.AuditService;
+import com.google.gerrit.server.audit.HttpAuditEvent;
+import com.google.gerrit.server.audit.group.GroupAuditListener;
+import com.google.gerrit.server.group.GroupAuditService;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicLong;
+
+@Singleton
+public class FakeGroupAuditService extends AuditService {
+  public static class Module extends AbstractModule {
+    @Override
+    public void configure() {
+      DynamicSet.setOf(binder(), GroupAuditListener.class);
+      DynamicSet.setOf(binder(), AuditListener.class);
+
+      // Use this fake service at the Guice level rather than depending on tests binding their own
+      // audit listeners. If we used per-test listeners, then there would be a race between
+      // dispatching the audit events from HTTP requests performed during test setup in
+      // AbstractDaemonTest, and the later test setup binding the audit listener. Using a separate
+      // audit service implementation ensures all events get recorded.
+      bind(GroupAuditService.class).to(FakeGroupAuditService.class);
+    }
+  }
+
+  private final GitOverHttpServlet.Metrics httpMetrics;
+  private final BlockingQueue<HttpAuditEvent> httpEvents;
+  private final AtomicLong drainedSoFar;
+
+  @Inject
+  FakeGroupAuditService(
+      PluginSetContext<AuditListener> auditListeners,
+      PluginSetContext<GroupAuditListener> groupAuditListeners,
+      GitOverHttpServlet.Metrics httpMetrics) {
+    super(auditListeners, groupAuditListeners);
+    this.httpMetrics = httpMetrics;
+    this.httpEvents = new LinkedBlockingQueue<>();
+    this.drainedSoFar = new AtomicLong();
+  }
+
+  @Override
+  public void dispatch(AuditEvent action) {
+    super.dispatch(action);
+    if (action instanceof HttpAuditEvent) {
+      httpEvents.add((HttpAuditEvent) action);
+    }
+  }
+
+  public ImmutableList<HttpAuditEvent> drainHttpAuditEvents() throws Exception {
+    // Assumes that all HttpAuditEvents are produced by GitOverHttpServlet.
+    int expectedSize = Ints.checkedCast(httpMetrics.getRequestsStarted() - drainedSoFar.get());
+    List<HttpAuditEvent> result = new ArrayList<>();
+    for (int i = 0; i < expectedSize; i++) {
+      HttpAuditEvent e = httpEvents.poll(30, SECONDS);
+      if (e == null) {
+        throw new AssertionError(
+            String.format("Timeout after receiving %d/%d audit events", i, expectedSize));
+      }
+      drainedSoFar.incrementAndGet();
+      result.add(e);
+    }
+    return ImmutableList.sortedCopyOf(comparing(e -> e.when), result);
+  }
+}
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index f829842..570b59c 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -47,7 +47,6 @@
 import com.google.gerrit.server.util.SocketUtil;
 import com.google.gerrit.server.util.SystemLog;
 import com.google.gerrit.testing.FakeEmailSender;
-import com.google.gerrit.testing.FakeGroupAuditService;
 import com.google.gerrit.testing.InMemoryRepositoryManager;
 import com.google.gerrit.testing.SshMode;
 import com.google.inject.AbstractModule;
@@ -109,6 +108,7 @@
           !has(UseLocalDisk.class, testDesc.getTestClass()) && !forceLocalDisk(),
           !has(NoHttpd.class, testDesc.getTestClass()),
           has(Sandboxed.class, testDesc.getTestClass()),
+          has(SkipProjectClone.class, testDesc.getTestClass()),
           has(UseSsh.class, testDesc.getTestClass()),
           null, // @GerritConfig is only valid on methods.
           null, // @GerritConfigs is only valid on methods.
@@ -128,6 +128,8 @@
               && !has(NoHttpd.class, testDesc.getTestClass()),
           testDesc.getAnnotation(Sandboxed.class) != null
               || has(Sandboxed.class, testDesc.getTestClass()),
+          testDesc.getAnnotation(SkipProjectClone.class) != null
+              || has(SkipProjectClone.class, testDesc.getTestClass()),
           testDesc.getAnnotation(UseSsh.class) != null
               || has(UseSsh.class, testDesc.getTestClass()),
           testDesc.getAnnotation(GerritConfig.class),
@@ -156,6 +158,8 @@
 
     abstract boolean sandboxed();
 
+    abstract boolean skipProjectClone();
+
     abstract boolean useSshAnnotation();
 
     boolean useSsh() {
diff --git a/java/com/google/gerrit/acceptance/SkipProjectClone.java b/java/com/google/gerrit/acceptance/SkipProjectClone.java
new file mode 100644
index 0000000..9a326d8
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/SkipProjectClone.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2019 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.acceptance;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Target({TYPE, METHOD})
+@Retention(RUNTIME)
+public @interface SkipProjectClone {}
diff --git a/java/com/google/gerrit/common/BUILD b/java/com/google/gerrit/common/BUILD
index f056f86..df5a4bb 100644
--- a/java/com/google/gerrit/common/BUILD
+++ b/java/com/google/gerrit/common/BUILD
@@ -1,6 +1,5 @@
 ANNOTATIONS = [
     "Nullable.java",
-    "audit/Audit.java",
 ]
 
 java_library(
diff --git a/java/com/google/gerrit/common/ProjectUtil.java b/java/com/google/gerrit/common/ProjectUtil.java
deleted file mode 100644
index bfd5ef9..0000000
--- a/java/com/google/gerrit/common/ProjectUtil.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.common;
-
-public class ProjectUtil {
-  public static String stripGitSuffix(String name) {
-    if (name.endsWith(".git")) {
-      // Be nice and drop the trailing ".git" suffix, which we never keep
-      // in our database, but clients might mistakenly provide anyway.
-      //
-      name = name.substring(0, name.length() - 4);
-      while (name.endsWith("/")) {
-        name = name.substring(0, name.length() - 1);
-      }
-    }
-    return name;
-  }
-
-  private ProjectUtil() {}
-}
diff --git a/java/com/google/gerrit/common/audit/Audit.java b/java/com/google/gerrit/common/audit/Audit.java
deleted file mode 100644
index a791e97..0000000
--- a/java/com/google/gerrit/common/audit/Audit.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2012 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.common.audit;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Audit annotation for JSON/RPC interfaces.
- *
- * <p>Flag with @Audit all the JSON/RPC methods to be traced in audit-trail and submitted to the
- * GroupAuditService.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD})
-public @interface Audit {
-  String action() default "";
-
-  /** List of positions of parameters to be obfuscated in audit-trail (i.e. passwords) */
-  int[] obfuscate() default {};
-}
diff --git a/java/com/google/gerrit/extensions/client/ListChangesOption.java b/java/com/google/gerrit/extensions/client/ListChangesOption.java
index 5e4a3a7..6382686 100644
--- a/java/com/google/gerrit/extensions/client/ListChangesOption.java
+++ b/java/com/google/gerrit/extensions/client/ListChangesOption.java
@@ -78,6 +78,7 @@
   TRACKING_IDS(21),
 
   /** Skip mergeability data */
+  @Deprecated
   SKIP_MERGEABLE(22);
 
   private final int value;
diff --git a/java/com/google/gerrit/httpd/GitOverHttpServlet.java b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index 7fbb8d7..c97ee10 100644
--- a/java/com/google/gerrit/httpd/GitOverHttpServlet.java
+++ b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.httpd;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.cache.Cache;
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ListMultimap;
@@ -54,6 +55,7 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
@@ -129,6 +131,25 @@
                   .expireAfterWrite(Duration.ofMinutes(10));
             }
           });
+
+      // Don't bind Metrics, which is bound in a parent injector in tests.
+    }
+  }
+
+  @VisibleForTesting
+  @Singleton
+  public static class Metrics {
+    // Recording requests separately in this class is only necessary because of a bug in the
+    // implementation of the generic RequestMetricsFilter; see
+    // https://gerrit-review.googlesource.com/c/gerrit/+/211692
+    private final AtomicLong requestsStarted = new AtomicLong();
+
+    void requestStarted() {
+      requestsStarted.incrementAndGet();
+    }
+
+    public long getRequestsStarted() {
+      return requestsStarted.get();
     }
   }
 
@@ -317,22 +338,26 @@
     private final PermissionBackend permissionBackend;
     private final Provider<CurrentUser> userProvider;
     private final GroupAuditService groupAuditService;
+    private final Metrics metrics;
 
     @Inject
     UploadFilter(
         UploadValidators.Factory uploadValidatorsFactory,
         PermissionBackend permissionBackend,
         Provider<CurrentUser> userProvider,
-        GroupAuditService groupAuditService) {
+        GroupAuditService groupAuditService,
+        Metrics metrics) {
       this.uploadValidatorsFactory = uploadValidatorsFactory;
       this.permissionBackend = permissionBackend;
       this.userProvider = userProvider;
       this.groupAuditService = groupAuditService;
+      this.metrics = metrics;
     }
 
     @Override
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain next)
         throws IOException, ServletException {
+      metrics.requestStarted();
       // The Resolver above already checked READ access for us.
       Repository repo = ServletUtils.getRepository(request);
       ProjectState state = (ProjectState) request.getAttribute(ATT_STATE);
@@ -431,22 +456,26 @@
     private final PermissionBackend permissionBackend;
     private final Provider<CurrentUser> userProvider;
     private final GroupAuditService groupAuditService;
+    private final Metrics metrics;
 
     @Inject
     ReceiveFilter(
         @Named(ID_CACHE) Cache<AdvertisedObjectsCacheKey, Set<ObjectId>> cache,
         PermissionBackend permissionBackend,
         Provider<CurrentUser> userProvider,
-        GroupAuditService groupAuditService) {
+        GroupAuditService groupAuditService,
+        Metrics metrics) {
       this.cache = cache;
       this.permissionBackend = permissionBackend;
       this.userProvider = userProvider;
       this.groupAuditService = groupAuditService;
+      this.metrics = metrics;
     }
 
     @Override
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
         throws IOException, ServletException {
+      metrics.requestStarted();
       boolean isGet = "GET".equalsIgnoreCase(((HttpServletRequest) request).getMethod());
 
       AsyncReceiveCommits arc = (AsyncReceiveCommits) request.getAttribute(ATT_ARC);
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiQuotaEnforcer.java b/java/com/google/gerrit/httpd/restapi/RestApiQuotaEnforcer.java
index 61132e7..a3aba6d 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiQuotaEnforcer.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiQuotaEnforcer.java
@@ -21,7 +21,7 @@
 import com.google.gerrit.server.quota.QuotaBackend;
 import com.google.gerrit.server.quota.QuotaException;
 import com.google.gerrit.util.http.RequestUtil;
-import javax.inject.Inject;
+import com.google.inject.Inject;
 import javax.servlet.http.HttpServletRequest;
 
 /**
@@ -32,7 +32,7 @@
  * <ul>
  *   <li>GET /a/accounts/self/detail => /restapi/accounts/detail:GET
  *   <li>GET /changes/123/revisions/current/detail => /restapi/changes/revisions/detail:GET
- *   <li>PUT /changes/10/reviewed => /changes/reviewed:PUT
+ *   <li>PUT /changes/10/reviewed => /restapi/changes/reviewed:PUT
  * </ul>
  *
  * <p>Adds context (change, project, account) to the quota check if the call is for an existing
diff --git a/java/com/google/gerrit/server/ProjectUtil.java b/java/com/google/gerrit/server/ProjectUtil.java
index 1a327db..fc14768 100644
--- a/java/com/google/gerrit/server/ProjectUtil.java
+++ b/java/com/google/gerrit/server/ProjectUtil.java
@@ -44,4 +44,17 @@
       return exists;
     }
   }
+
+  public static String stripGitSuffix(String name) {
+    if (name.endsWith(".git")) {
+      // Be nice and drop the trailing ".git" suffix, which we never keep
+      // in our database, but clients might mistakenly provide anyway.
+      //
+      name = name.substring(0, name.length() - 4);
+      while (name.endsWith("/")) {
+        name = name.substring(0, name.length() - 1);
+      }
+    }
+    return name;
+  }
 }
diff --git a/java/com/google/gerrit/server/args4j/ProjectHandler.java b/java/com/google/gerrit/server/args4j/ProjectHandler.java
index 223b112..f33a4ed 100644
--- a/java/com/google/gerrit/server/args4j/ProjectHandler.java
+++ b/java/com/google/gerrit/server/args4j/ProjectHandler.java
@@ -17,9 +17,9 @@
 import static com.google.gerrit.util.cli.Localizable.localizable;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.ProjectUtil;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.ProjectUtil;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index 28f9618..fa38139 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -83,6 +83,7 @@
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.account.AccountInfoComparator;
 import com.google.gerrit.server.account.AccountLoader;
+import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.TrackingFooters;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.notedb.ChangeNotes;
@@ -111,6 +112,7 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Supplier;
+import org.eclipse.jgit.lib.Config;
 
 /**
  * Produces {@link ChangeInfo} (which is serialized to JSON afterwards) from {@link ChangeData}.
@@ -216,6 +218,7 @@
   private final Metrics metrics;
   private final RevisionJson revisionJson;
   private final Optional<PluginDefinedAttributesFactory> pluginDefinedAttributesFactory;
+  private final boolean excludeMergeableInChangeInfo;
   private final boolean lazyLoad;
 
   private AccountLoader accountLoader;
@@ -236,6 +239,7 @@
       TrackingFooters trackingFooters,
       Metrics metrics,
       RevisionJson.Factory revisionJsonFactory,
+      @GerritServerConfig Config cfg,
       @Assisted Iterable<ListChangesOption> options,
       @Assisted Optional<PluginDefinedAttributesFactory> pluginDefinedAttributesFactory) {
     this.userProvider = user;
@@ -252,6 +256,8 @@
     this.metrics = metrics;
     this.revisionJson = revisionJsonFactory.create(options);
     this.options = Sets.immutableEnumSet(options);
+    this.excludeMergeableInChangeInfo =
+        cfg.getBoolean("change", "api", "excludeMergeableInChangeInfo", false);
     this.lazyLoad = containsAnyOf(this.options, REQUIRE_LAZY_LOAD);
     this.pluginDefinedAttributesFactory = pluginDefinedAttributesFactory;
 
@@ -509,7 +515,7 @@
       if (str.isOk()) {
         out.submitType = str.type;
       }
-      if (!has(SKIP_MERGEABLE)) {
+      if (!excludeMergeableInChangeInfo && !has(SKIP_MERGEABLE)) {
         out.mergeable = cd.isMergeable();
       }
       if (has(SUBMITTABLE)) {
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 3f9cb60..45847c8 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -122,8 +122,8 @@
 
   static final int MAX_ACCOUNTS_PER_DEFAULT_FIELD = 10;
 
-  // NOTE: As new search operations are added, please keep the
-  // SearchSuggestOracle up to date.
+  // NOTE: As new search operations are added, please keep the suggestions in
+  // gr-search-bar.js up to date.
 
   public static final String FIELD_ADDED = "added";
   public static final String FIELD_AGE = "age";
@@ -735,13 +735,16 @@
   }
 
   @Operator
-  public Predicate<ChangeData> ext(String ext) {
+  public Predicate<ChangeData> ext(String ext) throws QueryParseException {
     return extension(ext);
   }
 
   @Operator
-  public Predicate<ChangeData> extension(String ext) {
-    return new FileExtensionPredicate(ext);
+  public Predicate<ChangeData> extension(String ext) throws QueryParseException {
+    if (args.getSchema().hasField(ChangeField.EXTENSION)) {
+      return new FileExtensionPredicate(ext);
+    }
+    throw new QueryParseException("'extension' operator is not supported by change index version");
   }
 
   @Operator
diff --git a/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java b/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java
index 1f4f48c..479bb52 100644
--- a/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java
+++ b/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java
@@ -24,11 +24,11 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
 import com.google.gerrit.server.plugincontext.PluginSetEntryContext;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
 import java.util.ArrayList;
 import java.util.List;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.inject.Singleton;
 
 @Singleton
 public class DefaultQuotaBackend implements QuotaBackend {
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index c7bdda8..c712e31 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -30,6 +30,7 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
@@ -196,6 +197,12 @@
 
     Project.NameKey project = rsrc.getNameKey();
     String refName = RefNames.fullName(input.branch);
+    try {
+      permissionBackend.currentUser().project(project).ref(refName).check(RefPermission.READ);
+    } catch (AuthException e) {
+      throw new ResourceNotFoundException(String.format("ref %s not found", refName));
+    }
+
     permissionBackend
         .currentUser()
         .project(project)
diff --git a/java/com/google/gerrit/server/restapi/project/CreateProject.java b/java/com/google/gerrit/server/restapi/project/CreateProject.java
index 3785784..c703fe2 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateProject.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateProject.java
@@ -19,7 +19,6 @@
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
-import com.google.gerrit.common.ProjectUtil;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.api.projects.ConfigInput;
@@ -35,6 +34,7 @@
 import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.ProjectUtil;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.config.ProjectOwnerGroupsProvider;
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java
index 5be98d2..da5f7b9 100644
--- a/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java
@@ -22,9 +22,9 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.IntBlob;
 import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.Optional;
-import javax.inject.Inject;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevWalk;
diff --git a/java/com/google/gerrit/testing/FakeGroupAuditService.java b/java/com/google/gerrit/testing/FakeGroupAuditService.java
deleted file mode 100644
index 7765e02..0000000
--- a/java/com/google/gerrit/testing/FakeGroupAuditService.java
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.testing;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.server.AuditEvent;
-import com.google.gerrit.server.audit.AuditListener;
-import com.google.gerrit.server.audit.group.GroupAuditListener;
-import com.google.gerrit.server.audit.group.GroupMemberAuditEvent;
-import com.google.gerrit.server.audit.group.GroupSubgroupAuditEvent;
-import com.google.gerrit.server.group.GroupAuditService;
-import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.inject.AbstractModule;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.List;
-
-@Singleton
-public class FakeGroupAuditService implements GroupAuditService {
-
-  protected final PluginSetContext<GroupAuditListener> groupAuditListeners;
-  protected final PluginSetContext<AuditListener> auditListeners;
-
-  public final List<AuditEvent> auditEvents = new ArrayList<>();
-
-  public static class Module extends AbstractModule {
-    @Override
-    public void configure() {
-      DynamicSet.setOf(binder(), GroupAuditListener.class);
-      DynamicSet.setOf(binder(), AuditListener.class);
-      bind(GroupAuditService.class).to(FakeGroupAuditService.class);
-    }
-  }
-
-  @Inject
-  public FakeGroupAuditService(
-      PluginSetContext<GroupAuditListener> groupAuditListeners,
-      PluginSetContext<AuditListener> auditListeners) {
-    this.groupAuditListeners = groupAuditListeners;
-    this.auditListeners = auditListeners;
-  }
-
-  public void clearEvents() {
-    auditEvents.clear();
-  }
-
-  @Override
-  public void dispatch(AuditEvent action) {
-    synchronized (auditEvents) {
-      auditEvents.add(action);
-      auditEvents.notifyAll();
-    }
-  }
-
-  @Override
-  public void dispatchAddMembers(
-      Account.Id actor,
-      AccountGroup.UUID updatedGroup,
-      ImmutableSet<Account.Id> addedMembers,
-      Timestamp addedOn) {
-    GroupMemberAuditEvent event =
-        GroupMemberAuditEvent.create(actor, updatedGroup, addedMembers, addedOn);
-    groupAuditListeners.runEach(l -> l.onAddMembers(event));
-  }
-
-  @Override
-  public void dispatchDeleteMembers(
-      Account.Id actor,
-      AccountGroup.UUID updatedGroup,
-      ImmutableSet<Account.Id> deletedMembers,
-      Timestamp deletedOn) {
-    GroupMemberAuditEvent event =
-        GroupMemberAuditEvent.create(actor, updatedGroup, deletedMembers, deletedOn);
-    groupAuditListeners.runEach(l -> l.onDeleteMembers(event));
-  }
-
-  @Override
-  public void dispatchAddSubgroups(
-      Account.Id actor,
-      AccountGroup.UUID updatedGroup,
-      ImmutableSet<AccountGroup.UUID> addedSubgroups,
-      Timestamp addedOn) {
-    GroupSubgroupAuditEvent event =
-        GroupSubgroupAuditEvent.create(actor, updatedGroup, addedSubgroups, addedOn);
-    groupAuditListeners.runEach(l -> l.onAddSubgroups(event));
-  }
-
-  @Override
-  public void dispatchDeleteSubgroups(
-      Account.Id actor,
-      AccountGroup.UUID updatedGroup,
-      ImmutableSet<AccountGroup.UUID> deletedSubgroups,
-      Timestamp deletedOn) {
-    GroupSubgroupAuditEvent event =
-        GroupSubgroupAuditEvent.create(actor, updatedGroup, deletedSubgroups, deletedOn);
-    groupAuditListeners.runEach(l -> l.onDeleteSubgroups(event));
-  }
-}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 7bb7c01..04e23ae 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -254,6 +254,14 @@
   }
 
   @Test
+  @GerritConfig(name = "change.api.excludeMergeableInChangeInfo", value = "true")
+  public void excludeMergeableInChangeInfo() throws Exception {
+    PushOneCommit.Result r = createChange();
+    ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
+    assertThat(c.mergeable).isNull();
+  }
+
+  @Test
   public void setPrivateByOwner() throws Exception {
     TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
     PushOneCommit.Result result =
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 0fc3c80..143a4a4 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -50,6 +50,7 @@
 import com.google.gerrit.acceptance.GitUtil;
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.Sandboxed;
+import com.google.gerrit.acceptance.SkipProjectClone;
 import com.google.gerrit.acceptance.TestAccount;
 import com.google.gerrit.acceptance.TestProjectInput;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
@@ -125,6 +126,7 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+@SkipProjectClone
 public abstract class AbstractPushForReview extends AbstractDaemonTest {
   protected enum Protocol {
     // TODO(dborowitz): TEST.
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java b/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java
index 58051bb..d1349d0 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java
@@ -26,10 +26,10 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.project.ProjectConfig;
+import com.google.inject.Inject;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.StreamSupport;
-import javax.inject.Inject;
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheBuilder;
 import org.eclipse.jgit.dircache.DirCacheEditor;
diff --git a/javatests/com/google/gerrit/acceptance/git/GitOverHttpServletIT.java b/javatests/com/google/gerrit/acceptance/git/GitOverHttpServletIT.java
deleted file mode 100644
index 26ace25..0000000
--- a/javatests/com/google/gerrit/acceptance/git/GitOverHttpServletIT.java
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.acceptance.git;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.gerrit.acceptance.Sandboxed;
-import com.google.gerrit.server.AuditEvent;
-import com.google.gerrit.server.audit.HttpAuditEvent;
-import com.google.gerrit.testing.FakeGroupAuditService;
-import java.util.Collections;
-import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jgit.transport.CredentialsProvider;
-import org.eclipse.jgit.transport.RefSpec;
-import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
-import org.junit.Before;
-import org.junit.Test;
-
-public class GitOverHttpServletIT extends AbstractPushForReview {
-  private static final long AUDIT_EVENT_TIMEOUT = 500L;
-
-  @Before
-  public void beforeEach() throws Exception {
-    CredentialsProvider.setDefault(
-        new UsernamePasswordCredentialsProvider(admin.username, admin.httpPassword));
-    selectProtocol(AbstractPushForReview.Protocol.HTTP);
-  }
-
-  @Test
-  @Sandboxed
-  public void receivePackAuditEventLog() throws Exception {
-    FakeGroupAuditService auditService = clearAuditService();
-    testRepo
-        .git()
-        .push()
-        .setRemote("origin")
-        .setRefSpecs(new RefSpec("HEAD:refs/for/master"))
-        .call();
-    waitForAudit(auditService);
-
-    // Git smart protocol makes two requests:
-    // https://github.com/git/git/blob/master/Documentation/technical/http-protocol.txt
-    assertThat(auditService.auditEvents.size()).isEqualTo(2);
-
-    AuditEvent e = auditService.auditEvents.get(1);
-    assertThat(e.who.getAccountId()).isEqualTo(admin.id);
-    assertThat(e.what).endsWith("/git-receive-pack");
-    assertThat(e.params).isEmpty();
-    assertThat(((HttpAuditEvent) e).httpStatus).isEqualTo(HttpServletResponse.SC_OK);
-  }
-
-  @Test
-  @Sandboxed
-  public void uploadPackAuditEventLog() throws Exception {
-    FakeGroupAuditService auditService = clearAuditService();
-    testRepo.git().fetch().call();
-    waitForAudit(auditService);
-
-    assertThat(auditService.auditEvents.size()).isEqualTo(1);
-
-    AuditEvent e = auditService.auditEvents.get(0);
-    assertThat(e.who.toString()).isEqualTo("ANONYMOUS");
-    assertThat(e.params.get("service"))
-        .containsExactlyElementsIn(Collections.singletonList("git-upload-pack"));
-    assertThat(e.what).endsWith("service=git-upload-pack");
-    assertThat(((HttpAuditEvent) e).httpStatus).isEqualTo(HttpServletResponse.SC_OK);
-  }
-
-  private FakeGroupAuditService clearAuditService() {
-    FakeGroupAuditService auditService =
-        server.getTestInjector().getInstance(FakeGroupAuditService.class);
-    auditService.clearEvents();
-    return auditService;
-  }
-
-  private void waitForAudit(FakeGroupAuditService auditService) throws InterruptedException {
-    synchronized (auditService.auditEvents) {
-      auditService.auditEvents.wait(AUDIT_EVENT_TIMEOUT);
-    }
-  }
-}
diff --git a/javatests/com/google/gerrit/acceptance/git/HttpPushForReviewIT.java b/javatests/com/google/gerrit/acceptance/git/HttpPushForReviewIT.java
index ed17c38..6309e79 100644
--- a/javatests/com/google/gerrit/acceptance/git/HttpPushForReviewIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/HttpPushForReviewIT.java
@@ -14,15 +14,82 @@
 
 package com.google.gerrit.acceptance.git;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.FakeGroupAuditService;
+import com.google.gerrit.server.AnonymousUser;
+import com.google.gerrit.server.audit.HttpAuditEvent;
+import com.google.inject.Inject;
+import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.RefSpec;
 import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
 import org.junit.Before;
+import org.junit.Test;
 
 public class HttpPushForReviewIT extends AbstractPushForReview {
+  @Inject private FakeGroupAuditService auditService;
+
   @Before
   public void selectHttpUrl() throws Exception {
     CredentialsProvider.setDefault(
         new UsernamePasswordCredentialsProvider(admin.username, admin.httpPassword));
     selectProtocol(Protocol.HTTP);
+    // Don't clear audit events here, since we can't guarantee all test setup has run yet.
+  }
+
+  @Test
+  public void receivePackAuditEventLog() throws Exception {
+    auditService.drainHttpAuditEvents();
+    testRepo
+        .git()
+        .push()
+        .setRemote("origin")
+        .setRefSpecs(new RefSpec("HEAD:refs/for/master"))
+        .call();
+
+    ImmutableList<HttpAuditEvent> auditEvents = auditService.drainHttpAuditEvents();
+    assertThat(auditEvents).hasSize(2);
+
+    HttpAuditEvent lsRemote = auditEvents.get(0);
+    assertThat(lsRemote.who.getAccountId()).isEqualTo(admin.id);
+    assertThat(lsRemote.what).endsWith("/info/refs?service=git-receive-pack");
+    assertThat(lsRemote.params).containsExactly("service", "git-receive-pack");
+    assertThat(lsRemote.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
+
+    HttpAuditEvent receivePack = auditEvents.get(1);
+    assertThat(receivePack.who.getAccountId()).isEqualTo(admin.id);
+    assertThat(receivePack.what).endsWith("/git-receive-pack");
+    assertThat(receivePack.params).isEmpty();
+    assertThat(receivePack.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
+  }
+
+  @Test
+  public void uploadPackAuditEventLog() throws Exception {
+    auditService.drainHttpAuditEvents();
+    // testRepo is already a clone. Make a server-side change so we have something to fetch.
+    try (Repository repo = repoManager.openRepository(project)) {
+      new TestRepository<>(repo).branch("master").commit().create();
+    }
+    testRepo.git().fetch().call();
+
+    ImmutableList<HttpAuditEvent> auditEvents = auditService.drainHttpAuditEvents();
+    assertThat(auditEvents).hasSize(2);
+
+    HttpAuditEvent lsRemote = auditEvents.get(0);
+    // Repo URL doesn't include /a, so fetching doesn't cause authentication.
+    assertThat(lsRemote.who).isInstanceOf(AnonymousUser.class);
+    assertThat(lsRemote.what).endsWith("/info/refs?service=git-upload-pack");
+    assertThat(lsRemote.params).containsExactly("service", "git-upload-pack");
+    assertThat(lsRemote.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
+
+    HttpAuditEvent uploadPack = auditEvents.get(1);
+    assertThat(lsRemote.who).isInstanceOf(AnonymousUser.class);
+    assertThat(uploadPack.what).endsWith("/git-upload-pack");
+    assertThat(uploadPack.params).isEmpty();
+    assertThat(uploadPack.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
index 08daa18..5480ed1 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -268,16 +268,6 @@
   }
 
   @Test
-  public void createChangeOnInvisibleBranchFails() throws Exception {
-    changeInTwoBranches("invisible-branch", "a.txt", "branchB", "b.txt");
-    block(project, "refs/heads/invisible-branch", READ, REGISTERED_USERS);
-
-    ChangeInput in = newChangeInput(ChangeStatus.NEW);
-    in.branch = "invisible-branch";
-    assertCreateFails(in, ResourceNotFoundException.class, "");
-  }
-
-  @Test
   public void noteDbCommit() throws Exception {
     ChangeInfo c = assertCreateSucceeds(newChangeInput(ChangeStatus.NEW));
     try (Repository repo = repoManager.openRepository(project);
@@ -456,6 +446,30 @@
     assertThat(revInfo.commit.message).isEqualTo(input.message + "\n");
   }
 
+  @Test
+  public void createChangeOnExistingBranchNotPermitted() throws Exception {
+    createBranch(new Branch.NameKey(project, "foo"));
+    blockRead("refs/heads/*");
+    requestScopeOperations.setApiUser(user.id);
+    ChangeInput input = newChangeInput(ChangeStatus.NEW);
+    input.branch = "foo";
+
+    assertCreateFails(input, ResourceNotFoundException.class, "ref refs/heads/foo not found");
+  }
+
+  @Test
+  public void createChangeOnNonExistingBranchNotPermitted() throws Exception {
+    blockRead("refs/heads/*");
+    requestScopeOperations.setApiUser(user.id);
+    ChangeInput input = newChangeInput(ChangeStatus.NEW);
+    input.branch = "foo";
+    // sets this option to be true to make sure permission check happened before this option could
+    // be considered.
+    input.newBranch = true;
+
+    assertCreateFails(input, ResourceNotFoundException.class, "ref refs/heads/foo not found");
+  }
+
   private ChangeInput newChangeInput(ChangeStatus status) {
     ChangeInput in = new ChangeInput();
     in.project = project.get();
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index bb27ad0..79eae2a 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -1366,7 +1366,14 @@
 
   @Test
   public void byExtension() throws Exception {
-    assume().that(getSchema().hasField(ChangeField.EXTENSION)).isTrue();
+    if (getSchemaVersion() < 52) {
+      assertMissingField(ChangeField.EXTENSION);
+      String unsupportedOperatorMsg =
+          "'extension' operator is not supported by change index version";
+      assertFailingQuery("extension:txt", unsupportedOperatorMsg);
+      assertFailingQuery("ext:txt", unsupportedOperatorMsg);
+      return;
+    }
 
     TestRepository<Repo> repo = createProject("repo");
     Change change1 = insert(repo, newChangeWithFiles(repo, "foo.h", "foo.cc"));
diff --git a/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java b/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java
index 9490e38..e5890c9 100644
--- a/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java
+++ b/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java
@@ -77,8 +77,9 @@
     inMemoryModule.inject(this);
 
     // Creates an empty All-Projects.
-    Repository repo = repoManager.createRepository(allProjectsName);
-    repo.close();
+    try (Repository repo = repoManager.createRepository(allProjectsName)) {
+      // Intentionally empty.
+    }
   }
 
   @Test
diff --git a/lib/commons/BUILD b/lib/commons/BUILD
index 93d3c2f..e8de396 100644
--- a/lib/commons/BUILD
+++ b/lib/commons/BUILD
@@ -44,6 +44,13 @@
 )
 
 java_library(
+    name = "text",
+    data = ["//lib:LICENSE-Apache2.0"],
+    visibility = ["//visibility:public"],
+    exports = ["@commons-text//jar"],
+)
+
+java_library(
     name = "validator",
     data = ["//lib:LICENSE-Apache2.0"],
     exports = ["@commons-validator//jar"],
diff --git a/lib/gitiles/BUILD b/lib/gitiles/BUILD
index 86b6303..b1bbca1 100644
--- a/lib/gitiles/BUILD
+++ b/lib/gitiles/BUILD
@@ -4,11 +4,12 @@
     exports = [
         ":cm-autolink",
         ":commonmark",
-        ":commons-text",
         ":gfm-strikethrough",
         ":gfm-tables",
         ":gitiles-servlet",
         ":prettify",
+        "//lib/commons:lang3",
+        "//lib/commons:text",
     ],
 )
 
@@ -20,13 +21,6 @@
 )
 
 java_library(
-    name = "commons-text",
-    data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
-    exports = ["@commons-text//jar"],
-)
-
-java_library(
     name = "commonmark",
     data = ["//lib:LICENSE-commonmark"],
     visibility = ["//visibility:public"],
diff --git a/lib/highlightjs/highlight.min.js b/lib/highlightjs/highlight.min.js
index d045b9d..afe3f54 100644
--- a/lib/highlightjs/highlight.min.js
+++ b/lib/highlightjs/highlight.min.js
@@ -1,13 +1,13 @@
 /*
- highlight.js v9.13.1 | BSD3 License | git.io/hljslicense */
+ highlight.js v9.14.0 | BSD3 License | git.io/hljslicense */
 var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(b){var l=0;return function(){return l<b.length?{done:!1,value:b[l++]}:{done:!0}}};$jscomp.arrayIterator=function(b){return{next:$jscomp.arrayIteratorImpl(b)}};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;
 $jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(b,l,t){b!=Array.prototype&&b!=Object.prototype&&(b[l]=t.value)};$jscomp.getGlobal=function(b){return"undefined"!=typeof window&&window===b?b:"undefined"!=typeof global&&null!=global?global:b};$jscomp.global=$jscomp.getGlobal(this);$jscomp.SYMBOL_PREFIX="jscomp_symbol_";$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};
 $jscomp.Symbol=function(){var b=0;return function(l){return $jscomp.SYMBOL_PREFIX+(l||"")+b++}}();$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var b=$jscomp.global.Symbol.iterator;b||(b=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[b]&&$jscomp.defineProperty(Array.prototype,b,{configurable:!0,writable:!0,value:function(){return $jscomp.iteratorPrototype($jscomp.arrayIteratorImpl(this))}});$jscomp.initSymbolIterator=function(){}};
 $jscomp.initSymbolAsyncIterator=function(){$jscomp.initSymbol();var b=$jscomp.global.Symbol.asyncIterator;b||(b=$jscomp.global.Symbol.asyncIterator=$jscomp.global.Symbol("asyncIterator"));$jscomp.initSymbolAsyncIterator=function(){}};$jscomp.iteratorPrototype=function(b){$jscomp.initSymbolIterator();b={next:b};b[$jscomp.global.Symbol.iterator]=function(){return this};return b};
 $jscomp.iteratorFromArray=function(b,l){$jscomp.initSymbolIterator();b instanceof String&&(b+="");var t=0,q={next:function(){if(t<b.length){var u=t++;return{value:l(u,b[u]),done:!1}}q.next=function(){return{done:!0,value:void 0}};return q.next()}};q[Symbol.iterator]=function(){return q};return q};
 $jscomp.polyfill=function(b,l,t,q){if(l){t=$jscomp.global;b=b.split(".");for(q=0;q<b.length-1;q++){var u=b[q];u in t||(t[u]={});t=t[u]}b=b[b.length-1];q=t[b];l=l(q);l!=q&&null!=l&&$jscomp.defineProperty(t,b,{configurable:!0,writable:!0,value:l})}};$jscomp.polyfill("Array.prototype.keys",function(b){return b?b:function(){return $jscomp.iteratorFromArray(this,function(b){return b})}},"es6","es3");
-(function(b){var l="object"===typeof window&&window||"object"===typeof self&&self;"undefined"!==typeof exports?b(exports):l&&(l.hljs=b({}),"function"===typeof define&&define.amd&&define([],function(){return l.hljs}))})(function(b){function l(a){return a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function t(a,c){return(a=a&&a.exec(c))&&0===a.index}function q(a){var c,b={},e=Array.prototype.slice.call(arguments,1);for(c in a)b[c]=a[c];e.forEach(function(a){for(c in a)b[c]=a[c]});
-return b}function u(a){var c=[];(function g(a,b){for(a=a.firstChild;a;a=a.nextSibling)3===a.nodeType?b+=a.nodeValue.length:1===a.nodeType&&(c.push({event:"start",offset:b,node:a}),b=g(a,b),a.nodeName.toLowerCase().match(/br|hr|img|input/)||c.push({event:"stop",offset:b,node:a}));return b})(a,0);return c}function L(a,c,b){function d(){return a.length&&c.length?a[0].offset!==c[0].offset?a[0].offset<c[0].offset?a:c:"start"===c[0].event?a:c:a.length?a:c}function f(a){p+="<"+a.nodeName.toLowerCase()+G.map.call(a.attributes,
+(function(b){var l="object"===typeof window&&window||"object"===typeof self&&self;"undefined"!==typeof exports?b(exports):l&&(l.hljs=b({}),"function"===typeof define&&define.amd&&define([],function(){return l.hljs}))})(function(b){function l(a){return a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function t(a,c){return(a=a&&a.exec(c))&&0===a.index}function q(a){var c,d={},b=Array.prototype.slice.call(arguments,1);for(c in a)d[c]=a[c];b.forEach(function(a){for(c in a)d[c]=a[c]});
+return d}function u(a){var c=[];(function g(a,b){for(a=a.firstChild;a;a=a.nextSibling)3===a.nodeType?b+=a.nodeValue.length:1===a.nodeType&&(c.push({event:"start",offset:b,node:a}),b=g(a,b),a.nodeName.toLowerCase().match(/br|hr|img|input/)||c.push({event:"stop",offset:b,node:a}));return b})(a,0);return c}function L(a,c,b){function d(){return a.length&&c.length?a[0].offset!==c[0].offset?a[0].offset<c[0].offset?a:c:"start"===c[0].event?a:c:a.length?a:c}function f(a){p+="<"+a.nodeName.toLowerCase()+G.map.call(a.attributes,
 function(a){return" "+a.nodeName+'="'+l(a.value).replace('"',"&quot;")+'"'}).join("")+">"}function g(a){p+="</"+a.nodeName.toLowerCase()+">"}function k(a){("start"===a.event?f:g)(a.node)}for(var m=0,p="",n=[];a.length||c.length;){var h=d();p+=l(b.substring(m,h[0].offset));m=h[0].offset;if(h===a){n.reverse().forEach(g);do k(h.splice(0,1)[0]),h=d();while(h===a&&h.length&&h[0].offset===m);n.reverse().forEach(f)}else"start"===h[0].event?n.push(h[0].node):n.pop(),k(h.splice(0,1)[0])}return p+l(b.substr(m))}
 function M(a){a.variants&&!a.cached_variants&&(a.cached_variants=a.variants.map(function(c){return q(a,{variants:null},c)}));return a.cached_variants||a.endsWithParent&&[q(a)]||[a]}function N(a){function c(a){return a&&a.source||a}function b(b,d){return new RegExp(c(b),"m"+(a.case_insensitive?"i":"")+(d?"g":""))}function e(d,g){if(!d.compiled){d.compiled=!0;d.keywords=d.keywords||d.beginKeywords;if(d.keywords){var f={},m=function(c,d){a.case_insensitive&&(d=d.toLowerCase());d.split(" ").forEach(function(a){a=
 a.split("|");f[a[0]]=[c,a[1]?Number(a[1]):1]})};"string"===typeof d.keywords?m("keyword",d.keywords):y(d.keywords).forEach(function(a){m(a,d.keywords[a])});d.keywords=f}d.lexemesRe=b(d.lexemes||/\w+/,!0);g&&(d.beginKeywords&&(d.begin="\\b("+d.beginKeywords.split(" ").join("|")+")\\b"),d.begin||(d.begin=/\B|\b/),d.beginRe=b(d.begin),d.endSameAsBegin&&(d.end=d.begin),d.end||d.endsWithParent||(d.end=/\B|\b/),d.end&&(d.endRe=b(d.end)),d.terminator_end=c(d.end)||"",d.endsWithParent&&g.terminator_end&&
@@ -130,11 +130,11 @@
 relevance:0},{className:"number",begin:"\\b((0|[1-9][\\d_]*)|0[bB][01_]+|0[xX]([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))(L|u|U|Lu|LU|uL|UL)?",relevance:0},{className:"string",begin:"'(\\\\(['\"\\?\\\\abfnrtv]|u[\\dA-Fa-f]{4}|[0-7]{1,3}|x[\\dA-Fa-f]{2}|U[\\dA-Fa-f]{8})|&[a-zA-Z\\d]{2,};|.)",end:"'",illegal:"."},{className:"meta",begin:"^#!",end:"$",relevance:5},{className:"meta",begin:"#(line)",end:"$",relevance:5},{className:"keyword",begin:"@[a-zA-Z_][a-zA-Z_\\d]*"}]}});b.registerLanguage("markdown",
 function(a){return{aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$"},{begin:"^.+?\\n[=-]{2,}$"}]},{begin:"<",end:">",subLanguage:"xml",relevance:0},{className:"bullet",begin:"^([*+-]|(\\d+\\.))\\s+"},{className:"strong",begin:"[*_]{2}.+?[*_]{2}"},{className:"emphasis",variants:[{begin:"\\*.+?\\*"},{begin:"_.+?_",relevance:0}]},{className:"quote",begin:"^>\\s+",end:"$"},{className:"code",variants:[{begin:"^```w*s*$",end:"^```s*$"},{begin:"`.+?`"},{begin:"^( {4}|\t)",
 end:"$",relevance:0}]},{begin:"^[-\\*]{3,}",end:"$"},{begin:"\\[.+?\\][\\(\\[].*?[\\)\\]]",returnBegin:!0,contains:[{className:"string",begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0,relevance:0},{className:"link",begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}],relevance:10},{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",
-begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}});b.registerLanguage("dart",function(a){var c={className:"subst",variants:[{begin:"\\${",end:"}"},{begin:"\\$[A-Za-z0-9_]+"}],keywords:"true false null this is new super"},b={className:"string",variants:[{begin:"r'''",end:"'''"},{begin:'r"""',end:'"""'},{begin:"r'",end:"'",illegal:"\\n"},{begin:'r"',end:'"',illegal:"\\n"},{begin:"'''",end:"'''",contains:[a.BACKSLASH_ESCAPE,c]},{begin:'"""',end:'"""',contains:[a.BACKSLASH_ESCAPE,c]},{begin:"'",end:"'",illegal:"\\n",
-contains:[a.BACKSLASH_ESCAPE,c]},{begin:'"',end:'"',illegal:"\\n",contains:[a.BACKSLASH_ESCAPE,c]}]};c.contains=[a.C_NUMBER_MODE,b];return{keywords:{keyword:"assert async await break case catch class const continue default do else enum extends false final finally for if in is new null rethrow return super switch sync this throw true try var void while with yield abstract as dynamic export external factory get implements import library operator part set static typedef",built_in:"print Comparable DateTime Duration Function Iterable Iterator List Map Match Null Object Pattern RegExp Set Stopwatch String StringBuffer StringSink Symbol Type Uri bool double int num document window querySelector querySelectorAll Element ElementList"},
-contains:[b,a.COMMENT("/\\*\\*","\\*/",{subLanguage:"markdown"}),a.COMMENT("///","$",{subLanguage:"markdown"}),a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,{className:"class",beginKeywords:"class interface",end:"{",excludeEnd:!0,contains:[{beginKeywords:"extends implements"},a.UNDERSCORE_TITLE_MODE]},a.C_NUMBER_MODE,{className:"meta",begin:"@[A-Za-z]+"},{begin:"=>"}]}});b.registerLanguage("delphi",function(a){var c=[a.C_LINE_COMMENT_MODE,a.COMMENT(/\{/,/\}/,{relevance:0}),a.COMMENT(/\(\*/,/\*\)/,
-{relevance:10})],b={className:"meta",variants:[{begin:/\{\$/,end:/\}/},{begin:/\(\*\$/,end:/\*\)/}]},e={className:"string",begin:/'/,end:/'/,contains:[{begin:/''/}]},f={className:"string",begin:/(#\d+)+/},g={begin:a.IDENT_RE+"\\s*=\\s*class\\s*\\(",returnBegin:!0,contains:[a.TITLE_MODE]},k={className:"function",beginKeywords:"function constructor destructor procedure",end:/[:;]/,keywords:"function constructor|10 destructor|10 procedure|10",contains:[a.TITLE_MODE,{className:"params",begin:/\(/,end:/\)/,
-keywords:"exports register file shl array record property for mod while set ally label uses raise not stored class safecall var interface or private static exit index inherited to else stdcall override shr asm far resourcestring finalization packed virtual out and protected library do xorwrite goto near function end div overload object unit begin string on inline repeat until destructor write message program with read initialization except default nil if case cdecl in downto threadvar of try pascal const external constructor type public then implementation finally published procedure absolute reintroduce operator as is abstract alias assembler bitpacked break continue cppdecl cvar enumerator experimental platform deprecated unimplemented dynamic export far16 forward generic helper implements interrupt iochecks local name nodefault noreturn nostackframe oldfpccall otherwise saveregisters softfloat specialize strict unaligned varargs ",
+begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}});b.registerLanguage("dart",function(a){var c={className:"subst",variants:[{begin:"\\$[A-Za-z0-9_]+"}]},b={className:"subst",variants:[{begin:"\\${",end:"}"}],keywords:"true false null this is new super"};c={className:"string",variants:[{begin:"r'''",end:"'''"},{begin:'r"""',end:'"""'},{begin:"r'",end:"'",illegal:"\\n"},{begin:'r"',end:'"',illegal:"\\n"},{begin:"'''",end:"'''",contains:[a.BACKSLASH_ESCAPE,c,b]},{begin:'"""',end:'"""',contains:[a.BACKSLASH_ESCAPE,
+c,b]},{begin:"'",end:"'",illegal:"\\n",contains:[a.BACKSLASH_ESCAPE,c,b]},{begin:'"',end:'"',illegal:"\\n",contains:[a.BACKSLASH_ESCAPE,c,b]}]};b.contains=[a.C_NUMBER_MODE,c];return{keywords:{keyword:"assert async await break case catch class const continue default do else enum extends false final finally for if in is new null rethrow return super switch sync this throw true try var void while with yield abstract as dynamic export external factory get implements import library operator part set static typedef",
+built_in:"print Comparable DateTime Duration Function Iterable Iterator List Map Match Null Object Pattern RegExp Set Stopwatch String StringBuffer StringSink Symbol Type Uri bool double int num document window querySelector querySelectorAll Element ElementList"},contains:[c,a.COMMENT("/\\*\\*","\\*/",{subLanguage:"markdown"}),a.COMMENT("///","$",{subLanguage:"markdown"}),a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,{className:"class",beginKeywords:"class interface",end:"{",excludeEnd:!0,contains:[{beginKeywords:"extends implements"},
+a.UNDERSCORE_TITLE_MODE]},a.C_NUMBER_MODE,{className:"meta",begin:"@[A-Za-z]+"},{begin:"=>"}]}});b.registerLanguage("delphi",function(a){var c=[a.C_LINE_COMMENT_MODE,a.COMMENT(/\{/,/\}/,{relevance:0}),a.COMMENT(/\(\*/,/\*\)/,{relevance:10})],b={className:"meta",variants:[{begin:/\{\$/,end:/\}/},{begin:/\(\*\$/,end:/\*\)/}]},e={className:"string",begin:/'/,end:/'/,contains:[{begin:/''/}]},f={className:"string",begin:/(#\d+)+/},g={begin:a.IDENT_RE+"\\s*=\\s*class\\s*\\(",returnBegin:!0,contains:[a.TITLE_MODE]},
+k={className:"function",beginKeywords:"function constructor destructor procedure",end:/[:;]/,keywords:"function constructor|10 destructor|10 procedure|10",contains:[a.TITLE_MODE,{className:"params",begin:/\(/,end:/\)/,keywords:"exports register file shl array record property for mod while set ally label uses raise not stored class safecall var interface or private static exit index inherited to else stdcall override shr asm far resourcestring finalization packed virtual out and protected library do xorwrite goto near function end div overload object unit begin string on inline repeat until destructor write message program with read initialization except default nil if case cdecl in downto threadvar of try pascal const external constructor type public then implementation finally published procedure absolute reintroduce operator as is abstract alias assembler bitpacked break continue cppdecl cvar enumerator experimental platform deprecated unimplemented dynamic export far16 forward generic helper implements interrupt iochecks local name nodefault noreturn nostackframe oldfpccall otherwise saveregisters softfloat specialize strict unaligned varargs ",
 contains:[e,f,b].concat(c)},b].concat(c)};return{aliases:"dpr dfm pas pascal freepascal lazarus lpr lfm".split(" "),case_insensitive:!0,keywords:"exports register file shl array record property for mod while set ally label uses raise not stored class safecall var interface or private static exit index inherited to else stdcall override shr asm far resourcestring finalization packed virtual out and protected library do xorwrite goto near function end div overload object unit begin string on inline repeat until destructor write message program with read initialization except default nil if case cdecl in downto threadvar of try pascal const external constructor type public then implementation finally published procedure absolute reintroduce operator as is abstract alias assembler bitpacked break continue cppdecl cvar enumerator experimental platform deprecated unimplemented dynamic export far16 forward generic helper implements interrupt iochecks local name nodefault noreturn nostackframe oldfpccall otherwise saveregisters softfloat specialize strict unaligned varargs ",
 illegal:/"|\$[G-Zg-z]|\/\*|<\/|\|/,contains:[e,f,a.NUMBER_MODE,g,k,b].concat(c)}});b.registerLanguage("diff",function(a){return{aliases:["patch"],contains:[{className:"meta",relevance:10,variants:[{begin:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{begin:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{begin:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{className:"comment",variants:[{begin:/Index: /,end:/$/},{begin:/={3,}/,end:/$/},{begin:/^\-{3}/,end:/$/},{begin:/^\*{3} /,end:/$/},{begin:/^\+{3}/,end:/$/},{begin:/\*{5}/,end:/\*{5}$/}]},
 {className:"addition",begin:"^\\+",end:"$"},{className:"deletion",begin:"^\\-",end:"$"},{className:"addition",begin:"^\\!",end:"$"}]}});b.registerLanguage("django",function(a){var c={begin:/\|[A-Za-z]+:?/,keywords:{name:"truncatewords removetags linebreaksbr yesno get_digit timesince random striptags filesizeformat escape linebreaks length_is ljust rjust cut urlize fix_ampersands title floatformat capfirst pprint divisibleby add make_list unordered_list urlencode timeuntil urlizetrunc wordcount stringformat linenumbers slice date dictsort dictsortreversed default_if_none pluralize lower join center default truncatewords_html upper length phone2numeric wordwrap time addslashes slugify first escapejs force_escape iriencode last safe safeseq truncatechars localize unlocalize localtime utc timezone"},
@@ -235,7 +235,7 @@
 b={className:"number",variants:[{begin:"\\b(0[bB][01]+)"},{begin:"\\b(0[oO][0-7]+)"},{begin:a.C_NUMBER_RE}],relevance:0},e={className:"subst",begin:"\\$\\{",end:"\\}",keywords:c,contains:[]},f={className:"string",begin:"`",end:"`",contains:[a.BACKSLASH_ESCAPE,e]};e.contains=[a.APOS_STRING_MODE,a.QUOTE_STRING_MODE,f,b,a.REGEXP_MODE];e=e.contains.concat([a.C_BLOCK_COMMENT_MODE,a.C_LINE_COMMENT_MODE]);return{aliases:["js","jsx"],keywords:c,contains:[{className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},
 {className:"meta",begin:/^#!/,end:/$/},a.APOS_STRING_MODE,a.QUOTE_STRING_MODE,f,a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,b,{begin:/[{,]\s*/,relevance:0,contains:[{begin:"[A-Za-z$_][0-9A-Za-z$_]*\\s*:",returnBegin:!0,relevance:0,contains:[{className:"attr",begin:"[A-Za-z$_][0-9A-Za-z$_]*",relevance:0}]}]},{begin:"("+a.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,a.REGEXP_MODE,{className:"function",begin:"(\\(.*?\\)|[A-Za-z$_][0-9A-Za-z$_]*)\\s*=>",
 returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:"[A-Za-z$_][0-9A-Za-z$_]*"},{begin:/\(\s*\)/},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:c,contains:e}]}]},{begin:/</,end:/(\/\w+|\w+\/)>/,subLanguage:"xml",contains:[{begin:/<\w+\s*\/>/,skip:!0},{begin:/<\w+/,end:/(\/\w+|\w+\/)>/,skip:!0,contains:[{begin:/<\w+\s*\/>/,skip:!0},"self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/\{/,excludeEnd:!0,contains:[a.inherit(a.TITLE_MODE,{begin:"[A-Za-z$_][0-9A-Za-z$_]*"}),
-{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:e}],illegal:/\[|%/},{begin:/\$[(.]/},a.METHOD_GUARD,{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends"},a.UNDERSCORE_TITLE_MODE]},{beginKeywords:"constructor",end:/\{/,excludeEnd:!0}],illegal:/#(?!!)/}});b.registerLanguage("jboss-cli",function(a){return{aliases:["wildfly-cli"],lexemes:"[a-z-]+",keywords:{keyword:"alias batch cd clear command connect connection-factory connection-info data-source deploy deployment-info deployment-overlay echo echo-dmr help history if jdbc-driver-info jms-queue|20 jms-topic|20 ls patch pwd quit read-attribute read-operation reload rollout-plan run-batch set shutdown try unalias undeploy unset version xa-data-source",
+{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:e}],illegal:/\[|%/},{begin:/\$[(.]/},a.METHOD_GUARD,{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends"},a.UNDERSCORE_TITLE_MODE]},{beginKeywords:"constructor get set",end:/\{/,excludeEnd:!0}],illegal:/#(?!!)/}});b.registerLanguage("jboss-cli",function(a){return{aliases:["wildfly-cli"],lexemes:"[a-z-]+",keywords:{keyword:"alias batch cd clear command connect connection-factory connection-info data-source deploy deployment-info deployment-overlay echo echo-dmr help history if jdbc-driver-info jms-queue|20 jms-topic|20 ls patch pwd quit read-attribute read-operation reload rollout-plan run-batch set shutdown try unalias undeploy unset version xa-data-source",
 literal:"true false"},contains:[a.HASH_COMMENT_MODE,a.QUOTE_STRING_MODE,{className:"params",begin:/--[\w\-=\/]+/},{className:"function",begin:/:[\w\-.]+/,relevance:0},{className:"string",begin:/\B(([\/.])[\w\-.\/=]+)+/},{className:"params",begin:/\(/,end:/\)/,contains:[{begin:/[\w-]+ *=/,returnBegin:!0,relevance:0,contains:[{className:"attr",begin:/[\w-]+/}]}],relevance:0}]}});b.registerLanguage("json",function(a){var b={literal:"true false null"},d=[a.QUOTE_STRING_MODE,a.C_NUMBER_MODE],e={end:",",
 endsWithParent:!0,excludeEnd:!0,contains:d,keywords:b},f={begin:"{",end:"}",contains:[{className:"attr",begin:/"/,end:/"/,contains:[a.BACKSLASH_ESCAPE],illegal:"\\n"},a.inherit(e,{begin:/:/})],illegal:"\\S"};a={begin:"\\[",end:"\\]",contains:[a.inherit(e)],illegal:"\\S"};d.splice(d.length,0,f,a);return{contains:d,keywords:b,illegal:"\\S"}});b.registerLanguage("julia",function(a){var b={keyword:"in isa where baremodule begin break catch ccall const continue do else elseif end export false finally for function global if import importall let local macro module quote return true try using while type immutable abstract bitstype typealias ",
 literal:"true false ARGS C_NULL DevNull ENDIAN_BOM ENV I Inf Inf16 Inf32 Inf64 InsertionSort JULIA_HOME LOAD_PATH MergeSort NaN NaN16 NaN32 NaN64 PROGRAM_FILE QuickSort RoundDown RoundFromZero RoundNearest RoundNearestTiesAway RoundNearestTiesUp RoundToZero RoundUp STDERR STDIN STDOUT VERSION catalan e|0 eu|0 eulergamma golden im nothing pi \u03b3 \u03c0 \u03c6 ",built_in:"ANY AbstractArray AbstractChannel AbstractFloat AbstractMatrix AbstractRNG AbstractSerializer AbstractSet AbstractSparseArray AbstractSparseMatrix AbstractSparseVector AbstractString AbstractUnitRange AbstractVecOrMat AbstractVector Any ArgumentError Array AssertionError Associative Base64DecodePipe Base64EncodePipe Bidiagonal BigFloat BigInt BitArray BitMatrix BitVector Bool BoundsError BufferStream CachingPool CapturedException CartesianIndex CartesianRange Cchar Cdouble Cfloat Channel Char Cint Cintmax_t Clong Clonglong ClusterManager Cmd CodeInfo Colon Complex Complex128 Complex32 Complex64 CompositeException Condition ConjArray ConjMatrix ConjVector Cptrdiff_t Cshort Csize_t Cssize_t Cstring Cuchar Cuint Cuintmax_t Culong Culonglong Cushort Cwchar_t Cwstring DataType Date DateFormat DateTime DenseArray DenseMatrix DenseVecOrMat DenseVector Diagonal Dict DimensionMismatch Dims DirectIndexString Display DivideError DomainError EOFError EachLine Enum Enumerate ErrorException Exception ExponentialBackOff Expr Factorization FileMonitor Float16 Float32 Float64 Function Future GlobalRef GotoNode HTML Hermitian IO IOBuffer IOContext IOStream IPAddr IPv4 IPv6 IndexCartesian IndexLinear IndexStyle InexactError InitError Int Int128 Int16 Int32 Int64 Int8 IntSet Integer InterruptException InvalidStateException Irrational KeyError LabelNode LinSpace LineNumberNode LoadError LowerTriangular MIME Matrix MersenneTwister Method MethodError MethodTable Module NTuple NewvarNode NullException Nullable Number ObjectIdDict OrdinalRange OutOfMemoryError OverflowError Pair ParseError PartialQuickSort PermutedDimsArray Pipe PollingFileWatcher ProcessExitedException Ptr QuoteNode RandomDevice Range RangeIndex Rational RawFD ReadOnlyMemoryError Real ReentrantLock Ref Regex RegexMatch RemoteChannel RemoteException RevString RoundingMode RowVector SSAValue SegmentationFault SerializationState Set SharedArray SharedMatrix SharedVector Signed SimpleVector Slot SlotNumber SparseMatrixCSC SparseVector StackFrame StackOverflowError StackTrace StepRange StepRangeLen StridedArray StridedMatrix StridedVecOrMat StridedVector String SubArray SubString SymTridiagonal Symbol Symmetric SystemError TCPSocket Task Text TextDisplay Timer Tridiagonal Tuple Type TypeError TypeMapEntry TypeMapLevel TypeName TypeVar TypedSlot UDPSocket UInt UInt128 UInt16 UInt32 UInt64 UInt8 UndefRefError UndefVarError UnicodeError UniformScaling Union UnionAll UnitRange Unsigned UpperTriangular Val Vararg VecElement VecOrMat Vector VersionNumber Void WeakKeyDict WeakRef WorkerConfig WorkerPool "},
@@ -428,14 +428,15 @@
 ")\\b",starts:{end:/;|$/,contains:[d,b,a.APOS_STRING_MODE,a.QUOTE_STRING_MODE,a.CSS_NUMBER_MODE,a.NUMBER_MODE,a.C_BLOCK_COMMENT_MODE],illegal:/\./,relevance:0}}]}});b.registerLanguage("subunit",function(a){return{case_insensitive:!0,contains:[{className:"string",begin:"\\[\n(multipart)?",end:"\\]\n"},{className:"string",begin:"\\d{4}-\\d{2}-\\d{2}(\\s+)\\d{2}:\\d{2}:\\d{2}.\\d+Z"},{className:"string",begin:"(\\+|-)\\d+"},{className:"keyword",relevance:10,variants:[{begin:"^(test|testing|success|successful|failure|error|skip|xfail|uxsuccess)(:?)\\s+(test)?"},
 {begin:"^progress(:?)(\\s+)?(pop|push)?"},{begin:"^tags:"},{begin:"^time:"}]}]}});b.registerLanguage("swift",function(a){var b={keyword:"#available #colorLiteral #column #else #elseif #endif #file #fileLiteral #function #if #imageLiteral #line #selector #sourceLocation _ __COLUMN__ __FILE__ __FUNCTION__ __LINE__ Any as as! as? associatedtype associativity break case catch class continue convenience default defer deinit didSet do dynamic dynamicType else enum extension fallthrough false fileprivate final for func get guard if import in indirect infix init inout internal is lazy left let mutating nil none nonmutating open operator optional override postfix precedence prefix private protocol Protocol public repeat required rethrows return right self Self set static struct subscript super switch throw throws true try try! try? Type typealias unowned var weak where while willSet",
 literal:"true false nil",built_in:"abs advance alignof alignofValue anyGenerator assert assertionFailure bridgeFromObjectiveC bridgeFromObjectiveCUnconditional bridgeToObjectiveC bridgeToObjectiveCUnconditional c contains count countElements countLeadingZeros debugPrint debugPrintln distance dropFirst dropLast dump encodeBitsAsWords enumerate equal fatalError filter find getBridgedObjectiveCType getVaList indices insertionSort isBridgedToObjectiveC isBridgedVerbatimToObjectiveC isUniquelyReferenced isUniquelyReferencedNonObjC join lazy lexicographicalCompare map max maxElement min minElement numericCast overlaps partition posix precondition preconditionFailure print println quickSort readLine reduce reflect reinterpretCast reverse roundUpToAlignment sizeof sizeofValue sort split startsWith stride strideof strideofValue swap toString transcode underestimateCount unsafeAddressOf unsafeBitCast unsafeDowncast unsafeUnwrap unsafeReflect withExtendedLifetime withObjectAtPlusZero withUnsafePointer withUnsafePointerToObject withUnsafeMutablePointer withUnsafeMutablePointers withUnsafePointer withUnsafePointers withVaList zip"},
-d=a.COMMENT("/\\*","\\*/",{contains:["self"]}),e={className:"subst",begin:/\\\(/,end:"\\)",keywords:b,contains:[]},f={className:"string",contains:[a.BACKSLASH_ESCAPE,e],variants:[{begin:/"""/,end:/"""/},{begin:/"/,end:/"/}]},g={className:"number",begin:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",relevance:0};e.contains=[g];return{keywords:b,contains:[f,a.C_LINE_COMMENT_MODE,d,{className:"type",begin:"\\b[A-Z][\\w\u00c0-\u02b8']*",relevance:0},g,{className:"function",
-beginKeywords:"func",end:"{",excludeEnd:!0,contains:[a.inherit(a.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin:/</,end:/>/},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:b,contains:["self",g,f,a.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}],illegal:/\[|%/},{className:"class",beginKeywords:"struct protocol class extension enum",keywords:b,end:"\\{",excludeEnd:!0,contains:[a.inherit(a.TITLE_MODE,{begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/})]},{className:"meta",begin:"(@discardableResult|@warn_unused_result|@exported|@lazy|@noescape|@NSCopying|@NSManaged|@objc|@objcMembers|@convention|@required|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix|@autoclosure|@testable|@available|@nonobjc|@NSApplicationMain|@UIApplicationMain)"},
-{beginKeywords:"import",end:/$/,contains:[a.C_LINE_COMMENT_MODE,d]}]}});b.registerLanguage("taggerscript",function(a){return{contains:[{className:"comment",begin:/\$noop\(/,end:/\)/,contains:[{begin:/\(/,end:/\)/,contains:["self",{begin:/\\./}]}],relevance:10},{className:"keyword",begin:/\$(?!noop)[a-zA-Z][_a-zA-Z0-9]*/,end:/\(/,excludeEnd:!0},{className:"variable",begin:/%[_a-zA-Z0-9:]*/,end:"%"},{className:"symbol",begin:/\\./}]}});b.registerLanguage("yaml",function(a){var b={className:"attr",variants:[{begin:"^[ \\-]*[a-zA-Z_][\\w\\-]*:"},
-{begin:'^[ \\-]*"[a-zA-Z_][\\w\\-]*":'},{begin:"^[ \\-]*'[a-zA-Z_][\\w\\-]*':"}]},d={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[a.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]}]};return{case_insensitive:!0,aliases:["yml","YAML","yaml"],contains:[b,{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>] *$",returnEnd:!0,contains:d.contains,end:b.variants[0].begin},
-{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!"+a.UNDERSCORE_IDENT_RE},{className:"type",begin:"!!"+a.UNDERSCORE_IDENT_RE},{className:"meta",begin:"&"+a.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+a.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"^ *-",relevance:0},a.HASH_COMMENT_MODE,{beginKeywords:"true false yes no null",keywords:{literal:"true false yes no null"}},a.C_NUMBER_MODE,d]}});b.registerLanguage("tap",
-function(a){return{case_insensitive:!0,contains:[a.HASH_COMMENT_MODE,{className:"meta",variants:[{begin:"^TAP version (\\d+)$"},{begin:"^1\\.\\.(\\d+)$"}]},{begin:"(s+)?---$",end:"\\.\\.\\.$",subLanguage:"yaml",relevance:0},{className:"number",begin:" (\\d+) "},{className:"symbol",variants:[{begin:"^ok"},{begin:"^not ok"}]}]}});b.registerLanguage("tcl",function(a){return{aliases:["tk"],keywords:"after append apply array auto_execok auto_import auto_load auto_mkindex auto_mkindex_old auto_qualify auto_reset bgerror binary break catch cd chan clock close concat continue dde dict encoding eof error eval exec exit expr fblocked fconfigure fcopy file fileevent filename flush for foreach format gets glob global history http if incr info interp join lappend|10 lassign|10 lindex|10 linsert|10 list llength|10 load lrange|10 lrepeat|10 lreplace|10 lreverse|10 lsearch|10 lset|10 lsort|10 mathfunc mathop memory msgcat namespace open package parray pid pkg::create pkg_mkIndex platform platform::shell proc puts pwd read refchan regexp registry regsub|10 rename return safe scan seek set socket source split string subst switch tcl_endOfWord tcl_findLibrary tcl_startOfNextWord tcl_startOfPreviousWord tcl_wordBreakAfter tcl_wordBreakBefore tcltest tclvars tell time tm trace unknown unload unset update uplevel upvar variable vwait while",
+d=a.COMMENT("/\\*","\\*/",{contains:["self"]}),e={className:"subst",begin:/\\\(/,end:"\\)",keywords:b,contains:[]},f={className:"string",contains:[a.BACKSLASH_ESCAPE,e],variants:[{begin:/"""/,end:/"""/},{begin:/"/,end:/"/}]},g={className:"number",begin:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",relevance:0};e.contains=[g];return{keywords:b,contains:[f,a.C_LINE_COMMENT_MODE,d,{className:"type",begin:"\\b[A-Z][\\w\u00c0-\u02b8']*[!?]"},{className:"type",
+begin:"\\b[A-Z][\\w\u00c0-\u02b8']*",relevance:0},g,{className:"function",beginKeywords:"func",end:"{",excludeEnd:!0,contains:[a.inherit(a.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin:/</,end:/>/},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:b,contains:["self",g,f,a.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}],illegal:/\[|%/},{className:"class",beginKeywords:"struct protocol class extension enum",keywords:b,end:"\\{",excludeEnd:!0,contains:[a.inherit(a.TITLE_MODE,
+{begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/})]},{className:"meta",begin:"(@discardableResult|@warn_unused_result|@exported|@lazy|@noescape|@NSCopying|@NSManaged|@objc|@objcMembers|@convention|@required|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix|@autoclosure|@testable|@available|@nonobjc|@NSApplicationMain|@UIApplicationMain)"},{beginKeywords:"import",end:/$/,contains:[a.C_LINE_COMMENT_MODE,d]}]}});b.registerLanguage("taggerscript",function(a){return{contains:[{className:"comment",
+begin:/\$noop\(/,end:/\)/,contains:[{begin:/\(/,end:/\)/,contains:["self",{begin:/\\./}]}],relevance:10},{className:"keyword",begin:/\$(?!noop)[a-zA-Z][_a-zA-Z0-9]*/,end:/\(/,excludeEnd:!0},{className:"variable",begin:/%[_a-zA-Z0-9:]*/,end:"%"},{className:"symbol",begin:/\\./}]}});b.registerLanguage("yaml",function(a){var b={className:"attr",variants:[{begin:"^[ \\-]*[a-zA-Z_][\\w\\-]*:"},{begin:'^[ \\-]*"[a-zA-Z_][\\w\\-]*":'},{begin:"^[ \\-]*'[a-zA-Z_][\\w\\-]*':"}]},d={className:"string",relevance:0,
+variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[a.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]}]};return{case_insensitive:!0,aliases:["yml","YAML","yaml"],contains:[b,{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>] *$",returnEnd:!0,contains:d.contains,end:b.variants[0].begin},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",
+begin:"!"+a.UNDERSCORE_IDENT_RE},{className:"type",begin:"!!"+a.UNDERSCORE_IDENT_RE},{className:"meta",begin:"&"+a.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+a.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"^ *-",relevance:0},a.HASH_COMMENT_MODE,{beginKeywords:"true false yes no null",keywords:{literal:"true false yes no null"}},a.C_NUMBER_MODE,d]}});b.registerLanguage("tap",function(a){return{case_insensitive:!0,contains:[a.HASH_COMMENT_MODE,{className:"meta",variants:[{begin:"^TAP version (\\d+)$"},
+{begin:"^1\\.\\.(\\d+)$"}]},{begin:"(s+)?---$",end:"\\.\\.\\.$",subLanguage:"yaml",relevance:0},{className:"number",begin:" (\\d+) "},{className:"symbol",variants:[{begin:"^ok"},{begin:"^not ok"}]}]}});b.registerLanguage("tcl",function(a){return{aliases:["tk"],keywords:"after append apply array auto_execok auto_import auto_load auto_mkindex auto_mkindex_old auto_qualify auto_reset bgerror binary break catch cd chan clock close concat continue dde dict encoding eof error eval exec exit expr fblocked fconfigure fcopy file fileevent filename flush for foreach format gets glob global history http if incr info interp join lappend|10 lassign|10 lindex|10 linsert|10 list llength|10 load lrange|10 lrepeat|10 lreplace|10 lreverse|10 lsearch|10 lset|10 lsort|10 mathfunc mathop memory msgcat namespace open package parray pid pkg::create pkg_mkIndex platform platform::shell proc puts pwd read refchan regexp registry regsub|10 rename return safe scan seek set socket source split string subst switch tcl_endOfWord tcl_findLibrary tcl_startOfNextWord tcl_startOfPreviousWord tcl_wordBreakAfter tcl_wordBreakBefore tcltest tclvars tell time tm trace unknown unload unset update uplevel upvar variable vwait while",
 contains:[a.COMMENT(";[ \\t]*#","$"),a.COMMENT("^[ \\t]*#","$"),{beginKeywords:"proc",end:"[\\{]",excludeEnd:!0,contains:[{className:"title",begin:"[ \\t\\n\\r]+(::)?[a-zA-Z_]((::)?[a-zA-Z0-9_])*",end:"[ \\t\\n\\r]",endsWithParent:!0,excludeEnd:!0}]},{excludeEnd:!0,variants:[{begin:"\\$(\\{)?(::)?[a-zA-Z_]((::)?[a-zA-Z0-9_])*\\(([a-zA-Z0-9_])*\\)",end:"[^a-zA-Z0-9_\\}\\$]"},{begin:"\\$(\\{)?(::)?[a-zA-Z_]((::)?[a-zA-Z0-9_])*",end:"(\\))?[^a-zA-Z0-9_\\}\\$]"}]},{className:"string",contains:[a.BACKSLASH_ESCAPE],
-variants:[a.inherit(a.APOS_STRING_MODE,{illegal:null}),a.inherit(a.QUOTE_STRING_MODE,{illegal:null})]},{className:"number",variants:[a.BINARY_NUMBER_MODE,a.C_NUMBER_MODE]}]}});b.registerLanguage("tex",function(a){var b={className:"tag",begin:/\\/,relevance:0,contains:[{className:"name",variants:[{begin:/[a-zA-Z\u0430-\u044f\u0410-\u044f]+[*]?/},{begin:/[^a-zA-Z\u0430-\u044f\u0410-\u044f0-9]/}],starts:{endsWithParent:!0,relevance:0,contains:[{className:"string",variants:[{begin:/\[/,end:/\]/},{begin:/\{/,
+variants:[a.inherit(a.APOS_STRING_MODE,{illegal:null}),a.inherit(a.QUOTE_STRING_MODE,{illegal:null})]},{className:"number",variants:[a.BINARY_NUMBER_MODE,a.C_NUMBER_MODE]}]}});b.registerLanguage("tex",function(a){var b={className:"tag",begin:/\\/,relevance:0,contains:[{className:"name",variants:[{begin:/[a-zA-Z\u0430-\u044f\u0410-\u042f]+[*]?/},{begin:/[^a-zA-Z\u0430-\u044f\u0410-\u042f0-9]/}],starts:{endsWithParent:!0,relevance:0,contains:[{className:"string",variants:[{begin:/\[/,end:/\]/},{begin:/\{/,
 end:/\}/}]},{begin:/\s*=\s*/,endsWithParent:!0,relevance:0,contains:[{className:"number",begin:/-?\d*\.?\d+(pt|pc|mm|cm|in|dd|cc|ex|em)?/}]}]}}]};return{contains:[b,{className:"formula",contains:[b],relevance:0,variants:[{begin:/\$\$/,end:/\$\$/},{begin:/\$/,end:/\$/}]},a.COMMENT("%","$",{relevance:0})]}});b.registerLanguage("thrift",function(a){return{keywords:{keyword:"namespace const typedef struct enum service exception void oneway set list map required optional",built_in:"bool byte i16 i32 i64 double string binary",
 literal:"true false"},contains:[a.QUOTE_STRING_MODE,a.NUMBER_MODE,a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,{className:"class",beginKeywords:"struct enum service exception",end:/\{/,illegal:/\n/,contains:[a.inherit(a.TITLE_MODE,{starts:{endsWithParent:!0,excludeEnd:!0}})]},{begin:"\\b(set|list|map)\\s*<",end:">",keywords:"bool byte i16 i32 i64 double string binary",contains:["self"]}]}});b.registerLanguage("tp",function(a){var b={className:"number",begin:"[1-9][0-9]*",relevance:0},d={className:"symbol",
 begin:":[^\\]]+"};return{keywords:{keyword:"ABORT ACC ADJUST AND AP_LD BREAK CALL CNT COL CONDITION CONFIG DA DB DIV DETECT ELSE END ENDFOR ERR_NUM ERROR_PROG FINE FOR GP GUARD INC IF JMP LINEAR_MAX_SPEED LOCK MOD MONITOR OFFSET Offset OR OVERRIDE PAUSE PREG PTH RT_LD RUN SELECT SKIP Skip TA TB TO TOOL_OFFSET Tool_Offset UF UT UFRAME_NUM UTOOL_NUM UNLOCK WAIT X Y Z W P R STRLEN SUBSTR FINDSTR VOFFSET PROG ATTR MN POS",literal:"ON OFF max_speed LPOS JPOS ENABLE DISABLE START STOP RESET"},contains:[{className:"built_in",
diff --git a/plugins/BUILD b/plugins/BUILD
index b38658b..2469d96 100644
--- a/plugins/BUILD
+++ b/plugins/BUILD
@@ -42,6 +42,7 @@
     "//java/com/google/gerrit/metrics/dropwizard",
     "//java/com/google/gerrit/reviewdb:server",
     "//java/com/google/gerrit/server/audit",
+    "//java/com/google/gerrit/server/cache/mem",
     "//java/com/google/gerrit/server/logging",
     "//java/com/google/gerrit/server/schema",
     "//java/com/google/gerrit/server/util/time",