public function execute() { global $wgUser; // Before doing anything at all, let's check permissions if (!$wgUser->isAllowed('codereview-use')) { $this->dieUsage('You don\'t have permission to update code', 'permissiondenied'); } $params = $this->extractRequestParams(); $repo = CodeRepository::newFromName($params['repo']); if (!$repo) { $this->dieUsage("Invalid repo ``{$params['repo']}''", 'invalidrepo'); } $svn = SubversionAdaptor::newFromRepo($repo->getPath()); $lastStoredRev = $repo->getLastStoredRev(); if ($lastStoredRev >= $params['rev']) { // Nothing to do, we're up to date. // Return an empty result $this->getResult()->addValue(null, $this->getModuleName(), array()); return; } // FIXME: this could be a lot? $log = $svn->getLog('', $lastStoredRev + 1, $params['rev']); if (!$log) { // FIXME: When and how often does this happen? // Should we use dieUsage() here instead? ApiBase::dieDebug(__METHOD__, 'Something awry...'); } $result = array(); $revs = array(); foreach ($log as $data) { $codeRev = CodeRevision::newFromSvn($repo, $data); $codeRev->save(); $result[] = array('id' => $codeRev->getId(), 'author' => $codeRev->getAuthor(), 'timestamp' => wfTimestamp(TS_ISO_8601, $codeRev->getTimestamp()), 'message' => $codeRev->getMessage()); $revs[] = $codeRev; } // Cache the diffs if there are a only a few. // Mainly for WMF post-commit ping hook... if (count($revs) <= 2) { foreach ($revs as $codeRev) { $repo->setDiffCache($codeRev); // trigger caching } } $this->getResult()->setIndexedTagName($result, 'rev'); $this->getResult()->addValue(null, $this->getModuleName(), $result); }
function getContent() { if (is_null($this->mRepository)) { return wfMsgHtml('codebrowse-not-found', $this->mRepoName); } $this->svn = SubversionAdaptor::newFromRepo($this->mRepository->getPath()); $contents = $this->svn->getDirList($this->mBasePath); if (!is_array($contents)) { return wfMsgHtml('codebrowse-not-found', $this->mPath); } // FIXME if (count($contents) == 1 && $contents[0]['type'] == 'file') { return $this->showFile(); } else { if (substr($this->mPath, -1) !== '/') { $this->mPath .= '/'; $this->mBasePath .= '/'; } return $this->listDir($contents); } }
function __construct($repo, $proxy, $timeout = 30) { parent::__construct($repo); $this->mProxy = $proxy; $this->mTimeout = $timeout; }
/** * Import a repository in the local database. * @param $repoName String Local name of repository * @param $start Int Revision to begin the import from (Default: null, means last stored revision); */ private function importRepo($repoName, $start = null, $cacheSize = 0) { global $wgCodeReviewImportBatchSize; static $adaptorReported = false; $repo = CodeRepository::newFromName($repoName); if (!$repo) { $this->error("Invalid repo {$repoName}"); return; } $svn = SubversionAdaptor::newFromRepo($repo->getPath()); if (!$adaptorReported) { $this->output("Using " . get_class($svn) . " adaptor\n"); $adaptorReported = true; } $this->output("IMPORT FROM REPO: {$repoName}\n"); $lastStoredRev = $repo->getLastStoredRev(); $this->output("Last stored revision: {$lastStoredRev}\n"); $chunkSize = $wgCodeReviewImportBatchSize; $startTime = microtime(true); $revCount = 0; $start = $start !== null ? intval($start) : $lastStoredRev + 1; /* * FIXME: when importing only a part of a repository, the given path * might not have been created with revision 1. For example, the * mediawiki '/trunk/phase3' got created with r1284. */ if ($start > $lastStoredRev + 1) { $this->error("Invalid starting point. r{$start} is beyond last stored revision: r" . ($lastStoredRev + 1)); return; } $this->output("Syncing from r{$start} to HEAD...\n"); if (!$svn->canConnect()) { $this->error("Unable to connect to repository."); return; } while (true) { $log = $svn->getLog('', $start, $start + $chunkSize - 1); if (empty($log)) { # Repo seems to give a blank when max rev is invalid, which # stops new revisions from being added. Try to avoid this # by trying less at a time from the last point. if ($chunkSize <= 1) { break; // done! } $chunkSize = max(1, floor($chunkSize / 4)); continue; } else { $start += $chunkSize; } if (!is_array($log)) { var_dump($log); // @TODO: cleanup :) $this->error('Log entry is not an array! See content above.', true); } foreach ($log as $data) { $revCount++; $delta = microtime(true) - $startTime; $revSpeed = $revCount / $delta; $codeRev = CodeRevision::newFromSvn($repo, $data); $codeRev->save(); $this->output(sprintf("%d %s %s (%0.1f revs/sec)\n", $codeRev->getId(), wfTimestamp(TS_DB, $codeRev->getTimestamp()), $codeRev->getAuthor(), $revSpeed)); } wfWaitForSlaves(); } if ($cacheSize !== 0) { $dbw = wfGetDB(DB_MASTER); $options = array('ORDER BY' => 'cr_id DESC'); if ($cacheSize == "all") { $this->output("Pre-caching all uncached diffs...\n"); } else { if ($cacheSize == 1) { $this->output("Pre-caching the latest diff...\n"); } else { $this->output("Pre-caching the latest {$cacheSize} diffs...\n"); } $options['LIMIT'] = $cacheSize; } // Get all rows for this repository that don't already have a diff filled in. // This is LIMITed according to the $cacheSize setting, above, so only the // rows that we plan to pre-cache are returned. // TODO: This was optimised in order to skip rows that already have a diff, // which is mostly what is required, but there may be situations where // you want to re-calculate diffs (e.g. if $wgCodeReviewMaxDiffPaths // changes). If these situations arise we will either want to revert // this behaviour, or add a --force flag or something. $res = $dbw->select('code_rev', 'cr_id', array('cr_repo_id' => $repo->getId(), 'cr_diff IS NULL OR cr_diff = ""'), __METHOD__, $options); foreach ($res as $row) { $repo->getRevision($row->cr_id); $diff = $repo->getDiff($row->cr_id); // trigger caching $msg = "Diff r{$row->cr_id} "; if (is_integer($diff)) { $msg .= "Skipped: " . CodeRepository::getDiffErrorMessage($diff); } else { $msg .= "done"; } $this->output($msg . "\n"); } } else { $this->output("Pre-caching skipped.\n"); } $this->output("Done!\n"); }
/** * Set diff cache (for import operations) * @param $codeRev CodeRevision */ public function setDiffCache(CodeRevision $codeRev) { global $wgMemc; wfProfileIn(__METHOD__); $rev1 = $codeRev->getId() - 1; $rev2 = $codeRev->getId(); $svn = SubversionAdaptor::newFromRepo($this->path); $data = $svn->getDiff('', $rev1, $rev2); // Store to cache $key = wfMemcKey('svn', md5($this->path), 'diff', $rev1, $rev2); $wgMemc->set($key, $data, 3600 * 24 * 3); // Permanent DB storage $storedData = $data; $flags = Revision::compressRevisionText($storedData); $dbw = wfGetDB(DB_MASTER); $dbw->update('code_rev', array('cr_diff' => $storedData, 'cr_flags' => $flags), array('cr_repo_id' => $this->id, 'cr_id' => $codeRev->getId()), __METHOD__); wfProfileOut(__METHOD__); }