Fix exception due to duplicate keys in EditTransformer
As copies are treated as additions in the current version of JGit, having multiple PatchListEntries referring to the same file path didn't seem possible. However, when a file is renamed alongside those copies, JGit identifies the copies as such. The logic which transforms the edits from the parents' trees to the children's trees assumed that one edit was only mapped to a transformed edit. With the existence of copies, we need to map one edit to several ones instead. All in all, we compute the cross product of all possible combinations. When computing a real diff between two trees, not all of them would be present at the same time. As it seems difficult to figure out exactly which edits are 'necessary' to keep, we rather keep all of them for the moment and accept that we possibly waste some resources. Change-Id: I6d57fb6a49b8dea58aab9565ac41301c7365159f
This commit is contained in:
parent
a29041ce24
commit
264143d5b5
@ -138,6 +138,20 @@ public class RevisionDiffIT extends AbstractDaemonTest {
|
||||
assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, newFilePath);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copiedFileTreatedAsAddedFileInDiff() throws Exception {
|
||||
String copyFilePath = "copy_of_some_file.txt";
|
||||
gApi.changes().id(changeId).edit().modifyFile(copyFilePath, RawInputUtil.create(FILE_CONTENT));
|
||||
gApi.changes().id(changeId).edit().publish();
|
||||
|
||||
Map<String, FileInfo> changedFiles = gApi.changes().id(changeId).current().files();
|
||||
assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, copyFilePath);
|
||||
// If this ever changes, please add tests which cover copied files.
|
||||
assertThat(changedFiles.get(copyFilePath)).status().isEqualTo('A');
|
||||
assertThat(changedFiles.get(copyFilePath)).linesInserted().isEqualTo(100);
|
||||
assertThat(changedFiles.get(copyFilePath)).linesDeleted().isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addedBinaryFileIsIncludedInDiff() throws Exception {
|
||||
String imageFileName = "an_image.png";
|
||||
@ -1039,6 +1053,43 @@ public class RevisionDiffIT extends AbstractDaemonTest {
|
||||
assertThat(diffInfo).content().element(2).commonLines().hasSize(95);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copiedAndRenamedFilesWithOnlyRebaseHunksAreIdentified() throws Exception {
|
||||
String newFileContent = FILE_CONTENT.replace("Line 5\n", "Line five\n");
|
||||
ObjectId commit2 = addCommit(commit1, FILE_NAME, newFileContent);
|
||||
|
||||
rebaseChangeOn(changeId, commit2);
|
||||
// Copies are only identified by JGit when paired with renaming.
|
||||
String copyFileName = "copy_of_some_file.txt";
|
||||
String renamedFileName = "renamed_some_file.txt";
|
||||
gApi.changes()
|
||||
.id(changeId)
|
||||
.edit()
|
||||
.modifyFile(copyFileName, RawInputUtil.create(newFileContent));
|
||||
gApi.changes().id(changeId).edit().renameFile(FILE_NAME, renamedFileName);
|
||||
gApi.changes().id(changeId).edit().publish();
|
||||
|
||||
Map<String, FileInfo> changedFiles =
|
||||
gApi.changes().id(changeId).current().files(initialPatchSetId);
|
||||
assertThat(changedFiles.keySet()).containsExactly(COMMIT_MSG, copyFileName, renamedFileName);
|
||||
|
||||
DiffInfo renamedFileDiffInfo =
|
||||
getDiffRequest(changeId, CURRENT, renamedFileName).withBase(initialPatchSetId).get();
|
||||
assertThat(renamedFileDiffInfo).content().element(0).commonLines().hasSize(4);
|
||||
assertThat(renamedFileDiffInfo).content().element(1).linesOfA().containsExactly("Line 5");
|
||||
assertThat(renamedFileDiffInfo).content().element(1).linesOfB().containsExactly("Line five");
|
||||
assertThat(renamedFileDiffInfo).content().element(1).isDueToRebase();
|
||||
assertThat(renamedFileDiffInfo).content().element(2).commonLines().hasSize(95);
|
||||
|
||||
DiffInfo copiedFileDiffInfo =
|
||||
getDiffRequest(changeId, CURRENT, copyFileName).withBase(initialPatchSetId).get();
|
||||
assertThat(copiedFileDiffInfo).content().element(0).commonLines().hasSize(4);
|
||||
assertThat(copiedFileDiffInfo).content().element(1).linesOfA().containsExactly("Line 5");
|
||||
assertThat(copiedFileDiffInfo).content().element(1).linesOfB().containsExactly("Line five");
|
||||
assertThat(copiedFileDiffInfo).content().element(1).isDueToRebase();
|
||||
assertThat(copiedFileDiffInfo).content().element(2).commonLines().hasSize(95);
|
||||
}
|
||||
|
||||
/*
|
||||
* change PS B
|
||||
* |
|
||||
|
@ -19,7 +19,6 @@ import static com.google.common.collect.Multimaps.toMultimap;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.base.MoreObjects;
|
||||
@ -109,10 +108,8 @@ class EditTransformer {
|
||||
private void transformEdits(List<PatchListEntry> transformingEntries, SideStrategy sideStrategy) {
|
||||
Map<String, List<ContextAwareEdit>> editsPerFilePath =
|
||||
edits.stream().collect(groupingBy(sideStrategy::getFilePath));
|
||||
Map<String, PatchListEntry> transformingEntryPerPath =
|
||||
transformingEntries
|
||||
.stream()
|
||||
.collect(toMap(EditTransformer::getOldFilePath, Function.identity()));
|
||||
Map<String, List<PatchListEntry>> transEntriesPerPath =
|
||||
transformingEntries.stream().collect(groupingBy(EditTransformer::getOldFilePath));
|
||||
|
||||
edits =
|
||||
editsPerFilePath
|
||||
@ -120,10 +117,9 @@ class EditTransformer {
|
||||
.stream()
|
||||
.flatMap(
|
||||
pathAndEdits -> {
|
||||
PatchListEntry transformingEntry =
|
||||
transformingEntryPerPath.get(pathAndEdits.getKey());
|
||||
return transformEdits(sideStrategy, pathAndEdits.getValue(), transformingEntry)
|
||||
.stream();
|
||||
List<PatchListEntry> transEntries =
|
||||
transEntriesPerPath.getOrDefault(pathAndEdits.getKey(), ImmutableList.of());
|
||||
return transformEdits(sideStrategy, pathAndEdits.getValue(), transEntries);
|
||||
})
|
||||
.collect(toList());
|
||||
}
|
||||
@ -132,15 +128,22 @@ class EditTransformer {
|
||||
return MoreObjects.firstNonNull(patchListEntry.getOldName(), patchListEntry.getNewName());
|
||||
}
|
||||
|
||||
private static List<ContextAwareEdit> transformEdits(
|
||||
private static Stream<ContextAwareEdit> transformEdits(
|
||||
SideStrategy sideStrategy,
|
||||
List<ContextAwareEdit> originalEdits,
|
||||
PatchListEntry transformingEntry) {
|
||||
if (transformingEntry == null) {
|
||||
return originalEdits;
|
||||
List<PatchListEntry> transformingEntries) {
|
||||
if (transformingEntries.isEmpty()) {
|
||||
return originalEdits.stream();
|
||||
}
|
||||
return transformEdits(
|
||||
sideStrategy, originalEdits, transformingEntry.getEdits(), transformingEntry.getNewName());
|
||||
|
||||
// TODO(aliceks): Find a way to prevent an explosion of the number of entries.
|
||||
return transformingEntries
|
||||
.stream()
|
||||
.flatMap(
|
||||
transEntry ->
|
||||
transformEdits(
|
||||
sideStrategy, originalEdits, transEntry.getEdits(), transEntry.getNewName())
|
||||
.stream());
|
||||
}
|
||||
|
||||
private static List<ContextAwareEdit> transformEdits(
|
||||
|
@ -16,6 +16,7 @@ package com.google.gerrit.extensions.common;
|
||||
|
||||
import static com.google.common.truth.Truth.assertAbout;
|
||||
|
||||
import com.google.common.truth.ComparableSubject;
|
||||
import com.google.common.truth.FailureStrategy;
|
||||
import com.google.common.truth.IntegerSubject;
|
||||
import com.google.common.truth.Subject;
|
||||
@ -51,4 +52,10 @@ public class FileInfoSubject extends Subject<FileInfoSubject, FileInfo> {
|
||||
FileInfo fileInfo = actual();
|
||||
return Truth.assertThat(fileInfo.linesDeleted).named("linesDeleted");
|
||||
}
|
||||
|
||||
public ComparableSubject<?, Character> status() {
|
||||
isNotNull();
|
||||
FileInfo fileInfo = actual();
|
||||
return Truth.assertThat(fileInfo.status).named("status");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user