Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14750075
D8073.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
17 KB
Referenced Files
None
Subscribers
None
D8073.id.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D8073: Allow `arc` to identify repositories without "project_id"
Attached
Detach File
Event Timeline
Log In to Comment