parseDiff($diff);
$diff = DifferentialDiff::newFromRawChanges($changes);
$changesets = $diff->getChangesets();
$alt_changeset = reset($changesets);
$this->subparser = new DifferentialChangesetParser();
$this->subparser->setChangeset($alt_changeset);
$this->subparser->setWhitespaceMode(self::WHITESPACE_IGNORE_TRAILING);
}
foreach ($changeset->getHunks() as $hunk) {
$this->parseHunk($hunk);
}
$this->process();
if (!$skip_cache) {
$this->saveCache();
}
}
}
}
public function render(
$range_start = null,
$range_len = null,
$mask_force = array()) {
$this->highlightEngine = new PhutilDefaultSyntaxHighlighterEngine();
$this->tryCacheStuff();
$changeset_id = $this->changesetID;
$feedback_mask = array();
switch ($this->changeset->getFileType()) {
case DifferentialChangeType::FILE_IMAGE:
$old = null;
$cur = null;
$metadata = $this->changeset->getMetadata();
$data = idx($metadata, 'attachment-data');
$old_phid = idx($metadata, 'old:binary-phid');
$new_phid = idx($metadata, 'new:binary-phid');
if ($old_phid || $new_phid) {
if ($old_phid) {
$old_uri = PhabricatorFileURI::getViewURIForPHID($old_phid);
$old = phutil_render_tag(
'img',
array(
'src' => $old_uri,
));
}
if ($new_phid) {
$new_uri = PhabricatorFileURI::getViewURIForPHID($new_phid);
$cur = phutil_render_tag(
'img',
array(
'src' => $new_uri,
));
}
}
$output = $this->renderChangesetTable(
$this->changeset,
''.
' | '.
''.
' '.
$old.
' '.
' | '.
' | '.
''.
' '.
$cur.
' '.
' | '.
'
');
return $output;
case DifferentialChangeType::FILE_DIRECTORY:
case DifferentialChangeType::FILE_BINARY:
$output = $this->renderChangesetTable($this->changeset, null);
return $output;
}
$shield = null;
if ($range_start === null && $range_len === null) {
if ($this->isGenerated()) {
$shield = $this->renderShield(
"This file contains generated code, which does not normally need ".
"to be reviewed.",
true);
} else if ($this->isUnchanged()) {
if ($this->isWhitespaceOnly()) {
$shield = $this->renderShield(
"This file was changed only by adding or removing trailing ".
"whitespace.",
false);
} else {
$shield = $this->renderShield(
"The contents of this file were not changed.",
false);
}
} else if ($this->isDeleted()) {
$shield = $this->renderShield(
"This file was completely deleted.",
true);
} else if ($this->changeset->getAffectedLineCount() > 2500) {
$lines = number_format($this->changeset->getAffectedLineCount());
$shield = $this->renderShield(
"This file has a very large number of changes ({$lines} lines).",
true);
} else if (preg_match('/\.sql3$/', $this->changeset->getFilename())) {
$shield = $this->renderShield(
".sql3 files are hidden by default.",
true);
}
}
if ($shield) {
return $this->renderChangesetTable($this->changeset, $shield);
}
$old_comments = array();
$new_comments = array();
$old_mask = array();
$new_mask = array();
$feedback_mask = array();
if ($this->comments) {
foreach ($this->comments as $comment) {
$start = max($comment->getLineNumber() - self::LINES_CONTEXT, 0);
$end = $comment->getLineNumber() +
$comment->getLineLength() +
self::LINES_CONTEXT;
$new = $this->isCommentInNewFile($comment);
for ($ii = $start; $ii <= $end; $ii++) {
if ($new) {
$new_mask[$ii] = true;
} else {
$old_mask[$ii] = true;
}
}
}
foreach ($this->old as $ii => $old) {
if (isset($old['line']) && isset($old_mask[$old['line']])) {
$feedback_mask[$ii] = true;
}
}
foreach ($this->new as $ii => $new) {
if (isset($new['line']) && isset($new_mask[$new['line']])) {
$feedback_mask[$ii] = true;
}
}
$this->comments = msort($this->comments, 'getID');
foreach ($this->comments as $comment) {
$final = $comment->getLineNumber() +
$comment->getLineLength();
if ($this->isCommentInNewFile($comment)) {
$new_comments[$final][] = $comment;
} else {
$old_comments[$final][] = $comment;
}
}
}
$html = $this->renderTextChange(
$range_start,
$range_len,
$mask_force,
$feedback_mask,
$old_comments,
$new_comments);
return $this->renderChangesetTable($this->changeset, $html);
}
private function isCommentInNewFile(DifferentialInlineComment $comment) {
if ($this->oldChangesetID) {
return ($comment->getChangesetID() != $this->oldChangesetID);
} else {
return $comment->getIsNewFile();
}
}
protected function renderShield($message, $more) {
- $end = $this->getLength();
- $changeset_id = $this->getChangesetID();
if ($more) {
+ $end = $this->getLength();
+ $reference = $this->getChangeset()->getRenderingReference();
$more =
' '.
javelin_render_tag(
'a',
array(
'mustcapture' => true,
'sigil' => 'show-more',
'class' => 'complete',
'href' => '#',
'meta' => array(
- 'id' => $changeset_id,
+ 'id' => $reference,
'range' => "0-{$end}",
),
),
'Show File Contents');
} else {
$more = null;
}
return javelin_render_tag(
'tr',
array(
'sigil' => 'context-target',
),
''.
phutil_escape_html($message).
$more.
' | ');
}
protected function renderTextChange(
$range_start,
$range_len,
$mask_force,
$feedback_mask,
array $old_comments,
array $new_comments) {
$context_not_available = null;
if ($this->missingOld || $this->missingNew) {
$context_not_available = javelin_render_tag(
'tr',
array(
'sigil' => 'context-target',
),
''.
'Context not available.'.
' | ');
$context_not_available = $context_not_available;
}
$html = array();
$rows = max(
count($this->old),
count($this->new));
if ($range_start === null) {
$range_start = 0;
}
if ($range_len === null) {
$range_len = $rows;
}
$range_len = min($range_len, $rows - $range_start);
$gaps = array();
$gap_start = 0;
$in_gap = false;
$mask = $this->visible + $mask_force + $feedback_mask;
$mask[$range_start + $range_len] = true;
for ($ii = $range_start; $ii <= $range_start + $range_len; $ii++) {
if (isset($mask[$ii])) {
if ($in_gap) {
$gap_length = $ii - $gap_start;
if ($gap_length <= self::LINES_CONTEXT) {
for ($jj = $gap_start; $jj <= $gap_start + $gap_length; $jj++) {
$mask[$jj] = true;
}
} else {
$gaps[] = array($gap_start, $gap_length);
}
$in_gap = false;
}
} else {
if (!$in_gap) {
$gap_start = $ii;
$in_gap = true;
}
}
}
$gaps = array_reverse($gaps);
$changeset = $this->changesetID;
+ $reference = $this->getChangeset()->getRenderingReference();
for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) {
if (empty($mask[$ii])) {
$gap = array_pop($gaps);
$top = $gap[0];
$len = $gap[1];
$end = $top + $len - 20;
$contents = array();
if ($len > 40) {
$contents[] = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'show-more',
'meta' => array(
- 'id' => $changeset,
+ 'id' => $reference,
'range' => "{$top}-{$len}/{$top}-20",
),
),
"\xE2\x96\xB2 Show 20 Lines");
}
$contents[] = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'show-more',
'meta' => array(
- 'id' => $changeset,
+ 'id' => $reference,
'range' => "{$top}-{$len}/{$top}-{$len}",
),
),
'Show All '.$len.' Lines');
if ($len > 40) {
$contents[] = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'show-more',
'meta' => array(
- 'id' => $changeset,
+ 'id' => $reference,
'range' => "{$top}-{$len}/{$end}-20",
),
),
"\xE2\x96\xBC Show 20 Lines");
};
$container = javelin_render_tag(
'tr',
array(
'sigil' => 'context-target',
),
''.
implode(' • ', $contents).
' | ');
$html[] = $container;
$ii += ($len - 1);
continue;
}
if (isset($this->old[$ii])) {
$o_num = $this->old[$ii]['line'];
$o_text = isset($this->oldRender[$ii]) ? $this->oldRender[$ii] : null;
$o_attr = null;
if ($this->old[$ii]['type']) {
if (empty($this->new[$ii])) {
$o_attr = ' class="old old-full"';
} else {
$o_attr = ' class="old"';
}
}
} else {
$o_num = null;
$o_text = null;
$o_attr = null;
}
if (isset($this->new[$ii])) {
$n_num = $this->new[$ii]['line'];
$n_text = isset($this->newRender[$ii]) ? $this->newRender[$ii] : null;
$n_attr = null;
if ($this->new[$ii]['type']) {
if (empty($this->old[$ii])) {
$n_attr = ' class="new new-full"';
} else {
$n_attr = ' class="new"';
}
}
} else {
$n_num = null;
$n_text = null;
$n_attr = null;
}
if (($o_num && !empty($this->missingOld[$o_num])) ||
($n_num && !empty($this->missingNew[$n_num]))) {
$html[] = $context_not_available;
}
- if ($o_num) {
+ if ($o_num && $changeset) {
$o_id = ' id="C'.$changeset.'OL'.$o_num.'"';
} else {
$o_id = null;
}
- if ($n_num) {
+ if ($n_num && $changeset) {
$n_id = ' id="C'.$changeset.'NL'.$n_num.'"';
} else {
$n_id = null;
}
// NOTE: The Javascript is sensitive to whitespace changes in this
// block!
$html[] =
''.
''.$o_num.' | '.
''.$o_text.' | '.
''.$n_num.' | '.
''.$n_text.' | '.
'
';
if ($context_not_available && ($ii == $rows - 1)) {
$html[] = $context_not_available;
}
if ($o_num && isset($old_comments[$o_num])) {
foreach ($old_comments[$o_num] as $comment) {
$xhp = $this->renderInlineComment($comment);
$html[] =
' | '.
$xhp.
' | | |
';
}
}
if ($n_num && isset($new_comments[$n_num])) {
foreach ($new_comments[$n_num] as $comment) {
$xhp = $this->renderInlineComment($comment);
$html[] =
' | | | '.
$xhp.
' |
';
}
}
}
return implode('', $html);
}
private function renderInlineComment(DifferentialInlineComment $comment) {
$user = $this->user;
$edit = $user &&
($comment->getAuthorPHID() == $user->getPHID()) &&
(!$comment->getCommentID());
$on_right = $this->isCommentInNewFile($comment);
return id(new DifferentialInlineCommentView())
->setInlineComment($comment)
->setOnRight($on_right)
->setHandles($this->handles)
->setMarkupEngine($this->markupEngine)
->setEditable($edit)
->render();
}
protected function renderPropertyChangeHeader($changeset) {
$old = $changeset->getOldProperties();
$new = $changeset->getNewProperties();
if ($old === $new) {
return null;
}
if ($changeset->getChangeType() == DifferentialChangeType::TYPE_ADD &&
$new == array('unix:filemode' => '100644')) {
return null;
}
if ($changeset->getChangeType() == DifferentialChangeType::TYPE_DELETE &&
$old == array('unix:filemode' => '100644')) {
return null;
}
return null;
/*
TODO
$table = ;
$table->appendChild(
);
$keys = array_keys($old + $new);
sort($keys);
foreach ($keys as $key) {
$oval = idx($old, $key);
$nval = idx($new, $key);
if ($oval !== $nval) {
if ($oval === null) {
$oval = null;
}
if ($nval === null) {
$nval = null;
}
$table->appendChild(
{$key} |
{$oval} |
{$nval} |
);
}
}
return $table;
*/
}
protected function renderChangesetTable($changeset, $contents) {
$props = $this->renderPropertyChangeHeader($this->changeset);
$table = null;
if ($contents) {
$table =
'';
}
if (!$table && !$props) {
$notice = $this->renderChangeTypeHeader($this->changeset, true);
} else {
$notice = $this->renderChangeTypeHeader($this->changeset, false);
}
return implode(
"\n",
array(
$notice,
$props,
$table,
));
}
protected function renderChangeTypeHeader($changeset, $force) {
static $articles = array(
DifferentialChangeType::FILE_IMAGE => 'an',
);
static $files = array(
DifferentialChangeType::FILE_TEXT => 'file',
DifferentialChangeType::FILE_IMAGE => 'image',
DifferentialChangeType::FILE_DIRECTORY => 'directory',
DifferentialChangeType::FILE_BINARY => 'binary file',
DifferentialChangeType::FILE_SYMLINK => 'symlink',
);
static $changes = array(
DifferentialChangeType::TYPE_ADD => 'added',
DifferentialChangeType::TYPE_CHANGE => 'changed',
DifferentialChangeType::TYPE_DELETE => 'deleted',
DifferentialChangeType::TYPE_MOVE_HERE => 'moved from',
DifferentialChangeType::TYPE_COPY_HERE => 'copied from',
DifferentialChangeType::TYPE_MOVE_AWAY => 'moved to',
DifferentialChangeType::TYPE_COPY_AWAY => 'copied to',
DifferentialChangeType::TYPE_MULTICOPY
=> 'deleted after being copied to',
);
$change = $changeset->getChangeType();
$file = $changeset->getFileType();
$message = null;
if ($change == DifferentialChangeType::TYPE_CHANGE &&
$file == DifferentialChangeType::FILE_TEXT) {
if ($force) {
// We have to force something to render because there were no changes
// of other kinds.
$message = "This {$files[$file]} was not modified.";
} else {
// Default case of changes to a text file, no metadata.
return null;
}
} else {
$verb = idx($changes, $change, 'changed');
switch ($change) {
default:
$message = "This {$files[$file]} was {$verb}.";
break;
case DifferentialChangeType::TYPE_MOVE_HERE:
case DifferentialChangeType::TYPE_COPY_HERE:
$message =
"This {$files[$file]} was {$verb} ".
"{$changeset->getOldFile()}.";
break;
case DifferentialChangeType::TYPE_MOVE_AWAY:
case DifferentialChangeType::TYPE_COPY_AWAY:
case DifferentialChangeType::TYPE_MULTICOPY:
$paths = $changeset->getAwayPaths();
if (count($paths) > 1) {
$message =
"This {$files[$file]} was {$verb}: ".
"".implode(', ', $paths).".";
} else {
$message =
"This {$files[$file]} was {$verb} ".
"".reset($paths).".";
}
break;
case DifferentialChangeType::TYPE_CHANGE:
$message = "This is ".idx($articles, $file, 'a')." {$files[$file]}.";
break;
}
}
return
''.
$message.
'
';
}
public function renderForEmail() {
$ret = '';
$min = min(count($this->old), count($this->new));
for ($i = 0; $i < $min; $i++) {
$o = $this->old[$i];
$n = $this->new[$i];
if (!isset($this->visible[$i])) {
continue;
}
if ($o['line'] && $n['line']) {
// It is quite possible there are better ways to achieve this. For
// example, "white-space: pre;" can do a better job, WERE IT NOT for
// broken email clients like OWA which use newlines to do weird
// wrapping. So dont give them newlines.
if (isset($this->intra[$i])) {
$ret .= sprintf(
"- %s
",
str_replace(" ", " ", phutil_escape_html($o['text']))
);
$ret .= sprintf(
"+ %s
",
str_replace(" ", " ", phutil_escape_html($n['text']))
);
} else {
$ret .= sprintf(" %s
",
str_replace(" ", " ", phutil_escape_html($n['text']))
);
}
} else if ($o['line'] && !$n['line']) {
$ret .= sprintf(
"- %s
",
str_replace(" ", " ", phutil_escape_html($o['text']))
);
} else {
$ret .= sprintf(
"+ %s
",
str_replace(" ", " ", phutil_escape_html($n['text']))
);
}
}
return $ret;
}
}
diff --git a/src/applications/differential/storage/changeset/DifferentialChangeset.php b/src/applications/differential/storage/changeset/DifferentialChangeset.php
index 9fde61affb..14c2bb8e8c 100644
--- a/src/applications/differential/storage/changeset/DifferentialChangeset.php
+++ b/src/applications/differential/storage/changeset/DifferentialChangeset.php
@@ -1,147 +1,160 @@
array(
'metadata' => self::SERIALIZATION_JSON,
'oldProperties' => self::SERIALIZATION_JSON,
'newProperties' => self::SERIALIZATION_JSON,
'awayPaths' => self::SERIALIZATION_JSON,
)) + parent::getConfiguration();
}
public function getAffectedLineCount() {
return $this->getAddLines() + $this->getDelLines();
}
public function getFileType() {
return $this->fileType;
}
public function getChangeType() {
return $this->changeType;
}
public function attachHunks(array $hunks) {
$this->hunks = $hunks;
return $this;
}
public function getHunks() {
if ($this->hunks === null) {
throw new Exception("Must load and attach hunks first!");
}
return $this->hunks;
}
public function getDisplayFilename() {
$name = $this->getFilename();
if ($this->getFileType() == DifferentialChangeType::FILE_DIRECTORY) {
$name .= '/';
}
return $name;
}
+ public function setRenderingReference($rendering_reference) {
+ $this->renderingReference = $rendering_reference;
+ return $this;
+ }
+
+ public function getRenderingReference() {
+ if ($this->renderingReference) {
+ return $this->renderingReference;
+ }
+ return $this->getID();
+ }
+
public function addUnsavedHunk(DifferentialHunk $hunk) {
if ($this->hunks === null) {
$this->hunks = array();
}
$this->hunks[] = $hunk;
$this->unsavedHunks[] = $hunk;
return $this;
}
public function loadHunks() {
if (!$this->getID()) {
return array();
}
return id(new DifferentialHunk())->loadAllWhere(
'changesetID = %d',
$this->getID());
}
public function save() {
// TODO: Sort out transactions
// $this->openTransaction();
$ret = parent::save();
foreach ($this->unsavedHunks as $hunk) {
$hunk->setChangesetID($this->getID());
$hunk->save();
}
// $this->saveTransaction();
return $ret;
}
public function delete() {
// $this->openTransaction();
foreach ($this->loadHunks() as $hunk) {
$hunk->delete();
}
$this->_hunks = array();
$ret = parent::delete();
// $this->saveTransaction();
return $ret;
}
public function getSortKey() {
$sort_key = $this->getFilename();
// Sort files with ".h" in them first, so headers (.h, .hpp) come before
// implementations (.c, .cpp, .cs).
$sort_key = str_replace('.h', '.!h', $sort_key);
return $sort_key;
}
public function makeNewFile() {
$file = array();
foreach ($this->getHunks() as $hunk) {
$file[] = $hunk->makeNewFile();
}
return implode("\n", $file);
}
public function makeOldFile() {
$file = array();
foreach ($this->getHunks() as $hunk) {
$file[] = $hunk->makeOldFile();
}
return implode("\n", $file);
}
public function getAnchorName() {
return substr(md5($this->getFilename()), 0, 8);
}
}
diff --git a/src/applications/differential/view/changesetlistview/DifferentialChangesetListView.php b/src/applications/differential/view/changesetlistview/DifferentialChangesetListView.php
index 66fa6dd472..1dc86e05d8 100644
--- a/src/applications/differential/view/changesetlistview/DifferentialChangesetListView.php
+++ b/src/applications/differential/view/changesetlistview/DifferentialChangesetListView.php
@@ -1,134 +1,136 @@
changesets = $changesets;
return $this;
}
public function setEditable($editable) {
$this->editable = $editable;
return $this;
}
public function setRevision(DifferentialRevision $revision) {
$this->revision = $revision;
return $this;
}
public function setVsMap(array $vs_map) {
$this->vsMap = $vs_map;
return $this;
}
public function setRenderURI($render_uri) {
$this->renderURI = $render_uri;
return $this;
}
public function render() {
require_celerity_resource('differential-changeset-view-css');
$vs_map = $this->vsMap;
$changesets = $this->changesets;
$output = array();
$mapping = array();
foreach ($changesets as $key => $changeset) {
$file = $changeset->getFilename();
$class = 'differential-changeset';
if (!$this->editable) {
$class .= ' differential-changeset-noneditable';
}
$id = $changeset->getID();
if ($id) {
$vs_id = idx($vs_map, $id);
} else {
$vs_id = null;
}
- $detail_uri = new PhutilURI('/differential/changeset/');
+ $ref = $changeset->getRenderingReference();
+
+ $detail_uri = new PhutilURI($this->renderURI);
$detail_uri->setQueryParams(
array(
- 'id' => $id,
+ 'id' => $ref,
'vs' => $vs_id,
'whitespace' => 'TODO',
));
$detail_button = phutil_render_tag(
'a',
array(
'style' => 'float: right',
'class' => 'button small grey',
'href' => $detail_uri,
'target' => '_blank',
),
'Standalone View');
$uniq_id = celerity_generate_unique_node_id();
$detail = new DifferentialChangesetDetailView();
$detail->setChangeset($changeset);
$detail->addButton($detail_button);
$detail->appendChild(
phutil_render_tag(
'div',
array(
'id' => $uniq_id,
),
'Loading...
'));
$output[] = $detail->render();
$mapping[$uniq_id] = array(
- $changeset->getID(),
+ $ref,
$vs_id);
}
$whitespace = null;
Javelin::initBehavior('differential-populate', array(
'registry' => $mapping,
'whitespace' => $whitespace,
'uri' => $this->renderURI,
));
Javelin::initBehavior('differential-show-more', array(
'uri' => $this->renderURI,
));
if ($this->editable) {
$revision = $this->revision;
Javelin::initBehavior('differential-edit-inline-comments', array(
'uri' => '/differential/comment/inline/edit/'.$revision->getID().'/',
));
}
return
''.
implode("\n", $output).
'
';
}
}
diff --git a/src/applications/diffusion/controller/commit/DiffusionCommitController.php b/src/applications/diffusion/controller/commit/DiffusionCommitController.php
index 0c62de6e0a..0c7f4ecfc8 100644
--- a/src/applications/diffusion/controller/commit/DiffusionCommitController.php
+++ b/src/applications/diffusion/controller/commit/DiffusionCommitController.php
@@ -1,119 +1,146 @@
getDiffusionRequest();
+ $callsign = $drequest->getRepository()->getCallsign();
+
$content = array();
$content[] = $this->buildCrumbs(array(
'commit' => true,
));
$detail_panel = new AphrontPanelView();
$repository = $drequest->getRepository();
$commit = $drequest->loadCommit();
if (!$commit) {
// TODO: Make more user-friendly.
throw new Exception('This commit has not parsed yet.');
}
$commit_data = $drequest->loadCommitData();
require_celerity_resource('diffusion-commit-view-css');
$detail_panel->appendChild(
''.
'
'.
- 'r'.$repository->getCallsign().$commit->getCommitIdentifier().
+ 'r'.$callsign.$commit->getCommitIdentifier().
' · '.
date('F jS, Y g:i A', $commit->getEpoch()).
'
'.
'
Revision Detail
'.
'
'.
'
'.
''.
'Author: | '.
''.phutil_escape_html($commit_data->getAuthorName()).' | '.
'
'.
'
'.
'
'.
'
'.
phutil_escape_html($commit_data->getCommitMessage()).
'
'.
'
'.
'
');
$content[] = $detail_panel;
$change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
$drequest);
$changes = $change_query->loadChanges();
$change_table = new DiffusionCommitChangeTableView();
$change_table->setDiffusionRequest($drequest);
$change_table->setPathChanges($changes);
// TODO: Large number of modified files check.
$count = number_format(count($changes));
$bad_commit = null;
if ($count == 0) {
$bad_commit = queryfx_one(
id(new PhabricatorRepository())->establishConnection('r'),
'SELECT * FROM %T WHERE fullCommitName = %s',
PhabricatorRepository::TABLE_BADCOMMIT,
- 'r'.$repository->getCallsign().$commit->getCommitIdentifier());
+ 'r'.$callsign.$commit->getCommitIdentifier());
}
if ($bad_commit) {
$error_panel = new AphrontErrorView();
$error_panel->setWidth(AphrontErrorView::WIDTH_WIDE);
$error_panel->setTitle('Bad Commit');
$error_panel->appendChild(
phutil_escape_html($bad_commit['description']));
$content[] = $error_panel;
} else {
$change_panel = new AphrontPanelView();
$change_panel->setHeader("Changes ({$count})");
$change_panel->appendChild($change_table);
$content[] = $change_panel;
- $change_list =
- ''.
- '(list of changes goes here)'.
- '
';
+ if ($changes) {
+ $changesets = DiffusionPathChange::convertToDifferentialChangesets(
+ $changes);
+ foreach ($changesets as $changeset) {
+ $branch = $drequest->getBranchURIComponent(
+ $drequest->getBranch());
+ $filename = $changeset->getFilename();
+ $commit = $drequest->getCommit();
+ $reference = "{$branch}{$filename};{$commit}";
+ $changeset->setRenderingReference($reference);
+ }
+
+ $change_list = new DifferentialChangesetListView();
+ $change_list->setChangesets($changesets);
+ $change_list->setRenderURI('/diffusion/'.$callsign.'/diff/');
+
+ // TODO: This is pretty awkward, unify the CSS between Diffusion and
+ // Differential better.
+ require_celerity_resource('differential-core-view-css');
+ $change_list =
+ ''.
+ $change_list->render().
+ '
';
+ } else {
+ $change_list =
+ ''.
+ '(no changes blah blah)'.
+ '
';
+ }
$content[] = $change_list;
}
return $this->buildStandardPageResponse(
$content,
array(
'title' => 'Diffusion',
));
}
}
diff --git a/src/applications/diffusion/controller/commit/__init__.php b/src/applications/diffusion/controller/commit/__init__.php
index 6e629ab204..36ce108d3e 100644
--- a/src/applications/diffusion/controller/commit/__init__.php
+++ b/src/applications/diffusion/controller/commit/__init__.php
@@ -1,22 +1,24 @@
path = $path;
return $this;
}
final public function getPath() {
return $this->path;
}
public function setChangeType($change_type) {
$this->changeType = $change_type;
return $this;
}
public function getChangeType() {
return $this->changeType;
}
public function setFileType($file_type) {
$this->fileType = $file_type;
return $this;
}
public function getFileType() {
return $this->fileType;
}
public function setTargetPath($target_path) {
$this->targetPath = $target_path;
return $this;
}
public function getTargetPath() {
return $this->targetPath;
}
public function setAwayPaths(array $away_paths) {
$this->awayPaths = $away_paths;
return $this;
}
public function getAwayPaths() {
return $this->awayPaths;
}
final public function setCommitIdentifier($commit) {
$this->commitIdentifier = $commit;
return $this;
}
final public function getCommitIdentifier() {
return $this->commitIdentifier;
}
final public function setCommit($commit) {
$this->commit = $commit;
return $this;
}
final public function getCommit() {
return $this->commit;
}
final public function setCommitData($commit_data) {
$this->commitData = $commit_data;
return $this;
}
final public function getCommitData() {
return $this->commitData;
}
final public function getEpoch() {
if ($this->getCommit()) {
return $this->getCommit()->getEpoch();
}
return null;
}
final public function getAuthorName() {
if ($this->getCommitData()) {
return $this->getCommitData()->getAuthorName();
}
return null;
}
final public function getSummary() {
if (!$this->getCommitData()) {
return null;
}
$message = $this->getCommitData()->getCommitMessage();
$first = idx(explode("\n", $message), 0);
return substr($first, 0, 80);
}
+ final public static function convertToArcanistChanges(array $changes) {
+ $direct = array();
+ $result = array();
+ foreach ($changes as $path) {
+ $change = new ArcanistDiffChange();
+ $change->setCurrentPath($path->getPath());
+ $direct[] = $path->getPath();
+ $change->setType($path->getChangeType());
+ $file_type = $path->getFileType();
+ if ($file_type == DifferentialChangeType::FILE_NORMAL) {
+ $file_type = DifferentialChangeType::FILE_TEXT;
+ }
+ $change->setFileType($file_type);
+ $change->setOldPath($path->getTargetPath());
+ foreach ($path->getAwayPaths() as $away_path) {
+ $change->addAwayPath($away_path);
+ }
+ $result[$path->getPath()] = $change;
+ }
+
+ return array_select_keys($result, $direct);
+ }
+
+ final public static function convertToDifferentialChangesets(array $changes) {
+ $arcanist_changes = self::convertToArcanistChanges($changes);
+ $diff = DifferentialDiff::newFromRawChanges($arcanist_changes);
+ return $diff->getChangesets();
+ }
}
diff --git a/src/applications/diffusion/data/pathchange/__init__.php b/src/applications/diffusion/data/pathchange/__init__.php
index 0256f567bf..46cfbb20fa 100644
--- a/src/applications/diffusion/data/pathchange/__init__.php
+++ b/src/applications/diffusion/data/pathchange/__init__.php
@@ -1,12 +1,17 @@
getRequest();
$path_change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
$drequest);
$path_changes = $path_change_query->loadChanges();
$path = null;
foreach ($path_changes as $change) {
if ($change->getPath() == $drequest->getPath()) {
$path = $change;
}
}
if (!$path) {
return null;
}
$change_type = $path->getChangeType();
switch ($change_type) {
case DifferentialChangeType::TYPE_MULTICOPY:
case DifferentialChangeType::TYPE_DELETE:
if ($path->getTargetPath()) {
$old = array(
$path->getTargetPath(),
$path->getTargetCommitIdentifier());
} else {
$old = array($path->getPath(), $path->getCommitIdentifier() - 1);
}
$old_name = $path->getPath();
$new_name = '';
$new = null;
break;
case DifferentialChangeType::TYPE_ADD:
$old = null;
$new = array($path->getPath(), $path->getCommitIdentifier());
$old_name = '';
$new_name = $path->getPath();
break;
case DifferentialChangeType::TYPE_MOVE_HERE:
case DifferentialChangeType::TYPE_COPY_HERE:
$old = array(
$path->getTargetPath(),
$path->getTargetCommitIdentifier());
$new = array($path->getPath(), $path->getCommitIdentifier());
$old_name = $path->getTargetPath();
$new_name = $path->getPath();
break;
default:
$old = array($path->getPath(), $path->getCommitIdentifier() - 1);
$new = array($path->getPath(), $path->getCommitIdentifier());
$old_name = $path->getPath();
$new_name = $path->getPath();
break;
}
$futures = array(
'old' => $this->buildContentFuture($old),
'new' => $this->buildContentFuture($new),
);
$futures = array_filter($futures);
foreach (Futures($futures) as $key => $future) {
$futures[$key] = $future->resolvex();
}
$old_data = idx($futures, 'old', '');
$new_data = idx($futures, 'new', '');
$old_tmp = new TempFile();
$new_tmp = new TempFile();
Filesystem::writeFile($old_tmp, $old_data);
Filesystem::writeFile($new_tmp, $new_data);
list($err, $raw_diff) = exec_manual(
'diff -L %s -L %s -U65535 %s %s',
nonempty($old_name, '/dev/universe').' 9999-99-99',
nonempty($new_name, '/dev/universe').' 9999-99-99',
$old_tmp,
$new_tmp);
$parser = new ArcanistDiffParser();
$parser->setDetectBinaryFiles(true);
$change = $parser->parseDiffusionPathChangesAndRawDiff(
$drequest->getPath(),
$path_changes,
$raw_diff);
$diff = DifferentialDiff::newFromRawChanges(array($change));
$changesets = $diff->getChangesets();
$changeset = reset($changesets);
- $id = $drequest->getPath().';'.$drequest->getCommit();
- $changeset->setID($id);
+ $reference = $drequest->getPath().';'.$drequest->getCommit();
+ $changeset->setRenderingReference($reference);
return $changeset;
}
private function buildContentFuture($spec) {
if (!$spec) {
return null;
}
$drequest = $this->getRequest();
$repository = $drequest->getRepository();
list($ref, $rev) = $spec;
return new ExecFuture(
'svn --non-interactive cat %s%s@%d',
$repository->getDetail('remote-uri'),
$ref,
$rev);
}
}