mirror of
https://we.phorge.it/source/arcanist.git
synced 2025-01-04 03:41:01 +01:00
Rebuild "arc browse" using refs and hardpoints
Summary: Ref T10895. This mostly modularizes `arc browse` and puts it on ref/hardpoint infrastructure. Feels okay-ish? Major gripes: - Messaging for "some stuff won't work because you're in a random directory, not a working copy" could be better, but I think I want something like the "Guidance" infrastructure for this. - The `requiresStuff()` / `desiresStuff()` interactions on Workflow continue to feel bad, but I think I can sneak by without fixing those for now. - I want to improve some of the other UI/UX stuff but this diff is already gigantic. Test Plan: Ran `arc browse .`, `arc browse master`, `arc browse README.md`, inside and outside working directories. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10895 Differential Revision: https://secure.phabricator.com/D16925
This commit is contained in:
parent
71473af895
commit
909668082e
13 changed files with 868 additions and 240 deletions
|
@ -42,7 +42,13 @@ phutil_register_library_map(array(
|
||||||
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistBraceFormattingXHPASTLinterRuleTestCase.php',
|
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistBraceFormattingXHPASTLinterRuleTestCase.php',
|
||||||
'ArcanistBranchRef' => 'ref/ArcanistBranchRef.php',
|
'ArcanistBranchRef' => 'ref/ArcanistBranchRef.php',
|
||||||
'ArcanistBranchWorkflow' => 'workflow/ArcanistBranchWorkflow.php',
|
'ArcanistBranchWorkflow' => 'workflow/ArcanistBranchWorkflow.php',
|
||||||
'ArcanistBrowseWorkflow' => 'workflow/ArcanistBrowseWorkflow.php',
|
'ArcanistBrowseCommitURIHardpointLoader' => 'browse/loader/ArcanistBrowseCommitURIHardpointLoader.php',
|
||||||
|
'ArcanistBrowseObjectNameURIHardpointLoader' => 'browse/loader/ArcanistBrowseObjectNameURIHardpointLoader.php',
|
||||||
|
'ArcanistBrowsePathURIHardpointLoader' => 'browse/loader/ArcanistBrowsePathURIHardpointLoader.php',
|
||||||
|
'ArcanistBrowseRef' => 'browse/ref/ArcanistBrowseRef.php',
|
||||||
|
'ArcanistBrowseURIHardpointLoader' => 'browse/loader/ArcanistBrowseURIHardpointLoader.php',
|
||||||
|
'ArcanistBrowseURIRef' => 'browse/ref/ArcanistBrowseURIRef.php',
|
||||||
|
'ArcanistBrowseWorkflow' => 'browse/workflow/ArcanistBrowseWorkflow.php',
|
||||||
'ArcanistBundle' => 'parser/ArcanistBundle.php',
|
'ArcanistBundle' => 'parser/ArcanistBundle.php',
|
||||||
'ArcanistBundleTestCase' => 'parser/__tests__/ArcanistBundleTestCase.php',
|
'ArcanistBundleTestCase' => 'parser/__tests__/ArcanistBundleTestCase.php',
|
||||||
'ArcanistCSSLintLinter' => 'lint/linter/ArcanistCSSLintLinter.php',
|
'ArcanistCSSLintLinter' => 'lint/linter/ArcanistCSSLintLinter.php',
|
||||||
|
@ -322,6 +328,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistRepositoryAPI' => 'repository/api/ArcanistRepositoryAPI.php',
|
'ArcanistRepositoryAPI' => 'repository/api/ArcanistRepositoryAPI.php',
|
||||||
'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php',
|
'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php',
|
||||||
'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php',
|
'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php',
|
||||||
|
'ArcanistRepositoryRef' => 'ref/ArcanistRepositoryRef.php',
|
||||||
'ArcanistReusedAsIteratorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistReusedAsIteratorXHPASTLinterRule.php',
|
'ArcanistReusedAsIteratorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistReusedAsIteratorXHPASTLinterRule.php',
|
||||||
'ArcanistReusedAsIteratorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistReusedAsIteratorXHPASTLinterRuleTestCase.php',
|
'ArcanistReusedAsIteratorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistReusedAsIteratorXHPASTLinterRuleTestCase.php',
|
||||||
'ArcanistReusedIteratorReferenceXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistReusedIteratorReferenceXHPASTLinterRule.php',
|
'ArcanistReusedIteratorReferenceXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistReusedIteratorReferenceXHPASTLinterRule.php',
|
||||||
|
@ -472,6 +479,12 @@ phutil_register_library_map(array(
|
||||||
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||||
'ArcanistBranchRef' => 'ArcanistRef',
|
'ArcanistBranchRef' => 'ArcanistRef',
|
||||||
'ArcanistBranchWorkflow' => 'ArcanistFeatureWorkflow',
|
'ArcanistBranchWorkflow' => 'ArcanistFeatureWorkflow',
|
||||||
|
'ArcanistBrowseCommitURIHardpointLoader' => 'ArcanistBrowseURIHardpointLoader',
|
||||||
|
'ArcanistBrowseObjectNameURIHardpointLoader' => 'ArcanistBrowseURIHardpointLoader',
|
||||||
|
'ArcanistBrowsePathURIHardpointLoader' => 'ArcanistBrowseURIHardpointLoader',
|
||||||
|
'ArcanistBrowseRef' => 'ArcanistRef',
|
||||||
|
'ArcanistBrowseURIHardpointLoader' => 'ArcanistHardpointLoader',
|
||||||
|
'ArcanistBrowseURIRef' => 'ArcanistRef',
|
||||||
'ArcanistBrowseWorkflow' => 'ArcanistWorkflow',
|
'ArcanistBrowseWorkflow' => 'ArcanistWorkflow',
|
||||||
'ArcanistBundle' => 'Phobject',
|
'ArcanistBundle' => 'Phobject',
|
||||||
'ArcanistBundleTestCase' => 'PhutilTestCase',
|
'ArcanistBundleTestCase' => 'PhutilTestCase',
|
||||||
|
@ -752,6 +765,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistRepositoryAPI' => 'Phobject',
|
'ArcanistRepositoryAPI' => 'Phobject',
|
||||||
'ArcanistRepositoryAPIMiscTestCase' => 'PhutilTestCase',
|
'ArcanistRepositoryAPIMiscTestCase' => 'PhutilTestCase',
|
||||||
'ArcanistRepositoryAPIStateTestCase' => 'PhutilTestCase',
|
'ArcanistRepositoryAPIStateTestCase' => 'PhutilTestCase',
|
||||||
|
'ArcanistRepositoryRef' => 'ArcanistRef',
|
||||||
'ArcanistReusedAsIteratorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistReusedAsIteratorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistReusedAsIteratorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
'ArcanistReusedAsIteratorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||||
'ArcanistReusedIteratorReferenceXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistReusedIteratorReferenceXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
|
|
107
src/browse/loader/ArcanistBrowseCommitURIHardpointLoader.php
Normal file
107
src/browse/loader/ArcanistBrowseCommitURIHardpointLoader.php
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistBrowseCommitURIHardpointLoader
|
||||||
|
extends ArcanistBrowseURIHardpointLoader {
|
||||||
|
|
||||||
|
const LOADERKEY = 'browse.uri.commit';
|
||||||
|
const BROWSETYPE = 'commit';
|
||||||
|
|
||||||
|
public function willLoadBrowseURIRefs(array $refs) {
|
||||||
|
$refs = $this->getRefsWithSupportedTypes($refs);
|
||||||
|
|
||||||
|
if (!$refs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = $this->getQuery();
|
||||||
|
|
||||||
|
$working_ref = $query->getWorkingCopyRef();
|
||||||
|
if (!$working_ref) {
|
||||||
|
// If we aren't in a working copy, don't warn about this.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$repository_ref = $this->getQuery()->getRepositoryRef();
|
||||||
|
if (!$repository_ref) {
|
||||||
|
echo pht(
|
||||||
|
'NO REPOSITORY: Unable to determine which repository this working '.
|
||||||
|
'copy belongs to, so arguments can not be resolved as commits. Use '.
|
||||||
|
'"%s" to understand how repositories are resolved.',
|
||||||
|
'arc which');
|
||||||
|
echo "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadHardpoints(array $refs, $hardpoint) {
|
||||||
|
$api = $this->getQuery()->getRepositoryAPI();
|
||||||
|
if (!$api) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$repository_ref = $this->getQuery()->getRepositoryRef();
|
||||||
|
if (!$repository_ref) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$repository_phid = $repository_ref->getPHID();
|
||||||
|
|
||||||
|
$refs = $this->getRefsWithSupportedTypes($refs);
|
||||||
|
|
||||||
|
$commit_map = array();
|
||||||
|
foreach ($refs as $key => $ref) {
|
||||||
|
$is_commit = $ref->hasType('commit');
|
||||||
|
|
||||||
|
$token = $ref->getToken();
|
||||||
|
|
||||||
|
if ($token === '.') {
|
||||||
|
// Git resolves "." like HEAD, but we want to treat it as "browse the
|
||||||
|
// current directory" instead in all cases.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($token === null) {
|
||||||
|
if ($is_commit) {
|
||||||
|
$token = $api->getHeadCommit();
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$commit = $api->getCanonicalRevisionName($token);
|
||||||
|
if ($commit) {
|
||||||
|
$commit_map[$commit][] = $key;
|
||||||
|
}
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
// Ignore anything we can't resolve.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$commit_map) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$commit_info = $this->resolveCall(
|
||||||
|
'diffusion.querycommits',
|
||||||
|
array(
|
||||||
|
'repositoryPHID' => $repository_phid,
|
||||||
|
'names' => array_keys($commit_map),
|
||||||
|
));
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
foreach ($commit_info['identifierMap'] as $commit_key => $commit_phid) {
|
||||||
|
foreach ($commit_map[$commit_key] as $key) {
|
||||||
|
$commit_uri = $commit_info['data'][$commit_phid]['uri'];
|
||||||
|
|
||||||
|
$results[$key][] = id(new ArcanistBrowseURIRef())
|
||||||
|
->setURI($commit_uri)
|
||||||
|
->setType('commit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistBrowseObjectNameURIHardpointLoader
|
||||||
|
extends ArcanistBrowseURIHardpointLoader {
|
||||||
|
|
||||||
|
const LOADERKEY = 'browse.uri.name';
|
||||||
|
const BROWSETYPE = 'object';
|
||||||
|
|
||||||
|
public function loadHardpoints(array $refs, $hardpoint) {
|
||||||
|
$refs = $this->getRefsWithSupportedTypes($refs);
|
||||||
|
|
||||||
|
$name_map = array();
|
||||||
|
foreach ($refs as $key => $ref) {
|
||||||
|
$token = $ref->getToken();
|
||||||
|
if (!strlen($token)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$name_map[$key] = $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$name_map) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$objects = $this->resolveCall(
|
||||||
|
'phid.lookup',
|
||||||
|
array(
|
||||||
|
'names' => $name_map,
|
||||||
|
));
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
|
||||||
|
$reverse_map = array_flip($name_map);
|
||||||
|
foreach ($objects as $name => $object) {
|
||||||
|
$key = idx($reverse_map, $name);
|
||||||
|
if ($key === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$uri = idx($object, 'uri');
|
||||||
|
if (!strlen($uri)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[$key][] = id(new ArcanistBrowseURIRef())
|
||||||
|
->setURI($object['uri'])
|
||||||
|
->setType('object');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
132
src/browse/loader/ArcanistBrowsePathURIHardpointLoader.php
Normal file
132
src/browse/loader/ArcanistBrowsePathURIHardpointLoader.php
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistBrowsePathURIHardpointLoader
|
||||||
|
extends ArcanistBrowseURIHardpointLoader {
|
||||||
|
|
||||||
|
const LOADERKEY = 'browse.uri.path';
|
||||||
|
const BROWSETYPE = 'path';
|
||||||
|
|
||||||
|
public function willLoadBrowseURIRefs(array $refs) {
|
||||||
|
$refs = $this->getRefsWithSupportedTypes($refs);
|
||||||
|
if (!$refs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = $this->getQuery();
|
||||||
|
|
||||||
|
$working_ref = $query->getWorkingCopyRef();
|
||||||
|
if (!$working_ref) {
|
||||||
|
echo pht(
|
||||||
|
'NO WORKING COPY: The current directory is not a repository '.
|
||||||
|
'working copy, so arguments can not be resolved as paths. Run '.
|
||||||
|
'this command inside a working copy to resolve paths.');
|
||||||
|
echo "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$repository_ref = $query->getRepositoryRef();
|
||||||
|
if (!$repository_ref) {
|
||||||
|
echo pht(
|
||||||
|
'NO REPOSITORY: Unable to determine which repository this working '.
|
||||||
|
'copy belongs to, so arguments can not be resolved as paths. Use '.
|
||||||
|
'"%s" to understand how repositories are resolved.',
|
||||||
|
'arc which');
|
||||||
|
echo "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function didFailToLoadBrowseURIRefs(array $refs) {
|
||||||
|
$refs = $this->getRefsWithSupportedTypes($refs);
|
||||||
|
if (!$refs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = $this->getQuery();
|
||||||
|
|
||||||
|
$working_ref = $query->getWorkingCopyRef();
|
||||||
|
if (!$working_ref) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$repository_ref = $query->getRepositoryRef();
|
||||||
|
if (!$repository_ref) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo pht(
|
||||||
|
'Use "--types path" to force arguments to be interpreted as paths.');
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function loadHardpoints(array $refs, $hardpoint) {
|
||||||
|
$query = $this->getQuery();
|
||||||
|
|
||||||
|
$working_ref = $query->getWorkingCopyRef();
|
||||||
|
if (!$working_ref) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$repository_ref = $query->getRepositoryRef();
|
||||||
|
if (!$repository_ref) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$refs = $this->getRefsWithSupportedTypes($refs);
|
||||||
|
$project_root = $working_ref->getRootDirectory();
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
foreach ($refs as $key => $ref) {
|
||||||
|
$is_path = $ref->hasType(self::BROWSETYPE);
|
||||||
|
|
||||||
|
$path = $ref->getToken();
|
||||||
|
if ($path === null) {
|
||||||
|
// If we're explicitly resolving no arguments as a path, treat it
|
||||||
|
// as the current working directory.
|
||||||
|
if ($is_path) {
|
||||||
|
$path = '.';
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$lines = null;
|
||||||
|
$parts = explode(':', $path);
|
||||||
|
if (count($parts) > 1) {
|
||||||
|
$lines = array_pop($parts);
|
||||||
|
}
|
||||||
|
$path = implode(':', $parts);
|
||||||
|
|
||||||
|
$full_path = Filesystem::resolvePath($path);
|
||||||
|
|
||||||
|
if (!Filesystem::pathExists($full_path)) {
|
||||||
|
if (!$is_path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($full_path == $project_root) {
|
||||||
|
$path = '';
|
||||||
|
} else {
|
||||||
|
$path = Filesystem::readablePath($full_path, $project_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = array(
|
||||||
|
'path' => $path,
|
||||||
|
'lines' => $lines,
|
||||||
|
'branch' => $ref->getBranch(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$uri = $repository_ref->newBrowseURI($params);
|
||||||
|
|
||||||
|
$results[$key][] = id(new ArcanistBrowseURIRef())
|
||||||
|
->setURI($uri)
|
||||||
|
->setType(self::BROWSETYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
55
src/browse/loader/ArcanistBrowseURIHardpointLoader.php
Normal file
55
src/browse/loader/ArcanistBrowseURIHardpointLoader.php
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class ArcanistBrowseURIHardpointLoader
|
||||||
|
extends ArcanistHardpointLoader {
|
||||||
|
|
||||||
|
public function getSupportedBrowseType() {
|
||||||
|
return $this->getPhobjectClassConstant('BROWSETYPE', 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canLoadRepositoryAPI(ArcanistRepositoryAPI $api) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canLoadRef(ArcanistRef $ref) {
|
||||||
|
return ($ref instanceof ArcanistBrowseRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canLoadHardpoint(ArcanistRef $ref, $hardpoint) {
|
||||||
|
return ($hardpoint == 'uris');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function willLoadBrowseURIRefs(array $refs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function didFailToLoadBrowseURIRefs(array $refs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRefsWithSupportedTypes(array $refs) {
|
||||||
|
$type = $this->getSupportedBrowseType();
|
||||||
|
|
||||||
|
foreach ($refs as $key => $ref) {
|
||||||
|
if ($ref->isUntyped()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ref->hasType($type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($refs[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $refs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAllBrowseLoaders() {
|
||||||
|
return id(new PhutilClassMapQuery())
|
||||||
|
->setAncestorClass(__CLASS__)
|
||||||
|
->setUniqueMethod('getLoaderKey')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
64
src/browse/ref/ArcanistBrowseRef.php
Normal file
64
src/browse/ref/ArcanistBrowseRef.php
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistBrowseRef
|
||||||
|
extends ArcanistRef {
|
||||||
|
|
||||||
|
private $token;
|
||||||
|
private $types;
|
||||||
|
private $branch;
|
||||||
|
|
||||||
|
public function getRefIdentifier() {
|
||||||
|
return pht('Browse Query "%s"', $this->getToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function defineHardpoints() {
|
||||||
|
return array(
|
||||||
|
'uris' => array(
|
||||||
|
'type' => 'ArcanistBrowseURIRef',
|
||||||
|
'vector' => true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setToken($token) {
|
||||||
|
$this->token = $token;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getToken() {
|
||||||
|
return $this->token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTypes(array $types) {
|
||||||
|
$this->types = $types;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTypes() {
|
||||||
|
return $this->types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasType($type) {
|
||||||
|
$map = $this->getTypes();
|
||||||
|
$map = array_fuse($map);
|
||||||
|
return isset($map[$type]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isUntyped() {
|
||||||
|
return !$this->types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBranch($branch) {
|
||||||
|
$this->branch = $branch;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBranch() {
|
||||||
|
return $this->branch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getURIs() {
|
||||||
|
return $this->getHardpoint('uris');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
35
src/browse/ref/ArcanistBrowseURIRef.php
Normal file
35
src/browse/ref/ArcanistBrowseURIRef.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistBrowseURIRef
|
||||||
|
extends ArcanistRef {
|
||||||
|
|
||||||
|
private $uri;
|
||||||
|
private $type;
|
||||||
|
|
||||||
|
public function getRefIdentifier() {
|
||||||
|
return pht('Browse URI "%s"', $this->getURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function defineHardpoints() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setURI($uri) {
|
||||||
|
$this->uri = $uri;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getURI() {
|
||||||
|
return $this->uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setType($type) {
|
||||||
|
$this->type = $type;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType() {
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
224
src/browse/workflow/ArcanistBrowseWorkflow.php
Normal file
224
src/browse/workflow/ArcanistBrowseWorkflow.php
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Browse files or objects in the Phabricator web interface.
|
||||||
|
*/
|
||||||
|
final class ArcanistBrowseWorkflow extends ArcanistWorkflow {
|
||||||
|
|
||||||
|
public function getWorkflowName() {
|
||||||
|
return 'browse';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommandSynopses() {
|
||||||
|
return phutil_console_format(<<<EOTEXT
|
||||||
|
**browse** [__options__] __path__ ...
|
||||||
|
**browse** [__options__] __object__ ...
|
||||||
|
EOTEXT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommandHelp() {
|
||||||
|
return phutil_console_format(<<<EOTEXT
|
||||||
|
Supports: git, hg, svn
|
||||||
|
Open a file or object (like a task or revision) in your web browser.
|
||||||
|
|
||||||
|
$ arc browse README # Open a file in Diffusion.
|
||||||
|
$ arc browse T123 # View a task.
|
||||||
|
$ arc browse HEAD # View a symbolic commit.
|
||||||
|
|
||||||
|
Set the 'browser' value using 'arc set-config' to select a browser. If
|
||||||
|
no browser is set, the command will try to guess which browser to use.
|
||||||
|
EOTEXT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArguments() {
|
||||||
|
return array(
|
||||||
|
'branch' => array(
|
||||||
|
'param' => 'branch_name',
|
||||||
|
'help' => pht(
|
||||||
|
'Default branch name to view on server. Defaults to "%s".',
|
||||||
|
'master'),
|
||||||
|
),
|
||||||
|
'types' => array(
|
||||||
|
'param' => 'types',
|
||||||
|
'aliases' => array('type'),
|
||||||
|
'help' => pht(
|
||||||
|
'Parse arguments with particular types.'),
|
||||||
|
),
|
||||||
|
'force' => array(
|
||||||
|
'help' => pht(
|
||||||
|
'(DEPRECATED) Obsolete, use "--types path" instead.'),
|
||||||
|
),
|
||||||
|
'*' => 'targets',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function desiresWorkingCopy() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function desiresRepositoryAPI() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run() {
|
||||||
|
$conduit = $this->getConduitEngine();
|
||||||
|
|
||||||
|
$console = PhutilConsole::getConsole();
|
||||||
|
|
||||||
|
$targets = $this->getArgument('targets');
|
||||||
|
if (!$targets) {
|
||||||
|
throw new ArcanistUsageException(
|
||||||
|
pht(
|
||||||
|
'Specify one or more paths or objects to browse. Use the '.
|
||||||
|
'command "%s" if you want to browse this directory.',
|
||||||
|
'arc browse .'));
|
||||||
|
}
|
||||||
|
$targets = array_fuse($targets);
|
||||||
|
|
||||||
|
if (!$targets) {
|
||||||
|
$refs = array(
|
||||||
|
new ArcanistBrowseRef(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$refs = array();
|
||||||
|
foreach ($targets as $target) {
|
||||||
|
$refs[] = id(new ArcanistBrowseRef())
|
||||||
|
->setToken($target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_force = $this->getArgument('force');
|
||||||
|
if ($is_force) {
|
||||||
|
// TODO: Remove this completely.
|
||||||
|
$this->writeWarn(
|
||||||
|
pht('DEPRECATED'),
|
||||||
|
pht(
|
||||||
|
'Argument "--force" for "arc browse" is deprecated. Use '.
|
||||||
|
'"--type %s" instead.',
|
||||||
|
ArcanistBrowsePathURIHardpointLoader::BROWSETYPE));
|
||||||
|
}
|
||||||
|
|
||||||
|
$types = $this->getArgument('types');
|
||||||
|
if ($types !== null) {
|
||||||
|
$types = preg_split('/[\s,]+/', $types);
|
||||||
|
} else {
|
||||||
|
if ($is_force) {
|
||||||
|
$types = array(ArcanistBrowsePathURIHardpointLoader::BROWSETYPE);
|
||||||
|
} else {
|
||||||
|
$types = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($refs as $ref) {
|
||||||
|
$ref->setTypes($types);
|
||||||
|
}
|
||||||
|
|
||||||
|
$branch = $this->getArgument('branch');
|
||||||
|
if ($branch) {
|
||||||
|
foreach ($refs as $ref) {
|
||||||
|
$ref->setBranch($branch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$loaders = ArcanistBrowseURIHardpointLoader::getAllBrowseLoaders();
|
||||||
|
foreach ($loaders as $key => $loader) {
|
||||||
|
$loaders[$key] = clone $loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = $this->newRefQuery($refs)
|
||||||
|
->needHardpoints(
|
||||||
|
array(
|
||||||
|
'uris',
|
||||||
|
))
|
||||||
|
->setLoaders($loaders);
|
||||||
|
|
||||||
|
foreach ($loaders as $loader) {
|
||||||
|
$loader->willLoadBrowseURIRefs($refs);
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->execute();
|
||||||
|
|
||||||
|
$zero_hits = array();
|
||||||
|
$open_uris = array();
|
||||||
|
$many_hits = array();
|
||||||
|
foreach ($refs as $ref) {
|
||||||
|
$uris = $ref->getURIs();
|
||||||
|
if (!$uris) {
|
||||||
|
$zero_hits[] = $ref;
|
||||||
|
} else if (count($uris) == 1) {
|
||||||
|
$open_uris[] = $ref;
|
||||||
|
} else {
|
||||||
|
$many_hits[] = $ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($many_hits) {
|
||||||
|
foreach ($many_hits as $ref) {
|
||||||
|
$token = $ref->getToken();
|
||||||
|
if (strlen($token)) {
|
||||||
|
$message = pht('Argument "%s" is ambiguous.', $token);
|
||||||
|
} else {
|
||||||
|
$message = pht('Default behavior is ambiguous.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->writeWarn(pht('AMBIGUOUS'), $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = id(new PhutilConsoleTable())
|
||||||
|
->addColumn('argument', array('title' => pht('Argument')))
|
||||||
|
->addColumn('type', array('title' => pht('Type')))
|
||||||
|
->addColumn('uri', array('title' => pht('URI')));
|
||||||
|
|
||||||
|
foreach ($many_hits as $ref) {
|
||||||
|
$token_display = $ref->getToken();
|
||||||
|
if (!strlen($token)) {
|
||||||
|
$token_display = pht('<default>');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($ref->getURIs() as $uri) {
|
||||||
|
$row = array(
|
||||||
|
'argument' => $token_display,
|
||||||
|
'type' => $uri->getType(),
|
||||||
|
'uri' => $uri->getURI(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$table->addRow($row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$table->draw();
|
||||||
|
|
||||||
|
$this->writeInfo(
|
||||||
|
pht('CHOOSE'),
|
||||||
|
pht('Use "--types" to select between alternatives.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If anything failed to resolve, this is also an error.
|
||||||
|
if ($zero_hits) {
|
||||||
|
foreach ($zero_hits as $ref) {
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'Unable to resolve argument "%s".',
|
||||||
|
$ref->getToken()));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($loaders as $loader) {
|
||||||
|
$loader->didFailToLoadBrowseURIRefs($refs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$uris = array();
|
||||||
|
foreach ($open_uris as $ref) {
|
||||||
|
$ref_uri = head($ref->getURIs());
|
||||||
|
$uris[] = $ref_uri->getURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->openURIsInBrowser($uris);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,9 +4,12 @@ final class ArcanistRefQuery extends Phobject {
|
||||||
|
|
||||||
private $repositoryAPI;
|
private $repositoryAPI;
|
||||||
private $conduitEngine;
|
private $conduitEngine;
|
||||||
|
private $repositoryRef;
|
||||||
|
private $workingCopyRef;
|
||||||
|
|
||||||
private $refs;
|
private $refs;
|
||||||
private $hardpoints;
|
private $hardpoints;
|
||||||
|
private $loaders;
|
||||||
|
|
||||||
public function setRefs(array $refs) {
|
public function setRefs(array $refs) {
|
||||||
assert_instances_of($refs, 'ArcanistRef');
|
assert_instances_of($refs, 'ArcanistRef');
|
||||||
|
@ -27,6 +30,14 @@ final class ArcanistRefQuery extends Phobject {
|
||||||
return $this->repositoryAPI;
|
return $this->repositoryAPI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setRepositoryRef(ArcanistRepositoryRef $repository_ref) {
|
||||||
|
$this->repositoryRef = $repository_ref;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function getRepositoryRef() {
|
||||||
|
return $this->repositoryRef;
|
||||||
|
}
|
||||||
|
|
||||||
public function setConduitEngine(ArcanistConduitEngine $conduit_engine) {
|
public function setConduitEngine(ArcanistConduitEngine $conduit_engine) {
|
||||||
$this->conduitEngine = $conduit_engine;
|
$this->conduitEngine = $conduit_engine;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -36,11 +47,31 @@ final class ArcanistRefQuery extends Phobject {
|
||||||
return $this->conduitEngine;
|
return $this->conduitEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setWorkingCopyRef(ArcanistWorkingCopyStateRef $working_ref) {
|
||||||
|
$this->workingCopyRef = $working_ref;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWorkingCopyRef() {
|
||||||
|
return $this->workingCopyRef;
|
||||||
|
}
|
||||||
|
|
||||||
public function needHardpoints(array $hardpoints) {
|
public function needHardpoints(array $hardpoints) {
|
||||||
$this->hardpoints = $hardpoints;
|
$this->hardpoints = $hardpoints;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setLoaders(array $loaders) {
|
||||||
|
assert_instances_of($loaders, 'ArcanistHardpointLoader');
|
||||||
|
|
||||||
|
foreach ($loaders as $key => $loader) {
|
||||||
|
$loader->setQuery($this);
|
||||||
|
}
|
||||||
|
$this->loaders = $loaders;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function execute() {
|
public function execute() {
|
||||||
$refs = $this->getRefs();
|
$refs = $this->getRefs();
|
||||||
|
|
||||||
|
@ -52,13 +83,23 @@ final class ArcanistRefQuery extends Phobject {
|
||||||
throw new PhutilInvalidStateException('needHardpoints');
|
throw new PhutilInvalidStateException('needHardpoints');
|
||||||
}
|
}
|
||||||
|
|
||||||
$api = $this->getRepositoryAPI();
|
if ($this->loaders == null) {
|
||||||
$all_loaders = ArcanistHardpointLoader::getAllLoaders();
|
$all_loaders = ArcanistHardpointLoader::getAllLoaders();
|
||||||
|
foreach ($all_loaders as $key => $loader) {
|
||||||
|
$all_loaders[$key] = clone $loader;
|
||||||
|
}
|
||||||
|
$this->setLoaders($all_loaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
$all_loaders = $this->loaders;
|
||||||
|
|
||||||
|
$api = $this->getRepositoryAPI();
|
||||||
$loaders = array();
|
$loaders = array();
|
||||||
foreach ($all_loaders as $loader_key => $loader) {
|
foreach ($all_loaders as $loader_key => $loader) {
|
||||||
if (!$loader->canLoadRepositoryAPI($api)) {
|
if ($api) {
|
||||||
continue;
|
if (!$loader->canLoadRepositoryAPI($api)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$loaders[$loader_key] = id(clone $loader)
|
$loaders[$loader_key] = id(clone $loader)
|
||||||
|
|
79
src/ref/ArcanistRepositoryRef.php
Normal file
79
src/ref/ArcanistRepositoryRef.php
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistRepositoryRef
|
||||||
|
extends ArcanistRef {
|
||||||
|
|
||||||
|
private $phid;
|
||||||
|
private $browseURI;
|
||||||
|
|
||||||
|
public function getRefIdentifier() {
|
||||||
|
return pht('Remote Repository');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function defineHardpoints() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPHID($phid) {
|
||||||
|
$this->phid = $phid;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPHID() {
|
||||||
|
return $this->phid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBrowseURI($browse_uri) {
|
||||||
|
$this->browseURI = $browse_uri;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newBrowseURI(array $params) {
|
||||||
|
PhutilTypeSpec::checkMap(
|
||||||
|
$params,
|
||||||
|
array(
|
||||||
|
'path' => 'optional string|null',
|
||||||
|
'branch' => 'optional string|null',
|
||||||
|
'lines' => 'optional string|null',
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($params as $key => $value) {
|
||||||
|
if (!strlen($value)) {
|
||||||
|
unset($params[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$defaults = array(
|
||||||
|
'path' => '/',
|
||||||
|
'branch' => $this->getDefaultBranch(),
|
||||||
|
'lines' => null,
|
||||||
|
);
|
||||||
|
|
||||||
|
$params = $params + $defaults;
|
||||||
|
|
||||||
|
$uri_base = $this->browseURI;
|
||||||
|
$uri_base = rtrim($uri_base, '/');
|
||||||
|
|
||||||
|
$uri_branch = phutil_escape_uri_path_component($params['branch']);
|
||||||
|
|
||||||
|
$uri_path = ltrim($params['path'], '/');
|
||||||
|
$uri_path = phutil_escape_uri($uri_path);
|
||||||
|
|
||||||
|
$uri_lines = null;
|
||||||
|
if ($params['lines']) {
|
||||||
|
$uri_lines = '$'.phutil_escape_uri($params['lines']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This construction, which includes a branch, is probably wrong for
|
||||||
|
// Subversion.
|
||||||
|
|
||||||
|
return "{$uri_base}/browse/{$uri_branch}/{$uri_path}{$uri_lines}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDefaultBranch() {
|
||||||
|
// TODO: This should read from the remote, and is not correct for
|
||||||
|
// Mercurial anyway, as "default" would be a better default branch.
|
||||||
|
return 'master';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,6 +3,8 @@
|
||||||
final class ArcanistWorkingCopyStateRef
|
final class ArcanistWorkingCopyStateRef
|
||||||
extends ArcanistRef {
|
extends ArcanistRef {
|
||||||
|
|
||||||
|
private $rootDirectory;
|
||||||
|
|
||||||
public function getRefIdentifier() {
|
public function getRefIdentifier() {
|
||||||
// TODO: This could check attached hardpoints and render something more
|
// TODO: This could check attached hardpoints and render something more
|
||||||
// insightful.
|
// insightful.
|
||||||
|
@ -24,6 +26,15 @@ final class ArcanistWorkingCopyStateRef
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setRootDirectory($root_directory) {
|
||||||
|
$this->rootDirectory = $root_directory;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRootDirectory() {
|
||||||
|
return $this->rootDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
public function attachBranchRef(ArcanistBranchRef $branch_ref) {
|
public function attachBranchRef(ArcanistBranchRef $branch_ref) {
|
||||||
return $this->attachHardpoint('branchRef', $branch_ref);
|
return $this->attachHardpoint('branchRef', $branch_ref);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,232 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Browse files or objects in the Phabricator web interface.
|
|
||||||
*/
|
|
||||||
final class ArcanistBrowseWorkflow extends ArcanistWorkflow {
|
|
||||||
|
|
||||||
public function getWorkflowName() {
|
|
||||||
return 'browse';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCommandSynopses() {
|
|
||||||
return phutil_console_format(<<<EOTEXT
|
|
||||||
**browse** [__options__] __path__ ...
|
|
||||||
**browse** [__options__] __object__ ...
|
|
||||||
EOTEXT
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCommandHelp() {
|
|
||||||
return phutil_console_format(<<<EOTEXT
|
|
||||||
Supports: git, hg, svn
|
|
||||||
Open a file or object (like a task or revision) in your web browser.
|
|
||||||
|
|
||||||
$ arc browse README # Open a file in Diffusion.
|
|
||||||
$ arc browse T123 # View a task.
|
|
||||||
$ arc browse HEAD # View a symbolic commit.
|
|
||||||
|
|
||||||
Set the 'browser' value using 'arc set-config' to select a browser. If
|
|
||||||
no browser is set, the command will try to guess which browser to use.
|
|
||||||
EOTEXT
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getArguments() {
|
|
||||||
return array(
|
|
||||||
'branch' => array(
|
|
||||||
'param' => 'branch_name',
|
|
||||||
'help' => pht(
|
|
||||||
'Default branch name to view on server. Defaults to "%s".',
|
|
||||||
'master'),
|
|
||||||
),
|
|
||||||
'force' => array(
|
|
||||||
'help' => pht(
|
|
||||||
'Open arguments as paths, even if they do not exist in the '.
|
|
||||||
'working copy.'),
|
|
||||||
),
|
|
||||||
'*' => 'paths',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function desiresWorkingCopy() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function desiresRepositoryAPI() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function run() {
|
|
||||||
$conduit = $this->getConduitEngine();
|
|
||||||
|
|
||||||
$console = PhutilConsole::getConsole();
|
|
||||||
|
|
||||||
$is_force = $this->getArgument('force');
|
|
||||||
|
|
||||||
$things = $this->getArgument('paths');
|
|
||||||
if (!$things) {
|
|
||||||
throw new ArcanistUsageException(
|
|
||||||
pht(
|
|
||||||
'Specify one or more paths or objects to browse. Use the command '.
|
|
||||||
'"%s" if you want to browse this directory.',
|
|
||||||
'arc browse .'));
|
|
||||||
}
|
|
||||||
$things = array_fuse($things);
|
|
||||||
|
|
||||||
$method = 'phid.lookup';
|
|
||||||
$params = array(
|
|
||||||
'names' => array_keys($things),
|
|
||||||
);
|
|
||||||
|
|
||||||
$objects = $conduit->newCall($method, $params)
|
|
||||||
->resolve();
|
|
||||||
|
|
||||||
$uris = array();
|
|
||||||
foreach ($objects as $name => $object) {
|
|
||||||
$uris[] = $object['uri'];
|
|
||||||
|
|
||||||
$console->writeOut(
|
|
||||||
pht(
|
|
||||||
'Opening **%s** as an object.',
|
|
||||||
$name)."\n");
|
|
||||||
|
|
||||||
unset($things[$name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->hasRepositoryAPI()) {
|
|
||||||
$repository_api = $this->getRepositoryAPI();
|
|
||||||
$project_root = $this->getWorkingCopy()->getProjectRoot();
|
|
||||||
|
|
||||||
// First, try to resolve arguments as symbolic commits.
|
|
||||||
|
|
||||||
$commits = array();
|
|
||||||
foreach ($things as $key => $thing) {
|
|
||||||
if ($thing == '.') {
|
|
||||||
// Git resolves '.' like HEAD, but it should be interpreted to mean
|
|
||||||
// "the current directory". Just skip resolution and fall through.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$commit = $repository_api->getCanonicalRevisionName($thing);
|
|
||||||
if ($commit) {
|
|
||||||
$commits[$commit] = $key;
|
|
||||||
}
|
|
||||||
} catch (Exception $ex) {
|
|
||||||
// Ignore.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($commits) {
|
|
||||||
$method = 'diffusion.querycommits';
|
|
||||||
|
|
||||||
$params = array(
|
|
||||||
'repositoryPHID' => $this->getRepositoryPHID(),
|
|
||||||
'names' => array_keys($commits),
|
|
||||||
);
|
|
||||||
|
|
||||||
$commit_info = $conduit->newCall($method, $params)
|
|
||||||
->resolve();
|
|
||||||
|
|
||||||
foreach ($commit_info['identifierMap'] as $ckey => $cphid) {
|
|
||||||
$thing = $commits[$ckey];
|
|
||||||
unset($things[$thing]);
|
|
||||||
|
|
||||||
$uris[] = $commit_info['data'][$cphid]['uri'];
|
|
||||||
|
|
||||||
$console->writeOut(
|
|
||||||
pht(
|
|
||||||
'Opening **%s** as a commit.',
|
|
||||||
$thing)."\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we fail, try to resolve them as paths.
|
|
||||||
|
|
||||||
foreach ($things as $key => $path) {
|
|
||||||
$lines = null;
|
|
||||||
$parts = explode(':', $path);
|
|
||||||
if (count($parts) > 1) {
|
|
||||||
$lines = array_pop($parts);
|
|
||||||
}
|
|
||||||
$path = implode(':', $parts);
|
|
||||||
|
|
||||||
$full_path = Filesystem::resolvePath($path);
|
|
||||||
|
|
||||||
if (!$is_force && !Filesystem::pathExists($full_path)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$console->writeOut(
|
|
||||||
pht(
|
|
||||||
'Opening **%s** as a repository path.',
|
|
||||||
$key)."\n");
|
|
||||||
|
|
||||||
unset($things[$key]);
|
|
||||||
|
|
||||||
if ($full_path == $project_root) {
|
|
||||||
$path = '';
|
|
||||||
} else {
|
|
||||||
$path = Filesystem::readablePath($full_path, $project_root);
|
|
||||||
}
|
|
||||||
|
|
||||||
$base_uri = $this->getBaseURI();
|
|
||||||
$uri = $base_uri.$path;
|
|
||||||
|
|
||||||
if ($lines) {
|
|
||||||
$uri = $uri.'$'.$lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
$uris[] = $uri;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($things) {
|
|
||||||
$console->writeOut(
|
|
||||||
"%s\n",
|
|
||||||
pht(
|
|
||||||
"The current working directory is not a repository working ".
|
|
||||||
"copy, so remaining arguments can not be resolved as paths or ".
|
|
||||||
"commits. To browse paths or symbolic commits in Diffusion, run ".
|
|
||||||
"'%s' from inside a working copy.",
|
|
||||||
'arc browse'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($things as $thing) {
|
|
||||||
$console->writeOut(
|
|
||||||
"%s\n",
|
|
||||||
pht(
|
|
||||||
'Unable to find an object named **%s**, no such commit exists in '.
|
|
||||||
'the remote, and no such path exists in the working copy. Use '.
|
|
||||||
'__%s__ to treat this as a path anyway.',
|
|
||||||
$thing,
|
|
||||||
'--force'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($uris) {
|
|
||||||
$this->openURIsInBrowser($uris);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getBaseURI() {
|
|
||||||
$repo_uri = $this->getRepositoryURI();
|
|
||||||
if ($repo_uri === null) {
|
|
||||||
throw new ArcanistUsageException(
|
|
||||||
pht(
|
|
||||||
'arc is unable to determine which repository in Diffusion '.
|
|
||||||
'this working copy belongs to. Use "%s" to understand how '.
|
|
||||||
'%s looks for a repository.',
|
|
||||||
'arc which',
|
|
||||||
'arc'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$branch = $this->getArgument('branch', 'master');
|
|
||||||
$branch = phutil_escape_uri_path_component($branch);
|
|
||||||
|
|
||||||
return $repo_uri.'browse/'.$branch.'/';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -62,6 +62,7 @@ abstract class ArcanistWorkflow extends Phobject {
|
||||||
private $projectInfo;
|
private $projectInfo;
|
||||||
private $repositoryInfo;
|
private $repositoryInfo;
|
||||||
private $repositoryReasons;
|
private $repositoryReasons;
|
||||||
|
private $repositoryRef;
|
||||||
|
|
||||||
private $arcanistConfiguration;
|
private $arcanistConfiguration;
|
||||||
private $parentWorkflow;
|
private $parentWorkflow;
|
||||||
|
@ -583,6 +584,7 @@ abstract class ArcanistWorkflow extends Phobject {
|
||||||
$arc_config = $this->getArcanistConfiguration();
|
$arc_config = $this->getArcanistConfiguration();
|
||||||
$workflow = $arc_config->buildWorkflow($command);
|
$workflow = $arc_config->buildWorkflow($command);
|
||||||
$workflow->setParentWorkflow($this);
|
$workflow->setParentWorkflow($this);
|
||||||
|
$workflow->setConduitEngine($this->getConduitEngine());
|
||||||
$workflow->setCommand($command);
|
$workflow->setCommand($command);
|
||||||
$workflow->setConfigurationManager($this->getConfigurationManager());
|
$workflow->setConfigurationManager($this->getConfigurationManager());
|
||||||
|
|
||||||
|
@ -2074,15 +2076,57 @@ abstract class ArcanistWorkflow extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function newWorkingCopyStateRef() {
|
final protected function newWorkingCopyStateRef() {
|
||||||
return new ArcanistWorkingCopyStateRef();
|
$ref = new ArcanistWorkingCopyStateRef();
|
||||||
|
|
||||||
|
$working_copy = $this->getWorkingCopy();
|
||||||
|
$ref->setRootDirectory($working_copy->getProjectRoot());
|
||||||
|
|
||||||
|
return $ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function newRefQuery(array $refs) {
|
final protected function newRefQuery(array $refs) {
|
||||||
assert_instances_of($refs, 'ArcanistRef');
|
assert_instances_of($refs, 'ArcanistRef');
|
||||||
return id(new ArcanistRefQuery())
|
|
||||||
->setRepositoryAPI($this->getRepositoryAPI())
|
$query = id(new ArcanistRefQuery())
|
||||||
->setConduitEngine($this->getConduitEngine())
|
->setConduitEngine($this->getConduitEngine())
|
||||||
->setRefs($refs);
|
->setRefs($refs);
|
||||||
|
|
||||||
|
if ($this->hasRepositoryAPI()) {
|
||||||
|
$query->setRepositoryAPI($this->getRepositoryAPI());
|
||||||
|
}
|
||||||
|
|
||||||
|
$repository_ref = $this->getRepositoryRef();
|
||||||
|
if ($repository_ref) {
|
||||||
|
$query->setRepositoryRef($repository_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
$working_copy = $this->getConfigurationManager()->getWorkingCopyIdentity();
|
||||||
|
if ($working_copy) {
|
||||||
|
$working_ref = $this->newWorkingCopyStateRef();
|
||||||
|
$query->setWorkingCopyRef($working_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getRepositoryRef() {
|
||||||
|
if (!$this->getConfigurationManager()->getWorkingCopyIdentity()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->repositoryAPI) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->repositoryRef) {
|
||||||
|
$ref = id(new ArcanistRepositoryRef())
|
||||||
|
->setPHID($this->getRepositoryPHID())
|
||||||
|
->setBrowseURI($this->getRepositoryURI());
|
||||||
|
|
||||||
|
$this->repositoryRef = $ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->repositoryRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue