diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -635,6 +635,7 @@ 'DiffusionCommitRevisionReviewersHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionReviewersHeraldField.php', 'DiffusionCommitRevisionSubscribersHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionSubscribersHeraldField.php', 'DiffusionCommitTagsController' => 'applications/diffusion/controller/DiffusionCommitTagsController.php', + 'DiffusionCompareController' => 'applications/diffusion/controller/DiffusionCompareController.php', 'DiffusionConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionConduitAPIMethod.php', 'DiffusionController' => 'applications/diffusion/controller/DiffusionController.php', 'DiffusionCreateCommentConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionCreateCommentConduitAPIMethod.php', @@ -5273,6 +5274,7 @@ 'DiffusionCommitRevisionReviewersHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitRevisionSubscribersHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitTagsController' => 'DiffusionController', + 'DiffusionCompareController' => 'DiffusionController', 'DiffusionConduitAPIMethod' => 'ConduitAPIMethod', 'DiffusionController' => 'PhabricatorController', 'DiffusionCreateCommentConduitAPIMethod' => 'DiffusionConduitAPIMethod', diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php --- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php +++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php @@ -65,6 +65,7 @@ => 'DiffusionCommitTagsController', 'commit/(?P[a-z0-9]+)/edit/' => 'DiffusionCommitEditController', + 'compare/' => 'DiffusionCompareController', 'manage/(?:(?P[^/]+)/)?' => 'DiffusionRepositoryManagePanelsController', 'uri/' => array( diff --git a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php --- a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php @@ -22,6 +22,7 @@ protected function defineCustomParamTypes() { return array( 'commit' => 'required string', + 'against' => 'optional string', 'path' => 'required string', 'offset' => 'required int', 'limit' => 'required int', @@ -43,10 +44,17 @@ $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $commit_hash = $request->getValue('commit'); + $against_hash = $request->getValue('against'); $path = $request->getValue('path'); $offset = $request->getValue('offset'); $limit = $request->getValue('limit'); + if (strlen($against_hash)) { + $commit_range = "${against_hash}..${commit_hash}"; + } else { + $commit_range = $commit_hash; + } + list($stdout) = $repository->execxLocalCommand( 'log '. '--skip=%d '. @@ -56,15 +64,12 @@ $offset, $limit, '%H:%P', - $commit_hash, + $commit_range, // Git omits merge commits if the path is provided, even if it is empty. (strlen($path) ? csprintf('%s', $path) : '')); $lines = explode("\n", trim($stdout)); $lines = array_filter($lines); - if (!$lines) { - return array(); - } $hash_list = array(); $parent_map = array(); @@ -76,6 +81,10 @@ $this->parents = $parent_map; + if (!$hash_list) { + return array(); + } + return DiffusionQuery::loadHistoryForCommitIdentifiers( $hash_list, $drequest); diff --git a/src/applications/diffusion/controller/DiffusionCompareController.php b/src/applications/diffusion/controller/DiffusionCompareController.php new file mode 100644 --- /dev/null +++ b/src/applications/diffusion/controller/DiffusionCompareController.php @@ -0,0 +1,260 @@ +loadDiffusionContext(); + if ($response) { + return $response; + } + + $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + + $content = array(); + + $crumbs = $this->buildCrumbs(array('view' => 'compare')); + + $empty_title = null; + $error_message = null; + + if ($repository->getVersionControlSystem() != + PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) { + $empty_title = pht('Not supported'); + $error_message = pht( + 'This feature is not yet supported for %s repositories.', + $repository->getVersionControlSystem()); + $content[] = id(new PHUIInfoView()) + ->setTitle($empty_title) + ->setSeverity(PHUIInfoView::SEVERITY_WARNING) + ->setErrors(array($error_message)); + } + + $head_ref = $request->getStr('head'); + $against_ref = $request->getStr('against'); + + $refs = id(new DiffusionCachedResolveRefsQuery()) + ->setRepository($repository) + ->withRefs(array($head_ref, $against_ref)) + ->execute(); + + + if (count($refs) == 2) { + $content[] = $this->buildCompareContent($drequest); + } else { + $content[] = $this->buildCompareForm($request, $refs); + } + + + return $this->newPage() + ->setTitle( + array( + $repository->getName(), + $repository->getDisplayName(), + )) + ->setCrumbs($crumbs) + ->appendChild($content); + } + + private function buildCompareForm(AphrontRequest $request, array $resolved) { + $viewer = $this->getViewer(); + + $head_ref = $request->getStr('head'); + $against_ref = $request->getStr('against'); + + if (idx($resolved, $head_ref)) { + $e_head = null; + } else { + $e_head = 'Not Found'; + } + + if (idx($resolved, $against_ref)) { + $e_against = null; + } else { + $e_against = 'Not Found'; + } + + + $form = id(new AphrontFormView()) + ->setUser($viewer) + ->setMethod('GET') + ->appendControl( + id(new AphrontFormTextControl()) + ->setLabel(pht('Head')) + ->setName('head') + ->setError($e_head) + ->setValue($head_ref)) + ->appendControl( + id(new AphrontFormTextControl()) + ->setLabel(pht('Against')) + ->setName('against') + ->setError($e_against) + ->setValue($against_ref)) + ->appendControl( + id(new AphrontFormSubmitControl()) + ->setValue('Compare')); + + return $form; + } + + private function buildCompareContent(DiffusionRequest $drequest) { + $request = $this->getRequest(); + $repository = $drequest->getRepository(); + + $head_ref = $request->getStr('head'); + $against_ref = $request->getStr('against'); + + $content = array(); + + $pager = id(new PHUIPagerView()) + ->readFromRequest($request); + + try { + $history_results = $this->callConduitWithDiffusionRequest( + 'diffusion.historyquery', + array( + 'commit' => $head_ref, + 'against' => $against_ref, + 'path' => $drequest->getPath(), + 'offset' => $pager->getOffset(), + 'limit' => $pager->getPageSize() + 1, + )); + $history = DiffusionPathChange::newFromConduit( + $history_results['pathChanges']); + + $history = $pager->sliceResults($history); + + $history_exception = null; + } catch (Exception $ex) { + $history_results = null; + $history = null; + $history_exception = $ex; + } + + if ($history_results) { + $content[] = $this->buildCompareProperties($drequest, $history_results); + } + $content[] = $this->buildCompareForm( + $request, + array($head_ref => true, $against_ref => true)); + + $content[] = $this->buildHistoryTable( + $history_results, + $history, + $pager, + $history_exception); + + $content[] = $this->renderTablePagerBox($pager); + + return $content; + } + + private function buildCompareProperties($drequest, array $history_results) { + $viewer = $this->getViewer(); + + $request = $this->getRequest(); + $repository = $drequest->getRepository(); + + $head_ref = $request->getStr('head'); + $against_ref = $request->getStr('against'); + + $reverse_uri = $repository->getPathURI( + "compare/?head=${against_ref}&against=${head_ref}"); + $actions = id(new PhabricatorActionListView()); + $actions->setUser($viewer); + + + $actions->addAction(id(new PhabricatorActionView()) + ->setName(pht('Reverse Comparison')) + ->setHref($reverse_uri) + ->setIcon('fa-list')); + + $view = id(new PHUIPropertyListView()) + ->setUser($viewer) + ->setActionList($actions); + + $readme = + 'These are the commits that are reachable from **Head** commit and not '. + 'from the **Against** commit.'; + $readme = new PHUIRemarkupView($viewer, $readme); + $view->addTextContent($readme); + + $view->addProperty(pht('Head'), $head_ref); + $view->addProperty(pht('Against'), $against_ref); + + $header = id(new PHUIHeaderView()) + ->setUser($viewer) + ->setPolicyObject($drequest->getRepository()); + + $box = id(new PHUIObjectBoxView()) + ->setUser($viewer) + ->setHeader($header) + ->addPropertyList($view); + return $box; + } + + private function buildHistoryTable( + $history_results, + $history, + $pager, + $history_exception) { + + $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()); + } + } + + if (!$history) { + return $this->renderStatusMessage( + pht('Up to date'), + new PHUIRemarkupView( + $viewer, + pht( + '**Against** is strictly ahead of **Head** '. + '- no commits are missing.'))); + } + + $history_table = id(new DiffusionHistoryTableView()) + ->setUser($viewer) + ->setDiffusionRequest($drequest) + ->setHistory($history); + + // TODO: Super sketchy? + $history_table->loadRevisions(); + + if ($history_results) { + $history_table->setParents($history_results['parents']); + $history_table->setIsHead(!$pager->getOffset()); + $history_table->setIsTail(!$pager->getHasMorePages()); + } + + // TODO also expose diff. + + $panel = new PHUIObjectBoxView(); + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Missing Commits')); + $panel->setHeader($header); + $panel->setTable($history_table); + + return $panel; + } +} diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php --- a/src/applications/diffusion/controller/DiffusionController.php +++ b/src/applications/diffusion/controller/DiffusionController.php @@ -213,6 +213,9 @@ case 'change': $view_name = pht('Change'); break; + case 'compare': + $view_name = pht('Compare'); + break; } $crumb = id(new PHUICrumbView())