1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-29 02:02:40 +01:00

Add a "RevisionSymbolRef", revision commit messages, and make "--explore" recursive

Summary:
Ref T13490.

  - Support resolution of revision symbols into revision objects.
  - Align commit inspection better against commit symbols.
  - Make "arc inspect --explore" recursively load all object hardpoints.
  - Add a "commit message" hardpoint to "RevisionRef".

Test Plan: Used "arc inspect" to resolve commits and revisions. Used "--explore" to see the whole tree.

Maniphest Tasks: T13490

Differential Revision: https://secure.phabricator.com/D21091
This commit is contained in:
epriestley 2020-04-11 15:58:08 -07:00
parent 4cc05c377d
commit 1f18f25fa5
14 changed files with 286 additions and 85 deletions

View file

@ -102,10 +102,9 @@ phutil_register_library_map(array(
'ArcanistCommentSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistCommentSpacingXHPASTLinterRule.php', 'ArcanistCommentSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistCommentSpacingXHPASTLinterRule.php',
'ArcanistCommentStyleXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistCommentStyleXHPASTLinterRule.php', 'ArcanistCommentStyleXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistCommentStyleXHPASTLinterRule.php',
'ArcanistCommentStyleXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistCommentStyleXHPASTLinterRuleTestCase.php', 'ArcanistCommentStyleXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistCommentStyleXHPASTLinterRuleTestCase.php',
'ArcanistCommitRef' => 'ref/ArcanistCommitRef.php', 'ArcanistCommitRef' => 'ref/commit/ArcanistCommitRef.php',
'ArcanistCommitRefInspector' => 'inspector/ArcanistCommitRefInspector.php', 'ArcanistCommitSymbolRef' => 'ref/commit/ArcanistCommitSymbolRef.php',
'ArcanistCommitSymbolRef' => 'ref/commitsymbol/ArcanistCommitSymbolRef.php', 'ArcanistCommitSymbolRefInspector' => 'ref/commit/ArcanistCommitSymbolRefInspector.php',
'ArcanistCommitSymbolRefInspector' => 'ref/commitsymbol/ArcanistCommitSymbolRefInspector.php',
'ArcanistCommitUpstreamHardpointQuery' => 'query/ArcanistCommitUpstreamHardpointQuery.php', 'ArcanistCommitUpstreamHardpointQuery' => 'query/ArcanistCommitUpstreamHardpointQuery.php',
'ArcanistCommitWorkflow' => 'workflow/ArcanistCommitWorkflow.php', 'ArcanistCommitWorkflow' => 'workflow/ArcanistCommitWorkflow.php',
'ArcanistCompilerLintRenderer' => 'lint/renderer/ArcanistCompilerLintRenderer.php', 'ArcanistCompilerLintRenderer' => 'lint/renderer/ArcanistCompilerLintRenderer.php',
@ -208,7 +207,7 @@ phutil_register_library_map(array(
'ArcanistGetConfigWorkflow' => 'workflow/ArcanistGetConfigWorkflow.php', 'ArcanistGetConfigWorkflow' => 'workflow/ArcanistGetConfigWorkflow.php',
'ArcanistGitAPI' => 'repository/api/ArcanistGitAPI.php', 'ArcanistGitAPI' => 'repository/api/ArcanistGitAPI.php',
'ArcanistGitCommitMessageHardpointQuery' => 'query/ArcanistGitCommitMessageHardpointQuery.php', 'ArcanistGitCommitMessageHardpointQuery' => 'query/ArcanistGitCommitMessageHardpointQuery.php',
'ArcanistGitCommitSymbolCommitHardpointQuery' => 'ref/commitsymbol/ArcanistGitCommitSymbolCommitHardpointQuery.php', 'ArcanistGitCommitSymbolCommitHardpointQuery' => 'ref/commit/ArcanistGitCommitSymbolCommitHardpointQuery.php',
'ArcanistGitLandEngine' => 'land/ArcanistGitLandEngine.php', 'ArcanistGitLandEngine' => 'land/ArcanistGitLandEngine.php',
'ArcanistGitUpstreamPath' => 'repository/api/ArcanistGitUpstreamPath.php', 'ArcanistGitUpstreamPath' => 'repository/api/ArcanistGitUpstreamPath.php',
'ArcanistGitWorkingCopy' => 'workingcopy/ArcanistGitWorkingCopy.php', 'ArcanistGitWorkingCopy' => 'workingcopy/ArcanistGitWorkingCopy.php',
@ -398,8 +397,12 @@ phutil_register_library_map(array(
'ArcanistReusedIteratorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistReusedIteratorXHPASTLinterRule.php', 'ArcanistReusedIteratorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistReusedIteratorXHPASTLinterRule.php',
'ArcanistReusedIteratorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistReusedIteratorXHPASTLinterRuleTestCase.php', 'ArcanistReusedIteratorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistReusedIteratorXHPASTLinterRuleTestCase.php',
'ArcanistRevertWorkflow' => 'workflow/ArcanistRevertWorkflow.php', 'ArcanistRevertWorkflow' => 'workflow/ArcanistRevertWorkflow.php',
'ArcanistRevisionRef' => 'ref/ArcanistRevisionRef.php', 'ArcanistRevisionCommitMessageHardpointQuery' => 'ref/revision/ArcanistRevisionCommitMessageHardpointQuery.php',
'ArcanistRevisionRef' => 'ref/revision/ArcanistRevisionRef.php',
'ArcanistRevisionRefSource' => 'ref/ArcanistRevisionRefSource.php', 'ArcanistRevisionRefSource' => 'ref/ArcanistRevisionRefSource.php',
'ArcanistRevisionSymbolHardpointQuery' => 'ref/revision/ArcanistRevisionSymbolHardpointQuery.php',
'ArcanistRevisionSymbolRef' => 'ref/revision/ArcanistRevisionSymbolRef.php',
'ArcanistRevisionSymbolRefInspector' => 'ref/revision/ArcanistRevisionSymbolRefInspector.php',
'ArcanistRuboCopLinter' => 'lint/linter/ArcanistRuboCopLinter.php', 'ArcanistRuboCopLinter' => 'lint/linter/ArcanistRuboCopLinter.php',
'ArcanistRuboCopLinterTestCase' => 'lint/linter/__tests__/ArcanistRuboCopLinterTestCase.php', 'ArcanistRuboCopLinterTestCase' => 'lint/linter/__tests__/ArcanistRuboCopLinterTestCase.php',
'ArcanistRubyLinter' => 'lint/linter/ArcanistRubyLinter.php', 'ArcanistRubyLinter' => 'lint/linter/ArcanistRubyLinter.php',
@ -432,6 +435,7 @@ phutil_register_library_map(array(
'ArcanistSubversionAPI' => 'repository/api/ArcanistSubversionAPI.php', 'ArcanistSubversionAPI' => 'repository/api/ArcanistSubversionAPI.php',
'ArcanistSubversionWorkingCopy' => 'workingcopy/ArcanistSubversionWorkingCopy.php', 'ArcanistSubversionWorkingCopy' => 'workingcopy/ArcanistSubversionWorkingCopy.php',
'ArcanistSummaryLintRenderer' => 'lint/renderer/ArcanistSummaryLintRenderer.php', 'ArcanistSummaryLintRenderer' => 'lint/renderer/ArcanistSummaryLintRenderer.php',
'ArcanistSymbolRef' => 'ref/symbol/ArcanistSymbolRef.php',
'ArcanistSyntaxErrorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistSyntaxErrorXHPASTLinterRule.php', 'ArcanistSyntaxErrorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistSyntaxErrorXHPASTLinterRule.php',
'ArcanistSystemConfigurationSource' => 'config/source/ArcanistSystemConfigurationSource.php', 'ArcanistSystemConfigurationSource' => 'config/source/ArcanistSystemConfigurationSource.php',
'ArcanistTasksWorkflow' => 'workflow/ArcanistTasksWorkflow.php', 'ArcanistTasksWorkflow' => 'workflow/ArcanistTasksWorkflow.php',
@ -1068,8 +1072,7 @@ phutil_register_library_map(array(
'ArcanistCommentStyleXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistCommentStyleXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistCommentStyleXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistCommentStyleXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistCommitRef' => 'ArcanistRef', 'ArcanistCommitRef' => 'ArcanistRef',
'ArcanistCommitRefInspector' => 'ArcanistRefInspector', 'ArcanistCommitSymbolRef' => 'ArcanistSymbolRef',
'ArcanistCommitSymbolRef' => 'ArcanistRef',
'ArcanistCommitSymbolRefInspector' => 'ArcanistRefInspector', 'ArcanistCommitSymbolRefInspector' => 'ArcanistRefInspector',
'ArcanistCommitUpstreamHardpointQuery' => 'ArcanistWorkflowHardpointQuery', 'ArcanistCommitUpstreamHardpointQuery' => 'ArcanistWorkflowHardpointQuery',
'ArcanistCommitWorkflow' => 'ArcanistWorkflow', 'ArcanistCommitWorkflow' => 'ArcanistWorkflow',
@ -1363,8 +1366,12 @@ phutil_register_library_map(array(
'ArcanistReusedIteratorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistReusedIteratorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistReusedIteratorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistReusedIteratorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistRevertWorkflow' => 'ArcanistWorkflow', 'ArcanistRevertWorkflow' => 'ArcanistWorkflow',
'ArcanistRevisionCommitMessageHardpointQuery' => 'ArcanistWorkflowHardpointQuery',
'ArcanistRevisionRef' => 'ArcanistRef', 'ArcanistRevisionRef' => 'ArcanistRef',
'ArcanistRevisionRefSource' => 'Phobject', 'ArcanistRevisionRefSource' => 'Phobject',
'ArcanistRevisionSymbolHardpointQuery' => 'ArcanistWorkflowHardpointQuery',
'ArcanistRevisionSymbolRef' => 'ArcanistSymbolRef',
'ArcanistRevisionSymbolRefInspector' => 'ArcanistRefInspector',
'ArcanistRuboCopLinter' => 'ArcanistExternalLinter', 'ArcanistRuboCopLinter' => 'ArcanistExternalLinter',
'ArcanistRuboCopLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistRuboCopLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistRubyLinter' => 'ArcanistExternalLinter', 'ArcanistRubyLinter' => 'ArcanistExternalLinter',
@ -1396,6 +1403,7 @@ phutil_register_library_map(array(
'ArcanistSubversionAPI' => 'ArcanistRepositoryAPI', 'ArcanistSubversionAPI' => 'ArcanistRepositoryAPI',
'ArcanistSubversionWorkingCopy' => 'ArcanistWorkingCopy', 'ArcanistSubversionWorkingCopy' => 'ArcanistWorkingCopy',
'ArcanistSummaryLintRenderer' => 'ArcanistLintRenderer', 'ArcanistSummaryLintRenderer' => 'ArcanistLintRenderer',
'ArcanistSymbolRef' => 'ArcanistRef',
'ArcanistSyntaxErrorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistSyntaxErrorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistSystemConfigurationSource' => 'ArcanistFilesystemConfigurationSource', 'ArcanistSystemConfigurationSource' => 'ArcanistFilesystemConfigurationSource',
'ArcanistTasksWorkflow' => 'ArcanistWorkflow', 'ArcanistTasksWorkflow' => 'ArcanistWorkflow',

View file

@ -1,22 +0,0 @@
<?php
final class ArcanistCommitRefInspector
extends ArcanistRefInspector {
public function getInspectFunctionName() {
return 'commit';
}
public function newInspectRef(array $argv) {
if (count($argv) !== 1) {
throw new PhutilArgumentUsageException(
pht(
'Expected exactly one argument to "commit(...)" with a '.
'commit hash.'));
}
return id(new ArcanistCommitRef())
->setCommitHash($argv[0]);
}
}

View file

@ -0,0 +1,10 @@
<?php
final class ArcanistCommitSymbolRef
extends ArcanistSymbolRef {
public function getRefDisplayName() {
return pht('Commit Symbol "%s"', $this->getSymbol());
}
}

View file

@ -4,14 +4,14 @@ final class ArcanistCommitSymbolRefInspector
extends ArcanistRefInspector { extends ArcanistRefInspector {
public function getInspectFunctionName() { public function getInspectFunctionName() {
return 'symbol'; return 'commit';
} }
public function newInspectRef(array $argv) { public function newInspectRef(array $argv) {
if (count($argv) !== 1) { if (count($argv) !== 1) {
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
pht( pht(
'Expected exactly one argument to "symbol(...)" with a '. 'Expected exactly one argument to "commit(...)" with a '.
'commit symbol.')); 'commit symbol.'));
} }

View file

@ -5,7 +5,7 @@ final class ArcanistGitCommitSymbolCommitHardpointQuery
public function getHardpoints() { public function getHardpoints() {
return array( return array(
ArcanistCommitSymbolRef::HARDPOINT_COMMIT, ArcanistCommitSymbolRef::HARDPOINT_OBJECT,
); );
} }

View file

@ -1,37 +0,0 @@
<?php
final class ArcanistCommitSymbolRef
extends ArcanistRef {
private $symbol;
const HARDPOINT_COMMIT = 'ref.commit-symbol';
public function getRefDisplayName() {
return pht('Commit Symbol "%s"', $this->getSymbol());
}
protected function newHardpoints() {
return array(
$this->newHardpoint(self::HARDPOINT_COMMIT),
);
}
public function setSymbol($symbol) {
$this->symbol = $symbol;
return $this;
}
public function getSymbol() {
return $this->symbol;
}
public function attachCommit(ArcanistCommitRef $commit) {
return $this->attachHardpoint(self::HARDPOINT_COMMIT, $commit);
}
public function getCommit() {
return $this->getHardpoint(self::HARDPOINT_COMMIT);
}
}

View file

@ -0,0 +1,37 @@
<?php
final class ArcanistRevisionCommitMessageHardpointQuery
extends ArcanistWorkflowHardpointQuery {
public function getHardpoints() {
return array(
ArcanistRevisionRef::HARDPOINT_COMMITMESSAGE,
);
}
protected function canLoadRef(ArcanistRef $ref) {
return ($ref instanceof ArcanistRevisionRef);
}
public function loadHardpoint(array $refs, $hardpoint) {
$api = $this->getRepositoryAPI();
// NOTE: This is not efficient, but no bulk API exists at time of
// writing and no callers bulk-load this data.
$results = array();
foreach ($refs as $key => $ref) {
$message = (yield $this->yieldConduit(
'differential.getcommitmessage',
array(
'revision_id' => $ref->getID(),
'edit' => false,
)));
$results[$key] = $message;
}
yield $this->yieldMap($results);
}
}

View file

@ -3,6 +3,8 @@
final class ArcanistRevisionRef final class ArcanistRevisionRef
extends ArcanistRef { extends ArcanistRef {
const HARDPOINT_COMMITMESSAGE = 'ref.revision.commitmessage';
private $parameters; private $parameters;
private $sources = array(); private $sources = array();
@ -10,6 +12,12 @@ final class ArcanistRevisionRef
return pht('Revision %s', $this->getMonogram()); return pht('Revision %s', $this->getMonogram());
} }
protected function newHardpoints() {
return array(
$this->newHardpoint(self::HARDPOINT_COMMITMESSAGE),
);
}
public static function newFromConduit(array $dict) { public static function newFromConduit(array $dict) {
$ref = new self(); $ref = new self();
$ref->parameters = $dict; $ref->parameters = $dict;
@ -45,7 +53,7 @@ final class ArcanistRevisionRef
} }
public function getID() { public function getID() {
return idx($this->parameters, 'id'); return (int)idx($this->parameters, 'id');
} }
public function getPHID() { public function getPHID() {

View file

@ -0,0 +1,40 @@
<?php
final class ArcanistRevisionSymbolHardpointQuery
extends ArcanistWorkflowHardpointQuery {
public function getHardpoints() {
return array(
ArcanistRevisionSymbolRef::HARDPOINT_OBJECT,
);
}
protected function canLoadRef(ArcanistRef $ref) {
return ($ref instanceof ArcanistRevisionSymbolRef);
}
public function loadHardpoint(array $refs, $hardpoint) {
$id_map = mpull($refs, 'getSymbol');
$id_set = array_fuse($id_map);
$revisions = (yield $this->yieldConduit(
'differential.query',
array(
'ids' => $id_set,
)));
$refs = array();
foreach ($revisions as $revision) {
$ref = ArcanistRevisionRef::newFromConduit($revision);
$refs[$ref->getID()] = $ref;
}
$results = array();
foreach ($id_map as $key => $id) {
$results[$key] = idx($refs, $id);
}
yield $this->yieldMap($results);
}
}

View file

@ -0,0 +1,25 @@
<?php
final class ArcanistRevisionSymbolRef
extends ArcanistSymbolRef {
public function getRefDisplayName() {
return pht('Revision Symbol "%s"', $this->getSymbol());
}
protected function resolveSymbol($symbol) {
$matches = null;
if (!preg_match('/^[Dd]?([1-9]\d*)\z/', $symbol, $matches)) {
throw new PhutilArgumentUsageException(
pht(
'The format of revision symbol "%s" is unrecognized. '.
'Expected a revision monogram like "D123", or a '.
'revision ID like "123".',
$symbol));
}
return (int)$matches[1];
}
}

View file

@ -0,0 +1,22 @@
<?php
final class ArcanistRevisionSymbolRefInspector
extends ArcanistRefInspector {
public function getInspectFunctionName() {
return 'revision';
}
public function newInspectRef(array $argv) {
if (count($argv) !== 1) {
throw new PhutilArgumentUsageException(
pht(
'Expected exactly one argument to "revision(...)" with a '.
'revision symbol.'));
}
return id(new ArcanistRevisionSymbolRef())
->setSymbol($argv[0]);
}
}

View file

@ -0,0 +1,39 @@
<?php
abstract class ArcanistSymbolRef
extends ArcanistRef {
private $symbol;
const HARDPOINT_OBJECT = 'ref.symbol.object';
protected function newHardpoints() {
return array(
$this->newHardpoint(self::HARDPOINT_OBJECT),
);
}
final public function setSymbol($symbol) {
$symbol = $this->resolveSymbol($symbol);
$this->symbol = $symbol;
return $this;
}
final public function getSymbol() {
return $this->symbol;
}
final public function attachObject(ArcanistRef $object) {
return $this->attachHardpoint(self::HARDPOINT_OBJECT, $object);
}
final public function getObject() {
return $this->getHardpoint(self::HARDPOINT_OBJECT);
}
protected function resolveSymbol($symbol) {
return $symbol;
}
}

View file

@ -51,7 +51,6 @@ EOTEXT
} }
$all_refs = array(); $all_refs = array();
$ref_lists = array();
foreach ($objects as $description) { foreach ($objects as $description) {
$matches = null; $matches = null;
$pattern = '/^([\w-]+)(?:\(([^)]+)\))?\z/'; $pattern = '/^([\w-]+)(?:\(([^)]+)\))?\z/';
@ -84,23 +83,11 @@ EOTEXT
$ref = $inspector->newInspectRef($arguments); $ref = $inspector->newInspectRef($arguments);
$ref_lists[get_class($ref)][] = $ref;
$all_refs[] = $ref; $all_refs[] = $ref;
} }
if ($is_explore) { if ($is_explore) {
foreach ($ref_lists as $ref_class => $refs) { $this->exploreRefs($all_refs);
$ref = head($refs);
$hardpoint_list = $ref->getHardpointList();
$hardpoints = $hardpoint_list->getHardpoints();
if ($hardpoints) {
$hardpoint_keys = mpull($hardpoints, 'getHardpointKey');
$this->loadHardpoints($refs, $hardpoint_keys);
}
}
} }
$list = array(); $list = array();
@ -214,4 +201,88 @@ EOTEXT
return $out; return $out;
} }
private function exploreRefs(array $refs) {
$seen = array();
$look = $refs;
while ($look) {
$ref_map = $this->getRefsByClass($look);
$look = array();
$children = $this->inspectHardpoints($ref_map);
foreach ($children as $child) {
$hash = spl_object_hash($child);
if (isset($seen[$hash])) {
continue;
}
$seen[$hash] = true;
$look[] = $child;
}
}
}
private function getRefsByClass(array $refs) {
$ref_lists = array();
foreach ($refs as $ref) {
$ref_lists[get_class($ref)][] = $ref;
}
foreach ($ref_lists as $ref_class => $refs) {
$typical_ref = head($refs);
$hardpoint_list = $typical_ref->getHardpointList();
$hardpoints = $hardpoint_list->getHardpoints();
if (!$hardpoints) {
unset($ref_lists[$ref_class]);
continue;
}
$hardpoint_keys = mpull($hardpoints, 'getHardpointKey');
$ref_lists[$ref_class] = array(
'keys' => $hardpoint_keys,
'refs' => $refs,
);
}
return $ref_lists;
}
private function inspectHardpoints(array $ref_lists) {
foreach ($ref_lists as $ref_class => $spec) {
$refs = $spec['refs'];
$keys = $spec['keys'];
$this->loadHardpoints($refs, $keys);
}
$child_refs = array();
foreach ($ref_lists as $ref_class => $spec) {
$refs = $spec['refs'];
$keys = $spec['keys'];
foreach ($refs as $ref) {
foreach ($keys as $key) {
$value = $ref->getHardpoint($key);
if (!is_array($value)) {
$value = array($value);
}
foreach ($value as $child) {
if ($child instanceof ArcanistRef) {
$child_refs[] = $child;
}
}
}
}
}
return $child_refs;
}
} }