2011-03-09 02:31:44 +01:00
|
|
|
<?php
|
|
|
|
|
2011-03-13 01:17:34 +01:00
|
|
|
final class DiffusionHistoryTableView extends DiffusionView {
|
2011-03-09 02:31:44 +01:00
|
|
|
|
|
|
|
private $history;
|
2012-06-23 01:52:08 +02:00
|
|
|
private $revisions = array();
|
2011-04-03 01:39:23 +02:00
|
|
|
private $handles = array();
|
2012-03-24 01:11:15 +01:00
|
|
|
private $isHead;
|
|
|
|
private $parents;
|
2011-03-09 02:31:44 +01:00
|
|
|
|
|
|
|
public function setHistory(array $history) {
|
2012-04-04 01:22:31 +02:00
|
|
|
assert_instances_of($history, 'DiffusionPathChange');
|
2011-03-09 02:31:44 +01:00
|
|
|
$this->history = $history;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-06-23 01:52:08 +02:00
|
|
|
public function loadRevisions() {
|
|
|
|
$commit_phids = array();
|
|
|
|
foreach ($this->history as $item) {
|
|
|
|
if ($item->getCommit()) {
|
|
|
|
$commit_phids[] = $item->getCommit()->getPHID();
|
|
|
|
}
|
|
|
|
}
|
Add an "importing" state to repositories and clean up the UI
Summary:
Fixes T3217. Ref T776. Ref T1493. Broadly, this introduces a mechanism which works like this:
- When a repository is created, we set an "importing" flag.
- After discovery completes, we check if a repository has no importing commits. Basically, this is the first time we catch up to HEAD.
- If we're caught up, clear the "importing" flag.
This flag lets us fix some issues:
- T3217. Currently, when you import a new repository and users have rules like "Email me on every commit ever" or "trigger an audit on every commit", we take a bunch of publish actions. Instead, implicitly disable publishing during import.
- An imported but un-pulled repository currently has an incomprehensible error on `/diffusion/X/`. Fix that.
- Show more cues in the UI about importing.
- Made some exceptions more specific.
Test Plan:
This is the new screen for a completely new repo, replacing a giant exception:
{F75443}
- Created a repository, saw it "importing".
- Pulled and discovered it.
- Processed its commits.
- Ran discovery again, saw import flag clear.
- Also this repository was empty, which hit some of the other code.
This is the new "parsed empty repository" UI, which isn't good, but is less broken:
{F75446}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, hach-que
Maniphest Tasks: T3607, T1493, T776, T3217
Differential Revision: https://secure.phabricator.com/D7429
2013-10-27 03:59:57 +01:00
|
|
|
|
|
|
|
// TODO: Get rid of this.
|
2012-06-23 01:52:08 +02:00
|
|
|
$this->revisions = id(new DifferentialRevision())
|
|
|
|
->loadIDsByCommitPHIDs($commit_phids);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-04-03 01:39:23 +02:00
|
|
|
public function setHandles(array $handles) {
|
2012-04-04 01:22:31 +02:00
|
|
|
assert_instances_of($handles, 'PhabricatorObjectHandle');
|
2011-04-03 01:39:23 +02:00
|
|
|
$this->handles = $handles;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-03-23 23:32:26 +01:00
|
|
|
public function getRequiredHandlePHIDs() {
|
|
|
|
$phids = array();
|
|
|
|
foreach ($this->history as $item) {
|
|
|
|
$data = $item->getCommitData();
|
|
|
|
if ($data) {
|
|
|
|
if ($data->getCommitDetail('authorPHID')) {
|
|
|
|
$phids[$data->getCommitDetail('authorPHID')] = true;
|
|
|
|
}
|
2012-05-23 17:34:36 +02:00
|
|
|
if ($data->getCommitDetail('committerPHID')) {
|
|
|
|
$phids[$data->getCommitDetail('committerPHID')] = true;
|
|
|
|
}
|
2012-03-23 23:32:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return array_keys($phids);
|
|
|
|
}
|
|
|
|
|
2012-03-24 01:11:15 +01:00
|
|
|
public function setParents(array $parents) {
|
|
|
|
$this->parents = $parents;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setIsHead($is_head) {
|
|
|
|
$this->isHead = $is_head;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-03-09 02:31:44 +01:00
|
|
|
public function render() {
|
2011-03-13 01:17:34 +01:00
|
|
|
$drequest = $this->getDiffusionRequest();
|
|
|
|
|
2011-04-03 01:39:23 +02:00
|
|
|
$handles = $this->handles;
|
|
|
|
|
2012-03-24 01:11:15 +01:00
|
|
|
$graph = null;
|
|
|
|
if ($this->parents) {
|
|
|
|
$graph = $this->renderGraph();
|
|
|
|
}
|
|
|
|
|
2011-03-09 02:31:44 +01:00
|
|
|
$rows = array();
|
2012-03-24 01:11:15 +01:00
|
|
|
$ii = 0;
|
2011-03-09 02:31:44 +01:00
|
|
|
foreach ($this->history as $history) {
|
2011-03-13 07:51:40 +01:00
|
|
|
$epoch = $history->getEpoch();
|
|
|
|
|
|
|
|
if ($epoch) {
|
2012-12-21 00:04:20 +01:00
|
|
|
$date = phabricator_date($epoch, $this->user);
|
|
|
|
$time = phabricator_time($epoch, $this->user);
|
2011-03-13 07:51:40 +01:00
|
|
|
} else {
|
|
|
|
$date = null;
|
|
|
|
$time = null;
|
|
|
|
}
|
|
|
|
|
2011-04-03 01:39:23 +02:00
|
|
|
$data = $history->getCommitData();
|
2012-05-23 17:34:36 +02:00
|
|
|
$author_phid = $committer = $committer_phid = null;
|
2011-04-03 01:39:23 +02:00
|
|
|
if ($data) {
|
|
|
|
$author_phid = $data->getCommitDetail('authorPHID');
|
2012-05-23 17:34:36 +02:00
|
|
|
$committer_phid = $data->getCommitDetail('committerPHID');
|
|
|
|
$committer = $data->getCommitDetail('committer');
|
2011-04-03 01:39:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($author_phid && isset($handles[$author_phid])) {
|
|
|
|
$author = $handles[$author_phid]->renderLink();
|
|
|
|
} else {
|
2012-08-09 03:26:23 +02:00
|
|
|
$author = self::renderName($history->getAuthorName());
|
2011-04-03 01:39:23 +02:00
|
|
|
}
|
|
|
|
|
2012-06-07 21:26:19 +02:00
|
|
|
$different_committer = false;
|
|
|
|
if ($committer_phid) {
|
|
|
|
$different_committer = ($committer_phid != $author_phid);
|
|
|
|
} else if ($committer != '') {
|
|
|
|
$different_committer = ($committer != $history->getAuthorName());
|
|
|
|
}
|
|
|
|
if ($different_committer) {
|
2012-05-23 17:34:36 +02:00
|
|
|
if ($committer_phid && isset($handles[$committer_phid])) {
|
|
|
|
$committer = $handles[$committer_phid]->renderLink();
|
|
|
|
} else {
|
2012-08-09 03:26:23 +02:00
|
|
|
$committer = self::renderName($committer);
|
2012-05-23 17:34:36 +02:00
|
|
|
}
|
2013-02-13 23:50:15 +01:00
|
|
|
$author = hsprintf('%s/%s', $author, $committer);
|
2012-05-23 17:34:36 +02:00
|
|
|
}
|
|
|
|
|
2013-10-30 21:15:14 +01:00
|
|
|
// We can show details once the message and change have been imported.
|
|
|
|
$partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE |
|
|
|
|
PhabricatorRepositoryCommit::IMPORTED_CHANGE;
|
|
|
|
|
2012-05-10 02:28:57 +02:00
|
|
|
$commit = $history->getCommit();
|
2013-10-30 21:15:14 +01:00
|
|
|
if ($commit && $commit->isPartiallyImported($partial_import) && $data) {
|
2014-05-13 04:57:12 +02:00
|
|
|
$summary = AphrontTableView::renderSingleDisplayLine(
|
|
|
|
$history->getSummary());
|
2012-05-10 02:28:57 +02:00
|
|
|
} else {
|
2014-05-13 04:57:12 +02:00
|
|
|
$summary = phutil_tag('em', array(), "Importing\xE2\x80\xA6");
|
2012-05-10 02:28:57 +02:00
|
|
|
}
|
|
|
|
|
2011-03-09 02:31:44 +01:00
|
|
|
$rows[] = array(
|
2012-03-24 01:11:15 +01:00
|
|
|
$graph ? $graph[$ii++] : null,
|
2011-03-13 01:17:34 +01:00
|
|
|
self::linkCommit(
|
|
|
|
$drequest->getRepository(),
|
|
|
|
$history->getCommitIdentifier()),
|
2012-06-23 01:52:08 +02:00
|
|
|
($commit ?
|
|
|
|
self::linkRevision(idx($this->revisions, $commit->getPHID())) :
|
|
|
|
null),
|
2014-05-13 04:57:12 +02:00
|
|
|
$author,
|
|
|
|
$summary,
|
2011-03-13 07:51:40 +01:00
|
|
|
$date,
|
|
|
|
$time,
|
2011-03-09 02:31:44 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$view = new AphrontTableView($rows);
|
|
|
|
$view->setHeaders(
|
|
|
|
array(
|
2012-03-24 01:11:15 +01:00
|
|
|
'',
|
2013-05-11 17:23:19 +02:00
|
|
|
pht('Commit'),
|
|
|
|
pht('Revision'),
|
|
|
|
pht('Author/Committer'),
|
|
|
|
pht('Details'),
|
2014-05-13 04:57:12 +02:00
|
|
|
pht('Date'),
|
|
|
|
pht('Time'),
|
2011-03-13 01:17:34 +01:00
|
|
|
));
|
|
|
|
$view->setColumnClasses(
|
|
|
|
array(
|
2012-03-24 01:11:15 +01:00
|
|
|
'threads',
|
2011-03-13 01:17:34 +01:00
|
|
|
'n',
|
2012-06-23 01:52:08 +02:00
|
|
|
'n',
|
2011-03-13 01:17:34 +01:00
|
|
|
'',
|
2014-05-13 04:57:12 +02:00
|
|
|
'wide',
|
2011-03-13 01:17:34 +01:00
|
|
|
'',
|
2011-03-13 07:51:40 +01:00
|
|
|
'right',
|
2011-03-09 02:31:44 +01:00
|
|
|
));
|
2012-03-24 01:11:15 +01:00
|
|
|
$view->setColumnVisibility(
|
|
|
|
array(
|
|
|
|
$graph ? true : false,
|
|
|
|
));
|
2014-05-13 04:57:12 +02:00
|
|
|
$view->setDeviceVisibility(
|
|
|
|
array(
|
|
|
|
$graph ? true : false,
|
|
|
|
true,
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
));
|
2011-03-09 02:31:44 +01:00
|
|
|
return $view->render();
|
|
|
|
}
|
|
|
|
|
2012-03-24 01:11:15 +01:00
|
|
|
/**
|
|
|
|
* 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 eventaully 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.
|
|
|
|
|
2012-03-29 22:24:06 +02:00
|
|
|
foreach ($threads as $idx => $thread_commit) {
|
2012-03-24 01:11:15 +01:00
|
|
|
if ($thread_commit == $parent) {
|
|
|
|
$found = true;
|
2012-03-29 22:24:06 +02:00
|
|
|
$splits[] = $idx;
|
2012-03-24 01:11:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Render into tags for the behavior.
|
|
|
|
|
|
|
|
foreach ($graph as $k => $meta) {
|
2013-01-25 21:57:17 +01:00
|
|
|
$graph[$k] = javelin_tag(
|
2012-03-24 01:11:15 +01:00
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'sigil' => 'commit-graph',
|
|
|
|
'meta' => $meta,
|
|
|
|
),
|
|
|
|
'');
|
|
|
|
}
|
|
|
|
|
|
|
|
Javelin::initBehavior(
|
|
|
|
'diffusion-commit-graph',
|
|
|
|
array(
|
|
|
|
'count' => $count,
|
|
|
|
));
|
|
|
|
|
|
|
|
return $graph;
|
|
|
|
}
|
|
|
|
|
2011-03-09 02:31:44 +01:00
|
|
|
}
|