Merge branch 'stable-2.15'

* stable-2.15:
  setup_gjf.sh: Add support for multiple version
  Update .mailmap
  SmtpEmailSender: Open Writer in try-with-resource
  Open instances of JsonReader in try-with-resource
  Set 2.13.12-SNAPSHOT in pom.xml and plugin documentation
  Clarify behavior of ownerin: and reviewerin: predicates
  AbstractElasticIndex: Open XContentBuilder in try-with-resource
  Docs: Clarify that for external groups the name in GroupInfo can be missing
  AccountGroupAuditLogScreen: Display group UUID if group name is missing
  GetAuditLog: Fix NPE if group UUID cannot be resolved
  VersionedMetaData: Open TreeWalk in try-with-resource
  FileContentUtil: Open TreeWalk in try-with-resource
  Add ProjectCache.remove(Project.NameKey name) method
  Set version to 2.13.12-SNAPSHOT
  Use SecureRandom instead of Random
  Use try with resource in DelegatingClassLoader
  AbstractQueryChangesTest: Add test coverage for query: predicate
  AbstractQueryChangesTest: Add test coverage for destination: predicate
  doc: fix the example of index activate command
  doc: add groups index to index activate command
  AbstractQueryChangesTest: explicitly cover is:owner
  AbstractQueryChangesTest: Add stub test for "query:" predicate
  Fix internal errors when 'destination:' refers to non-existing destination
  lib/elasticsearch: restore jackson_dataformat_smile
  AbstractQueryChangesTest: Add back test for visible: predicate
  AbstractQueryChangesTest: Add coverage of is:mergeable
  AbstractQueryChangesTest: Add coverage of is:cc and cc:self
  AbstractQueryChangesTest: Add coverage for is:visible
  AbstractQueryChangesTest#submitRecords: Add coverage for submittable:closed
  AbstractQueryChangesTest#bySize: Make it easier to discover size: and delta:
  AbstractQueryChangesTest: Use Lists.newArrayList
  AbstractQueryChangesTest: Add coverage for the commit: predicate
  user-search: Add missing documentation of is:cc predicate
  AbstractQueryChangesTest: Add coverage for is:reviewer and reviewer:self
  AbstractQueryChangesTest: Add coverage for parentproject: predicate
  AbstractQueryChangesTest: Add coverage for is:reviewed and status:reviewed
  AbstractQueryChangesTest#byStatusOpen: Add coverage for is:pending
  AbstractQueryChangesTest: Add test coverage for since: and until: predicates
  AbstractQueryChangesTest: Add test coverage for assignee related queries
  Add extra info when checking access to change
  Fix logging of a change ID that failed to rebuild.
  Log NoteDb migration state transitions
  lib/elasticsearch: remove unnecessary dependencies
  lib/jest for Elasticsearch: refactor dependencies
  Elasticsearch BUILD: remove unneeded dependencies

Change-Id: I93d0c7cd71dfcbfe0fcbd6ec0cd2884a8081bb9d
diff --git a/.mailmap b/.mailmap
index f5f8f3e..b4e81e2 100644
--- a/.mailmap
+++ b/.mailmap
@@ -67,6 +67,7 @@
 Ulrik Sjölin <ulrik.sjolin@sonyericsson.com>                                                Ulrik Sjolin <ulrik.sjolin@gmail.com>
 Ulrik Sjölin <ulrik.sjolin@sonyericsson.com>                                                Ulrik Sjölin <ulrik.sjolin@sonyericsson.com>
 Ulrik Sjölin <ulrik.sjolin@sonyericsson.com>                                                Ulrik Sjolin <ulrik.sjolin@sonyericsson.com>
+Viktar Donich <viktard@google.com>                                                          viktard
 Yuxuan 'fishy' Wang <fishywang@google.com>                                                  Yuxuan Wang <fishywang@google.com>
 Zalán Blénessy <zalanb@axis.com>                                                            Zalan Blenessy <zalanb@axis.com>
 飞 李 <lifei@7v1.net>                                                                       lifei <lifei@7v1.net>
diff --git a/Documentation/cmd-index-activate.txt b/Documentation/cmd-index-activate.txt
index 418e872..4428d12 100644
--- a/Documentation/cmd-index-activate.txt
+++ b/Documentation/cmd-index-activate.txt
@@ -31,12 +31,13 @@
   Currently supported values:
     * changes
     * accounts
+    * groups
 
 == EXAMPLES
 Activate the latest change index:
 
 ----
-  $ ssh -p 29418 review.example.com gerrit activate changes
+  $ ssh -p 29418 review.example.com gerrit index activate changes
 ----
 
 GERRIT
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 2ad8cbd..db42826 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -108,7 +108,9 @@
 [[ownerin]]
 ownerin:'GROUP'::
 +
-Changes originally submitted by a user in 'GROUP'.
+Changes originally submitted by a user in 'GROUP'. When no other index
+predicate is explicitly added in the query, defaults to only include
+changes in status 'OPEN'.
 
 [[query]]
 query:'NAME'::
@@ -137,7 +139,9 @@
 [[reviewerin]]
 reviewerin:'GROUP'::
 +
-Changes that have been, or need to be, reviewed by a user in 'GROUP'.
+Changes that have been, or need to be, reviewed by a user in 'GROUP'. When
+no other index predicate is explicitly added in the query, defaults to only
+include changes in status 'OPEN'.
 
 [[commit]]
 commit:'SHA1'::
@@ -338,6 +342,11 @@
 True on any change where the current user is a reviewer.
 Same as `reviewer:self`.
 
