protected function executeQuery()
 {
     $drequest = $this->getRequest();
     $repository = $drequest->getRepository();
     $commit = $drequest->loadCommit();
     $raw_changes = queryfx_all($repository->establishConnection('r'), 'SELECT c.*, p.path pathName, t.path targetPathName
     FROM %T c
       LEFT JOIN %T p ON c.pathID = p.id
       LEFT JOIN %T t on c.targetPathID = t.id
     WHERE c.commitID = %d AND isDirect = 1', PhabricatorRepository::TABLE_PATHCHANGE, PhabricatorRepository::TABLE_PATH, PhabricatorRepository::TABLE_PATH, $commit->getID());
     $changes = array();
     $raw_changes = isort($raw_changes, 'pathName');
     foreach ($raw_changes as $raw_change) {
         $type = $raw_change['changeType'];
         if ($type == DifferentialChangeType::TYPE_CHILD) {
             continue;
         }
         $change = new DiffusionPathChange();
         $change->setPath(ltrim($raw_change['pathName'], '/'));
         $change->setChangeType($raw_change['changeType']);
         $change->setFileType($raw_change['fileType']);
         $change->setCommitIdentifier($commit->getCommitIdentifier());
         $changes[] = $change;
     }
     return $changes;
 }
 public function handleRequest(AphrontRequest $request)
 {
     $response = $this->loadDiffusionContext();
     if ($response) {
         return $response;
     }
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
     $pager = id(new PHUIPagerView())->readFromRequest($request);
     $params = array('commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), 'offset' => $pager->getOffset(), 'limit' => $pager->getPageSize() + 1);
     if (!$request->getBool('copies')) {
         $params['needDirectChanges'] = true;
         $params['needChildChanges'] = true;
     }
     $history_results = $this->callConduitWithDiffusionRequest('diffusion.historyquery', $params);
     $history = DiffusionPathChange::newFromConduit($history_results['pathChanges']);
     $history = $pager->sliceResults($history);
     $show_graph = !strlen($drequest->getPath());
     $history_table = id(new DiffusionHistoryTableView())->setUser($request->getUser())->setDiffusionRequest($drequest)->setHistory($history);
     $history_table->loadRevisions();
     if ($show_graph) {
         $history_table->setParents($history_results['parents']);
         $history_table->setIsHead(!$pager->getOffset());
         $history_table->setIsTail(!$pager->getHasMorePages());
     }
     $history_header = $this->buildHistoryHeader($drequest);
     $history_panel = id(new PHUIObjectBoxView())->setHeader($history_header)->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)->setTable($history_table);
     $header = $this->buildHeader($drequest, $repository);
     $crumbs = $this->buildCrumbs(array('branch' => true, 'path' => true, 'view' => 'history'));
     $crumbs->setBorder(true);
     $pager_box = $this->renderTablePagerBox($pager);
     $view = id(new PHUITwoColumnView())->setHeader($header)->setFooter(array($history_panel, $pager_box));
     return $this->newPage()->setTitle(array(pht('History'), $repository->getDisplayName()))->setCrumbs($crumbs)->appendChild(array($view));
 }
 public function updateTransactionMailBody(PhabricatorMetaMTAMailBody $body, PhabricatorApplicationTransactionEditor $editor, array $xactions)
 {
     // Put all the merged commits info int the mail body if this is a merge
     $merges_caption = '';
     // TODO: Make this limit configurable after T6030
     $limit = 50;
     $commit = $this->getObject();
     try {
         $merges = DiffusionPathChange::newFromConduit(id(new ConduitCall('diffusion.mergedcommitsquery', array('commit' => $commit->getCommitIdentifier(), 'limit' => $limit + 1, 'repository' => $commit->getRepository()->getPHID())))->setUser($this->getViewer())->execute());
         if (count($merges) > $limit) {
             $merges = array_slice($merges, 0, $limit);
             $merges_caption = pht("This commit merges more than %d changes. Only the first " . "%d are shown.\n", $limit, $limit);
         }
         if ($merges) {
             $merge_commits = array();
             foreach ($merges as $merge) {
                 $merge_commits[] = $merge->getAuthorName() . ': ' . $merge->getSummary();
             }
             $body->addTextSection(pht('MERGED COMMITS'), $merges_caption . implode("\n", $merge_commits));
         }
     } catch (ConduitException $ex) {
         // Log the exception into the email body
         $body->addTextSection(pht('MERGED COMMITS'), pht('Error generating merged commits: ') . $ex->getMessage());
     }
 }
 protected function execute(ConduitAPIRequest $request)
 {
     $drequest = DiffusionRequest::newFromDictionary(array('user' => $request->getUser(), 'callsign' => $request->getValue('callsign'), 'path' => $request->getValue('path'), 'branch' => $request->getValue('branch')));
     $limit = nonempty($request->getValue('limit'), self::DEFAULT_LIMIT);
     $history_result = DiffusionQuery::callConduitWithDiffusionRequest($request->getUser(), $drequest, 'diffusion.historyquery', array('commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), 'offset' => 0, 'limit' => $limit, 'needDirectChanges' => true, 'needChildChanges' => true));
     $history = DiffusionPathChange::newFromConduit($history_result['pathChanges']);
     $raw_commit_identifiers = mpull($history, 'getCommitIdentifier');
     $result = array();
     foreach ($raw_commit_identifiers as $id) {
         $result[] = 'r' . $request->getValue('callsign') . $id;
     }
     return $result;
 }
 protected function getSVNResult(ConduitAPIRequest $request)
 {
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
     $results = array();
     foreach ($request->getValue('paths') as $path => $commit) {
         $history_result = DiffusionQuery::callConduitWithDiffusionRequest($request->getUser(), $drequest, 'diffusion.historyquery', array('commit' => $commit, 'path' => $path, 'limit' => 1, 'offset' => 0, 'needDirectChanges' => true, 'needChildChanges' => true));
         $history_array = DiffusionPathChange::newFromConduit($history_result['pathChanges']);
         if ($history_array) {
             $results[$path] = head($history_array)->getCommit()->getCommitIdentifier();
         }
     }
     return $results;
 }
 protected function executeQuery()
 {
     $drequest = $this->getRequest();
     $repository = $drequest->getRepository();
     $path = $drequest->getPath();
     $commit = $drequest->getCommit();
     $conn_r = $repository->establishConnection('r');
     $paths = queryfx_all($conn_r, 'SELECT id, path FROM %T WHERE pathHash IN (%Ls)', PhabricatorRepository::TABLE_PATH, array(md5('/' . trim($path, '/'))));
     $paths = ipull($paths, 'id', 'path');
     $path_id = idx($paths, '/' . trim($path, '/'));
     if (!$path_id) {
         return array();
     }
     $filter_query = '';
     if ($this->needDirectChanges) {
         if ($this->needChildChanges) {
             $type = DifferentialChangeType::TYPE_CHILD;
             $filter_query = 'AND (isDirect = 1 OR changeType = ' . $type . ')';
         } else {
             $filter_query = 'AND (isDirect = 1)';
         }
     }
     $history_data = queryfx_all($conn_r, 'SELECT * FROM %T WHERE repositoryID = %d AND pathID = %d
     AND commitSequence <= %d
     %Q
     ORDER BY commitSequence DESC
     LIMIT %d, %d', PhabricatorRepository::TABLE_PATHCHANGE, $repository->getID(), $path_id, $commit ? $commit : 0x7fffffff, $filter_query, $this->getOffset(), $this->getLimit());
     $commits = array();
     $commit_data = array();
     $commit_ids = ipull($history_data, 'commitID');
     if ($commit_ids) {
         $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere('id IN (%Ld)', $commit_ids);
         if ($commits) {
             $commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere('commitID in (%Ld)', $commit_ids);
             $commit_data = mpull($commit_data, null, 'getCommitID');
         }
     }
     $history = array();
     foreach ($history_data as $row) {
         $item = new DiffusionPathChange();
         $commit = idx($commits, $row['commitID']);
         if ($commit) {
             $item->setCommit($commit);
             $item->setCommitIdentifier($commit->getCommitIdentifier());
             $data = idx($commit_data, $commit->getID());
             if ($data) {
                 $item->setCommitData($data);
             }
         }
         $item->setChangeType($row['changeType']);
         $item->setFileType($row['fileType']);
         $history[] = $item;
     }
     return $history;
 }
 public function processRequest()
 {
     $drequest = $this->diffusionRequest;
     $request = $this->getRequest();
     $viewer = $request->getUser();
     $repository = $drequest->getRepository();
     $page_size = $request->getInt('pagesize', 100);
     $offset = $request->getInt('offset', 0);
     $params = array('commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), 'offset' => $offset, 'limit' => $page_size + 1);
     if (!$request->getBool('copies')) {
         $params['needDirectChanges'] = true;
         $params['needChildChanges'] = true;
     }
     $history_results = $this->callConduitWithDiffusionRequest('diffusion.historyquery', $params);
     $history = DiffusionPathChange::newFromConduit($history_results['pathChanges']);
     $pager = new AphrontPagerView();
     $pager->setPageSize($page_size);
     $pager->setOffset($offset);
     $history = $pager->sliceResults($history);
     $pager->setURI($request->getRequestURI(), 'offset');
     $show_graph = !strlen($drequest->getPath());
     $content = array();
     $history_table = new DiffusionHistoryTableView();
     $history_table->setUser($request->getUser());
     $history_table->setDiffusionRequest($drequest);
     $history_table->setHistory($history);
     $history_table->loadRevisions();
     $phids = $history_table->getRequiredHandlePHIDs();
     $handles = $this->loadViewerHandles($phids);
     $history_table->setHandles($handles);
     if ($show_graph) {
         $history_table->setParents($history_results['parents']);
         $history_table->setIsHead($offset == 0);
     }
     $history_panel = new AphrontPanelView();
     $history_panel->appendChild($history_table);
     $history_panel->appendChild($pager);
     $history_panel->setNoBackground();
     $content[] = $history_panel;
     $header = id(new PHUIHeaderView())->setUser($viewer)->setPolicyObject($repository)->setHeader($this->renderPathLinks($drequest, $mode = 'history'));
     $actions = $this->buildActionView($drequest);
     $properties = $this->buildPropertyView($drequest, $actions);
     $object_box = id(new PHUIObjectBoxView())->setHeader($header)->addPropertyList($properties);
     $crumbs = $this->buildCrumbs(array('branch' => true, 'path' => true, 'view' => 'history'));
     return $this->buildApplicationPage(array($crumbs, $object_box, $content), array('title' => array(pht('History'), pht('%s Repository', $drequest->getRepository()->getCallsign()))));
 }
 protected function executeQuery()
 {
     $drequest = $this->getRequest();
     $repository = $drequest->getRepository();
     $path = $drequest->getPath();
     $commit_hash = $drequest->getCommit();
     $local_path = $repository->getDetail('local-path');
     list($stdout) = execx('(cd %s && git log ' . '--skip=%d ' . '-n %d ' . '--abbrev=40 ' . '--pretty=format:%%H ' . '%s -- %s)', $local_path, $this->getOffset(), $this->getLimit(), $commit_hash, $path);
     $hashes = explode("\n", $stdout);
     $hashes = array_filter($hashes);
     $commits = array();
     $commit_data = array();
     $path_changes = array();
     $conn_r = $repository->establishConnection('r');
     if ($hashes) {
         $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere('repositoryID = %d AND commitIdentifier IN (%Ls)', $repository->getID(), $hashes);
         $commits = mpull($commits, null, 'getCommitIdentifier');
         if ($commits) {
             $commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere('commitID in (%Ld)', mpull($commits, 'getID'));
             $commit_data = mpull($commit_data, null, 'getCommitID');
         }
         if ($commits) {
             $path_normal = '/' . trim($path, '/');
             $paths = queryfx_all($conn_r, 'SELECT id, path FROM %T WHERE path IN (%Ls)', PhabricatorRepository::TABLE_PATH, array($path_normal));
             $paths = ipull($paths, 'id', 'path');
             $path_id = idx($paths, $path_normal);
             $path_changes = queryfx_all($conn_r, 'SELECT * FROM %T WHERE commitID IN (%Ld) AND pathID = %d', PhabricatorRepository::TABLE_PATHCHANGE, mpull($commits, 'getID'), $path_id);
             $path_changes = ipull($path_changes, null, 'commitID');
         }
     }
     $history = array();
     foreach ($hashes as $hash) {
         $item = new DiffusionPathChange();
         $item->setCommitIdentifier($hash);
         $commit = idx($commits, $hash);
         if ($commit) {
             $item->setCommit($commit);
             $data = idx($commit_data, $commit->getID());
             if ($data) {
                 $item->setCommitData($data);
             }
             $change = idx($path_changes, $commit->getID());
             if ($change) {
                 $item->setChangeType($change['changeType']);
                 $item->setFileType($change['fileType']);
             }
         }
         $history[] = $item;
     }
     return $history;
 }
 public function handleRequest(AphrontRequest $request)
 {
     $response = $this->loadDiffusionContext();
     if ($response) {
         return $response;
     }
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
     $pager = id(new PHUIPagerView())->readFromRequest($request);
     $params = array('commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), 'offset' => $pager->getOffset(), 'limit' => $pager->getPageSize() + 1);
     if (!$request->getBool('copies')) {
         $params['needDirectChanges'] = true;
         $params['needChildChanges'] = true;
     }
     $history_results = $this->callConduitWithDiffusionRequest('diffusion.historyquery', $params);
     $history = DiffusionPathChange::newFromConduit($history_results['pathChanges']);
     $history = $pager->sliceResults($history);
     $show_graph = !strlen($drequest->getPath());
     $content = array();
     $history_table = id(new DiffusionHistoryTableView())->setUser($request->getUser())->setDiffusionRequest($drequest)->setHistory($history);
     $history_table->loadRevisions();
     if ($show_graph) {
         $history_table->setParents($history_results['parents']);
         $history_table->setIsHead(!$pager->getOffset());
         $history_table->setIsTail(!$pager->getHasMorePages());
     }
     $history_panel = new PHUIObjectBoxView();
     $history_panel->setHeaderText(pht('History'));
     $history_panel->setTable($history_table);
     $content[] = $history_panel;
     $header = id(new PHUIHeaderView())->setUser($viewer)->setPolicyObject($repository)->setHeader($this->renderPathLinks($drequest, $mode = 'history'));
     $actions = $this->buildActionView($drequest);
     $properties = $this->buildPropertyView($drequest, $actions);
     $object_box = id(new PHUIObjectBoxView())->setHeader($header)->addPropertyList($properties);
     $crumbs = $this->buildCrumbs(array('branch' => true, 'path' => true, 'view' => 'history'));
     $pager_box = $this->renderTablePagerBox($pager);
     return $this->newPage()->setTitle(array(pht('History'), $repository->getDisplayName()))->setCrumbs($crumbs)->appendChild(array($object_box, $content, $pager_box));
 }
 private function loadCommitState()
 {
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
     $commit = $drequest->getCommit();
     // TODO: We could use futures here and resolve these calls in parallel.
     $exceptions = array();
     try {
         $parent_refs = $this->callConduitWithDiffusionRequest('diffusion.commitparentsquery', array('commit' => $commit));
         if ($parent_refs) {
             $parents = id(new DiffusionCommitQuery())->setViewer($viewer)->withRepository($repository)->withIdentifiers($parent_refs)->execute();
         } else {
             $parents = array();
         }
         $this->commitParents = $parents;
     } catch (Exception $ex) {
         $this->commitParents = false;
         $exceptions[] = $ex;
     }
     $merge_limit = $this->getMergeDisplayLimit();
     try {
         if ($repository->isSVN()) {
             $this->commitMerges = array();
         } else {
             $merges = $this->callConduitWithDiffusionRequest('diffusion.mergedcommitsquery', array('commit' => $commit, 'limit' => $merge_limit + 1));
             $this->commitMerges = DiffusionPathChange::newFromConduit($merges);
         }
     } catch (Exception $ex) {
         $this->commitMerges = false;
         $exceptions[] = $ex;
     }
     try {
         if ($repository->isGit()) {
             $refs = $this->callConduitWithDiffusionRequest('diffusion.refsquery', array('commit' => $commit));
         } else {
             $refs = array();
         }
         $this->commitRefs = $refs;
     } catch (Exception $ex) {
         $this->commitRefs = false;
         $exceptions[] = $ex;
     }
     if ($exceptions) {
         $exists = $this->callConduitWithDiffusionRequest('diffusion.existsquery', array('commit' => $commit));
         if ($exists) {
             $this->commitExists = true;
             foreach ($exceptions as $exception) {
                 $this->commitErrors[] = $exception->getMessage();
             }
         } else {
             $this->commitExists = false;
             $this->commitErrors[] = pht('This commit no longer exists in the repository. It may have ' . 'been part of a branch which was deleted.');
         }
     } else {
         $this->commitExists = true;
         $this->commitErrors = array();
     }
 }
 public function processRequest()
 {
     $drequest = $this->getDiffusionRequest();
     $request = $this->getRequest();
     $user = $request->getUser();
     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 AphrontErrorView())->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'), 'device' => false));
     }
     $top_anchor = id(new PhabricatorAnchorView())->setAnchorName('top')->setNavigationMarker(true);
     $audit_requests = $commit->getAudits();
     $this->auditAuthorityPHIDs = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
     $commit_data = $commit->getCommitData();
     $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
     $changesets = null;
     if ($is_foreign) {
         $subpath = $commit_data->getCommitDetail('svn-subpath');
         $error_panel = new AphrontErrorView();
         $error_panel->setTitle(pht('Commit Not Tracked'));
         $error_panel->setSeverity(AphrontErrorView::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;
         $content[] = $top_anchor;
     } 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));
         $content[] = $top_anchor;
         $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);
     $owners_paths = array();
     if ($highlighted_audits) {
         $packages = id(new PhabricatorOwnersPackage())->loadAllWhere('phid IN (%Ls)', mpull($highlighted_audits, '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());
     }
     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 {
                         // 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('Changes (' . number_format($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 AphrontErrorView())->setSeverity(AphrontErrorView::SEVERITY_WARNING)->setTitle('Very Large Commit')->appendChild(pht('This commit is very large. Load each file individually.'));
                             $change_panel->setErrorView($warning_view);
                             $header->addActionLink($button);
                         }
                         $change_panel->appendChild($change_table);
                         $change_panel->setHeader($header);
                         $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).
                         $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()) . '/');
                         $change_references = array();
                         foreach ($changesets as $key => $changeset) {
                             $change_references[$changeset->getID()] = $references[$key];
                         }
                         $change_table->setRenderingReferences($change_references);
                         $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 ($changesets && $show_filetree) {
         $nav = id(new DifferentialChangesetFileTreeSideNavBuilder())->setAnchorName('top')->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()), 'device' => false));
 }
