Merge changes I2f26ff5d,I46993b48 into stable-3.3

* changes:
  Fix flaky test for group renaming in ProjectConfig
  ProjectConfig: Write resolved groups to file
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index 4473a8d..a62ed47 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -1701,7 +1701,7 @@
 |======================
 |Field Name|Description
 |`status`  |The status of the consistency problem. +
-Possible values are `ERROR` and `WARNING`.
+Possible values are `FATAL`, `ERROR` and `WARNING`.
 |`message` |Message describing the consistency problem.
 |======================
 
diff --git a/java/com/google/gerrit/extensions/api/config/ConsistencyCheckInfo.java b/java/com/google/gerrit/extensions/api/config/ConsistencyCheckInfo.java
index 2c166d0..e582f1b 100644
--- a/java/com/google/gerrit/extensions/api/config/ConsistencyCheckInfo.java
+++ b/java/com/google/gerrit/extensions/api/config/ConsistencyCheckInfo.java
@@ -48,6 +48,7 @@
 
   public static class ConsistencyProblemInfo {
     public enum Status {
+      FATAL,
       ERROR,
       WARNING,
     }
diff --git a/java/com/google/gerrit/server/change/ChangeInserter.java b/java/com/google/gerrit/server/change/ChangeInserter.java
index a086cb1..6091091 100644
--- a/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -546,6 +546,7 @@
               cmd,
               projectState.getProject(),
               change.getDest().branch(),
+              ctx.getRepoView().getConfig(),
               ctx.getRevWalk().getObjectReader(),
               commitId,
               ctx.getIdentifiedUser())) {
diff --git a/java/com/google/gerrit/server/change/PatchSetInserter.java b/java/com/google/gerrit/server/change/PatchSetInserter.java
index 882352d..ef06ea1 100644
--- a/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -342,6 +342,7 @@
                 .orElseThrow(illegalState(origNotes.getProjectName()))
                 .getProject(),
             origNotes.getChange().getDest().branch(),
+            ctx.getRepoView().getConfig(),
             ctx.getRevWalk().getObjectReader(),
             commitId,
             ctx.getIdentifiedUser())) {
diff --git a/java/com/google/gerrit/server/events/CommitReceivedEvent.java b/java/com/google/gerrit/server/events/CommitReceivedEvent.java
index 6e43621..eb4d9ee 100644
--- a/java/com/google/gerrit/server/events/CommitReceivedEvent.java
+++ b/java/com/google/gerrit/server/events/CommitReceivedEvent.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.IdentifiedUser;
 import java.io.IOException;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -28,6 +29,7 @@
   public ReceiveCommand command;
   public Project project;
   public String refName;
+  public Config repoConfig;
   public RevWalk revWalk;
   public RevCommit commit;
   public IdentifiedUser user;
@@ -40,6 +42,7 @@
       ReceiveCommand command,
       Project project,
       String refName,
+      Config repoConfig,
       ObjectReader reader,
       ObjectId commitId,
       IdentifiedUser user)
@@ -48,6 +51,7 @@
     this.command = command;
     this.project = project;
     this.refName = refName;
+    this.repoConfig = repoConfig;
     this.revWalk = new RevWalk(reader);
     this.commit = revWalk.parseCommit(commitId);
     this.user = user;
