protected final function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit)
 {
     $viewer = PhabricatorUser::getOmnipotentUser();
     $refs_raw = DiffusionQuery::callConduitWithDiffusionRequest($viewer, DiffusionRequest::newFromDictionary(array('repository' => $repository, 'user' => $viewer)), 'diffusion.querycommits', array('repositoryPHID' => $repository->getPHID(), 'phids' => array($commit->getPHID()), 'bypassCache' => true, 'needMessages' => true));
     if (empty($refs_raw['data'])) {
         throw new Exception(pht('Unable to retrieve details for commit "%s"!', $commit->getPHID()));
     }
     $ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data']));
     $this->parseCommitWithRef($repository, $commit, $ref);
 }
 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 final function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit)
 {
     if (!$this->shouldSkipImportStep()) {
         $viewer = PhabricatorUser::getOmnipotentUser();
         $refs_raw = DiffusionQuery::callConduitWithDiffusionRequest($viewer, DiffusionRequest::newFromDictionary(array('repository' => $repository, 'user' => $viewer)), 'diffusion.querycommits', array('repositoryPHID' => $repository->getPHID(), 'phids' => array($commit->getPHID()), 'bypassCache' => true, 'needMessages' => true));
         if (empty($refs_raw['data'])) {
             throw new Exception(pht('Unable to retrieve details for commit "%s"!', $commit->getPHID()));
         }
         $ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data']));
         $this->updateCommitData($ref);
     }
     if ($this->shouldQueueFollowupTasks()) {
         $this->queueTask($this->getFollowupTaskClass(), array('commitID' => $commit->getID()), array('priority' => PhabricatorWorker::PRIORITY_DEFAULT));
     }
 }