+is:cc::
++
+True on any change where the current user is in CC.
+Same as `cc:self`.
+
 is:open, is:pending::
 +
 True if the change is open.
diff --git a/WORKSPACE b/WORKSPACE
index 2041bc7..6967c1f 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -444,12 +444,6 @@
 )
 
 maven_jar(
-    name = "lucene_codecs",
-    artifact = "org.apache.lucene:lucene-codecs:" + LUCENE_VERS,
-    sha1 = "afdad570668469b1734fbd32b8f98561561bed48",
-)
-
-maven_jar(
     name = "backward_codecs",
     artifact = "org.apache.lucene:lucene-backward-codecs:" + LUCENE_VERS,
     sha1 = "a933f42e758c54c43083398127ea7342b54d8212",
@@ -486,12 +480,6 @@
 )
 
 maven_jar(
-    name = "lucene_sandbox",
-    artifact = "org.apache.lucene:lucene-sandbox:" + LUCENE_VERS,
-    sha1 = "49498bbb2adc333e98bdca4bf6170ae770cbad11",
-)
-
-maven_jar(
     name = "lucene_spatial",
     artifact = "org.apache.lucene:lucene-spatial:" + LUCENE_VERS,
     sha1 = "0217d302dc0ef4d9b8b475ffe327d83c1e0ceba5",
@@ -967,12 +955,6 @@
     sha1 = "84ccf145ac2215e6bfa63baa3101c0af41017cfc",
 )
 
-maven_jar(
-    name = "jna",
-    artifact = "net.java.dev.jna:jna:4.1.0",
-    sha1 = "1c12d070e602efd8021891cdd7fd18bc129372d4",
-)
-
 JACKSON_VERSION = "2.8.9"
 
 maven_jar(
@@ -982,18 +964,18 @@
 )
 
 maven_jar(
-    name = "jackson_dataformat_smile",
-    artifact = "com.fasterxml.jackson.dataformat:jackson-dataformat-smile:" + JACKSON_VERSION,
-    sha1 = "d36cbae6b06ac12fca16fda403759e479316141b",
-)
-
-maven_jar(
     name = "jackson_dataformat_cbor",
     artifact = "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:" + JACKSON_VERSION,
     sha1 = "93242092324cad33d777e06c0515e40a6b862659",
 )
 
 maven_jar(
+    name = "jackson_dataformat_smile",
+    artifact = "com.fasterxml.jackson.dataformat:jackson-dataformat-smile:" + JACKSON_VERSION,
+    sha1 = "d36cbae6b06ac12fca16fda403759e479316141b",
+)
+
+maven_jar(
     name = "httpasyncclient",
     artifact = "org.apache.httpcomponents:httpasyncclient:4.1.2",
     sha1 = "95aa3e6fb520191a0970a73cf09f62948ee614be",
@@ -1005,13 +987,6 @@
     sha1 = "a8c5e3c3bfea5ce23fb647c335897e415eb442e3",
 )
 
