diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -213,6 +213,7 @@ 'ArcanistGitRevisionHardpointLoader' => 'loader/ArcanistGitRevisionHardpointLoader.php', 'ArcanistGitUpstreamPath' => 'repository/api/ArcanistGitUpstreamPath.php', 'ArcanistGitWorkingCopy' => 'workingcopy/ArcanistGitWorkingCopy.php', + 'ArcanistGitWorkingCopyRevisionHardpointQuery' => 'query/ArcanistGitWorkingCopyRevisionHardpointQuery.php', 'ArcanistGlobalVariableXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistGlobalVariableXHPASTLinterRule.php', 'ArcanistGlobalVariableXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistGlobalVariableXHPASTLinterRuleTestCase.php', 'ArcanistGoLintLinter' => 'lint/linter/ArcanistGoLintLinter.php', @@ -320,6 +321,7 @@ 'ArcanistMergeConflictLinter' => 'lint/linter/ArcanistMergeConflictLinter.php', 'ArcanistMergeConflictLinterTestCase' => 'lint/linter/__tests__/ArcanistMergeConflictLinterTestCase.php', 'ArcanistMessageRevisionHardpointLoader' => 'loader/ArcanistMessageRevisionHardpointLoader.php', + 'ArcanistMessageRevisionHardpointQuery' => 'query/ArcanistMessageRevisionHardpointQuery.php', 'ArcanistMissingArgumentTerminatorException' => 'exception/ArcanistMissingArgumentTerminatorException.php', 'ArcanistMissingLinterException' => 'lint/linter/exception/ArcanistMissingLinterException.php', 'ArcanistModifierOrderingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistModifierOrderingXHPASTLinterRule.php', @@ -340,6 +342,7 @@ 'ArcanistNoParentScopeXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistNoParentScopeXHPASTLinterRuleTestCase.php', 'ArcanistNoURIConduitException' => 'conduit/ArcanistNoURIConduitException.php', 'ArcanistNoneLintRenderer' => 'lint/renderer/ArcanistNoneLintRenderer.php', + 'ArcanistObjectListHardpoint' => 'hardpoint/ArcanistObjectListHardpoint.php', 'ArcanistObjectOperatorSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistObjectOperatorSpacingXHPASTLinterRule.php', 'ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase.php', 'ArcanistPEP8Linter' => 'lint/linter/ArcanistPEP8Linter.php', @@ -403,6 +406,7 @@ 'ArcanistReusedIteratorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistReusedIteratorXHPASTLinterRuleTestCase.php', 'ArcanistRevertWorkflow' => 'workflow/ArcanistRevertWorkflow.php', 'ArcanistRevisionRef' => 'ref/ArcanistRevisionRef.php', + 'ArcanistRevisionRefPro' => 'ref/ArcanistRevisionRefPro.php', 'ArcanistRevisionRefSource' => 'ref/ArcanistRevisionRefSource.php', 'ArcanistRuboCopLinter' => 'lint/linter/ArcanistRuboCopLinter.php', 'ArcanistRuboCopLinterTestCase' => 'lint/linter/__tests__/ArcanistRuboCopLinterTestCase.php', @@ -498,6 +502,7 @@ 'ArcanistWildConfigOption' => 'config/option/ArcanistWildConfigOption.php', 'ArcanistWorkflow' => 'workflow/ArcanistWorkflow.php', 'ArcanistWorkflowArgument' => 'toolset/ArcanistWorkflowArgument.php', + 'ArcanistWorkflowGitHardpointQuery' => 'query/ArcanistWorkflowGitHardpointQuery.php', 'ArcanistWorkflowHardpointQuery' => 'toolset/query/ArcanistWorkflowHardpointQuery.php', 'ArcanistWorkflowInformation' => 'toolset/ArcanistWorkflowInformation.php', 'ArcanistWorkingCopy' => 'workingcopy/ArcanistWorkingCopy.php', @@ -506,6 +511,7 @@ 'ArcanistWorkingCopyIdentity' => 'workingcopyidentity/ArcanistWorkingCopyIdentity.php', 'ArcanistWorkingCopyPath' => 'workingcopy/ArcanistWorkingCopyPath.php', 'ArcanistWorkingCopyStateRef' => 'ref/ArcanistWorkingCopyStateRef.php', + 'ArcanistWorkingCopyStateRefInspector' => 'inspector/ArcanistWorkingCopyStateRefInspector.php', 'ArcanistWorkingCopyStateRefPro' => 'ref/ArcanistWorkingCopyStateRefPro.php', 'ArcanistXHPASTLintNamingHook' => 'lint/linter/xhpast/ArcanistXHPASTLintNamingHook.php', 'ArcanistXHPASTLintNamingHookTestCase' => 'lint/linter/xhpast/__tests__/ArcanistXHPASTLintNamingHookTestCase.php', @@ -1175,12 +1181,13 @@ 'ArcanistGetConfigWorkflow' => 'ArcanistWorkflow', 'ArcanistGitAPI' => 'ArcanistRepositoryAPI', 'ArcanistGitCommitMessageHardpointLoader' => 'ArcanistGitHardpointLoader', - 'ArcanistGitCommitMessageHardpointQuery' => 'ArcanistWorkflowHardpointQuery', + 'ArcanistGitCommitMessageHardpointQuery' => 'ArcanistWorkflowGitHardpointQuery', 'ArcanistGitHardpointLoader' => 'ArcanistHardpointLoader', 'ArcanistGitLandEngine' => 'ArcanistLandEngine', 'ArcanistGitRevisionHardpointLoader' => 'ArcanistGitHardpointLoader', 'ArcanistGitUpstreamPath' => 'Phobject', 'ArcanistGitWorkingCopy' => 'ArcanistWorkingCopy', + 'ArcanistGitWorkingCopyRevisionHardpointQuery' => 'ArcanistWorkflowGitHardpointQuery', 'ArcanistGlobalVariableXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistGlobalVariableXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistGoLintLinter' => 'ArcanistExternalLinter', @@ -1288,6 +1295,7 @@ 'ArcanistMergeConflictLinter' => 'ArcanistLinter', 'ArcanistMergeConflictLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistMessageRevisionHardpointLoader' => 'ArcanistHardpointLoader', + 'ArcanistMessageRevisionHardpointQuery' => 'ArcanistWorkflowHardpointQuery', 'ArcanistMissingArgumentTerminatorException' => 'Exception', 'ArcanistMissingLinterException' => 'Exception', 'ArcanistModifierOrderingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', @@ -1308,6 +1316,7 @@ 'ArcanistNoParentScopeXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistNoURIConduitException' => 'ArcanistConduitException', 'ArcanistNoneLintRenderer' => 'ArcanistLintRenderer', + 'ArcanistObjectListHardpoint' => 'ArcanistHardpoint', 'ArcanistObjectOperatorSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistPEP8Linter' => 'ArcanistExternalLinter', @@ -1371,6 +1380,7 @@ 'ArcanistReusedIteratorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistRevertWorkflow' => 'ArcanistWorkflow', 'ArcanistRevisionRef' => 'ArcanistRef', + 'ArcanistRevisionRefPro' => 'ArcanistRefPro', 'ArcanistRevisionRefSource' => 'Phobject', 'ArcanistRuboCopLinter' => 'ArcanistExternalLinter', 'ArcanistRuboCopLinterTestCase' => 'ArcanistExternalLinterTestCase', @@ -1465,6 +1475,7 @@ 'ArcanistWildConfigOption' => 'ArcanistConfigOption', 'ArcanistWorkflow' => 'Phobject', 'ArcanistWorkflowArgument' => 'Phobject', + 'ArcanistWorkflowGitHardpointQuery' => 'ArcanistWorkflowHardpointQuery', 'ArcanistWorkflowHardpointQuery' => 'ArcanistHardpointQuery', 'ArcanistWorkflowInformation' => 'Phobject', 'ArcanistWorkingCopy' => 'Phobject', @@ -1473,6 +1484,7 @@ 'ArcanistWorkingCopyIdentity' => 'Phobject', 'ArcanistWorkingCopyPath' => 'Phobject', 'ArcanistWorkingCopyStateRef' => 'ArcanistRef', + 'ArcanistWorkingCopyStateRefInspector' => 'ArcanistRefInspector', 'ArcanistWorkingCopyStateRefPro' => 'ArcanistRefPro', 'ArcanistXHPASTLintNamingHook' => 'Phobject', 'ArcanistXHPASTLintNamingHookTestCase' => 'PhutilTestCase', diff --git a/src/hardpoint/ArcanistHardpointObject.php b/src/hardpoint/ArcanistHardpointObject.php --- a/src/hardpoint/ArcanistHardpointObject.php +++ b/src/hardpoint/ArcanistHardpointObject.php @@ -64,6 +64,14 @@ ->setHardpointKey($hardpoint_key); } + final protected function newTemplateHardpoint( + $hardpoint_key, + ArcanistHardpoint $template) { + + return id(clone $template) + ->setHardpointKey($hardpoint_key); + } + final public function getHardpointList() { if ($this->hardpointList === null) { diff --git a/src/hardpoint/ArcanistObjectListHardpoint.php b/src/hardpoint/ArcanistObjectListHardpoint.php new file mode 100644 --- /dev/null +++ b/src/hardpoint/ArcanistObjectListHardpoint.php @@ -0,0 +1,25 @@ +getPHID(); + if (!isset($old[$phid])) { + $old[$phid] = $item; + } + } + + return $old; + } + +} diff --git a/src/inspector/ArcanistWorkingCopyStateRefInspector.php b/src/inspector/ArcanistWorkingCopyStateRefInspector.php new file mode 100644 --- /dev/null +++ b/src/inspector/ArcanistWorkingCopyStateRefInspector.php @@ -0,0 +1,26 @@ +setCommitHash($commit_hash); + + return id(new ArcanistWorkingCopyStateRefPro()) + ->setCommitRef($commit_ref); + } + +} diff --git a/src/query/ArcanistGitCommitMessageHardpointQuery.php b/src/query/ArcanistGitCommitMessageHardpointQuery.php --- a/src/query/ArcanistGitCommitMessageHardpointQuery.php +++ b/src/query/ArcanistGitCommitMessageHardpointQuery.php @@ -1,7 +1,7 @@ getRepositoryAPI(); - return ($api instanceof ArcanistGitAPI); - } - public function loadHardpoint(array $refs, $hardpoint) { $api = $this->getRepositoryAPI(); diff --git a/src/query/ArcanistGitWorkingCopyRevisionHardpointQuery.php b/src/query/ArcanistGitWorkingCopyRevisionHardpointQuery.php new file mode 100644 --- /dev/null +++ b/src/query/ArcanistGitWorkingCopyRevisionHardpointQuery.php @@ -0,0 +1,81 @@ +yieldRequests( + $refs, + array( + ArcanistWorkingCopyStateRefPro::HARDPOINT_COMMITREF, + )); + + $hashes = array(); + $map = array(); + foreach ($refs as $ref_key => $ref) { + $commit = $ref->getCommitRef(); + + $commit_hashes = array(); + + $commit_hashes[] = array( + 'gtcm', + $commit->getCommitHash(), + ); + + if ($commit->getTreeHash()) { + $commit_hashes[] = array( + 'gttr', + $commit->getTreeHash(), + ); + } + + foreach ($commit_hashes as $hash) { + $hashes[] = $hash; + $hash_key = $this->getHashKey($hash); + $map[$hash_key][$ref_key] = $ref; + } + } + + $results = array_fill_keys(array_keys($refs), array()); + if ($hashes) { + $revisions = (yield $this->yieldConduit( + 'differential.query', + array( + 'commitHashes' => $hashes, + ))); + + foreach ($revisions as $dict) { + $revision_hashes = idx($dict, 'hashes'); + if (!$revision_hashes) { + continue; + } + + $revision_ref = ArcanistRevisionRefPro::newFromConduit($dict); + foreach ($revision_hashes as $revision_hash) { + $hash_key = $this->getHashKey($revision_hash); + $state_refs = idx($map, $hash_key, array()); + foreach ($state_refs as $ref_key => $state_ref) { + $results[$ref_key][] = $revision_ref; + } + } + } + } + + yield $this->yieldMap($results); + } + + private function getHashKey(array $hash) { + return $hash[0].':'.$hash[1]; + } + +} diff --git a/src/query/ArcanistMessageRevisionHardpointQuery.php b/src/query/ArcanistMessageRevisionHardpointQuery.php new file mode 100644 --- /dev/null +++ b/src/query/ArcanistMessageRevisionHardpointQuery.php @@ -0,0 +1,76 @@ +yieldRequests( + $refs, + array( + ArcanistWorkingCopyStateRefPro::HARDPOINT_COMMITREF, + )); + + $commit_refs = array(); + foreach ($refs as $ref) { + $commit_refs[] = $ref->getCommitRef(); + } + + yield $this->yieldRequests( + $commit_refs, + array( + ArcanistCommitRefPro::HARDPOINT_MESSAGE, + )); + + $map = array(); + foreach ($refs as $ref_key => $ref) { + $commit_ref = $ref->getCommitRef(); + $corpus = $commit_ref->getMessage(); + + $id = null; + try { + $message = ArcanistDifferentialCommitMessage::newFromRawCorpus($corpus); + $id = $message->getRevisionID(); + } catch (ArcanistUsageException $ex) { + continue; + } + + if (!$id) { + continue; + } + + $map[$id][$ref_key] = $ref; + } + + $results = array(); + if ($map) { + $revisions = (yield $this->yieldConduit( + 'differential.query', + array( + 'ids' => array_keys($map), + ))); + + foreach ($revisions as $dict) { + $revision_ref = ArcanistRevisionRefPro::newFromConduit($dict); + $id = $dict['id']; + + $state_refs = idx($map, $id, array()); + foreach ($state_refs as $ref_key => $state_ref) { + $results[$ref_key][] = $revision_ref; + } + } + } + + yield $this->yieldMap($results); + } + +} diff --git a/src/query/ArcanistWorkflowGitHardpointQuery.php b/src/query/ArcanistWorkflowGitHardpointQuery.php new file mode 100644 --- /dev/null +++ b/src/query/ArcanistWorkflowGitHardpointQuery.php @@ -0,0 +1,11 @@ +getRepositoryAPI(); + return ($api instanceof ArcanistGitAPI); + } + +} diff --git a/src/ref/ArcanistRevisionRefPro.php b/src/ref/ArcanistRevisionRefPro.php new file mode 100644 --- /dev/null +++ b/src/ref/ArcanistRevisionRefPro.php @@ -0,0 +1,76 @@ +getMonogram()); + } + + public function defineHardpoints() { + return array(); + } + + public static function newFromConduit(array $dict) { + $ref = new self(); + $ref->parameters = $dict; + return $ref; + } + + public function getMonogram() { + return 'D'.$this->getID(); + } + + public function getStatusDisplayName() { + return idx($this->parameters, 'statusName'); + } + + public function isClosed() { + // TODO: This should use sensible constants, not English language + // display text. + switch ($this->getStatusDisplayName()) { + case 'Abandoned': + case 'Closed': + return true; + } + + return false; + } + + public function getURI() { + return idx($this->parameters, 'uri'); + } + + public function getFullName() { + return pht('%s: %s', $this->getMonogram(), $this->getName()); + } + + public function getID() { + return idx($this->parameters, 'id'); + } + + public function getPHID() { + return idx($this->parameters, 'phid'); + } + + public function getName() { + return idx($this->parameters, 'title'); + } + + public function getAuthorPHID() { + return idx($this->parameters, 'authorPHID'); + } + + public function addSource(ArcanistRevisionRefSource $source) { + $this->sources[] = $source; + return $this; + } + + public function getSources() { + return $this->sources; + } + +} diff --git a/src/ref/ArcanistWorkingCopyStateRefPro.php b/src/ref/ArcanistWorkingCopyStateRefPro.php --- a/src/ref/ArcanistWorkingCopyStateRefPro.php +++ b/src/ref/ArcanistWorkingCopyStateRefPro.php @@ -4,7 +4,6 @@ extends ArcanistRefPro { const HARDPOINT_COMMITREF = 'commitRef'; - const HARDPOINT_BRANCHREF = 'branchRef'; const HARDPOINT_REVISIONREFS = 'revisionRefs'; public function getRefDisplayName() { @@ -14,22 +13,15 @@ } protected function newHardpoints() { + $object_list = new ArcanistObjectListHardpoint(); return array( $this->newHardpoint(self::HARDPOINT_COMMITREF), - $this->newHardpoint(self::HARDPOINT_BRANCHREF), - $this->newVectorHardpoint(self::HARDPOINT_REVISIONREFS), + $this->newTemplateHardpoint(self::HARDPOINT_REVISIONREFS, $object_list), ); } - public function attachBranchRef(ArcanistBranchRef $branch_ref) { - return $this->attachHardpoint(self::HARDPOINT_BRANCHREF, $branch_ref); - } - - public function getBranchRef() { - return $this->getHardpoint(self::HARDPOINT_BRANCHREF); - } - - public function setCommitRef(ArcanistCommitRef $commit_ref) { + // TODO: This should be "attachCommitRef()". + public function setCommitRef(ArcanistCommitRefPro $commit_ref) { return $this->attachHardpoint(self::HARDPOINT_COMMITREF, $commit_ref); } diff --git a/src/workflow/ArcanistInspectWorkflow.php b/src/workflow/ArcanistInspectWorkflow.php --- a/src/workflow/ArcanistInspectWorkflow.php +++ b/src/workflow/ArcanistInspectWorkflow.php @@ -54,11 +54,13 @@ $ref_lists = array(); foreach ($objects as $description) { $matches = null; - if (!preg_match('/^(\w+)(?:\(([^)]+)\))?\z/', $description, $matches)) { + $pattern = '/^([\w-]+)(?:\(([^)]+)\))?\z/'; + if (!preg_match($pattern, $description, $matches)) { throw new PhutilArgumentUsageException( pht( 'Object specification "%s" is unknown, expected a specification '. - 'like "commit(HEAD)".')); + 'like "commit(HEAD)".', + $description)); } $function = $matches[1]; @@ -149,11 +151,21 @@ if ($ref->hasAttachedHardpoint($hardpoint_key)) { $mode = '*'; $value = $ref->getHardpoint($hardpoint_key); + if ($value instanceof ArcanistRefPro) { $children[] = $value; + } else if (is_array($value)) { + foreach ($value as $key => $child) { + if ($child instanceof ArcanistRefPro) { + $children[] = $child; + } else { + $values[] = $value; + } + } } else { $values[] = $value; } + } else { $mode = 'o'; }