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

When a ref is moved or deleted, put it on a list; later, check for reachability

Summary:
Ref T9028. This allows us to detect when commits are unreachable:

- When a ref (tag, branch, etc) is moved or deleted, store the old thing it pointed at in a list.
- After discovery, go through the list and check if all the stuff on it is still reachable.
- If something isn't, try to follow its ancestors back until we find something that is reachable.
- Then, mark everything we found as unreachable.
- Finally, rebuild the repository summary table to correct the commit count.

Test Plan:
- Deleted a ref, ran `pull` + `refs`, saw oldref in database.
- Ran `discover`, saw it process the oldref, mark the unreachable commit, and update the summary table.
- Visited commit page, saw it properly marked.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9028

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

+204
+6
resources/sql/autopatches/20160616.repo.01.oldref.sql
··· 1 + CREATE TABLE {$NAMESPACE}_repository.repository_oldref ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + repositoryPHID VARBINARY(64) NOT NULL, 4 + commitIdentifier VARCHAR(40) NOT NULL COLLATE {$COLLATE_TEXT}, 5 + KEY `key_repository` (repositoryPHID) 6 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+5
src/__phutil_library_map__.php
··· 3277 3277 'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryMercurialCommitMessageParserWorker.php', 3278 3278 'PhabricatorRepositoryMirror' => 'applications/repository/storage/PhabricatorRepositoryMirror.php', 3279 3279 'PhabricatorRepositoryMirrorEngine' => 'applications/repository/engine/PhabricatorRepositoryMirrorEngine.php', 3280 + 'PhabricatorRepositoryOldRef' => 'applications/repository/storage/PhabricatorRepositoryOldRef.php', 3280 3281 'PhabricatorRepositoryParsedChange' => 'applications/repository/data/PhabricatorRepositoryParsedChange.php', 3281 3282 'PhabricatorRepositoryPullEngine' => 'applications/repository/engine/PhabricatorRepositoryPullEngine.php', 3282 3283 'PhabricatorRepositoryPullEvent' => 'applications/repository/storage/PhabricatorRepositoryPullEvent.php', ··· 8067 8068 'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker', 8068 8069 'PhabricatorRepositoryMirror' => 'PhabricatorRepositoryDAO', 8069 8070 'PhabricatorRepositoryMirrorEngine' => 'PhabricatorRepositoryEngine', 8071 + 'PhabricatorRepositoryOldRef' => array( 8072 + 'PhabricatorRepositoryDAO', 8073 + 'PhabricatorPolicyInterface', 8074 + ), 8070 8075 'PhabricatorRepositoryParsedChange' => 'Phobject', 8071 8076 'PhabricatorRepositoryPullEngine' => 'PhabricatorRepositoryEngine', 8072 8077 'PhabricatorRepositoryPullEvent' => array(
+134
src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php
··· 105 105 $this->commitCache[$ref->getIdentifier()] = true; 106 106 } 107 107 108 + $this->markUnreachableCommits($repository); 109 + 108 110 $version = $this->getObservedVersion($repository); 109 111 if ($version !== null) { 110 112 id(new DiffusionRepositoryClusterEngine()) ··· 729 731 } 730 732 731 733 return (int)$version['version']; 734 + } 735 + 736 + private function markUnreachableCommits(PhabricatorRepository $repository) { 737 + // For now, this is only supported for Git. 738 + if (!$repository->isGit()) { 739 + return; 740 + } 741 + 742 + // Find older versions of refs which we haven't processed yet. We're going 743 + // to make sure their commits are still reachable. 744 + $old_refs = id(new PhabricatorRepositoryOldRef())->loadAllWhere( 745 + 'repositoryPHID = %s', 746 + $repository->getPHID()); 747 + 748 + // We can share a single graph stream across all the checks we need to do. 749 + $stream = new PhabricatorGitGraphStream($repository); 750 + 751 + foreach ($old_refs as $old_ref) { 752 + $identifier = $old_ref->getCommitIdentifier(); 753 + $this->markUnreachableFrom($repository, $stream, $identifier); 754 + 755 + // If nothing threw an exception, we're all done with this ref. 756 + $old_ref->delete(); 757 + } 758 + } 759 + 760 + private function markUnreachableFrom( 761 + PhabricatorRepository $repository, 762 + PhabricatorGitGraphStream $stream, 763 + $identifier) { 764 + 765 + $unreachable = array(); 766 + 767 + $commit = id(new PhabricatorRepositoryCommit())->loadOneWhere( 768 + 'repositoryID = %s AND commitIdentifier = %s', 769 + $repository->getID(), 770 + $identifier); 771 + if (!$commit) { 772 + return; 773 + } 774 + 775 + $look = array($commit); 776 + $seen = array(); 777 + while ($look) { 778 + $target = array_pop($look); 779 + 780 + // If we've already checked this commit (for example, because history 781 + // branches and then merges) we don't need to check it again. 782 + $target_identifier = $target->getCommitIdentifier(); 783 + if (isset($seen[$target_identifier])) { 784 + continue; 785 + } 786 + 787 + $seen[$target_identifier] = true; 788 + 789 + try { 790 + $stream->getCommitDate($target_identifier); 791 + $reachable = true; 792 + } catch (Exception $ex) { 793 + $reachable = false; 794 + } 795 + 796 + if ($reachable) { 797 + // This commit is reachable, so we don't need to go any further 798 + // down this road. 799 + continue; 800 + } 801 + 802 + $unreachable[] = $target; 803 + 804 + // Find the commit's parents and check them for reachability, too. We 805 + // have to look in the database since we no may longer have the commit 806 + // in the repository. 807 + $rows = queryfx_all( 808 + $commit->establishConnection('w'), 809 + 'SELECT commit.* FROM %T commit 810 + JOIN %T parents ON commit.id = parents.parentCommitID 811 + WHERE parents.childCommitID = %d', 812 + $commit->getTableName(), 813 + PhabricatorRepository::TABLE_PARENTS, 814 + $target->getID()); 815 + if (!$rows) { 816 + continue; 817 + } 818 + 819 + $parents = id(new PhabricatorRepositoryCommit()) 820 + ->loadAllFromArray($rows); 821 + foreach ($parents as $parent) { 822 + $look[] = $parent; 823 + } 824 + } 825 + 826 + $unreachable = array_reverse($unreachable); 827 + 828 + $flag = PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE; 829 + foreach ($unreachable as $unreachable_commit) { 830 + $unreachable_commit->writeImportStatusFlag($flag); 831 + } 832 + 833 + // If anything was unreachable, just rebuild the whole summary table. 834 + // We can't really update it incrementally when a commit becomes 835 + // unreachable. 836 + if ($unreachable) { 837 + $this->rebuildSummaryTable($repository); 838 + } 839 + } 840 + 841 + private function rebuildSummaryTable(PhabricatorRepository $repository) { 842 + $conn_w = $repository->establishConnection('w'); 843 + 844 + $data = queryfx_one( 845 + $conn_w, 846 + 'SELECT COUNT(*) N, MAX(id) id, MAX(epoch) epoch 847 + FROM %T WHERE repositoryID = %d AND (importStatus & %d) != %d', 848 + id(new PhabricatorRepositoryCommit())->getTableName(), 849 + $repository->getID(), 850 + PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE, 851 + PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE); 852 + 853 + queryfx( 854 + $conn_w, 855 + 'INSERT INTO %T (repositoryID, size, lastCommitID, epoch) 856 + VALUES (%d, %d, %d, %d) 857 + ON DUPLICATE KEY UPDATE 858 + size = VALUES(size), 859 + lastCommitID = VALUES(lastCommitID), 860 + epoch = VALUES(epoch)', 861 + PhabricatorRepository::TABLE_SUMMARY, 862 + $repository->getID(), 863 + $data['N'], 864 + $data['id'], 865 + $data['epoch']); 732 866 } 733 867 734 868 }
+7
src/applications/repository/engine/PhabricatorRepositoryRefEngine.php
··· 85 85 $ref->save(); 86 86 } 87 87 foreach ($this->deadRefs as $ref) { 88 + // Shove this ref into the old refs table so the discovery engine 89 + // can check if any commits have been rendered unreachable. 90 + id(new PhabricatorRepositoryOldRef()) 91 + ->setRepositoryPHID($repository->getPHID()) 92 + ->setCommitIdentifier($ref->getCommitIdentifier()) 93 + ->save(); 94 + 88 95 $ref->delete(); 89 96 } 90 97 $repository->saveTransaction();
+52
src/applications/repository/storage/PhabricatorRepositoryOldRef.php
··· 1 + <?php 2 + 3 + /** 4 + * Stores outdated refs which need to be checked for reachability. 5 + * 6 + * When a branch is deleted, the old HEAD ends up here and the discovery 7 + * engine marks all the commits that previously appeared on it as unreachable. 8 + */ 9 + final class PhabricatorRepositoryOldRef 10 + extends PhabricatorRepositoryDAO 11 + implements PhabricatorPolicyInterface { 12 + 13 + protected $repositoryPHID; 14 + protected $commitIdentifier; 15 + 16 + protected function getConfiguration() { 17 + return array( 18 + self::CONFIG_TIMESTAMPS => false, 19 + self::CONFIG_COLUMN_SCHEMA => array( 20 + 'commitIdentifier' => 'text40', 21 + ), 22 + self::CONFIG_KEY_SCHEMA => array( 23 + 'key_repository' => array( 24 + 'columns' => array('repositoryPHID'), 25 + ), 26 + ), 27 + ) + parent::getConfiguration(); 28 + } 29 + 30 + 31 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 32 + 33 + 34 + public function getCapabilities() { 35 + return array( 36 + PhabricatorPolicyCapability::CAN_VIEW, 37 + ); 38 + } 39 + 40 + public function getPolicy($capability) { 41 + return PhabricatorPolicies::getMostOpenPolicy(); 42 + } 43 + 44 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 45 + return false; 46 + } 47 + 48 + public function describeAutomaticCapability($capability) { 49 + return null; 50 + } 51 + 52 + }