DiffEditor: Parse git diffs properly

Do not put filenames into a regular expression and then hope for
the best... any file with any character in its name that has a
special meaning for a regexp will break the parsing.

Task-number: QTCREATORBUG-14322
Change-Id: Ifab513d675168aff041f20d2b3f06a8d27919aa7
Reviewed-by: Tobias Hunger <tobias.hunger@theqtcompany.com>
This commit is contained in:
Tobias Hunger
2015-04-23 17:48:32 +02:00
parent 3b0a75c9e0
commit ad9dc94eae

View File

@@ -868,6 +868,11 @@ static QList<FileData> readDiffPatch(const QString &patch,
return fileDataList; return fileDataList;
} }
static bool fileNameEnd(const QChar &c)
{
return c == QLatin1Char('\n') || c == QLatin1Char('\t');
}
static FileData readGitHeaderAndChunks(const QString &headerAndChunks, static FileData readGitHeaderAndChunks(const QString &headerAndChunks,
const QString &fileName, const QString &fileName,
bool *ok) bool *ok)
@@ -909,34 +914,30 @@ static FileData readGitHeaderAndChunks(const QString &headerAndChunks,
patch.remove(0, indexRegExp.matchedLength()); patch.remove(0, indexRegExp.matchedLength());
} }
const QRegExp leftFileRegExp(QLatin1String("^-{3} ") // "--- " const QString binaryLine = QString::fromLatin1("Binary files ") + leftFileName
+ leftFileName // "a/fileName" or "/dev/null" + QLatin1String(" and ") + rightFileName + QLatin1String(" differ");
+ QLatin1String("(?:\\t[^\\n]*)*\\n")); // optionally followed by: \t anything \t anything ...) const QString leftStart = QString::fromLatin1("--- ") + leftFileName;
const QRegExp rightFileRegExp(QLatin1String("^\\+{3} ") // "+++ " QChar leftFollow = patch.count() > leftStart.count() ? patch.at(leftStart.count()) : QLatin1Char('\n');
+ rightFileName // "b/fileName" or "/dev/null"
+ QLatin1String("(?:\\t[^\\n]*)*\\n")); // optionally followed by: \t anything \t anything ...)
const QRegExp binaryRegExp(QLatin1String("^Binary files ")
+ leftFileName
+ QLatin1String(" and ")
+ rightFileName
+ QLatin1String(" differ$"));
// empty or followed either by leftFileRegExp or by binaryRegExp // empty or followed either by leftFileRegExp or by binaryRegExp
if (patch.isEmpty() && (fileData.fileOperation == FileData::NewFile if (patch.isEmpty() && (fileData.fileOperation == FileData::NewFile
|| fileData.fileOperation == FileData::DeleteFile)) { || fileData.fileOperation == FileData::DeleteFile)) {
readOk = true; readOk = true;
} else if (leftFileRegExp.indexIn(patch) == 0) { } else if (patch.startsWith(leftStart) && fileNameEnd(leftFollow)) {
patch.remove(0, leftFileRegExp.matchedLength()); patch.remove(0, patch.indexOf(QLatin1Char('\n'), leftStart.count()) + 1);
const QString rightStart = QString::fromLatin1("+++ ") + rightFileName;
QChar rightFollow = patch.count() > rightStart.count() ? patch.at(rightStart.count()) : QLatin1Char('\n');
// followed by rightFileRegExp // followed by rightFileRegExp
if (rightFileRegExp.indexIn(patch) == 0) { if (patch.startsWith(rightStart) && fileNameEnd(rightFollow)) {
patch.remove(0, rightFileRegExp.matchedLength()); patch.remove(0, patch.indexOf(QLatin1Char('\n'), rightStart.count()) + 1);
fileData.chunks = readChunks(patch, fileData.chunks = readChunks(patch,
&fileData.lastChunkAtTheEndOfFile, &fileData.lastChunkAtTheEndOfFile,
&readOk); &readOk);
} }
} else if (binaryRegExp.indexIn(patch) == 0) { } else if (patch == binaryLine) {
readOk = true; readOk = true;
fileData.binaryFiles = true; fileData.binaryFiles = true;
} }
@@ -966,9 +967,6 @@ static FileData readCopyRenameChunks(const QString &copyRenameChunks,
const QRegExp indexRegExp(QLatin1String("^index (\\w+)\\.{2}(\\w+)(?: \\d+)?(\\n|$)")); // index cap1..cap2(optionally: octal) const QRegExp indexRegExp(QLatin1String("^index (\\w+)\\.{2}(\\w+)(?: \\d+)?(\\n|$)")); // index cap1..cap2(optionally: octal)
QString leftGitFileName = QLatin1String("a/") + leftFileName;
QString rightGitFileName = QLatin1String("b/") + rightFileName;
if (fileOperation == FileData::CopyFile || fileOperation == FileData::RenameFile) { if (fileOperation == FileData::CopyFile || fileOperation == FileData::RenameFile) {
if (indexRegExp.indexIn(patch) == 0) { if (indexRegExp.indexIn(patch) == 0) {
fileData.leftFileInfo.typeInfo = indexRegExp.cap(1); fileData.leftFileInfo.typeInfo = indexRegExp.cap(1);
@@ -976,20 +974,20 @@ static FileData readCopyRenameChunks(const QString &copyRenameChunks,
patch.remove(0, indexRegExp.matchedLength()); patch.remove(0, indexRegExp.matchedLength());
const QRegExp leftFileRegExp(QLatin1String("^-{3} ") // "--- " const QString leftStart = QString::fromLatin1("--- a/") + leftFileName;
+ leftGitFileName // "a/fileName" or "/dev/null" QChar leftFollow = patch.count() > leftStart.count() ? patch.at(leftStart.count()) : QLatin1Char('\n');
+ 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 // followed by leftFileRegExp
if (leftFileRegExp.indexIn(patch) == 0) { if (patch.startsWith(leftStart) && fileNameEnd(leftFollow)) {
patch.remove(0, leftFileRegExp.matchedLength()); patch.remove(0, patch.indexOf(QLatin1Char('\n'), leftStart.count()) + 1);
// followed by rightFileRegExp // followed by rightFileRegExp
if (rightFileRegExp.indexIn(patch) == 0) { const QString rightStart = QString::fromLatin1("+++ b/") + rightFileName;
patch.remove(0, rightFileRegExp.matchedLength()); QChar rightFollow = patch.count() > rightStart.count() ? patch.at(rightStart.count()) : QLatin1Char('\n');
// followed by rightFileRegExp
if (patch.startsWith(rightStart) && fileNameEnd(rightFollow)) {
patch.remove(0, patch.indexOf(QLatin1Char('\n'), rightStart.count()) + 1);
fileData.chunks = readChunks(patch, fileData.chunks = readChunks(patch,
&fileData.lastChunkAtTheEndOfFile, &fileData.lastChunkAtTheEndOfFile,