Changeset View
Changeset View
Standalone View
Standalone View
src/workflow/ArcanistDiffWorkflow.php
| Show First 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | EOTEXT | ||||
| } | } | ||||
| public function getArguments() { | public function getArguments() { | ||||
| $arguments = array( | $arguments = array( | ||||
| 'message' => array( | 'message' => array( | ||||
| 'short' => 'm', | 'short' => 'm', | ||||
| 'param' => 'message', | 'param' => 'message', | ||||
| 'help' => | 'help' => | ||||
| "When updating a revision, use the specified message instead of ". | 'When updating a revision, use the specified message instead of '. | ||||
| "prompting.", | 'prompting.', | ||||
| ), | ), | ||||
| 'message-file' => array( | 'message-file' => array( | ||||
| 'short' => 'F', | 'short' => 'F', | ||||
| 'param' => 'file', | 'param' => 'file', | ||||
| 'paramtype' => 'file', | 'paramtype' => 'file', | ||||
| 'help' => 'When creating a revision, read revision information '. | 'help' => 'When creating a revision, read revision information '. | ||||
| 'from this file.', | 'from this file.', | ||||
| ), | ), | ||||
| Show All 15 Lines | $arguments = array( | ||||
| 'supports' => array( | 'supports' => array( | ||||
| 'git', | 'git', | ||||
| 'hg', | 'hg', | ||||
| ), | ), | ||||
| 'nosupport' => array( | 'nosupport' => array( | ||||
| 'svn' => 'Edit revisions via the web interface when using SVN.', | 'svn' => 'Edit revisions via the web interface when using SVN.', | ||||
| ), | ), | ||||
| 'help' => | 'help' => | ||||
| "When updating a revision under git, edit revision information ". | 'When updating a revision under git, edit revision information '. | ||||
| "before updating.", | 'before updating.', | ||||
| ), | ), | ||||
| 'raw' => array( | 'raw' => array( | ||||
| 'help' => | 'help' => | ||||
| "Read diff from stdin, not from the working copy. This disables ". | 'Read diff from stdin, not from the working copy. This disables '. | ||||
| "many Arcanist/Phabricator features which depend on having access ". | 'many Arcanist/Phabricator features which depend on having access '. | ||||
| "to the working copy.", | 'to the working copy.', | ||||
| 'conflicts' => array( | 'conflicts' => array( | ||||
| 'less-context' => null, | 'less-context' => null, | ||||
| 'apply-patches' => '--raw disables lint.', | 'apply-patches' => '--raw disables lint.', | ||||
| 'never-apply-patches' => '--raw disables lint.', | 'never-apply-patches' => '--raw disables lint.', | ||||
| 'advice' => '--raw disables lint.', | 'advice' => '--raw disables lint.', | ||||
| 'lintall' => '--raw disables lint.', | 'lintall' => '--raw disables lint.', | ||||
| 'create' => '--raw and --create both need stdin. '. | 'create' => '--raw and --create both need stdin. '. | ||||
| 'Use --raw-command.', | 'Use --raw-command.', | ||||
| 'edit' => '--raw and --edit both need stdin. '. | 'edit' => '--raw and --edit both need stdin. '. | ||||
| 'Use --raw-command.', | 'Use --raw-command.', | ||||
| 'raw-command' => null, | 'raw-command' => null, | ||||
| ), | ), | ||||
| ), | ), | ||||
| 'raw-command' => array( | 'raw-command' => array( | ||||
| 'param' => 'command', | 'param' => 'command', | ||||
| 'help' => | 'help' => | ||||
| "Generate diff by executing a specified command, not from the ". | 'Generate diff by executing a specified command, not from the '. | ||||
| "working copy. This disables many Arcanist/Phabricator features ". | 'working copy. This disables many Arcanist/Phabricator features '. | ||||
| "which depend on having access to the working copy.", | 'which depend on having access to the working copy.', | ||||
| 'conflicts' => array( | 'conflicts' => array( | ||||
| 'less-context' => null, | 'less-context' => null, | ||||
| 'apply-patches' => '--raw-command disables lint.', | 'apply-patches' => '--raw-command disables lint.', | ||||
| 'never-apply-patches' => '--raw-command disables lint.', | 'never-apply-patches' => '--raw-command disables lint.', | ||||
| 'advice' => '--raw-command disables lint.', | 'advice' => '--raw-command disables lint.', | ||||
| 'lintall' => '--raw-command disables lint.', | 'lintall' => '--raw-command disables lint.', | ||||
| ), | ), | ||||
| ), | ), | ||||
| 'create' => array( | 'create' => array( | ||||
| 'help' => "Always create a new revision.", | 'help' => 'Always create a new revision.', | ||||
| 'conflicts' => array( | 'conflicts' => array( | ||||
| 'edit' => '--create can not be used with --edit.', | 'edit' => '--create can not be used with --edit.', | ||||
| 'only' => '--create can not be used with --only.', | 'only' => '--create can not be used with --only.', | ||||
| 'preview' => '--create can not be used with --preview.', | 'preview' => '--create can not be used with --preview.', | ||||
| 'update' => '--create can not be used with --update.', | 'update' => '--create can not be used with --update.', | ||||
| ), | ), | ||||
| ), | ), | ||||
| 'update' => array( | 'update' => array( | ||||
| 'param' => 'revision_id', | 'param' => 'revision_id', | ||||
| 'help' => "Always update a specific revision.", | 'help' => 'Always update a specific revision.', | ||||
| ), | ), | ||||
| 'nounit' => array( | 'nounit' => array( | ||||
| 'help' => | 'help' => | ||||
| "Do not run unit tests.", | 'Do not run unit tests.', | ||||
| ), | ), | ||||
| 'nolint' => array( | 'nolint' => array( | ||||
| 'help' => | 'help' => | ||||
| "Do not run lint.", | 'Do not run lint.', | ||||
| 'conflicts' => array( | 'conflicts' => array( | ||||
| 'lintall' => '--nolint suppresses lint.', | 'lintall' => '--nolint suppresses lint.', | ||||
| 'advice' => '--nolint suppresses lint.', | 'advice' => '--nolint suppresses lint.', | ||||
| 'apply-patches' => '--nolint suppresses lint.', | 'apply-patches' => '--nolint suppresses lint.', | ||||
| 'never-apply-patches' => '--nolint suppresses lint.', | 'never-apply-patches' => '--nolint suppresses lint.', | ||||
| ), | ), | ||||
| ), | ), | ||||
| 'only' => array( | 'only' => array( | ||||
| 'help' => | 'help' => | ||||
| "Only generate a diff, without running lint, unit tests, or other ". | 'Only generate a diff, without running lint, unit tests, or other '. | ||||
| "auxiliary steps. See also --preview.", | 'auxiliary steps. See also --preview.', | ||||
| 'conflicts' => array( | 'conflicts' => array( | ||||
| 'preview' => null, | 'preview' => null, | ||||
| 'message' => '--only does not affect revisions.', | 'message' => '--only does not affect revisions.', | ||||
| 'edit' => '--only does not affect revisions.', | 'edit' => '--only does not affect revisions.', | ||||
| 'lintall' => '--only suppresses lint.', | 'lintall' => '--only suppresses lint.', | ||||
| 'advice' => '--only suppresses lint.', | 'advice' => '--only suppresses lint.', | ||||
| 'apply-patches' => '--only suppresses lint.', | 'apply-patches' => '--only suppresses lint.', | ||||
| 'never-apply-patches' => '--only suppresses lint.', | 'never-apply-patches' => '--only suppresses lint.', | ||||
| ), | ), | ||||
| ), | ), | ||||
| 'preview' => array( | 'preview' => array( | ||||
| 'help' => | 'help' => | ||||
| "Instead of creating or updating a revision, only create a diff, ". | 'Instead of creating or updating a revision, only create a diff, '. | ||||
| "which you may later attach to a revision. This still runs lint ". | 'which you may later attach to a revision. This still runs lint '. | ||||
| "unit tests. See also --only.", | 'unit tests. See also --only.', | ||||
| 'conflicts' => array( | 'conflicts' => array( | ||||
| 'only' => null, | 'only' => null, | ||||
| 'edit' => '--preview does affect revisions.', | 'edit' => '--preview does affect revisions.', | ||||
| 'message' => '--preview does not update any revision.', | 'message' => '--preview does not update any revision.', | ||||
| ), | ), | ||||
| ), | ), | ||||
| 'plan-changes' => array( | 'plan-changes' => array( | ||||
| 'help' => | 'help' => | ||||
| "Create or update a revision without requesting a code review.", | 'Create or update a revision without requesting a code review.', | ||||
| 'conflicts' => array( | 'conflicts' => array( | ||||
| 'only' => '--only does not affect revisions.', | 'only' => '--only does not affect revisions.', | ||||
| 'preview' => '--preview does not affect revisions.', | 'preview' => '--preview does not affect revisions.', | ||||
| ), | ), | ||||
| ), | ), | ||||
| 'encoding' => array( | 'encoding' => array( | ||||
| 'param' => 'encoding', | 'param' => 'encoding', | ||||
| 'help' => | 'help' => | ||||
| "Attempt to convert non UTF-8 hunks into specified encoding.", | 'Attempt to convert non UTF-8 hunks into specified encoding.', | ||||
| ), | ), | ||||
| 'allow-untracked' => array( | 'allow-untracked' => array( | ||||
| 'help' => | 'help' => | ||||
| "Skip checks for untracked files in the working copy.", | 'Skip checks for untracked files in the working copy.', | ||||
| ), | ), | ||||
| 'excuse' => array( | 'excuse' => array( | ||||
| 'param' => 'excuse', | 'param' => 'excuse', | ||||
| 'help' => 'Provide a prepared in advance excuse for any lints/tests'. | 'help' => 'Provide a prepared in advance excuse for any lints/tests'. | ||||
| ' shall they fail.', | ' shall they fail.', | ||||
| ), | ), | ||||
| 'less-context' => array( | 'less-context' => array( | ||||
| 'help' => | 'help' => | ||||
| "Normally, files are diffed with full context: the entire file is ". | "Normally, files are diffed with full context: the entire file is ". | ||||
| "sent to Differential so reviewers can 'show more' and see it. If ". | "sent to Differential so reviewers can 'show more' and see it. If ". | ||||
| "you are making changes to very large files with tens of thousands ". | "you are making changes to very large files with tens of thousands ". | ||||
| "of lines, this may not work well. With this flag, a diff will ". | "of lines, this may not work well. With this flag, a diff will ". | ||||
| "be created that has only a few lines of context.", | "be created that has only a few lines of context.", | ||||
| ), | ), | ||||
| 'lintall' => array( | 'lintall' => array( | ||||
| 'help' => | 'help' => | ||||
| "Raise all lint warnings, not just those on lines you changed.", | 'Raise all lint warnings, not just those on lines you changed.', | ||||
| 'passthru' => array( | 'passthru' => array( | ||||
| 'lint' => true, | 'lint' => true, | ||||
| ), | ), | ||||
| ), | ), | ||||
| 'advice' => array( | 'advice' => array( | ||||
| 'help' => | 'help' => | ||||
| "Require excuse for lint advice in addition to lint warnings and ". | 'Require excuse for lint advice in addition to lint warnings and '. | ||||
| "errors.", | 'errors.', | ||||
| ), | ), | ||||
| 'only-new' => array( | 'only-new' => array( | ||||
| 'param' => 'bool', | 'param' => 'bool', | ||||
| 'help' => | 'help' => | ||||
| 'Display only lint messages not present in the original code.', | 'Display only lint messages not present in the original code.', | ||||
| 'passthru' => array( | 'passthru' => array( | ||||
| 'lint' => true, | 'lint' => true, | ||||
| ), | ), | ||||
| ▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | $arguments = array( | ||||
| 'background' => array( | 'background' => array( | ||||
| 'param' => 'bool', | 'param' => 'bool', | ||||
| 'help' => | 'help' => | ||||
| 'Run lint and unit tests on background. '. | 'Run lint and unit tests on background. '. | ||||
| '"0" to disable, "1" to enable (default).', | '"0" to disable, "1" to enable (default).', | ||||
| ), | ), | ||||
| 'cache' => array( | 'cache' => array( | ||||
| 'param' => 'bool', | 'param' => 'bool', | ||||
| 'help' => "0 to disable lint cache, 1 to enable (default).", | 'help' => '0 to disable lint cache, 1 to enable (default).', | ||||
| 'passthru' => array( | 'passthru' => array( | ||||
| 'lint' => true, | 'lint' => true, | ||||
| ), | ), | ||||
| ), | ), | ||||
| 'coverage' => array( | 'coverage' => array( | ||||
| 'help' => 'Always enable coverage information.', | 'help' => 'Always enable coverage information.', | ||||
| 'conflicts' => array( | 'conflicts' => array( | ||||
| 'no-coverage' => null, | 'no-coverage' => null, | ||||
| ▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | if ($this->getArgument('nounit')) { | ||||
| $this->excuses['unit'] = $this->getSkipExcuse( | $this->excuses['unit'] = $this->getSkipExcuse( | ||||
| 'Provide explanation for skipping unit tests or press Enter to abort:', | 'Provide explanation for skipping unit tests or press Enter to abort:', | ||||
| 'unit-excuses'); | 'unit-excuses'); | ||||
| } | } | ||||
| $changes = $this->generateChanges(); | $changes = $this->generateChanges(); | ||||
| if (!$changes) { | if (!$changes) { | ||||
| throw new ArcanistUsageException( | throw new ArcanistUsageException( | ||||
| "There are no changes to generate a diff from!"); | 'There are no changes to generate a diff from!'); | ||||
| } | } | ||||
| $diff_spec = array( | $diff_spec = array( | ||||
| 'changes' => mpull($changes, 'toDictionary'), | 'changes' => mpull($changes, 'toDictionary'), | ||||
| 'lintStatus' => $this->getLintStatus($lint_result), | 'lintStatus' => $this->getLintStatus($lint_result), | ||||
| 'unitStatus' => $this->getUnitStatus($unit_result), | 'unitStatus' => $this->getUnitStatus($unit_result), | ||||
| ) + $this->buildDiffSpecification(); | ) + $this->buildDiffSpecification(); | ||||
| ▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | if ($this->shouldOnlyCreateDiff()) { | ||||
| )); | )); | ||||
| if ($this->shouldAmend()) { | if ($this->shouldAmend()) { | ||||
| $repository_api = $this->getRepositoryAPI(); | $repository_api = $this->getRepositoryAPI(); | ||||
| if ($repository_api->supportsAmend()) { | if ($repository_api->supportsAmend()) { | ||||
| echo "Updating commit message...\n"; | echo "Updating commit message...\n"; | ||||
| $repository_api->amendCommit($revised_message); | $repository_api->amendCommit($revised_message); | ||||
| } else { | } else { | ||||
| echo "Commit message was not amended. Amending commit message is ". | echo 'Commit message was not amended. Amending commit message is '. | ||||
| "only supported in git and hg (version 2.2 or newer)"; | 'only supported in git and hg (version 2.2 or newer)'; | ||||
| } | } | ||||
| } | } | ||||
| echo "Created a new Differential revision:\n"; | echo "Created a new Differential revision:\n"; | ||||
| } | } | ||||
| $uri = $result['uri']; | $uri = $result['uri']; | ||||
| echo phutil_console_format( | echo phutil_console_format( | ||||
| ▲ Show 20 Lines • Show All 235 Lines • ▼ Show 20 Lines | if ($repository_api instanceof ArcanistSubversionAPI) { | ||||
| echo phutil_console_format( | echo phutil_console_format( | ||||
| "The working copy includes changes to 'svn:externals' paths. These ". | "The working copy includes changes to 'svn:externals' paths. These ". | ||||
| "changes will not be included in the diff because SVN can not ". | "changes will not be included in the diff because SVN can not ". | ||||
| "commit 'svn:externals' changes alongside normal changes.". | "commit 'svn:externals' changes alongside normal changes.". | ||||
| "\n\n". | "\n\n". | ||||
| "Modified 'svn:externals' files:". | "Modified 'svn:externals' files:". | ||||
| "\n\n". | "\n\n". | ||||
| phutil_console_wrap(implode("\n", $warn_externals), 8)); | phutil_console_wrap(implode("\n", $warn_externals), 8)); | ||||
| $prompt = "Generate a diff (with just local changes) anyway?"; | $prompt = 'Generate a diff (with just local changes) anyway?'; | ||||
| if (!phutil_console_confirm($prompt)) { | if (!phutil_console_confirm($prompt)) { | ||||
| throw new ArcanistUserAbortException(); | throw new ArcanistUserAbortException(); | ||||
| } else { | } else { | ||||
| $this->hasWarnedExternals = true; | $this->hasWarnedExternals = true; | ||||
| } | } | ||||
| } | } | ||||
| } else { | } else { | ||||
| Show All 17 Lines | protected function generateChanges() { | ||||
| if ($is_raw) { | if ($is_raw) { | ||||
| if ($this->getArgument('raw')) { | if ($this->getArgument('raw')) { | ||||
| fwrite(STDERR, "Reading diff from stdin...\n"); | fwrite(STDERR, "Reading diff from stdin...\n"); | ||||
| $raw_diff = file_get_contents('php://stdin'); | $raw_diff = file_get_contents('php://stdin'); | ||||
| } else if ($this->getArgument('raw-command')) { | } else if ($this->getArgument('raw-command')) { | ||||
| list($raw_diff) = execx('%C', $this->getArgument('raw-command')); | list($raw_diff) = execx('%C', $this->getArgument('raw-command')); | ||||
| } else { | } else { | ||||
| throw new Exception("Unknown raw diff source."); | throw new Exception('Unknown raw diff source.'); | ||||
| } | } | ||||
| $changes = $parser->parseDiff($raw_diff); | $changes = $parser->parseDiff($raw_diff); | ||||
| foreach ($changes as $key => $change) { | foreach ($changes as $key => $change) { | ||||
| // Remove "message" changes, e.g. from "git show". | // Remove "message" changes, e.g. from "git show". | ||||
| if ($change->getType() == ArcanistDiffChangeType::TYPE_MESSAGE) { | if ($change->getType() == ArcanistDiffChangeType::TYPE_MESSAGE) { | ||||
| unset($changes[$key]); | unset($changes[$key]); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | if ($repository_api instanceof ArcanistSubversionAPI) { | ||||
| $changes = $parser->parseSubversionDiff( | $changes = $parser->parseSubversionDiff( | ||||
| $repository_api, | $repository_api, | ||||
| $paths); | $paths); | ||||
| } else if ($repository_api instanceof ArcanistGitAPI) { | } else if ($repository_api instanceof ArcanistGitAPI) { | ||||
| $diff = $repository_api->getFullGitDiff(); | $diff = $repository_api->getFullGitDiff(); | ||||
| if (!strlen($diff)) { | if (!strlen($diff)) { | ||||
| throw new ArcanistUsageException( | throw new ArcanistUsageException( | ||||
| "No changes found. (Did you specify the wrong commit range?)"); | 'No changes found. (Did you specify the wrong commit range?)'); | ||||
| } | } | ||||
| $changes = $parser->parseDiff($diff); | $changes = $parser->parseDiff($diff); | ||||
| } else if ($repository_api instanceof ArcanistMercurialAPI) { | } else if ($repository_api instanceof ArcanistMercurialAPI) { | ||||
| $diff = $repository_api->getFullMercurialDiff(); | $diff = $repository_api->getFullMercurialDiff(); | ||||
| if (!strlen($diff)) { | if (!strlen($diff)) { | ||||
| throw new ArcanistUsageException( | throw new ArcanistUsageException( | ||||
| "No changes found. (Did you specify the wrong commit range?)"); | 'No changes found. (Did you specify the wrong commit range?)'); | ||||
| } | } | ||||
| $changes = $parser->parseDiff($diff); | $changes = $parser->parseDiff($diff); | ||||
| } else { | } else { | ||||
| throw new Exception("Repository API is not supported."); | throw new Exception('Repository API is not supported.'); | ||||
| } | } | ||||
| if (count($changes) > 250) { | if (count($changes) > 250) { | ||||
| $count = number_format(count($changes)); | $count = number_format(count($changes)); | ||||
| $link = | $link = | ||||
| "http://www.phabricator.com/docs/phabricator/article/". | 'http://www.phabricator.com/docs/phabricator/article/'. | ||||
| "Differential_User_Guide_Large_Changes.html"; | 'Differential_User_Guide_Large_Changes.html'; | ||||
| $message = | $message = | ||||
| "This diff has a very large number of changes ({$count}). ". | "This diff has a very large number of changes ({$count}). ". | ||||
| "Differential works best for changes which will receive detailed ". | "Differential works best for changes which will receive detailed ". | ||||
| "human review, and not as well for large automated changes or ". | "human review, and not as well for large automated changes or ". | ||||
| "bulk checkins. See {$link} for information about reviewing big ". | "bulk checkins. See {$link} for information about reviewing big ". | ||||
| "checkins. Continue anyway?"; | "checkins. Continue anyway?"; | ||||
| if (!phutil_console_confirm($message)) { | if (!phutil_console_confirm($message)) { | ||||
| throw new ArcanistUsageException( | throw new ArcanistUsageException( | ||||
| "Aborted generation of gigantic diff."); | 'Aborted generation of gigantic diff.'); | ||||
| } | } | ||||
| } | } | ||||
| $limit = 1024 * 1024 * 4; | $limit = 1024 * 1024 * 4; | ||||
| foreach ($changes as $change) { | foreach ($changes as $change) { | ||||
| $size = 0; | $size = 0; | ||||
| foreach ($change->getHunks() as $hunk) { | foreach ($change->getHunks() as $hunk) { | ||||
| $size += strlen($hunk->getCorpus()); | $size += strlen($hunk->getCorpus()); | ||||
| Show All 19 Lines | foreach ($changes as $change) { | ||||
| } else { | } else { | ||||
| $confirm = | $confirm = | ||||
| "{$byte_warning} If the file is not a text file, you can ". | "{$byte_warning} If the file is not a text file, you can ". | ||||
| "mark it 'binary'. Mark this file as 'binary' and continue?"; | "mark it 'binary'. Mark this file as 'binary' and continue?"; | ||||
| if (phutil_console_confirm($confirm)) { | if (phutil_console_confirm($confirm)) { | ||||
| $change->convertToBinaryChange($repository_api); | $change->convertToBinaryChange($repository_api); | ||||
| } else { | } else { | ||||
| throw new ArcanistUsageException( | throw new ArcanistUsageException( | ||||
| "Aborted generation of gigantic diff."); | 'Aborted generation of gigantic diff.'); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| $try_encoding = nonempty($this->getArgument('encoding'), null); | $try_encoding = nonempty($this->getArgument('encoding'), null); | ||||
| $utf8_problems = array(); | $utf8_problems = array(); | ||||
| ▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | protected function generateChanges() { | ||||
| } | } | ||||
| // If there are non-binary files which aren't valid UTF-8, warn the user | // If there are non-binary files which aren't valid UTF-8, warn the user | ||||
| // and treat them as binary changes. See D327 for discussion of why Arcanist | // and treat them as binary changes. See D327 for discussion of why Arcanist | ||||
| // has this behavior. | // has this behavior. | ||||
| if ($utf8_problems) { | if ($utf8_problems) { | ||||
| $utf8_warning = | $utf8_warning = | ||||
| pht( | pht( | ||||
| "This diff includes file(s) which are not valid UTF-8 (they contain ". | 'This diff includes file(s) which are not valid UTF-8 (they contain '. | ||||
| "invalid byte sequences). You can either stop this workflow and ". | 'invalid byte sequences). You can either stop this workflow and '. | ||||
| "fix these files, or continue. If you continue, these files will ". | 'fix these files, or continue. If you continue, these files will '. | ||||
| "be marked as binary.", | 'be marked as binary.', | ||||
| count($utf8_problems))."\n\n". | count($utf8_problems))."\n\n". | ||||
| "You can learn more about how Phabricator handles character encodings ". | "You can learn more about how Phabricator handles character encodings ". | ||||
| "(and how to configure encoding settings and detect and correct ". | "(and how to configure encoding settings and detect and correct ". | ||||
| "encoding problems) by reading 'User Guide: UTF-8 and Character ". | "encoding problems) by reading 'User Guide: UTF-8 and Character ". | ||||
| "Encoding' in the Phabricator documentation.\n\n". | "Encoding' in the Phabricator documentation.\n\n". | ||||
| " ".pht('AFFECTED FILE(S)', count($utf8_problems))."\n"; | " ".pht('AFFECTED FILE(S)', count($utf8_problems))."\n"; | ||||
| $confirm = pht( | $confirm = pht( | ||||
| 'Do you want to mark these files as binary and continue?', | 'Do you want to mark these files as binary and continue?', | ||||
| count($utf8_problems)); | count($utf8_problems)); | ||||
| echo phutil_console_format("**Invalid Content Encoding (Non-UTF8)**\n"); | echo phutil_console_format("**Invalid Content Encoding (Non-UTF8)**\n"); | ||||
| echo phutil_console_wrap($utf8_warning); | echo phutil_console_wrap($utf8_warning); | ||||
| $file_list = mpull($utf8_problems, 'getCurrentPath'); | $file_list = mpull($utf8_problems, 'getCurrentPath'); | ||||
| $file_list = ' '.implode("\n ", $file_list); | $file_list = ' '.implode("\n ", $file_list); | ||||
| echo $file_list; | echo $file_list; | ||||
| if (!phutil_console_confirm($confirm, $default_no = false)) { | if (!phutil_console_confirm($confirm, $default_no = false)) { | ||||
| throw new ArcanistUsageException("Aborted workflow to fix UTF-8."); | throw new ArcanistUsageException('Aborted workflow to fix UTF-8.'); | ||||
| } else { | } else { | ||||
| foreach ($utf8_problems as $change) { | foreach ($utf8_problems as $change) { | ||||
| $change->convertToBinaryChange($repository_api); | $change->convertToBinaryChange($repository_api); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| $this->uploadFilesForChanges($changes); | $this->uploadFilesForChanges($changes); | ||||
| ▲ Show 20 Lines • Show All 145 Lines • ▼ Show 20 Lines | try { | ||||
| $lint_result = $lint_workflow->run(); | $lint_result = $lint_workflow->run(); | ||||
| switch ($lint_result) { | switch ($lint_result) { | ||||
| case ArcanistLintWorkflow::RESULT_OKAY: | case ArcanistLintWorkflow::RESULT_OKAY: | ||||
| if ($this->getArgument('advice') && | if ($this->getArgument('advice') && | ||||
| $lint_workflow->getUnresolvedMessages()) { | $lint_workflow->getUnresolvedMessages()) { | ||||
| $this->getErrorExcuse( | $this->getErrorExcuse( | ||||
| 'lint', | 'lint', | ||||
| "Lint issued unresolved advice.", | 'Lint issued unresolved advice.', | ||||
| 'lint-excuses'); | 'lint-excuses'); | ||||
| } else { | } else { | ||||
| $this->console->writeOut( | $this->console->writeOut( | ||||
| "<bg:green>** LINT OKAY **</bg> No lint problems.\n"); | "<bg:green>** LINT OKAY **</bg> No lint problems.\n"); | ||||
| } | } | ||||
| break; | break; | ||||
| case ArcanistLintWorkflow::RESULT_WARNINGS: | case ArcanistLintWorkflow::RESULT_WARNINGS: | ||||
| $this->getErrorExcuse( | $this->getErrorExcuse( | ||||
| 'lint', | 'lint', | ||||
| "Lint issued unresolved warnings.", | 'Lint issued unresolved warnings.', | ||||
| 'lint-excuses'); | 'lint-excuses'); | ||||
| break; | break; | ||||
| case ArcanistLintWorkflow::RESULT_ERRORS: | case ArcanistLintWorkflow::RESULT_ERRORS: | ||||
| $this->console->writeOut( | $this->console->writeOut( | ||||
| "<bg:red>** LINT ERRORS **</bg> Lint raised errors!\n"); | "<bg:red>** LINT ERRORS **</bg> Lint raised errors!\n"); | ||||
| $this->getErrorExcuse( | $this->getErrorExcuse( | ||||
| 'lint', | 'lint', | ||||
| "Lint issued unresolved errors!", | 'Lint issued unresolved errors!', | ||||
| 'lint-excuses'); | 'lint-excuses'); | ||||
| break; | break; | ||||
| case ArcanistLintWorkflow::RESULT_POSTPONED: | case ArcanistLintWorkflow::RESULT_POSTPONED: | ||||
| $this->console->writeOut( | $this->console->writeOut( | ||||
| "<bg:yellow>** LINT POSTPONED **</bg> ". | "<bg:yellow>** LINT POSTPONED **</bg> ". | ||||
| "Lint results are postponed.\n"); | "Lint results are postponed.\n"); | ||||
| break; | break; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | try { | ||||
| break; | break; | ||||
| case ArcanistUnitWorkflow::RESULT_UNSOUND: | case ArcanistUnitWorkflow::RESULT_UNSOUND: | ||||
| if ($this->getArgument('ignore-unsound-tests')) { | if ($this->getArgument('ignore-unsound-tests')) { | ||||
| echo phutil_console_format( | echo phutil_console_format( | ||||
| "<bg:yellow>** UNIT UNSOUND **</bg> Unit testing raised errors, ". | "<bg:yellow>** UNIT UNSOUND **</bg> Unit testing raised errors, ". | ||||
| "but all failing tests are unsound.\n"); | "but all failing tests are unsound.\n"); | ||||
| } else { | } else { | ||||
| $continue = $this->console->confirm( | $continue = $this->console->confirm( | ||||
| "Unit test results included failures, but all failing tests ". | 'Unit test results included failures, but all failing tests '. | ||||
| "are known to be unsound. Ignore unsound test failures?"); | 'are known to be unsound. Ignore unsound test failures?'); | ||||
| if (!$continue) { | if (!$continue) { | ||||
| throw new ArcanistUserAbortException(); | throw new ArcanistUserAbortException(); | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| case ArcanistUnitWorkflow::RESULT_FAIL: | case ArcanistUnitWorkflow::RESULT_FAIL: | ||||
| $this->console->writeOut( | $this->console->writeOut( | ||||
| "<bg:red>** UNIT ERRORS **</bg> Unit testing raised errors!\n"); | "<bg:red>** UNIT ERRORS **</bg> Unit testing raised errors!\n"); | ||||
| $this->getErrorExcuse( | $this->getErrorExcuse( | ||||
| 'unit', | 'unit', | ||||
| "Unit test results include failures!", | 'Unit test results include failures!', | ||||
| 'unit-excuses'); | 'unit-excuses'); | ||||
| break; | break; | ||||
| } | } | ||||
| $this->testResults = array(); | $this->testResults = array(); | ||||
| foreach ($unit_workflow->getTestResults() as $test) { | foreach ($unit_workflow->getTestResults() as $test) { | ||||
| $this->testResults[] = array( | $this->testResults[] = array( | ||||
| 'name' => $test->getName(), | 'name' => $test->getName(), | ||||
| Show All 33 Lines | private function getSkipExcuse($prompt, $history) { | ||||
| return $excuse; | return $excuse; | ||||
| } | } | ||||
| private function getErrorExcuse($type, $prompt, $history) { | private function getErrorExcuse($type, $prompt, $history) { | ||||
| if ($this->getArgument('excuse')) { | if ($this->getArgument('excuse')) { | ||||
| $this->console->sendMessage(array( | $this->console->sendMessage(array( | ||||
| 'type' => $type, | 'type' => $type, | ||||
| 'confirm' => $prompt." Ignore them?", | 'confirm' => $prompt.' Ignore them?', | ||||
| )); | )); | ||||
| return; | return; | ||||
| } | } | ||||
| $history = $this->getRepositoryAPI()->getScratchFilePath($history); | $history = $this->getRepositoryAPI()->getScratchFilePath($history); | ||||
| $prompt .= " Provide explanation to continue or press Enter to abort."; | $prompt .= ' Provide explanation to continue or press Enter to abort.'; | ||||
| $this->console->writeOut("\n\n%s", phutil_console_wrap($prompt)); | $this->console->writeOut("\n\n%s", phutil_console_wrap($prompt)); | ||||
| $this->console->sendMessage(array( | $this->console->sendMessage(array( | ||||
| 'type' => $type, | 'type' => $type, | ||||
| 'prompt' => "Explanation:", | 'prompt' => 'Explanation:', | ||||
| 'history' => $history, | 'history' => $history, | ||||
| )); | )); | ||||
| } | } | ||||
| public function handleServerMessage(PhutilConsoleMessage $message) { | public function handleServerMessage(PhutilConsoleMessage $message) { | ||||
| $data = $message->getData(); | $data = $message->getData(); | ||||
| if ($this->getArgument('excuse')) { | if ($this->getArgument('excuse')) { | ||||
| ▲ Show 20 Lines • Show All 127 Lines • ▼ Show 20 Lines | if (!$this->getArgument('verbatim')) { | ||||
| } | } | ||||
| echo | echo | ||||
| "You have a saved revision message in '{$where}'.\n". | "You have a saved revision message in '{$where}'.\n". | ||||
| "{$preview}". | "{$preview}". | ||||
| "You can use this message, or discard it."; | "You can use this message, or discard it."; | ||||
| $use = phutil_console_confirm( | $use = phutil_console_confirm( | ||||
| "Do you want to use this message?", | 'Do you want to use this message?', | ||||
| $default_no = false); | $default_no = false); | ||||
| if ($use) { | if ($use) { | ||||
| $template = $saved; | $template = $saved; | ||||
| } else { | } else { | ||||
| $this->removeScratchFile('create-message'); | $this->removeScratchFile('create-message'); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| Show All 33 Lines | if ($included_commits) { | ||||
| $included[] = ' '.$commit; | $included[] = ' '.$commit; | ||||
| } | } | ||||
| $in_branch = ''; | $in_branch = ''; | ||||
| if (!$this->isRawDiffSource()) { | if (!$this->isRawDiffSource()) { | ||||
| $in_branch = ' in branch '.$this->getRepositoryAPI()->getBranchName(); | $in_branch = ' in branch '.$this->getRepositoryAPI()->getBranchName(); | ||||
| } | } | ||||
| $included = array_merge( | $included = array_merge( | ||||
| array( | array( | ||||
| "", | '', | ||||
| "Included commits{$in_branch}:", | "Included commits{$in_branch}:", | ||||
| "", | '', | ||||
| ), | ), | ||||
| $included); | $included); | ||||
| } | } | ||||
| $issues = array_merge( | $issues = array_merge( | ||||
| array( | array( | ||||
| 'NEW DIFFERENTIAL REVISION', | 'NEW DIFFERENTIAL REVISION', | ||||
| 'Describe the changes in this new revision.', | 'Describe the changes in this new revision.', | ||||
| Show All 24 Lines | while (!$done) { | ||||
| } else { | } else { | ||||
| $new_template = $this->newInteractiveEditor($template) | $new_template = $this->newInteractiveEditor($template) | ||||
| ->setName('new-commit') | ->setName('new-commit') | ||||
| ->editInteractively(); | ->editInteractively(); | ||||
| } | } | ||||
| $first = false; | $first = false; | ||||
| if ($template_is_default && ($new_template == $template)) { | if ($template_is_default && ($new_template == $template)) { | ||||
| throw new ArcanistUsageException("Template not edited."); | throw new ArcanistUsageException('Template not edited.'); | ||||
| } | } | ||||
| $template = ArcanistCommentRemover::removeComments($new_template); | $template = ArcanistCommentRemover::removeComments($new_template); | ||||
| // With --raw-command, we may not have a repository API. | // With --raw-command, we may not have a repository API. | ||||
| if ($this->hasRepositoryAPI()) { | if ($this->hasRepositoryAPI()) { | ||||
| $repository_api = $this->getRepositoryAPI(); | $repository_api = $this->getRepositoryAPI(); | ||||
| // special check for whether to amend here. optimizes a common git | // special check for whether to amend here. optimizes a common git | ||||
| Show All 26 Lines | while (!$done) { | ||||
| } catch (ArcanistDifferentialCommitMessageParserException $ex) { | } catch (ArcanistDifferentialCommitMessageParserException $ex) { | ||||
| echo "Commit message has errors:\n\n"; | echo "Commit message has errors:\n\n"; | ||||
| $issues = array('Resolve these errors:'); | $issues = array('Resolve these errors:'); | ||||
| foreach ($ex->getParserErrors() as $error) { | foreach ($ex->getParserErrors() as $error) { | ||||
| echo phutil_console_wrap("- ".$error."\n", 6); | echo phutil_console_wrap("- ".$error."\n", 6); | ||||
| $issues[] = ' - '.$error; | $issues[] = ' - '.$error; | ||||
| } | } | ||||
| echo "\n"; | echo "\n"; | ||||
| echo "You must resolve these errors to continue."; | echo 'You must resolve these errors to continue.'; | ||||
| $again = phutil_console_confirm( | $again = phutil_console_confirm( | ||||
| "Do you want to edit the message?", | 'Do you want to edit the message?', | ||||
| $default_no = false); | $default_no = false); | ||||
| if ($again) { | if ($again) { | ||||
| // Keep going. | // Keep going. | ||||
| } else { | } else { | ||||
| $saved = null; | $saved = null; | ||||
| if ($wrote) { | if ($wrote) { | ||||
| $saved = "A copy was saved to {$where}."; | $saved = "A copy was saved to {$where}."; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | if ($revision_id) { | ||||
| 'differential.query', | 'differential.query', | ||||
| array( | array( | ||||
| 'ids' => array($revision_id), | 'ids' => array($revision_id), | ||||
| )); | )); | ||||
| } | } | ||||
| $reviewers = $message->getFieldValue('reviewerPHIDs'); | $reviewers = $message->getFieldValue('reviewerPHIDs'); | ||||
| if (!$reviewers) { | if (!$reviewers) { | ||||
| $confirm = "You have not specified any reviewers. Continue anyway?"; | $confirm = 'You have not specified any reviewers. Continue anyway?'; | ||||
| if (!phutil_console_confirm($confirm)) { | if (!phutil_console_confirm($confirm)) { | ||||
| throw new ArcanistUsageException('Specify reviewers and retry.'); | throw new ArcanistUsageException('Specify reviewers and retry.'); | ||||
| } | } | ||||
| } else { | } else { | ||||
| $futures['reviewers'] = $this->getConduit()->callMethod( | $futures['reviewers'] = $this->getConduit()->callMethod( | ||||
| 'user.query', | 'user.query', | ||||
| array( | array( | ||||
| 'phids' => $reviewers, | 'phids' => $reviewers, | ||||
| ▲ Show 20 Lines • Show All 720 Lines • ▼ Show 20 Lines | foreach ($need_upload as $key => $spec) { | ||||
| $mime = $this->getFileMimeType($spec['data']); | $mime = $this->getFileMimeType($spec['data']); | ||||
| if (preg_match('@^image/@', $mime)) { | if (preg_match('@^image/@', $mime)) { | ||||
| $change->setFileType($type_image); | $change->setFileType($type_image); | ||||
| } | } | ||||
| $change->setMetadata("{$type}:file:mime-type", $mime); | $change->setMetadata("{$type}:file:mime-type", $mime); | ||||
| } | } | ||||
| echo pht("Uploading %d files...", count($need_upload))."\n"; | echo pht('Uploading %d files...', count($need_upload))."\n"; | ||||
| // Now we're ready to upload the actual file data. If possible, we'll just | // Now we're ready to upload the actual file data. If possible, we'll just | ||||
| // transmit a hash of the file instead of the actual file data. If the data | // transmit a hash of the file instead of the actual file data. If the data | ||||
| // already exists, Phabricator can share storage. Check if we can use | // already exists, Phabricator can share storage. Check if we can use | ||||
| // "file.uploadhash" yet (i.e., if the server is up to date enough). | // "file.uploadhash" yet (i.e., if the server is up to date enough). | ||||
| // TODO: Drop this check once we bump the protocol version. | // TODO: Drop this check once we bump the protocol version. | ||||
| $conduit_methods = $this->getConduit()->callMethodSynchronous( | $conduit_methods = $this->getConduit()->callMethodSynchronous( | ||||
| 'conduit.query', | 'conduit.query', | ||||
| ▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | foreach (Futures($upload_futures)->limit(4) as $key => $future) { | ||||
| if (!phutil_console_confirm('Continue?', $default_no = false)) { | if (!phutil_console_confirm('Continue?', $default_no = false)) { | ||||
| throw new ArcanistUsageException( | throw new ArcanistUsageException( | ||||
| 'Aborted due to file upload failure. You can use --skip-binaries '. | 'Aborted due to file upload failure. You can use --skip-binaries '. | ||||
| 'to skip binary uploads.'); | 'to skip binary uploads.'); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| echo pht("Upload complete.")."\n"; | echo pht('Upload complete.')."\n"; | ||||
| } | } | ||||
| private function getFileMimeType($data) { | private function getFileMimeType($data) { | ||||
| $tmp = new TempFile(); | $tmp = new TempFile(); | ||||
| Filesystem::writeFile($tmp, $data); | Filesystem::writeFile($tmp, $data); | ||||
| return Filesystem::getMimeType($tmp); | return Filesystem::getMimeType($tmp); | ||||
| } | } | ||||
| } | } | ||||