@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
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Implement a dedicated "diffusion.blame" API method

Summary:
Fixes T2451. Several motivations here, from strongest to weakest:

- Currently, getting blame and file content are closely entwined. This makes fixing T9319 more difficult, and I want to fix it. I want to separate blame from content so there's more flexibility in how we approach this issue.
- This makes pursuing T2450 easier, if it turns out to be a meaningful win.
- If we can get a win on blame performance, we can do `arc blame` eventually if we want.

Test Plan:
- Blamed in SVN, Git and Mercurial.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T2451

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

+236
+10
src/__phutil_library_map__.php
··· 528 528 'DiffusionAuditorsAddAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php', 529 529 'DiffusionAuditorsAddSelfHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php', 530 530 'DiffusionAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsHeraldAction.php', 531 + 'DiffusionBlameConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionBlameConduitAPIMethod.php', 532 + 'DiffusionBlameQuery' => 'applications/diffusion/query/blame/DiffusionBlameQuery.php', 531 533 'DiffusionBlockHeraldAction' => 'applications/diffusion/herald/DiffusionBlockHeraldAction.php', 532 534 'DiffusionBranchQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php', 533 535 'DiffusionBranchTableController' => 'applications/diffusion/controller/DiffusionBranchTableController.php', ··· 601 603 'DiffusionFindSymbolsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionFindSymbolsConduitAPIMethod.php', 602 604 'DiffusionGetLintMessagesConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionGetLintMessagesConduitAPIMethod.php', 603 605 'DiffusionGetRecentCommitsByPathConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionGetRecentCommitsByPathConduitAPIMethod.php', 606 + 'DiffusionGitBlameQuery' => 'applications/diffusion/query/blame/DiffusionGitBlameQuery.php', 604 607 'DiffusionGitBranch' => 'applications/diffusion/data/DiffusionGitBranch.php', 605 608 'DiffusionGitBranchTestCase' => 'applications/diffusion/data/__tests__/DiffusionGitBranchTestCase.php', 606 609 'DiffusionGitFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionGitFileContentQuery.php', ··· 632 635 'DiffusionLowLevelParentsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelParentsQuery.php', 633 636 'DiffusionLowLevelQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelQuery.php', 634 637 'DiffusionLowLevelResolveRefsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php', 638 + 'DiffusionMercurialBlameQuery' => 'applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php', 635 639 'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php', 636 640 'DiffusionMercurialRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php', 637 641 'DiffusionMercurialRequest' => 'applications/diffusion/request/DiffusionMercurialRequest.php', ··· 743 747 'DiffusionSubversionServeSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php', 744 748 'DiffusionSubversionWireProtocol' => 'applications/diffusion/protocol/DiffusionSubversionWireProtocol.php', 745 749 'DiffusionSubversionWireProtocolTestCase' => 'applications/diffusion/protocol/__tests__/DiffusionSubversionWireProtocolTestCase.php', 750 + 'DiffusionSvnBlameQuery' => 'applications/diffusion/query/blame/DiffusionSvnBlameQuery.php', 746 751 'DiffusionSvnFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionSvnFileContentQuery.php', 747 752 'DiffusionSvnRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionSvnRawDiffQuery.php', 748 753 'DiffusionSvnRequest' => 'applications/diffusion/request/DiffusionSvnRequest.php', ··· 4493 4498 'DiffusionAuditorsAddAuditorsHeraldAction' => 'DiffusionAuditorsHeraldAction', 4494 4499 'DiffusionAuditorsAddSelfHeraldAction' => 'DiffusionAuditorsHeraldAction', 4495 4500 'DiffusionAuditorsHeraldAction' => 'HeraldAction', 4501 + 'DiffusionBlameConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', 4502 + 'DiffusionBlameQuery' => 'DiffusionQuery', 4496 4503 'DiffusionBlockHeraldAction' => 'HeraldAction', 4497 4504 'DiffusionBranchQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', 4498 4505 'DiffusionBranchTableController' => 'DiffusionController', ··· 4566 4573 'DiffusionFindSymbolsConduitAPIMethod' => 'DiffusionConduitAPIMethod', 4567 4574 'DiffusionGetLintMessagesConduitAPIMethod' => 'DiffusionConduitAPIMethod', 4568 4575 'DiffusionGetRecentCommitsByPathConduitAPIMethod' => 'DiffusionConduitAPIMethod', 4576 + 'DiffusionGitBlameQuery' => 'DiffusionBlameQuery', 4569 4577 'DiffusionGitBranch' => 'Phobject', 4570 4578 'DiffusionGitBranchTestCase' => 'PhabricatorTestCase', 4571 4579 'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery', ··· 4597 4605 'DiffusionLowLevelParentsQuery' => 'DiffusionLowLevelQuery', 4598 4606 'DiffusionLowLevelQuery' => 'Phobject', 4599 4607 'DiffusionLowLevelResolveRefsQuery' => 'DiffusionLowLevelQuery', 4608 + 'DiffusionMercurialBlameQuery' => 'DiffusionBlameQuery', 4600 4609 'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery', 4601 4610 'DiffusionMercurialRawDiffQuery' => 'DiffusionRawDiffQuery', 4602 4611 'DiffusionMercurialRequest' => 'DiffusionRequest', ··· 4708 4717 'DiffusionSubversionServeSSHWorkflow' => 'DiffusionSubversionSSHWorkflow', 4709 4718 'DiffusionSubversionWireProtocol' => 'Phobject', 4710 4719 'DiffusionSubversionWireProtocolTestCase' => 'PhabricatorTestCase', 4720 + 'DiffusionSvnBlameQuery' => 'DiffusionBlameQuery', 4711 4721 'DiffusionSvnFileContentQuery' => 'DiffusionFileContentQuery', 4712 4722 'DiffusionSvnRawDiffQuery' => 'DiffusionRawDiffQuery', 4713 4723 'DiffusionSvnRequest' => 'DiffusionRequest',
+44
src/applications/diffusion/conduit/DiffusionBlameConduitAPIMethod.php
··· 1 + <?php 2 + 3 + final class DiffusionBlameConduitAPIMethod 4 + extends DiffusionQueryConduitAPIMethod { 5 + 6 + public function getAPIMethodName() { 7 + return 'diffusion.blame'; 8 + } 9 + 10 + public function getMethodDescription() { 11 + return pht('Get blame information for a list of paths.'); 12 + } 13 + 14 + protected function defineReturnType() { 15 + return 'map<string, wild>'; 16 + } 17 + 18 + protected function defineCustomParamTypes() { 19 + return array( 20 + 'paths' => 'required list<string>', 21 + 'commit' => 'required string', 22 + 'timeout' => 'optional int', 23 + ); 24 + } 25 + 26 + protected function getResult(ConduitAPIRequest $request) { 27 + $drequest = $this->getDiffusionRequest(); 28 + 29 + $paths = $request->getValue('paths'); 30 + 31 + $blame_query = DiffusionBlameQuery::newFromDiffusionRequest($drequest) 32 + ->setPaths($paths); 33 + 34 + $timeout = $request->getValue('timeout'); 35 + if ($timeout) { 36 + $blame_query->setTimeout($timeout); 37 + } 38 + 39 + $blame = $blame_query->execute(); 40 + 41 + return $blame; 42 + } 43 + 44 + }
+7
src/applications/diffusion/conduit/DiffusionQueryConduitAPIMethod.php
··· 110 110 'commit' => $request->getValue('commit'), 111 111 )); 112 112 113 + if (!$drequest) { 114 + throw new Exception( 115 + pht( 116 + 'Repository "%s" is not a valid repository.', 117 + $identifier)); 118 + } 119 + 113 120 // Figure out whether we're going to handle this request on this device, 114 121 // or proxy it to another node in the cluster. 115 122
+69
src/applications/diffusion/query/blame/DiffusionBlameQuery.php
··· 1 + <?php 2 + 3 + abstract class DiffusionBlameQuery extends DiffusionQuery { 4 + 5 + private $timeout; 6 + private $paths; 7 + 8 + public function setTimeout($timeout) { 9 + $this->timeout = $timeout; 10 + return $this; 11 + } 12 + 13 + public function getTimeout() { 14 + return $this->timeout; 15 + } 16 + 17 + public function setPaths(array $paths) { 18 + $this->paths = $paths; 19 + return $this; 20 + } 21 + 22 + public function getPaths() { 23 + return $this->paths; 24 + } 25 + 26 + abstract protected function newBlameFuture(DiffusionRequest $request, $path); 27 + 28 + abstract protected function resolveBlameFuture(ExecFuture $future); 29 + 30 + final public static function newFromDiffusionRequest( 31 + DiffusionRequest $request) { 32 + return parent::newQueryObject(__CLASS__, $request); 33 + } 34 + 35 + final protected function executeQuery() { 36 + $paths = $this->getPaths(); 37 + $request = $this->getRequest(); 38 + $timeout = $this->getTimeout(); 39 + 40 + $futures = array(); 41 + foreach ($paths as $path) { 42 + $future = $this->newBlameFuture($request, $path); 43 + 44 + if ($timeout) { 45 + $future->setTimeout($timeout); 46 + } 47 + 48 + $futures[$path] = $future; 49 + } 50 + 51 + 52 + $blame = array(); 53 + 54 + if ($futures) { 55 + $futures = id(new FutureIterator($futures)) 56 + ->limit(4); 57 + 58 + foreach ($futures as $path => $future) { 59 + $path_blame = $this->resolveBlameFuture($future); 60 + if ($path_blame !== null) { 61 + $blame[$path] = $path_blame; 62 + } 63 + } 64 + } 65 + 66 + return $blame; 67 + } 68 + 69 + }
+34
src/applications/diffusion/query/blame/DiffusionGitBlameQuery.php
··· 1 + <?php 2 + 3 + final class DiffusionGitBlameQuery extends DiffusionBlameQuery { 4 + 5 + protected function newBlameFuture(DiffusionRequest $request, $path) { 6 + $repository = $request->getRepository(); 7 + 8 + $commit = $request->getCommit(); 9 + 10 + return $repository->getLocalCommandFuture( 11 + '--no-pager blame -s -l %s -- %s', 12 + $commit, 13 + $path); 14 + } 15 + 16 + protected function resolveBlameFuture(ExecFuture $future) { 17 + list($err, $stdout) = $future->resolve(); 18 + 19 + if ($err) { 20 + return null; 21 + } 22 + 23 + $result = array(); 24 + 25 + $lines = phutil_split_lines($stdout); 26 + foreach ($lines as $line) { 27 + list($commit) = explode(' ', $line, 2); 28 + $result[] = $commit; 29 + } 30 + 31 + return $result; 32 + } 33 + 34 + }
+36
src/applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php
··· 1 + <?php 2 + 3 + final class DiffusionMercurialBlameQuery extends DiffusionBlameQuery { 4 + 5 + protected function newBlameFuture(DiffusionRequest $request, $path) { 6 + $repository = $request->getRepository(); 7 + $commit = $request->getCommit(); 8 + 9 + // NOTE: We're using "--debug" to make "--changeset" give us the full 10 + // commit hashes. 11 + 12 + return $repository->getLocalCommandFuture( 13 + 'annotate --debug --changeset --rev %s -- %s', 14 + $commit, 15 + $path); 16 + } 17 + 18 + protected function resolveBlameFuture(ExecFuture $future) { 19 + list($err, $stdout) = $future->resolve(); 20 + 21 + if ($err) { 22 + return null; 23 + } 24 + 25 + $result = array(); 26 + 27 + $lines = phutil_split_lines($stdout); 28 + foreach ($lines as $line) { 29 + list($commit) = explode(':', $line, 2); 30 + $result[] = $commit; 31 + } 32 + 33 + return $result; 34 + } 35 + 36 + }
+36
src/applications/diffusion/query/blame/DiffusionSvnBlameQuery.php
··· 1 + <?php 2 + 3 + final class DiffusionSvnBlameQuery extends DiffusionBlameQuery { 4 + 5 + protected function newBlameFuture(DiffusionRequest $request, $path) { 6 + $repository = $request->getRepository(); 7 + $commit = $request->getCommit(); 8 + 9 + return $repository->getRemoteCommandFuture( 10 + 'blame --force %s', 11 + $repository->getSubversionPathURI($path, $commit)); 12 + } 13 + 14 + protected function resolveBlameFuture(ExecFuture $future) { 15 + list($err, $stdout) = $future->resolve(); 16 + 17 + if ($err) { 18 + return null; 19 + } 20 + 21 + $result = array(); 22 + $matches = null; 23 + 24 + $lines = phutil_split_lines($stdout); 25 + foreach ($lines as $line) { 26 + if (preg_match('/^\s*(\d+)/', $line, $matches)) { 27 + $result[] = (int)$matches[1]; 28 + } else { 29 + $result[] = null; 30 + } 31 + } 32 + 33 + return $result; 34 + } 35 + 36 + }