-maven_jar(
-    name = "httpcore_niossl",
-    artifact = "org.apache.httpcomponents:httpcore-niossl:4.0-alpha6",
-    attach_source = False,
-    sha1 = "9c662e7247ca8ceb1de5de629f685c9ef3e4ab58",
-)
-
 load("//tools/bzl:js.bzl", "npm_binary", "bower_archive")
 
 npm_binary(
diff --git a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
index 3ad31ec..d4d33c7 100644
--- a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
+++ b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
@@ -189,21 +189,24 @@
   }
 
   private String toDocument(V v) throws IOException {
-    XContentBuilder builder = jsonBuilder().startObject();
-    for (Values<V> values : schema.buildFields(v)) {
-      String name = values.getField().getName();
-      if (values.getField().isRepeatable()) {
-        builder.field(
-            name,
-            Streams.stream(values.getValues()).filter(e -> shouldAddElement(e)).collect(toList()));
-      } else {
-        Object element = Iterables.getOnlyElement(values.getValues(), "");
-        if (shouldAddElement(element)) {
-          builder.field(name, element);
+    try (XContentBuilder builder = jsonBuilder().startObject()) {
+      for (Values<V> values : schema.buildFields(v)) {
+        String name = values.getField().getName();
+        if (values.getField().isRepeatable()) {
+          builder.field(
+              name,
+              Streams.stream(values.getValues())
+                  .filter(e -> shouldAddElement(e))
+                  .collect(toList()));
+        } else {
+          Object element = Iterables.getOnlyElement(values.getValues(), "");
+          if (shouldAddElement(element)) {
+            builder.field(name, element);
+          }
         }
       }
+      return builder.endObject().string();
     }
-    return builder.endObject().string();
   }
 
   protected abstract V fromDocument(JsonObject doc, Set<String> fields);
diff --git a/java/com/google/gerrit/server/account/VersionedAccountDestinations.java b/java/com/google/gerrit/server/account/VersionedAccountDestinations.java
index 0bb5f5e..a57dc7b 100644
--- a/java/com/google/gerrit/server/account/VersionedAccountDestinations.java
+++ b/java/com/google/gerrit/server/account/VersionedAccountDestinations.java
@@ -52,6 +52,9 @@
 
   @Override
   protected void onLoad() throws IOException, ConfigInvalidException {
+    if (revision == null) {
+      return;
+    }
     String prefix = DestinationList.DIR_NAME + "/";
     for (PathInfo p : getPathInfos(true)) {
       if (p.fileMode == FileMode.REGULAR_FILE) {
diff --git a/java/com/google/gerrit/server/change/FileContentUtil.java b/java/com/google/gerrit/server/change/FileContentUtil.java
index ff5fb0b..00b7a88 100644
--- a/java/com/google/gerrit/server/change/FileContentUtil.java
+++ b/java/com/google/gerrit/server/change/FileContentUtil.java
@@ -34,7 +34,7 @@
 import eu.medsea.mimeutil.MimeType;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.util.Random;
+import java.security.SecureRandom;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 import org.eclipse.jgit.errors.LargeObjectException;
@@ -42,7 +42,6 @@
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
@@ -57,7 +56,7 @@
   private static final String X_GIT_GITLINK = "x-git/gitlink";
   private static final int MAX_SIZE = 5 << 20;
   private static final String ZIP_TYPE = "application/zip";
-  private static final Random rng = new Random();
+  private static final SecureRandom rng = new SecureRandom();
 
   private final GitRepositoryManager repoManager;
   private final FileTypeRegistry registry;
@@ -104,35 +103,35 @@
       throws IOException, ResourceNotFoundException {
     try (RevWalk rw = new RevWalk(repo)) {
       RevCommit commit = rw.parseCommit(revstr);
-      ObjectReader reader = rw.getObjectReader();
-      TreeWalk tw = TreeWalk.forPath(reader, path, commit.getTree());
-      if (tw == null) {
-        throw new ResourceNotFoundException();
-      }
+      try (TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(), path, commit.getTree())) {
+        if (tw == null) {
+          throw new ResourceNotFoundException();
+        }
 
-      org.eclipse.jgit.lib.FileMode mode = tw.getFileMode(0);
-      ObjectId id = tw.getObjectId(0);
-      if (mode == org.eclipse.jgit.lib.FileMode.GITLINK) {
-        return BinaryResult.create(id.name()).setContentType(X_GIT_GITLINK).base64();
-      }
+        org.eclipse.jgit.lib.FileMode mode = tw.getFileMode(0);
+        ObjectId id = tw.getObjectId(0);
+        if (mode == org.eclipse.jgit.lib.FileMode.GITLINK) {
+          return BinaryResult.create(id.name()).setContentType(X_GIT_GITLINK).base64();
+        }
 
-      ObjectLoader obj = repo.open(id, OBJ_BLOB);
-      byte[] raw;
-      try {
-        raw = obj.getCachedBytes(MAX_SIZE);
-      } catch (LargeObjectException e) {
-        raw = null;
-      }
+        ObjectLoader obj = repo.open(id, OBJ_BLOB);
+        byte[] raw;
+        try {
+          raw = obj.getCachedBytes(MAX_SIZE);
+        } catch (LargeObjectException e) {
+          raw = null;
+        }
 
-      String type;
-      if (mode == org.eclipse.jgit.lib.FileMode.SYMLINK) {
-        type = X_GIT_SYMLINK;
-      } else {
-        type = registry.getMimeType(path, raw).toString();
-        type = resolveContentType(project, path, FileMode.FILE, type);
-      }
+        String type;
+        if (mode == org.eclipse.jgit.lib.FileMode.SYMLINK) {
+          type = X_GIT_SYMLINK;
+        } else {
+          type = registry.getMimeType(path, raw).toString();
+          type = resolveContentType(project, path, FileMode.FILE, type);
+        }
 
-      return asBinaryResult(raw, obj).setContentType(type).base64();
+        return asBinaryResult(raw, obj).setContentType(type).base64();
+      }
     }
   }
 
@@ -166,30 +165,30 @@
         }
         commit = rw.parseCommit(commit.getParent(parent - 1));
       }
-      ObjectReader reader = rw.getObjectReader();
-      TreeWalk tw = TreeWalk.forPath(reader, path, commit.getTree());
-      if (tw == null) {
-        throw new ResourceNotFoundException();
-      }
+      try (TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(), path, commit.getTree())) {
+        if (tw == null) {
+          throw new ResourceNotFoundException();
+        }
 
-      int mode = tw.getFileMode(0).getObjectType();
-      if (mode != Constants.OBJ_BLOB) {
-        throw new ResourceNotFoundException();
-      }
+        int mode = tw.getFileMode(0).getObjectType();
+        if (mode != Constants.OBJ_BLOB) {
+          throw new ResourceNotFoundException();
+        }
 
-      ObjectId id = tw.getObjectId(0);
-      ObjectLoader obj = repo.open(id, OBJ_BLOB);
-      byte[] raw;
-      try {
-        raw = obj.getCachedBytes(MAX_SIZE);
-      } catch (LargeObjectException e) {
-        raw = null;
-      }
+        ObjectId id = tw.getObjectId(0);
+        ObjectLoader obj = repo.open(id, OBJ_BLOB);
+        byte[] raw;
+        try {
+          raw = obj.getCachedBytes(MAX_SIZE);
+        } catch (LargeObjectException e) {
+          raw = null;
+        }
 
-      MimeType contentType = registry.getMimeType(path, raw);
-      return registry.isSafeInline(contentType)
-          ? wrapBlob(path, obj, raw, contentType, suffix)
-          : zipBlob(path, obj, commit, suffix);
+        MimeType contentType = registry.getMimeType(path, raw);
+        return registry.isSafeInline(contentType)
+            ? wrapBlob(path, obj, raw, contentType, suffix)
+            : zipBlob(path, obj, commit, suffix);
+      }
     }
   }
 
diff --git a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
index 59fd58b2..da1f1ac 100644
--- a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
+++ b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
@@ -481,10 +481,11 @@
       return new byte[] {};
     }
 
