@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator

Return Diffusion diffs through Files, not directly over Conduit

Summary:
Fixes T10423. Ref T11524. This changes `diffusion.rawdiffquery` to return a file PHID instead of a blob of data.

This is better in general, but particularly better for huge diffs (as in T10423) and diffs with non-utf8 data (as in T10423).

Test Plan:
- Used `bin/differential extract` to extract a latin1 diff, got a clean diff.
- Used `bin/repository reparse --herald` to rerun herald on a latin1 diff, got a clean result.
- Pushed latin1 diffs to test commit hooks.
- Triggered the the too large / too slow logic.
- Viewed latin1 diffs in Diffusion.
- Used "blame past this change" in Diffusion to hit the `before` logic.

Reviewers: chad

Reviewed By: chad

Subscribers: eadler

Maniphest Tasks: T10423, T11524

Differential Revision: https://secure.phabricator.com/D16460

+160 -155
+1 -1
src/__phutil_library_map__.php
··· 5260 5260 'DiffusionQueryCommitsConduitAPIMethod' => 'DiffusionConduitAPIMethod', 5261 5261 'DiffusionQueryConduitAPIMethod' => 'DiffusionConduitAPIMethod', 5262 5262 'DiffusionQueryPathsConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', 5263 - 'DiffusionRawDiffQuery' => 'DiffusionQuery', 5263 + 'DiffusionRawDiffQuery' => 'DiffusionFileFutureQuery', 5264 5264 'DiffusionRawDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', 5265 5265 'DiffusionReadmeView' => 'DiffusionView', 5266 5266 'DiffusionRefDatasource' => 'PhabricatorTypeaheadDatasource',
+16 -1
src/applications/differential/engine/DifferentialDiffExtractionEngine.php
··· 36 36 'repository' => $repository, 37 37 )); 38 38 39 - $raw_diff = DiffusionQuery::callConduitWithDiffusionRequest( 39 + $diff_info = DiffusionQuery::callConduitWithDiffusionRequest( 40 40 $viewer, 41 41 $drequest, 42 42 'diffusion.rawdiffquery', 43 43 array( 44 44 'commit' => $identifier, 45 45 )); 46 + 47 + $file_phid = $diff_info['filePHID']; 48 + $diff_file = id(new PhabricatorFileQuery()) 49 + ->setViewer($viewer) 50 + ->withPHIDs(array($file_phid)) 51 + ->executeOne(); 52 + if (!$diff_file) { 53 + throw new Exception( 54 + pht( 55 + 'Failed to load file ("%s") returned by "%s".', 56 + $file_phid, 57 + 'diffusion.rawdiffquery')); 58 + } 59 + 60 + $raw_diff = $diff_file->loadFileData(); 46 61 47 62 // TODO: Support adds, deletes and moves under SVN. 48 63 if (strlen($raw_diff)) {
+1 -1
src/applications/diffusion/DiffusionLintSaveRunner.php
··· 262 262 263 263 $query = DiffusionFileContentQuery::newFromDiffusionRequest($drequest); 264 264 $queries[$path] = $query; 265 - $futures[$path] = $query->getFileContentFuture(); 265 + $futures[$path] = new ImmediateFuture($query->executeInline()); 266 266 } 267 267 268 268 $authors = array();
+1 -1
src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php
··· 207 207 $raw_query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest) 208 208 ->setAnchorCommit($effective_commit); 209 209 210 - $raw_diff = $raw_query->loadRawDiff(); 210 + $raw_diff = $raw_query->executeInline(); 211 211 if (!$raw_diff) { 212 212 return $this->getEmptyResult(2); 213 213 }
+5 -17
src/applications/diffusion/conduit/DiffusionRawDiffQueryConduitAPIMethod.php
··· 21 21 return array( 22 22 'commit' => 'required string', 23 23 'path' => 'optional string', 24 - 'timeout' => 'optional int', 25 - 'byteLimit' => 'optional int', 26 24 'linesOfContext' => 'optional int', 27 25 'againstCommit' => 'optional string', 28 - ); 26 + ) + DiffusionFileFutureQuery::getConduitParameters(); 29 27 } 30 28 31 29 protected function getResult(ConduitAPIRequest $request) { 32 30 $drequest = $this->getDiffusionRequest(); 33 31 34 - $raw_query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest); 35 - 36 - $timeout = $request->getValue('timeout'); 37 - if ($timeout !== null) { 38 - $raw_query->setTimeout($timeout); 39 - } 32 + $query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest); 40 33 41 34 $lines_of_context = $request->getValue('linesOfContext'); 42 35 if ($lines_of_context !== null) { 43 - $raw_query->setLinesOfContext($lines_of_context); 36 + $query->setLinesOfContext($lines_of_context); 44 37 } 45 38 46 39 $against_commit = $request->getValue('againstCommit'); 47 40 if ($against_commit !== null) { 48 - $raw_query->setAgainstCommit($against_commit); 41 + $query->setAgainstCommit($against_commit); 49 42 } 50 43 51 - $byte_limit = $request->getValue('byteLimit'); 52 - if ($byte_limit !== null) { 53 - $raw_query->setByteLimit($byte_limit); 54 - } 55 - 56 - return $raw_query->loadRawDiff(); 44 + return $query->respondToConduitRequest($request); 57 45 } 58 46 59 47 }
+18 -1
src/applications/diffusion/controller/DiffusionBrowseController.php
··· 1527 1527 1528 1528 private function getBeforeLineNumber($target_commit) { 1529 1529 $drequest = $this->getDiffusionRequest(); 1530 + $viewer = $this->getViewer(); 1530 1531 1531 1532 $line = $drequest->getLine(); 1532 1533 if (!$line) { 1533 1534 return null; 1534 1535 } 1535 1536 1536 - $raw_diff = $this->callConduitWithDiffusionRequest( 1537 + $diff_info = $this->callConduitWithDiffusionRequest( 1537 1538 'diffusion.rawdiffquery', 1538 1539 array( 1539 1540 'commit' => $drequest->getCommit(), 1540 1541 'path' => $drequest->getPath(), 1541 1542 'againstCommit' => $target_commit, 1542 1543 )); 1544 + 1545 + $file_phid = $diff_info['filePHID']; 1546 + $file = id(new PhabricatorFileQuery()) 1547 + ->setViewer($viewer) 1548 + ->withPHIDs(array($file_phid)) 1549 + ->executeOne(); 1550 + if (!$file) { 1551 + throw new Exception( 1552 + pht( 1553 + 'Failed to load file ("%s") returned by "%s".', 1554 + $file_phid, 1555 + 'diffusion.rawdiffquery.')); 1556 + } 1557 + 1558 + $raw_diff = $file->loadFileData(); 1559 + 1543 1560 $old_line = 0; 1544 1561 $new_line = 0; 1545 1562
+13 -11
src/applications/diffusion/controller/DiffusionCommitController.php
··· 1027 1027 } 1028 1028 1029 1029 private function buildRawDiffResponse(DiffusionRequest $drequest) { 1030 - $raw_diff = $this->callConduitWithDiffusionRequest( 1030 + $diff_info = $this->callConduitWithDiffusionRequest( 1031 1031 'diffusion.rawdiffquery', 1032 1032 array( 1033 1033 'commit' => $drequest->getCommit(), 1034 1034 'path' => $drequest->getPath(), 1035 1035 )); 1036 1036 1037 - $file = PhabricatorFile::buildFromFileDataOrHash( 1038 - $raw_diff, 1039 - array( 1040 - 'name' => $drequest->getCommit().'.diff', 1041 - 'ttl' => (60 * 60 * 24), 1042 - 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, 1043 - )); 1037 + $file_phid = $diff_info['filePHID']; 1044 1038 1045 - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 1046 - $file->attachToObject($drequest->getRepository()->getPHID()); 1047 - unset($unguarded); 1039 + $file = id(new PhabricatorFileQuery()) 1040 + ->setViewer($this->getViewer()) 1041 + ->withPHIDs(array($file_phid)) 1042 + ->executeOne(); 1043 + if (!$file) { 1044 + throw new Exception( 1045 + pht( 1046 + 'Failed to load file ("%s") returned by "%s".', 1047 + $file_phid, 1048 + 'diffusion.rawdiffquery')); 1049 + } 1048 1050 1049 1051 return $file->getRedirectResponse(); 1050 1052 }
+1 -1
src/applications/diffusion/engine/DiffusionCommitHookEngine.php
··· 1095 1095 ->setTimeout($time_limit) 1096 1096 ->setByteLimit($byte_limit) 1097 1097 ->setLinesOfContext(0) 1098 - ->loadRawDiff(); 1098 + ->executeInline(); 1099 1099 break; 1100 1100 case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: 1101 1101 // TODO: This diff has 3 lines of context, which produces slightly
+30 -5
src/applications/diffusion/herald/HeraldCommitAdapter.php
··· 204 204 } 205 205 206 206 private function loadCommitDiff() { 207 + $viewer = PhabricatorUser::getOmnipotentUser(); 208 + 207 209 $byte_limit = self::getEnormousByteLimit(); 210 + $time_limit = self::getEnormousTimeLimit(); 208 211 209 - $raw = $this->callConduit( 212 + $diff_info = $this->callConduit( 210 213 'diffusion.rawdiffquery', 211 214 array( 212 215 'commit' => $this->commit->getCommitIdentifier(), 213 - 'timeout' => self::getEnormousTimeLimit(), 216 + 'timeout' => $time_limit, 214 217 'byteLimit' => $byte_limit, 215 218 'linesOfContext' => 0, 216 219 )); 217 220 218 - if (strlen($raw) >= $byte_limit) { 221 + if ($diff_info['tooHuge']) { 219 222 throw new Exception( 220 223 pht( 221 - 'The raw text of this change is enormous (larger than %d bytes). '. 224 + 'The raw text of this change is enormous (larger than %s byte(s)). '. 222 225 'Herald can not process it.', 223 - $byte_limit)); 226 + new PhutilNumber($byte_limit))); 227 + } 228 + 229 + if ($diff_info['tooSlow']) { 230 + throw new Exception( 231 + pht( 232 + 'The raw text of this change took too long to process (longer '. 233 + 'than %s second(s)). Herald can not process it.', 234 + new PhutilNumber($time_limit))); 235 + } 236 + 237 + $file_phid = $diff_info['filePHID']; 238 + $diff_file = id(new PhabricatorFileQuery()) 239 + ->setViewer($viewer) 240 + ->withPHIDs(array($file_phid)) 241 + ->executeOne(); 242 + if (!$diff_file) { 243 + throw new Exception( 244 + pht( 245 + 'Failed to load diff ("%s") for this change.', 246 + $file_phid)); 224 247 } 248 + 249 + $raw = $diff_file->loadFileData(); 225 250 226 251 $parser = new ArcanistDiffParser(); 227 252 $changes = $parser->parseDiff($raw);
+6 -7
src/applications/diffusion/query/DiffusionFileFutureQuery.php
··· 42 42 return $this->didHitTimeLimit; 43 43 } 44 44 45 - abstract protected function getFileContentFuture(); 45 + abstract protected function newQueryFuture(); 46 46 47 47 final public function respondToConduitRequest(ConduitAPIRequest $request) { 48 48 $drequest = $this->getRequest(); ··· 82 82 } 83 83 84 84 final public function executeInline() { 85 - $future = $this->getFileContentFuture(); 85 + $future = $this->newConfiguredQueryFuture(); 86 86 list($stdout) = $future->resolvex(); 87 - return $future; 87 + return $stdout; 88 88 } 89 89 90 90 final protected function executeQuery() { 91 - $future = $this->newConfiguredFileContentFuture(); 91 + $future = $this->newQueryFuture(); 92 92 93 93 $drequest = $this->getRequest(); 94 94 ··· 134 134 return $file; 135 135 } 136 136 137 - private function newConfiguredFileContentFuture() { 138 - $future = $this->getFileContentFuture(); 137 + private function newConfiguredQueryFuture() { 138 + $future = $this->newQueryFuture(); 139 139 140 140 if ($this->getTimeout()) { 141 141 $future->setTimeout($this->getTimeout()); ··· 148 148 149 149 return $future; 150 150 } 151 - 152 151 153 152 }
+1 -1
src/applications/diffusion/query/filecontent/DiffusionGitFileContentQuery.php
··· 2 2 3 3 final class DiffusionGitFileContentQuery extends DiffusionFileContentQuery { 4 4 5 - protected function getFileContentFuture() { 5 + protected function newQueryFuture() { 6 6 $drequest = $this->getRequest(); 7 7 8 8 $repository = $drequest->getRepository();
+1 -1
src/applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php
··· 3 3 final class DiffusionMercurialFileContentQuery 4 4 extends DiffusionFileContentQuery { 5 5 6 - protected function getFileContentFuture() { 6 + protected function newQueryFuture() { 7 7 $drequest = $this->getRequest(); 8 8 9 9 $repository = $drequest->getRepository();
+1 -1
src/applications/diffusion/query/filecontent/DiffusionSvnFileContentQuery.php
··· 2 2 3 3 final class DiffusionSvnFileContentQuery extends DiffusionFileContentQuery { 4 4 5 - protected function getFileContentFuture() { 5 + protected function newQueryFuture() { 6 6 $drequest = $this->getRequest(); 7 7 8 8 $repository = $drequest->getRepository();
+17 -37
src/applications/diffusion/query/rawdiff/DiffusionGitRawDiffQuery.php
··· 2 2 3 3 final class DiffusionGitRawDiffQuery extends DiffusionRawDiffQuery { 4 4 5 - protected function executeQuery() { 5 + protected function newQueryFuture() { 6 6 $drequest = $this->getRequest(); 7 7 $repository = $drequest->getRepository(); 8 8 ··· 17 17 '--dst-prefix=b/', 18 18 '-U'.(int)$this->getLinesOfContext(), 19 19 ); 20 - $options = implode(' ', $options); 21 20 22 21 $against = $this->getAgainstCommit(); 23 22 if ($against === null) { 24 - $against = $commit.'^'; 25 - } 26 - 27 - // If there's no path, get the entire raw diff. 28 - $path = nonempty($drequest->getPath(), '.'); 29 - 30 - $future = $repository->getLocalCommandFuture( 31 - 'diff %C %s %s -- %s', 32 - $options, 33 - $against, 34 - $commit, 35 - $path); 36 - 37 - $this->configureFuture($future); 38 - 39 - try { 40 - list($raw_diff) = $future->resolvex(); 41 - } catch (CommandException $ex) { 42 - // Check if this is the root commit by seeing if it has parents. 23 + // Check if this is the root commit by seeing if it has parents, since 24 + // `git diff X^ X` does not work if "X" is the initial commit. 43 25 list($parents) = $repository->execxLocalCommand( 44 26 'log --format=%s %s --', 45 - '%P', // "parents" 27 + '%P', 46 28 $commit); 47 29 48 30 if (strlen(trim($parents))) { 49 - throw $ex; 31 + $against = $commit.'^'; 32 + } else { 33 + $against = ArcanistGitAPI::GIT_MAGIC_ROOT_COMMIT; 50 34 } 35 + } 51 36 52 - // No parents means we're looking at the root revision. Diff against 53 - // the empty tree hash instead, since there is no parent so "^" does 54 - // not work. See ArcanistGitAPI for more discussion. 55 - $future = $repository->getLocalCommandFuture( 56 - 'diff %C %s %s -- %s', 57 - $options, 58 - ArcanistGitAPI::GIT_MAGIC_ROOT_COMMIT, 59 - $commit, 60 - $drequest->getPath()); 61 - 62 - $this->configureFuture($future); 63 - 64 - list($raw_diff) = $future->resolvex(); 37 + $path = $drequest->getPath(); 38 + if (!strlen($path)) { 39 + $path = '.'; 65 40 } 66 41 67 - return $raw_diff; 42 + return $repository->getLocalCommandFuture( 43 + 'diff %Ls %s %s -- %s', 44 + $options, 45 + $against, 46 + $commit, 47 + $path); 68 48 } 69 49 70 50 }
+2 -10
src/applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php
··· 2 2 3 3 final class DiffusionMercurialRawDiffQuery extends DiffusionRawDiffQuery { 4 4 5 - protected function executeQuery() { 6 - return $this->executeRawDiffCommand(); 7 - } 8 - 9 - protected function executeRawDiffCommand() { 5 + protected function newQueryFuture() { 10 6 $drequest = $this->getRequest(); 11 7 $repository = $drequest->getRepository(); 12 8 ··· 30 26 $commit, 31 27 $path); 32 28 33 - $this->configureFuture($future); 34 - 35 - list($raw_diff) = $future->resolvex(); 36 - 37 - return $raw_diff; 29 + return $future; 38 30 } 39 31 40 32 }
+2 -36
src/applications/diffusion/query/rawdiff/DiffusionRawDiffQuery.php
··· 1 1 <?php 2 2 3 - abstract class DiffusionRawDiffQuery extends DiffusionQuery { 3 + abstract class DiffusionRawDiffQuery 4 + extends DiffusionFileFutureQuery { 4 5 5 - private $timeout; 6 6 private $linesOfContext = 65535; 7 7 private $anchorCommit; 8 8 private $againstCommit; 9 - private $byteLimit; 10 9 11 10 final public static function newFromDiffusionRequest( 12 11 DiffusionRequest $request) { 13 12 return parent::newQueryObject(__CLASS__, $request); 14 13 } 15 14 16 - final public function loadRawDiff() { 17 - return $this->executeQuery(); 18 - } 19 - 20 - final public function setTimeout($timeout) { 21 - $this->timeout = $timeout; 22 - return $this; 23 - } 24 - 25 - final public function getTimeout() { 26 - return $this->timeout; 27 - } 28 - 29 - public function setByteLimit($byte_limit) { 30 - $this->byteLimit = $byte_limit; 31 - return $this; 32 - } 33 - 34 - public function getByteLimit() { 35 - return $this->byteLimit; 36 - } 37 - 38 15 final public function setLinesOfContext($lines_of_context) { 39 16 $this->linesOfContext = $lines_of_context; 40 17 return $this; ··· 64 41 } 65 42 66 43 return $this->getRequest()->getStableCommit(); 67 - } 68 - 69 - protected function configureFuture(ExecFuture $future) { 70 - if ($this->getTimeout()) { 71 - $future->setTimeout($this->getTimeout()); 72 - } 73 - 74 - if ($this->getByteLimit()) { 75 - $future->setStdoutSizeLimit($this->getByteLimit()); 76 - $future->setStderrSizeLimit($this->getByteLimit()); 77 - } 78 44 } 79 45 80 46 }
+2 -5
src/applications/diffusion/query/rawdiff/DiffusionSvnRawDiffQuery.php
··· 2 2 3 3 final class DiffusionSvnRawDiffQuery extends DiffusionRawDiffQuery { 4 4 5 - protected function executeQuery() { 5 + protected function newQueryFuture() { 6 6 $drequest = $this->getRequest(); 7 7 $repository = $drequest->getRepository(); 8 8 ··· 22 22 $commit, 23 23 $repository->getSubversionPathURI($drequest->getPath())); 24 24 25 - $this->configureFuture($future); 26 - 27 - list($raw_diff) = $future->resolvex(); 28 - return $raw_diff; 25 + return $future; 29 26 } 30 27 31 28 }
+42 -18
src/applications/repository/worker/PhabricatorRepositoryCommitHeraldWorker.php
··· 105 105 private function loadRawPatchText( 106 106 PhabricatorRepository $repository, 107 107 PhabricatorRepositoryCommit $commit) { 108 + $viewer = PhabricatorUser::getOmnipotentUser(); 109 + 110 + $identifier = $commit->getCommitIdentifier(); 108 111 109 112 $drequest = DiffusionRequest::newFromDictionary( 110 113 array( 111 - 'user' => PhabricatorUser::getOmnipotentUser(), 114 + 'user' => $viewer, 112 115 'repository' => $repository, 113 - 'commit' => $commit->getCommitIdentifier(), 114 116 )); 115 117 116 - $raw_query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest); 117 - $raw_query->setLinesOfContext(3); 118 - 119 118 $time_key = 'metamta.diffusion.time-limit'; 120 119 $byte_key = 'metamta.diffusion.byte-limit'; 121 120 $time_limit = PhabricatorEnv::getEnvConfig($time_key); 122 121 $byte_limit = PhabricatorEnv::getEnvConfig($byte_key); 123 122 124 - if ($time_limit) { 125 - $raw_query->setTimeout($time_limit); 126 - } 123 + $diff_info = DiffusionQuery::callConduitWithDiffusionRequest( 124 + $viewer, 125 + $drequest, 126 + 'diffusion.rawdiffquery', 127 + array( 128 + 'commit' => $identifier, 129 + 'linesOfContext' => 3, 130 + 'timeout' => $time_limit, 131 + 'byteLimit' => $byte_limit, 132 + )); 127 133 128 - $raw_diff = $raw_query->loadRawDiff(); 134 + if ($diff_info['tooSlow']) { 135 + throw new Exception( 136 + pht( 137 + 'Patch generation took longer than configured limit ("%s") of '. 138 + '%s second(s).', 139 + $time_key, 140 + new PhutilNumber($time_limit))); 141 + } 129 142 130 - $size = strlen($raw_diff); 131 - if ($byte_limit && $size > $byte_limit) { 132 - $pretty_size = phutil_format_bytes($size); 143 + if ($diff_info['tooHuge']) { 133 144 $pretty_limit = phutil_format_bytes($byte_limit); 134 - throw new Exception(pht( 135 - 'Patch size of %s exceeds configured byte size limit (%s) of %s.', 136 - $pretty_size, 137 - $byte_key, 138 - $pretty_limit)); 145 + throw new Exception( 146 + pht( 147 + 'Patch size exceeds configured byte size limit ("%s") of %s.', 148 + $byte_key, 149 + $pretty_limit)); 139 150 } 140 151 141 - return $raw_diff; 152 + $file_phid = $diff_info['filePHID']; 153 + $file = id(new PhabricatorFileQuery()) 154 + ->setViewer($viewer) 155 + ->withPHIDs(array($file_phid)) 156 + ->executeOne(); 157 + if (!$file) { 158 + throw new Exception( 159 + pht( 160 + 'Failed to load file ("%s") returned by "%s".', 161 + $file_phid, 162 + 'diffusion.rawdiffquery')); 163 + } 164 + 165 + return $file->loadFileData(); 142 166 } 143 167 144 168 }