diff --git a/src/repository/api/base/ArcanistRepositoryAPI.php b/src/repository/api/base/ArcanistRepositoryAPI.php index e5a280e5..469e2a0e 100644 --- a/src/repository/api/base/ArcanistRepositoryAPI.php +++ b/src/repository/api/base/ArcanistRepositoryAPI.php @@ -155,6 +155,10 @@ abstract class ArcanistRepositoryAPI { abstract public function supportsRelativeLocalCommits(); abstract public function getWorkingCopyRevision(); + public function getCommitMessageForRevision($revision) { + throw new ArcanistCapabilityNotSupportedException($this); + } + public function parseRelativeLocalCommit(array $argv) { throw new ArcanistCapabilityNotSupportedException($this); } diff --git a/src/repository/api/git/ArcanistGitAPI.php b/src/repository/api/git/ArcanistGitAPI.php index b27ba143..32248f2e 100644 --- a/src/repository/api/git/ArcanistGitAPI.php +++ b/src/repository/api/git/ArcanistGitAPI.php @@ -610,4 +610,13 @@ class ArcanistGitAPI extends ArcanistRepositoryAPI { "'git push', or 'git svn dcommit', or by printing and faxing it)."; } + public function getCommitMessageForRevision($rev) { + list($message) = execx( + '(cd %s && git log -n1 %s)', + $this->getPath(), + $rev); + $parser = new ArcanistDiffParser(); + return head($parser->parseDiff($message)); + } + } diff --git a/src/workflow/diff/ArcanistDiffWorkflow.php b/src/workflow/diff/ArcanistDiffWorkflow.php index f538978b..0643fcce 100644 --- a/src/workflow/diff/ArcanistDiffWorkflow.php +++ b/src/workflow/diff/ArcanistDiffWorkflow.php @@ -94,6 +94,20 @@ EOTEXT 'help' => 'When creating a revision, read revision information '. 'from this file.', ), + 'use-commit-message' => array( + 'supports' => array( + 'git', + // TODO: Support mercurial. + ), + 'short' => 'C', + 'param' => 'commit', + 'help' => 'Read revision information from a specific commit.', + 'conflicts' => array( + 'only' => null, + 'preview' => null, + 'update' => null, + ), + ), 'edit' => array( 'supports' => array( 'git', @@ -144,7 +158,6 @@ EOTEXT 'edit' => '--create can not be used with --edit.', 'only' => '--create can not be used with --only.', 'preview' => '--create can not be used with --preview.', - 'update' => '--create can not be used with --update.', ), ), @@ -469,6 +482,10 @@ EOTEXT return false; } + if ($this->getArgument('use-commit-message')) { + return false; + } + if ($this->isRawDiffSource()) { return true; } @@ -1073,6 +1090,11 @@ EOTEXT $is_create = $this->getArgument('create'); $is_update = $this->getArgument('update'); $is_raw = $this->isRawDiffSource(); + $is_message = $this->getArgument('use-commit-message'); + + if ($is_message) { + return $this->getCommitMessageFromCommit($is_message); + } $message = null; if ($is_create) { @@ -1100,6 +1122,19 @@ EOTEXT } + /** + * @task message + */ + private function getCommitMessageFromCommit($rev) { + $change = $this->getRepositoryAPI()->getCommitMessageForRevision($rev); + $message = ArcanistDifferentialCommitMessage::newFromRawCorpus( + $change->getMetadata('message')); + $message->pullDataFromConduit($this->getConduit()); + $this->validateCommitMessage($message); + return $message; + } + + /** * @task message */ @@ -1287,8 +1322,10 @@ EOTEXT $problems = array(); $parsed = array(); + $hashes = array(); foreach ($commit_messages as $key => $change) { $problems[$key] = array(); + $hashes[$key] = $change->getCommitHash(); try { $message = ArcanistDifferentialCommitMessage::newFromRawCorpus( @@ -1305,26 +1342,47 @@ EOTEXT } } - $blessed = null; - $revision_id = -1; + $valid = array(); foreach ($problems as $key => $problem_list) { if ($problem_list) { continue; } - if ($revision_id === -1) { - $revision_id = $parsed[$key]->getRevisionID(); - $blessed = $parsed[$key]; - } else { - throw new ArcanistUsageException( - "Changes in the specified commit range include more than one ". - "commit with a valid template commit message. This is ambiguous, ". - "your commit range should contain only one template commit ". - "message. Alternatively, use --preview to ignore commit ". - "messages."); - } + $valid[$key] = $parsed[$key]; } - if ($revision_id === -1) { + $blessed = null; + if (count($valid) == 1) { + $blessed = head($valid); + } else if (count($valid) > 1) { + echo phutil_console_wrap( + "Changes in the specified commit range include more than one commit ". + "with a valid template commit message. Choose the message you want ". + "to use (you can also use the -C flag).\n\n"); + foreach ($valid as $key => $message) { + $hash = substr($hashes[$key], 0, 7); + $title = $commit_messages[$key]->getMetadata('message'); + $title = head(explode("\n", trim($title))); + $title = phutil_utf8_shorten($title, 64); + echo " {$hash} {$title}\n"; + } + echo " none Edit a blank template."; + + do { + $choose = phutil_console_prompt('Use which commit message [none]?'); + if ($choose == 'none' || $choose == '') { + return $this->getCommitMessageFromUser(); + } else { + foreach ($valid as $key => $message) { + if (!strncmp($hashes[$key], $choose, strlen($choose))) { + $blessed = $valid[$key]; + break; + } + } + } + } while (!$blessed); + } + + if (!$blessed) { $desc = implode("\n", array_mergev($problems)); if (count($problems) > 1) { throw new ArcanistUsageException(