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

Introduce ref cursors for repository parsing

Summary:
Ref T4327. I want to make change parsing testable; one thing which is blocking this is that the Git discovery process is still part of `PullLocal` daemon instead of being part of `DiscoveryEngine`. The unit test stuff which I want to use for change parsing relies on `DiscoveryEngine` to discover repositories during unit tests.

The major reason git discovery isn't part of `DiscoveryEngine` is that it relies on the messy "autoclose" logic, which we never implemented for Mercurial. Generally, I don't like how autoclose was implemented: it's complicated and gross and too hard to figure out and extend.

Instead, I want to do something more similar to what we do for pushes, which is cleaner overall. Basically this means remembering the old branch heads from the last time we parsed a repository, and figuring out what's new by comparing the old and new branch heads. This should give us several advantages:

- It should be simpler to understand than the autoclose stuff, which is pretty mind-numbing, at least for me.
- It will let us satisfy branch and tag queries cheaply (from the database) instead of having to go to the repository. We could also satisfy some ref-resolve queries from the database.
- It should be easier to extend to Mercurial.

This implements the basics -- pretty much a table to store the cursors, which we update only for Git for now.

Test Plan:
- Ran migration.
- Ran `bin/repository discover X --trace --verbose` on various repositories with branches and tags, before and after modifying pushes.
- Pushed commits to a git repo.
- Looked at database tables.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T4327

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

