Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14074349
D16925.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
34 KB
Referenced Files
None
Subscribers
None
D16925.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -42,7 +42,13 @@
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistBraceFormattingXHPASTLinterRuleTestCase.php',
'ArcanistBranchRef' => 'ref/ArcanistBranchRef.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',
'ArcanistBundleTestCase' => 'parser/__tests__/ArcanistBundleTestCase.php',
'ArcanistCSSLintLinter' => 'lint/linter/ArcanistCSSLintLinter.php',
@@ -322,6 +328,7 @@
'ArcanistRepositoryAPI' => 'repository/api/ArcanistRepositoryAPI.php',
'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php',
'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php',
+ 'ArcanistRepositoryRef' => 'ref/ArcanistRepositoryRef.php',
'ArcanistReusedAsIteratorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistReusedAsIteratorXHPASTLinterRule.php',
'ArcanistReusedAsIteratorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistReusedAsIteratorXHPASTLinterRuleTestCase.php',
'ArcanistReusedIteratorReferenceXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistReusedIteratorReferenceXHPASTLinterRule.php',
@@ -472,6 +479,12 @@
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistBranchRef' => 'ArcanistRef',
'ArcanistBranchWorkflow' => 'ArcanistFeatureWorkflow',
+ 'ArcanistBrowseCommitURIHardpointLoader' => 'ArcanistBrowseURIHardpointLoader',
+ 'ArcanistBrowseObjectNameURIHardpointLoader' => 'ArcanistBrowseURIHardpointLoader',
+ 'ArcanistBrowsePathURIHardpointLoader' => 'ArcanistBrowseURIHardpointLoader',
+ 'ArcanistBrowseRef' => 'ArcanistRef',
+ 'ArcanistBrowseURIHardpointLoader' => 'ArcanistHardpointLoader',
+ 'ArcanistBrowseURIRef' => 'ArcanistRef',
'ArcanistBrowseWorkflow' => 'ArcanistWorkflow',
'ArcanistBundle' => 'Phobject',
'ArcanistBundleTestCase' => 'PhutilTestCase',
@@ -752,6 +765,7 @@
'ArcanistRepositoryAPI' => 'Phobject',
'ArcanistRepositoryAPIMiscTestCase' => 'PhutilTestCase',
'ArcanistRepositoryAPIStateTestCase' => 'PhutilTestCase',
+ 'ArcanistRepositoryRef' => 'ArcanistRef',
'ArcanistReusedAsIteratorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistReusedAsIteratorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistReusedIteratorReferenceXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
diff --git a/src/browse/loader/ArcanistBrowseCommitURIHardpointLoader.php b/src/browse/loader/ArcanistBrowseCommitURIHardpointLoader.php
new file mode 100644
--- /dev/null
+++ b/src/browse/loader/ArcanistBrowseCommitURIHardpointLoader.php
@@ -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;
+ }
+
+
+}
diff --git a/src/browse/loader/ArcanistBrowseObjectNameURIHardpointLoader.php b/src/browse/loader/ArcanistBrowseObjectNameURIHardpointLoader.php
new file mode 100644
--- /dev/null
+++ b/src/browse/loader/ArcanistBrowseObjectNameURIHardpointLoader.php
@@ -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;
+ }
+
+}
diff --git a/src/browse/loader/ArcanistBrowsePathURIHardpointLoader.php b/src/browse/loader/ArcanistBrowsePathURIHardpointLoader.php
new file mode 100644
--- /dev/null
+++ b/src/browse/loader/ArcanistBrowsePathURIHardpointLoader.php
@@ -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;
+ }
+
+
+}
diff --git a/src/browse/loader/ArcanistBrowseURIHardpointLoader.php b/src/browse/loader/ArcanistBrowseURIHardpointLoader.php
new file mode 100644
--- /dev/null
+++ b/src/browse/loader/ArcanistBrowseURIHardpointLoader.php
@@ -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();
+ }
+
+}
diff --git a/src/browse/ref/ArcanistBrowseRef.php b/src/browse/ref/ArcanistBrowseRef.php
new file mode 100644
--- /dev/null
+++ b/src/browse/ref/ArcanistBrowseRef.php
@@ -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');
+ }
+
+}
diff --git a/src/browse/ref/ArcanistBrowseURIRef.php b/src/browse/ref/ArcanistBrowseURIRef.php
new file mode 100644
--- /dev/null
+++ b/src/browse/ref/ArcanistBrowseURIRef.php
@@ -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;
+ }
+
+}
diff --git a/src/browse/workflow/ArcanistBrowseWorkflow.php b/src/browse/workflow/ArcanistBrowseWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/browse/workflow/ArcanistBrowseWorkflow.php
@@ -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;
+ }
+
+}
diff --git a/src/ref/ArcanistRefQuery.php b/src/ref/ArcanistRefQuery.php
--- a/src/ref/ArcanistRefQuery.php
+++ b/src/ref/ArcanistRefQuery.php
@@ -4,9 +4,12 @@
private $repositoryAPI;
private $conduitEngine;
+ private $repositoryRef;
+ private $workingCopyRef;
private $refs;
private $hardpoints;
+ private $loaders;
public function setRefs(array $refs) {
assert_instances_of($refs, 'ArcanistRef');
@@ -27,6 +30,14 @@
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) {
$this->conduitEngine = $conduit_engine;
return $this;
@@ -36,11 +47,31 @@
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) {
$this->hardpoints = $hardpoints;
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() {
$refs = $this->getRefs();
@@ -52,13 +83,23 @@
throw new PhutilInvalidStateException('needHardpoints');
}
- $api = $this->getRepositoryAPI();
- $all_loaders = ArcanistHardpointLoader::getAllLoaders();
+ if ($this->loaders == null) {
+ $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();
foreach ($all_loaders as $loader_key => $loader) {
- if (!$loader->canLoadRepositoryAPI($api)) {
- continue;
+ if ($api) {
+ if (!$loader->canLoadRepositoryAPI($api)) {
+ continue;
+ }
}
$loaders[$loader_key] = id(clone $loader)
diff --git a/src/ref/ArcanistRepositoryRef.php b/src/ref/ArcanistRepositoryRef.php
new file mode 100644
--- /dev/null
+++ b/src/ref/ArcanistRepositoryRef.php
@@ -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';
+ }
+
+}
diff --git a/src/ref/ArcanistWorkingCopyStateRef.php b/src/ref/ArcanistWorkingCopyStateRef.php
--- a/src/ref/ArcanistWorkingCopyStateRef.php
+++ b/src/ref/ArcanistWorkingCopyStateRef.php
@@ -3,6 +3,8 @@
final class ArcanistWorkingCopyStateRef
extends ArcanistRef {
+ private $rootDirectory;
+
public function getRefIdentifier() {
// TODO: This could check attached hardpoints and render something more
// insightful.
@@ -24,6 +26,15 @@
);
}
+ public function setRootDirectory($root_directory) {
+ $this->rootDirectory = $root_directory;
+ return $this;
+ }
+
+ public function getRootDirectory() {
+ return $this->rootDirectory;
+ }
+
public function attachBranchRef(ArcanistBranchRef $branch_ref) {
return $this->attachHardpoint('branchRef', $branch_ref);
}
diff --git a/src/workflow/ArcanistBrowseWorkflow.php b/src/workflow/ArcanistBrowseWorkflow.php
deleted file mode 100644
--- a/src/workflow/ArcanistBrowseWorkflow.php
+++ /dev/null
@@ -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.'/';
- }
-
-}
diff --git a/src/workflow/ArcanistWorkflow.php b/src/workflow/ArcanistWorkflow.php
--- a/src/workflow/ArcanistWorkflow.php
+++ b/src/workflow/ArcanistWorkflow.php
@@ -62,6 +62,7 @@
private $projectInfo;
private $repositoryInfo;
private $repositoryReasons;
+ private $repositoryRef;
private $arcanistConfiguration;
private $parentWorkflow;
@@ -583,6 +584,7 @@
$arc_config = $this->getArcanistConfiguration();
$workflow = $arc_config->buildWorkflow($command);
$workflow->setParentWorkflow($this);
+ $workflow->setConduitEngine($this->getConduitEngine());
$workflow->setCommand($command);
$workflow->setConfigurationManager($this->getConfigurationManager());
@@ -2074,15 +2076,57 @@
}
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) {
assert_instances_of($refs, 'ArcanistRef');
- return id(new ArcanistRefQuery())
- ->setRepositoryAPI($this->getRepositoryAPI())
+
+ $query = id(new ArcanistRefQuery())
->setConduitEngine($this->getConduitEngine())
->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;
}
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Nov 22, 5:56 AM (5 h, 58 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6773741
Default Alt Text
D16925.diff (34 KB)
Attached To
Mode
D16925: Rebuild "arc browse" using refs and hardpoints
Attached
Detach File
Event Timeline
Log In to Comment