Merge "Add the ability to display error to gr-autocomplete."
diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt
index 358324d..87f3851 100644
--- a/Documentation/cmd-create-project.txt
+++ b/Documentation/cmd-create-project.txt
@@ -108,6 +108,7 @@
 	Action used by Gerrit to submit an approved change to its
 	destination branch.  Supported options are:
 +
+* INHERIT: inherits the submit-type from the parent project.
 * FAST_FORWARD_ONLY: produces a strictly linear history.
 * MERGE_IF_NECESSARY: create a merge commit when required.
 * REBASE_IF_NECESSARY: rebase the commit when required.
@@ -116,7 +117,7 @@
 * CHERRY_PICK: always cherry-pick the commit.
 
 +
-Defaults to MERGE_IF_NECESSARY unless
+Defaults to INHERIT unless
 link:config-gerrit.html#repository.name.defaultSubmitType[
 repository.<name>.defaultSubmitType] is set to a different value.
 For more details see link:config-project-config.html#submit-type[
diff --git a/java/com/google/gerrit/index/IndexedField.java b/java/com/google/gerrit/index/IndexedField.java
index 5fcb062..99004bb 100644
--- a/java/com/google/gerrit/index/IndexedField.java
+++ b/java/com/google/gerrit/index/IndexedField.java
@@ -249,6 +249,8 @@
 
   public SearchSpec integerRange(String name) {
     checkState(fieldType().equals(INTEGER_TYPE));
+    // we currently store all integer range fields, this may change in the future
+    checkState(stored());
     return addSearchSpec(name, SearchOption.RANGE);
   }
 
@@ -367,7 +369,12 @@
   /** Optional description of the field data. */
   public abstract Optional<String> description();
 
-  /** True if this field is mandatory. Default is false. */
+  /**
+   * True if this field is mandatory. Default is false.
+   *
+   * <p>This property is not enforced by the common indexing logic. It is up to the index
+   * implementations to enforce that the field is required.
+   */
   public abstract boolean required();
 
   /** Allow reading the actual data from the index. Default is false. */
