diff --git a/src/applications/differential/controller/changesetview/DifferentialChangesetViewController.php b/src/applications/differential/controller/changesetview/DifferentialChangesetViewController.php
index 558f747d7c..f9cd71b188 100644
--- a/src/applications/differential/controller/changesetview/DifferentialChangesetViewController.php
+++ b/src/applications/differential/controller/changesetview/DifferentialChangesetViewController.php
@@ -22,15 +22,91 @@ class DifferentialChangesetViewController extends DifferentialController {
public function processRequest() {
$request = $this->getRequest();
- $id = $request->getStr('id');
$author_phid = $request->getUser()->getPHID();
+ $id = $request->getStr('id');
+ $vs = $request->getInt('vs');
+
+
$changeset = id(new DifferentialChangeset())->load($id);
if (!$changeset) {
return new Aphront404Response();
}
- $changeset->attachHunks($changeset->loadHunks());
+ if ($vs && ($vs != -1)) {
+ $vs_changeset = id(new DifferentialChangeset())->load($vs);
+ if (!$vs_changeset) {
+ return new Aphront404Response();
+ }
+ }
+
+ if (!$vs) {
+ $right = $changeset;
+ $left = null;
+
+ $right_source = $right->getID();
+ $right_new = true;
+ $left_source = $right->getID();
+ $left_new = false;
+ } else if ($vs == -1) {
+ $right = null;
+ $left = $changeset;
+
+ $right_source = $left->getID();
+ $right_new = false;
+ $left_source = $left->getID();
+ $left_new = true;
+ } else {
+ $right = $changeset;
+ $left = $vs_changeset;
+
+ $right_source = $right->getID();
+ $right_new = true;
+ $left_source = $left->getID();
+ $left_new = true;
+ }
+
+ if ($left) {
+ $left->attachHunks($left->loadHunks());
+ }
+
+ if ($right) {
+ $right->attachHunks($right->loadHunks());
+ }
+
+ if ($left) {
+
+ $left_data = $left->makeNewFile();
+ if ($right) {
+ $right_data = $right->makeNewFile();
+ } else {
+ $right_data = $left->makeOldFile();
+ }
+
+ $left_tmp = new TempFile();
+ $right_tmp = new TempFile();
+ Filesystem::writeFile($left_tmp, $left_data);
+ Filesystem::writeFile($right_tmp, $right_data);
+ list($err, $stdout) = exec_manual(
+ '/usr/bin/diff -U65535 %s %s',
+ $left_tmp,
+ $right_tmp);
+
+ $choice = nonempty($left, $right);
+ if ($stdout) {
+ $parser = new ArcanistDiffParser();
+ $changes = $parser->parseDiff($stdout);
+ $diff = DifferentialDiff::newFromRawChanges($changes);
+ $changesets = $diff->getChangesets();
+ $first = reset($changesets);
+ $choice->attachHunks($first->getHunks());
+ } else {
+ $choice->attachHunks(array());
+ }
+
+ $changeset = $choice;
+ $changeset->setID(null);
+ }
$range_s = null;
$range_e = null;
@@ -54,6 +130,8 @@ class DifferentialChangesetViewController extends DifferentialController {
$parser = new DifferentialChangesetParser();
$parser->setChangeset($changeset);
+ $parser->setRightSideCommentMapping($right_source, $right_new);
+ $parser->setLeftSideCommentMapping($left_source, $left_new);
$phids = array();
$inlines = $this->loadInlineComments($id, $author_phid);
diff --git a/src/applications/differential/controller/changesetview/__init__.php b/src/applications/differential/controller/changesetview/__init__.php
index 124a9dc4e0..3c23c13098 100644
--- a/src/applications/differential/controller/changesetview/__init__.php
+++ b/src/applications/differential/controller/changesetview/__init__.php
@@ -6,17 +6,23 @@
+phutil_require_module('arcanist', 'parser/diff');
+
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'aphront/response/ajax');
phutil_require_module('phabricator', 'applications/differential/controller/base');
phutil_require_module('phabricator', 'applications/differential/parser/changeset');
phutil_require_module('phabricator', 'applications/differential/parser/markup');
phutil_require_module('phabricator', 'applications/differential/storage/changeset');
+phutil_require_module('phabricator', 'applications/differential/storage/diff');
phutil_require_module('phabricator', 'applications/differential/storage/inlinecomment');
phutil_require_module('phabricator', 'applications/differential/view/changesetdetailview');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'infrastructure/javelin/api');
+phutil_require_module('phutil', 'filesystem');
+phutil_require_module('phutil', 'filesystem/tempfile');
+phutil_require_module('phutil', 'future/exec');
phutil_require_module('phutil', 'utils');
diff --git a/src/applications/differential/controller/diffview/DifferentialDiffViewController.php b/src/applications/differential/controller/diffview/DifferentialDiffViewController.php
index db37db9308..9f13d22d3b 100644
--- a/src/applications/differential/controller/diffview/DifferentialDiffViewController.php
+++ b/src/applications/differential/controller/diffview/DifferentialDiffViewController.php
@@ -40,6 +40,35 @@ class DifferentialDiffViewController extends DifferentialController {
'When you are satisfied, either create a new revision '.
'or update an existing revision.');
+ // TODO: implmenent optgroup support in AphrontFormSelectControl?
+ $select = array();
+ $select[] = '';
+
+ $revision_data = new DifferentialRevisionListData(
+ DifferentialRevisionListData::QUERY_OPEN_OWNED,
+ array($request->getUser()->getPHID()));
+ $revisions = $revision_data->loadRevisions();
+
+ if ($revisions) {
+ $select[] = '';
+ }
+
+ $select =
+ '';
+
$action_form = new AphrontFormView();
$action_form
->setUser($request->getUser())
@@ -47,13 +76,9 @@ class DifferentialDiffViewController extends DifferentialController {
->addHiddenInput('diffID', $diff->getID())
->addHiddenInput('viaDiffView', 1)
->appendChild(
- id(new AphrontFormSelectControl())
+ id(new AphrontFormMarkupControl())
->setLabel('Attach To')
- ->setName('revisionID')
- ->setValue('')
- ->setOptions(array(
- '' => "Create a new Revision...",
- )))
+ ->setValue($select))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Continue'));
diff --git a/src/applications/differential/controller/diffview/__init__.php b/src/applications/differential/controller/diffview/__init__.php
index efcd27da68..b685510625 100644
--- a/src/applications/differential/controller/diffview/__init__.php
+++ b/src/applications/differential/controller/diffview/__init__.php
@@ -8,6 +8,7 @@
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'applications/differential/controller/base');
+phutil_require_module('phabricator', 'applications/differential/data/revisionlist');
phutil_require_module('phabricator', 'applications/differential/storage/diff');
phutil_require_module('phabricator', 'applications/differential/view/changesetlistview');
phutil_require_module('phabricator', 'applications/differential/view/difftableofcontents');
@@ -15,6 +16,7 @@ phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/layout/panel');
+phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
diff --git a/src/applications/differential/controller/revisionedit/DifferentialRevisionEditController.php b/src/applications/differential/controller/revisionedit/DifferentialRevisionEditController.php
index 8d85e520a9..5f4822db12 100644
--- a/src/applications/differential/controller/revisionedit/DifferentialRevisionEditController.php
+++ b/src/applications/differential/controller/revisionedit/DifferentialRevisionEditController.php
@@ -26,6 +26,12 @@ class DifferentialRevisionEditController extends DifferentialController {
public function processRequest() {
+ $request = $this->getRequest();
+
+ if (!$this->id) {
+ $this->id = $request->getInt('revisionID');
+ }
+
if ($this->id) {
$revision = id(new DifferentialRevision())->load($this->id);
if (!$revision) {
@@ -35,7 +41,6 @@ class DifferentialRevisionEditController extends DifferentialController {
$revision = new DifferentialRevision();
}
- $request = $this->getRequest();
$diff_id = $request->getInt('diffID');
if ($diff_id) {
$diff = id(new DifferentialDiff())->load($diff_id);
diff --git a/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
index 56ab73edb1..b92c29e98f 100644
--- a/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
+++ b/src/applications/differential/controller/revisionview/DifferentialRevisionViewController.php
@@ -37,10 +37,16 @@ class DifferentialRevisionViewController extends DifferentialController {
$diffs = $revision->loadDiffs();
- $diff_vs = null;
+ $diff_vs = $request->getInt('vs');
$target = end($diffs);
- $changesets = $target->loadChangesets();
+ $diffs = mpull($diffs, null, 'getID');
+ if (empty($diffs[$diff_vs])) {
+ $diff_vs = null;
+ }
+
+ list($changesets, $vs_map) =
+ $this->loadChangesetsAndVsMap($diffs, $diff_vs, $target);
$comments = $revision->loadComments();
$comments = array_merge(
@@ -89,6 +95,7 @@ class DifferentialRevisionViewController extends DifferentialController {
$changeset_view->setChangesets($changesets);
$changeset_view->setEditable(true);
$changeset_view->setRevision($revision);
+ $changeset_view->setVsMap($vs_map);
$comment_form = new DifferentialAddCommentView();
$comment_form->setRevision($revision);
@@ -315,6 +322,44 @@ class DifferentialRevisionViewController extends DifferentialController {
return $inline_comments;
}
+ private function loadChangesetsAndVsMap(array $diffs, $diff_vs, $target) {
+ $load_ids = array();
+ if ($diff_vs) {
+ $load_ids[] = $diff_vs;
+ }
+ $load_ids[] = $target->getID();
+
+ $raw_changesets = id(new DifferentialChangeset())
+ ->loadAllWhere(
+ 'diffID IN (%Ld)',
+ $load_ids);
+ $changeset_groups = mgroup($raw_changesets, 'getDiffID');
+
+ $changesets = idx($changeset_groups, $target->getID(), array());
+ $changesets = mpull($changesets, null, 'getID');
+
+ $vs_map = array();
+ if ($diff_vs) {
+ $vs_changesets = idx($changeset_groups, $diff_vs, array());
+ $vs_changesets = mpull($vs_changesets, null, 'getFilename');
+ foreach ($changesets as $key => $changeset) {
+ $file = $changeset->getFilename();
+ if (isset($vs_changesets[$file])) {
+ $vs_map[$changeset->getID()] = $vs_changesets[$file]->getID();
+ unset($vs_changesets[$file]);
+ }
+ }
+ foreach ($vs_changesets as $changeset) {
+ $changesets[$changeset->getID()] = $changeset;
+ $vs_map[$changeset->getID()] = -1;
+ }
+ }
+
+ $changesets = msort($changesets, 'getSortKey');
+
+ return array($changesets, $vs_map);
+ }
+
}
/*
@@ -417,16 +462,6 @@ class DifferentialRevisionViewController extends DifferentialController {
}
}
- $reviewer_links = array();
- foreach ($revision->getReviewers() as $reviewer) {
- $reviewer_links[] =