diff --git a/src/lint/linter/xhpast/rules/ArcanistDeprecationXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistDeprecationXHPASTLinterRule.php index bc57bb2d..d94192b5 100644 --- a/src/lint/linter/xhpast/rules/ArcanistDeprecationXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistDeprecationXHPASTLinterRule.php @@ -20,7 +20,7 @@ final class ArcanistDeprecationXHPASTLinterRule 'xhpast.deprecated.functions' => array( 'type' => 'optional map', 'help' => pht( - 'Functions which should should be considered deprecated.'), + 'Functions which should be considered deprecated.'), ), ); } diff --git a/src/lint/linter/xhpast/rules/ArcanistUnsafeDynamicStringXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistUnsafeDynamicStringXHPASTLinterRule.php index bf3c8e51..67cdfcc0 100644 --- a/src/lint/linter/xhpast/rules/ArcanistUnsafeDynamicStringXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistUnsafeDynamicStringXHPASTLinterRule.php @@ -21,14 +21,14 @@ final class ArcanistUnsafeDynamicStringXHPASTLinterRule 'xhpast.dynamic-string.classes' => array( 'type' => 'optional map', 'help' => pht( - 'Classes which should should not be used because they represent the '. + 'Classes which should not be used because they represent the '. 'unsafe usage of dynamic strings.'), ), 'xhpast.dynamic-string.functions' => array( 'type' => 'optional map', 'help' => pht( - 'Functions which should should not be used because they represent '. - 'the unsafe usage of dynamic strings.'), + 'Functions which should not be used because they represent the '. + 'unsafe usage of dynamic strings.'), ), ); diff --git a/src/parser/ArcanistBundle.php b/src/parser/ArcanistBundle.php index 0ec9a2ef..6d72047b 100644 --- a/src/parser/ArcanistBundle.php +++ b/src/parser/ArcanistBundle.php @@ -560,7 +560,16 @@ final class ArcanistBundle extends Phobject { } } - if ($jj - $last_change > (($context + 1) * 2)) { + // If the context value is "3" and there are 7 unchanged lines + // between the two changes, we could either generate one or two hunks + // and end up with the same number of output lines. If we generate + // one hunk, the middle line will be a line of source. If we generate + // two hunks, the middle line will be an "@@ -1,2 +3,4 @@" header. + + // We choose to generate two hunks because this is the behavior of + // "diff -u". See PHI838. + + if ($jj - $last_change >= ($context * 2 + 1)) { // We definitely aren't going to merge this with the next hunk, so // break out of the loop. We'll end the hunk at $break_here. break; diff --git a/src/parser/__tests__/ArcanistBundleTestCase.php b/src/parser/__tests__/ArcanistBundleTestCase.php index f64e8992..0b183324 100644 --- a/src/parser/__tests__/ArcanistBundleTestCase.php +++ b/src/parser/__tests__/ArcanistBundleTestCase.php @@ -642,6 +642,24 @@ EODIFF; 'disjoint-hunks.new')->toUnifiedDiff()); } + public function testMergeHunks() { + // Hunks should merge if represented by sufficiently few unchanged + // lines. + $this->assertEqual( + $this->loadResource('merge-hunks.diff'), + $this->loadOneChangeBundle( + 'merge-hunks.old', + 'merge-hunks.new')->toUnifiedDiff()); + + // Hunks should not merge if they are separated by too many unchanged + // lines. + $this->assertEqual( + $this->loadResource('no-merge-hunks.diff'), + $this->loadOneChangeBundle( + 'no-merge-hunks.old', + 'no-merge-hunks.new')->toUnifiedDiff()); + } + public function testNonlocalTrailingNewline() { // Diffs without changes near the end of the file should not generate a // bogus, change-free hunk if the file has no trailing newline. diff --git a/src/parser/__tests__/bundle/merge-hunks.diff b/src/parser/__tests__/bundle/merge-hunks.diff new file mode 100644 index 00000000..ea5ca64b --- /dev/null +++ b/src/parser/__tests__/bundle/merge-hunks.diff @@ -0,0 +1,19 @@ +Index: file +=================================================================== +--- file ++++ file +@@ -2,12 +2,14 @@ + B + C + D ++Chocolate + E + F + G + H + I + J ++Caramel + K + L + M diff --git a/src/parser/__tests__/bundle/merge-hunks.new b/src/parser/__tests__/bundle/merge-hunks.new new file mode 100644 index 00000000..a02f54dd --- /dev/null +++ b/src/parser/__tests__/bundle/merge-hunks.new @@ -0,0 +1,17 @@ +A +B +C +D +Chocolate +E +F +G +H +I +J +Caramel +K +L +M +N +O diff --git a/src/parser/__tests__/bundle/merge-hunks.old b/src/parser/__tests__/bundle/merge-hunks.old new file mode 100644 index 00000000..cf494a19 --- /dev/null +++ b/src/parser/__tests__/bundle/merge-hunks.old @@ -0,0 +1,15 @@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O diff --git a/src/parser/__tests__/bundle/no-merge-hunks.diff b/src/parser/__tests__/bundle/no-merge-hunks.diff new file mode 100644 index 00000000..878450c9 --- /dev/null +++ b/src/parser/__tests__/bundle/no-merge-hunks.diff @@ -0,0 +1,20 @@ +Index: file +=================================================================== +--- file ++++ file +@@ -2,6 +2,7 @@ + B + C + D ++Chocolate + E + F + G +@@ -9,6 +10,7 @@ + I + J + K ++Caramel + L + M + N diff --git a/src/parser/__tests__/bundle/no-merge-hunks.new b/src/parser/__tests__/bundle/no-merge-hunks.new new file mode 100644 index 00000000..0158638d --- /dev/null +++ b/src/parser/__tests__/bundle/no-merge-hunks.new @@ -0,0 +1,17 @@ +A +B +C +D +Chocolate +E +F +G +H +I +J +K +Caramel +L +M +N +O diff --git a/src/parser/__tests__/bundle/no-merge-hunks.old b/src/parser/__tests__/bundle/no-merge-hunks.old new file mode 100644 index 00000000..cf494a19 --- /dev/null +++ b/src/parser/__tests__/bundle/no-merge-hunks.old @@ -0,0 +1,15 @@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O diff --git a/src/repository/api/ArcanistGitAPI.php b/src/repository/api/ArcanistGitAPI.php index 6a011f44..ba752162 100644 --- a/src/repository/api/ArcanistGitAPI.php +++ b/src/repository/api/ArcanistGitAPI.php @@ -1032,6 +1032,11 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI { // and treat it as though it as a file containing a list of other files, // which is silly. + if (!strlen($path)) { + // No filename, so there's no content (Probably new/deleted file). + return null; + } + list($stdout) = $this->execxLocal( 'ls-tree %s -- %s', $revision, diff --git a/src/repository/parser/ArcanistMercurialParser.php b/src/repository/parser/ArcanistMercurialParser.php index 4eb10e2f..33e59c5c 100644 --- a/src/repository/parser/ArcanistMercurialParser.php +++ b/src/repository/parser/ArcanistMercurialParser.php @@ -175,8 +175,9 @@ final class ArcanistMercurialParser extends Phobject { $commit['bookmark'] = $value; break; case 'obsolete': - // This is an extra field added by the "evolve" extension even - // if HGPLAIN=1 is set. See PHI502. + case 'instability': + // These are extra fields added by the "evolve" extension even + // if HGPLAIN=1 is set. See PHI502 and PHI718. break; default: throw new Exception(