-    TreeWalk tw = TreeWalk.forPath(reader, fileName, revision.getTree());
-    if (tw != null) {
-      ObjectLoader obj = reader.open(tw.getObjectId(0), Constants.OBJ_BLOB);
-      return obj.getCachedBytes(Integer.MAX_VALUE);
+    try (TreeWalk tw = TreeWalk.forPath(reader, fileName, revision.getTree())) {
+      if (tw != null) {
+        ObjectLoader obj = reader.open(tw.getObjectId(0), Constants.OBJ_BLOB);
+        return obj.getCachedBytes(Integer.MAX_VALUE);
+      }
     }
     return new byte[] {};
   }
@@ -495,9 +496,10 @@
       return null;
     }
 
-    TreeWalk tw = TreeWalk.forPath(reader, fileName, revision.getTree());
-    if (tw != null) {
-      return tw.getObjectId(0);
+    try (TreeWalk tw = TreeWalk.forPath(reader, fileName, revision.getTree())) {
+      if (tw != null) {
+        return tw.getObjectId(0);
+      }
     }
 
     return null;
diff --git a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
index a8289c4..b08e594 100644
--- a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
@@ -200,30 +200,31 @@
           }
         }
 
-        Writer messageDataWriter = client.sendMessageData();
-        if (messageDataWriter == null) {
-          /* Include rejected recipient error messages here to not lose that
-           * information. That piece of the puzzle is vital if zero recipients
-           * are accepted and the server consequently rejects the DATA command.
-           */
-          throw new EmailException(
-              rejected
-                  + "Server "
-                  + smtpHost
-                  + " rejected DATA command: "
-                  + client.getReplyString());
-        }
+        try (Writer messageDataWriter = client.sendMessageData()) {
+          if (messageDataWriter == null) {
+            /* Include rejected recipient error messages here to not lose that
+             * information. That piece of the puzzle is vital if zero recipients
+             * are accepted and the server consequently rejects the DATA command.
+             */
+            throw new EmailException(
+                rejected
+                    + "Server "
+                    + smtpHost
+                    + " rejected DATA command: "
+                    + client.getReplyString());
+          }
 
-        render(messageDataWriter, callerHeaders, textBody, htmlBody);
+          render(messageDataWriter, callerHeaders, textBody, htmlBody);
 
-        if (!client.completePendingCommand()) {
-          throw new EmailException(
-              "Server " + smtpHost + " rejected message body: " + client.getReplyString());
-        }
+          if (!client.completePendingCommand()) {
+            throw new EmailException(
+                "Server " + smtpHost + " rejected message body: " + client.getReplyString());
+          }
 
-        client.logout();
-        if (rejected.length() > 0) {
-          throw new EmailException(rejected.toString());
+          client.logout();
+          if (rejected.length() > 0) {
+            throw new EmailException(rejected.toString());
+          }
         }
       } finally {
         client.disconnect();
diff --git a/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java b/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
index c11aeef..df415ce 100644
--- a/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
+++ b/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
@@ -615,6 +615,7 @@
                                   log.warn(
                                       "Change {} previously failed to rebuild;"
                                           + " skipping primary storage migration",
+                                      id,
                                       e);
                                 } else {
                                   throw e;
@@ -724,6 +725,7 @@
 
       // Only set in-memory state once it's been persisted to storage.
       globalNotesMigration.setFrom(newState);
+      log.info("Migration state: {} => {}", expectedOldState, newState);
 
       return newState;
     }
diff --git a/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java b/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
index 1052d33..063a18c 100644
--- a/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
@@ -93,7 +93,7 @@
         logger.info("No such project: {}", cd.project());
         return false;
       }
-      throw new OrmException("unable to check permissions", e);
+      throw new OrmException("unable to check permissions on change " + cd.getId(), e);
     }
     if (visible) {
       cd.cacheVisibleTo(user);
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 5a04a7c..630f2f3 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -1103,7 +1103,7 @@
       VersionedAccountDestinations d = VersionedAccountDestinations.forUser(self());
       d.load(git);
       Set<Branch.NameKey> destinations = d.getDestinationList().getDestinations(name);
-      if (destinations != null) {
+      if (destinations != null && !destinations.isEmpty()) {
         return new DestinationPredicate(destinations, name);
       }
     } catch (RepositoryNotFoundException e) {
diff --git a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
index f4bb2a9..082ef28 100644
--- a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
+++ b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -846,16 +846,18 @@
 
   private <T> T readContentFromJson(RestResponse r, Class<T> clazz) throws Exception {
     r.assertOK();
-    JsonReader jsonReader = new JsonReader(r.getReader());
-    jsonReader.setLenient(true);
-    return newGson().fromJson(jsonReader, clazz);
+    try (JsonReader jsonReader = new JsonReader(r.getReader())) {
+      jsonReader.setLenient(true);
+      return newGson().fromJson(jsonReader, clazz);
+    }
   }
 
   private <T> T readContentFromJson(RestResponse r, TypeToken<T> typeToken) throws Exception {
     r.assertOK();
-    JsonReader jsonReader = new JsonReader(r.getReader());
-    jsonReader.setLenient(true);
-    return newGson().fromJson(jsonReader, typeToken.getType());
+    try (JsonReader jsonReader = new JsonReader(r.getReader())) {
+      jsonReader.setLenient(true);
+      return newGson().fromJson(jsonReader, typeToken.getType());
+    }
   }
 
   private String readContentFromJson(RestResponse r) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
index f1bba8a..ee5d3b0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
@@ -830,9 +830,10 @@
   private static <T> T readContentFromJson(RestResponse r, int expectedStatus, Class<T> clazz)
       throws Exception {
     r.assertStatus(expectedStatus);
-    JsonReader jsonReader = new JsonReader(r.getReader());
-    jsonReader.setLenient(true);
-    return newGson().fromJson(jsonReader, clazz);
+    try (JsonReader jsonReader = new JsonReader(r.getReader())) {
+      jsonReader.setLenient(true);
+      return newGson().fromJson(jsonReader, clazz);
+    }
   }
 
   private static void assertReviewers(
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index c9c3636..311dd74 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -57,6 +57,7 @@
 import com.google.gerrit.extensions.api.changes.StarsInput;
 import com.google.gerrit.extensions.api.groups.GroupInput;
 import com.google.gerrit.extensions.api.projects.ConfigInput;
+import com.google.gerrit.extensions.api.projects.ProjectInput;
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.client.ProjectWatchInfo;
 import com.google.gerrit.extensions.client.ReviewerState;
@@ -375,6 +376,7 @@
     assertQuery("status:pe", expected);
     assertQuery("status:pen", expected);
     assertQuery("is:open", expected);
+    assertQuery("is:pending", expected);
   }
 
   @Test
@@ -630,13 +632,15 @@
   public void byCommit() throws Exception {
     TestRepository<Repo> repo = createProject("repo");
     ChangeInserter ins = newChange(repo);
-    insert(repo, ins);
+    Change change = insert(repo, ins);
     String sha = ins.getCommitId().name();
 
     assertQuery("0000000000000000000000000000000000000000");
+    assertQuery("commit:0000000000000000000000000000000000000000");
     for (int i = 0; i <= 36; i++) {
       String q = sha.substring(0, 40 - i);
-      assertQuery(q, ins.getChange());
+      assertQuery(q, change);
+      assertQuery("commit:" + q, change);
     }
   }
 
@@ -648,6 +652,7 @@
         accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
     Change change2 = insert(repo, newChange(repo), user2);
 
+    assertQuery("is:owner", change1);
     assertQuery("owner:" + userId.get(), change1);
     assertQuery("owner:" + user2, change2);
 
@@ -749,6 +754,7 @@
 
     assertQuery("ownerin:Administrators", change1);
     assertQuery("ownerin:\"Registered Users\"", change2, change1);
+    assertQuery("ownerin:\"Registered Users\" project:repo", change3, change2, change1);
     assertQuery("ownerin:\"Registered Users\" status:merged", change3);
   }
 
@@ -766,6 +772,17 @@
   }
 
   @Test
+  public void byParentProject() throws Exception {
+    TestRepository<Repo> repo1 = createProject("repo1");
+    TestRepository<Repo> repo2 = createProject("repo2", "repo1");
+    Change change1 = insert(repo1, newChange(repo1));
+    Change change2 = insert(repo2, newChange(repo2));
+
+    assertQuery("parentproject:repo1", change2, change1);
+    assertQuery("parentproject:repo2", change2);
+  }
+
+  @Test
   public void byProjectPrefix() throws Exception {
     TestRepository<Repo> repo1 = createProject("repo1");
     TestRepository<Repo> repo2 = createProject("repo2");
@@ -1363,7 +1380,7 @@
   }
 
   @Test
-  public void byBefore() throws Exception {
+  public void byBeforeUntil() throws Exception {
     long thirtyHoursInMs = MILLISECONDS.convert(30, HOURS);
     resetTimeWithClockStep(thirtyHoursInMs, MILLISECONDS);
     TestRepository<Repo> repo = createProject("repo");
@@ -1372,20 +1389,22 @@
     Change change2 = insert(repo, newChange(repo), null, new Timestamp(startMs + thirtyHoursInMs));
     TestTimeUtil.setClockStep(0, MILLISECONDS);
 
-    assertQuery("before:2009-09-29");
-    assertQuery("before:2009-09-30");
-    assertQuery("before:\"2009-09-30 16:59:00 -0400\"");
-    assertQuery("before:\"2009-09-30 20:59:00 -0000\"");
-    assertQuery("before:\"2009-09-30 20:59:00\"");
-    assertQuery("before:\"2009-09-30 17:02:00 -0400\"", change1);
-    assertQuery("before:\"2009-10-01 21:02:00 -0000\"", change1);
-    assertQuery("before:\"2009-10-01 21:02:00\"", change1);
-    assertQuery("before:2009-10-01", change1);
-    assertQuery("before:2009-10-03", change2, change1);
+    for (String predicate : Lists.newArrayList("before:", "until:")) {
+      assertQuery(predicate + "2009-09-29");
+      assertQuery(predicate + "2009-09-30");
+      assertQuery(predicate + "\"2009-09-30 16:59:00 -0400\"");
+      assertQuery(predicate + "\"2009-09-30 20:59:00 -0000\"");
+      assertQuery(predicate + "\"2009-09-30 20:59:00\"");
+      assertQuery(predicate + "\"2009-09-30 17:02:00 -0400\"", change1);
+      assertQuery(predicate + "\"2009-10-01 21:02:00 -0000\"", change1);
+      assertQuery(predicate + "\"2009-10-01 21:02:00\"", change1);
+      assertQuery(predicate + "2009-10-01", change1);
+      assertQuery(predicate + "2009-10-03", change2, change1);
+    }
   }
 
   @Test
-  public void byAfter() throws Exception {
+  public void byAfterSince() throws Exception {
     long thirtyHoursInMs = MILLISECONDS.convert(30, HOURS);
     resetTimeWithClockStep(thirtyHoursInMs, MILLISECONDS);
     TestRepository<Repo> repo = createProject("repo");
@@ -1394,11 +1413,13 @@
     Change change2 = insert(repo, newChange(repo), null, new Timestamp(startMs + thirtyHoursInMs));
     TestTimeUtil.setClockStep(0, MILLISECONDS);
 
-    assertQuery("after:2009-10-03");
-    assertQuery("after:\"2009-10-01 20:59:59 -0400\"", change2);
-    assertQuery("after:\"2009-10-01 20:59:59 -0000\"", change2);
-    assertQuery("after:2009-10-01", change2);
-    assertQuery("after:2009-09-30", change2, change1);
+    for (String predicate : Lists.newArrayList("after:", "since:")) {
+      assertQuery(predicate + "2009-10-03");
+      assertQuery(predicate + "\"2009-10-01 20:59:59 -0400\"", change2);
+      assertQuery(predicate + "\"2009-10-01 20:59:59 -0000\"", change2);
+      assertQuery(predicate + "2009-10-01", change2);
+      assertQuery(predicate + "2009-09-30", change2, change1);
+    }
   }
 
   @Test
@@ -1448,13 +1469,13 @@
 
     assertQuery("deleted:<=0", change1);
 
-    for (String str : Lists.newArrayList("delta", "size")) {
-      assertQuery(str + ":<2");
-      assertQuery(str + ":3", change1);
-      assertQuery(str + ":>2", change1);
-      assertQuery(str + ":>=3", change1);
-      assertQuery(str + ":<3", change2);
-      assertQuery(str + ":<=2", change2);
+    for (String str : Lists.newArrayList("delta:", "size:")) {
+      assertQuery(str + "<2");
+      assertQuery(str + "3", change1);
+      assertQuery(str + ">2", change1);
+      assertQuery(str + ">=3", change1);
+      assertQuery(str + "<3", change2);
+      assertQuery(str + "<=2", change2);
     }
   }
 
@@ -1564,6 +1585,28 @@
   }
 
   @Test
+  public void visible() throws Exception {
+    TestRepository<Repo> repo = createProject("repo");
+    Change change1 = insert(repo, newChange(repo));
+    Change change2 = insert(repo, newChange(repo));
+
+    gApi.changes().id(change2.getChangeId()).setPrivate(true, "private");
+
+    String q = "project:repo";
+    assertQuery(q, change2, change1);
+
+    // Second user cannot see first user's private change.
+    Account.Id user2 =
+        accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+    assertQuery(q + " visibleto:" + user2.get(), change1);
+
+    requestContext.setContext(
+        newRequestContext(
+            accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId()));
+    assertQuery("is:visible", change1);
+  }
+
+  @Test
   public void byCommentBy() throws Exception {
     TestRepository<Repo> repo = createProject("repo");
     Change change1 = insert(repo, newChange(repo));
@@ -1793,6 +1836,26 @@
   }
 
   @Test
+  public void mergeable() throws Exception {
+    TestRepository<Repo> repo = createProject("repo");
+    RevCommit commit1 = repo.parseBody(repo.commit().add("file1", "contents1").create());
+    RevCommit commit2 = repo.parseBody(repo.commit().add("file1", "contents2").create());
+    Change change1 = insert(repo, newChangeForCommit(repo, commit1));
+    Change change2 = insert(repo, newChangeForCommit(repo, commit2));
+
+    assertQuery("conflicts:" + change1.getId().get(), change2);
+    assertQuery("conflicts:" + change2.getId().get(), change1);
+    assertQuery("is:mergeable", change2, change1);
+
+    gApi.changes().id(change1.getChangeId()).revision("current").review(ReviewInput.approve());
+    gApi.changes().id(change1.getChangeId()).revision("current").submit();
+
+    assertQuery("status:open conflicts:" + change2.getId().get());
+    assertQuery("status:open is:mergeable");
+    assertQuery("status:open -is:mergeable", change2);
+  }
+
+  @Test
   public void reviewedBy() throws Exception {
     resetTimeWithClockStep(2, MINUTES);
     TestRepository<Repo> repo = createProject("repo");
@@ -1839,6 +1902,7 @@
     TestRepository<Repo> repo = createProject("repo");
     Change change1 = insert(repo, newChange(repo));
     Change change2 = insert(repo, newChange(repo));
+    Change change3 = insert(repo, newChange(repo));
     insert(repo, newChange(repo));
 
     AddReviewerInput rin = new AddReviewerInput();
@@ -1851,16 +1915,49 @@
     rin.state = ReviewerState.CC;
     gApi.changes().id(change2.getId().get()).addReviewer(rin);
 
+    assertQuery("is:reviewer");
+    assertQuery("reviewer:self");
+    gApi.changes().id(change3.getChangeId()).revision("current").review(ReviewInput.recommend());
+    assertQuery("is:reviewer", change3);
+    assertQuery("reviewer:self", change3);
+
+    requestContext.setContext(newRequestContext(user1));
     if (notesMigration.readChanges()) {
       assertQuery("reviewer:" + user1, change1);
       assertQuery("cc:" + user1, change2);
+      assertQuery("is:cc", change2);
+      assertQuery("cc:self", change2);
     } else {
       assertQuery("reviewer:" + user1, change2, change1);
       assertQuery("cc:" + user1);
+      assertQuery("is:cc");
+      assertQuery("cc:self");
     }
   }
 
   @Test
+  public void byReviewed() throws Exception {
+    TestRepository<Repo> repo = createProject("repo");
+    Account.Id otherUser =
+        accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+    Change change1 = insert(repo, newChange(repo));
+    Change change2 = insert(repo, newChange(repo));
+
+    assertQuery("is:reviewed");
+    assertQuery("status:reviewed");
+    assertQuery("-is:reviewed", change2, change1);
+    assertQuery("-status:reviewed", change2, change1);
+
+    requestContext.setContext(newRequestContext(otherUser));
+    gApi.changes().id(change1.getChangeId()).current().review(ReviewInput.recommend());
+
+    assertQuery("is:reviewed", change1);
+    assertQuery("status:reviewed", change1);
+    assertQuery("-is:reviewed", change2);
+    assertQuery("-status:reviewed", change2);
+  }
+
+  @Test
   public void reviewerin() throws Exception {
     Account.Id user1 = accountManager.authenticate(AuthRequest.forUser("user1")).getAccountId();
     Account.Id user2 = accountManager.authenticate(AuthRequest.forUser("user2")).getAccountId();
@@ -1899,6 +1996,7 @@
     gApi.changes().id(change2.getId().get()).current().submit();
 
     assertQuery("reviewerin:" + group);
+    assertQuery("project:repo reviewerin:" + group, change2);
     assertQuery("status:merged reviewerin:" + group, change2);
   }
 
@@ -2016,6 +2114,10 @@
     // NEED records don't have associated users.
     assertQuery("label:CodE-RevieW=need,user1");
     assertQuery("label:CodE-RevieW=need,user");
+
+    gApi.changes().id(change1.getId().get()).current().submit();
+    assertQuery("submittable:ok");
+    assertQuery("submittable:closed", change1);
   }
 
   @Test
