3 final class DiffusionCommitController
extends DiffusionController
{
5 const CHANGES_LIMIT
= 100;
7 private $commitParents;
10 private $commitErrors;
11 private $commitExists;
13 public function shouldAllowPublic() {
17 public function handleRequest(AphrontRequest
$request) {
18 $response = $this->loadDiffusionContext();
23 $drequest = $this->getDiffusionRequest();
24 $viewer = $request->getUser();
25 $repository = $drequest->getRepository();
26 $commit_identifier = $drequest->getCommit();
28 // If this page is being accessed via "/source/xyz/commit/...", redirect
29 // to the canonical URI.
30 $repo_callsign = $request->getURIData('repositoryCallsign');
31 $has_callsign = $repo_callsign !== null && strlen($repo_callsign);
32 $repo_id = $request->getURIData('repositoryID');
33 $has_id = $repo_id !== null && strlen($repo_id);
35 if (!$has_callsign && !$has_id) {
36 $canonical_uri = $repository->getCommitURI($commit_identifier);
37 return id(new AphrontRedirectResponse())
38 ->setURI($canonical_uri);
41 if ($request->getStr('diff')) {
42 return $this->buildRawDiffResponse($drequest);
45 $commits = id(new DiffusionCommitQuery())
47 ->withRepository($repository)
48 ->withIdentifiers(array($commit_identifier))
49 ->needCommitData(true)
50 ->needAuditRequests(true)
51 ->needAuditAuthority(array($viewer))
53 ->needIdentities(true)
56 $multiple_results = count($commits) > 1;
58 $crumbs = $this->buildCrumbs(array(
59 'commit' => !$multiple_results,
61 $crumbs->setBorder(true);
64 if (!$this->getCommitExists()) {
65 return new Aphront404Response();
68 $error = id(new PHUIInfoView())
69 ->setTitle(pht('Commit Still Parsing'))
72 'Failed to load the commit because the commit has not been '.
75 $title = pht('Commit Still Parsing');
77 return $this->newPage()
80 ->appendChild($error);
81 } else if ($multiple_results) {
85 'The identifier %s is ambiguous and matches more than one commit.',
91 $error = id(new PHUIInfoView())
92 ->setTitle(pht('Ambiguous Commit'))
93 ->setSeverity(PHUIInfoView
::SEVERITY_WARNING
)
94 ->appendChild($warning_message);
96 $list = id(new DiffusionCommitGraphView())
98 ->setCommits($commits);
100 $crumbs->addTextCrumb(pht('Ambiguous Commit'));
102 $matched_commits = id(new PHUITwoColumnView())
108 return $this->newPage()
109 ->setTitle(pht('Ambiguous Commit'))
111 ->appendChild($matched_commits);
113 $commit = head($commits);
116 $audit_requests = $commit->getAudits();
118 $commit_data = $commit->getCommitData();
119 $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
121 $unpublished_panel = null;
125 if ($commit->isImported()) {
126 $change_query = DiffusionPathChangeQuery
::newFromDiffusionRequest(
128 $change_query->setLimit($hard_limit +
1);
129 $changes = $change_query->loadChanges();
134 $was_limited = (count($changes) > $hard_limit);
136 $changes = array_slice($changes, 0, $hard_limit);
139 $count = count($changes);
141 $is_unreadable = false;
143 if (!$count ||
$commit->isUnreachable()) {
144 $hint = id(new DiffusionCommitHintQuery())
146 ->withRepositoryPHIDs(array($repository->getPHID()))
147 ->withOldCommitIdentifiers(array($commit->getCommitIdentifier()))
150 $is_unreadable = $hint->isUnreadable();
155 $subpath = $commit_data->getCommitDetail('svn-subpath');
157 $error_panel = new PHUIInfoView();
158 $error_panel->setTitle(pht('Commit Not Tracked'));
159 $error_panel->setSeverity(PHUIInfoView
::SEVERITY_WARNING
);
160 $error_panel->appendChild(
162 "This Diffusion repository is configured to track only one ".
163 "subdirectory of the entire Subversion repository, and this commit ".
164 "didn't affect the tracked subdirectory ('%s'), so no ".
165 "information is available.",
168 $engine = PhabricatorMarkupEngine
::newDifferentialMarkupEngine();
169 $engine->setConfig('viewer', $viewer);
171 $commit_tag = $this->renderCommitHashTag($drequest);
172 $header = id(new PHUIHeaderView())
173 ->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')))
174 ->setHeaderIcon('fa-code-fork')
175 ->addTag($commit_tag);
177 if (!$commit->isAuditStatusNoAudit()) {
178 $status = $commit->getAuditStatusObject();
180 $icon = $status->getIcon();
181 $color = $status->getColor();
182 $status = $status->getName();
184 $header->setStatus($icon, $color, $status);
187 $curtain = $this->buildCurtain($commit, $repository);
188 $details = $this->buildPropertyListView(
193 $message = $commit_data->getCommitMessage();
195 $revision = $commit->getCommitIdentifier();
196 $message = $this->linkBugtraq($message);
197 $message = $engine->markupText($message);
199 $detail_list = new PHUIPropertyListView();
200 $detail_list->addTextContent(
204 'class' => 'diffusion-commit-message phabricator-remarkup',
208 if ($commit->isUnreachable()) {
209 $did_rewrite = false;
211 if ($hint->isRewritten()) {
212 $rewritten = id(new DiffusionCommitQuery())
214 ->withRepository($repository)
215 ->withIdentifiers(array($hint->getNewCommitIdentifier()))
219 $rewritten_uri = $rewritten->getURI();
220 $rewritten_name = $rewritten->getLocalName();
222 $rewritten_link = phutil_tag(
225 'href' => $rewritten_uri,
229 $this->commitErrors
[] = pht(
230 'This commit was rewritten after it was published, which '.
231 'changed the commit hash. This old version of the commit is '.
232 'no longer reachable from any branch, tag or ref. The new '.
233 'version of this commit is %s.',
240 $this->commitErrors
[] = pht(
241 'This commit has been deleted in the repository: it is no longer '.
242 'reachable from any branch, tag, or ref.');
245 if (!$commit->isPermanentCommit()) {
246 $nonpermanent_tag = id(new PHUITagView())
247 ->setType(PHUITagView
::TYPE_SHADE
)
248 ->setName(pht('Unpublished'))
249 ->setColor(PHUITagView
::COLOR_ORANGE
);
251 $header->addTag($nonpermanent_tag);
253 $holds = $commit_data->newPublisherHoldReasons();
256 foreach ($holds as $hold) {
258 phutil_tag('strong', array(), pht('%s:', $hold->getName())),
265 $reasons[] = pht('No further details are available.');
268 $doc_href = PhabricatorEnv
::getDoclink(
269 'Diffusion User Guide: Permanent Refs');
270 $doc_link = phutil_tag(
274 'target' => '_blank',
279 pht('Unpublished Commit'),
284 $unpublished_panel = id(new PHUIInfoView())
286 ->setErrors($reasons)
287 ->setSeverity(PHUIInfoView
::SEVERITY_WARNING
);
291 if ($this->getCommitErrors()) {
292 $error_panel = id(new PHUIInfoView())
293 ->appendChild($this->getCommitErrors())
294 ->setSeverity(PHUIInfoView
::SEVERITY_WARNING
);
298 $timeline = $this->buildComments($commit);
299 $merge_table = $this->buildMergesTable($commit);
301 $show_changesets = false;
304 $change_table = null;
305 if ($is_unreadable) {
306 $info_panel = $this->renderStatusMessage(
307 pht('Unreadable Commit'),
309 'This commit has been marked as unreadable by an administrator. '.
310 'It may have been corrupted or created improperly by an external '.
312 } else if ($is_foreign) {
313 // Don't render anything else.
314 } else if (!$commit->isImported()) {
315 $info_panel = $this->renderStatusMessage(
316 pht('Still Importing...'),
318 'This commit is still importing. Changes will be visible once '.
319 'the import finishes.'));
320 } else if (!count($changes)) {
321 $info_panel = $this->renderStatusMessage(
324 'This commit is empty and does not affect any paths.'));
325 } else if ($was_limited) {
326 $info_panel = $this->renderStatusMessage(
327 pht('Very Large Commit'),
329 'This commit is very large, and affects more than %d files. '.
330 'Changes are not shown.',
332 } else if (!$this->getCommitExists()) {
333 $info_panel = $this->renderStatusMessage(
334 pht('Commit No Longer Exists'),
335 pht('This commit no longer exists in the repository.'));
337 $show_changesets = true;
339 // The user has clicked "Show All Changes", and we should show all the
340 // changes inline even if there are more than the soft limit.
341 $show_all_details = $request->getBool('show_all');
343 $change_header = id(new PHUIHeaderView())
344 ->setHeader(pht('Changes (%s)', new PhutilNumber($count)));
346 $warning_view = null;
347 if ($count > self
::CHANGES_LIMIT
&& !$show_all_details) {
348 $button = id(new PHUIButtonView())
349 ->setText(pht('Show All Changes'))
350 ->setHref('?show_all=true')
352 ->setIcon('fa-files-o');
354 $warning_view = id(new PHUIInfoView())
355 ->setSeverity(PHUIInfoView
::SEVERITY_WARNING
)
356 ->setTitle(pht('Very Large Commit'))
358 pht('This commit is very large. Load each file individually.'));
360 $change_header->addActionLink($button);
363 $changesets = DiffusionPathChange
::convertToDifferentialChangesets(
367 // TODO: This table and panel shouldn't really be separate, but we need
368 // to clean up the "Load All Files" interaction first.
369 $change_table = $this->buildTableOfContents(
374 $vcs = $repository->getVersionControlSystem();
376 case PhabricatorRepositoryType
::REPOSITORY_TYPE_SVN
:
377 $vcs_supports_directory_changes = true;
379 case PhabricatorRepositoryType
::REPOSITORY_TYPE_GIT
:
380 case PhabricatorRepositoryType
::REPOSITORY_TYPE_MERCURIAL
:
381 $vcs_supports_directory_changes = false;
384 throw new Exception(pht('Unknown VCS.'));
387 $references = array();
388 foreach ($changesets as $key => $changeset) {
389 $file_type = $changeset->getFileType();
390 if ($file_type == DifferentialChangeType
::FILE_DIRECTORY
) {
391 if (!$vcs_supports_directory_changes) {
392 unset($changesets[$key]);
397 $references[$key] = $drequest->generateURI(
399 'action' => 'rendering-ref',
400 'path' => $changeset->getFilename(),
404 // TODO: Some parts of the views still rely on properties of the
405 // DifferentialChangeset. Make the objects ephemeral to make sure we don't
406 // accidentally save them, and then set their ID to the appropriate ID for
407 // this application (the path IDs).
408 $path_ids = array_flip(mpull($changes, 'getPath'));
409 foreach ($changesets as $changeset) {
410 $changeset->makeEphemeral();
411 $changeset->setID($path_ids[$changeset->getFilename()]);
414 if ($count <= self
::CHANGES_LIMIT ||
$show_all_details) {
415 $visible_changesets = $changesets;
417 $visible_changesets = array();
419 $inlines = id(new DiffusionDiffInlineCommentQuery())
421 ->withCommitPHIDs(array($commit->getPHID()))
422 ->withPublishedComments(true)
423 ->withPublishableComments(true)
425 $inlines = mpull($inlines, 'newInlineCommentObject');
427 $path_ids = mpull($inlines, null, 'getPathID');
428 foreach ($changesets as $key => $changeset) {
429 if (array_key_exists($changeset->getID(), $path_ids)) {
430 $visible_changesets[$key] = $changeset;
435 $change_list_title = $commit->getDisplayName();
437 $change_list = new DifferentialChangesetListView();
438 $change_list->setTitle($change_list_title);
439 $change_list->setChangesets($changesets);
440 $change_list->setVisibleChangesets($visible_changesets);
441 $change_list->setRenderingReferences($references);
442 $change_list->setRenderURI($repository->getPathURI('diff/'));
443 $change_list->setRepository($repository);
444 $change_list->setUser($viewer);
445 $change_list->setBackground(PHUIObjectBoxView
::BLUE_PROPERTY
);
447 // TODO: Try to setBranch() to something reasonable here?
449 $change_list->setStandaloneURI(
450 $repository->getPathURI('diff/'));
452 $change_list->setRawFileURIs(
453 // TODO: Implement this, somewhat tricky if there's an octopus merge
456 $repository->getPathURI('diff/?view=r'));
458 $change_list->setInlineCommentControllerURI(
459 '/diffusion/inline/edit/'.phutil_escape_uri($commit->getPHID()).'/');
463 $add_comment = $this->renderAddCommentPanel(
467 $filetree = id(new DifferentialFileTreeEngine())
469 ->setDisabled(!$show_changesets);
471 if ($show_changesets) {
472 $filetree->setChangesets($changesets);
475 $description_box = id(new PHUIObjectBoxView())
476 ->setHeaderText(pht('Description'))
477 ->setBackground(PHUIObjectBoxView
::BLUE_PROPERTY
)
478 ->appendChild($detail_list);
480 $detail_box = id(new PHUIObjectBoxView())
481 ->setHeaderText(pht('Details'))
482 ->setBackground(PHUIObjectBoxView
::BLUE_PROPERTY
)
483 ->appendChild($details);
485 $view = id(new PHUITwoColumnView())
487 ->setCurtain($curtain)
505 $main_content = array(
510 $main_content = $filetree->newView($main_content);
511 if (!$filetree->getDisabled()) {
512 $change_list->setFormationView($main_content);
515 $page = $this->newPage()
516 ->setTitle($commit->getDisplayName())
517 ->setPageObjectPHIDS(array($commit->getPHID()))
518 ->appendChild($main_content);
524 private function buildPropertyListView(
525 PhabricatorRepositoryCommit
$commit,
526 PhabricatorRepositoryCommitData
$data,
527 array $audit_requests) {
529 $viewer = $this->getViewer();
530 $commit_phid = $commit->getPHID();
531 $drequest = $this->getDiffusionRequest();
532 $repository = $drequest->getRepository();
534 $view = id(new PHUIPropertyListView())
535 ->setUser($this->getRequest()->getUser())
536 ->setObject($commit);
538 $edge_query = id(new PhabricatorEdgeQuery())
539 ->withSourcePHIDs(array($commit_phid))
540 ->withEdgeTypes(array(
541 DiffusionCommitHasTaskEdgeType
::EDGECONST
,
542 DiffusionCommitHasRevisionEdgeType
::EDGECONST
,
543 DiffusionCommitRevertsCommitEdgeType
::EDGECONST
,
544 DiffusionCommitRevertedByCommitEdgeType
::EDGECONST
,
547 $edges = $edge_query->execute();
549 $task_phids = array_keys(
550 $edges[$commit_phid][DiffusionCommitHasTaskEdgeType
::EDGECONST
]);
551 $revision_phid = key(
552 $edges[$commit_phid][DiffusionCommitHasRevisionEdgeType
::EDGECONST
]);
554 $reverts_phids = array_keys(
555 $edges[$commit_phid][DiffusionCommitRevertsCommitEdgeType
::EDGECONST
]);
556 $reverted_by_phids = array_keys(
557 $edges[$commit_phid][DiffusionCommitRevertedByCommitEdgeType
::EDGECONST
]);
559 $phids = $edge_query->getDestinationPHIDs(array($commit_phid));
562 if ($data->getCommitDetail('reviewerPHID')) {
563 $phids[] = $data->getCommitDetail('reviewerPHID');
566 $phids[] = $commit->getCommitterDisplayPHID();
567 $phids[] = $commit->getAuthorDisplayPHID();
569 // NOTE: We should never normally have more than a single push log, but
570 // it can occur naturally if a commit is pushed, then the branch it was
571 // on is deleted, then the commit is pushed again (or through other similar
572 // chains of events). This should be rare, but does not indicate a bug
575 // NOTE: We never query push logs in SVN because the committer is always
576 // the pusher and the commit time is always the push time; the push log
577 // is redundant and we save a query by skipping it.
579 $push_logs = array();
580 if ($repository->isHosted() && !$repository->isSVN()) {
581 $push_logs = id(new PhabricatorRepositoryPushLogQuery())
583 ->withRepositoryPHIDs(array($repository->getPHID()))
584 ->withNewRefs(array($commit->getCommitIdentifier()))
585 ->withRefTypes(array(PhabricatorRepositoryPushLog
::REFTYPE_COMMIT
))
587 foreach ($push_logs as $log) {
588 $phids[] = $log->getPusherPHID();
594 $handles = $this->loadViewerHandles($phids);
599 if ($audit_requests) {
600 $user_requests = array();
601 $other_requests = array();
603 foreach ($audit_requests as $audit_request) {
604 if ($audit_request->isUser()) {
605 $user_requests[] = $audit_request;
607 $other_requests[] = $audit_request;
611 if ($user_requests) {
614 $this->renderAuditStatusView($commit, $user_requests));
617 if ($other_requests) {
619 pht('Group Auditors'),
620 $this->renderAuditStatusView($commit, $other_requests));
624 $provenance_list = new PHUIStatusListView();
626 $author_view = $commit->newCommitAuthorView($viewer);
628 $author_date = $data->getAuthorEpoch();
629 $author_date = phabricator_datetime($author_date, $viewer);
631 $provenance_list->addItem(
632 id(new PHUIStatusItemView())
633 ->setTarget($author_view)
634 ->setNote(pht('Authored on %s', $author_date)));
637 if (!$commit->isAuthorSameAsCommitter()) {
638 $committer_view = $commit->newCommitCommitterView($viewer);
639 if ($committer_view) {
640 $committer_date = $commit->getEpoch();
641 $committer_date = phabricator_datetime($committer_date, $viewer);
643 $provenance_list->addItem(
644 id(new PHUIStatusItemView())
645 ->setTarget($committer_view)
646 ->setNote(pht('Committed on %s', $committer_date)));
651 $pushed_list = new PHUIStatusListView();
653 foreach ($push_logs as $push_log) {
654 $pusher_date = $push_log->getEpoch();
655 $pusher_date = phabricator_datetime($pusher_date, $viewer);
657 $pusher_view = $handles[$push_log->getPusherPHID()]->renderLink();
659 $provenance_list->addItem(
660 id(new PHUIStatusItemView())
661 ->setTarget($pusher_view)
662 ->setNote(pht('Pushed on %s', $pusher_date)));
666 $view->addProperty(pht('Provenance'), $provenance_list);
668 $reviewer_phid = $data->getCommitDetail('reviewerPHID');
669 if ($reviewer_phid) {
672 $handles[$reviewer_phid]->renderLink());
675 if ($revision_phid) {
677 pht('Differential Revision'),
678 $handles[$revision_phid]->renderLink());
681 $parents = $this->getCommitParents();
685 $viewer->renderHandleList(mpull($parents, 'getPHID')));
688 if ($this->getCommitExists()) {
694 'id' => 'commit-branches',
703 'id' => 'commit-tags',
707 $identifier = $commit->getCommitIdentifier();
708 $root = $repository->getPathURI("commit/{$identifier}");
709 Javelin
::initBehavior(
710 'diffusion-commit-branches',
712 $root.'/branches/' => 'commit-branches',
713 $root.'/tags/' => 'commit-tags',
717 $refs = $this->getCommitRefs();
719 $ref_links = array();
720 foreach ($refs as $ref_data) {
721 $ref_links[] = phutil_tag(
724 'href' => $ref_data['href'],
730 phutil_implode_html(', ', $ref_links));
733 if ($reverts_phids) {
736 $viewer->renderHandleList($reverts_phids));
739 if ($reverted_by_phids) {
742 $viewer->renderHandleList($reverted_by_phids));
746 $task_list = array();
747 foreach ($task_phids as $phid) {
748 $task_list[] = $handles[$phid]->renderLink();
750 $task_list = phutil_implode_html(phutil_tag('br'), $task_list);
759 private function buildComments(PhabricatorRepositoryCommit
$commit) {
760 $timeline = $this->buildTransactionTimeline(
762 new PhabricatorAuditTransactionQuery());
764 $timeline->setQuoteRef($commit->getMonogram());
769 private function renderAddCommentPanel(
770 PhabricatorRepositoryCommit
$commit,
773 $request = $this->getRequest();
774 $viewer = $request->getUser();
776 // TODO: This is pretty awkward, unify the CSS between Diffusion and
777 // Differential better.
778 require_celerity_resource('differential-core-view-css');
780 $comment_view = id(new DiffusionCommitEditEngine())
782 ->buildEditEngineCommentView($commit);
784 $comment_view->setTransactionTimeline($timeline);
786 return $comment_view;
789 private function buildMergesTable(PhabricatorRepositoryCommit
$commit) {
790 $viewer = $this->getViewer();
791 $drequest = $this->getDiffusionRequest();
792 $repository = $drequest->getRepository();
794 $merges = $this->getCommitMerges();
799 $limit = $this->getMergeDisplayLimit();
802 if (count($merges) > $limit) {
803 $merges = array_slice($merges, 0, $limit);
804 $caption = new PHUIInfoView();
805 $caption->setSeverity(PHUIInfoView
::SEVERITY_NOTICE
);
806 $caption->appendChild(
808 'This commit merges a very large number of changes. '.
809 'Only the first %s are shown.',
810 new PhutilNumber($limit)));
813 $commit_list = id(new DiffusionCommitGraphView())
815 ->setDiffusionRequest($drequest)
816 ->setHistory($merges);
818 $panel = id(new PHUIObjectBoxView())
819 ->setHeaderText(pht('Merged Changes'))
820 ->setBackground(PHUIObjectBoxView
::BLUE_PROPERTY
)
821 ->setObjectList($commit_list->newObjectItemListView());
823 $panel->setInfoView($caption);
829 private function buildCurtain(
830 PhabricatorRepositoryCommit
$commit,
831 PhabricatorRepository
$repository) {
833 $request = $this->getRequest();
834 $viewer = $this->getViewer();
835 $curtain = $this->newCurtainView($commit);
837 $can_edit = PhabricatorPolicyFilter
::hasCapability(
840 PhabricatorPolicyCapability
::CAN_EDIT
);
842 $id = $commit->getID();
843 $edit_uri = $this->getApplicationURI("/commit/edit/{$id}/");
845 $action = id(new PhabricatorActionView())
846 ->setName(pht('Edit Commit'))
848 ->setIcon('fa-pencil')
849 ->setDisabled(!$can_edit)
850 ->setWorkflow(!$can_edit);
851 $curtain->addAction($action);
853 $action = id(new PhabricatorActionView())
854 ->setName(pht('Download Raw Diff'))
855 ->setHref($request->getRequestURI()->alter('diff', true))
856 ->setIcon('fa-download');
857 $curtain->addAction($action);
859 $relationship_list = PhabricatorObjectRelationshipList
::newForObject(
863 $relationship_submenu = $relationship_list->newActionMenu();
864 if ($relationship_submenu) {
865 $curtain->addAction($relationship_submenu);
871 private function buildRawDiffResponse(DiffusionRequest
$drequest) {
872 $diff_info = $this->callConduitWithDiffusionRequest(
873 'diffusion.rawdiffquery',
875 'commit' => $drequest->getCommit(),
876 'path' => $drequest->getPath(),
879 $file_phid = $diff_info['filePHID'];
881 $file = id(new PhabricatorFileQuery())
882 ->setViewer($this->getViewer())
883 ->withPHIDs(array($file_phid))
888 'Failed to load file ("%s") returned by "%s".',
890 'diffusion.rawdiffquery'));
893 return $file->getRedirectResponse();
896 private function renderAuditStatusView(
897 PhabricatorRepositoryCommit
$commit,
898 array $audit_requests) {
899 assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
900 $viewer = $this->getViewer();
902 $view = new PHUIStatusListView();
903 foreach ($audit_requests as $request) {
904 $status = $request->getAuditRequestStatusObject();
906 $item = new PHUIStatusItemView();
908 $status->getIconIcon(),
909 $status->getIconColor(),
910 $status->getStatusName());
912 $auditor_phid = $request->getAuditorPHID();
913 $target = $viewer->renderHandle($auditor_phid);
914 $item->setTarget($target);
916 if ($commit->hasAuditAuthority($viewer, $request)) {
917 $item->setHighlighted(true);
920 $view->addItem($item);
926 private function linkBugtraq($corpus) {
927 $url = PhabricatorEnv
::getEnvConfig('bugtraq.url');
928 if ($url === null ||
!strlen($url)) {
932 $regexes = PhabricatorEnv
::getEnvConfig('bugtraq.logregex');
937 $parser = id(new PhutilBugtraqParser())
938 ->setBugtraqPattern("[[ {$url} | %BUGID% ]]")
939 ->setBugtraqCaptureExpression(array_shift($regexes));
941 $select = array_shift($regexes);
943 $parser->setBugtraqSelectExpression($select);
946 return $parser->processCorpus($corpus);
949 private function buildTableOfContents(
954 $drequest = $this->getDiffusionRequest();
955 $viewer = $this->getViewer();
957 $toc_view = id(new PHUIDiffTableOfContentsListView())
960 ->setBackground(PHUIObjectBoxView
::BLUE_PROPERTY
);
963 $toc_view->setInfoView($info_view);
966 // TODO: This is hacky, we just want access to the linkX() methods on
968 $diffusion_view = id(new DiffusionEmptyResultView())
969 ->setDiffusionRequest($drequest);
971 $have_owners = PhabricatorApplication
::isClassInstalledForViewer(
972 'PhabricatorOwnersApplication',
976 $have_owners = false;
980 if ($viewer->getPHID()) {
981 $packages = id(new PhabricatorOwnersPackageQuery())
983 ->withStatuses(array(PhabricatorOwnersPackage
::STATUS_ACTIVE
))
984 ->withAuthorityPHIDs(array($viewer->getPHID()))
986 $toc_view->setAuthorityPackages($packages);
989 $repository = $drequest->getRepository();
990 $repository_phid = $repository->getPHID();
992 $control_query = id(new PhabricatorOwnersPackageQuery())
994 ->withStatuses(array(PhabricatorOwnersPackage
::STATUS_ACTIVE
))
995 ->withControl($repository_phid, mpull($changesets, 'getFilename'));
996 $control_query->execute();
999 foreach ($changesets as $changeset_id => $changeset) {
1000 $path = $changeset->getFilename();
1001 $anchor = $changeset->getAnchorName();
1003 $history_link = $diffusion_view->linkHistory($path);
1004 $browse_link = $diffusion_view->linkBrowse(
1007 'type' => $changeset->getFileType(),
1010 $item = id(new PHUIDiffTableOfContentsItemView())
1011 ->setChangeset($changeset)
1012 ->setAnchor($anchor)
1021 $packages = $control_query->getControllingPackagesForPath(
1023 $changeset->getFilename());
1024 $item->setPackages($packages);
1027 $toc_view->addItem($item);
1033 private function loadCommitState() {
1034 $viewer = $this->getViewer();
1035 $drequest = $this->getDiffusionRequest();
1036 $repository = $drequest->getRepository();
1037 $commit = $drequest->getCommit();
1039 // TODO: We could use futures here and resolve these calls in parallel.
1041 $exceptions = array();
1044 $parent_refs = $this->callConduitWithDiffusionRequest(
1045 'diffusion.commitparentsquery',
1047 'commit' => $commit,
1051 $parents = id(new DiffusionCommitQuery())
1052 ->setViewer($viewer)
1053 ->withRepository($repository)
1054 ->withIdentifiers($parent_refs)
1060 $this->commitParents
= $parents;
1061 } catch (Exception
$ex) {
1062 $this->commitParents
= false;
1063 $exceptions[] = $ex;
1066 $merge_limit = $this->getMergeDisplayLimit();
1069 if ($repository->isSVN()) {
1070 $this->commitMerges
= array();
1072 $merges = $this->callConduitWithDiffusionRequest(
1073 'diffusion.mergedcommitsquery',
1075 'commit' => $commit,
1076 'limit' => $merge_limit +
1,
1078 $this->commitMerges
= DiffusionPathChange
::newFromConduit($merges);
1080 } catch (Exception
$ex) {
1081 $this->commitMerges
= false;
1082 $exceptions[] = $ex;
1087 if ($repository->isGit()) {
1088 $refs = $this->callConduitWithDiffusionRequest(
1089 'diffusion.refsquery',
1091 'commit' => $commit,
1097 $this->commitRefs
= $refs;
1098 } catch (Exception
$ex) {
1099 $this->commitRefs
= false;
1100 $exceptions[] = $ex;
1104 $exists = $this->callConduitWithDiffusionRequest(
1105 'diffusion.existsquery',
1107 'commit' => $commit,
1111 $this->commitExists
= true;
1112 foreach ($exceptions as $exception) {
1113 $this->commitErrors
[] = $exception->getMessage();
1116 $this->commitExists
= false;
1117 $this->commitErrors
[] = pht(
1118 'This commit no longer exists in the repository. It may have '.
1119 'been part of a branch which was deleted.');
1122 $this->commitExists
= true;
1123 $this->commitErrors
= array();
1127 private function getMergeDisplayLimit() {
1131 private function getCommitExists() {
1132 if ($this->commitExists
=== null) {
1133 $this->loadCommitState();
1136 return $this->commitExists
;
1139 private function getCommitParents() {
1140 if ($this->commitParents
=== null) {
1141 $this->loadCommitState();
1144 return $this->commitParents
;
1147 private function getCommitRefs() {
1148 if ($this->commitRefs
=== null) {
1149 $this->loadCommitState();
1152 return $this->commitRefs
;
1155 private function getCommitMerges() {
1156 if ($this->commitMerges
=== null) {
1157 $this->loadCommitState();
1160 return $this->commitMerges
;
1163 private function getCommitErrors() {
1164 if ($this->commitErrors
=== null) {
1165 $this->loadCommitState();
1168 return $this->commitErrors
;