+496 -8
+11
resources/sql/autopatches/20140116.reporefcursor.sql
··· 1 + CREATE TABLE {$NAMESPACE}_repository.repository_refcursor ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + repositoryPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 4 + refType VARCHAR(32) NOT NULL COLLATE utf8_bin, 5 + refNameHash VARCHAR(12) NOT NULL COLLATE latin1_bin, 6 + refNameRaw LONGTEXT NOT NULL COLLATE latin1_bin, 7 + refNameEncoding VARCHAR(16) COLLATE utf8_bin, 8 + commitIdentifier VARCHAR(40) NOT NULL COLLATE utf8_bin, 9 + 10 + KEY `key_cursor` (repositoryPHID, refType, refNameHash) 11 + ) ENGINE=InnoDB, COLLATE=utf8_general_ci;
+12
src/__phutil_library_map__.php
··· 1856 1856 'PhabricatorRepositoryManagementLookupUsersWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php', 1857 1857 'PhabricatorRepositoryManagementMarkImportedWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMarkImportedWorkflow.php', 1858 1858 'PhabricatorRepositoryManagementPullWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementPullWorkflow.php', 1859 + 'PhabricatorRepositoryManagementRefsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php', 1859 1860 'PhabricatorRepositoryManagementWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementWorkflow.php', 1860 1861 'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositoryMercurialCommitChangeParserWorker.php', 1861 1862 'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryMercurialCommitMessageParserWorker.php', ··· 1872 1873 'PhabricatorRepositoryPushLogQuery' => 'applications/repository/query/PhabricatorRepositoryPushLogQuery.php', 1873 1874 'PhabricatorRepositoryPushLogSearchEngine' => 'applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php', 1874 1875 'PhabricatorRepositoryQuery' => 'applications/repository/query/PhabricatorRepositoryQuery.php', 1876 + 'PhabricatorRepositoryRefCursor' => 'applications/repository/storage/PhabricatorRepositoryRefCursor.php', 1877 + 'PhabricatorRepositoryRefCursorQuery' => 'applications/repository/query/PhabricatorRepositoryRefCursorQuery.php', 1878 + 'PhabricatorRepositoryRefEngine' => 'applications/repository/engine/PhabricatorRepositoryRefEngine.php', 1875 1879 'PhabricatorRepositorySearchEngine' => 'applications/repository/query/PhabricatorRepositorySearchEngine.php', 1876 1880 'PhabricatorRepositoryStatusMessage' => 'applications/repository/storage/PhabricatorRepositoryStatusMessage.php', 1877 1881 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositorySvnCommitChangeParserWorker.php', ··· 4521 4525 'PhabricatorRepositoryManagementLookupUsersWorkflow' => 'PhabricatorRepositoryManagementWorkflow', 4522 4526 'PhabricatorRepositoryManagementMarkImportedWorkflow' => 'PhabricatorRepositoryManagementWorkflow', 4523 4527 'PhabricatorRepositoryManagementPullWorkflow' => 'PhabricatorRepositoryManagementWorkflow', 4528 + 'PhabricatorRepositoryManagementRefsWorkflow' => 'PhabricatorRepositoryManagementWorkflow', 4524 4529 'PhabricatorRepositoryManagementWorkflow' => 'PhabricatorManagementWorkflow', 4525 4530 'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', 4526 4531 'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker', ··· 4545 4550 'PhabricatorRepositoryPushLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4546 4551 'PhabricatorRepositoryPushLogSearchEngine' => 'PhabricatorApplicationSearchEngine', 4547 4552 'PhabricatorRepositoryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4553 + 'PhabricatorRepositoryRefCursor' => 4554 + array( 4555 + 0 => 'PhabricatorRepositoryDAO', 4556 + 1 => 'PhabricatorPolicyInterface', 4557 + ), 4558 + 'PhabricatorRepositoryRefCursorQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4559 + 'PhabricatorRepositoryRefEngine' => 'PhabricatorRepositoryEngine', 4548 4560 'PhabricatorRepositorySearchEngine' => 'PhabricatorApplicationSearchEngine', 4549 4561 'PhabricatorRepositoryStatusMessage' => 'PhabricatorRepositoryDAO', 4550 4562 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
+11
src/applications/diffusion/data/DiffusionBranchInformation.php
··· 42 42 ); 43 43 } 44 44 45 + // TODO: These are hacks to make this compatible with DiffusionRepositoryRef 46 + // for PhabricatorRepositoryRefEngine. The two classes should be merged. 47 + 48 + public function getShortName() { 49 + return $this->getName(); 50 + } 51 + 52 + public function getCommitIdentifier() { 53 + return $this->getHeadCommitIdentifier(); 54 + } 55 + 45 56 }
+8
src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
··· 149 149 PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE, 150 150 null); 151 151 $this->discoverRepository($repository); 152 + $this->updateRepositoryRefs($repository); 152 153 $repository->writeStatusMessage( 153 154 PhabricatorRepositoryStatusMessage::TYPE_FETCH, 154 155 PhabricatorRepositoryStatusMessage::CODE_OKAY); ··· 265 266 } else { 266 267 return $result; 267 268 } 269 + } 270 + 271 + private function updateRepositoryRefs(PhabricatorRepository $repository) { 272 + id(new PhabricatorRepositoryRefEngine()) 273 + ->setRepository($repository) 274 + ->updateRefs(); 268 275 } 269 276 270 277 private function getDiscoveryEngine(PhabricatorRepository $repository) { ··· 613 620 $this->executeGitDiscoverCommit($repository, $commit, $name, true); 614 621 } 615 622 } 623 + 616 624 617 625 618 626 /**
+223
src/applications/repository/engine/PhabricatorRepositoryRefEngine.php
··· 1 + <?php 2 + 3 + /** 4 + * Update the ref cursors for a repository, which track the positions of 5 + * branches, bookmarks, and tags. 6 + */ 7 + final class PhabricatorRepositoryRefEngine 8 + extends PhabricatorRepositoryEngine { 9 + 10 + private $newRefs = array(); 11 + private $deadRefs = array(); 12 + 13 + public function updateRefs() { 14 + $this->newRefs = array(); 15 + $this->deadRefs = array(); 16 + 17 + $repository = $this->getRepository(); 18 + 19 + $vcs = $repository->getVersionControlSystem(); 20 + switch ($vcs) { 21 + case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: 22 + // No meaningful refs of any type in Subversion. 23 + $branches = array(); 24 + $bookmarks = array(); 25 + $tags = array(); 26 + break; 27 + case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: 28 + $branches = $this->loadMercurialBranchPositions($repository); 29 + $bookmarks = $this->loadMercurialBookmarkPositions($repository); 30 + $tags = array(); 31 + break; 32 + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: 33 + $branches = $this->loadGitBranchPositions($repository); 34 + $bookmarks = array(); 35 + $tags = $this->loadGitTagPositions($repository); 36 + break; 37 + default: 38 + throw new Exception(pht('Unknown VCS "%s"!', $vcs)); 39 + } 40 + 41 + $maps = array( 42 + PhabricatorRepositoryRefCursor::TYPE_BRANCH => $branches, 43 + PhabricatorRepositoryRefCursor::TYPE_TAG => $tags, 44 + PhabricatorRepositoryRefCursor::TYPE_BOOKMARK => $bookmarks, 45 + ); 46 + 47 + $all_cursors = id(new PhabricatorRepositoryRefCursorQuery()) 48 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 49 + ->withRepositoryPHIDs(array($repository->getPHID())) 50 + ->execute(); 51 + $cursor_groups = mgroup($all_cursors, 'getRefType'); 52 + 53 + foreach ($maps as $type => $refs) { 54 + $cursor_group = idx($cursor_groups, $type, array()); 55 + $this->updateCursors($cursor_group, $refs, $type); 56 + } 57 + 58 + if ($this->newRefs || $this->deadRefs) { 59 + $repository->openTransaction(); 60 + foreach ($this->newRefs as $ref) { 61 + $ref->save(); 62 + } 63 + foreach ($this->deadRefs as $ref) { 64 + $ref->delete(); 65 + } 66 + $repository->saveTransaction(); 67 + 68 + $this->newRefs = array(); 69 + $this->deadRefs = array(); 70 + } 71 + } 72 + 73 + private function markRefNew(PhabricatorRepositoryRefCursor $cursor) { 74 + $this->newRefs[] = $cursor; 75 + return $this; 76 + } 77 + 78 + private function markRefDead(PhabricatorRepositoryRefCursor $cursor) { 79 + $this->deadRefs[] = $cursor; 80 + return $this; 81 + } 82 + 83 + private function updateCursors( 84 + array $cursors, 85 + array $new_refs, 86 + $ref_type) { 87 + $repository = $this->getRepository(); 88 + 89 + // NOTE: Mercurial branches may have multiple branch heads; this logic 90 + // is complex primarily to account for that. 91 + 92 + // Group all the cursors by their ref name, like "master". Since Mercurial 93 + // branches may have multiple heads, there could be several cursors with 94 + // the same name. 95 + $cursor_groups = mgroup($cursors, 'getRefNameRaw'); 96 + 97 + // Group all the new ref values by their name. As above, these groups may 98 + // have multiple members in Mercurial. 99 + $ref_groups = mgroup($new_refs, 'getShortName'); 100 + 101 + foreach ($ref_groups as $name => $refs) { 102 + $new_commits = mpull($refs, 'getCommitIdentifier', 'getCommitIdentifier'); 103 + 104 + $ref_cursors = idx($cursor_groups, $name, array()); 105 + $old_commits = mpull($ref_cursors, null, 'getCommitIdentifier'); 106 + 107 + // We're going to delete all the cursors pointing at commits which are 108 + // no longer associated with the refs. This primarily makes the Mercurial 109 + // multiple head case easier, and means that when we update a ref we 110 + // delete the old one and write a new one. 111 + foreach ($ref_cursors as $cursor) { 112 + if (isset($new_commits[$cursor->getCommitIdentifier()])) { 113 + // This ref previously pointed at this commit, and still does. 114 + $this->log( 115 + pht( 116 + 'Ref %s "%s" still points at %s.', 117 + $ref_type, 118 + $name, 119 + $cursor->getCommitIdentifier())); 120 + } else { 121 + // This ref previously pointed at this commit, but no longer does. 122 + $this->log( 123 + pht( 124 + 'Ref %s "%s" no longer points at %s.', 125 + $ref_type, 126 + $name, 127 + $cursor->getCommitIdentifier())); 128 + 129 + // Nuke the obsolete cursor. 130 + $this->markRefDead($cursor); 131 + } 132 + } 133 + 134 + // Now, we're going to insert new cursors for all the commits which are 135 + // associated with this ref that don't currently have cursors. 136 + $added_commits = array_diff_key($new_commits, $old_commits); 137 + foreach ($added_commits as $identifier) { 138 + $this->log( 139 + pht( 140 + 'Ref %s "%s" now points at %s.', 141 + $ref_type, 142 + $name, 143 + $identifier)); 144 + $this->markRefNew( 145 + id(new PhabricatorRepositoryRefCursor()) 146 + ->setRepositoryPHID($repository->getPHID()) 147 + ->setRefType($ref_type) 148 + ->setRefName($name) 149 + ->setCommitIdentifier($identifier)); 150 + } 151 + 152 + foreach ($added_commits as $identifier) { 153 + // TODO: Do autoclose stuff here. 154 + } 155 + } 156 + 157 + // Find any cursors for refs which no longer exist. This happens when a 158 + // branch, tag or bookmark is deleted. 159 + 160 + foreach ($cursor_groups as $name => $cursor_group) { 161 + if (idx($ref_groups, $name) === null) { 162 + $this->log( 163 + pht( 164 + 'Ref %s "%s" no longer exists.', 165 + $cursor->getRefType(), 166 + $cursor->getRefName())); 167 + foreach ($cursor_group as $cursor) { 168 + $this->markRefDead($cursor); 169 + } 170 + } 171 + } 172 + } 173 + 174 + 175 + /* -( Updating Git Refs )-------------------------------------------------- */ 176 + 177 + 178 + /** 179 + * @task git 180 + */ 181 + private function loadGitBranchPositions(PhabricatorRepository $repository) { 182 + return id(new DiffusionLowLevelGitRefQuery()) 183 + ->setRepository($repository) 184 + ->withIsOriginBranch(true) 185 + ->execute(); 186 + } 187 + 188 + 189 + /** 190 + * @task git 191 + */ 192 + private function loadGitTagPositions(PhabricatorRepository $repository) { 193 + return id(new DiffusionLowLevelGitRefQuery()) 194 + ->setRepository($repository) 195 + ->withIsTag(true) 196 + ->execute(); 197 + } 198 + 199 + 200 + /* -( Updating Mercurial Refs )-------------------------------------------- */ 201 + 202 + 203 + /** 204 + * @task hg 205 + */ 206 + private function loadMercurialBranchPositions( 207 + PhabricatorRepository $repository) { 208 + return id(new DiffusionLowLevelMercurialBranchesQuery()) 209 + ->setRepository($repository) 210 + ->execute(); 211 + } 212 + 213 + 214 + /** 215 + * @task hg 216 + */ 217 + private function loadMercurialBookmarkPositions( 218 + PhabricatorRepository $repository) { 219 + // TODO: Implement support for Mercurial bookmarks. 220 + return array(); 221 + } 222 + 223 + }
+49
src/applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorRepositoryManagementRefsWorkflow 4 + extends PhabricatorRepositoryManagementWorkflow { 5 + 6 + public function didConstruct() { 7 + $this 8 + ->setName('refs') 9 + ->setExamples('**refs** [__options__] __repository__ ...') 10 + ->setSynopsis('Update refs in __repository__, named by callsign.') 11 + ->setArguments( 12 + array( 13 + array( 14 + 'name' => 'verbose', 15 + 'help' => 'Show additional debugging information.', 16 + ), 17 + array( 18 + 'name' => 'repos', 19 + 'wildcard' => true, 20 + ), 21 + )); 22 + } 23 + 24 + public function execute(PhutilArgumentParser $args) { 25 + $repos = $this->loadRepositories($args, 'repos'); 26 + 27 + if (!$repos) { 28 + throw new PhutilArgumentUsageException( 29 + pht( 30 + "Specify one or more repositories to update refs for, ". 31 + "by callsign.")); 32 + } 33 + 34 + $console = PhutilConsole::getConsole(); 35 + foreach ($repos as $repo) { 36 + $console->writeOut("Updating refs in '%s'...\n", $repo->getCallsign()); 37 + 38 + $engine = id(new PhabricatorRepositoryRefEngine()) 39 + ->setRepository($repo) 40 + ->setVerbose($args->getArg('verbose')) 41 + ->updateRefs(); 42 + } 43 + 44 + $console->writeOut("Done.\n"); 45 + 46 + return 0; 47 + } 48 + 49 + }
+83
src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorRepositoryRefCursorQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $repositoryPHIDs; 7 + private $refTypes; 8 + 9 + public function withRepositoryPHIDs(array $phids) { 10 + $this->repositoryPHIDs = $phids; 11 + return $this; 12 + } 13 + 14 + public function withRefTypes(array $types) { 15 + $this->refTypes = $types; 16 + return $this; 17 + } 18 + 19 + protected function loadPage() { 20 + $table = new PhabricatorRepositoryRefCursor(); 21 + $conn_r = $table->establishConnection('r'); 22 + 23 + $data = queryfx_all( 24 + $conn_r, 25 + 'SELECT * FROM %T r %Q %Q %Q', 26 + $table->getTableName(), 27 + $this->buildWhereClause($conn_r), 28 + $this->buildOrderClause($conn_r), 29 + $this->buildLimitClause($conn_r)); 30 + 31 + return $table->loadAllFromArray($data); 32 + } 33 + 34 + public function willFilterPage(array $refs) { 35 + $repository_phids = mpull($refs, 'getRepositoryPHID'); 36 + 37 + $repositories = id(new PhabricatorRepositoryQuery()) 38 + ->setViewer($this->getViewer()) 39 + ->setParentQuery($this) 40 + ->withPHIDs($repository_phids) 41 + ->execute(); 42 + $repositories = mpull($repositories, null, 'getPHID'); 43 + 44 + foreach ($refs as $key => $ref) { 45 + $repository = idx($repositories, $ref->getRepositoryPHID()); 46 + if (!$repository) { 47 + unset($refs[$key]); 48 + continue; 49 + } 50 + $ref->attachRepository($repository); 51 + } 52 + 53 + return $refs; 54 + } 55 + 56 + private function buildWhereClause(AphrontDatabaseConnection $conn_r) { 57 + $where = array(); 58 + 59 + if ($this->repositoryPHIDs) { 60 + $where[] = qsprintf( 61 + $conn_r, 62 + 'repositoryPHID IN (%Ls)', 63 + $this->repositoryPHIDs); 64 + } 65 + 66 + if ($this->refTypes) { 67 + $where[] = qsprintf( 68 + $conn_r, 69 + 'refType IN (%Ls)', 70 + $this->refTypes); 71 + } 72 + 73 + $where[] = $this->buildPagingClause($conn_r); 74 + 75 + return $this->formatWhereClause($where); 76 + } 77 + 78 + 79 + public function getQueryApplicationClass() { 80 + return 'PhabricatorApplicationDiffusion'; 81 + } 82 + 83 + }
+6
src/applications/repository/storage/PhabricatorRepository.php
··· 802 802 $mirror->delete(); 803 803 } 804 804 805 + $ref_cursors = id(new PhabricatorRepositoryRefCursor()) 806 + ->loadAllWhere('repositoryPHID = %s', $this->getPHID()); 807 + foreach ($ref_cursors as $cursor) { 808 + $cursor->delete(); 809 + } 810 + 805 811 $conn_w = $this->establishConnection('w'); 806 812 807 813 queryfx(
+3 -1
src/applications/repository/storage/PhabricatorRepositoryCommit.php
··· 24 24 const IMPORTED_HERALD = 8; 25 25 const IMPORTED_ALL = 15; 26 26 27 + const IMPORTED_CLOSEABLE = 1024; 28 + 27 29 private $commitData = self::ATTACHABLE; 28 30 private $audits; 29 31 private $repository = self::ATTACHABLE; ··· 42 44 } 43 45 44 46 public function isImported() { 45 - return ($this->getImportStatus() == self::IMPORTED_ALL); 47 + return $this->isPartiallyImported(self::IMPORTED_ALL); 46 48 } 47 49 48 50 public function writeImportStatusFlag($flag) {
+4 -7
src/applications/repository/storage/PhabricatorRepositoryPushLog.php
··· 77 77 } 78 78 79 79 public function getRefName() { 80 - if ($this->getRefNameEncoding() == 'utf8') { 81 - return $this->getRefNameRaw(); 82 - } 83 - return phutil_utf8ize($this->getRefNameRaw()); 80 + return $this->getUTF8StringFromStorage( 81 + $this->getRefNameRaw(), 82 + $this->getRefNameEncoding()); 84 83 } 85 84 86 85 public function setRefName($ref_raw) { 87 - $encoding = phutil_is_utf8($ref_raw) ? 'utf8' : null; 88 - 89 86 $this->setRefNameRaw($ref_raw); 90 87 $this->setRefNameHash(PhabricatorHash::digestForIndex($ref_raw)); 91 - $this->setRefNameEncoding($encoding); 88 + $this->setRefNameEncoding($this->detectEncodingForStorage($ref_raw)); 92 89 93 90 return $this; 94 91 }
+75
src/applications/repository/storage/PhabricatorRepositoryRefCursor.php
··· 1 + <?php 2 + 3 + /** 4 + * Stores the previous value of a ref (like a branch or tag) so we can figure 5 + * out how a repository has changed when we discover new commits or branch 6 + * heads. 7 + */ 8 + final class PhabricatorRepositoryRefCursor extends PhabricatorRepositoryDAO 9 + implements PhabricatorPolicyInterface { 10 + 11 + const TYPE_BRANCH = 'branch'; 12 + const TYPE_TAG = 'tag'; 13 + const TYPE_BOOKMARK = 'bookmark'; 14 + 15 + protected $repositoryPHID; 16 + protected $refType; 17 + protected $refNameHash; 18 + protected $refNameRaw; 19 + protected $refNameEncoding; 20 + protected $commitIdentifier; 21 + 22 + private $repository = self::ATTACHABLE; 23 + 24 + public function getConfiguration() { 25 + return array( 26 + self::CONFIG_TIMESTAMPS => false, 27 + ) + parent::getConfiguration(); 28 + } 29 + 30 + public function getRefName() { 31 + return $this->getUTF8StringFromStorage( 32 + $this->getRefNameRaw(), 33 + $this->getRefNameEncoding()); 34 + } 35 + 36 + public function setRefName($ref_raw) { 37 + $this->setRefNameRaw($ref_raw); 38 + $this->setRefNameHash(PhabricatorHash::digestForIndex($ref_raw)); 39 + $this->setRefNameEncoding($this->detectEncodingForStorage($ref_raw)); 40 + 41 + return $this; 42 + } 43 + 44 + public function attachRepository(PhabricatorRepository $repository) { 45 + $this->repository = $repository; 46 + return $this; 47 + } 48 + 49 + public function getRepository() { 50 + return $this->assertAttached($this->repository); 51 + } 52 + 53 + 54 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 55 + 56 + 57 + public function getCapabilities() { 58 + return array( 59 + PhabricatorPolicyCapability::CAN_VIEW, 60 + ); 61 + } 62 + 63 + public function getPolicy($capability) { 64 + return $this->getRepository()->getPolicy($capability); 65 + } 66 + 67 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 68 + return $this->getRepository()->hasAutomaticCapability($capability, $viewer); 69 + } 70 + 71 + public function describeAutomaticCapability($capability) { 72 + return pht('Repository refs have the same policies as their repository.'); 73 + } 74 + 75 + }
+11
src/infrastructure/storage/lisk/PhabricatorLiskDAO.php
··· 175 175 return $value[$key]; 176 176 } 177 177 178 + protected function detectEncodingForStorage($string) { 179 + return phutil_is_utf8($string) ? 'utf8' : null; 180 + } 181 + 182 + protected function getUTF8StringFromStorage($string, $encoding) { 183 + if ($encoding == 'utf8') { 184 + return $string; 185 + } 186 + return phutil_utf8ize($string); 187 + } 188 + 178 189 }