Remove handling of ignored diff context parameter (just backend logic)
Since Ib70c6526a and I6f33b2141, it hasn't mattered whether a request asked for a specific context as we internally 'upgraded' the context to MAX_CONTEXT (= 5,000,000 lines). As a result, Gerrit either always returned the full file content or none at all (-> special 'skip' behavior, which seems to be outdated and problematic as well). In Iae2b098c3, we documented with tests that the context parameter was ignored. We've kept around this effectively dead code for years and carried it through several refactorings. In addition, the handling of the context parameter increased the complexity of the code, making the code base harder to understand for developers. The different behavior when comments are present also had some unintended side effects as one of the tests in Iae2b098c3 showed. Furthermore, we don't even know whether the original code for the context handling still works as we don't have any tests covering a less-than-file context. Hence, we decided to remove the context parameter from the diff calculation in the backend. This doesn't mean that Gerrit diffs shown on the UI now always expand to the full file content. Since Ib70c6526a, the frontend has been responsible for honoring the diff context setting and we'll keep it that way. If it turns out that we'll need such a context parameter in the backend for optimizations in the future, we can always add it back (and potentially use the deleted code as guidance). Instead of just deleting the context parameter from the involved methods, we also adjusted some coding constructs which can now be much simpler. One example is the EditList class, which was simplified to the EditHunk class as we now have just one large EditHunk instead of a list. The involved code could be improved much further with newer Java practices and functionality in mind. That's beyond the scope of this change, though. For the moment, we still need to keep the context parameter as request parameter as the frontend unnecessarily sends it along. Without adapting it, requests would result in errors as GET request parameters are checked for existence. I79e307b63 will remove the parameter from the frontend calls. Change-Id: I6f5acd4e2cbe2a6a0b296b1bc323982ef0049d3a
This commit is contained in:
@@ -5585,9 +5585,6 @@ The `whitespace` parameter can be specified to control how whitespace
|
|||||||
differences are reported in the result. Valid values are `IGNORE_NONE`,
|
differences are reported in the result. Valid values are `IGNORE_NONE`,
|
||||||
`IGNORE_TRAILING`, `IGNORE_LEADING_AND_TRAILING` or `IGNORE_ALL`.
|
`IGNORE_TRAILING`, `IGNORE_LEADING_AND_TRAILING` or `IGNORE_ALL`.
|
||||||
|
|
||||||
The `context` parameter can be specified to control the number of lines of surrounding context
|
|
||||||
in the diff. Valid values are `ALL` or number of lines.
|
|
||||||
|
|
||||||
[[preview-fix]]
|
[[preview-fix]]
|
||||||
=== Preview fix
|
=== Preview fix
|
||||||
--
|
--
|
||||||
|
@@ -52,7 +52,6 @@ public interface FileApi {
|
|||||||
|
|
||||||
abstract class DiffRequest {
|
abstract class DiffRequest {
|
||||||
private String base;
|
private String base;
|
||||||
private Integer context;
|
|
||||||
private Boolean intraline;
|
private Boolean intraline;
|
||||||
private Whitespace whitespace;
|
private Whitespace whitespace;
|
||||||
private OptionalInt parent = OptionalInt.empty();
|
private OptionalInt parent = OptionalInt.empty();
|
||||||
@@ -64,11 +63,6 @@ public interface FileApi {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiffRequest withContext(int context) {
|
|
||||||
this.context = context;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiffRequest withIntraline(boolean intraline) {
|
public DiffRequest withIntraline(boolean intraline) {
|
||||||
this.intraline = intraline;
|
this.intraline = intraline;
|
||||||
return this;
|
return this;
|
||||||
@@ -88,10 +82,6 @@ public interface FileApi {
|
|||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getContext() {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean getIntraline() {
|
public Boolean getIntraline() {
|
||||||
return intraline;
|
return intraline;
|
||||||
}
|
}
|
||||||
|
@@ -28,12 +28,6 @@ public class DiffPreferencesInfo {
|
|||||||
/** Default line length. */
|
/** Default line length. */
|
||||||
public static final int DEFAULT_LINE_LENGTH = 100;
|
public static final int DEFAULT_LINE_LENGTH = 100;
|
||||||
|
|
||||||
/** Context setting to display the entire file. */
|
|
||||||
public static final short WHOLE_FILE_CONTEXT = -1;
|
|
||||||
|
|
||||||
/** Typical valid choices for the default context setting. */
|
|
||||||
public static final short[] CONTEXT_CHOICES = {3, 10, 25, 50, 75, 100, WHOLE_FILE_CONTEXT};
|
|
||||||
|
|
||||||
public enum Whitespace {
|
public enum Whitespace {
|
||||||
IGNORE_NONE,
|
IGNORE_NONE,
|
||||||
IGNORE_TRAILING,
|
IGNORE_TRAILING,
|
||||||
|
92
java/com/google/gerrit/prettify/common/EditHunk.java
Normal file
92
java/com/google/gerrit/prettify/common/EditHunk.java
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// Copyright (C) 2009 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.prettify.common;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.eclipse.jgit.diff.Edit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a legacy class. It was only simplified but not improved regarding readability or code
|
||||||
|
* health. Feel free to completely rewrite it or replace it with some other, better code.
|
||||||
|
*/
|
||||||
|
public class EditHunk {
|
||||||
|
private final List<Edit> edits;
|
||||||
|
|
||||||
|
private int curIdx;
|
||||||
|
private Edit curEdit;
|
||||||
|
|
||||||
|
private int aCur;
|
||||||
|
private int bCur;
|
||||||
|
private final int aEnd;
|
||||||
|
private final int bEnd;
|
||||||
|
|
||||||
|
public EditHunk(List<Edit> edits, int aSize, int bSize) {
|
||||||
|
this.edits = edits;
|
||||||
|
|
||||||
|
curIdx = 0;
|
||||||
|
curEdit = edits.get(curIdx);
|
||||||
|
|
||||||
|
aCur = 0;
|
||||||
|
bCur = 0;
|
||||||
|
aEnd = aSize;
|
||||||
|
bEnd = bSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCurA() {
|
||||||
|
return aCur;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCurB() {
|
||||||
|
return bCur;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incA() {
|
||||||
|
aCur++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incB() {
|
||||||
|
bCur++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incBoth() {
|
||||||
|
incA();
|
||||||
|
incB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUnmodifiedLine() {
|
||||||
|
return !isDeletedA() && !isInsertedB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeletedA() {
|
||||||
|
return curEdit.getBeginA() <= aCur && aCur < curEdit.getEndA();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInsertedB() {
|
||||||
|
return curEdit.getBeginB() <= bCur && bCur < curEdit.getEndB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean next() {
|
||||||
|
if (!in(curEdit)) {
|
||||||
|
if (curIdx < edits.size() - 1) {
|
||||||
|
curEdit = edits.get(++curIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aCur < aEnd || bCur < bEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean in(Edit edit) {
|
||||||
|
return aCur < edit.getEndA() || bCur < edit.getEndB();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,149 +0,0 @@
|
|||||||
// Copyright (C) 2009 The Android Open Source Project
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package com.google.gerrit.prettify.common;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import org.eclipse.jgit.diff.Edit;
|
|
||||||
|
|
||||||
public class EditList {
|
|
||||||
private final List<Edit> edits;
|
|
||||||
private final int context;
|
|
||||||
private final int aSize;
|
|
||||||
private final int bSize;
|
|
||||||
|
|
||||||
public EditList(final List<Edit> edits, int contextLines, int aSize, int bSize) {
|
|
||||||
this.edits = edits;
|
|
||||||
this.context = contextLines;
|
|
||||||
this.aSize = aSize;
|
|
||||||
this.bSize = bSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterable<Hunk> getHunks() {
|
|
||||||
return () ->
|
|
||||||
new Iterator<Hunk>() {
|
|
||||||
private int curIdx;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
return curIdx < edits.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Hunk next() {
|
|
||||||
final int c = curIdx;
|
|
||||||
final int e = findCombinedEnd(c);
|
|
||||||
curIdx = e + 1;
|
|
||||||
return new Hunk(c, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private int findCombinedEnd(int i) {
|
|
||||||
int end = i + 1;
|
|
||||||
while (end < edits.size() && (combineA(end) || combineB(end))) {
|
|
||||||
end++;
|
|
||||||
}
|
|
||||||
return end - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean combineA(int i) {
|
|
||||||
final Edit s = edits.get(i);
|
|
||||||
final Edit e = edits.get(i - 1);
|
|
||||||
// + 1 to prevent '... skipping 1 common line ...' messages.
|
|
||||||
return s.getBeginA() - e.getEndA() <= 2 * context + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean combineB(int i) {
|
|
||||||
final int s = edits.get(i).getBeginB();
|
|
||||||
final int e = edits.get(i - 1).getEndB();
|
|
||||||
// + 1 to prevent '... skipping 1 common line ...' messages.
|
|
||||||
return s - e <= 2 * context + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Hunk {
|
|
||||||
private int curIdx;
|
|
||||||
private Edit curEdit;
|
|
||||||
private final int endIdx;
|
|
||||||
|
|
||||||
private int aCur;
|
|
||||||
private int bCur;
|
|
||||||
private final int aEnd;
|
|
||||||
private final int bEnd;
|
|
||||||
|
|
||||||
private Hunk(int ci, int ei) {
|
|
||||||
curIdx = ci;
|
|
||||||
endIdx = ei;
|
|
||||||
curEdit = edits.get(curIdx);
|
|
||||||
Edit endEdit = edits.get(endIdx);
|
|
||||||
|
|
||||||
aCur = Math.max(0, curEdit.getBeginA() - context);
|
|
||||||
bCur = Math.max(0, curEdit.getBeginB() - context);
|
|
||||||
aEnd = Math.min(aSize, endEdit.getEndA() + context);
|
|
||||||
bEnd = Math.min(bSize, endEdit.getEndB() + context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCurA() {
|
|
||||||
return aCur;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCurB() {
|
|
||||||
return bCur;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void incA() {
|
|
||||||
aCur++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void incB() {
|
|
||||||
bCur++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void incBoth() {
|
|
||||||
incA();
|
|
||||||
incB();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isContextLine() {
|
|
||||||
return !isDeletedA() && !isInsertedB();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDeletedA() {
|
|
||||||
return curEdit.getBeginA() <= aCur && aCur < curEdit.getEndA();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isInsertedB() {
|
|
||||||
return curEdit.getBeginB() <= bCur && bCur < curEdit.getEndB();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean next() {
|
|
||||||
if (!in(curEdit)) {
|
|
||||||
if (curIdx < endIdx) {
|
|
||||||
curEdit = edits.get(++curIdx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return aCur < aEnd || bCur < bEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean in(Edit edit) {
|
|
||||||
return aCur < edit.getEndA() || bCur < edit.getEndB();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -123,9 +123,6 @@ class FileApiImpl implements FileApi {
|
|||||||
if (r.getBase() != null) {
|
if (r.getBase() != null) {
|
||||||
getDiff.setBase(r.getBase());
|
getDiff.setBase(r.getBase());
|
||||||
}
|
}
|
||||||
if (r.getContext() != null) {
|
|
||||||
getDiff.setContext(r.getContext());
|
|
||||||
}
|
|
||||||
if (r.getIntraline() != null) {
|
if (r.getIntraline() != null) {
|
||||||
getDiff.setIntraline(r.getIntraline());
|
getDiff.setIntraline(r.getIntraline());
|
||||||
}
|
}
|
||||||
|
@@ -14,28 +14,19 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.patch;
|
package com.google.gerrit.server.patch;
|
||||||
|
|
||||||
import static java.util.Comparator.comparing;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.gerrit.common.data.CommentDetail;
|
|
||||||
import com.google.gerrit.entities.Comment;
|
|
||||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
||||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
|
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
|
||||||
import com.google.gerrit.jgit.diff.ReplaceEdit;
|
import com.google.gerrit.jgit.diff.ReplaceEdit;
|
||||||
import com.google.gerrit.prettify.common.EditList;
|
import com.google.gerrit.prettify.common.EditHunk;
|
||||||
import com.google.gerrit.prettify.common.SparseFileContent;
|
import com.google.gerrit.prettify.common.SparseFileContent;
|
||||||
import com.google.gerrit.prettify.common.SparseFileContentBuilder;
|
import com.google.gerrit.prettify.common.SparseFileContentBuilder;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.eclipse.jgit.diff.Edit;
|
import org.eclipse.jgit.diff.Edit;
|
||||||
|
|
||||||
/** Collects all lines and their content to be displayed in diff view. */
|
/** Collects all lines and their content to be displayed in diff view. */
|
||||||
class DiffContentCalculator {
|
class DiffContentCalculator {
|
||||||
private static final int MAX_CONTEXT = 5000000;
|
|
||||||
|
|
||||||
private static final Comparator<Edit> EDIT_SORT = comparing(Edit::getBeginA);
|
|
||||||
|
|
||||||
private final DiffPreferencesInfo diffPrefs;
|
private final DiffPreferencesInfo diffPrefs;
|
||||||
|
|
||||||
DiffContentCalculator(DiffPreferencesInfo diffPrefs) {
|
DiffContentCalculator(DiffPreferencesInfo diffPrefs) {
|
||||||
@@ -60,13 +51,11 @@ class DiffContentCalculator {
|
|||||||
* @param srcA Original text content
|
* @param srcA Original text content
|
||||||
* @param srcB New text content
|
* @param srcB New text content
|
||||||
* @param edits List of edits which was applied to srcA to produce srcB
|
* @param edits List of edits which was applied to srcA to produce srcB
|
||||||
* @param comments Existing comments for srcA and srcB
|
|
||||||
* @return an instance of {@link DiffCalculatorResult}.
|
* @return an instance of {@link DiffCalculatorResult}.
|
||||||
*/
|
*/
|
||||||
DiffCalculatorResult calculateDiffContent(
|
DiffCalculatorResult calculateDiffContent(
|
||||||
TextSource srcA, TextSource srcB, ImmutableList<Edit> edits, CommentDetail comments) {
|
TextSource srcA, TextSource srcB, ImmutableList<Edit> edits) {
|
||||||
int context = getContext();
|
if (srcA.src == srcB.src && edits.isEmpty()) {
|
||||||
if (srcA.src == srcB.src && srcA.size() <= context && edits.isEmpty()) {
|
|
||||||
// Odd special case; the files are identical (100% rename or copy)
|
// Odd special case; the files are identical (100% rename or copy)
|
||||||
// and the user has asked for context that is larger than the file.
|
// and the user has asked for context that is larger than the file.
|
||||||
// Send them the entire file, with an empty edit after the last line.
|
// Send them the entire file, with an empty edit after the last line.
|
||||||
@@ -80,43 +69,13 @@ class DiffContentCalculator {
|
|||||||
Edit emptyEdit = new Edit(srcA.size(), srcA.size());
|
Edit emptyEdit = new Edit(srcA.size(), srcA.size());
|
||||||
return new DiffCalculatorResult(diffContent, ImmutableList.of(emptyEdit));
|
return new DiffCalculatorResult(diffContent, ImmutableList.of(emptyEdit));
|
||||||
}
|
}
|
||||||
ImmutableList.Builder<Edit> builder = ImmutableList.builder();
|
ImmutableList<Edit> sortedEdits = correctForDifferencesInNewlineAtEnd(srcA, srcB, edits);
|
||||||
|
|
||||||
builder.addAll(correctForDifferencesInNewlineAtEnd(srcA, srcB, edits));
|
|
||||||
|
|
||||||
boolean nonsortedEdits = false;
|
|
||||||
if (comments != null) {
|
|
||||||
ImmutableList<Edit> commentEdits = ensureCommentsVisible(comments, edits);
|
|
||||||
builder.addAll(commentEdits);
|
|
||||||
nonsortedEdits = !commentEdits.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImmutableList<Edit> sortedEdits = builder.build();
|
|
||||||
if (nonsortedEdits) {
|
|
||||||
sortedEdits = ImmutableList.sortedCopyOf(EDIT_SORT, sortedEdits);
|
|
||||||
}
|
|
||||||
|
|
||||||
// In order to expand the skipped common lines or syntax highlight the
|
|
||||||
// file properly we need to give the client the complete file contents.
|
|
||||||
// So force our context temporarily to the complete file size.
|
|
||||||
//
|
|
||||||
DiffContent diffContent =
|
DiffContent diffContent =
|
||||||
packContent(
|
packContent(srcA, srcB, diffPrefs.ignoreWhitespace != Whitespace.IGNORE_NONE, sortedEdits);
|
||||||
srcA,
|
|
||||||
srcB,
|
|
||||||
diffPrefs.ignoreWhitespace != Whitespace.IGNORE_NONE,
|
|
||||||
sortedEdits,
|
|
||||||
MAX_CONTEXT);
|
|
||||||
return new DiffCalculatorResult(diffContent, sortedEdits);
|
return new DiffCalculatorResult(diffContent, sortedEdits);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getContext() {
|
|
||||||
if (diffPrefs.context == DiffPreferencesInfo.WHOLE_FILE_CONTEXT) {
|
|
||||||
return MAX_CONTEXT;
|
|
||||||
}
|
|
||||||
return Math.min(diffPrefs.context, MAX_CONTEXT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImmutableList<Edit> correctForDifferencesInNewlineAtEnd(
|
private ImmutableList<Edit> correctForDifferencesInNewlineAtEnd(
|
||||||
TextSource a, TextSource b, ImmutableList<Edit> edits) {
|
TextSource a, TextSource b, ImmutableList<Edit> edits) {
|
||||||
// a.src.size() is the size ignoring a newline at the end whereas a.size() considers it.
|
// a.src.size() is the size ignoring a newline at the end whereas a.size() considers it.
|
||||||
@@ -205,128 +164,14 @@ class DiffContentCalculator {
|
|||||||
return a.src.isMissingNewlineAtEnd() && !b.src.isMissingNewlineAtEnd();
|
return a.src.isMissingNewlineAtEnd() && !b.src.isMissingNewlineAtEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImmutableList<Edit> ensureCommentsVisible(
|
|
||||||
CommentDetail comments, ImmutableList<Edit> edits) {
|
|
||||||
if (comments.getCommentsA().isEmpty() && comments.getCommentsB().isEmpty()) {
|
|
||||||
// No comments, no additional dummy edits are required.
|
|
||||||
//
|
|
||||||
return ImmutableList.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct empty Edit blocks around each location where a comment is.
|
|
||||||
// This will force the later packContent method to include the regions
|
|
||||||
// containing comments, potentially combining those regions together if
|
|
||||||
// they have overlapping contexts. UI renders will also be able to make
|
|
||||||
// correct hunks from this, but because the Edit is empty they will not
|
|
||||||
// style it specially.
|
|
||||||
//
|
|
||||||
final ImmutableList.Builder<Edit> commmentEdits = ImmutableList.builder();
|
|
||||||
int lastLine;
|
|
||||||
|
|
||||||
lastLine = -1;
|
|
||||||
for (Comment c : comments.getCommentsA()) {
|
|
||||||
final int a = c.lineNbr;
|
|
||||||
if (lastLine != a) {
|
|
||||||
final int b = mapA2B(a - 1, edits);
|
|
||||||
if (0 <= b) {
|
|
||||||
getNewEditForComment(edits, new Edit(a - 1, b)).ifPresent(commmentEdits::add);
|
|
||||||
}
|
|
||||||
lastLine = a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lastLine = -1;
|
|
||||||
for (Comment c : comments.getCommentsB()) {
|
|
||||||
int b = c.lineNbr;
|
|
||||||
if (lastLine != b) {
|
|
||||||
final int a = mapB2A(b - 1, edits);
|
|
||||||
if (0 <= a) {
|
|
||||||
getNewEditForComment(edits, new Edit(a, b - 1)).ifPresent(commmentEdits::add);
|
|
||||||
}
|
|
||||||
lastLine = b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return commmentEdits.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<Edit> getNewEditForComment(ImmutableList<Edit> edits, Edit toAdd) {
|
|
||||||
final int a = toAdd.getBeginA();
|
|
||||||
final int b = toAdd.getBeginB();
|
|
||||||
for (Edit e : edits) {
|
|
||||||
if (e.getBeginA() <= a && a <= e.getEndA()) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
if (e.getBeginB() <= b && b <= e.getEndB()) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Optional.of(toAdd);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int mapA2B(int a, ImmutableList<Edit> edits) {
|
|
||||||
if (edits.isEmpty()) {
|
|
||||||
// Magic special case of an unmodified file.
|
|
||||||
//
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < edits.size(); i++) {
|
|
||||||
final Edit e = edits.get(i);
|
|
||||||
if (a < e.getBeginA()) {
|
|
||||||
if (i == 0) {
|
|
||||||
// Special case of context at start of file.
|
|
||||||
//
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
return e.getBeginB() - (e.getBeginA() - a);
|
|
||||||
}
|
|
||||||
if (e.getBeginA() <= a && a <= e.getEndA()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Edit last = edits.get(edits.size() - 1);
|
|
||||||
return last.getEndB() + (a - last.getEndA());
|
|
||||||
}
|
|
||||||
|
|
||||||
private int mapB2A(int b, ImmutableList<Edit> edits) {
|
|
||||||
if (edits.isEmpty()) {
|
|
||||||
// Magic special case of an unmodified file.
|
|
||||||
//
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < edits.size(); i++) {
|
|
||||||
final Edit e = edits.get(i);
|
|
||||||
if (b < e.getBeginB()) {
|
|
||||||
if (i == 0) {
|
|
||||||
// Special case of context at start of file.
|
|
||||||
//
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
return e.getBeginA() - (e.getBeginB() - b);
|
|
||||||
}
|
|
||||||
if (e.getBeginB() <= b && b <= e.getEndB()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Edit last = edits.get(edits.size() - 1);
|
|
||||||
return last.getEndA() + (b - last.getEndB());
|
|
||||||
}
|
|
||||||
|
|
||||||
private DiffContent packContent(
|
private DiffContent packContent(
|
||||||
TextSource a,
|
TextSource a, TextSource b, boolean ignoredWhitespace, ImmutableList<Edit> edits) {
|
||||||
TextSource b,
|
|
||||||
boolean ignoredWhitespace,
|
|
||||||
ImmutableList<Edit> edits,
|
|
||||||
int context) {
|
|
||||||
SparseFileContentBuilder diffA = new SparseFileContentBuilder(a.size());
|
SparseFileContentBuilder diffA = new SparseFileContentBuilder(a.size());
|
||||||
SparseFileContentBuilder diffB = new SparseFileContentBuilder(b.size());
|
SparseFileContentBuilder diffB = new SparseFileContentBuilder(b.size());
|
||||||
EditList list = new EditList(edits, context, a.size(), b.size());
|
if (!edits.isEmpty()) {
|
||||||
for (EditList.Hunk hunk : list.getHunks()) {
|
EditHunk hunk = new EditHunk(edits, a.size(), b.size());
|
||||||
while (hunk.next()) {
|
while (hunk.next()) {
|
||||||
if (hunk.isContextLine()) {
|
if (hunk.isUnmodifiedLine()) {
|
||||||
String lineA = a.getSourceLine(hunk.getCurA());
|
String lineA = a.getSourceLine(hunk.getCurA());
|
||||||
diffA.addLine(hunk.getCurA(), lineA);
|
diffA.addLine(hunk.getCurA(), lineA);
|
||||||
|
|
||||||
|
@@ -18,7 +18,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.gerrit.common.data.CommentDetail;
|
|
||||||
import com.google.gerrit.common.data.PatchScript;
|
import com.google.gerrit.common.data.PatchScript;
|
||||||
import com.google.gerrit.common.data.PatchScript.DisplayMethod;
|
import com.google.gerrit.common.data.PatchScript.DisplayMethod;
|
||||||
import com.google.gerrit.entities.FixReplacement;
|
import com.google.gerrit.entities.FixReplacement;
|
||||||
@@ -69,8 +68,7 @@ class PatchScriptBuilder {
|
|||||||
intralineDiffCalculator = calculator;
|
intralineDiffCalculator = calculator;
|
||||||
}
|
}
|
||||||
|
|
||||||
PatchScript toPatchScript(
|
PatchScript toPatchScript(Repository git, PatchList list, PatchListEntry content)
|
||||||
Repository git, PatchList list, PatchListEntry content, CommentDetail comments)
|
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
PatchFileChange change =
|
PatchFileChange change =
|
||||||
@@ -86,7 +84,7 @@ class PatchScriptBuilder {
|
|||||||
ResolvedSides sides =
|
ResolvedSides sides =
|
||||||
resolveSides(
|
resolveSides(
|
||||||
git, sidesResolver, oldName(change), newName(change), list.getOldId(), list.getNewId());
|
git, sidesResolver, oldName(change), newName(change), list.getOldId(), list.getNewId());
|
||||||
return build(sides.a, sides.b, change, comments);
|
return build(sides.a, sides.b, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResolvedSides resolveSides(
|
private ResolvedSides resolveSides(
|
||||||
@@ -136,7 +134,7 @@ class PatchScriptBuilder {
|
|||||||
ChangeType.MODIFIED,
|
ChangeType.MODIFIED,
|
||||||
PatchType.UNIFIED);
|
PatchType.UNIFIED);
|
||||||
|
|
||||||
return build(a, b, change, null);
|
return build(a, b, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PatchSide resolveSideA(
|
private PatchSide resolveSideA(
|
||||||
@@ -147,9 +145,7 @@ class PatchScriptBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PatchScript build(
|
private PatchScript build(PatchSide a, PatchSide b, PatchFileChange content) {
|
||||||
PatchSide a, PatchSide b, PatchFileChange content, CommentDetail comments) {
|
|
||||||
|
|
||||||
ImmutableList<Edit> contentEdits = content.getEdits();
|
ImmutableList<Edit> contentEdits = content.getEdits();
|
||||||
ImmutableSet<Edit> editsDueToRebase = content.getEditsDueToRebase();
|
ImmutableSet<Edit> editsDueToRebase = content.getEditsDueToRebase();
|
||||||
|
|
||||||
@@ -163,8 +159,7 @@ class PatchScriptBuilder {
|
|||||||
ImmutableList<Edit> finalEdits = intralineResult.edits.orElse(contentEdits);
|
ImmutableList<Edit> finalEdits = intralineResult.edits.orElse(contentEdits);
|
||||||
DiffContentCalculator calculator = new DiffContentCalculator(diffPrefs);
|
DiffContentCalculator calculator = new DiffContentCalculator(diffPrefs);
|
||||||
DiffCalculatorResult diffCalculatorResult =
|
DiffCalculatorResult diffCalculatorResult =
|
||||||
calculator.calculateDiffContent(
|
calculator.calculateDiffContent(new TextSource(a.src), new TextSource(b.src), finalEdits);
|
||||||
new TextSource(a.src), new TextSource(b.src), finalEdits, comments);
|
|
||||||
|
|
||||||
return new PatchScript(
|
return new PatchScript(
|
||||||
content.getChangeType(),
|
content.getChangeType(),
|
||||||
|
@@ -20,19 +20,13 @@ import static com.google.common.base.Preconditions.checkState;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.flogger.FluentLogger;
|
import com.google.common.flogger.FluentLogger;
|
||||||
import com.google.gerrit.common.Nullable;
|
import com.google.gerrit.common.Nullable;
|
||||||
import com.google.gerrit.common.data.CommentDetail;
|
|
||||||
import com.google.gerrit.common.data.PatchScript;
|
import com.google.gerrit.common.data.PatchScript;
|
||||||
import com.google.gerrit.entities.Account;
|
|
||||||
import com.google.gerrit.entities.Change;
|
import com.google.gerrit.entities.Change;
|
||||||
import com.google.gerrit.entities.HumanComment;
|
|
||||||
import com.google.gerrit.entities.Patch.ChangeType;
|
|
||||||
import com.google.gerrit.entities.PatchSet;
|
import com.google.gerrit.entities.PatchSet;
|
||||||
import com.google.gerrit.entities.Project;
|
import com.google.gerrit.entities.Project;
|
||||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
||||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
|
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
import com.google.gerrit.server.CommentsUtil;
|
|
||||||
import com.google.gerrit.server.CurrentUser;
|
|
||||||
import com.google.gerrit.server.PatchSetUtil;
|
import com.google.gerrit.server.PatchSetUtil;
|
||||||
import com.google.gerrit.server.edit.ChangeEdit;
|
import com.google.gerrit.server.edit.ChangeEdit;
|
||||||
import com.google.gerrit.server.edit.ChangeEditUtil;
|
import com.google.gerrit.server.edit.ChangeEditUtil;
|
||||||
@@ -84,7 +78,6 @@ public class PatchScriptFactory implements Callable<PatchScript> {
|
|||||||
private final PatchSetUtil psUtil;
|
private final PatchSetUtil psUtil;
|
||||||
private final Provider<PatchScriptBuilder> builderFactory;
|
private final Provider<PatchScriptBuilder> builderFactory;
|
||||||
private final PatchListCache patchListCache;
|
private final PatchListCache patchListCache;
|
||||||
private final CommentsUtil commentsUtil;
|
|
||||||
|
|
||||||
private final String fileName;
|
private final String fileName;
|
||||||
@Nullable private final PatchSet.Id psa;
|
@Nullable private final PatchSet.Id psa;
|
||||||
@@ -92,12 +85,10 @@ public class PatchScriptFactory implements Callable<PatchScript> {
|
|||||||
private final PatchSet.Id psb;
|
private final PatchSet.Id psb;
|
||||||
private final DiffPreferencesInfo diffPrefs;
|
private final DiffPreferencesInfo diffPrefs;
|
||||||
private final ChangeEditUtil editReader;
|
private final ChangeEditUtil editReader;
|
||||||
private final Provider<CurrentUser> userProvider;
|
|
||||||
private final PermissionBackend permissionBackend;
|
private final PermissionBackend permissionBackend;
|
||||||
private final ProjectCache projectCache;
|
private final ProjectCache projectCache;
|
||||||
|
|
||||||
private final Change.Id changeId;
|
private final Change.Id changeId;
|
||||||
private boolean loadComments = true;
|
|
||||||
|
|
||||||
private ChangeNotes notes;
|
private ChangeNotes notes;
|
||||||
|
|
||||||
@@ -107,9 +98,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
|
|||||||
PatchSetUtil psUtil,
|
PatchSetUtil psUtil,
|
||||||
Provider<PatchScriptBuilder> builderFactory,
|
Provider<PatchScriptBuilder> builderFactory,
|
||||||
PatchListCache patchListCache,
|
PatchListCache patchListCache,
|
||||||
CommentsUtil commentsUtil,
|
|
||||||
ChangeEditUtil editReader,
|
ChangeEditUtil editReader,
|
||||||
Provider<CurrentUser> userProvider,
|
|
||||||
PermissionBackend permissionBackend,
|
PermissionBackend permissionBackend,
|
||||||
ProjectCache projectCache,
|
ProjectCache projectCache,
|
||||||
@Assisted ChangeNotes notes,
|
@Assisted ChangeNotes notes,
|
||||||
@@ -122,9 +111,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
|
|||||||
this.builderFactory = builderFactory;
|
this.builderFactory = builderFactory;
|
||||||
this.patchListCache = patchListCache;
|
this.patchListCache = patchListCache;
|
||||||
this.notes = notes;
|
this.notes = notes;
|
||||||
this.commentsUtil = commentsUtil;
|
|
||||||
this.editReader = editReader;
|
this.editReader = editReader;
|
||||||
this.userProvider = userProvider;
|
|
||||||
this.permissionBackend = permissionBackend;
|
this.permissionBackend = permissionBackend;
|
||||||
this.projectCache = projectCache;
|
this.projectCache = projectCache;
|
||||||
|
|
||||||
@@ -143,9 +130,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
|
|||||||
PatchSetUtil psUtil,
|
PatchSetUtil psUtil,
|
||||||
Provider<PatchScriptBuilder> builderFactory,
|
Provider<PatchScriptBuilder> builderFactory,
|
||||||
PatchListCache patchListCache,
|
PatchListCache patchListCache,
|
||||||
CommentsUtil commentsUtil,
|
|
||||||
ChangeEditUtil editReader,
|
ChangeEditUtil editReader,
|
||||||
Provider<CurrentUser> userProvider,
|
|
||||||
PermissionBackend permissionBackend,
|
PermissionBackend permissionBackend,
|
||||||
ProjectCache projectCache,
|
ProjectCache projectCache,
|
||||||
@Assisted ChangeNotes notes,
|
@Assisted ChangeNotes notes,
|
||||||
@@ -158,9 +143,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
|
|||||||
this.builderFactory = builderFactory;
|
this.builderFactory = builderFactory;
|
||||||
this.patchListCache = patchListCache;
|
this.patchListCache = patchListCache;
|
||||||
this.notes = notes;
|
this.notes = notes;
|
||||||
this.commentsUtil = commentsUtil;
|
|
||||||
this.editReader = editReader;
|
this.editReader = editReader;
|
||||||
this.userProvider = userProvider;
|
|
||||||
this.permissionBackend = permissionBackend;
|
this.permissionBackend = permissionBackend;
|
||||||
this.projectCache = projectCache;
|
this.projectCache = projectCache;
|
||||||
|
|
||||||
@@ -174,10 +157,6 @@ public class PatchScriptFactory implements Callable<PatchScript> {
|
|||||||
checkArgument(parentNum >= 0, "parentNum must be >= 0");
|
checkArgument(parentNum >= 0, "parentNum must be >= 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLoadComments(boolean load) {
|
|
||||||
loadComments = load;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PatchScript call()
|
public PatchScript call()
|
||||||
throws LargeObjectException, AuthException, InvalidChangeOperationException, IOException,
|
throws LargeObjectException, AuthException, InvalidChangeOperationException, IOException,
|
||||||
@@ -218,9 +197,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
|
|||||||
final PatchScriptBuilder b = newBuilder();
|
final PatchScriptBuilder b = newBuilder();
|
||||||
final PatchListEntry content = list.get(fileName);
|
final PatchListEntry content = list.get(fileName);
|
||||||
|
|
||||||
Optional<CommentDetail> comments = loadComments(content, changeEdit);
|
return b.toPatchScript(git, list, content);
|
||||||
|
|
||||||
return b.toPatchScript(git, list, content, comments.orElse(null));
|
|
||||||
} catch (PatchListNotAvailableException e) {
|
} catch (PatchListNotAvailableException e) {
|
||||||
throw new NoSuchChangeException(changeId, e);
|
throw new NoSuchChangeException(changeId, e);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -238,14 +215,6 @@ public class PatchScriptFactory implements Callable<PatchScript> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<CommentDetail> loadComments(PatchListEntry content, boolean changeEdit) {
|
|
||||||
if (!loadComments) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
return new CommentsLoader(psa, psb, userProvider, notes, commentsUtil)
|
|
||||||
.load(changeEdit, content.getChangeType(), content.getOldName(), content.getNewName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<ObjectId> getAId() {
|
private Optional<ObjectId> getAId() {
|
||||||
if (psa == null) {
|
if (psa == null) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
@@ -300,99 +269,6 @@ public class PatchScriptFactory implements Callable<PatchScript> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class CommentsLoader {
|
|
||||||
private final PatchSet.Id psa;
|
|
||||||
private final PatchSet.Id psb;
|
|
||||||
private final Provider<CurrentUser> userProvider;
|
|
||||||
private final ChangeNotes notes;
|
|
||||||
private final CommentsUtil commentsUtil;
|
|
||||||
private CommentDetail comments;
|
|
||||||
|
|
||||||
CommentsLoader(
|
|
||||||
PatchSet.Id psa,
|
|
||||||
PatchSet.Id psb,
|
|
||||||
Provider<CurrentUser> userProvider,
|
|
||||||
ChangeNotes notes,
|
|
||||||
CommentsUtil commentsUtil) {
|
|
||||||
this.psa = psa;
|
|
||||||
this.psb = psb;
|
|
||||||
this.userProvider = userProvider;
|
|
||||||
this.notes = notes;
|
|
||||||
this.commentsUtil = commentsUtil;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<CommentDetail> load(
|
|
||||||
boolean changeEdit, ChangeType changeType, String oldName, String newName) {
|
|
||||||
// TODO: Implement this method with CommentDetailBuilder (this class doesn't exists yet).
|
|
||||||
// This is a legacy code which create final object and populate it and then returns it.
|
|
||||||
if (changeEdit) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
comments = new CommentDetail(psa, psb);
|
|
||||||
switch (changeType) {
|
|
||||||
case ADDED:
|
|
||||||
case MODIFIED:
|
|
||||||
loadPublished(newName);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DELETED:
|
|
||||||
loadPublished(newName);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case COPIED:
|
|
||||||
case RENAMED:
|
|
||||||
if (psa != null) {
|
|
||||||
loadPublished(oldName);
|
|
||||||
}
|
|
||||||
loadPublished(newName);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REWRITE:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentUser user = userProvider.get();
|
|
||||||
if (user.isIdentifiedUser()) {
|
|
||||||
Account.Id me = user.getAccountId();
|
|
||||||
switch (changeType) {
|
|
||||||
case ADDED:
|
|
||||||
case MODIFIED:
|
|
||||||
loadDrafts(me, newName);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DELETED:
|
|
||||||
loadDrafts(me, newName);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case COPIED:
|
|
||||||
case RENAMED:
|
|
||||||
if (psa != null) {
|
|
||||||
loadDrafts(me, oldName);
|
|
||||||
}
|
|
||||||
loadDrafts(me, newName);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REWRITE:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Optional.of(comments);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadPublished(String file) {
|
|
||||||
for (HumanComment c : commentsUtil.publishedByChangeFile(notes, file)) {
|
|
||||||
comments.include(notes.getChangeId(), c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadDrafts(Account.Id me, String file) {
|
|
||||||
for (HumanComment c : commentsUtil.draftByChangeFileAuthor(notes, file, me)) {
|
|
||||||
comments.include(notes.getChangeId(), c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class IntraLineDiffCalculator
|
private static class IntraLineDiffCalculator
|
||||||
implements PatchScriptBuilder.IntraLineDiffCalculator {
|
implements PatchScriptBuilder.IntraLineDiffCalculator {
|
||||||
|
|
||||||
|
@@ -15,7 +15,6 @@
|
|||||||
package com.google.gerrit.server.restapi.change;
|
package com.google.gerrit.server.restapi.change;
|
||||||
|
|
||||||
import static com.google.gerrit.server.project.ProjectCache.illegalState;
|
import static com.google.gerrit.server.project.ProjectCache.illegalState;
|
||||||
import static com.google.gerrit.util.cli.Localizable.localizable;
|
|
||||||
|
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
@@ -54,9 +53,7 @@ import com.google.gerrit.server.project.ProjectState;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.kohsuke.args4j.CmdLineException;
|
|
||||||
import org.kohsuke.args4j.CmdLineParser;
|
import org.kohsuke.args4j.CmdLineParser;
|
||||||
import org.kohsuke.args4j.NamedOptionDef;
|
|
||||||
import org.kohsuke.args4j.Option;
|
import org.kohsuke.args4j.Option;
|
||||||
import org.kohsuke.args4j.OptionDef;
|
import org.kohsuke.args4j.OptionDef;
|
||||||
import org.kohsuke.args4j.spi.OptionHandler;
|
import org.kohsuke.args4j.spi.OptionHandler;
|
||||||
@@ -84,8 +81,9 @@ public class GetDiff implements RestReadView<FileResource> {
|
|||||||
@Option(name = "--whitespace")
|
@Option(name = "--whitespace")
|
||||||
Whitespace whitespace;
|
Whitespace whitespace;
|
||||||
|
|
||||||
|
// TODO(hiesel): Remove parameter when not used by callers (e.g. frontend) anymore.
|
||||||
@Option(name = "--context", handler = ContextOptionHandler.class)
|
@Option(name = "--context", handler = ContextOptionHandler.class)
|
||||||
int context = DiffPreferencesInfo.DEFAULT_CONTEXT;
|
int context;
|
||||||
|
|
||||||
@Option(name = "--intraline")
|
@Option(name = "--intraline")
|
||||||
boolean intraline;
|
boolean intraline;
|
||||||
@@ -114,11 +112,10 @@ public class GetDiff implements RestReadView<FileResource> {
|
|||||||
} else {
|
} else {
|
||||||
prefs.ignoreWhitespace = Whitespace.IGNORE_LEADING_AND_TRAILING;
|
prefs.ignoreWhitespace = Whitespace.IGNORE_LEADING_AND_TRAILING;
|
||||||
}
|
}
|
||||||
prefs.context = context;
|
|
||||||
prefs.intralineDifference = intraline;
|
prefs.intralineDifference = intraline;
|
||||||
logger.atFine().log(
|
logger.atFine().log(
|
||||||
"diff preferences: ignoreWhitespace = %s, context = %s, intralineDifference = %s",
|
"diff preferences: ignoreWhitespace = %s, intralineDifference = %s",
|
||||||
prefs.ignoreWhitespace, prefs.context, prefs.intralineDifference);
|
prefs.ignoreWhitespace, prefs.intralineDifference);
|
||||||
|
|
||||||
PatchScriptFactory psf;
|
PatchScriptFactory psf;
|
||||||
PatchSet basePatchSet = null;
|
PatchSet basePatchSet = null;
|
||||||
@@ -143,7 +140,6 @@ public class GetDiff implements RestReadView<FileResource> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
psf.setLoadComments(context != DiffPreferencesInfo.WHOLE_FILE_CONTEXT);
|
|
||||||
PatchScript ps = psf.call();
|
PatchScript ps = psf.call();
|
||||||
Project.NameKey projectName = resource.getRevision().getChange().getProject();
|
Project.NameKey projectName = resource.getRevision().getChange().getProject();
|
||||||
ProjectState state = projectCache.get(projectName).orElseThrow(illegalState(projectName));
|
ProjectState state = projectCache.get(projectName).orElseThrow(illegalState(projectName));
|
||||||
@@ -245,11 +241,6 @@ public class GetDiff implements RestReadView<FileResource> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetDiff setContext(int context) {
|
|
||||||
this.context = context;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GetDiff setIntraline(boolean intraline) {
|
public GetDiff setIntraline(boolean intraline) {
|
||||||
this.intraline = intraline;
|
this.intraline = intraline;
|
||||||
return this;
|
return this;
|
||||||
@@ -274,6 +265,7 @@ public class GetDiff implements RestReadView<FileResource> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(hiesel): Remove this class once clients don't send the context parameter anymore.
|
||||||
public static class ContextOptionHandler extends OptionHandler<Short> {
|
public static class ContextOptionHandler extends OptionHandler<Short> {
|
||||||
|
|
||||||
public ContextOptionHandler(CmdLineParser parser, OptionDef option, Setter<Short> setter) {
|
public ContextOptionHandler(CmdLineParser parser, OptionDef option, Setter<Short> setter) {
|
||||||
@@ -281,33 +273,14 @@ public class GetDiff implements RestReadView<FileResource> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int parseArguments(Parameters params) throws CmdLineException {
|
public final int parseArguments(Parameters params) {
|
||||||
final String value = params.getParameter(0);
|
// Return 1 to consume the context parameter.
|
||||||
short context;
|
|
||||||
if ("all".equalsIgnoreCase(value)) {
|
|
||||||
context = DiffPreferencesInfo.WHOLE_FILE_CONTEXT;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
context = Short.parseShort(value, 10);
|
|
||||||
if (context < 0) {
|
|
||||||
throw new NumberFormatException();
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
logger.atFine().withCause(e).log("invalid numeric value");
|
|
||||||
throw new CmdLineException(
|
|
||||||
owner,
|
|
||||||
localizable("\"%s\" is not a valid value for \"%s\""),
|
|
||||||
value,
|
|
||||||
((NamedOptionDef) option).name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setter.addValue(context);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final String getDefaultMetaVariable() {
|
public final String getDefaultMetaVariable() {
|
||||||
return "ALL|# LINES";
|
return "ignored";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -36,16 +36,12 @@ import com.google.gerrit.acceptance.PushOneCommit.Result;
|
|||||||
import com.google.gerrit.common.RawInputUtil;
|
import com.google.gerrit.common.RawInputUtil;
|
||||||
import com.google.gerrit.extensions.api.changes.FileApi;
|
import com.google.gerrit.extensions.api.changes.FileApi;
|
||||||
import com.google.gerrit.extensions.api.changes.RebaseInput;
|
import com.google.gerrit.extensions.api.changes.RebaseInput;
|
||||||
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
|
||||||
import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput;
|
|
||||||
import com.google.gerrit.extensions.client.Comment;
|
|
||||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
||||||
import com.google.gerrit.extensions.common.ChangeType;
|
import com.google.gerrit.extensions.common.ChangeType;
|
||||||
import com.google.gerrit.extensions.common.DiffInfo;
|
import com.google.gerrit.extensions.common.DiffInfo;
|
||||||
import com.google.gerrit.extensions.common.FileInfo;
|
import com.google.gerrit.extensions.common.FileInfo;
|
||||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
|
||||||
import com.google.gerrit.testing.ConfigSuite;
|
import com.google.gerrit.testing.ConfigSuite;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -64,6 +60,7 @@ import org.eclipse.jgit.lib.ObjectId;
|
|||||||
import org.eclipse.jgit.lib.PersonIdent;
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class RevisionDiffIT extends AbstractDaemonTest {
|
public class RevisionDiffIT extends AbstractDaemonTest {
|
||||||
@@ -676,12 +673,6 @@ public class RevisionDiffIT extends AbstractDaemonTest {
|
|||||||
String baseFileContent = FILE_CONTENT.concat("Line 101");
|
String baseFileContent = FILE_CONTENT.concat("Line 101");
|
||||||
ObjectId commit2 = addCommit(commit1, FILE_NAME, baseFileContent);
|
ObjectId commit2 = addCommit(commit1, FILE_NAME, baseFileContent);
|
||||||
rebaseChangeOn(changeId, commit2);
|
rebaseChangeOn(changeId, commit2);
|
||||||
// Add a comment so that file contents are not 'skipped'. To be able to add a comment, touch
|
|
||||||
// (= modify) the file in the change.
|
|
||||||
addModifiedPatchSet(
|
|
||||||
changeId, FILE_NAME, fileContent -> fileContent.replace("Line 2\n", "Line two\n"));
|
|
||||||
CommentInput comment = createCommentInput(3, 0, 4, 0, "Comment to not skip file content.");
|
|
||||||
addCommentTo(changeId, CURRENT, FILE_NAME, comment);
|
|
||||||
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
|
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
|
||||||
String newBaseFileContent = baseFileContent.concat("\n");
|
String newBaseFileContent = baseFileContent.concat("\n");
|
||||||
ObjectId commit3 = addCommit(commit2, FILE_NAME, newBaseFileContent);
|
ObjectId commit3 = addCommit(commit2, FILE_NAME, newBaseFileContent);
|
||||||
@@ -2517,17 +2508,14 @@ public class RevisionDiffIT extends AbstractDaemonTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void diffOfUnmodifiedFileWithWholeFileContextReturnsFileContents() throws Exception {
|
public void diffOfUnmodifiedFileReturnsAllFileContents() throws Exception {
|
||||||
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
|
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
|
||||||
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
|
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
|
||||||
addModifiedPatchSet(
|
addModifiedPatchSet(
|
||||||
changeId, FILE_NAME2, content -> content.replace("2nd line\n", "Second line\n"));
|
changeId, FILE_NAME2, content -> content.replace("2nd line\n", "Second line\n"));
|
||||||
|
|
||||||
DiffInfo diffInfo =
|
DiffInfo diffInfo =
|
||||||
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get();
|
||||||
.withBase(previousPatchSetId)
|
|
||||||
.withContext(DiffPreferencesInfo.WHOLE_FILE_CONTEXT)
|
|
||||||
.get();
|
|
||||||
// We don't list the full file contents here as that is not the focus of this test.
|
// We don't list the full file contents here as that is not the focus of this test.
|
||||||
assertThat(diffInfo)
|
assertThat(diffInfo)
|
||||||
.content()
|
.content()
|
||||||
@@ -2538,40 +2526,16 @@ public class RevisionDiffIT extends AbstractDaemonTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void diffOfUnmodifiedFileWithCommentAndWholeFileContextReturnsFileContents()
|
// TODO(ghareeb): Don't exclude diffs which only contain rebase hunks within the diff caches. Only
|
||||||
throws Exception {
|
// filter such files in the GetFiles REST endpoint.
|
||||||
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
|
@Ignore
|
||||||
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
|
public void diffOfFileWithOnlyRebaseHunksConsideringWhitespaceReturnsFileContents()
|
||||||
CommentInput comment = createCommentInput(2, 0, 3, 0, "Should be 'Line 2'.");
|
|
||||||
addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
|
|
||||||
addModifiedPatchSet(
|
|
||||||
changeId, FILE_NAME2, content -> content.replace("2nd line\n", "Second line\n"));
|
|
||||||
|
|
||||||
DiffInfo diffInfo =
|
|
||||||
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
|
||||||
.withBase(previousPatchSetId)
|
|
||||||
.withContext(DiffPreferencesInfo.WHOLE_FILE_CONTEXT)
|
|
||||||
.get();
|
|
||||||
// We don't list the full file contents here as that is not the focus of this test.
|
|
||||||
assertThat(diffInfo)
|
|
||||||
.content()
|
|
||||||
.element(0)
|
|
||||||
.commonLines()
|
|
||||||
.containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
|
|
||||||
.inOrder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void
|
|
||||||
diffOfFileWithOnlyRebaseHunksAndWithCommentAndConsideringWhitespaceReturnsFileContents()
|
|
||||||
throws Exception {
|
throws Exception {
|
||||||
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
|
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
|
||||||
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
|
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
|
||||||
String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line seventy\n");
|
String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line seventy\n");
|
||||||
ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent);
|
ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent);
|
||||||
rebaseChangeOn(changeId, commit2);
|
rebaseChangeOn(changeId, commit2);
|
||||||
CommentInput comment = createCommentInput(2, 0, 3, 0, "Should be 'Line 2'.");
|
|
||||||
addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
|
|
||||||
|
|
||||||
DiffInfo diffInfo =
|
DiffInfo diffInfo =
|
||||||
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
||||||
@@ -2585,18 +2549,23 @@ public class RevisionDiffIT extends AbstractDaemonTest {
|
|||||||
.commonLines()
|
.commonLines()
|
||||||
.containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
|
.containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
|
||||||
.inOrder();
|
.inOrder();
|
||||||
|
// It's crucial that the line changed in the rebase is reported correctly.
|
||||||
|
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 70");
|
||||||
|
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line seventy");
|
||||||
|
assertThat(diffInfo).content().element(1).isDueToRebase();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void diffOfFileWithOnlyRebaseHunksAndWithCommentAndIgnoringWhitespaceReturnsFileContents()
|
// TODO(ghareeb): Don't exclude diffs which only contain rebase hunks within the diff caches. Only
|
||||||
|
// filter such files in the GetFiles REST endpoint.
|
||||||
|
@Ignore
|
||||||
|
public void diffOfFileWithOnlyRebaseHunksAndIgnoringWhitespaceReturnsFileContents()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
|
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
|
||||||
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
|
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
|
||||||
String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line seventy\n");
|
String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line seventy\n");
|
||||||
ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent);
|
ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent);
|
||||||
rebaseChangeOn(changeId, commit2);
|
rebaseChangeOn(changeId, commit2);
|
||||||
CommentInput comment = createCommentInput(2, 0, 3, 0, "Should be 'Line 2'.");
|
|
||||||
addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
|
|
||||||
|
|
||||||
DiffInfo diffInfo =
|
DiffInfo diffInfo =
|
||||||
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
||||||
@@ -2610,11 +2579,17 @@ public class RevisionDiffIT extends AbstractDaemonTest {
|
|||||||
.commonLines()
|
.commonLines()
|
||||||
.containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
|
.containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
|
||||||
.inOrder();
|
.inOrder();
|
||||||
|
// It's crucial that the line changed in the rebase is reported correctly.
|
||||||
|
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 70");
|
||||||
|
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line seventy");
|
||||||
|
assertThat(diffInfo).content().element(1).isDueToRebase();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void
|
// TODO(ghareeb): Don't exclude diffs which only contain rebase hunks within the diff caches. Only
|
||||||
diffOfFileWithMultilineRebaseHunkAddingNewlineAtEndOfFileAndWithCommentReturnsFileContents()
|
// filter such files in the GetFiles REST endpoint.
|
||||||
|
@Ignore
|
||||||
|
public void diffOfFileWithMultilineRebaseHunkAddingNewlineAtEndOfFileReturnsFileContents()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String baseFileContent = FILE_CONTENT.concat("Line 101");
|
String baseFileContent = FILE_CONTENT.concat("Line 101");
|
||||||
ObjectId commit2 = addCommit(commit1, FILE_NAME, baseFileContent);
|
ObjectId commit2 = addCommit(commit1, FILE_NAME, baseFileContent);
|
||||||
@@ -2624,8 +2599,6 @@ public class RevisionDiffIT extends AbstractDaemonTest {
|
|||||||
String newBaseFileContent = baseFileContent.concat("\nLine 102\nLine 103\n");
|
String newBaseFileContent = baseFileContent.concat("\nLine 102\nLine 103\n");
|
||||||
ObjectId commit3 = addCommit(commit2, FILE_NAME, newBaseFileContent);
|
ObjectId commit3 = addCommit(commit2, FILE_NAME, newBaseFileContent);
|
||||||
rebaseChangeOn(changeId, commit3);
|
rebaseChangeOn(changeId, commit3);
|
||||||
CommentInput comment = createCommentInput(2, 0, 3, 0, "Should be 'Line 2'.");
|
|
||||||
addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
|
|
||||||
|
|
||||||
DiffInfo diffInfo =
|
DiffInfo diffInfo =
|
||||||
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
||||||
@@ -2639,19 +2612,27 @@ public class RevisionDiffIT extends AbstractDaemonTest {
|
|||||||
.commonLines()
|
.commonLines()
|
||||||
.containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
|
.containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
|
||||||
.inOrder();
|
.inOrder();
|
||||||
|
// It's crucial that the lines changed in the rebase are reported correctly.
|
||||||
|
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 101");
|
||||||
|
assertThat(diffInfo)
|
||||||
|
.content()
|
||||||
|
.element(1)
|
||||||
|
.linesOfB()
|
||||||
|
.containsExactly("Line 101", "Line 102", "Line 103", "");
|
||||||
|
assertThat(diffInfo).content().element(1).isDueToRebase();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void
|
// TODO(ghareeb): Don't exclude diffs which only contain rebase hunks within the diff caches. Only
|
||||||
diffOfFileWithMultilineRebaseHunkRemovingNewlineAtEndOfFileAndWithCommentReturnsFileContents()
|
// filter such files in the GetFiles REST endpoint.
|
||||||
|
@Ignore
|
||||||
|
public void diffOfFileWithMultilineRebaseHunkRemovingNewlineAtEndOfFileReturnsFileContents()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
|
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
|
||||||
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
|
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
|
||||||
String newBaseFileContent = FILE_CONTENT.concat("Line 101\nLine 103\nLine 104");
|
String newBaseFileContent = FILE_CONTENT.concat("Line 101\nLine 102\nLine 103");
|
||||||
ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent);
|
ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent);
|
||||||
rebaseChangeOn(changeId, commit2);
|
rebaseChangeOn(changeId, commit2);
|
||||||
CommentInput comment = createCommentInput(2, 0, 3, 0, "Should be 'Line 2'.");
|
|
||||||
addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
|
|
||||||
|
|
||||||
DiffInfo diffInfo =
|
DiffInfo diffInfo =
|
||||||
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
||||||
@@ -2665,6 +2646,14 @@ public class RevisionDiffIT extends AbstractDaemonTest {
|
|||||||
.commonLines()
|
.commonLines()
|
||||||
.containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
|
.containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
|
||||||
.inOrder();
|
.inOrder();
|
||||||
|
// It's crucial that the lines changed in the rebase are reported correctly.
|
||||||
|
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 100", "");
|
||||||
|
assertThat(diffInfo)
|
||||||
|
.content()
|
||||||
|
.element(1)
|
||||||
|
.linesOfB()
|
||||||
|
.containsExactly("Line 100", "Line 101", "Line 102", "Line 103");
|
||||||
|
assertThat(diffInfo).content().element(1).isDueToRebase();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -2674,49 +2663,10 @@ public class RevisionDiffIT extends AbstractDaemonTest {
|
|||||||
DiffInfo diffInfo =
|
DiffInfo diffInfo =
|
||||||
getDiffRequest(changeId, CURRENT, "a_non-existent_file.txt")
|
getDiffRequest(changeId, CURRENT, "a_non-existent_file.txt")
|
||||||
.withBase(initialPatchSetId)
|
.withBase(initialPatchSetId)
|
||||||
.withContext(DiffPreferencesInfo.WHOLE_FILE_CONTEXT)
|
|
||||||
.get();
|
.get();
|
||||||
assertThat(diffInfo).content().isEmpty();
|
assertThat(diffInfo).content().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This behavior is likely a bug. A fix might not be easy as it might break syntax highlighting.
|
|
||||||
// TODO: Fix this issue or remove the broken parameter (at least in the documentation).
|
|
||||||
@Test
|
|
||||||
public void contextParameterIsIgnored() throws Exception {
|
|
||||||
addModifiedPatchSet(
|
|
||||||
changeId, FILE_NAME, content -> content.replace("Line 20\n", "Line twenty\n"));
|
|
||||||
|
|
||||||
DiffInfo diffInfo =
|
|
||||||
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
|
||||||
.withBase(initialPatchSetId)
|
|
||||||
.withContext(5)
|
|
||||||
.get();
|
|
||||||
assertThat(diffInfo).content().element(0).commonLines().hasSize(19);
|
|
||||||
assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 20");
|
|
||||||
assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line twenty");
|
|
||||||
assertThat(diffInfo).content().element(2).commonLines().hasSize(81);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This behavior is likely a bug. A fix might not be easy as it might break syntax highlighting.
|
|
||||||
// TODO: Fix this issue or remove the broken parameter (at least in the documentation).
|
|
||||||
@Test
|
|
||||||
public void contextParameterIsIgnoredForUnmodifiedFileWithComment() throws Exception {
|
|
||||||
addModifiedPatchSet(
|
|
||||||
changeId, FILE_NAME, content -> content.replace("Line 20\n", "Line twenty\n"));
|
|
||||||
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
|
|
||||||
CommentInput comment = createCommentInput(20, 0, 21, 0, "Should be 'Line 20'.");
|
|
||||||
addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
|
|
||||||
addModifiedPatchSet(
|
|
||||||
changeId, FILE_NAME2, content -> content.replace("2nd line\n", "Second line\n"));
|
|
||||||
|
|
||||||
DiffInfo diffInfo =
|
|
||||||
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
|
||||||
.withBase(previousPatchSetId)
|
|
||||||
.withContext(5)
|
|
||||||
.get();
|
|
||||||
assertThat(diffInfo).content().element(0).commonLines().hasSize(101);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestingDiffForOldFileNameOfRenamedFileYieldsReasonableResult() throws Exception {
|
public void requestingDiffForOldFileNameOfRenamedFileYieldsReasonableResult() throws Exception {
|
||||||
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
|
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
|
||||||
@@ -2726,40 +2676,13 @@ public class RevisionDiffIT extends AbstractDaemonTest {
|
|||||||
gApi.changes().id(changeId).edit().publish();
|
gApi.changes().id(changeId).edit().publish();
|
||||||
|
|
||||||
DiffInfo diffInfo =
|
DiffInfo diffInfo =
|
||||||
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
getDiffRequest(changeId, CURRENT, FILE_NAME).withBase(previousPatchSetId).get();
|
||||||
.withBase(previousPatchSetId)
|
|
||||||
.withContext(DiffPreferencesInfo.WHOLE_FILE_CONTEXT)
|
|
||||||
.get();
|
|
||||||
// This behavior has been present in Gerrit for quite some time. It differs from the results
|
// This behavior has been present in Gerrit for quite some time. It differs from the results
|
||||||
// returned for other cases (e.g. requesting the diff with whole file context for an unmodified
|
// returned for other cases (e.g. requesting the diff for an unmodified file; requesting the
|
||||||
// file; requesting the diff with whole file context for a non-existent file). However, it's not
|
// diff for a non-existent file). After a rename, the original file doesn't exist anymore.
|
||||||
// completely clear what should be returned. The closest would be the result of a file deletion
|
// Hence, the most reasonable thing would be to match the behavior of requesting the diff for a
|
||||||
// but that might also be misleading for users as actually a file rename occurred. In fact,
|
// non-existent file, which returns an empty diff.
|
||||||
// requesting the diff result for the old file name of a renamed file is not a reasonable use
|
// This test at least guarantees that we don't run into an internal error.
|
||||||
// case at all. We at least guarantee that we don't run into an internal error.
|
|
||||||
assertThat(diffInfo).content().element(0).commonLines().isNull();
|
|
||||||
assertThat(diffInfo).content().element(0).numberOfSkippedLines().isGreaterThan(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void requestingDiffForOldFileNameOfRenamedFileWithCommentOnOldFileYieldsReasonableResult()
|
|
||||||
throws Exception {
|
|
||||||
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
|
|
||||||
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
|
|
||||||
CommentInput comment = createCommentInput(2, 0, 3, 0, "Should be 'Line 2'.");
|
|
||||||
addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
|
|
||||||
String newFilePath = "a_new_file.txt";
|
|
||||||
gApi.changes().id(changeId).edit().renameFile(FILE_NAME, newFilePath);
|
|
||||||
gApi.changes().id(changeId).edit().publish();
|
|
||||||
|
|
||||||
DiffInfo diffInfo =
|
|
||||||
getDiffRequest(changeId, CURRENT, FILE_NAME)
|
|
||||||
.withBase(previousPatchSetId)
|
|
||||||
.withContext(DiffPreferencesInfo.WHOLE_FILE_CONTEXT)
|
|
||||||
.get();
|
|
||||||
// See comment for requestingDiffForOldFileNameOfRenamedFileYieldsReasonableResult().
|
|
||||||
// This test should additionally ensure that we also don't run into an internal error when
|
|
||||||
// a comment is present.
|
|
||||||
assertThat(diffInfo).content().element(0).commonLines().isNull();
|
assertThat(diffInfo).content().element(0).commonLines().isNull();
|
||||||
assertThat(diffInfo).content().element(0).numberOfSkippedLines().isGreaterThan(0);
|
assertThat(diffInfo).content().element(0).numberOfSkippedLines().isGreaterThan(0);
|
||||||
}
|
}
|
||||||
@@ -2781,26 +2704,6 @@ public class RevisionDiffIT extends AbstractDaemonTest {
|
|||||||
assertThat(e).hasMessageThat().isEqualTo("edit not allowed as base");
|
assertThat(e).hasMessageThat().isEqualTo("edit not allowed as base");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CommentInput createCommentInput(
|
|
||||||
int startLine, int startCharacter, int endLine, int endCharacter, String message) {
|
|
||||||
CommentInput comment = new CommentInput();
|
|
||||||
comment.range = new Comment.Range();
|
|
||||||
comment.range.startLine = startLine;
|
|
||||||
comment.range.startCharacter = startCharacter;
|
|
||||||
comment.range.endLine = endLine;
|
|
||||||
comment.range.endCharacter = endCharacter;
|
|
||||||
comment.message = message;
|
|
||||||
return comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addCommentTo(
|
|
||||||
String changeId, String previousPatchSetId, String fileName, CommentInput comment)
|
|
||||||
throws RestApiException {
|
|
||||||
ReviewInput reviewInput = new ReviewInput();
|
|
||||||
reviewInput.comments = ImmutableMap.of(fileName, ImmutableList.of(comment));
|
|
||||||
gApi.changes().id(changeId).revision(previousPatchSetId).review(reviewInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertDiffForNewFile(
|
private void assertDiffForNewFile(
|
||||||
PushOneCommit.Result pushResult, String path, String expectedContentSideB) throws Exception {
|
PushOneCommit.Result pushResult, String path, String expectedContentSideB) throws Exception {
|
||||||
DiffInfo diff =
|
DiffInfo diff =
|
||||||
|
@@ -1016,11 +1016,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String urlDiff(String changeId, String fileName) {
|
private String urlDiff(String changeId, String fileName) {
|
||||||
return "/changes/"
|
return "/changes/" + changeId + "/revisions/0/files/" + fileName + "/diff?intraline";
|
||||||
+ changeId
|
|
||||||
+ "/revisions/0/files/"
|
|
||||||
+ fileName
|
|
||||||
+ "/diff?context=ALL&intraline";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String urlDiff(String changeId, String revisionId, String fileName) {
|
private String urlDiff(String changeId, String revisionId, String fileName) {
|
||||||
@@ -1030,7 +1026,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
|
|||||||
+ revisionId
|
+ revisionId
|
||||||
+ "/files/"
|
+ "/files/"
|
||||||
+ fileName
|
+ fileName
|
||||||
+ "/diff?context=ALL&intraline";
|
+ "/diff?intraline";
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T readContentFromJson(RestResponse r, Class<T> clazz) throws Exception {
|
private <T> T readContentFromJson(RestResponse r, Class<T> clazz) throws Exception {
|
||||||
|
Reference in New Issue
Block a user