Merge changes I3b520220,I09533336
* changes:
Reduce default value for maxPathsInChangeMessages config parameter
Remove sorting of owned paths to avoid that all owned paths are computed
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/ChangedFiles.java b/java/com/google/gerrit/plugins/codeowners/backend/ChangedFiles.java
index c96af17..b691e21 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/ChangedFiles.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/ChangedFiles.java
@@ -15,10 +15,10 @@
package com.google.gerrit.plugins.codeowners.backend;
import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.Objects.requireNonNull;
-import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.Patch;
import com.google.gerrit.entities.Project;
@@ -84,12 +84,12 @@
* <p>The diff is computed against the parent commit.
*
* @param revisionResource the revision resource for which the changed files should be computed
- * @return the files that have been changed in the given revision
+ * @return the files that have been changed in the given revision, sorted alphabetically by path
* @throws IOException thrown if the computation fails due to an I/O error
* @throws PatchListNotAvailableException thrown if getting the patch list for a merge commit
* against the auto merge failed
*/
- public ImmutableSet<ChangedFile> compute(RevisionResource revisionResource)
+ public ImmutableList<ChangedFile> compute(RevisionResource revisionResource)
throws IOException, PatchListNotAvailableException {
requireNonNull(revisionResource, "revisionResource");
return compute(revisionResource.getProject(), revisionResource.getPatchSet().commitId());
@@ -102,12 +102,12 @@
*
* @param project the project
* @param revision the revision for which the changed files should be computed
- * @return the files that have been changed in the given revision
+ * @return the files that have been changed in the given revision, sorted alphabetically by path
* @throws IOException thrown if the computation fails due to an I/O error
* @throws PatchListNotAvailableException thrown if getting the patch list for a merge commit
* against the auto merge failed
*/
- public ImmutableSet<ChangedFile> compute(Project.NameKey project, ObjectId revision)
+ public ImmutableList<ChangedFile> compute(Project.NameKey project, ObjectId revision)
throws IOException, PatchListNotAvailableException {
requireNonNull(project, "project");
requireNonNull(revision, "revision");
@@ -119,7 +119,7 @@
}
}
- public ImmutableSet<ChangedFile> compute(
+ public ImmutableList<ChangedFile> compute(
Project.NameKey project, Config repoConfig, RevWalk revWalk, RevCommit revCommit)
throws IOException, PatchListNotAvailableException {
return compute(
@@ -130,7 +130,7 @@
codeOwnersPluginConfiguration.getProjectConfig(project).getMergeCommitStrategy());
}
- public ImmutableSet<ChangedFile> compute(
+ public ImmutableList<ChangedFile> compute(
Project.NameKey project,
Config repoConfig,
RevWalk revWalk,
@@ -164,7 +164,7 @@
* @param mergeCommit the merge commit for which the changed files should be computed
* @return the changed files for the given merge commit
*/
- private ImmutableSet<ChangedFile> computeByComparingAgainstAutoMerge(
+ private ImmutableList<ChangedFile> computeByComparingAgainstAutoMerge(
Project.NameKey project, RevCommit mergeCommit) throws PatchListNotAvailableException {
checkState(
mergeCommit.getParentCount() > 1, "expected %s to be a merge commit", mergeCommit.name());
@@ -180,7 +180,7 @@
patchListEntry.getNewName() == null
|| !Patch.isMagic(patchListEntry.getNewName()))
.map(ChangedFile::create)
- .collect(toImmutableSet());
+ .collect(toImmutableList());
}
}
@@ -192,9 +192,9 @@
* @param repoConfig the repository configuration
* @param revWalk the rev walk
* @param revCommit the commit for which the changed files should be computed
- * @return the changed files for the given commit
+ * @return the changed files for the given commit, sorted alphabetically by path
*/
- private ImmutableSet<ChangedFile> computeByComparingAgainstFirstParent(
+ private ImmutableList<ChangedFile> computeByComparingAgainstFirstParent(
Config repoConfig, RevWalk revWalk, RevCommit revCommit) throws IOException {
try (Timer0.Context ctx = codeOwnerMetrics.computeChangedFilesAgainstFirstParent.start()) {
RevCommit baseCommit = revCommit.getParentCount() > 0 ? revCommit.getParent(0) : null;
@@ -209,8 +209,8 @@
diffFormatter.setReader(revWalk.getObjectReader(), repoConfig);
diffFormatter.setDiffComparator(RawTextComparator.DEFAULT);
List<DiffEntry> diffEntries = diffFormatter.scan(baseCommit, revCommit);
- ImmutableSet<ChangedFile> changedFiles =
- diffEntries.stream().map(ChangedFile::create).collect(toImmutableSet());
+ ImmutableList<ChangedFile> changedFiles =
+ diffEntries.stream().map(ChangedFile::create).collect(toImmutableList());
if (changedFiles.size() <= MAX_CHANGED_FILES_TO_LOG) {
logger.atFine().log("changed files = %s", changedFiles);
} else {
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerApprovalCheck.java b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerApprovalCheck.java
index 1c5e87f..5ab28b4 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerApprovalCheck.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerApprovalCheck.java
@@ -17,7 +17,6 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
@@ -149,8 +148,7 @@
.map(Optional::get))
.filter(
pathCodeOwnerStatus -> pathCodeOwnerStatus.status() == CodeOwnerStatus.APPROVED)
- .map(PathCodeOwnerStatus::path)
- .sorted(comparing(Path::toString));
+ .map(PathCodeOwnerStatus::path);
if (limit > 0) {
ownedPaths = ownedPaths.limit(limit);
}
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfig.java b/java/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfig.java
index ddeec46..251504a 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfig.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfig.java
@@ -69,11 +69,12 @@
public static final String KEY_EXEMPTED_USER = "exemptedUser";
public static final String KEY_ENABLE_IMPLICIT_APPROVALS = "enableImplicitApprovals";
public static final String KEY_OVERRIDE_INFO_URL = "overrideInfoUrl";
- public static final int DEFAULT_MAX_PATHS_IN_CHANGE_MESSAGES = 100;
public static final String KEY_REJECT_NON_RESOLVABLE_CODE_OWNERS =
"rejectNonResolvableCodeOwners";
public static final String KEY_REJECT_NON_RESOLVABLE_IMPORTS = "rejectNonResolvableImports";
+ public static final int DEFAULT_MAX_PATHS_IN_CHANGE_MESSAGES = 50;
+
private static final String KEY_ALLOWED_EMAIL_DOMAIN = "allowedEmailDomain";
private final String pluginName;
diff --git a/java/com/google/gerrit/plugins/codeowners/testing/BUILD b/java/com/google/gerrit/plugins/codeowners/testing/BUILD
index cff6196..16af5f4 100644
--- a/java/com/google/gerrit/plugins/codeowners/testing/BUILD
+++ b/java/com/google/gerrit/plugins/codeowners/testing/BUILD
@@ -19,5 +19,6 @@
"//plugins/code-owners/java/com/google/gerrit/plugins/codeowners/backend",
"//plugins/code-owners/java/com/google/gerrit/plugins/codeowners/common",
"//plugins/code-owners/java/com/google/gerrit/plugins/codeowners/restapi",
+ "//plugins/code-owners/java/com/google/gerrit/plugins/codeowners/util",
],
)
diff --git a/java/com/google/gerrit/plugins/codeowners/testing/ChangedFileSubject.java b/java/com/google/gerrit/plugins/codeowners/testing/ChangedFileSubject.java
index 069122c..9484c97 100644
--- a/java/com/google/gerrit/plugins/codeowners/testing/ChangedFileSubject.java
+++ b/java/com/google/gerrit/plugins/codeowners/testing/ChangedFileSubject.java
@@ -18,16 +18,34 @@
import static com.google.gerrit.truth.OptionalSubject.optionals;
import com.google.common.collect.ImmutableList;
+import com.google.common.truth.Correspondence;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.Subject;
import com.google.gerrit.plugins.codeowners.common.ChangedFile;
+import com.google.gerrit.plugins.codeowners.util.JgitPath;
import com.google.gerrit.truth.ListSubject;
+import com.google.gerrit.truth.NullAwareCorrespondence;
import com.google.gerrit.truth.OptionalSubject;
import java.util.Collection;
/** {@link Subject} for doing assertions on {@link ChangedFile}s. */
public class ChangedFileSubject extends Subject {
/**
+ * Constructs a {@link Correspondence} that maps {@link ChangedFile}s to their paths (new path if
+ * set, otherwise old path).
+ */
+ public static Correspondence<ChangedFile, String> hasPath() {
+ return NullAwareCorrespondence.transforming(
+ changedFile ->
+ JgitPath.of(
+ changedFile.newPath().isPresent()
+ ? changedFile.newPath().get()
+ : changedFile.oldPath().get())
+ .get(),
+ "has path");
+ }
+
+ /**
* Starts fluent chain to do assertions on a {@link ChangedFile}.
*
* @param changedFile the changed file on which assertions should be done
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/ChangedFilesTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/ChangedFilesTest.java
index 34e0cb2..2bbc4f4 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/backend/ChangedFilesTest.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/backend/ChangedFilesTest.java
@@ -18,9 +18,12 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.plugins.codeowners.testing.ChangedFileSubject.assertThat;
import static com.google.gerrit.plugins.codeowners.testing.ChangedFileSubject.assertThatCollection;
+import static com.google.gerrit.plugins.codeowners.testing.ChangedFileSubject.hasPath;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.junit.Assert.fail;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.PushOneCommit;
@@ -88,7 +91,8 @@
String changeId =
createChange("Change Adding A File", JgitPath.of(path).get(), "file content").getChangeId();
- ImmutableSet<ChangedFile> changedFilesSet = changedFiles.compute(getRevisionResource(changeId));
+ ImmutableList<ChangedFile> changedFilesSet =
+ changedFiles.compute(getRevisionResource(changeId));
assertThat(changedFilesSet).hasSize(1);
ChangedFile changedFile = Iterables.getOnlyElement(changedFilesSet);
assertThat(changedFile).hasNewPath().value().isEqualTo(Paths.get(path));
@@ -106,7 +110,8 @@
createChange("Change Modifying A File", JgitPath.of(path).get(), "new file content")
.getChangeId();
- ImmutableSet<ChangedFile> changedFilesSet = changedFiles.compute(getRevisionResource(changeId));
+ ImmutableList<ChangedFile> changedFilesSet =
+ changedFiles.compute(getRevisionResource(changeId));
assertThat(changedFilesSet).hasSize(1);
ChangedFile changedFile = Iterables.getOnlyElement(changedFilesSet);
assertThat(changedFile).hasNewPath().value().isEqualTo(Paths.get(path));
@@ -120,7 +125,8 @@
String path = "/foo/bar/baz.txt";
String changeId = createChangeWithFileDeletion(path);
- ImmutableSet<ChangedFile> changedFilesSet = changedFiles.compute(getRevisionResource(changeId));
+ ImmutableList<ChangedFile> changedFilesSet =
+ changedFiles.compute(getRevisionResource(changeId));
assertThat(changedFilesSet).hasSize(1);
ChangedFile changedFile = Iterables.getOnlyElement(changedFilesSet);
assertThat(changedFile).hasNewPath().isEmpty();
@@ -140,7 +146,8 @@
// A renamed file is reported as addition of new path + deletion of old path. This is because
// ChangedFiles uses a DiffFormatter without rename detection (because rename detection requires
// loading the file contents which is too expensive).
- ImmutableSet<ChangedFile> changedFilesSet = changedFiles.compute(getRevisionResource(changeId));
+ ImmutableList<ChangedFile> changedFilesSet =
+ changedFiles.compute(getRevisionResource(changeId));
assertThat(changedFilesSet).hasSize(2);
ChangedFileSubject changedFile1 = assertThatCollection(changedFilesSet).element(0);
changedFile1.hasNewPath().value().isEqualTo(Paths.get(newPath));
@@ -162,7 +169,8 @@
assertThat(r.getCommit().getParents()).isEmpty();
String changeId = r.getChangeId();
- ImmutableSet<ChangedFile> changedFilesSet = changedFiles.compute(getRevisionResource(changeId));
+ ImmutableList<ChangedFile> changedFilesSet =
+ changedFiles.compute(getRevisionResource(changeId));
assertThat(changedFilesSet).hasSize(1);
ChangedFile changedFile = Iterables.getOnlyElement(changedFilesSet);
assertThat(changedFile).hasNewPath().value().isEqualTo(Paths.get(path));
@@ -241,17 +249,13 @@
.content("content")
.create();
- ImmutableSet<ChangedFile> changedFilesSet =
+ ImmutableList<ChangedFile> changedFilesSet =
changedFiles.compute(getRevisionResource(Integer.toString(mergeChange.get())));
- ImmutableSet<String> paths =
- changedFilesSet.stream()
- .map(changedFile -> JgitPath.of(changedFile.newPath().get()).get())
- .collect(toImmutableSet());
if (MergeCommitStrategy.ALL_CHANGED_FILES.equals(mergeCommitStrategy)) {
- assertThat(paths).containsExactly(file1, file2);
+ assertThat(changedFilesSet).comparingElementsUsing(hasPath()).containsExactly(file1, file2);
} else if (MergeCommitStrategy.FILES_WITH_CONFLICT_RESOLUTION.equals(mergeCommitStrategy)) {
- assertThat(paths).containsExactly(file1);
+ assertThat(changedFilesSet).comparingElementsUsing(hasPath()).containsExactly(file1);
} else {
fail("expected merge commit strategy: " + mergeCommitStrategy);
}
@@ -318,7 +322,7 @@
.delete()
.create();
- ImmutableSet<ChangedFile> changedFilesSet =
+ ImmutableList<ChangedFile> changedFilesSet =
changedFiles.compute(getRevisionResource(Integer.toString(mergeChange.get())));
ImmutableSet<String> oldPaths =
changedFilesSet.stream()
@@ -327,6 +331,153 @@
assertThat(oldPaths).containsExactly(file);
}
+ @Test
+ public void sortedByPath() throws Exception {
+ String file1 = "foo/bar.baz";
+ String file2 = "foo/baz.bar";
+ String file3 = "bar/foo.baz";
+ String file4 = "bar/baz.foo";
+ String file5 = "baz/foo.bar";
+ String changeId =
+ createChange(
+ "Test Change",
+ ImmutableMap.of(
+ file1,
+ "file content",
+ file2,
+ "file content",
+ file3,
+ "file content",
+ file4,
+ "file content",
+ file5,
+ "file content"))
+ .getChangeId();
+
+ ImmutableList<ChangedFile> changedFilesSet =
+ changedFiles.compute(getRevisionResource(changeId));
+ assertThat(changedFilesSet)
+ .comparingElementsUsing(hasPath())
+ .containsExactly(file4, file3, file5, file1, file2)
+ .inOrder();
+ }
+
+ @Test
+ @GerritConfig(name = "plugin.code-owners.mergeCommitStrategy", value = "ALL_CHANGED_FILES")
+ public void sortedByPath_mergeCommitAgainstFirstParent() throws Exception {
+ testSortedByPathForMerge(MergeCommitStrategy.ALL_CHANGED_FILES);
+ }
+
+ @Test
+ @GerritConfig(
+ name = "plugin.code-owners.mergeCommitStrategy",
+ value = "FILES_WITH_CONFLICT_RESOLUTION")
+ public void sortedByPath_mergeCommitAgainstAutoMerge() throws Exception {
+ testSortedByPathForMerge(MergeCommitStrategy.FILES_WITH_CONFLICT_RESOLUTION);
+ }
+
+ private void testSortedByPathForMerge(MergeCommitStrategy mergeCommitStrategy) throws Exception {
+ setAsRootCodeOwners(admin);
+
+ String file1 = "foo/bar.baz";
+ String file2 = "foo/baz.bar";
+ String file3 = "bar/foo.baz";
+ String file4 = "bar/baz.foo";
+ String file5 = "baz/foo.bar";
+
+ // Create a base change.
+ Change.Id baseChange =
+ changeOperations
+ .newChange()
+ .branch("master")
+ .file(file1)
+ .content("base content")
+ .file(file3)
+ .content("base content")
+ .file(file5)
+ .content("base content")
+ .create();
+ approveAndSubmit(baseChange);
+
+ // Create another branch
+ String branchName = "foo";
+ BranchInput branchInput = new BranchInput();
+ branchInput.ref = branchName;
+ branchInput.revision = projectOperations.project(project).getHead("master").name();
+ gApi.projects().name(project.get()).branch(branchInput.ref).create(branchInput);
+
+ // Create a change in master that touches file1, file3 and file5
+ Change.Id changeInMaster =
+ changeOperations
+ .newChange()
+ .branch("master")
+ .file(file1)
+ .content("master content")
+ .file(file3)
+ .content("master content")
+ .file(file5)
+ .content("master content")
+ .create();
+ approveAndSubmit(changeInMaster);
+
+ // Create a change in the other branch and that touches file1, file3, file5 and creates file2,
+ // file4.
+ Change.Id changeInOtherBranch =
+ changeOperations
+ .newChange()
+ .branch(branchName)
+ .file(file1)
+ .content("other content")
+ .file(file2)
+ .content("content")
+ .file(file3)
+ .content("other content")
+ .file(file4)
+ .content("content")
+ .file(file5)
+ .content("other content")
+ .create();
+ approveAndSubmit(changeInOtherBranch);
+
+ // Create a merge change with a conflict resolution for file1 and file2 with the same content as
+ // in the other branch (no conflict on file2).
+ Change.Id mergeChange =
+ changeOperations
+ .newChange()
+ .branch("master")
+ .mergeOfButBaseOnFirst()
+ .tipOfBranch("master")
+ .and()
+ .tipOfBranch(branchName)
+ .file(file1)
+ .content("merged content")
+ .file(file2)
+ .content("content")
+ .file(file3)
+ .content("merged content")
+ .file(file4)
+ .content("content")
+ .file(file5)
+ .content("merged content")
+ .create();
+
+ ImmutableList<ChangedFile> changedFilesSet =
+ changedFiles.compute(getRevisionResource(Integer.toString(mergeChange.get())));
+
+ if (MergeCommitStrategy.ALL_CHANGED_FILES.equals(mergeCommitStrategy)) {
+ assertThat(changedFilesSet)
+ .comparingElementsUsing(hasPath())
+ .containsExactly(file4, file3, file5, file1, file2)
+ .inOrder();
+ } else if (MergeCommitStrategy.FILES_WITH_CONFLICT_RESOLUTION.equals(mergeCommitStrategy)) {
+ assertThat(changedFilesSet)
+ .comparingElementsUsing(hasPath())
+ .containsExactly(file3, file5, file1);
+ } else {
+ fail("expected merge commit strategy: " + mergeCommitStrategy);
+ }
+ }
+
private void approveAndSubmit(Change.Id changeId) throws Exception {
approve(Integer.toString(changeId.get()));
gApi.changes().id(changeId.get()).current().submit();
diff --git a/resources/Documentation/config.md b/resources/Documentation/config.md
index 8207d33..ffa4018 100644
--- a/resources/Documentation/config.md
+++ b/resources/Documentation/config.md
@@ -407,7 +407,7 @@
Can be overridden per project by setting
[codeOwners.maxPathsInChangeMessages](#codeOwnersMaxPathsInChangeMessages)
in `@PLUGIN@.config`.\
- By default `100`.
+ By default `50`.
<a id="pluginCodeOwnersMaxCodeOwnerConfigCacheSize">plugin.@PLUGIN@.maxCodeOwnerConfigCacheSize</a>
: When computing code owner file statuses for a change (e.g. to compute
@@ -770,8 +770,7 @@
from parent projects.\
If not set, the global setting
[plugin.@PLUGIN@.maxPathsInChangeMessages](#pluginCodeOwnersMaxPathsInChangeMessages)
- in `gerrit.config` is used.\
- By default `100`.
+ in `gerrit.config` is used.
---