Allow fixes to refer to several files
Suggested fixes may now also refer to other files than the one on which the robot comment was placed. In addition, a fix may modify several files. Whether a file exists or not is only verified when a fix is applied. The check isn't done when a robot comment is added in order to not slow down the operation. Change-Id: Id26232019b42dfe8286c9b0696f00b8a376482b0
This commit is contained in:
@@ -5737,8 +5737,8 @@ fix. It will be generated automatically and hence will be ignored if it's set
|
|||||||
for input objects.
|
for input objects.
|
||||||
|`description` ||A description of the suggested fix.
|
|`description` ||A description of the suggested fix.
|
||||||
|`replacements` ||A list of <<fix-replacement-info,FixReplacementInfo>>
|
|`replacements` ||A list of <<fix-replacement-info,FixReplacementInfo>>
|
||||||
entities indicating how the content of the file on which the comment was placed
|
entities indicating how the content of one or several files should be modified.
|
||||||
should be modified. They should refer to non-overlapping regions.
|
Within a file, they should refer to non-overlapping regions.
|
||||||
|==========================
|
|==========================
|
||||||
|
|
||||||
[[fix-replacement-info]]
|
[[fix-replacement-info]]
|
||||||
@@ -5749,8 +5749,8 @@ replaced by another content.
|
|||||||
[options="header",cols="1,6"]
|
[options="header",cols="1,6"]
|
||||||
|==========================
|
|==========================
|
||||||
|Field Name |Description
|
|Field Name |Description
|
||||||
|`path` |The path of the file which should be modified. Modifications
|
|`path` |The path of the file which should be modified. Any file in
|
||||||
are only allowed for the file on which the corresponding comment was placed.
|
the repository may be modified.
|
||||||
|`range` |A <<comment-range,CommentRange>> indicating which content
|
|`range` |A <<comment-range,CommentRange>> indicating which content
|
||||||
of the file should be replaced. Lines in the file are assumed to be separated
|
of the file should be replaced. Lines in the file are assumed to be separated
|
||||||
by the line feed character, the carriage return character, the carriage return
|
by the line feed character, the carriage return character, the carriage return
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import static com.google.gerrit.extensions.common.RobotCommentInfoSubject.assert
|
|||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||||
import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
|
import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
|
||||||
@@ -53,9 +54,11 @@ import org.junit.Test;
|
|||||||
|
|
||||||
public class RobotCommentsIT extends AbstractDaemonTest {
|
public class RobotCommentsIT extends AbstractDaemonTest {
|
||||||
private static final String FILE_NAME = "file_to_fix.txt";
|
private static final String FILE_NAME = "file_to_fix.txt";
|
||||||
|
private static final String FILE_NAME2 = "another_file_to_fix.txt";
|
||||||
private static final String FILE_CONTENT =
|
private static final String FILE_CONTENT =
|
||||||
"First line\nSecond line\nThird line\nFourth line\nFifth line\nSixth line"
|
"First line\nSecond line\nThird line\nFourth line\nFifth line\nSixth line"
|
||||||
+ "\nSeventh line\nEighth line\nNinth line\nTenth line\n";
|
+ "\nSeventh line\nEighth line\nNinth line\nTenth line\n";
|
||||||
|
private static final String FILE_CONTENT2 = "1st line\n2nd line\n3rd line\n";
|
||||||
|
|
||||||
private String changeId;
|
private String changeId;
|
||||||
private FixReplacementInfo fixReplacementInfo;
|
private FixReplacementInfo fixReplacementInfo;
|
||||||
@@ -64,8 +67,14 @@ public class RobotCommentsIT extends AbstractDaemonTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
PushOneCommit.Result changeResult =
|
PushOneCommit push =
|
||||||
createChange("Provide a file which can be used for fixes", FILE_NAME, FILE_CONTENT);
|
pushFactory.create(
|
||||||
|
db,
|
||||||
|
admin.getIdent(),
|
||||||
|
testRepo,
|
||||||
|
"Provide files which can be used for fixes",
|
||||||
|
ImmutableMap.of(FILE_NAME, FILE_CONTENT, FILE_NAME2, FILE_CONTENT2));
|
||||||
|
PushOneCommit.Result changeResult = push.to("refs/for/master");
|
||||||
changeId = changeResult.getChangeId();
|
changeId = changeResult.getChangeId();
|
||||||
|
|
||||||
fixReplacementInfo = createFixReplacementInfo();
|
fixReplacementInfo = createFixReplacementInfo();
|
||||||
@@ -276,21 +285,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
|
|||||||
addRobotComment(changeId, withFixRobotCommentInput);
|
addRobotComment(changeId, withFixRobotCommentInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void pathOfFixReplacementMustReferToFileOfComment() throws Exception {
|
|
||||||
assume().that(notesMigration.enabled()).isTrue();
|
|
||||||
|
|
||||||
fixReplacementInfo.path = "anotherFile.txt";
|
|
||||||
|
|
||||||
exception.expect(BadRequestException.class);
|
|
||||||
exception.expectMessage(
|
|
||||||
String.format(
|
|
||||||
"Replacements may only be specified "
|
|
||||||
+ "for the file %s on which the robot comment was added",
|
|
||||||
withFixRobotCommentInput.path));
|
|
||||||
addRobotComment(changeId, withFixRobotCommentInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void rangeOfFixReplacementIsAcceptedAsIs() throws Exception {
|
public void rangeOfFixReplacementIsAcceptedAsIs() throws Exception {
|
||||||
assume().that(notesMigration.enabled()).isTrue();
|
assume().that(notesMigration.enabled()).isTrue();
|
||||||
@@ -332,7 +326,8 @@ public class RobotCommentsIT extends AbstractDaemonTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void rangesOfFixReplacementsOfSameFixSuggestionMayNotOverlap() throws Exception {
|
public void rangesOfFixReplacementsOfSameFixSuggestionForSameFileMayNotOverlap()
|
||||||
|
throws Exception {
|
||||||
assume().that(notesMigration.enabled()).isTrue();
|
assume().that(notesMigration.enabled()).isTrue();
|
||||||
|
|
||||||
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
|
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
|
||||||
@@ -355,7 +350,33 @@ public class RobotCommentsIT extends AbstractDaemonTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void rangesOfFixReplacementsOfDifferentFixSuggestionsMayOverlap() throws Exception {
|
public void rangesOfFixReplacementsOfSameFixSuggestionForDifferentFileMayOverlap()
|
||||||
|
throws Exception {
|
||||||
|
assume().that(notesMigration.enabled()).isTrue();
|
||||||
|
|
||||||
|
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
|
||||||
|
fixReplacementInfo1.path = FILE_NAME;
|
||||||
|
fixReplacementInfo1.range = createRange(2, 0, 3, 1);
|
||||||
|
fixReplacementInfo1.replacement = "First modification\n";
|
||||||
|
|
||||||
|
FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo();
|
||||||
|
fixReplacementInfo2.path = FILE_NAME2;
|
||||||
|
fixReplacementInfo2.range = createRange(3, 0, 4, 0);
|
||||||
|
fixReplacementInfo2.replacement = "Second modification\n";
|
||||||
|
|
||||||
|
FixSuggestionInfo fixSuggestionInfo =
|
||||||
|
createFixSuggestionInfo(fixReplacementInfo1, fixReplacementInfo2);
|
||||||
|
withFixRobotCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo);
|
||||||
|
|
||||||
|
addRobotComment(changeId, withFixRobotCommentInput);
|
||||||
|
|
||||||
|
List<RobotCommentInfo> robotCommentInfos = getRobotComments();
|
||||||
|
assertThatList(robotCommentInfos).onlyElement().fixSuggestions().hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rangesOfFixReplacementsOfDifferentFixSuggestionsForSameFileMayOverlap()
|
||||||
|
throws Exception {
|
||||||
assume().that(notesMigration.enabled()).isTrue();
|
assume().that(notesMigration.enabled()).isTrue();
|
||||||
|
|
||||||
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
|
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
|
||||||
@@ -621,6 +642,84 @@ public class RobotCommentsIT extends AbstractDaemonTest {
|
|||||||
+ "Seventh line\nSome other modified content\nNinth line\nTenth line\n");
|
+ "Seventh line\nSome other modified content\nNinth line\nTenth line\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fixReferringToDifferentFileThanRobotCommentCanBeApplied() throws Exception {
|
||||||
|
assume().that(notesMigration.enabled()).isTrue();
|
||||||
|
|
||||||
|
fixReplacementInfo.path = FILE_NAME2;
|
||||||
|
fixReplacementInfo.range = createRange(2, 0, 3, 0);
|
||||||
|
fixReplacementInfo.replacement = "Modified content\n";
|
||||||
|
|
||||||
|
addRobotComment(changeId, withFixRobotCommentInput);
|
||||||
|
List<RobotCommentInfo> robotCommentInfos = getRobotComments();
|
||||||
|
List<String> fixIds = getFixIds(robotCommentInfos);
|
||||||
|
String fixId = Iterables.getOnlyElement(fixIds);
|
||||||
|
|
||||||
|
gApi.changes().id(changeId).current().applyFix(fixId);
|
||||||
|
|
||||||
|
Optional<BinaryResult> file = gApi.changes().id(changeId).edit().getFile(FILE_NAME2);
|
||||||
|
BinaryResultSubject.assertThat(file)
|
||||||
|
.value()
|
||||||
|
.asString()
|
||||||
|
.isEqualTo("1st line\nModified content\n3rd line\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fixInvolvingTwoFilesCanBeApplied() throws Exception {
|
||||||
|
assume().that(notesMigration.enabled()).isTrue();
|
||||||
|
|
||||||
|
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
|
||||||
|
fixReplacementInfo1.path = FILE_NAME;
|
||||||
|
fixReplacementInfo1.range = createRange(2, 0, 3, 0);
|
||||||
|
fixReplacementInfo1.replacement = "First modification\n";
|
||||||
|
|
||||||
|
FixReplacementInfo fixReplacementInfo2 = new FixReplacementInfo();
|
||||||
|
fixReplacementInfo2.path = FILE_NAME2;
|
||||||
|
fixReplacementInfo2.range = createRange(1, 0, 2, 0);
|
||||||
|
fixReplacementInfo2.replacement = "Different file modification\n";
|
||||||
|
|
||||||
|
FixSuggestionInfo fixSuggestionInfo =
|
||||||
|
createFixSuggestionInfo(fixReplacementInfo1, fixReplacementInfo2);
|
||||||
|
withFixRobotCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo);
|
||||||
|
|
||||||
|
addRobotComment(changeId, withFixRobotCommentInput);
|
||||||
|
List<RobotCommentInfo> robotCommentInfos = getRobotComments();
|
||||||
|
List<String> fixIds = getFixIds(robotCommentInfos);
|
||||||
|
String fixId = Iterables.getOnlyElement(fixIds);
|
||||||
|
|
||||||
|
gApi.changes().id(changeId).current().applyFix(fixId);
|
||||||
|
|
||||||
|
Optional<BinaryResult> file = gApi.changes().id(changeId).edit().getFile(FILE_NAME);
|
||||||
|
BinaryResultSubject.assertThat(file)
|
||||||
|
.value()
|
||||||
|
.asString()
|
||||||
|
.isEqualTo(
|
||||||
|
"First line\nFirst modification\nThird line\nFourth line\nFifth line\nSixth line\n"
|
||||||
|
+ "Seventh line\nEighth line\nNinth line\nTenth line\n");
|
||||||
|
Optional<BinaryResult> file2 = gApi.changes().id(changeId).edit().getFile(FILE_NAME2);
|
||||||
|
BinaryResultSubject.assertThat(file2)
|
||||||
|
.value()
|
||||||
|
.asString()
|
||||||
|
.isEqualTo("Different file modification\n2nd line\n3rd line\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fixReferringToNonExistentFileCannotBeApplied() throws Exception {
|
||||||
|
assume().that(notesMigration.enabled()).isTrue();
|
||||||
|
|
||||||
|
fixReplacementInfo.path = "a_non_existent_file.txt";
|
||||||
|
fixReplacementInfo.range = createRange(1, 0, 2, 0);
|
||||||
|
fixReplacementInfo.replacement = "Modified content\n";
|
||||||
|
|
||||||
|
addRobotComment(changeId, withFixRobotCommentInput);
|
||||||
|
List<RobotCommentInfo> robotCommentInfos = getRobotComments();
|
||||||
|
List<String> fixIds = getFixIds(robotCommentInfos);
|
||||||
|
String fixId = Iterables.getOnlyElement(fixIds);
|
||||||
|
|
||||||
|
exception.expect(ResourceNotFoundException.class);
|
||||||
|
gApi.changes().id(changeId).current().applyFix(fixId);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void fixOnPreviousPatchSetWithoutChangeEditCannotBeApplied() throws Exception {
|
public void fixOnPreviousPatchSetWithoutChangeEditCannotBeApplied() throws Exception {
|
||||||
assume().that(notesMigration.enabled()).isTrue();
|
assume().that(notesMigration.enabled()).isTrue();
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ java_library(
|
|||||||
deps = [
|
deps = [
|
||||||
":server",
|
":server",
|
||||||
"//gerrit-extension-api:api",
|
"//gerrit-extension-api:api",
|
||||||
|
"//gerrit-test-util:test_util",
|
||||||
"//lib:truth",
|
"//lib:truth",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import com.google.gwtorm.server.OrmException;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
@@ -68,12 +69,12 @@ public class ApplyFix implements RestModifyView<FixResource, Void> {
|
|||||||
ObjectId patchSetCommitId = ObjectId.fromString(patchSet.getRevision().get());
|
ObjectId patchSetCommitId = ObjectId.fromString(patchSet.getRevision().get());
|
||||||
|
|
||||||
try (Repository repository = gitRepositoryManager.openRepository(project)) {
|
try (Repository repository = gitRepositoryManager.openRepository(project)) {
|
||||||
TreeModification treeModification =
|
List<TreeModification> treeModifications =
|
||||||
fixReplacementInterpreter.toTreeModification(
|
fixReplacementInterpreter.toTreeModifications(
|
||||||
repository, projectState, patchSetCommitId, fixResource.getFixReplacements());
|
repository, projectState, patchSetCommitId, fixResource.getFixReplacements());
|
||||||
ChangeEdit changeEdit =
|
ChangeEdit changeEdit =
|
||||||
changeEditModifier.combineWithModifiedPatchSetTree(
|
changeEditModifier.combineWithModifiedPatchSetTree(
|
||||||
repository, revisionResource.getControl(), patchSet, treeModification);
|
repository, revisionResource.getControl(), patchSet, treeModifications);
|
||||||
EditInfo editInfo = changeEditJson.toEditInfo(changeEdit, false);
|
EditInfo editInfo = changeEditJson.toEditInfo(changeEdit, false);
|
||||||
return Response.ok(editInfo);
|
return Response.ok(editInfo);
|
||||||
} catch (InvalidChangeOperationException e) {
|
} catch (InvalidChangeOperationException e) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState;
|
|||||||
import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
|
import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
|
||||||
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
|
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static java.util.stream.Collectors.groupingBy;
|
||||||
import static java.util.stream.Collectors.joining;
|
import static java.util.stream.Collectors.joining;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
import static java.util.stream.Collectors.toSet;
|
import static java.util.stream.Collectors.toSet;
|
||||||
@@ -426,7 +427,8 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends CommentInput> void cleanUpComments(Map<String, List<T>> commentsPerPath) {
|
private static <T extends CommentInput> void cleanUpComments(
|
||||||
|
Map<String, List<T>> commentsPerPath) {
|
||||||
Iterator<List<T>> mapValueIterator = commentsPerPath.values().iterator();
|
Iterator<List<T>> mapValueIterator = commentsPerPath.values().iterator();
|
||||||
while (mapValueIterator.hasNext()) {
|
while (mapValueIterator.hasNext()) {
|
||||||
List<T> comments = mapValueIterator.next();
|
List<T> comments = mapValueIterator.next();
|
||||||
@@ -442,7 +444,7 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends CommentInput> void cleanUpComments(List<T> comments) {
|
private static <T extends CommentInput> void cleanUpComments(List<T> comments) {
|
||||||
Iterator<T> commentsIterator = comments.iterator();
|
Iterator<T> commentsIterator = comments.iterator();
|
||||||
while (commentsIterator.hasNext()) {
|
while (commentsIterator.hasNext()) {
|
||||||
T comment = commentsIterator.next();
|
T comment = commentsIterator.next();
|
||||||
@@ -481,7 +483,7 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
|
|||||||
return new HashSet<>(changeData.filePaths(revision.getPatchSet()));
|
return new HashSet<>(changeData.filePaths(revision.getPatchSet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensurePathRefersToAvailableOrMagicFile(
|
private static void ensurePathRefersToAvailableOrMagicFile(
|
||||||
String path, Set<String> availableFilePaths, PatchSet.Id patchSetId)
|
String path, Set<String> availableFilePaths, PatchSet.Id patchSetId)
|
||||||
throws BadRequestException {
|
throws BadRequestException {
|
||||||
if (!availableFilePaths.contains(path) && !Patch.isMagic(path)) {
|
if (!availableFilePaths.contains(path) && !Patch.isMagic(path)) {
|
||||||
@@ -490,14 +492,15 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureLineIsNonNegative(Integer line, String path) throws BadRequestException {
|
private static void ensureLineIsNonNegative(Integer line, String path)
|
||||||
|
throws BadRequestException {
|
||||||
if (line != null && line < 0) {
|
if (line != null && line < 0) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
String.format("negative line number %d not allowed on %s", line, path));
|
String.format("negative line number %d not allowed on %s", line, path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends CommentInput> void ensureCommentNotOnMagicFilesOfAutoMerge(
|
private static <T extends CommentInput> void ensureCommentNotOnMagicFilesOfAutoMerge(
|
||||||
String path, T comment) throws BadRequestException {
|
String path, T comment) throws BadRequestException {
|
||||||
if (Patch.isMagic(path) && comment.side == Side.PARENT && comment.parent == null) {
|
if (Patch.isMagic(path) && comment.side == Side.PARENT && comment.parent == null) {
|
||||||
throw new BadRequestException(String.format("cannot comment on %s on auto-merge", path));
|
throw new BadRequestException(String.format("cannot comment on %s on auto-merge", path));
|
||||||
@@ -519,14 +522,15 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
|
|||||||
checkComments(revision, in);
|
checkComments(revision, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureRobotIdIsSet(String robotId, String commentPath) throws BadRequestException {
|
private static void ensureRobotIdIsSet(String robotId, String commentPath)
|
||||||
|
throws BadRequestException {
|
||||||
if (robotId == null) {
|
if (robotId == null) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
String.format("robotId is missing for robot comment on %s", commentPath));
|
String.format("robotId is missing for robot comment on %s", commentPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureRobotRunIdIsSet(String robotRunId, String commentPath)
|
private static void ensureRobotRunIdIsSet(String robotRunId, String commentPath)
|
||||||
throws BadRequestException {
|
throws BadRequestException {
|
||||||
if (robotRunId == null) {
|
if (robotRunId == null) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
@@ -534,7 +538,7 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureFixSuggestionsAreAddable(
|
private static void ensureFixSuggestionsAreAddable(
|
||||||
List<FixSuggestionInfo> fixSuggestionInfos, String commentPath) throws BadRequestException {
|
List<FixSuggestionInfo> fixSuggestionInfos, String commentPath) throws BadRequestException {
|
||||||
if (fixSuggestionInfos == null) {
|
if (fixSuggestionInfos == null) {
|
||||||
return;
|
return;
|
||||||
@@ -546,7 +550,7 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureDescriptionIsSet(String commentPath, String description)
|
private static void ensureDescriptionIsSet(String commentPath, String description)
|
||||||
throws BadRequestException {
|
throws BadRequestException {
|
||||||
if (description == null) {
|
if (description == null) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
@@ -556,21 +560,25 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureFixReplacementsAreAddable(
|
private static void ensureFixReplacementsAreAddable(
|
||||||
String commentPath, List<FixReplacementInfo> fixReplacementInfos) throws BadRequestException {
|
String commentPath, List<FixReplacementInfo> fixReplacementInfos) throws BadRequestException {
|
||||||
ensureReplacementsArePresent(commentPath, fixReplacementInfos);
|
ensureReplacementsArePresent(commentPath, fixReplacementInfos);
|
||||||
|
|
||||||
for (FixReplacementInfo fixReplacementInfo : fixReplacementInfos) {
|
for (FixReplacementInfo fixReplacementInfo : fixReplacementInfos) {
|
||||||
ensureReplacementPathIsSet(commentPath, fixReplacementInfo.path);
|
ensureReplacementPathIsSet(commentPath, fixReplacementInfo.path);
|
||||||
ensureReplacementPathRefersToFileOfComment(commentPath, fixReplacementInfo.path);
|
|
||||||
ensureRangeIsSet(commentPath, fixReplacementInfo.range);
|
ensureRangeIsSet(commentPath, fixReplacementInfo.range);
|
||||||
ensureRangeIsValid(commentPath, fixReplacementInfo.range);
|
ensureRangeIsValid(commentPath, fixReplacementInfo.range);
|
||||||
ensureReplacementStringIsSet(commentPath, fixReplacementInfo.replacement);
|
ensureReplacementStringIsSet(commentPath, fixReplacementInfo.replacement);
|
||||||
}
|
}
|
||||||
ensureRangesDoNotOverlap(commentPath, fixReplacementInfos);
|
|
||||||
|
Map<String, List<FixReplacementInfo>> replacementsPerFilePath =
|
||||||
|
fixReplacementInfos.stream().collect(groupingBy(fixReplacement -> fixReplacement.path));
|
||||||
|
for (List<FixReplacementInfo> sameFileReplacements : replacementsPerFilePath.values()) {
|
||||||
|
ensureRangesDoNotOverlap(commentPath, sameFileReplacements);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureReplacementsArePresent(
|
private static void ensureReplacementsArePresent(
|
||||||
String commentPath, List<FixReplacementInfo> fixReplacementInfos) throws BadRequestException {
|
String commentPath, List<FixReplacementInfo> fixReplacementInfos) throws BadRequestException {
|
||||||
if (fixReplacementInfos == null || fixReplacementInfos.isEmpty()) {
|
if (fixReplacementInfos == null || fixReplacementInfos.isEmpty()) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
@@ -581,7 +589,7 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureReplacementPathIsSet(String commentPath, String replacementPath)
|
private static void ensureReplacementPathIsSet(String commentPath, String replacementPath)
|
||||||
throws BadRequestException {
|
throws BadRequestException {
|
||||||
if (replacementPath == null) {
|
if (replacementPath == null) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
@@ -591,18 +599,7 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureReplacementPathRefersToFileOfComment(
|
private static void ensureRangeIsSet(String commentPath, Range range) throws BadRequestException {
|
||||||
String commentPath, String replacementPath) throws BadRequestException {
|
|
||||||
if (!Objects.equals(commentPath, replacementPath)) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
String.format(
|
|
||||||
"Replacements may only be "
|
|
||||||
+ "specified for the file %s on which the robot comment was added",
|
|
||||||
commentPath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ensureRangeIsSet(String commentPath, Range range) throws BadRequestException {
|
|
||||||
if (range == null) {
|
if (range == null) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
String.format(
|
String.format(
|
||||||
@@ -610,7 +607,8 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureRangeIsValid(String commentPath, Range range) throws BadRequestException {
|
private static void ensureRangeIsValid(String commentPath, Range range)
|
||||||
|
throws BadRequestException {
|
||||||
if (range == null) {
|
if (range == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -626,7 +624,7 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureReplacementStringIsSet(String commentPath, String replacement)
|
private static void ensureReplacementStringIsSet(String commentPath, String replacement)
|
||||||
throws BadRequestException {
|
throws BadRequestException {
|
||||||
if (replacement == null) {
|
if (replacement == null) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package com.google.gerrit.server.edit;
|
|||||||
import static com.google.common.base.Preconditions.checkState;
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.gerrit.common.TimeUtil;
|
import com.google.gerrit.common.TimeUtil;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
import com.google.gerrit.extensions.restapi.MergeConflictException;
|
import com.google.gerrit.extensions.restapi.MergeConflictException;
|
||||||
@@ -44,6 +45,7 @@ import com.google.inject.Provider;
|
|||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import org.eclipse.jgit.lib.BatchRefUpdate;
|
import org.eclipse.jgit.lib.BatchRefUpdate;
|
||||||
@@ -307,7 +309,7 @@ public class ChangeEditModifier {
|
|||||||
RevCommit baseCommit =
|
RevCommit baseCommit =
|
||||||
optionalChangeEdit.map(ChangeEdit::getEditCommit).orElse(basePatchSetCommit);
|
optionalChangeEdit.map(ChangeEdit::getEditCommit).orElse(basePatchSetCommit);
|
||||||
|
|
||||||
ObjectId newTreeId = createNewTree(repository, baseCommit, treeModification);
|
ObjectId newTreeId = createNewTree(repository, baseCommit, ImmutableList.of(treeModification));
|
||||||
|
|
||||||
String commitMessage = baseCommit.getFullMessage();
|
String commitMessage = baseCommit.getFullMessage();
|
||||||
Timestamp nowTimestamp = TimeUtil.nowTs();
|
Timestamp nowTimestamp = TimeUtil.nowTs();
|
||||||
@@ -322,14 +324,14 @@ public class ChangeEditModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the indicated modification to the specified patch set. If a change edit exists and is
|
* Applies the indicated modifications to the specified patch set. If a change edit exists and is
|
||||||
* based on the same patch set, the modified patch set tree is merged with the change edit. If the
|
* based on the same patch set, the modified patch set tree is merged with the change edit. If the
|
||||||
* change edit doesn't exist, a new one will be created.
|
* change edit doesn't exist, a new one will be created.
|
||||||
*
|
*
|
||||||
* @param repository the affected Git repository
|
* @param repository the affected Git repository
|
||||||
* @param changeControl the {@code ChangeControl} of the change to which the patch set belongs
|
* @param changeControl the {@code ChangeControl} of the change to which the patch set belongs
|
||||||
* @param patchSet the {@code PatchSet} which should be modified
|
* @param patchSet the {@code PatchSet} which should be modified
|
||||||
* @param treeModification the modification which should be applied
|
* @param treeModifications the modifications which should be applied
|
||||||
* @return the resulting {@code ChangeEdit}
|
* @return the resulting {@code ChangeEdit}
|
||||||
* @throws AuthException if the user isn't authenticated or not allowed to use change edits
|
* @throws AuthException if the user isn't authenticated or not allowed to use change edits
|
||||||
* @throws InvalidChangeOperationException if the existing change edit is based on another patch
|
* @throws InvalidChangeOperationException if the existing change edit is based on another patch
|
||||||
@@ -341,7 +343,7 @@ public class ChangeEditModifier {
|
|||||||
Repository repository,
|
Repository repository,
|
||||||
ChangeControl changeControl,
|
ChangeControl changeControl,
|
||||||
PatchSet patchSet,
|
PatchSet patchSet,
|
||||||
TreeModification treeModification)
|
List<TreeModification> treeModifications)
|
||||||
throws AuthException, IOException, InvalidChangeOperationException, MergeConflictException,
|
throws AuthException, IOException, InvalidChangeOperationException, MergeConflictException,
|
||||||
OrmException {
|
OrmException {
|
||||||
ensureAuthenticatedAndPermitted(changeControl);
|
ensureAuthenticatedAndPermitted(changeControl);
|
||||||
@@ -350,13 +352,13 @@ public class ChangeEditModifier {
|
|||||||
ensureAllowedPatchSet(changeControl, optionalChangeEdit, patchSet);
|
ensureAllowedPatchSet(changeControl, optionalChangeEdit, patchSet);
|
||||||
|
|
||||||
RevCommit patchSetCommit = lookupCommit(repository, patchSet);
|
RevCommit patchSetCommit = lookupCommit(repository, patchSet);
|
||||||
ObjectId newTreeId = createNewTree(repository, patchSetCommit, treeModification);
|
ObjectId newTreeId = createNewTree(repository, patchSetCommit, treeModifications);
|
||||||
|
|
||||||
if (optionalChangeEdit.isPresent()) {
|
if (optionalChangeEdit.isPresent()) {
|
||||||
ChangeEdit changeEdit = optionalChangeEdit.get();
|
ChangeEdit changeEdit = optionalChangeEdit.get();
|
||||||
newTreeId = merge(repository, changeEdit, newTreeId);
|
newTreeId = merge(repository, changeEdit, newTreeId);
|
||||||
if (ObjectId.equals(newTreeId, changeEdit.getEditCommit().getTree())) {
|
if (ObjectId.equals(newTreeId, changeEdit.getEditCommit().getTree())) {
|
||||||
// Modification is already contained in the change edit.
|
// Modifications are already contained in the change edit.
|
||||||
return changeEdit;
|
return changeEdit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -459,10 +461,10 @@ public class ChangeEditModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ObjectId createNewTree(
|
private static ObjectId createNewTree(
|
||||||
Repository repository, RevCommit baseCommit, TreeModification treeModification)
|
Repository repository, RevCommit baseCommit, List<TreeModification> treeModifications)
|
||||||
throws IOException, InvalidChangeOperationException {
|
throws IOException, InvalidChangeOperationException {
|
||||||
TreeCreator treeCreator = new TreeCreator(baseCommit);
|
TreeCreator treeCreator = new TreeCreator(baseCommit);
|
||||||
treeCreator.addTreeModification(treeModification);
|
treeCreator.addTreeModifications(treeModifications);
|
||||||
ObjectId newTreeId = treeCreator.createNewTreeAndGetId(repository);
|
ObjectId newTreeId = treeCreator.createNewTreeAndGetId(repository);
|
||||||
|
|
||||||
if (ObjectId.equals(newTreeId, baseCommit.getTree())) {
|
if (ObjectId.equals(newTreeId, baseCommit.getTree())) {
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ public class ChangeFileContentModification implements TreeModification {
|
|||||||
return Collections.singletonList(changeContentEdit);
|
return Collections.singletonList(changeContentEdit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@Override
|
||||||
String getFilePath() {
|
public String getFilePath() {
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,4 +34,9 @@ public class DeleteFileModification implements TreeModification {
|
|||||||
DirCacheEditor.DeletePath deletePathEdit = new DirCacheEditor.DeletePath(filePath);
|
DirCacheEditor.DeletePath deletePathEdit = new DirCacheEditor.DeletePath(filePath);
|
||||||
return Collections.singletonList(deletePathEdit);
|
return Collections.singletonList(deletePathEdit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFilePath() {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,4 +52,9 @@ public class RenameFileModification implements TreeModification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFilePath() {
|
||||||
|
return newFilePath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,4 +58,9 @@ public class RestoreFileModification implements TreeModification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFilePath() {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,6 @@ import org.eclipse.jgit.revwalk.RevCommit;
|
|||||||
public class TreeCreator {
|
public class TreeCreator {
|
||||||
|
|
||||||
private final RevCommit baseCommit;
|
private final RevCommit baseCommit;
|
||||||
// At the moment, a list wouldn't be necessary as only one modification is
|
|
||||||
// applied per created tree. This is going to change in the near future.
|
|
||||||
private final List<TreeModification> treeModifications = new ArrayList<>();
|
private final List<TreeModification> treeModifications = new ArrayList<>();
|
||||||
|
|
||||||
public TreeCreator(RevCommit baseCommit) {
|
public TreeCreator(RevCommit baseCommit) {
|
||||||
@@ -45,14 +43,14 @@ public class TreeCreator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply a modification to the tree which is taken as a basis. If this method is called multiple
|
* Apply modifications to the tree which is taken as a basis. If this method is called multiple
|
||||||
* times, the modifications are applied subsequently in exactly the order they were provided.
|
* times, the modifications are applied subsequently in exactly the order they were provided.
|
||||||
*
|
*
|
||||||
* @param treeModification a modification which should be applied to the base tree
|
* @param treeModifications modifications which should be applied to the base tree
|
||||||
*/
|
*/
|
||||||
public void addTreeModification(TreeModification treeModification) {
|
public void addTreeModifications(List<TreeModification> treeModifications) {
|
||||||
checkNotNull(treeModification, "treeModification must not be null");
|
checkNotNull(treeModifications, "treeModifications must not be null");
|
||||||
treeModifications.add(treeModification);
|
this.treeModifications.addAll(treeModifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.edit.tree;
|
package com.google.gerrit.server.edit.tree;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.eclipse.jgit.dircache.DirCacheEditor;
|
import org.eclipse.jgit.dircache.DirCacheEditor;
|
||||||
@@ -35,4 +36,14 @@ public interface TreeModification {
|
|||||||
*/
|
*/
|
||||||
List<DirCacheEditor.PathEdit> getPathEdits(Repository repository, RevCommit baseCommit)
|
List<DirCacheEditor.PathEdit> getPathEdits(Repository repository, RevCommit baseCommit)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates a file path which is affected by this {@code TreeModification}. If the modification
|
||||||
|
* refers to several file paths (e.g. renaming a file), returning either of them is appropriate as
|
||||||
|
* long as the returned value is deterministic.
|
||||||
|
*
|
||||||
|
* @return an affected file path
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
String getFilePath();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.fixes;
|
package com.google.gerrit.server.fixes;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import com.google.gerrit.common.RawInputUtil;
|
import com.google.gerrit.common.RawInputUtil;
|
||||||
@@ -33,6 +32,8 @@ import java.io.IOException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
@@ -51,31 +52,39 @@ public class FixReplacementInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms the given {@code FixReplacement}s into a {@code TreeModification}.
|
* Transforms the given {@code FixReplacement}s into {@code TreeModification}s.
|
||||||
*
|
*
|
||||||
* @param repository the affected Git repository
|
* @param repository the affected Git repository
|
||||||
* @param projectState the affected project
|
* @param projectState the affected project
|
||||||
* @param patchSetCommitId the patch set which should be modified
|
* @param patchSetCommitId the patch set which should be modified
|
||||||
* @param fixReplacements the replacements which should be applied
|
* @param fixReplacements the replacements which should be applied
|
||||||
* @return a {@code TreeModification} representing the given replacements
|
* @return a list of {@code TreeModification}s representing the given replacements
|
||||||
* @throws ResourceNotFoundException if a file to which one of the replacements refers doesn't
|
* @throws ResourceNotFoundException if a file to which one of the replacements refers doesn't
|
||||||
* exist
|
* exist
|
||||||
* @throws ResourceConflictException if the replacements can't be transformed into a {@code
|
* @throws ResourceConflictException if the replacements can't be transformed into {@code
|
||||||
* TreeModification}
|
* TreeModification}s
|
||||||
*/
|
*/
|
||||||
public TreeModification toTreeModification(
|
public List<TreeModification> toTreeModifications(
|
||||||
Repository repository,
|
Repository repository,
|
||||||
ProjectState projectState,
|
ProjectState projectState,
|
||||||
ObjectId patchSetCommitId,
|
ObjectId patchSetCommitId,
|
||||||
List<FixReplacement> fixReplacements)
|
List<FixReplacement> fixReplacements)
|
||||||
throws ResourceNotFoundException, IOException, ResourceConflictException {
|
throws ResourceNotFoundException, IOException, ResourceConflictException {
|
||||||
checkNotNull(fixReplacements, "Fix replacements must not be null");
|
checkNotNull(fixReplacements, "Fix replacements must not be null");
|
||||||
checkArgument(!fixReplacements.isEmpty(), "Fix replacements must not be empty");
|
|
||||||
|
|
||||||
// For a fix suggestion, we allow only fix replacements for the same file.
|
Map<String, List<FixReplacement>> fixReplacementsPerFilePath =
|
||||||
String filePath = fixReplacements.get(0).path;
|
fixReplacements
|
||||||
return toTreeModification(
|
.stream()
|
||||||
repository, projectState, patchSetCommitId, filePath, fixReplacements);
|
.collect(Collectors.groupingBy(fixReplacement -> fixReplacement.path));
|
||||||
|
|
||||||
|
List<TreeModification> treeModifications = new ArrayList<>();
|
||||||
|
for (Map.Entry<String, List<FixReplacement>> entry : fixReplacementsPerFilePath.entrySet()) {
|
||||||
|
TreeModification treeModification =
|
||||||
|
toTreeModification(
|
||||||
|
repository, projectState, patchSetCommitId, entry.getKey(), entry.getValue());
|
||||||
|
treeModifications.add(treeModification);
|
||||||
|
}
|
||||||
|
return treeModifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TreeModification toTreeModification(
|
private TreeModification toTreeModification(
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import static com.google.common.truth.Truth.assertAbout;
|
|||||||
import com.google.common.truth.FailureStrategy;
|
import com.google.common.truth.FailureStrategy;
|
||||||
import com.google.common.truth.Subject;
|
import com.google.common.truth.Subject;
|
||||||
import com.google.common.truth.SubjectFactory;
|
import com.google.common.truth.SubjectFactory;
|
||||||
|
import com.google.gerrit.truth.ListSubject;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class TreeModificationSubject extends Subject<TreeModificationSubject, TreeModification> {
|
public class TreeModificationSubject extends Subject<TreeModificationSubject, TreeModification> {
|
||||||
|
|
||||||
@@ -36,6 +38,12 @@ public class TreeModificationSubject extends Subject<TreeModificationSubject, Tr
|
|||||||
return assertAbout(TREE_MODIFICATION_SUBJECT_FACTORY).that(treeModification);
|
return assertAbout(TREE_MODIFICATION_SUBJECT_FACTORY).that(treeModification);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ListSubject<TreeModificationSubject, TreeModification> assertThatList(
|
||||||
|
List<TreeModification> treeModifications) {
|
||||||
|
return ListSubject.assertThat(treeModifications, TreeModificationSubject::assertThat)
|
||||||
|
.named("treeModifications");
|
||||||
|
}
|
||||||
|
|
||||||
private TreeModificationSubject(
|
private TreeModificationSubject(
|
||||||
FailureStrategy failureStrategy, TreeModification treeModification) {
|
FailureStrategy failureStrategy, TreeModification treeModification) {
|
||||||
super(failureStrategy, treeModification);
|
super(failureStrategy, treeModification);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.fixes;
|
package com.google.gerrit.server.fixes;
|
||||||
|
|
||||||
import static com.google.gerrit.server.edit.tree.TreeModificationSubject.assertThat;
|
import static com.google.gerrit.server.edit.tree.TreeModificationSubject.assertThatList;
|
||||||
import static org.easymock.EasyMock.createMock;
|
import static org.easymock.EasyMock.createMock;
|
||||||
import static org.easymock.EasyMock.replay;
|
import static org.easymock.EasyMock.replay;
|
||||||
|
|
||||||
@@ -26,6 +26,9 @@ import com.google.gerrit.reviewdb.client.FixReplacement;
|
|||||||
import com.google.gerrit.server.change.FileContentUtil;
|
import com.google.gerrit.server.change.FileContentUtil;
|
||||||
import com.google.gerrit.server.edit.tree.TreeModification;
|
import com.google.gerrit.server.edit.tree.TreeModification;
|
||||||
import com.google.gerrit.server.project.ProjectState;
|
import com.google.gerrit.server.project.ProjectState;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
import org.easymock.EasyMock;
|
import org.easymock.EasyMock;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
@@ -42,7 +45,8 @@ public class FixReplacementInterpreterTest {
|
|||||||
private final Repository repository = createMock(Repository.class);
|
private final Repository repository = createMock(Repository.class);
|
||||||
private final ProjectState projectState = createMock(ProjectState.class);
|
private final ProjectState projectState = createMock(ProjectState.class);
|
||||||
private final ObjectId patchSetCommitId = createMock(ObjectId.class);
|
private final ObjectId patchSetCommitId = createMock(ObjectId.class);
|
||||||
private final String filePath = "an/arbitrary/file.txt";
|
private final String filePath1 = "an/arbitrary/file.txt";
|
||||||
|
private final String filePath2 = "another/arbitrary/file.txt";
|
||||||
|
|
||||||
private FixReplacementInterpreter fixReplacementInterpreter;
|
private FixReplacementInterpreter fixReplacementInterpreter;
|
||||||
|
|
||||||
@@ -52,24 +56,57 @@ public class FixReplacementInterpreterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void treeModificationTargetsCorrectFile() throws Exception {
|
public void noReplacementsResultInNoTreeModifications() throws Exception {
|
||||||
|
List<TreeModification> treeModifications = toTreeModifications();
|
||||||
|
assertThatList(treeModifications).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void treeModificationsTargetCorrectFiles() throws Exception {
|
||||||
FixReplacement fixReplacement =
|
FixReplacement fixReplacement =
|
||||||
new FixReplacement(filePath, new Range(1, 1, 3, 2), "Modified content");
|
new FixReplacement(filePath1, new Range(1, 6, 3, 2), "Modified content");
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\n");
|
FixReplacement fixReplacement2 =
|
||||||
|
new FixReplacement(filePath1, new Range(3, 5, 3, 5), "Second modification");
|
||||||
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
FixReplacement fixReplacement3 =
|
||||||
|
new FixReplacement(filePath2, new Range(2, 0, 3, 0), "Another modified content");
|
||||||
|
mockFileContent(filePath2, "1st line\n2nd line\n3rd line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
TreeModification treeModification = toTreeModification(fixReplacement);
|
List<TreeModification> treeModifications =
|
||||||
assertThat(treeModification).asChangeFileContentModification().filePath().isEqualTo(filePath);
|
toTreeModifications(fixReplacement, fixReplacement3, fixReplacement2);
|
||||||
|
List<TreeModification> sortedTreeModifications = getSortedCopy(treeModifications);
|
||||||
|
assertThatList(sortedTreeModifications)
|
||||||
|
.element(0)
|
||||||
|
.asChangeFileContentModification()
|
||||||
|
.filePath()
|
||||||
|
.isEqualTo(filePath1);
|
||||||
|
assertThatList(sortedTreeModifications)
|
||||||
|
.element(0)
|
||||||
|
.asChangeFileContentModification()
|
||||||
|
.newContent()
|
||||||
|
.startsWith("First");
|
||||||
|
assertThatList(sortedTreeModifications)
|
||||||
|
.element(1)
|
||||||
|
.asChangeFileContentModification()
|
||||||
|
.filePath()
|
||||||
|
.isEqualTo(filePath2);
|
||||||
|
assertThatList(sortedTreeModifications)
|
||||||
|
.element(1)
|
||||||
|
.asChangeFileContentModification()
|
||||||
|
.newContent()
|
||||||
|
.startsWith("1st");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void replacementsCanDeleteALine() throws Exception {
|
public void replacementsCanDeleteALine() throws Exception {
|
||||||
FixReplacement fixReplacement = new FixReplacement(filePath, new Range(2, 0, 3, 0), "");
|
FixReplacement fixReplacement = new FixReplacement(filePath1, new Range(2, 0, 3, 0), "");
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\n");
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
TreeModification treeModification = toTreeModification(fixReplacement);
|
List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
|
||||||
assertThat(treeModification)
|
assertThatList(treeModifications)
|
||||||
|
.onlyElement()
|
||||||
.asChangeFileContentModification()
|
.asChangeFileContentModification()
|
||||||
.newContent()
|
.newContent()
|
||||||
.isEqualTo("First line\nThird line\n");
|
.isEqualTo("First line\nThird line\n");
|
||||||
@@ -78,12 +115,13 @@ public class FixReplacementInterpreterTest {
|
|||||||
@Test
|
@Test
|
||||||
public void replacementsCanAddALine() throws Exception {
|
public void replacementsCanAddALine() throws Exception {
|
||||||
FixReplacement fixReplacement =
|
FixReplacement fixReplacement =
|
||||||
new FixReplacement(filePath, new Range(2, 0, 2, 0), "A new line\n");
|
new FixReplacement(filePath1, new Range(2, 0, 2, 0), "A new line\n");
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\n");
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
TreeModification treeModification = toTreeModification(fixReplacement);
|
List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
|
||||||
assertThat(treeModification)
|
assertThatList(treeModifications)
|
||||||
|
.onlyElement()
|
||||||
.asChangeFileContentModification()
|
.asChangeFileContentModification()
|
||||||
.newContent()
|
.newContent()
|
||||||
.isEqualTo("First line\nA new line\nSecond line\nThird line\n");
|
.isEqualTo("First line\nA new line\nSecond line\nThird line\n");
|
||||||
@@ -91,12 +129,13 @@ public class FixReplacementInterpreterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void replacementsMaySpanMultipleLines() throws Exception {
|
public void replacementsMaySpanMultipleLines() throws Exception {
|
||||||
FixReplacement fixReplacement = new FixReplacement(filePath, new Range(1, 6, 3, 1), "and t");
|
FixReplacement fixReplacement = new FixReplacement(filePath1, new Range(1, 6, 3, 1), "and t");
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\n");
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
TreeModification treeModification = toTreeModification(fixReplacement);
|
List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
|
||||||
assertThat(treeModification)
|
assertThatList(treeModifications)
|
||||||
|
.onlyElement()
|
||||||
.asChangeFileContentModification()
|
.asChangeFileContentModification()
|
||||||
.newContent()
|
.newContent()
|
||||||
.isEqualTo("First and third line\n");
|
.isEqualTo("First and third line\n");
|
||||||
@@ -104,14 +143,16 @@ public class FixReplacementInterpreterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void replacementsMayOccurOnSameLine() throws Exception {
|
public void replacementsMayOccurOnSameLine() throws Exception {
|
||||||
FixReplacement fixReplacement1 = new FixReplacement(filePath, new Range(2, 0, 2, 6), "A");
|
FixReplacement fixReplacement1 = new FixReplacement(filePath1, new Range(2, 0, 2, 6), "A");
|
||||||
FixReplacement fixReplacement2 =
|
FixReplacement fixReplacement2 =
|
||||||
new FixReplacement(filePath, new Range(2, 7, 2, 11), "modification");
|
new FixReplacement(filePath1, new Range(2, 7, 2, 11), "modification");
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\n");
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
TreeModification treeModification = toTreeModification(fixReplacement1, fixReplacement2);
|
List<TreeModification> treeModifications =
|
||||||
assertThat(treeModification)
|
toTreeModifications(fixReplacement1, fixReplacement2);
|
||||||
|
assertThatList(treeModifications)
|
||||||
|
.onlyElement()
|
||||||
.asChangeFileContentModification()
|
.asChangeFileContentModification()
|
||||||
.newContent()
|
.newContent()
|
||||||
.isEqualTo("First line\nA modification\nThird line\n");
|
.isEqualTo("First line\nA modification\nThird line\n");
|
||||||
@@ -120,13 +161,16 @@ public class FixReplacementInterpreterTest {
|
|||||||
@Test
|
@Test
|
||||||
public void replacementsMayTouch() throws Exception {
|
public void replacementsMayTouch() throws Exception {
|
||||||
FixReplacement fixReplacement1 =
|
FixReplacement fixReplacement1 =
|
||||||
new FixReplacement(filePath, new Range(1, 6, 2, 7), "modified ");
|
new FixReplacement(filePath1, new Range(1, 6, 2, 7), "modified ");
|
||||||
FixReplacement fixReplacement2 = new FixReplacement(filePath, new Range(2, 7, 3, 5), "content");
|
FixReplacement fixReplacement2 =
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\n");
|
new FixReplacement(filePath1, new Range(2, 7, 3, 5), "content");
|
||||||
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
TreeModification treeModification = toTreeModification(fixReplacement1, fixReplacement2);
|
List<TreeModification> treeModifications =
|
||||||
assertThat(treeModification)
|
toTreeModifications(fixReplacement1, fixReplacement2);
|
||||||
|
assertThatList(treeModifications)
|
||||||
|
.onlyElement()
|
||||||
.asChangeFileContentModification()
|
.asChangeFileContentModification()
|
||||||
.newContent()
|
.newContent()
|
||||||
.isEqualTo("First modified content line\n");
|
.isEqualTo("First modified content line\n");
|
||||||
@@ -135,25 +179,54 @@ public class FixReplacementInterpreterTest {
|
|||||||
@Test
|
@Test
|
||||||
public void replacementsCanAddContentAtEndOfFile() throws Exception {
|
public void replacementsCanAddContentAtEndOfFile() throws Exception {
|
||||||
FixReplacement fixReplacement =
|
FixReplacement fixReplacement =
|
||||||
new FixReplacement(filePath, new Range(4, 0, 4, 0), "New content");
|
new FixReplacement(filePath1, new Range(4, 0, 4, 0), "New content");
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\n");
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
TreeModification treeModification = toTreeModification(fixReplacement);
|
List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
|
||||||
assertThat(treeModification)
|
assertThatList(treeModifications)
|
||||||
|
.onlyElement()
|
||||||
.asChangeFileContentModification()
|
.asChangeFileContentModification()
|
||||||
.newContent()
|
.newContent()
|
||||||
.isEqualTo("First line\nSecond line\nThird line\nNew content");
|
.isEqualTo("First line\nSecond line\nThird line\nNew content");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void lineSeparatorCanBeChanged() throws Exception {
|
public void replacementsCanModifySeveralFilesInAnyOrder() throws Exception {
|
||||||
FixReplacement fixReplacement = new FixReplacement(filePath, new Range(2, 11, 3, 0), "\r");
|
FixReplacement fixReplacement1 =
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\n");
|
new FixReplacement(filePath1, new Range(1, 1, 3, 2), "Modified content");
|
||||||
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
FixReplacement fixReplacement2 =
|
||||||
|
new FixReplacement(filePath2, new Range(2, 0, 3, 0), "First modification\n");
|
||||||
|
FixReplacement fixReplacement3 =
|
||||||
|
new FixReplacement(filePath2, new Range(3, 0, 4, 0), "Second modification\n");
|
||||||
|
mockFileContent(filePath2, "1st line\n2nd line\n3rd line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
TreeModification treeModification = toTreeModification(fixReplacement);
|
List<TreeModification> treeModifications =
|
||||||
assertThat(treeModification)
|
toTreeModifications(fixReplacement3, fixReplacement1, fixReplacement2);
|
||||||
|
List<TreeModification> sortedTreeModifications = getSortedCopy(treeModifications);
|
||||||
|
assertThatList(sortedTreeModifications)
|
||||||
|
.element(0)
|
||||||
|
.asChangeFileContentModification()
|
||||||
|
.newContent()
|
||||||
|
.isEqualTo("FModified contentird line\n");
|
||||||
|
assertThatList(sortedTreeModifications)
|
||||||
|
.element(1)
|
||||||
|
.asChangeFileContentModification()
|
||||||
|
.newContent()
|
||||||
|
.isEqualTo("1st line\nFirst modification\nSecond modification\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lineSeparatorCanBeChanged() throws Exception {
|
||||||
|
FixReplacement fixReplacement = new FixReplacement(filePath1, new Range(2, 11, 3, 0), "\r");
|
||||||
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
|
||||||
|
replay(fileContentUtil);
|
||||||
|
List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
|
||||||
|
assertThatList(treeModifications)
|
||||||
|
.onlyElement()
|
||||||
.asChangeFileContentModification()
|
.asChangeFileContentModification()
|
||||||
.newContent()
|
.newContent()
|
||||||
.isEqualTo("First line\nSecond line\rThird line\n");
|
.isEqualTo("First line\nSecond line\rThird line\n");
|
||||||
@@ -162,17 +235,18 @@ public class FixReplacementInterpreterTest {
|
|||||||
@Test
|
@Test
|
||||||
public void replacementsDoNotNeedToBeOrderedAccordingToRange() throws Exception {
|
public void replacementsDoNotNeedToBeOrderedAccordingToRange() throws Exception {
|
||||||
FixReplacement fixReplacement1 =
|
FixReplacement fixReplacement1 =
|
||||||
new FixReplacement(filePath, new Range(1, 0, 2, 0), "1st modification\n");
|
new FixReplacement(filePath1, new Range(1, 0, 2, 0), "1st modification\n");
|
||||||
FixReplacement fixReplacement2 =
|
FixReplacement fixReplacement2 =
|
||||||
new FixReplacement(filePath, new Range(3, 0, 4, 0), "2nd modification\n");
|
new FixReplacement(filePath1, new Range(3, 0, 4, 0), "2nd modification\n");
|
||||||
FixReplacement fixReplacement3 =
|
FixReplacement fixReplacement3 =
|
||||||
new FixReplacement(filePath, new Range(4, 0, 5, 0), "3rd modification\n");
|
new FixReplacement(filePath1, new Range(4, 0, 5, 0), "3rd modification\n");
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\nFourth line\nFifth line\n");
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\nFourth line\nFifth line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
TreeModification treeModification =
|
List<TreeModification> treeModifications =
|
||||||
toTreeModification(fixReplacement2, fixReplacement1, fixReplacement3);
|
toTreeModifications(fixReplacement2, fixReplacement1, fixReplacement3);
|
||||||
assertThat(treeModification)
|
assertThatList(treeModifications)
|
||||||
|
.onlyElement()
|
||||||
.asChangeFileContentModification()
|
.asChangeFileContentModification()
|
||||||
.newContent()
|
.newContent()
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
@@ -182,61 +256,61 @@ public class FixReplacementInterpreterTest {
|
|||||||
@Test
|
@Test
|
||||||
public void replacementsMustNotReferToNotExistingLine() throws Exception {
|
public void replacementsMustNotReferToNotExistingLine() throws Exception {
|
||||||
FixReplacement fixReplacement =
|
FixReplacement fixReplacement =
|
||||||
new FixReplacement(filePath, new Range(5, 0, 5, 0), "A new line\n");
|
new FixReplacement(filePath1, new Range(5, 0, 5, 0), "A new line\n");
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\n");
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
|
|
||||||
expectedException.expect(ResourceConflictException.class);
|
expectedException.expect(ResourceConflictException.class);
|
||||||
toTreeModification(fixReplacement);
|
toTreeModifications(fixReplacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void replacementsMustNotReferToZeroLine() throws Exception {
|
public void replacementsMustNotReferToZeroLine() throws Exception {
|
||||||
FixReplacement fixReplacement =
|
FixReplacement fixReplacement =
|
||||||
new FixReplacement(filePath, new Range(0, 0, 0, 0), "A new line\n");
|
new FixReplacement(filePath1, new Range(0, 0, 0, 0), "A new line\n");
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\n");
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
|
|
||||||
expectedException.expect(ResourceConflictException.class);
|
expectedException.expect(ResourceConflictException.class);
|
||||||
toTreeModification(fixReplacement);
|
toTreeModifications(fixReplacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void replacementsMustNotReferToNotExistingOffsetOfIntermediateLine() throws Exception {
|
public void replacementsMustNotReferToNotExistingOffsetOfIntermediateLine() throws Exception {
|
||||||
FixReplacement fixReplacement =
|
FixReplacement fixReplacement =
|
||||||
new FixReplacement(filePath, new Range(1, 0, 1, 11), "modified");
|
new FixReplacement(filePath1, new Range(1, 0, 1, 11), "modified");
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\n");
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
|
|
||||||
expectedException.expect(ResourceConflictException.class);
|
expectedException.expect(ResourceConflictException.class);
|
||||||
toTreeModification(fixReplacement);
|
toTreeModifications(fixReplacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void replacementsMustNotReferToNotExistingOffsetOfLastLine() throws Exception {
|
public void replacementsMustNotReferToNotExistingOffsetOfLastLine() throws Exception {
|
||||||
FixReplacement fixReplacement =
|
FixReplacement fixReplacement =
|
||||||
new FixReplacement(filePath, new Range(3, 0, 3, 11), "modified");
|
new FixReplacement(filePath1, new Range(3, 0, 3, 11), "modified");
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\n");
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
|
|
||||||
expectedException.expect(ResourceConflictException.class);
|
expectedException.expect(ResourceConflictException.class);
|
||||||
toTreeModification(fixReplacement);
|
toTreeModifications(fixReplacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void replacementsMustNotReferToNegativeOffset() throws Exception {
|
public void replacementsMustNotReferToNegativeOffset() throws Exception {
|
||||||
FixReplacement fixReplacement =
|
FixReplacement fixReplacement =
|
||||||
new FixReplacement(filePath, new Range(1, -1, 1, 5), "modified");
|
new FixReplacement(filePath1, new Range(1, -1, 1, 5), "modified");
|
||||||
mockFileContent(filePath, "First line\nSecond line\nThird line\n");
|
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
|
||||||
|
|
||||||
replay(fileContentUtil);
|
replay(fileContentUtil);
|
||||||
|
|
||||||
expectedException.expect(ResourceConflictException.class);
|
expectedException.expect(ResourceConflictException.class);
|
||||||
toTreeModification(fixReplacement);
|
toTreeModifications(fixReplacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockFileContent(String filePath, String fileContent) throws Exception {
|
private void mockFileContent(String filePath, String fileContent) throws Exception {
|
||||||
@@ -245,8 +319,15 @@ public class FixReplacementInterpreterTest {
|
|||||||
.andReturn(BinaryResult.create(fileContent));
|
.andReturn(BinaryResult.create(fileContent));
|
||||||
}
|
}
|
||||||
|
|
||||||
private TreeModification toTreeModification(FixReplacement... fixReplacements) throws Exception {
|
private List<TreeModification> toTreeModifications(FixReplacement... fixReplacements)
|
||||||
return fixReplacementInterpreter.toTreeModification(
|
throws Exception {
|
||||||
|
return fixReplacementInterpreter.toTreeModifications(
|
||||||
repository, projectState, patchSetCommitId, ImmutableList.copyOf(fixReplacements));
|
repository, projectState, patchSetCommitId, ImmutableList.copyOf(fixReplacements));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<TreeModification> getSortedCopy(List<TreeModification> treeModifications) {
|
||||||
|
List<TreeModification> sortedTreeModifications = new ArrayList<>(treeModifications);
|
||||||
|
sortedTreeModifications.sort(Comparator.comparing(TreeModification::getFilePath));
|
||||||
|
return sortedTreeModifications;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user