diff --git a/java/com/google/gerrit/server/git/receive/BranchCommitValidator.java b/java/com/google/gerrit/server/git/receive/BranchCommitValidator.java
index 7b5f90bd..55261223 100644
--- a/java/com/google/gerrit/server/git/receive/BranchCommitValidator.java
+++ b/java/com/google/gerrit/server/git/receive/BranchCommitValidator.java
@@ -37,7 +37,9 @@
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.notes.NoteMap;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.transport.ReceiveCommand;
@@ -94,6 +96,7 @@
   /**
    * Validates a single commit. If the commit does not validate, the command is rejected.
    *
+   * @param repository the repository
    * @param objectReader the object reader to use.
    * @param cmd the ReceiveCommand executing the push.
    * @param commit the commit being validated.
@@ -102,6 +105,7 @@
    * @return The validation {@link Result}.
    */
   Result validateCommit(
+      Repository repository,
       ObjectReader objectReader,
       ReceiveCommand cmd,
       RevCommit commit,
@@ -109,12 +113,14 @@
       NoteMap rejectCommits,
       @Nullable Change change)
       throws IOException {
-    return validateCommit(objectReader, cmd, commit, isMerged, rejectCommits, change, false);
+    return validateCommit(
+        repository, objectReader, cmd, commit, isMerged, rejectCommits, change, false);
   }
 
   /**
    * Validates a single commit. If the commit does not validate, the command is rejected.
    *
+   * @param repository the repository
    * @param objectReader the object reader to use.
    * @param cmd the ReceiveCommand executing the push.
    * @param commit the commit being validated.
@@ -124,6 +130,7 @@
    * @return The validation {@link Result}.
    */
   Result validateCommit(
+      Repository repository,
       ObjectReader objectReader,
       ReceiveCommand cmd,
       RevCommit commit,
@@ -135,7 +142,14 @@
     try (TraceTimer traceTimer = TraceContext.newTimer("BranchCommitValidator#validateCommit")) {
       ImmutableList.Builder<CommitValidationMessage> messages = new ImmutableList.Builder<>();
       try (CommitReceivedEvent receiveEvent =
-          new CommitReceivedEvent(cmd, project, branch.branch(), objectReader, commit, user)) {
+          new CommitReceivedEvent(
+              cmd,
+              project,
+              branch.branch(),
+              new Config(repository.getConfig()),
+              objectReader,
+              commit,
+              user)) {
         CommitValidators validators;
         if (isMerged) {
           validators =
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 4347f8f..6e1f8d3 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -2212,6 +2212,7 @@
 
           BranchCommitValidator.Result validationResult =
               validator.validateCommit(
+                  repo,
                   receivePack.getRevWalk().getObjectReader(),
                   magicBranch.cmd,
                   c,
@@ -3231,7 +3232,7 @@
 
           BranchCommitValidator.Result validationResult =
               validator.validateCommit(
-                  walk.getObjectReader(), cmd, c, false, rejectCommits, null, skipValidation);
+                  repo, walk.getObjectReader(), cmd, c, false, rejectCommits, null, skipValidation);
           messages.addAll(validationResult.messages());
           if (!validationResult.isValid()) {
             break;
diff --git a/java/com/google/gerrit/server/git/validators/MergeValidationListener.java b/java/com/google/gerrit/server/git/validators/MergeValidationListener.java
index b47d7d6..79d53ac 100644
--- a/java/com/google/gerrit/server/git/validators/MergeValidationListener.java
+++ b/java/com/google/gerrit/server/git/validators/MergeValidationListener.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.annotations.ExtensionPoint;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.git.CodeReviewCommit;
+import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
 import com.google.gerrit.server.project.ProjectState;
 import org.eclipse.jgit.lib.Repository;
 
@@ -33,6 +34,7 @@
    * Validate a commit before it is merged.
    *
    * @param repo the repository
+   * @param revWalk the rev walk
    * @param commit commit details
    * @param destProject the destination project
    * @param destBranch the destination branch
@@ -42,6 +44,7 @@
    */
   void onPreMerge(
       Repository repo,
+      CodeReviewRevWalk revWalk,
       CodeReviewCommit commit,
       ProjectState destProject,
       BranchNameKey destBranch,
diff --git a/java/com/google/gerrit/server/git/validators/MergeValidators.java b/java/com/google/gerrit/server/git/validators/MergeValidators.java
index 4e5ce0c..cbaa121 100644
--- a/java/com/google/gerrit/server/git/validators/MergeValidators.java
+++ b/java/com/google/gerrit/server/git/validators/MergeValidators.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.server.config.PluginConfig;
 import com.google.gerrit.server.config.ProjectConfigEntry;
 import com.google.gerrit.server.git.CodeReviewCommit;
+import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -51,7 +52,6 @@
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
 
 /**
  * Collection of validators that run inside Gerrit before a change is submitted. The main purpose is
@@ -92,6 +92,7 @@
    */
   public void validatePreMerge(
       Repository repo,
+      CodeReviewRevWalk revWalk,
       CodeReviewCommit commit,
       ProjectState destProject,
       BranchNameKey destBranch,
@@ -106,7 +107,7 @@
             groupValidatorFactory.create());
 
     for (MergeValidationListener validator : validators) {
-      validator.onPreMerge(repo, commit, destProject, destBranch, patchSetId, caller);
+      validator.onPreMerge(repo, revWalk, commit, destProject, destBranch, patchSetId, caller);
     }
   }
 
@@ -168,11 +169,12 @@
 
     @Override
     public void onPreMerge(
-        final Repository repo,
-        final CodeReviewCommit commit,
-        final ProjectState destProject,
-        final BranchNameKey destBranch,
-        final PatchSet.Id patchSetId,
+        Repository repo,
+        CodeReviewRevWalk revWalk,
+        CodeReviewCommit commit,
+        ProjectState destProject,
+        BranchNameKey destBranch,
+        PatchSet.Id patchSetId,
         IdentifiedUser caller)
         throws MergeValidationException {
       if (RefNames.REFS_CONFIG.equals(destBranch.branch())) {
@@ -260,6 +262,7 @@
     @Override
     public void onPreMerge(
         Repository repo,
+        CodeReviewRevWalk revWalk,
         CodeReviewCommit commit,
         ProjectState destProject,
         BranchNameKey destBranch,
@@ -267,7 +270,7 @@
         IdentifiedUser caller)
         throws MergeValidationException {
       mergeValidationListeners.runEach(
-          l -> l.onPreMerge(repo, commit, destProject, destBranch, patchSetId, caller),
+          l -> l.onPreMerge(repo, revWalk, commit, destProject, destBranch, patchSetId, caller),
           MergeValidationException.class);
     }
   }
@@ -294,6 +297,7 @@
     @Override
     public void onPreMerge(
         Repository repo,
+        CodeReviewRevWalk revWalk,
         CodeReviewCommit commit,
         ProjectState destProject,
         BranchNameKey destBranch,
@@ -316,8 +320,9 @@
         throw new MergeValidationException("account validation unavailable");
       }
 
-      try (RevWalk rw = new RevWalk(repo)) {
-        List<String> errorMessages = accountValidator.validate(accountId, repo, rw, null, commit);
+      try {
+        List<String> errorMessages =
+            accountValidator.validate(accountId, repo, revWalk, null, commit);
         if (!errorMessages.isEmpty()) {
           throw new MergeValidationException(
               "invalid account configuration: " + Joiner.on("; ").join(errorMessages));
@@ -345,6 +350,7 @@
     @Override
     public void onPreMerge(
         Repository repo,
+        CodeReviewRevWalk revWalk,
         CodeReviewCommit commit,
         ProjectState destProject,
         BranchNameKey destBranch,
diff --git a/java/com/google/gerrit/server/git/validators/ValidationMessage.java b/java/com/google/gerrit/server/git/validators/ValidationMessage.java
index 372fc17..b5d7eb1 100644
--- a/java/com/google/gerrit/server/git/validators/ValidationMessage.java
+++ b/java/com/google/gerrit/server/git/validators/ValidationMessage.java
@@ -22,6 +22,7 @@
  */
 public class ValidationMessage {
   public enum Type {
+    FATAL("FATAL: "),
     ERROR("ERROR: "),
     WARNING("WARNING: "),
     HINT("hint: "),
@@ -70,7 +71,7 @@
    * Returns {@true} if this message is an error. Used to decide if the operation should be aborted.
    */
   public boolean isError() {
-    return type == Type.ERROR;
+    return type == Type.FATAL || type == Type.ERROR;
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java
index 58b0c8e..8a293a4 100644
--- a/java/com/google/gerrit/server/submit/MergeOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -864,7 +864,8 @@
 
       MergeValidators mergeValidators = mergeValidatorsFactory.create();
       try {
-        mergeValidators.validatePreMerge(or.repo, commit, or.project, destBranch, ps.id(), caller);
+        mergeValidators.validatePreMerge(
+            or.repo, or.rw, commit, or.project, destBranch, ps.id(), caller);
       } catch (MergeValidationException mve) {
         commitStatus.problem(changeId, mve.getMessage());
         continue;
diff --git a/plugins/hooks b/plugins/hooks
index 7ed555f..ad4f877 160000
--- a/plugins/hooks
+++ b/plugins/hooks
@@ -1 +1 @@
-Subproject commit 7ed555fe88f4be028acbfd5c245ac78537ac3666
+Subproject commit ad4f877749928b69ef94b62176c5797f6648887d