private static function loadDiffusionChangesForCommit($commit)
 {
     $repository = id(new PhabricatorRepository())->load($commit->getRepositoryID());
     $data = array('user' => PhabricatorUser::getOmnipotentUser(), 'initFromConduit' => false, 'repository' => $repository, 'commit' => $commit->getCommitIdentifier());
     $drequest = DiffusionRequest::newFromDictionary($data);
     $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest);
     return $change_query->loadChanges();
 }
 public static function loadAffectedPaths(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit, PhabricatorUser $user)
 {
     $drequest = DiffusionRequest::newFromDictionary(array('user' => $user, 'repository' => $repository, 'commit' => $commit->getCommitIdentifier()));
     $path_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest);
     $paths = $path_query->loadChanges();
     $result = array();
     foreach ($paths as $path) {
         $basic_path = '/' . $path->getPath();
         if ($path->getFileType() == DifferentialChangeType::FILE_DIRECTORY) {
             $basic_path = rtrim($basic_path, '/') . '/';
         }
         $result[] = $basic_path;
     }
     return $result;
 }
 public static function loadAffectedPaths(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit)
 {
     $drequest = self::buildDiffusionRequest($repository, $commit);
     $path_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest);
     $paths = $path_query->loadChanges();
     $result = array();
     foreach ($paths as $path) {
         $basic_path = '/' . $path->getPath();
         if ($path->getFileType() == DifferentialChangeType::FILE_DIRECTORY) {
             $basic_path = rtrim($basic_path, '/') . '/';
         }
         $result[] = $basic_path;
     }
     return $result;
 }
 public function loadAffectedPaths()
 {
     if ($this->affectedPaths === null) {
         $drequest = $this->buildDiffusionRequest();
         $path_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest);
         $paths = $path_query->loadChanges();
         $result = array();
         foreach ($paths as $path) {
             $basic_path = '/' . $path->getPath();
             if ($path->getFileType() == DifferentialChangeType::FILE_DIRECTORY) {
                 $basic_path = rtrim($basic_path, '/') . '/';
             }
             $result[] = $basic_path;
         }
         $this->affectedPaths = $result;
     }
     return $this->affectedPaths;
 }
 protected function processDiffusionRequest(AphrontRequest $request)
 {
     $user = $request->getUser();
     // This controller doesn't use blob/path stuff, just pass the dictionary
     // in directly instead of using the AphrontRequest parsing mechanism.
     $data = $request->getURIMap();
     $data['user'] = $user;
     $drequest = DiffusionRequest::newFromDictionary($data);
     $this->diffusionRequest = $drequest;
     if ($request->getStr('diff')) {
         return $this->buildRawDiffResponse($drequest);
     }
     $repository = $drequest->getRepository();
     $callsign = $repository->getCallsign();
     $content = array();
     $commit = id(new DiffusionCommitQuery())->setViewer($request->getUser())->withRepository($repository)->withIdentifiers(array($drequest->getCommit()))->needCommitData(true)->needAuditRequests(true)->executeOne();
     $crumbs = $this->buildCrumbs(array('commit' => true));
     if (!$commit) {
         $exists = $this->callConduitWithDiffusionRequest('diffusion.existsquery', array('commit' => $drequest->getCommit()));
         if (!$exists) {
             return new Aphront404Response();
         }
         $error = id(new PHUIInfoView())->setTitle(pht('Commit Still Parsing'))->appendChild(pht('Failed to load the commit because the commit has not been ' . 'parsed yet.'));
         return $this->buildApplicationPage(array($crumbs, $error), array('title' => pht('Commit Still Parsing')));
     }
     $audit_requests = $commit->getAudits();
     $this->auditAuthorityPHIDs = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
     $commit_data = $commit->getCommitData();
     $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
     if ($is_foreign) {
         $subpath = $commit_data->getCommitDetail('svn-subpath');
         $error_panel = new PHUIInfoView();
         $error_panel->setTitle(pht('Commit Not Tracked'));
         $error_panel->setSeverity(PHUIInfoView::SEVERITY_WARNING);
         $error_panel->appendChild(pht("This Diffusion repository is configured to track only one " . "subdirectory of the entire Subversion repository, and this commit " . "didn't affect the tracked subdirectory ('%s'), so no " . "information is available.", $subpath));
         $content[] = $error_panel;
     } else {
         $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
         $engine->setConfig('viewer', $user);
         require_celerity_resource('phabricator-remarkup-css');
         $parents = $this->callConduitWithDiffusionRequest('diffusion.commitparentsquery', array('commit' => $drequest->getCommit()));
         if ($parents) {
             $parents = id(new DiffusionCommitQuery())->setViewer($user)->withRepository($repository)->withIdentifiers($parents)->execute();
         }
         $headsup_view = id(new PHUIHeaderView())->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')));
         $headsup_actions = $this->renderHeadsupActionList($commit, $repository);
         $commit_properties = $this->loadCommitProperties($commit, $commit_data, $parents, $audit_requests);
         $property_list = id(new PHUIPropertyListView())->setHasKeyboardShortcuts(true)->setUser($user)->setObject($commit);
         foreach ($commit_properties as $key => $value) {
             $property_list->addProperty($key, $value);
         }
         $message = $commit_data->getCommitMessage();
         $revision = $commit->getCommitIdentifier();
         $message = $this->linkBugtraq($message);
         $message = $engine->markupText($message);
         $property_list->invokeWillRenderEvent();
         $property_list->setActionList($headsup_actions);
         $detail_list = new PHUIPropertyListView();
         $detail_list->addSectionHeader(pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
         $detail_list->addTextContent(phutil_tag('div', array('class' => 'diffusion-commit-message phabricator-remarkup'), $message));
         $headsup_view->setTall(true);
         $object_box = id(new PHUIObjectBoxView())->setHeader($headsup_view)->addPropertyList($property_list)->addPropertyList($detail_list);
         $content[] = $object_box;
     }
     $content[] = $this->buildComments($commit);
     $hard_limit = 1000;
     if ($commit->isImported()) {
         $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest);
         $change_query->setLimit($hard_limit + 1);
         $changes = $change_query->loadChanges();
     } else {
         $changes = array();
     }
     $was_limited = count($changes) > $hard_limit;
     if ($was_limited) {
         $changes = array_slice($changes, 0, $hard_limit);
     }
     $content[] = $this->buildMergesTable($commit);
     $highlighted_audits = $commit->getAuthorityAudits($user, $this->auditAuthorityPHIDs);
     $count = 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' . $callsign . $commit->getCommitIdentifier());
     }
     $show_changesets = false;
     if ($bad_commit) {
         $content[] = $this->renderStatusMessage(pht('Bad Commit'), $bad_commit['description']);
     } else {
         if ($is_foreign) {
             // Don't render anything else.
         } else {
             if (!$commit->isImported()) {
                 $content[] = $this->renderStatusMessage(pht('Still Importing...'), pht('This commit is still importing. Changes will be visible once ' . 'the import finishes.'));
             } else {
                 if (!count($changes)) {
                     $content[] = $this->renderStatusMessage(pht('Empty Commit'), pht('This commit is empty and does not affect any paths.'));
                 } else {
                     if ($was_limited) {
                         $content[] = $this->renderStatusMessage(pht('Enormous Commit'), pht('This commit is enormous, and affects more than %d files. ' . 'Changes are not shown.', $hard_limit));
                     } else {
                         $show_changesets = true;
                         // The user has clicked "Show All Changes", and we should show all the
                         // changes inline even if there are more than the soft limit.
                         $show_all_details = $request->getBool('show_all');
                         $change_panel = new PHUIObjectBoxView();
                         $header = new PHUIHeaderView();
                         $header->setHeader(pht('Changes (%s)', new PhutilNumber($count)));
                         $change_panel->setID('toc');
                         if ($count > self::CHANGES_LIMIT && !$show_all_details) {
                             $icon = id(new PHUIIconView())->setIconFont('fa-files-o');
                             $button = id(new PHUIButtonView())->setText(pht('Show All Changes'))->setHref('?show_all=true')->setTag('a')->setIcon($icon);
                             $warning_view = id(new PHUIInfoView())->setSeverity(PHUIInfoView::SEVERITY_WARNING)->setTitle(pht('Very Large Commit'))->appendChild(pht('This commit is very large. Load each file individually.'));
                             $change_panel->setInfoView($warning_view);
                             $header->addActionLink($button);
                         }
                         $changesets = DiffusionPathChange::convertToDifferentialChangesets($user, $changes);
                         // TODO: This table and panel shouldn't really be separate, but we need
                         // to clean up the "Load All Files" interaction first.
                         $change_table = $this->buildTableOfContents($changesets);
                         $change_panel->setTable($change_table);
                         $change_panel->setHeader($header);
                         $content[] = $change_panel;
                         $vcs = $repository->getVersionControlSystem();
                         switch ($vcs) {
                             case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
                                 $vcs_supports_directory_changes = true;
                                 break;
                             case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
                             case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
                                 $vcs_supports_directory_changes = false;
                                 break;
                             default:
                                 throw new Exception(pht('Unknown VCS.'));
                         }
                         $references = array();
                         foreach ($changesets as $key => $changeset) {
                             $file_type = $changeset->getFileType();
                             if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
                                 if (!$vcs_supports_directory_changes) {
                                     unset($changesets[$key]);
                                     continue;
                                 }
                             }
                             $references[$key] = $drequest->generateURI(array('action' => 'rendering-ref', 'path' => $changeset->getFilename()));
                         }
                         // TODO: Some parts of the views still rely on properties of the
                         // DifferentialChangeset. Make the objects ephemeral to make sure we don't
                         // accidentally save them, and then set their ID to the appropriate ID for
                         // this application (the path IDs).
                         $path_ids = array_flip(mpull($changes, 'getPath'));
                         foreach ($changesets as $changeset) {
                             $changeset->makeEphemeral();
                             $changeset->setID($path_ids[$changeset->getFilename()]);
                         }
                         if ($count <= self::CHANGES_LIMIT || $show_all_details) {
                             $visible_changesets = $changesets;
                         } else {
                             $visible_changesets = array();
                             $inlines = PhabricatorAuditInlineComment::loadDraftAndPublishedComments($user, $commit->getPHID());
                             $path_ids = mpull($inlines, null, 'getPathID');
                             foreach ($changesets as $key => $changeset) {
                                 if (array_key_exists($changeset->getID(), $path_ids)) {
                                     $visible_changesets[$key] = $changeset;
                                 }
                             }
                         }
                         $change_list_title = DiffusionView::nameCommit($repository, $commit->getCommitIdentifier());
                         $change_list = new DifferentialChangesetListView();
                         $change_list->setTitle($change_list_title);
                         $change_list->setChangesets($changesets);
                         $change_list->setVisibleChangesets($visible_changesets);
                         $change_list->setRenderingReferences($references);
                         $change_list->setRenderURI('/diffusion/' . $callsign . '/diff/');
                         $change_list->setRepository($repository);
                         $change_list->setUser($user);
                         // TODO: Try to setBranch() to something reasonable here?
                         $change_list->setStandaloneURI('/diffusion/' . $callsign . '/diff/');
                         $change_list->setRawFileURIs(null, '/diffusion/' . $callsign . '/diff/?view=r');
                         $change_list->setInlineCommentControllerURI('/diffusion/inline/edit/' . phutil_escape_uri($commit->getPHID()) . '/');
                         $content[] = $change_list->render();
                     }
                 }
             }
         }
     }
     $content[] = $this->renderAddCommentPanel($commit, $audit_requests);
     $commit_id = 'r' . $callsign . $commit->getCommitIdentifier();
     $short_name = DiffusionView::nameCommit($repository, $commit->getCommitIdentifier());
     $prefs = $user->loadPreferences();
     $pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
     $pref_collapse = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED;
     $show_filetree = $prefs->getPreference($pref_filetree);
     $collapsed = $prefs->getPreference($pref_collapse);
     if ($show_changesets && $show_filetree) {
         $nav = id(new DifferentialChangesetFileTreeSideNavBuilder())->setTitle($short_name)->setBaseURI(new PhutilURI('/' . $commit_id))->build($changesets)->setCrumbs($crumbs)->setCollapsed((bool) $collapsed)->appendChild($content);
         $content = $nav;
     } else {
         $content = array($crumbs, $content);
     }
     return $this->buildApplicationPage($content, array('title' => $commit_id, 'pageObjects' => array($commit->getPHID())));
 }
 private function isCommitOnBranch(PhabricatorRepository $repo, PhabricatorRepositoryCommit $commit, ReleephBranch $releeph_branch)
 {
     switch ($repo->getVersionControlSystem()) {
         case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
             list($output) = $repo->execxLocalCommand('branch --all --no-color --contains %s', $commit->getCommitIdentifier());
             $remote_prefix = 'remotes/origin/';
             $branches = array();
             foreach (array_filter(explode("\n", $output)) as $line) {
                 $tokens = explode(' ', $line);
                 $ref = last($tokens);
                 if (strncmp($ref, $remote_prefix, strlen($remote_prefix)) === 0) {
                     $branch = substr($ref, strlen($remote_prefix));
                     $branches[$branch] = $branch;
                 }
             }
             return idx($branches, $releeph_branch->getName());
             break;
         case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
             $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(DiffusionRequest::newFromDictionary(array('user' => $this->getUser(), 'repository' => $repo, 'commit' => $commit->getCommitIdentifier())));
             $path_changes = $change_query->loadChanges();
             $commit_paths = mpull($path_changes, 'getPath');
             $branch_path = $releeph_branch->getName();
             $in_branch = array();
             $ex_branch = array();
             foreach ($commit_paths as $path) {
                 if (strncmp($path, $branch_path, strlen($branch_path)) === 0) {
                     $in_branch[] = $path;
                 } else {
                     $ex_branch[] = $path;
                 }
             }
             if ($in_branch && $ex_branch) {
                 $error = pht('CONFUSION: commit %s in %s contains %d path change(s) that were ' . 'part of a Releeph branch, but also has %d path change(s) not ' . 'part of a Releeph branch!', $commit->getCommitIdentifier(), $repo->getCallsign(), count($in_branch), count($ex_branch));
                 phlog($error);
             }
             return !empty($in_branch);
             break;
     }
 }
 public function processRequest()
 {
     $drequest = $this->getDiffusionRequest();
     $request = $this->getRequest();
     $user = $request->getUser();
     $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();
     $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
     if ($is_foreign) {
         $subpath = $commit_data->getCommitDetail('svn-subpath');
         $error_panel = new AphrontErrorView();
         $error_panel->setWidth(AphrontErrorView::WIDTH_WIDE);
         $error_panel->setTitle('Commit Not Tracked');
         $error_panel->setSeverity(AphrontErrorView::SEVERITY_WARNING);
         $error_panel->appendChild("This Diffusion repository is configured to track only one " . "subdirectory of the entire Subversion repository, and this commit " . "didn't affect the tracked subdirectory ('" . phutil_escape_html($subpath) . "'), so no information is available.");
         $content[] = $error_panel;
     } else {
         $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
         require_celerity_resource('diffusion-commit-view-css');
         require_celerity_resource('phabricator-remarkup-css');
         $property_table = $this->renderPropertyTable($commit, $commit_data);
         $detail_panel->appendChild('<div class="diffusion-commit-view">' . '<div class="diffusion-commit-dateline">' . 'r' . $callsign . $commit->getCommitIdentifier() . ' &middot; ' . phabricator_datetime($commit->getEpoch(), $user) . '</div>' . '<h1>Revision Detail</h1>' . '<div class="diffusion-commit-details">' . $property_table . '<hr />' . '<div class="diffusion-commit-message phabricator-remarkup">' . $engine->markupText($commit_data->getCommitMessage()) . '</div>' . '</div>' . '</div>');
         $content[] = $detail_panel;
     }
     $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest);
     $changes = $change_query->loadChanges();
     $original_changes_count = count($changes);
     if ($request->getStr('show_all') !== 'true' && $original_changes_count > self::CHANGES_LIMIT) {
         $changes = array_slice($changes, 0, self::CHANGES_LIMIT);
     }
     $change_table = new DiffusionCommitChangeTableView();
     $change_table->setDiffusionRequest($drequest);
     $change_table->setPathChanges($changes);
     $count = 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' . $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 {
         if ($is_foreign) {
             // Don't render anything else.
         } else {
             if (!count($changes)) {
                 $no_changes = new AphrontErrorView();
                 $no_changes->setWidth(AphrontErrorView::WIDTH_WIDE);
                 $no_changes->setSeverity(AphrontErrorView::SEVERITY_WARNING);
                 $no_changes->setTitle('Not Yet Parsed');
                 // TODO: This can also happen with weird SVN changes that don't do
                 // anything (or only alter properties?), although the real no-changes case
                 // is extremely rare and might be impossible to produce organically. We
                 // should probably write some kind of "Nothing Happened!" change into the
                 // DB once we parse these changes so we can distinguish between
                 // "not parsed yet" and "no changes".
                 $no_changes->appendChild("This commit hasn't been fully parsed yet (or doesn't affect any " . "paths).");
                 $content[] = $no_changes;
             } else {
                 $change_panel = new AphrontPanelView();
                 $change_panel->setHeader("Changes (" . number_format($count) . ")");
                 if ($count !== $original_changes_count) {
                     $show_all_button = phutil_render_tag('a', array('class' => 'button green', 'href' => '?show_all=true'), phutil_escape_html('Show All Changes'));
                     $warning_view = id(new AphrontErrorView())->setSeverity(AphrontErrorView::SEVERITY_WARNING)->setTitle(sprintf("Showing only the first %d changes out of %s!", self::CHANGES_LIMIT, number_format($original_changes_count)));
                     $change_panel->appendChild($warning_view);
                     $change_panel->addButton($show_all_button);
                 }
                 $change_panel->appendChild($change_table);
                 $content[] = $change_panel;
                 $changesets = DiffusionPathChange::convertToDifferentialChangesets($changes);
                 $vcs = $repository->getVersionControlSystem();
                 switch ($vcs) {
                     case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
                         $vcs_supports_directory_changes = true;
                         break;
                     case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
                         $vcs_supports_directory_changes = false;
                         break;
                     default:
                         throw new Exception("Unknown VCS.");
                 }
                 $references = array();
                 foreach ($changesets as $key => $changeset) {
                     $file_type = $changeset->getFileType();
                     if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
                         if (!$vcs_supports_directory_changes) {
                             unset($changesets[$key]);
                             continue;
                         }
                     }
                     $branch = $drequest->getBranchURIComponent($drequest->getBranch());
                     $filename = $changeset->getFilename();
                     $commit = $drequest->getCommit();
                     $reference = "{$branch}{$filename};{$commit}";
                     $references[$key] = $reference;
                 }
                 $change_list = new DifferentialChangesetListView();
                 $change_list->setChangesets($changesets);
                 $change_list->setRenderingReferences($references);
                 $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 = '<div class="differential-primary-pane">' . $change_list->render() . '</div>';
                 $content[] = $change_list;
             }
         }
     }
     return $this->buildStandardPageResponse($content, array('title' => 'Diffusion'));
 }
 /**
  * NOTE: We have to work particularly hard for SVN as compared to other VCS.
  * That's okay but means this shares little code with the other VCS.
  */
 protected function getSVNResult(ConduitAPIRequest $request)
 {
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
     $effective_commit = $this->getEffectiveCommit($request);
     if (!$effective_commit) {
         return $this->getEmptyResult();
     }
     $drequest = clone $drequest;
     $drequest->updateSymbolicCommit($effective_commit);
     $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 $this->getEmptyResult();
     }
     $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;
         case DifferentialChangeType::TYPE_MOVE_AWAY:
             $old = array($path->getPath(), $path->getCommitIdentifier() - 1);
             $old_name = $path->getPath();
             $new_name = null;
             $new = null;
             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->buildSVNContentFuture($old), 'new' => $this->buildSVNContentFuture($new));
     $futures = array_filter($futures);
     foreach (new FutureIterator($futures) as $key => $future) {
         $stdout = '';
         try {
             list($stdout) = $future->resolvex();
         } catch (CommandException $e) {
             if ($path->getFileType() != DifferentialChangeType::FILE_DIRECTORY) {
                 throw $e;
             }
         }
         $futures[$key] = $stdout;
     }
     $old_data = idx($futures, 'old', '');
     $new_data = idx($futures, 'new', '');
     $engine = new PhabricatorDifferenceEngine();
     $engine->setOldName($old_name);
     $engine->setNewName($new_name);
     $raw_diff = $engine->generateRawDiffFromFileContent($old_data, $new_data);
     $arcanist_changes = DiffusionPathChange::convertToArcanistChanges($path_changes);
     $parser = $this->getDefaultParser();
     $parser->setChanges($arcanist_changes);
     $parser->forcePath($path->getPath());
     $changes = $parser->parseDiff($raw_diff);
     $change = $changes[$path->getPath()];
     return array($change);
 }
 public function processRequest()
 {
     $drequest = $this->getDiffusionRequest();
     $request = $this->getRequest();
     $user = $request->getUser();
     if ($request->getStr('diff')) {
         return $this->buildRawDiffResponse($drequest);
     }
     $callsign = $drequest->getRepository()->getCallsign();
     $content = array();
     $content[] = $this->buildCrumbs(array('commit' => true));
     $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();
     $commit->attachCommitData($commit_data);
     $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
     if ($is_foreign) {
         $subpath = $commit_data->getCommitDetail('svn-subpath');
         $error_panel = new AphrontErrorView();
         $error_panel->setWidth(AphrontErrorView::WIDTH_WIDE);
         $error_panel->setTitle('Commit Not Tracked');
         $error_panel->setSeverity(AphrontErrorView::SEVERITY_WARNING);
         $error_panel->appendChild("This Diffusion repository is configured to track only one " . "subdirectory of the entire Subversion repository, and this commit " . "didn't affect the tracked subdirectory ('" . phutil_escape_html($subpath) . "'), so no information is available.");
         $content[] = $error_panel;
     } else {
         $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
         require_celerity_resource('diffusion-commit-view-css');
         require_celerity_resource('phabricator-remarkup-css');
         $parent_query = DiffusionCommitParentsQuery::newFromDiffusionRequest($drequest);
         $headsup_panel = new AphrontHeadsupView();
         $headsup_panel->setHeader('Commit Detail');
         $headsup_panel->setActionList($this->renderHeadsupActionList($commit));
         $headsup_panel->setProperties($this->getCommitProperties($commit, $commit_data, $parent_query->loadParents()));
         $headsup_panel->appendChild('<div class="diffusion-commit-message phabricator-remarkup">' . $engine->markupText($commit_data->getCommitMessage()) . '</div>');
         $content[] = $headsup_panel;
     }
     $query = new PhabricatorAuditQuery();
     $query->withCommitPHIDs(array($commit->getPHID()));
     $audit_requests = $query->execute();
     $this->auditAuthorityPHIDs = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
     $content[] = $this->buildAuditTable($commit, $audit_requests);
     $content[] = $this->buildComments($commit);
     $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest);
     $changes = $change_query->loadChanges();
     $content[] = $this->buildMergesTable($commit);
     $original_changes_count = count($changes);
     if ($request->getStr('show_all') !== 'true' && $original_changes_count > self::CHANGES_LIMIT) {
         $changes = array_slice($changes, 0, self::CHANGES_LIMIT);
     }
     $owners_paths = array();
     if ($this->highlightedAudits) {
         $packages = id(new PhabricatorOwnersPackage())->loadAllWhere('phid IN (%Ls)', mpull($this->highlightedAudits, 'getAuditorPHID'));
         if ($packages) {
             $owners_paths = id(new PhabricatorOwnersPath())->loadAllWhere('repositoryPHID = %s AND packageID IN (%Ld)', $repository->getPHID(), mpull($packages, 'getID'));
         }
     }
     $change_table = new DiffusionCommitChangeTableView();
     $change_table->setDiffusionRequest($drequest);
     $change_table->setPathChanges($changes);
     $change_table->setOwnersPaths($owners_paths);
     $count = 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' . $callsign . $commit->getCommitIdentifier());
     }
     $pane_id = null;
     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 {
         if ($is_foreign) {
             // Don't render anything else.
         } else {
             if (!count($changes)) {
                 $no_changes = new AphrontErrorView();
                 $no_changes->setWidth(AphrontErrorView::WIDTH_WIDE);
                 $no_changes->setSeverity(AphrontErrorView::SEVERITY_WARNING);
                 $no_changes->setTitle('Not Yet Parsed');
                 // TODO: This can also happen with weird SVN changes that don't do
                 // anything (or only alter properties?), although the real no-changes case
                 // is extremely rare and might be impossible to produce organically. We
                 // should probably write some kind of "Nothing Happened!" change into the
                 // DB once we parse these changes so we can distinguish between
                 // "not parsed yet" and "no changes".
                 $no_changes->appendChild("This commit hasn't been fully parsed yet (or doesn't affect any " . "paths).");
                 $content[] = $no_changes;
             } else {
                 $change_panel = new AphrontPanelView();
                 $change_panel->setHeader("Changes (" . number_format($count) . ")");
                 $change_panel->setID('differential-review-toc');
                 if ($count !== $original_changes_count) {
                     $show_all_button = phutil_render_tag('a', array('class' => 'button green', 'href' => '?show_all=true'), phutil_escape_html('Show All Changes'));
                     $warning_view = id(new AphrontErrorView())->setSeverity(AphrontErrorView::SEVERITY_WARNING)->setTitle(sprintf("Showing only the first %d changes out of %s!", self::CHANGES_LIMIT, number_format($original_changes_count)));
                     $change_panel->appendChild($warning_view);
                     $change_panel->addButton($show_all_button);
                 }
                 $change_panel->appendChild($change_table);
                 $content[] = $change_panel;
                 $changesets = DiffusionPathChange::convertToDifferentialChangesets($changes);
                 $vcs = $repository->getVersionControlSystem();
                 switch ($vcs) {
                     case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
                         $vcs_supports_directory_changes = true;
                         break;
                     case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
                     case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
                         $vcs_supports_directory_changes = false;
                         break;
                     default:
                         throw new Exception("Unknown VCS.");
                 }
                 $references = array();
                 foreach ($changesets as $key => $changeset) {
                     $file_type = $changeset->getFileType();
                     if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
                         if (!$vcs_supports_directory_changes) {
                             unset($changesets[$key]);
                             continue;
                         }
                     }
                     $references[$key] = $drequest->generateURI(array('action' => 'rendering-ref', 'path' => $changeset->getFilename()));
                 }
                 // TODO: Some parts of the views still rely on properties of the
                 // DifferentialChangeset. Make the objects ephemeral to make sure we don't
                 // accidentally save them, and then set their ID to the appropriate ID for
                 // this application (the path IDs).
                 $pquery = new DiffusionPathIDQuery(mpull($changesets, 'getFilename'));
                 $path_ids = $pquery->loadPathIDs();
                 foreach ($changesets as $changeset) {
                     $changeset->makeEphemeral();
                     $changeset->setID($path_ids[$changeset->getFilename()]);
                 }
                 $change_list = new DifferentialChangesetListView();
                 $change_list->setChangesets($changesets);
                 $change_list->setVisibleChangesets($changesets);
                 $change_list->setRenderingReferences($references);
                 $change_list->setRenderURI('/diffusion/' . $callsign . '/diff/');
                 $change_list->setRepository($repository);
                 $change_list->setUser($user);
                 $change_list->setStandaloneURI('/diffusion/' . $callsign . '/diff/');
                 $change_list->setRawFileURIs(null, '/diffusion/' . $callsign . '/diff/?view=r');
                 $change_list->setInlineCommentControllerURI('/diffusion/inline/edit/' . phutil_escape_uri($commit->getPHID()) . '/');
                 // TODO: This is pretty awkward, unify the CSS between Diffusion and
                 // Differential better.
                 require_celerity_resource('differential-core-view-css');
                 $pane_id = celerity_generate_unique_node_id();
                 $add_comment_view = $this->renderAddCommentPanel($commit, $audit_requests, $pane_id);
                 $main_pane = phutil_render_tag('div', array('class' => 'differential-primary-pane', 'id' => $pane_id), $change_list->render() . $add_comment_view);
                 $content[] = $main_pane;
             }
         }
     }
     return $this->buildStandardPageResponse($content, array('title' => 'r' . $callsign . $commit->getCommitIdentifier()));
 }
 protected function executeQuery()
 {
     $drequest = $this->getRequest();
     if (!$drequest->getRawCommit()) {
         $effective_commit = $this->getEffectiveCommit();
         if (!$effective_commit) {
             return null;
         }
         // TODO: Sketchy side effect.
         $drequest->setCommit($effective_commit);
     }
     $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;
         case DifferentialChangeType::TYPE_MOVE_AWAY:
             $old = array($path->getPath(), $path->getCommitIdentifier() - 1);
             $old_name = $path->getPath();
             $new_name = null;
             $new = null;
             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) {
         list($stdout) = $future->resolvex();
         $futures[$key] = $stdout;
     }
     $old_data = idx($futures, 'old', '');
     $new_data = idx($futures, 'new', '');
     $engine = new PhabricatorDifferenceEngine();
     $engine->setOldName($old_name);
     $engine->setNewName($new_name);
     $raw_diff = $engine->generateRawDiffFromFileContent($old_data, $new_data);
     $parser = new ArcanistDiffParser();
     $parser->setDetectBinaryFiles(true);
     $arcanist_changes = DiffusionPathChange::convertToArcanistChanges($path_changes);
     $parser->setChanges($arcanist_changes);
     $parser->forcePath($path->getPath());
     $changes = $parser->parseDiff($raw_diff);
     $change = $changes[$path->getPath()];
     $diff = DifferentialDiff::newFromRawChanges(array($change));
     $changesets = $diff->getChangesets();
     $changeset = reset($changesets);
     $this->renderingReference = $drequest->getPath() . ';' . $drequest->getCommit();
     return $changeset;
 }
 public function processRequest()
 {
     $drequest = $this->getDiffusionRequest();
     $request = $this->getRequest();
     $user = $request->getUser();
     $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();
     $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
     require_celerity_resource('diffusion-commit-view-css');
     require_celerity_resource('phabricator-remarkup-css');
     $property_table = $this->renderPropertyTable($commit, $commit_data);
     $detail_panel->appendChild('<div class="diffusion-commit-view">' . '<div class="diffusion-commit-dateline">' . 'r' . $callsign . $commit->getCommitIdentifier() . ' &middot; ' . phabricator_datetime($commit->getEpoch(), $user) . '</div>' . '<h1>Revision Detail</h1>' . '<div class="diffusion-commit-details">' . $property_table . '<hr />' . '<div class="diffusion-commit-message phabricator-remarkup">' . $engine->markupText($commit_data->getCommitMessage()) . '</div>' . '</div>' . '</div>');
     $content[] = $detail_panel;
     $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest);
     $changes = $change_query->loadChanges();
     $original_changes_count = count($changes);
     if ($request->getStr('show_all') !== 'true' && $original_changes_count > self::CHANGES_LIMIT) {
         $changes = array_slice($changes, 0, self::CHANGES_LIMIT);
     }
     $change_table = new DiffusionCommitChangeTableView();
     $change_table->setDiffusionRequest($drequest);
     $change_table->setPathChanges($changes);
     $count = 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' . $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 (" . number_format($count) . ")");
         if ($count !== $original_changes_count) {
             $show_all_button = phutil_render_tag('a', array('class' => 'button green', 'href' => '?show_all=true'), phutil_escape_html('Show All Changes'));
             $warning_view = id(new AphrontErrorView())->setSeverity(AphrontErrorView::SEVERITY_WARNING)->setTitle(sprintf("Showing only the first %d changes out of %s!", self::CHANGES_LIMIT, number_format($original_changes_count)));
             $change_panel->appendChild($warning_view);
             $change_panel->addButton($show_all_button);
         }
         $change_panel->appendChild($change_table);
         $content[] = $change_panel;
         if ($changes) {
             $changesets = DiffusionPathChange::convertToDifferentialChangesets($changes);
             $vcs = $repository->getVersionControlSystem();
             switch ($vcs) {
                 case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
                     $vcs_supports_directory_changes = true;
                     break;
                 case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
                     $vcs_supports_directory_changes = false;
                     break;
                 default:
                     throw new Exception("Unknown VCS.");
             }
             $references = array();
             foreach ($changesets as $key => $changeset) {
                 $file_type = $changeset->getFileType();
                 if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
                     if (!$vcs_supports_directory_changes) {
                         unset($changesets[$key]);
                         continue;
                     }
                 }
                 $branch = $drequest->getBranchURIComponent($drequest->getBranch());
                 $filename = $changeset->getFilename();
                 $commit = $drequest->getCommit();
                 $reference = "{$branch}{$filename};{$commit}";
                 $references[$key] = $reference;
             }
             $change_list = new DifferentialChangesetListView();
             $change_list->setChangesets($changesets);
             $change_list->setRenderingReferences($references);
             $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 = '<div class="differential-primary-pane">' . $change_list->render() . '</div>';
         } else {
             $change_list = '<div style="margin: 2em; color: #666; padding: 1em;
         background: #eee;">' . '(no changes blah blah)' . '</div>';
         }
         $content[] = $change_list;
     }
     return $this->buildStandardPageResponse($content, array('title' => 'Diffusion'));
 }
 public function handleRequest(AphrontRequest $request)
 {
     $response = $this->loadDiffusionContext();
     if ($response) {
         return $response;
     }
     $drequest = $this->getDiffusionRequest();
     $viewer = $request->getUser();
     if ($request->getStr('diff')) {
         return $this->buildRawDiffResponse($drequest);
     }
     $repository = $drequest->getRepository();
     $commit = id(new DiffusionCommitQuery())->setViewer($viewer)->withRepository($repository)->withIdentifiers(array($drequest->getCommit()))->needCommitData(true)->needAuditRequests(true)->executeOne();
     $crumbs = $this->buildCrumbs(array('commit' => true));
     $crumbs->setBorder(true);
     if (!$commit) {
         if (!$this->getCommitExists()) {
             return new Aphront404Response();
         }
         $error = id(new PHUIInfoView())->setTitle(pht('Commit Still Parsing'))->appendChild(pht('Failed to load the commit because the commit has not been ' . 'parsed yet.'));
         $title = pht('Commit Still Parsing');
         return $this->newPage()->setTitle($title)->setCrumbs($crumbs)->appendChild($error);
     }
     $audit_requests = $commit->getAudits();
     $this->auditAuthorityPHIDs = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($viewer);
     $commit_data = $commit->getCommitData();
     $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
     $error_panel = null;
     if ($is_foreign) {
         $subpath = $commit_data->getCommitDetail('svn-subpath');
         $error_panel = new PHUIInfoView();
         $error_panel->setTitle(pht('Commit Not Tracked'));
         $error_panel->setSeverity(PHUIInfoView::SEVERITY_WARNING);
         $error_panel->appendChild(pht("This Diffusion repository is configured to track only one " . "subdirectory of the entire Subversion repository, and this commit " . "didn't affect the tracked subdirectory ('%s'), so no " . "information is available.", $subpath));
     } else {
         $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
         $engine->setConfig('viewer', $viewer);
         $commit_tag = $this->renderCommitHashTag($drequest);
         $header = id(new PHUIHeaderView())->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')))->setHeaderIcon('fa-code-fork')->addTag($commit_tag);
         if ($commit->getAuditStatus()) {
             $icon = PhabricatorAuditCommitStatusConstants::getStatusIcon($commit->getAuditStatus());
             $color = PhabricatorAuditCommitStatusConstants::getStatusColor($commit->getAuditStatus());
             $status = PhabricatorAuditCommitStatusConstants::getStatusName($commit->getAuditStatus());
             $header->setStatus($icon, $color, $status);
         }
         $curtain = $this->buildCurtain($commit, $repository);
         $subheader = $this->buildSubheaderView($commit, $commit_data);
         $details = $this->buildPropertyListView($commit, $commit_data, $audit_requests);
         $message = $commit_data->getCommitMessage();
         $revision = $commit->getCommitIdentifier();
         $message = $this->linkBugtraq($message);
         $message = $engine->markupText($message);
         $detail_list = new PHUIPropertyListView();
         $detail_list->addTextContent(phutil_tag('div', array('class' => 'diffusion-commit-message phabricator-remarkup'), $message));
         if ($this->getCommitErrors()) {
             $error_panel = id(new PHUIInfoView())->appendChild($this->getCommitErrors())->setSeverity(PHUIInfoView::SEVERITY_WARNING);
         }
     }
     $timeline = $this->buildComments($commit);
     $hard_limit = 1000;
     if ($commit->isImported()) {
         $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest);
         $change_query->setLimit($hard_limit + 1);
         $changes = $change_query->loadChanges();
     } else {
         $changes = array();
     }
     $was_limited = count($changes) > $hard_limit;
     if ($was_limited) {
         $changes = array_slice($changes, 0, $hard_limit);
     }
     $merge_table = $this->buildMergesTable($commit);
     $highlighted_audits = $commit->getAuthorityAudits($viewer, $this->auditAuthorityPHIDs);
     $count = 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, $commit->getMonogram());
     }
     $show_changesets = false;
     $info_panel = null;
     $change_list = null;
     $change_table = null;
     if ($bad_commit) {
         $info_panel = $this->renderStatusMessage(pht('Bad Commit'), $bad_commit['description']);
     } else {
         if ($is_foreign) {
             // Don't render anything else.
         } else {
             if (!$commit->isImported()) {
                 $info_panel = $this->renderStatusMessage(pht('Still Importing...'), pht('This commit is still importing. Changes will be visible once ' . 'the import finishes.'));
             } else {
                 if (!count($changes)) {
                     $info_panel = $this->renderStatusMessage(pht('Empty Commit'), pht('This commit is empty and does not affect any paths.'));
                 } else {
                     if ($was_limited) {
                         $info_panel = $this->renderStatusMessage(pht('Enormous Commit'), pht('This commit is enormous, and affects more than %d files. ' . 'Changes are not shown.', $hard_limit));
                     } else {
                         if (!$this->getCommitExists()) {
                             $info_panel = $this->renderStatusMessage(pht('Commit No Longer Exists'), pht('This commit no longer exists in the repository.'));
                         } else {
                             $show_changesets = true;
                             // The user has clicked "Show All Changes", and we should show all the
                             // changes inline even if there are more than the soft limit.
                             $show_all_details = $request->getBool('show_all');
                             $change_header = id(new PHUIHeaderView())->setHeader(pht('Changes (%s)', new PhutilNumber($count)));
                             $warning_view = null;
                             if ($count > self::CHANGES_LIMIT && !$show_all_details) {
                                 $button = id(new PHUIButtonView())->setText(pht('Show All Changes'))->setHref('?show_all=true')->setTag('a')->setIcon('fa-files-o');
                                 $warning_view = id(new PHUIInfoView())->setSeverity(PHUIInfoView::SEVERITY_WARNING)->setTitle(pht('Very Large Commit'))->appendChild(pht('This commit is very large. Load each file individually.'));
                                 $change_header->addActionLink($button);
                             }
                             $changesets = DiffusionPathChange::convertToDifferentialChangesets($viewer, $changes);
                             // TODO: This table and panel shouldn't really be separate, but we need
                             // to clean up the "Load All Files" interaction first.
                             $change_table = $this->buildTableOfContents($changesets, $change_header, $warning_view);
                             $vcs = $repository->getVersionControlSystem();
                             switch ($vcs) {
                                 case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
                                     $vcs_supports_directory_changes = true;
                                     break;
                                 case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
                                 case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
                                     $vcs_supports_directory_changes = false;
                                     break;
                                 default:
                                     throw new Exception(pht('Unknown VCS.'));
                             }
                             $references = array();
                             foreach ($changesets as $key => $changeset) {
                                 $file_type = $changeset->getFileType();
                                 if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
                                     if (!$vcs_supports_directory_changes) {
                                         unset($changesets[$key]);
                                         continue;
                                     }
                                 }
                                 $references[$key] = $drequest->generateURI(array('action' => 'rendering-ref', 'path' => $changeset->getFilename()));
                             }
                             // TODO: Some parts of the views still rely on properties of the
                             // DifferentialChangeset. Make the objects ephemeral to make sure we don't
                             // accidentally save them, and then set their ID to the appropriate ID for
                             // this application (the path IDs).
                             $path_ids = array_flip(mpull($changes, 'getPath'));
                             foreach ($changesets as $changeset) {
                                 $changeset->makeEphemeral();
                                 $changeset->setID($path_ids[$changeset->getFilename()]);
                             }
                             if ($count <= self::CHANGES_LIMIT || $show_all_details) {
                                 $visible_changesets = $changesets;
                             } else {
                                 $visible_changesets = array();
                                 $inlines = PhabricatorAuditInlineComment::loadDraftAndPublishedComments($viewer, $commit->getPHID());
                                 $path_ids = mpull($inlines, null, 'getPathID');
                                 foreach ($changesets as $key => $changeset) {
                                     if (array_key_exists($changeset->getID(), $path_ids)) {
                                         $visible_changesets[$key] = $changeset;
                                     }
                                 }
                             }
                             $change_list_title = $commit->getDisplayName();
                             $change_list = new DifferentialChangesetListView();
                             $change_list->setTitle($change_list_title);
                             $change_list->setChangesets($changesets);
                             $change_list->setVisibleChangesets($visible_changesets);
                             $change_list->setRenderingReferences($references);
                             $change_list->setRenderURI($repository->getPathURI('diff/'));
                             $change_list->setRepository($repository);
                             $change_list->setUser($viewer);
                             $change_list->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
                             // TODO: Try to setBranch() to something reasonable here?
                             $change_list->setStandaloneURI($repository->getPathURI('diff/'));
                             $change_list->setRawFileURIs(null, $repository->getPathURI('diff/?view=r'));
                             $change_list->setInlineCommentControllerURI('/diffusion/inline/edit/' . phutil_escape_uri($commit->getPHID()) . '/');
                         }
                     }
                 }
             }
         }
     }
     $add_comment = $this->renderAddCommentPanel($commit, $audit_requests);
     $filetree_on = $viewer->compareUserSetting(PhabricatorShowFiletreeSetting::SETTINGKEY, PhabricatorShowFiletreeSetting::VALUE_ENABLE_FILETREE);
     $pref_collapse = PhabricatorFiletreeVisibleSetting::SETTINGKEY;
     $collapsed = $viewer->getUserSetting($pref_collapse);
     $nav = null;
     if ($show_changesets && $filetree_on) {
         $nav = id(new DifferentialChangesetFileTreeSideNavBuilder())->setTitle($commit->getDisplayName())->setBaseURI(new PhutilURI($commit->getURI()))->build($changesets)->setCrumbs($crumbs)->setCollapsed((bool) $collapsed);
     }
     $view = id(new PHUITwoColumnView())->setHeader($header)->setSubheader($subheader)->setMainColumn(array($error_panel, $timeline, $merge_table, $info_panel))->setFooter(array($change_table, $change_list, $add_comment))->addPropertySection(pht('Description'), $detail_list)->addPropertySection(pht('Details'), $details)->setCurtain($curtain);
     $page = $this->newPage()->setTitle($commit->getDisplayName())->setCrumbs($crumbs)->setPageObjectPHIDS(array($commit->getPHID()))->appendChild(array($view));
     if ($nav) {
         $page->setNavigation($nav);
     }
     return $page;
 }