mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-18 21:02:41 +01:00
Build that thing someone posted a screenshot of on Facebook
Summary: Seemed kinda cool. Test Plan: {F1707244} Reviewers: chad Reviewed By: chad Differential Revision: https://secure.phabricator.com/D16210
This commit is contained in:
parent
6c7e392f89
commit
dc37789d53
10 changed files with 450 additions and 225 deletions
|
@ -556,6 +556,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php',
|
'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php',
|
||||||
'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php',
|
'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php',
|
||||||
'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php',
|
'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php',
|
||||||
|
'DifferentialStackGraph' => 'applications/differential/edge/DifferentialStackGraph.php',
|
||||||
'DifferentialStoredCustomField' => 'applications/differential/customfield/DifferentialStoredCustomField.php',
|
'DifferentialStoredCustomField' => 'applications/differential/customfield/DifferentialStoredCustomField.php',
|
||||||
'DifferentialSubscribersField' => 'applications/differential/customfield/DifferentialSubscribersField.php',
|
'DifferentialSubscribersField' => 'applications/differential/customfield/DifferentialSubscribersField.php',
|
||||||
'DifferentialSummaryField' => 'applications/differential/customfield/DifferentialSummaryField.php',
|
'DifferentialSummaryField' => 'applications/differential/customfield/DifferentialSummaryField.php',
|
||||||
|
@ -1599,6 +1600,7 @@ phutil_register_library_map(array(
|
||||||
'PHUICurtainExtension' => 'view/extension/PHUICurtainExtension.php',
|
'PHUICurtainExtension' => 'view/extension/PHUICurtainExtension.php',
|
||||||
'PHUICurtainPanelView' => 'view/layout/PHUICurtainPanelView.php',
|
'PHUICurtainPanelView' => 'view/layout/PHUICurtainPanelView.php',
|
||||||
'PHUICurtainView' => 'view/layout/PHUICurtainView.php',
|
'PHUICurtainView' => 'view/layout/PHUICurtainView.php',
|
||||||
|
'PHUIDiffGraphView' => 'infrastructure/diff/view/PHUIDiffGraphView.php',
|
||||||
'PHUIDiffInlineCommentDetailView' => 'infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php',
|
'PHUIDiffInlineCommentDetailView' => 'infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php',
|
||||||
'PHUIDiffInlineCommentEditView' => 'infrastructure/diff/view/PHUIDiffInlineCommentEditView.php',
|
'PHUIDiffInlineCommentEditView' => 'infrastructure/diff/view/PHUIDiffInlineCommentEditView.php',
|
||||||
'PHUIDiffInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffInlineCommentRowScaffold.php',
|
'PHUIDiffInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffInlineCommentRowScaffold.php',
|
||||||
|
@ -4928,6 +4930,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialRevisionViewController' => 'DifferentialController',
|
'DifferentialRevisionViewController' => 'DifferentialController',
|
||||||
'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||||
'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
||||||
|
'DifferentialStackGraph' => 'AbstractDirectedGraph',
|
||||||
'DifferentialStoredCustomField' => 'DifferentialCustomField',
|
'DifferentialStoredCustomField' => 'DifferentialCustomField',
|
||||||
'DifferentialSubscribersField' => 'DifferentialCoreCustomField',
|
'DifferentialSubscribersField' => 'DifferentialCoreCustomField',
|
||||||
'DifferentialSummaryField' => 'DifferentialCoreCustomField',
|
'DifferentialSummaryField' => 'DifferentialCoreCustomField',
|
||||||
|
@ -6132,6 +6135,7 @@ phutil_register_library_map(array(
|
||||||
'PHUICurtainExtension' => 'Phobject',
|
'PHUICurtainExtension' => 'Phobject',
|
||||||
'PHUICurtainPanelView' => 'AphrontTagView',
|
'PHUICurtainPanelView' => 'AphrontTagView',
|
||||||
'PHUICurtainView' => 'AphrontTagView',
|
'PHUICurtainView' => 'AphrontTagView',
|
||||||
|
'PHUIDiffGraphView' => 'Phobject',
|
||||||
'PHUIDiffInlineCommentDetailView' => 'PHUIDiffInlineCommentView',
|
'PHUIDiffInlineCommentDetailView' => 'PHUIDiffInlineCommentView',
|
||||||
'PHUIDiffInlineCommentEditView' => 'PHUIDiffInlineCommentView',
|
'PHUIDiffInlineCommentEditView' => 'PHUIDiffInlineCommentView',
|
||||||
'PHUIDiffInlineCommentRowScaffold' => 'AphrontView',
|
'PHUIDiffInlineCommentRowScaffold' => 'AphrontView',
|
||||||
|
|
|
@ -341,6 +341,21 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
||||||
->setKey('commits')
|
->setKey('commits')
|
||||||
->appendChild($local_table));
|
->appendChild($local_table));
|
||||||
|
|
||||||
|
$stack_graph = id(new DifferentialStackGraph())
|
||||||
|
->setSeedRevision($revision)
|
||||||
|
->loadGraph();
|
||||||
|
if (!$stack_graph->isEmpty()) {
|
||||||
|
$stack_view = $this->renderStackView($revision, $stack_graph);
|
||||||
|
list($stack_name, $stack_color, $stack_table) = $stack_view;
|
||||||
|
|
||||||
|
$tab_group->addTab(
|
||||||
|
id(new PHUITabView())
|
||||||
|
->setName($stack_name)
|
||||||
|
->setKey('stack')
|
||||||
|
->setColor($stack_color)
|
||||||
|
->appendChild($stack_table));
|
||||||
|
}
|
||||||
|
|
||||||
if ($other_view) {
|
if ($other_view) {
|
||||||
$tab_group->addTab(
|
$tab_group->addTab(
|
||||||
id(new PHUITabView())
|
id(new PHUITabView())
|
||||||
|
@ -1198,4 +1213,149 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function renderStackView(
|
||||||
|
DifferentialRevision $current,
|
||||||
|
DifferentialStackGraph $graph) {
|
||||||
|
|
||||||
|
$ancestry = $graph->getParentEdges();
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$revisions = id(new DifferentialRevisionQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(array_keys($ancestry))
|
||||||
|
->execute();
|
||||||
|
$revisions = mpull($revisions, null, 'getPHID');
|
||||||
|
|
||||||
|
$order = id(new PhutilDirectedScalarGraph())
|
||||||
|
->addNodes($ancestry)
|
||||||
|
->getTopographicallySortedNodes();
|
||||||
|
|
||||||
|
$ancestry = array_select_keys($ancestry, $order);
|
||||||
|
|
||||||
|
$traces = id(new PHUIDiffGraphView())
|
||||||
|
->renderGraph($ancestry);
|
||||||
|
|
||||||
|
// Load author handles, and also revision handles for any revisions which
|
||||||
|
// we failed to load (they might be policy restricted).
|
||||||
|
$handle_phids = mpull($revisions, 'getAuthorPHID');
|
||||||
|
foreach ($order as $phid) {
|
||||||
|
if (empty($revisions[$phid])) {
|
||||||
|
$handle_phids[] = $phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$handles = $viewer->loadHandles($handle_phids);
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
$rowc = array();
|
||||||
|
|
||||||
|
$ii = 0;
|
||||||
|
$seen = false;
|
||||||
|
foreach ($ancestry as $phid => $ignored) {
|
||||||
|
$revision = idx($revisions, $phid);
|
||||||
|
if ($revision) {
|
||||||
|
$status_icon = $revision->getStatusIcon();
|
||||||
|
$status_name = $revision->getStatusDisplayName();
|
||||||
|
|
||||||
|
$status = array(
|
||||||
|
id(new PHUIIconView())->setIcon($status_icon),
|
||||||
|
' ',
|
||||||
|
$status_name,
|
||||||
|
);
|
||||||
|
|
||||||
|
$author = $viewer->renderHandle($revision->getAuthorPHID());
|
||||||
|
$title = phutil_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $revision->getURI(),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
$revision->getMonogram(),
|
||||||
|
' ',
|
||||||
|
$revision->getTitle(),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
$status = null;
|
||||||
|
$author = null;
|
||||||
|
$title = $viewer->renderHandle($phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows[] = array(
|
||||||
|
$traces[$ii++],
|
||||||
|
$status,
|
||||||
|
$author,
|
||||||
|
$title,
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($phid == $current->getPHID()) {
|
||||||
|
$rowc[] = 'highlighted';
|
||||||
|
} else {
|
||||||
|
$rowc[] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$stack_table = id(new AphrontTableView($rows))
|
||||||
|
->setHeaders(
|
||||||
|
array(
|
||||||
|
null,
|
||||||
|
pht('Status'),
|
||||||
|
pht('Author'),
|
||||||
|
pht('Revision'),
|
||||||
|
))
|
||||||
|
->setRowClasses($rowc)
|
||||||
|
->setColumnClasses(
|
||||||
|
array(
|
||||||
|
'threads',
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'wide',
|
||||||
|
));
|
||||||
|
|
||||||
|
// Count how many revisions this one depends on that are not yet closed.
|
||||||
|
$seen = array();
|
||||||
|
$look = array($current->getPHID());
|
||||||
|
while ($look) {
|
||||||
|
$phid = array_pop($look);
|
||||||
|
|
||||||
|
$parents = idx($ancestry, $phid, array());
|
||||||
|
foreach ($parents as $parent) {
|
||||||
|
if (isset($seen[$parent])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$seen[$parent] = $parent;
|
||||||
|
$look[] = $parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$blocking_count = 0;
|
||||||
|
foreach ($seen as $parent) {
|
||||||
|
if ($parent == $current->getPHID()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$revision = idx($revisions, $parent);
|
||||||
|
if (!$revision) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($revision->isClosed()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$blocking_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$blocking_count) {
|
||||||
|
$stack_name = pht('Stack');
|
||||||
|
$stack_color = null;
|
||||||
|
} else {
|
||||||
|
$stack_name = pht(
|
||||||
|
'Stack (%s Open)',
|
||||||
|
new PhutilNumber($blocking_count));
|
||||||
|
$stack_color = PHUIListItemView::STATUS_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array($stack_name, $stack_color, $stack_table);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,22 +19,4 @@ final class DifferentialChildRevisionsField
|
||||||
return pht('Lists revisions this one is depended on by.');
|
return pht('Lists revisions this one is depended on by.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function shouldAppearInPropertyView() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderPropertyViewLabel() {
|
|
||||||
return $this->getFieldName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRequiredHandlePHIDsForPropertyView() {
|
|
||||||
return PhabricatorEdgeQuery::loadDestinationPHIDs(
|
|
||||||
$this->getObject()->getPHID(),
|
|
||||||
DifferentialRevisionDependedOnByRevisionEdgeType::EDGECONST);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderPropertyViewValue(array $handles) {
|
|
||||||
return $this->renderHandleList($handles);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,24 +23,6 @@ final class DifferentialParentRevisionsField
|
||||||
return pht('Lists revisions this one depends on.');
|
return pht('Lists revisions this one depends on.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function shouldAppearInPropertyView() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderPropertyViewLabel() {
|
|
||||||
return $this->getFieldName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRequiredHandlePHIDsForPropertyView() {
|
|
||||||
return PhabricatorEdgeQuery::loadDestinationPHIDs(
|
|
||||||
$this->getObject()->getPHID(),
|
|
||||||
DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderPropertyViewValue(array $handles) {
|
|
||||||
return $this->renderHandleList($handles);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getProTips() {
|
public function getProTips() {
|
||||||
return array(
|
return array(
|
||||||
pht(
|
pht(
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DifferentialStackGraph
|
||||||
|
extends AbstractDirectedGraph {
|
||||||
|
|
||||||
|
private $parentEdges = array();
|
||||||
|
private $childEdges = array();
|
||||||
|
|
||||||
|
public function setSeedRevision(DifferentialRevision $revision) {
|
||||||
|
return $this->addNodes(
|
||||||
|
array(
|
||||||
|
'<seed>' => array($revision->getPHID()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEmpty() {
|
||||||
|
return (count($this->getNodes()) <= 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParentEdges() {
|
||||||
|
return $this->parentEdges;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadEdges(array $nodes) {
|
||||||
|
$query = id(new PhabricatorEdgeQuery())
|
||||||
|
->withSourcePHIDs($nodes)
|
||||||
|
->withEdgeTypes(
|
||||||
|
array(
|
||||||
|
DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST,
|
||||||
|
DifferentialRevisionDependedOnByRevisionEdgeType::EDGECONST,
|
||||||
|
));
|
||||||
|
|
||||||
|
$query->execute();
|
||||||
|
|
||||||
|
$map = array();
|
||||||
|
foreach ($nodes as $node) {
|
||||||
|
$parents = $query->getDestinationPHIDs(
|
||||||
|
array($node),
|
||||||
|
array(
|
||||||
|
DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST,
|
||||||
|
));
|
||||||
|
|
||||||
|
$children = $query->getDestinationPHIDs(
|
||||||
|
array($node),
|
||||||
|
array(
|
||||||
|
DifferentialRevisionDependedOnByRevisionEdgeType::EDGECONST,
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->parentEdges[$node] = $parents;
|
||||||
|
$this->childEdges[$node] = $children;
|
||||||
|
|
||||||
|
$map[$node] = array_values(array_fuse($parents) + array_fuse($children));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -136,6 +136,10 @@ final class DifferentialRevision extends DifferentialDAO
|
||||||
return "D{$id}";
|
return "D{$id}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getURI() {
|
||||||
|
return '/'.$this->getMonogram();
|
||||||
|
}
|
||||||
|
|
||||||
public function setTitle($title) {
|
public function setTitle($title) {
|
||||||
$this->title = $title;
|
$this->title = $title;
|
||||||
if (!$this->getID()) {
|
if (!$this->getID()) {
|
||||||
|
@ -426,6 +430,31 @@ final class DifferentialRevision extends DifferentialDAO
|
||||||
return DifferentialRevisionStatus::isClosedStatus($this->getStatus());
|
return DifferentialRevisionStatus::isClosedStatus($this->getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getStatusIcon() {
|
||||||
|
$map = array(
|
||||||
|
ArcanistDifferentialRevisionStatus::NEEDS_REVIEW
|
||||||
|
=> 'fa-code grey',
|
||||||
|
ArcanistDifferentialRevisionStatus::NEEDS_REVISION
|
||||||
|
=> 'fa-refresh red',
|
||||||
|
ArcanistDifferentialRevisionStatus::CHANGES_PLANNED
|
||||||
|
=> 'fa-headphones red',
|
||||||
|
ArcanistDifferentialRevisionStatus::ACCEPTED
|
||||||
|
=> 'fa-check green',
|
||||||
|
ArcanistDifferentialRevisionStatus::CLOSED
|
||||||
|
=> 'fa-check-square-o black',
|
||||||
|
ArcanistDifferentialRevisionStatus::ABANDONED
|
||||||
|
=> 'fa-plane black',
|
||||||
|
);
|
||||||
|
|
||||||
|
return idx($map, $this->getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatusDisplayName() {
|
||||||
|
$status = $this->getStatus();
|
||||||
|
return ArcanistDifferentialRevisionStatus::getNameForRevisionStatus(
|
||||||
|
$status);
|
||||||
|
}
|
||||||
|
|
||||||
public function getFlag(PhabricatorUser $viewer) {
|
public function getFlag(PhabricatorUser $viewer) {
|
||||||
return $this->assertAttachedKey($this->flags, $viewer->getPHID());
|
return $this->assertAttachedKey($this->flags, $viewer->getPHID());
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,10 +104,6 @@ final class DifferentialRevisionListView extends AphrontView {
|
||||||
|
|
||||||
$modified = $revision->getDateModified();
|
$modified = $revision->getDateModified();
|
||||||
|
|
||||||
$status = $revision->getStatus();
|
|
||||||
$status_name =
|
|
||||||
ArcanistDifferentialRevisionStatus::getNameForRevisionStatus($status);
|
|
||||||
|
|
||||||
if (isset($icons['flag'])) {
|
if (isset($icons['flag'])) {
|
||||||
$item->addHeadIcon($icons['flag']);
|
$item->addHeadIcon($icons['flag']);
|
||||||
}
|
}
|
||||||
|
@ -155,29 +151,14 @@ final class DifferentialRevisionListView extends AphrontView {
|
||||||
$item->addAttribute(pht('Reviewers: %s', $reviewers));
|
$item->addAttribute(pht('Reviewers: %s', $reviewers));
|
||||||
$item->setEpoch($revision->getDateModified());
|
$item->setEpoch($revision->getDateModified());
|
||||||
|
|
||||||
switch ($status) {
|
if ($revision->isClosed()) {
|
||||||
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
|
|
||||||
$item->setStatusIcon('fa-code grey', pht('Needs Review'));
|
|
||||||
break;
|
|
||||||
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
|
|
||||||
$item->setStatusIcon('fa-refresh red', pht('Needs Revision'));
|
|
||||||
break;
|
|
||||||
case ArcanistDifferentialRevisionStatus::CHANGES_PLANNED:
|
|
||||||
$item->setStatusIcon('fa-headphones red', pht('Changes Planned'));
|
|
||||||
break;
|
|
||||||
case ArcanistDifferentialRevisionStatus::ACCEPTED:
|
|
||||||
$item->setStatusIcon('fa-check green', pht('Accepted'));
|
|
||||||
break;
|
|
||||||
case ArcanistDifferentialRevisionStatus::CLOSED:
|
|
||||||
$item->setDisabled(true);
|
$item->setDisabled(true);
|
||||||
$item->setStatusIcon('fa-check-square-o black', pht('Closed'));
|
|
||||||
break;
|
|
||||||
case ArcanistDifferentialRevisionStatus::ABANDONED:
|
|
||||||
$item->setDisabled(true);
|
|
||||||
$item->setStatusIcon('fa-plane black', pht('Abandoned'));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$item->setStatusIcon(
|
||||||
|
$revision->getStatusIcon(),
|
||||||
|
$revision->getStatusDisplayName());
|
||||||
|
|
||||||
$list->addItem($item);
|
$list->addItem($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,10 @@ final class DiffusionHistoryTableView extends DiffusionView {
|
||||||
|
|
||||||
$graph = null;
|
$graph = null;
|
||||||
if ($this->parents) {
|
if ($this->parents) {
|
||||||
$graph = $this->renderGraph();
|
$graph = id(new PHUIDiffGraphView())
|
||||||
|
->setIsHead($this->isHead)
|
||||||
|
->setIsTail($this->isTail)
|
||||||
|
->renderGraph($this->parents);
|
||||||
}
|
}
|
||||||
|
|
||||||
$show_builds = PhabricatorApplication::isClassInstalledForViewer(
|
$show_builds = PhabricatorApplication::isClassInstalledForViewer(
|
||||||
|
@ -219,166 +222,4 @@ final class DiffusionHistoryTableView extends DiffusionView {
|
||||||
return $view->render();
|
return $view->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Draw a merge/branch graph from the parent revision data. We're basically
|
|
||||||
* building up a bunch of strings like this:
|
|
||||||
*
|
|
||||||
* ^
|
|
||||||
* |^
|
|
||||||
* o|
|
|
||||||
* |o
|
|
||||||
* o
|
|
||||||
*
|
|
||||||
* ...which form an ASCII representation of the graph we eventually want to
|
|
||||||
* draw.
|
|
||||||
*
|
|
||||||
* NOTE: The actual implementation is black magic.
|
|
||||||
*/
|
|
||||||
private function renderGraph() {
|
|
||||||
// This keeps our accumulated information about each line of the
|
|
||||||
// merge/branch graph.
|
|
||||||
$graph = array();
|
|
||||||
|
|
||||||
// This holds the next commit we're looking for in each column of the
|
|
||||||
// graph.
|
|
||||||
$threads = array();
|
|
||||||
|
|
||||||
// This is the largest number of columns any row has, i.e. the width of
|
|
||||||
// the graph.
|
|
||||||
$count = 0;
|
|
||||||
|
|
||||||
foreach ($this->history as $key => $history) {
|
|
||||||
$joins = array();
|
|
||||||
$splits = array();
|
|
||||||
|
|
||||||
$parent_list = $this->parents[$history->getCommitIdentifier()];
|
|
||||||
|
|
||||||
// Look for some thread which has this commit as the next commit. If
|
|
||||||
// we find one, this commit goes on that thread. Otherwise, this commit
|
|
||||||
// goes on a new thread.
|
|
||||||
|
|
||||||
$line = '';
|
|
||||||
$found = false;
|
|
||||||
$pos = count($threads);
|
|
||||||
for ($n = 0; $n < $count; $n++) {
|
|
||||||
if (empty($threads[$n])) {
|
|
||||||
$line .= ' ';
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($threads[$n] == $history->getCommitIdentifier()) {
|
|
||||||
if ($found) {
|
|
||||||
$line .= ' ';
|
|
||||||
$joins[] = $n;
|
|
||||||
unset($threads[$n]);
|
|
||||||
} else {
|
|
||||||
$line .= 'o';
|
|
||||||
$found = true;
|
|
||||||
$pos = $n;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// We render a "|" for any threads which have a commit that we haven't
|
|
||||||
// seen yet, this is later drawn as a vertical line.
|
|
||||||
$line .= '|';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we didn't find the thread this commit goes on, start a new thread.
|
|
||||||
// We use "o" to mark the commit for the rendering engine, or "^" to
|
|
||||||
// indicate that there's nothing after it so the line from the commit
|
|
||||||
// upward should not be drawn.
|
|
||||||
|
|
||||||
if (!$found) {
|
|
||||||
if ($this->isHead) {
|
|
||||||
$line .= '^';
|
|
||||||
} else {
|
|
||||||
$line .= 'o';
|
|
||||||
foreach ($graph as $k => $meta) {
|
|
||||||
// Go back across all the lines we've already drawn and add a
|
|
||||||
// "|" to the end, since this is connected to some future commit
|
|
||||||
// we don't know about.
|
|
||||||
for ($jj = strlen($meta['line']); $jj <= $count; $jj++) {
|
|
||||||
$graph[$k]['line'] .= '|';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the next commit on this thread to the commit's first parent.
|
|
||||||
// This might have the effect of making a new thread.
|
|
||||||
$threads[$pos] = head($parent_list);
|
|
||||||
|
|
||||||
// If we made a new thread, increase the thread count.
|
|
||||||
$count = max($pos + 1, $count);
|
|
||||||
|
|
||||||
// Now, deal with splits (merges). I picked this terms opposite to the
|
|
||||||
// underlying repository term to confuse you.
|
|
||||||
foreach (array_slice($parent_list, 1) as $parent) {
|
|
||||||
$found = false;
|
|
||||||
|
|
||||||
// Try to find the other parent(s) in our existing threads. If we find
|
|
||||||
// them, split to that thread.
|
|
||||||
|
|
||||||
foreach ($threads as $idx => $thread_commit) {
|
|
||||||
if ($thread_commit == $parent) {
|
|
||||||
$found = true;
|
|
||||||
$splits[] = $idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we didn't find the parent, we don't know about it yet. Find the
|
|
||||||
// first free thread and add it as the "next" commit in that thread.
|
|
||||||
// This might create a new thread.
|
|
||||||
|
|
||||||
if (!$found) {
|
|
||||||
for ($n = 0; $n < $count; $n++) {
|
|
||||||
if (empty($threads[$n])) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$threads[$n] = $parent;
|
|
||||||
$splits[] = $n;
|
|
||||||
$count = max($n + 1, $count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$graph[] = array(
|
|
||||||
'line' => $line,
|
|
||||||
'split' => $splits,
|
|
||||||
'join' => $joins,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is the last page in history, replace the "o" with an "x" so we
|
|
||||||
// do not draw a connecting line downward, and replace "^" with an "X" for
|
|
||||||
// repositories with exactly one commit.
|
|
||||||
if ($this->isTail && $graph) {
|
|
||||||
$last = array_pop($graph);
|
|
||||||
$last['line'] = str_replace('o', 'x', $last['line']);
|
|
||||||
$last['line'] = str_replace('^', 'X', $last['line']);
|
|
||||||
$graph[] = $last;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render into tags for the behavior.
|
|
||||||
|
|
||||||
foreach ($graph as $k => $meta) {
|
|
||||||
$graph[$k] = javelin_tag(
|
|
||||||
'div',
|
|
||||||
array(
|
|
||||||
'sigil' => 'commit-graph',
|
|
||||||
'meta' => $meta,
|
|
||||||
),
|
|
||||||
'');
|
|
||||||
}
|
|
||||||
|
|
||||||
Javelin::initBehavior(
|
|
||||||
'diffusion-commit-graph',
|
|
||||||
array(
|
|
||||||
'count' => $count,
|
|
||||||
));
|
|
||||||
|
|
||||||
return $graph;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
171
src/infrastructure/diff/view/PHUIDiffGraphView.php
Normal file
171
src/infrastructure/diff/view/PHUIDiffGraphView.php
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PHUIDiffGraphView extends Phobject {
|
||||||
|
|
||||||
|
private $isHead = true;
|
||||||
|
private $isTail = true;
|
||||||
|
|
||||||
|
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 renderGraph(array $parents) {
|
||||||
|
// This keeps our accumulated information about each line of the
|
||||||
|
// merge/branch graph.
|
||||||
|
$graph = array();
|
||||||
|
|
||||||
|
// This holds the next commit we're looking for in each column of the
|
||||||
|
// graph.
|
||||||
|
$threads = array();
|
||||||
|
|
||||||
|
// This is the largest number of columns any row has, i.e. the width of
|
||||||
|
// the graph.
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
foreach ($parents as $cursor => $parent_list) {
|
||||||
|
$joins = array();
|
||||||
|
$splits = array();
|
||||||
|
|
||||||
|
// Look for some thread which has this commit as the next commit. If
|
||||||
|
// we find one, this commit goes on that thread. Otherwise, this commit
|
||||||
|
// goes on a new thread.
|
||||||
|
|
||||||
|
$line = '';
|
||||||
|
$found = false;
|
||||||
|
$pos = count($threads);
|
||||||
|
for ($n = 0; $n < $count; $n++) {
|
||||||
|
if (empty($threads[$n])) {
|
||||||
|
$line .= ' ';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($threads[$n] == $cursor) {
|
||||||
|
if ($found) {
|
||||||
|
$line .= ' ';
|
||||||
|
$joins[] = $n;
|
||||||
|
unset($threads[$n]);
|
||||||
|
} else {
|
||||||
|
$line .= 'o';
|
||||||
|
$found = true;
|
||||||
|
$pos = $n;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// We render a "|" for any threads which have a commit that we haven't
|
||||||
|
// seen yet, this is later drawn as a vertical line.
|
||||||
|
$line .= '|';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't find the thread this commit goes on, start a new thread.
|
||||||
|
// We use "o" to mark the commit for the rendering engine, or "^" to
|
||||||
|
// indicate that there's nothing after it so the line from the commit
|
||||||
|
// upward should not be drawn.
|
||||||
|
|
||||||
|
if (!$found) {
|
||||||
|
if ($this->getIsHead()) {
|
||||||
|
$line .= '^';
|
||||||
|
} else {
|
||||||
|
$line .= 'o';
|
||||||
|
foreach ($graph as $k => $meta) {
|
||||||
|
// Go back across all the lines we've already drawn and add a
|
||||||
|
// "|" to the end, since this is connected to some future commit
|
||||||
|
// we don't know about.
|
||||||
|
for ($jj = strlen($meta['line']); $jj <= $count; $jj++) {
|
||||||
|
$graph[$k]['line'] .= '|';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the next commit on this thread to the commit's first parent.
|
||||||
|
// This might have the effect of making a new thread.
|
||||||
|
$threads[$pos] = head($parent_list);
|
||||||
|
|
||||||
|
// If we made a new thread, increase the thread count.
|
||||||
|
$count = max($pos + 1, $count);
|
||||||
|
|
||||||
|
// Now, deal with splits (merges). I picked this terms opposite to the
|
||||||
|
// underlying repository term to confuse you.
|
||||||
|
foreach (array_slice($parent_list, 1) as $parent) {
|
||||||
|
$found = false;
|
||||||
|
|
||||||
|
// Try to find the other parent(s) in our existing threads. If we find
|
||||||
|
// them, split to that thread.
|
||||||
|
|
||||||
|
foreach ($threads as $idx => $thread_commit) {
|
||||||
|
if ($thread_commit == $parent) {
|
||||||
|
$found = true;
|
||||||
|
$splits[] = $idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't find the parent, we don't know about it yet. Find the
|
||||||
|
// first free thread and add it as the "next" commit in that thread.
|
||||||
|
// This might create a new thread.
|
||||||
|
|
||||||
|
if (!$found) {
|
||||||
|
for ($n = 0; $n < $count; $n++) {
|
||||||
|
if (empty($threads[$n])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$threads[$n] = $parent;
|
||||||
|
$splits[] = $n;
|
||||||
|
$count = max($n + 1, $count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$graph[] = array(
|
||||||
|
'line' => $line,
|
||||||
|
'split' => $splits,
|
||||||
|
'join' => $joins,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the last page in history, replace the "o" with an "x" so we
|
||||||
|
// do not draw a connecting line downward, and replace "^" with an "X" for
|
||||||
|
// repositories with exactly one commit.
|
||||||
|
if ($this->getIsTail() && $graph) {
|
||||||
|
$last = array_pop($graph);
|
||||||
|
$last['line'] = str_replace('o', 'x', $last['line']);
|
||||||
|
$last['line'] = str_replace('^', 'X', $last['line']);
|
||||||
|
$graph[] = $last;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render into tags for the behavior.
|
||||||
|
|
||||||
|
foreach ($graph as $k => $meta) {
|
||||||
|
$graph[$k] = javelin_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'sigil' => 'commit-graph',
|
||||||
|
'meta' => $meta,
|
||||||
|
),
|
||||||
|
'');
|
||||||
|
}
|
||||||
|
|
||||||
|
Javelin::initBehavior(
|
||||||
|
'diffusion-commit-graph',
|
||||||
|
array(
|
||||||
|
'count' => $count,
|
||||||
|
));
|
||||||
|
|
||||||
|
return $graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ final class PHUITabView extends AphrontTagView {
|
||||||
private $key;
|
private $key;
|
||||||
private $keyLocked;
|
private $keyLocked;
|
||||||
private $contentID;
|
private $contentID;
|
||||||
|
private $color;
|
||||||
|
|
||||||
public function setKey($key) {
|
public function setKey($key) {
|
||||||
if ($this->keyLocked) {
|
if ($this->keyLocked) {
|
||||||
|
@ -58,8 +59,17 @@ final class PHUITabView extends AphrontTagView {
|
||||||
return $this->contentID;
|
return $this->contentID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setColor($color) {
|
||||||
|
$this->color = $color;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColor() {
|
||||||
|
return $this->color;
|
||||||
|
}
|
||||||
|
|
||||||
public function newMenuItem() {
|
public function newMenuItem() {
|
||||||
return id(new PHUIListItemView())
|
$item = id(new PHUIListItemView())
|
||||||
->setName($this->getName())
|
->setName($this->getName())
|
||||||
->setKey($this->getKey())
|
->setKey($this->getKey())
|
||||||
->setType(PHUIListItemView::TYPE_LINK)
|
->setType(PHUIListItemView::TYPE_LINK)
|
||||||
|
@ -69,6 +79,13 @@ final class PHUITabView extends AphrontTagView {
|
||||||
array(
|
array(
|
||||||
'tabKey' => $this->getKey(),
|
'tabKey' => $this->getKey(),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
$color = $this->getColor();
|
||||||
|
if ($color !== null) {
|
||||||
|
$item->setStatusColor($color);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue