protected function executeQuery()
 {
     $drequest = $this->getRequest();
     $repository = $drequest->getRepository();
     $path = $drequest->getPath();
     $commit = $drequest->getStableCommitName();
     // TODO: This is a really really awful mess but Mercurial doesn't offer
     // an equivalent of "git ls-files -- directory". If it's any comfort, this
     // is what "hgweb" does too, see:
     //
     //   http://selenic.com/repo/hg/file/91dc8878f888/mercurial/hgweb/webcommands.py#l320
     //
     // derp derp derp derp
     //
     // Anyway, figure out what's in this path by applying massive amounts
     // of brute force.
     list($entire_manifest) = $repository->execxLocalCommand('manifest --rev %s', $commit);
     $entire_manifest = explode("\n", $entire_manifest);
     $results = array();
     $match_against = trim($path, '/');
     $match_len = strlen($match_against);
     // For the root, don't trim. For other paths, trim the "/" after we match.
     // We need this because Mercurial's canonical paths have no leading "/",
     // but ours do.
     $trim_len = $match_len ? $match_len + 1 : 0;
     foreach ($entire_manifest as $path) {
         if (strncmp($path, $match_against, $match_len)) {
             continue;
         }
         if (!strlen($path)) {
             continue;
         }
         $remainder = substr($path, $trim_len);
         if (!strlen($remainder)) {
             // There is a file with this exact name in the manifest, so clearly
             // it's a file.
             $this->reason = self::REASON_IS_FILE;
             return array();
         }
         $parts = explode('/', $remainder);
         if (count($parts) == 1) {
             $type = DifferentialChangeType::FILE_NORMAL;
         } else {
             $type = DifferentialChangeType::FILE_DIRECTORY;
         }
         $results[reset($parts)] = $type;
     }
     foreach ($results as $key => $type) {
         $result = new DiffusionRepositoryPath();
         $result->setPath($key);
         $result->setFileType($type);
         $result->setFullPath(ltrim($match_against . '/', '/') . $key);
         $results[$key] = $result;
     }
     if (empty($results)) {
         // TODO: Detect "deleted" by issuing "hg log"?
         $this->reason = self::REASON_IS_NONEXISTENT;
     }
     return $results;
 }
 protected function executeQuery()
 {
     $drequest = $this->getRequest();
     $repository = $drequest->getRepository();
     $path = $drequest->getPath();
     $commit = $drequest->getCommit();
     $local_path = $repository->getDetail('local-path');
     if ($path == '') {
         // Fast path to improve the performance of the repository view; we know
         // the root is always a tree at any commit and always exists.
         $stdout = 'tree';
     } else {
         try {
             list($stdout) = execx("(cd %s && git cat-file -t %s:%s)", $local_path, $commit, $path);
         } catch (CommandException $e) {
             $stderr = $e->getStdErr();
             if (preg_match('/^fatal: Not a valid object name/', $stderr)) {
                 // Grab two logs, since the first one is when the object was deleted.
                 list($stdout) = execx('(cd %s && git log -n2 --format="%%H" %s -- %s)', $local_path, $commit, $path);
                 $stdout = trim($stdout);
                 if ($stdout) {
                     $commits = explode("\n", $stdout);
                     $this->reason = self::REASON_IS_DELETED;
                     $this->deletedAtCommit = idx($commits, 0);
                     $this->existedAtCommit = idx($commits, 1);
                     return array();
                 }
                 $this->reason = self::REASON_IS_NONEXISTENT;
                 return array();
             } else {
                 throw $e;
             }
         }
     }
     if (trim($stdout) == 'blob') {
         $this->reason = self::REASON_IS_FILE;
         return array();
     }
     if ($this->shouldOnlyTestValidity()) {
         return true;
     }
     list($stdout) = execx("(cd %s && git ls-tree -l %s:%s)", $local_path, $commit, $path);
     $results = array();
     foreach (explode("\n", rtrim($stdout)) as $line) {
         list($mode, $type, $hash, $size, $name) = preg_split('/\\s+/', $line);
         if ($type == 'tree') {
             $file_type = DifferentialChangeType::FILE_DIRECTORY;
         } else {
             $file_type = DifferentialChangeType::FILE_NORMAL;
         }
         $result = new DiffusionRepositoryPath();
         $result->setPath($name);
         $result->setHash($hash);
         $result->setFileType($file_type);
         $result->setFileSize($size);
         $results[] = $result;
     }
     return $results;
 }
 public static function newFromConduit(array $data)
 {
     $paths = array();
     $path_dicts = $data['paths'];
     foreach ($path_dicts as $dict) {
         $paths[] = DiffusionRepositoryPath::newFromDictionary($dict);
     }
     return id(new DiffusionBrowseResultSet())->setPaths($paths)->setIsValidResults($data['isValidResults'])->setReasonForEmptyResultSet($data['reasonForEmptyResultSet'])->setExistedAtCommit($data['existedAtCommit'])->setDeletedAtCommit($data['deletedAtCommit']);
 }
 protected function executeQuery()
 {
     $drequest = $this->getRequest();
     $repository = $drequest->getRepository();
     $path = $drequest->getPath();
     $commit = $drequest->getCommit();
     $subpath = $repository->getDetail('svn-subpath');
     if ($subpath && strncmp($subpath, $path, strlen($subpath))) {
         // If we have a subpath and the path isn't a child of it, it (almost
         // certainly) won't exist since we don't track commits which affect
         // it. (Even if it exists, return a consistent result.)
         $this->reason = self::REASON_IS_UNTRACKED_PARENT;
         return array();
     }
     $conn_r = $repository->establishConnection('r');
     $parent_path = DiffusionPathIDQuery::getParentPath($path);
     $path_query = new DiffusionPathIDQuery(array($path, $parent_path));
     $path_map = $path_query->loadPathIDs();
     $path_id = $path_map[$path];
     $parent_path_id = $path_map[$parent_path];
     if (empty($path_id)) {
         $this->reason = self::REASON_IS_NONEXISTENT;
         return array();
     }
     if ($commit) {
         $slice_clause = 'AND svnCommit <= ' . (int) $commit;
     } else {
         $slice_clause = '';
     }
     $index = queryfx_all($conn_r, 'SELECT pathID, max(svnCommit) maxCommit FROM %T WHERE
     repositoryID = %d AND parentID = %d
     %Q GROUP BY pathID', PhabricatorRepository::TABLE_FILESYSTEM, $repository->getID(), $path_id, $slice_clause);
     if (!$index) {
         if ($path == '/') {
             $this->reason = self::REASON_IS_EMPTY;
         } else {
             // NOTE: The parent path ID is included so this query can take
             // advantage of the table's primary key; it is uniquely determined by
             // the pathID but if we don't do the lookup ourselves MySQL doesn't have
             // the information it needs to avoid a table scan.
             $reasons = queryfx_all($conn_r, 'SELECT * FROM %T WHERE repositoryID = %d
           AND parentID = %d
           AND pathID = %d
         %Q ORDER BY svnCommit DESC LIMIT 2', PhabricatorRepository::TABLE_FILESYSTEM, $repository->getID(), $parent_path_id, $path_id, $slice_clause);
             $reason = reset($reasons);
             if (!$reason) {
                 $this->reason = self::REASON_IS_NONEXISTENT;
             } else {
                 $file_type = $reason['fileType'];
                 if (empty($reason['existed'])) {
                     $this->reason = self::REASON_IS_DELETED;
                     $this->deletedAtCommit = $reason['svnCommit'];
                     if (!empty($reasons[1])) {
                         $this->existedAtCommit = $reasons[1]['svnCommit'];
                     }
                 } else {
                     if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
                         $this->reason = self::REASON_IS_EMPTY;
                     } else {
                         $this->reason = self::REASON_IS_FILE;
                     }
                 }
             }
         }
         return array();
     }
     if ($this->shouldOnlyTestValidity()) {
         return true;
     }
     $sql = array();
     foreach ($index as $row) {
         $sql[] = '(' . (int) $row['pathID'] . ', ' . (int) $row['maxCommit'] . ')';
     }
     $browse = queryfx_all($conn_r, 'SELECT *, p.path pathName
     FROM %T f JOIN %T p ON f.pathID = p.id
     WHERE repositoryID = %d
       AND parentID = %d
       AND existed = 1
     AND (pathID, svnCommit) in (%Q)
     ORDER BY pathName', PhabricatorRepository::TABLE_FILESYSTEM, PhabricatorRepository::TABLE_PATH, $repository->getID(), $path_id, implode(', ', $sql));
     $loadable_commits = array();
     foreach ($browse as $key => $file) {
         // We need to strip out directories because we don't store last-modified
         // in the filesystem table.
         if ($file['fileType'] != DifferentialChangeType::FILE_DIRECTORY) {
             $loadable_commits[] = $file['svnCommit'];
             $browse[$key]['hasCommit'] = true;
         }
     }
     $commits = array();
     $commit_data = array();
     if ($loadable_commits) {
         // NOTE: Even though these are integers, use '%Ls' because MySQL doesn't
         // use the second part of the key otherwise!
         $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere('repositoryID = %d AND commitIdentifier IN (%Ls)', $repository->getID(), $loadable_commits);
         $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');
         } else {
             $commit_data = array();
         }
     }
     $path_normal = DiffusionPathIDQuery::normalizePath($path);
     $results = array();
     foreach ($browse as $file) {
         $full_path = $file['pathName'];
         $file_path = ltrim(substr($full_path, strlen($path_normal)), '/');
         $full_path = ltrim($full_path, '/');
         $result = new DiffusionRepositoryPath();
         $result->setPath($file_path);
         $result->setFullPath($full_path);
         //      $result->setHash($hash);
         $result->setFileType($file['fileType']);
         //      $result->setFileSize($size);
         if (!empty($file['hasCommit'])) {
             $commit = idx($commits, $file['svnCommit']);
             if ($commit) {
                 $data = idx($commit_data, $commit->getID());
                 $result->setLastModifiedCommit($commit);
                 $result->setLastCommitData($data);
             }
         }
         $results[] = $result;
     }
     if (empty($results)) {
         $this->reason = self::REASON_IS_EMPTY;
     }
     return $results;
 }
 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 executeQuery()
 {
     $drequest = $this->getRequest();
     $repository = $drequest->getRepository();
     $path = $drequest->getPath();
     $commit = $drequest->getCommit();
     if ($path == '') {
         // Fast path to improve the performance of the repository view; we know
         // the root is always a tree at any commit and always exists.
         $stdout = 'tree';
     } else {
         try {
             list($stdout) = $repository->execxLocalCommand('cat-file -t %s:%s', $commit, $path);
         } catch (CommandException $e) {
             $stderr = $e->getStdErr();
             if (preg_match('/^fatal: Not a valid object name/', $stderr)) {
                 // Grab two logs, since the first one is when the object was deleted.
                 list($stdout) = $repository->execxLocalCommand('log -n2 --format="%%H" %s -- %s', $commit, $path);
                 $stdout = trim($stdout);
                 if ($stdout) {
                     $commits = explode("\n", $stdout);
                     $this->reason = self::REASON_IS_DELETED;
                     $this->deletedAtCommit = idx($commits, 0);
                     $this->existedAtCommit = idx($commits, 1);
                     return array();
                 }
                 $this->reason = self::REASON_IS_NONEXISTENT;
                 return array();
             } else {
                 throw $e;
             }
         }
     }
     if (trim($stdout) == 'blob') {
         $this->reason = self::REASON_IS_FILE;
         return array();
     }
     if ($this->shouldOnlyTestValidity()) {
         return true;
     }
     list($stdout) = $repository->execxLocalCommand('ls-tree -z -l %s:%s', $commit, $path);
     $submodules = array();
     $results = array();
     foreach (explode("", rtrim($stdout)) as $line) {
         // NOTE: Limit to 5 components so we parse filenames with spaces in them
         // correctly.
         list($mode, $type, $hash, $size, $name) = preg_split('/\\s+/', $line, 5);
         $result = new DiffusionRepositoryPath();
         if ($type == 'tree') {
             $file_type = DifferentialChangeType::FILE_DIRECTORY;
         } else {
             if ($type == 'commit') {
                 $file_type = DifferentialChangeType::FILE_SUBMODULE;
                 $submodules[] = $result;
             } else {
                 $mode = intval($mode, 8);
                 if (($mode & 0120000) == 0120000) {
                     $file_type = DifferentialChangeType::FILE_SYMLINK;
                 } else {
                     $file_type = DifferentialChangeType::FILE_NORMAL;
                 }
             }
         }
         $result->setFullPath($path . $name);
         $result->setPath($name);
         $result->setHash($hash);
         $result->setFileType($file_type);
         $result->setFileSize($size);
         $results[] = $result;
     }
     // If we identified submodules, lookup the module info at this commit to
     // find their source URIs.
     if ($submodules) {
         // NOTE: We need to read the file out of git and write it to a temporary
         // location because "git config -f" doesn't accept a "commit:path"-style
         // argument.
         // NOTE: This file may not exist, e.g. because the commit author removed
         // it when they added the submodule. See T1448. If it's not present, just
         // show the submodule without enriching it. If ".gitmodules" was removed
         // it seems to partially break submodules, but the repository as a whole
         // continues to work fine and we've seen at least two cases of this in
         // the wild.
         list($err, $contents) = $repository->execLocalCommand('cat-file blob %s:.gitmodules', $commit);
         if (!$err) {
             $tmp = new TempFile();
             Filesystem::writeFile($tmp, $contents);
             list($module_info) = $repository->execxLocalCommand('config -l -f %s', $tmp);
             $dict = array();
             $lines = explode("\n", trim($module_info));
             foreach ($lines as $line) {
                 list($key, $value) = explode('=', $line, 2);
                 $parts = explode('.', $key);
                 $dict[$key] = $value;
             }
             foreach ($submodules as $path) {
                 $full_path = $path->getFullPath();
                 $key = 'submodule.' . $full_path . '.url';
                 if (isset($dict[$key])) {
                     $path->setExternalURI($dict[$key]);
                 }
             }
         }
     }
     return $results;
 }
 protected function executeQuery()
 {
     $drequest = $this->getRequest();
     $repository = $drequest->getRepository();
     $path = $drequest->getPath();
     $commit = $drequest->getCommit();
     if ($path == '') {
         // Fast path to improve the performance of the repository view; we know
         // the root is always a tree at any commit and always exists.
         $stdout = 'tree';
     } else {
         try {
             list($stdout) = $repository->execxLocalCommand('cat-file -t %s:%s', $commit, $path);
         } catch (CommandException $e) {
             $stderr = $e->getStdErr();
             if (preg_match('/^fatal: Not a valid object name/', $stderr)) {
                 // Grab two logs, since the first one is when the object was deleted.
                 list($stdout) = $repository->execxLocalCommand('log -n2 --format="%%H" %s -- %s', $commit, $path);
                 $stdout = trim($stdout);
                 if ($stdout) {
                     $commits = explode("\n", $stdout);
                     $this->reason = self::REASON_IS_DELETED;
                     $this->deletedAtCommit = idx($commits, 0);
                     $this->existedAtCommit = idx($commits, 1);
                     return array();
                 }
                 $this->reason = self::REASON_IS_NONEXISTENT;
                 return array();
             } else {
                 throw $e;
             }
         }
     }
     if (trim($stdout) == 'blob') {
         $this->reason = self::REASON_IS_FILE;
         return array();
     }
     if ($this->shouldOnlyTestValidity()) {
         return true;
     }
     list($stdout) = $repository->execxLocalCommand('ls-tree -z -l %s:%s', $commit, $path);
     $submodules = array();
     $results = array();
     foreach (explode("", rtrim($stdout)) as $line) {
         // NOTE: Limit to 5 components so we parse filenames with spaces in them
         // correctly.
         list($mode, $type, $hash, $size, $name) = preg_split('/\\s+/', $line, 5);
         $result = new DiffusionRepositoryPath();
         if ($type == 'tree') {
             $file_type = DifferentialChangeType::FILE_DIRECTORY;
         } else {
             if ($type == 'commit') {
                 $file_type = DifferentialChangeType::FILE_SUBMODULE;
                 $submodules[] = $result;
             } else {
                 $mode = intval($mode, 8);
                 if (($mode & 0120000) == 0120000) {
                     $file_type = DifferentialChangeType::FILE_SYMLINK;
                 } else {
                     $file_type = DifferentialChangeType::FILE_NORMAL;
                 }
             }
         }
         $result->setFullPath($path . $name);
         $result->setPath($name);
         $result->setHash($hash);
         $result->setFileType($file_type);
         $result->setFileSize($size);
         $results[] = $result;
     }
     // If we identified submodules, lookup the module info at this commit to
     // find their source URIs.
     if ($submodules) {
         list($module_info) = $repository->execxLocalCommand('show %s:.gitmodules', $commit);
         $module_info = parse_ini_string($module_info, $process_sections = true, $scanner_mode = INI_SCANNER_RAW);
         foreach ($submodules as $path) {
             foreach ($module_info as $section) {
                 if (empty($section['path']) || empty($section['url'])) {
                     continue;
                 }
                 if ($section['path'] == $path->getFullPath()) {
                     $path->setExternalURI($section['url']);
                 }
             }
         }
     }
     return $results;
 }