@@ -380,6 +387,9 @@
    * Optional size constrain on the field. The size is not constrained if this property is {@link
    * Optional#empty()}
    *
+   * <p>This property is not enforced by the common indexing logic. It is up to the index
+   * implementations to enforce the size.
+   *
    * <p>If the field is {@link #repeatable()}, the constraint applies to each element separately.
    */
   public abstract Optional<Integer> size();
diff --git a/java/com/google/gerrit/index/Schema.java b/java/com/google/gerrit/index/Schema.java
index 9f07cab..25d7cf3 100644
--- a/java/com/google/gerrit/index/Schema.java
+++ b/java/com/google/gerrit/index/Schema.java
@@ -209,7 +209,10 @@
     return indexedFields;
   }
 
-  /** Returns all fields in this schema where {@link FieldDef#isStored()} is true. */
+  /**
+   * Returns names of {@link SchemaField} fields in this schema where {@link SchemaField#isStored()}
+   * is true.
+   */
   public final ImmutableSet<String> getStoredFields() {
     return storedFields;
   }
diff --git a/java/com/google/gerrit/index/testing/AbstractFakeIndex.java b/java/com/google/gerrit/index/testing/AbstractFakeIndex.java
index 6f79dce..591ae26 100644
--- a/java/com/google/gerrit/index/testing/AbstractFakeIndex.java
+++ b/java/com/google/gerrit/index/testing/AbstractFakeIndex.java
@@ -251,7 +251,7 @@
     protected Map<String, Object> docFor(ChangeData value) {
       ImmutableMap.Builder<String, Object> doc = ImmutableMap.builder();
       for (SchemaField<ChangeData, ?> field : getSchema().getSchemaFields().values()) {
-        if (ChangeField.MERGEABLE.getName().equals(field.getName()) && skipMergable) {
+        if (ChangeField.MERGEABLE_SPEC.getName().equals(field.getName()) && skipMergable) {
           continue;
         }
         Object docifiedValue = field.get(value);
diff --git a/java/com/google/gerrit/index/testing/TestIndexedFields.java b/java/com/google/gerrit/index/testing/TestIndexedFields.java
index 7a120b7..51440fb 100644
--- a/java/com/google/gerrit/index/testing/TestIndexedFields.java
+++ b/java/com/google/gerrit/index/testing/TestIndexedFields.java
@@ -164,6 +164,12 @@
   public static final IndexedField<TestIndexedData, String>.SearchSpec STRING_FIELD_SPEC =
       STRING_FIELD.fullText("string_test");
 
+  public static final IndexedField<TestIndexedData, String>.SearchSpec PREFIX_STRING_FIELD_SPEC =
+      STRING_FIELD.prefix("prefix_string_test");
+
+  public static final IndexedField<TestIndexedData, String>.SearchSpec EXACT_STRING_FIELD_SPEC =
+      STRING_FIELD.exact("exact_string_test");
+
   public static final IndexedField<TestIndexedData, Iterable<byte[]>> ITERABLE_STORED_BYTE_FIELD =
       IndexedField.<TestIndexedData>iterableByteArrayBuilder("IterableByteTestField")
           .stored()
diff --git a/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 4122181..de4b26f 100644
--- a/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -143,7 +143,7 @@
     this.skipFields =
         MergeabilityComputationBehavior.fromConfig(cfg).includeInIndex()
             ? ImmutableSet.of()
-            : ImmutableSet.of(ChangeField.MERGEABLE.getName());
+            : ImmutableSet.of(ChangeField.MERGEABLE_SPEC.getName());
 
     GerritIndexWriterConfig openConfig = new GerritIndexWriterConfig(cfg, "changes_open");
     GerritIndexWriterConfig closedConfig = new GerritIndexWriterConfig(cfg, "changes_closed");
diff --git a/java/com/google/gerrit/server/index/change/ChangeField.java b/java/com/google/gerrit/server/index/change/ChangeField.java
index e60a2fd..7aefa63 100644
--- a/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -19,8 +19,6 @@
 import static com.google.common.collect.ImmutableListMultimap.toImmutableListMultimap;
 import static com.google.common.collect.ImmutableSet.toImmutableSet;
 import static com.google.gerrit.index.FieldDef.exact;
-import static com.google.gerrit.index.FieldDef.fullText;
-import static com.google.gerrit.index.FieldDef.intRange;
 import static com.google.gerrit.index.FieldDef.integer;
 import static com.google.gerrit.index.FieldDef.prefix;
 import static com.google.gerrit.index.FieldDef.storedOnly;
@@ -452,7 +450,7 @@
    * Users included in the attention set of the change. This omits timestamp, reason and possible
    * future fields.
    *
-   * @see #ATTENTION_SET_FULL
+   * @see #ATTENTION_SET_FULL_SPEC
    */
   public static final IndexedField<ChangeData, Iterable<Integer>> ATTENTION_SET_USERS_FIELD =
       IndexedField.<ChangeData>iterableIntegerBuilder("AttentionSetUsers")
@@ -464,6 +462,7 @@
   /** Number of changes that contain attention set. */
   public static final IndexedField<ChangeData, Integer> ATTENTION_SET_USERS_COUNT_FIELD =
       IndexedField.<ChangeData>integerBuilder("AttentionSetUsersCount")
+          .stored()
           .build(cd -> additionsOnly(cd.attentionSet()).size());
 
   public static final IndexedField<ChangeData, Integer>.SearchSpec ATTENTION_SET_USERS_COUNT =
@@ -475,9 +474,11 @@
    *
    * @see #ATTENTION_SET_USERS
    */
-  public static final FieldDef<ChangeData, Iterable<byte[]>> ATTENTION_SET_FULL =
-      storedOnly(ChangeQueryBuilder.FIELD_ATTENTION_SET_FULL)
-          .buildRepeatable(
+  public static final IndexedField<ChangeData, Iterable<byte[]>> ATTENTION_SET_FULL_FIELD =
+      IndexedField.<ChangeData>iterableByteArrayBuilder("AttentionSetFull")
+          .stored()
+          .required()
+          .build(
               ChangeField::storedAttentionSet,
               (cd, value) ->
                   parseAttentionSet(
@@ -486,6 +487,10 @@
                           .collect(toImmutableSet()),
                       cd));
 
+  public static final IndexedField<ChangeData, Iterable<byte[]>>.SearchSpec
+      ATTENTION_SET_FULL_SPEC =
+          ATTENTION_SET_FULL_FIELD.storedOnly(ChangeQueryBuilder.FIELD_ATTENTION_SET_FULL);
+
   /** The user assigned to the change. */
   public static final IndexedField<ChangeData, Integer> ASSIGNEE_FIELD =
       IndexedField.<ChangeData>integerBuilder("Assignee")
@@ -749,25 +754,37 @@
   }
 
   /** Commit ID of any patch set on the change, using prefix match. */
-  public static final FieldDef<ChangeData, Iterable<String>> COMMIT =
-      prefix(ChangeQueryBuilder.FIELD_COMMIT).buildRepeatable(ChangeField::getRevisions);
+  public static final IndexedField<ChangeData, Iterable<String>> COMMIT_FIELD =
+      IndexedField.<ChangeData>iterableStringBuilder("Commit")
+          .size(40)
+          .required()
+          .build(ChangeField::getRevisions);
+
+  public static final IndexedField<ChangeData, Iterable<String>>.SearchSpec COMMIT_SPEC =
+      COMMIT_FIELD.prefix(ChangeQueryBuilder.FIELD_COMMIT);
 
   /** Commit ID of any patch set on the change, using exact match. */
-  public static final FieldDef<ChangeData, Iterable<String>> EXACT_COMMIT =
-      exact(ChangeQueryBuilder.FIELD_EXACTCOMMIT).buildRepeatable(ChangeField::getRevisions);
+  public static final IndexedField<ChangeData, Iterable<String>>.SearchSpec EXACT_COMMIT_SPEC =
+      COMMIT_FIELD.exact(ChangeQueryBuilder.FIELD_EXACTCOMMIT);
 
   private static ImmutableSet<String> getRevisions(ChangeData cd) {
     return cd.patchSets().stream().map(ps -> ps.commitId().name()).collect(toImmutableSet());
   }
 
   /** Tracking id extracted from a footer. */
-  public static final FieldDef<ChangeData, Iterable<String>> TR =
-      exact(ChangeQueryBuilder.FIELD_TR)
-          .buildRepeatable(cd -> ImmutableSet.copyOf(cd.trackingFooters().values()));
+  public static final IndexedField<ChangeData, Iterable<String>> TR_FIELD =
+      IndexedField.<ChangeData>iterableStringBuilder("TrackingFooter")
+          .build(cd -> ImmutableSet.copyOf(cd.trackingFooters().values()));
+
+  public static final IndexedField<ChangeData, Iterable<String>>.SearchSpec TR_SPEC =
+      TR_FIELD.exact(ChangeQueryBuilder.FIELD_TR);
 
   /** List of labels on the current patch set including change owner votes. */
-  public static final FieldDef<ChangeData, Iterable<String>> LABEL =
-      exact("label2").buildRepeatable(cd -> getLabels(cd));
+  public static final IndexedField<ChangeData, Iterable<String>> LABEL_FIELD =
+      IndexedField.<ChangeData>iterableStringBuilder("Label").required().build(cd -> getLabels(cd));
+
+  public static final IndexedField<ChangeData, Iterable<String>>.SearchSpec LABEL_SPEC =
+      LABEL_FIELD.exact("label2");
 
   private static Iterable<String> getLabels(ChangeData cd) {
     Set<String> allApprovals = new HashSet<>();
@@ -1010,18 +1027,29 @@
   }
 
   /** Commit message of the current patch set. */
-  public static final FieldDef<ChangeData, String> COMMIT_MESSAGE =
-      fullText(ChangeQueryBuilder.FIELD_MESSAGE).build(ChangeData::commitMessage);
+  public static final IndexedField<ChangeData, String> COMMIT_MESSAGE_FIELD =
+      IndexedField.<ChangeData>stringBuilder("CommitMessage")
+          .required()
+          .build(ChangeData::commitMessage);
 
-  /** Commit message of the current patch set. */
-  public static final FieldDef<ChangeData, String> COMMIT_MESSAGE_EXACT =
-      exact(ChangeQueryBuilder.FIELD_MESSAGE_EXACT)
+  public static final IndexedField<ChangeData, String>.SearchSpec COMMIT_MESSAGE =
+      COMMIT_MESSAGE_FIELD.fullText(ChangeQueryBuilder.FIELD_MESSAGE);
+
+  /** Commit message of the current patch set, used to exactly match the commit message */
+  public static final IndexedField<ChangeData, String> COMMIT_MESSAGE_EXACT_FIELD =
+      IndexedField.<ChangeData>stringBuilder("CommitMessageExact")
+          .required()
+          .description(
+              "Same as CommitMessage, but truncated, since supporting such large tokens may be problematic for indexes.")
           .build(cd -> truncateStringValueToMaxTermLength(cd.commitMessage()));
 
+  public static final IndexedField<ChangeData, String>.SearchSpec COMMIT_MESSAGE_EXACT =
+      COMMIT_MESSAGE_EXACT_FIELD.exact(ChangeQueryBuilder.FIELD_MESSAGE_EXACT);
+
   /** Summary or inline comment. */
-  public static final FieldDef<ChangeData, Iterable<String>> COMMENT =
-      fullText(ChangeQueryBuilder.FIELD_COMMENT)
-          .buildRepeatable(
+  public static final IndexedField<ChangeData, Iterable<String>> COMMENT_FIELD =
+      IndexedField.<ChangeData>iterableStringBuilder("Comment")
+          .build(
               cd ->
                   Stream.concat(
                           cd.publishedComments().stream().map(c -> c.message),
@@ -1032,22 +1060,35 @@
                           cd.messages().stream().map(ChangeMessage::getMessage))
                       .collect(toSet()));
 
+  public static final IndexedField<ChangeData, Iterable<String>>.SearchSpec COMMENT_SPEC =
+      COMMENT_FIELD.fullText(ChangeQueryBuilder.FIELD_COMMENT);
+
   /** Number of unresolved comment threads of the change, including robot comments. */
-  public static final FieldDef<ChangeData, Integer> UNRESOLVED_COMMENT_COUNT =
-      intRange(ChangeQueryBuilder.FIELD_UNRESOLVED_COMMENT_COUNT)
+  public static final IndexedField<ChangeData, Integer> UNRESOLVED_COMMENT_COUNT_FIELD =
+      IndexedField.<ChangeData>integerBuilder("UnresolvedCommentCount")
+          .stored()
           .build(
               ChangeData::unresolvedCommentCount,
               (cd, field) -> cd.setUnresolvedCommentCount(field));
 
+  public static final IndexedField<ChangeData, Integer>.SearchSpec UNRESOLVED_COMMENT_COUNT_SPEC =
+      UNRESOLVED_COMMENT_COUNT_FIELD.integerRange(
+          ChangeQueryBuilder.FIELD_UNRESOLVED_COMMENT_COUNT);
+
   /** Total number of published inline comments of the change, including robot comments. */
-  public static final FieldDef<ChangeData, Integer> TOTAL_COMMENT_COUNT =
-      intRange("total_comments")
+  public static final IndexedField<ChangeData, Integer> TOTAL_COMMENT_COUNT_FIELD =
+      IndexedField.<ChangeData>integerBuilder("TotalCommentCount")
+          .stored()
           .build(ChangeData::totalCommentCount, (cd, field) -> cd.setTotalCommentCount(field));
 
+  public static final IndexedField<ChangeData, Integer>.SearchSpec TOTAL_COMMENT_COUNT_SPEC =
+      TOTAL_COMMENT_COUNT_FIELD.integerRange("total_comments");
+
   /** Whether the change is mergeable. */
-  public static final FieldDef<ChangeData, String> MERGEABLE =
-      exact(ChangeQueryBuilder.FIELD_MERGEABLE)
+  public static final IndexedField<ChangeData, String> MERGEABLE_FIELD =
+      IndexedField.<ChangeData>stringBuilder("Mergeable")
           .stored()
+          .size(1)
           .build(
               cd -> {
                 Boolean m = cd.isMergeable();
@@ -1058,10 +1099,14 @@
               },
               (cd, field) -> cd.setMergeable(field == null ? false : field.equals("1")));
 
+  public static final IndexedField<ChangeData, String>.SearchSpec MERGEABLE_SPEC =
+      MERGEABLE_FIELD.exact(ChangeQueryBuilder.FIELD_MERGEABLE);
+
   /** Whether the change is a merge commit. */
-  public static final FieldDef<ChangeData, String> MERGE =
-      exact(ChangeQueryBuilder.FIELD_MERGE)
+  public static final IndexedField<ChangeData, String> MERGE_FIELD =
+      IndexedField.<ChangeData>stringBuilder("Merge")
           .stored()
+          .size(1)
           .build(
               cd -> {
                 Boolean m = cd.isMerge();
@@ -1071,15 +1116,23 @@
                 return m ? "1" : "0";
               });
 
+  public static final IndexedField<ChangeData, String>.SearchSpec MERGE_SPEC =
+      MERGE_FIELD.exact(ChangeQueryBuilder.FIELD_MERGE);
+
   /** Whether the change is a cherry pick of another change. */
-  public static final FieldDef<ChangeData, String> CHERRY_PICK =
-      exact(ChangeQueryBuilder.FIELD_CHERRYPICK)
+  public static final IndexedField<ChangeData, String> CHERRY_PICK_FIELD =
+      IndexedField.<ChangeData>stringBuilder("CherryPick")
           .stored()
+          .size(1)
           .build(cd -> cd.change().getCherryPickOf() != null ? "1" : "0");
 
+  public static final IndexedField<ChangeData, String>.SearchSpec CHERRY_PICK_SPEC =
+      CHERRY_PICK_FIELD.exact(ChangeQueryBuilder.FIELD_CHERRYPICK);
+
   /** The number of inserted lines in this change. */
-  public static final FieldDef<ChangeData, Integer> ADDED =
-      intRange(ChangeQueryBuilder.FIELD_ADDED)
+  public static final IndexedField<ChangeData, Integer> ADDED_LINES_FIELD =
+      IndexedField.<ChangeData>integerBuilder("AddedLines")
+          .stored()
           .build(
               cd -> cd.changedLines().isPresent() ? cd.changedLines().get().insertions : null,
               (cd, field) -> {
@@ -1088,9 +1141,13 @@
                 }
               });
 
+  public static final IndexedField<ChangeData, Integer>.SearchSpec ADDED_LINES_SPEC =
+      ADDED_LINES_FIELD.integerRange(ChangeQueryBuilder.FIELD_ADDED);
+
   /** The number of deleted lines in this change. */
-  public static final FieldDef<ChangeData, Integer> DELETED =
-      intRange(ChangeQueryBuilder.FIELD_DELETED)
+  public static final IndexedField<ChangeData, Integer> DELETED_LINES_FIELD =
+      IndexedField.<ChangeData>integerBuilder("DeletedLines")
+          .stored()
           .build(
               cd -> cd.changedLines().isPresent() ? cd.changedLines().get().deletions : null,
               (cd, field) -> {
@@ -1099,28 +1156,49 @@
                 }
               });
 
+  public static final IndexedField<ChangeData, Integer>.SearchSpec DELETED_LINES_SPEC =
+      DELETED_LINES_FIELD.integerRange(ChangeQueryBuilder.FIELD_DELETED);
+
   /** The total number of modified lines in this change. */
-  public static final FieldDef<ChangeData, Integer> DELTA =
-      intRange(ChangeQueryBuilder.FIELD_DELTA)
+  public static final IndexedField<ChangeData, Integer> DELTA_LINES_FIELD =
+      IndexedField.<ChangeData>integerBuilder("DeltaLines")
+          .stored()
           .build(cd -> cd.changedLines().map(c -> c.insertions + c.deletions).orElse(null));
 
+  public static final IndexedField<ChangeData, Integer>.SearchSpec DELTA_LINES_SPEC =
+      DELTA_LINES_FIELD.integerRange(ChangeQueryBuilder.FIELD_DELTA);
+
   /** Determines if this change is private. */
-  public static final FieldDef<ChangeData, String> PRIVATE =
-      exact(ChangeQueryBuilder.FIELD_PRIVATE).build(cd -> cd.change().isPrivate() ? "1" : "0");
+  public static final IndexedField<ChangeData, String> PRIVATE_FIELD =
+      IndexedField.<ChangeData>stringBuilder("Private")
+          .size(1)
+          .build(cd -> cd.change().isPrivate() ? "1" : "0");
+
+  public static final IndexedField<ChangeData, String>.SearchSpec PRIVATE_SPEC =
+      PRIVATE_FIELD.exact(ChangeQueryBuilder.FIELD_PRIVATE);
 
   /** Determines if this change is work in progress. */
-  public static final FieldDef<ChangeData, String> WIP =
-      exact(ChangeQueryBuilder.FIELD_WIP).build(cd -> cd.change().isWorkInProgress() ? "1" : "0");
+  public static final IndexedField<ChangeData, String> WIP_FIELD =
+      IndexedField.<ChangeData>stringBuilder("WIP")
+          .size(1)
+          .build(cd -> cd.change().isWorkInProgress() ? "1" : "0");
+
+  public static final IndexedField<ChangeData, String>.SearchSpec WIP_SPEC =
+      WIP_FIELD.exact(ChangeQueryBuilder.FIELD_WIP);
 
   /** Determines if this change has started review. */
-  public static final FieldDef<ChangeData, String> STARTED =
-      exact(ChangeQueryBuilder.FIELD_STARTED)
+  public static final IndexedField<ChangeData, String> STARTED_FIELD =
+      IndexedField.<ChangeData>stringBuilder("ReviewStarted")
+          .size(1)
           .build(cd -> cd.change().hasReviewStarted() ? "1" : "0");
 
+  public static final IndexedField<ChangeData, String>.SearchSpec STARTED_SPEC =
+      STARTED_FIELD.exact(ChangeQueryBuilder.FIELD_STARTED);
+
   /** Users who have commented on this change. */
-  public static final FieldDef<ChangeData, Iterable<Integer>> COMMENTBY =
-      integer(ChangeQueryBuilder.FIELD_COMMENTBY)
-          .buildRepeatable(
+  public static final IndexedField<ChangeData, Iterable<Integer>> COMMENTBY_FIELD =
+      IndexedField.<ChangeData>iterableIntegerBuilder("CommentBy")
+          .build(
               cd ->
                   Stream.concat(
                           cd.messages().stream().map(ChangeMessage::getAuthor),
@@ -1129,6 +1207,9 @@
                       .map(Account.Id::get)
                       .collect(toSet()));
 
+  public static final IndexedField<ChangeData, Iterable<Integer>>.SearchSpec COMMENTBY_SPEC =
+      COMMENTBY_FIELD.integer(ChangeQueryBuilder.FIELD_COMMENTBY);
+
   /** Star labels on this change in the format: &lt;account-id&gt;:&lt;label&gt; */
   public static final FieldDef<ChangeData, Iterable<String>> STAR =
       exact(ChangeQueryBuilder.FIELD_STAR)
diff --git a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
index 3e43505..2af5d60 100644
--- a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
+++ b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
@@ -35,52 +35,42 @@
       schema(
           /* version= */ 74,
           ImmutableList.of(
-              ChangeField.ADDED,
               ChangeField.APPROVAL,
-              ChangeField.ATTENTION_SET_FULL,
               ChangeField.CHANGE,
-              ChangeField.CHERRY_PICK,
-              ChangeField.COMMENT,
-              ChangeField.COMMENTBY,
-              ChangeField.COMMIT,
-              ChangeField.COMMIT_MESSAGE,
-              ChangeField.DELETED,
-              ChangeField.DELTA,
               ChangeField.DRAFTBY,
               ChangeField.EDITBY,
-              ChangeField.EXACT_COMMIT,
               ChangeField.GROUP,
               ChangeField.ID,
-              ChangeField.LABEL,
               ChangeField.LEGACY_ID_STR,
-              ChangeField.MERGE,
-              ChangeField.MERGEABLE,
               ChangeField.PATCH_SET,
-              ChangeField.PRIVATE,
               ChangeField.REF_STATE,
               ChangeField.REF_STATE_PATTERN,
               ChangeField.REVIEWEDBY,
               ChangeField.STAR,
               ChangeField.STARBY,
-              ChangeField.STARTED,
               ChangeField.STORED_SUBMIT_RECORD_LENIENT,
               ChangeField.STORED_SUBMIT_RECORD_STRICT,
               ChangeField.STORED_SUBMIT_REQUIREMENTS,
               ChangeField.SUBMIT_RECORD,
               ChangeField.SUBMIT_RULE_RESULT,
-              ChangeField.TOTAL_COMMENT_COUNT,
-              ChangeField.TR,
-              ChangeField.UNRESOLVED_COMMENT_COUNT,
-              ChangeField.UPDATED,
-              ChangeField.WIP),
+              ChangeField.UPDATED),
           ImmutableList.<IndexedField<ChangeData, ?>>of(
+              ChangeField.ADDED_LINES_FIELD,
               ChangeField.ASSIGNEE_FIELD,
+              ChangeField.ATTENTION_SET_FULL_FIELD,
               ChangeField.ATTENTION_SET_USERS_COUNT_FIELD,
               ChangeField.ATTENTION_SET_USERS_FIELD,
               ChangeField.AUTHOR_PARTS_FIELD,
+              ChangeField.CHERRY_PICK_FIELD,
               ChangeField.CHERRY_PICK_OF_CHANGE_FIELD,
               ChangeField.CHERRY_PICK_OF_PATCHSET_FIELD,
+              ChangeField.COMMENTBY_FIELD,
+              ChangeField.COMMENT_FIELD,
               ChangeField.COMMITTER_PARTS_FIELD,
+              ChangeField.COMMIT_FIELD,
+              ChangeField.COMMIT_MESSAGE_FIELD,
+              ChangeField.DELETED_LINES_FIELD,
+              ChangeField.DELTA_LINES_FIELD,
               ChangeField.DIRECTORY_FIELD,
               ChangeField.EXACT_AUTHOR_FIELD,
               ChangeField.EXACT_COMMITTER_FIELD,
@@ -91,32 +81,51 @@
               ChangeField.HASHTAG_FIELD,
               ChangeField.IS_PURE_REVERT_FIELD,
               ChangeField.IS_SUBMITTABLE_FIELD,
+              ChangeField.LABEL_FIELD,
+              ChangeField.MERGEABLE_FIELD,
               ChangeField.MERGED_ON_FIELD,
+              ChangeField.MERGE_FIELD,
               ChangeField.ONLY_EXTENSIONS_FIELD,
               ChangeField.OWNER_FIELD,
               ChangeField.PATH_FIELD,
               ChangeField.PENDING_REVIEWER_BY_EMAIL_FIELD,
               ChangeField.PENDING_REVIEWER_FIELD,
+              ChangeField.PRIVATE_FIELD,
               ChangeField.PROJECT_FIELD,
               ChangeField.REF_FIELD,
               ChangeField.REVERT_OF_FIELD,
               ChangeField.REVIEWER_BY_EMAIL_FIELD,
               ChangeField.REVIEWER_FIELD,
+              ChangeField.STARTED_FIELD,
               ChangeField.STATUS_FIELD,
               ChangeField.SUBMISSIONID_FIELD,
               ChangeField.TOPIC_FIELD,
-              ChangeField.UPLOADER_FIELD),
+              ChangeField.TOTAL_COMMENT_COUNT_FIELD,
+              ChangeField.TR_FIELD,
+              ChangeField.UNRESOLVED_COMMENT_COUNT_FIELD,
+              ChangeField.UPLOADER_FIELD,
+              ChangeField.WIP_FIELD),
           ImmutableList.<IndexedField<ChangeData, ?>.SearchSpec>of(
+              ChangeField.ADDED_LINES_SPEC,
               ChangeField.ASSIGNEE_SPEC,
+              ChangeField.ATTENTION_SET_FULL_SPEC,
               ChangeField.ATTENTION_SET_USERS,
               ChangeField.ATTENTION_SET_USERS_COUNT,
               ChangeField.AUTHOR_PARTS_SPEC,
               ChangeField.CHERRY_PICK_OF_CHANGE,
               ChangeField.CHERRY_PICK_OF_PATCHSET,
+              ChangeField.CHERRY_PICK_SPEC,
+              ChangeField.COMMENTBY_SPEC,
+              ChangeField.COMMENT_SPEC,
               ChangeField.COMMITTER_PARTS_SPEC,
+              ChangeField.COMMIT_MESSAGE,
+              ChangeField.COMMIT_SPEC,
+              ChangeField.DELETED_LINES_SPEC,
+              ChangeField.DELTA_LINES_SPEC,
               ChangeField.DIRECTORY_SPEC,
               ChangeField.EXACT_AUTHOR_SPEC,
               ChangeField.EXACT_COMMITTER_SPEC,
+              ChangeField.EXACT_COMMIT_SPEC,
               ChangeField.EXACT_TOPIC,
               ChangeField.EXTENSION_SPEC,
               ChangeField.FILE_PART_SPEC,
@@ -127,21 +136,30 @@
               ChangeField.HASHTAG_SPEC,
               ChangeField.IS_PURE_REVERT_SPEC,
               ChangeField.IS_SUBMITTABLE_SPEC,
+              ChangeField.LABEL_SPEC,
+              ChangeField.MERGEABLE_SPEC,
               ChangeField.MERGED_ON_SPEC,
+              ChangeField.MERGE_SPEC,
               ChangeField.ONLY_EXTENSIONS_SPEC,
               ChangeField.OWNER_SPEC,
               ChangeField.PATH_SPEC,
               ChangeField.PENDING_REVIEWER_BY_EMAIL,
               ChangeField.PENDING_REVIEWER_SPEC,
+              ChangeField.PRIVATE_SPEC,
               ChangeField.PROJECTS_SPEC,
               ChangeField.PROJECT_SPEC,
               ChangeField.REF_SPEC,
               ChangeField.REVERT_OF,
               ChangeField.REVIEWER_BY_EMAIL,
               ChangeField.REVIEWER_SPEC,
+              ChangeField.STARTED_SPEC,
               ChangeField.STATUS_SPEC,
               ChangeField.SUBMISSIONID_SPEC,
-              ChangeField.UPLOADER_SPEC));
+              ChangeField.TOTAL_COMMENT_COUNT_SPEC,
+              ChangeField.TR_SPEC,
+              ChangeField.UNRESOLVED_COMMENT_COUNT_SPEC,
+              ChangeField.UPLOADER_SPEC,
+              ChangeField.WIP_SPEC));
 
   /**
    * Added new field {@link ChangeField#PREFIX_HASHTAG} and {@link ChangeField#PREFIX_TOPIC} to
@@ -167,7 +185,11 @@
   /** Added new field {@link ChangeField#COMMIT_MESSAGE_EXACT}. */
   @Deprecated
   static final Schema<ChangeData> V77 =
-      new Schema.Builder<ChangeData>().add(V76).add(ChangeField.COMMIT_MESSAGE_EXACT).build();
+      new Schema.Builder<ChangeData>()
+          .add(V76)
+          .addIndexedFields(ChangeField.COMMIT_MESSAGE_EXACT_FIELD)
+          .addSearchSpecs(ChangeField.COMMIT_MESSAGE_EXACT)
+          .build();
 
   // Upgrade Lucene to 7.x requires reindexing.
   @Deprecated static final Schema<ChangeData> V78 = schema(V77);
diff --git a/java/com/google/gerrit/server/project/CreateProjectArgs.java b/java/com/google/gerrit/server/project/CreateProjectArgs.java
index 196873f..8da0510 100644
--- a/java/com/google/gerrit/server/project/CreateProjectArgs.java
+++ b/java/com/google/gerrit/server/project/CreateProjectArgs.java
@@ -49,7 +49,7 @@
     newChangeForAllNotInTarget = InheritableBoolean.INHERIT;
     enableSignedPush = InheritableBoolean.INHERIT;
     requireSignedPush = InheritableBoolean.INHERIT;
-    submitType = SubmitType.MERGE_IF_NECESSARY;
+    submitType = SubmitType.INHERIT;
     rejectEmptyCommit = InheritableBoolean.INHERIT;
   }
 
diff --git a/java/com/google/gerrit/server/query/change/AddedPredicate.java b/java/com/google/gerrit/server/query/change/AddedPredicate.java
index 1f526c5..698884c 100644
--- a/java/com/google/gerrit/server/query/change/AddedPredicate.java
+++ b/java/com/google/gerrit/server/query/change/AddedPredicate.java
@@ -19,11 +19,11 @@
 
 public class AddedPredicate extends IntegerRangeChangePredicate {
   public AddedPredicate(String value) throws QueryParseException {
-    super(ChangeField.ADDED, value);
+    super(ChangeField.ADDED_LINES_SPEC, value);
   }
 
   @Override
   protected Integer getValueInt(ChangeData changeData) {
-    return ChangeField.ADDED.get(changeData);
+    return ChangeField.ADDED_LINES_SPEC.get(changeData);
   }
 }
diff --git a/java/com/google/gerrit/server/query/change/ChangePredicates.java b/java/com/google/gerrit/server/query/change/ChangePredicates.java
index 70f241c..6b8cdd8 100644
--- a/java/com/google/gerrit/server/query/change/ChangePredicates.java
+++ b/java/com/google/gerrit/server/query/change/ChangePredicates.java
@@ -66,7 +66,7 @@
    * com.google.gerrit.entities.Account.Id}.
    */
   public static Predicate<ChangeData> commentBy(Account.Id id) {
-    return new ChangeIndexPredicate(ChangeField.COMMENTBY, id.toString());
+    return new ChangeIndexPredicate(ChangeField.COMMENTBY_SPEC, id.toString());
   }
 
   /**
@@ -268,7 +268,7 @@
 
   /** Returns a predicate that matches changes with the provided {@code trackingId}. */
   public static Predicate<ChangeData> trackingId(String trackingId) {
-    return new ChangeIndexCardinalPredicate(ChangeField.TR, trackingId, 5);
+    return new ChangeIndexCardinalPredicate(ChangeField.TR_SPEC, trackingId, 5);
   }
 
   /** Returns a predicate that matches changes authored by the provided {@code exactAuthor}. */
