diff --git a/scripts/repository/discover.php b/scripts/repository/discover.php index 51a02f9618..8e74bc63ca 100755 --- a/scripts/repository/discover.php +++ b/scripts/repository/discover.php @@ -1,51 +1,54 @@ #!/usr/bin/env php setTagline('manually discover working copies'); $args->setSynopsis(<<parseStandardArguments(); $args->parse( array( array( 'name' => 'repositories', 'wildcard' => true, ), )); $repo_names = $args->getArg('repositories'); if (!$repo_names) { echo "Specify one or more repositories to pull, by callsign or PHID.\n"; exit(1); } $repos = PhabricatorRepository::loadAllByPHIDOrCallsign($repo_names); foreach ($repos as $repo) { $callsign = $repo->getCallsign(); echo "Discovering '{$callsign}'...\n"; - PhabricatorRepositoryPullLocalDaemon::discoverRepository($repo); + + $daemon = new PhabricatorRepositoryPullLocalDaemon(array()); + $daemon->setVerbose(true); + $daemon->discoverRepository($repo); } echo "Done.\n"; diff --git a/scripts/repository/pull.php b/scripts/repository/pull.php index 668c005f7e..53abee38eb 100755 --- a/scripts/repository/pull.php +++ b/scripts/repository/pull.php @@ -1,51 +1,54 @@ #!/usr/bin/env php setTagline('manually pull working copies'); $args->setSynopsis(<<parseStandardArguments(); $args->parse( array( array( 'name' => 'repositories', 'wildcard' => true, ), )); $repo_names = $args->getArg('repositories'); if (!$repo_names) { echo "Specify one or more repositories to pull, by callsign or PHID.\n"; exit(1); } $repos = PhabricatorRepository::loadAllByPHIDOrCallsign($repo_names); foreach ($repos as $repo) { $callsign = $repo->getCallsign(); echo "Pulling '{$callsign}'...\n"; - PhabricatorRepositoryPullLocalDaemon::pullRepository($repo); + + $daemon = new PhabricatorRepositoryPullLocalDaemon(array()); + $daemon->setVerbose(true); + $daemon->pullRepository($repo); } echo "Done.\n"; diff --git a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php index fef61a6eae..4f50450a10 100644 --- a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php +++ b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php @@ -1,888 +1,911 @@ getArgv(); array_unshift($argv, __CLASS__); $args = new PhutilArgumentParser($argv); $args->parse( array( array( 'name' => 'no-discovery', 'help' => 'Pull only, without discovering commits.', ), array( 'name' => 'not', 'param' => 'repository', 'repeat' => true, 'help' => 'Do not pull __repository__.', ), array( 'name' => 'repositories', 'wildcard' => true, 'help' => 'Pull specific __repositories__ instead of all.', ), )); $no_discovery = $args->getArg('no-discovery'); $repo_names = $args->getArg('repositories'); $exclude_names = $args->getArg('not'); // Each repository has an individual pull frequency; after we pull it, // wait that long to pull it again. When we start up, try to pull everything // serially. $retry_after = array(); $min_sleep = 15; while (true) { $repositories = $this->loadRepositories($repo_names); if ($exclude_names) { $exclude = $this->loadRepositories($exclude_names); $repositories = array_diff_key($repositories, $exclude); } // Shuffle the repositories, then re-key the array since shuffle() // discards keys. This is mostly for startup, we'll use soft priorities // later. shuffle($repositories); $repositories = mpull($repositories, null, 'getID'); // If any repositories were deleted, remove them from the retry timer map // so we don't end up with a retry timer that never gets updated and // causes us to sleep for the minimum amount of time. $retry_after = array_select_keys( $retry_after, array_keys($repositories)); // Assign soft priorities to repositories based on how frequently they // should pull again. asort($retry_after); $repositories = array_select_keys( $repositories, array_keys($retry_after)) + $repositories; foreach ($repositories as $id => $repository) { $after = idx($retry_after, $id, 0); if ($after > time()) { continue; } $tracked = $repository->isTracked(); if (!$tracked) { continue; } try { - self::pullRepository($repository); + $this->pullRepository($repository); if (!$no_discovery) { // TODO: It would be nice to discover only if we pulled something, // but this isn't totally trivial. - self::discoverRepository($repository); + $this->discoverRepository($repository); } $sleep_for = $repository->getDetail('pull-frequency', $min_sleep); $retry_after[$id] = time() + $sleep_for; } catch (Exception $ex) { $retry_after[$id] = time() + $min_sleep; phlog($ex); } $this->stillWorking(); } if ($retry_after) { $sleep_until = max(min($retry_after), time() + $min_sleep); } else { $sleep_until = time() + $min_sleep; } $this->sleep($sleep_until - time()); } } /** * @task pull */ protected function loadRepositories(array $names) { if (!count($names)) { return id(new PhabricatorRepository())->loadAll(); } else { return PhabricatorRepository::loadAllByPHIDOrCallsign($names); } } /** * @task pull */ - public static function pullRepository(PhabricatorRepository $repository) { + public function pullRepository(PhabricatorRepository $repository) { $vcs = $repository->getVersionControlSystem(); $is_svn = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN); $is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT); $is_hg = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL); if ($is_svn) { return; } $callsign = $repository->getCallsign(); if (!$is_git && !$is_hg) { throw new Exception( "Unknown VCS '{$vcs}' for repository '{$callsign}'!"); } $local_path = $repository->getDetail('local-path'); if (!$local_path) { throw new Exception( "No local path is available for repository '{$callsign}'."); } if (!Filesystem::pathExists($local_path)) { $dirname = dirname($local_path); if (!Filesystem::pathExists($dirname)) { Filesystem::createDirectory($dirname, 0755, $recursive = true); } if ($is_git) { - return self::executeGitCreate($repository, $local_path); + return $this->executeGitCreate($repository, $local_path); } else if ($is_hg) { - return self::executeHgCreate($repository, $local_path); + return $this->executeHgCreate($repository, $local_path); } } else { if ($is_git) { - return self::executeGitUpdate($repository, $local_path); + return $this->executeGitUpdate($repository, $local_path); } else if ($is_hg) { - return self::executeHgUpdate($repository, $local_path); + return $this->executeHgUpdate($repository, $local_path); } } } - public static function discoverRepository(PhabricatorRepository $repository) { + public function discoverRepository(PhabricatorRepository $repository) { $vcs = $repository->getVersionControlSystem(); switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: - return self::executeGitDiscover($repository); + return $this->executeGitDiscover($repository); case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - return self::executeSvnDiscover($repository); + return $this->executeSvnDiscover($repository); case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: - return self::executeHgDiscover($repository); + return $this->executeHgDiscover($repository); default: throw new Exception("Unknown VCS '{$vcs}'!"); } } - private static function isKnownCommit( + private function isKnownCommit( PhabricatorRepository $repository, $target) { - if (self::getCache($repository, $target)) { + if ($this->getCache($repository, $target)) { return true; } $commit = id(new PhabricatorRepositoryCommit())->loadOneWhere( 'repositoryID = %s AND commitIdentifier = %s', $repository->getID(), $target); if (!$commit) { return false; } - self::setCache($repository, $target); - while (count(self::$commitCache) > 2048) { - array_shift(self::$commitCache); + $this->setCache($repository, $target); + while (count($this->commitCache) > 2048) { + array_shift($this->commitCache); } return true; } - private static function isKnownCommitOnAnyAutocloseBranch( + private function isKnownCommitOnAnyAutocloseBranch( PhabricatorRepository $repository, $target) { $commit = id(new PhabricatorRepositoryCommit())->loadOneWhere( 'repositoryID = %s AND commitIdentifier = %s', $repository->getID(), $target); if (!$commit) { return false; } $data = $commit->loadCommitData(); if (!$data) { return false; } if ($repository->shouldAutocloseCommit($commit, $data)) { return true; } return false; } - private static function recordCommit( + private function recordCommit( PhabricatorRepository $repository, $commit_identifier, $epoch) { $commit = new PhabricatorRepositoryCommit(); $commit->setRepositoryID($repository->getID()); $commit->setCommitIdentifier($commit_identifier); $commit->setEpoch($epoch); try { $commit->save(); $event = new PhabricatorTimelineEvent( 'cmit', array( 'id' => $commit->getID(), )); $event->recordEvent(); - self::insertTask($repository, $commit); + $this->insertTask($repository, $commit); queryfx( $repository->establishConnection('w'), 'INSERT INTO %T (repositoryID, size, lastCommitID, epoch) VALUES (%d, 1, %d, %d) ON DUPLICATE KEY UPDATE size = size + 1, lastCommitID = IF(VALUES(epoch) > epoch, VALUES(lastCommitID), lastCommitID), epoch = IF(VALUES(epoch) > epoch, VALUES(epoch), epoch)', PhabricatorRepository::TABLE_SUMMARY, $repository->getID(), $commit->getID(), $epoch); - self::setCache($repository, $commit_identifier); + $this->setCache($repository, $commit_identifier); } catch (AphrontQueryDuplicateKeyException $ex) { // Ignore. This can happen because we discover the same new commit // more than once when looking at history, or because of races or // data inconsistency or cosmic radiation; in any case, we're still // in a good state if we ignore the failure. - self::setCache($repository, $commit_identifier); + $this->setCache($repository, $commit_identifier); } } - private static function updateCommit( + private function updateCommit( PhabricatorRepository $repository, $commit_identifier, $branch) { $commit = id(new PhabricatorRepositoryCommit())->loadOneWhere( 'repositoryID = %s AND commitIdentifier = %s', $repository->getID(), $commit_identifier); if (!$commit) { // This can happen if the phabricator DB doesn't have the commit info, // or the commit is so big that phabricator couldn't parse it. In this // case we just ignore it. return; } $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere( 'commitID = %d', $commit->getID()); if (!$data) { $data = new PhabricatorRepositoryCommitData(); $data->setCommitID($commit->getID()); } $branches = $data->getCommitDetail('seenOnBranches', array()); $branches[] = $branch; $data->setCommitDetail('seenOnBranches', $branches); $data->save(); - self::insertTask( + $this->insertTask( $repository, $commit, array( 'only' => true )); } - private static function insertTask( + private function insertTask( PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit, $data = array()) { $vcs = $repository->getVersionControlSystem(); switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $class = 'PhabricatorRepositoryGitCommitMessageParserWorker'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $class = 'PhabricatorRepositorySvnCommitMessageParserWorker'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $class = 'PhabricatorRepositoryMercurialCommitMessageParserWorker'; break; default: throw new Exception("Unknown repository type '{$vcs}'!"); } $task = new PhabricatorWorkerTask(); $task->setTaskClass($class); $data['commitID'] = $commit->getID(); $task->setData($data); $task->save(); } - private static function setCache( + private function setCache( PhabricatorRepository $repository, $commit_identifier) { - $key = self::getCacheKey($repository, $commit_identifier); - self::$commitCache[$key] = true; + $key = $this->getCacheKey($repository, $commit_identifier); + $this->commitCache[$key] = true; } - private static function getCache( + private function getCache( PhabricatorRepository $repository, $commit_identifier) { - $key = self::getCacheKey($repository, $commit_identifier); - return idx(self::$commitCache, $key, false); + $key = $this->getCacheKey($repository, $commit_identifier); + return idx($this->commitCache, $key, false); } - private static function getCacheKey( + private function getCacheKey( PhabricatorRepository $repository, $commit_identifier) { return $repository->getID().':'.$commit_identifier; } /* -( Git Implementation )------------------------------------------------- */ /** * @task git */ - private static function executeGitCreate( + private function executeGitCreate( PhabricatorRepository $repository, $path) { $repository->execxRemoteCommand( 'clone --origin origin %s %s', $repository->getRemoteURI(), rtrim($path, '/')); } /** * @task git */ - private static function executeGitUpdate( + private function executeGitUpdate( PhabricatorRepository $repository, $path) { // Run a bunch of sanity checks to detect people checking out repositories // inside other repositories, making empty directories, pointing the local // path at some random file or path, etc. list($err, $stdout) = $repository->execLocalCommand( 'rev-parse --show-toplevel'); if ($err) { // Try to raise a more tailored error message in the more common case // of the user creating an empty directory. (We could try to remove it, // but might not be able to, and it's much simpler to raise a good // message than try to navigate those waters.) if (is_dir($path)) { $files = Filesystem::listDirectory($path, $include_hidden = true); if (!$files) { throw new Exception( "Expected to find a git repository at '{$path}', but there ". "is an empty directory there. Remove the directory: the daemon ". "will run 'git clone' for you."); } } throw new Exception( "Expected to find a git repository at '{$path}', but there is ". "a non-repository directory (with other stuff in it) there. Move or ". "remove this directory (or reconfigure the repository to use a ". "different directory), and then either clone a repository yourself ". "or let the daemon do it."); } else { $repo_path = rtrim($stdout, "\n"); if (empty($repo_path)) { throw new Exception( "Expected to find a git repository at '{$path}', but ". "there was no result from `git rev-parse --show-toplevel`. ". "Something is misconfigured or broken. The git repository ". "may be inside a '.git/' directory."); } if (!Filesystem::pathsAreEquivalent($repo_path, $path)) { throw new Exception( "Expected to find repo at '{$path}', but the actual ". "git repository root for this directory is '{$repo_path}'. ". "Something is misconfigured. The repository's 'Local Path' should ". "be set to some place where the daemon can check out a working ". "copy, and should not be inside another git repository."); } } // This is a local command, but needs credentials. $future = $repository->getRemoteCommandFuture('fetch --all --prune'); $future->setCWD($path); $future->resolvex(); } /** * @task git */ - private static function executeGitDiscover( + private function executeGitDiscover( PhabricatorRepository $repository) { list($remotes) = $repository->execxLocalCommand( 'remote show -n origin'); $matches = null; if (!preg_match('/^\s*Fetch URL:\s*(.*?)\s*$/m', $remotes, $matches)) { throw new Exception( "Expected 'Fetch URL' in 'git remote show -n origin'."); } - self::executeGitverifySameOrigin( + self::executeGitVerifySameOrigin( $matches[1], $repository->getRemoteURI(), $repository->getLocalPath()); list($stdout) = $repository->execxLocalCommand( 'branch -r --verbose --no-abbrev'); $branches = DiffusionGitBranchQuery::parseGitRemoteBranchOutput( $stdout, $only_this_remote = DiffusionBranchInformation::DEFAULT_GIT_REMOTE); + $callsign = $repository->getCallsign(); + $tracked_something = false; + + $this->log("Discovering commits in repository '{$callsign}'..."); foreach ($branches as $name => $commit) { + $this->log("Examining branch '{$name}', at {$commit}."); if (!$repository->shouldTrackBranch($name)) { + $this->log("Skipping, branch is untracked."); continue; } $tracked_something = true; - if (self::isKnownCommit($repository, $commit)) { + if ($this->isKnownCommit($repository, $commit)) { + $this->log("Skipping, HEAD is known."); continue; - } else { - self::executeGitDiscoverCommit($repository, $commit); } + + $this->log("Looking for new commits."); + $this->executeGitDiscoverCommit($repository, $commit); } if (!$tracked_something) { $repo_name = $repository->getName(); $repo_callsign = $repository->getCallsign(); throw new Exception( "Repository r{$repo_callsign} '{$repo_name}' has no tracked branches! ". "Verify that your branch filtering settings are correct."); } + + $this->log("Discovering commits on autoclose branches..."); foreach ($branches as $name => $commit) { + $this->log("Examining branch '{$name}', at {$commit}'."); if (!$repository->shouldTrackBranch($name)) { + $this->log("Skipping, branch is untracked."); continue; } if (!$repository->shouldAutocloseBranch($name)) { + $this->log("Skipping, branch is not autoclose."); continue; } - if (self::isKnownCommitOnAnyAutocloseBranch($repository, $commit)) { + if ($this->isKnownCommitOnAnyAutocloseBranch($repository, $commit)) { + $this->log("Skipping, commit is known on an autoclose branch."); continue; } - self::executeGitDiscoverCommit($repository, $commit, $name); + $this->log("Looking for new autoclose commits."); + $this->executeGitDiscoverCommit($repository, $commit, $name); } } /** * @task git */ - private static function executeGitDiscoverCommit( + private function executeGitDiscoverCommit( PhabricatorRepository $repository, $commit, $branch = null) { $discover = array($commit); $insert = array($commit); $seen_parent = array(); while (true) { $target = array_pop($discover); list($parents) = $repository->execxLocalCommand( 'log -n1 --pretty="%%P" %s', $target); $parents = array_filter(explode(' ', trim($parents))); foreach ($parents as $parent) { if (isset($seen_parent[$parent])) { // We end up in a loop here somehow when we parse Arcanist if we // don't do this. TODO: Figure out why and draw a pretty diagram // since it's not evident how parsing a DAG with this causes the // loop to stop terminating. continue; } $seen_parent[$parent] = true; if ($branch !== null) { - $known = self::isKnownCommitOnAnyAutocloseBranch( + $known = $this->isKnownCommitOnAnyAutocloseBranch( $repository, $parent); } else { - $known = self::isKnownCommit($repository, $parent); + $known = $this->isKnownCommit($repository, $parent); } if (!$known) { + $this->log("Discovered commit '{$parent}'."); $discover[] = $parent; $insert[] = $parent; } } if (empty($discover)) { break; } } + $n = count($insert); + if ($branch !== null) { + $this->log("Found {$n} new autoclose commits on branch '{$branch}'."); + } else { + $this->log("Found {$n} new commits."); + } + while (true) { $target = array_pop($insert); list($epoch) = $repository->execxLocalCommand( 'log -n1 --pretty="%%ct" %s', $target); $epoch = trim($epoch); if ($branch !== null) { - self::updateCommit($repository, $target, $branch); + $this->updateCommit($repository, $target, $branch); } else { - self::recordCommit($repository, $target, $epoch); + $this->recordCommit($repository, $target, $epoch); } if (empty($insert)) { break; } } } /** * @task git */ public static function executeGitVerifySameOrigin($remote, $expect, $where) { $remote_uri = PhabricatorRepository::newPhutilURIFromGitURI($remote); $expect_uri = PhabricatorRepository::newPhutilURIFromGitURI($expect); $remote_path = $remote_uri->getPath(); $expect_path = $expect_uri->getPath(); $remote_match = self::executeGitNormalizePath($remote_path); $expect_match = self::executeGitNormalizePath($expect_path); if ($remote_match != $expect_match) { throw new Exception( "Working copy at '{$where}' has a mismatched origin URL. It has ". "origin URL '{$remote}' (with remote path '{$remote_path}'), but the ". "configured URL '{$expect}' (with remote path '{$expect_path}') is ". "expected. Refusing to proceed because this may indicate that the ". "working copy is actually some other repository."); } } /** * @task git */ private static function executeGitNormalizePath($path) { // Strip away trailing "/" and ".git", so similar paths correctly match. $path = rtrim($path, '/'); $path = preg_replace('/\.git$/', '', $path); return $path; } /* -( Mercurial Implementation )------------------------------------------- */ /** * @task hg */ - private static function executeHgCreate( + private function executeHgCreate( PhabricatorRepository $repository, $path) { $repository->execxRemoteCommand( 'clone %s %s', $repository->getRemoteURI(), rtrim($path, '/')); } /** * @task hg */ - private static function executeHgUpdate( + private function executeHgUpdate( PhabricatorRepository $repository, $path) { // This is a local command, but needs credentials. $future = $repository->getRemoteCommandFuture('pull -u'); $future->setCWD($path); try { $future->resolvex(); } catch (CommandException $ex) { $err = $ex->getError(); $stdout = $ex->getStdOut(); // NOTE: Between versions 2.1 and 2.1.1, Mercurial changed the behavior // of "hg pull" to return 1 in case of a successful pull with no changes. // This behavior has been reverted, but users who updated between Feb 1, // 2012 and Mar 1, 2012 will have the erroring version. Do a dumb test // against stdout to check for this possibility. // See: https://github.com/facebook/phabricator/issues/101/ // NOTE: Mercurial has translated versions, which translate this error // string. In a translated version, the string will be something else, // like "aucun changement trouve". There didn't seem to be an easy way // to handle this (there are hard ways but this is not a common problem // and only creates log spam, not application failures). Assume English. // TODO: Remove this once we're far enough in the future that deployment // of 2.1 is exceedingly rare? if ($err == 1 && preg_match('/no changes found/', $stdout)) { return; } else { throw $ex; } } } - private static function executeHgDiscover(PhabricatorRepository $repository) { + private function executeHgDiscover(PhabricatorRepository $repository) { // NOTE: "--debug" gives us 40-character hashes. list($stdout) = $repository->execxLocalCommand('--debug branches'); $branches = ArcanistMercurialParser::parseMercurialBranches($stdout); $got_something = false; foreach ($branches as $name => $branch) { $commit = $branch['rev']; - if (self::isKnownCommit($repository, $commit)) { + if ($this->isKnownCommit($repository, $commit)) { continue; } else { - self::executeHgDiscoverCommit($repository, $commit); + $this->executeHgDiscoverCommit($repository, $commit); $got_something = true; } } return $got_something; } - private static function executeHgDiscoverCommit( + private function executeHgDiscoverCommit( PhabricatorRepository $repository, $commit) { $discover = array($commit); $insert = array($commit); $seen_parent = array(); $stream = new PhabricatorMercurialGraphStream($repository); // For all the new commits at the branch heads, walk backward until we // find only commits we've aleady seen. while ($discover) { $target = array_pop($discover); $parents = $stream->getParents($target); foreach ($parents as $parent) { if (isset($seen_parent[$parent])) { continue; } $seen_parent[$parent] = true; - if (!self::isKnownCommit($repository, $parent)) { + if (!$this->isKnownCommit($repository, $parent)) { $discover[] = $parent; $insert[] = $parent; } } } foreach ($insert as $target) { $epoch = $stream->getCommitDate($target); - self::recordCommit($repository, $target, $epoch); + $this->recordCommit($repository, $target, $epoch); } } /* -( Subversion Implementation )------------------------------------------ */ - private static function executeSvnDiscover( + private function executeSvnDiscover( PhabricatorRepository $repository) { - $uri = self::executeSvnGetBaseSVNLogURI($repository); + $uri = $this->executeSvnGetBaseSVNLogURI($repository); list($xml) = $repository->execxRemoteCommand( 'log --xml --quiet --limit 1 %s@HEAD', $uri); - $results = self::executeSvnParseLogXML($xml); + $results = $this->executeSvnParseLogXML($xml); $commit = head_key($results); $epoch = head($results); - if (self::isKnownCommit($repository, $commit)) { + if ($this->isKnownCommit($repository, $commit)) { return false; } - self::executeSvnDiscoverCommit($repository, $commit, $epoch); + $this->executeSvnDiscoverCommit($repository, $commit, $epoch); return true; } - private static function executeSvnDiscoverCommit( + private function executeSvnDiscoverCommit( PhabricatorRepository $repository, $commit, $epoch) { - $uri = self::executeSvnGetBaseSVNLogURI($repository); + $uri = $this->executeSvnGetBaseSVNLogURI($repository); $discover = array( $commit => $epoch, ); $upper_bound = $commit; $limit = 1; while ($upper_bound > 1 && - !self::isKnownCommit($repository, $upper_bound)) { + !$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 += self::executeSvnParseLogXML($xml); + $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) { - self::recordCommit($repository, $commit, $epoch); + $this->recordCommit($repository, $commit, $epoch); } } - private static function executeSvnParseLogXML($xml) { + 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 static function executeSvnGetBaseSVNLogURI( + private function executeSvnGetBaseSVNLogURI( PhabricatorRepository $repository) { $uri = $repository->getDetail('remote-uri'); $subpath = $repository->getDetail('svn-subpath'); return $uri.$subpath; } }