Changeset View
Changeset View
Standalone View
Standalone View
src/parser/ArcanistDiffParser.php
| Show First 20 Lines • Show All 159 Lines • ▼ Show 20 Lines | foreach ($paths as $path => $status) { | ||||
| break; | break; | ||||
| case ArcanistDiffChangeType::TYPE_MOVE_AWAY: | case ArcanistDiffChangeType::TYPE_MOVE_AWAY: | ||||
| $origin->setType(ArcanistDiffChangeType::TYPE_MULTICOPY); | $origin->setType(ArcanistDiffChangeType::TYPE_MULTICOPY); | ||||
| break; | break; | ||||
| case ArcanistDiffChangeType::TYPE_CHANGE: | case ArcanistDiffChangeType::TYPE_CHANGE: | ||||
| $origin->setType(ArcanistDiffChangeType::TYPE_COPY_AWAY); | $origin->setType(ArcanistDiffChangeType::TYPE_COPY_AWAY); | ||||
| break; | break; | ||||
| default: | default: | ||||
| throw new Exception("Bad origin state {$type}."); | throw new Exception(pht('Bad origin state %s.', $type)); | ||||
| } | } | ||||
| $type = $origin->getType(); | $type = $origin->getType(); | ||||
| switch ($type) { | switch ($type) { | ||||
| case ArcanistDiffChangeType::TYPE_MULTICOPY: | case ArcanistDiffChangeType::TYPE_MULTICOPY: | ||||
| case ArcanistDiffChangeType::TYPE_MOVE_AWAY: | case ArcanistDiffChangeType::TYPE_MOVE_AWAY: | ||||
| $change->setType(ArcanistDiffChangeType::TYPE_MOVE_HERE); | $change->setType(ArcanistDiffChangeType::TYPE_MOVE_HERE); | ||||
| break; | break; | ||||
| case ArcanistDiffChangeType::TYPE_ADD: | case ArcanistDiffChangeType::TYPE_ADD: | ||||
| case ArcanistDiffChangeType::TYPE_COPY_AWAY: | case ArcanistDiffChangeType::TYPE_COPY_AWAY: | ||||
| $change->setType(ArcanistDiffChangeType::TYPE_COPY_HERE); | $change->setType(ArcanistDiffChangeType::TYPE_COPY_HERE); | ||||
| break; | break; | ||||
| default: | default: | ||||
| throw new Exception("Bad origin state {$type}."); | throw new Exception(pht('Bad origin state %s.', $type)); | ||||
| } | } | ||||
| } | } | ||||
| return $this->changes; | return $this->changes; | ||||
| } | } | ||||
| public function parseDiff($diff) { | public function parseDiff($diff) { | ||||
| if (!strlen(trim($diff))) { | if (!strlen(trim($diff))) { | ||||
| throw new Exception("Can't parse an empty diff!"); | throw new Exception(pht("Can't parse an empty diff!")); | ||||
| } | } | ||||
| // Detect `git-format-patch`, by looking for a "---" line somewhere in | // Detect `git-format-patch`, by looking for a "---" line somewhere in | ||||
| // the file and then a footer with Git version number, which looks like | // the file and then a footer with Git version number, which looks like | ||||
| // this: | // this: | ||||
| // | // | ||||
| // -- | // -- | ||||
| // 1.8.4.2 | // 1.8.4.2 | ||||
| ▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | do { | ||||
| $failed_parse = true; | $failed_parse = true; | ||||
| } | } | ||||
| } else if (!$ok) { | } else if (!$ok) { | ||||
| $failed_parse = true; | $failed_parse = true; | ||||
| } | } | ||||
| if ($failed_parse) { | if ($failed_parse) { | ||||
| $this->didFailParse( | $this->didFailParse( | ||||
| "Expected a hunk header, like 'Index: /path/to/file.ext' (svn), ". | pht( | ||||
| "'Property changes on: /path/to/file.ext' (svn properties), ". | "Expected a hunk header, like '%s' (svn), '%s' (svn properties), ". | ||||
| "'commit 59bcc3ad6775562f845953cf01624225' (git show), ". | "'%s' (git show), '%s' (git diff), '%s' (unified diff), or ". | ||||
| "'diff --git' (git diff), '--- filename' (unified diff), or ". | "'%s' (hg diff or patch).", | ||||
| "'diff -r' (hg diff or patch)."); | 'Index: /path/to/file.ext', | ||||
| 'Property changes on: /path/to/file.ext', | |||||
| 'commit 59bcc3ad6775562f845953cf01624225', | |||||
| 'diff --git', | |||||
| '--- filename', | |||||
| 'diff -r')); | |||||
| } | } | ||||
| if (isset($match['type'])) { | if (isset($match['type'])) { | ||||
| if ($match['type'] == 'diff --git') { | if ($match['type'] == 'diff --git') { | ||||
| list($old, $new) = self::splitGitDiffPaths($match['oldnew']); | list($old, $new) = self::splitGitDiffPaths($match['oldnew']); | ||||
| $match['old'] = $old; | $match['old'] = $old; | ||||
| $match['cur'] = $new; | $match['cur'] = $new; | ||||
| } | } | ||||
| Show All 37 Lines | do { | ||||
| $this->parseCommitMessage($change); | $this->parseCommitMessage($change); | ||||
| break; | break; | ||||
| case '---': | case '---': | ||||
| $ok = preg_match( | $ok = preg_match( | ||||
| '@^(?:\+\+\+) (.*)\s+\d{4}-\d{2}-\d{2}.*$@', | '@^(?:\+\+\+) (.*)\s+\d{4}-\d{2}-\d{2}.*$@', | ||||
| $line, | $line, | ||||
| $match); | $match); | ||||
| if (!$ok) { | if (!$ok) { | ||||
| $this->didFailParse("Expected '+++ filename' in unified diff."); | $this->didFailParse(pht( | ||||
| "Expected '%s' in unified diff.", | |||||
| '+++ filename')); | |||||
| } | } | ||||
| $change->setCurrentPath($match[1]); | $change->setCurrentPath($match[1]); | ||||
| $line = $this->nextLine(); | $line = $this->nextLine(); | ||||
| $this->parseChangeset($change); | $this->parseChangeset($change); | ||||
| break; | break; | ||||
| case 'diff -r': | case 'diff -r': | ||||
| $this->setIsMercurial(true); | $this->setIsMercurial(true); | ||||
| $this->parseIndexHunk($change); | $this->parseIndexHunk($change); | ||||
| break; | break; | ||||
| case 'rcsdiff -u': | case 'rcsdiff -u': | ||||
| $this->isRCS = true; | $this->isRCS = true; | ||||
| $this->parseIndexHunk($change); | $this->parseIndexHunk($change); | ||||
| break; | break; | ||||
| default: | default: | ||||
| $this->didFailParse('Unknown diff type.'); | $this->didFailParse(pht('Unknown diff type.')); | ||||
| break; | break; | ||||
| } | } | ||||
| } while ($this->getLine() !== null); | } while ($this->getLine() !== null); | ||||
| $this->didFinishParse(); | $this->didFinishParse(); | ||||
| $this->loadSyntheticData(); | $this->loadSyntheticData(); | ||||
| Show All 16 Lines | protected function parseCommitMessage(ArcanistDiffChange $change) { | ||||
| $line = $this->getLine(); | $line = $this->getLine(); | ||||
| if (preg_match('/^Merge: /', $line)) { | if (preg_match('/^Merge: /', $line)) { | ||||
| $this->nextLine(); | $this->nextLine(); | ||||
| } | } | ||||
| $line = $this->getLine(); | $line = $this->getLine(); | ||||
| if (!preg_match('/^Author: /', $line)) { | if (!preg_match('/^Author: /', $line)) { | ||||
| $this->didFailParse("Expected 'Author:'."); | $this->didFailParse(pht("Expected 'Author:'.")); | ||||
| } | } | ||||
| $line = $this->nextLine(); | $line = $this->nextLine(); | ||||
| if (!preg_match('/^Date: /', $line)) { | if (!preg_match('/^Date: /', $line)) { | ||||
| $this->didFailParse("Expected 'Date:'."); | $this->didFailParse(pht("Expected 'Date:'.")); | ||||
| } | } | ||||
| while (($line = $this->nextLineTrimmed()) !== null) { | while (($line = $this->nextLineTrimmed()) !== null) { | ||||
| if (strlen($line) && $line[0] != ' ') { | if (strlen($line) && $line[0] != ' ') { | ||||
| break; | break; | ||||
| } | } | ||||
| // Strip leading spaces from Git commit messages. Note that empty lines | // Strip leading spaces from Git commit messages. Note that empty lines | ||||
| // are represented as just "\n"; don't touch those. | // are represented as just "\n"; don't touch those. | ||||
| $message[] = preg_replace('/^ /', '', $this->getLine()); | $message[] = preg_replace('/^ /', '', $this->getLine()); | ||||
| } | } | ||||
| $message = rtrim(implode('', $message), "\r\n"); | $message = rtrim(implode('', $message), "\r\n"); | ||||
| $change->setMetadata('message', $message); | $change->setMetadata('message', $message); | ||||
| } | } | ||||
| /** | /** | ||||
| * Parse an SVN property change hunk. These hunks are ambiguous so just sort | * Parse an SVN property change hunk. These hunks are ambiguous so just sort | ||||
| * of try to get it mostly right. It's entirely possible to foil this parser | * of try to get it mostly right. It's entirely possible to foil this parser | ||||
| * (or any other parser) with a carefully constructed property change. | * (or any other parser) with a carefully constructed property change. | ||||
| */ | */ | ||||
| protected function parsePropertyHunk(ArcanistDiffChange $change) { | protected function parsePropertyHunk(ArcanistDiffChange $change) { | ||||
| $line = $this->getLineTrimmed(); | $line = $this->getLineTrimmed(); | ||||
| if (!preg_match('/^_+$/', $line)) { | if (!preg_match('/^_+$/', $line)) { | ||||
| $this->didFailParse("Expected '______________________'."); | $this->didFailParse(pht("Expected '%s'.", '______________________')); | ||||
| } | } | ||||
| $line = $this->nextLine(); | $line = $this->nextLine(); | ||||
| while ($line !== null) { | while ($line !== null) { | ||||
| $done = preg_match('/^(Index|Property changes on):/', $line); | $done = preg_match('/^(Index|Property changes on):/', $line); | ||||
| if ($done) { | if ($done) { | ||||
| break; | break; | ||||
| } | } | ||||
| // NOTE: Before 1.5, SVN uses "Name". At 1.5 and later, SVN uses | // NOTE: Before 1.5, SVN uses "Name". At 1.5 and later, SVN uses | ||||
| // "Modified", "Added" and "Deleted". | // "Modified", "Added" and "Deleted". | ||||
| $matches = null; | $matches = null; | ||||
| $ok = preg_match( | $ok = preg_match( | ||||
| '/^(Name|Modified|Added|Deleted): (.*)$/', | '/^(Name|Modified|Added|Deleted): (.*)$/', | ||||
| $line, | $line, | ||||
| $matches); | $matches); | ||||
| if (!$ok) { | if (!$ok) { | ||||
| $this->didFailParse( | $this->didFailParse( | ||||
| "Expected 'Name', 'Added', 'Deleted', or 'Modified'."); | pht("Expected 'Name', 'Added', 'Deleted', or 'Modified'.")); | ||||
| } | } | ||||
| $op = $matches[1]; | $op = $matches[1]; | ||||
| $prop = $matches[2]; | $prop = $matches[2]; | ||||
| list($old, $new) = $this->parseSVNPropertyChange($op, $prop); | list($old, $new) = $this->parseSVNPropertyChange($op, $prop); | ||||
| if ($old !== null) { | if ($old !== null) { | ||||
| Show All 29 Lines | while ($line !== null) { | ||||
| // and the line with the property change. If we have such a line, we'll | // and the line with the property change. If we have such a line, we'll | ||||
| // just ignore it (: | // just ignore it (: | ||||
| $line = $this->nextLine(); | $line = $this->nextLine(); | ||||
| $prop_index = 1; | $prop_index = 1; | ||||
| $trimline = ltrim($line); | $trimline = ltrim($line); | ||||
| } | } | ||||
| if ($trimline && $trimline[0] == '+') { | if ($trimline && $trimline[0] == '+') { | ||||
| if ($op == 'Deleted') { | if ($op == 'Deleted') { | ||||
| $this->didFailParse('Unexpected "+" section in property deletion.'); | $this->didFailParse(pht( | ||||
| 'Unexpected "%s" section in property deletion.', | |||||
| '+')); | |||||
| } | } | ||||
| $target = 'new'; | $target = 'new'; | ||||
| $line = substr($trimline, $prop_index); | $line = substr($trimline, $prop_index); | ||||
| } else if ($trimline && $trimline[0] == '-') { | } else if ($trimline && $trimline[0] == '-') { | ||||
| if ($op == 'Added') { | if ($op == 'Added') { | ||||
| $this->didFailParse('Unexpected "-" section in property addition.'); | $this->didFailParse(pht( | ||||
| 'Unexpected "%s" section in property addition.', | |||||
| '-')); | |||||
| } | } | ||||
| $target = 'old'; | $target = 'old'; | ||||
| $line = substr($trimline, $prop_index); | $line = substr($trimline, $prop_index); | ||||
| } else if (!strncmp($trimline, 'Merged', 6)) { | } else if (!strncmp($trimline, 'Merged', 6)) { | ||||
| if ($op == 'Added') { | if ($op == 'Added') { | ||||
| $target = 'new'; | $target = 'new'; | ||||
| } else { | } else { | ||||
| // These can appear on merges. No idea how to interpret this (unclear | // These can appear on merges. No idea how to interpret this (unclear | ||||
| Show All 24 Lines | if (!strlen($new)) { | ||||
| $new = null; | $new = null; | ||||
| } | } | ||||
| return array($old, $new); | return array($old, $new); | ||||
| } | } | ||||
| protected function setIsGit($git) { | protected function setIsGit($git) { | ||||
| if ($this->isGit !== null && $this->isGit != $git) { | if ($this->isGit !== null && $this->isGit != $git) { | ||||
| throw new Exception('Git status has changed!'); | throw new Exception(pht('Git status has changed!')); | ||||
| } | } | ||||
| $this->isGit = $git; | $this->isGit = $git; | ||||
| return $this; | return $this; | ||||
| } | } | ||||
| protected function getIsGit() { | protected function getIsGit() { | ||||
| return $this->isGit; | return $this->isGit; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 142 Lines • ▼ Show 20 Lines | if ($is_git) { | ||||
| } while (true); | } while (true); | ||||
| } | } | ||||
| $line = $this->getLine(); | $line = $this->getLine(); | ||||
| if ($is_svn) { | if ($is_svn) { | ||||
| $ok = preg_match('/^=+\s*$/', $line); | $ok = preg_match('/^=+\s*$/', $line); | ||||
| if (!$ok) { | if (!$ok) { | ||||
| $this->didFailParse("Expected '=======================' divider line."); | $this->didFailParse(pht( | ||||
| "Expected '%s' divider line.", | |||||
| '=======================')); | |||||
| } else { | } else { | ||||
| // Adding an empty file in SVN can produce an empty line here. | // Adding an empty file in SVN can produce an empty line here. | ||||
| $line = $this->nextNonemptyLine(); | $line = $this->nextNonemptyLine(); | ||||
| } | } | ||||
| } else if ($is_git) { | } else if ($is_git) { | ||||
| $ok = preg_match('/^index .*$/', $line); | $ok = preg_match('/^index .*$/', $line); | ||||
| if (!$ok) { | if (!$ok) { | ||||
| // TODO: "hg diff -g" diffs ("mercurial git-style diffs") do not include | // TODO: "hg diff -g" diffs ("mercurial git-style diffs") do not include | ||||
| ▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | final class ArcanistDiffParser { | ||||
| private function parseGitBinaryPatch() { | private function parseGitBinaryPatch() { | ||||
| // TODO: We could decode the patches, but it's a giant mess so don't bother | // TODO: We could decode the patches, but it's a giant mess so don't bother | ||||
| // for now. We'll pick up the data from the working copy in the common | // for now. We'll pick up the data from the working copy in the common | ||||
| // case ("arc diff"). | // case ("arc diff"). | ||||
| $line = $this->getLine(); | $line = $this->getLine(); | ||||
| if (!preg_match('/^literal /', $line)) { | if (!preg_match('/^literal /', $line)) { | ||||
| $this->didFailParse("Expected 'literal NNNN' to start git binary patch."); | $this->didFailParse( | ||||
| pht("Expected '%s' to start git binary patch.", 'literal NNNN')); | |||||
| } | } | ||||
| do { | do { | ||||
| $line = $this->nextLineTrimmed(); | $line = $this->nextLineTrimmed(); | ||||
| if ($line === '' || $line === null) { | if ($line === '' || $line === null) { | ||||
| // Some versions of Mercurial apparently omit the terminal newline, | // Some versions of Mercurial apparently omit the terminal newline, | ||||
| // although it's unclear if Git will ever do this. In either case, | // although it's unclear if Git will ever do this. In either case, | ||||
| // rely on the base85 check for sanity. | // rely on the base85 check for sanity. | ||||
| $this->nextNonemptyLine(); | $this->nextNonemptyLine(); | ||||
| return; | return; | ||||
| } else if (!preg_match('/^[a-zA-Z]/', $line)) { | } else if (!preg_match('/^[a-zA-Z]/', $line)) { | ||||
| $this->didFailParse('Expected base85 line length character (a-zA-Z).'); | $this->didFailParse( | ||||
| pht('Expected base85 line length character (a-zA-Z).')); | |||||
| } | } | ||||
| } while (true); | } while (true); | ||||
| } | } | ||||
| protected function parseHunkTarget() { | protected function parseHunkTarget() { | ||||
| $line = $this->getLine(); | $line = $this->getLine(); | ||||
| $matches = null; | $matches = null; | ||||
| Show All 12 Lines | protected function parseHunkTarget() { | ||||
| $ok = preg_match( | $ok = preg_match( | ||||
| '@^[-+]{3} (?:[ab]/)?(?P<path>.*?)'.$remainder.'$@', | '@^[-+]{3} (?:[ab]/)?(?P<path>.*?)'.$remainder.'$@', | ||||
| $line, | $line, | ||||
| $matches); | $matches); | ||||
| if (!$ok) { | if (!$ok) { | ||||
| $this->didFailParse( | $this->didFailParse( | ||||
| "Expected hunk target '+++ path/to/file.ext (revision N)'."); | pht( | ||||
| "Expected hunk target '%s'.", | |||||
| '+++ path/to/file.ext (revision N)')); | |||||
| } | } | ||||
| $this->nextLine(); | $this->nextLine(); | ||||
| return $matches['path']; | return $matches['path']; | ||||
| } | } | ||||
| protected function markBinary(ArcanistDiffChange $change) { | protected function markBinary(ArcanistDiffChange $change) { | ||||
| $change->setFileType(ArcanistDiffChangeType::FILE_BINARY); | $change->setFileType(ArcanistDiffChangeType::FILE_BINARY); | ||||
| Show All 27 Lines | do { | ||||
| if (!$ok) { | if (!$ok) { | ||||
| // It's possible we hit the style of an svn1.7 property change. | // It's possible we hit the style of an svn1.7 property change. | ||||
| // This is a 4-line Index block, followed by an empty line, followed | // This is a 4-line Index block, followed by an empty line, followed | ||||
| // by a "Property changes on:" section similar to svn1.6. | // by a "Property changes on:" section similar to svn1.6. | ||||
| if ($line == '') { | if ($line == '') { | ||||
| $line = $this->nextNonemptyLine(); | $line = $this->nextNonemptyLine(); | ||||
| $ok = preg_match('/^Property changes on:/', $line); | $ok = preg_match('/^Property changes on:/', $line); | ||||
| if (!$ok) { | if (!$ok) { | ||||
| $this->didFailParse('Confused by empty line'); | $this->didFailParse(pht('Confused by empty line')); | ||||
| } | } | ||||
| $line = $this->nextLine(); | $line = $this->nextLine(); | ||||
| return $this->parsePropertyHunk($change); | return $this->parsePropertyHunk($change); | ||||
| } | } | ||||
| $this->didFailParse("Expected hunk header '@@ -NN,NN +NN,NN @@'."); | $this->didFailParse(pht( | ||||
| "Expected hunk header '%s'.", | |||||
| '@@ -NN,NN +NN,NN @@')); | |||||
| } | } | ||||
| $hunk->setOldOffset($matches[1]); | $hunk->setOldOffset($matches[1]); | ||||
| $hunk->setNewOffset($matches[3]); | $hunk->setNewOffset($matches[3]); | ||||
| // Cover for the cases where length wasn't present (implying one line). | // Cover for the cases where length wasn't present (implying one line). | ||||
| $old_len = idx($matches, 2); | $old_len = idx($matches, 2); | ||||
| if (!strlen($old_len)) { | if (!strlen($old_len)) { | ||||
| Show All 21 Lines | do { | ||||
| // emit as completely empty. If we encounter a completely empty line, | // emit as completely empty. If we encounter a completely empty line, | ||||
| // treat it as a ' ' (i.e., unchanged empty line) line. | // treat it as a ' ' (i.e., unchanged empty line) line. | ||||
| $char = ' '; | $char = ' '; | ||||
| } | } | ||||
| switch ($char) { | switch ($char) { | ||||
| case '\\': | case '\\': | ||||
| if (!preg_match('@\\ No newline at end of file@', $line)) { | if (!preg_match('@\\ No newline at end of file@', $line)) { | ||||
| $this->didFailParse( | $this->didFailParse( | ||||
| "Expected '\ No newline at end of file'."); | pht("Expected '\ No newline at end of file'.")); | ||||
| } | } | ||||
| if ($new_len) { | if ($new_len) { | ||||
| $real[] = $line; | $real[] = $line; | ||||
| $hunk->setIsMissingOldNewline(true); | $hunk->setIsMissingOldNewline(true); | ||||
| } else { | } else { | ||||
| $real[] = $line; | $real[] = $line; | ||||
| $hunk->setIsMissingNewNewline(true); | $hunk->setIsMissingNewNewline(true); | ||||
| } | } | ||||
| Show All 28 Lines | do { | ||||
| default: | default: | ||||
| // We hit something, likely another hunk. | // We hit something, likely another hunk. | ||||
| $hit_next_hunk = true; | $hit_next_hunk = true; | ||||
| break 2; | break 2; | ||||
| } | } | ||||
| } | } | ||||
| if ($old_len || $new_len) { | if ($old_len || $new_len) { | ||||
| $this->didFailParse('Found the wrong number of hunk lines.'); | $this->didFailParse(pht('Found the wrong number of hunk lines.')); | ||||
| } | } | ||||
| $corpus = implode('', $real); | $corpus = implode('', $real); | ||||
| $is_binary = false; | $is_binary = false; | ||||
| if ($this->detectBinaryFiles) { | if ($this->detectBinaryFiles) { | ||||
| $is_binary = !phutil_is_utf8($corpus); | $is_binary = !phutil_is_utf8($corpus); | ||||
| $try_encoding = $this->tryEncoding; | $try_encoding = $this->tryEncoding; | ||||
| if ($is_binary && $try_encoding) { | if ($is_binary && $try_encoding) { | ||||
| $is_binary = ArcanistDiffUtils::isHeuristicBinaryFile($corpus); | $is_binary = ArcanistDiffUtils::isHeuristicBinaryFile($corpus); | ||||
| if (!$is_binary) { | if (!$is_binary) { | ||||
| $corpus = phutil_utf8_convert($corpus, 'UTF-8', $try_encoding); | $corpus = phutil_utf8_convert($corpus, 'UTF-8', $try_encoding); | ||||
| if (!phutil_is_utf8($corpus)) { | if (!phutil_is_utf8($corpus)) { | ||||
| throw new Exception( | throw new Exception( | ||||
| "Failed to convert a hunk from '{$try_encoding}' to UTF-8. ". | pht( | ||||
| "Check that the specified encoding is correct."); | "Failed to convert a hunk from '%s' to UTF-8. ". | ||||
| "Check that the specified encoding is correct.", | |||||
| $try_encoding)); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if ($is_binary) { | if ($is_binary) { | ||||
| // SVN happily treats binary files which aren't marked with the right | // SVN happily treats binary files which aren't marked with the right | ||||
| ▲ Show 20 Lines • Show All 353 Lines • ▼ Show 20 Lines | public static function splitGitDiffPaths($paths) { | ||||
| foreach ($patterns as $pattern) { | foreach ($patterns as $pattern) { | ||||
| if (preg_match($pattern, $paths, $matches)) { | if (preg_match($pattern, $paths, $matches)) { | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (!$matches) { | if (!$matches) { | ||||
| throw new Exception( | throw new Exception( | ||||
| "Input diff contains ambiguous line 'diff --git {$paths}'. This line ". | pht( | ||||
| "is ambiguous because there are spaces in the file names, so the ". | "Input diff contains ambiguous line '%s'. This line is ambiguous ". | ||||
| "parser can not determine where the file names begin and end. To ". | "because there are spaces in the file names, so the parser can not ". | ||||
| "resolve this ambiguity, use standard prefixes ('a/' and 'b/') when ". | "determine where the file names begin and end. To resolve this ". | ||||
| "generating diffs."); | "ambiguity, use standard prefixes ('a/' and 'b/') when ". | ||||
| "generating diffs.", | |||||
| "diff --git {$paths}")); | |||||
| } | } | ||||
| $old = $matches['old']; | $old = $matches['old']; | ||||
| $old = self::unescapeFilename($old); | $old = self::unescapeFilename($old); | ||||
| $old = self::stripGitPathPrefix($old); | $old = self::stripGitPathPrefix($old); | ||||
| $new = $matches['new']; | $new = $matches['new']; | ||||
| $new = self::unescapeFilename($new); | $new = self::unescapeFilename($new); | ||||
| ▲ Show 20 Lines • Show All 43 Lines • Show Last 20 Lines | |||||