Merge "Merge branch 'stable-3.1'"
diff --git a/java/com/google/gerrit/server/git/validators/CommitValidators.java b/java/com/google/gerrit/server/git/validators/CommitValidators.java
index 6c4aacc..1e97a44 100644
--- a/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -403,6 +403,17 @@
     @Override
     public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
         throws CommitValidationException {
+      // TODO(zieren): Refactor interface to signal the intent of the event instead of hard-coding
+      // it here. Due to interface limitations, this method is called from both receive commits
+      // and from main Gerrit (e.g. when publishing a change edit). This is why we need to gate the
+      // early return on REFS_CHANGES (though pushes to refs/changes are not possible).
+      String refName = receiveEvent.command.getRefName();
+      if (!refName.startsWith("refs/for/") && !refName.startsWith(RefNames.REFS_CHANGES)) {
+        // This is a direct push bypassing review. We don't need to enforce any file-count limits
+        // here.
+        return Collections.emptyList();
+      }
+
       PatchListKey patchListKey =
           PatchListKey.againstBase(
               receiveEvent.commit.getId(), receiveEvent.commit.getParentCount());
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 9399c3b..50aaa27 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -3210,7 +3210,8 @@
     mergeInput.source = "dev";
     MergePatchSetInput in = new MergePatchSetInput();
     in.merge = mergeInput;
-    in.subject = "update change by merge ps2";
+    String subject = "update change by merge ps2";
+    in.subject = subject;
 
     TestWorkInProgressStateChangedListener wipStateChangedListener =
         new TestWorkInProgressStateChangedListener();
@@ -3234,6 +3235,16 @@
     List<ChangeMessageInfo> messages = gApi.changes().id(changeId).messages();
     assertThat(messages).hasSize(2);
     assertThat(Iterables.getLast(messages).message).isEqualTo("Uploaded patch set 2.");
+
+    assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.message)
+        .contains(subject);
+
+    // No subject: reuse message from previous patchset.
+    in.subject = null;
+    gApi.changes().id(changeId).createMergePatchSet(in);
+    changeInfo = gApi.changes().id(changeId).get(ALL_REVISIONS, CURRENT_COMMIT, CURRENT_REVISION);
+    assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.message)
+        .contains(subject);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java b/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java
index 876e342..d7952e4 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java
@@ -29,6 +29,7 @@
 import com.google.gerrit.acceptance.ExtensionRegistry;
 import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
 import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.extensions.api.projects.BranchInput;
@@ -206,4 +207,22 @@
       r4.assertErrorStatus(UPDATE_NONFASTFORWARD.name());
     }
   }
+
+  @Test
+  @GerritConfig(name = "change.maxFiles", value = "0")
+  public void dontEnforceFileCountForDirectPushes() throws Exception {
+    PushOneCommit push =
+        pushFactory.create(admin.newIdent(), testRepo, "change", "c.txt", "content");
+    PushOneCommit.Result result = push.to("refs/heads/master");
+    result.assertOkStatus();
+  }
+
+  @Test
+  @GerritConfig(name = "change.maxFiles", value = "0")
+  public void enforceFileCountLimitOnPushesForReview() throws Exception {
+    PushOneCommit push =
+        pushFactory.create(admin.newIdent(), testRepo, "change", "c.txt", "content");
+    PushOneCommit.Result result = push.to("refs/for/master");
+    result.assertErrorStatus("Exceeding maximum number of files per change");
+  }
 }
diff --git a/polygerrit-ui/app/wct.conf.js b/polygerrit-ui/app/wct.conf.js
index 0e53adb..1a9300e 100644
--- a/polygerrit-ui/app/wct.conf.js
+++ b/polygerrit-ui/app/wct.conf.js
@@ -27,7 +27,7 @@
 */
 
 const headless = 'WCT_HEADLESS_MODE' in process.env ?
-  process.env['WCT_HEADLESS_MODE'] !== '0' : false;
+  process.env['WCT_HEADLESS_MODE'] === '1' : false;
 
 const headlessBrowserOptions = {
   chrome: ['start-maximized', 'headless', 'disable-gpu', 'no-sandbox'],