summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/plugins/diffeditor/diffeditorplugin.cpp45
-rw-r--r--src/plugins/diffeditor/diffutils.cpp185
-rw-r--r--src/plugins/diffeditor/diffutils.h22
3 files changed, 223 insertions, 29 deletions
diff --git a/src/plugins/diffeditor/diffeditorplugin.cpp b/src/plugins/diffeditor/diffeditorplugin.cpp
index 20ff559d28..0d6ad83df0 100644
--- a/src/plugins/diffeditor/diffeditorplugin.cpp
+++ b/src/plugins/diffeditor/diffeditorplugin.cpp
@@ -483,7 +483,23 @@ void DiffEditor::Internal::DiffEditorPlugin::testReadPatch_data()
"diff --git a/empty b/empty\n"
"deleted file mode 100644\n"
"index e69de29..0000000\n"
- );
+ "diff --git a/file a.txt b/file b.txt\n"
+ "similarity index 99%\n"
+ "copy from file a.txt\n"
+ "copy to file b.txt\n"
+ "index 1234567..9876543\n"
+ "--- a/file a.txt\n"
+ "+++ b/file b.txt\n"
+ "@@ -20,3 +20,3 @@\n"
+ " A\n"
+ "-B\n"
+ "+C\n"
+ " D\n"
+ "diff --git a/file a.txt b/file b.txt\n"
+ "similarity index 99%\n"
+ "rename from file a.txt\n"
+ "rename to file b.txt\n"
+ );
FileData fileData1;
fileData1.leftFileInfo = DiffFileInfo(QLatin1String("src/plugins/diffeditor/diffeditor.cpp"),
@@ -543,6 +559,7 @@ void DiffEditor::Internal::DiffEditorPlugin::testReadPatch_data()
FileData fileData3;
fileData3.leftFileInfo = DiffFileInfo(QLatin1String("new"), QLatin1String("0000000"));
fileData3.rightFileInfo = DiffFileInfo(QLatin1String("new"), QLatin1String("257cc56"));
+ fileData3.fileOperation = FileData::NewFile;
ChunkData chunkData3;
chunkData3.leftStartingLineNumber = -1;
chunkData3.rightStartingLineNumber = 0;
@@ -556,6 +573,7 @@ void DiffEditor::Internal::DiffEditorPlugin::testReadPatch_data()
FileData fileData4;
fileData4.leftFileInfo = DiffFileInfo(QLatin1String("deleted"), QLatin1String("257cc56"));
fileData4.rightFileInfo = DiffFileInfo(QLatin1String("deleted"), QLatin1String("0000000"));
+ fileData4.fileOperation = FileData::DeleteFile;
ChunkData chunkData4;
chunkData4.leftStartingLineNumber = 0;
chunkData4.rightStartingLineNumber = -1;
@@ -569,13 +587,35 @@ void DiffEditor::Internal::DiffEditorPlugin::testReadPatch_data()
FileData fileData5;
fileData5.leftFileInfo = DiffFileInfo(QLatin1String("empty"), QLatin1String("0000000"));
fileData5.rightFileInfo = DiffFileInfo(QLatin1String("empty"), QLatin1String("e69de29"));
+ fileData5.fileOperation = FileData::NewFile;
FileData fileData6;
fileData6.leftFileInfo = DiffFileInfo(QLatin1String("empty"), QLatin1String("e69de29"));
fileData6.rightFileInfo = DiffFileInfo(QLatin1String("empty"), QLatin1String("0000000"));
+ fileData6.fileOperation = FileData::DeleteFile;
+
+ FileData fileData7;
+ fileData7.leftFileInfo = DiffFileInfo(QLatin1String("file a.txt"), QLatin1String("1234567"));
+ fileData7.rightFileInfo = DiffFileInfo(QLatin1String("file b.txt"), QLatin1String("9876543"));
+ fileData7.fileOperation = FileData::CopyFile;
+ ChunkData chunkData7;
+ chunkData7.leftStartingLineNumber = 19;
+ chunkData7.rightStartingLineNumber = 19;
+ QList<RowData> rows7;
+ rows7.append(RowData(TextLineData(QLatin1String("A"))));
+ rows7.append(RowData(TextLineData(QLatin1String("B")),
+ TextLineData(QLatin1String("C"))));
+ rows7.append(RowData(TextLineData(QLatin1String("D"))));
+ chunkData7.rows = rows7;
+ fileData7.chunks.append(chunkData7);
+
+ FileData fileData8;
+ fileData8.leftFileInfo = DiffFileInfo(QLatin1String("file a.txt"));
+ fileData8.rightFileInfo = DiffFileInfo(QLatin1String("file b.txt"));
+ fileData8.fileOperation = FileData::RenameFile;
QList<FileData> fileDataList;
- fileDataList << fileData1 << fileData2 << fileData3 << fileData4 << fileData5 << fileData6;
+ fileDataList << fileData1 << fileData2 << fileData3 << fileData4 << fileData5 << fileData6 << fileData7 << fileData8;
QTest::newRow("Git patch") << patch
<< fileDataList;
@@ -599,6 +639,7 @@ void DiffEditor::Internal::DiffEditorPlugin::testReadPatch()
QCOMPARE(resultFileData.rightFileInfo.fileName, origFileData.rightFileInfo.fileName);
QCOMPARE(resultFileData.rightFileInfo.typeInfo, origFileData.rightFileInfo.typeInfo);
QCOMPARE(resultFileData.chunks.count(), origFileData.chunks.count());
+ QCOMPARE(resultFileData.fileOperation, origFileData.fileOperation);
for (int j = 0; j < origFileData.chunks.count(); j++) {
const ChunkData &origChunkData = origFileData.chunks.at(j);
const ChunkData &resultChunkData = resultFileData.chunks.at(j);
diff --git a/src/plugins/diffeditor/diffutils.cpp b/src/plugins/diffeditor/diffutils.cpp
index 0785e815bc..18725363fb 100644
--- a/src/plugins/diffeditor/diffutils.cpp
+++ b/src/plugins/diffeditor/diffutils.cpp
@@ -859,11 +859,11 @@ static FileData readGitHeaderAndChunks(const QString &headerAndChunks,
QString rightFileName = QLatin1String("b/") + fileName;
if (newFileMode.indexIn(patch, 0) == 0) {
- fileData.leftFileInfo.devNull = true;
+ fileData.fileOperation = FileData::NewFile;
leftFileName = devNull;
patch = patch.mid(newFileMode.capturedTexts().at(1).count());
} else if (deletedFileMode.indexIn(patch, 0) == 0) {
- fileData.rightFileInfo.devNull = true;
+ fileData.fileOperation = FileData::DeleteFile;
rightFileName = devNull;
patch = patch.mid(deletedFileMode.capturedTexts().at(1).count());
}
@@ -889,7 +889,8 @@ static FileData readGitHeaderAndChunks(const QString &headerAndChunks,
+ QLatin1String(" differ$)"));
// empty or followed either by leftFileRegExp or by binaryRegExp
- if (patch.isEmpty() && (fileData.leftFileInfo.devNull || fileData.rightFileInfo.devNull)) {
+ if (patch.isEmpty() && (fileData.fileOperation == FileData::NewFile
+ || fileData.fileOperation == FileData::DeleteFile)) {
readOk = true;
} else if (leftFileRegExp.indexIn(patch, 0) == 0) {
patch = patch.mid(leftFileRegExp.capturedTexts().at(1).count());
@@ -918,51 +919,193 @@ static FileData readGitHeaderAndChunks(const QString &headerAndChunks,
return fileData;
}
+static FileData readCopyRenameChunks(const QString &copyRenameChunks,
+ FileData::FileOperation fileOperation,
+ const QString &leftFileName,
+ const QString &rightFileName,
+ bool ignoreWhitespace,
+ bool *ok)
+{
+ FileData fileData;
+ fileData.fileOperation = fileOperation;
+ fileData.leftFileInfo.fileName = leftFileName;
+ fileData.rightFileInfo.fileName = rightFileName;
+
+ QString patch = copyRenameChunks;
+ bool readOk = false;
+
+ const QRegExp indexRegExp(QLatin1String("(^index (\\w+)\\.{2}(\\w+)(?: \\d+)?(\\n|$))")); // index cap2..cap3(optionally: octal)
+
+ QString leftGitFileName = QLatin1String("a/") + leftFileName;
+ QString rightGitFileName = QLatin1String("b/") + rightFileName;
+
+ if (fileOperation == FileData::CopyFile || fileOperation == FileData::RenameFile) {
+ if (indexRegExp.indexIn(patch, 0) == 0) {
+ const QStringList capturedTexts = indexRegExp.capturedTexts();
+ const QString captured = capturedTexts.at(1);
+ fileData.leftFileInfo.typeInfo = capturedTexts.at(2);
+ fileData.rightFileInfo.typeInfo = capturedTexts.at(3);
+
+ patch = patch.mid(captured.count());
+
+ const QRegExp leftFileRegExp(QLatin1String("(^-{3} ") // "--- "
+ + leftGitFileName // "a/fileName" or "/dev/null"
+ + QLatin1String("(?:\\t[^\\n]*)*\\n)")); // optionally followed by: \t anything \t anything ...)
+ const QRegExp rightFileRegExp(QLatin1String("(^\\+{3} ") // "+++ "
+ + rightGitFileName // "b/fileName" or "/dev/null"
+ + QLatin1String("(?:\\t[^\\n]*)*\\n)")); // optionally followed by: \t anything \t anything ...)
+
+ // followed by leftFileRegExp
+ if (leftFileRegExp.indexIn(patch, 0) == 0) {
+ patch = patch.mid(leftFileRegExp.capturedTexts().at(1).count());
+
+ // followed by rightFileRegExp
+ if (rightFileRegExp.indexIn(patch, 0) == 0) {
+ patch = patch.mid(rightFileRegExp.capturedTexts().at(1).count());
+
+ fileData.chunks = readChunks(patch,
+ ignoreWhitespace,
+ &fileData.lastChunkAtTheEndOfFile,
+ &readOk);
+ }
+ }
+ } else if (copyRenameChunks.isEmpty()) {
+ readOk = true;
+ }
+ }
+
+ if (ok)
+ *ok = readOk;
+
+ if (!readOk)
+ return FileData();
+
+ return fileData;
+}
+
static QList<FileData> readGitPatch(const QString &patch, bool ignoreWhitespace, bool *ok)
{
- const QRegExp gitRegExp(QLatin1String("((?:\\n|^)diff --git a/([^\\n]+) b/\\2\\n)")); // diff --git a/cap2 b/cap2
+ const QRegExp simpleGitRegExp(QLatin1String("((?:\\n|^)diff --git a/([^\\n]+) b/\\2\\n)")); // diff --git a/cap2 b/cap2
+
+ const QRegExp similarityRegExp(QLatin1String(
+ "((?:\\n|^)diff --git a/([^\\n]+) b/([^\\n]+)\\n" // diff --git a/cap2 b/cap3
+ "(?:dis)?similarity index \\d{1,3}%\\n" // similarity / dissimilarity index xxx% (100% max)
+ "(copy|rename) from \\2\\n" // copy / rename from cap2
+ "\\4 to \\3\\n)")); // copy / rename (cap4) to cap3
bool readOk = false;
QList<FileData> fileDataList;
- int pos = gitRegExp.indexIn(patch, 0);
+ const int simpleGitPos = simpleGitRegExp.indexIn(patch, 0);
+ const int similarityPos = similarityRegExp.indexIn(patch, 0);
+
+ bool simpleGitMatched = false;
+ int pos = -1;
+ if (simpleGitPos < 0) {
+ pos = similarityPos;
+ } else if (similarityPos < 0) {
+ pos = simpleGitPos;
+ simpleGitMatched = true;
+ } else {
+ pos = qMin(simpleGitPos, similarityPos);
+ simpleGitMatched = (pos == simpleGitPos);
+ }
+
if (pos == 0) { // git style patch
readOk = true;
int endOfLastHeader = 0;
- QString lastFileName;
+ QString lastLeftFileName;
+ QString lastRightFileName;
+ FileData::FileOperation lastOperation = FileData::ChangeFile;
do {
- const QStringList capturedTexts = gitRegExp.capturedTexts();
- const QString captured = capturedTexts.at(1);
- const QString fileName = capturedTexts.at(2);
if (endOfLastHeader > 0) {
const QString headerAndChunks = patch.mid(endOfLastHeader,
pos - endOfLastHeader);
- const FileData fileData = readGitHeaderAndChunks(headerAndChunks,
- lastFileName,
- ignoreWhitespace,
- &readOk);
+ FileData fileData;
+ if (lastOperation == FileData::ChangeFile) {
+ fileData = readGitHeaderAndChunks(headerAndChunks,
+ lastLeftFileName,
+ ignoreWhitespace,
+ &readOk);
+ } else {
+ fileData = readCopyRenameChunks(headerAndChunks,
+ lastOperation,
+ lastLeftFileName,
+ lastRightFileName,
+ ignoreWhitespace,
+ &readOk);
+ }
if (!readOk)
break;
fileDataList.append(fileData);
}
- pos += captured.count();
- endOfLastHeader = pos;
- lastFileName = fileName;
- } while ((pos = gitRegExp.indexIn(patch, pos)) != -1);
+
+ if (simpleGitMatched) {
+ const QStringList capturedTexts = simpleGitRegExp.capturedTexts();
+ const QString captured = capturedTexts.at(1);
+ const QString fileName = capturedTexts.at(2);
+ pos += captured.count();
+ endOfLastHeader = pos;
+ lastLeftFileName = fileName;
+ lastRightFileName = fileName;
+ lastOperation = FileData::ChangeFile;
+ } else {
+ const QStringList capturedTexts = similarityRegExp.capturedTexts();
+ const QString captured = capturedTexts.at(1);
+ const QString leftFileName = capturedTexts.at(2);
+ const QString rightFileName = capturedTexts.at(3);
+ const QString operation = capturedTexts.at(4);
+ pos += captured.count();
+ endOfLastHeader = pos;
+ lastLeftFileName = leftFileName;
+ lastRightFileName = rightFileName;
+ if (operation == QLatin1String("copy"))
+ lastOperation = FileData::CopyFile;
+ else if (operation == QLatin1String("rename"))
+ lastOperation = FileData::RenameFile;
+ else
+ break; // either copy or rename, otherwise broken
+ }
+
+ const int simpleGitPos = simpleGitRegExp.indexIn(patch, pos);
+ const int similarityPos = similarityRegExp.indexIn(patch, pos);
+
+ simpleGitMatched = false;
+ pos = -1;
+ if (simpleGitPos < 0) {
+ pos = similarityPos;
+ } else if (similarityPos < 0) {
+ pos = simpleGitPos;
+ simpleGitMatched = true;
+ } else {
+ pos = qMin(simpleGitPos, similarityPos);
+ simpleGitMatched = (pos == simpleGitPos);
+ }
+ } while (pos != -1);
if (endOfLastHeader > 0 && readOk) {
const QString headerAndChunks = patch.mid(endOfLastHeader,
patch.count() - endOfLastHeader - 1);
- const FileData fileData = readGitHeaderAndChunks(headerAndChunks,
- lastFileName,
- ignoreWhitespace,
- &readOk);
+ FileData fileData;
+ if (lastOperation == FileData::ChangeFile) {
+ fileData = readGitHeaderAndChunks(headerAndChunks,
+ lastLeftFileName,
+ ignoreWhitespace,
+ &readOk);
+ } else {
+ fileData = readCopyRenameChunks(headerAndChunks,
+ lastOperation,
+ lastLeftFileName,
+ lastRightFileName,
+ ignoreWhitespace,
+ &readOk);
+ }
if (readOk)
fileDataList.append(fileData);
}
diff --git a/src/plugins/diffeditor/diffutils.h b/src/plugins/diffeditor/diffutils.h
index 1c5d237e2d..b649538026 100644
--- a/src/plugins/diffeditor/diffutils.h
+++ b/src/plugins/diffeditor/diffutils.h
@@ -46,13 +46,12 @@ class Diff;
class DIFFEDITOR_EXPORT DiffFileInfo {
public:
- DiffFileInfo() : devNull(false) {}
- DiffFileInfo(const QString &file) : fileName(file), devNull(false) {}
+ DiffFileInfo() {}
+ DiffFileInfo(const QString &file) : fileName(file) {}
DiffFileInfo(const QString &file, const QString &type)
- : fileName(file), typeInfo(type), devNull(false) {}
+ : fileName(file), typeInfo(type) {}
QString fileName;
QString typeInfo;
- bool devNull;
};
class DIFFEDITOR_EXPORT TextLineData {
@@ -100,17 +99,28 @@ public:
class DIFFEDITOR_EXPORT FileData {
public:
+ enum FileOperation {
+ ChangeFile,
+ NewFile,
+ DeleteFile,
+ CopyFile,
+ RenameFile
+ };
+
FileData()
- : binaryFiles(false),
+ : fileOperation(ChangeFile),
+ binaryFiles(false),
lastChunkAtTheEndOfFile(false),
contextChunksIncluded(false) {}
FileData(const ChunkData &chunkData)
- : binaryFiles(false),
+ : fileOperation(ChangeFile),
+ binaryFiles(false),
lastChunkAtTheEndOfFile(false),
contextChunksIncluded(false) { chunks.append(chunkData); }
QList<ChunkData> chunks;
DiffFileInfo leftFileInfo;
DiffFileInfo rightFileInfo;
+ FileOperation fileOperation;
bool binaryFiles;
bool lastChunkAtTheEndOfFile;
bool contextChunksIncluded;