mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-10 00:42:40 +01:00
Rebuild "arc branch" on new "hardpoint" infrastructure
Summary: Ref T11355. Ref T10895. Ref T11518. This is heading to `experiemntal`. This may or may not be a good idea, but basically it's a more generic version of `Query` classes in Phabricator. This starts creating generic objects ("CommitRef", "BranchRef") which have attachable properties, like many Phabricator objects do. Here, they're formalized (and theoretically extensible), as "hardpoints". So: a hardpoint is something on an object which you can attach stuff to, but which we don't start with the data for. All of the logic for actually figuruing out how to attach stuff to hardpoints is also modular. `Loader` classes have code for loading stuff onto objects. For example, `ArcanistMercurialBranchCommitHardpointLoader` knows how to run `hg log` to build the commit for a branch. One issue is that `arc feature` in Mercurial is 100% bookmarks, so maybe I should actually be making `ArcanistRefRef` here. But we can probbbably deal with that later. This moves us somewhat closer to T11355 and T11518, although the immediate thing I want to do with it is define an `ArcanistObjectNameRef` and use hardpoints to load URIs for it for T10895. Overall, I expect this will see some revision in future changes, and perhaps most of it will go away. Test Plan: Ran `arc branch` / `arc feature` in Git and Mercurial repositories. Reviewers: avivey, chad Reviewed By: chad Maniphest Tasks: T10895, T11518, T11355 Differential Revision: https://secure.phabricator.com/D16857
This commit is contained in:
parent
45c2152988
commit
71473af895
21 changed files with 1023 additions and 145 deletions
|
@ -40,6 +40,7 @@ phutil_register_library_map(array(
|
|||
'ArcanistBookmarkWorkflow' => 'workflow/ArcanistBookmarkWorkflow.php',
|
||||
'ArcanistBraceFormattingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistBraceFormattingXHPASTLinterRule.php',
|
||||
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistBraceFormattingXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistBranchRef' => 'ref/ArcanistBranchRef.php',
|
||||
'ArcanistBranchWorkflow' => 'workflow/ArcanistBranchWorkflow.php',
|
||||
'ArcanistBrowseWorkflow' => 'workflow/ArcanistBrowseWorkflow.php',
|
||||
'ArcanistBundle' => 'parser/ArcanistBundle.php',
|
||||
|
@ -76,6 +77,7 @@ phutil_register_library_map(array(
|
|||
'ArcanistCommentSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistCommentSpacingXHPASTLinterRule.php',
|
||||
'ArcanistCommentStyleXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistCommentStyleXHPASTLinterRule.php',
|
||||
'ArcanistCommentStyleXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistCommentStyleXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistCommitRef' => 'ref/ArcanistCommitRef.php',
|
||||
'ArcanistCommitWorkflow' => 'workflow/ArcanistCommitWorkflow.php',
|
||||
'ArcanistCompilerLintRenderer' => 'lint/renderer/ArcanistCompilerLintRenderer.php',
|
||||
'ArcanistComposerLinter' => 'lint/linter/ArcanistComposerLinter.php',
|
||||
|
@ -158,7 +160,9 @@ phutil_register_library_map(array(
|
|||
'ArcanistGeneratedLinterTestCase' => 'lint/linter/__tests__/ArcanistGeneratedLinterTestCase.php',
|
||||
'ArcanistGetConfigWorkflow' => 'workflow/ArcanistGetConfigWorkflow.php',
|
||||
'ArcanistGitAPI' => 'repository/api/ArcanistGitAPI.php',
|
||||
'ArcanistGitHardpointLoader' => 'loader/ArcanistGitHardpointLoader.php',
|
||||
'ArcanistGitLandEngine' => 'land/ArcanistGitLandEngine.php',
|
||||
'ArcanistGitRevisionHardpointLoader' => 'loader/ArcanistGitRevisionHardpointLoader.php',
|
||||
'ArcanistGitUpstreamPath' => 'repository/api/ArcanistGitUpstreamPath.php',
|
||||
'ArcanistGlobalVariableXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistGlobalVariableXHPASTLinterRule.php',
|
||||
'ArcanistGlobalVariableXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistGlobalVariableXHPASTLinterRuleTestCase.php',
|
||||
|
@ -168,6 +172,7 @@ phutil_register_library_map(array(
|
|||
'ArcanistGoTestResultParserTestCase' => 'unit/parser/__tests__/ArcanistGoTestResultParserTestCase.php',
|
||||
'ArcanistHLintLinter' => 'lint/linter/ArcanistHLintLinter.php',
|
||||
'ArcanistHLintLinterTestCase' => 'lint/linter/__tests__/ArcanistHLintLinterTestCase.php',
|
||||
'ArcanistHardpointLoader' => 'loader/ArcanistHardpointLoader.php',
|
||||
'ArcanistHelpWorkflow' => 'workflow/ArcanistHelpWorkflow.php',
|
||||
'ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule.php',
|
||||
'ArcanistHexadecimalNumericScalarCasingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistHexadecimalNumericScalarCasingXHPASTLinterRuleTestCase.php',
|
||||
|
@ -241,10 +246,14 @@ phutil_register_library_map(array(
|
|||
'ArcanistLowercaseFunctionsXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistLowercaseFunctionsXHPASTLinterRule.php',
|
||||
'ArcanistLowercaseFunctionsXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistLowercaseFunctionsXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistMercurialAPI' => 'repository/api/ArcanistMercurialAPI.php',
|
||||
'ArcanistMercurialBranchCommitHardpointLoader' => 'loader/ArcanistMercurialBranchCommitHardpointLoader.php',
|
||||
'ArcanistMercurialHardpointLoader' => 'loader/ArcanistMercurialHardpointLoader.php',
|
||||
'ArcanistMercurialParser' => 'repository/parser/ArcanistMercurialParser.php',
|
||||
'ArcanistMercurialParserTestCase' => 'repository/parser/__tests__/ArcanistMercurialParserTestCase.php',
|
||||
'ArcanistMercurialWorkingCopyCommitHardpointLoader' => 'loader/ArcanistMercurialWorkingCopyCommitHardpointLoader.php',
|
||||
'ArcanistMergeConflictLinter' => 'lint/linter/ArcanistMergeConflictLinter.php',
|
||||
'ArcanistMergeConflictLinterTestCase' => 'lint/linter/__tests__/ArcanistMergeConflictLinterTestCase.php',
|
||||
'ArcanistMessageRevisionHardpointLoader' => 'loader/ArcanistMessageRevisionHardpointLoader.php',
|
||||
'ArcanistMissingLinterException' => 'lint/linter/exception/ArcanistMissingLinterException.php',
|
||||
'ArcanistModifierOrderingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistModifierOrderingXHPASTLinterRule.php',
|
||||
'ArcanistModifierOrderingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistModifierOrderingXHPASTLinterRuleTestCase.php',
|
||||
|
@ -308,6 +317,8 @@ phutil_register_library_map(array(
|
|||
'ArcanistPyLintLinterTestCase' => 'lint/linter/__tests__/ArcanistPyLintLinterTestCase.php',
|
||||
'ArcanistRaggedClassTreeEdgeXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistRaggedClassTreeEdgeXHPASTLinterRule.php',
|
||||
'ArcanistRaggedClassTreeEdgeXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistRaggedClassTreeEdgeXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistRef' => 'ref/ArcanistRef.php',
|
||||
'ArcanistRefQuery' => 'ref/ArcanistRefQuery.php',
|
||||
'ArcanistRepositoryAPI' => 'repository/api/ArcanistRepositoryAPI.php',
|
||||
'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php',
|
||||
'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php',
|
||||
|
@ -318,6 +329,8 @@ phutil_register_library_map(array(
|
|||
'ArcanistReusedIteratorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistReusedIteratorXHPASTLinterRule.php',
|
||||
'ArcanistReusedIteratorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistReusedIteratorXHPASTLinterRuleTestCase.php',
|
||||
'ArcanistRevertWorkflow' => 'workflow/ArcanistRevertWorkflow.php',
|
||||
'ArcanistRevisionRef' => 'ref/ArcanistRevisionRef.php',
|
||||
'ArcanistRevisionRefSource' => 'ref/ArcanistRevisionRefSource.php',
|
||||
'ArcanistRuboCopLinter' => 'lint/linter/ArcanistRuboCopLinter.php',
|
||||
'ArcanistRuboCopLinterTestCase' => 'lint/linter/__tests__/ArcanistRuboCopLinterTestCase.php',
|
||||
'ArcanistRubyLinter' => 'lint/linter/ArcanistRubyLinter.php',
|
||||
|
@ -399,6 +412,7 @@ phutil_register_library_map(array(
|
|||
'ArcanistWhichWorkflow' => 'workflow/ArcanistWhichWorkflow.php',
|
||||
'ArcanistWorkflow' => 'workflow/ArcanistWorkflow.php',
|
||||
'ArcanistWorkingCopyIdentity' => 'workingcopyidentity/ArcanistWorkingCopyIdentity.php',
|
||||
'ArcanistWorkingCopyStateRef' => 'ref/ArcanistWorkingCopyStateRef.php',
|
||||
'ArcanistXHPASTLintNamingHook' => 'lint/linter/xhpast/ArcanistXHPASTLintNamingHook.php',
|
||||
'ArcanistXHPASTLintNamingHookTestCase' => 'lint/linter/xhpast/__tests__/ArcanistXHPASTLintNamingHookTestCase.php',
|
||||
'ArcanistXHPASTLintSwitchHook' => 'lint/linter/xhpast/ArcanistXHPASTLintSwitchHook.php',
|
||||
|
@ -456,6 +470,7 @@ phutil_register_library_map(array(
|
|||
'ArcanistBookmarkWorkflow' => 'ArcanistFeatureWorkflow',
|
||||
'ArcanistBraceFormattingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistBranchRef' => 'ArcanistRef',
|
||||
'ArcanistBranchWorkflow' => 'ArcanistFeatureWorkflow',
|
||||
'ArcanistBrowseWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistBundle' => 'Phobject',
|
||||
|
@ -492,6 +507,7 @@ phutil_register_library_map(array(
|
|||
'ArcanistCommentSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistCommentStyleXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistCommentStyleXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistCommitRef' => 'ArcanistRef',
|
||||
'ArcanistCommitWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistCompilerLintRenderer' => 'ArcanistLintRenderer',
|
||||
'ArcanistComposerLinter' => 'ArcanistLinter',
|
||||
|
@ -574,7 +590,9 @@ phutil_register_library_map(array(
|
|||
'ArcanistGeneratedLinterTestCase' => 'ArcanistLinterTestCase',
|
||||
'ArcanistGetConfigWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistGitAPI' => 'ArcanistRepositoryAPI',
|
||||
'ArcanistGitHardpointLoader' => 'ArcanistHardpointLoader',
|
||||
'ArcanistGitLandEngine' => 'ArcanistLandEngine',
|
||||
'ArcanistGitRevisionHardpointLoader' => 'ArcanistGitHardpointLoader',
|
||||
'ArcanistGitUpstreamPath' => 'Phobject',
|
||||
'ArcanistGlobalVariableXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistGlobalVariableXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
|
@ -584,6 +602,7 @@ phutil_register_library_map(array(
|
|||
'ArcanistGoTestResultParserTestCase' => 'PhutilTestCase',
|
||||
'ArcanistHLintLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistHLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistHardpointLoader' => 'Phobject',
|
||||
'ArcanistHelpWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistHexadecimalNumericScalarCasingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
|
@ -657,10 +676,14 @@ phutil_register_library_map(array(
|
|||
'ArcanistLowercaseFunctionsXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistLowercaseFunctionsXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistMercurialAPI' => 'ArcanistRepositoryAPI',
|
||||
'ArcanistMercurialBranchCommitHardpointLoader' => 'ArcanistMercurialHardpointLoader',
|
||||
'ArcanistMercurialHardpointLoader' => 'ArcanistHardpointLoader',
|
||||
'ArcanistMercurialParser' => 'Phobject',
|
||||
'ArcanistMercurialParserTestCase' => 'PhutilTestCase',
|
||||
'ArcanistMercurialWorkingCopyCommitHardpointLoader' => 'ArcanistMercurialHardpointLoader',
|
||||
'ArcanistMergeConflictLinter' => 'ArcanistLinter',
|
||||
'ArcanistMergeConflictLinterTestCase' => 'ArcanistLinterTestCase',
|
||||
'ArcanistMessageRevisionHardpointLoader' => 'ArcanistHardpointLoader',
|
||||
'ArcanistMissingLinterException' => 'Exception',
|
||||
'ArcanistModifierOrderingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistModifierOrderingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
|
@ -724,6 +747,8 @@ phutil_register_library_map(array(
|
|||
'ArcanistPyLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistRaggedClassTreeEdgeXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistRaggedClassTreeEdgeXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistRef' => 'Phobject',
|
||||
'ArcanistRefQuery' => 'Phobject',
|
||||
'ArcanistRepositoryAPI' => 'Phobject',
|
||||
'ArcanistRepositoryAPIMiscTestCase' => 'PhutilTestCase',
|
||||
'ArcanistRepositoryAPIStateTestCase' => 'PhutilTestCase',
|
||||
|
@ -734,6 +759,8 @@ phutil_register_library_map(array(
|
|||
'ArcanistReusedIteratorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||
'ArcanistReusedIteratorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||
'ArcanistRevertWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistRevisionRef' => 'ArcanistRef',
|
||||
'ArcanistRevisionRefSource' => 'Phobject',
|
||||
'ArcanistRuboCopLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistRuboCopLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistRubyLinter' => 'ArcanistExternalLinter',
|
||||
|
@ -815,6 +842,7 @@ phutil_register_library_map(array(
|
|||
'ArcanistWhichWorkflow' => 'ArcanistWorkflow',
|
||||
'ArcanistWorkflow' => 'Phobject',
|
||||
'ArcanistWorkingCopyIdentity' => 'Phobject',
|
||||
'ArcanistWorkingCopyStateRef' => 'ArcanistRef',
|
||||
'ArcanistXHPASTLintNamingHook' => 'Phobject',
|
||||
'ArcanistXHPASTLintNamingHookTestCase' => 'PhutilTestCase',
|
||||
'ArcanistXHPASTLintSwitchHook' => 'Phobject',
|
||||
|
|
|
@ -78,6 +78,10 @@ final class ArcanistConduitEngine
|
|||
->setParameters($parameters);
|
||||
}
|
||||
|
||||
public function resolveCall($method, array $parameters) {
|
||||
return $this->newCall($method, $parameters)->resolve();
|
||||
}
|
||||
|
||||
public function newFuture(ArcanistConduitCall $call) {
|
||||
$method = $call->getMethod();
|
||||
$parameters = $call->getParameters();
|
||||
|
|
10
src/loader/ArcanistGitHardpointLoader.php
Normal file
10
src/loader/ArcanistGitHardpointLoader.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
abstract class ArcanistGitHardpointLoader
|
||||
extends ArcanistHardpointLoader {
|
||||
|
||||
public function canLoadRepositoryAPI(ArcanistRepositoryAPI $api) {
|
||||
return ($api instanceof ArcanistGitAPI);
|
||||
}
|
||||
|
||||
}
|
80
src/loader/ArcanistGitRevisionHardpointLoader.php
Normal file
80
src/loader/ArcanistGitRevisionHardpointLoader.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistGitRevisionHardpointLoader
|
||||
extends ArcanistGitHardpointLoader {
|
||||
|
||||
const LOADERKEY = 'git.revision';
|
||||
|
||||
public function canLoadRef(ArcanistRef $ref) {
|
||||
return ($ref instanceof ArcanistWorkingCopyStateRef);
|
||||
}
|
||||
|
||||
public function canLoadHardpoint(ArcanistRef $ref, $hardpoint) {
|
||||
return ($hardpoint == 'revisionRefs');
|
||||
}
|
||||
|
||||
public function loadHardpoints(array $refs, $hardpoint) {
|
||||
$this->newQuery($refs)
|
||||
->needHardpoints(
|
||||
array(
|
||||
'commitRef',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$hashes = array();
|
||||
$map = array();
|
||||
foreach ($refs as $ref_key => $ref) {
|
||||
$commit = $ref->getCommitRef();
|
||||
|
||||
$commit_hashes = array();
|
||||
|
||||
$commit_hashes[] = array(
|
||||
'gtcm',
|
||||
$commit->getCommitHash(),
|
||||
);
|
||||
|
||||
$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();
|
||||
if ($hashes) {
|
||||
$revisions = $this->resolveCall(
|
||||
'differential.query',
|
||||
array(
|
||||
'commitHashes' => $hashes,
|
||||
));
|
||||
|
||||
foreach ($revisions as $dict) {
|
||||
$revision_hashes = idx($dict, 'hashes');
|
||||
if (!$revision_hashes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$revision_ref = ArcanistRevisionRef::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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function getHashKey(array $hash) {
|
||||
return $hash[0].':'.$hash[1];
|
||||
}
|
||||
|
||||
}
|
58
src/loader/ArcanistHardpointLoader.php
Normal file
58
src/loader/ArcanistHardpointLoader.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
abstract class ArcanistHardpointLoader
|
||||
extends Phobject {
|
||||
|
||||
private $query;
|
||||
private $conduitEngine;
|
||||
|
||||
abstract public function canLoadRepositoryAPI(ArcanistRepositoryAPI $api);
|
||||
abstract public function canLoadRef(ArcanistRef $ref);
|
||||
abstract public function canLoadHardpoint(ArcanistRef $ref, $hardpoint);
|
||||
abstract public function loadHardpoints(array $refs, $hardpoint);
|
||||
|
||||
final public function setQuery(ArcanistRefQuery $query) {
|
||||
$this->query = $query;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getQuery() {
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
final public function getConduitEngine() {
|
||||
return $this->getQuery()->getConduitEngine();
|
||||
}
|
||||
|
||||
final protected function newQuery(array $refs) {
|
||||
return id(new ArcanistRefQuery())
|
||||
->setRepositoryAPI($this->getQuery()->getRepositoryAPI())
|
||||
->setConduitEngine($this->getQuery()->getConduitEngine())
|
||||
->setRefs($refs);
|
||||
}
|
||||
|
||||
final public function getLoaderKey() {
|
||||
return $this->getPhobjectClassConstant('LOADERKEY', 64);
|
||||
}
|
||||
|
||||
final public static function getAllLoaders() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setUniqueMethod('getLoaderKey')
|
||||
->execute();
|
||||
}
|
||||
|
||||
final public function resolveCall($method, array $parameters) {
|
||||
return $this->newCall($method, $parameters)->resolve();
|
||||
}
|
||||
|
||||
final public function newCall($method, array $parameters) {
|
||||
return $this->getConduitEngine()->newCall($method, $parameters);
|
||||
}
|
||||
|
||||
final protected function newFutureIterator(array $futures) {
|
||||
return id(new FutureIterator($futures))
|
||||
->limit(16);
|
||||
}
|
||||
|
||||
}
|
49
src/loader/ArcanistMercurialBranchCommitHardpointLoader.php
Normal file
49
src/loader/ArcanistMercurialBranchCommitHardpointLoader.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistMercurialBranchCommitHardpointLoader
|
||||
extends ArcanistMercurialHardpointLoader {
|
||||
|
||||
const LOADERKEY = 'hg.branch.commit';
|
||||
|
||||
public function canLoadRef(ArcanistRef $ref) {
|
||||
return ($ref instanceof ArcanistBranchRef);
|
||||
}
|
||||
|
||||
public function canLoadHardpoint(ArcanistRef $ref, $hardpoint) {
|
||||
return ($hardpoint == 'commitRef');
|
||||
}
|
||||
|
||||
public function loadHardpoints(array $refs, $hardpoint) {
|
||||
$api = $this->getQuery()->getRepositoryAPI();
|
||||
|
||||
$futures = array();
|
||||
foreach ($refs as $ref_key => $branch) {
|
||||
$branch_name = $branch->getBranchName();
|
||||
|
||||
$futures[$ref_key] = $api->execFutureLocal(
|
||||
'log -l 1 --template %s -r %s',
|
||||
"{node}\1{date|hgdate}\1{p1node}\1{desc|firstline}\1{desc}",
|
||||
hgsprintf('%s', $branch_name));
|
||||
}
|
||||
|
||||
$results = array();
|
||||
|
||||
$iterator = $this->newFutureIterator($futures);
|
||||
foreach ($iterator as $ref_key => $future) {
|
||||
list($info) = $future->resolvex();
|
||||
|
||||
$fields = explode("\1", trim($info), 5);
|
||||
list($hash, $epoch, $parent, $desc, $text) = $fields;
|
||||
|
||||
$commit_ref = $api->newCommitRef()
|
||||
->setCommitHash($hash)
|
||||
->setCommitEpoch((int)$epoch)
|
||||
->attachMessage($text);
|
||||
|
||||
$results[$ref_key] = $commit_ref;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
10
src/loader/ArcanistMercurialHardpointLoader.php
Normal file
10
src/loader/ArcanistMercurialHardpointLoader.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
abstract class ArcanistMercurialHardpointLoader
|
||||
extends ArcanistHardpointLoader {
|
||||
|
||||
public function canLoadRepositoryAPI(ArcanistRepositoryAPI $api) {
|
||||
return ($api instanceof ArcanistMercurialAPI);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistMercurialWorkingCopyCommitHardpointLoader
|
||||
extends ArcanistMercurialHardpointLoader {
|
||||
|
||||
const LOADERKEY = 'hg.state.commit';
|
||||
|
||||
public function canLoadRef(ArcanistRef $ref) {
|
||||
return ($ref instanceof ArcanistWorkingCopyStateRef);
|
||||
}
|
||||
|
||||
public function canLoadHardpoint(ArcanistRef $ref, $hardpoint) {
|
||||
return ($hardpoint == 'commitRef');
|
||||
}
|
||||
|
||||
public function loadHardpoints(array $refs, $hardpoint) {
|
||||
$branch_refs = array();
|
||||
foreach ($refs as $ref_key => $ref) {
|
||||
if ($ref->hasAttachedHardpoint('branchRef')) {
|
||||
$branch_refs[$ref_key] = $ref->getBranchRef();
|
||||
}
|
||||
}
|
||||
|
||||
if ($branch_refs) {
|
||||
$this->newQuery($branch_refs)
|
||||
->needHardpoints(
|
||||
array(
|
||||
'commitRef',
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
return mpull($branch_refs, 'getCommitRef');
|
||||
}
|
||||
|
||||
}
|
82
src/loader/ArcanistMessageRevisionHardpointLoader.php
Normal file
82
src/loader/ArcanistMessageRevisionHardpointLoader.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistMessageRevisionHardpointLoader
|
||||
extends ArcanistHardpointLoader {
|
||||
|
||||
const LOADERKEY = 'message.revision';
|
||||
|
||||
public function canLoadRepositoryAPI(ArcanistRepositoryAPI $api) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function canLoadRef(ArcanistRef $ref) {
|
||||
return ($ref instanceof ArcanistWorkingCopyStateRef);
|
||||
}
|
||||
|
||||
public function canLoadHardpoint(ArcanistRef $ref, $hardpoint) {
|
||||
return ($hardpoint == 'revisionRefs');
|
||||
}
|
||||
|
||||
public function loadHardpoints(array $refs, $hardpoint) {
|
||||
$this->newQuery($refs)
|
||||
->needHardpoints(
|
||||
array(
|
||||
'commitRef',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$commit_refs = array();
|
||||
foreach ($refs as $ref) {
|
||||
$commit_refs[] = $ref->getCommitRef();
|
||||
}
|
||||
|
||||
$this->newQuery($commit_refs)
|
||||
->needHardpoints(
|
||||
array(
|
||||
'message',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$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 = $this->resolveCall(
|
||||
'differential.query',
|
||||
array(
|
||||
'ids' => array_keys($map),
|
||||
));
|
||||
|
||||
foreach ($revisions as $dict) {
|
||||
$revision_ref = ArcanistRevisionRef::newFromConduit($dict);
|
||||
$id = $dict['id'];
|
||||
|
||||
$state_refs = idx($map, $id, array());
|
||||
foreach ($state_refs as $ref_key => $state_ref) {
|
||||
$results[$ref_key][] = $revision_ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
57
src/ref/ArcanistBranchRef.php
Normal file
57
src/ref/ArcanistBranchRef.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistBranchRef
|
||||
extends ArcanistRef {
|
||||
|
||||
private $branchName;
|
||||
private $refName;
|
||||
private $isCurrentBranch;
|
||||
|
||||
public function getRefIdentifier() {
|
||||
return pht('Branch %s', $this->getBranchName());
|
||||
}
|
||||
|
||||
public function defineHardpoints() {
|
||||
return array(
|
||||
'commitRef' => array(
|
||||
'type' => 'ArcanistCommitRef',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function setBranchName($branch_name) {
|
||||
$this->branchName = $branch_name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBranchName() {
|
||||
return $this->branchName;
|
||||
}
|
||||
|
||||
public function setRefName($ref_name) {
|
||||
$this->refName = $ref_name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRefName() {
|
||||
return $this->refName;
|
||||
}
|
||||
|
||||
public function setIsCurrentBranch($is_current_branch) {
|
||||
$this->isCurrentBranch = $is_current_branch;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsCurrentBranch() {
|
||||
return $this->isCurrentBranch;
|
||||
}
|
||||
|
||||
public function attachCommitRef(ArcanistCommitRef $ref) {
|
||||
return $this->attachHardpoint('commitRef', $ref);
|
||||
}
|
||||
|
||||
public function getCommitRef() {
|
||||
return $this->getHardpoint('commitRef');
|
||||
}
|
||||
|
||||
}
|
76
src/ref/ArcanistCommitRef.php
Normal file
76
src/ref/ArcanistCommitRef.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistCommitRef
|
||||
extends ArcanistRef {
|
||||
|
||||
private $commitHash;
|
||||
private $treeHash;
|
||||
private $commitEpoch;
|
||||
private $authorEpoch;
|
||||
|
||||
public function getRefIdentifier() {
|
||||
return pht('Commit %s', $this->getCommitHash());
|
||||
}
|
||||
|
||||
public function defineHardpoints() {
|
||||
return array(
|
||||
'message' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function setCommitHash($commit_hash) {
|
||||
$this->commitHash = $commit_hash;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCommitHash() {
|
||||
return $this->commitHash;
|
||||
}
|
||||
|
||||
public function setTreeHash($tree_hash) {
|
||||
$this->treeHash = $tree_hash;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTreeHash() {
|
||||
return $this->treeHash;
|
||||
}
|
||||
|
||||
public function setCommitEpoch($commit_epoch) {
|
||||
$this->commitEpoch = $commit_epoch;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCommitEpoch() {
|
||||
return $this->commitEpoch;
|
||||
}
|
||||
|
||||
public function setAuthorEpoch($author_epoch) {
|
||||
$this->authorEpoch = $author_epoch;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAuthorEpoch() {
|
||||
return $this->authorEpoch;
|
||||
}
|
||||
|
||||
public function getSummary() {
|
||||
$message = $this->getMessage();
|
||||
|
||||
$message = trim($message);
|
||||
$lines = phutil_split_lines($message, false);
|
||||
|
||||
return head($lines);
|
||||
}
|
||||
|
||||
public function attachMessage($message) {
|
||||
return $this->attachHardpoint('message', $message);
|
||||
}
|
||||
|
||||
public function getMessage() {
|
||||
return $this->getHardpoint('message');
|
||||
}
|
||||
|
||||
}
|
106
src/ref/ArcanistRef.php
Normal file
106
src/ref/ArcanistRef.php
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
abstract class ArcanistRef
|
||||
extends Phobject {
|
||||
|
||||
private $hardpoints = array();
|
||||
|
||||
abstract public function getRefIdentifier();
|
||||
abstract public function defineHardpoints();
|
||||
|
||||
final public function hasHardpoint($hardpoint) {
|
||||
$map = $this->getHardpointMap();
|
||||
return isset($map[$hardpoint]);
|
||||
}
|
||||
|
||||
final public function hasAttachedHardpoint($hardpoint) {
|
||||
if (array_key_exists($hardpoint, $this->hardpoints)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->canReadHardpoint($hardpoint);
|
||||
}
|
||||
|
||||
final public function attachHardpoint($hardpoint, $value) {
|
||||
if (!$this->hasHardpoint($hardpoint)) {
|
||||
throw new Exception(pht('No hardpoint "%s".', $hardpoint));
|
||||
}
|
||||
|
||||
$this->hardpoints[$hardpoint] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function appendHardpoint($hardpoint, array $value) {
|
||||
if (!$this->isVectorHardpoint($hardpoint)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Hardpoint "%s" is not a vector hardpoint.',
|
||||
$hardpoint));
|
||||
}
|
||||
|
||||
if (!isset($this->hardpoints[$hardpoint])) {
|
||||
$this->hardpoints[$hardpoint] = array();
|
||||
}
|
||||
|
||||
$this->hardpoints[$hardpoint] = $this->mergeHardpoint(
|
||||
$hardpoint,
|
||||
$this->hardpoints[$hardpoint],
|
||||
$value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function mergeHardpoint($hardpoint, array $src, array $new) {
|
||||
foreach ($new as $value) {
|
||||
$src[] = $value;
|
||||
}
|
||||
return $src;
|
||||
}
|
||||
|
||||
final public function isVectorHardpoint($hardpoint) {
|
||||
if (!$this->hasHardpoint($hardpoint)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$map = $this->getHardpointMap();
|
||||
$spec = idx($map, $hardpoint, array());
|
||||
|
||||
return (idx($spec, 'vector') === true);
|
||||
}
|
||||
|
||||
final public function getHardpoint($hardpoint) {
|
||||
if (!$this->hasAttachedHardpoint($hardpoint)) {
|
||||
if (!$this->hasHardpoint($hardpoint)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Ref does not have hardpoint "%s"!',
|
||||
$hardpoint));
|
||||
} else {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Hardpoint "%s" is not attached!',
|
||||
$hardpoint));
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists($hardpoint, $this->hardpoints)) {
|
||||
return $this->hardpoints[$hardpoint];
|
||||
}
|
||||
|
||||
return $this->readHardpoint($hardpoint);
|
||||
}
|
||||
|
||||
private function getHardpointMap() {
|
||||
return $this->defineHardpoints();
|
||||
}
|
||||
|
||||
protected function canReadHardpoint($hardpoint) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function readHardpoint($hardpoint) {
|
||||
throw new Exception(pht('Can not read hardpoint "%s".', $hardpoint));
|
||||
}
|
||||
|
||||
}
|
147
src/ref/ArcanistRefQuery.php
Normal file
147
src/ref/ArcanistRefQuery.php
Normal file
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistRefQuery extends Phobject {
|
||||
|
||||
private $repositoryAPI;
|
||||
private $conduitEngine;
|
||||
|
||||
private $refs;
|
||||
private $hardpoints;
|
||||
|
||||
public function setRefs(array $refs) {
|
||||
assert_instances_of($refs, 'ArcanistRef');
|
||||
$this->refs = $refs;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRefs() {
|
||||
return $this->refs;
|
||||
}
|
||||
|
||||
public function setRepositoryAPI(ArcanistRepositoryAPI $repository_api) {
|
||||
$this->repositoryAPI = $repository_api;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRepositoryAPI() {
|
||||
return $this->repositoryAPI;
|
||||
}
|
||||
|
||||
public function setConduitEngine(ArcanistConduitEngine $conduit_engine) {
|
||||
$this->conduitEngine = $conduit_engine;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getConduitEngine() {
|
||||
return $this->conduitEngine;
|
||||
}
|
||||
|
||||
public function needHardpoints(array $hardpoints) {
|
||||
$this->hardpoints = $hardpoints;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$refs = $this->getRefs();
|
||||
|
||||
if ($this->refs === null) {
|
||||
throw new PhutilInvalidStateException('setRefs');
|
||||
}
|
||||
|
||||
if ($this->hardpoints === null) {
|
||||
throw new PhutilInvalidStateException('needHardpoints');
|
||||
}
|
||||
|
||||
$api = $this->getRepositoryAPI();
|
||||
$all_loaders = ArcanistHardpointLoader::getAllLoaders();
|
||||
|
||||
$loaders = array();
|
||||
foreach ($all_loaders as $loader_key => $loader) {
|
||||
if (!$loader->canLoadRepositoryAPI($api)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$loaders[$loader_key] = id(clone $loader)
|
||||
->setQuery($this);
|
||||
}
|
||||
|
||||
foreach ($this->hardpoints as $hardpoint) {
|
||||
$load = array();
|
||||
$need = array();
|
||||
$has_hardpoint = false;
|
||||
foreach ($refs as $ref_key => $ref) {
|
||||
if (!$ref->hasHardpoint($hardpoint)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$has_hardpoint = true;
|
||||
|
||||
if ($ref->hasAttachedHardpoint($hardpoint)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($loaders as $loader_key => $loader) {
|
||||
if (!$loader->canLoadRef($ref)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$loader->canLoadHardpoint($ref, $hardpoint)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$load[$loader_key][$ref_key] = $ref;
|
||||
}
|
||||
|
||||
$need[$ref_key] = $ref_key;
|
||||
}
|
||||
|
||||
if ($refs && !$has_hardpoint) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'No ref in query has hardpoint "%s".',
|
||||
$hardpoint));
|
||||
}
|
||||
|
||||
$vectors = array();
|
||||
foreach ($need as $ref_key) {
|
||||
$ref = $refs[$ref_key];
|
||||
if ($ref->isVectorHardpoint($hardpoint)) {
|
||||
$vectors[$ref_key] = $ref_key;
|
||||
$ref->attachHardpoint($hardpoint, array());
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($load as $loader_key => $loader_refs) {
|
||||
$loader_refs = array_select_keys($loader_refs, $need);
|
||||
|
||||
$loader = $loaders[$loader_key];
|
||||
$data = $loader->loadHardpoints($loader_refs, $hardpoint);
|
||||
|
||||
foreach ($data as $ref_key => $value) {
|
||||
$ref = $refs[$ref_key];
|
||||
if (isset($vectors[$ref_key])) {
|
||||
$ref->appendHardpoint($hardpoint, $value);
|
||||
} else {
|
||||
unset($need[$ref_key]);
|
||||
$ref->attachHardpoint($hardpoint, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($vectors as $ref_key) {
|
||||
unset($need[$ref_key]);
|
||||
}
|
||||
|
||||
if ($need) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Nothing could attach data to hardpoint "%s" for ref "%s".',
|
||||
$hardpoint,
|
||||
$refs[head($need)]->getRefIdentifier()));
|
||||
}
|
||||
}
|
||||
|
||||
return $refs;
|
||||
}
|
||||
|
||||
}
|
52
src/ref/ArcanistRevisionRef.php
Normal file
52
src/ref/ArcanistRevisionRef.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistRevisionRef
|
||||
extends ArcanistRef {
|
||||
|
||||
private $parameters;
|
||||
private $sources = array();
|
||||
|
||||
public function getRefIdentifier() {
|
||||
return pht('Revision %s', $this->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 getFullName() {
|
||||
return pht('%s: %s', $this->getMonogram(), $this->getName());
|
||||
}
|
||||
|
||||
public function getID() {
|
||||
return idx($this->parameters, 'id');
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
return idx($this->parameters, 'title');
|
||||
}
|
||||
|
||||
public function addSource(ArcanistRevisionRefSource $source) {
|
||||
$this->sources[] = $source;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSources() {
|
||||
return $this->sources;
|
||||
}
|
||||
|
||||
}
|
4
src/ref/ArcanistRevisionRefSource.php
Normal file
4
src/ref/ArcanistRevisionRefSource.php
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
|
||||
abstract class ArcanistRevisionRefSource
|
||||
extends Phobject {}
|
111
src/ref/ArcanistWorkingCopyStateRef.php
Normal file
111
src/ref/ArcanistWorkingCopyStateRef.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistWorkingCopyStateRef
|
||||
extends ArcanistRef {
|
||||
|
||||
public function getRefIdentifier() {
|
||||
// TODO: This could check attached hardpoints and render something more
|
||||
// insightful.
|
||||
return pht('Working Copy State');
|
||||
}
|
||||
|
||||
public function defineHardpoints() {
|
||||
return array(
|
||||
'commitRef' => array(
|
||||
'type' => 'ArcanistCommitRef',
|
||||
),
|
||||
'branchRef' => array(
|
||||
'type' => 'ArcanistBranchRef',
|
||||
),
|
||||
'revisionRefs' => array(
|
||||
'type' => 'ArcanistRevisionRef',
|
||||
'vector' => true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function attachBranchRef(ArcanistBranchRef $branch_ref) {
|
||||
return $this->attachHardpoint('branchRef', $branch_ref);
|
||||
}
|
||||
|
||||
public function getBranchRef() {
|
||||
return $this->getHardpoint('branchRef');
|
||||
}
|
||||
|
||||
public function setCommitRef(ArcanistCommitRef $commit_ref) {
|
||||
return $this->attachHardpoint('commitRef', $commit_ref);
|
||||
}
|
||||
|
||||
public function getCommitRef() {
|
||||
return $this->getHardpoint('commitRef');
|
||||
}
|
||||
|
||||
public function getRevisionRefs() {
|
||||
return $this->getHardpoint('revisionRefs');
|
||||
}
|
||||
|
||||
public function getRevisionRef() {
|
||||
if ($this->hasAmbiguousRevisionRefs()) {
|
||||
throw new Exception(
|
||||
pht('State has multiple ambiguous revisions refs.'));
|
||||
}
|
||||
|
||||
$refs = $this->getRevisionRefs();
|
||||
if ($refs) {
|
||||
return head($refs);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hasAmbiguousRevisionRefs() {
|
||||
return (count($this->getRevisionRefs()) > 1);
|
||||
}
|
||||
|
||||
protected function canReadHardpoint($hardpoint) {
|
||||
switch ($hardpoint) {
|
||||
case 'commitRef':
|
||||
// If we have a branch ref, we can try to read the commit ref from the
|
||||
// branch ref.
|
||||
if ($this->hasAttachedHardpoint('branchRef')) {
|
||||
if ($this->getBranchRef()->hasAttachedHardpoint('commitRef')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function readHardpoint($hardpoint) {
|
||||
switch ($hardpoint) {
|
||||
case 'commitRef':
|
||||
return $this->getBranchRef()->getCommitRef();
|
||||
}
|
||||
|
||||
return parent::readHardpoint($hardpoint);
|
||||
}
|
||||
|
||||
protected function mergeHardpoint($hardpoint, array $src, array $new) {
|
||||
if ($hardpoint == 'revisionRefs') {
|
||||
$src = mpull($src, null, 'getID');
|
||||
$new = mpull($new, null, 'getID');
|
||||
|
||||
foreach ($new as $id => $ref) {
|
||||
if (isset($src[$id])) {
|
||||
foreach ($ref->getSources() as $source) {
|
||||
$src[$id]->addSource($source);
|
||||
}
|
||||
} else {
|
||||
$src[$id] = $ref;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($src);
|
||||
}
|
||||
|
||||
return parent::mergeHardpoint($hardpoint, $src, $new);
|
||||
}
|
||||
|
||||
}
|
|
@ -1014,6 +1014,7 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
|||
$result[] = array(
|
||||
'current' => ($branch === $current),
|
||||
'name' => $branch,
|
||||
'ref' => $ref,
|
||||
'hash' => $hash,
|
||||
'tree' => $tree,
|
||||
'epoch' => (int)$epoch,
|
||||
|
@ -1026,6 +1027,27 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
|
|||
return $result;
|
||||
}
|
||||
|
||||
public function getAllBranchRefs() {
|
||||
$branches = $this->getAllBranches();
|
||||
|
||||
$refs = array();
|
||||
foreach ($branches as $branch) {
|
||||
$commit_ref = $this->newCommitRef()
|
||||
->setCommitHash($branch['hash'])
|
||||
->setTreeHash($branch['tree'])
|
||||
->setCommitEpoch($branch['epoch'])
|
||||
->attachMessage($branch['text']);
|
||||
|
||||
$refs[] = $this->newBranchRef()
|
||||
->setBranchName($branch['name'])
|
||||
->setRefName($branch['ref'])
|
||||
->setIsCurrentBranch($branch['current'])
|
||||
->attachCommitRef($commit_ref);
|
||||
}
|
||||
|
||||
return $refs;
|
||||
}
|
||||
|
||||
public function getWorkingCopyRevision() {
|
||||
list($stdout) = $this->execxLocal('rev-parse HEAD');
|
||||
return rtrim($stdout, "\n");
|
||||
|
|
|
@ -583,6 +583,19 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
|
|||
return $return;
|
||||
}
|
||||
|
||||
public function getAllBranchRefs() {
|
||||
$branches = $this->getAllBranches();
|
||||
|
||||
$refs = array();
|
||||
foreach ($branches as $branch) {
|
||||
$refs[] = $this->newBranchRef()
|
||||
->setBranchName($branch['name'])
|
||||
->setIsCurrentBranch($branch['current']);
|
||||
}
|
||||
|
||||
return $refs;
|
||||
}
|
||||
|
||||
public function hasLocalCommit($commit) {
|
||||
try {
|
||||
$this->getCanonicalRevisionName($commit);
|
||||
|
|
|
@ -375,6 +375,10 @@ abstract class ArcanistRepositoryAPI extends Phobject {
|
|||
return array();
|
||||
}
|
||||
|
||||
public function getAllBranchRefs() {
|
||||
throw new ArcanistCapabilityNotSupportedException($this);
|
||||
}
|
||||
|
||||
public function hasLocalCommit($commit) {
|
||||
throw new ArcanistCapabilityNotSupportedException($this);
|
||||
}
|
||||
|
@ -668,4 +672,12 @@ abstract class ArcanistRepositoryAPI extends Phobject {
|
|||
return null;
|
||||
}
|
||||
|
||||
final public function newCommitRef() {
|
||||
return new ArcanistCommitRef();
|
||||
}
|
||||
|
||||
final public function newBranchRef() {
|
||||
return new ArcanistBranchRef();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,18 +38,10 @@ EOTEXT
|
|||
);
|
||||
}
|
||||
|
||||
public function requiresConduit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function requiresRepositoryAPI() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function requiresAuthentication() {
|
||||
return !$this->getArgument('branch');
|
||||
}
|
||||
|
||||
public function getArguments() {
|
||||
return array(
|
||||
'view-all' => array(
|
||||
|
@ -86,15 +78,29 @@ EOTEXT
|
|||
return $this->checkoutBranch($names);
|
||||
}
|
||||
|
||||
$branches = $repository_api->getAllBranches();
|
||||
// TODO: Everything in this whole workflow that says "branch" means
|
||||
// "bookmark" in Mercurial.
|
||||
|
||||
$branches = $repository_api->getAllBranchRefs();
|
||||
if (!$branches) {
|
||||
throw new ArcanistUsageException(
|
||||
pht('No branches in this working copy.'));
|
||||
}
|
||||
|
||||
$branches = $this->loadCommitInfo($branches);
|
||||
$revisions = $this->loadRevisions($branches);
|
||||
$this->printBranches($branches, $revisions);
|
||||
$states = array();
|
||||
foreach ($branches as $branch) {
|
||||
$states[] = $this->newWorkingCopyStateRef()
|
||||
->attachBranchRef($branch);
|
||||
}
|
||||
|
||||
$this->newRefQuery($states)
|
||||
->needHardpoints(
|
||||
array(
|
||||
'revisionRefs',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$this->printBranches($states);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -125,21 +131,19 @@ EOTEXT
|
|||
if ($err) {
|
||||
$match = null;
|
||||
if (preg_match('/^D(\d+)$/', $name, $match)) {
|
||||
try {
|
||||
$diff = $this->getConduit()->callMethodSynchronous(
|
||||
'differential.querydiffs',
|
||||
array(
|
||||
'revisionIDs' => array($match[1]),
|
||||
));
|
||||
$diff = head($diff);
|
||||
$diff = $this->getConduitEngine()->resolveCall(
|
||||
'differential.querydiffs',
|
||||
array(
|
||||
'revisionIDs' => array($match[1]),
|
||||
));
|
||||
$diff = head($diff);
|
||||
|
||||
if ($diff['branch'] != '') {
|
||||
$name = $diff['branch'];
|
||||
list($err, $stdout, $stderr) = $api->execManualLocal(
|
||||
$command,
|
||||
$name);
|
||||
}
|
||||
} catch (ConduitClientException $ex) {}
|
||||
if ($diff['branch'] != '') {
|
||||
$name = $diff['branch'];
|
||||
list($err, $stdout, $stderr) = $api->execManualLocal(
|
||||
$command,
|
||||
$name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,99 +175,7 @@ EOTEXT
|
|||
return $err;
|
||||
}
|
||||
|
||||
private function loadCommitInfo(array $branches) {
|
||||
$repository_api = $this->getRepositoryAPI();
|
||||
|
||||
$branches = ipull($branches, null, 'name');
|
||||
|
||||
if ($repository_api instanceof ArcanistMercurialAPI) {
|
||||
$futures = array();
|
||||
foreach ($branches as $branch) {
|
||||
$futures[$branch['name']] = $repository_api->execFutureLocal(
|
||||
'log -l 1 --template %s -r %s',
|
||||
"{node}\1{date|hgdate}\1{p1node}\1{desc|firstline}\1{desc}",
|
||||
hgsprintf('%s', $branch['name']));
|
||||
}
|
||||
|
||||
$futures = id(new FutureIterator($futures))
|
||||
->limit(16);
|
||||
foreach ($futures as $name => $future) {
|
||||
list($info) = $future->resolvex();
|
||||
|
||||
$fields = explode("\1", trim($info), 5);
|
||||
list($hash, $epoch, $tree, $desc, $text) = $fields;
|
||||
|
||||
$branches[$name] += array(
|
||||
'hash' => $hash,
|
||||
'desc' => $desc,
|
||||
'tree' => $tree,
|
||||
'epoch' => (int)$epoch,
|
||||
'text' => $text,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($branches as $name => $branch) {
|
||||
$text = $branch['text'];
|
||||
|
||||
try {
|
||||
$message = ArcanistDifferentialCommitMessage::newFromRawCorpus($text);
|
||||
$id = $message->getRevisionID();
|
||||
|
||||
$branch['revisionID'] = $id;
|
||||
} catch (ArcanistUsageException $ex) {
|
||||
// In case of invalid commit message which fails the parsing,
|
||||
// do nothing.
|
||||
$branch['revisionID'] = null;
|
||||
}
|
||||
|
||||
$branches[$name] = $branch;
|
||||
}
|
||||
|
||||
return $branches;
|
||||
}
|
||||
|
||||
private function loadRevisions(array $branches) {
|
||||
$ids = array();
|
||||
$hashes = array();
|
||||
|
||||
foreach ($branches as $branch) {
|
||||
if ($branch['revisionID']) {
|
||||
$ids[] = $branch['revisionID'];
|
||||
}
|
||||
$hashes[] = array('gtcm', $branch['hash']);
|
||||
$hashes[] = array('gttr', $branch['tree']);
|
||||
}
|
||||
|
||||
$calls = array();
|
||||
|
||||
if ($ids) {
|
||||
$calls[] = $this->getConduit()->callMethod(
|
||||
'differential.query',
|
||||
array(
|
||||
'ids' => $ids,
|
||||
));
|
||||
}
|
||||
|
||||
if ($hashes) {
|
||||
$calls[] = $this->getConduit()->callMethod(
|
||||
'differential.query',
|
||||
array(
|
||||
'commitHashes' => $hashes,
|
||||
));
|
||||
}
|
||||
|
||||
$results = array();
|
||||
foreach (new FutureIterator($calls) as $call) {
|
||||
$results[] = $call->resolve();
|
||||
}
|
||||
|
||||
return array_mergev($results);
|
||||
}
|
||||
|
||||
private function printBranches(array $branches, array $revisions) {
|
||||
$revisions = ipull($revisions, null, 'id');
|
||||
|
||||
private function printBranches(array $states) {
|
||||
static $color_map = array(
|
||||
'Closed' => 'cyan',
|
||||
'Needs Review' => 'magenta',
|
||||
|
@ -282,48 +194,45 @@ EOTEXT
|
|||
);
|
||||
|
||||
$out = array();
|
||||
foreach ($branches as $branch) {
|
||||
$revision = idx($revisions, idx($branch, 'revisionID'));
|
||||
foreach ($states as $state) {
|
||||
$branch = $state->getBranchRef();
|
||||
|
||||
// If we haven't identified a revision by ID, try to identify it by hash.
|
||||
if (!$revision) {
|
||||
foreach ($revisions as $rev) {
|
||||
$hashes = idx($rev, 'hashes', array());
|
||||
foreach ($hashes as $hash) {
|
||||
if (($hash[0] == 'gtcm' && $hash[1] == $branch['hash']) ||
|
||||
($hash[0] == 'gttr' && $hash[1] == $branch['tree'])) {
|
||||
$revision = $rev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$revision = null;
|
||||
if ($state->hasAmbiguousRevisionRefs()) {
|
||||
$status = pht('Ambiguous Revision');
|
||||
} else {
|
||||
$revision = $state->getRevisionRef();
|
||||
if ($revision) {
|
||||
$status = $revision->getStatusDisplayName();
|
||||
} else {
|
||||
$status = pht('No Revision');
|
||||
}
|
||||
}
|
||||
|
||||
if ($revision) {
|
||||
$desc = 'D'.$revision['id'].': '.$revision['title'];
|
||||
$status = $revision['statusName'];
|
||||
} else {
|
||||
$desc = $branch['desc'];
|
||||
$status = pht('No Revision');
|
||||
}
|
||||
|
||||
if (!$this->getArgument('view-all') && !$branch['current']) {
|
||||
if (!$this->getArgument('view-all') && !$branch->getIsCurrentBranch()) {
|
||||
if ($status == 'Closed' || $status == 'Abandoned') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$epoch = $branch['epoch'];
|
||||
$commit = $branch->getCommitRef();
|
||||
$epoch = $commit->getCommitEpoch();
|
||||
|
||||
$color = idx($color_map, $status, 'default');
|
||||
$ssort = sprintf('%d%012d', idx($ssort_map, $status, 0), $epoch);
|
||||
|
||||
if ($revision) {
|
||||
$desc = $revision->getFullName();
|
||||
} else {
|
||||
$desc = $commit->getSummary();
|
||||
}
|
||||
|
||||
$out[] = array(
|
||||
'name' => $branch['name'],
|
||||
'current' => $branch['current'],
|
||||
'name' => $branch->getBranchName(),
|
||||
'current' => $branch->getIsCurrentBranch(),
|
||||
'status' => $status,
|
||||
'desc' => $desc,
|
||||
'revision' => $revision ? $revision['id'] : null,
|
||||
'revision' => $revision ? $revision->getID() : null,
|
||||
'color' => $color,
|
||||
'esort' => $epoch,
|
||||
'epoch' => $epoch,
|
||||
|
|
|
@ -2073,4 +2073,16 @@ abstract class ArcanistWorkflow extends Phobject {
|
|||
return $this->conduitEngine;
|
||||
}
|
||||
|
||||
final protected function newWorkingCopyStateRef() {
|
||||
return new ArcanistWorkingCopyStateRef();
|
||||
}
|
||||
|
||||
final protected function newRefQuery(array $refs) {
|
||||
assert_instances_of($refs, 'ArcanistRef');
|
||||
return id(new ArcanistRefQuery())
|
||||
->setRepositoryAPI($this->getRepositoryAPI())
|
||||
->setConduitEngine($this->getConduitEngine())
|
||||
->setRefs($refs);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue