diff --git a/scripts/arcanist.php b/scripts/arcanist.php index 6f1265ba..99af52e8 100755 --- a/scripts/arcanist.php +++ b/scripts/arcanist.php @@ -32,12 +32,17 @@ phutil_require_module('arcanist', 'repository/api/base'); $config_trace_mode = false; $args = array_slice($argv, 1); +$load = array(); +$matches = null; foreach ($args as $key => $arg) { if ($arg == '--') { break; } else if ($arg == '--trace') { unset($args[$key]); $config_trace_mode = true; + } else if (preg_match('/^--load-phutil-library=(.*)$/', $arg, $matches)) { + unset($args[$key]); + $load['?'] = $matches[1]; } } @@ -54,7 +59,11 @@ try { } $working_copy = ArcanistWorkingCopyIdentity::newFromPath($_SERVER['PWD']); - $libs = $working_copy->getConfig('phutil_libraries'); + if ($load) { + $libs = $load; + } else { + $libs = $working_copy->getConfig('phutil_libraries'); + } if ($libs) { foreach ($libs as $name => $location) { if ($config_trace_mode) { diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2b53d7b8..d0d784a4 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -59,6 +59,7 @@ phutil_register_library_map(array( 'ArcanistSubversionAPI' => 'repository/api/subversion', 'ArcanistSvnHookPreCommitWorkflow' => 'workflow/svn-hook-pre-commit', 'ArcanistTextLinter' => 'lint/linter/text', + 'ArcanistTextLinterTestCase' => 'lint/linter/text/__tests__', 'ArcanistUnitTestResult' => 'unit/result', 'ArcanistUnitWorkflow' => 'workflow/unit', 'ArcanistUsageException' => 'exception/usage', @@ -104,6 +105,7 @@ phutil_register_library_map(array( 'ArcanistSubversionAPI' => 'ArcanistRepositoryAPI', 'ArcanistSvnHookPreCommitWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistTextLinter' => 'ArcanistLinter', + 'ArcanistTextLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistUnitWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistUserAbortException' => 'ArcanistUsageException', 'ArcanistXHPASTLinter' => 'ArcanistLinter', diff --git a/src/differential/commitmessage/ArcanistDifferentialCommitMessage.php b/src/differential/commitmessage/ArcanistDifferentialCommitMessage.php index 5b45084f..728397ca 100644 --- a/src/differential/commitmessage/ArcanistDifferentialCommitMessage.php +++ b/src/differential/commitmessage/ArcanistDifferentialCommitMessage.php @@ -30,8 +30,9 @@ class ArcanistDifferentialCommitMessage { $obj = new ArcanistDifferentialCommitMessage(); $obj->rawCorpus = $corpus; + // TODO: Remove "Diffcamp" backward compatibility. $match = null; - if (preg_match('/^Differential Revision:\s*D?(\d+)/m', $corpus, $match)) { + if (preg_match('/^(?:Differential|DiffCamp) Revision:\s*D?(\d+)/im', $corpus, $match)) { $obj->revisionID = (int)$match[1]; } diff --git a/src/lint/linter/apachelicense/__tests__/ArcanistApacheLicenseLinterTestCase.php b/src/lint/linter/apachelicense/__tests__/ArcanistApacheLicenseLinterTestCase.php index b06c3ec1..96643aea 100644 --- a/src/lint/linter/apachelicense/__tests__/ArcanistApacheLicenseLinterTestCase.php +++ b/src/lint/linter/apachelicense/__tests__/ArcanistApacheLicenseLinterTestCase.php @@ -20,7 +20,11 @@ class ArcanistApacheLicenseLinterTestCase extends ArcanistLinterTestCase { public function testApacheLicenseLint() { $linter = new ArcanistApacheLicenseLinter(); - return $this->executeTestsInDirectory(dirname(__FILE__).'/data/', $linter); + $working_copy = ArcanistWorkingCopyIdentity::newFromPath(__FILE__); + return $this->executeTestsInDirectory( + dirname(__FILE__).'/data/', + $linter, + $working_copy); } } diff --git a/src/lint/linter/apachelicense/__tests__/__init__.php b/src/lint/linter/apachelicense/__tests__/__init__.php index d9c8c50f..49d0c3a4 100644 --- a/src/lint/linter/apachelicense/__tests__/__init__.php +++ b/src/lint/linter/apachelicense/__tests__/__init__.php @@ -8,6 +8,7 @@ phutil_require_module('arcanist', 'lint/linter/apachelicense'); phutil_require_module('arcanist', 'lint/linter/base/test'); +phutil_require_module('arcanist', 'workingcopyidentity'); phutil_require_source('ArcanistApacheLicenseLinterTestCase.php'); diff --git a/src/lint/linter/base/test/ArcanistLinterTestCase.php b/src/lint/linter/base/test/ArcanistLinterTestCase.php index 10d6a291..8b02bdf1 100644 --- a/src/lint/linter/base/test/ArcanistLinterTestCase.php +++ b/src/lint/linter/base/test/ArcanistLinterTestCase.php @@ -18,17 +18,15 @@ abstract class ArcanistLinterTestCase extends ArcanistPhutilTestCase { - public function executeTestsInDirectory($root, $linter) { + public function executeTestsInDirectory($root, $linter, $working_copy) { foreach (Filesystem::listDirectory($root, $hidden = false) as $file) { - $this->lintFile($root.$file, $linter); + $this->lintFile($root.$file, $linter, $working_copy); } } - private function lintFile($file, $linter) { + private function lintFile($file, $linter, $working_copy) { $linter = clone $linter; - $working_copy = ArcanistWorkingCopyIdentity::newFromPath(__FILE__); - $contents = Filesystem::readFile($file); $contents = explode("~~~~~~~~~~\n", $contents); if (count($contents) < 2) { @@ -77,8 +75,7 @@ abstract class ArcanistLinterTestCase extends ArcanistPhutilTestCase { $engine->setWorkingCopy($working_copy); $engine->setPaths(array($path)); -// TODO: restore this -// $engine->setCommitHookMode(idx($config, 'hook', false)); + $engine->setCommitHookMode(idx($config, 'hook', false)); $linter->addPath($path); $linter->addData($path, $data); diff --git a/src/lint/linter/base/test/__init__.php b/src/lint/linter/base/test/__init__.php index c51dbca4..3ae02c6e 100644 --- a/src/lint/linter/base/test/__init__.php +++ b/src/lint/linter/base/test/__init__.php @@ -9,7 +9,6 @@ phutil_require_module('arcanist', 'lint/engine/test'); phutil_require_module('arcanist', 'lint/patcher'); phutil_require_module('arcanist', 'unit/engine/phutil/testcase'); -phutil_require_module('arcanist', 'workingcopyidentity'); phutil_require_module('phutil', 'filesystem'); phutil_require_module('phutil', 'utils'); diff --git a/src/lint/linter/text/ArcanistTextLinter.php b/src/lint/linter/text/ArcanistTextLinter.php index 61ccad07..23c24c77 100644 --- a/src/lint/linter/text/ArcanistTextLinter.php +++ b/src/lint/linter/text/ArcanistTextLinter.php @@ -24,6 +24,7 @@ class ArcanistTextLinter extends ArcanistLinter { const LINT_EOF_NEWLINE = 4; const LINT_BAD_CHARSET = 5; const LINT_TRAILING_WHITESPACE = 6; + const LINT_NO_COMMIT = 7; private $maxLineLength = 80; @@ -54,6 +55,7 @@ class ArcanistTextLinter extends ArcanistLinter { self::LINT_EOF_NEWLINE => 'File Does Not End in Newline', self::LINT_BAD_CHARSET => 'Bad Charset', self::LINT_TRAILING_WHITESPACE => 'Trailing Whitespace', + self::LINT_NO_COMMIT => 'Explicit @no'.'commit', ); } @@ -74,6 +76,10 @@ class ArcanistTextLinter extends ArcanistLinter { $this->lintLineLength($path); $this->lintEOFNewline($path); $this->lintTrailingWhitespace($path); + + if ($this->getEngine()->getCommitHookMode()) { + $this->lintNoCommit($path); + } } protected function lintNewlines($path) { @@ -181,4 +187,21 @@ class ArcanistTextLinter extends ArcanistLinter { } } + private function lintNoCommit($path) { + $data = $this->getData($path); + + $deadly = '@no'.'commit'; + + $offset = strpos($data, $deadly); + if ($offset !== false) { + $this->raiseLintAtOffset( + $offset, + self::LINT_NO_COMMIT, + 'This file is explicitly marked as "'.$deadly.'", which blocks '. + 'commits.', + $deadly); + } + } + + } diff --git a/src/lint/linter/text/__tests__/ArcanistTextLinterTestCase.php b/src/lint/linter/text/__tests__/ArcanistTextLinterTestCase.php new file mode 100644 index 00000000..caff6243 --- /dev/null +++ b/src/lint/linter/text/__tests__/ArcanistTextLinterTestCase.php @@ -0,0 +1,30 @@ +executeTestsInDirectory( + dirname(__FILE__).'/data/', + $linter, + $working_copy); + } + +} diff --git a/src/lint/linter/text/__tests__/__init__.php b/src/lint/linter/text/__tests__/__init__.php new file mode 100644 index 00000000..773f78c6 --- /dev/null +++ b/src/lint/linter/text/__tests__/__init__.php @@ -0,0 +1,14 @@ +executeTestsInDirectory(dirname(__FILE__).'/data/', $linter); + $working_copy = ArcanistWorkingCopyIdentity::newFromPath(__FILE__); + return $this->executeTestsInDirectory( + dirname(__FILE__).'/data/', + $linter, + $working_copy); } } diff --git a/src/lint/linter/xhpast/__tests__/__init__.php b/src/lint/linter/xhpast/__tests__/__init__.php index bed07e76..d960ac84 100644 --- a/src/lint/linter/xhpast/__tests__/__init__.php +++ b/src/lint/linter/xhpast/__tests__/__init__.php @@ -8,6 +8,7 @@ phutil_require_module('arcanist', 'lint/linter/base/test'); phutil_require_module('arcanist', 'lint/linter/xhpast'); +phutil_require_module('arcanist', 'workingcopyidentity'); phutil_require_source('ArcanistXHPASTLinterTestCase.php'); diff --git a/src/workflow/amend/ArcanistAmendWorkflow.php b/src/workflow/amend/ArcanistAmendWorkflow.php index afa5c605..4c14013f 100644 --- a/src/workflow/amend/ArcanistAmendWorkflow.php +++ b/src/workflow/amend/ArcanistAmendWorkflow.php @@ -124,6 +124,10 @@ EOTEXT array($revision_id)); $mark_workflow->run(); } + + echo phutil_console_wrap( + "You may now push this commit upstream, as appropriate (e.g. with ". + "'git push', or 'git svn dcommit', or by printing and faxing it).\n"); } return 0; diff --git a/src/workflow/shell-complete/ArcanistShellCompleteWorkflow.php b/src/workflow/shell-complete/ArcanistShellCompleteWorkflow.php index 6b117761..9ce06663 100644 --- a/src/workflow/shell-complete/ArcanistShellCompleteWorkflow.php +++ b/src/workflow/shell-complete/ArcanistShellCompleteWorkflow.php @@ -123,6 +123,7 @@ EOTEXT } return 0; } else { + $output = array(); foreach ($arguments as $argument => $spec) { if ($argument == '*') { @@ -135,8 +136,23 @@ EOTEXT } $output[] = '--'.$argument; } - - echo implode(' ', $output)."\n"; + + $cur = idx($argv, $pos, ''); + $any_match = false; + foreach ($output as $possible) { + if (!strncmp($possible, $cur, strlen($cur))) { + $any_match = true; + } + } + + if (!$any_match && isset($arguments['*'])) { + // TODO: the '*' specifier should probably have more details about + // whether or not it is a list of files. Since it almost always is in + // practice, assume FILE for now. + echo "FILE\n"; + } else { + echo implode(' ', $output)."\n"; + } return 0; } } diff --git a/src/workflow/unit/ArcanistUnitWorkflow.php b/src/workflow/unit/ArcanistUnitWorkflow.php index e4d517e8..46d632ed 100644 --- a/src/workflow/unit/ArcanistUnitWorkflow.php +++ b/src/workflow/unit/ArcanistUnitWorkflow.php @@ -36,6 +36,11 @@ EOTEXT public function getArguments() { return array( + 'engine' => array( + 'param' => 'classname', + 'help' => + "Override configured unit engine for this project." + ), '*' => 'paths', ); }