@@ -319,9 +319,9 @@
    */
   public static Predicate<ChangeData> commitPrefix(String commitId) {
     if (commitId.length() == ObjectIds.STR_LEN) {
-      return new ChangeIndexCardinalPredicate(ChangeField.EXACT_COMMIT, commitId, 5);
+      return new ChangeIndexCardinalPredicate(ChangeField.EXACT_COMMIT_SPEC, commitId, 5);
     }
-    return new ChangeIndexCardinalPredicate(ChangeField.COMMIT, commitId, 5);
+    return new ChangeIndexCardinalPredicate(ChangeField.COMMIT_SPEC, commitId, 5);
   }
 
   /**
@@ -337,7 +337,7 @@
    * comment on any patch set of the change. Uses full-text search semantics.
    */
   public static Predicate<ChangeData> comment(String comment) {
-    return new ChangeIndexPredicate(ChangeField.COMMENT, comment);
+    return new ChangeIndexPredicate(ChangeField.COMMENT_SPEC, comment);
   }
 
   /**
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index fe4cb5d..b433b25 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -678,7 +678,8 @@
 
     if ("reviewer".equalsIgnoreCase(value)) {
       return Predicate.and(
-          Predicate.not(new BooleanPredicate(ChangeField.WIP)), ReviewerPredicate.reviewer(self()));
+          Predicate.not(new BooleanPredicate(ChangeField.WIP_SPEC)),
+          ReviewerPredicate.reviewer(self()));
     }
 
     if ("cc".equalsIgnoreCase(value)) {
@@ -689,16 +690,16 @@
       if (!args.indexMergeable) {
         throw new QueryParseException("'is:mergeable' operator is not supported by server");
       }
-      return new BooleanPredicate(ChangeField.MERGEABLE);
+      return new BooleanPredicate(ChangeField.MERGEABLE_SPEC);
     }
 
     if ("merge".equalsIgnoreCase(value)) {
-      checkFieldAvailable(ChangeField.MERGE, "is:merge");
-      return new BooleanPredicate(ChangeField.MERGE);
+      checkFieldAvailable(ChangeField.MERGE_SPEC, "is:merge");
+      return new BooleanPredicate(ChangeField.MERGE_SPEC);
     }
 
     if ("private".equalsIgnoreCase(value)) {
-      return new BooleanPredicate(ChangeField.PRIVATE);
+      return new BooleanPredicate(ChangeField.PRIVATE_SPEC);
     }
 
     if ("attention".equalsIgnoreCase(value)) {
@@ -736,17 +737,17 @@
     }
 
     if ("started".equalsIgnoreCase(value)) {
-      checkFieldAvailable(ChangeField.STARTED, "is:started");
-      return new BooleanPredicate(ChangeField.STARTED);
+      checkFieldAvailable(ChangeField.STARTED_SPEC, "is:started");
+      return new BooleanPredicate(ChangeField.STARTED_SPEC);
     }
 
     if ("wip".equalsIgnoreCase(value)) {
-      return new BooleanPredicate(ChangeField.WIP);
+      return new BooleanPredicate(ChangeField.WIP_SPEC);
     }
 
     if ("cherrypick".equalsIgnoreCase(value)) {
-      checkFieldAvailable(ChangeField.CHERRY_PICK, "is:cherrypick");
-      return new BooleanPredicate(ChangeField.CHERRY_PICK);
+      checkFieldAvailable(ChangeField.CHERRY_PICK_SPEC, "is:cherrypick");
+      return new BooleanPredicate(ChangeField.CHERRY_PICK_SPEC);
     }
 
     // for plugins the value will be operandName_pluginName
@@ -1328,7 +1329,7 @@
     if (Objects.equals(byState, Predicate.<ChangeData>any())) {
       return Predicate.any();
     }
-    return Predicate.and(Predicate.not(new BooleanPredicate(ChangeField.WIP)), byState);
+    return Predicate.and(Predicate.not(new BooleanPredicate(ChangeField.WIP_SPEC)), byState);
   }
 
   @Operator
diff --git a/java/com/google/gerrit/server/query/change/DeletedPredicate.java b/java/com/google/gerrit/server/query/change/DeletedPredicate.java
index d4bdc67..40e4c6e 100644
--- a/java/com/google/gerrit/server/query/change/DeletedPredicate.java
+++ b/java/com/google/gerrit/server/query/change/DeletedPredicate.java
@@ -19,11 +19,11 @@
 
 public class DeletedPredicate extends IntegerRangeChangePredicate {
   public DeletedPredicate(String value) throws QueryParseException {
-    super(ChangeField.DELETED, value);
+    super(ChangeField.DELETED_LINES_SPEC, value);
   }
 
   @Override
   protected Integer getValueInt(ChangeData changeData) {
-    return ChangeField.DELETED.get(changeData);
+    return ChangeField.DELETED_LINES_SPEC.get(changeData);
   }
 }
diff --git a/java/com/google/gerrit/server/query/change/DeltaPredicate.java b/java/com/google/gerrit/server/query/change/DeltaPredicate.java
index 821ec94..e9eaa32 100644
--- a/java/com/google/gerrit/server/query/change/DeltaPredicate.java
+++ b/java/com/google/gerrit/server/query/change/DeltaPredicate.java
@@ -19,11 +19,11 @@
 
 public class DeltaPredicate extends IntegerRangeChangePredicate {
   public DeltaPredicate(String value) throws QueryParseException {
-    super(ChangeField.DELTA, value);
+    super(ChangeField.DELTA_LINES_SPEC, value);
   }
 
   @Override
   protected Integer getValueInt(ChangeData changeData) {
-    return ChangeField.DELTA.get(changeData);
+    return ChangeField.DELTA_LINES_SPEC.get(changeData);
   }
 }
diff --git a/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java b/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
index 5e8674e..5662e4d 100644
--- a/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
+++ b/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
@@ -59,7 +59,7 @@
       int expVal,
       Account.Id account,
       @Nullable Integer count) {
-    super(ChangeField.LABEL, ChangeField.formatLabel(label, expVal, account, count));
+    super(ChangeField.LABEL_SPEC, ChangeField.formatLabel(label, expVal, account, count));
     this.permissionBackend = args.permissionBackend;
     this.projectCache = args.projectCache;
     this.userFactory = args.userFactory;
diff --git a/java/com/google/gerrit/server/query/change/IsUnresolvedPredicate.java b/java/com/google/gerrit/server/query/change/IsUnresolvedPredicate.java
index 27309af..ffa29ba 100644
--- a/java/com/google/gerrit/server/query/change/IsUnresolvedPredicate.java
+++ b/java/com/google/gerrit/server/query/change/IsUnresolvedPredicate.java
@@ -23,11 +23,11 @@
   }
 
   public IsUnresolvedPredicate(String value) throws QueryParseException {
-    super(ChangeField.UNRESOLVED_COMMENT_COUNT, value);
+    super(ChangeField.UNRESOLVED_COMMENT_COUNT_SPEC, value);
   }
 
   @Override
   protected Integer getValueInt(ChangeData changeData) {
-    return ChangeField.UNRESOLVED_COMMENT_COUNT.get(changeData);
+    return ChangeField.UNRESOLVED_COMMENT_COUNT_SPEC.get(changeData);
   }
 }
diff --git a/java/com/google/gerrit/server/query/change/MagicLabelPredicate.java b/java/com/google/gerrit/server/query/change/MagicLabelPredicate.java
index e890066..32a8fdf 100644
--- a/java/com/google/gerrit/server/query/change/MagicLabelPredicate.java
+++ b/java/com/google/gerrit/server/query/change/MagicLabelPredicate.java
@@ -39,7 +39,7 @@
       Account.Id account,
       @Nullable Integer count) {
     super(
-        ChangeField.LABEL,
+        ChangeField.LABEL_SPEC,
         ChangeField.formatLabel(
             magicLabelVote.label(), magicLabelVote.value().name(), account, count));
     this.account = account;
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
index 7543ba8..fed22f2 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
@@ -160,7 +160,8 @@
     Project.NameKey key = projectOperations.newProject().create();
     Config config = projectOperations.project(key).getConfig();
     assertThat(config).isNotInstanceOf(StoredConfig.class);
-    assertThat(config).text().isEmpty();
+    assertThat(config).sections().containsExactly("submit");
+    assertThat(config).sectionValues("submit").containsExactly("action", "inherit");
 
     ConfigInput input = new ConfigInput();
     input.description = "my fancy project";
@@ -168,7 +169,7 @@
 
     config = projectOperations.project(key).getConfig();
     assertThat(config).isNotInstanceOf(StoredConfig.class);
-    assertThat(config).sections().containsExactly("project");
+    assertThat(config).sections().containsExactly("project", "submit");
     assertThat(config).subsections("project").isEmpty();
     assertThat(config).sectionValues("project").containsExactly("description", "my fancy project");
   }
@@ -193,7 +194,7 @@
         .update();
 
     Config config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().containsExactly("access", "submit");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
@@ -210,7 +211,7 @@
         .update();
 
     Config config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().containsExactly("access", "submit");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
@@ -227,7 +228,7 @@
         .update();
 
     Config config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().containsExactly("access", "submit");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
@@ -244,7 +245,7 @@
         .update();
 
     Config config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().containsExactly("access", "submit");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
@@ -262,7 +263,7 @@
         .update();
 
     Config config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().containsExactly("access", "submit");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
@@ -277,7 +278,7 @@
         .update();
 
     config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().containsExactly("access", "submit");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
@@ -318,7 +319,7 @@
         .update();
 
     Config config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().containsExactly("access", "submit");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
@@ -335,7 +336,7 @@
     projectOperations.project(key).forUpdate().add(permission).add(permission).update();
 
     Config config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().contains("access");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
@@ -345,7 +346,7 @@
 
     projectOperations.project(key).forUpdate().add(permission).update();
     config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().containsExactly("access", "submit");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
@@ -365,7 +366,7 @@
         .update();
 
     Config config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().containsExactly("access", "submit");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
@@ -382,7 +383,7 @@
         .update();
 
     Config config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().containsExactly("access", "submit");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
@@ -400,7 +401,7 @@
         .update();
 
     Config config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().containsExactly("access", "submit");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
@@ -415,7 +416,7 @@
         .update();
 
     config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().containsExactly("access", "submit");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
@@ -437,7 +438,7 @@
         .update();
 
     Config config = projectOperations.project(key).getConfig();
-    assertThat(config).sections().containsExactly("access");
+    assertThat(config).sections().containsExactly("access", "submit");
     assertThat(config).subsections("access").containsExactly("refs/foo");
     assertThat(config)
         .subsectionValues("access", "refs/foo")
diff --git a/javatests/com/google/gerrit/server/index/IndexedFieldTest.java b/javatests/com/google/gerrit/server/index/IndexedFieldTest.java
index 6a62ed1..dacc37d 100644
--- a/javatests/com/google/gerrit/server/index/IndexedFieldTest.java
+++ b/javatests/com/google/gerrit/server/index/IndexedFieldTest.java
@@ -16,6 +16,7 @@
 
 import static com.google.common.collect.ImmutableList.toImmutableList;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.index.testing.TestIndexedFields.EXACT_STRING_FIELD_SPEC;
 import static com.google.gerrit.index.testing.TestIndexedFields.INTEGER_FIELD;
 import static com.google.gerrit.index.testing.TestIndexedFields.INTEGER_FIELD_SPEC;
 import static com.google.gerrit.index.testing.TestIndexedFields.INTEGER_RANGE_FIELD_SPEC;
@@ -31,6 +32,7 @@
 import static com.google.gerrit.index.testing.TestIndexedFields.ITERABLE_STRING_FIELD_SPEC;
 import static com.google.gerrit.index.testing.TestIndexedFields.LONG_FIELD_SPEC;
 import static com.google.gerrit.index.testing.TestIndexedFields.LONG_RANGE_FIELD_SPEC;
+import static com.google.gerrit.index.testing.TestIndexedFields.PREFIX_STRING_FIELD_SPEC;
 import static com.google.gerrit.index.testing.TestIndexedFields.STORED_BYTE_FIELD;
 import static com.google.gerrit.index.testing.TestIndexedFields.STORED_BYTE_SPEC;
 import static com.google.gerrit.index.testing.TestIndexedFields.STORED_PROTO_FIELD;
@@ -78,6 +80,8 @@
               .put(ITERABLE_LONG_RANGE_FIELD_SPEC, ImmutableList.of(123456L, 654321L))
               .put(TIMESTAMP_FIELD_SPEC, new Timestamp(1234567L))
               .put(STRING_FIELD_SPEC, "123456")
+              .put(PREFIX_STRING_FIELD_SPEC, "123456")
+              .put(EXACT_STRING_FIELD_SPEC, "123456")
               .put(ITERABLE_STRING_FIELD_SPEC, ImmutableList.of("123456"))
               .put(
                   ITERABLE_STORED_BYTE_SPEC,
diff --git a/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java b/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java
index 0bdf5cd..35077db 100644
--- a/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java
+++ b/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java
@@ -158,14 +158,15 @@
   public void tolerateNullValuesForInsertion() {
     Project.NameKey project = Project.nameKey("project");
     ChangeData cd = ChangeData.createForTest(project, Change.id(1), 1, ObjectId.zeroId());
-    assertThat(ChangeField.ADDED.setIfPossible(cd, new FakeStoredValue(null))).isTrue();
+    assertThat(ChangeField.ADDED_LINES_SPEC.setIfPossible(cd, new FakeStoredValue(null))).isTrue();
   }
 
   @Test
   public void tolerateNullValuesForDeletion() {
     Project.NameKey project = Project.nameKey("project");
     ChangeData cd = ChangeData.createForTest(project, Change.id(1), 1, ObjectId.zeroId());
-    assertThat(ChangeField.DELETED.setIfPossible(cd, new FakeStoredValue(null))).isTrue();
+    assertThat(ChangeField.DELETED_LINES_SPEC.setIfPossible(cd, new FakeStoredValue(null)))
+        .isTrue();
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 28149d8..d7ec030 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -2755,7 +2755,7 @@
 
   @Test
   public void cherrypick() throws Exception {
-    assume().that(getSchema().hasField(ChangeField.CHERRY_PICK)).isTrue();
+    assume().that(getSchema().hasField(ChangeField.CHERRY_PICK_SPEC)).isTrue();
     TestRepository<Repo> repo = createProject("repo");
     Change change1 = insert(repo, newChange(repo));
     Change change2 = insert(repo, newCherryPickChange(repo, "foo", change1.currentPatchSetId()));
@@ -2766,7 +2766,7 @@
 
   @Test
   public void merge() throws Exception {
-    assume().that(getSchema().hasField(ChangeField.MERGE)).isTrue();
+    assume().that(getSchema().hasField(ChangeField.MERGE_SPEC)).isTrue();
     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());
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
index 7ce0c65..fe2bb4d 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
@@ -876,11 +876,6 @@
       sharedStyles,
       modalStyles,
       css`
-        .header,
-        #fileListHeader {
-          position: sticky;
-          top: 0; /* Necessary to ensure it sticks to the top. */
-        }
         .container:not(.loading) {
           background-color: var(--background-color-tertiary);
         }
