mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-23 14:00:56 +01:00
Diffusion - move DiffusionBrowseQuery => Conduit
Summary: see title. Ref T2784. Test Plan: In diffusion, for each of SVN, Mercurial, and Git, I loaded up /diffusion/CALLSIGN/. I verified the README was displayed and things looked good. Next I clicked on "browse" on the top-most commit and verified things looked correct. Also clicked through to a file for a good measure and things looked good. In owners, for each of SVN, Mercurial, and Git, I played around with the path typeahead / validator. It worked correctly! Reviewers: epriestley Reviewed By: epriestley CC: chad, aran, Korvin Maniphest Tasks: T2784 Differential Revision: https://secure.phabricator.com/D5883
This commit is contained in:
parent
5c2715a13b
commit
a42851501f
16 changed files with 812 additions and 649 deletions
|
@ -146,6 +146,7 @@ phutil_register_library_map(array(
|
||||||
'ConduitAPI_diffusion_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_Method.php',
|
'ConduitAPI_diffusion_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_Method.php',
|
||||||
'ConduitAPI_diffusion_abstractquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_abstractquery_Method.php',
|
'ConduitAPI_diffusion_abstractquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_abstractquery_Method.php',
|
||||||
'ConduitAPI_diffusion_branchquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_branchquery_Method.php',
|
'ConduitAPI_diffusion_branchquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_branchquery_Method.php',
|
||||||
|
'ConduitAPI_diffusion_browsequery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_browsequery_Method.php',
|
||||||
'ConduitAPI_diffusion_existsquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_existsquery_Method.php',
|
'ConduitAPI_diffusion_existsquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_existsquery_Method.php',
|
||||||
'ConduitAPI_diffusion_filecontentquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_filecontentquery_Method.php',
|
'ConduitAPI_diffusion_filecontentquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_filecontentquery_Method.php',
|
||||||
'ConduitAPI_diffusion_findsymbols_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_findsymbols_Method.php',
|
'ConduitAPI_diffusion_findsymbols_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_findsymbols_Method.php',
|
||||||
|
@ -404,7 +405,7 @@ phutil_register_library_map(array(
|
||||||
'DiffusionBranchTableView' => 'applications/diffusion/view/DiffusionBranchTableView.php',
|
'DiffusionBranchTableView' => 'applications/diffusion/view/DiffusionBranchTableView.php',
|
||||||
'DiffusionBrowseController' => 'applications/diffusion/controller/DiffusionBrowseController.php',
|
'DiffusionBrowseController' => 'applications/diffusion/controller/DiffusionBrowseController.php',
|
||||||
'DiffusionBrowseFileController' => 'applications/diffusion/controller/DiffusionBrowseFileController.php',
|
'DiffusionBrowseFileController' => 'applications/diffusion/controller/DiffusionBrowseFileController.php',
|
||||||
'DiffusionBrowseQuery' => 'applications/diffusion/query/browse/DiffusionBrowseQuery.php',
|
'DiffusionBrowseResultSet' => 'applications/diffusion/data/DiffusionBrowseResultSet.php',
|
||||||
'DiffusionBrowseTableView' => 'applications/diffusion/view/DiffusionBrowseTableView.php',
|
'DiffusionBrowseTableView' => 'applications/diffusion/view/DiffusionBrowseTableView.php',
|
||||||
'DiffusionChangeController' => 'applications/diffusion/controller/DiffusionChangeController.php',
|
'DiffusionChangeController' => 'applications/diffusion/controller/DiffusionChangeController.php',
|
||||||
'DiffusionCommentListView' => 'applications/diffusion/view/DiffusionCommentListView.php',
|
'DiffusionCommentListView' => 'applications/diffusion/view/DiffusionCommentListView.php',
|
||||||
|
@ -427,7 +428,6 @@ phutil_register_library_map(array(
|
||||||
'DiffusionFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionFileContentQuery.php',
|
'DiffusionFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionFileContentQuery.php',
|
||||||
'DiffusionGitBranch' => 'applications/diffusion/data/DiffusionGitBranch.php',
|
'DiffusionGitBranch' => 'applications/diffusion/data/DiffusionGitBranch.php',
|
||||||
'DiffusionGitBranchTestCase' => 'applications/diffusion/data/__tests__/DiffusionGitBranchTestCase.php',
|
'DiffusionGitBranchTestCase' => 'applications/diffusion/data/__tests__/DiffusionGitBranchTestCase.php',
|
||||||
'DiffusionGitBrowseQuery' => 'applications/diffusion/query/browse/DiffusionGitBrowseQuery.php',
|
|
||||||
'DiffusionGitCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionGitCommitParentsQuery.php',
|
'DiffusionGitCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionGitCommitParentsQuery.php',
|
||||||
'DiffusionGitCommitTagsQuery' => 'applications/diffusion/query/committags/DiffusionGitCommitTagsQuery.php',
|
'DiffusionGitCommitTagsQuery' => 'applications/diffusion/query/committags/DiffusionGitCommitTagsQuery.php',
|
||||||
'DiffusionGitContainsQuery' => 'applications/diffusion/query/contains/DiffusionGitContainsQuery.php',
|
'DiffusionGitContainsQuery' => 'applications/diffusion/query/contains/DiffusionGitContainsQuery.php',
|
||||||
|
@ -451,7 +451,6 @@ phutil_register_library_map(array(
|
||||||
'DiffusionLintController' => 'applications/diffusion/controller/DiffusionLintController.php',
|
'DiffusionLintController' => 'applications/diffusion/controller/DiffusionLintController.php',
|
||||||
'DiffusionLintDetailsController' => 'applications/diffusion/controller/DiffusionLintDetailsController.php',
|
'DiffusionLintDetailsController' => 'applications/diffusion/controller/DiffusionLintDetailsController.php',
|
||||||
'DiffusionLintSaveRunner' => 'applications/diffusion/DiffusionLintSaveRunner.php',
|
'DiffusionLintSaveRunner' => 'applications/diffusion/DiffusionLintSaveRunner.php',
|
||||||
'DiffusionMercurialBrowseQuery' => 'applications/diffusion/query/browse/DiffusionMercurialBrowseQuery.php',
|
|
||||||
'DiffusionMercurialCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionMercurialCommitParentsQuery.php',
|
'DiffusionMercurialCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionMercurialCommitParentsQuery.php',
|
||||||
'DiffusionMercurialCommitTagsQuery' => 'applications/diffusion/query/committags/DiffusionMercurialCommitTagsQuery.php',
|
'DiffusionMercurialCommitTagsQuery' => 'applications/diffusion/query/committags/DiffusionMercurialCommitTagsQuery.php',
|
||||||
'DiffusionMercurialContainsQuery' => 'applications/diffusion/query/contains/DiffusionMercurialContainsQuery.php',
|
'DiffusionMercurialContainsQuery' => 'applications/diffusion/query/contains/DiffusionMercurialContainsQuery.php',
|
||||||
|
@ -481,7 +480,6 @@ phutil_register_library_map(array(
|
||||||
'DiffusionRepositoryTag' => 'applications/diffusion/data/DiffusionRepositoryTag.php',
|
'DiffusionRepositoryTag' => 'applications/diffusion/data/DiffusionRepositoryTag.php',
|
||||||
'DiffusionRequest' => 'applications/diffusion/request/DiffusionRequest.php',
|
'DiffusionRequest' => 'applications/diffusion/request/DiffusionRequest.php',
|
||||||
'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php',
|
'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php',
|
||||||
'DiffusionSvnBrowseQuery' => 'applications/diffusion/query/browse/DiffusionSvnBrowseQuery.php',
|
|
||||||
'DiffusionSvnCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionSvnCommitParentsQuery.php',
|
'DiffusionSvnCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionSvnCommitParentsQuery.php',
|
||||||
'DiffusionSvnCommitTagsQuery' => 'applications/diffusion/query/committags/DiffusionSvnCommitTagsQuery.php',
|
'DiffusionSvnCommitTagsQuery' => 'applications/diffusion/query/committags/DiffusionSvnCommitTagsQuery.php',
|
||||||
'DiffusionSvnContainsQuery' => 'applications/diffusion/query/contains/DiffusionSvnContainsQuery.php',
|
'DiffusionSvnContainsQuery' => 'applications/diffusion/query/contains/DiffusionSvnContainsQuery.php',
|
||||||
|
@ -1921,6 +1919,7 @@ phutil_register_library_map(array(
|
||||||
'ConduitAPI_diffusion_Method' => 'ConduitAPIMethod',
|
'ConduitAPI_diffusion_Method' => 'ConduitAPIMethod',
|
||||||
'ConduitAPI_diffusion_abstractquery_Method' => 'ConduitAPI_diffusion_Method',
|
'ConduitAPI_diffusion_abstractquery_Method' => 'ConduitAPI_diffusion_Method',
|
||||||
'ConduitAPI_diffusion_branchquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method',
|
'ConduitAPI_diffusion_branchquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method',
|
||||||
|
'ConduitAPI_diffusion_browsequery_Method' => 'ConduitAPI_diffusion_abstractquery_Method',
|
||||||
'ConduitAPI_diffusion_existsquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method',
|
'ConduitAPI_diffusion_existsquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method',
|
||||||
'ConduitAPI_diffusion_filecontentquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method',
|
'ConduitAPI_diffusion_filecontentquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method',
|
||||||
'ConduitAPI_diffusion_findsymbols_Method' => 'ConduitAPI_diffusion_Method',
|
'ConduitAPI_diffusion_findsymbols_Method' => 'ConduitAPI_diffusion_Method',
|
||||||
|
@ -2190,7 +2189,6 @@ phutil_register_library_map(array(
|
||||||
'DiffusionExternalController' => 'DiffusionController',
|
'DiffusionExternalController' => 'DiffusionController',
|
||||||
'DiffusionFileContentQuery' => 'DiffusionQuery',
|
'DiffusionFileContentQuery' => 'DiffusionQuery',
|
||||||
'DiffusionGitBranchTestCase' => 'PhabricatorTestCase',
|
'DiffusionGitBranchTestCase' => 'PhabricatorTestCase',
|
||||||
'DiffusionGitBrowseQuery' => 'DiffusionBrowseQuery',
|
|
||||||
'DiffusionGitCommitParentsQuery' => 'DiffusionCommitParentsQuery',
|
'DiffusionGitCommitParentsQuery' => 'DiffusionCommitParentsQuery',
|
||||||
'DiffusionGitCommitTagsQuery' => 'DiffusionCommitTagsQuery',
|
'DiffusionGitCommitTagsQuery' => 'DiffusionCommitTagsQuery',
|
||||||
'DiffusionGitContainsQuery' => 'DiffusionContainsQuery',
|
'DiffusionGitContainsQuery' => 'DiffusionContainsQuery',
|
||||||
|
@ -2213,7 +2211,6 @@ phutil_register_library_map(array(
|
||||||
'DiffusionLastModifiedQuery' => 'DiffusionQuery',
|
'DiffusionLastModifiedQuery' => 'DiffusionQuery',
|
||||||
'DiffusionLintController' => 'DiffusionController',
|
'DiffusionLintController' => 'DiffusionController',
|
||||||
'DiffusionLintDetailsController' => 'DiffusionController',
|
'DiffusionLintDetailsController' => 'DiffusionController',
|
||||||
'DiffusionMercurialBrowseQuery' => 'DiffusionBrowseQuery',
|
|
||||||
'DiffusionMercurialCommitParentsQuery' => 'DiffusionCommitParentsQuery',
|
'DiffusionMercurialCommitParentsQuery' => 'DiffusionCommitParentsQuery',
|
||||||
'DiffusionMercurialCommitTagsQuery' => 'DiffusionCommitTagsQuery',
|
'DiffusionMercurialCommitTagsQuery' => 'DiffusionCommitTagsQuery',
|
||||||
'DiffusionMercurialContainsQuery' => 'DiffusionContainsQuery',
|
'DiffusionMercurialContainsQuery' => 'DiffusionContainsQuery',
|
||||||
|
@ -2235,7 +2232,6 @@ phutil_register_library_map(array(
|
||||||
'DiffusionRemarkupRule' => 'PhabricatorRemarkupRuleObject',
|
'DiffusionRemarkupRule' => 'PhabricatorRemarkupRuleObject',
|
||||||
'DiffusionRepositoryController' => 'DiffusionController',
|
'DiffusionRepositoryController' => 'DiffusionController',
|
||||||
'DiffusionSetupException' => 'AphrontUsageException',
|
'DiffusionSetupException' => 'AphrontUsageException',
|
||||||
'DiffusionSvnBrowseQuery' => 'DiffusionBrowseQuery',
|
|
||||||
'DiffusionSvnCommitParentsQuery' => 'DiffusionCommitParentsQuery',
|
'DiffusionSvnCommitParentsQuery' => 'DiffusionCommitParentsQuery',
|
||||||
'DiffusionSvnCommitTagsQuery' => 'DiffusionCommitTagsQuery',
|
'DiffusionSvnCommitTagsQuery' => 'DiffusionCommitTagsQuery',
|
||||||
'DiffusionSvnContainsQuery' => 'DiffusionContainsQuery',
|
'DiffusionSvnContainsQuery' => 'DiffusionContainsQuery',
|
||||||
|
|
|
@ -0,0 +1,547 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group conduit
|
||||||
|
*/
|
||||||
|
final class ConduitAPI_diffusion_browsequery_Method
|
||||||
|
extends ConduitAPI_diffusion_abstractquery_Method {
|
||||||
|
|
||||||
|
public function getMethodDescription() {
|
||||||
|
return
|
||||||
|
'File(s) information for a repository at an (optional) path and '.
|
||||||
|
'(optional) commit.';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function defineReturnType() {
|
||||||
|
return 'array';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function defineCustomParamTypes() {
|
||||||
|
return array(
|
||||||
|
'path' => 'optional string',
|
||||||
|
'commit' => 'optional string',
|
||||||
|
'needValidityOnly' => 'optional bool',
|
||||||
|
'renderReadme' => 'optional bool',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getResult(ConduitAPIRequest $request) {
|
||||||
|
$result = parent::getResult($request);
|
||||||
|
if ($request->getValue('renderReadme', false)) {
|
||||||
|
$readme = $this->renderReadme($request, $result);
|
||||||
|
}
|
||||||
|
return $result->toDictionary();
|
||||||
|
}
|
||||||
|
|
||||||
|
final private function renderReadme(
|
||||||
|
ConduitAPIRequest $request,
|
||||||
|
DiffusionBrowseResultSet $result) {
|
||||||
|
$drequest = $this->getDiffusionRequest();
|
||||||
|
|
||||||
|
$readme = null;
|
||||||
|
foreach ($result->getPaths() as $result_path) {
|
||||||
|
$file_type = $result_path->getFileType();
|
||||||
|
if (($file_type != ArcanistDiffChangeType::FILE_NORMAL) &&
|
||||||
|
($file_type != ArcanistDiffChangeType::FILE_TEXT)) {
|
||||||
|
// Skip directories, etc.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = $result_path->getPath();
|
||||||
|
|
||||||
|
if (preg_match('/^readme(|\.txt|\.remarkup|\.rainbow)$/i', $path)) {
|
||||||
|
$readme = $result_path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$readme) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$readme_request = DiffusionRequest::newFromDictionary(
|
||||||
|
array(
|
||||||
|
'repository' => $drequest->getRepository(),
|
||||||
|
'commit' => $drequest->getStableCommitName(),
|
||||||
|
'path' => $readme->getFullPath(),
|
||||||
|
));
|
||||||
|
|
||||||
|
$file_content = DiffusionFileContent::newFromConduit(
|
||||||
|
DiffusionQuery::callConduitWithDiffusionRequest(
|
||||||
|
$request->getUser(),
|
||||||
|
$readme_request,
|
||||||
|
'diffusion.filecontentquery',
|
||||||
|
array(
|
||||||
|
'commit' => $drequest->getStableCommitName(),
|
||||||
|
'path' => $readme->getFullPath(),
|
||||||
|
'needsBlame' => false,
|
||||||
|
)));
|
||||||
|
$readme_content = $file_content->getCorpus();
|
||||||
|
|
||||||
|
if (preg_match('/\\.txt$/', $readme->getPath())) {
|
||||||
|
$readme_content = phutil_escape_html_newlines($readme_content);
|
||||||
|
|
||||||
|
$class = null;
|
||||||
|
} else if (preg_match('/\\.rainbow$/', $readme->getPath())) {
|
||||||
|
$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';
|
||||||
|
} else {
|
||||||
|
// Markup extensionless files as remarkup so we get links and such.
|
||||||
|
$engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine();
|
||||||
|
$engine->setConfig('viewer', $request->getUser());
|
||||||
|
$readme_content = $engine->markupText($readme_content);
|
||||||
|
|
||||||
|
$class = 'phabricator-remarkup';
|
||||||
|
}
|
||||||
|
|
||||||
|
$readme_content = phutil_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'class' => $class,
|
||||||
|
),
|
||||||
|
$readme_content);
|
||||||
|
|
||||||
|
$result->setReadmeContent($readme_content);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getGitResult(ConduitAPIRequest $request) {
|
||||||
|
$drequest = $this->getDiffusionRequest();
|
||||||
|
$repository = $drequest->getRepository();
|
||||||
|
$path = $request->getValue('path');
|
||||||
|
$commit = $request->getValue('commit');
|
||||||
|
$result = $this->getEmptyResultSet();
|
||||||
|
|
||||||
|
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);
|
||||||
|
$result
|
||||||
|
->setReasonForEmptyResultSet(
|
||||||
|
DiffusionBrowseResultSet::REASON_IS_DELETED)
|
||||||
|
->setDeletedAtCommit(idx($commits, 0))
|
||||||
|
->setExistedAtCommit(idx($commits, 1));
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result->setReasonForEmptyResultSet(
|
||||||
|
DiffusionBrowseResultSet::REASON_IS_NONEXISTENT);
|
||||||
|
return $result;
|
||||||
|
} else {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trim($stdout) == 'blob') {
|
||||||
|
$result->setReasonForEmptyResultSet(
|
||||||
|
DiffusionBrowseResultSet::REASON_IS_FILE);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result->setIsValidResults(true);
|
||||||
|
if ($this->shouldOnlyTestValidity($request)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
list($stdout) = $repository->execxLocalCommand(
|
||||||
|
'ls-tree -z -l %s:%s',
|
||||||
|
$commit,
|
||||||
|
$path);
|
||||||
|
|
||||||
|
$submodules = array();
|
||||||
|
|
||||||
|
if (strlen($path)) {
|
||||||
|
$prefix = rtrim($path, '/').'/';
|
||||||
|
} else {
|
||||||
|
$prefix = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
foreach (explode("\0", 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);
|
||||||
|
|
||||||
|
$path_result = new DiffusionRepositoryPath();
|
||||||
|
|
||||||
|
if ($type == 'tree') {
|
||||||
|
$file_type = DifferentialChangeType::FILE_DIRECTORY;
|
||||||
|
} else if ($type == 'commit') {
|
||||||
|
$file_type = DifferentialChangeType::FILE_SUBMODULE;
|
||||||
|
$submodules[] = $path_result;
|
||||||
|
} else {
|
||||||
|
$mode = intval($mode, 8);
|
||||||
|
if (($mode & 0120000) == 0120000) {
|
||||||
|
$file_type = DifferentialChangeType::FILE_SYMLINK;
|
||||||
|
} else {
|
||||||
|
$file_type = DifferentialChangeType::FILE_NORMAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$path_result->setFullPath($prefix.$name);
|
||||||
|
$path_result->setPath($name);
|
||||||
|
$path_result->setHash($hash);
|
||||||
|
$path_result->setFileType($file_type);
|
||||||
|
$path_result->setFileSize($size);
|
||||||
|
|
||||||
|
$results[] = $path_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 $result->setPaths($results);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getMercurialResult(ConduitAPIRequest $request) {
|
||||||
|
$drequest = $this->getDiffusionRequest();
|
||||||
|
$repository = $drequest->getRepository();
|
||||||
|
$path = $request->getValue('path');
|
||||||
|
$commit = $request->getValue('commit');
|
||||||
|
$result = $this->getEmptyResultSet();
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
$result->setReasonForEmptyResultSet(
|
||||||
|
DiffusionBrowseResultSet::REASON_IS_FILE);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
$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) {
|
||||||
|
$path_result = new DiffusionRepositoryPath();
|
||||||
|
$path_result->setPath($key);
|
||||||
|
$path_result->setFileType($type);
|
||||||
|
$path_result->setFullPath(ltrim($match_against.'/', '/').$key);
|
||||||
|
|
||||||
|
$results[$key] = $path_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$valid_results = true;
|
||||||
|
if (empty($results)) {
|
||||||
|
// TODO: Detect "deleted" by issuing "hg log"?
|
||||||
|
$result->setReasonForEmptyResultSet(
|
||||||
|
DiffusionBrowseResultSet::REASON_IS_NONEXISTENT);
|
||||||
|
$valid_results = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result
|
||||||
|
->setPaths($results)
|
||||||
|
->setIsValidResults($valid_results);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSVNResult(ConduitAPIRequest $request) {
|
||||||
|
$drequest = $this->getDiffusionRequest();
|
||||||
|
$repository = $drequest->getRepository();
|
||||||
|
$path = $request->getValue('path');
|
||||||
|
$commit = $request->getValue('commit');
|
||||||
|
$result = $this->getEmptyResultSet();
|
||||||
|
|
||||||
|
$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.)
|
||||||
|
$result->setReasonForEmptyResultSet(
|
||||||
|
DiffusionBrowseResultSet::REASON_IS_UNTRACKED_PARENT);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$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)) {
|
||||||
|
$result->setReasonForEmptyResultSet(
|
||||||
|
DiffusionBrowseResultSet::REASON_IS_NONEXISTENT);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 == '/') {
|
||||||
|
$result->setReasonForEmptyResultSet(
|
||||||
|
DiffusionBrowseResultSet::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) {
|
||||||
|
$result->setReasonForEmptyResultSet(
|
||||||
|
DiffusionBrowseResultSet::REASON_IS_NONEXISTENT);
|
||||||
|
} else {
|
||||||
|
$file_type = $reason['fileType'];
|
||||||
|
if (empty($reason['existed'])) {
|
||||||
|
$result->setReasonForEmptyResultSet(
|
||||||
|
DiffusionBrowseResultSet::REASON_IS_DELETED);
|
||||||
|
$result->setDeletedAtCommit($reason['svnCommit']);
|
||||||
|
if (!empty($reasons[1])) {
|
||||||
|
$result->setExistedAtCommit($reasons[1]['svnCommit']);
|
||||||
|
}
|
||||||
|
} else if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
|
||||||
|
$result->setReasonForEmptyResultSet(
|
||||||
|
DiffusionBrowseResultSet::REASON_IS_EMPTY);
|
||||||
|
} else {
|
||||||
|
$result->setReasonForEmptyResultSet(
|
||||||
|
DiffusionBrowseResultSet::REASON_IS_FILE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result->setIsValidResults(true);
|
||||||
|
if ($this->shouldOnlyTestValidity($request)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = array();
|
||||||
|
foreach ($index as $row) {
|
||||||
|
$sql[] =
|
||||||
|
'(pathID = '.(int)$row['pathID'].' AND '.
|
||||||
|
'svnCommit = '.(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 (%Q)
|
||||||
|
ORDER BY pathName',
|
||||||
|
PhabricatorRepository::TABLE_FILESYSTEM,
|
||||||
|
PhabricatorRepository::TABLE_PATH,
|
||||||
|
$repository->getID(),
|
||||||
|
$path_id,
|
||||||
|
implode(' OR ', $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_path = new DiffusionRepositoryPath();
|
||||||
|
$result_path->setPath($file_path);
|
||||||
|
$result_path->setFullPath($full_path);
|
||||||
|
// $result_path->setHash($hash);
|
||||||
|
$result_path->setFileType($file['fileType']);
|
||||||
|
// $result_path->setFileSize($size);
|
||||||
|
|
||||||
|
if (!empty($file['hasCommit'])) {
|
||||||
|
$commit = idx($commits, $file['svnCommit']);
|
||||||
|
if ($commit) {
|
||||||
|
$data = idx($commit_data, $commit->getID());
|
||||||
|
$result_path->setLastModifiedCommit($commit);
|
||||||
|
$result_path->setLastCommitData($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$results[] = $result_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($results)) {
|
||||||
|
$result->setReasonForEmptyResultSet(
|
||||||
|
DiffusionBrowseResultSet::REASON_IS_EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result->setPaths($results);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getEmptyResultSet() {
|
||||||
|
return id(new DiffusionBrowseResultSet())
|
||||||
|
->setPaths(array())
|
||||||
|
->setReasonForEmptyResultSet(null)
|
||||||
|
->setIsValidResults(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function shouldOnlyTestValidity(ConduitAPIRequest $request) {
|
||||||
|
return $request->getValue('needValidityOnly', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -9,11 +9,16 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||||
if ($this->getRequest()->getStr('before')) {
|
if ($this->getRequest()->getStr('before')) {
|
||||||
$is_file = true;
|
$is_file = true;
|
||||||
} else if ($this->getRequest()->getStr('grep') == '') {
|
} else if ($this->getRequest()->getStr('grep') == '') {
|
||||||
$browse_query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest);
|
$results = DiffusionBrowseResultSet::newFromConduit(
|
||||||
$browse_query->setViewer($this->getRequest()->getUser());
|
$this->callConduitWithDiffusionRequest(
|
||||||
$results = $browse_query->loadPaths();
|
'diffusion.browsequery',
|
||||||
$reason = $browse_query->getReasonForEmptyResultSet();
|
array(
|
||||||
$is_file = ($reason == DiffusionBrowseQuery::REASON_IS_FILE);
|
'path' => $drequest->getPath(),
|
||||||
|
'commit' => $drequest->getCommit(),
|
||||||
|
'renderReadme' => true,
|
||||||
|
)));
|
||||||
|
$reason = $results->getReasonForEmptyResultSet();
|
||||||
|
$is_file = ($reason == DiffusionBrowseResultSet::REASON_IS_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($is_file) {
|
if ($is_file) {
|
||||||
|
@ -42,17 +47,17 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||||
$content[] = $this->renderSearchResults();
|
$content[] = $this->renderSearchResults();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (!$results) {
|
if (!$results->isValidResults()) {
|
||||||
$empty_result = new DiffusionEmptyResultView();
|
$empty_result = new DiffusionEmptyResultView();
|
||||||
$empty_result->setDiffusionRequest($drequest);
|
$empty_result->setDiffusionRequest($drequest);
|
||||||
$empty_result->setBrowseQuery($browse_query);
|
$empty_result->setDiffusionBrowseResultSet($results);
|
||||||
$empty_result->setView($this->getRequest()->getStr('view'));
|
$empty_result->setView($this->getRequest()->getStr('view'));
|
||||||
$content[] = $empty_result;
|
$content[] = $empty_result;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
$phids = array();
|
$phids = array();
|
||||||
foreach ($results as $result) {
|
foreach ($results->getPaths() as $result) {
|
||||||
$data = $result->getLastCommitData();
|
$data = $result->getLastCommitData();
|
||||||
if ($data) {
|
if ($data) {
|
||||||
if ($data->getCommitDetail('authorPHID')) {
|
if ($data->getCommitDetail('authorPHID')) {
|
||||||
|
@ -67,7 +72,7 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||||
$browse_table = new DiffusionBrowseTableView();
|
$browse_table = new DiffusionBrowseTableView();
|
||||||
$browse_table->setDiffusionRequest($drequest);
|
$browse_table->setDiffusionRequest($drequest);
|
||||||
$browse_table->setHandles($handles);
|
$browse_table->setHandles($handles);
|
||||||
$browse_table->setPaths($results);
|
$browse_table->setPaths($results->getPaths());
|
||||||
$browse_table->setUser($this->getRequest()->getUser());
|
$browse_table->setUser($this->getRequest()->getUser());
|
||||||
|
|
||||||
$browse_panel = new AphrontPanelView();
|
$browse_panel = new AphrontPanelView();
|
||||||
|
@ -79,7 +84,7 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||||
|
|
||||||
$content[] = $this->buildOpenRevisions();
|
$content[] = $this->buildOpenRevisions();
|
||||||
|
|
||||||
$readme_content = $browse_query->renderReadme($results);
|
$readme_content = $results->getReadmeContent();
|
||||||
if ($readme_content) {
|
if ($readme_content) {
|
||||||
$readme_panel = new AphrontPanelView();
|
$readme_panel = new AphrontPanelView();
|
||||||
$readme_panel->setHeader('README');
|
$readme_panel->setHeader('README');
|
||||||
|
@ -99,7 +104,6 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||||
'view' => 'browse',
|
'view' => 'browse',
|
||||||
));
|
));
|
||||||
$nav->setCrumbs($crumbs);
|
$nav->setCrumbs($crumbs);
|
||||||
|
|
||||||
return $this->buildApplicationPage(
|
return $this->buildApplicationPage(
|
||||||
$nav,
|
$nav,
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -30,10 +30,16 @@ final class DiffusionPathCompleteController extends DiffusionController {
|
||||||
'repository' => $repository,
|
'repository' => $repository,
|
||||||
'path' => $query_dir,
|
'path' => $query_dir,
|
||||||
));
|
));
|
||||||
|
$this->setDiffusionRequest($drequest);
|
||||||
|
|
||||||
$browse_query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest);
|
$browse_results = DiffusionBrowseResultSet::newFromConduit(
|
||||||
$browse_query->setViewer($request->getUser());
|
$this->callConduitWithDiffusionRequest(
|
||||||
$paths = $browse_query->loadPaths();
|
'diffusion.browsequery',
|
||||||
|
array(
|
||||||
|
'path' => $drequest->getPath(),
|
||||||
|
'commit' => $drequest->getCommit(),
|
||||||
|
)));
|
||||||
|
$paths = $browse_results->getPaths();
|
||||||
|
|
||||||
$output = array();
|
$output = array();
|
||||||
foreach ($paths as $path) {
|
foreach ($paths as $path) {
|
||||||
|
|
|
@ -25,18 +25,24 @@ final class DiffusionPathValidateController extends DiffusionController {
|
||||||
'repository' => $repository,
|
'repository' => $repository,
|
||||||
'path' => $path,
|
'path' => $path,
|
||||||
));
|
));
|
||||||
|
$this->setDiffusionRequest($drequest);
|
||||||
|
|
||||||
$browse_query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest);
|
$browse_results = DiffusionBrowseResultSet::newFromConduit(
|
||||||
$browse_query->setViewer($request->getUser());
|
$this->callConduitWithDiffusionRequest(
|
||||||
$browse_query->needValidityOnly(true);
|
'diffusion.browsequery',
|
||||||
$valid = $browse_query->loadPaths();
|
array(
|
||||||
|
'path' => $drequest->getPath(),
|
||||||
|
'commit' => $drequest->getCommit(),
|
||||||
|
'needValidityOnly' => true,
|
||||||
|
)));
|
||||||
|
$valid = $browse_results->isValidResults();
|
||||||
|
|
||||||
if (!$valid) {
|
if (!$valid) {
|
||||||
switch ($browse_query->getReasonForEmptyResultSet()) {
|
switch ($browse_results->getReasonForEmptyResultSet()) {
|
||||||
case DiffusionBrowseQuery::REASON_IS_FILE:
|
case DiffusionBrowseResultSet::REASON_IS_FILE:
|
||||||
$valid = true;
|
$valid = true;
|
||||||
break;
|
break;
|
||||||
case DiffusionBrowseQuery::REASON_IS_EMPTY:
|
case DiffusionBrowseResultSet::REASON_IS_EMPTY:
|
||||||
$valid = true;
|
$valid = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,15 @@ final class DiffusionRepositoryController extends DiffusionController {
|
||||||
$history_query->needParents(true);
|
$history_query->needParents(true);
|
||||||
$history = $history_query->loadHistory();
|
$history = $history_query->loadHistory();
|
||||||
|
|
||||||
$browse_query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest);
|
$browse_results = DiffusionBrowseResultSet::newFromConduit(
|
||||||
$browse_query->setViewer($this->getRequest()->getUser());
|
$this->callConduitWithDiffusionRequest(
|
||||||
$browse_results = $browse_query->loadPaths();
|
'diffusion.browsequery',
|
||||||
|
array(
|
||||||
|
'path' => $drequest->getPath(),
|
||||||
|
'commit' => $drequest->getCommit(),
|
||||||
|
'renderReadme' => true,
|
||||||
|
)));
|
||||||
|
$browse_paths = $browse_results->getPaths();
|
||||||
|
|
||||||
$phids = array();
|
$phids = array();
|
||||||
|
|
||||||
|
@ -36,7 +42,7 @@ final class DiffusionRepositoryController extends DiffusionController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($browse_results as $item) {
|
foreach ($browse_paths as $item) {
|
||||||
$data = $item->getLastCommitData();
|
$data = $item->getLastCommitData();
|
||||||
if ($data) {
|
if ($data) {
|
||||||
if ($data->getCommitDetail('authorPHID')) {
|
if ($data->getCommitDetail('authorPHID')) {
|
||||||
|
@ -82,7 +88,7 @@ final class DiffusionRepositoryController extends DiffusionController {
|
||||||
$browse_table = new DiffusionBrowseTableView();
|
$browse_table = new DiffusionBrowseTableView();
|
||||||
$browse_table->setDiffusionRequest($drequest);
|
$browse_table->setDiffusionRequest($drequest);
|
||||||
$browse_table->setHandles($handles);
|
$browse_table->setHandles($handles);
|
||||||
$browse_table->setPaths($browse_results);
|
$browse_table->setPaths($browse_paths);
|
||||||
$browse_table->setUser($this->getRequest()->getUser());
|
$browse_table->setUser($this->getRequest()->getUser());
|
||||||
|
|
||||||
$browse_panel = new AphrontPanelView();
|
$browse_panel = new AphrontPanelView();
|
||||||
|
@ -99,7 +105,7 @@ final class DiffusionRepositoryController extends DiffusionController {
|
||||||
|
|
||||||
$content[] = $this->buildBranchListTable($drequest);
|
$content[] = $this->buildBranchListTable($drequest);
|
||||||
|
|
||||||
$readme = $browse_query->renderReadme($browse_results);
|
$readme = $browse_results->getReadmeContent();
|
||||||
if ($readme) {
|
if ($readme) {
|
||||||
$panel = new AphrontPanelView();
|
$panel = new AphrontPanelView();
|
||||||
$panel->setHeader('README');
|
$panel->setHeader('README');
|
||||||
|
|
97
src/applications/diffusion/data/DiffusionBrowseResultSet.php
Normal file
97
src/applications/diffusion/data/DiffusionBrowseResultSet.php
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DiffusionBrowseResultSet {
|
||||||
|
|
||||||
|
const REASON_IS_FILE = 'is-file';
|
||||||
|
const REASON_IS_DELETED = 'is-deleted';
|
||||||
|
const REASON_IS_NONEXISTENT = 'nonexistent';
|
||||||
|
const REASON_BAD_COMMIT = 'bad-commit';
|
||||||
|
const REASON_IS_EMPTY = 'empty';
|
||||||
|
const REASON_IS_UNTRACKED_PARENT = 'untracked-parent';
|
||||||
|
|
||||||
|
private $paths;
|
||||||
|
private $isValidResults;
|
||||||
|
private $reasonForEmptyResultSet;
|
||||||
|
private $existedAtCommit;
|
||||||
|
private $deletedAtCommit;
|
||||||
|
private $readmeContent;
|
||||||
|
|
||||||
|
public function setPaths(array $paths) {
|
||||||
|
assert_instances_of($paths, 'DiffusionRepositoryPath');
|
||||||
|
$this->paths = $paths;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function getPaths() {
|
||||||
|
return $this->paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsValidResults($is_valid) {
|
||||||
|
$this->isValidResults = $is_valid;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function isValidResults() {
|
||||||
|
return $this->isValidResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setReasonForEmptyResultSet($reason) {
|
||||||
|
$this->reasonForEmptyResultSet = $reason;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function getReasonForEmptyResultSet() {
|
||||||
|
return $this->reasonForEmptyResultSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setExistedAtCommit($existed_at_commit) {
|
||||||
|
$this->existedAtCommit = $existed_at_commit;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function getExistedAtCommit() {
|
||||||
|
return $this->existedAtCommit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDeletedAtCommit($deleted_at_commit) {
|
||||||
|
$this->deletedAtCommit = $deleted_at_commit;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function getDeletedAtCommit() {
|
||||||
|
return $this->deletedAtCommit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setReadmeContent($readme_content) {
|
||||||
|
$this->readmeContent = $readme_content;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function getReadmeContent() {
|
||||||
|
return $this->readmeContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toDictionary() {
|
||||||
|
$paths = $this->getPaths();
|
||||||
|
if ($paths) {
|
||||||
|
$paths = mpull($paths, 'toDictionary');
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'paths' => $paths,
|
||||||
|
'isValidResults' => $this->isValidResults(),
|
||||||
|
'reasonForEmptyResultSet' => $this->getReasonForEmptyResultSet(),
|
||||||
|
'existedAtCommit' => $this->getExistedAtCommit(),
|
||||||
|
'deletedAtCommit' => $this->getDeletedAtCommit(),
|
||||||
|
'readmeContent' => $this->getReadmeContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
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'])
|
||||||
|
->setReadmeContent($data['readmeContent']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,69 +21,110 @@ final class DiffusionRepositoryPath {
|
||||||
return $this->fullPath;
|
return $this->fullPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function setPath($path) {
|
public function setPath($path) {
|
||||||
$this->path = $path;
|
$this->path = $path;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function getPath() {
|
public function getPath() {
|
||||||
return $this->path;
|
return $this->path;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function setHash($hash) {
|
public function setHash($hash) {
|
||||||
$this->hash = $hash;
|
$this->hash = $hash;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function getHash() {
|
public function getHash() {
|
||||||
return $this->hash;
|
return $this->hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function setLastModifiedCommit(
|
public function setLastModifiedCommit(
|
||||||
PhabricatorRepositoryCommit $commit) {
|
PhabricatorRepositoryCommit $commit) {
|
||||||
$this->lastModifiedCommit = $commit;
|
$this->lastModifiedCommit = $commit;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function getLastModifiedCommit() {
|
public function getLastModifiedCommit() {
|
||||||
return $this->lastModifiedCommit;
|
return $this->lastModifiedCommit;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function setLastCommitData(
|
public function setLastCommitData(
|
||||||
PhabricatorRepositoryCommitData $last_commit_data) {
|
PhabricatorRepositoryCommitData $last_commit_data) {
|
||||||
$this->lastCommitData = $last_commit_data;
|
$this->lastCommitData = $last_commit_data;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function getLastCommitData() {
|
public function getLastCommitData() {
|
||||||
return $this->lastCommitData;
|
return $this->lastCommitData;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function setFileType($file_type) {
|
public function setFileType($file_type) {
|
||||||
$this->fileType = $file_type;
|
$this->fileType = $file_type;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function getFileType() {
|
public function getFileType() {
|
||||||
return $this->fileType;
|
return $this->fileType;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function setFileSize($file_size) {
|
public function setFileSize($file_size) {
|
||||||
$this->fileSize = $file_size;
|
$this->fileSize = $file_size;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function getFileSize() {
|
public function getFileSize() {
|
||||||
return $this->fileSize;
|
return $this->fileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function setExternalURI($external_uri) {
|
public function setExternalURI($external_uri) {
|
||||||
$this->externalURI = $external_uri;
|
$this->externalURI = $external_uri;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function getExternalURI() {
|
public function getExternalURI() {
|
||||||
return $this->externalURI;
|
return $this->externalURI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function toDictionary() {
|
||||||
|
$last_modified_commit = $this->getLastModifiedCommit();
|
||||||
|
if ($last_modified_commit) {
|
||||||
|
$last_modified_commit = $last_modified_commit->toDictionary();
|
||||||
|
}
|
||||||
|
$last_commit_data = $this->getLastCommitData();
|
||||||
|
if ($last_commit_data) {
|
||||||
|
$last_commit_data = $last_commit_data->toDictionary();
|
||||||
|
}
|
||||||
|
return array(
|
||||||
|
'fullPath' => $this->getFullPath(),
|
||||||
|
'path' => $this->getPath(),
|
||||||
|
'hash' => $this->getHash(),
|
||||||
|
'fileType' => $this->getFileType(),
|
||||||
|
'fileSize' => $this->getFileSize(),
|
||||||
|
'externalURI' => $this->getExternalURI(),
|
||||||
|
'lastModifiedCommit' => $last_modified_commit,
|
||||||
|
'lastCommitData' => $last_commit_data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function newFromDictionary(array $dict) {
|
||||||
|
$path = id(new DiffusionRepositoryPath())
|
||||||
|
->setFullPath($dict['fullPath'])
|
||||||
|
->setPath($dict['path'])
|
||||||
|
->setHash($dict['hash'])
|
||||||
|
->setFileType($dict['fileType'])
|
||||||
|
->setFileSize($dict['fileSize'])
|
||||||
|
->setExternalURI($dict['externalURI']);
|
||||||
|
if ($dict['lastModifiedCommit']) {
|
||||||
|
$last_modified_commit = PhabricatorRepositoryCommit::newFromDictionary(
|
||||||
|
$dict['lastModifiedCommit']);
|
||||||
|
$path->setLastModifiedCommit($last_modified_commit);
|
||||||
|
}
|
||||||
|
if ($dict['lastCommitData']) {
|
||||||
|
$last_commit_data = PhabricatorRepositoryCommitData::newFromDictionary(
|
||||||
|
$dict['lastCommitData']);
|
||||||
|
$path->setLastCommitData($last_commit_data);
|
||||||
|
}
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,164 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
abstract class DiffusionBrowseQuery {
|
|
||||||
|
|
||||||
private $request;
|
|
||||||
|
|
||||||
protected $reason;
|
|
||||||
protected $existedAtCommit;
|
|
||||||
protected $deletedAtCommit;
|
|
||||||
protected $validityOnly;
|
|
||||||
private $viewer;
|
|
||||||
|
|
||||||
public function setViewer(PhabricatorUser $viewer) {
|
|
||||||
$this->viewer = $viewer;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getViewer() {
|
|
||||||
return $this->viewer;
|
|
||||||
}
|
|
||||||
|
|
||||||
const REASON_IS_FILE = 'is-file';
|
|
||||||
const REASON_IS_DELETED = 'is-deleted';
|
|
||||||
const REASON_IS_NONEXISTENT = 'nonexistent';
|
|
||||||
const REASON_BAD_COMMIT = 'bad-commit';
|
|
||||||
const REASON_IS_EMPTY = 'empty';
|
|
||||||
const REASON_IS_UNTRACKED_PARENT = 'untracked-parent';
|
|
||||||
|
|
||||||
final private function __construct() {
|
|
||||||
// <private>
|
|
||||||
}
|
|
||||||
|
|
||||||
final public static function newFromDiffusionRequest(
|
|
||||||
DiffusionRequest $request) {
|
|
||||||
|
|
||||||
$repository = $request->getRepository();
|
|
||||||
|
|
||||||
switch ($repository->getVersionControlSystem()) {
|
|
||||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
|
||||||
// TODO: Verify local-path?
|
|
||||||
$query = new DiffusionGitBrowseQuery();
|
|
||||||
break;
|
|
||||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
|
||||||
$query = new DiffusionMercurialBrowseQuery();
|
|
||||||
break;
|
|
||||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
|
||||||
$query = new DiffusionSvnBrowseQuery();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Unsupported VCS!");
|
|
||||||
}
|
|
||||||
|
|
||||||
$query->request = $request;
|
|
||||||
|
|
||||||
return $query;
|
|
||||||
}
|
|
||||||
|
|
||||||
final protected function getRequest() {
|
|
||||||
return $this->request;
|
|
||||||
}
|
|
||||||
|
|
||||||
final public function getReasonForEmptyResultSet() {
|
|
||||||
return $this->reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
final public function getExistedAtCommit() {
|
|
||||||
return $this->existedAtCommit;
|
|
||||||
}
|
|
||||||
|
|
||||||
final public function getDeletedAtCommit() {
|
|
||||||
return $this->deletedAtCommit;
|
|
||||||
}
|
|
||||||
|
|
||||||
final public function loadPaths() {
|
|
||||||
$this->reason = null;
|
|
||||||
return $this->executeQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
final public function shouldOnlyTestValidity() {
|
|
||||||
return $this->validityOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
final public function needValidityOnly($need_validity_only) {
|
|
||||||
$this->validityOnly = $need_validity_only;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
final public function renderReadme(array $results) {
|
|
||||||
$drequest = $this->getRequest();
|
|
||||||
|
|
||||||
$readme = null;
|
|
||||||
foreach ($results as $result) {
|
|
||||||
$file_type = $result->getFileType();
|
|
||||||
if (($file_type != ArcanistDiffChangeType::FILE_NORMAL) &&
|
|
||||||
($file_type != ArcanistDiffChangeType::FILE_TEXT)) {
|
|
||||||
// Skip directories, etc.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$path = $result->getPath();
|
|
||||||
|
|
||||||
if (preg_match('/^readme(|\.txt|\.remarkup|\.rainbow)$/i', $path)) {
|
|
||||||
$readme = $result;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$readme) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$readme_request = DiffusionRequest::newFromDictionary(
|
|
||||||
array(
|
|
||||||
'repository' => $drequest->getRepository(),
|
|
||||||
'commit' => $drequest->getStableCommitName(),
|
|
||||||
'path' => $readme->getFullPath(),
|
|
||||||
));
|
|
||||||
|
|
||||||
$file_content = DiffusionFileContent::newFromConduit(
|
|
||||||
DiffusionQuery::callConduitWithDiffusionRequest(
|
|
||||||
$this->getViewer(),
|
|
||||||
$readme_request,
|
|
||||||
'diffusion.filecontentquery',
|
|
||||||
array(
|
|
||||||
'commit' => $drequest->getStableCommitName(),
|
|
||||||
'path' => $readme->getFullPath(),
|
|
||||||
'needsBlame' => false,
|
|
||||||
)));
|
|
||||||
$readme_content = $file_content->getCorpus();
|
|
||||||
|
|
||||||
if (preg_match('/\\.txt$/', $readme->getPath())) {
|
|
||||||
$readme_content = phutil_escape_html_newlines($readme_content);
|
|
||||||
|
|
||||||
$class = null;
|
|
||||||
} else if (preg_match('/\\.rainbow$/', $readme->getPath())) {
|
|
||||||
$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';
|
|
||||||
} else {
|
|
||||||
// Markup extensionless files as remarkup so we get links and such.
|
|
||||||
$engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine();
|
|
||||||
$engine->setConfig('viewer', $this->getViewer());
|
|
||||||
$readme_content = $engine->markupText($readme_content);
|
|
||||||
|
|
||||||
$class = 'phabricator-remarkup';
|
|
||||||
}
|
|
||||||
|
|
||||||
$readme_content = phutil_tag(
|
|
||||||
'div',
|
|
||||||
array(
|
|
||||||
'class' => $class,
|
|
||||||
),
|
|
||||||
$readme_content);
|
|
||||||
|
|
||||||
return $readme_content;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract protected function executeQuery();
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class DiffusionGitBrowseQuery extends DiffusionBrowseQuery {
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
if (strlen($path)) {
|
|
||||||
$prefix = rtrim($path, '/').'/';
|
|
||||||
} else {
|
|
||||||
$prefix = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$results = array();
|
|
||||||
foreach (explode("\0", 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($prefix.$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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class DiffusionMercurialBrowseQuery extends DiffusionBrowseQuery {
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,190 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class DiffusionSvnBrowseQuery extends DiffusionBrowseQuery {
|
|
||||||
|
|
||||||
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[] =
|
|
||||||
'(pathID = '.(int)$row['pathID'].' AND '.
|
|
||||||
'svnCommit = '.(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 (%Q)
|
|
||||||
ORDER BY pathName',
|
|
||||||
PhabricatorRepository::TABLE_FILESYSTEM,
|
|
||||||
PhabricatorRepository::TABLE_PATH,
|
|
||||||
$repository->getID(),
|
|
||||||
$path_id,
|
|
||||||
implode(' OR ', $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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
final class DiffusionEmptyResultView extends DiffusionView {
|
final class DiffusionEmptyResultView extends DiffusionView {
|
||||||
|
|
||||||
private $browseQuery;
|
private $browseResultSet;
|
||||||
private $view;
|
private $view;
|
||||||
|
|
||||||
public function setBrowseQuery($browse_query) {
|
public function setDiffusionBrowseResultSet(DiffusionBrowseResultSet $set) {
|
||||||
$this->browseQuery = $browse_query;
|
$this->browseResultSet = $set;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,22 +26,23 @@ final class DiffusionEmptyResultView extends DiffusionView {
|
||||||
$commit = 'HEAD';
|
$commit = 'HEAD';
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($this->browseQuery->getReasonForEmptyResultSet()) {
|
$reason = $this->browseResultSet->getReasonForEmptyResultSet();
|
||||||
case DiffusionBrowseQuery::REASON_IS_NONEXISTENT:
|
switch ($reason) {
|
||||||
|
case DiffusionBrowseResultSet::REASON_IS_NONEXISTENT:
|
||||||
$title = 'Path Does Not Exist';
|
$title = 'Path Does Not Exist';
|
||||||
// TODO: Under git, this error message should be more specific. It
|
// TODO: Under git, this error message should be more specific. It
|
||||||
// may exist on some other branch.
|
// may exist on some other branch.
|
||||||
$body = "This path does not exist anywhere.";
|
$body = "This path does not exist anywhere.";
|
||||||
$severity = AphrontErrorView::SEVERITY_ERROR;
|
$severity = AphrontErrorView::SEVERITY_ERROR;
|
||||||
break;
|
break;
|
||||||
case DiffusionBrowseQuery::REASON_IS_EMPTY:
|
case DiffusionBrowseResultSet::REASON_IS_EMPTY:
|
||||||
$title = 'Empty Directory';
|
$title = 'Empty Directory';
|
||||||
$body = "This path was an empty directory at {$commit}.\n";
|
$body = "This path was an empty directory at {$commit}.\n";
|
||||||
$severity = AphrontErrorView::SEVERITY_NOTICE;
|
$severity = AphrontErrorView::SEVERITY_NOTICE;
|
||||||
break;
|
break;
|
||||||
case DiffusionBrowseQuery::REASON_IS_DELETED:
|
case DiffusionBrowseResultSet::REASON_IS_DELETED:
|
||||||
$deleted = $this->browseQuery->getDeletedAtCommit();
|
$deleted = $this->browseResultSet->getDeletedAtCommit();
|
||||||
$existed = $this->browseQuery->getExistedAtCommit();
|
$existed = $this->browseResultSet->getExistedAtCommit();
|
||||||
|
|
||||||
$browse = $this->linkBrowse(
|
$browse = $this->linkBrowse(
|
||||||
$drequest->getPath(),
|
$drequest->getPath(),
|
||||||
|
@ -61,7 +62,7 @@ final class DiffusionEmptyResultView extends DiffusionView {
|
||||||
"r{$callsign}{$existed}");
|
"r{$callsign}{$existed}");
|
||||||
$severity = AphrontErrorView::SEVERITY_WARNING;
|
$severity = AphrontErrorView::SEVERITY_WARNING;
|
||||||
break;
|
break;
|
||||||
case DiffusionBrowseQuery::REASON_IS_UNTRACKED_PARENT:
|
case DiffusionBrowseResultSet::REASON_IS_UNTRACKED_PARENT:
|
||||||
$subdir = $drequest->getRepository()->getDetail('svn-subpath');
|
$subdir = $drequest->getRepository()->getDetail('svn-subpath');
|
||||||
$title = 'Directory Not Tracked';
|
$title = 'Directory Not Tracked';
|
||||||
$body =
|
$body =
|
||||||
|
@ -72,7 +73,7 @@ final class DiffusionEmptyResultView extends DiffusionView {
|
||||||
$severity = AphrontErrorView::SEVERITY_WARNING;
|
$severity = AphrontErrorView::SEVERITY_WARNING;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception("Unknown failure reason!");
|
throw new Exception("Unknown failure reason: $reason");
|
||||||
}
|
}
|
||||||
|
|
||||||
$error_view = new AphrontErrorView();
|
$error_view = new AphrontErrorView();
|
||||||
|
|
|
@ -312,18 +312,23 @@ final class PhabricatorOwnersPackage extends PhabricatorOwnersDAO
|
||||||
'repository' => $repository,
|
'repository' => $repository,
|
||||||
'path' => $path,
|
'path' => $path,
|
||||||
));
|
));
|
||||||
$query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest);
|
$results = DiffusionBrowseResultSet::newFromConduit(
|
||||||
$query->setViewer($this->getActor());
|
DiffusionQuery::callConduitWithDiffusionRequest(
|
||||||
$query->needValidityOnly(true);
|
$this->getActor(),
|
||||||
$valid = $query->loadPaths();
|
$drequest,
|
||||||
|
'diffusion.browsequery',
|
||||||
|
array(
|
||||||
|
'path' => $path,
|
||||||
|
'needValidityOnly' => true)));
|
||||||
|
$valid = $results->isValidResults();
|
||||||
$is_directory = true;
|
$is_directory = true;
|
||||||
if (!$valid) {
|
if (!$valid) {
|
||||||
switch ($query->getReasonForEmptyResultSet()) {
|
switch ($results->getReasonForEmptyResultSet()) {
|
||||||
case DiffusionBrowseQuery::REASON_IS_FILE:
|
case DiffusionBrowseResultSet::REASON_IS_FILE:
|
||||||
$valid = true;
|
$valid = true;
|
||||||
$is_directory = false;
|
$is_directory = false;
|
||||||
break;
|
break;
|
||||||
case DiffusionBrowseQuery::REASON_IS_EMPTY:
|
case DiffusionBrowseResultSet::REASON_IS_EMPTY:
|
||||||
$valid = true;
|
$valid = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,4 +180,27 @@ final class PhabricatorRepositoryCommit
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -( Stuff for serialization )---------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: this is not a complete serialization; only the 'protected' fields are
|
||||||
|
* involved. This is due to ease of (ab)using the Lisk abstraction to get this
|
||||||
|
* done, as well as complexity of the other fields.
|
||||||
|
*/
|
||||||
|
public function toDictionary() {
|
||||||
|
return array(
|
||||||
|
'repositoryID' => $this->getRepositoryID(),
|
||||||
|
'phid' => $this->getPHID(),
|
||||||
|
'commitIdentifier' => $this->getCommitIdentifier(),
|
||||||
|
'epoch' => $this->getEpoch(),
|
||||||
|
'mailKey' => $this->getMailKey(),
|
||||||
|
'authorPHID' => $this->getAuthorPHID(),
|
||||||
|
'auditStatus' => $this->getAuditStatus(),
|
||||||
|
'summary' => $this->getSummary());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function newFromDictionary(array $dict) {
|
||||||
|
return id(new PhabricatorRepositoryCommit())
|
||||||
|
->loadFromArray($dict);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,4 +41,18 @@ final class PhabricatorRepositoryCommitData extends PhabricatorRepositoryDAO {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function toDictionary() {
|
||||||
|
return array(
|
||||||
|
'commitID' => $this->commitID,
|
||||||
|
'authorName' => $this->authorName,
|
||||||
|
'commitMessage' => $this->commitMessage,
|
||||||
|
'commitDetails' => json_encode($this->commitDetails),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function newFromDictionary(array $dict) {
|
||||||
|
return id(new PhabricatorRepositoryCommitData())
|
||||||
|
->loadFromArray($dict);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue