1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-12-24 14:30:55 +01:00

Make "arc which" really really really verbose

Summary: Provide far more information about what "arc diff" intends to do.

Test Plan: Ran "arc which" in a variety of circumstances.

Reviewers: btrahan, Makinde

Reviewed By: Makinde

CC: aran

Maniphest Tasks: T1183

Differential Revision: https://secure.phabricator.com/D2454
This commit is contained in:
epriestley 2012-05-11 06:07:33 -07:00
parent 97262085b7
commit e50ea62271
5 changed files with 202 additions and 27 deletions

View file

@ -194,6 +194,14 @@ abstract class ArcanistRepositoryAPI {
throw new ArcanistCapabilityNotSupportedException($this); throw new ArcanistCapabilityNotSupportedException($this);
} }
public function getRelativeExplanation() {
throw new ArcanistCapabilityNotSupportedException($this);
}
public function getCommitSummary($commit) {
throw new ArcanistCapabilityNotSupportedException($this);
}
public function getAllLocalChanges() { public function getAllLocalChanges() {
throw new ArcanistCapabilityNotSupportedException($this); throw new ArcanistCapabilityNotSupportedException($this);
} }

View file

@ -25,6 +25,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
private $status; private $status;
private $relativeCommit = null; private $relativeCommit = null;
private $relativeExplanation = '???';
private $repositoryHasNoCommits = false; private $repositoryHasNoCommits = false;
const SEARCH_LENGTH_FOR_PARENT_REVISIONS = 16; const SEARCH_LENGTH_FOR_PARENT_REVISIONS = 16;
@ -93,6 +94,10 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
$commits = array(); $commits = array();
$info = trim($info, " \n\2"); $info = trim($info, " \n\2");
if (!strlen($info)) {
return array();
}
$info = explode("\2", $info); $info = explode("\2", $info);
foreach ($info as $line) { foreach ($info as $line) {
list($commit, $tree, $parents, $time, $author, $title, $message) list($commit, $tree, $parents, $time, $author, $title, $message)
@ -128,6 +133,12 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
$this->relativeCommit = self::GIT_MAGIC_ROOT_COMMIT; $this->relativeCommit = self::GIT_MAGIC_ROOT_COMMIT;
if ($this->repositoryHasNoCommits) {
$this->relativeExplanation = "the repository has no commits.";
} else {
$this->relativeExplanation = "the repository has only one commit.";
}
return $this->relativeCommit; return $this->relativeCommit;
} }
@ -137,6 +148,10 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
if ($working_copy) { if ($working_copy) {
$default_relative = $working_copy->getConfig( $default_relative = $working_copy->getConfig(
'git.default-relative-commit'); 'git.default-relative-commit');
$this->relativeExplanation =
"it is the merge-base of '{$default_relative}' and HEAD, as ".
"specified in 'git.default-relative-commit' in '.arcconfig'. This ".
"setting overrides other settings.";
} }
if (!$default_relative) { if (!$default_relative) {
@ -145,12 +160,20 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
if (!$err) { if (!$err) {
$default_relative = trim($upstream); $default_relative = trim($upstream);
$this->relativeExplanation =
"it is the merge-base of '{$default_relative}' (the Git upstream ".
"of the current branch) HEAD.";
} }
} }
if (!$default_relative) { if (!$default_relative) {
$default_relative = $this->readScratchFile('default-relative-commit'); $default_relative = $this->readScratchFile('default-relative-commit');
$default_relative = trim($default_relative); $default_relative = trim($default_relative);
if ($default_relative) {
$this->relativeExplanation =
"it is the merge-base of '{$default_relative}' and HEAD, as ".
"specified in '.arc/default-relative-commit'.";
}
} }
if (!$default_relative) { if (!$default_relative) {
@ -200,6 +223,9 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
// Don't perform this write until we've verified that the object is a // Don't perform this write until we've verified that the object is a
// valid commit name. // valid commit name.
$this->writeScratchFile('default-relative-commit', $default_relative); $this->writeScratchFile('default-relative-commit', $default_relative);
$this->relativeExplanation =
"it is the merge-base of '{$default_relative}' and HEAD, as you ".
"just specified.";
} }
list($merge_base) = $this->execxLocal( list($merge_base) = $this->execxLocal(
@ -658,6 +684,8 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
$base = reset($argv); $base = reset($argv);
if ($base == ArcanistGitAPI::GIT_MAGIC_ROOT_COMMIT) { if ($base == ArcanistGitAPI::GIT_MAGIC_ROOT_COMMIT) {
$merge_base = $base; $merge_base = $base;
$this->relativeExplanation =
"you explicitly specified the empty tree.";
} else { } else {
list($err, $merge_base) = $this->execManualLocal( list($err, $merge_base) = $this->execManualLocal(
'merge-base %s HEAD', 'merge-base %s HEAD',
@ -666,6 +694,9 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
throw new ArcanistUsageException( throw new ArcanistUsageException(
"Unable to find any git commit named '{$base}' in this repository."); "Unable to find any git commit named '{$base}' in this repository.");
} }
$this->relativeExplanation =
"it is the merge-base of '{$base}' and HEAD, as you explicitly ".
"specified.";
} }
$this->setRelativeCommit(trim($merge_base)); $this->setRelativeCommit(trim($merge_base));
} }
@ -725,12 +756,14 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
$messages = $parser->parseDiff($messages); $messages = $parser->parseDiff($messages);
// First, try to find revisions by explicit revision IDs in commit messages. // First, try to find revisions by explicit revision IDs in commit messages.
$reason_map = array();
$revision_ids = array(); $revision_ids = array();
foreach ($messages as $message) { foreach ($messages as $message) {
$object = ArcanistDifferentialCommitMessage::newFromRawCorpus( $object = ArcanistDifferentialCommitMessage::newFromRawCorpus(
$message->getMetadata('message')); $message->getMetadata('message'));
if ($object->getRevisionID()) { if ($object->getRevisionID()) {
$revision_ids[] = $object->getRevisionID(); $revision_ids[] = $object->getRevisionID();
$reason_map[$object->getRevisionID()] = $message->getCommitHash();
} }
} }
@ -740,6 +773,13 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
$query + array( $query + array(
'ids' => $revision_ids, 'ids' => $revision_ids,
)); ));
foreach ($results as $key => $result) {
$hash = substr($reason_map[$result['id']], 0, 16);
$results[$key]['why'] =
"Commit message for '{$hash}' has explicit 'Differential Revision'.";
}
return $results; return $results;
} }
@ -756,6 +796,12 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
'commitHashes' => $hashes, 'commitHashes' => $hashes,
)); ));
foreach ($results as $key => $result) {
$results[$key]['why'] =
"A git commit or tree hash in the commit range is already attached ".
"to the Differential revision.";
}
return $results; return $results;
} }
@ -763,4 +809,21 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
$this->execxLocal('pull'); $this->execxLocal('pull');
} }
public function getRelativeExplanation() {
return $this->relativeExplanation;
}
public function getCommitSummary($commit) {
if ($commit == self::GIT_MAGIC_ROOT_COMMIT) {
return '(The Empty Tree)';
}
list($summary) = $this->execxLocal(
'log -n 1 --format=%C %s',
'%s',
$commit);
return trim($summary);
}
} }

View file

@ -26,6 +26,7 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
private $status; private $status;
private $base; private $base;
private $relativeCommit; private $relativeCommit;
private $relativeExplanation;
private $workingCopyRevision; private $workingCopyRevision;
private $localCommitInfo; private $localCommitInfo;
private $includeDirectoryStateInDiffs; private $includeDirectoryStateInDiffs;
@ -107,6 +108,10 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
} }
if (!$logs) { if (!$logs) {
$this->relativeExplanation =
"you have no outgoing commits, so arc assumes you intend to submit ".
"uncommitted changes in the working copy.";
// In Mercurial, we support operations against uncommitted changes. // In Mercurial, we support operations against uncommitted changes.
$this->setRelativeCommit($this->getWorkingCopyRevision()); $this->setRelativeCommit($this->getWorkingCopyRevision());
return $this->relativeCommit; return $this->relativeCommit;
@ -148,6 +153,15 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
} }
} }
if ($against == 'null') {
$this->relativeExplanation =
"this is a new repository (all changes are outgoing).";
} else {
$this->relativeExplanation =
"it is the first commit reachable from the working copy state ".
"which is not outgoing.";
}
$this->setRelativeCommit($against); $this->setRelativeCommit($against);
} }
return $this->relativeCommit; return $this->relativeCommit;
@ -163,8 +177,6 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
$this->getWorkingCopyRevision()); $this->getWorkingCopyRevision());
$logs = array_filter(explode("\2", $info)); $logs = array_filter(explode("\2", $info));
array_shift($logs);
$last_node = null; $last_node = null;
$is_first = true; $is_first = true;
@ -200,6 +212,7 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
'branch' => $branch, 'branch' => $branch,
'tag' => $tag, 'tag' => $tag,
'commit' => $node, 'commit' => $node,
'rev' => $node, // TODO: Remove eventually.
'local' => $rev, 'local' => $rev,
'parents' => $commit_parents, 'parents' => $commit_parents,
'summary' => head(explode("\n", $desc)), 'summary' => head(explode("\n", $desc)),
@ -415,6 +428,9 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
if (count($argv) != 1) { if (count($argv) != 1) {
throw new ArcanistUsageException("Specify only one commit."); throw new ArcanistUsageException("Specify only one commit.");
} }
$this->relativeExplanation = "you explicitly specified it.";
// This does the "hg id" call we need to normalize/validate the revision // This does the "hg id" call we need to normalize/validate the revision
// identifier. // identifier.
$this->setRelativeCommit(reset($argv)); $this->setRelativeCommit(reset($argv));
@ -481,7 +497,7 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
// Try to find revisions by hash. // Try to find revisions by hash.
$hashes = array(); $hashes = array();
foreach ($this->getLocalCommitInformation() as $commit) { foreach ($this->getLocalCommitInformation() as $commit) {
$hashes[] = array('hgcm', $commit['rev']); $hashes[] = array('hgcm', $commit['commit']);
} }
$results = $conduit->callMethodSynchronous( $results = $conduit->callMethodSynchronous(
@ -490,6 +506,12 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
'commitHashes' => $hashes, 'commitHashes' => $hashes,
)); ));
foreach ($results as $key => $hash) {
$results[$key]['why'] =
"A mercurial commit hash in the commit range is already attached ".
"to the Differential revision.";
}
return $results; return $results;
} }
@ -519,4 +541,22 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
$this->localCommitInfo = null; $this->localCommitInfo = null;
} }
public function getRelativeExplanation() {
return $this->relativeExplanation;
}
public function getCommitSummary($commit) {
if ($commit == 'null') {
return '(The Empty Void)';
}
list($summary) = $this->execxLocal(
'log --template {desc} --limit 1 --rev %s',
$commit);
$summary = head(explode("\n", $summary));
return trim($summary);
}
} }

View file

@ -553,6 +553,11 @@ EODIFF;
} }
} }
foreach ($results as $key => $result) {
$results[$key]['why'] =
"Matching arcanist project name and working copy directory path.";
}
return $results; return $results;
} }

View file

@ -34,8 +34,8 @@ EOTEXT
public function getCommandHelp() { public function getCommandHelp() {
return phutil_console_format(<<<EOTEXT return phutil_console_format(<<<EOTEXT
Supports: svn, git, hg Supports: svn, git, hg
Shows which revision is in the working copy (or which revisions, if Shows which commits 'arc diff' will select, and which revision is in
more than one matches). the working copy (or which revisions, if more than one matches).
EOTEXT EOTEXT
); );
} }
@ -60,10 +60,6 @@ EOTEXT
'any-status' => array( 'any-status' => array(
'help' => "Show committed and abandoned revisions.", 'help' => "Show committed and abandoned revisions.",
), ),
'id' => array(
'help' => "If exactly one revision matches, print it to stdout. ".
"Otherwise, exit with an error. Intended for scripts.",
),
'*' => 'commit', '*' => 'commit',
); );
} }
@ -72,15 +68,63 @@ EOTEXT
$repository_api = $this->getRepositoryAPI(); $repository_api = $this->getRepositoryAPI();
$commit = $this->getArgument('commit'); $arg_commit = $this->getArgument('commit');
if (count($commit)) { if (count($arg_commit)) {
if (!$repository_api->supportsRelativeLocalCommits()) { if (!$repository_api->supportsRelativeLocalCommits()) {
throw new ArcanistUsageException( throw new ArcanistUsageException(
"This version control system does not support relative commits."); "This version control system does not support relative commits.");
} else { } else {
$repository_api->parseRelativeLocalCommit($commit); $repository_api->parseRelativeLocalCommit($arg_commit);
} }
} }
$arg = $arg_commit ? ' '.head($arg_commit) : '';
if ($repository_api->supportsRelativeLocalCommits()) {
$relative = $repository_api->getRelativeCommit();
$info = $repository_api->getLocalCommitInformation();
if ($info) {
$commits = array();
foreach ($info as $commit) {
$hash = substr($commit['commit'], 0, 16);
$summary = $commit['summary'];
$commits[] = " {$hash} {$summary}";
}
$commits = implode("\n", $commits);
} else {
$commits = ' (No commits.)';
}
$explanation = $repository_api->getRelativeExplanation();
$relative_summary = $repository_api->getCommitSummary($relative);
$relative = substr($relative, 0, 16);
if ($repository_api instanceof ArcanistGitAPI) {
$command = "git diff {$relative}..HEAD";
} else if ($repository_api instanceof ArcanistMercurialAPI) {
$command = "hg diff --rev {$relative} --rev .";
} else {
throw new Exception("Unknown VCS!");
}
echo phutil_console_wrap(
phutil_console_format(
"**RELATIVE COMMIT**\n".
"If you run 'arc diff{$arg}', changes between the commit:\n\n"));
echo " {$relative} {$relative_summary}\n\n";
echo phutil_console_wrap(
"...and the current working copy state will be sent to ".
"Differential, because {$explanation}\n\n".
"You can see the exact changes that will be sent by running ".
"this command:\n\n".
" $ {$command}\n\n".
"These commits will be included in the diff:\n\n");
echo $commits."\n\n\n";
}
$any_author = $this->getArgument('any-author'); $any_author = $this->getArgument('any-author');
$any_status = $this->getArgument('any-status'); $any_status = $this->getArgument('any-status');
@ -98,23 +142,38 @@ EOTEXT
$this->getConduit(), $this->getConduit(),
$query); $query);
echo phutil_console_wrap(
phutil_console_format(
"**MATCHING REVISIONS**\n".
"These Differential revisions match the changes in this working ".
"copy:\n\n"));
if (empty($revisions)) { if (empty($revisions)) {
$this->writeStatusMessage("No matching revisions.\n"); echo " (No revisions match.)\n";
return 1; echo "\n";
} echo phutil_console_wrap(
phutil_console_format(
if ($this->getArgument('id')) { "Since there are no revisions in Differential which match this ".
if (count($revisions) == 1) { "working copy, a new revision will be **created** if you run ".
echo idx(head($revisions), 'id'); "'arc diff{$arg}'.\n\n"));
return 0; } else {
} else { foreach ($revisions as $revision) {
$this->writeStatusMessage("More than one matching revision.\n"); echo ' D'.$revision['id'].' '.$revision['title']."\n";
return 1; echo ' Reason: '.$revision['why']."\n";
echo "\n";
} }
} if (count($revisions) == 1) {
echo phutil_console_wrap(
foreach ($revisions as $revision) { phutil_console_format(
echo 'D'.$revision['id'].' '.$revision['title']."\n"; "Since exactly one revision in Differential matches this working ".
"copy, it will be **updated** if you run 'arc diff{$arg}'."));
} else {
echo phutil_console_wrap(
"Since more than one revision in Differential matches this working ".
"copy, you will be asked which revision you want to update if ".
"you run 'arc diff {$arg}'.");
}
echo "\n\n";
} }
return 0; return 0;