@@ -1242,20 +1237,14 @@
     `;
   }
 
-  private renderHeader() {
-    return html`
+  private renderChangeInfoSection() {
+    return html`<section class="changeInfoSection">
       <div class=${this.computeHeaderClass()}>
         <h1 class="assistive-tech-only">
           Change ${this.change?._number}: ${this.change?.subject}
         </h1>
         ${this.renderHeaderTitle()} ${this.renderCommitActions()}
       </div>
-    `;
-  }
-
-  private renderChangeInfoSection() {
-    return html`<section class="changeInfoSection">
-      ${this.renderHeader()}
       <h2 class="assistive-tech-only">Change metadata</h2>
       ${this.renderChangeInfo()}
     </section>`;
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.ts b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.ts
index b114831..89a1ff5 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.ts
@@ -149,9 +149,6 @@
   static override styles = [
     sharedStyles,
     css`
-      :host {
-        display: block;
-      }
       .prefsButton {
         float: right;
       }
@@ -162,9 +159,6 @@
         align-items: center;
         display: flex;
         padding: var(--spacing-s) var(--spacing-l);
-        /* Necessary to avoid that the header is transparent and shows the files */
-        background-color: var(--background-color-primary);
-        border-bottom: 1px solid var(--border-color);
       }
       .patchInfo-left {
         align-items: baseline;
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
index 2dcfd8c..00d5a8c 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
@@ -337,9 +337,6 @@
           min-height: calc(var(--line-height-normal) + 2 * var(--spacing-s));
           padding: var(--spacing-xs) var(--spacing-l);
         }
-        .header-row.row {
-          border-top: none;
-        }
         /* The class defines a content visible only to screen readers */
         .noCommentsScreenReaderText {
           opacity: 0;
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
index 11406b4..933d06f 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
@@ -75,6 +75,7 @@
 } from '../../../utils/common-util';
 import {
   CommentThread,
+  createPatchsetLevelUnsavedDraft,
   DraftInfo,
   getFirstComment,
   isDraft,
@@ -940,20 +941,13 @@
     `;
   }
 
-  // TODO: move to comment-util
-  private createDraft(): UnsavedInfo {
-    return {
-      patch_set: this.latestPatchNum,
-      message: this.patchsetLevelDraftMessage,
-      unresolved: !this.patchsetLevelDraftIsResolved,
-      path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS,
-      __unsaved: true,
-    };
-  }
-
   private renderPatchsetLevelComment() {
     if (!this.patchsetLevelComment)
-      this.patchsetLevelComment = this.createDraft();
+      this.patchsetLevelComment = createPatchsetLevelUnsavedDraft(
+        this.latestPatchNum,
+        this.patchsetLevelDraftMessage,
+        !this.patchsetLevelDraftIsResolved
+      );
     return html`
       <gr-comment
         id="patchsetLevelComment"
@@ -1413,6 +1407,14 @@
 
     if (startReview) {
       reviewInput.ready = true;
+    } else {
+      const addedAccounts = [
+        ...(this.reviewersList?.additions() ?? []),
+        ...(this.ccsList?.additions() ?? []),
+      ];
+      if (addedAccounts.length > 0) {
+        fireAlert(this, 'Reviewers are not notified for WIP changes');
+      }
     }
 
     this.disabled = true;
diff --git a/polygerrit-ui/app/utils/comment-util.ts b/polygerrit-ui/app/utils/comment-util.ts
index f531698..8af5beb 100644
--- a/polygerrit-ui/app/utils/comment-util.ts
+++ b/polygerrit-ui/app/utils/comment-util.ts
@@ -22,6 +22,7 @@
   VotingRangeInfo,
   FixSuggestionInfo,
   FixId,
+  PatchSetNumber,
 } from '../types/common';
 import {CommentSide, SpecialFilePath} from '../constants/constants';
 import {parseDate} from './date-util';
@@ -136,6 +137,20 @@
   };
 }
 
+export function createPatchsetLevelUnsavedDraft(
+  patchNum?: PatchSetNumber,
+  message?: string,
+  unresolved?: boolean
+): UnsavedInfo {
+  return {
+    patch_set: patchNum,
+    message,
+    unresolved,
+    path: SpecialFilePath.PATCHSET_LEVEL_COMMENTS,
+    __unsaved: true,
+  };
+}
+
 export function createUnsavedReply(
   replyingTo: CommentInfo,
   message: string,