Index: src/applications/diffusion/controller/DiffusionCommitController.php =================================================================== --- src/applications/diffusion/controller/DiffusionCommitController.php +++ src/applications/diffusion/controller/DiffusionCommitController.php @@ -1080,11 +1080,5 @@ return $parser->processCorpus($corpus); } - private function renderStatusMessage($title, $body) { - return id(new AphrontErrorView()) - ->setSeverity(AphrontErrorView::SEVERITY_WARNING) - ->setTitle($title) - ->appendChild($body); - } } Index: src/applications/diffusion/controller/DiffusionController.php =================================================================== --- src/applications/diffusion/controller/DiffusionController.php +++ src/applications/diffusion/controller/DiffusionController.php @@ -442,5 +442,11 @@ return preg_replace('@^/diffusion/[A-Z]+@', '', $request_path); } + protected function renderStatusMessage($title, $body) { + return id(new AphrontErrorView()) + ->setSeverity(AphrontErrorView::SEVERITY_WARNING) + ->setTitle($title) + ->appendChild($body); + } } Index: src/applications/diffusion/controller/DiffusionRepositoryController.php =================================================================== --- src/applications/diffusion/controller/DiffusionRepositoryController.php +++ src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -7,7 +7,8 @@ } public function processRequest() { - $drequest = $this->diffusionRequest; + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); $content = array(); @@ -15,106 +16,108 @@ $content[] = $crumbs; $content[] = $this->buildPropertiesTable($drequest->getRepository()); + $phids = array(); - $history_results = $this->callConduitWithDiffusionRequest( - 'diffusion.historyquery', - array( - 'commit' => $drequest->getCommit(), - 'path' => $drequest->getPath(), - 'offset' => 0, - 'limit' => 15)); - $history = DiffusionPathChange::newFromConduit( - $history_results['pathChanges']); - - $browse_results = DiffusionBrowseResultSet::newFromConduit( - $this->callConduitWithDiffusionRequest( - 'diffusion.browsequery', + try { + $history_results = $this->callConduitWithDiffusionRequest( + 'diffusion.historyquery', array( - 'path' => $drequest->getPath(), 'commit' => $drequest->getCommit(), - ))); - $browse_paths = $browse_results->getPaths(); - - $phids = array(); - foreach ($history as $item) { - $data = $item->getCommitData(); - if ($data) { - if ($data->getCommitDetail('authorPHID')) { - $phids[$data->getCommitDetail('authorPHID')] = true; - } - if ($data->getCommitDetail('committerPHID')) { - $phids[$data->getCommitDetail('committerPHID')] = true; + 'path' => $drequest->getPath(), + 'offset' => 0, + 'limit' => 15)); + $history = DiffusionPathChange::newFromConduit( + $history_results['pathChanges']); + + foreach ($history as $item) { + $data = $item->getCommitData(); + if ($data) { + if ($data->getCommitDetail('authorPHID')) { + $phids[$data->getCommitDetail('authorPHID')] = true; + } + if ($data->getCommitDetail('committerPHID')) { + $phids[$data->getCommitDetail('committerPHID')] = true; + } } } + } catch (Exception $ex) { + $history_results = null; + $history = null; + $history_exception = $ex; } - foreach ($browse_paths as $item) { - $data = $item->getLastCommitData(); - if ($data) { - if ($data->getCommitDetail('authorPHID')) { - $phids[$data->getCommitDetail('authorPHID')] = true; - } - if ($data->getCommitDetail('committerPHID')) { - $phids[$data->getCommitDetail('committerPHID')] = true; + try { + $browse_results = DiffusionBrowseResultSet::newFromConduit( + $this->callConduitWithDiffusionRequest( + 'diffusion.browsequery', + array( + 'path' => $drequest->getPath(), + 'commit' => $drequest->getCommit(), + ))); + $browse_paths = $browse_results->getPaths(); + + foreach ($browse_paths as $item) { + $data = $item->getLastCommitData(); + if ($data) { + if ($data->getCommitDetail('authorPHID')) { + $phids[$data->getCommitDetail('authorPHID')] = true; + } + if ($data->getCommitDetail('committerPHID')) { + $phids[$data->getCommitDetail('committerPHID')] = true; + } } } + + } catch (Exception $ex) { + $browse_results = null; + $browse_paths = null; + $browse_exception = $ex; } + $phids = array_keys($phids); $handles = $this->loadViewerHandles($phids); - $readme = $this->callConduitWithDiffusionRequest( - 'diffusion.readmequery', - array( - 'paths' => $browse_results->getPathDicts() + if ($browse_results) { + $readme = $this->callConduitWithDiffusionRequest( + 'diffusion.readmequery', + array( + 'paths' => $browse_results->getPathDicts() )); + } else { + $readme = null; + } - $history_table = new DiffusionHistoryTableView(); - $history_table->setUser($this->getRequest()->getUser()); - $history_table->setDiffusionRequest($drequest); - $history_table->setHandles($handles); - $history_table->setHistory($history); - $history_table->loadRevisions(); - $history_table->setParents($history_results['parents']); - $history_table->setIsHead(true); - - $callsign = $drequest->getRepository()->getCallsign(); - $all = phutil_tag( - 'a', - array( - 'href' => $drequest->generateURI( - array( - 'action' => 'history', - )), - ), - pht('View Full Commit History')); - - $panel = new AphrontPanelView(); - $panel->setHeader(pht("Recent Commits · %s", $all)); - $panel->appendChild($history_table); - $panel->setNoBackground(); - - $content[] = $panel; - - - $browse_table = new DiffusionBrowseTableView(); - $browse_table->setDiffusionRequest($drequest); - $browse_table->setHandles($handles); - $browse_table->setPaths($browse_paths); - $browse_table->setUser($this->getRequest()->getUser()); - - $browse_panel = new AphrontPanelView(); - $browse_panel->setHeader(phutil_tag( - 'a', - array('href' => $drequest->generateURI(array('action' => 'browse'))), - pht('Browse Repository'))); - $browse_panel->appendChild($browse_table); - $browse_panel->setNoBackground(); + $content[] = $this->buildHistoryTable( + $history_results, + $history, + $history_exception, + $handles); - $content[] = $browse_panel; + $content[] = $this->buildBrowseTable( + $browse_results, + $browse_paths, + $browse_exception, + $handles); - $content[] = $this->buildTagListTable($drequest); + try { + $content[] = $this->buildTagListTable($drequest); + } catch (Exception $ex) { + if (!$repository->isImporting()) { + $content[] = $this->renderStatusMessage( + pht('Unable to Load Tags'), + $ex->getMessage()); + } + } - $content[] = $this->buildBranchListTable($drequest); + try { + $content[] = $this->buildBranchListTable($drequest); + } catch (Exception $ex) { + if (!$repository->isImporting()) { + $content[] = $this->renderStatusMessage( + pht('Unable to Load Branches'), + $ex->getMessage()); + } + } if ($readme) { $box = new PHUIBoxView(); @@ -145,6 +148,15 @@ ->setUser($user) ->setPolicyObject($repository); + if (!$repository->isTracked()) { + $header->setStatus('policy-noone', '', pht('Inactive')); + } else if ($repository->isImporting()) { + $header->setStatus('time', 'red', pht('Importing...')); + } else { + $header->setStatus('oh-ok', '', pht('Active')); + } + + $actions = $this->buildActionList($repository); $view = id(new PHUIPropertyListView()) @@ -333,4 +345,109 @@ return $view; } + private function buildHistoryTable( + $history_results, + $history, + $history_exception, + array $handles) { + + $request = $this->getRequest(); + $viewer = $request->getUser(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + + if ($history_exception) { + if ($repository->isImporting()) { + return $this->renderStatusMessage( + pht('Still Importing...'), + pht( + 'This repository is still importing. History is not yet '. + 'available.')); + } else { + return $this->renderStatusMessage( + pht('Unable to Retrieve History'), + $history_exception->getMessage()); + } + } + + $history_table = id(new DiffusionHistoryTableView()) + ->setUser($viewer) + ->setDiffusionRequest($drequest) + ->setHandles($handles) + ->setHistory($history); + + // TODO: Super sketchy. + $history_table->loadRevisions(); + + if ($history_results) { + $history_table->setParents($history_results['parents']); + } + + $history_table->setIsHead(true); + + $callsign = $drequest->getRepository()->getCallsign(); + $all = phutil_tag( + 'a', + array( + 'href' => $drequest->generateURI( + array( + 'action' => 'history', + )), + ), + pht('View Full Commit History')); + + $panel = new AphrontPanelView(); + $panel->setHeader(pht("Recent Commits · %s", $all)); + $panel->appendChild($history_table); + $panel->setNoBackground(); + + return $panel; + } + + private function buildBrowseTable( + $browse_results, + $browse_paths, + $browse_exception, + array $handles) { + + $request = $this->getRequest(); + $viewer = $request->getUser(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + + if ($browse_exception) { + if ($repository->isImporting()) { + // The history table renders a useful message. + return null; + } else { + return $this->renderStatusMessage( + pht('Unable to Retrieve Paths'), + $browse_exception->getMessage()); + } + } + + $browse_table = id(new DiffusionBrowseTableView()) + ->setUser($viewer) + ->setDiffusionRequest($drequest) + ->setHandles($handles); + if ($browse_paths) { + $browse_table->setPaths($browse_paths); + } else { + $browse_table->setPaths(array()); + } + + $browse_uri = $drequest->generateURI(array('action' => 'browse')); + + $browse_panel = new AphrontPanelView(); + $browse_panel->setHeader( + phutil_tag( + 'a', + array('href' => $browse_uri), + pht('Browse Repository'))); + $browse_panel->appendChild($browse_table); + $browse_panel->setNoBackground(); + + return $browse_panel; + } + } Index: src/applications/diffusion/controller/DiffusionRepositoryCreateController.php =================================================================== --- src/applications/diffusion/controller/DiffusionRepositoryCreateController.php +++ src/applications/diffusion/controller/DiffusionRepositoryCreateController.php @@ -95,6 +95,10 @@ // transactions to apply this change. $repository->setCallsign($callsign); + // Put the repository in "Importing" mode until we finish + // parsing it. + $repository->setDetail('importing', true); + $xactions[] = id(clone $template) ->setTransactionType($type_name) ->setNewValue( Index: src/applications/diffusion/view/DiffusionHistoryTableView.php =================================================================== --- src/applications/diffusion/view/DiffusionHistoryTableView.php +++ src/applications/diffusion/view/DiffusionHistoryTableView.php @@ -21,6 +21,8 @@ $commit_phids[] = $item->getCommit()->getPHID(); } } + + // TODO: Get rid of this. $this->revisions = id(new DifferentialRevision()) ->loadIDsByCommitPHIDs($commit_phids); return $this; Index: src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php =================================================================== --- src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php +++ src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php @@ -201,28 +201,41 @@ public function discoverRepository(PhabricatorRepository $repository) { $vcs = $repository->getVersionControlSystem(); + + $result = null; + $refs = null; switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: - return $this->executeGitDiscover($repository); + $result = $this->executeGitDiscover($repository); + break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $refs = $this->getDiscoveryEngine($repository) ->discoverCommits(); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: - return $this->executeHgDiscover($repository); + $result = $this->executeHgDiscover($repository); + break; default: throw new Exception("Unknown VCS '{$vcs}'!"); } - foreach ($refs as $ref) { - $this->recordCommit( - $repository, - $ref->getIdentifier(), - $ref->getEpoch(), - $ref->getBranch()); + if ($refs !== null) { + foreach ($refs as $ref) { + $this->recordCommit( + $repository, + $ref->getIdentifier(), + $ref->getEpoch(), + $ref->getBranch()); + } } - return (bool)count($refs); + $this->checkIfRepositoryIsFullyImported($repository); + + if ($refs !== null) { + return (bool)count($refs); + } else { + return $result; + } } private function getDiscoveryEngine(PhabricatorRepository $repository) { @@ -456,7 +469,39 @@ return $repository->getID().':'.$commit_identifier; } + private function checkIfRepositoryIsFullyImported( + PhabricatorRepository $repository) { + // Check if the repository has the "Importing" flag set. We want to clear + // the flag if we can. + $importing = $repository->getDetail('importing'); + if (!$importing) { + // This repository isn't marked as "Importing", so we're done. + return; + } + + // Look for any commit which hasn't imported. + $unparsed_commit = queryfx_one( + $repository->establishConnection('r'), + 'SELECT * FROM %T WHERE repositoryID = %d AND importStatus != %d', + id(new PhabricatorRepositoryCommit())->getTableName(), + $repository->getID(), + PhabricatorRepositoryCommit::IMPORTED_ALL); + if ($unparsed_commit) { + // We found a commit which still needs to import, so we can't clear the + // flag. + return; + } + + // Clear the "importing" flag. + $repository->openTransaction(); + $repository->beginReadLocking(); + $repository = $repository->reload(); + $repository->setDetail('importing', false); + $repository->save(); + $repository->endReadLocking(); + $repository->saveTransaction(); + } /* -( Git Implementation )------------------------------------------------- */ @@ -488,6 +533,12 @@ $stdout, $only_this_remote = DiffusionBranchInformation::DEFAULT_GIT_REMOTE); + if (!$branches) { + // This repository has no branches at all, so we don't need to do + // anything. Generally, this means the repository is empty. + return; + } + $callsign = $repository->getCallsign(); $tracked_something = false; Index: src/applications/repository/storage/PhabricatorRepository.php =================================================================== --- src/applications/repository/storage/PhabricatorRepository.php +++ src/applications/repository/storage/PhabricatorRepository.php @@ -192,24 +192,32 @@ } public function execLocalCommand($pattern /* , $arg, ... */) { + $this->assertLocalExists(); + $args = func_get_args(); $args = $this->formatLocalCommand($args); return call_user_func_array('exec_manual', $args); } public function execxLocalCommand($pattern /* , $arg, ... */) { + $this->assertLocalExists(); + $args = func_get_args(); $args = $this->formatLocalCommand($args); return call_user_func_array('execx', $args); } public function getLocalCommandFuture($pattern /* , $arg, ... */) { + $this->assertLocalExists(); + $args = func_get_args(); $args = $this->formatLocalCommand($args); return newv('ExecFuture', $args); } public function passthruLocalCommand($pattern /* , $arg, ... */) { + $this->assertLocalExists(); + $args = func_get_args(); $args = $this->formatLocalCommand($args); return call_user_func_array('phutil_passthru', $args); @@ -433,6 +441,10 @@ } public function shouldAutocloseBranch($branch) { + if ($this->isImporting()) { + return false; + } + if ($this->getDetail('disable-autoclose', false)) { return false; } @@ -486,6 +498,10 @@ return 'r'.$this->getCallsign().$short_identifier; } + public function isImporting() { + return (bool)$this->getDetail('importing', false); + } + /* -( Repository URI Management )------------------------------------------ */ @@ -750,6 +766,27 @@ } + /** + * Raise more useful errors when there are basic filesystem problems. + */ + private function assertLocalExists() { + switch ($this->getVersionControlSystem()) { + case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: + if (!$this->isHosted()) { + // For non-hosted SVN repositories, we don't expect a local directory + // to exist. + return; + } + break; + } + + $local = $this->getLocalPath(); + Filesystem::assertExists($local); + Filesystem::assertIsDirectory($local); + Filesystem::assertReadable($local); + } + + /* -( PhabricatorPolicyInterface )----------------------------------------- */ Index: src/applications/repository/worker/PhabricatorRepositoryCommitHeraldWorker.php =================================================================== --- src/applications/repository/worker/PhabricatorRepositoryCommitHeraldWorker.php +++ src/applications/repository/worker/PhabricatorRepositoryCommitHeraldWorker.php @@ -24,6 +24,13 @@ PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { + // Don't take any actions on an importing repository. Principally, this + // avoids generating thousands of audits or emails when you import an + // established repository on an existing install. + if ($repository->isImporting()) { + return; + } + $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere( 'commitID = %d', $commit->getID());