예제 #5
0
 private function loadCacheKeys(array $paths)
 {
     $request = $this->getRequest();
     $viewer = $request->getUser();
     $repository = $request->getRepository();
     $repository_id = $repository->getID();
     $last_modified = parent::callConduitWithDiffusionRequest($viewer, $request, 'diffusion.lastmodifiedquery', array('paths' => array_fill_keys($paths, $request->getCommit())));
     $map = array();
     foreach ($paths as $path) {
         $identifier = idx($last_modified, $path);
         if ($identifier === null) {
             continue;
         }
         $path_hash = PhabricatorHash::digestForIndex($path);
         $map[$path] = "blame({$repository_id}, {$identifier}, {$path_hash}, raw)";
     }
     return $map;
 }
 public function execute(PhutilArgumentParser $args)
 {
     $commits = $this->loadCommits($args, 'commits');
     if (!$commits) {
         throw new PhutilArgumentUsageException(pht('Specify one or more commits to resolve users for.'));
     }
     $console = PhutilConsole::getConsole();
     foreach ($commits as $commit) {
         $repo = $commit->getRepository();
         $name = $repo->formatCommitName($commit->getCommitIdentifier());
         $console->writeOut("%s\n", pht('Examining commit %s...', $name));
         $refs_raw = DiffusionQuery::callConduitWithDiffusionRequest($this->getViewer(), DiffusionRequest::newFromDictionary(array('repository' => $repo, 'user' => $this->getViewer())), 'diffusion.querycommits', array('repositoryPHID' => $repo->getPHID(), 'phids' => array($commit->getPHID()), 'bypassCache' => true));
         if (empty($refs_raw['data'])) {
             throw new Exception(pht('Unable to retrieve details for commit "%s"!', $commit->getPHID()));
         }
         $ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data']));
         $author = $ref->getAuthor();
         $console->writeOut("%s\n", pht('Raw author string: %s', coalesce($author, 'null')));
         if ($author !== null) {
             $handle = $this->resolveUser($commit, $author);
             if ($handle) {
                 $console->writeOut("%s\n", pht('Phabricator user: %s', $handle->getFullName()));
             } else {
                 $console->writeOut("%s\n", pht('Unable to resolve a corresponding Phabricator user.'));
             }
         }
         $committer = $ref->getCommitter();
         $console->writeOut("%s\n", pht('Raw committer string: %s', coalesce($committer, 'null')));
         if ($committer !== null) {
             $handle = $this->resolveUser($commit, $committer);
             if ($handle) {
                 $console->writeOut("%s\n", pht('Phabricator user: %s', $handle->getFullName()));
             } else {
                 $console->writeOut("%s\n", pht('Unable to resolve a corresponding Phabricator user.'));
             }
         }
     }
     return 0;
 }
 private function getEffectiveCommit(ConduitAPIRequest $request)
 {
     if ($this->effectiveCommit === null) {
         $drequest = $this->getDiffusionRequest();
         $path = $drequest->getPath();
         $result = DiffusionQuery::callConduitWithDiffusionRequest($request->getUser(), $drequest, 'diffusion.lastmodifiedquery', array('paths' => array($path => $drequest->getStableCommit())));
         $this->effectiveCommit = idx($result, $path);
     }
     return $this->effectiveCommit;
 }
 private function generateFinalDiff(DifferentialRevision $revision, $actor_phid)
 {
     $viewer = PhabricatorUser::getOmnipotentUser();
     $drequest = DiffusionRequest::newFromDictionary(array('user' => $viewer, 'repository' => $this->repository));
     $raw_diff = DiffusionQuery::callConduitWithDiffusionRequest($viewer, $drequest, 'diffusion.rawdiffquery', array('commit' => $this->commit->getCommitIdentifier()));
     // TODO: Support adds, deletes and moves under SVN.
     if (strlen($raw_diff)) {
         $changes = id(new ArcanistDiffParser())->parseDiff($raw_diff);
     } else {
         // This is an empty diff, maybe made with `git commit --allow-empty`.
         // NOTE: These diffs have the same tree hash as their ancestors, so
         // they may attach to revisions in an unexpected way. Just let this
         // happen for now, although it might make sense to special case it
         // eventually.
         $changes = array();
     }
     $diff = DifferentialDiff::newFromRawChanges($viewer, $changes)->setRepositoryPHID($this->repository->getPHID())->setAuthorPHID($actor_phid)->setCreationMethod('commit')->setSourceControlSystem($this->repository->getVersionControlSystem())->setLintStatus(DifferentialLintStatus::LINT_AUTO_SKIP)->setUnitStatus(DifferentialUnitStatus::UNIT_AUTO_SKIP)->setDateCreated($this->commit->getEpoch())->setDescription(pht('Commit %s', $this->commit->getMonogram()));
     $parents = DiffusionQuery::callConduitWithDiffusionRequest($viewer, $drequest, 'diffusion.commitparentsquery', array('commit' => $this->commit->getCommitIdentifier()));
     if ($parents) {
         $diff->setSourceControlBaseRevision(head($parents));
     }
     // TODO: Attach binary files.
     return $diff->save();
 }
 private function loadParentCommitOf($commit)
 {
     $drequest = $this->getDiffusionRequest();
     $user = $this->getRequest()->getUser();
     $before_req = DiffusionRequest::newFromDictionary(array('user' => $user, 'repository' => $drequest->getRepository(), 'commit' => $commit));
     $parents = DiffusionQuery::callConduitWithDiffusionRequest($user, $before_req, 'diffusion.commitparentsquery', array('commit' => $commit));
     return head($parents);
 }
 private function loadCommitDiff()
 {
     $drequest = DiffusionRequest::newFromDictionary(array('user' => PhabricatorUser::getOmnipotentUser(), 'repository' => $this->repository, 'commit' => $this->commit->getCommitIdentifier()));
     $byte_limit = self::getEnormousByteLimit();
     $raw = DiffusionQuery::callConduitWithDiffusionRequest(PhabricatorUser::getOmnipotentUser(), $drequest, 'diffusion.rawdiffquery', array('commit' => $this->commit->getCommitIdentifier(), 'timeout' => self::getEnormousTimeLimit(), 'byteLimit' => $byte_limit, 'linesOfContext' => 0));
     if (strlen($raw) >= $byte_limit) {
         throw new Exception(pht('The raw text of this change is enormous (larger than %d bytes). ' . 'Herald can not process it.', $byte_limit));
     }
     $parser = new ArcanistDiffParser();
     $changes = $parser->parseDiff($raw);
     $diff = DifferentialDiff::newEphemeralFromRawChanges($changes);
     return $diff;
 }
 protected function callConduitWithDiffusionRequest($method, array $params = array())
 {
     $user = $this->getRequest()->getUser();
     $drequest = $this->getDiffusionRequest();
     return DiffusionQuery::callConduitWithDiffusionRequest($user, $drequest, $method, $params);
 }