@@ -2692,6 +2794,89 @@
         mergedOwned);
   }
 
+  @Test
+  public void assignee() throws Exception {
+    TestRepository<Repo> repo = createProject("repo");
+    Change change1 = insert(repo, newChange(repo));
+    Change change2 = insert(repo, newChange(repo));
+
+    AssigneeInput input = new AssigneeInput();
+    input.assignee = user.getUserName().get();
+    gApi.changes().id(change1.getChangeId()).setAssignee(input);
+
+    assertQuery("is:assigned", change1);
+    assertQuery("-is:assigned", change2);
+    assertQuery("is:unassigned", change2);
+    assertQuery("-is:unassigned", change1);
+    assertQuery("assignee:" + user.getUserName().get(), change1);
+    assertQuery("-assignee:" + user.getUserName().get(), change2);
+  }
+
+  @Test
+  public void userDestination() throws Exception {
+    TestRepository<Repo> repo1 = createProject("repo1");
+    Change change1 = insert(repo1, newChange(repo1));
+    TestRepository<Repo> repo2 = createProject("repo2");
+    Change change2 = insert(repo2, newChange(repo2));
+
+    assertThatQueryException("destination:foo")
+        .hasMessageThat()
+        .isEqualTo("Unknown named destination: foo");
+
+    String destination1 = "refs/heads/master\trepo1";
+    String destination2 = "refs/heads/master\trepo2";
+    String destination3 = "refs/heads/master\trepo1\nrefs/heads/master\trepo2";
+    String destination4 = "refs/heads/master\trepo3";
+    String destination5 = "refs/heads/other\trepo1";
+
+    TestRepository<Repo> allUsers = new TestRepository<>(repoManager.openRepository(allUsersName));
+    String refsUsers = RefNames.refsUsers(userId);
+    allUsers.branch(refsUsers).commit().add("destinations/destination1", destination1).create();
+    allUsers.branch(refsUsers).commit().add("destinations/destination2", destination2).create();
+    allUsers.branch(refsUsers).commit().add("destinations/destination3", destination3).create();
+    allUsers.branch(refsUsers).commit().add("destinations/destination4", destination4).create();
+    allUsers.branch(refsUsers).commit().add("destinations/destination5", destination5).create();
+
+    Ref userRef = allUsers.getRepository().exactRef(refsUsers);
+    assertThat(userRef).isNotNull();
+
+    assertQuery("destination:destination1", change1);
+    assertQuery("destination:destination2", change2);
+    assertQuery("destination:destination3", change2, change1);
+    assertQuery("destination:destination4");
+    assertQuery("destination:destination5");
+  }
+
+  @Test
+  public void userQuery() throws Exception {
+    TestRepository<Repo> repo = createProject("repo");
+    Change change1 = insert(repo, newChange(repo));
+    Change change2 = insert(repo, newChangeForBranch(repo, "stable"));
+
+    String queries =
+        "query1\tproject:repo\n"
+            + "query2\tproject:repo status:open\n"
+            + "query3\tproject:repo branch:stable\n"
+            + "query4\tproject:repo branch:other";
+
+    TestRepository<Repo> allUsers = new TestRepository<>(repoManager.openRepository(allUsersName));
+    String refsUsers = RefNames.refsUsers(userId);
+    allUsers.branch(refsUsers).commit().add("queries", queries).create();
+
+    Ref userRef = allUsers.getRepository().exactRef(refsUsers);
+    assertThat(userRef).isNotNull();
+
+    assertThatQueryException("query:foo").hasMessageThat().isEqualTo("Unknown named query: foo");
+
+    assertQuery("query:query1", change2, change1);
+    assertQuery("query:query2", change2, change1);
+    gApi.changes().id(change1.getChangeId()).revision("current").review(ReviewInput.approve());
+    gApi.changes().id(change1.getChangeId()).revision("current").submit();
+    assertQuery("query:query2", change2);
+    assertQuery("query:query3", change2);
+    assertQuery("query:query4");
+  }
+
   protected ChangeInserter newChange(TestRepository<Repo> repo) throws Exception {
     return newChange(repo, null, null, null, null, false);
   }
