@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.

Merge "expandshortcommitquery" and "stablecommitnamequery" into "resolverefs"

Summary:
Ref T1493. Diffusion has some garbagey behavior for things we can't resolve. Common cases are:

- Looking at a branch that doesn't exist.
- Looking at a repository with no branches.
- Looking at a commit that doesn't exist.
- Looking at an empty repository.

In these cases, we generally fatal unhelpfully. I want to untangle this mess.

This doesn't help much, but does clean things up a bit. We currently have two separate query paths, "stablecommitname" and "expandshortcommit". These are pretty much doing the same thing -- taking some ref like "master" or "default" or a tag name or part of a commit name, and turning it into a full commit name. Merge them into a single "resolverefs" method.

This simplifies the code a fair bit, and gives us better error messages. They still aren't great, but they're like this now:

Ref "7498aec194ecf2d333e0e2baddd9d5cdf922d7f1" is ambiguous or does not exist.

...instead of just:

ERR-INVALID-COMMIT

Test Plan: Looked at Git, Mercurial and Subversion repositories that were empty and non-empty. Looked at branches/heads. Tried to look at invalid commits. Looked at tags. All of this still works, and some behaviors are a bit better than they used to be.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T1493

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

+304 -417
+4 -20
src/__phutil_library_map__.php
··· 151 151 'ConduitAPI_diffusion_createcomment_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_createcomment_Method.php', 152 152 'ConduitAPI_diffusion_diffquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_diffquery_Method.php', 153 153 'ConduitAPI_diffusion_existsquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_existsquery_Method.php', 154 - 'ConduitAPI_diffusion_expandshortcommitquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_expandshortcommitquery_Method.php', 155 154 'ConduitAPI_diffusion_filecontentquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_filecontentquery_Method.php', 156 155 'ConduitAPI_diffusion_findsymbols_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_findsymbols_Method.php', 157 156 'ConduitAPI_diffusion_getcommits_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_getcommits_Method.php', ··· 164 163 'ConduitAPI_diffusion_rawdiffquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_rawdiffquery_Method.php', 165 164 'ConduitAPI_diffusion_readmequery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_readmequery_Method.php', 166 165 'ConduitAPI_diffusion_refsquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_refsquery_Method.php', 166 + 'ConduitAPI_diffusion_resolverefs_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_resolverefs_Method.php', 167 167 'ConduitAPI_diffusion_searchquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_searchquery_Method.php', 168 - 'ConduitAPI_diffusion_stablecommitnamequery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_stablecommitnamequery_Method.php', 169 168 'ConduitAPI_diffusion_tagsquery_Method' => 'applications/diffusion/conduit/ConduitAPI_diffusion_tagsquery_Method.php', 170 169 'ConduitAPI_feed_Method' => 'applications/feed/conduit/ConduitAPI_feed_Method.php', 171 170 'ConduitAPI_feed_publish_Method' => 'applications/feed/conduit/ConduitAPI_feed_publish_Method.php', ··· 472 471 'DiffusionDiffController' => 'applications/diffusion/controller/DiffusionDiffController.php', 473 472 'DiffusionDoorkeeperCommitFeedStoryPublisher' => 'applications/diffusion/doorkeeper/DiffusionDoorkeeperCommitFeedStoryPublisher.php', 474 473 'DiffusionEmptyResultView' => 'applications/diffusion/view/DiffusionEmptyResultView.php', 475 - 'DiffusionExpandCommitQueryException' => 'applications/diffusion/exception/DiffusionExpandCommitQueryException.php', 476 - 'DiffusionExpandShortNameQuery' => 'applications/diffusion/query/expandshortname/DiffusionExpandShortNameQuery.php', 477 474 'DiffusionExternalController' => 'applications/diffusion/controller/DiffusionExternalController.php', 478 475 'DiffusionFileContent' => 'applications/diffusion/data/DiffusionFileContent.php', 479 476 'DiffusionFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionFileContentQuery.php', 480 477 'DiffusionGitBranch' => 'applications/diffusion/data/DiffusionGitBranch.php', 481 478 'DiffusionGitBranchTestCase' => 'applications/diffusion/data/__tests__/DiffusionGitBranchTestCase.php', 482 479 'DiffusionGitCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionGitCommitParentsQuery.php', 483 - 'DiffusionGitExpandShortNameQuery' => 'applications/diffusion/query/expandshortname/DiffusionGitExpandShortNameQuery.php', 484 480 'DiffusionGitFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionGitFileContentQuery.php', 485 481 'DiffusionGitRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionGitRawDiffQuery.php', 486 482 'DiffusionGitRequest' => 'applications/diffusion/request/DiffusionGitRequest.php', 487 483 'DiffusionGitResponse' => 'applications/diffusion/response/DiffusionGitResponse.php', 488 - 'DiffusionGitStableCommitNameQuery' => 'applications/diffusion/query/stablecommitname/DiffusionGitStableCommitNameQuery.php', 489 484 'DiffusionHistoryController' => 'applications/diffusion/controller/DiffusionHistoryController.php', 490 485 'DiffusionHistoryTableView' => 'applications/diffusion/view/DiffusionHistoryTableView.php', 491 486 'DiffusionHovercardEventListener' => 'applications/diffusion/events/DiffusionHovercardEventListener.php', ··· 498 493 'DiffusionLowLevelGitRefQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php', 499 494 'DiffusionLowLevelMercurialBranchesQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelMercurialBranchesQuery.php', 500 495 'DiffusionLowLevelQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelQuery.php', 496 + 'DiffusionLowLevelResolveRefsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php', 501 497 'DiffusionMercurialCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionMercurialCommitParentsQuery.php', 502 - 'DiffusionMercurialExpandShortNameQuery' => 'applications/diffusion/query/expandshortname/DiffusionMercurialExpandShortNameQuery.php', 503 498 'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php', 504 499 'DiffusionMercurialRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php', 505 500 'DiffusionMercurialRequest' => 'applications/diffusion/request/DiffusionMercurialRequest.php', 506 - 'DiffusionMercurialStableCommitNameQuery' => 'applications/diffusion/query/stablecommitname/DiffusionMercurialStableCommitNameQuery.php', 507 501 'DiffusionPathChange' => 'applications/diffusion/data/DiffusionPathChange.php', 508 502 'DiffusionPathChangeQuery' => 'applications/diffusion/query/pathchange/DiffusionPathChangeQuery.php', 509 503 'DiffusionPathCompleteController' => 'applications/diffusion/controller/DiffusionPathCompleteController.php', ··· 542 536 'DiffusionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSSHWorkflow.php', 543 537 'DiffusionSetPasswordPanel' => 'applications/diffusion/panel/DiffusionSetPasswordPanel.php', 544 538 'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php', 545 - 'DiffusionStableCommitNameQuery' => 'applications/diffusion/query/stablecommitname/DiffusionStableCommitNameQuery.php', 546 539 'DiffusionSvnCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionSvnCommitParentsQuery.php', 547 540 'DiffusionSvnFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionSvnFileContentQuery.php', 548 541 'DiffusionSvnRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionSvnRawDiffQuery.php', 549 542 'DiffusionSvnRequest' => 'applications/diffusion/request/DiffusionSvnRequest.php', 550 - 'DiffusionSvnStableCommitNameQuery' => 'applications/diffusion/query/stablecommitname/DiffusionSvnStableCommitNameQuery.php', 551 543 'DiffusionSymbolController' => 'applications/diffusion/controller/DiffusionSymbolController.php', 552 544 'DiffusionSymbolQuery' => 'applications/diffusion/query/DiffusionSymbolQuery.php', 553 545 'DiffusionTagListController' => 'applications/diffusion/controller/DiffusionTagListController.php', ··· 2366 2358 'ConduitAPI_diffusion_createcomment_Method' => 'ConduitAPI_diffusion_Method', 2367 2359 'ConduitAPI_diffusion_diffquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method', 2368 2360 'ConduitAPI_diffusion_existsquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method', 2369 - 'ConduitAPI_diffusion_expandshortcommitquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method', 2370 2361 'ConduitAPI_diffusion_filecontentquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method', 2371 2362 'ConduitAPI_diffusion_findsymbols_Method' => 'ConduitAPI_diffusion_Method', 2372 2363 'ConduitAPI_diffusion_getcommits_Method' => 'ConduitAPI_diffusion_Method', ··· 2379 2370 'ConduitAPI_diffusion_rawdiffquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method', 2380 2371 'ConduitAPI_diffusion_readmequery_Method' => 'ConduitAPI_diffusion_abstractquery_Method', 2381 2372 'ConduitAPI_diffusion_refsquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method', 2373 + 'ConduitAPI_diffusion_resolverefs_Method' => 'ConduitAPI_diffusion_abstractquery_Method', 2382 2374 'ConduitAPI_diffusion_searchquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method', 2383 - 'ConduitAPI_diffusion_stablecommitnamequery_Method' => 'ConduitAPI_diffusion_abstractquery_Method', 2384 2375 'ConduitAPI_diffusion_tagsquery_Method' => 'ConduitAPI_diffusion_abstractquery_Method', 2385 2376 'ConduitAPI_feed_Method' => 'ConduitAPIMethod', 2386 2377 'ConduitAPI_feed_publish_Method' => 'ConduitAPI_feed_Method', ··· 2681 2672 'DiffusionDiffController' => 'DiffusionController', 2682 2673 'DiffusionDoorkeeperCommitFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher', 2683 2674 'DiffusionEmptyResultView' => 'DiffusionView', 2684 - 'DiffusionExpandCommitQueryException' => 'Exception', 2685 - 'DiffusionExpandShortNameQuery' => 'DiffusionQuery', 2686 2675 'DiffusionExternalController' => 'DiffusionController', 2687 2676 'DiffusionFileContentQuery' => 'DiffusionQuery', 2688 2677 'DiffusionGitBranchTestCase' => 'PhabricatorTestCase', 2689 2678 'DiffusionGitCommitParentsQuery' => 'DiffusionCommitParentsQuery', 2690 - 'DiffusionGitExpandShortNameQuery' => 'DiffusionExpandShortNameQuery', 2691 2679 'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery', 2692 2680 'DiffusionGitRawDiffQuery' => 'DiffusionRawDiffQuery', 2693 2681 'DiffusionGitRequest' => 'DiffusionRequest', 2694 2682 'DiffusionGitResponse' => 'AphrontResponse', 2695 - 'DiffusionGitStableCommitNameQuery' => 'DiffusionStableCommitNameQuery', 2696 2683 'DiffusionHistoryController' => 'DiffusionController', 2697 2684 'DiffusionHistoryTableView' => 'DiffusionView', 2698 2685 'DiffusionHovercardEventListener' => 'PhabricatorEventListener', ··· 2704 2691 'DiffusionLowLevelGitRefQuery' => 'DiffusionLowLevelQuery', 2705 2692 'DiffusionLowLevelMercurialBranchesQuery' => 'DiffusionLowLevelQuery', 2706 2693 'DiffusionLowLevelQuery' => 'Phobject', 2694 + 'DiffusionLowLevelResolveRefsQuery' => 'DiffusionLowLevelQuery', 2707 2695 'DiffusionMercurialCommitParentsQuery' => 'DiffusionCommitParentsQuery', 2708 - 'DiffusionMercurialExpandShortNameQuery' => 'DiffusionExpandShortNameQuery', 2709 2696 'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery', 2710 2697 'DiffusionMercurialRawDiffQuery' => 'DiffusionRawDiffQuery', 2711 2698 'DiffusionMercurialRequest' => 'DiffusionRequest', 2712 - 'DiffusionMercurialStableCommitNameQuery' => 'DiffusionStableCommitNameQuery', 2713 2699 'DiffusionPathCompleteController' => 'DiffusionController', 2714 2700 'DiffusionPathQueryTestCase' => 'PhabricatorTestCase', 2715 2701 'DiffusionPathValidateController' => 'DiffusionController', ··· 2743 2729 'DiffusionSSHWorkflow' => 'PhabricatorSSHWorkflow', 2744 2730 'DiffusionSetPasswordPanel' => 'PhabricatorSettingsPanel', 2745 2731 'DiffusionSetupException' => 'AphrontUsageException', 2746 - 'DiffusionStableCommitNameQuery' => 'DiffusionQuery', 2747 2732 'DiffusionSvnCommitParentsQuery' => 'DiffusionCommitParentsQuery', 2748 2733 'DiffusionSvnFileContentQuery' => 'DiffusionFileContentQuery', 2749 2734 'DiffusionSvnRawDiffQuery' => 'DiffusionRawDiffQuery', 2750 2735 'DiffusionSvnRequest' => 'DiffusionRequest', 2751 - 'DiffusionSvnStableCommitNameQuery' => 'DiffusionStableCommitNameQuery', 2752 2736 'DiffusionSymbolController' => 'DiffusionController', 2753 2737 'DiffusionSymbolQuery' => 'PhabricatorOffsetPagedQuery', 2754 2738 'DiffusionTagListController' => 'DiffusionController',
+1 -1
src/applications/diffusion/conduit/ConduitAPI_diffusion_browsequery_Method.php
··· 198 198 199 199 list($entire_manifest) = $repository->execxLocalCommand( 200 200 'manifest --rev %s', 201 - $commit); 201 + hgsprintf('%s', $commit)); 202 202 $entire_manifest = explode("\n", $entire_manifest); 203 203 204 204 $results = array();
-70
src/applications/diffusion/conduit/ConduitAPI_diffusion_expandshortcommitquery_Method.php
··· 1 - <?php 2 - 3 - /** 4 - * @group conduit 5 - */ 6 - final class ConduitAPI_diffusion_expandshortcommitquery_Method 7 - extends ConduitAPI_diffusion_abstractquery_Method { 8 - 9 - public function __construct() { 10 - $this->setShouldCreateDiffusionRequest(false); 11 - } 12 - 13 - public function getMethodDescription() { 14 - return 15 - 'Expands a short commit name to its full glory.'; 16 - } 17 - 18 - public function defineReturnType() { 19 - return 'array'; 20 - } 21 - 22 - protected function defineCustomParamTypes() { 23 - return array( 24 - 'commit' => 'required string', 25 - ); 26 - } 27 - 28 - protected function defineCustomErrorTypes() { 29 - return array( 30 - 'ERR-MISSING-COMMIT' => pht( 31 - 'Bad commit.'), 32 - 'ERR-INVALID-COMMIT' => pht( 33 - 'Invalid object name.'), 34 - 'ERR-UNPARSEABLE-OUTPUT' => pht( 35 - 'Unparseable output from cat-file.') 36 - ); 37 - } 38 - 39 - protected function getGitResult(ConduitAPIRequest $request) { 40 - return $this->getGitOrMercurialResult($request); 41 - } 42 - protected function getMercurialResult(ConduitAPIRequest $request) { 43 - return $this->getGitOrMercurialResult($request); 44 - } 45 - 46 - private function getGitOrMercurialResult(ConduitAPIRequest $request) { 47 - $repository = $this->getRepository($request); 48 - $query = DiffusionExpandShortNameQuery::newFromRepository($repository); 49 - $query->setCommit($request->getValue('commit')); 50 - try { 51 - $result = $query->expand(); 52 - return $result; 53 - } catch (DiffusionExpandCommitQueryException $e) { 54 - switch ($e->getStatusCode()) { 55 - case DiffusionExpandCommitQueryException::CODE_INVALID: 56 - throw id(new ConduitException('ERR-INVALID-COMMIT')) 57 - ->setErrorDescription($e->getMessage()); 58 - break; 59 - case DiffusionExpandCommitQueryException::CODE_MISSING: 60 - throw id(new ConduitException('ERR-MISSING-COMMIT')) 61 - ->setErrorDescription($e->getMessage()); 62 - break; 63 - case DiffusionExpandCommitQueryException::CODE_UNPARSEABLE: 64 - throw id(new ConduitException('ERR-UNPARSEABLE-OUTPUT')) 65 - ->setErrorDescription($e->getMessage()); 66 - break; 67 - } 68 - } 69 - } 70 - }
+29
src/applications/diffusion/conduit/ConduitAPI_diffusion_resolverefs_Method.php
··· 1 + <?php 2 + 3 + final class ConduitAPI_diffusion_resolverefs_Method 4 + extends ConduitAPI_diffusion_abstractquery_Method { 5 + 6 + public function getMethodDescription() { 7 + return pht('Resolve references into stable, canonical identifiers.'); 8 + } 9 + 10 + public function defineReturnType() { 11 + return 'dict<string, list<dict<string, wild>>>'; 12 + } 13 + 14 + protected function defineCustomParamTypes() { 15 + return array( 16 + 'refs' => 'required list<string>', 17 + ); 18 + } 19 + 20 + protected function getResult(ConduitAPIRequest $request) { 21 + $refs = $request->getValue('refs'); 22 + 23 + return id(new DiffusionLowLevelResolveRefsQuery()) 24 + ->setRepository($this->getDiffusionRequest()->getRepository()) 25 + ->withRefs($refs) 26 + ->execute(); 27 + } 28 + 29 + }
-35
src/applications/diffusion/conduit/ConduitAPI_diffusion_stablecommitnamequery_Method.php
··· 1 - <?php 2 - 3 - /** 4 - * @group conduit 5 - */ 6 - final class ConduitAPI_diffusion_stablecommitnamequery_Method 7 - extends ConduitAPI_diffusion_abstractquery_Method { 8 - 9 - public function __construct() { 10 - $this->setShouldCreateDiffusionRequest(false); 11 - } 12 - 13 - public function getMethodDescription() { 14 - return 15 - 'Identifies the latest commit in a repository. Repositories with '. 16 - 'branch support must specify which branch to look at.'; 17 - } 18 - 19 - public function defineReturnType() { 20 - return 'string'; 21 - } 22 - 23 - protected function defineCustomParamTypes() { 24 - return array( 25 - 'branch' => 'required string', 26 - ); 27 - } 28 - 29 - protected function getResult(ConduitAPIRequest $request) { 30 - $repository = $this->getRepository($request); 31 - $query = DiffusionStableCommitNameQuery::newFromRepository($repository); 32 - $query->setBranch($request->getValue('branch')); 33 - return $query->load(); 34 - } 35 - }
-21
src/applications/diffusion/exception/DiffusionExpandCommitQueryException.php
··· 1 - <?php 2 - 3 - final class DiffusionExpandCommitQueryException extends Exception { 4 - const CODE_UNPARSEABLE = 'unparseable'; 5 - const CODE_MISSING = 'missing'; 6 - const CODE_INVALID = 'invalid'; 7 - 8 - private $statusCode; 9 - public function getStatusCode() { 10 - return $this->statusCode; 11 - } 12 - 13 - public function __construct($status_code /* ... */) { 14 - $args = func_get_args(); 15 - $this->statusCode = $args[0]; 16 - 17 - $args = array_slice($args, 1); 18 - call_user_func_array(array('parent', '__construct'), $args); 19 - } 20 - 21 - }
-51
src/applications/diffusion/query/expandshortname/DiffusionExpandShortNameQuery.php
··· 1 - <?php 2 - 3 - abstract class DiffusionExpandShortNameQuery extends DiffusionQuery { 4 - 5 - private $commit; 6 - private $commitType = 'commit'; 7 - private $tagContent; 8 - private $repository; 9 - 10 - public function setCommit($commit) { 11 - $this->commit = $commit; 12 - } 13 - public function getCommit() { 14 - return $this->commit; 15 - } 16 - 17 - public function setRepository(PhabricatorRepository $repository) { 18 - $this->repository = $repository; 19 - return $this; 20 - } 21 - public function getRepository() { 22 - return $this->repository; 23 - } 24 - 25 - protected function setCommitType($type) { 26 - $this->commitType = $type; 27 - return $this; 28 - } 29 - protected function setTagContent($content) { 30 - $this->tagContent = $content; 31 - return $this; 32 - } 33 - 34 - final public static function newFromRepository( 35 - PhabricatorRepository $repository) { 36 - 37 - $obj = parent::initQueryObject(__CLASS__, $repository); 38 - $obj->setRepository($repository); 39 - return $obj; 40 - } 41 - 42 - final public function expand() { 43 - $this->executeQuery(); 44 - 45 - return array( 46 - 'commit' => $this->commit, 47 - 'commitType' => $this->commitType, 48 - 'tagContent' => $this->tagContent); 49 - } 50 - 51 - }
-52
src/applications/diffusion/query/expandshortname/DiffusionGitExpandShortNameQuery.php
··· 1 - <?php 2 - 3 - final class DiffusionGitExpandShortNameQuery 4 - extends DiffusionExpandShortNameQuery { 5 - 6 - protected function executeQuery() { 7 - $repository = $this->getRepository(); 8 - $commit = $this->getCommit(); 9 - 10 - $future = $repository->getLocalCommandFuture( 11 - 'cat-file --batch'); 12 - $future->write($commit); 13 - list($stdout) = $future->resolvex(); 14 - 15 - list($hash, $type) = explode(' ', $stdout); 16 - if ($type == 'missing') { 17 - throw new DiffusionExpandCommitQueryException( 18 - DiffusionExpandCommitQueryException::CODE_MISSING, 19 - "Bad commit '{$commit}'."); 20 - } 21 - 22 - switch ($type) { 23 - case 'tag': 24 - $this->setCommitType('tag'); 25 - 26 - $matches = null; 27 - $ok = preg_match( 28 - '/^object ([a-f0-9]+)$.*?\n\n(.*)$/sm', 29 - $stdout, 30 - $matches); 31 - if (!$ok) { 32 - throw new DiffusionExpandCommitQueryException( 33 - DiffusionExpandCommitQueryException::CODE_UNPARSEABLE, 34 - "Unparseable output from cat-file: {$stdout}"); 35 - } 36 - 37 - $hash = $matches[1]; 38 - $this->setTagContent(trim($matches[2])); 39 - break; 40 - case 'commit': 41 - break; 42 - default: 43 - throw new DiffusionExpandCommitQueryException( 44 - DiffusionExpandCommitQueryException::CODE_INVALID, 45 - "The reference '{$commit}' does not name a valid ". 46 - 'commit or a tag in this repository.'); 47 - break; 48 - } 49 - 50 - $this->setCommit($hash); 51 - } 52 - }
-23
src/applications/diffusion/query/expandshortname/DiffusionMercurialExpandShortNameQuery.php
··· 1 - <?php 2 - 3 - final class DiffusionMercurialExpandShortNameQuery 4 - extends DiffusionExpandShortNameQuery { 5 - 6 - protected function executeQuery() { 7 - $repository = $this->getRepository(); 8 - $commit = $this->getCommit(); 9 - 10 - list($full_hash) = $repository->execxLocalCommand( 11 - 'log --template=%s --rev %s', 12 - '{node}', 13 - $commit); 14 - 15 - $full_hash = explode("\n", trim($full_hash)); 16 - 17 - // TODO: Show "multiple matching commits" if count is larger than 1. For 18 - // now, pick the first one. 19 - 20 - $this->setCommit(head($full_hash)); 21 - } 22 - 23 - }
+4
src/applications/diffusion/query/lowlevel/DiffusionLowLevelQuery.php
··· 16 16 } 17 17 18 18 public function execute() { 19 + if (!$this->getRepository()) { 20 + throw new Exception("Call setRepository() before execute()!"); 21 + } 22 + 19 23 return $this->executeQuery(); 20 24 } 21 25
+225
src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php
··· 1 + <?php 2 + 3 + /** 4 + * Resolves references (like short commit names, branch names, tag names, etc.) 5 + * into canonical, stable commit identifiers. This query works for all 6 + * repository types. 7 + */ 8 + final class DiffusionLowLevelResolveRefsQuery 9 + extends DiffusionLowLevelQuery { 10 + 11 + private $refs; 12 + 13 + public function withRefs(array $refs) { 14 + $this->refs = $refs; 15 + return $this; 16 + } 17 + 18 + public function executeQuery() { 19 + if (!$this->refs) { 20 + return array(); 21 + } 22 + 23 + switch ($this->getRepository()->getVersionControlSystem()) { 24 + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: 25 + $result = $this->resolveGitRefs(); 26 + break; 27 + case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: 28 + $result = $this->resolveMercurialRefs(); 29 + break; 30 + case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: 31 + $result = $this->resolveSubversionRefs(); 32 + break; 33 + default: 34 + throw new Exception("Unsupported repository type!"); 35 + } 36 + 37 + return $result; 38 + } 39 + 40 + private function resolveGitRefs() { 41 + $repository = $this->getRepository(); 42 + 43 + $future = $repository->getLocalCommandFuture('cat-file --batch-check'); 44 + $future->write(implode("\n", $this->refs)); 45 + list($stdout) = $future->resolvex(); 46 + 47 + $lines = explode("\n", rtrim($stdout, "\n")); 48 + if (count($lines) !== count($this->refs)) { 49 + throw new Exception("Unexpected line count from `git cat-file`!"); 50 + } 51 + 52 + $hits = array(); 53 + $tags = array(); 54 + 55 + $lines = array_combine($this->refs, $lines); 56 + foreach ($lines as $ref => $line) { 57 + $parts = explode(' ', $line); 58 + if (count($parts) < 2) { 59 + throw new Exception("Failed to parse `git cat-file` output: {$line}"); 60 + } 61 + list($identifier, $type) = $parts; 62 + 63 + if ($type == 'missing') { 64 + // This is either an ambiguous reference which resolves to several 65 + // objects, or an invalid reference. For now, always treat it as 66 + // invalid. It would be nice to resolve all possibilities for 67 + // ambiguous references at some point, although the strategy for doing 68 + // so isn't clear to me. 69 + continue; 70 + } 71 + 72 + switch ($type) { 73 + case 'commit': 74 + break; 75 + case 'tag': 76 + $tags[] = $identifier; 77 + break; 78 + default: 79 + throw new Exception( 80 + "Unexpected object type from `git cat-file`: {$line}"); 81 + } 82 + 83 + $hits[] = array( 84 + 'ref' => $ref, 85 + 'type' => $type, 86 + 'identifier' => $identifier, 87 + ); 88 + } 89 + 90 + $tag_map = array(); 91 + if ($tags) { 92 + // If some of the refs were tags, just load every tag in order to figure 93 + // out which commits they map to. This might be somewhat inefficient in 94 + // repositories with a huge number of tags. 95 + $tag_refs = id(new DiffusionLowLevelGitRefQuery()) 96 + ->setRepository($repository) 97 + ->withIsTag(true) 98 + ->executeQuery(); 99 + foreach ($tag_refs as $tag_ref) { 100 + $tag_map[$tag_ref->getShortName()] = $tag_ref->getCommitIdentifier(); 101 + } 102 + } 103 + 104 + $results = array(); 105 + foreach ($hits as $hit) { 106 + $type = $hit['type']; 107 + $ref = $hit['ref']; 108 + 109 + $alternate = null; 110 + if ($type == 'tag') { 111 + $alternate = $identifier; 112 + $identifier = idx($tag_map, $ref); 113 + if (!$identifier) { 114 + throw new Exception("Failed to look up tag '{$ref}'!"); 115 + } 116 + } 117 + 118 + $result = array( 119 + 'type' => $type, 120 + 'identifier' => $identifier, 121 + ); 122 + 123 + if ($alternate !== null) { 124 + $result['alternate'] = $alternate; 125 + } 126 + 127 + $results[$ref][] = $result; 128 + } 129 + 130 + return $results; 131 + } 132 + 133 + private function resolveMercurialRefs() { 134 + $repository = $this->getRepository(); 135 + 136 + $futures = array(); 137 + foreach ($this->refs as $ref) { 138 + // TODO: There was a note about `--rev 'a b'` not working for branches 139 + // with spaces in their names in older code, but I suspect this was 140 + // misidentified and resulted from the branch name being interpeted as 141 + // a revset. Use hgsprintf() to avoid that. If this doesn't break for a 142 + // bit, remove this comment. Otherwise, consider `-b %s --limit 1`. 143 + 144 + $futures[$ref] = $repository->getLocalCommandFuture( 145 + 'log --template=%s --rev %s', 146 + '{node}', 147 + hgsprintf('%s', $ref)); 148 + } 149 + 150 + $results = array(); 151 + foreach (Futures($futures) as $ref => $future) { 152 + try { 153 + list($stdout) = $future->resolvex(); 154 + } catch (CommandException $ex) { 155 + if (preg_match('/ambiguous identifier/', $ex->getStdErr())) { 156 + // This indicates that the ref ambiguously matched several things. 157 + // Eventually, it would be nice to return all of them, but it is 158 + // unclear how to best do that. For now, treat it as a miss instead. 159 + continue; 160 + } 161 + throw $ex; 162 + } 163 + 164 + // It doesn't look like we can figure out the type (commit/branch/rev) 165 + // from this output very easily. For now, just call everything a commit. 166 + $type = 'commit'; 167 + 168 + $results[$ref][] = array( 169 + 'type' => $type, 170 + 'identifier' => trim($stdout), 171 + ); 172 + } 173 + 174 + return $results; 175 + } 176 + 177 + private function resolveSubversionRefs() { 178 + $repository = $this->getRepository(); 179 + 180 + $max_commit = id(new PhabricatorRepositoryCommit()) 181 + ->loadOneWhere( 182 + 'repositoryID = %d ORDER BY epoch DESC, id DESC LIMIT 1', 183 + $repository->getID()); 184 + if (!$max_commit) { 185 + // This repository is empty or hasn't parsed yet, so none of the refs are 186 + // going to resolve. 187 + return array(); 188 + } 189 + 190 + $max_commit_id = (int)$max_commit->getCommitIdentifier(); 191 + 192 + $results = array(); 193 + foreach ($this->refs as $ref) { 194 + if ($ref == 'HEAD') { 195 + // Resolve "HEAD" to mean "the most recent commit". 196 + $results[$ref][] = array( 197 + 'type' => 'commit', 198 + 'identifier' => $max_commit_id, 199 + ); 200 + continue; 201 + } 202 + 203 + if (!preg_match('/^\d+$/', $ref)) { 204 + // This ref is non-numeric, so it doesn't resolve to anything. 205 + continue; 206 + } 207 + 208 + // Resolve other commits if we can deduce their existence. 209 + 210 + // TODO: When we import only part of a repository, we won't necessarily 211 + // have all of the smaller commits. Should we fail to resolve them here 212 + // for repositories with a subpath? It might let us simplify other things 213 + // elsewhere. 214 + if ((int)$ref <= $max_commit_id) { 215 + $results[$ref][] = array( 216 + 'type' => 'commit', 217 + 'identifier' => (int)$ref, 218 + ); 219 + } 220 + } 221 + 222 + return $results; 223 + } 224 + 225 + }
-24
src/applications/diffusion/query/stablecommitname/DiffusionGitStableCommitNameQuery.php
··· 1 - <?php 2 - 3 - final class DiffusionGitStableCommitNameQuery 4 - extends DiffusionStableCommitNameQuery { 5 - 6 - protected function executeQuery() { 7 - $repository = $this->getRepository(); 8 - $branch = $this->getBranch(); 9 - 10 - if ($repository->isWorkingCopyBare()) { 11 - list($stdout) = $repository->execxLocalCommand( 12 - 'rev-parse --verify %s', 13 - $branch); 14 - } else { 15 - list($stdout) = $repository->execxLocalCommand( 16 - 'rev-parse --verify %s/%s', 17 - DiffusionBranchInformation::DEFAULT_GIT_REMOTE, 18 - $branch); 19 - } 20 - 21 - $commit = trim($stdout); 22 - return substr($commit, 0, 16); 23 - } 24 - }
-30
src/applications/diffusion/query/stablecommitname/DiffusionMercurialStableCommitNameQuery.php
··· 1 - <?php 2 - 3 - final class DiffusionMercurialStableCommitNameQuery 4 - extends DiffusionStableCommitNameQuery { 5 - 6 - protected function executeQuery() { 7 - $repository = $this->getRepository(); 8 - 9 - // NOTE: For branches with spaces in their name like "a b", this 10 - // does not work properly: 11 - // 12 - // $ hg log --rev 'a b' 13 - // 14 - // We can use revsets instead: 15 - // 16 - // $ hg log --rev branch('a b') 17 - // 18 - // ...but they require a somewhat newer version of Mercurial. Instead, 19 - // use "-b" flag with limit 1 for greatest compatibility across 20 - // versions. 21 - 22 - list($stable_commit_name) = $repository->execxLocalCommand( 23 - 'log --template=%s -b %s --limit 1', 24 - '{node}', 25 - $this->getBranch()); 26 - 27 - return $stable_commit_name; 28 - } 29 - 30 - }
-35
src/applications/diffusion/query/stablecommitname/DiffusionStableCommitNameQuery.php
··· 1 - <?php 2 - 3 - abstract class DiffusionStableCommitNameQuery extends DiffusionQuery { 4 - 5 - private $branch; 6 - private $repository; 7 - 8 - public function setBranch($branch) { 9 - $this->branch = $branch; 10 - return $this; 11 - } 12 - public function getBranch() { 13 - return $this->branch; 14 - } 15 - 16 - public function setRepository(PhabricatorRepository $repository) { 17 - $this->repository = $repository; 18 - return $this; 19 - } 20 - public function getRepository() { 21 - return $this->repository; 22 - } 23 - 24 - final public static function newFromRepository( 25 - PhabricatorRepository $repository) { 26 - 27 - $obj = parent::initQueryObject(__CLASS__, $repository); 28 - $obj->setRepository($repository); 29 - return $obj; 30 - } 31 - 32 - final public function load() { 33 - return $this->executeQuery(); 34 - } 35 - }
-23
src/applications/diffusion/query/stablecommitname/DiffusionSvnStableCommitNameQuery.php
··· 1 - <?php 2 - 3 - final class DiffusionSvnStableCommitNameQuery 4 - extends DiffusionStableCommitNameQuery { 5 - 6 - protected function executeQuery() { 7 - $repository = $this->getRepository(); 8 - 9 - $commit = id(new PhabricatorRepositoryCommit()) 10 - ->loadOneWhere( 11 - 'repositoryID = %d ORDER BY epoch DESC LIMIT 1', 12 - $repository->getID()); 13 - if ($commit) { 14 - $stable_commit_name = $commit->getCommitIdentifier(); 15 - } else { 16 - // For new repositories, we may not have parsed any commits yet. Call 17 - // the stable commit "1" and avoid fataling. 18 - $stable_commit_name = 1; 19 - } 20 - 21 - return $stable_commit_name; 22 - } 23 - }
+41 -32
src/applications/diffusion/request/DiffusionRequest.php
··· 18 18 protected $symbolicCommit; 19 19 protected $commit; 20 20 protected $commitType = 'commit'; 21 - protected $tagContent; 22 21 protected $branch; 23 22 protected $lint; 24 23 ··· 261 260 'repositoryID = %d AND name = %s', 262 261 $this->getRepository()->getID(), 263 262 $this->getArcanistBranch()); 264 - } 265 - 266 - public function getTagContent() { 267 - return $this->tagContent; 268 263 } 269 264 270 265 public function loadCommit() { ··· 624 619 } 625 620 626 621 final protected function expandCommitName() { 627 - if ($this->shouldInitFromConduit()) { 628 - $commit_data = DiffusionQuery::callConduitWithDiffusionRequest( 629 - $this->getUser(), 630 - $this, 631 - 'diffusion.expandshortcommitquery', 632 - array( 633 - 'commit' => $this->commit 634 - )); 635 - } else { 636 - $repository = $this->getRepository(); 637 - $this->validateWorkingCopy($repository->getLocalPath()); 638 - $query = DiffusionExpandShortNameQuery::newFromRepository( 639 - $repository); 640 - $query->setCommit($this->commit); 641 - $commit_data = $query->expand(); 622 + $results = $this->resolveRefs(array($this->commit)); 623 + $matches = idx($results, $this->commit, array()); 624 + if (count($results) !== 1) { 625 + throw new Exception( 626 + pht('Ref "%s" is ambiguous or does not exist.', $this->commit)); 642 627 } 643 628 644 - $this->commit = $commit_data['commit']; 645 - $this->commitType = $commit_data['commitType']; 646 - $this->tagContent = $commit_data['tagContent']; 629 + $match = head($matches); 630 + 631 + $this->commit = $match['identifier']; 632 + $this->commitType = $match['type']; 647 633 } 648 634 649 635 public function getCommitType() { ··· 653 639 private function queryStableCommitName() { 654 640 if ($this->commit) { 655 641 $this->stableCommitName = $this->commit; 656 - } else if ($this->shouldInitFromConduit()) { 657 - $this->stableCommitName = DiffusionQuery::callConduitWithDiffusionRequest( 642 + return $this->stableCommitName; 643 + } 644 + 645 + if ($this->getSupportsBranches()) { 646 + $branch = $this->getBranch(); 647 + } else { 648 + $branch = 'HEAD'; 649 + } 650 + 651 + $results = $this->resolveRefs(array($branch)); 652 + 653 + $matches = idx($results, $branch, array()); 654 + if (count($matches) !== 1) { 655 + throw new Exception( 656 + pht('Ref "%s" is ambiguous or does not exist.', $branch)); 657 + } 658 + 659 + $this->stableCommitName = idx(head($matches), 'identifier'); 660 + return $this->stableCommitName; 661 + } 662 + 663 + private function resolveRefs(array $refs) { 664 + if ($this->shouldInitFromConduit()) { 665 + return DiffusionQuery::callConduitWithDiffusionRequest( 658 666 $this->getUser(), 659 667 $this, 660 - 'diffusion.stablecommitnamequery', 668 + 'diffusion.resolverefs', 661 669 array( 662 - 'branch' => $this->getBranch() 670 + 'refs' => $refs, 663 671 )); 664 672 } else { 665 - $query = DiffusionStableCommitNameQuery::newFromRepository( 666 - $this->getRepository()); 667 - $query->setBranch($this->getBranch()); 668 - $this->stableCommitName = $query->load(); 673 + return id(new DiffusionLowLevelResolveRefsQuery()) 674 + ->setRepository($this->getRepository()) 675 + ->withRefs($refs) 676 + ->execute(); 669 677 } 670 - return $this->stableCommitName; 671 678 } 679 + 680 + 672 681 }