Merge "Fix DereferenceWithNullBranch bug pattern flagged by error prone"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 927d3fb..6b1dea5 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1456,7 +1456,8 @@
[[change.maxFiles]]change.maxFiles::
+
Maximum number of files allowed per change. Larger changes are rejected and must
-be split up.
+be split up. For merge changes we are comparing against the auto-merge commit,
+so we allow large merges, if they merge cleanly.
+
By default 100,000.
diff --git a/java/com/google/gerrit/server/git/validators/CommitValidators.java b/java/com/google/gerrit/server/git/validators/CommitValidators.java
index cd1e2ee..6adaae2 100644
--- a/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -31,6 +31,7 @@
import com.google.gerrit.entities.BooleanProjectConfig;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Patch;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
import com.google.gerrit.extensions.registration.DynamicItem;
@@ -50,7 +51,10 @@
import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
+import com.google.gerrit.server.patch.DiffNotAvailableException;
import com.google.gerrit.server.patch.DiffOperations;
+import com.google.gerrit.server.patch.DiffOptions;
+import com.google.gerrit.server.patch.filediff.FileDiffOutput;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.RefPermission;
@@ -70,10 +74,10 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
-import org.eclipse.jgit.diff.DiffEntry;
-import org.eclipse.jgit.diff.DiffFormatter;
+import java.util.stream.Collectors;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.PersonIdent;
@@ -84,7 +88,6 @@
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.SystemReader;
-import org.eclipse.jgit.util.io.DisabledOutputStream;
/**
* Represents a list of {@link CommitValidationListener}s to run for a push to one branch of one
@@ -163,7 +166,7 @@
.add(new ProjectStateValidationListener(projectState))
.add(new AmendedGerritMergeCommitValidationListener(perm, gerritIdent))
.add(new AuthorUploaderValidator(user, perm, urlFormatter.get()))
- .add(new FileCountValidator(repoManager, config, urlFormatter.get()))
+ .add(new FileCountValidator(config, urlFormatter.get(), diffOperations))
.add(new CommitterUploaderValidator(user, perm, urlFormatter.get()))
.add(new SignedOffByValidator(user, perm, projectState))
.add(
@@ -195,7 +198,7 @@
.add(new ProjectStateValidationListener(projectState))
.add(new AmendedGerritMergeCommitValidationListener(perm, gerritIdent))
.add(new AuthorUploaderValidator(user, perm, urlFormatter.get()))
- .add(new FileCountValidator(repoManager, config, urlFormatter.get()))
+ .add(new FileCountValidator(config, urlFormatter.get(), diffOperations))
.add(new SignedOffByValidator(user, perm, projectState))
.add(
new ChangeIdValidator(
@@ -392,7 +395,8 @@
String httpHook =
String.format(
- "f=\"$(git rev-parse --git-dir)/hooks/commit-msg\"; curl -o \"$f\" %stools/hooks/commit-msg ; chmod +x \"$f\"",
+ "f=\"$(git rev-parse --git-dir)/hooks/commit-msg\"; curl -o \"$f\""
+ + " %stools/hooks/commit-msg ; chmod +x \"$f\"",
webUrl.get());
if (hostKeys.isEmpty()) {
@@ -426,7 +430,8 @@
String sshHook =
String.format(
- "gitdir=$(git rev-parse --git-dir); scp -p -P %d %s@%s:hooks/commit-msg ${gitdir}/hooks/",
+ "gitdir=$(git rev-parse --git-dir); scp -p -P %d %s@%s:hooks/commit-msg"
+ + " ${gitdir}/hooks/",
sshPort, user.getUserName().orElse("<USERNAME>"), sshHost);
return String.format(" %s\n%s\nor, for http(s):\n %s", sshHook, scpFlagHint, httpHook);
}
@@ -437,13 +442,13 @@
private static final int FILE_COUNT_WARNING_THRESHOLD = 10_000;
- private final GitRepositoryManager repoManager;
private final int maxFileCount;
private final UrlFormatter urlFormatter;
+ private final DiffOperations diffOperations;
- FileCountValidator(GitRepositoryManager repoManager, Config config, UrlFormatter urlFormatter) {
- this.repoManager = repoManager;
+ FileCountValidator(Config config, UrlFormatter urlFormatter, DiffOperations diffOperations) {
this.urlFormatter = urlFormatter;
+ this.diffOperations = diffOperations;
maxFileCount = config.getInt("change", null, "maxFiles", 100_000);
}
@@ -478,7 +483,7 @@
"Warning: Change with %d files on host %s, project %s, ref %s",
changedFiles, host, project, refName);
}
- } catch (IOException e) {
+ } catch (DiffNotAvailableException e) {
// This happens e.g. for cherrypicks.
if (!receiveEvent.command.getRefName().startsWith(REFS_CHANGES)) {
logger.atWarning().withCause(e).log(
@@ -488,20 +493,18 @@
return Collections.emptyList();
}
- private long countChangedFiles(CommitReceivedEvent receiveEvent) throws IOException {
- try (Repository repository = repoManager.openRepository(receiveEvent.project.getNameKey());
- DiffFormatter diffFormatter = new DiffFormatter(DisabledOutputStream.INSTANCE)) {
- diffFormatter.setRepository(repository);
- // Do not detect renames; that would require reading file contents, which is slow for large
- // files.
- diffFormatter.setDetectRenames(false);
- // For merge commits, i.e. >1 parents, we use parent #0 by convention.
- List<DiffEntry> diffEntries =
- diffFormatter.scan(
- receiveEvent.commit.getParentCount() > 0 ? receiveEvent.commit.getParent(0) : null,
- receiveEvent.commit);
- return diffEntries.stream().map(DiffEntry::getNewPath).distinct().count();
- }
+ private int countChangedFiles(CommitReceivedEvent receiveEvent)
+ throws DiffNotAvailableException {
+ // For merge commits this will compare against auto-merge.
+ Map<String, FileDiffOutput> modifiedFiles =
+ diffOperations.listModifiedFilesAgainstParent(
+ receiveEvent.getProjectNameKey(), receiveEvent.commit, 0, DiffOptions.DEFAULTS);
+ // We don't want to count the COMMIT_MSG and MERGE_LIST files.
+ List<FileDiffOutput> modifiedFilesList =
+ modifiedFiles.values().stream()
+ .filter(p -> !Patch.isMagic(p.newPath().orElse("")))
+ .collect(Collectors.toList());
+ return modifiedFilesList.size();
}
}
diff --git a/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsLimitsIT.java b/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsLimitsIT.java
index 89b5f6e..eab0d39 100644
--- a/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsLimitsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsLimitsIT.java
@@ -83,27 +83,18 @@
.create();
testRepo.reset(commitFoo);
- // By convention we diff against the first parent.
-
- // commitFoo is first -> 1 file changed -> OK
+ // compared to AUTO_MERGE only one file is changed -> OK
pushFactory
.create(
- admin.newIdent(),
- testRepo,
- "blah",
- ImmutableMap.of("foo.txt", "same old, same old", "bar.txt", "changed file"))
+ admin.newIdent(), testRepo, "blah", ImmutableMap.of("foo.txt", "same old, same old"))
.setParents(ImmutableList.of(commitFoo, commitBar))
.to("refs/for/master")
.assertOkStatus();
- // commitBar is first -> 2 files changed -> rejected
+ // compared to AUTO_MERGE two files are changed -> rejected
pushFactory
- .create(
- admin.newIdent(),
- testRepo,
- "blah",
- ImmutableMap.of("foo.txt", "same old, same old", "bar.txt", "changed file"))
- .setParents(ImmutableList.of(commitBar, commitFoo))
+ .create(admin.newIdent(), testRepo, "blah", ImmutableMap.of("foo.txt", "changed"))
+ .setParents(ImmutableList.of(commitFoo, commitBar))
.to("refs/for/master")
.assertErrorStatus("Exceeding maximum number of files per change (2 > 1)");
}