Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F21409765
D19689.id47043.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
48 KB
Referenced Files
None
Subscribers
None
D19689.id47043.diff
View Options
Index: src/workingcopy/ArcanistGitWorkingCopy.php
===================================================================
--- src/workingcopy/ArcanistGitWorkingCopy.php
+++ src/workingcopy/ArcanistGitWorkingCopy.php
@@ -1,340 +1,22 @@
<?php
-abstract class ArcanistWorkingCopy
- extends Phobject {
+final class ArcanistGitWorkingCopy
+ extends ArcanistWorkingCopy {
-
-
- private $projectConfig;
- private $projectRoot;
- private $localConfig = array();
- private $localMetaDir;
- private $vcsType;
- private $vcsRoot;
-
- public static function newDummyWorkingCopy() {
- return self::newFromPathWithConfig('/', array());
- }
-
- public static function newFromPath($path) {
- return self::newFromPathWithConfig($path, null);
- }
-
- /**
- * Locate all the information we need about a directory which we presume
- * to be a working copy. Particularly, we want to discover:
- *
- * - Is the directory inside a working copy (hg, git, svn)?
- * - If so, what is the root of the working copy?
- * - Is there a `.arcconfig` file?
- *
- * This is complicated, mostly because Subversion has special rules. In
- * particular:
- *
- * - Until 1.7, Subversion put a `.svn/` directory inside //every//
- * directory in a working copy. After 1.7, it //only// puts one at the
- * root.
- * - We allow `.arcconfig` to appear anywhere in a Subversion working copy,
- * and use the one closest to the directory.
- * - Although we may use a `.arcconfig` from a subdirectory, we store
- * metadata in the root's `.svn/`, because it's the only one guaranteed
- * to exist.
- *
- * Users also do these kinds of things in the wild:
- *
- * - Put working copies inside other working copies.
- * - Put working copies inside `.git/` directories.
- * - Create `.arcconfig` files at `/.arcconfig`, `/home/.arcconfig`, etc.
- *
- * This method attempts to be robust against all sorts of possible
- * misconfiguration.
- *
- * @param string Path to load information for, usually the current working
- * directory (unless running unit tests).
- * @param map|null Pass `null` to locate and load a `.arcconfig` file if one
- * exists. Pass a map to use it to set configuration.
- * @return ArcanistWorkingCopyIdentity Constructed working copy identity.
- */
- private static function newFromPathWithConfig($path, $config) {
- $project_root = null;
- $vcs_root = null;
- $vcs_type = null;
-
- // First, find the outermost directory which is a Git, Mercurial or
- // Subversion repository, if one exists. We go from the top because this
- // makes it easier to identify the root of old SVN working copies (which
- // have a ".svn/" directory inside every directory in the working copy) and
- // gives us the right result if you have a Git repository inside a
- // Subversion repository or something equally ridiculous.
-
- $paths = Filesystem::walkToRoot($path);
- $config_paths = array();
- $paths = array_reverse($paths);
- foreach ($paths as $path_key => $parent_path) {
- $try = array(
- 'git' => $parent_path.'/.git',
- 'hg' => $parent_path.'/.hg',
- 'svn' => $parent_path.'/.svn',
- );
-
- foreach ($try as $vcs => $try_dir) {
- if (!Filesystem::pathExists($try_dir)) {
- continue;
- }
-
- // NOTE: We're distinguishing between the `$project_root` and the
- // `$vcs_root` because they may not be the same in Subversion. Normally,
- // they are identical. However, in Subversion, the `$vcs_root` is the
- // base directory of the working copy (the directory which has the
- // `.svn/` directory, after SVN 1.7), while the `$project_root` might
- // be any subdirectory of the `$vcs_root`: it's the the directory
- // closest to the current directory which contains a `.arcconfig`.
-
- $project_root = $parent_path;
- $vcs_root = $parent_path;
- $vcs_type = $vcs;
- if ($vcs == 'svn') {
- // For Subversion, we'll look for a ".arcconfig" file here or in
- // any subdirectory, starting at the deepest subdirectory.
- $config_paths = array_slice($paths, $path_key);
- $config_paths = array_reverse($config_paths);
- } else {
- // For Git and Mercurial, we'll only look for ".arcconfig" right here.
- $config_paths = array($parent_path);
- }
- break;
- }
- }
-
- $console = PhutilConsole::getConsole();
-
- $looked_in = array();
- foreach ($config_paths as $config_path) {
- $config_file = $config_path.'/.arcconfig';
- $looked_in[] = $config_file;
- if (Filesystem::pathExists($config_file)) {
- // We always need to examine the filesystem to look for `.arcconfig`
- // so we can set the project root correctly. We might or might not
- // actually read the file: if the caller passed in configuration data,
- // we'll ignore the actual file contents.
- $project_root = $config_path;
- if ($config === null) {
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Reading %s from "%s".',
- '.arcconfig',
- $config_file));
- $config_data = Filesystem::readFile($config_file);
- $config = self::parseRawConfigFile($config_data, $config_file);
- }
- break;
- }
- }
-
- if ($config === null) {
- if ($looked_in) {
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Unable to find %s in any of these locations: %s.',
- '.arcconfig',
- implode(', ', $looked_in)));
- } else {
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: No candidate locations for %s from '.
- 'this working directory.',
- '.arcconfig'));
- }
- $config = array();
- }
-
- if ($project_root === null) {
- // We aren't in a working directory at all. This is fine if we're
- // running a command like "arc help". If we're running something that
- // requires a working directory, an exception will be raised a little
- // later on.
- $console->writeLog(
- "%s\n",
- pht('Working Copy: Path "%s" is not in any working copy.', $path));
- return new ArcanistWorkingCopyIdentity($path, $config);
- }
-
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Path "%s" is part of `%s` working copy "%s".',
- $path,
- $vcs_type,
- $vcs_root));
-
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Project root is at "%s".',
- $project_root));
-
- $identity = new ArcanistWorkingCopyIdentity($project_root, $config);
- $identity->localMetaDir = $vcs_root.'/.'.$vcs_type;
- $identity->localConfig = $identity->readLocalArcConfig();
- $identity->vcsType = $vcs_type;
- $identity->vcsRoot = $vcs_root;
-
- return $identity;
- }
-
- public static function newFromRootAndConfigFile(
- $root,
- $config_raw,
- $from_where) {
-
- if ($config_raw === null) {
- $config = array();
- } else {
- $config = self::parseRawConfigFile($config_raw, $from_where);
- }
-
- return self::newFromPathWithConfig($root, $config);
- }
-
- private static function parseRawConfigFile($raw_config, $from_where) {
- try {
- return phutil_json_decode($raw_config);
- } catch (PhutilJSONParserException $ex) {
- throw new PhutilProxyException(
- pht("Unable to parse '%s' file '%s'.", '.arcconfig', $from_where),
- $ex);
- }
- }
-
- private function __construct($root, array $config) {
- $this->projectRoot = $root;
- $this->projectConfig = $config;
- }
-
- public function getProjectRoot() {
- return $this->projectRoot;
- }
-
- public function getProjectPath($to_file) {
- return $this->projectRoot.'/'.$to_file;
- }
-
- public function getVCSType() {
- return $this->vcsType;
- }
-
- public function getVCSRoot() {
- return $this->vcsRoot;
+ public function getMetadataDirectory() {
+ return $this->getPath('.git');
}
+ protected function newWorkingCopyFromDirectories(
+ $working_directory,
+ $ancestor_directory) {
-/* -( Config )------------------------------------------------------------- */
-
- public function readProjectConfig() {
- return $this->projectConfig;
- }
-
- /**
- * Read a configuration directive from project configuration. This reads ONLY
- * permanent project configuration (i.e., ".arcconfig"), not other
- * configuration sources. See @{method:getConfigFromAnySource} to read from
- * user configuration.
- *
- * @param key Key to read.
- * @param wild Default value if key is not found.
- * @return wild Value, or default value if not found.
- *
- * @task config
- */
- public function getProjectConfig($key, $default = null) {
- $settings = new ArcanistSettings();
-
- $pval = idx($this->projectConfig, $key);
-
- // Test for older names in the per-project config only, since
- // they've only been used there.
- if ($pval === null) {
- $legacy = $settings->getLegacyName($key);
- if ($legacy) {
- $pval = $this->getProjectConfig($legacy);
- }
- }
-
- if ($pval === null) {
- $pval = $default;
- } else {
- $pval = $settings->willReadValue($key, $pval);
+ if (!Filesystem::pathExits($ancestor_directory.'/.git')) {
+ return null;
}
- return $pval;
- }
-
- /**
- * Read a configuration directive from local configuration. This
- * reads ONLY the per-working copy configuration,
- * i.e. .(git|hg|svn)/arc/config, and not other configuration
- * sources. See @{method:getConfigFromAnySource} to read from any
- * config source or @{method:getProjectConfig} to read permanent
- * project-level config.
- *
- * @task config
- */
- public function getLocalConfig($key, $default = null) {
- return idx($this->localConfig, $key, $default);
- }
-
- public function readLocalArcConfig() {
- if (strlen($this->localMetaDir)) {
- $local_path = Filesystem::resolvePath('arc/config', $this->localMetaDir);
-
- $console = PhutilConsole::getConsole();
-
- if (Filesystem::pathExists($local_path)) {
- $console->writeLog(
- "%s\n",
- pht(
- 'Config: Reading local configuration file "%s"...',
- $local_path));
-
- try {
- $json = Filesystem::readFile($local_path);
- return phutil_json_decode($json);
- } catch (PhutilJSONParserException $ex) {
- throw new PhutilProxyException(
- pht("Failed to parse '%s' as JSON.", $local_path),
- $ex);
- }
- } else {
- $console->writeLog(
- "%s\n",
- pht(
- 'Config: Did not find local configuration at "%s".',
- $local_path));
- }
- }
-
- return array();
- }
-
- public function writeLocalArcConfig(array $config) {
- $json_encoder = new PhutilJSON();
- $json = $json_encoder->encodeFormatted($config);
-
- $dir = $this->localMetaDir;
- if (!strlen($dir)) {
- throw new Exception(pht('No working copy to write config into!'));
- }
-
- $local_dir = $dir.DIRECTORY_SEPARATOR.'arc';
- if (!Filesystem::pathExists($local_dir)) {
- Filesystem::createDirectory($local_dir, 0755);
- }
-
- $config_file = $local_dir.DIRECTORY_SEPARATOR.'config';
- Filesystem::writeFile($config_file, $json);
+ return new self();
}
}
+
Index: src/workingcopy/ArcanistMercurialWorkingCopy.php
===================================================================
--- src/workingcopy/ArcanistMercurialWorkingCopy.php
+++ src/workingcopy/ArcanistMercurialWorkingCopy.php
@@ -1,340 +1,22 @@
<?php
-abstract class ArcanistWorkingCopy
- extends Phobject {
+final class ArcanistMercurialWorkingCopy
+ extends ArcanistWorkingCopy {
-
-
- private $projectConfig;
- private $projectRoot;
- private $localConfig = array();
- private $localMetaDir;
- private $vcsType;
- private $vcsRoot;
-
- public static function newDummyWorkingCopy() {
- return self::newFromPathWithConfig('/', array());
- }
-
- public static function newFromPath($path) {
- return self::newFromPathWithConfig($path, null);
- }
-
- /**
- * Locate all the information we need about a directory which we presume
- * to be a working copy. Particularly, we want to discover:
- *
- * - Is the directory inside a working copy (hg, git, svn)?
- * - If so, what is the root of the working copy?
- * - Is there a `.arcconfig` file?
- *
- * This is complicated, mostly because Subversion has special rules. In
- * particular:
- *
- * - Until 1.7, Subversion put a `.svn/` directory inside //every//
- * directory in a working copy. After 1.7, it //only// puts one at the
- * root.
- * - We allow `.arcconfig` to appear anywhere in a Subversion working copy,
- * and use the one closest to the directory.
- * - Although we may use a `.arcconfig` from a subdirectory, we store
- * metadata in the root's `.svn/`, because it's the only one guaranteed
- * to exist.
- *
- * Users also do these kinds of things in the wild:
- *
- * - Put working copies inside other working copies.
- * - Put working copies inside `.git/` directories.
- * - Create `.arcconfig` files at `/.arcconfig`, `/home/.arcconfig`, etc.
- *
- * This method attempts to be robust against all sorts of possible
- * misconfiguration.
- *
- * @param string Path to load information for, usually the current working
- * directory (unless running unit tests).
- * @param map|null Pass `null` to locate and load a `.arcconfig` file if one
- * exists. Pass a map to use it to set configuration.
- * @return ArcanistWorkingCopyIdentity Constructed working copy identity.
- */
- private static function newFromPathWithConfig($path, $config) {
- $project_root = null;
- $vcs_root = null;
- $vcs_type = null;
-
- // First, find the outermost directory which is a Git, Mercurial or
- // Subversion repository, if one exists. We go from the top because this
- // makes it easier to identify the root of old SVN working copies (which
- // have a ".svn/" directory inside every directory in the working copy) and
- // gives us the right result if you have a Git repository inside a
- // Subversion repository or something equally ridiculous.
-
- $paths = Filesystem::walkToRoot($path);
- $config_paths = array();
- $paths = array_reverse($paths);
- foreach ($paths as $path_key => $parent_path) {
- $try = array(
- 'git' => $parent_path.'/.git',
- 'hg' => $parent_path.'/.hg',
- 'svn' => $parent_path.'/.svn',
- );
-
- foreach ($try as $vcs => $try_dir) {
- if (!Filesystem::pathExists($try_dir)) {
- continue;
- }
-
- // NOTE: We're distinguishing between the `$project_root` and the
- // `$vcs_root` because they may not be the same in Subversion. Normally,
- // they are identical. However, in Subversion, the `$vcs_root` is the
- // base directory of the working copy (the directory which has the
- // `.svn/` directory, after SVN 1.7), while the `$project_root` might
- // be any subdirectory of the `$vcs_root`: it's the the directory
- // closest to the current directory which contains a `.arcconfig`.
-
- $project_root = $parent_path;
- $vcs_root = $parent_path;
- $vcs_type = $vcs;
- if ($vcs == 'svn') {
- // For Subversion, we'll look for a ".arcconfig" file here or in
- // any subdirectory, starting at the deepest subdirectory.
- $config_paths = array_slice($paths, $path_key);
- $config_paths = array_reverse($config_paths);
- } else {
- // For Git and Mercurial, we'll only look for ".arcconfig" right here.
- $config_paths = array($parent_path);
- }
- break;
- }
- }
-
- $console = PhutilConsole::getConsole();
-
- $looked_in = array();
- foreach ($config_paths as $config_path) {
- $config_file = $config_path.'/.arcconfig';
- $looked_in[] = $config_file;
- if (Filesystem::pathExists($config_file)) {
- // We always need to examine the filesystem to look for `.arcconfig`
- // so we can set the project root correctly. We might or might not
- // actually read the file: if the caller passed in configuration data,
- // we'll ignore the actual file contents.
- $project_root = $config_path;
- if ($config === null) {
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Reading %s from "%s".',
- '.arcconfig',
- $config_file));
- $config_data = Filesystem::readFile($config_file);
- $config = self::parseRawConfigFile($config_data, $config_file);
- }
- break;
- }
- }
-
- if ($config === null) {
- if ($looked_in) {
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Unable to find %s in any of these locations: %s.',
- '.arcconfig',
- implode(', ', $looked_in)));
- } else {
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: No candidate locations for %s from '.
- 'this working directory.',
- '.arcconfig'));
- }
- $config = array();
- }
-
- if ($project_root === null) {
- // We aren't in a working directory at all. This is fine if we're
- // running a command like "arc help". If we're running something that
- // requires a working directory, an exception will be raised a little
- // later on.
- $console->writeLog(
- "%s\n",
- pht('Working Copy: Path "%s" is not in any working copy.', $path));
- return new ArcanistWorkingCopyIdentity($path, $config);
- }
-
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Path "%s" is part of `%s` working copy "%s".',
- $path,
- $vcs_type,
- $vcs_root));
-
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Project root is at "%s".',
- $project_root));
-
- $identity = new ArcanistWorkingCopyIdentity($project_root, $config);
- $identity->localMetaDir = $vcs_root.'/.'.$vcs_type;
- $identity->localConfig = $identity->readLocalArcConfig();
- $identity->vcsType = $vcs_type;
- $identity->vcsRoot = $vcs_root;
-
- return $identity;
- }
-
- public static function newFromRootAndConfigFile(
- $root,
- $config_raw,
- $from_where) {
-
- if ($config_raw === null) {
- $config = array();
- } else {
- $config = self::parseRawConfigFile($config_raw, $from_where);
- }
-
- return self::newFromPathWithConfig($root, $config);
- }
-
- private static function parseRawConfigFile($raw_config, $from_where) {
- try {
- return phutil_json_decode($raw_config);
- } catch (PhutilJSONParserException $ex) {
- throw new PhutilProxyException(
- pht("Unable to parse '%s' file '%s'.", '.arcconfig', $from_where),
- $ex);
- }
- }
-
- private function __construct($root, array $config) {
- $this->projectRoot = $root;
- $this->projectConfig = $config;
- }
-
- public function getProjectRoot() {
- return $this->projectRoot;
- }
-
- public function getProjectPath($to_file) {
- return $this->projectRoot.'/'.$to_file;
- }
-
- public function getVCSType() {
- return $this->vcsType;
- }
-
- public function getVCSRoot() {
- return $this->vcsRoot;
+ public function getMetadataDirectory() {
+ return $this->getPath('.hg');
}
+ protected function newWorkingCopyFromDirectories(
+ $working_directory,
+ $ancestor_directory) {
-/* -( Config )------------------------------------------------------------- */
-
- public function readProjectConfig() {
- return $this->projectConfig;
- }
-
- /**
- * Read a configuration directive from project configuration. This reads ONLY
- * permanent project configuration (i.e., ".arcconfig"), not other
- * configuration sources. See @{method:getConfigFromAnySource} to read from
- * user configuration.
- *
- * @param key Key to read.
- * @param wild Default value if key is not found.
- * @return wild Value, or default value if not found.
- *
- * @task config
- */
- public function getProjectConfig($key, $default = null) {
- $settings = new ArcanistSettings();
-
- $pval = idx($this->projectConfig, $key);
-
- // Test for older names in the per-project config only, since
- // they've only been used there.
- if ($pval === null) {
- $legacy = $settings->getLegacyName($key);
- if ($legacy) {
- $pval = $this->getProjectConfig($legacy);
- }
- }
-
- if ($pval === null) {
- $pval = $default;
- } else {
- $pval = $settings->willReadValue($key, $pval);
+ if (!Filesystem::pathExits($ancestor_directory.'/.hg')) {
+ return null;
}
- return $pval;
- }
-
- /**
- * Read a configuration directive from local configuration. This
- * reads ONLY the per-working copy configuration,
- * i.e. .(git|hg|svn)/arc/config, and not other configuration
- * sources. See @{method:getConfigFromAnySource} to read from any
- * config source or @{method:getProjectConfig} to read permanent
- * project-level config.
- *
- * @task config
- */
- public function getLocalConfig($key, $default = null) {
- return idx($this->localConfig, $key, $default);
- }
-
- public function readLocalArcConfig() {
- if (strlen($this->localMetaDir)) {
- $local_path = Filesystem::resolvePath('arc/config', $this->localMetaDir);
-
- $console = PhutilConsole::getConsole();
-
- if (Filesystem::pathExists($local_path)) {
- $console->writeLog(
- "%s\n",
- pht(
- 'Config: Reading local configuration file "%s"...',
- $local_path));
-
- try {
- $json = Filesystem::readFile($local_path);
- return phutil_json_decode($json);
- } catch (PhutilJSONParserException $ex) {
- throw new PhutilProxyException(
- pht("Failed to parse '%s' as JSON.", $local_path),
- $ex);
- }
- } else {
- $console->writeLog(
- "%s\n",
- pht(
- 'Config: Did not find local configuration at "%s".',
- $local_path));
- }
- }
-
- return array();
- }
-
- public function writeLocalArcConfig(array $config) {
- $json_encoder = new PhutilJSON();
- $json = $json_encoder->encodeFormatted($config);
-
- $dir = $this->localMetaDir;
- if (!strlen($dir)) {
- throw new Exception(pht('No working copy to write config into!'));
- }
-
- $local_dir = $dir.DIRECTORY_SEPARATOR.'arc';
- if (!Filesystem::pathExists($local_dir)) {
- Filesystem::createDirectory($local_dir, 0755);
- }
-
- $config_file = $local_dir.DIRECTORY_SEPARATOR.'config';
- Filesystem::writeFile($config_file, $json);
+ return new self();
}
}
+
Index: src/workingcopy/ArcanistSubversionWorkingCopy.php
===================================================================
--- src/workingcopy/ArcanistSubversionWorkingCopy.php
+++ src/workingcopy/ArcanistSubversionWorkingCopy.php
@@ -1,340 +1,51 @@
<?php
-abstract class ArcanistWorkingCopy
- extends Phobject {
+final class ArcanistSubversionWorkingCopy
+ extends ArcanistWorkingCopy {
+ public function getProjectConfigurationFilePath() {
+ // In Subversion, we allow ".arcconfig" to appear at any level of the
+ // filesystem between the working directory and the working copy root.
+ // We allow this because Subversion repositories are hierarchical and
+ // may have a "projects/xyz/" directory which is meaningfully an entirely
+ // different project from "projects/abc/".
- private $projectConfig;
- private $projectRoot;
- private $localConfig = array();
- private $localMetaDir;
- private $vcsType;
- private $vcsRoot;
+ // You can checkout "projects/" and have the ".svn/" directory appear
+ // there, then change into "abc/" and expect "arc" to operate within the
+ // context of the "abc/" project.
- public static function newDummyWorkingCopy() {
- return self::newFromPathWithConfig('/', array());
- }
-
- public static function newFromPath($path) {
- return self::newFromPathWithConfig($path, null);
- }
-
- /**
- * Locate all the information we need about a directory which we presume
- * to be a working copy. Particularly, we want to discover:
- *
- * - Is the directory inside a working copy (hg, git, svn)?
- * - If so, what is the root of the working copy?
- * - Is there a `.arcconfig` file?
- *
- * This is complicated, mostly because Subversion has special rules. In
- * particular:
- *
- * - Until 1.7, Subversion put a `.svn/` directory inside //every//
- * directory in a working copy. After 1.7, it //only// puts one at the
- * root.
- * - We allow `.arcconfig` to appear anywhere in a Subversion working copy,
- * and use the one closest to the directory.
- * - Although we may use a `.arcconfig` from a subdirectory, we store
- * metadata in the root's `.svn/`, because it's the only one guaranteed
- * to exist.
- *
- * Users also do these kinds of things in the wild:
- *
- * - Put working copies inside other working copies.
- * - Put working copies inside `.git/` directories.
- * - Create `.arcconfig` files at `/.arcconfig`, `/home/.arcconfig`, etc.
- *
- * This method attempts to be robust against all sorts of possible
- * misconfiguration.
- *
- * @param string Path to load information for, usually the current working
- * directory (unless running unit tests).
- * @param map|null Pass `null` to locate and load a `.arcconfig` file if one
- * exists. Pass a map to use it to set configuration.
- * @return ArcanistWorkingCopyIdentity Constructed working copy identity.
- */
- private static function newFromPathWithConfig($path, $config) {
- $project_root = null;
- $vcs_root = null;
- $vcs_type = null;
-
- // First, find the outermost directory which is a Git, Mercurial or
- // Subversion repository, if one exists. We go from the top because this
- // makes it easier to identify the root of old SVN working copies (which
- // have a ".svn/" directory inside every directory in the working copy) and
- // gives us the right result if you have a Git repository inside a
- // Subversion repository or something equally ridiculous.
-
- $paths = Filesystem::walkToRoot($path);
- $config_paths = array();
- $paths = array_reverse($paths);
- foreach ($paths as $path_key => $parent_path) {
- $try = array(
- 'git' => $parent_path.'/.git',
- 'hg' => $parent_path.'/.hg',
- 'svn' => $parent_path.'/.svn',
- );
-
- foreach ($try as $vcs => $try_dir) {
- if (!Filesystem::pathExists($try_dir)) {
- continue;
- }
-
- // NOTE: We're distinguishing between the `$project_root` and the
- // `$vcs_root` because they may not be the same in Subversion. Normally,
- // they are identical. However, in Subversion, the `$vcs_root` is the
- // base directory of the working copy (the directory which has the
- // `.svn/` directory, after SVN 1.7), while the `$project_root` might
- // be any subdirectory of the `$vcs_root`: it's the the directory
- // closest to the current directory which contains a `.arcconfig`.
-
- $project_root = $parent_path;
- $vcs_root = $parent_path;
- $vcs_type = $vcs;
- if ($vcs == 'svn') {
- // For Subversion, we'll look for a ".arcconfig" file here or in
- // any subdirectory, starting at the deepest subdirectory.
- $config_paths = array_slice($paths, $path_key);
- $config_paths = array_reverse($config_paths);
- } else {
- // For Git and Mercurial, we'll only look for ".arcconfig" right here.
- $config_paths = array($parent_path);
- }
- break;
- }
- }
-
- $console = PhutilConsole::getConsole();
-
- $looked_in = array();
- foreach ($config_paths as $config_path) {
- $config_file = $config_path.'/.arcconfig';
- $looked_in[] = $config_file;
- if (Filesystem::pathExists($config_file)) {
- // We always need to examine the filesystem to look for `.arcconfig`
- // so we can set the project root correctly. We might or might not
- // actually read the file: if the caller passed in configuration data,
- // we'll ignore the actual file contents.
- $project_root = $config_path;
- if ($config === null) {
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Reading %s from "%s".',
- '.arcconfig',
- $config_file));
- $config_data = Filesystem::readFile($config_file);
- $config = self::parseRawConfigFile($config_data, $config_file);
- }
+ $paths = Filesystem::walkToRoot($this->getWorkingDirectory());
+ $root = $this->getPath();
+ foreach ($paths as $path) {
+ if (!Filesystem::isDescendant($path, $root)) {
break;
}
- }
- if ($config === null) {
- if ($looked_in) {
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Unable to find %s in any of these locations: %s.',
- '.arcconfig',
- implode(', ', $looked_in)));
- } else {
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: No candidate locations for %s from '.
- 'this working directory.',
- '.arcconfig'));
+ $candidate = $path.'/.arcconfig';
+ if (Filesystem::pathExists($candidate)) {
+ return $candidate;
}
- $config = array();
}
- if ($project_root === null) {
- // We aren't in a working directory at all. This is fine if we're
- // running a command like "arc help". If we're running something that
- // requires a working directory, an exception will be raised a little
- // later on.
- $console->writeLog(
- "%s\n",
- pht('Working Copy: Path "%s" is not in any working copy.', $path));
- return new ArcanistWorkingCopyIdentity($path, $config);
- }
-
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Path "%s" is part of `%s` working copy "%s".',
- $path,
- $vcs_type,
- $vcs_root));
-
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Project root is at "%s".',
- $project_root));
-
- $identity = new ArcanistWorkingCopyIdentity($project_root, $config);
- $identity->localMetaDir = $vcs_root.'/.'.$vcs_type;
- $identity->localConfig = $identity->readLocalArcConfig();
- $identity->vcsType = $vcs_type;
- $identity->vcsRoot = $vcs_root;
-
- return $identity;
+ return parent::getProjectConfigurationFilePath();
}
- public static function newFromRootAndConfigFile(
- $root,
- $config_raw,
- $from_where) {
-
- if ($config_raw === null) {
- $config = array();
- } else {
- $config = self::parseRawConfigFile($config_raw, $from_where);
- }
-
- return self::newFromPathWithConfig($root, $config);
+ public function getMetadataDirectory() {
+ return $this->getPath('.svn');
}
- private static function parseRawConfigFile($raw_config, $from_where) {
- try {
- return phutil_json_decode($raw_config);
- } catch (PhutilJSONParserException $ex) {
- throw new PhutilProxyException(
- pht("Unable to parse '%s' file '%s'.", '.arcconfig', $from_where),
- $ex);
- }
- }
-
- private function __construct($root, array $config) {
- $this->projectRoot = $root;
- $this->projectConfig = $config;
- }
-
- public function getProjectRoot() {
- return $this->projectRoot;
- }
-
- public function getProjectPath($to_file) {
- return $this->projectRoot.'/'.$to_file;
- }
+ protected function newWorkingCopyFromDirectories(
+ $working_directory,
+ $ancestor_directory) {
- public function getVCSType() {
- return $this->vcsType;
- }
-
- public function getVCSRoot() {
- return $this->vcsRoot;
- }
-
-
-/* -( Config )------------------------------------------------------------- */
-
- public function readProjectConfig() {
- return $this->projectConfig;
- }
-
- /**
- * Read a configuration directive from project configuration. This reads ONLY
- * permanent project configuration (i.e., ".arcconfig"), not other
- * configuration sources. See @{method:getConfigFromAnySource} to read from
- * user configuration.
- *
- * @param key Key to read.
- * @param wild Default value if key is not found.
- * @return wild Value, or default value if not found.
- *
- * @task config
- */
- public function getProjectConfig($key, $default = null) {
- $settings = new ArcanistSettings();
-
- $pval = idx($this->projectConfig, $key);
-
- // Test for older names in the per-project config only, since
- // they've only been used there.
- if ($pval === null) {
- $legacy = $settings->getLegacyName($key);
- if ($legacy) {
- $pval = $this->getProjectConfig($legacy);
- }
- }
-
- if ($pval === null) {
- $pval = $default;
- } else {
- $pval = $settings->willReadValue($key, $pval);
- }
-
- return $pval;
- }
-
- /**
- * Read a configuration directive from local configuration. This
- * reads ONLY the per-working copy configuration,
- * i.e. .(git|hg|svn)/arc/config, and not other configuration
- * sources. See @{method:getConfigFromAnySource} to read from any
- * config source or @{method:getProjectConfig} to read permanent
- * project-level config.
- *
- * @task config
- */
- public function getLocalConfig($key, $default = null) {
- return idx($this->localConfig, $key, $default);
- }
-
- public function readLocalArcConfig() {
- if (strlen($this->localMetaDir)) {
- $local_path = Filesystem::resolvePath('arc/config', $this->localMetaDir);
-
- $console = PhutilConsole::getConsole();
-
- if (Filesystem::pathExists($local_path)) {
- $console->writeLog(
- "%s\n",
- pht(
- 'Config: Reading local configuration file "%s"...',
- $local_path));
-
- try {
- $json = Filesystem::readFile($local_path);
- return phutil_json_decode($json);
- } catch (PhutilJSONParserException $ex) {
- throw new PhutilProxyException(
- pht("Failed to parse '%s' as JSON.", $local_path),
- $ex);
- }
- } else {
- $console->writeLog(
- "%s\n",
- pht(
- 'Config: Did not find local configuration at "%s".',
- $local_path));
- }
+ if (!Filesystem::pathExits($ancestor_directory.'/.svn')) {
+ return null;
}
- return array();
+ return id(new self());
}
- public function writeLocalArcConfig(array $config) {
- $json_encoder = new PhutilJSON();
- $json = $json_encoder->encodeFormatted($config);
-
- $dir = $this->localMetaDir;
- if (!strlen($dir)) {
- throw new Exception(pht('No working copy to write config into!'));
- }
-
- $local_dir = $dir.DIRECTORY_SEPARATOR.'arc';
- if (!Filesystem::pathExists($local_dir)) {
- Filesystem::createDirectory($local_dir, 0755);
- }
-
- $config_file = $local_dir.DIRECTORY_SEPARATOR.'config';
- Filesystem::writeFile($config_file, $json);
- }
}
+
Index: src/workingcopy/ArcanistWorkingCopy.php
===================================================================
--- src/workingcopy/ArcanistWorkingCopy.php
+++ src/workingcopy/ArcanistWorkingCopy.php
@@ -3,338 +3,93 @@
abstract class ArcanistWorkingCopy
extends Phobject {
-
-
- private $projectConfig;
- private $projectRoot;
- private $localConfig = array();
- private $localMetaDir;
- private $vcsType;
- private $vcsRoot;
-
- public static function newDummyWorkingCopy() {
- return self::newFromPathWithConfig('/', array());
- }
-
- public static function newFromPath($path) {
- return self::newFromPathWithConfig($path, null);
- }
-
- /**
- * Locate all the information we need about a directory which we presume
- * to be a working copy. Particularly, we want to discover:
- *
- * - Is the directory inside a working copy (hg, git, svn)?
- * - If so, what is the root of the working copy?
- * - Is there a `.arcconfig` file?
- *
- * This is complicated, mostly because Subversion has special rules. In
- * particular:
- *
- * - Until 1.7, Subversion put a `.svn/` directory inside //every//
- * directory in a working copy. After 1.7, it //only// puts one at the
- * root.
- * - We allow `.arcconfig` to appear anywhere in a Subversion working copy,
- * and use the one closest to the directory.
- * - Although we may use a `.arcconfig` from a subdirectory, we store
- * metadata in the root's `.svn/`, because it's the only one guaranteed
- * to exist.
- *
- * Users also do these kinds of things in the wild:
- *
- * - Put working copies inside other working copies.
- * - Put working copies inside `.git/` directories.
- * - Create `.arcconfig` files at `/.arcconfig`, `/home/.arcconfig`, etc.
- *
- * This method attempts to be robust against all sorts of possible
- * misconfiguration.
- *
- * @param string Path to load information for, usually the current working
- * directory (unless running unit tests).
- * @param map|null Pass `null` to locate and load a `.arcconfig` file if one
- * exists. Pass a map to use it to set configuration.
- * @return ArcanistWorkingCopyIdentity Constructed working copy identity.
- */
- private static function newFromPathWithConfig($path, $config) {
- $project_root = null;
- $vcs_root = null;
- $vcs_type = null;
-
- // First, find the outermost directory which is a Git, Mercurial or
- // Subversion repository, if one exists. We go from the top because this
- // makes it easier to identify the root of old SVN working copies (which
- // have a ".svn/" directory inside every directory in the working copy) and
- // gives us the right result if you have a Git repository inside a
- // Subversion repository or something equally ridiculous.
+ private $path;
+ private $workingDirectory;
+
+ public static function newFromWorkingDirectory($path) {
+ $working_types = id(new PhutilClassMapQuery())
+ ->setParentClass(__CLASS__)
+ ->execute();
+
+ // Find the outermost directory which is under version control. We go from
+ // the top because:
+ //
+ // - This gives us a more reasonable behavior if you embed one repository
+ // inside another repository.
+ // - This handles old Subversion working copies correctly. Before
+ // SVN 1.7, Subversion put a ".svn/" directory in every subdirectory.
$paths = Filesystem::walkToRoot($path);
- $config_paths = array();
$paths = array_reverse($paths);
- foreach ($paths as $path_key => $parent_path) {
- $try = array(
- 'git' => $parent_path.'/.git',
- 'hg' => $parent_path.'/.hg',
- 'svn' => $parent_path.'/.svn',
- );
+ foreach ($paths as $path_key => $ancestor_path) {
+ foreach ($working_types as $working_type) {
- foreach ($try as $vcs => $try_dir) {
- if (!Filesystem::pathExists($try_dir)) {
+ $working_copy = $working_type->newWorkingCopyFromDirectories(
+ $path,
+ $ancestor_path);
+ if (!$working_copy) {
continue;
}
- // NOTE: We're distinguishing between the `$project_root` and the
- // `$vcs_root` because they may not be the same in Subversion. Normally,
- // they are identical. However, in Subversion, the `$vcs_root` is the
- // base directory of the working copy (the directory which has the
- // `.svn/` directory, after SVN 1.7), while the `$project_root` might
- // be any subdirectory of the `$vcs_root`: it's the the directory
- // closest to the current directory which contains a `.arcconfig`.
-
- $project_root = $parent_path;
- $vcs_root = $parent_path;
- $vcs_type = $vcs;
- if ($vcs == 'svn') {
- // For Subversion, we'll look for a ".arcconfig" file here or in
- // any subdirectory, starting at the deepest subdirectory.
- $config_paths = array_slice($paths, $path_key);
- $config_paths = array_reverse($config_paths);
- } else {
- // For Git and Mercurial, we'll only look for ".arcconfig" right here.
- $config_paths = array($parent_path);
- }
- break;
- }
- }
-
- $console = PhutilConsole::getConsole();
+ $working_copy->path = $ancestor_path;
+ $working_copy->workingDirectory = $path;
- $looked_in = array();
- foreach ($config_paths as $config_path) {
- $config_file = $config_path.'/.arcconfig';
- $looked_in[] = $config_file;
- if (Filesystem::pathExists($config_file)) {
- // We always need to examine the filesystem to look for `.arcconfig`
- // so we can set the project root correctly. We might or might not
- // actually read the file: if the caller passed in configuration data,
- // we'll ignore the actual file contents.
- $project_root = $config_path;
- if ($config === null) {
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Reading %s from "%s".',
- '.arcconfig',
- $config_file));
- $config_data = Filesystem::readFile($config_file);
- $config = self::parseRawConfigFile($config_data, $config_file);
- }
- break;
- }
- }
-
- if ($config === null) {
- if ($looked_in) {
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Unable to find %s in any of these locations: %s.',
- '.arcconfig',
- implode(', ', $looked_in)));
- } else {
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: No candidate locations for %s from '.
- 'this working directory.',
- '.arcconfig'));
+ return $working_copy;
}
- $config = array();
- }
-
- if ($project_root === null) {
- // We aren't in a working directory at all. This is fine if we're
- // running a command like "arc help". If we're running something that
- // requires a working directory, an exception will be raised a little
- // later on.
- $console->writeLog(
- "%s\n",
- pht('Working Copy: Path "%s" is not in any working copy.', $path));
- return new ArcanistWorkingCopyIdentity($path, $config);
- }
-
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Path "%s" is part of `%s` working copy "%s".',
- $path,
- $vcs_type,
- $vcs_root));
-
- $console->writeLog(
- "%s\n",
- pht(
- 'Working Copy: Project root is at "%s".',
- $project_root));
-
- $identity = new ArcanistWorkingCopyIdentity($project_root, $config);
- $identity->localMetaDir = $vcs_root.'/.'.$vcs_type;
- $identity->localConfig = $identity->readLocalArcConfig();
- $identity->vcsType = $vcs_type;
- $identity->vcsRoot = $vcs_root;
-
- return $identity;
- }
-
- public static function newFromRootAndConfigFile(
- $root,
- $config_raw,
- $from_where) {
-
- if ($config_raw === null) {
- $config = array();
- } else {
- $config = self::parseRawConfigFile($config_raw, $from_where);
}
- return self::newFromPathWithConfig($root, $config);
- }
-
- private static function parseRawConfigFile($raw_config, $from_where) {
- try {
- return phutil_json_decode($raw_config);
- } catch (PhutilJSONParserException $ex) {
- throw new PhutilProxyException(
- pht("Unable to parse '%s' file '%s'.", '.arcconfig', $from_where),
- $ex);
- }
+ return null;
}
- private function __construct($root, array $config) {
- $this->projectRoot = $root;
- $this->projectConfig = $config;
- }
-
- public function getProjectRoot() {
- return $this->projectRoot;
- }
+ abstract protected function newWorkingCopyFromDirectories(
+ $working_directory,
+ $ancestor_directory);
- public function getProjectPath($to_file) {
- return $this->projectRoot.'/'.$to_file;
+ final public function getPath($to_file = null) {
+ return Filesystem::concatenatePaths(
+ array(
+ $this->path,
+ $to_file,
+ ));
}
- public function getVCSType() {
- return $this->vcsType;
+ final public function getWorkingDirectory() {
+ return $this->workingDirectory;
}
- public function getVCSRoot() {
- return $this->vcsRoot;
+ public function getProjectConfigurationFilePath() {
+ return $this->getPath('.arcconfig');
}
-
-/* -( Config )------------------------------------------------------------- */
-
- public function readProjectConfig() {
- return $this->projectConfig;
- }
-
- /**
- * Read a configuration directive from project configuration. This reads ONLY
- * permanent project configuration (i.e., ".arcconfig"), not other
- * configuration sources. See @{method:getConfigFromAnySource} to read from
- * user configuration.
- *
- * @param key Key to read.
- * @param wild Default value if key is not found.
- * @return wild Value, or default value if not found.
- *
- * @task config
- */
- public function getProjectConfig($key, $default = null) {
- $settings = new ArcanistSettings();
-
- $pval = idx($this->projectConfig, $key);
-
- // Test for older names in the per-project config only, since
- // they've only been used there.
- if ($pval === null) {
- $legacy = $settings->getLegacyName($key);
- if ($legacy) {
- $pval = $this->getProjectConfig($legacy);
- }
- }
-
- if ($pval === null) {
- $pval = $default;
- } else {
- $pval = $settings->willReadValue($key, $pval);
+ public function getLocalConfigurationFilePath() {
+ if ($this->hasMetadataDirectory()) {
+ return $this->getMetadataPath('arc/config');
}
- return $pval;
+ return null;
}
- /**
- * Read a configuration directive from local configuration. This
- * reads ONLY the per-working copy configuration,
- * i.e. .(git|hg|svn)/arc/config, and not other configuration
- * sources. See @{method:getConfigFromAnySource} to read from any
- * config source or @{method:getProjectConfig} to read permanent
- * project-level config.
- *
- * @task config
- */
- public function getLocalConfig($key, $default = null) {
- return idx($this->localConfig, $key, $default);
+ public function getMetadataDirectory() {
+ return null;
}
- public function readLocalArcConfig() {
- if (strlen($this->localMetaDir)) {
- $local_path = Filesystem::resolvePath('arc/config', $this->localMetaDir);
-
- $console = PhutilConsole::getConsole();
-
- if (Filesystem::pathExists($local_path)) {
- $console->writeLog(
- "%s\n",
- pht(
- 'Config: Reading local configuration file "%s"...',
- $local_path));
-
- try {
- $json = Filesystem::readFile($local_path);
- return phutil_json_decode($json);
- } catch (PhutilJSONParserException $ex) {
- throw new PhutilProxyException(
- pht("Failed to parse '%s' as JSON.", $local_path),
- $ex);
- }
- } else {
- $console->writeLog(
- "%s\n",
- pht(
- 'Config: Did not find local configuration at "%s".',
- $local_path));
- }
- }
-
- return array();
+ final public function hasMetadataDirectory() {
+ return ($this->getMetadataDirectory() !== null);
}
- public function writeLocalArcConfig(array $config) {
- $json_encoder = new PhutilJSON();
- $json = $json_encoder->encodeFormatted($config);
-
- $dir = $this->localMetaDir;
- if (!strlen($dir)) {
- throw new Exception(pht('No working copy to write config into!'));
- }
-
- $local_dir = $dir.DIRECTORY_SEPARATOR.'arc';
- if (!Filesystem::pathExists($local_dir)) {
- Filesystem::createDirectory($local_dir, 0755);
+ final public function getMetadataPath($to_file = null) {
+ if (!$this->hasMetadataDirectory()) {
+ throw new Exception(
+ pht(
+ 'This working copy has no metadata directory, so you can not '.
+ 'resolve metadata paths within it.'));
}
- $config_file = $local_dir.DIRECTORY_SEPARATOR.'config';
- Filesystem::writeFile($config_file, $json);
+ return Filesystem::concatenatePaths(
+ array(
+ $this->getMetadataDirectory(),
+ $to_file,
+ ));
}
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Jul 30, 11:24 AM (23 h, 41 s)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
16432419
Default Alt Text
D19689.id47043.diff (48 KB)
Attached To
Mode
D19689: [Wilds] Rewrite WorkingCopyIdentity in a more modern/modular way
Attached
Detach File
Event Timeline
Log In to Comment