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 @@ -770,6 +770,7 @@ 'DiffusionCommitEditEngine' => 'applications/diffusion/editor/DiffusionCommitEditEngine.php', 'DiffusionCommitFerretEngine' => 'applications/repository/search/DiffusionCommitFerretEngine.php', 'DiffusionCommitFulltextEngine' => 'applications/repository/search/DiffusionCommitFulltextEngine.php', + 'DiffusionCommitGraphView' => 'applications/diffusion/view/DiffusionCommitGraphView.php', 'DiffusionCommitHasPackageEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasPackageEdgeType.php', 'DiffusionCommitHasRevisionEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php', 'DiffusionCommitHasRevisionRelationship' => 'applications/diffusion/relationships/DiffusionCommitHasRevisionRelationship.php', @@ -862,7 +863,6 @@ 'DiffusionGitWireProtocolRef' => 'applications/diffusion/protocol/DiffusionGitWireProtocolRef.php', 'DiffusionGitWireProtocolRefList' => 'applications/diffusion/protocol/DiffusionGitWireProtocolRefList.php', 'DiffusionHistoryController' => 'applications/diffusion/controller/DiffusionHistoryController.php', - 'DiffusionHistoryListView' => 'applications/diffusion/view/DiffusionHistoryListView.php', 'DiffusionHistoryQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php', 'DiffusionHistoryTableView' => 'applications/diffusion/view/DiffusionHistoryTableView.php', 'DiffusionHistoryView' => 'applications/diffusion/view/DiffusionHistoryView.php', @@ -6860,6 +6860,7 @@ 'DiffusionCommitEditEngine' => 'PhabricatorEditEngine', 'DiffusionCommitFerretEngine' => 'PhabricatorFerretEngine', 'DiffusionCommitFulltextEngine' => 'PhabricatorFulltextEngine', + 'DiffusionCommitGraphView' => 'DiffusionView', 'DiffusionCommitHasPackageEdgeType' => 'PhabricatorEdgeType', 'DiffusionCommitHasRevisionEdgeType' => 'PhabricatorEdgeType', 'DiffusionCommitHasRevisionRelationship' => 'DiffusionCommitRelationship', @@ -6955,7 +6956,6 @@ 'DiffusionGitWireProtocolRef' => 'Phobject', 'DiffusionGitWireProtocolRefList' => 'Phobject', 'DiffusionHistoryController' => 'DiffusionController', - 'DiffusionHistoryListView' => 'DiffusionHistoryView', 'DiffusionHistoryQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', 'DiffusionHistoryTableView' => 'DiffusionHistoryView', 'DiffusionHistoryView' => 'DiffusionView', diff --git a/src/applications/diffusion/controller/DiffusionHistoryController.php b/src/applications/diffusion/controller/DiffusionHistoryController.php --- a/src/applications/diffusion/controller/DiffusionHistoryController.php +++ b/src/applications/diffusion/controller/DiffusionHistoryController.php @@ -35,10 +35,31 @@ $history = $pager->sliceResults($history); - $history_list = id(new DiffusionHistoryListView()) + $identifiers = array(); + foreach ($history as $item) { + $identifiers[] = $item->getCommitIdentifier(); + } + + if ($identifiers) { + $commits = id(new DiffusionCommitQuery()) + ->setViewer($viewer) + ->withRepositoryPHIDs(array($repository->getPHID())) + ->withIdentifiers($identifiers) + ->needCommitData(true) + ->needIdentities(true) + ->execute(); + } else { + $commits = array(); + } + + $history_list = id(new DiffusionCommitGraphView()) ->setViewer($viewer) + ->setParents($history_results['parents']) + ->setIsHead(!$pager->getOffset()) + ->setIsTail(!$pager->getHasMorePages()) ->setDiffusionRequest($drequest) - ->setHistory($history); + ->setHistory($history) + ->setCommits($commits); $header = $this->buildHeader($drequest); diff --git a/src/applications/diffusion/view/DiffusionCommitGraphView.php b/src/applications/diffusion/view/DiffusionCommitGraphView.php new file mode 100644 --- /dev/null +++ b/src/applications/diffusion/view/DiffusionCommitGraphView.php @@ -0,0 +1,415 @@ +history = $history; + return $this; + } + + public function getHistory() { + return $this->history; + } + + public function setCommits(array $commits) { + assert_instances_of($commits, 'PhabricatorRepositoryCommit'); + $this->commits = $commits; + $this->commitMap = mpull($commits, null, 'getCommitIdentifier'); + return $this; + } + + public function getCommits() { + return $this->commits; + } + + public function setParents(array $parents) { + $this->parents = $parents; + return $this; + } + + public function getParents() { + return $this->parents; + } + + public function setIsHead($is_head) { + $this->isHead = $is_head; + return $this; + } + + public function getIsHead() { + return $this->isHead; + } + + public function setIsTail($is_tail) { + $this->isTail = $is_tail; + return $this; + } + + public function getIsTail() { + return $this->isTail; + } + + public function setFilterParents($filter_parents) { + $this->filterParents = $filter_parents; + return $this; + } + + public function getFilterParents() { + return $this->filterParents; + } + + private function getRepository() { + $drequest = $this->getDiffusionRequest(); + + if (!$drequest) { + return null; + } + + return $drequest->getRepository(); + } + + public function render() { + $viewer = $this->getUser(); + + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + + require_celerity_resource('diffusion-css'); + Javelin::initBehavior('phabricator-tooltips'); + + $show_builds = $this->shouldShowBuilds(); + $show_revisions = $this->shouldShowRevisions(); + + $items = $this->newHistoryItems(); + + $rows = array(); + $last_date = null; + foreach ($items as $item) { + $content = array(); + + $item_epoch = $item['epoch']; + $item_hash = $item['hash']; + $commit = $item['commit']; + + $item_date = phabricator_date($item_epoch, $viewer); + if ($item_date !== $last_date) { + $last_date = $item_date; + $content[] = $item_date; + } + + $commit_description = $this->getCommitDescription($commit); + $commit_link = $this->getCommitURI($commit, $item_hash); + + $short_hash = $this->getCommitObjectName($commit, $item_hash); + $is_disabled = $this->getCommitIsDisabled($commit); + + $author_view = $this->getCommitAuthorView($commit); + + $item_view = id(new PHUIObjectItemView()) + ->setHeader($commit_description) + ->setObjectName($short_hash) + ->setHref($commit_link) + ->setDisabled($is_disabled); + + if ($author_view !== null) { + $item_view->addAttribute($author_view); + } + + $browse_button = $this->newBrowseButton($item_hash); + + $build_view = null; + if ($show_builds) { + $build_view = $this->newBuildView($item_hash); + } + + $item_view->setSideColumn( + array( + $build_view, + $browse_button, + )); + + $revision_view = null; + if ($show_revisions) { + $revision_view = $this->newRevisionView($item_hash); + } + + if ($revision_view !== null) { + $item_view->addAttribute($revision_view); + } + + $view = id(new PHUIObjectItemListView()) + ->setFlush(true) + ->addItem($item_view); + + $content[] = $view; + + $rows[] = array( + $content, + ); + } + + $graph = $this->newGraphView(); + if ($graph) { + $idx = 0; + foreach ($rows as $key => $row) { + array_unshift($row, $graph[$idx++]); + $rows[$key] = $row; + } + } + + foreach ($rows as $key => $row) { + $cells = array(); + foreach ($row as $cell) { + $cells[] = phutil_tag('td', array(), $cell); + } + $rows[$key] = phutil_tag('tr', array(), $cells); + } + + $table = phutil_tag('table', array(), $rows); + + return $table; + } + + private function newGraphView() { + if (!$this->getParents()) { + return null; + } + + $parents = $this->getParents(); + + // If we're filtering parents, remove relationships which point to + // commits that are not part of the visible graph. Otherwise, we get + // a big tree of nonsense when viewing release branches like "stable" + // versus "master". + if ($this->getFilterParents()) { + foreach ($parents as $key => $nodes) { + foreach ($nodes as $nkey => $node) { + if (empty($parents[$node])) { + unset($parents[$key][$nkey]); + } + } + } + } + + return id(new PHUIDiffGraphView()) + ->setIsHead($this->getIsHead()) + ->setIsTail($this->getIsTail()) + ->renderGraph($parents); + } + + private function shouldShowBuilds() { + $viewer = $this->getViewer(); + + $show_builds = PhabricatorApplication::isClassInstalledForViewer( + 'PhabricatorHarbormasterApplication', + $this->getUser()); + + return $show_builds; + } + + private function shouldShowRevisions() { + $viewer = $this->getViewer(); + + $show_revisions = PhabricatorApplication::isClassInstalledForViewer( + 'PhabricatorDifferentialApplication', + $viewer); + + return $show_revisions; + } + + private function newHistoryItems() { + $items = array(); + + $commits = $this->getCommits(); + $commit_map = mpull($commits, null, 'getCommitIdentifier'); + + $history = $this->getHistory(); + if ($history !== null) { + foreach ($history as $history_item) { + $commit_hash = $history_item->getCommitIdentifier(); + + $items[] = array( + 'epoch' => $history_item->getEpoch(), + 'hash' => $commit_hash, + 'commit' => idx($commit_map, $commit_hash), + ); + } + } else { + foreach ($commits as $commit) { + $items[] = array( + 'epoch' => $commit->getEpoch(), + 'hash' => $commit->getCommitIdentifier(), + 'commit' => $commit, + ); + } + } + + return $items; + } + + private function getCommitDescription($commit) { + if (!$commit) { + return phutil_tag('em', array(), pht("Discovering\xE2\x80\xA6")); + } + + // We can show details once the message and change have been imported. + $partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE | + PhabricatorRepositoryCommit::IMPORTED_CHANGE; + if (!$commit->isPartiallyImported($partial_import)) { + return phutil_tag('em', array(), pht("Importing\xE2\x80\xA6")); + } + + return $commit->getCommitData()->getSummary(); + } + + private function getCommitURI($commit, $hash) { + $repository = $this->getRepository(); + + if ($repository) { + return $repository->getCommitURI($hash); + } + + return $commit->getURI(); + } + + private function getCommitObjectName($commit, $hash) { + $repository = $this->getRepository(); + + if ($repository) { + return $repository->formatCommitName( + $hash, + $is_local = true); + } + + return $commit->getDisplayName(); + } + + private function getCommitIsDisabled($commit) { + if (!$commit) { + return true; + } + + if ($commit->isUnreachable()) { + return true; + } + + return false; + } + + private function getCommitAuthorView($commit) { + if (!$commit) { + return null; + } + + $viewer = $this->getViewer(); + + return $commit->newCommitAuthorView($viewer); + } + + private function newBrowseButton($hash) { + $commit = $this->getCommit($hash); + + return $this->linkBrowse( + '/', + array( + 'commit' => $hash, + ), + $as_button = true); + } + + private function getCommit($hash) { + $commit_map = $this->getCommitMap(); + return idx($commit_map, $hash); + } + + private function getCommitMap() { + return $this->commitMap; + } + + private function newBuildView($hash) { + $commit = $this->getCommit($hash); + if (!$commit) { + return null; + } + + $buildable = $this->getBuildable($commit); + if (!$buildable) { + return null; + } + + return $this->renderBuildable($buildable, 'button'); + } + + private function getBuildable(PhabricatorRepositoryCommit $commit) { + $buildable_map = $this->getBuildableMap(); + return idx($buildable_map, $commit->getPHID()); + } + + private function getBuildableMap() { + if ($this->buildableMap === null) { + $commits = $this->getCommits(); + $buildables = $this->loadBuildables($commits); + $this->buildableMap = $buildables; + } + + return $this->buildableMap; + } + + private function newRevisionView($hash) { + $commit = $this->getCommit($hash); + if (!$commit) { + return null; + } + + $revisions = $this->getRevisions($commit); + if (!$revisions) { + return null; + } + + $revision = head($revisions); + + return id(new PHUITagView()) + ->setName($revision->getMonogram()) + ->setType(PHUITagView::TYPE_SHADE) + ->setColor(PHUITagView::COLOR_BLUE) + ->setHref($revision->getURI()) + ->setBorder(PHUITagView::BORDER_NONE) + ->setSlimShady(true); + } + + private function getRevisions(PhabricatorRepositoryCommit $commit) { + $revision_map = $this->getRevisionMap(); + return idx($revision_map, $commit->getPHID(), array()); + } + + private function getRevisionMap() { + if ($this->revisionMap === null) { + $this->revisionMap = $this->newRevisionMap(); + } + + return $this->revisionMap; + } + + private function newRevisionMap() { + $viewer = $this->getViewer(); + $commits = $this->getCommits(); + + return DiffusionCommitRevisionQuery::loadRevisionMapForCommits( + $viewer, + $commits); + } + +} diff --git a/src/applications/diffusion/view/DiffusionHistoryListView.php b/src/applications/diffusion/view/DiffusionHistoryListView.php deleted file mode 100644 --- a/src/applications/diffusion/view/DiffusionHistoryListView.php +++ /dev/null @@ -1,172 +0,0 @@ -getDiffusionRequest(); - $viewer = $this->getUser(); - $repository = $drequest->getRepository(); - - require_celerity_resource('diffusion-css'); - Javelin::initBehavior('phabricator-tooltips'); - - $buildables = $this->loadBuildables( - mpull($this->getHistory(), 'getCommit')); - - $show_revisions = PhabricatorApplication::isClassInstalledForViewer( - 'PhabricatorDifferentialApplication', - $viewer); - - $handles = $viewer->loadHandles($this->getRequiredHandlePHIDs()); - - $show_builds = PhabricatorApplication::isClassInstalledForViewer( - 'PhabricatorHarbormasterApplication', - $this->getUser()); - - $cur_date = null; - $view = array(); - foreach ($this->getHistory() as $history) { - $epoch = $history->getEpoch(); - $new_date = phabricator_date($history->getEpoch(), $viewer); - if ($cur_date !== $new_date) { - $date = ucfirst( - phabricator_relative_date($history->getEpoch(), $viewer)); - $header = id(new PHUIHeaderView()) - ->setHeader($date); - $list = id(new PHUIObjectItemListView()) - ->setFlush(true) - ->addClass('diffusion-history-list'); - - $view[] = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->addClass('diffusion-mobile-view') - ->setObjectList($list); - } - - if ($epoch) { - $committed = $viewer->formatShortDateTime($epoch); - } else { - $committed = null; - } - - $data = $history->getCommitData(); - $author_phid = $committer = $committer_phid = null; - if ($data) { - $author_phid = $data->getCommitDetail('authorPHID'); - $committer_phid = $data->getCommitDetail('committerPHID'); - $committer = $data->getCommitDetail('committer'); - } - - if ($author_phid && isset($handles[$author_phid])) { - $author_name = $handles[$author_phid]->renderLink(); - $author_image = $handles[$author_phid]->getImageURI(); - } else { - $author_name = self::renderName($history->getAuthorName()); - $author_image = - celerity_get_resource_uri('/rsrc/image/people/user0.png'); - } - - $different_committer = false; - if ($committer_phid) { - $different_committer = ($committer_phid != $author_phid); - } else if ($committer != '') { - $different_committer = ($committer != $history->getAuthorName()); - } - if ($different_committer) { - if ($committer_phid && isset($handles[$committer_phid])) { - $committer = $handles[$committer_phid]->renderLink(); - } else { - $committer = self::renderName($committer); - } - $author_name = hsprintf('%s / %s', $author_name, $committer); - } - - // We can show details once the message and change have been imported. - $partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE | - PhabricatorRepositoryCommit::IMPORTED_CHANGE; - - $commit = $history->getCommit(); - if ($commit && $commit->isPartiallyImported($partial_import) && $data) { - $commit_desc = $history->getSummary(); - } else { - $commit_desc = phutil_tag('em', array(), pht("Importing\xE2\x80\xA6")); - } - - $browse_button = $this->linkBrowse( - $history->getPath(), - array( - 'commit' => $history->getCommitIdentifier(), - 'branch' => $drequest->getBranch(), - 'type' => $history->getFileType(), - ), - true); - - $diff_tag = null; - if ($show_revisions && $commit) { - $revisions = $this->getRevisionsForCommit($commit); - if ($revisions) { - $revision = head($revisions); - $diff_tag = id(new PHUITagView()) - ->setName($revision->getMonogram()) - ->setType(PHUITagView::TYPE_SHADE) - ->setColor(PHUITagView::COLOR_BLUE) - ->setHref($revision->getURI()) - ->setBorder(PHUITagView::BORDER_NONE) - ->setSlimShady(true); - } - } - - $build_view = null; - if ($show_builds) { - $buildable = idx($buildables, $commit->getPHID()); - if ($buildable !== null) { - $build_view = $this->renderBuildable($buildable, 'button'); - } - } - - $message = null; - $commit_link = $repository->getCommitURI( - $history->getCommitIdentifier()); - - $commit_name = $repository->formatCommitName( - $history->getCommitIdentifier(), $local = true); - - $committed = phabricator_datetime($commit->getEpoch(), $viewer); - $author_name = phutil_tag( - 'strong', - array( - 'class' => 'diffusion-history-author-name', - ), - $author_name); - $authored = pht('%s on %s.', $author_name, $committed); - - $commit_tag = id(new PHUITagView()) - ->setName($commit_name) - ->setType(PHUITagView::TYPE_SHADE) - ->setColor(PHUITagView::COLOR_INDIGO) - ->setBorder(PHUITagView::BORDER_NONE) - ->setSlimShady(true); - - $item = id(new PHUIObjectItemView()) - ->setHeader($commit_desc) - ->setHref($commit_link) - ->setDisabled($commit->isUnreachable()) - ->setDescription($message) - ->setImageURI($author_image) - ->addAttribute(array($commit_tag, ' ', $diff_tag)) // For Copy Pasta - ->addAttribute($authored) - ->setSideColumn(array( - $build_view, - $browse_button, - )); - - $list->addItem($item); - $cur_date = $new_date; - } - - - return $view; - } - -}