mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-29 18:22:41 +01:00
Improve Diffusion blame views
Summary: - Make some effort to simplify the code. - Make "Skip Past This Commit" work in Git and Mercurial. - Make blame work in Mercurial. - Add tooltip hover state to show more information about commits. Test Plan: Viewed blame views in SVN, Git, Hg. Clicked line numbers, hovered/clicked commits, hovered/clicked "blame past..." Reviewers: btrahan, vrana, jungejason Reviewed By: vrana CC: aran Maniphest Tasks: T378 Differential Revision: https://secure.phabricator.com/D2142
This commit is contained in:
parent
df67401e24
commit
ee278a302e
10 changed files with 362 additions and 134 deletions
|
@ -295,7 +295,7 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'diffusion-source-css' =>
|
'diffusion-source-css' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/5f5ac1d6/rsrc/css/application/diffusion/diffusion-source.css',
|
'uri' => '/res/9f79ed12/rsrc/css/application/diffusion/diffusion-source.css',
|
||||||
'type' => 'css',
|
'type' => 'css',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -24,6 +24,7 @@ final class PhabricatorAuditCommitQuery {
|
||||||
private $commitPHIDs;
|
private $commitPHIDs;
|
||||||
private $authorPHIDs;
|
private $authorPHIDs;
|
||||||
private $packagePHIDs;
|
private $packagePHIDs;
|
||||||
|
private $identifiers = array();
|
||||||
|
|
||||||
private $needCommitData;
|
private $needCommitData;
|
||||||
|
|
||||||
|
@ -51,6 +52,11 @@ final class PhabricatorAuditCommitQuery {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withIdentifiers($repository_id, array $identifiers) {
|
||||||
|
$this->identifiers[] = array($repository_id, $identifiers);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function needCommitData($need) {
|
public function needCommitData($need) {
|
||||||
$this->needCommitData = $need;
|
$this->needCommitData = $need;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -152,6 +158,23 @@ final class PhabricatorAuditCommitQuery {
|
||||||
$this->packagePHIDs);
|
$this->packagePHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->identifiers) {
|
||||||
|
$clauses = array();
|
||||||
|
foreach ($this->identifiers as $spec) {
|
||||||
|
list($repository_id, $identifiers) = $spec;
|
||||||
|
if ($identifiers) {
|
||||||
|
$clauses[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'c.repositoryID = %d AND c.commitIdentifier IN (%Ls)',
|
||||||
|
$repository_id,
|
||||||
|
$identifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($clauses) {
|
||||||
|
$where[] = '('.implode(') OR (', $clauses).')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$status = $this->status;
|
$status = $this->status;
|
||||||
switch ($status) {
|
switch ($status) {
|
||||||
case self::STATUS_OPEN:
|
case self::STATUS_OPEN:
|
||||||
|
|
|
@ -23,11 +23,17 @@ final class DiffusionBrowseFileController extends DiffusionController {
|
||||||
public function processRequest() {
|
public function processRequest() {
|
||||||
|
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
|
|
||||||
$drequest = $this->getDiffusionRequest();
|
$drequest = $this->getDiffusionRequest();
|
||||||
|
|
||||||
|
$before = $request->getStr('before');
|
||||||
|
if ($before) {
|
||||||
|
return $this->buildBeforeResponse($before);
|
||||||
|
}
|
||||||
|
|
||||||
$path = $drequest->getPath();
|
$path = $drequest->getPath();
|
||||||
$selected = $request->getStr('view');
|
$selected = $request->getStr('view');
|
||||||
$needs_blame = ($selected == 'blame' || $selected == 'plainblame');
|
$needs_blame = ($selected == 'blame' || $selected == 'plainblame');
|
||||||
|
|
||||||
$file_query = DiffusionFileContentQuery::newFromDiffusionRequest(
|
$file_query = DiffusionFileContentQuery::newFromDiffusionRequest(
|
||||||
$this->diffusionRequest);
|
$this->diffusionRequest);
|
||||||
$file_query->setNeedsBlame($needs_blame);
|
$file_query->setNeedsBlame($needs_blame);
|
||||||
|
@ -99,8 +105,6 @@ final class DiffusionBrowseFileController extends DiffusionController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: blame of blame.
|
|
||||||
switch ($selected) {
|
switch ($selected) {
|
||||||
case 'plain':
|
case 'plain':
|
||||||
$style =
|
$style =
|
||||||
|
@ -166,7 +170,7 @@ final class DiffusionBrowseFileController extends DiffusionController {
|
||||||
$corpus = phutil_render_tag(
|
$corpus = phutil_render_tag(
|
||||||
'div',
|
'div',
|
||||||
array(
|
array(
|
||||||
'style' => 'padding: 0pt 2em;',
|
'style' => 'padding: 0 2em;',
|
||||||
),
|
),
|
||||||
$corpus_table);
|
$corpus_table);
|
||||||
|
|
||||||
|
@ -245,20 +249,13 @@ final class DiffusionBrowseFileController extends DiffusionController {
|
||||||
DiffusionFileContentQuery $file_query,
|
DiffusionFileContentQuery $file_query,
|
||||||
$selected) {
|
$selected) {
|
||||||
|
|
||||||
$last_rev = null;
|
|
||||||
$color = '#eeeeee';
|
|
||||||
$rows = array();
|
|
||||||
$n = 1;
|
|
||||||
$view = $this->getRequest()->getStr('view');
|
|
||||||
|
|
||||||
if ($blame_dict) {
|
if ($blame_dict) {
|
||||||
$epoch_list = ipull($blame_dict, 'epoch');
|
$epoch_list = ipull($blame_dict, 'epoch');
|
||||||
$epoch_max = max($epoch_list);
|
|
||||||
$epoch_min = min($epoch_list);
|
$epoch_min = min($epoch_list);
|
||||||
$epoch_range = $epoch_max - $epoch_min + 1;
|
$epoch_max = max($epoch_list);
|
||||||
|
$epoch_range = ($epoch_max - $epoch_min) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
$targ = '';
|
|
||||||
$min_line = 0;
|
$min_line = 0;
|
||||||
$line = $drequest->getLine();
|
$line = $drequest->getLine();
|
||||||
if (strpos($line, '-') !== false) {
|
if (strpos($line, '-') !== false) {
|
||||||
|
@ -270,109 +267,219 @@ final class DiffusionBrowseFileController extends DiffusionController {
|
||||||
$max_line = $line;
|
$max_line = $line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$display = array();
|
||||||
|
|
||||||
|
$line_number = 1;
|
||||||
|
$last_rev = null;
|
||||||
|
$color = null;
|
||||||
foreach ($text_list as $k => $line) {
|
foreach ($text_list as $k => $line) {
|
||||||
|
$display_line = array(
|
||||||
|
'color' => null,
|
||||||
|
'epoch' => null,
|
||||||
|
'commit' => null,
|
||||||
|
'author' => null,
|
||||||
|
'target' => null,
|
||||||
|
'highlighted' => null,
|
||||||
|
'line' => $line_number,
|
||||||
|
'data' => $line,
|
||||||
|
);
|
||||||
|
|
||||||
if ($needs_blame) {
|
if ($needs_blame) {
|
||||||
// If the line's rev is same as the line above, show empty content
|
// If the line's rev is same as the line above, show empty content
|
||||||
// with same color; otherwise generate blame info. The newer a change
|
// with same color; otherwise generate blame info. The newer a change
|
||||||
// is, the darker the color.
|
// is, the more saturated the color.
|
||||||
$rev = $rev_list[$k];
|
|
||||||
|
// TODO: SVN doesn't always give us blame for the last line, if empty?
|
||||||
|
// Bug with our stuff or with SVN?
|
||||||
|
$rev = idx($rev_list, $k, $last_rev);
|
||||||
|
|
||||||
if ($last_rev == $rev) {
|
if ($last_rev == $rev) {
|
||||||
$blame_info =
|
$display_line['color'] = $color;
|
||||||
($file_query->getSupportsBlameOnBlame() ?
|
|
||||||
'<th style="background: '.$color.'; width: 2em;"></th>' : '').
|
|
||||||
'<th style="background: '.$color.'; width: 9em;"></th>'.
|
|
||||||
'<th style="background: '.$color.'"></th>';
|
|
||||||
} else {
|
} else {
|
||||||
|
$blame = $blame_dict[$rev];
|
||||||
|
|
||||||
$revision_time = null;
|
$color_ratio = ($blame['epoch'] - $epoch_min) / $epoch_range;
|
||||||
if ($blame_dict) {
|
|
||||||
$color_number = (int)(0xEE -
|
|
||||||
0xEE * ($blame_dict[$rev]['epoch'] - $epoch_min) / $epoch_range);
|
|
||||||
$color = sprintf('#%02xee%02x', $color_number, $color_number);
|
|
||||||
$revision_time = phabricator_datetime(
|
|
||||||
$blame_dict[$rev]['epoch'],
|
|
||||||
$this->getRequest()->getUser());
|
|
||||||
}
|
|
||||||
|
|
||||||
$revision_link = self::renderRevision(
|
$color_value = 0xF6 * (1.0 - $color_ratio);
|
||||||
$drequest,
|
$color = sprintf(
|
||||||
substr($rev, 0, 7));
|
'#%02x%02x%02x',
|
||||||
|
$color_value,
|
||||||
|
0xF6,
|
||||||
|
$color_value);
|
||||||
|
|
||||||
if (!$file_query->getSupportsBlameOnBlame()) {
|
$display_line['epoch'] = $blame['epoch'];
|
||||||
$prev_link = '';
|
$display_line['color'] = $color;
|
||||||
} else {
|
$display_line['commit'] = $rev;
|
||||||
$prev_rev = $file_query->getPrevRev($rev);
|
|
||||||
$path = $drequest->getPath();
|
|
||||||
$prev_link = self::renderBrowse(
|
|
||||||
$drequest,
|
|
||||||
$path,
|
|
||||||
"\xC2\xAB",
|
|
||||||
$prev_rev,
|
|
||||||
$n,
|
|
||||||
$selected,
|
|
||||||
'Blame previous revision');
|
|
||||||
$prev_link = phutil_render_tag(
|
|
||||||
'th',
|
|
||||||
array(
|
|
||||||
'class' => 'diffusion-wide-link',
|
|
||||||
'style' => 'background: '.$color.'; width: 2em;',
|
|
||||||
),
|
|
||||||
$prev_link);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($blame_dict[$rev]['handle'])) {
|
if (isset($blame_dict[$rev]['handle'])) {
|
||||||
$author_link = $blame_dict[$rev]['handle']->renderLink();
|
$author_link = $blame_dict[$rev]['handle']->renderLink();
|
||||||
} else {
|
} else {
|
||||||
$author_link = phutil_escape_html($blame_dict[$rev]['author']);
|
$author_link = phutil_render_tag(
|
||||||
|
'span',
|
||||||
|
array(
|
||||||
|
),
|
||||||
|
phutil_escape_html($blame_dict[$rev]['author']));
|
||||||
}
|
}
|
||||||
$blame_info =
|
$display_line['author'] = $author_link;
|
||||||
$prev_link .
|
|
||||||
'<th style="background: '.$color.'; width: 12em;" title="'.
|
|
||||||
phutil_escape_html($revision_time).'">'.$revision_link.'</th>'.
|
|
||||||
'<th style="background: '.$color.'; width: 12em'.
|
|
||||||
'; font-weight: normal; color: #333;">'.$author_link.'</th>';
|
|
||||||
$last_rev = $rev;
|
$last_rev = $rev;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$blame_info = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Highlight the line of interest if needed.
|
if ($min_line) {
|
||||||
if ($min_line > 0 && ($n >= $min_line && $n <= $max_line)) {
|
if ($line_number == $min_line) {
|
||||||
$tr = '<tr style="background: #ffff00;">';
|
$display_line['target'] = true;
|
||||||
if ($targ == '') {
|
}
|
||||||
$targ = '<a id="scroll_target"></a>';
|
if ($line_number >= $min_line && $line_number <= $max_line) {
|
||||||
Javelin::initBehavior('diffusion-jump-to',
|
$display_line['highlighted'] = true;
|
||||||
array('target' => 'scroll_target'));
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$tr = '<tr>';
|
|
||||||
$targ = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$href = $drequest->generateURI(
|
$display[] = $display_line;
|
||||||
|
++$line_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
$commits = id(new PhabricatorAuditCommitQuery())
|
||||||
|
->withIdentifiers(
|
||||||
|
$drequest->getRepository()->getID(),
|
||||||
|
array_filter(ipull($display, 'commit')))
|
||||||
|
->needCommitData(true)
|
||||||
|
->execute();
|
||||||
|
$commits = mpull($commits, null, 'getCommitIdentifier');
|
||||||
|
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$user = $request->getUser();
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
foreach ($display as $line) {
|
||||||
|
|
||||||
|
$line_href = $drequest->generateURI(
|
||||||
array(
|
array(
|
||||||
'action' => 'browse',
|
'action' => 'browse',
|
||||||
|
'line' => $line['line'],
|
||||||
'stable' => true,
|
'stable' => true,
|
||||||
));
|
));
|
||||||
$href = (string)$href;
|
|
||||||
|
|
||||||
$query_params = null;
|
$line_href->setQueryParams($request->getRequestURI()->getQueryParams());
|
||||||
if ($view) {
|
|
||||||
$query_params = '?view='.$view;
|
$blame = array();
|
||||||
|
if ($line['color']) {
|
||||||
|
$color = $line['color'];
|
||||||
|
|
||||||
|
$before_link = null;
|
||||||
|
$commit_link = null;
|
||||||
|
if (idx($line, 'commit')) {
|
||||||
|
$commit = $line['commit'];
|
||||||
|
|
||||||
|
$summary = 'Unknown';
|
||||||
|
if (idx($commits, $commit)) {
|
||||||
|
$summary = $commits[$commit]->getCommitData()->getSummary();
|
||||||
}
|
}
|
||||||
|
|
||||||
$link = phutil_render_tag(
|
$tooltip = phabricator_date(
|
||||||
|
$line['epoch'],
|
||||||
|
$user)." \xC2\xB7 ".$summary;
|
||||||
|
|
||||||
|
Javelin::initBehavior('phabricator-tooltips', array());
|
||||||
|
require_celerity_resource('aphront-tooltip-css');
|
||||||
|
|
||||||
|
$commit_link = javelin_render_tag(
|
||||||
'a',
|
'a',
|
||||||
array(
|
array(
|
||||||
'href' => $href.'$'.$n.$query_params,
|
'href' => $drequest->generateURI(
|
||||||
|
array(
|
||||||
|
'action' => 'commit',
|
||||||
|
'commit' => $line['commit'],
|
||||||
|
)),
|
||||||
|
'sigil' => 'has-tooltip',
|
||||||
|
'meta' => array(
|
||||||
|
'tip' => $tooltip,
|
||||||
|
'align' => 'E',
|
||||||
|
'size' => 600,
|
||||||
),
|
),
|
||||||
$n);
|
),
|
||||||
|
phutil_escape_html(phutil_utf8_shorten($line['commit'], 9, '')));
|
||||||
|
|
||||||
$rows[] = $tr.$blame_info.
|
$before_link = javelin_render_tag(
|
||||||
'<th class="diffusion-wide-link">'.$link.'</th>'.
|
'a',
|
||||||
'<td>'.$targ.$line.'</td></tr>';
|
array(
|
||||||
++$n;
|
'href' => $line_href->alter('before', $commit),
|
||||||
|
'sigil' => 'has-tooltip',
|
||||||
|
'meta' => array(
|
||||||
|
'tip' => 'Skip Past This Commit',
|
||||||
|
'align' => 'E',
|
||||||
|
'size' => 300,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"\xC2\xAB");
|
||||||
|
}
|
||||||
|
|
||||||
|
$blame[] = phutil_render_tag(
|
||||||
|
'th',
|
||||||
|
array(
|
||||||
|
'class' => 'diffusion-blame-link',
|
||||||
|
'style' => 'background: '.$color,
|
||||||
|
),
|
||||||
|
$before_link);
|
||||||
|
|
||||||
|
$blame[] = phutil_render_tag(
|
||||||
|
'th',
|
||||||
|
array(
|
||||||
|
'class' => 'diffusion-rev-link',
|
||||||
|
'style' => 'background: '.$color,
|
||||||
|
),
|
||||||
|
$commit_link);
|
||||||
|
|
||||||
|
$blame[] = phutil_render_tag(
|
||||||
|
'th',
|
||||||
|
array(
|
||||||
|
'class' => 'diffusion-author-link',
|
||||||
|
'style' => 'background: '.$color,
|
||||||
|
),
|
||||||
|
idx($line, 'author'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$line_link = phutil_render_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $line_href,
|
||||||
|
),
|
||||||
|
phutil_escape_html($line['line']));
|
||||||
|
|
||||||
|
$blame[] = phutil_render_tag(
|
||||||
|
'th',
|
||||||
|
array(
|
||||||
|
'class' => 'diffusion-line-link',
|
||||||
|
'style' => isset($color) ? 'background: '.$color : null,
|
||||||
|
),
|
||||||
|
$line_link);
|
||||||
|
|
||||||
|
$blame = implode('', $blame);
|
||||||
|
|
||||||
|
if ($line['target']) {
|
||||||
|
Javelin::initBehavior(
|
||||||
|
'diffusion-jump-to',
|
||||||
|
array(
|
||||||
|
'target' => 'scroll_target',
|
||||||
|
));
|
||||||
|
$anchor_text = '<a id="scroll_target"></a>';
|
||||||
|
} else {
|
||||||
|
$anchor_text = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$line_text = phutil_render_tag(
|
||||||
|
'td',
|
||||||
|
array(
|
||||||
|
),
|
||||||
|
$anchor_text.$line['data']);
|
||||||
|
|
||||||
|
$rows[] = phutil_render_tag(
|
||||||
|
'tr',
|
||||||
|
array(
|
||||||
|
'style' => ($line['highlighted'] ? 'background: #ffff00;' : null),
|
||||||
|
),
|
||||||
|
$blame.
|
||||||
|
$line_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $rows;
|
return $rows;
|
||||||
|
@ -493,5 +600,34 @@ final class DiffusionBrowseFileController extends DiffusionController {
|
||||||
return $panel;
|
return $panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildBeforeResponse($before) {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$drequest = $this->getDiffusionRequest();
|
||||||
|
|
||||||
|
$before_req = DiffusionRequest::newFromDictionary(
|
||||||
|
array(
|
||||||
|
'repository' => $drequest->getRepository(),
|
||||||
|
'commit' => $before,
|
||||||
|
));
|
||||||
|
|
||||||
|
$query = DiffusionCommitParentsQuery::newFromDiffusionRequest($before_req);
|
||||||
|
$parents = $query->loadParents();
|
||||||
|
$parent = head($parents);
|
||||||
|
|
||||||
|
// NOTE: If they get back to the very first commit, we just keep them there.
|
||||||
|
// We could maybe show a message or something.
|
||||||
|
|
||||||
|
$before_uri = $drequest->generateURI(
|
||||||
|
array(
|
||||||
|
'action' => 'browse',
|
||||||
|
'commit' => $parent ? $parent->getCommitIdentifier() : $before,
|
||||||
|
'line' => $drequest->getLine(),
|
||||||
|
));
|
||||||
|
|
||||||
|
$before_uri->setQueryParams($request->getRequestURI()->getQueryParams());
|
||||||
|
$before_uri = $before_uri->alter('before', null);
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())->setURI($before_uri);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,16 @@ phutil_require_module('arcanist', 'difference');
|
||||||
|
|
||||||
phutil_require_module('phabricator', 'aphront/response/redirect');
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
phutil_require_module('phabricator', 'aphront/writeguard');
|
phutil_require_module('phabricator', 'aphront/writeguard');
|
||||||
|
phutil_require_module('phabricator', 'applications/audit/query/commit');
|
||||||
phutil_require_module('phabricator', 'applications/diffusion/controller/base');
|
phutil_require_module('phabricator', 'applications/diffusion/controller/base');
|
||||||
phutil_require_module('phabricator', 'applications/diffusion/query/filecontent/base');
|
phutil_require_module('phabricator', 'applications/diffusion/query/filecontent/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/diffusion/query/parents/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/diffusion/request/base');
|
||||||
phutil_require_module('phabricator', 'applications/files/storage/file');
|
phutil_require_module('phabricator', 'applications/files/storage/file');
|
||||||
phutil_require_module('phabricator', 'applications/markup/syntax');
|
phutil_require_module('phabricator', 'applications/markup/syntax');
|
||||||
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||||
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||||
phutil_require_module('phabricator', 'infrastructure/util/hash');
|
phutil_require_module('phabricator', 'infrastructure/util/hash');
|
||||||
phutil_require_module('phabricator', 'view/form/control/select');
|
phutil_require_module('phabricator', 'view/form/control/select');
|
||||||
phutil_require_module('phabricator', 'view/layout/panel');
|
phutil_require_module('phabricator', 'view/layout/panel');
|
||||||
|
|
|
@ -26,16 +26,6 @@ abstract class DiffusionFileContentQuery extends DiffusionQuery {
|
||||||
return parent::newQueryObject(__CLASS__, $request);
|
return parent::newQueryObject(__CLASS__, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSupportsBlameOnBlame() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPrevRev($rev) {
|
|
||||||
// TODO: support git once the 'parent' info of a commit is saved
|
|
||||||
// to the database.
|
|
||||||
throw new Exception("Unsupported VCS!");
|
|
||||||
}
|
|
||||||
|
|
||||||
final public function loadFileContent() {
|
final public function loadFileContent() {
|
||||||
$this->fileContent = $this->executeQuery();
|
$this->fileContent = $this->executeQuery();
|
||||||
}
|
}
|
||||||
|
@ -54,11 +44,20 @@ abstract class DiffusionFileContentQuery extends DiffusionQuery {
|
||||||
if (!$this->getNeedsBlame()) {
|
if (!$this->getNeedsBlame()) {
|
||||||
$text_list = explode("\n", rtrim($raw_data));
|
$text_list = explode("\n", rtrim($raw_data));
|
||||||
} else {
|
} else {
|
||||||
|
$lines = array();
|
||||||
foreach (explode("\n", rtrim($raw_data)) as $k => $line) {
|
foreach (explode("\n", rtrim($raw_data)) as $k => $line) {
|
||||||
list($rev_id, $author, $text) = $this->tokenizeLine($line);
|
$lines[$k] = $this->tokenizeLine($line);
|
||||||
|
|
||||||
|
list($rev_id, $author, $text) = $lines[$k];
|
||||||
$text_list[$k] = $text;
|
$text_list[$k] = $text;
|
||||||
$rev_list[$k] = $rev_id;
|
$rev_list[$k] = $rev_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rev_list = $this->processRevList($rev_list);
|
||||||
|
|
||||||
|
foreach ($lines as $k => $line) {
|
||||||
|
list($rev_id, $author, $text) = $line;
|
||||||
|
$rev_id = $rev_list[$k];
|
||||||
|
|
||||||
if (!isset($blame_dict[$rev_id]) &&
|
if (!isset($blame_dict[$rev_id]) &&
|
||||||
!isset($blame_dict[$rev_id]['author'] )) {
|
!isset($blame_dict[$rev_id]['author'] )) {
|
||||||
|
@ -68,9 +67,11 @@ abstract class DiffusionFileContentQuery extends DiffusionQuery {
|
||||||
|
|
||||||
$repository = $this->getRequest()->getRepository();
|
$repository = $this->getRequest()->getRepository();
|
||||||
|
|
||||||
$commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
|
$commits = id(new PhabricatorAuditCommitQuery())
|
||||||
'repositoryID = %d AND commitIdentifier IN (%Ls)', $repository->getID(),
|
->withIdentifiers(
|
||||||
array_unique($rev_list));
|
$repository->getID(),
|
||||||
|
array_unique($rev_list))
|
||||||
|
->execute();
|
||||||
|
|
||||||
foreach ($commits as $commit) {
|
foreach ($commits as $commit) {
|
||||||
$blame_dict[$commit->getCommitIdentifier()]['epoch'] =
|
$blame_dict[$commit->getCommitIdentifier()]['epoch'] =
|
||||||
|
@ -115,4 +116,8 @@ abstract class DiffusionFileContentQuery extends DiffusionQuery {
|
||||||
public function getNeedsBlame() {
|
public function getNeedsBlame() {
|
||||||
return $this->needsBlame;
|
return $this->needsBlame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function processRevList(array $rev_list) {
|
||||||
|
return $rev_list;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/audit/query/commit');
|
||||||
phutil_require_module('phabricator', 'applications/diffusion/query/base');
|
phutil_require_module('phabricator', 'applications/diffusion/query/base');
|
||||||
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||||
phutil_require_module('phabricator', 'applications/repository/storage/commit');
|
|
||||||
phutil_require_module('phabricator', 'applications/repository/storage/commitdata');
|
phutil_require_module('phabricator', 'applications/repository/storage/commitdata');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'utils');
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2011 Facebook, Inc.
|
* Copyright 2012 Facebook, Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -26,10 +26,19 @@ final class DiffusionMercurialFileContentQuery
|
||||||
$path = $drequest->getPath();
|
$path = $drequest->getPath();
|
||||||
$commit = $drequest->getCommit();
|
$commit = $drequest->getCommit();
|
||||||
|
|
||||||
|
if ($this->getNeedsBlame()) {
|
||||||
|
// NOTE: We're using "--number" instead of "--changeset" because there is
|
||||||
|
// no way to get "--changeset" to show us the full commit hashes.
|
||||||
|
list($corpus) = $repository->execxLocalCommand(
|
||||||
|
'annotate --user --number --rev %s -- %s',
|
||||||
|
$commit,
|
||||||
|
$path);
|
||||||
|
} else {
|
||||||
list($corpus) = $repository->execxLocalCommand(
|
list($corpus) = $repository->execxLocalCommand(
|
||||||
'cat --rev %s -- %s',
|
'cat --rev %s -- %s',
|
||||||
$commit,
|
$commit,
|
||||||
$path);
|
$path);
|
||||||
|
}
|
||||||
|
|
||||||
$file_content = new DiffusionFileContent();
|
$file_content = new DiffusionFileContent();
|
||||||
$file_content->setCorpus($corpus);
|
$file_content->setCorpus($corpus);
|
||||||
|
@ -37,11 +46,45 @@ final class DiffusionMercurialFileContentQuery
|
||||||
return $file_content;
|
return $file_content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected function tokenizeLine($line) {
|
protected function tokenizeLine($line) {
|
||||||
// TODO: Support blame.
|
$matches = null;
|
||||||
throw new Exception(
|
|
||||||
"Diffusion does not currently support blame for Mercurial.");
|
preg_match(
|
||||||
|
'/^(.*?)\s+([0-9]+): (.*)$/',
|
||||||
|
$line,
|
||||||
|
$matches);
|
||||||
|
|
||||||
|
return array($matches[2], $matches[1], $matches[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert local revision IDs into full commit identifier hashes.
|
||||||
|
*/
|
||||||
|
protected function processRevList(array $rev_list) {
|
||||||
|
$drequest = $this->getRequest();
|
||||||
|
$repository = $drequest->getRepository();
|
||||||
|
|
||||||
|
$revs = array_unique($rev_list);
|
||||||
|
foreach ($revs as $key => $rev) {
|
||||||
|
$revs[$key] = '--rev '.(int)$rev;
|
||||||
|
}
|
||||||
|
|
||||||
|
list($stdout) = $repository->execxLocalCommand(
|
||||||
|
'log --template=%s %C',
|
||||||
|
'{rev} {node}\\n',
|
||||||
|
implode(' ', $revs));
|
||||||
|
|
||||||
|
$map = array();
|
||||||
|
foreach (explode("\n", trim($stdout)) as $line) {
|
||||||
|
list($rev, $node) = explode(' ', $line);
|
||||||
|
$map[$rev] = $node;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($rev_list as $k => $rev) {
|
||||||
|
$rev_list[$k] = $map[$rev];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rev_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2011 Facebook, Inc.
|
* Copyright 2012 Facebook, Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -18,14 +18,6 @@
|
||||||
|
|
||||||
final class DiffusionSvnFileContentQuery extends DiffusionFileContentQuery {
|
final class DiffusionSvnFileContentQuery extends DiffusionFileContentQuery {
|
||||||
|
|
||||||
public function getSupportsBlameOnBlame() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPrevRev($rev) {
|
|
||||||
return max($rev - 1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function executeQuery() {
|
protected function executeQuery() {
|
||||||
$drequest = $this->getRequest();
|
$drequest = $this->getRequest();
|
||||||
|
|
||||||
|
|
|
@ -348,6 +348,7 @@ abstract class DiffusionRequest {
|
||||||
|
|
||||||
$req_callsign = false;
|
$req_callsign = false;
|
||||||
$req_branch = false;
|
$req_branch = false;
|
||||||
|
$req_commit = false;
|
||||||
|
|
||||||
switch ($action) {
|
switch ($action) {
|
||||||
case 'history':
|
case 'history':
|
||||||
|
@ -360,6 +361,10 @@ abstract class DiffusionRequest {
|
||||||
$req_callsign = true;
|
$req_callsign = true;
|
||||||
$req_branch = true;
|
$req_branch = true;
|
||||||
break;
|
break;
|
||||||
|
case 'commit':
|
||||||
|
$req_callsign = true;
|
||||||
|
$req_commit = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($req_callsign && !strlen($callsign)) {
|
if ($req_callsign && !strlen($callsign)) {
|
||||||
|
@ -372,6 +377,11 @@ abstract class DiffusionRequest {
|
||||||
"Diffusion URI action '{$action}' requires branch!");
|
"Diffusion URI action '{$action}' requires branch!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($req_commit && !strlen($commit)) {
|
||||||
|
throw new Exception(
|
||||||
|
"Diffusion URI action '{$action}' requires commit!");
|
||||||
|
}
|
||||||
|
|
||||||
switch ($action) {
|
switch ($action) {
|
||||||
case 'change':
|
case 'change':
|
||||||
case 'history':
|
case 'history':
|
||||||
|
@ -392,6 +402,11 @@ abstract class DiffusionRequest {
|
||||||
// it came from a URI.
|
// it came from a URI.
|
||||||
$uri = "{$path}{$commit}";
|
$uri = "{$path}{$commit}";
|
||||||
break;
|
break;
|
||||||
|
case 'commit':
|
||||||
|
$commit = ltrim($commit, ';');
|
||||||
|
$callsign = rtrim($callsign, '/');
|
||||||
|
$uri = "/r{$callsign}{$commit}";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception("Unknown Diffusion URI action '{$action}'!");
|
throw new Exception("Unknown Diffusion URI action '{$action}'!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
.diffusion-source {
|
.diffusion-source {
|
||||||
margin: 1em 0 2em;
|
margin: 1em 0 2em;
|
||||||
table-layout: fixed;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-family: "Monaco", Consolas, monospace;
|
font-family: "Monaco", Consolas, monospace;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
|
@ -12,35 +11,46 @@
|
||||||
|
|
||||||
.diffusion-source th {
|
.diffusion-source th {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
padding: 2px 6px;
|
|
||||||
width: 44px;
|
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
background: #eeeeee;
|
background: #eeeeee;
|
||||||
color: #888888;
|
color: #888888;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 0px 1px;
|
border-width: 0px 1px;
|
||||||
border-color: #eeeeee #999999 #eeeeee #dddddd;
|
border-color: #eeeeee #999999 #eeeeee #dddddd;
|
||||||
font-weight: bold;
|
|
||||||
font-family: "Verdana";
|
font-family: "Verdana";
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.diffusion-source td {
|
.diffusion-source td {
|
||||||
letter-spacing: 0.0083334px;
|
letter-spacing: 0.0083334px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
white-space: pre;
|
white-space: pre-wrap;
|
||||||
padding-bottom: 1px;
|
padding-bottom: 1px;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
overflow: hidden;
|
width: 100%;
|
||||||
}
|
|
||||||
|
|
||||||
.diffusion-wide-link a {
|
|
||||||
/* Give the user a larger click target. */
|
|
||||||
display: block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.diffusion-browse-type-form {
|
.diffusion-browse-type-form {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.diffusion-blame-link,
|
||||||
|
.diffusion-rev-link,
|
||||||
|
.diffusion-author-link {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diffusion-blame-link a,
|
||||||
|
.diffusion-rev-link a,
|
||||||
|
.diffusion-author-link a,
|
||||||
|
.diffusion-line-link a {
|
||||||
|
/* Give the user a larger click target. */
|
||||||
|
display: block;
|
||||||
|
padding: 2px 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diffusion-author-link span {
|
||||||
|
padding: 2px 8px;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue