Changeset View
Changeset View
Standalone View
Standalone View
src/infrastructure/diff/PhabricatorDifferenceEngine.php
<?php | <?php | ||||
/** | /** | ||||
* Utility class which encapsulates some shared behavior between different | * Utility class which encapsulates some shared behavior between different | ||||
* applications which render diffs. | * applications which render diffs. | ||||
* | * | ||||
* @task config Configuring the Engine | * @task config Configuring the Engine | ||||
* @task diff Generating Diffs | * @task diff Generating Diffs | ||||
*/ | */ | ||||
final class PhabricatorDifferenceEngine extends Phobject { | final class PhabricatorDifferenceEngine extends Phobject { | ||||
private $ignoreWhitespace; | |||||
private $oldName; | private $oldName; | ||||
private $newName; | private $newName; | ||||
/* -( Configuring the Engine )--------------------------------------------- */ | /* -( Configuring the Engine )--------------------------------------------- */ | ||||
/** | /** | ||||
* If true, ignore whitespace when computing differences. | |||||
* | |||||
* @param bool Ignore whitespace? | |||||
* @return this | |||||
* @task config | |||||
*/ | |||||
public function setIgnoreWhitespace($ignore_whitespace) { | |||||
$this->ignoreWhitespace = $ignore_whitespace; | |||||
return $this; | |||||
} | |||||
/** | |||||
* Set the name to identify the old file with. Primarily cosmetic. | * Set the name to identify the old file with. Primarily cosmetic. | ||||
* | * | ||||
* @param string Old file name. | * @param string Old file name. | ||||
* @return this | * @return this | ||||
* @task config | * @task config | ||||
*/ | */ | ||||
public function setOldName($old_name) { | public function setOldName($old_name) { | ||||
$this->oldName = $old_name; | $this->oldName = $old_name; | ||||
Show All 25 Lines | /* -( Generating Diffs )--------------------------------------------------- */ | ||||
* @param string Entire previous file content. | * @param string Entire previous file content. | ||||
* @param string Entire current file content. | * @param string Entire current file content. | ||||
* @return string Raw diff between the two files. | * @return string Raw diff between the two files. | ||||
* @task diff | * @task diff | ||||
*/ | */ | ||||
public function generateRawDiffFromFileContent($old, $new) { | public function generateRawDiffFromFileContent($old, $new) { | ||||
$options = array(); | $options = array(); | ||||
if ($this->ignoreWhitespace) { | |||||
$options[] = '-bw'; | |||||
} | |||||
// Generate diffs with full context. | // Generate diffs with full context. | ||||
$options[] = '-U65535'; | $options[] = '-U65535'; | ||||
$old_name = nonempty($this->oldName, '/dev/universe').' 9999-99-99'; | $old_name = nonempty($this->oldName, '/dev/universe').' 9999-99-99'; | ||||
$new_name = nonempty($this->newName, '/dev/universe').' 9999-99-99'; | $new_name = nonempty($this->newName, '/dev/universe').' 9999-99-99'; | ||||
$options[] = '-L'; | $options[] = '-L'; | ||||
$options[] = $old_name; | $options[] = $old_name; | ||||
$options[] = '-L'; | $options[] = '-L'; | ||||
$options[] = $new_name; | $options[] = $new_name; | ||||
$old_tmp = new TempFile(); | $old_tmp = new TempFile(); | ||||
$new_tmp = new TempFile(); | $new_tmp = new TempFile(); | ||||
Filesystem::writeFile($old_tmp, $old); | Filesystem::writeFile($old_tmp, $old); | ||||
Filesystem::writeFile($new_tmp, $new); | Filesystem::writeFile($new_tmp, $new); | ||||
list($err, $diff) = exec_manual( | list($err, $diff) = exec_manual( | ||||
'diff %Ls %s %s', | 'diff %Ls %s %s', | ||||
$options, | $options, | ||||
$old_tmp, | $old_tmp, | ||||
$new_tmp); | $new_tmp); | ||||
if (!$err) { | if (!$err) { | ||||
// This indicates that the two files are the same (or, possibly, the | // This indicates that the two files are the same. Build a synthetic, | ||||
// same modulo whitespace differences, which is why we can't do this | // changeless diff so that we can still render the raw, unchanged file | ||||
// check trivially before running `diff`). Build a synthetic, changeless | // instead of being forced to just say "this file didn't change" since we | ||||
// diff so that we can still render the raw, unchanged file instead of | // don't have the content. | ||||
// being forced to just say "this file didn't change" since we don't have | |||||
// the content. | |||||
$entire_file = explode("\n", $old); | $entire_file = explode("\n", $old); | ||||
foreach ($entire_file as $k => $line) { | foreach ($entire_file as $k => $line) { | ||||
$entire_file[$k] = ' '.$line; | $entire_file[$k] = ' '.$line; | ||||
} | } | ||||
$len = count($entire_file); | $len = count($entire_file); | ||||
$entire_file = implode("\n", $entire_file); | $entire_file = implode("\n", $entire_file); | ||||
// TODO: If both files were identical but missing newlines, we probably | // TODO: If both files were identical but missing newlines, we probably | ||||
// get this wrong. Unclear if it ever matters. | // get this wrong. Unclear if it ever matters. | ||||
// This is a bit hacky but the diff parser can handle it. | // This is a bit hacky but the diff parser can handle it. | ||||
$diff = "--- {$old_name}\n". | $diff = "--- {$old_name}\n". | ||||
"+++ {$new_name}\n". | "+++ {$new_name}\n". | ||||
"@@ -1,{$len} +1,{$len} @@\n". | "@@ -1,{$len} +1,{$len} @@\n". | ||||
$entire_file."\n"; | $entire_file."\n"; | ||||
} else { | |||||
if ($this->ignoreWhitespace) { | |||||
// Under "-bw", `diff` is inconsistent about emitting "\ No newline | |||||
// at end of file". For instance, a long file with a change in the | |||||
// middle will emit a contextless "\ No newline..." at the end if a | |||||
// newline is removed, but not if one is added. A file with a change | |||||
// at the end will emit the "old" "\ No newline..." block only, even | |||||
// if the newline was not removed. Since we're ostensibly ignoring | |||||
// whitespace changes, just drop these lines if they appear anywhere | |||||
// in the diff. | |||||
$lines = explode("\n", $diff); | |||||
foreach ($lines as $key => $line) { | |||||
if (isset($line[0]) && $line[0] == '\\') { | |||||
unset($lines[$key]); | |||||
} | |||||
} | |||||
$diff = implode("\n", $lines); | |||||
} | |||||
} | } | ||||
return $diff; | return $diff; | ||||
} | } | ||||
/** | /** | ||||
* Generate an @{class:DifferentialChangeset} from two raw files. This is | * Generate an @{class:DifferentialChangeset} from two raw files. This is | ||||
Show All 18 Lines |