Example #12
0
 public static final function loadHistoryForCommitIdentifiers(array $identifiers, DiffusionRequest $drequest)
 {
     if (!$identifiers) {
         return array();
     }
     $repository = $drequest->getRepository();
     $commits = self::loadCommitsByIdentifiers($identifiers, $drequest);
     if (!$commits) {
         return array();
     }
     $path = $drequest->getPath();
     $conn_r = $repository->establishConnection('r');
     $path_normal = DiffusionPathIDQuery::normalizePath($path);
     $paths = queryfx_all($conn_r, 'SELECT id, path FROM %T WHERE pathHash IN (%Ls)', PhabricatorRepository::TABLE_PATH, array(md5($path_normal)));
     $paths = ipull($paths, 'id', 'path');
     $path_id = idx($paths, $path_normal);
     $commit_ids = array_filter(mpull($commits, 'getID'));
     $path_changes = array();
     if ($path_id && $commit_ids) {
         $path_changes = queryfx_all($conn_r, 'SELECT * FROM %T WHERE commitID IN (%Ld) AND pathID = %d', PhabricatorRepository::TABLE_PATHCHANGE, $commit_ids, $path_id);
         $path_changes = ipull($path_changes, null, 'commitID');
     }
     $history = array();
     foreach ($identifiers as $identifier) {
         $item = new DiffusionPathChange();
         $item->setCommitIdentifier($identifier);
         $commit = idx($commits, $identifier);
         if ($commit) {
             $item->setCommit($commit);
             try {
                 $item->setCommitData($commit->getCommitData());
             } catch (Exception $ex) {
                 // Ignore, commit just doesn't have data.
             }
             $change = idx($path_changes, $commit->getID());
             if ($change) {
                 $item->setChangeType($change['changeType']);
                 $item->setFileType($change['fileType']);
             }
         }
         $history[] = $item;
     }
     return $history;
 }
 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()));
 }
 private function buildNormalContent(DiffusionRequest $drequest)
 {
     $repository = $drequest->getRepository();
     $phids = array();
     $content = array();
     try {
         $history_results = $this->callConduitWithDiffusionRequest('diffusion.historyquery', array('commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), 'offset' => 0, 'limit' => 15));
         $history = DiffusionPathChange::newFromConduit($history_results['pathChanges']);
         foreach ($history as $item) {
             $data = $item->getCommitData();
             if ($data) {
                 if ($data->getCommitDetail('authorPHID')) {
                     $phids[$data->getCommitDetail('authorPHID')] = true;
                 }
                 if ($data->getCommitDetail('committerPHID')) {
                     $phids[$data->getCommitDetail('committerPHID')] = true;
                 }
             }
         }
         $history_exception = null;
     } catch (Exception $ex) {
         $history_results = null;
         $history = null;
         $history_exception = $ex;
     }
     try {
         $browse_results = DiffusionBrowseResultSet::newFromConduit($this->callConduitWithDiffusionRequest('diffusion.browsequery', array('path' => $drequest->getPath(), 'commit' => $drequest->getCommit())));
         $browse_paths = $browse_results->getPaths();
         foreach ($browse_paths as $item) {
             $data = $item->getLastCommitData();
             if ($data) {
                 if ($data->getCommitDetail('authorPHID')) {
                     $phids[$data->getCommitDetail('authorPHID')] = true;
                 }
                 if ($data->getCommitDetail('committerPHID')) {
                     $phids[$data->getCommitDetail('committerPHID')] = true;
                 }
             }
         }
         $browse_exception = null;
     } catch (Exception $ex) {
         $browse_results = null;
         $browse_paths = null;
         $browse_exception = $ex;
     }
     $phids = array_keys($phids);
     $handles = $this->loadViewerHandles($phids);
     if ($browse_results) {
         $readme = $this->callConduitWithDiffusionRequest('diffusion.readmequery', array('paths' => $browse_results->getPathDicts(), 'commit' => $drequest->getStableCommit()));
     } else {
         $readme = null;
     }
     $content[] = $this->buildBrowseTable($browse_results, $browse_paths, $browse_exception, $handles);
     $content[] = $this->buildHistoryTable($history_results, $history, $history_exception, $handles);
     try {
         $content[] = $this->buildTagListTable($drequest);
     } catch (Exception $ex) {
         if (!$repository->isImporting()) {
             $content[] = $this->renderStatusMessage(pht('Unable to Load Tags'), $ex->getMessage());
         }
     }
     try {
         $content[] = $this->buildBranchListTable($drequest);
     } catch (Exception $ex) {
         if (!$repository->isImporting()) {
             $content[] = $this->renderStatusMessage(pht('Unable to Load Branches'), $ex->getMessage());
         }
     }
     if ($readme) {
         $box = new PHUIBoxView();
         $box->appendChild($readme);
         $box->addPadding(PHUI::PADDING_LARGE);
         $panel = new PHUIObjectBoxView();
         $panel->setHeaderText(pht('README'));
         $panel->appendChild($box);
         $content[] = $panel;
     }
     return $content;
 }
 protected function executeQuery()
 {
     $drequest = $this->getRequest();
     $repository = $drequest->getRepository();
     $commit = $drequest->loadCommit();
     $conn_r = $repository->establishConnection('r');
     $limit = '';
     if ($this->limit) {
         $limit = qsprintf($conn_r, 'LIMIT %d', $this->limit + 1);
     }
     $raw_changes = queryfx_all($conn_r, 'SELECT c.*, p.path pathName, t.path targetPathName,
       i.commitIdentifier targetCommitIdentifier
     FROM %T c
       LEFT JOIN %T p ON c.pathID = p.id
       LEFT JOIN %T t ON c.targetPathID = t.id
       LEFT JOIN %T i ON c.targetCommitID = i.id
     WHERE c.commitID = %d AND isDirect = 1 %Q', PhabricatorRepository::TABLE_PATHCHANGE, PhabricatorRepository::TABLE_PATH, PhabricatorRepository::TABLE_PATH, $commit->getTableName(), $commit->getID(), $limit);
     $limited = $this->limit && count($raw_changes) > $this->limit;
     if ($limited) {
         $raw_changes = array_slice($raw_changes, 0, $this->limit);
     }
     $changes = array();
     $raw_changes = isort($raw_changes, 'pathName');
     foreach ($raw_changes as $raw_change) {
         $type = $raw_change['changeType'];
         if ($type == DifferentialChangeType::TYPE_CHILD) {
             continue;
         }
         $change = new DiffusionPathChange();
         $change->setPath(ltrim($raw_change['pathName'], '/'));
         $change->setChangeType($raw_change['changeType']);
         $change->setFileType($raw_change['fileType']);
         $change->setCommitIdentifier($commit->getCommitIdentifier());
         $change->setTargetPath(ltrim($raw_change['targetPathName'], '/'));
         $change->setTargetCommitIdentifier($raw_change['targetCommitIdentifier']);
         $id = $raw_change['pathID'];
         $changes[$id] = $change;
     }
     // Deduce the away paths by examining all the changes, if we loaded them
     // all.
     if (!$limited) {
         $away = array();
         foreach ($changes as $change) {
             if ($change->getTargetPath()) {
                 $away[$change->getTargetPath()][] = $change->getPath();
             }
         }
         foreach ($changes as $change) {
             if (isset($away[$change->getPath()])) {
                 $change->setAwayPaths($away[$change->getPath()]);
             }
         }
     }
     return $changes;
 }
 /**
  * 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();
     $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'));
 }
 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'));
 }
 private function buildNormalContent(DiffusionRequest $drequest)
 {
     $repository = $drequest->getRepository();
     $phids = array();
     $content = array();
     try {
         $history_results = $this->callConduitWithDiffusionRequest('diffusion.historyquery', array('commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), 'offset' => 0, 'limit' => 15));
         $history = DiffusionPathChange::newFromConduit($history_results['pathChanges']);
         foreach ($history as $item) {
             $data = $item->getCommitData();
             if ($data) {
                 if ($data->getCommitDetail('authorPHID')) {
                     $phids[$data->getCommitDetail('authorPHID')] = true;
                 }
                 if ($data->getCommitDetail('committerPHID')) {
                     $phids[$data->getCommitDetail('committerPHID')] = true;
                 }
             }
         }
         $history_exception = null;
     } catch (Exception $ex) {
         $history_results = null;
         $history = null;
         $history_exception = $ex;
     }
     try {
         $browse_results = DiffusionBrowseResultSet::newFromConduit($this->callConduitWithDiffusionRequest('diffusion.browsequery', array('path' => $drequest->getPath(), 'commit' => $drequest->getCommit())));
         $browse_paths = $browse_results->getPaths();
         foreach ($browse_paths as $item) {
             $data = $item->getLastCommitData();
             if ($data) {
                 if ($data->getCommitDetail('authorPHID')) {
                     $phids[$data->getCommitDetail('authorPHID')] = true;
                 }
                 if ($data->getCommitDetail('committerPHID')) {
                     $phids[$data->getCommitDetail('committerPHID')] = true;
                 }
             }
         }
         $browse_exception = null;
     } catch (Exception $ex) {
         $browse_results = null;
         $browse_paths = null;
         $browse_exception = $ex;
     }
     $phids = array_keys($phids);
     $handles = $this->loadViewerHandles($phids);
     $readme = null;
     if ($browse_results) {
         $readme_path = $browse_results->getReadmePath();
         if ($readme_path) {
             $readme_content = $this->callConduitWithDiffusionRequest('diffusion.filecontentquery', array('path' => $readme_path, 'commit' => $drequest->getStableCommit()));
             if ($readme_content) {
                 $readme = id(new DiffusionReadmeView())->setUser($this->getViewer())->setPath($readme_path)->setContent($readme_content['corpus']);
             }
         }
     }
     $content[] = $this->buildBrowseTable($browse_results, $browse_paths, $browse_exception, $handles);
     $content[] = $this->buildHistoryTable($history_results, $history, $history_exception);
     try {
         $content[] = $this->buildTagListTable($drequest);
     } catch (Exception $ex) {
         if (!$repository->isImporting()) {
             $content[] = $this->renderStatusMessage(pht('Unable to Load Tags'), $ex->getMessage());
         }
     }
     try {
         $content[] = $this->buildBranchListTable($drequest);
     } catch (Exception $ex) {
         if (!$repository->isImporting()) {
             $content[] = $this->renderStatusMessage(pht('Unable to Load Branches'), $ex->getMessage());
         }
     }
     if ($readme) {
         $content[] = $readme;
     }
     return $content;
 }
 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;
 }
 private function buildMergesTable(PhabricatorRepositoryCommit $commit)
 {
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
     $vcs = $repository->getVersionControlSystem();
     switch ($vcs) {
         case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
             // These aren't supported under SVN.
             return null;
     }
     $limit = 50;
     $merges = $this->callConduitWithDiffusionRequest('diffusion.mergedcommitsquery', array('commit' => $drequest->getCommit(), 'limit' => $limit + 1));
     if (!$merges) {
         return null;
     }
     $merges = DiffusionPathChange::newFromConduit($merges);
     $caption = null;
     if (count($merges) > $limit) {
         $merges = array_slice($merges, 0, $limit);
         $caption = new PHUIInfoView();
         $caption->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
         $caption->appendChild(pht('This commit merges a very large number of changes. ' . 'Only the first %s are shown.', new PhutilNumber($limit)));
     }
     $history_table = new DiffusionHistoryTableView();
     $history_table->setUser($this->getRequest()->getUser());
     $history_table->setDiffusionRequest($drequest);
     $history_table->setHistory($merges);
     $history_table->loadRevisions();
     $phids = $history_table->getRequiredHandlePHIDs();
     $handles = $this->loadViewerHandles($phids);
     $history_table->setHandles($handles);
     $panel = new PHUIObjectBoxView();
     $panel->setHeaderText(pht('Merged Changes'));
     $panel->setTable($history_table);
     if ($caption) {
         $panel->setInfoView($caption);
     }
     return $panel;
 }
 protected final function loadHistoryForCommitIdentifiers(array $identifiers)
 {
     if (!$identifiers) {
         return array();
     }
     $commits = array();
     $commit_data = array();
     $path_changes = array();
     $drequest = $this->getRequest();
     $repository = $drequest->getRepository();
     $path = $drequest->getPath();
     $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere('repositoryID = %d AND commitIdentifier IN (%Ls)', $repository->getID(), $identifiers);
     $commits = mpull($commits, null, 'getCommitIdentifier');
     if (!$commits) {
         return array();
     }
     $commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere('commitID in (%Ld)', mpull($commits, 'getID'));
     $commit_data = mpull($commit_data, null, 'getCommitID');
     $conn_r = $repository->establishConnection('r');
     $path_normal = DiffusionPathIDQuery::normalizePath($path);
     $paths = queryfx_all($conn_r, 'SELECT id, path FROM %T WHERE path IN (%Ls)', PhabricatorRepository::TABLE_PATH, array($path_normal));
     $paths = ipull($paths, 'id', 'path');
     $path_id = idx($paths, $path_normal);
     $path_changes = queryfx_all($conn_r, 'SELECT * FROM %T WHERE commitID IN (%Ld) AND pathID = %d', PhabricatorRepository::TABLE_PATHCHANGE, mpull($commits, 'getID'), $path_id);
     $path_changes = ipull($path_changes, null, 'commitID');
     $history = array();
     foreach ($identifiers as $identifier) {
         $item = new DiffusionPathChange();
         $item->setCommitIdentifier($identifier);
         $commit = idx($commits, $identifier);
         if ($commit) {
             $item->setCommit($commit);
             $data = idx($commit_data, $commit->getID());
             if ($data) {
                 $item->setCommitData($data);
             }
             $change = idx($path_changes, $commit->getID());
             if ($change) {
                 $item->setChangeType($change['changeType']);
                 $item->setFileType($change['fileType']);
             }
         }
         $history[] = $item;
     }
     return $history;
 }