@@ -2817,6 +3002,14 @@
     return new TestRepository<>(repoManager.openRepository(new Project.NameKey(name)));
   }
 
+  protected TestRepository<Repo> createProject(String name, String parent) throws Exception {
+    ProjectInput input = new ProjectInput();
+    input.name = name;
+    input.parent = parent;
+    gApi.projects().create(input).get();
+    return new TestRepository<>(repoManager.openRepository(new Project.NameKey(name)));
+  }
+
   protected QueryRequest newQuery(Object query) {
     return gApi.changes().query(query.toString());
   }
diff --git a/lib/elasticsearch/BUILD b/lib/elasticsearch/BUILD
index 387cdad..13c033e 100644
--- a/lib/elasticsearch/BUILD
+++ b/lib/elasticsearch/BUILD
@@ -7,7 +7,6 @@
     runtime_deps = [
         ":compress-lzf",
         ":hppc",
-        ":jna",
         ":joda-time",
         ":jsr166e",
         ":netty",
@@ -15,12 +14,10 @@
         "//lib/jackson:jackson-core",
         "//lib/jackson:jackson-dataformat-cbor",
         "//lib/jackson:jackson-dataformat-smile",
-        "//lib/lucene:lucene-codecs",
         "//lib/lucene:lucene-highlighter",
         "//lib/lucene:lucene-join",
         "//lib/lucene:lucene-memory",
         "//lib/lucene:lucene-queries",
-        "//lib/lucene:lucene-sandbox",
         "//lib/lucene:lucene-spatial",
         "//lib/lucene:lucene-suggest",
     ],
@@ -73,9 +70,3 @@
     visibility = ["//lib/elasticsearch:__pkg__"],
     exports = ["@t_digest//jar"],
 )
-
-java_library(
-    name = "jna",
-    data = ["//lib:LICENSE-Apache2.0"],
-    exports = ["@jna//jar"],
-)
diff --git a/lib/httpcomponents/BUILD b/lib/httpcomponents/BUILD
index 2b2cc6f..6e8fcd8 100644
--- a/lib/httpcomponents/BUILD
+++ b/lib/httpcomponents/BUILD
@@ -45,9 +45,3 @@
     data = ["//lib:LICENSE-Apache2.0"],
     exports = ["@httpcore_nio//jar"],
 )
-
-java_library(
-    name = "httpcore-niossl",
-    data = ["//lib:LICENSE-Apache2.0"],
-    exports = ["@httpcore_niossl//jar"],
-)
diff --git a/lib/jackson/BUILD b/lib/jackson/BUILD
index 4847371..8ade0cf 100644
--- a/lib/jackson/BUILD
+++ b/lib/jackson/BUILD
@@ -9,13 +9,13 @@
 )
 
 java_library(
-    name = "jackson-dataformat-smile",
-    data = ["//lib:LICENSE-Apache2.0"],
-    exports = ["@jackson_dataformat_smile//jar"],
-)
-
-java_library(
     name = "jackson-dataformat-cbor",
     data = ["//lib:LICENSE-Apache2.0"],
     exports = ["@jackson_dataformat_cbor//jar"],
 )
+
+java_library(
+    name = "jackson-dataformat-smile",
+    data = ["//lib:LICENSE-Apache2.0"],
+    exports = ["@jackson_dataformat_smile//jar"],
+)
diff --git a/lib/jest/BUILD b/lib/jest/BUILD
index 3fdb5f7..169f271 100644
--- a/lib/jest/BUILD
+++ b/lib/jest/BUILD
@@ -5,6 +5,9 @@
     data = ["//lib:LICENSE-Apache2.0"],
     visibility = ["//visibility:public"],
     exports = ["@jest_common//jar"],
+    runtime_deps = [
+        "//lib/commons:lang3",
+    ],
 )
 
 java_library(
@@ -13,11 +16,8 @@
     visibility = ["//visibility:public"],
     exports = ["@jest//jar"],
     runtime_deps = [
-        ":jest-common",
-        "//lib/commons:lang3",
         "//lib/httpcomponents:httpasyncclient",
         "//lib/httpcomponents:httpclient",
         "//lib/httpcomponents:httpcore-nio",
-        "//lib/httpcomponents:httpcore-niossl",
     ],
 )
diff --git a/lib/lucene/BUILD b/lib/lucene/BUILD
index bbf43a6..6590af4 100644
--- a/lib/lucene/BUILD
+++ b/lib/lucene/BUILD
@@ -23,13 +23,6 @@
 )
 
 java_library(
-    name = "lucene-codecs",
-    data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
-    exports = ["@lucene_codecs//jar"],
-)
-
-java_library(
     name = "lucene-core",
     data = ["//lib:LICENSE-Apache2.0"],
     visibility = ["//visibility:public"],
@@ -71,12 +64,6 @@
 )
 
 java_library(
-    name = "lucene-sandbox",
-    data = ["//lib:LICENSE-Apache2.0"],
-    exports = ["@lucene_sandbox//jar"],
-)
-
-java_library(
     name = "lucene-spatial",
     data = ["//lib:LICENSE-Apache2.0"],
     exports = ["@lucene_spatial//jar"],