Page MenuHomePhabricator

D8073.id.diff
No OneTemporary

D8073.id.diff

diff --git a/.arcconfig b/.arcconfig
--- a/.arcconfig
+++ b/.arcconfig
@@ -1,6 +1,5 @@
{
- "project_id" : "arcanist",
- "conduit_uri" : "https://secure.phabricator.com/",
+ "phabricator.uri" : "https://secure.phabricator.com/",
"lint.engine" : "PhutilLintEngine",
"unit.engine" : "PhutilUnitTestEngine",
"load" : [
diff --git a/scripts/arcanist.php b/scripts/arcanist.php
--- a/scripts/arcanist.php
+++ b/scripts/arcanist.php
@@ -204,8 +204,8 @@
if ($force_conduit) {
$conduit_uri = $force_conduit;
} else {
- $project_conduit_uri =
- $configuration_manager->getProjectConfig('conduit_uri');
+ $project_conduit_uri = $configuration_manager->getProjectConfig(
+ 'phabricator.uri');
if ($project_conduit_uri) {
$conduit_uri = $project_conduit_uri;
} else {
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
@@ -205,7 +205,7 @@
'ArcanistBundleTestCase' => 'ArcanistTestCase',
'ArcanistCSSLintLinter' => 'ArcanistExternalLinter',
'ArcanistCSSLintLinterTestCase' => 'ArcanistArcanistLinterTestCase',
- 'ArcanistCSharpLinter' => 'ArcanistFutureLinter',
+ 'ArcanistCSharpLinter' => 'ArcanistLinter',
'ArcanistCallConduitWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistCapabilityNotSupportedException' => 'Exception',
'ArcanistChooseInvalidRevisionException' => 'Exception',
diff --git a/src/configuration/ArcanistSettings.php b/src/configuration/ArcanistSettings.php
--- a/src/configuration/ArcanistSettings.php
+++ b/src/configuration/ArcanistSettings.php
@@ -32,6 +32,32 @@
'unit test engines.',
'example' => '["/var/arc/customlib/src"]',
),
+ 'repository.callsign' => array(
+ 'type' => 'string',
+ 'example' => '"X"',
+ 'help' => pht(
+ 'Associate the working copy with a specific Phabricator repository. '.
+ 'Normally, arc can figure this association out on its own, but if '.
+ 'your setup is unusual you can use this option to tell it what the '.
+ 'desired value is.'),
+ ),
+ 'phabricator.uri' => array(
+ 'type' => 'string',
+ 'legacy' => 'conduit_uri',
+ 'example' => '"https://phabricator.mycompany.com/"',
+ 'help' => pht(
+ 'Associates this working copy with a specific installation of '.
+ 'Phabricator.'),
+ ),
+ 'project.name' => array(
+ 'type' => 'string',
+ 'legacy' => 'project_id',
+ 'example' => '"arcanist"',
+ 'help' => pht(
+ 'Associates this working copy with a named Arcanist Project. '.
+ 'This is primarily useful if you use SVN and have several different '.
+ 'projects in the same repository.'),
+ ),
'lint.engine' => array(
'type' => 'string',
'legacy' => 'lint_engine',
diff --git a/src/repository/api/ArcanistGitAPI.php b/src/repository/api/ArcanistGitAPI.php
--- a/src/repository/api/ArcanistGitAPI.php
+++ b/src/repository/api/ArcanistGitAPI.php
@@ -369,6 +369,18 @@
if (preg_match('/^\* ([^\(].*)$/m', $stdout, $matches)) {
return $matches[1];
}
+
+ return null;
+ }
+
+ public function getRemoteURI() {
+ list($stdout) = $this->execxLocal('remote show -n origin');
+
+ $matches = null;
+ if (preg_match('/^\s*Fetch URL: (.*)$/m', $stdout, $matches)) {
+ return trim($matches[1]);
+ }
+
return null;
}
diff --git a/src/repository/api/ArcanistMercurialAPI.php b/src/repository/api/ArcanistMercurialAPI.php
--- a/src/repository/api/ArcanistMercurialAPI.php
+++ b/src/repository/api/ArcanistMercurialAPI.php
@@ -1038,4 +1038,16 @@
return array(trim($name), trim($rev));
}
+
+ public function getRemoteURI() {
+ list($stdout) = $this->execxLocal('paths default');
+
+ $stdout = trim($stdout);
+ if (strlen($stdout)) {
+ return $stdout;
+ }
+
+ return null;
+ }
+
}
diff --git a/src/repository/api/ArcanistRepositoryAPI.php b/src/repository/api/ArcanistRepositoryAPI.php
--- a/src/repository/api/ArcanistRepositoryAPI.php
+++ b/src/repository/api/ArcanistRepositoryAPI.php
@@ -330,6 +330,8 @@
abstract public function loadWorkingCopyDifferentialRevisions(
ConduitClient $conduit,
array $query);
+ abstract public function getRemoteURI();
+
public function getUnderlyingWorkingCopyRevision() {
return $this->getWorkingCopyRevision();
@@ -643,4 +645,8 @@
return $commit;
}
+ public function getRepositoryUUID() {
+ return null;
+ }
+
}
diff --git a/src/repository/api/ArcanistSubversionAPI.php b/src/repository/api/ArcanistSubversionAPI.php
--- a/src/repository/api/ArcanistSubversionAPI.php
+++ b/src/repository/api/ArcanistSubversionAPI.php
@@ -243,6 +243,10 @@
return 'svn';
}
+ public function getRemoteURI() {
+ return idx($this->getSVNInfo('/'), 'Repository Root');
+ }
+
public function buildInfoFuture($path) {
if ($path == '/') {
// When the root of a working copy is referenced by a symlink and you
@@ -587,7 +591,7 @@
return null;
}
- public function getRepositorySVNUUID() {
+ public function getRepositoryUUID() {
$info = $this->getSVNInfo('/');
return $info['Repository UUID'];
}
diff --git a/src/repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php b/src/repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php
--- a/src/repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php
+++ b/src/repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php
@@ -87,6 +87,7 @@
'UNSTAGED' => $f_mod | $f_uns | $f_unc,
'UNTRACKED' => $f_unt,
);
+
$this->assertEqual($expect_uncommitted, $api->getUncommittedStatus());
$expect_range = array(
diff --git a/src/workflow/ArcanistBaseWorkflow.php b/src/workflow/ArcanistBaseWorkflow.php
--- a/src/workflow/ArcanistBaseWorkflow.php
+++ b/src/workflow/ArcanistBaseWorkflow.php
@@ -31,7 +31,8 @@
*
* @task conduit Conduit
* @task scratch Scratch Files
- * @group workflow
+ * @task phabrep Phabricator Repositories
+ *
* @stable
*/
abstract class ArcanistBaseWorkflow extends Phobject {
@@ -64,6 +65,8 @@
private $stashed;
private $projectInfo;
+ private $repositoryInfo;
+ private $repositoryReasons;
private $arcanistConfiguration;
private $parentWorkflow;
@@ -1518,4 +1521,187 @@
return $this->repositoryVersion;
}
+
+/* -( Phabricator Repositories )------------------------------------------- */
+
+
+ /**
+ * Get the PHID of the Phabricator repository this working copy corresponds
+ * to. Returns `null` no repository can be identified.
+ *
+ * @return phid|null Repository PHID, or null if no repository can be
+ * identified.
+ *
+ * @task phabrep
+ */
+ protected function getRepositoryPHID() {
+ return idx($this->getRepositoryInformation(), 'phid');
+ }
+
+
+ /**
+ * Get the callsign of the Phabricator repository this working copy
+ * corresponds to. Returns `null` no repository can be identified.
+ *
+ * @return string|null Repository callsign, or null if no repository can be
+ * identified.
+ *
+ * @task phabrep
+ */
+ protected function getRepositoryCallsign() {
+ return idx($this->getRepositoryInformation(), 'callsign');
+ }
+
+
+ /**
+ * Get human-readable reasoning explaining how `arc` evaluated which
+ * Phabricator repository corresponds to this working copy. Used by
+ * `arc which` to explain the process to users.
+ *
+ * @return list<string> Human-readable explanation of the repository
+ * association process.
+ *
+ * @task phabrep
+ */
+ protected function getRepositoryReasons() {
+ $this->getRepositoryInformation();
+ return $this->repositoryReasons;
+ }
+
+
+ /**
+ * @task phabrep
+ */
+ private function getRepositoryInformation() {
+ if ($this->repositoryInfo === null) {
+ list($info, $reasons) = $this->loadRepositoryInformation();
+ $this->repositoryInfo = $info;
+ $this->repositoryReasons = $reasons;
+ }
+
+ return $this->repositoryInfo;
+ }
+
+
+ /**
+ * @task phabrep
+ */
+ private function loadRepositoryInformation() {
+ list($query, $reasons) = $this->getRepositoryQuery();
+ if (!$query) {
+ return array(null, $reasons);
+ }
+
+ try {
+ $results = $this->getConduit()->callMethodSynchronous(
+ 'repository.query',
+ $query);
+ } catch (ConduitClientException $ex) {
+ if ($ex->getErrorCode() == 'ERR-CONDUIT-CALL') {
+ $reasons[] = pht(
+ 'This version of Arcanist is more recent than the version of '.
+ 'Phabricator you are connecting to: the Phabricator install is '.
+ 'out of date and does not have support for identifying '.
+ 'repositories by callsign or URI. Update Phabricator to enable '.
+ 'these features.');
+ return array(null, $reasons);
+ }
+ throw $ex;
+ }
+
+ $result = null;
+ if (!$results) {
+ $reasons[] = pht(
+ 'No repositories matched the query. Check that your configuration '.
+ 'is correct, or use "repository.callsign" to select a repository '.
+ 'explicitly.');
+ } else if (count($results) > 1) {
+ $reasons[] = pht(
+ 'Multiple repostories (%s) matched the query. You can use the '.
+ '"repository.callsign" configuration to select the one you want.',
+ implode(', ', ipull($results, 'callsign')));
+ } else {
+ $result = head($results);
+ $reasons[] = pht('Found a unique matching repository.');
+ }
+
+ return array($result, $reasons);
+ }
+
+
+ /**
+ * @task phabrep
+ */
+ private function getRepositoryQuery() {
+ $reasons = array();
+
+ $callsign = $this->getConfigFromAnySource('repository.callsign');
+ if ($callsign) {
+ $query = array(
+ 'callsigns' => array($callsign),
+ );
+ $reasons[] = pht(
+ 'Configuration value "repository.callsign" is set to "%s".',
+ $callsign);
+ return array($query, $reasons);
+ } else {
+ $reasons[] = pht(
+ 'Configuration value "repository.callsign" is empty.');
+ }
+
+ $project_info = $this->getProjectInfo();
+ if ($this->getProjectInfo()) {
+ if (!empty($project_info['repository']['callsign'])) {
+ $callsign = $project_info['repository']['callsign'];
+ $query = array(
+ 'callsigns' => array($callsign),
+ );
+ $reasons[] = pht(
+ 'Configuration value "project.id" is set to "%s"; this project '.
+ 'is associated with the "%s" repository.',
+ $this->getWorkingCopy()->getProjectID(),
+ $callsign);
+ return array($query, $reasons);
+ } else {
+ $reasons[] = pht(
+ 'Configuration value "project.id" is set to "%s", but this '.
+ 'project is not associated with a repository.');
+ }
+ } else {
+ $reasons[] = pht(
+ 'Configuration value "project.id" is empty.');
+ }
+
+ $uuid = $this->getRepositoryAPI()->getRepositoryUUID();
+ if ($uuid !== null) {
+ $query = array(
+ 'uuids' => array($uuid),
+ );
+ $reasons[] = pht(
+ 'The UUID for this working copy is "%s".',
+ $uuid);
+ return array($query, $reasons);
+ } else {
+ $reasons[] = pht(
+ 'This repository has no VCS UUID (this is normal for git/hg).');
+ }
+
+ $remote_uri = $this->getRepositoryAPI()->getRemoteURI();
+ if ($remote_uri !== null) {
+ $query = array(
+ 'remoteURIs' => array($remote_uri),
+ );
+ $reasons[] = pht(
+ 'The remote URI for this working copy is "%s".',
+ $remote_uri);
+ return array($query, $reasons);
+ } else {
+ $reasons[] = pht(
+ 'Unable to determine the remote URI for this repository.');
+ }
+
+ return array(null, $reasons);
+ }
+
+
}
diff --git a/src/workflow/ArcanistDiffWorkflow.php b/src/workflow/ArcanistDiffWorkflow.php
--- a/src/workflow/ArcanistDiffWorkflow.php
+++ b/src/workflow/ArcanistDiffWorkflow.php
@@ -508,9 +508,9 @@
}
$diff_spec = array(
- 'changes' => mpull($changes, 'toDictionary'),
- 'lintStatus' => $this->getLintStatus($lint_result),
- 'unitStatus' => $this->getUnitStatus($unit_result),
+ 'changes' => mpull($changes, 'toDictionary'),
+ 'lintStatus' => $this->getLintStatus($lint_result),
+ 'unitStatus' => $this->getUnitStatus($unit_result),
) + $this->buildDiffSpecification();
$conduit = $this->getConduit();
@@ -2243,6 +2243,7 @@
$vcs = $repository_api->getSourceControlSystemName();
$source_path = $repository_api->getPath();
$branch = $repository_api->getBranchName();
+ $repo_uuid = $repository_api->getRepositoryUUID();
if ($repository_api instanceof ArcanistGitAPI) {
$info = $this->getGitParentLogInfo();
@@ -2258,8 +2259,6 @@
if ($info['uuid']) {
$repo_uuid = $info['uuid'];
}
- } else if ($repository_api instanceof ArcanistSubversionAPI) {
- $repo_uuid = $repository_api->getRepositorySVNUUID();
} else if ($repository_api instanceof ArcanistMercurialAPI) {
$bookmark = $repository_api->getActiveBookmark();
@@ -2280,7 +2279,7 @@
$project_id = $this->getWorkingCopy()->getProjectID();
}
- return array(
+ $data = array(
'sourceMachine' => php_uname('n'),
'sourcePath' => $source_path,
'branch' => $branch,
@@ -2288,12 +2287,16 @@
'sourceControlSystem' => $vcs,
'sourceControlPath' => $base_path,
'sourceControlBaseRevision' => $base_revision,
- 'parentRevisionID' => $parent,
- 'repositoryUUID' => $repo_uuid,
'creationMethod' => 'arc',
'arcanistProject' => $project_id,
- 'authorPHID' => $this->getUserPHID(),
);
+
+ $repository_phid = $this->getRepositoryPHID();
+ if ($repository_phid) {
+ $data['repositoryPHID'] = $repository_phid;
+ }
+
+ return $data;
}
diff --git a/src/workflow/ArcanistWhichWorkflow.php b/src/workflow/ArcanistWhichWorkflow.php
--- a/src/workflow/ArcanistWhichWorkflow.php
+++ b/src/workflow/ArcanistWhichWorkflow.php
@@ -22,7 +22,8 @@
public function getCommandHelp() {
return phutil_console_format(<<<EOTEXT
Supports: svn, git, hg
- Shows which commits 'arc diff' will select, and which revision is in
+ Shows which repository the current working copy corresponds to,
+ which commits 'arc diff' will select, and which revision is in
the working copy (or which revisions, if more than one matches).
EOTEXT
);
@@ -69,6 +70,11 @@
public function run() {
+ $console = PhutilConsole::getConsole();
+
+ $this->printRepositorySection();
+ $console->writeOut("\n");
+
$repository_api = $this->getRepositoryAPI();
$arg_commit = $this->getArgument('commit');
@@ -184,4 +190,34 @@
return 0;
}
+
+ private function printRepositorySection() {
+ $console = PhutilConsole::getConsole();
+ $console->writeOut("**%s**\n", pht('REPOSITORY'));
+
+ $callsign = $this->getRepositoryCallsign();
+
+ $console->writeOut(
+ "%s\n\n",
+ pht(
+ 'To identify the repository associated with this working copy, '.
+ 'arc followed this process:'));
+
+ foreach ($this->getRepositoryReasons() as $reason) {
+ $reason = phutil_console_wrap($reason, 4);
+ $console->writeOut("%s\n\n", $reason);
+ }
+
+ if ($callsign) {
+ $console->writeOut(
+ "%s\n",
+ pht('This working copy is associated with the %s repository.',
+ phutil_console_format('**%s**', $callsign)));
+ } else {
+ $console->writeOut(
+ "%s\n",
+ pht('This working copy is not associated with any repository.'));
+ }
+ }
+
}
diff --git a/src/workingcopyidentity/ArcanistWorkingCopyIdentity.php b/src/workingcopyidentity/ArcanistWorkingCopyIdentity.php
--- a/src/workingcopyidentity/ArcanistWorkingCopyIdentity.php
+++ b/src/workingcopyidentity/ArcanistWorkingCopyIdentity.php
@@ -187,6 +187,7 @@
private static function parseRawConfigFile($raw_config, $from_where) {
$proj = json_decode($raw_config, true);
+
if (!is_array($proj)) {
throw new Exception(
"Unable to parse '.arcconfig' file '{$from_where}'. The file contents ".
@@ -194,16 +195,7 @@
"FILE CONTENTS\n".
substr($raw_config, 0, 2048));
}
- $required_keys = array(
- 'project_id',
- );
- foreach ($required_keys as $key) {
- if (!array_key_exists($key, $proj)) {
- throw new Exception(
- "Required key '{$key}' is missing from '.arcconfig' file ".
- "'{$from_where}'.");
- }
- }
+
return $proj;
}
@@ -213,6 +205,12 @@
}
public function getProjectID() {
+ $project_id = $this->getProjectConfig('project.name');
+ if ($project_id) {
+ return $project_id;
+ }
+
+ // This is an older name for the setting.
return $this->getProjectConfig('project_id');
}

File Metadata

Mime Type
text/plain
Expires
Wed, Jan 22, 11:41 AM (5 h, 58 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7032196
Default Alt Text
D8073.id.diff (17 KB)

Event Timeline