From e7fde9a77c49349151f3f600b0cdce7a0a36e25a Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 12 May 2013 19:08:37 -0700 Subject: [PATCH] Make repository discovery partially testable Summary: Ref T2784. Begins pulling discovery into Engines and covering it with tests. In particular: - Discovery is currently a one-shot process where we find all the new commits and write them to the database in one go. Split it apart so we find and return the new commits first, then write them to the database separately. This makes things simpler and more testable. - This diff only brings SVN into an engine (and only the "find the commits" part), since it's simpler than Git or Mercurial. - Creates a base Engine class and moves common functionality there. - Restores the `--verbose` flag to `repository pull`. Test Plan: Added unit tests. Ran `bin/repository discover`. Ran `bin/phd debug pulllocal`. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2784 Differential Revision: https://secure.phabricator.com/D5906 --- src/__phutil_library_map__.php | 9 +- .../PhabricatorRepositoryPullLocalDaemon.php | 132 +++---------- .../engine/PhabricatorRepositoryCommitRef.php | 36 ++++ .../PhabricatorRepositoryDiscoveryEngine.php | 173 ++++++++++++++++++ .../engine/PhabricatorRepositoryEngine.php | 62 +++++++ .../PhabricatorRepositoryPullEngine.php | 29 ++- ...abricatorWorkingCopyDiscoveryTestCase.php} | 25 ++- .../PhabricatorWorkingCopyTestCase.php | 12 -- .../engine/__tests__/data/ST.svn.tgz | Bin 7743 -> 8925 bytes ...icatorRepositoryManagementPullWorkflow.php | 5 + .../storage/PhabricatorRepository.php | 12 ++ 11 files changed, 357 insertions(+), 138 deletions(-) create mode 100644 src/applications/repository/engine/PhabricatorRepositoryCommitRef.php create mode 100644 src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php create mode 100644 src/applications/repository/engine/PhabricatorRepositoryEngine.php rename src/applications/repository/{daemon/__tests__/PhabricatorRepositoryPullLocalDaemonTestCase.php => engine/__tests__/PhabricatorWorkingCopyDiscoveryTestCase.php} (79%) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index e7f9b09d86..c6666fb2f6 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1283,13 +1283,16 @@ phutil_register_library_map(array( 'PhabricatorRepositoryCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php', 'PhabricatorRepositoryCommitOwnersWorker' => 'applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php', 'PhabricatorRepositoryCommitParserWorker' => 'applications/repository/worker/PhabricatorRepositoryCommitParserWorker.php', + 'PhabricatorRepositoryCommitRef' => 'applications/repository/engine/PhabricatorRepositoryCommitRef.php', 'PhabricatorRepositoryCommitSearchIndexer' => 'applications/repository/search/PhabricatorRepositoryCommitSearchIndexer.php', 'PhabricatorRepositoryConfigOptions' => 'applications/repository/PhabricatorRepositoryConfigOptions.php', 'PhabricatorRepositoryController' => 'applications/repository/controller/PhabricatorRepositoryController.php', 'PhabricatorRepositoryCreateController' => 'applications/repository/controller/PhabricatorRepositoryCreateController.php', 'PhabricatorRepositoryDAO' => 'applications/repository/storage/PhabricatorRepositoryDAO.php', 'PhabricatorRepositoryDeleteController' => 'applications/repository/controller/PhabricatorRepositoryDeleteController.php', + 'PhabricatorRepositoryDiscoveryEngine' => 'applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php', 'PhabricatorRepositoryEditController' => 'applications/repository/controller/PhabricatorRepositoryEditController.php', + 'PhabricatorRepositoryEngine' => 'applications/repository/engine/PhabricatorRepositoryEngine.php', 'PhabricatorRepositoryGitCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositoryGitCommitChangeParserWorker.php', 'PhabricatorRepositoryGitCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryGitCommitMessageParserWorker.php', 'PhabricatorRepositoryListController' => 'applications/repository/controller/PhabricatorRepositoryListController.php', @@ -1302,7 +1305,6 @@ phutil_register_library_map(array( 'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryMercurialCommitMessageParserWorker.php', 'PhabricatorRepositoryPullEngine' => 'applications/repository/engine/PhabricatorRepositoryPullEngine.php', 'PhabricatorRepositoryPullLocalDaemon' => 'applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php', - 'PhabricatorRepositoryPullLocalDaemonTestCase' => 'applications/repository/daemon/__tests__/PhabricatorRepositoryPullLocalDaemonTestCase.php', 'PhabricatorRepositoryQuery' => 'applications/repository/query/PhabricatorRepositoryQuery.php', 'PhabricatorRepositoryShortcut' => 'applications/repository/storage/PhabricatorRepositoryShortcut.php', 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositorySvnCommitChangeParserWorker.php', @@ -1488,6 +1490,7 @@ phutil_register_library_map(array( 'PhabricatorWorkerTaskDetailController' => 'applications/daemon/controller/PhabricatorWorkerTaskDetailController.php', 'PhabricatorWorkerTaskUpdateController' => 'applications/daemon/controller/PhabricatorWorkerTaskUpdateController.php', 'PhabricatorWorkerTestCase' => 'infrastructure/daemon/workers/__tests__/PhabricatorWorkerTestCase.php', + 'PhabricatorWorkingCopyDiscoveryTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyDiscoveryTestCase.php', 'PhabricatorWorkingCopyPullTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyPullTestCase.php', 'PhabricatorWorkingCopyTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php', 'PhabricatorWorkpanelView' => 'view/layout/PhabricatorWorkpanelView.php', @@ -3014,6 +3017,7 @@ phutil_register_library_map(array( 'PhabricatorRepositoryCreateController' => 'PhabricatorRepositoryController', 'PhabricatorRepositoryDAO' => 'PhabricatorLiskDAO', 'PhabricatorRepositoryDeleteController' => 'PhabricatorRepositoryController', + 'PhabricatorRepositoryDiscoveryEngine' => 'PhabricatorRepositoryEngine', 'PhabricatorRepositoryEditController' => 'PhabricatorRepositoryController', 'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', 'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker', @@ -3025,8 +3029,8 @@ phutil_register_library_map(array( 'PhabricatorRepositoryManagementWorkflow' => 'PhutilArgumentWorkflow', 'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', 'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker', + 'PhabricatorRepositoryPullEngine' => 'PhabricatorRepositoryEngine', 'PhabricatorRepositoryPullLocalDaemon' => 'PhabricatorDaemon', - 'PhabricatorRepositoryPullLocalDaemonTestCase' => 'PhabricatorTestCase', 'PhabricatorRepositoryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorRepositoryShortcut' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', @@ -3197,6 +3201,7 @@ phutil_register_library_map(array( 'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController', 'PhabricatorWorkerTaskUpdateController' => 'PhabricatorDaemonController', 'PhabricatorWorkerTestCase' => 'PhabricatorTestCase', + 'PhabricatorWorkingCopyDiscoveryTestCase' => 'PhabricatorWorkingCopyTestCase', 'PhabricatorWorkingCopyPullTestCase' => 'PhabricatorWorkingCopyTestCase', 'PhabricatorWorkingCopyTestCase' => 'PhabricatorTestCase', 'PhabricatorWorkpanelView' => 'AphrontView', diff --git a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php index ca8739f700..5fc45162f6 100644 --- a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php +++ b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php @@ -31,6 +31,7 @@ final class PhabricatorRepositoryPullLocalDaemon private $commitCache = array(); private $repair; + private $discoveryEngines = array(); public function setRepair($repair) { $this->repair = $repair; @@ -183,14 +184,38 @@ final class PhabricatorRepositoryPullLocalDaemon case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: return $this->executeGitDiscover($repository); case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - return $this->executeSvnDiscover($repository); + $refs = $this->getDiscoveryEngine($repository) + ->discoverCommits(); + break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: return $this->executeHgDiscover($repository); default: throw new Exception("Unknown VCS '{$vcs}'!"); } + + foreach ($refs as $ref) { + $this->recordCommit( + $repository, + $ref->getIdentifier(), + $ref->getEpoch(), + $ref->getBranch()); + } + + return (bool)count($refs); } + private function getDiscoveryEngine(PhabricatorRepository $repository) { + $id = $repository->getID(); + if (empty($this->discoveryEngines[$id])) { + $engine = id(new PhabricatorRepositoryDiscoveryEngine()) + ->setRepository($repository) + ->setVerbose($this->getVerbose()) + ->setRepairMode($this->repair); + + $this->discoveryEngines[$id] = $engine; + } + return $this->discoveryEngines[$id]; + } private function isKnownCommit( PhabricatorRepository $repository, @@ -681,109 +706,4 @@ final class PhabricatorRepositoryPullLocalDaemon } } - -/* -( Subversion Implementation )------------------------------------------ */ - - - private function executeSvnDiscover( - PhabricatorRepository $repository) { - - $uri = $this->executeSvnGetBaseSVNLogURI($repository); - - list($xml) = $repository->execxRemoteCommand( - 'log --xml --quiet --limit 1 %s@HEAD', - $uri); - - $results = $this->executeSvnParseLogXML($xml); - $commit = head_key($results); - $epoch = head($results); - - if ($this->isKnownCommit($repository, $commit)) { - return false; - } - - $this->executeSvnDiscoverCommit($repository, $commit, $epoch); - return true; - } - - private function executeSvnDiscoverCommit( - PhabricatorRepository $repository, - $commit, - $epoch) { - - $uri = $this->executeSvnGetBaseSVNLogURI($repository); - - $discover = array( - $commit => $epoch, - ); - $upper_bound = $commit; - - $limit = 1; - while ($upper_bound > 1 && - !$this->isKnownCommit($repository, $upper_bound)) { - // Find all the unknown commits on this path. Note that we permit - // importing an SVN subdirectory rather than the entire repository, so - // commits may be nonsequential. - list($err, $xml, $stderr) = $repository->execRemoteCommand( - ' log --xml --quiet --limit %d %s@%d', - $limit, - $uri, - $upper_bound - 1); - if ($err) { - if (preg_match('/(path|File) not found/', $stderr)) { - // We've gone all the way back through history and this path was not - // affected by earlier commits. - break; - } else { - throw new Exception("svn log error #{$err}: {$stderr}"); - } - } - $discover += $this->executeSvnParseLogXML($xml); - - $upper_bound = min(array_keys($discover)); - - // Discover 2, 4, 8, ... 256 logs at a time. This allows us to initially - // import large repositories fairly quickly, while pulling only as much - // data as we need in the common case (when we've already imported the - // repository and are just grabbing one commit at a time). - $limit = min($limit * 2, 256); - } - - // NOTE: We do writes only after discovering all the commits so that we're - // never left in a state where we've missed commits -- if the discovery - // script terminates it can always resume and restore the import to a good - // state. This is also why we sort the discovered commits so we can do - // writes forward from the smallest one. - - ksort($discover); - foreach ($discover as $commit => $epoch) { - $this->recordCommit($repository, $commit, $epoch); - } - } - - private function executeSvnParseLogXML($xml) { - $xml = phutil_utf8ize($xml); - - $result = array(); - - $log = new SimpleXMLElement($xml); - foreach ($log->logentry as $entry) { - $commit = (int)$entry['revision']; - $epoch = (int)strtotime((string)$entry->date[0]); - $result[$commit] = $epoch; - } - - return $result; - } - - - private function executeSvnGetBaseSVNLogURI( - PhabricatorRepository $repository) { - - $uri = $repository->getDetail('remote-uri'); - $subpath = $repository->getDetail('svn-subpath'); - - return $uri.$subpath; - } - } diff --git a/src/applications/repository/engine/PhabricatorRepositoryCommitRef.php b/src/applications/repository/engine/PhabricatorRepositoryCommitRef.php new file mode 100644 index 0000000000..c010088a92 --- /dev/null +++ b/src/applications/repository/engine/PhabricatorRepositoryCommitRef.php @@ -0,0 +1,36 @@ +identifier = $identifier; + return $this; + } + + public function getIdentifier() { + return $this->identifier; + } + + public function setEpoch($epoch) { + $this->epoch = $epoch; + return $this; + } + + public function getEpoch() { + return $this->epoch; + } + + public function setBranch($branch) { + $this->branch = $branch; + return $this; + } + + public function getBranch() { + return $this->branch; + } + +} diff --git a/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php b/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php new file mode 100644 index 0000000000..5ee09daf90 --- /dev/null +++ b/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php @@ -0,0 +1,173 @@ +repairMode = $repair_mode; + return $this; + } + + + public function getRepairMode() { + return $this->repairMode; + } + + + /** + * @task discovery + */ + public function discoverCommits() { + $repository = $this->getRepository(); + + $vcs = $repository->getVersionControlSystem(); + switch ($vcs) { + case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: + $refs = $this->discoverSubversionCommits(); + break; +/* + + TODO: Implement these! + + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: + $refs = $this->executeGitDiscovery(); + break; + case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: + $refs = $this->executeMercurialDiscovery(); + break; + +*/ + default: + throw new Exception("Unknown VCS '{$vcs}'!"); + } + + // Mark discovered commits in the cache. + foreach ($refs as $ref) { + $this->commitCache[$ref->getIdentifier()] = true; + } + + return $refs; + } + + +/* -( Discovering Subversion Repositories )-------------------------------- */ + + + /** + * @task svn + */ + private function discoverSubversionCommits() { + $repository = $this->getRepository(); + + $upper_bound = null; + $limit = 1; + $refs = array(); + do { + // Find all the unknown commits on this path. Note that we permit + // importing an SVN subdirectory rather than the entire repository, so + // commits may be nonsequential. + + if ($upper_bound === null) { + $at_rev = 'HEAD'; + } else { + $at_rev = ($upper_bound - 1); + } + + try { + list($xml, $stderr) = $repository->execxRemoteCommand( + 'log --xml --quiet --limit %d %s@%s', + $limit, + $repository->getSubversionBaseURI(), + $at_rev); + } catch (CommandException $ex) { + $stderr = $ex->getStdErr(); + if (preg_match('/(path|File) not found/', $stderr)) { + // We've gone all the way back through history and this path was not + // affected by earlier commits. + break; + } + throw $ex; + } + + $xml = phutil_utf8ize($xml); + $log = new SimpleXMLElement($xml); + foreach ($log->logentry as $entry) { + $identifier = (int)$entry['revision']; + $epoch = (int)strtotime((string)$entry->date[0]); + $refs[$identifier] = id(new PhabricatorRepositoryCommitRef()) + ->setIdentifier($identifier) + ->setEpoch($epoch); + + if ($upper_bound === null) { + $upper_bound = $identifier; + } else { + $upper_bound = min($upper_bound, $identifier); + } + } + + // Discover 2, 4, 8, ... 256 logs at a time. This allows us to initially + // import large repositories fairly quickly, while pulling only as much + // data as we need in the common case (when we've already imported the + // repository and are just grabbing one commit at a time). + $limit = min($limit * 2, 256); + + } while ($upper_bound > 1 && !$this->isKnownCommit($upper_bound)); + + krsort($refs); + while ($refs && $this->isKnownCommit(last($refs)->getIdentifier())) { + array_pop($refs); + } + $refs = array_reverse($refs); + + return $refs; + } + + +/* -( Internals )---------------------------------------------------------- */ + + + private function isKnownCommit($identifier) { + if (isset($this->commitCache[$identifier])) { + return true; + } + + if ($this->repairMode) { + // In repair mode, rediscover the entire repository, ignoring the + // database state. We can hit the local cache above, but if we miss it + // stop the script from going to the database cache. + return false; + } + + $commit = id(new PhabricatorRepositoryCommit())->loadOneWhere( + 'repositoryID = %d AND commitIdentifier = %s', + $this->getRepository()->getID(), + $identifier); + + if (!$commit) { + return false; + } + + $this->commitCache[$identifier] = true; + while (count($this->commitCache) > self::MAX_COMMIT_CACHE_SIZE) { + array_shift($this->commitCache); + } + + return true; + } + +} diff --git a/src/applications/repository/engine/PhabricatorRepositoryEngine.php b/src/applications/repository/engine/PhabricatorRepositoryEngine.php new file mode 100644 index 0000000000..60b8a9584c --- /dev/null +++ b/src/applications/repository/engine/PhabricatorRepositoryEngine.php @@ -0,0 +1,62 @@ +repository = $repository; + return $this; + } + + + /** + * @task config + */ + protected function getRepository() { + if ($this->repository === null) { + throw new Exception("Call setRepository() to provide a repository!"); + } + + return $this->repository; + } + + + /** + * @task config + */ + public function setVerbose($verbose) { + $this->verbose = $verbose; + return $this; + } + + + /** + * @task config + */ + public function getVerbose() { + return $this->verbose; + } + + + /** + * @task internal + */ + protected function log($pattern /* ... */) { + if ($this->getVerbose()) { + $console = PhutilConsole::getConsole(); + $argv = func_get_args(); + call_user_func_array(array($console, 'writeLog'), $argv); + } + return $this; + } + +} diff --git a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php index d295cc7346..9aafcceb87 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php @@ -10,37 +10,28 @@ * @task hg Pulling Mercurial Working Copies * @task internal Internals */ -final class PhabricatorRepositoryPullEngine { - - private $repository; +final class PhabricatorRepositoryPullEngine + extends PhabricatorRepositoryEngine { /* -( Pulling Working Copies )--------------------------------------------- */ - public function setRepository(PhabricatorRepository $repository) { - $this->repository = $repository; - return $this; - } - - private function getRepository() { - return $this->repository; - } - public function pullRepository() { $repository = $this->getRepository(); - if (!$repository) { - throw new Exception("Call setRepository() before pullRepository()!"); - } - $is_hg = false; $is_git = false; $vcs = $repository->getVersionControlSystem(); + $callsign = $repository->getCallsign(); switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: // We never pull a local copy of Subversion repositories. + $this->log( + "Repository '%s' is a Subversion repository, which does not require ". + "a local working copy to be pulled.", + $callsign); return; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $is_git = true; @@ -65,12 +56,18 @@ final class PhabricatorRepositoryPullEngine { } if (!Filesystem::pathExists($local_path)) { + $this->log( + "Creating a new working copy for repository '%s'.", + $callsign); if ($is_git) { $this->executeGitCreate(); } else { $this->executeMercurialCreate(); } } else { + $this->log( + "Updating the working copy for repository '%s'.", + $callsign); if ($is_git) { $this->executeGitUpdate(); } else { diff --git a/src/applications/repository/daemon/__tests__/PhabricatorRepositoryPullLocalDaemonTestCase.php b/src/applications/repository/engine/__tests__/PhabricatorWorkingCopyDiscoveryTestCase.php similarity index 79% rename from src/applications/repository/daemon/__tests__/PhabricatorRepositoryPullLocalDaemonTestCase.php rename to src/applications/repository/engine/__tests__/PhabricatorWorkingCopyDiscoveryTestCase.php index ced1ae2986..03a4708699 100644 --- a/src/applications/repository/daemon/__tests__/PhabricatorRepositoryPullLocalDaemonTestCase.php +++ b/src/applications/repository/engine/__tests__/PhabricatorWorkingCopyDiscoveryTestCase.php @@ -1,7 +1,28 @@ buildPulledRepository('ST'); + + $engine = id(new PhabricatorRepositoryDiscoveryEngine()) + ->setRepository($repo); + + $refs = $engine->discoverCommits($repo); + $this->assertEqual( + array( + 1368319433, + 1368319448, + ), + mpull($refs, 'getEpoch'), + 'Commit Epochs'); + + // The next time through, these should be cached as already discovered. + + $refs = $engine->discoverCommits($repo); + $this->assertEqual(array(), $refs); + } public function testExecuteGitVerifySameOrigin() { $cases = array( diff --git a/src/applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php b/src/applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php index e8da90dde1..a8f160cd94 100644 --- a/src/applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php +++ b/src/applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php @@ -3,8 +3,6 @@ abstract class PhabricatorWorkingCopyTestCase extends PhabricatorTestCase { private $dirs = array(); - private $repos = array(); - private $pulled = array(); protected function getPhabricatorTestCaseConfiguration() { return array( @@ -13,10 +11,6 @@ abstract class PhabricatorWorkingCopyTestCase extends PhabricatorTestCase { } protected function buildBareRepository($callsign) { - if (isset($this->repos[$callsign])) { - return $this->repos[$callsign]; - } - $data_dir = dirname(__FILE__).'/data/'; $types = array( @@ -67,8 +61,6 @@ abstract class PhabricatorWorkingCopyTestCase extends PhabricatorTestCase { $this->dirs[] = $dir; $this->dirs[] = $local; - $this->repos[$callsign] = $repo; - return $repo; } @@ -79,10 +71,6 @@ abstract class PhabricatorWorkingCopyTestCase extends PhabricatorTestCase { protected function buildPulledRepository($callsign) { $repository = $this->buildBareRepository($callsign); - if (isset($this->pulled[$callsign])) { - return $repository; - } - id(new PhabricatorRepositoryPullEngine()) ->setRepository($repository) ->pullRepository(); diff --git a/src/applications/repository/engine/__tests__/data/ST.svn.tgz b/src/applications/repository/engine/__tests__/data/ST.svn.tgz index c7899faeb18daa912b9164a5c6405305a62d065e..a30c2dc4ef8a815314545b229566f6405440d035 100644 GIT binary patch literal 8925 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
m5?wK* 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 literal 7743 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'Pull __repository__, named by callsign or PHID.') ->setArguments( array( + array( + 'name' => 'verbose', + 'help' => 'Show additional debugging information.', + ), array( 'name' => 'repos', 'wildcard' => true, @@ -32,6 +36,7 @@ final class PhabricatorRepositoryManagementPullWorkflow id(new PhabricatorRepositoryPullEngine()) ->setRepository($repo) + ->setVerbose($args->getArg('verbose')) ->pullRepository(); } diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 08eb853b11..94cb122fb1 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -90,6 +90,18 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO return $this->getDetail('local-path'); } + public function getSubversionBaseURI() { + $vcs = $this->getVersionControlSystem(); + if ($vcs != PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) { + throw new Exception("Not a subversion repository!"); + } + + $uri = $this->getDetail('remote-uri'); + $subpath = $this->getDetail('svn-subpath'); + + return $uri.$subpath; + } + public function execRemoteCommand($pattern /* , $arg, ... */) { $args = func_get_args(); $args = $this->formatRemoteCommand($args);