예제 #12
0
 private function callConduit($method, array $params)
 {
     $viewer = PhabricatorUser::getOmnipotentUser();
     $drequest = DiffusionRequest::newFromDictionary(array('user' => $viewer, 'repository' => $this->repository, 'commit' => $this->commit->getCommitIdentifier()));
     return DiffusionQuery::callConduitWithDiffusionRequest($viewer, $drequest, $method, $params);
 }
예제 #13
0
 private function resolveRefs(array $refs, array $types)
 {
     // First, try to resolve refs from fast cache sources.
     $cached_query = id(new DiffusionCachedResolveRefsQuery())->setRepository($this->getRepository())->withRefs($refs);
     if ($types) {
         $cached_query->withTypes($types);
     }
     $cached_results = $cached_query->execute();
     // Throw away all the refs we resolved. Hopefully, we'll throw away
     // everything here.
     foreach ($refs as $key => $ref) {
         if (isset($cached_results[$ref])) {
             unset($refs[$key]);
         }
     }
     // If we couldn't pull everything out of the cache, execute the underlying
     // VCS operation.
     if ($refs) {
         $vcs_results = DiffusionQuery::callConduitWithDiffusionRequest($this->getUser(), $this, 'diffusion.resolverefs', array('types' => $types, 'refs' => $refs));
     } else {
         $vcs_results = array();
     }
     return $vcs_results + $cached_results;
 }
 protected function processDiffusionRequest(AphrontRequest $request)
 {
     $viewer = $this->getViewer();
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
     if (!$drequest->supportsBranches()) {
         return $this->newDialog()->setTitle(pht('No Ref Support'))->appendParagraph(pht('The version control system this repository uses does not ' . 'support named references, so you can not resolve or list ' . 'repository refs in this repository.'))->addCancelButton($repository->getURI());
     }
     $ref_name = $drequest->getBranch();
     $cache_query = id(new DiffusionCachedResolveRefsQuery())->setRepository($repository);
     if ($ref_name !== null) {
         $cache_query->withRefs(array($ref_name));
     }
     $cache_refs = $cache_query->execute();
     $vcs_refs = DiffusionQuery::callConduitWithDiffusionRequest($viewer, $drequest, 'diffusion.resolverefs', array('refs' => array($ref_name)));
     $all = array();
     foreach ($cache_refs as $ref => $results) {
         foreach ($results as $result) {
             $id = $result['type'] . '/' . $result['identifier'];
             $all[$ref][$id]['cache'] = $result;
         }
     }
     foreach ($vcs_refs as $ref => $results) {
         foreach ($results as $result) {
             $id = $result['type'] . '/' . $result['identifier'];
             $all[$ref][$id]['vcs'] = $result;
         }
     }
     $rows = array();
     foreach ($all as $ref => $results) {
         foreach ($results as $info) {
             $cache = idx($info, 'cache', array());
             $vcs = idx($info, 'vcs', array());
             $type = idx($vcs, 'type');
             if (!$type) {
                 $type = idx($cache, 'type');
             }
             $hash = idx($vcs, 'identifier');
             if ($hash !== null) {
                 $hash = DiffusionView::linkCommit($repository, $hash);
             }
             $cached_hash = idx($cache, 'identifier');
             if ($cached_hash !== null) {
                 $cached_hash = DiffusionView::linkCommit($repository, $cached_hash);
             }
             $closed = idx($vcs, 'closed', false);
             if (!$vcs) {
                 $state = null;
             } else {
                 $state = $closed ? pht('Closed') : pht('Open');
             }
             $cached_closed = idx($cache, 'closed', false);
             if (!$cache) {
                 $cached_state = null;
             } else {
                 $cached_state = $cached_closed ? pht('Closed') : pht('Open');
             }
             $alternate = idx($vcs, 'alternate');
             if ($alternate !== null) {
                 $alternate = DiffusionView::linkCommit($repository, $alternate);
             }
             $rows[] = array($ref, $type, $hash, $cached_hash, $state, $cached_state, $alternate);
         }
     }
     $table = id(new AphrontTableView($rows))->setHeaders(array(pht('Ref'), pht('Type'), pht('Hash'), pht('Cached Hash'), pht('State'), pht('Cached State'), pht('Alternate')));
     $content = id(new PHUIObjectBoxView())->setHeaderText(pht('Ref "%s"', $ref_name))->setTable($table);
     $crumbs = $this->buildCrumbs(array());
     $crumbs->addTextCrumb(pht('Refs'));
     return $this->buildApplicationPage(array($crumbs, $content), array('title' => array(pht('Refs'), $repository->getMonogram(), $ref_name)));
 }
 public function isDiffChangedBeforeCommit(PhabricatorRepositoryCommit $commit, DifferentialDiff $old, DifferentialDiff $new)
 {
     $viewer = $this->getViewer();
     $repository = $commit->getRepository();
     $identifier = $commit->getCommitIdentifier();
     $vs_changesets = array();
     foreach ($old->getChangesets() as $changeset) {
         $path = $changeset->getAbsoluteRepositoryPath($repository, $old);
         $path = ltrim($path, '/');
         $vs_changesets[$path] = $changeset;
     }
     $changesets = array();
     foreach ($new->getChangesets() as $changeset) {
         $path = $changeset->getAbsoluteRepositoryPath($repository, $new);
         $path = ltrim($path, '/');
         $changesets[$path] = $changeset;
     }
     if (array_fill_keys(array_keys($changesets), true) != array_fill_keys(array_keys($vs_changesets), true)) {
         return true;
     }
     $file_phids = array();
     foreach ($vs_changesets as $changeset) {
         $metadata = $changeset->getMetadata();
         $file_phid = idx($metadata, 'new:binary-phid');
         if ($file_phid) {
             $file_phids[$file_phid] = $file_phid;
         }
     }
     $files = array();
     if ($file_phids) {
         $files = id(new PhabricatorFileQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withPHIDs($file_phids)->execute();
         $files = mpull($files, null, 'getPHID');
     }
     foreach ($changesets as $path => $changeset) {
         $vs_changeset = $vs_changesets[$path];
         $file_phid = idx($vs_changeset->getMetadata(), 'new:binary-phid');
         if ($file_phid) {
             if (!isset($files[$file_phid])) {
                 return true;
             }
             $drequest = DiffusionRequest::newFromDictionary(array('user' => $viewer, 'repository' => $repository));
             $response = DiffusionQuery::callConduitWithDiffusionRequest($viewer, $drequest, 'diffusion.filecontentquery', array('commit' => $identifier, 'path' => $path));
             $new_file_phid = $response['filePHID'];
             if (!$new_file_phid) {
                 return true;
             }
             $new_file = id(new PhabricatorFileQuery())->setViewer($viewer)->withPHIDs(array($new_file_phid))->executeOne();
             if (!$new_file) {
                 return true;
             }
             if ($files[$file_phid]->loadFileData() != $new_file->loadFileData()) {
                 return true;
             }
         } else {
             $context = implode("\n", $changeset->makeChangesWithContext());
             $vs_context = implode("\n", $vs_changeset->makeChangesWithContext());
             // We couldn't just compare $context and $vs_context because following
             // diffs will be considered different:
             //
             //   -(empty line)
             //   -echo 'test';
             //    (empty line)
             //
             //    (empty line)
             //   -echo "test";
             //   -(empty line)
             $hunk = id(new DifferentialModernHunk())->setChanges($context);
             $vs_hunk = id(new DifferentialModernHunk())->setChanges($vs_context);
             if ($hunk->makeOldFile() != $vs_hunk->makeOldFile() || $hunk->makeNewFile() != $vs_hunk->makeNewFile()) {
                 return true;
             }
         }
     }
     return false;
 }
예제 #16
0
 private function resolveRefs(array $refs)
 {
     if ($this->shouldInitFromConduit()) {
         return DiffusionQuery::callConduitWithDiffusionRequest($this->getUser(), $this, 'diffusion.resolverefs', array('refs' => $refs));
     } else {
         return id(new DiffusionLowLevelResolveRefsQuery())->setRepository($this->getRepository())->withRefs($refs)->execute();
     }
 }
 public function save()
 {
     if ($this->getID()) {
         $is_new = false;
     } else {
         $is_new = true;
     }
     $this->openTransaction();
     $ret = parent::save();
     $add_owners = array();
     $remove_owners = array();
     $all_owners = array();
     if ($this->unsavedOwners) {
         $new_owners = array_fill_keys($this->unsavedOwners, true);
         $cur_owners = array();
         foreach ($this->loadOwners() as $owner) {
             if (empty($new_owners[$owner->getUserPHID()])) {
                 $remove_owners[$owner->getUserPHID()] = true;
                 $owner->delete();
                 continue;
             }
             $cur_owners[$owner->getUserPHID()] = true;
         }
         $add_owners = array_diff_key($new_owners, $cur_owners);
         $all_owners = array_merge(array($this->getPrimaryOwnerPHID() => true), $new_owners, $remove_owners);
         foreach ($add_owners as $phid => $ignored) {
             $owner = new PhabricatorOwnersOwner();
             $owner->setPackageID($this->getID());
             $owner->setUserPHID($phid);
             $owner->save();
         }
         unset($this->unsavedOwners);
     }
     $add_paths = array();
     $remove_paths = array();
     $touched_repos = array();
     if ($this->unsavedPaths) {
         $new_paths = igroup($this->unsavedPaths, 'repositoryPHID', 'path');
         $cur_paths = $this->loadPaths();
         foreach ($cur_paths as $key => $path) {
             $repository_phid = $path->getRepositoryPHID();
             $new_path = head(idx(idx($new_paths, $repository_phid, array()), $path->getPath(), array()));
             $excluded = $path->getExcluded();
             if (!$new_path || idx($new_path, 'excluded') != $excluded) {
                 $touched_repos[$repository_phid] = true;
                 $remove_paths[$repository_phid][$path->getPath()] = $excluded;
                 $path->delete();
                 unset($cur_paths[$key]);
             }
         }
         $cur_paths = mgroup($cur_paths, 'getRepositoryPHID', 'getPath');
         foreach ($new_paths as $repository_phid => $paths) {
             // TODO: (T603) Thread policy stuff in here.
             // get repository object for path validation
             $repository = id(new PhabricatorRepository())->loadOneWhere('phid = %s', $repository_phid);
             if (!$repository) {
                 continue;
             }
             foreach ($paths as $path => $dicts) {
                 $path = ltrim($path, '/');
                 // build query to validate path
                 $drequest = DiffusionRequest::newFromDictionary(array('user' => $this->getActor(), 'repository' => $repository, 'path' => $path));
                 $results = DiffusionBrowseResultSet::newFromConduit(DiffusionQuery::callConduitWithDiffusionRequest($this->getActor(), $drequest, 'diffusion.browsequery', array('commit' => $drequest->getCommit(), 'path' => $path, 'needValidityOnly' => true)));
                 $valid = $results->isValidResults();
                 $is_directory = true;
                 if (!$valid) {
                     switch ($results->getReasonForEmptyResultSet()) {
                         case DiffusionBrowseResultSet::REASON_IS_FILE:
                             $valid = true;
                             $is_directory = false;
                             break;
                         case DiffusionBrowseResultSet::REASON_IS_EMPTY:
                             $valid = true;
                             break;
                     }
                 }
                 if ($is_directory && substr($path, -1) != '/') {
                     $path .= '/';
                 }
                 if (substr($path, 0, 1) != '/') {
                     $path = '/' . $path;
                 }
                 if (empty($cur_paths[$repository_phid][$path]) && $valid) {
                     $touched_repos[$repository_phid] = true;
                     $excluded = idx(reset($dicts), 'excluded', 0);
                     $add_paths[$repository_phid][$path] = $excluded;
                     $obj = new PhabricatorOwnersPath();
                     $obj->setPackageID($this->getID());
                     $obj->setRepositoryPHID($repository_phid);
                     $obj->setPath($path);
                     $obj->setExcluded($excluded);
                     $obj->save();
                 }
             }
         }
         unset($this->unsavedPaths);
     }
     $this->saveTransaction();
     if ($is_new) {
         $mail = new PackageCreateMail($this);
     } else {
         $mail = new PackageModifyMail($this, array_keys($add_owners), array_keys($remove_owners), array_keys($all_owners), array_keys($touched_repos), $add_paths, $remove_paths);
     }
     $mail->setActor($this->getActor());
     $mail->send();
     return $ret;
 }
 protected function getResult(ConduitAPIRequest $request)
 {
     $drequest = $this->getDiffusionRequest();
     $path_dicts = $request->getValue('paths', array());
     $paths = array();
     foreach ($path_dicts as $dict) {
         $paths[] = DiffusionRepositoryPath::newFromDictionary($dict);
     }
     $best = -1;
     $readme = '';
     $best_render_type = 'plain';
     foreach ($paths as $result_path) {
         $file_type = $result_path->getFileType();
         if ($file_type != ArcanistDiffChangeType::FILE_NORMAL && $file_type != ArcanistDiffChangeType::FILE_TEXT) {
             // Skip directories, etc.
             continue;
         }
         $path = strtolower($result_path->getPath());
         if ($path === 'readme') {
             $path .= '.remarkup';
         }
         if (strncmp($path, 'readme.', 7) !== 0) {
             continue;
         }
         $priority = 0;
         switch (substr($path, 7)) {
             case 'remarkup':
                 $priority = 100;
                 $render_type = 'remarkup';
                 break;
             case 'rainbow':
                 $priority = 90;
                 $render_type = 'rainbow';
                 break;
             case 'md':
                 $priority = 50;
                 $render_type = 'remarkup';
                 break;
             case 'txt':
                 $priority = 10;
                 $render_type = 'plain';
                 break;
             default:
                 $priority = 0;
                 $render_type = 'plain';
                 break;
         }
         if ($priority > $best) {
             $best = $priority;
             $readme = $result_path;
             $best_render_type = $render_type;
         }
     }
     if (!$readme) {
         return '';
     }
     $readme_request = DiffusionRequest::newFromDictionary(array('user' => $request->getUser(), 'repository' => $drequest->getRepository(), 'commit' => $drequest->getStableCommit(), 'path' => $readme->getFullPath()));
     $file_content = DiffusionFileContent::newFromConduit(DiffusionQuery::callConduitWithDiffusionRequest($request->getUser(), $readme_request, 'diffusion.filecontentquery', array('commit' => $drequest->getStableCommit(), 'path' => $readme->getFullPath(), 'needsBlame' => false)));
     $readme_content = $file_content->getCorpus();
     switch ($best_render_type) {
         case 'plain':
             $readme_content = phutil_escape_html_newlines($readme_content);
             $class = null;
             break;
         case 'rainbow':
             $highlighter = new PhutilRainbowSyntaxHighlighter();
             $readme_content = $highlighter->getHighlightFuture($readme_content)->resolve();
             $readme_content = phutil_escape_html_newlines($readme_content);
             require_celerity_resource('syntax-highlighting-css');
             $class = 'remarkup-code';
             break;
         case 'remarkup':
             // TODO: This is sketchy, but make sure we hit the markup cache.
             $markup_object = id(new PhabricatorMarkupOneOff())->setEngineRuleset('diffusion-readme')->setContent($readme_content);
             $markup_field = 'default';
             $readme_content = id(new PhabricatorMarkupEngine())->setViewer($request->getUser())->addObject($markup_object, $markup_field)->process()->getOutput($markup_object, $markup_field);
             $engine = $markup_object->newMarkupEngine($markup_field);
             $toc = PhutilRemarkupHeaderBlockRule::renderTableOfContents($engine);
             if ($toc) {
                 $toc = phutil_tag_div('phabricator-remarkup-toc', array(phutil_tag_div('phabricator-remarkup-toc-header', pht('Table of Contents')), $toc));
                 $readme_content = array($toc, $readme_content);
             }
             $class = 'phabricator-remarkup';
             break;
     }
     $readme_content = phutil_tag('div', array('class' => $class), $readme_content);
     return $readme_content;
 }
 protected function parseCommitChanges(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit)
 {
     $viewer = PhabricatorUser::getOmnipotentUser();
     $raw = DiffusionQuery::callConduitWithDiffusionRequest($viewer, DiffusionRequest::newFromDictionary(array('repository' => $repository, 'user' => $viewer)), 'diffusion.internal.gitrawdiffquery', array('commit' => $commit->getCommitIdentifier()));
     $changes = array();
     $move_away = array();
     $copy_away = array();
     $lines = explode("\n", $raw);
     foreach ($lines as $line) {
         if (!strlen(trim($line))) {
             continue;
         }
         list($old_mode, $new_mode, $old_hash, $new_hash, $more_stuff) = preg_split('/ +/', $line, 5);
         // We may only have two pieces here.
         list($action, $src_path, $dst_path) = array_merge(explode("\t", $more_stuff), array(null));
         // Normalize the paths for consistency with the SVN workflow.
         $src_path = '/' . $src_path;
         if ($dst_path) {
             $dst_path = '/' . $dst_path;
         }
         $old_mode = intval($old_mode, 8);
         $new_mode = intval($new_mode, 8);
         switch ($new_mode & 0160000) {
             case 0160000:
                 $file_type = DifferentialChangeType::FILE_SUBMODULE;
                 break;
             case 0120000:
                 $file_type = DifferentialChangeType::FILE_SYMLINK;
                 break;
             case 040000:
                 $file_type = DifferentialChangeType::FILE_DIRECTORY;
                 break;
             default:
                 $file_type = DifferentialChangeType::FILE_NORMAL;
                 break;
         }
         // TODO: We can detect binary changes as git does, through a combination
         // of running 'git check-attr' for stuff like 'binary', 'merge' or 'diff',
         // and by falling back to inspecting the first 8,000 characters of the
         // buffer for null bytes (this is seriously git's algorithm, see
         // buffer_is_binary() in xdiff-interface.c).
         $change_type = null;
         $change_path = $src_path;
         $change_target = null;
         $is_direct = true;
         switch ($action[0]) {
             case 'A':
                 $change_type = DifferentialChangeType::TYPE_ADD;
                 break;
             case 'D':
                 $change_type = DifferentialChangeType::TYPE_DELETE;
                 break;
             case 'C':
                 $change_type = DifferentialChangeType::TYPE_COPY_HERE;
                 $change_path = $dst_path;
                 $change_target = $src_path;
                 $copy_away[$change_target][] = $change_path;
                 break;
             case 'R':
                 $change_type = DifferentialChangeType::TYPE_MOVE_HERE;
                 $change_path = $dst_path;
                 $change_target = $src_path;
                 $move_away[$change_target][] = $change_path;
                 break;
             case 'T':
                 // Type of the file changed, fall through and treat it as a
                 // modification. Not 100% sure this is the right thing to do but it
                 // seems reasonable.
             // Type of the file changed, fall through and treat it as a
             // modification. Not 100% sure this is the right thing to do but it
             // seems reasonable.
             case 'M':
                 if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
                     $change_type = DifferentialChangeType::TYPE_CHILD;
                     $is_direct = false;
                 } else {
                     $change_type = DifferentialChangeType::TYPE_CHANGE;
                 }
                 break;
                 // NOTE: "U" (unmerged) and "X" (unknown) statuses are also possible
                 // in theory but shouldn't appear here.
             // NOTE: "U" (unmerged) and "X" (unknown) statuses are also possible
             // in theory but shouldn't appear here.
             default:
                 throw new Exception(pht("Failed to parse line '%s'.", $line));
         }
         $changes[$change_path] = array('repositoryID' => $repository->getID(), 'commitID' => $commit->getID(), 'path' => $change_path, 'changeType' => $change_type, 'fileType' => $file_type, 'isDirect' => $is_direct, 'commitSequence' => $commit->getEpoch(), 'targetPath' => $change_target, 'targetCommitID' => $change_target ? $commit->getID() : null);
     }
     // Add a change to '/' since git doesn't mention it.
     $changes['/'] = array('repositoryID' => $repository->getID(), 'commitID' => $commit->getID(), 'path' => '/', 'changeType' => DifferentialChangeType::TYPE_CHILD, 'fileType' => DifferentialChangeType::FILE_DIRECTORY, 'isDirect' => false, 'commitSequence' => $commit->getEpoch(), 'targetPath' => null, 'targetCommitID' => null);
     foreach ($copy_away as $change_path => $destinations) {
         if (isset($move_away[$change_path])) {
             $change_type = DifferentialChangeType::TYPE_MULTICOPY;
             $is_direct = true;
             unset($move_away[$change_path]);
         } else {
             $change_type = DifferentialChangeType::TYPE_COPY_AWAY;
             // This change is direct if we picked up a modification above (i.e.,
             // the original copy source was also edited). Otherwise the original
             // wasn't touched, so leave it as an indirect change.
             $is_direct = isset($changes[$change_path]);
         }
         $reference = $changes[reset($destinations)];
         $changes[$change_path] = array('repositoryID' => $repository->getID(), 'commitID' => $commit->getID(), 'path' => $change_path, 'changeType' => $change_type, 'fileType' => $reference['fileType'], 'isDirect' => $is_direct, 'commitSequence' => $commit->getEpoch(), 'targetPath' => null, 'targetCommitID' => null);
     }
     foreach ($move_away as $change_path => $destinations) {
         $reference = $changes[reset($destinations)];
         $changes[$change_path] = array('repositoryID' => $repository->getID(), 'commitID' => $commit->getID(), 'path' => $change_path, 'changeType' => DifferentialChangeType::TYPE_MOVE_AWAY, 'fileType' => $reference['fileType'], 'isDirect' => true, 'commitSequence' => $commit->getEpoch(), 'targetPath' => null, 'targetCommitID' => null);
     }
     $paths = array();
     foreach ($changes as $change) {
         $paths[$change['path']] = true;
         if ($change['targetPath']) {
             $paths[$change['targetPath']] = true;
         }
     }
     $path_map = $this->lookupOrCreatePaths(array_keys($paths));
     foreach ($changes as $key => $change) {
         $changes[$key]['pathID'] = $path_map[$change['path']];
         if ($change['targetPath']) {
             $changes[$key]['targetPathID'] = $path_map[$change['targetPath']];
         } else {
             $changes[$key]['targetPathID'] = null;
         }
     }
     $results = array();
     foreach ($changes as $change) {
         $result = id(new PhabricatorRepositoryParsedChange())->setPathID($change['pathID'])->setTargetPathID($change['targetPathID'])->setTargetCommitID($change['targetCommitID'])->setChangeType($change['changeType'])->setFileType($change['fileType'])->setIsDirect($change['isDirect'])->setCommitSequence($change['commitSequence']);
         $results[] = $result;
     }
     return $results;
 }